From 3c84a7e1bdb942eb9fb0f299c01ca99983172b7a Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Fri, 8 Mar 2019 19:03:55 -0700 Subject: [PATCH] v1.2.12: fix EC sig padding issues --- keypairs.js | 36 +++++++++++++++++++++--------------- package.json | 2 +- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/keypairs.js b/keypairs.js index 730fac7..98079bd 100644 --- a/keypairs.js +++ b/keypairs.js @@ -237,7 +237,7 @@ Keypairs.signJws = function (opts) { .update(protect ? (protected64 + "." + payload64) : payload64) .sign(pem) ; - if (!opts.jwk || 'RSA' !== opts.jwk.kty) { + if ('EC' === opts.jwk.kty) { // ECDSA JWT signatures differ from "normal" ECDSA signatures // https://tools.ietf.org/html/rfc7518#section-3.4 binsig = convertIfEcdsa(binsig); @@ -259,31 +259,37 @@ Keypairs.signJws = function (opts) { function convertIfEcdsa(binsig) { // should have asn1 sequence header of 0x30 - if (0x30 !== binsig[0]) { return binsig; } + 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; - // the length of the signature won't be over 256 bytes (2048 bits) for many years yet - if (1 !== lenlen) { return binsig; } - // the length is this number - len = binsig[2]; + lenlen = len - 0x80; // should be exactly 1 + len = binsig[2]; // should be <= 130 (two 64-bit SHA-512s, plus padding) index += lenlen; } - // should have bigint header of 0x02 followd by a single byte of length - if (0x02 !== binsig[index]) { return binsig; } + // should be of BigInt type + if (0x02 !== binsig[index]) { throw new Error("Impossible EC SHA R marker"); } index += 1; + var rlen = binsig[index]; - var r = binsig.slice(index + 1, index + 1 + rlen); + 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); - if (slen !== s.byteLength) { return binsig; } + 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 - if (33 === r.byteLength) { r = r.slice(1); } - if (33 === s.byteLength) { s = s.slice(1); } - return Buffer.concat([r, s]); + 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')]); } if (opts.pem && opts.jwk) { diff --git a/package.json b/package.json index 775bbca..24dd99c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keypairs", - "version": "1.2.11", + "version": "1.2.12", "description": "Lightweight RSA/ECDSA keypair generation and JWK <-> PEM", "main": "keypairs.js", "files": [