2019-10-04 23:35:59 +00:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
var Keypairs = module.exports;
|
|
|
|
var crypto = require('crypto');
|
|
|
|
|
|
|
|
Keypairs._sign = function(opts, payload) {
|
|
|
|
return Keypairs._import(opts).then(function(pem) {
|
|
|
|
payload = Buffer.from(payload);
|
|
|
|
|
|
|
|
// node specifies RSA-SHAxxx even when it's actually ecdsa (it's all encoded x509 shasums anyway)
|
|
|
|
// TODO opts.alg = (protect||header).alg
|
|
|
|
var nodeAlg = 'SHA' + Keypairs._getBits(opts);
|
|
|
|
var binsig = crypto
|
|
|
|
.createSign(nodeAlg)
|
|
|
|
.update(payload)
|
|
|
|
.sign(pem);
|
|
|
|
|
2019-10-05 11:21:07 +00:00
|
|
|
if ('EC' === opts.jwk.kty && !/x509|asn1/i.test(opts.format)) {
|
2019-10-04 23:35:59 +00:00
|
|
|
// ECDSA JWT signatures differ from "normal" ECDSA signatures
|
|
|
|
// https://tools.ietf.org/html/rfc7518#section-3.4
|
|
|
|
binsig = Keypairs._ecdsaAsn1SigToJoseSig(binsig);
|
|
|
|
}
|
|
|
|
|
|
|
|
return binsig;
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
Keypairs._import = function(opts) {
|
|
|
|
if (opts.pem && opts.jwk) {
|
|
|
|
return Promise.resolve(opts.pem);
|
|
|
|
} else {
|
|
|
|
// XXX added by caller
|
|
|
|
return Keypairs.export({ jwk: opts.jwk });
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Keypairs._ecdsaAsn1SigToJoseSig = function(binsig) {
|
|
|
|
// should have asn1 sequence header of 0x30
|
|
|
|
if (0x30 !== binsig[0]) {
|
|
|
|
throw new Error('Impossible EC SHA head marker');
|
|
|
|
}
|
|
|
|
var index = 2; // first ecdsa "R" header byte
|
|
|
|
var len = binsig[1];
|
|
|
|
var lenlen = 0;
|
|
|
|
// Seek length of length if length is greater than 127 (i.e. two 512-bit / 64-byte R and S values)
|
|
|
|
if (0x80 & len) {
|
|
|
|
lenlen = len - 0x80; // should be exactly 1
|
|
|
|
len = binsig[2]; // should be <= 130 (two 64-bit SHA-512s, plus padding)
|
|
|
|
index += lenlen;
|
|
|
|
}
|
|
|
|
// should be of BigInt type
|
|
|
|
if (0x02 !== binsig[index]) {
|
|
|
|
throw new Error('Impossible EC SHA R marker');
|
|
|
|
}
|
|
|
|
index += 1;
|
|
|
|
|
|
|
|
var rlen = binsig[index];
|
|
|
|
var bits = 32;
|
|
|
|
if (rlen > 49) {
|
|
|
|
bits = 64;
|
|
|
|
} else if (rlen > 33) {
|
|
|
|
bits = 48;
|
|
|
|
}
|
|
|
|
var r = binsig.slice(index + 1, index + 1 + rlen).toString('hex');
|
|
|
|
var slen = binsig[index + 1 + rlen + 1]; // skip header and read length
|
|
|
|
var s = binsig.slice(index + 1 + rlen + 1 + 1).toString('hex');
|
|
|
|
if (2 * slen !== s.length) {
|
|
|
|
throw new Error('Impossible EC SHA S length');
|
|
|
|
}
|
|
|
|
// There may be one byte of padding on either
|
|
|
|
while (r.length < 2 * bits) {
|
|
|
|
r = '00' + r;
|
|
|
|
}
|
|
|
|
while (s.length < 2 * bits) {
|
|
|
|
s = '00' + s;
|
|
|
|
}
|
|
|
|
if (2 * (bits + 1) === r.length) {
|
|
|
|
r = r.slice(2);
|
|
|
|
}
|
|
|
|
if (2 * (bits + 1) === s.length) {
|
|
|
|
s = s.slice(2);
|
|
|
|
}
|
|
|
|
return Buffer.concat([Buffer.from(r, 'hex'), Buffer.from(s, 'hex')]);
|
|
|
|
};
|