Socket
Socket
Sign inDemoInstall

sshpk

Package Overview
Dependencies
10
Maintainers
1
Versions
50
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.10.2 to 1.11.0

90

lib/certificate.js

@@ -42,2 +42,4 @@ // Copyright 2016 Joyent, Inc.

assert.optionalArrayOfString(opts.purposes, 'options.purposes');
this._hashCache = {};

@@ -53,2 +55,3 @@

this.validUntil = opts.validUntil;
this.purposes = opts.purposes;
}

@@ -113,2 +116,6 @@

return (false);
if (this.issuer.purposes && this.issuer.purposes.length > 0 &&
this.issuer.purposes.indexOf('ca') === -1) {
return (false);
}

@@ -186,2 +193,43 @@ return (this.isSignedByKey(issuerCert.subjectKey));

var purposes = options.purposes;
if (purposes === undefined)
purposes = [];
if (purposes.indexOf('signature') === -1)
purposes.push('signature');
/* Self-signed certs are always CAs. */
if (purposes.indexOf('ca') === -1)
purposes.push('ca');
if (purposes.indexOf('crl') === -1)
purposes.push('crl');
/*
* If we weren't explicitly given any other purposes, do the sensible
* thing and add some basic ones depending on the subject type.
*/
if (purposes.length <= 3) {
var hostSubjects = subjects.filter(function (subject) {
return (subject.type === 'host');
});
var userSubjects = subjects.filter(function (subject) {
return (subject.type === 'user');
});
if (hostSubjects.length > 0) {
if (purposes.indexOf('serverAuth') === -1)
purposes.push('serverAuth');
}
if (userSubjects.length > 0) {
if (purposes.indexOf('clientAuth') === -1)
purposes.push('clientAuth');
}
if (userSubjects.length > 0 || hostSubjects.length > 0) {
if (purposes.indexOf('keyAgreement') === -1)
purposes.push('keyAgreement');
if (key.type === 'rsa' &&
purposes.indexOf('encryption') === -1)
purposes.push('encryption');
}
}
var cert = new Certificate({

@@ -195,3 +243,4 @@ subjects: subjects,

validFrom: validFrom,
validUntil: validUntil
validUntil: validUntil,
purposes: purposes
});

@@ -244,2 +293,38 @@ cert.signWith(key);

var purposes = options.purposes;
if (purposes === undefined)
purposes = [];
if (purposes.indexOf('signature') === -1)
purposes.push('signature');
if (options.ca === true) {
if (purposes.indexOf('ca') === -1)
purposes.push('ca');
if (purposes.indexOf('crl') === -1)
purposes.push('crl');
}
var hostSubjects = subjects.filter(function (subject) {
return (subject.type === 'host');
});
var userSubjects = subjects.filter(function (subject) {
return (subject.type === 'user');
});
if (hostSubjects.length > 0) {
if (purposes.indexOf('serverAuth') === -1)
purposes.push('serverAuth');
}
if (userSubjects.length > 0) {
if (purposes.indexOf('clientAuth') === -1)
purposes.push('clientAuth');
}
if (userSubjects.length > 0 || hostSubjects.length > 0) {
if (purposes.indexOf('keyAgreement') === -1)
purposes.push('keyAgreement');
if (key.type === 'rsa' &&
purposes.indexOf('encryption') === -1)
purposes.push('encryption');
}
var cert = new Certificate({

@@ -253,3 +338,4 @@ subjects: subjects,

validFrom: validFrom,
validUntil: validUntil
validUntil: validUntil,
purposes: purposes
});

@@ -256,0 +342,0 @@ cert.signWith(issuerKey);

@@ -82,3 +82,6 @@ // Copyright 2016 Joyent, Inc.

'issuerKeyId': '2.5.29.35',
'altName': '2.5.29.17'
'altName': '2.5.29.17',
'basicConstraints': '2.5.29.19',
'keyUsage': '2.5.29.15',
'extKeyUsage': '2.5.29.37'
};

@@ -214,2 +217,22 @@

/* RFC5280, section 4.2.1.12 (KeyPurposeId) */
var EXTPURPOSE = {
'serverAuth': '1.3.6.1.5.5.7.3.1',
'clientAuth': '1.3.6.1.5.5.7.3.2',
'codeSigning': '1.3.6.1.5.5.7.3.3',
/* See https://github.com/joyent/oid-docs/blob/master/root.md */
'joyentDocker': '1.3.6.1.4.1.38678.1.4.1',
'joyentCmon': '1.3.6.1.4.1.38678.1.4.2'
};
var EXTPURPOSE_REV = {};
Object.keys(EXTPURPOSE).forEach(function (k) {
EXTPURPOSE_REV[EXTPURPOSE[k]] = k;
});
var KEYUSEBITS = [
'signature', 'identity', 'keyEncryption',
'encryption', 'keyAgreement', 'ca', 'crl'
];
function readExtension(cert, buf, der) {

@@ -228,2 +251,77 @@ der.readSequence();

switch (extId) {
case (EXTS.basicConstraints):
der.readSequence(asn1.Ber.OctetString);
der.readSequence();
var bcEnd = der.offset + der.length;
var ca = false;
if (der.peek() === asn1.Ber.Boolean)
ca = der.readBoolean();
if (cert.purposes === undefined)
cert.purposes = [];
if (ca === true)
cert.purposes.push('ca');
var bc = { oid: extId, critical: critical };
if (der.offset < bcEnd && der.peek() === asn1.Ber.Integer)
bc.pathLen = der.readInt();
sig.extras.exts.push(bc);
break;
case (EXTS.extKeyUsage):
der.readSequence(asn1.Ber.OctetString);
der.readSequence();
if (cert.purposes === undefined)
cert.purposes = [];
var ekEnd = der.offset + der.length;
while (der.offset < ekEnd) {
var oid = der.readOID();
cert.purposes.push(EXTPURPOSE_REV[oid] || oid);
}
/*
* This is a bit of a hack: in the case where we have a cert
* that's only allowed to do serverAuth or clientAuth (and not
* the other), we want to make sure all our Subjects are of
* the right type. But we already parsed our Subjects and
* decided if they were hosts or users earlier (since it appears
* first in the cert).
*
* So we go through and mutate them into the right kind here if
* it doesn't match. This might not be hugely beneficial, as it
* seems that single-purpose certs are not often seen in the
* wild.
*/
if (cert.purposes.indexOf('serverAuth') !== -1 &&
cert.purposes.indexOf('clientAuth') === -1) {
cert.subjects.forEach(function (ide) {
if (ide.type !== 'host') {
ide.type = 'host';
ide.hostname = ide.uid ||
ide.email ||
ide.components[0].value;
}
});
} else if (cert.purposes.indexOf('clientAuth') !== -1 &&
cert.purposes.indexOf('serverAuth') === -1) {
cert.subjects.forEach(function (ide) {
if (ide.type !== 'user') {
ide.type = 'user';
ide.uid = ide.hostname ||
ide.email ||
ide.components[0].value;
}
});
}
sig.extras.exts.push({ oid: extId, critical: critical });
break;
case (EXTS.keyUsage):
der.readSequence(asn1.Ber.OctetString);
var bits = der.readString(asn1.Ber.BitString, true);
var setBits = readBitField(bits, KEYUSEBITS);
setBits.forEach(function (bit) {
if (cert.purposes === undefined)
cert.purposes = [];
if (cert.purposes.indexOf(bit) === -1)
cert.purposes.push(bit);
});
sig.extras.exts.push({ oid: extId, critical: critical,
bits: bits });
break;
case (EXTS.altName):

@@ -427,2 +525,3 @@ der.readSequence(asn1.Ber.OctetString);

if (altNames.length > 0 || subject.type === 'host' ||
(cert.purposes !== undefined && cert.purposes.length > 0) ||
(sig.extras && sig.extras.exts)) {

@@ -432,5 +531,18 @@ der.startSequence(Local(3));

var exts = [
{ oid: EXTS.altName }
];
var exts = [];
if (cert.purposes !== undefined && cert.purposes.length > 0) {
exts.push({
oid: EXTS.basicConstraints,
critical: true
});
exts.push({
oid: EXTS.keyUsage,
critical: true
});
exts.push({
oid: EXTS.extKeyUsage,
critical: true
});
}
exts.push({ oid: EXTS.altName });
if (sig.extras && sig.extras.exts)

@@ -476,2 +588,50 @@ exts = sig.extras.exts;

der.endSequence();
} else if (exts[i].oid === EXTS.basicConstraints) {
der.startSequence(asn1.Ber.OctetString);
der.startSequence();
var ca = (cert.purposes.indexOf('ca') !== -1);
var pathLen = exts[i].pathLen;
der.writeBoolean(ca);
if (pathLen !== undefined)
der.writeInt(pathLen);
der.endSequence();
der.endSequence();
} else if (exts[i].oid === EXTS.extKeyUsage) {
der.startSequence(asn1.Ber.OctetString);
der.startSequence();
cert.purposes.forEach(function (purpose) {
if (purpose === 'ca')
return;
if (KEYUSEBITS.indexOf(purpose) !== -1)
return;
var oid = purpose;
if (EXTPURPOSE[purpose] !== undefined)
oid = EXTPURPOSE[purpose];
der.writeOID(oid);
});
der.endSequence();
der.endSequence();
} else if (exts[i].oid === EXTS.keyUsage) {
der.startSequence(asn1.Ber.OctetString);
/*
* If we parsed this certificate from a byte
* stream (i.e. we didn't generate it in sshpk)
* then we'll have a ".bits" property on the
* ext with the original raw byte contents.
*
* If we have this, use it here instead of
* regenerating it. This guarantees we output
* the same data we parsed, so signatures still
* validate.
*/
if (exts[i].bits !== undefined) {
der.writeBuffer(exts[i].bits,
asn1.Ber.BitString);
} else {
var bits = writeBitField(cert.purposes,
KEYUSEBITS);
der.writeBuffer(bits,
asn1.Ber.BitString);
}
der.endSequence();
} else {

@@ -491,1 +651,56 @@ der.writeBuffer(exts[i].data,

}
/*
* Reads an ASN.1 BER bitfield out of the Buffer produced by doing
* `BerReader#readString(asn1.Ber.BitString)`. That function gives us the raw
* contents of the BitString tag, which is a count of unused bits followed by
* the bits as a right-padded byte string.
*
* `bits` is the Buffer, `bitIndex` should contain an array of string names
* for the bits in the string, ordered starting with bit #0 in the ASN.1 spec.
*
* Returns an array of Strings, the names of the bits that were set to 1.
*/
function readBitField(bits, bitIndex) {
var bitLen = 8 * (bits.length - 1) - bits[0];
var setBits = {};
for (var i = 0; i < bitLen; ++i) {
var byteN = 1 + Math.floor(i / 8);
var bit = 7 - (i % 8);
var mask = 1 << bit;
var bitVal = ((bits[byteN] & mask) !== 0);
var name = bitIndex[i];
if (bitVal && typeof (name) === 'string') {
setBits[name] = true;
}
}
return (Object.keys(setBits));
}
/*
* `setBits` is an array of strings, containing the names for each bit that
* sould be set to 1. `bitIndex` is same as in `readBitField()`.
*
* Returns a Buffer, ready to be written out with `BerWriter#writeString()`.
*/
function writeBitField(setBits, bitIndex) {
var bitLen = bitIndex.length;
var blen = Math.ceil(bitLen / 8);
var unused = blen * 8 - bitLen;
var bits = new Buffer(1 + blen);
bits.fill(0);
bits[0] = unused;
for (var i = 0; i < bitLen; ++i) {
var byteN = 1 + Math.floor(i / 8);
var bit = 7 - (i % 8);
var mask = 1 << bit;
var name = bitIndex[i];
if (name === undefined)
continue;
var bitVal = (setBits.indexOf(name) !== -1);
if (bitVal) {
bits[byteN] |= mask;
}
}
return (bits);
}

2

package.json
{
"name": "sshpk",
"version": "1.10.2",
"version": "1.11.0",
"description": "A library for finding and using SSH public keys",

@@ -5,0 +5,0 @@ "main": "lib/index.js",

@@ -411,6 +411,6 @@ sshpk

Notably, there is no implementation of CA chain-of-trust verification, and no
support for key usage restrictions (or other kinds of restrictions). Please do
the security world a favour, and DO NOT use this code for certificate
verification in the traditional X.509 CA chain style.
Notably, there is no implementation of CA chain-of-trust verification, and only
very minimal support for key usage restrictions. Please do the security world
a favour, and DO NOT use this code for certificate verification in the
traditional X.509 CA chain style.

@@ -440,2 +440,3 @@ ### `parseCertificate(data, format)`

- `serial` -- optional Buffer, the serial number of the certificate
- `purposes` -- optional Array of String, X.509 key usage restrictions

@@ -457,2 +458,3 @@ ### `createCertificate(subject, key, issuer, issuerKey[, options])`

- `serial` -- optional Buffer, the serial number of the certificate
- `purposes` -- optional Array of String, X.509 key usage restrictions

@@ -481,2 +483,19 @@ ### `Certificate#subjects`

### `Certificate#purposes`
Array of Strings indicating the X.509 key usage purposes that this certificate
is valid for. The possible strings at the moment are:
* `'signature'` -- key can be used for digital signatures
* `'identity'` -- key can be used to attest about the identity of the signer
(X.509 calls this `nonRepudiation`)
* `'codeSigning'` -- key can be used to sign executable code
* `'keyEncryption'` -- key can be used to encrypt other keys
* `'encryption'` -- key can be used to encrypt data (only applies for RSA)
* `'keyAgreement'` -- key can be used for key exchange protocols such as
Diffie-Hellman
* `'ca'` -- key can be used to sign other certificates (is a Certificate
Authority)
* `'crl'` -- key can be used to sign Certificate Revocation Lists (CRLs)
### `Certificate#isExpired([when])`

@@ -483,0 +502,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc