From 2d86e2cd362ed05766ce359899160a2e2fe54336 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Wa=CC=88rting?= Date: Fri, 15 Sep 2023 10:32:17 +0200 Subject: [PATCH 1/6] Drop support for old nodejs versions --- .travis.yml | 12 +------- index.js | 81 ++++++++++++++++++---------------------------------- package.json | 7 +++-- 3 files changed, 33 insertions(+), 67 deletions(-) diff --git a/.travis.yml b/.travis.yml index 602a0c6..70bdef0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,6 @@ language: node_js node_js: - - 0.10 - - 0.11 - - 0.12 - - 4 - - 5 - - 6 - - 7 - - 8 - - 9 - - 10 - - 11 + - 14 notifications: email: recipients: diff --git a/index.js b/index.js index d2061ef..c3bf02f 100644 --- a/index.js +++ b/index.js @@ -1,18 +1,23 @@ -var bufferEqual = require('buffer-equal-constant-time'); -var Buffer = require('safe-buffer').Buffer; +var Buffer = require('buffer').Buffer; var crypto = require('crypto'); var formatEcdsa = require('ecdsa-sig-formatter'); var util = require('util'); var MSG_INVALID_ALGORITHM = '"%s" is not a valid algorithm.\n Supported algorithms are:\n "HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "PS256", "PS384", "PS512", "ES256", "ES384", "ES512" and "none".' -var MSG_INVALID_SECRET = 'secret must be a string or buffer'; -var MSG_INVALID_VERIFIER_KEY = 'key must be a string or a buffer'; +var MSG_INVALID_SECRET = 'secret must be a string or buffer or a KeyObject'; +var MSG_INVALID_VERIFIER_KEY = 'key must be a string or a buffer or a KeyObject'; var MSG_INVALID_SIGNER_KEY = 'key must be a string, a buffer or an object'; -var supportsKeyObjects = typeof crypto.createPublicKey === 'function'; -if (supportsKeyObjects) { - MSG_INVALID_VERIFIER_KEY += ' or a KeyObject'; - MSG_INVALID_SECRET += 'or a KeyObject'; +/** + * @param {Uint8Array} a + * @param {Uint8Array} b + */ +function timingSafeEqual(a, b) { + if (a.length !== b.length) return false + + let c = 0; + for (let i = 0, len = a.length; i < len; i++) c |= a[i] ^ b[i]; + return !c; } function checkIsPublicKey(key) { @@ -24,10 +29,6 @@ function checkIsPublicKey(key) { return; } - if (!supportsKeyObjects) { - throw typeError(MSG_INVALID_VERIFIER_KEY); - } - if (typeof key !== 'object') { throw typeError(MSG_INVALID_VERIFIER_KEY); } @@ -70,10 +71,6 @@ function checkIsSecretKey(key) { return key; } - if (!supportsKeyObjects) { - throw typeError(MSG_INVALID_SECRET); - } - if (typeof key !== 'object') { throw typeError(MSG_INVALID_SECRET); } @@ -87,31 +84,8 @@ function checkIsSecretKey(key) { } } -function fromBase64(base64) { - return base64 - .replace(/=/g, '') - .replace(/\+/g, '-') - .replace(/\//g, '_'); -} - -function toBase64(base64url) { - base64url = base64url.toString(); - - var padding = 4 - base64url.length % 4; - if (padding !== 4) { - for (var i = 0; i < padding; ++i) { - base64url += '='; - } - } - - return base64url - .replace(/\-/g, '+') - .replace(/_/g, '/'); -} - -function typeError(template) { - var args = [].slice.call(arguments, 1); - var errMsg = util.format.bind(util, template).apply(null, args); +function typeError(template, ...args) { + var errMsg = util.format(template, ...args); return new TypeError(errMsg); } @@ -130,15 +104,16 @@ function createHmacSigner(bits) { checkIsSecretKey(secret); thing = normalizeInput(thing); var hmac = crypto.createHmac('sha' + bits, secret); - var sig = (hmac.update(thing), hmac.digest('base64')) - return fromBase64(sig); + hmac.update(thing); + var sig = hmac.digest('base64url') + return sig; } } function createHmacVerifier(bits) { return function verify(thing, signature, secret) { var computedSig = createHmacSigner(bits)(thing, secret); - return bufferEqual(Buffer.from(signature), Buffer.from(computedSig)); + return timingSafeEqual(Buffer.from(signature), Buffer.from(computedSig)); } } @@ -149,8 +124,9 @@ function createKeySigner(bits) { // Even though we are specifying "RSA" here, this works with ECDSA // keys as well. var signer = crypto.createSign('RSA-SHA' + bits); - var sig = (signer.update(thing), signer.sign(privateKey, 'base64')); - return fromBase64(sig); + signer.update(thing); + var sig = signer.sign(privateKey, 'base64url'); + return sig; } } @@ -158,10 +134,9 @@ function createKeyVerifier(bits) { return function verify(thing, signature, publicKey) { checkIsPublicKey(publicKey); thing = normalizeInput(thing); - signature = toBase64(signature); var verifier = crypto.createVerify('RSA-SHA' + bits); verifier.update(thing); - return verifier.verify(publicKey, signature, 'base64'); + return verifier.verify(publicKey, signature, 'base64url'); } } @@ -170,12 +145,13 @@ function createPSSKeySigner(bits) { checkIsPrivateKey(privateKey); thing = normalizeInput(thing); var signer = crypto.createSign('RSA-SHA' + bits); - var sig = (signer.update(thing), signer.sign({ + signer.update(thing); + var sig = signer.sign({ key: privateKey, padding: crypto.constants.RSA_PKCS1_PSS_PADDING, saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST - }, 'base64')); - return fromBase64(sig); + }, 'base64url'); + return sig; } } @@ -183,14 +159,13 @@ function createPSSKeyVerifier(bits) { return function verify(thing, signature, publicKey) { checkIsPublicKey(publicKey); thing = normalizeInput(thing); - signature = toBase64(signature); var verifier = crypto.createVerify('RSA-SHA' + bits); verifier.update(thing); return verifier.verify({ key: publicKey, padding: crypto.constants.RSA_PKCS1_PSS_PADDING, saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST - }, signature, 'base64'); + }, signature, 'base64url'); } } diff --git a/package.json b/package.json index 7b3d5e5..9ce8ac4 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,11 @@ "directories": { "test": "test" }, + "engines": { + "node": ">=14.18.0" + }, "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" + "ecdsa-sig-formatter": "1.0.11" }, "devDependencies": { "base64url": "^2.0.0", From d709800951c5f0c730b432b82df9d1c2b1901e5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Wa=CC=88rting?= Date: Fri, 15 Sep 2023 16:07:12 +0200 Subject: [PATCH 2/6] use crypto.timingSafeEqual and drop prefix --- index.js | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/index.js b/index.js index c3bf02f..0e24864 100644 --- a/index.js +++ b/index.js @@ -8,17 +8,7 @@ var MSG_INVALID_SECRET = 'secret must be a string or buffer or a KeyObject'; var MSG_INVALID_VERIFIER_KEY = 'key must be a string or a buffer or a KeyObject'; var MSG_INVALID_SIGNER_KEY = 'key must be a string, a buffer or an object'; -/** - * @param {Uint8Array} a - * @param {Uint8Array} b - */ -function timingSafeEqual(a, b) { - if (a.length !== b.length) return false - - let c = 0; - for (let i = 0, len = a.length; i < len; i++) c |= a[i] ^ b[i]; - return !c; -} +var timingSafeEqual = crypto.timingSafeEqual function checkIsPublicKey(key) { if (Buffer.isBuffer(key)) { @@ -113,7 +103,9 @@ function createHmacSigner(bits) { function createHmacVerifier(bits) { return function verify(thing, signature, secret) { var computedSig = createHmacSigner(bits)(thing, secret); - return timingSafeEqual(Buffer.from(signature), Buffer.from(computedSig)); + var a = Buffer.from(signature); + var b = Buffer.from(computedSig); + return a.byteLength === b.byteLength && timingSafeEqual(a, b); } } @@ -123,7 +115,7 @@ function createKeySigner(bits) { thing = normalizeInput(thing); // Even though we are specifying "RSA" here, this works with ECDSA // keys as well. - var signer = crypto.createSign('RSA-SHA' + bits); + var signer = crypto.createSign('SHA' + bits); signer.update(thing); var sig = signer.sign(privateKey, 'base64url'); return sig; From 3d7413751784c2cacc52dbc4c86513f1699e5f48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Wa=CC=88rting?= Date: Fri, 15 Sep 2023 16:11:27 +0200 Subject: [PATCH 3/6] drop more prefixes --- index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 0e24864..cb2f648 100644 --- a/index.js +++ b/index.js @@ -126,7 +126,7 @@ function createKeyVerifier(bits) { return function verify(thing, signature, publicKey) { checkIsPublicKey(publicKey); thing = normalizeInput(thing); - var verifier = crypto.createVerify('RSA-SHA' + bits); + var verifier = crypto.createVerify('SHA' + bits); verifier.update(thing); return verifier.verify(publicKey, signature, 'base64url'); } @@ -136,7 +136,7 @@ function createPSSKeySigner(bits) { return function sign(thing, privateKey) { checkIsPrivateKey(privateKey); thing = normalizeInput(thing); - var signer = crypto.createSign('RSA-SHA' + bits); + var signer = crypto.createSign('SHA' + bits); signer.update(thing); var sig = signer.sign({ key: privateKey, @@ -151,7 +151,7 @@ function createPSSKeyVerifier(bits) { return function verify(thing, signature, publicKey) { checkIsPublicKey(publicKey); thing = normalizeInput(thing); - var verifier = crypto.createVerify('RSA-SHA' + bits); + var verifier = crypto.createVerify('SHA' + bits); verifier.update(thing); return verifier.verify({ key: publicKey, From 8042161b2e6c003fd5e079386cb240b331753a2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Wa=CC=88rting?= Date: Fri, 15 Sep 2023 16:17:20 +0200 Subject: [PATCH 4/6] remove dep on node:util --- index.js | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/index.js b/index.js index cb2f648..37612a6 100644 --- a/index.js +++ b/index.js @@ -1,9 +1,8 @@ var Buffer = require('buffer').Buffer; var crypto = require('crypto'); var formatEcdsa = require('ecdsa-sig-formatter'); -var util = require('util'); -var MSG_INVALID_ALGORITHM = '"%s" is not a valid algorithm.\n Supported algorithms are:\n "HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "PS256", "PS384", "PS512", "ES256", "ES384", "ES512" and "none".' +var MSG_INVALID_ALGORITHM = 'is not a valid algorithm.\n Supported algorithms are:\n "HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "PS256", "PS384", "PS512", "ES256", "ES384", "ES512" and "none".' var MSG_INVALID_SECRET = 'secret must be a string or buffer or a KeyObject'; var MSG_INVALID_VERIFIER_KEY = 'key must be a string or a buffer or a KeyObject'; var MSG_INVALID_SIGNER_KEY = 'key must be a string, a buffer or an object'; @@ -20,19 +19,19 @@ function checkIsPublicKey(key) { } if (typeof key !== 'object') { - throw typeError(MSG_INVALID_VERIFIER_KEY); + throw new TypeError(MSG_INVALID_VERIFIER_KEY); } if (typeof key.type !== 'string') { - throw typeError(MSG_INVALID_VERIFIER_KEY); + throw new TypeError(MSG_INVALID_VERIFIER_KEY); } if (typeof key.asymmetricKeyType !== 'string') { - throw typeError(MSG_INVALID_VERIFIER_KEY); + throw new TypeError(MSG_INVALID_VERIFIER_KEY); } if (typeof key.export !== 'function') { - throw typeError(MSG_INVALID_VERIFIER_KEY); + throw new TypeError(MSG_INVALID_VERIFIER_KEY); } }; @@ -49,7 +48,7 @@ function checkIsPrivateKey(key) { return; } - throw typeError(MSG_INVALID_SIGNER_KEY); + throw new TypeError(MSG_INVALID_SIGNER_KEY); }; function checkIsSecretKey(key) { @@ -62,23 +61,18 @@ function checkIsSecretKey(key) { } if (typeof key !== 'object') { - throw typeError(MSG_INVALID_SECRET); + throw new TypeError(MSG_INVALID_SECRET); } if (key.type !== 'secret') { - throw typeError(MSG_INVALID_SECRET); + throw new TypeError(MSG_INVALID_SECRET); } if (typeof key.export !== 'function') { - throw typeError(MSG_INVALID_SECRET); + throw new TypeError(MSG_INVALID_SECRET); } } -function typeError(template, ...args) { - var errMsg = util.format(template, ...args); - return new TypeError(errMsg); -} - function bufferOrString(obj) { return Buffer.isBuffer(obj) || typeof obj === 'string'; } @@ -208,7 +202,7 @@ module.exports = function jwa(algorithm) { } var match = algorithm.match(/^(RS|PS|ES|HS)(256|384|512)$|^(none)$/); if (!match) - throw typeError(MSG_INVALID_ALGORITHM, algorithm); + throw new TypeError(`"${algorithm}" ` + MSG_INVALID_ALGORITHM); var algo = (match[1] || match[3]).toLowerCase(); var bits = match[2]; From d1740306fc0de765400bb0ece3bbf7227be0a3c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Wa=CC=88rting?= Date: Fri, 15 Sep 2023 16:22:45 +0200 Subject: [PATCH 5/6] now also solves #45 --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 37612a6..be668cf 100644 --- a/index.js +++ b/index.js @@ -57,7 +57,7 @@ function checkIsSecretKey(key) { } if (typeof key === 'string') { - return key; + return; } if (typeof key !== 'object') { From f3c449918187e70dffd8281dba0cf045f9777a2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Wa=CC=88rting?= Date: Fri, 15 Sep 2023 16:52:35 +0200 Subject: [PATCH 6/6] merged some if statement --- index.js | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/index.js b/index.js index be668cf..49191a9 100644 --- a/index.js +++ b/index.js @@ -36,15 +36,7 @@ function checkIsPublicKey(key) { }; function checkIsPrivateKey(key) { - if (Buffer.isBuffer(key)) { - return; - } - - if (typeof key === 'string') { - return; - } - - if (typeof key === 'object') { + if (Buffer.isBuffer(key) || typeof key === 'string' || typeof key === 'object') { return; } @@ -52,23 +44,11 @@ function checkIsPrivateKey(key) { }; function checkIsSecretKey(key) { - if (Buffer.isBuffer(key)) { + if (Buffer.isBuffer(key) || typeof key === 'string' || typeof key === 'object') { return; } - if (typeof key === 'string') { - return; - } - - if (typeof key !== 'object') { - throw new TypeError(MSG_INVALID_SECRET); - } - - if (key.type !== 'secret') { - throw new TypeError(MSG_INVALID_SECRET); - } - - if (typeof key.export !== 'function') { + if (key.type !== 'secret' || typeof key.export !== 'function') { throw new TypeError(MSG_INVALID_SECRET); } }