Skip to content

Commit

Permalink
Add support for adding the keyId and algorithm params into the signin…
Browse files Browse the repository at this point in the history
…g string (#100)
  • Loading branch information
dgwynne committed Mar 4, 2020
1 parent 28def30 commit 202ebc3
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 25 deletions.
6 changes: 5 additions & 1 deletion lib/parser.js
Expand Up @@ -235,7 +235,6 @@ module.exports = {
throw new InvalidHeaderError('signature was not specified');

// Check the algorithm against the official list
parsed.params.algorithm = parsed.params.algorithm.toLowerCase();
try {
validateAlgorithm(parsed.params.algorithm);
} catch (e) {
Expand Down Expand Up @@ -268,6 +267,10 @@ module.exports = {
parsed.signingString +=
'(request-target): ' + request.method.toLowerCase() + ' ' +
request.url;
} else if (h === '(keyid)') {
parsed.signingString += '(keyid): ' + parsed.params.keyId;
} else if (h === '(algorithm)') {
parsed.signingString += '(algorithm): ' + parsed.params.algorithm;
} else {
var value = request.headers[h];
if (value === undefined)
Expand Down Expand Up @@ -305,6 +308,7 @@ module.exports = {
throw new MissingHeaderError(hdr + ' was not a signed header');
});

parsed.params.algorithm = parsed.params.algorithm.toLowerCase();
if (options.algorithms) {
if (options.algorithms.indexOf(parsed.params.algorithm) === -1)
throw new InvalidParamsError(parsed.params.algorithm +
Expand Down
61 changes: 37 additions & 24 deletions lib/signer.js
Expand Up @@ -305,6 +305,36 @@ module.exports = {
alg = validateAlgorithm(options.algorithm);
}

var key = options.key;
if (alg[0] === 'hmac') {
if (typeof (key) !== 'string' && !Buffer.isBuffer(key))
throw (new TypeError('options.key must be a string or Buffer'));
} else {
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) {
alg[0] = key.type;
} else if (key.type !== alg[0]) {
throw (new InvalidAlgorithmError('options.key must be a ' +
alg[0].toUpperCase() + ' key, was given a ' +
key.type.toUpperCase() + ' key instead'));
}
if (alg[1] === undefined) {
alg[1] = key.defaultHashAlgorithm();
}

options.algorithm = alg[0] + '-' + alg[1];
}

var i;
var stringToSign = '';
for (i = 0; i < options.headers.length; i++) {
Expand All @@ -331,6 +361,10 @@ module.exports = {
stringToSign +=
'(request-target): ' + request.method.toLowerCase() + ' ' +
request.path;
} else if (h === '(keyid)') {
stringToSign += '(keyid): ' + options.keyId;
} else if (h === '(algorithm)') {
stringToSign += '(algorithm): ' + options.algorithm;
} else {
var value = request.getHeader(h);
if (value === undefined || value === '') {
Expand All @@ -350,40 +384,19 @@ module.exports = {

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);
var hmac = crypto.createHmac(alg[1].toUpperCase(), 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;
assert.strictEqual(alg[1], sigObj.hashAlgorithm,
'hash algorithm mismatch');
signature = sigObj.toString();
assert.notStrictEqual(signature, '', 'empty signature produced');
}
Expand Down
61 changes: 61 additions & 0 deletions test/signer.test.js
Expand Up @@ -180,6 +180,67 @@ test('request target', function(t) {
req.end();
});

test('keyid', function(t) {
var req = http.request(httpOptions, function(res) {
t.end();
});
var opts = {
keyId: 'unit',
key: rsaPrivate,
headers: ['date', '(keyid)']
};

req._stringToSign = null;
t.ok(httpSignature.sign(req, opts));
t.ok(req.getHeader('Authorization'));
t.strictEqual(typeof (req._stringToSign), 'string');
t.ok(req._stringToSign.match(/^date: [^\n]*\n\(keyid\): unit$/));
console.log('> ' + req.getHeader('Authorization'));
req.end();
});

test('signing algorithm', function(t) {
var req = http.request(httpOptions, function(res) {
t.end();
});
var opts = {
algorithm: 'rsa-sha256',
keyId: 'unit',
key: rsaPrivate,
headers: ['date', '(algorithm)']
};

req._stringToSign = null;
t.ok(httpSignature.sign(req, opts));
t.ok(req.getHeader('Authorization'));
t.strictEqual(typeof (opts.algorithm), 'string');
t.strictEqual(opts.algorithm, 'rsa-sha256');
t.strictEqual(typeof (req._stringToSign), 'string');
t.ok(req._stringToSign.match(/^date: [^\n]*\n\(algorithm\): [^\n]*$/));
console.log('> ' + req.getHeader('Authorization'));
req.end();
});

test('signing with unspecified algorithm', function(t) {
var req = http.request(httpOptions, function(res) {
t.end();
});
var opts = {
keyId: 'unit',
key: rsaPrivate,
headers: ['date', '(algorithm)']
};

req._stringToSign = null;
t.ok(httpSignature.sign(req, opts));
t.ok(req.getHeader('Authorization'));
t.strictEqual(typeof (opts.algorithm), 'string');
t.strictEqual(typeof (req._stringToSign), 'string');
t.ok(req._stringToSign.match(/^date: [^\n]*\n\(algorithm\): [^\n]*$/));
console.log('> ' + req.getHeader('Authorization'));
req.end();
});

test('request-target with dsa key', function(t) {
var req = http.request(httpOptions, function(res) {
t.end();
Expand Down

0 comments on commit 202ebc3

Please sign in to comment.