diff options
Diffstat (limited to 'deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib')
20 files changed, 1647 insertions, 80 deletions
diff --git a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/certificate.js b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/certificate.js new file mode 100644 index 0000000000..4fbe6abad4 --- /dev/null +++ b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/certificate.js @@ -0,0 +1,291 @@ +// Copyright 2016 Joyent, Inc. + +module.exports = Certificate; + +var assert = require('assert-plus'); +var algs = require('./algs'); +var crypto = require('crypto'); +var Fingerprint = require('./fingerprint'); +var Signature = require('./signature'); +var errs = require('./errors'); +var util = require('util'); +var utils = require('./utils'); +var Key = require('./key'); +var PrivateKey = require('./private-key'); +var Identity = require('./identity'); + +var formats = {}; +formats['openssh'] = require('./formats/openssh-cert'); +formats['x509'] = require('./formats/x509'); +formats['pem'] = require('./formats/x509-pem'); + +var CertificateParseError = errs.CertificateParseError; +var InvalidAlgorithmError = errs.InvalidAlgorithmError; + +function Certificate(opts) { + assert.object(opts, 'options'); + assert.arrayOfObject(opts.subjects, 'options.subjects'); + utils.assertCompatible(opts.subjects[0], Identity, [1, 0], + 'options.subjects'); + utils.assertCompatible(opts.subjectKey, Key, [1, 0], + 'options.subjectKey'); + utils.assertCompatible(opts.issuer, Identity, [1, 0], 'options.issuer'); + if (opts.issuerKey !== undefined) { + utils.assertCompatible(opts.issuerKey, Key, [1, 0], + 'options.issuerKey'); + } + assert.object(opts.signatures, 'options.signatures'); + assert.buffer(opts.serial, 'options.serial'); + assert.date(opts.validFrom, 'options.validFrom'); + assert.date(opts.validUntil, 'optons.validUntil'); + + this._hashCache = {}; + + this.subjects = opts.subjects; + this.issuer = opts.issuer; + this.subjectKey = opts.subjectKey; + this.issuerKey = opts.issuerKey; + this.signatures = opts.signatures; + this.serial = opts.serial; + this.validFrom = opts.validFrom; + this.validUntil = opts.validUntil; +} + +Certificate.formats = formats; + +Certificate.prototype.toBuffer = function (format, options) { + if (format === undefined) + format = 'x509'; + assert.string(format, 'format'); + assert.object(formats[format], 'formats[format]'); + assert.optionalObject(options, 'options'); + + return (formats[format].write(this, options)); +}; + +Certificate.prototype.toString = function (format, options) { + if (format === undefined) + format = 'pem'; + return (this.toBuffer(format, options).toString()); +}; + +Certificate.prototype.fingerprint = function (algo) { + if (algo === undefined) + algo = 'sha256'; + assert.string(algo, 'algorithm'); + var opts = { + type: 'certificate', + hash: this.hash(algo), + algorithm: algo + }; + return (new Fingerprint(opts)); +}; + +Certificate.prototype.hash = function (algo) { + assert.string(algo, 'algorithm'); + algo = algo.toLowerCase(); + if (algs.hashAlgs[algo] === undefined) + throw (new InvalidAlgorithmError(algo)); + + if (this._hashCache[algo]) + return (this._hashCache[algo]); + + var hash = crypto.createHash(algo). + update(this.toBuffer('x509')).digest(); + this._hashCache[algo] = hash; + return (hash); +}; + +Certificate.prototype.isExpired = function (when) { + if (when === undefined) + when = new Date(); + return (!((when.getTime() >= this.validFrom.getTime()) && + (when.getTime() < this.validUntil.getTime()))); +}; + +Certificate.prototype.isSignedBy = function (issuerCert) { + utils.assertCompatible(issuerCert, Certificate, [1, 0], 'issuer'); + + if (!this.issuer.equals(issuerCert.subjects[0])) + return (false); + + return (this.isSignedByKey(issuerCert.subjectKey)); +}; + +Certificate.prototype.isSignedByKey = function (issuerKey) { + utils.assertCompatible(issuerKey, Key, [1, 2], 'issuerKey'); + + if (this.issuerKey !== undefined) { + return (this.issuerKey. + fingerprint('sha512').matches(issuerKey)); + } + + var fmt = Object.keys(this.signatures)[0]; + var valid = formats[fmt].verify(this, issuerKey); + if (valid) + this.issuerKey = issuerKey; + return (valid); +}; + +Certificate.prototype.signWith = function (key) { + utils.assertCompatible(key, PrivateKey, [1, 2], 'key'); + var fmts = Object.keys(formats); + var didOne = false; + for (var i = 0; i < fmts.length; ++i) { + if (fmts[i] !== 'pem') { + var ret = formats[fmts[i]].sign(this, key); + if (ret === true) + didOne = true; + } + } + if (!didOne) { + throw (new Error('Failed to sign the certificate for any ' + + 'available certificate formats')); + } +}; + +Certificate.createSelfSigned = function (subjectOrSubjects, key, options) { + var subjects; + if (Array.isArray(subjectOrSubjects)) + subjects = subjectOrSubjects; + else + subjects = [subjectOrSubjects]; + + assert.arrayOfObject(subjects); + subjects.forEach(function (subject) { + utils.assertCompatible(subject, Identity, [1, 0], 'subject'); + }); + + utils.assertCompatible(key, PrivateKey, [1, 2], 'private key'); + + assert.optionalObject(options, 'options'); + if (options === undefined) + options = {}; + assert.optionalObject(options.validFrom, 'options.validFrom'); + assert.optionalObject(options.validUntil, 'options.validUntil'); + var validFrom = options.validFrom; + var validUntil = options.validUntil; + if (validFrom === undefined) + validFrom = new Date(); + if (validUntil === undefined) { + assert.optionalNumber(options.lifetime, 'options.lifetime'); + var lifetime = options.lifetime; + if (lifetime === undefined) + lifetime = 10*365*24*3600; + validUntil = new Date(); + validUntil.setTime(validUntil.getTime() + lifetime*1000); + } + assert.optionalBuffer(options.serial, 'options.serial'); + var serial = options.serial; + if (serial === undefined) + serial = new Buffer('0000000000000001', 'hex'); + + var cert = new Certificate({ + subjects: subjects, + issuer: subjects[0], + subjectKey: key.toPublic(), + issuerKey: key.toPublic(), + signatures: {}, + serial: serial, + validFrom: validFrom, + validUntil: validUntil + }); + cert.signWith(key); + + return (cert); +}; + +Certificate.create = + function (subjectOrSubjects, key, issuer, issuerKey, options) { + var subjects; + if (Array.isArray(subjectOrSubjects)) + subjects = subjectOrSubjects; + else + subjects = [subjectOrSubjects]; + + assert.arrayOfObject(subjects); + subjects.forEach(function (subject) { + utils.assertCompatible(subject, Identity, [1, 0], 'subject'); + }); + + utils.assertCompatible(key, Key, [1, 0], 'key'); + if (PrivateKey.isPrivateKey(key)) + key = key.toPublic(); + utils.assertCompatible(issuer, Identity, [1, 0], 'issuer'); + utils.assertCompatible(issuerKey, PrivateKey, [1, 2], 'issuer key'); + + assert.optionalObject(options, 'options'); + if (options === undefined) + options = {}; + assert.optionalObject(options.validFrom, 'options.validFrom'); + assert.optionalObject(options.validUntil, 'options.validUntil'); + var validFrom = options.validFrom; + var validUntil = options.validUntil; + if (validFrom === undefined) + validFrom = new Date(); + if (validUntil === undefined) { + assert.optionalNumber(options.lifetime, 'options.lifetime'); + var lifetime = options.lifetime; + if (lifetime === undefined) + lifetime = 10*365*24*3600; + validUntil = new Date(); + validUntil.setTime(validUntil.getTime() + lifetime*1000); + } + assert.optionalBuffer(options.serial, 'options.serial'); + var serial = options.serial; + if (serial === undefined) + serial = new Buffer('0000000000000001', 'hex'); + + var cert = new Certificate({ + subjects: subjects, + issuer: issuer, + subjectKey: key, + issuerKey: issuerKey.toPublic(), + signatures: {}, + serial: serial, + validFrom: validFrom, + validUntil: validUntil + }); + cert.signWith(issuerKey); + + return (cert); +}; + +Certificate.parse = function (data, format, options) { + if (typeof (data) !== 'string') + assert.buffer(data, 'data'); + if (format === undefined) + format = 'auto'; + assert.string(format, 'format'); + if (typeof (options) === 'string') + options = { filename: options }; + assert.optionalObject(options, 'options'); + if (options === undefined) + options = {}; + assert.optionalString(options.filename, 'options.filename'); + if (options.filename === undefined) + options.filename = '(unnamed)'; + + assert.object(formats[format], 'formats[format]'); + + try { + var k = formats[format].read(data, options); + return (k); + } catch (e) { + throw (new CertificateParseError(options.filename, format, e)); + } +}; + +Certificate.isCertificate = function (obj, ver) { + return (utils.isCompatible(obj, Certificate, ver)); +}; + +/* + * API versions for Certificate: + * [1,0] -- initial ver + */ +Certificate.prototype._sshpkApiVersion = [1, 0]; + +Certificate._oldVersionDetect = function (obj) { + return ([1, 0]); +}; diff --git a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/errors.js b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/errors.js index 3551c1071e..1cc09ec71d 100644 --- a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/errors.js +++ b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/errors.js @@ -50,9 +50,35 @@ function SignatureParseError(type, format, innerErr) { } util.inherits(SignatureParseError, Error); +function CertificateParseError(name, format, innerErr) { + if (Error.captureStackTrace) + Error.captureStackTrace(this, CertificateParseError); + this.name = 'CertificateParseError'; + this.format = format; + this.certName = name; + this.innerErr = innerErr; + this.message = 'Failed to parse ' + name + ' as a valid ' + format + + ' format certificate: ' + innerErr.message; +} +util.inherits(CertificateParseError, Error); + +function KeyEncryptedError(name, format) { + if (Error.captureStackTrace) + Error.captureStackTrace(this, KeyEncryptedError); + this.name = 'KeyEncryptedError'; + this.format = format; + this.keyName = name; + this.message = 'The ' + format + ' format key ' + name + ' is ' + + 'encrypted (password-protected), and no passphrase was ' + + 'provided in `options`'; +} +util.inherits(KeyEncryptedError, Error); + module.exports = { FingerprintFormatError: FingerprintFormatError, InvalidAlgorithmError: InvalidAlgorithmError, KeyParseError: KeyParseError, - SignatureParseError: SignatureParseError + SignatureParseError: SignatureParseError, + KeyEncryptedError: KeyEncryptedError, + CertificateParseError: CertificateParseError }; diff --git a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/fingerprint.js b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/fingerprint.js index c607330e7f..7ed7e51304 100644 --- a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/fingerprint.js +++ b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/fingerprint.js @@ -7,6 +7,7 @@ var algs = require('./algs'); var crypto = require('crypto'); var errs = require('./errors'); var Key = require('./key'); +var Certificate = require('./certificate'); var utils = require('./utils'); var FingerprintFormatError = errs.FingerprintFormatError; @@ -14,6 +15,7 @@ var InvalidAlgorithmError = errs.InvalidAlgorithmError; function Fingerprint(opts) { assert.object(opts, 'options'); + assert.string(opts.type, 'options.type'); assert.buffer(opts.hash, 'options.hash'); assert.string(opts.algorithm, 'options.algorithm'); @@ -22,6 +24,7 @@ function Fingerprint(opts) { throw (new InvalidAlgorithmError(this.algorithm)); this.hash = opts.hash; + this.type = opts.type; } Fingerprint.prototype.toString = function (format) { @@ -44,11 +47,16 @@ Fingerprint.prototype.toString = function (format) { } }; -Fingerprint.prototype.matches = function (key) { - assert.object(key, 'key'); - utils.assertCompatible(key, Key, [1, 0], 'key'); +Fingerprint.prototype.matches = function (other) { + assert.object(other, 'key or certificate'); + if (this.type === 'key') { + utils.assertCompatible(other, Key, [1, 0], 'key'); + } else { + utils.assertCompatible(other, Certificate, [1, 0], + 'certificate'); + } - var theirHash = key.hash(this.algorithm); + var theirHash = other.hash(this.algorithm); var theirHash2 = crypto.createHash(this.algorithm). update(theirHash).digest('base64'); @@ -59,10 +67,19 @@ Fingerprint.prototype.matches = function (key) { return (this.hash2 === theirHash2); }; -Fingerprint.parse = function (fp, enAlgs) { +Fingerprint.parse = function (fp, options) { assert.string(fp, 'fingerprint'); - var alg, hash; + var alg, hash, enAlgs; + if (Array.isArray(options)) { + enAlgs = options; + options = {}; + } + assert.optionalObject(options, 'options'); + if (options === undefined) + options = {}; + if (options.enAlgs !== undefined) + enAlgs = options.enAlgs; assert.optionalArrayOfString(enAlgs, 'algorithms'); var parts = fp.split(':'); @@ -105,7 +122,11 @@ Fingerprint.parse = function (fp, enAlgs) { throw (new InvalidAlgorithmError(alg)); } - return (new Fingerprint({algorithm: alg, hash: hash})); + return (new Fingerprint({ + algorithm: alg, + hash: hash, + type: options.type || 'key' + })); }; function addColons(s) { diff --git a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/auto.js b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/auto.js index 37c3cc8135..973c03245e 100644 --- a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/auto.js +++ b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/auto.js @@ -14,24 +14,24 @@ var pem = require('./pem'); var ssh = require('./ssh'); var rfc4253 = require('./rfc4253'); -function read(buf) { +function read(buf, options) { if (typeof (buf) === 'string') { if (buf.trim().match(/^[-]+[ ]*BEGIN/)) - return (pem.read(buf)); + return (pem.read(buf, options)); if (buf.match(/^\s*ssh-[a-z]/)) - return (ssh.read(buf)); + return (ssh.read(buf, options)); if (buf.match(/^\s*ecdsa-/)) - return (ssh.read(buf)); + return (ssh.read(buf, options)); buf = new Buffer(buf, 'binary'); } else { assert.buffer(buf); if (findPEMHeader(buf)) - return (pem.read(buf)); + return (pem.read(buf, options)); if (findSSHHeader(buf)) - return (ssh.read(buf)); + return (ssh.read(buf, options)); } if (buf.readUInt32BE(0) < buf.length) - return (rfc4253.read(buf)); + return (rfc4253.read(buf, options)); throw (new Error('Failed to auto-detect format of key')); } @@ -68,6 +68,6 @@ function findPEMHeader(buf) { return (true); } -function write(key) { +function write(key, options) { throw (new Error('"auto" format cannot be used for writing')); } diff --git a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/openssh-cert.js b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/openssh-cert.js new file mode 100644 index 0000000000..8ce7350fee --- /dev/null +++ b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/openssh-cert.js @@ -0,0 +1,289 @@ +// Copyright 2016 Joyent, Inc. + +module.exports = { + read: read, + verify: verify, + sign: sign, + write: write, + + /* Internal private API */ + fromBuffer: fromBuffer, + toBuffer: toBuffer +}; + +var assert = require('assert-plus'); +var SSHBuffer = require('../ssh-buffer'); +var crypto = require('crypto'); +var algs = require('../algs'); +var Key = require('../key'); +var PrivateKey = require('../private-key'); +var Identity = require('../identity'); +var rfc4253 = require('./rfc4253'); +var Signature = require('../signature'); +var utils = require('../utils'); +var Certificate = require('../certificate'); + +function verify(cert, key) { + /* + * We always give an issuerKey, so if our verify() is being called then + * there was no signature. Return false. + */ + return (false); +} + +var TYPES = { + 'user': 1, + 'host': 2 +}; +Object.keys(TYPES).forEach(function (k) { TYPES[TYPES[k]] = k; }); + +var ECDSA_ALGO = /^ecdsa-sha2-([^@-]+)-cert-v01@openssh.com$/; + +function read(buf, options) { + if (Buffer.isBuffer(buf)) + buf = buf.toString('ascii'); + var parts = buf.trim().split(/[ \t\n]+/g); + if (parts.length < 2 || parts.length > 3) + throw (new Error('Not a valid SSH certificate line')); + + var algo = parts[0]; + var data = parts[1]; + + data = new Buffer(data, 'base64'); + return (fromBuffer(data, algo)); +} + +function fromBuffer(data, algo, partial) { + var sshbuf = new SSHBuffer({ buffer: data }); + var innerAlgo = sshbuf.readString(); + if (algo !== undefined && innerAlgo !== algo) + throw (new Error('SSH certificate algorithm mismatch')); + if (algo === undefined) + algo = innerAlgo; + + var cert = {}; + cert.signatures = {}; + cert.signatures.openssh = {}; + + cert.signatures.openssh.nonce = sshbuf.readBuffer(); + + var key = {}; + var parts = (key.parts = []); + key.type = getAlg(algo); + + var partCount = algs.info[key.type].parts.length; + while (parts.length < partCount) + parts.push(sshbuf.readPart()); + assert.ok(parts.length >= 1, 'key must have at least one part'); + + var algInfo = algs.info[key.type]; + if (key.type === 'ecdsa') { + var res = ECDSA_ALGO.exec(algo); + assert.ok(res !== null); + assert.strictEqual(res[1], parts[0].data.toString()); + } + + for (var i = 0; i < algInfo.parts.length; ++i) { + parts[i].name = algInfo.parts[i]; + if (parts[i].name !== 'curve' && + algInfo.normalize !== false) { + var p = parts[i]; + p.data = utils.mpNormalize(p.data); + } + } + + cert.subjectKey = new Key(key); + + cert.serial = sshbuf.readInt64(); + + var type = TYPES[sshbuf.readInt()]; + assert.string(type, 'valid cert type'); + + cert.signatures.openssh.keyId = sshbuf.readString(); + + var principals = []; + var pbuf = sshbuf.readBuffer(); + var psshbuf = new SSHBuffer({ buffer: pbuf }); + while (!psshbuf.atEnd()) + principals.push(psshbuf.readString()); + if (principals.length === 0) + principals = ['*']; + + cert.subjects = principals.map(function (pr) { + if (type === 'user') + return (Identity.forUser(pr)); + else if (type === 'host') + return (Identity.forHost(pr)); + throw (new Error('Unknown identity type ' + type)); + }); + + cert.validFrom = int64ToDate(sshbuf.readInt64()); + cert.validUntil = int64ToDate(sshbuf.readInt64()); + + cert.signatures.openssh.critical = sshbuf.readBuffer(); + cert.signatures.openssh.exts = sshbuf.readBuffer(); + + /* reserved */ + sshbuf.readBuffer(); + + var signingKeyBuf = sshbuf.readBuffer(); + cert.issuerKey = rfc4253.read(signingKeyBuf); + + /* + * OpenSSH certs don't give the identity of the issuer, just their + * public key. So, we use an Identity that matches anything. The + * isSignedBy() function will later tell you if the key matches. + */ + cert.issuer = Identity.forHost('**'); + + var sigBuf = sshbuf.readBuffer(); + cert.signatures.openssh.signature = + Signature.parse(sigBuf, cert.issuerKey.type, 'ssh'); + + if (partial !== undefined) { + partial.remainder = sshbuf.remainder(); + partial.consumed = sshbuf._offset; + } + + return (new Certificate(cert)); +} + +function int64ToDate(buf) { + var i = buf.readUInt32BE(0) * 4294967296; + i += buf.readUInt32BE(4); + var d = new Date(); + d.setTime(i * 1000); + d.sourceInt64 = buf; + return (d); +} + +function dateToInt64(date) { + if (date.sourceInt64 !== undefined) + return (date.sourceInt64); + var i = Math.round(date.getTime() / 1000); + var upper = Math.floor(i / 4294967296); + var lower = Math.floor(i % 4294967296); + var buf = new Buffer(8); + buf.writeUInt32BE(upper, 0); + buf.writeUInt32BE(lower, 4); + return (buf); +} + +function sign(cert, key) { + if (cert.signatures.openssh === undefined) + cert.signatures.openssh = {}; + try { + var blob = toBuffer(cert, true); + } catch (e) { + delete (cert.signatures.openssh); + return (false); + } + var sig = cert.signatures.openssh; + var hashAlgo = undefined; + if (key.type === 'rsa' || key.type === 'dsa') + hashAlgo = 'sha1'; + var signer = key.createSign(hashAlgo); + signer.write(blob); + sig.signature = signer.sign(); + return (true); +} + +function write(cert, options) { + if (options === undefined) + options = {}; + + var blob = toBuffer(cert); + var out = getCertType(cert.subjectKey) + ' ' + blob.toString('base64'); + if (options.comment) + out = out + ' ' + options.comment; + return (out); +} + + +function toBuffer(cert, noSig) { + assert.object(cert.signatures.openssh, 'signature for openssh format'); + var sig = cert.signatures.openssh; + + if (sig.nonce === undefined) + sig.nonce = crypto.randomBytes(16); + var buf = new SSHBuffer({}); + buf.writeString(getCertType(cert.subjectKey)); + buf.writeBuffer(sig.nonce); + + var key = cert.subjectKey; + var algInfo = algs.info[key.type]; + algInfo.parts.forEach(function (part) { + buf.writePart(key.part[part]); + }); + + buf.writeInt64(cert.serial); + + var type = cert.subjects[0].type; + assert.notStrictEqual(type, 'unknown'); + cert.subjects.forEach(function (id) { + assert.strictEqual(id.type, type); + }); + type = TYPES[type]; + buf.writeInt(type); + + if (sig.keyId === undefined) { + sig.keyId = cert.subjects[0].type + '_' + + (cert.subjects[0].uid || cert.subjects[0].hostname); + } + buf.writeString(sig.keyId); + + var sub = new SSHBuffer({}); + cert.subjects.forEach(function (id) { + if (type === TYPES.host) + sub.writeString(id.hostname); + else if (type === TYPES.user) + sub.writeString(id.uid); + }); + buf.writeBuffer(sub.toBuffer()); + + buf.writeInt64(dateToInt64(cert.validFrom)); + buf.writeInt64(dateToInt64(cert.validUntil)); + + if (sig.critical === undefined) + sig.critical = new Buffer(0); + buf.writeBuffer(sig.critical); + + if (sig.exts === undefined) + sig.exts = new Buffer(0); + buf.writeBuffer(sig.exts); + + /* reserved */ + buf.writeBuffer(new Buffer(0)); + + sub = rfc4253.write(cert.issuerKey); + buf.writeBuffer(sub); + + if (!noSig) + buf.writeBuffer(sig.signature.toBuffer('ssh')); + + return (buf.toBuffer()); +} + +function getAlg(certType) { + if (certType === 'ssh-rsa-cert-v01@openssh.com') + return ('rsa'); + if (certType === 'ssh-dss-cert-v01@openssh.com') + return ('dsa'); + if (certType.match(ECDSA_ALGO)) + return ('ecdsa'); + if (certType === 'ssh-ed25519-cert-v01@openssh.com') + return ('ed25519'); + throw (new Error('Unsupported cert type ' + certType)); +} + +function getCertType(key) { + if (key.type === 'rsa') + return ('ssh-rsa-cert-v01@openssh.com'); + if (key.type === 'dsa') + return ('ssh-dss-cert-v01@openssh.com'); + if (key.type === 'ecdsa') + return ('ecdsa-sha2-' + key.curve + '-cert-v01@openssh.com'); + if (key.type === 'ed25519') + return ('ssh-ed25519-cert-v01@openssh.com'); + throw (new Error('Unsupported key type ' + key.type)); +} diff --git a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pem.js b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pem.js index 1d907607e2..5318b35165 100644 --- a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pem.js +++ b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pem.js @@ -7,6 +7,7 @@ module.exports = { var assert = require('assert-plus'); var asn1 = require('asn1'); +var crypto = require('crypto'); var algs = require('../algs'); var utils = require('../utils'); var Key = require('../key'); @@ -17,11 +18,13 @@ var pkcs8 = require('./pkcs8'); var sshpriv = require('./ssh-private'); var rfc4253 = require('./rfc4253'); +var errors = require('../errors'); + /* * For reading we support both PKCS#1 and PKCS#8. If we find a private key, * we just take the public component of it and use that. */ -function read(buf, forceType) { +function read(buf, options, forceType) { var input = buf; if (typeof (buf) !== 'string') { assert.buffer(buf, 'buf'); @@ -58,12 +61,26 @@ function read(buf, forceType) { break; headers[m[1].toLowerCase()] = m[2]; } + + var cipher, key, iv; if (headers['proc-type']) { var parts = headers['proc-type'].split(','); if (parts[0] === '4' && parts[1] === 'ENCRYPTED') { - throw (new Error('PEM key is encrypted ' + - '(password-protected). Please use the ' + - 'SSH agent or decrypt the key.')); + if (typeof (options.passphrase) === 'string') { + options.passphrase = new Buffer( + options.passphrase, 'utf-8'); + } + if (!Buffer.isBuffer(options.passphrase)) { + throw (new errors.KeyEncryptedError( + options.filename, 'PEM')); + } else { + parts = headers['dek-info'].split(','); + assert.ok(parts.length === 2); + cipher = parts[0].toLowerCase(); + iv = new Buffer(parts[1], 'hex'); + key = utils.opensslKeyDeriv(cipher, iv, + options.passphrase, 1).key; + } } } @@ -71,6 +88,23 @@ function read(buf, forceType) { lines = lines.slice(0, -1).join(''); buf = new Buffer(lines, 'base64'); + if (cipher && key && iv) { + var cipherStream = crypto.createDecipheriv(cipher, key, iv); + var chunk, chunks = []; + cipherStream.once('error', function (e) { + if (e.toString().indexOf('bad decrypt') !== -1) { + throw (new Error('Incorrect passphrase ' + + 'supplied, could not decrypt key')); + } + throw (e); + }); + cipherStream.write(buf); + cipherStream.end(); + while ((chunk = cipherStream.read()) !== null) + chunks.push(chunk); + buf = Buffer.concat(chunks); + } + /* The new OpenSSH internal format abuses PEM headers */ if (alg && alg.toLowerCase() === 'openssh') return (sshpriv.readSSHPrivate(type, buf)); @@ -98,7 +132,7 @@ function read(buf, forceType) { } } -function write(key, type) { +function write(key, options, type) { assert.object(key); var alg = {'ecdsa': 'EC', 'rsa': 'RSA', 'dsa': 'DSA'}[key.type]; diff --git a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pkcs1.js b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pkcs1.js index 6f9a24f115..a5676af6ef 100644 --- a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pkcs1.js +++ b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pkcs1.js @@ -19,12 +19,12 @@ var pem = require('./pem'); var pkcs8 = require('./pkcs8'); var readECDSACurve = pkcs8.readECDSACurve; -function read(buf) { - return (pem.read(buf, 'pkcs1')); +function read(buf, options) { + return (pem.read(buf, options, 'pkcs1')); } -function write(key) { - return (pem.write(key, 'pkcs1')); +function write(key, options) { + return (pem.write(key, options, 'pkcs1')); } /* Helper to read in a single mpint */ diff --git a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pkcs8.js b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pkcs8.js index c57c85537a..4ccbefcbec 100644 --- a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pkcs8.js +++ b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pkcs8.js @@ -18,12 +18,12 @@ var Key = require('../key'); var PrivateKey = require('../private-key'); var pem = require('./pem'); -function read(buf) { - return (pem.read(buf, 'pkcs8')); +function read(buf, options) { + return (pem.read(buf, options, 'pkcs8')); } -function write(key) { - return (pem.write(key, 'pkcs8')); +function write(key, options) { + return (pem.write(key, options, 'pkcs8')); } /* Helper to read in a single mpint */ @@ -42,10 +42,12 @@ function readPkcs8(alg, type, der) { } der.readSequence(); + var next = der.offset + der.length; var oid = der.readOID(); switch (oid) { case '1.2.840.113549.1.1.1': + der._offset = next; if (type === 'public') return (readPkcs8RSAPublic(der)); else @@ -66,10 +68,6 @@ function readPkcs8(alg, type, der) { } function readPkcs8RSAPublic(der) { - // Null -- XXX this probably isn't good practice - der.readByte(); - der.readByte(); - // bit string sequence der.readSequence(asn1.Ber.BitString); der.readByte(); @@ -93,9 +91,6 @@ function readPkcs8RSAPublic(der) { } function readPkcs8RSAPrivate(der) { - der.readByte(); - der.readByte(); - der.readSequence(asn1.Ber.OctetString); der.readSequence(); diff --git a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/rfc4253.js b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/rfc4253.js index 3e0e286cde..9d436dd921 100644 --- a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/rfc4253.js +++ b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/rfc4253.js @@ -52,7 +52,7 @@ function keyTypeToAlg(key) { throw (new Error('Unknown key type ' + key.type)); } -function read(partial, type, buf) { +function read(partial, type, buf, options) { if (typeof (buf) === 'string') buf = new Buffer(buf); assert.buffer(buf, 'buf'); @@ -120,7 +120,7 @@ function read(partial, type, buf) { return (new Constructor(key)); } -function write(key) { +function write(key, options) { assert.object(key); var alg = keyTypeToAlg(key); diff --git a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/ssh-private.js b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/ssh-private.js index 02d7c27e8f..bfbdab527f 100644 --- a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/ssh-private.js +++ b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/ssh-private.js @@ -18,8 +18,8 @@ var pem = require('./pem'); var rfc4253 = require('./rfc4253'); var SSHBuffer = require('../ssh-buffer'); -function read(buf) { - return (pem.read(buf)); +function read(buf, options) { + return (pem.read(buf, options)); } var MAGIC = 'openssh-key-v1'; @@ -76,7 +76,7 @@ function readSSHPrivate(type, buf) { return (key); } -function write(key) { +function write(key, options) { var pubKey; if (PrivateKey.isPrivateKey(key)) pubKey = key.toPublic(); diff --git a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/ssh.js b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/ssh.js index f7baa48cfe..655c9eaf3b 100644 --- a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/ssh.js +++ b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/ssh.js @@ -18,7 +18,7 @@ var SSHKEY_RE = /^([a-z0-9-]+)[ \t]+([a-zA-Z0-9+\/]+[=]*)([\n \t]+([^\n]+))?$/; /*JSSTYLED*/ var SSHKEY_RE2 = /^([a-z0-9-]+)[ \t]+([a-zA-Z0-9+\/ \t\n]+[=]*)(.*)$/; -function read(buf) { +function read(buf, options) { if (typeof (buf) !== 'string') { assert.buffer(buf, 'buf'); buf = buf.toString('ascii'); @@ -95,7 +95,7 @@ function read(buf) { return (key); } -function write(key) { +function write(key, options) { assert.object(key); if (!Key.isKey(key)) throw (new Error('Must be a public key')); diff --git a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/x509-pem.js b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/x509-pem.js new file mode 100644 index 0000000000..c59c7d5ff0 --- /dev/null +++ b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/x509-pem.js @@ -0,0 +1,77 @@ +// Copyright 2016 Joyent, Inc. + +var x509 = require('./x509'); + +module.exports = { + read: read, + verify: x509.verify, + sign: x509.sign, + write: write +}; + +var assert = require('assert-plus'); +var asn1 = require('asn1'); +var algs = require('../algs'); +var utils = require('../utils'); +var Key = require('../key'); +var PrivateKey = require('../private-key'); +var pem = require('./pem'); +var Identity = require('../identity'); +var Signature = require('../signature'); +var Certificate = require('../certificate'); + +function read(buf, options) { + if (typeof (buf) !== 'string') { + assert.buffer(buf, 'buf'); + buf = buf.toString('ascii'); + } + + var lines = buf.trim().split(/[\r\n]+/g); + + var m = lines[0].match(/*JSSTYLED*/ + /[-]+[ ]*BEGIN CERTIFICATE[ ]*[-]+/); + assert.ok(m, 'invalid PEM header'); + + var m2 = lines[lines.length - 1].match(/*JSSTYLED*/ + /[-]+[ ]*END CERTIFICATE[ ]*[-]+/); + assert.ok(m2, 'invalid PEM footer'); + + var headers = {}; + while (true) { + lines = lines.slice(1); + m = lines[0].match(/*JSSTYLED*/ + /^([A-Za-z0-9-]+): (.+)$/); + if (!m) + break; + headers[m[1].toLowerCase()] = m[2]; + } + + /* Chop off the first and last lines */ + lines = lines.slice(0, -1).join(''); + buf = new Buffer(lines, 'base64'); + + return (x509.read(buf, options)); +} + +function write(cert, options) { + var dbuf = x509.write(cert, options); + + var header = 'CERTIFICATE'; + var tmp = dbuf.toString('base64'); + var len = tmp.length + (tmp.length / 64) + + 18 + 16 + header.length*2 + 10; + var buf = new Buffer(len); + var o = 0; + o += buf.write('-----BEGIN ' + header + '-----\n', o); + for (var i = 0; i < tmp.length; ) { + var limit = i + 64; + if (limit > tmp.length) + limit = tmp.length; + o += buf.write(tmp.slice(i, limit), o); + buf[o++] = 10; + i = limit; + } + o += buf.write('-----END ' + header + '-----\n', o); + + return (buf.slice(0, o)); +} 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 new file mode 100644 index 0000000000..a2975404a5 --- /dev/null +++ b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/x509.js @@ -0,0 +1,484 @@ +// Copyright 2016 Joyent, Inc. + +module.exports = { + read: read, + verify: verify, + sign: sign, + write: write +}; + +var assert = require('assert-plus'); +var asn1 = require('asn1'); +var algs = require('../algs'); +var utils = require('../utils'); +var Key = require('../key'); +var PrivateKey = require('../private-key'); +var pem = require('./pem'); +var Identity = require('../identity'); +var Signature = require('../signature'); +var Certificate = require('../certificate'); +var pkcs8 = require('./pkcs8'); + +/* + * This file is based on RFC5280 (X.509). + */ + +/* Helper to read in a single mpint */ +function readMPInt(der, nm) { + assert.strictEqual(der.peek(), asn1.Ber.Integer, + nm + ' is not an Integer'); + return (utils.mpNormalize(der.readString(asn1.Ber.Integer, true))); +} + +function verify(cert, key) { + var sig = cert.signatures.x509; + assert.object(sig, 'x509 signature'); + + var algParts = sig.algo.split('-'); + if (algParts[0] !== key.type) + return (false); + + var blob = sig.cache; + if (blob === undefined) { + var der = new asn1.BerWriter(); + writeTBSCert(cert, der); + blob = der.buffer; + } + + var verifier = key.createVerify(algParts[1]); + verifier.write(blob); + return (verifier.verify(sig.signature)); +} + +function Local(i) { + return (asn1.Ber.Context | asn1.Ber.Constructor | i); +} + +function Context(i) { + return (asn1.Ber.Context | i); +} + +var SIGN_ALGS = { + 'rsa-md5': '1.2.840.113549.1.1.4', + 'rsa-sha1': '1.2.840.113549.1.1.5', + 'rsa-sha256': '1.2.840.113549.1.1.11', + 'rsa-sha384': '1.2.840.113549.1.1.12', + 'rsa-sha512': '1.2.840.113549.1.1.13', + 'dsa-sha1': '1.2.840.10040.4.3', + 'dsa-sha256': '2.16.840.1.101.3.4.3.2', + 'ecdsa-sha1': '1.2.840.10045.4.1', + 'ecdsa-sha256': '1.2.840.10045.4.3.2', + 'ecdsa-sha384': '1.2.840.10045.4.3.3', + 'ecdsa-sha512': '1.2.840.10045.4.3.4' +}; +Object.keys(SIGN_ALGS).forEach(function (k) { + SIGN_ALGS[SIGN_ALGS[k]] = k; +}); +SIGN_ALGS['1.3.14.3.2.3'] = 'rsa-md5'; +SIGN_ALGS['1.3.14.3.2.29'] = 'rsa-sha1'; + +var EXTS = { + 'issuerKeyId': '2.5.29.35', + 'altName': '2.5.29.17' +}; + +function read(buf, options) { + if (typeof (buf) === 'string') { + buf = new Buffer(buf, 'binary'); + } + assert.buffer(buf, 'buf'); + + var der = new asn1.BerReader(buf); + + der.readSequence(); + if (Math.abs(der.length - der.remain) > 1) { + throw (new Error('DER sequence does not contain whole byte ' + + 'stream')); + } + + var tbsStart = der.offset; + der.readSequence(); + var sigOffset = der.offset + der.length; + var tbsEnd = sigOffset; + + if (der.peek() === Local(0)) { + der.readSequence(Local(0)); + var version = der.readInt(); + assert.ok(version <= 3, + 'only x.509 versions up to v3 supported'); + } + + var cert = {}; + cert.signatures = {}; + var sig = (cert.signatures.x509 = {}); + sig.extras = {}; + + cert.serial = readMPInt(der, 'serial'); + + der.readSequence(); + var after = der.offset + der.length; + var certAlgOid = der.readOID(); + var certAlg = SIGN_ALGS[certAlgOid]; + if (certAlg === undefined) + throw (new Error('unknown signature algorithm ' + certAlgOid)); + + der._offset = after; + cert.issuer = Identity.parseAsn1(der); + + der.readSequence(); + cert.validFrom = readDate(der); + cert.validUntil = readDate(der); + + cert.subjects = [Identity.parseAsn1(der)]; + + der.readSequence(); + after = der.offset + der.length; + cert.subjectKey = pkcs8.readPkcs8(undefined, 'public', der); + der._offset = after; + + /* issuerUniqueID */ + if (der.peek() === Local(1)) { + der.readSequence(Local(1)); + sig.extras.issuerUniqueID = + buf.slice(der.offset, der.offset + der.length); + der._offset += der.length; + } + + /* subjectUniqueID */ + if (der.peek() === Local(2)) { + der.readSequence(Local(2)); + sig.extras.subjectUniqueID = + buf.slice(der.offset, der.offset + der.length); + der._offset += der.length; + } + + /* extensions */ + if (der.peek() === Local(3)) { + der.readSequence(Local(3)); + var extEnd = der.offset + der.length; + der.readSequence(); + + while (der.offset < extEnd) + readExtension(cert, buf, der); + + assert.strictEqual(der.offset, extEnd); + } + + assert.strictEqual(der.offset, sigOffset); + + der.readSequence(); + after = der.offset + der.length; + var sigAlgOid = der.readOID(); + var sigAlg = SIGN_ALGS[sigAlgOid]; + if (sigAlg === undefined) + throw (new Error('unknown signature algorithm ' + sigAlgOid)); + der._offset = after; + + var sigData = der.readString(asn1.Ber.BitString, true); + if (sigData[0] === 0) + sigData = sigData.slice(1); + var algParts = sigAlg.split('-'); + + sig.signature = Signature.parse(sigData, algParts[0], 'asn1'); + sig.signature.hashAlgorithm = algParts[1]; + sig.algo = sigAlg; + sig.cache = buf.slice(tbsStart, tbsEnd); + + return (new Certificate(cert)); +} + +function readDate(der) { + if (der.peek() === asn1.Ber.UTCTime) { + return (utcTimeToDate(der.readString(asn1.Ber.UTCTime))); + } else if (der.peek() === asn1.Ber.GeneralizedTime) { + return (gTimeToDate(der.readString(asn1.Ber.GeneralizedTime))); + } else { + throw (new Error('Unsupported date format')); + } +} + +/* RFC5280, section 4.2.1.6 (GeneralName type) */ +var ALTNAME = { + OtherName: Local(0), + RFC822Name: Context(1), + DNSName: Context(2), + X400Address: Local(3), + DirectoryName: Local(4), + EDIPartyName: Local(5), + URI: Context(6), + IPAddress: Context(7), + OID: Context(8) +}; + +function readExtension(cert, buf, der) { + der.readSequence(); + var after = der.offset + der.length; + var extId = der.readOID(); + var id; + var sig = cert.signatures.x509; + sig.extras.exts = []; + + var critical; + if (der.peek() === asn1.Ber.Boolean) + critical = der.readBoolean(); + + switch (extId) { + case (EXTS.altName): + der.readSequence(asn1.Ber.OctetString); + der.readSequence(); + var aeEnd = der.offset + der.length; + while (der.offset < aeEnd) { + switch (der.peek()) { + case ALTNAME.OtherName: + case ALTNAME.EDIPartyName: + der.readSequence(); + der._offset += der.length; + break; + case ALTNAME.OID: + der.readOID(ALTNAME.OID); + break; + case ALTNAME.RFC822Name: + /* RFC822 specifies email addresses */ + var email = der.readString(ALTNAME.RFC822Name); + id = Identity.forEmail(email); + if (!cert.subjects[0].equals(id)) + cert.subjects.push(id); + break; + case ALTNAME.DirectoryName: + der.readSequence(ALTNAME.DirectoryName); + id = Identity.parseAsn1(der); + if (!cert.subjects[0].equals(id)) + cert.subjects.push(id); + break; + case ALTNAME.DNSName: + var host = der.readString( + ALTNAME.DNSName); + id = Identity.forHost(host); + if (!cert.subjects[0].equals(id)) + cert.subjects.push(id); + break; + default: + der.readString(der.peek()); + break; + } + } + sig.extras.exts.push({ oid: extId, critical: critical }); + break; + default: + sig.extras.exts.push({ + oid: extId, + critical: critical, + data: der.readString(asn1.Ber.OctetString, true) + }); + break; + } + + der._offset = after; +} + +var UTCTIME_RE = + /^([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})?Z$/; +function utcTimeToDate(t) { + var m = t.match(UTCTIME_RE); + assert.ok(m, 'timestamps must be in UTC'); + var d = new Date(); + + var thisYear = d.getUTCFullYear(); + var century = Math.floor(thisYear / 100) * 100; + + var year = parseInt(m[1], 10); + if (thisYear % 100 < 50 && year >= 60) + year += (century - 1); + else + year += century; + d.setUTCFullYear(year, parseInt(m[2], 10) - 1, parseInt(m[3], 10)); + d.setUTCHours(parseInt(m[4], 10), parseInt(m[5], 10)); + if (m[6] && m[6].length > 0) + d.setUTCSeconds(parseInt(m[6], 10)); + return (d); +} + +var GTIME_RE = + /^([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})?Z$/; +function gTimeToDate(t) { + var m = t.match(GTIME_RE); + assert.ok(m); + var d = new Date(); + + d.setUTCFullYear(parseInt(m[1], 10), parseInt(m[2], 10) - 1, + parseInt(m[3], 10)); + d.setUTCHours(parseInt(m[4], 10), parseInt(m[5], 10)); + if (m[6] && m[6].length > 0) + d.setUTCSeconds(parseInt(m[6], 10)); + return (d); +} + +function zeroPad(n) { + var s = '' + n; + while (s.length < 2) + s = '0' + s; + return (s); +} + +function dateToUTCTime(d) { + var s = ''; + s += zeroPad(d.getUTCFullYear() % 100); + s += zeroPad(d.getUTCMonth() + 1); + s += zeroPad(d.getUTCDate()); + s += zeroPad(d.getUTCHours()); + s += zeroPad(d.getUTCMinutes()); + s += zeroPad(d.getUTCSeconds()); + s += 'Z'; + return (s); +} + +function sign(cert, key) { + if (cert.signatures.x509 === undefined) + cert.signatures.x509 = {}; + var sig = cert.signatures.x509; + + sig.algo = key.type + '-' + key.defaultHashAlgorithm(); + if (SIGN_ALGS[sig.algo] === undefined) + return (false); + + var der = new asn1.BerWriter(); + writeTBSCert(cert, der); + var blob = der.buffer; + sig.cache = blob; + + var signer = key.createSign(); + signer.write(blob); + cert.signatures.x509.signature = signer.sign(); + + return (true); +} + +function write(cert, options) { + var sig = cert.signatures.x509; + assert.object(sig, 'x509 signature'); + + var der = new asn1.BerWriter(); + der.startSequence(); + if (sig.cache) { + der._ensure(sig.cache.length); + sig.cache.copy(der._buf, der._offset); + der._offset += sig.cache.length; + } else { + writeTBSCert(cert, der); + } + + der.startSequence(); + der.writeOID(SIGN_ALGS[sig.algo]); + if (sig.algo.match(/^rsa-/)) + der.writeNull(); + der.endSequence(); + + var sigData = sig.signature.toBuffer('asn1'); + var data = new Buffer(sigData.length + 1); + data[0] = 0; + sigData.copy(data, 1); + der.writeBuffer(data, asn1.Ber.BitString); + der.endSequence(); + + return (der.buffer); +} + +function writeTBSCert(cert, der) { + var sig = cert.signatures.x509; + assert.object(sig, 'x509 signature'); + + der.startSequence(); + + der.startSequence(Local(0)); + der.writeInt(2); + der.endSequence(); + + der.writeBuffer(utils.mpNormalize(cert.serial), asn1.Ber.Integer); + + der.startSequence(); + der.writeOID(SIGN_ALGS[sig.algo]); + der.endSequence(); + + cert.issuer.toAsn1(der); + + der.startSequence(); + der.writeString(dateToUTCTime(cert.validFrom), asn1.Ber.UTCTime); + der.writeString(dateToUTCTime(cert.validUntil), asn1.Ber.UTCTime); + der.endSequence(); + + var subject = cert.subjects[0]; + var altNames = cert.subjects.slice(1); + subject.toAsn1(der); + + pkcs8.writePkcs8(der, cert.subjectKey); + + if (sig.extras && sig.extras.issuerUniqueID) { + der.writeBuffer(sig.extras.issuerUniqueID, Local(1)); + } + + if (sig.extras && sig.extras.subjectUniqueID) { + der.writeBuffer(sig.extras.subjectUniqueID, Local(2)); + } + + if (altNames.length > 0 || subject.type === 'host' || + (sig.extras && sig.extras.exts)) { + der.startSequence(Local(3)); + der.startSequence(); + + var exts = [ + { oid: EXTS.altName } + ]; + if (sig.extras && sig.extras.exts) + exts = sig.extras.exts; + + for (var i = 0; i < exts.length; ++i) { + der.startSequence(); + der.writeOID(exts[i].oid); + + if (exts[i].critical !== undefined) + der.writeBoolean(exts[i].critical); + + if (exts[i].oid === EXTS.altName) { + der.startSequence(asn1.Ber.OctetString); + der.startSequence(); + if (subject.type === 'host') { + der.writeString(subject.hostname, + Context(2)); + } + for (var j = 0; j < altNames.length; ++j) { + if (altNames[j].type === 'host') { + der.writeString( + altNames[j].hostname, + ALTNAME.DNSName); + } else if (altNames[j].type === + 'email') { + der.writeString( + altNames[j].email, + ALTNAME.RFC822Name); + } else { + /* + * Encode anything else as a + * DN style name for now. + */ + der.startSequence( + ALTNAME.DirectoryName); + altNames[j].toAsn1(der); + der.endSequence(); + } + } + der.endSequence(); + der.endSequence(); + } else { + der.writeBuffer(exts[i].data, + asn1.Ber.OctetString); + } + + der.endSequence(); + } + + der.endSequence(); + der.endSequence(); + } + + der.endSequence(); +} diff --git a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/identity.js b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/identity.js new file mode 100644 index 0000000000..b4f5cd73ec --- /dev/null +++ b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/identity.js @@ -0,0 +1,255 @@ +// Copyright 2016 Joyent, Inc. + +module.exports = Identity; + +var assert = require('assert-plus'); +var algs = require('./algs'); +var crypto = require('crypto'); +var Fingerprint = require('./fingerprint'); +var Signature = require('./signature'); +var errs = require('./errors'); +var util = require('util'); +var utils = require('./utils'); +var asn1 = require('asn1'); + +/*JSSTYLED*/ +var DNS_NAME_RE = /^([*]|[a-z0-9][a-z0-9\-]{0,62})(?:\.([*]|[a-z0-9][a-z0-9\-]{0,62}))*$/i; + +var oids = {}; +oids.cn = '2.5.4.3'; +oids.o = '2.5.4.10'; +oids.ou = '2.5.4.11'; +oids.l = '2.5.4.7'; +oids.s = '2.5.4.8'; +oids.c = '2.5.4.6'; +oids.sn = '2.5.4.4'; +oids.dc = '0.9.2342.19200300.100.1.25'; +oids.uid = '0.9.2342.19200300.100.1.1'; +oids.mail = '0.9.2342.19200300.100.1.3'; + +var unoids = {}; +Object.keys(oids).forEach(function (k) { + unoids[oids[k]] = k; +}); + +function Identity(opts) { + var self = this; + assert.object(opts, 'options'); + assert.arrayOfObject(opts.components, 'options.components'); + this.components = opts.components; + this.componentLookup = {}; + this.components.forEach(function (c) { + if (c.name && !c.oid) + c.oid = oids[c.name]; + if (c.oid && !c.name) + c.name = unoids[c.oid]; + if (self.componentLookup[c.name] === undefined) + self.componentLookup[c.name] = []; + self.componentLookup[c.name].push(c); + }); + if (this.componentLookup.cn && this.componentLookup.cn.length > 0) { + this.cn = this.componentLookup.cn[0].value; + } + assert.optionalString(opts.type, 'options.type'); + if (opts.type === undefined) { + if (this.components.length === 1 && + this.componentLookup.cn && + this.componentLookup.cn.length === 1 && + this.componentLookup.cn[0].value.match(DNS_NAME_RE)) { + this.type = 'host'; + this.hostname = this.componentLookup.cn[0].value; + + } else if (this.componentLookup.dc && + this.components.length === this.componentLookup.dc.length) { + this.type = 'host'; + this.hostname = this.componentLookup.dc.map( + function (c) { + return (c.value); + }).join('.'); + + } else if (this.componentLookup.uid && + this.components.length === + this.componentLookup.uid.length) { + this.type = 'user'; + this.uid = this.componentLookup.uid[0].value; + + } else if (this.componentLookup.cn && + this.componentLookup.cn.length === 1 && + this.componentLookup.cn[0].value.match(DNS_NAME_RE)) { + this.type = 'host'; + this.hostname = this.componentLookup.cn[0].value; + + } else if (this.componentLookup.uid && + this.componentLookup.uid.length === 1) { + this.type = 'user'; + this.uid = this.componentLookup.uid[0].value; + + } else if (this.componentLookup.mail && + this.componentLookup.mail.length === 1) { + this.type = 'email'; + this.email = this.componentLookup.mail[0].value; + + } else if (this.componentLookup.cn && + this.componentLookup.cn.length === 1) { + this.type = 'user'; + this.uid = this.componentLookup.cn[0].value; + + } else { + this.type = 'unknown'; + } + } else { + this.type = opts.type; + if (this.type === 'host') + this.hostname = opts.hostname; + else if (this.type === 'user') + this.uid = opts.uid; + else if (this.type === 'email') + this.email = opts.email; + else + throw (new Error('Unknown type ' + this.type)); + } +} + +Identity.prototype.toString = function () { + return (this.components.map(function (c) { + return (c.name.toUpperCase() + '=' + c.value); + }).join(', ')); +}; + +Identity.prototype.toAsn1 = function (der, tag) { + der.startSequence(tag); + this.components.forEach(function (c) { + der.startSequence(asn1.Ber.Constructor | asn1.Ber.Set); + der.startSequence(); + der.writeOID(c.oid); + der.writeString(c.value, asn1.Ber.PrintableString); + der.endSequence(); + der.endSequence(); + }); + der.endSequence(); +}; + +function globMatch(a, b) { + if (a === '**' || b === '**') + return (true); + var aParts = a.split('.'); + var bParts = b.split('.'); + if (aParts.length !== bParts.length) + return (false); + for (var i = 0; i < aParts.length; ++i) { + if (aParts[i] === '*' || bParts[i] === '*') + continue; + if (aParts[i] !== bParts[i]) + return (false); + } + return (true); +} + +Identity.prototype.equals = function (other) { + if (!Identity.isIdentity(other, [1, 0])) + return (false); + if (other.components.length !== this.components.length) + return (false); + for (var i = 0; i < this.components.length; ++i) { + if (this.components[i].oid !== other.components[i].oid) + return (false); + if (!globMatch(this.components[i].value, + other.components[i].value)) { + return (false); + } + } + return (true); +}; + +Identity.forHost = function (hostname) { + assert.string(hostname, 'hostname'); + return (new Identity({ + type: 'host', + hostname: hostname, + components: [ { name: 'cn', value: hostname } ] + })); +}; + +Identity.forUser = function (uid) { + assert.string(uid, 'uid'); + return (new Identity({ + type: 'user', + uid: uid, + components: [ { name: 'uid', value: uid } ] + })); +}; + +Identity.forEmail = function (email) { + assert.string(email, 'email'); + return (new Identity({ + type: 'email', + email: email, + components: [ { name: 'mail', value: email } ] + })); +}; + +Identity.parseDN = function (dn) { + assert.string(dn, 'dn'); + var parts = dn.split(','); + var cmps = parts.map(function (c) { + c = c.trim(); + var eqPos = c.indexOf('='); + var name = c.slice(0, eqPos).toLowerCase(); + var value = c.slice(eqPos + 1); + return ({ name: name, value: value }); + }); + return (new Identity({ components: cmps })); +}; + +Identity.parseAsn1 = function (der, top) { + var components = []; + der.readSequence(top); + var end = der.offset + der.length; + while (der.offset < end) { + der.readSequence(asn1.Ber.Constructor | asn1.Ber.Set); + var after = der.offset + der.length; + der.readSequence(); + var oid = der.readOID(); + var type = der.peek(); + var value; + switch (type) { + case asn1.Ber.PrintableString: + case asn1.Ber.IA5String: + case asn1.Ber.OctetString: + case asn1.Ber.T61String: + value = der.readString(type); + break; + case asn1.Ber.Utf8String: + value = der.readString(type, true); + value = value.toString('utf8'); + break; + case asn1.Ber.CharacterString: + case asn1.Ber.BMPString: + value = der.readString(type, true); + value = value.toString('utf16le'); + break; + default: + throw (new Error('Unknown asn1 type ' + type)); + } + components.push({ oid: oid, value: value }); + der._offset = after; + } + der._offset = end; + return (new Identity({ + components: components + })); +}; + +Identity.isIdentity = function (obj, ver) { + return (utils.isCompatible(obj, Identity, ver)); +}; + +/* + * API versions for Identity: + * [1,0] -- initial ver + */ +Identity.prototype._sshpkApiVersion = [1, 0]; + +Identity._oldVersionDetect = function (obj) { + return ([1, 0]); +}; diff --git a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/index.js b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/index.js index 5cd962b312..96a1384286 100644 --- a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/index.js +++ b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/index.js @@ -4,6 +4,8 @@ var Key = require('./key'); var Fingerprint = require('./fingerprint'); var Signature = require('./signature'); var PrivateKey = require('./private-key'); +var Certificate = require('./certificate'); +var Identity = require('./identity'); var errs = require('./errors'); module.exports = { @@ -16,10 +18,21 @@ module.exports = { parseSignature: Signature.parse, PrivateKey: PrivateKey, parsePrivateKey: PrivateKey.parse, + Certificate: Certificate, + parseCertificate: Certificate.parse, + createSelfSignedCertificate: Certificate.createSelfSigned, + createCertificate: Certificate.create, + Identity: Identity, + identityFromDN: Identity.parseDN, + identityForHost: Identity.forHost, + identityForUser: Identity.forUser, + identityForEmail: Identity.forEmail, /* errors */ FingerprintFormatError: errs.FingerprintFormatError, InvalidAlgorithmError: errs.InvalidAlgorithmError, KeyParseError: errs.KeyParseError, - SignatureParseError: errs.SignatureParseError + SignatureParseError: errs.SignatureParseError, + KeyEncryptedError: errs.KeyEncryptedError, + CertificateParseError: errs.CertificateParseError }; diff --git a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/key.js b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/key.js index d140f0e54d..ff5c363733 100644 --- a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/key.js +++ b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/key.js @@ -77,11 +77,12 @@ function Key(opts) { Key.formats = formats; -Key.prototype.toBuffer = function (format) { +Key.prototype.toBuffer = function (format, options) { if (format === undefined) format = 'ssh'; assert.string(format, 'format'); assert.object(formats[format], 'formats[format]'); + assert.optionalObject(options, 'options'); if (format === 'rfc4253') { if (this._rfc4253Cache === undefined) @@ -89,11 +90,11 @@ Key.prototype.toBuffer = function (format) { return (this._rfc4253Cache); } - return (formats[format].write(this)); + return (formats[format].write(this, options)); }; -Key.prototype.toString = function (format) { - return (this.toBuffer(format).toString()); +Key.prototype.toString = function (format, options) { + return (this.toBuffer(format, options).toString()); }; Key.prototype.hash = function (algo) { @@ -107,9 +108,6 @@ Key.prototype.hash = function (algo) { var hash = crypto.createHash(algo). update(this.toBuffer('rfc4253')).digest(); - /* Workaround for node 0.8 */ - if (typeof (hash) === 'string') - hash = new Buffer(hash, 'binary'); this._hashCache[algo] = hash; return (hash); }; @@ -119,6 +117,7 @@ Key.prototype.fingerprint = function (algo) { algo = 'sha256'; assert.string(algo, 'algorithm'); var opts = { + type: 'key', hash: this.hash(algo), algorithm: algo }; @@ -158,10 +157,7 @@ Key.prototype.createVerify = function (hashAlgo) { var v, nm, err; try { - nm = this.type.toUpperCase() + '-'; - if (this.type === 'ecdsa') - nm = 'ecdsa-with-'; - nm += hashAlgo.toUpperCase(); + nm = hashAlgo.toUpperCase(); v = crypto.createVerify(nm); } catch (e) { err = e; @@ -213,26 +209,34 @@ Key.prototype.createDiffieHellman = function () { }; Key.prototype.createDH = Key.prototype.createDiffieHellman; -Key.parse = function (data, format, name) { +Key.parse = function (data, format, options) { if (typeof (data) !== 'string') assert.buffer(data, 'data'); if (format === undefined) format = 'auto'; assert.string(format, 'format'); - if (name === undefined) - name = '(unnamed)'; + if (typeof (options) === 'string') + options = { filename: options }; + assert.optionalObject(options, 'options'); + if (options === undefined) + options = {}; + assert.optionalString(options.filename, 'options.filename'); + if (options.filename === undefined) + options.filename = '(unnamed)'; assert.object(formats[format], 'formats[format]'); try { - var k = formats[format].read(data); + var k = formats[format].read(data, options); if (k instanceof PrivateKey) k = k.toPublic(); if (!k.comment) - k.comment = name; + k.comment = options.filename; return (k); } catch (e) { - throw (new KeyParseError(name, format, e)); + if (e.name === 'KeyEncryptedError') + throw (e); + throw (new KeyParseError(options.filename, format, e)); } }; diff --git a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/private-key.js b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/private-key.js index 993ae32650..f80d939662 100644 --- a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/private-key.js +++ b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/private-key.js @@ -23,6 +23,7 @@ var Key = require('./key'); var InvalidAlgorithmError = errs.InvalidAlgorithmError; var KeyParseError = errs.KeyParseError; +var KeyEncryptedError = errs.KeyEncryptedError; var formats = {}; formats['auto'] = require('./formats/auto'); @@ -44,13 +45,14 @@ util.inherits(PrivateKey, Key); PrivateKey.formats = formats; -PrivateKey.prototype.toBuffer = function (format) { +PrivateKey.prototype.toBuffer = function (format, options) { if (format === undefined) format = 'pkcs1'; assert.string(format, 'format'); assert.object(formats[format], 'formats[format]'); + assert.optionalObject(options, 'options'); - return (formats[format].write(this)); + return (formats[format].write(this, options)); }; PrivateKey.prototype.hash = function (algo) { @@ -146,10 +148,7 @@ PrivateKey.prototype.createSign = function (hashAlgo) { var v, nm, err; try { - nm = this.type.toUpperCase() + '-'; - if (this.type === 'ecdsa') - nm = 'ecdsa-with-'; - nm += hashAlgo.toUpperCase(); + nm = hashAlgo.toUpperCase(); v = crypto.createSign(nm); } catch (e) { err = e; @@ -175,25 +174,33 @@ PrivateKey.prototype.createSign = function (hashAlgo) { return (v); }; -PrivateKey.parse = function (data, format, name) { +PrivateKey.parse = function (data, format, options) { if (typeof (data) !== 'string') assert.buffer(data, 'data'); if (format === undefined) format = 'auto'; assert.string(format, 'format'); - if (name === undefined) - name = '(unnamed)'; + if (typeof (options) === 'string') + options = { filename: options }; + assert.optionalObject(options, 'options'); + if (options === undefined) + options = {}; + assert.optionalString(options.filename, 'options.filename'); + if (options.filename === undefined) + options.filename = '(unnamed)'; assert.object(formats[format], 'formats[format]'); try { - var k = formats[format].read(data); + var k = formats[format].read(data, options); assert.ok(k instanceof PrivateKey, 'key is not a private key'); if (!k.comment) - k.comment = name; + k.comment = options.filename; return (k); } catch (e) { - throw (new KeyParseError(name, format, e)); + if (e.name === 'KeyEncryptedError') + throw (e); + throw (new KeyParseError(options.filename, format, e)); } }; diff --git a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/signature.js b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/signature.js index ddf4a8c988..964f55cb56 100644 --- a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/signature.js +++ b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/signature.js @@ -65,23 +65,31 @@ Signature.prototype.toBuffer = function (format) { buf = new SSHBuffer({}); buf.writeString('ssh-dss'); r = this.part.r.data; - if (r[0] === 0x00) + if (r.length > 20 && r[0] === 0x00) r = r.slice(1); s = this.part.s.data; + if (s.length > 20 && s[0] === 0x00) + s = s.slice(1); + if ((this.hashAlgorithm && + this.hashAlgorithm !== 'sha1') || + r.length + s.length !== 40) { + throw (new Error('OpenSSH only supports ' + + 'DSA signatures with SHA1 hash')); + } buf.writeBuffer(Buffer.concat([r, s])); return (buf.toBuffer()); } else if (format === 'ssh' && this.type === 'ecdsa') { var inner = new SSHBuffer({}); - r = this.part.r; - if (r[0] === 0x00) - r = r.slice(1); - inner.writePart(r); + r = this.part.r.data; + inner.writeBuffer(r); inner.writePart(this.part.s); buf = new SSHBuffer({}); /* XXX: find a more proper way to do this? */ var curve; - var sz = this.part.r.data.length * 8; + if (r[0] === 0x00) + r = r.slice(1); + var sz = r.length * 8; if (sz === 256) curve = 'nistp256'; else if (sz === 384) diff --git a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/ssh-buffer.js b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/ssh-buffer.js index 0b00277349..8fc2cb8785 100644 --- a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/ssh-buffer.js +++ b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/ssh-buffer.js @@ -73,6 +73,14 @@ SSHBuffer.prototype.readInt = function () { return (v); }; +SSHBuffer.prototype.readInt64 = function () { + assert.ok(this._offset + 8 < this._buffer.length, + 'buffer not long enough to read Int64'); + var v = this._buffer.slice(this._offset, this._offset + 8); + this._offset += 8; + return (v); +}; + SSHBuffer.prototype.readChar = function () { var v = this._buffer[this._offset++]; return (v); @@ -106,6 +114,22 @@ SSHBuffer.prototype.writeInt = function (v) { this._offset += 4; }; +SSHBuffer.prototype.writeInt64 = function (v) { + assert.buffer(v, 'value'); + if (v.length > 8) { + var lead = v.slice(0, v.length - 8); + for (var i = 0; i < lead.length; ++i) { + assert.strictEqual(lead[i], 0, + 'must fit in 64 bits of precision'); + } + v = v.slice(v.length - 8, v.length); + } + while (this._offset + 8 > this._size) + this.expand(); + v.copy(this._buffer, this._offset); + this._offset += 8; +}; + SSHBuffer.prototype.writeChar = function (v) { while (this._offset + 1 > this._size) this.expand(); diff --git a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/utils.js b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/utils.js index 34c22d31a6..d57245cc16 100644 --- a/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/utils.js +++ b/deps/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/utils.js @@ -8,11 +8,13 @@ module.exports = { ecNormalize: ecNormalize, countZeros: countZeros, assertCompatible: assertCompatible, - isCompatible: isCompatible + isCompatible: isCompatible, + opensslKeyDeriv: opensslKeyDeriv }; var assert = require('assert-plus'); var PrivateKey = require('./private-key'); +var crypto = require('crypto'); var MAX_CLASS_DEPTH = 3; @@ -68,6 +70,43 @@ function assertCompatible(obj, klass, needVer, name) { 'version ' + needVer[0] + '.' + needVer[1]); } +var CIPHER_LEN = { + 'des-ede3-cbc': { key: 7, iv: 8 }, + 'aes-128-cbc': { key: 16, iv: 16 } +}; +var PKCS5_SALT_LEN = 8; + +function opensslKeyDeriv(cipher, salt, passphrase, count) { + assert.buffer(salt, 'salt'); + assert.buffer(passphrase, 'passphrase'); + assert.number(count, 'iteration count'); + + var clen = CIPHER_LEN[cipher]; + assert.object(clen, 'supported cipher'); + + salt = salt.slice(0, PKCS5_SALT_LEN); + + var D, D_prev, bufs; + var material = new Buffer(0); + while (material.length < clen.key + clen.iv) { + bufs = []; + if (D_prev) + bufs.push(D_prev); + bufs.push(passphrase); + bufs.push(salt); + D = Buffer.concat(bufs); + for (var j = 0; j < count; ++j) + D = crypto.createHash('md5').update(D).digest(); + material = Buffer.concat([material, D]); + D_prev = D; + } + + return ({ + key: material.slice(0, clen.key), + iv: material.slice(clen.key, clen.key + clen.iv) + }); +} + /* Count leading zero bits on a buffer */ function countZeros(buf) { var o = 0, obit = 8; |