function privateKeySigner(options) { mod_assert.object(options, 'options'); if (typeof (options.key) !== 'string' && !Buffer.isBuffer(options.key)) { throw (new Error('options.key (a String or Buffer) is ' + 'required')); } mod_assert.string(options.user, 'options.user'); mod_assert.optionalString(options.subuser, 'options.subuser'); mod_assert.optionalString(options.keyId, 'options.keyId'); var key = mod_sshpk.parsePrivateKey(options.key); var kp = KeyPair.fromPrivateKey(key); return (kp.createSign(options)); }
function RequestSigner(options) { assert.object(options, 'options'); var alg = []; if (options.algorithm !== undefined) { assert.string(options.algorithm, 'options.algorithm'); alg = validateAlgorithm(options.algorithm); } this.rs_alg = alg; if (options.sign !== undefined) { assert.func(options.sign, 'options.sign'); this.rs_signFunc = options.sign; } else if (alg[0] === 'hmac' && options.key !== undefined) { assert.string(options.keyId, 'options.keyId'); this.rs_keyId = options.keyId; if (typeof(options.key) !== 'string' && !Buffer.isBuffer(options.key)) throw (new TypeError('options.key for HMAC must be a string or Buffer')); this.rs_signer = crypto.createHmac(alg[1].toUpperCase(), options.key); this.rs_signer.sign = function() { var digest = this.digest('base64'); return ({ hashAlgorithm: alg[1], toString: function() { return (digest); } }); }; } else if (options.key !== undefined) { var key = options.key; if (typeof(key) === 'string' || Buffer.isBuffer(key)) key = sshpk.parsePrivateKey(key); assert.ok(sshpk.PrivateKey.isPrivateKey(key, [1, 2]), 'options.key must be a sshpk.PrivateKey'); this.rs_key = key; assert.string(options.keyId, 'options.keyId'); this.rs_keyId = options.keyId; if (!PK_ALGOS[key.type]) { throw (new InvalidAlgorithmError(key.type.toUpperCase() + ' type ' + 'keys are not supported')); } if (alg[0] !== undefined && key.type !== alg[0]) { throw (new InvalidAlgorithmError('options.key must be a ' + alg[0].toUpperCase() + ' key, was given a ' + key.type.toUpperCase() + ' key instead')); } this.rs_signer = key.createSign(alg[1]); } else { throw (new TypeError('options.sign (func) or options.key is required')); } this.rs_headers = []; this.rs_lines = []; }
test('defaults', function(t) { var req = http.request(httpOptions, function(res) { t.end(); }); req._stringToSign = null; t.ok(httpSignature.sign(req, signOptions)); var authz = req.getHeader('Authorization'); t.ok(authz); t.strictEqual(typeof (req._stringToSign), 'string'); t.ok(req._stringToSign.match(/^date: [^\n]*$/)); var key = sshpk.parsePrivateKey(rsaPrivate); var sig = key.createSign().update(req._stringToSign).sign(); t.ok(authz.indexOf(sig.toString()) !== -1); console.log('> ' + authz); req.end(); });
test('with custom authorizationHeaderName', function(t) { var req = http.request(httpOptions, function(res) { t.end(); }); req._stringToSign = null; var opts = Object.create(signOptions); opts.authorizationHeaderName = 'x-auths'; t.ok(httpSignature.sign(req, opts)); var authz = req.getHeader('x-auths'); t.ok(authz); t.strictEqual(typeof (req._stringToSign), 'string'); t.ok(req._stringToSign.match(/^date: [^\n]*$/)); var key = sshpk.parsePrivateKey(rsaPrivate); var sig = key.createSign().update(req._stringToSign).sign(); t.ok(authz.indexOf(sig.toString()) !== -1); console.log('> ' + authz); req.end(); });
test('valid ecdsa', function(t) { server.tester = function(req, res) { var parsed = httpSignature.parseRequest(req); t.ok(httpSignature.verify(parsed, ecdsaPublic)); res.writeHead(200); res.write(JSON.stringify(parsed, null, 2)); res.end(); }; options.headers.Date = jsprim.rfc1123(new Date()); var key = sshpk.parsePrivateKey(ecdsaPrivate); var signer = key.createSign('sha512'); signer.update('date: ' + options.headers.Date); options.headers.Authorization = 'Signature keyId="foo",algorithm="ecdsa-sha512",signature="' + signer.sign().toString() + '"'; http.get(options, function(res) { t.equal(res.statusCode, 200); t.end(); }); });
/* See createSigner() */ function RequestSigner(options) { assert.object(options, 'options'); var alg = []; if (options.algorithm !== undefined) { assert.string(options.algorithm, 'options.algorithm'); alg = validateAlgorithm(options.algorithm); } this.rs_alg = alg; /* * RequestSigners come in two varieties: ones with an rs_signFunc, and ones * with an rs_signer. * * rs_signFunc-based RequestSigners have to build up their entire signing * string within the rs_lines array and give it to rs_signFunc as a single * concat'd blob. rs_signer-based RequestSigners can add a line at a time to * their signing state by using rs_signer.update(), thus only needing to * buffer the hash function state and one line at a time. */ if (options.sign !== undefined) { assert.func(options.sign, 'options.sign'); this.rs_signFunc = options.sign; } else if (alg[0] === 'hmac' && options.key !== undefined) { assert.string(options.keyId, 'options.keyId'); this.rs_keyId = options.keyId; if (typeof (options.key) !== 'string' && !Buffer.isBuffer(options.key)) throw (new TypeError('options.key for HMAC must be a string or Buffer')); /* * Make an rs_signer for HMACs, not a rs_signFunc -- HMACs digest their * data in chunks rather than requiring it all to be given in one go * at the end, so they are more similar to signers than signFuncs. */ this.rs_signer = crypto.createHmac(alg[1].toUpperCase(), options.key); this.rs_signer.sign = function () { var digest = this.digest('base64'); return ({ hashAlgorithm: alg[1], toString: function () { return (digest); } }); }; } else if (options.key !== undefined) { var key = options.key; if (typeof (key) === 'string' || Buffer.isBuffer(key)) key = sshpk.parsePrivateKey(key); assert.ok(sshpk.PrivateKey.isPrivateKey(key, [1, 2]), 'options.key must be a sshpk.PrivateKey'); this.rs_key = key; assert.string(options.keyId, 'options.keyId'); this.rs_keyId = options.keyId; if (!PK_ALGOS[key.type]) { throw (new InvalidAlgorithmError(key.type.toUpperCase() + ' type ' + 'keys are not supported')); } if (alg[0] !== undefined && key.type !== alg[0]) { throw (new InvalidAlgorithmError('options.key must be a ' + alg[0].toUpperCase() + ' key, was given a ' + key.type.toUpperCase() + ' key instead')); } this.rs_signer = key.createSign(alg[1]); } else { throw (new TypeError('options.sign (func) or options.key is required')); } this.rs_headers = []; this.rs_lines = []; }
signRequest: function signRequest(request, options) { assert.object(request, 'request'); assert.object(options, 'options'); assert.optionalString(options.algorithm, 'options.algorithm'); assert.string(options.keyId, 'options.keyId'); assert.optionalArrayOfString(options.headers, 'options.headers'); assert.optionalString(options.httpVersion, 'options.httpVersion'); if (!request.getHeader('Date')) request.setHeader('Date', jsprim.rfc1123(new Date())); if (!options.headers) options.headers = ['date']; if (!options.httpVersion) options.httpVersion = '1.1'; var alg = []; if (options.algorithm) { options.algorithm = options.algorithm.toLowerCase(); alg = validateAlgorithm(options.algorithm); } var i; var stringToSign = ''; for (i = 0; i < options.headers.length; i++) { if (typeof (options.headers[i]) !== 'string') throw new TypeError('options.headers must be an array of Strings'); var h = options.headers[i].toLowerCase(); if (h === 'request-line') { if (!options.strict) { /** * We allow headers from the older spec drafts if strict parsing isn't * specified in options. */ stringToSign += request.method + ' ' + request.path + ' HTTP/' + options.httpVersion; } else { /* Strict parsing doesn't allow older draft headers. */ throw (new StrictParsingError('request-line is not a valid header ' + 'with strict parsing enabled.')); } } else if (h === '(request-target)') { stringToSign += '(request-target): ' + request.method.toLowerCase() + ' ' + request.path; } else { var value = request.getHeader(h); if (value === undefined || value === '') { throw new MissingHeaderError(h + ' was not in the request'); } stringToSign += h + ': ' + value; } if ((i + 1) < options.headers.length) stringToSign += '\n'; } /* This is just for unit tests. */ if (request.hasOwnProperty('_stringToSign')) { request._stringToSign = stringToSign; } var signature; if (alg[0] === 'hmac') { if (typeof (options.key) !== 'string' && !Buffer.isBuffer(options.key)) throw (new TypeError('options.key must be a string or Buffer')); var hmac = crypto.createHmac(alg[1].toUpperCase(), options.key); hmac.update(stringToSign); signature = hmac.digest('base64'); } else { var key = options.key; if (typeof (key) === 'string' || Buffer.isBuffer(key)) key = sshpk.parsePrivateKey(options.key); assert.ok(sshpk.PrivateKey.isPrivateKey(key, [1, 2]), 'options.key must be a sshpk.PrivateKey'); if (!PK_ALGOS[key.type]) { throw (new InvalidAlgorithmError(key.type.toUpperCase() + ' type ' + 'keys are not supported')); } if (alg[0] !== undefined && key.type !== alg[0]) { throw (new InvalidAlgorithmError('options.key must be a ' + alg[0].toUpperCase() + ' key, was given a ' + key.type.toUpperCase() + ' key instead')); } var signer = key.createSign(alg[1]); signer.update(stringToSign); var sigObj = signer.sign(); if (!HASH_ALGOS[sigObj.hashAlgorithm]) { throw (new InvalidAlgorithmError(sigObj.hashAlgorithm.toUpperCase() + ' is not a supported hash algorithm')); } options.algorithm = key.type + '-' + sigObj.hashAlgorithm; signature = sigObj.toString(); assert.notStrictEqual(signature, '', 'empty signature produced'); } request.setHeader('Authorization', sprintf(AUTHZ_FMT, options.keyId, options.algorithm, options.headers.join(' '), signature)); return true; }
test('find keys and examples', function (t) { var i = 0; for (; i < doc.length; ++i) if (/^# Appendix A/.test(doc[i])) break; if (i >= doc.length) t.fail('could not find appendix A') var pubKeyLines = []; for (; i < doc.length; ++i) if (/-BEGIN PUBLIC KEY-/.test(doc[i])) break; for (; i < doc.length; ++i) { pubKeyLines.push(doc[i]); if (/-END PUBLIC KEY-/.test(doc[i])) break; } pubKey = sshpk.parseKey(pubKeyLines. map(function (l) { return (l.replace(/^ /g, '')); }). join('\n')); var privKeyLines = []; for (; i < doc.length; ++i) if (/-BEGIN RSA PRIVATE KEY-/.test(doc[i])) break; for (; i < doc.length; ++i) { privKeyLines.push(doc[i]); if (/-END RSA PRIVATE KEY-/.test(doc[i])) break; } privKey = sshpk.parsePrivateKey(privKeyLines. map(function (l) { return (l.replace(/^ /g, '')); }). join('\n')); var reqLines = []; for (; i < doc.length; ++i) if (doc[i] === '<!-- httpreq -->') break; for (++i; i < doc.length; ++i) { if (doc[i] === '<!-- /httpreq -->') break; reqLines.push(doc[i]); } httpReqData = reqLines. map(function (l) { return (l.replace(/^ /g, '')); }). join('\r\n'); var thisConfig; var lines; do { thisConfig = {}; for (; i < doc.length; ++i) { var m = doc[i].match(/^<!-- sign (.+) -->$/); if (m && m[1]) { thisConfig = JSON.parse(m[1]); break; } } for (; i < doc.length; ++i) if (doc[i] === '<!-- signstring -->') break; lines = []; for (++i; i < doc.length; ++i) { if (doc[i] === '<!-- /signstring -->') break; if (doc[i].length > 0) lines.push(doc[i]); } thisConfig.signString = lines. map(function (l) { return (l.replace(/^ /g, '')); }). join('\n'); for (; i < doc.length; ++i) if (doc[i] === '<!-- authz -->') break; lines = []; for (++i; i < doc.length; ++i) { if (doc[i] === '<!-- /authz -->') break; if (doc[i].length > 0) lines.push(doc[i]); } thisConfig.authz = lines. map(function (l) { return (l.replace(/^ /g, '')); }). join('\n'); if (thisConfig.name) signs.push(thisConfig); } while (i < doc.length); t.end(); });
mod_assert.object(config, 'config'); mod_assert.optionalNumber(config.port, 'config.port'); if (config.port === undefined) config.port = 8080; var log = mod_bunyan.createLogger({ name: 'makecheckbot' }); var repoHasMakeCheck = {}; repoHasMakeCheck['joyent/illumos-extra'] = false; repoHasMakeCheck['joyent/illumos-joyent'] = false; repoHasMakeCheck['joyent/zfs_snapshot_tar'] = false; repoHasMakeCheck['joyent/illumos-kvm'] = false; repoHasMakeCheck['joyent/postgres'] = false; var dockerKeyPem = mod_fs.readFileSync(config.docker.keyFile); var dockerKey = mod_sshpk.parsePrivateKey(dockerKeyPem); var id = mod_sshpk.identityFromDN('CN=' + config.docker.user); var cert = mod_sshpk.createSelfSignedCertificate(id, dockerKey); config.gerrit.log = log; config.gerrit.recovery = { default: { timeout: 30000, maxTimeout: 120000, delay: 5000, maxDelay: 15000, retries: Infinity } }; var gerrit = new mod_gbot.Client(config.gerrit);
var tmpDir; var ID_RSA_FP = 'SHA256:29GY+6bxcBkcNNUzTnEcTdTv1W3d3PN/OxyplcYSoX4'; var ID_RSA_MD5 = 'fa:56:a1:6b:cc:04:97:fe:e2:98:54:c4:2e:0d:26:c6'; var ID_RSA2_FP = 'SHA256:FWEns/VvPZdbSPtoVDUlUpewdP/LgC/4+l/V42Oltpw'; var ID_DSA_FP = 'SHA256:WI2QyT/UuJ4LaPylGynx244f6k+xqVHYOyxg1cfnL0I'; var ID_DSA_MD5 = 'a6:e6:68:d3:28:2b:0a:a0:12:54:da:c4:c0:22:8d:ba'; var SIG_RSA_SHA256 = 'KX1okEE5wWjgrDYM35z9sO49WRk/DeZy7QeSNCFdOsn45BO6rVOIH5v' + 'V7WD25/VWyGCiN86Pml/Eulhx3Xx4ZUEHHc18K0BAKU5CSu/jCRI0dEFt4q1bXCyM7aK' + 'FlAXpk7CJIM0Gx91CJEXcZFuUddngoqljyt9hu4dpMhrjVFA='; var SIG_RSA_SHA1 = 'parChQDdkj8wFY75IUW/W7KN9q5FFTPYfcAf+W7PmN8yxnRJB884NHYNT' + 'hl/TjZB2s0vt+kkfX3nldi54heTKbDKFwCOoDmVWQ2oE2ZrJPPFiUHReUAIRvwD0V/q7' + '4c/DiRR6My7FEa8Szce27DBrjBmrMvMcmd7/jDbhaGusy4='; var ID_RSA = sshpk.parsePrivateKey( fs.readFileSync(path.join(testDir, 'id_rsa'))); function copyAsset(name, dst, cb) { var rd = fs.createReadStream(path.join(testDir, name)); var wr = fs.createWriteStream(path.join(tmpDir, dst)); wr.on('close', cb); rd.pipe(wr); } test('setup fs only', function (t) { temp.mkdir('smartdc-auth.signers.test', function (err, tmp) { t.error(err); tmpDir = tmp; fs.mkdirSync(path.join(tmpDir, '.ssh')); vasync.parallel({