summaryrefslogtreecommitdiff
path: root/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/x509.js
diff options
context:
space:
mode:
Diffstat (limited to 'deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/x509.js')
-rw-r--r--deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/x509.js223
1 files changed, 219 insertions, 4 deletions
diff --git a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/x509.js b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/x509.js
index a2975404a5..c630ce1059 100644
--- a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/x509.js
+++ b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/x509.js
@@ -79,7 +79,10 @@ SIGN_ALGS['1.3.14.3.2.29'] = 'rsa-sha1';
var EXTS = {
'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'
};
function read(buf, options) {
@@ -210,6 +213,26 @@ var ALTNAME = {
OID: Context(8)
};
+/* 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) {
der.readSequence();
var after = der.offset + der.length;
@@ -223,6 +246,81 @@ function readExtension(cert, buf, der) {
critical = der.readBoolean();
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):
der.readSequence(asn1.Ber.OctetString);
der.readSequence();
@@ -421,13 +519,27 @@ function writeTBSCert(cert, der) {
}
if (altNames.length > 0 || subject.type === 'host' ||
+ (cert.purposes !== undefined && cert.purposes.length > 0) ||
(sig.extras && sig.extras.exts)) {
der.startSequence(Local(3));
der.startSequence();
- 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)
exts = sig.extras.exts;
@@ -468,6 +580,54 @@ function writeTBSCert(cert, der) {
}
der.endSequence();
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 {
der.writeBuffer(exts[i].data,
asn1.Ber.OctetString);
@@ -482,3 +642,58 @@ function writeTBSCert(cert, der) {
der.endSequence();
}
+
+/*
+ * 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);
+}