From a12774ce3bf21fd45949a876e16a96eec774e1b3 Mon Sep 17 00:00:00 2001 From: Matteo Pietro Dazzi Date: Wed, 13 Nov 2024 22:34:30 +0100 Subject: [PATCH 01/11] feat: deprecate use new crypto --- src/crypto.js | 18 +---- src/signer.js | 9 +-- src/verifier.js | 10 +-- test/compatibility.spec.js | 35 ++++----- test/compliance.spec.js | 43 +++++------ test/crypto.spec.js | 71 +++++++++-------- test/signer.spec.js | 151 ++++++++++++------------------------- test/verifier.spec.js | 79 ++++++++----------- 8 files changed, 151 insertions(+), 265 deletions(-) diff --git a/src/crypto.js b/src/crypto.js index 6df803c..1a13e4c 100644 --- a/src/crypto.js +++ b/src/crypto.js @@ -15,13 +15,11 @@ const { RSA_PSS_SALTLEN_AUTO } } = require('node:crypto') -let { sign: directSign, verify: directVerify } = require('node:crypto') +const { sign: directSign, verify: directVerify } = require('node:crypto') const { joseToDer, derToJose } = require('ecdsa-sig-formatter') const Cache = require('mnemonist/lru-cache') const { TokenError } = require('./error') -const useNewCrypto = typeof directSign === 'function' - const base64UrlMatcher = /[=+/]/g const encoderMap = { '=': '', '+': '-', '/': '_' } @@ -42,19 +40,6 @@ const ecCurves = { '1.3.132.0.35': { bits: '512', names: ['P-521', 'secp521r1'] } } -/* istanbul ignore next */ -if (!useNewCrypto) { - directSign = function (alg, data, options) { - if (typeof alg === 'undefined') { - throw new TokenError(TokenError.codes.signError, 'EdDSA algorithms are not supported by your Node.js version.') - } - - return createSign(alg) - .update(data) - .sign(options) - } -} - const PrivateKey = asn.define('PrivateKey', function () { this.seq().obj( this.key('version').int(), @@ -351,7 +336,6 @@ function verifySignature(algorithm, key, input, signature) { } module.exports = { - useNewCrypto, base64UrlMatcher, base64UrlReplacer, hsAlgorithms, diff --git a/src/signer.js b/src/signer.js index 95fe3fe..ecb0c67 100644 --- a/src/signer.js +++ b/src/signer.js @@ -3,7 +3,6 @@ const { base64UrlMatcher, base64UrlReplacer, - useNewCrypto, hsAlgorithms, esAlgorithms, rsaAlgorithms, @@ -45,13 +44,7 @@ function prepareKeyOrSecret(key, algorithm) { key = Buffer.from(key, 'utf-8') } - // Only on Node 12 - Create a key object - /* istanbul ignore next */ - if (useNewCrypto) { - key = algorithm[0] === 'H' ? createSecretKey(key) : createPrivateKey(key) - } - - return key + return algorithm[0] === 'H' ? createSecretKey(key) : createPrivateKey(key) } function sign( diff --git a/src/verifier.js b/src/verifier.js index 3a6078e..2843345 100644 --- a/src/verifier.js +++ b/src/verifier.js @@ -3,7 +3,7 @@ const { createPublicKey, createSecretKey } = require('node:crypto') const Cache = require('mnemonist/lru-cache') -const { useNewCrypto, hsAlgorithms, verifySignature, detectPublicKeyAlgorithms } = require('./crypto') +const { hsAlgorithms, verifySignature, detectPublicKeyAlgorithms } = require('./crypto') const createDecoder = require('./decoder') const { TokenError } = require('./error') const { getAsyncKey, ensurePromiseCallback, hashToken } = require('./utils') @@ -39,13 +39,7 @@ function prepareKeyOrSecret(key, isSecret) { key = Buffer.from(key, 'utf-8') } - // Only on Node 12 - Create a key object - /* istanbul ignore next */ - if (useNewCrypto) { - key = isSecret ? createSecretKey(key) : createPublicKey(key) - } - - return key + return isSecret ? createSecretKey(key) : createPublicKey(key) } function ensureStringClaimMatcher(raw) { diff --git a/test/compatibility.spec.js b/test/compatibility.spec.js index 3d563e9..4ab9830 100644 --- a/test/compatibility.spec.js +++ b/test/compatibility.spec.js @@ -10,7 +10,6 @@ const { resolve } = require('node:path') const { test } = require('node:test') const { createSigner, createVerifier } = require('../src') -const { useNewCrypto } = require('../src/crypto') const privateKeys = { HS: 'secretsecretsecret', @@ -56,25 +55,23 @@ for (const type of ['HS', 'ES', 'RS', 'PS']) { } } -if (useNewCrypto) { - for (const curve of ['Ed25519', 'Ed448']) { - test(`fast-jwt should correcty verify tokens created by jose - EdDSA with ${curve}`, t => { - const verify = createVerifier({ key: publicKeys[curve].toString() }) - const token = joseSign({ a: 1, b: 2, c: 3 }, asKey(privateKeys[curve]), { - iat: false, - header: { - typ: 'JWT' - } - }) - - t.assert.deepStrictEqual(verify(token), { a: 1, b: 2, c: 3 }) +for (const curve of ['Ed25519', 'Ed448']) { + test(`fast-jwt should correcty verify tokens created by jose - EdDSA with ${curve}`, t => { + const verify = createVerifier({ key: publicKeys[curve].toString() }) + const token = joseSign({ a: 1, b: 2, c: 3 }, asKey(privateKeys[curve]), { + iat: false, + header: { + typ: 'JWT' + } }) - test(`jose should correcty verify tokens created by fast-jwt - EdDSA with ${curve}`, t => { - const signer = createSigner({ key: privateKeys[curve], noTimestamp: true }) - const token = signer({ a: 1, b: 2, c: 3 }) + t.assert.deepStrictEqual(verify(token), { a: 1, b: 2, c: 3 }) + }) - t.assert.deepStrictEqual(joseVerify(token, asKey(publicKeys[curve])), { a: 1, b: 2, c: 3 }) - }) - } + test(`jose should correcty verify tokens created by fast-jwt - EdDSA with ${curve}`, t => { + const signer = createSigner({ key: privateKeys[curve], noTimestamp: true }) + const token = signer({ a: 1, b: 2, c: 3 }) + + t.assert.deepStrictEqual(joseVerify(token, asKey(publicKeys[curve])), { a: 1, b: 2, c: 3 }) + }) } diff --git a/test/compliance.spec.js b/test/compliance.spec.js index 3b0a972..5fc5932 100644 --- a/test/compliance.spec.js +++ b/test/compliance.spec.js @@ -6,7 +6,6 @@ const { } = require('jose') const { createVerifier, createSigner } = require('../src') -const { useNewCrypto } = require('../src/crypto') const payload = { text: "It’s a dangerous business, Frodo, going out your door. You step onto the road, and if you don't keep your feet, there’s no knowing where you might be swept off to." @@ -142,30 +141,28 @@ test('ES512', t => { t.assert.equal(token.replace(/\..+/, ''), expectedToken.replace(/\..+/, '')) }) -if (useNewCrypto) { - // All the keys here are extracted from https://tools.ietf.org/html/rfc8037 - const ed25519PublicKey = asKey({ - kty: 'OKP', - crv: 'Ed25519', - x: '11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo' - }).toPEM() +// All the keys here are extracted from https://tools.ietf.org/html/rfc8037 +const ed25519PublicKey = asKey({ + kty: 'OKP', + crv: 'Ed25519', + x: '11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo' +}).toPEM() - const ed25519PrivateKey = asKey({ - kty: 'OKP', - crv: 'Ed25519', - d: 'nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A', - x: '11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo' - }).toPEM(true) +const ed25519PrivateKey = asKey({ + kty: 'OKP', + crv: 'Ed25519', + d: 'nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A', + x: '11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo' +}).toPEM(true) - test('EdDSA - Ed25519', t => { - const expectedToken = - 'eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ0ZXh0IjoiSXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4ifQ.s6A86zrJs551R4UxXwJsfRCGswdTJYFeNWjHUkZvragJ7hN43T5UetbpG4S6L2G7wOq5N_JJKrkbs0q0Gd-EAQ' +test('EdDSA - Ed25519', t => { + const expectedToken = + 'eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ0ZXh0IjoiSXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4ifQ.s6A86zrJs551R4UxXwJsfRCGswdTJYFeNWjHUkZvragJ7hN43T5UetbpG4S6L2G7wOq5N_JJKrkbs0q0Gd-EAQ' - const token = createSigner({ algorithm: 'EdDSA', key: ed25519PrivateKey, noTimestamp: true })(payload) + const token = createSigner({ algorithm: 'EdDSA', key: ed25519PrivateKey, noTimestamp: true })(payload) - const verified = createVerifier({ key: ed25519PublicKey })(token) + const verified = createVerifier({ key: ed25519PublicKey })(token) - t.assert.deepStrictEqual(verified, payload) - t.assert.equal(token, expectedToken) - }) -} + t.assert.deepStrictEqual(verified, payload) + t.assert.equal(token, expectedToken) +}) diff --git a/test/crypto.spec.js b/test/crypto.spec.js index b666e01..9cd7971 100644 --- a/test/crypto.spec.js +++ b/test/crypto.spec.js @@ -6,7 +6,6 @@ const { resolve } = require('node:path') const { createVerifier, createSigner } = require('../src') const { - useNewCrypto, hsAlgorithms, rsaAlgorithms, detectPrivateKeyAlgorithm, @@ -315,48 +314,46 @@ for (const type of ['ES', 'RS', 'PS']) { } } -if (useNewCrypto) { - for (const type of ['Ed25519', 'Ed448']) { - const privateKey = privateKeys[type] - const publicKey = publicKeys[type] +for (const type of ['Ed25519', 'Ed448']) { + const privateKey = privateKeys[type] + const publicKey = publicKeys[type] - test(`EdDSA with ${type} based tokens round trip with buffer keys`, t => { - const token = createSigner({ algorithm: 'EdDSA', key: privateKey })({ payload: 'PAYLOAD' }) - const verified = createVerifier({ algorithms: ['EdDSA'], key: publicKey })(token) + test(`EdDSA with ${type} based tokens round trip with buffer keys`, t => { + const token = createSigner({ algorithm: 'EdDSA', key: privateKey })({ payload: 'PAYLOAD' }) + const verified = createVerifier({ algorithms: ['EdDSA'], key: publicKey })(token) - t.assert.equal(verified.payload, 'PAYLOAD') - t.assert.ok(verified.iat >= start) - t.assert.ok(verified.iat <= Date.now() / 1000) - }) - - test(`EdDSA with ${type} based tokens round trip with string keys`, t => { - const token = createSigner({ algorithm: 'EdDSA', key: privateKey.toString('utf-8') })({ - payload: 'PAYLOAD' - }) - const verified = createVerifier({ algorithms: ['EdDSA'], key: publicKey.toString('utf-8') })(token) + t.assert.equal(verified.payload, 'PAYLOAD') + t.assert.ok(verified.iat >= start) + t.assert.ok(verified.iat <= Date.now() / 1000) + }) - t.assert.equal(verified.payload, 'PAYLOAD') - t.assert.ok(verified.iat >= start) - t.assert.ok(verified.iat <= Date.now() / 1000) + test(`EdDSA with ${type} based tokens round trip with string keys`, t => { + const token = createSigner({ algorithm: 'EdDSA', key: privateKey.toString('utf-8') })({ + payload: 'PAYLOAD' }) + const verified = createVerifier({ algorithms: ['EdDSA'], key: publicKey.toString('utf-8') })(token) - test(`EdDSA with ${type} based tokens should validate the private key`, async t => { - await t.assert.rejects( - createSigner({ algorithm: 'EdDSA', key: async () => 123 })({ payload: 'PAYLOAD' }), - { - message: - 'The key returned from the callback must be a string or a buffer containing a secret or a private key.' - }, - null - ) - }) + t.assert.equal(verified.payload, 'PAYLOAD') + t.assert.ok(verified.iat >= start) + t.assert.ok(verified.iat <= Date.now() / 1000) + }) - test(`EdDSA with ${type} based tokens should validate the public key`, async t => { - const token = createSigner({ algorithm: 'EdDSA', key: privateKey })({ payload: 'PAYLOAD' }) + test(`EdDSA with ${type} based tokens should validate the private key`, async t => { + await t.assert.rejects( + createSigner({ algorithm: 'EdDSA', key: async () => 123 })({ payload: 'PAYLOAD' }), + { + message: + 'The key returned from the callback must be a string or a buffer containing a secret or a private key.' + }, + null + ) + }) - await t.assert.rejects(createVerifier({ algorithms: ['EdDSA'], key: async () => 123 })(token), { - message: 'The key returned from the callback must be a string or a buffer containing a secret or a public key.' - }) + test(`EdDSA with ${type} based tokens should validate the public key`, async t => { + const token = createSigner({ algorithm: 'EdDSA', key: privateKey })({ payload: 'PAYLOAD' }) + + await t.assert.rejects(createVerifier({ algorithms: ['EdDSA'], key: async () => 123 })(token), { + message: 'The key returned from the callback must be a string or a buffer containing a secret or a public key.' }) - } + }) } diff --git a/test/signer.spec.js b/test/signer.spec.js index 3aba8ab..b382542 100644 --- a/test/signer.spec.js +++ b/test/signer.spec.js @@ -5,7 +5,6 @@ const { resolve } = require('node:path') const { test } = require('node:test') const { createSigner, createVerifier, TokenError, createDecoder } = require('../src') -const { useNewCrypto } = require('../src/crypto') const privateKeys = { HS: 'secretsecretsecret', @@ -79,19 +78,10 @@ test('it correctly returns a token - sync', async t => { 'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJhIjoxfQ.' ) - if (useNewCrypto) { - t.assert.equal( - sign({ a: 1 }, { noTimestamp: true, key: privateKeys.Ed25519 }), - 'eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJhIjoxfQ.pIRjmLR-JW4sTCslD24h5fs0sTUpGYBG7zh4Z_UyEZ_u29NojdH2dSNKQZwwgjl1WvfYNtBCCF_EnYTazAXmDQ' - ) - } else { - t.assert.throws(() => sign({ a: 1 }, { noTimestamp: true, key: privateKeys.Ed25519 }), { - message: 'Cannot create the signature.', - originalError: { - message: 'EdDSA algorithms are not supported by your Node.js version.' - } - }) - } + t.assert.equal( + sign({ a: 1 }, { noTimestamp: true, key: privateKeys.Ed25519 }), + 'eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJhIjoxfQ.pIRjmLR-JW4sTCslD24h5fs0sTUpGYBG7zh4Z_UyEZ_u29NojdH2dSNKQZwwgjl1WvfYNtBCCF_EnYTazAXmDQ' + ) }) test('it correctly returns a token - async - key with callback', async t => { @@ -127,92 +117,47 @@ test('it correctly returns a token - callback - key as promise', t => { test('it correctly returns a token - key as an RSA X509 key', async t => { const payload = { a: 1 } - if (useNewCrypto) { - const signedToken = sign(payload, { key: privateKeys.RSX509, noTimestamp: true }) - const decoder = createDecoder() - const result = decoder(signedToken) - - t.assert.equal(payload.a, result.a) - } else { - t.assert.throws(() => sign(payload, { key: privateKeys.RSX509 }), { - message: 'Cannot create the signature.', - originalError: { - message: 'The "key" argument must be one of type string, Buffer, TypedArray, or DataView. Received type object' - } - }) - } + const signedToken = sign(payload, { key: privateKeys.RSX509, noTimestamp: true }) + const decoder = createDecoder() + const result = decoder(signedToken) + + t.assert.equal(payload.a, result.a) }) test('it correctly returns a token - key as an RSA passphrase protected key', async t => { const payload = { a: 1 } - if (useNewCrypto) { - const signedToken = sign(payload, { algorithm: 'RS256', key: { key: privateKeys.PPRS, passphrase: 'secret' } }) - const decoder = createDecoder() - const result = decoder(signedToken) - - t.assert.equal(payload.a, result.a) - } else { - t.assert.throws(() => sign(payload, { key: { key: privateKeys.PPRS, passphrase: 'secret' } }), { - message: 'Cannot create the signature.', - originalError: { - message: 'The "key" argument must be one of type string, Buffer, TypedArray, or DataView. Received type object' - } - }) - } + const signedToken = sign(payload, { algorithm: 'RS256', key: { key: privateKeys.PPRS, passphrase: 'secret' } }) + const decoder = createDecoder() + const result = decoder(signedToken) + + t.assert.equal(payload.a, result.a) }) test('it correctly returns a token - key as an ES256 passphrase protected key', async t => { const payload = { a: 1 } - if (useNewCrypto) { - const signedToken = sign(payload, { algorithm: 'ES256', key: { key: privateKeys.PPES256, passphrase: 'secret' } }) - const decoder = createDecoder() - const result = decoder(signedToken) - - t.assert.equal(payload.a, result.a) - } else { - t.assert.throws(() => sign(payload, { key: { key: privateKeys.PPES256, passphrase: 'secret' } }), { - message: 'Cannot create the signature.', - originalError: { - message: 'The "key" argument must be one of type string, Buffer, TypedArray, or DataView. Received type object' - } - }) - } + const signedToken = sign(payload, { algorithm: 'ES256', key: { key: privateKeys.PPES256, passphrase: 'secret' } }) + const decoder = createDecoder() + const result = decoder(signedToken) + + t.assert.equal(payload.a, result.a) }) test('it correctly returns a token - key as an ES384 passphrase protected key', async t => { const payload = { a: 1 } - if (useNewCrypto) { - const signedToken = sign(payload, { algorithm: 'ES384', key: { key: privateKeys.PPES384, passphrase: 'secret' } }) - const decoder = createDecoder() - const result = decoder(signedToken) - - t.assert.equal(payload.a, result.a) - } else { - t.assert.throws(() => sign(payload, { key: { key: privateKeys.PPES384, passphrase: 'secret' } }), { - message: 'Cannot create the signature.', - originalError: { - message: 'The "key" argument must be one of type string, Buffer, TypedArray, or DataView. Received type object' - } - }) - } + const signedToken = sign(payload, { algorithm: 'ES384', key: { key: privateKeys.PPES384, passphrase: 'secret' } }) + const decoder = createDecoder() + const result = decoder(signedToken) + + t.assert.equal(payload.a, result.a) }) test('it correctly returns a token - key as an ES512 passphrase protected key', async t => { const payload = { a: 1 } - if (useNewCrypto) { - const signedToken = sign(payload, { algorithm: 'ES512', key: { key: privateKeys.PPES512, passphrase: 'secret' } }) - const decoder = createDecoder() - const result = decoder(signedToken) - - t.assert.equal(payload.a, result.a) - } else { - t.assert.throws(() => sign(payload, { key: { key: privateKeys.PPES512, passphrase: 'secret' } }), { - message: 'Cannot create the signature.', - originalError: { - message: 'The "key" argument must be one of type string, Buffer, TypedArray, or DataView. Received type object' - } - }) - } + const signedToken = sign(payload, { algorithm: 'ES512', key: { key: privateKeys.PPES512, passphrase: 'secret' } }) + const decoder = createDecoder() + const result = decoder(signedToken) + + t.assert.equal(payload.a, result.a) }) test('it correctly returns an error when algorithm is not provided when using passphrase protected key', async t => { @@ -273,31 +218,29 @@ test('it correctly autodetects the algorithm depending on the secret provided', verification = es512Verifier(token) t.assert.equal(verification.header.alg, 'ES512') - if (useNewCrypto) { - token = createSigner({ algorithm: 'RS256', key: { key: privateKeys.PPRS, passphrase: 'secret' } })({ a: 1 }) - verification = pprsVerifier(token) - t.assert.equal(verification.header.alg, 'RS256') + token = createSigner({ algorithm: 'RS256', key: { key: privateKeys.PPRS, passphrase: 'secret' } })({ a: 1 }) + verification = pprsVerifier(token) + t.assert.equal(verification.header.alg, 'RS256') - token = createSigner({ algorithm: 'ES256', key: { key: privateKeys.PPES256, passphrase: 'secret' } })({ a: 1 }) - verification = ppes256Verifier(token) - t.assert.equal(verification.header.alg, 'ES256') + token = createSigner({ algorithm: 'ES256', key: { key: privateKeys.PPES256, passphrase: 'secret' } })({ a: 1 }) + verification = ppes256Verifier(token) + t.assert.equal(verification.header.alg, 'ES256') - token = createSigner({ algorithm: 'ES384', key: { key: privateKeys.PPES384, passphrase: 'secret' } })({ a: 1 }) - verification = ppes384Verifier(token) - t.assert.equal(verification.header.alg, 'ES384') + token = createSigner({ algorithm: 'ES384', key: { key: privateKeys.PPES384, passphrase: 'secret' } })({ a: 1 }) + verification = ppes384Verifier(token) + t.assert.equal(verification.header.alg, 'ES384') - token = createSigner({ algorithm: 'ES512', key: { key: privateKeys.PPES512, passphrase: 'secret' } })({ a: 1 }) - verification = ppes512Verifier(token) - t.assert.equal(verification.header.alg, 'ES512') + token = createSigner({ algorithm: 'ES512', key: { key: privateKeys.PPES512, passphrase: 'secret' } })({ a: 1 }) + verification = ppes512Verifier(token) + t.assert.equal(verification.header.alg, 'ES512') - token = createSigner({ key: privateKeys.Ed25519 })({ a: 1 }) - verification = es25519Verifier(token) - t.assert.equal(verification.header.alg, 'EdDSA') + token = createSigner({ key: privateKeys.Ed25519 })({ a: 1 }) + verification = es25519Verifier(token) + t.assert.equal(verification.header.alg, 'EdDSA') - token = createSigner({ key: privateKeys.Ed448 })({ a: 1 }) - verification = es448Verifier(token) - t.assert.equal(verification.header.alg, 'EdDSA') - } + token = createSigner({ key: privateKeys.Ed448 })({ a: 1 }) + verification = es448Verifier(token) + t.assert.equal(verification.header.alg, 'EdDSA') }) test('it correctly set a timestamp', async t => { diff --git a/test/verifier.spec.js b/test/verifier.spec.js index b365d8a..ec9aa15 100644 --- a/test/verifier.spec.js +++ b/test/verifier.spec.js @@ -7,7 +7,6 @@ const { test } = require('node:test') const { install: fakeTime } = require('@sinonjs/fake-timers') const { createSigner, createVerifier, TokenError } = require('../src') -const { useNewCrypto } = require('../src/crypto') const { hashToken } = require('../src/utils') const privateKeys = { @@ -120,29 +119,13 @@ test('it correctly verifies a token - sync', t => { } ) - if (useNewCrypto) { - t.assert.deepStrictEqual( - verify( - 'eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkifQ.eyJhIjoxfQ.n4isU7JqaKRVOyx2ni7b_iaAzB75pAUCW6CetcoClhtJ5yDM7YkNMbKqmDUhTKMpupAcztIjX8m4mZwpA33HAA', - { key: publicKeys.Ed25519 } - ), - { a: 1 } - ) - } else { - t.assert.throws( - () => - verify( - 'eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkifQ.eyJhIjoxfQ.n4isU7JqaKRVOyx2ni7b_iaAzB75pAUCW6CetcoClhtJ5yDM7YkNMbKqmDUhTKMpupAcztIjX8m4mZwpA33HAA', - { key: publicKeys.Ed25519 } - ), - { - message: 'Cannot verify the signature.', - originalError: { - message: 'EdDSA algorithms are not supported by your Node.js version.' - } - } - ) - } + t.assert.deepStrictEqual( + verify( + 'eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkifQ.eyJhIjoxfQ.n4isU7JqaKRVOyx2ni7b_iaAzB75pAUCW6CetcoClhtJ5yDM7YkNMbKqmDUhTKMpupAcztIjX8m4mZwpA33HAA', + { key: publicKeys.Ed25519 } + ), + { a: 1 } + ) }) test('it correctly verifies a token - async - key with callback', async t => { @@ -1062,34 +1045,32 @@ for (const type of ['HS', 'ES', 'RS', 'PS']) { } } -if (useNewCrypto) { - test('caching - should use the right hash method for storing values - EdDSA with Ed25519', t => { - const signer = createSigner({ algorithm: 'EdDSA', key: privateKeys.Ed25519, noTimestamp: 1 }) - const verifier = createVerifier({ key: publicKeys.Ed25519, cache: true }) - const token = signer({ a: 1 }) - const hash = createHash('sha512').update(token).digest('hex') - - t.assert.deepStrictEqual(verifier(token), { a: 1 }) - t.assert.equal(verifier.cache.size, 1) - t.assert.equal(Array.from(verifier.cache.keys())[0], hash) - }) +test('caching - should use the right hash method for storing values - EdDSA with Ed25519', t => { + const signer = createSigner({ algorithm: 'EdDSA', key: privateKeys.Ed25519, noTimestamp: 1 }) + const verifier = createVerifier({ key: publicKeys.Ed25519, cache: true }) + const token = signer({ a: 1 }) + const hash = createHash('sha512').update(token).digest('hex') - test('caching - should use the right hash method for storing values - EdDSA with Ed448', t => { - const signer = createSigner({ - algorithm: 'EdDSA', - key: privateKeys.Ed448, - noTimestamp: 1, - header: { crv: 'Ed448' } - }) - const verifier = createVerifier({ key: publicKeys.Ed448, cache: true }) - const token = signer({ a: 1 }) - const hash = createHash('shake256', { outputLength: 114 }).update(token).digest('hex') + t.assert.deepStrictEqual(verifier(token), { a: 1 }) + t.assert.equal(verifier.cache.size, 1) + t.assert.equal(Array.from(verifier.cache.keys())[0], hash) +}) - t.assert.deepStrictEqual(verifier(token), { a: 1 }) - t.assert.equal(verifier.cache.size, 1) - t.assert.equal(Array.from(verifier.cache.keys())[0], hash) +test('caching - should use the right hash method for storing values - EdDSA with Ed448', t => { + const signer = createSigner({ + algorithm: 'EdDSA', + key: privateKeys.Ed448, + noTimestamp: 1, + header: { crv: 'Ed448' } }) -} + const verifier = createVerifier({ key: publicKeys.Ed448, cache: true }) + const token = signer({ a: 1 }) + const hash = createHash('shake256', { outputLength: 114 }).update(token).digest('hex') + + t.assert.deepStrictEqual(verifier(token), { a: 1 }) + t.assert.equal(verifier.cache.size, 1) + t.assert.equal(Array.from(verifier.cache.keys())[0], hash) +}) test('caching - should be able to manipulate cache directy', t => { const clock = fakeTime({ now: 100000 }) From e44f0635f44460e3048644946dd16e0f0619f2e6 Mon Sep 17 00:00:00 2001 From: Matteo Pietro Dazzi Date: Wed, 13 Nov 2024 22:46:22 +0100 Subject: [PATCH 02/11] chore: default clockTimestamp --- src/verifier.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/verifier.js b/src/verifier.js index 2843345..c61742b 100644 --- a/src/verifier.js +++ b/src/verifier.js @@ -72,7 +72,7 @@ function cacheSet( ignoreExpiration, ignoreNotBefore, maxAge, - clockTimestamp, + clockTimestamp = Date.now(), clockTolerance, errorCacheTTL }, @@ -86,7 +86,7 @@ function cacheSet( if (value instanceof TokenError) { const ttl = typeof errorCacheTTL === 'function' ? errorCacheTTL(value) : errorCacheTTL - cacheValue[2] = (clockTimestamp || Date.now()) + clockTolerance + ttl + cacheValue[2] = clockTimestamp + clockTolerance + ttl cache.set(hashToken(token), cacheValue) return value } @@ -107,7 +107,7 @@ function cacheSet( } // The maximum TTL for the token cannot exceed the configured cacheTTL - const maxTTL = (clockTimestamp || Date.now()) + clockTolerance + cacheTTL + const maxTTL = clockTimestamp + clockTolerance + cacheTTL cacheValue[2] = cacheValue[2] === 0 ? maxTTL : Math.min(cacheValue[2], maxTTL) cache.set(hashToken(token), cacheValue) From 3cc3d0eb3c2c0546cc94819d9fddac80bbc56763 Mon Sep 17 00:00:00 2001 From: Matteo Pietro Dazzi Date: Wed, 13 Nov 2024 22:49:02 +0100 Subject: [PATCH 03/11] chore: avoid re-assign --- src/verifier.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/verifier.js b/src/verifier.js index c61742b..75d0f9a 100644 --- a/src/verifier.js +++ b/src/verifier.js @@ -135,10 +135,8 @@ function handleCachedResult(cached, callback, promise) { function validateAlgorithmAndSignature(input, header, signature, key, allowedAlgorithms) { // According to the signature and key, check with algorithms are supported - const algorithms = allowedAlgorithms - // Verify the token is allowed - if (!algorithms.includes(header.alg)) { + if (!allowedAlgorithms.includes(header.alg)) { throw new TokenError(TokenError.codes.invalidAlgorithm, 'The token algorithm is invalid.') } From 0e211ae9abb500f8508b92bfb18f31686cda20f7 Mon Sep 17 00:00:00 2001 From: Matteo Pietro Dazzi Date: Wed, 13 Nov 2024 22:50:20 +0100 Subject: [PATCH 04/11] chore: removed unused value --- src/verifier.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/verifier.js b/src/verifier.js index 75d0f9a..aa191f0 100644 --- a/src/verifier.js +++ b/src/verifier.js @@ -178,7 +178,7 @@ function validateClaimDateValue(value, modifier, now, greater, errorCode, errorV function verifyToken( key, { input, header, payload, signature }, - { validators, allowedAlgorithms, checkTyp, clockTimestamp, clockTolerance, requiredClaims } + { validators, allowedAlgorithms, checkTyp, clockTimestamp, requiredClaims } ) { // Verify the key /* istanbul ignore next */ From b7289ce23a9dab317501b91053693e5f8756eac1 Mon Sep 17 00:00:00 2001 From: Matteo Pietro Dazzi Date: Wed, 13 Nov 2024 22:52:42 +0100 Subject: [PATCH 05/11] fix: removed valid var --- src/verifier.js | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/verifier.js b/src/verifier.js index aa191f0..849d003 100644 --- a/src/verifier.js +++ b/src/verifier.js @@ -15,23 +15,17 @@ function exactStringClaimMatcher(allowed, actual) { } function checkAreCompatibleAlgorithms(expected, actual) { - let valid = false - for (const expectedAlg of expected) { - valid = actual.indexOf(expectedAlg) !== -1 - // if at least one of the expected algorithms is compatible we're done - if (valid) { - break + if (actual.includes(expectedAlg)) { + return } } - if (!valid) { - throw new TokenError( - TokenError.codes.invalidKey, - `Invalid public key provided for algorithms ${expected.join(', ')}.` - ) - } + throw new TokenError( + TokenError.codes.invalidKey, + `Invalid public key provided for algorithms ${expected.join(', ')}.` + ) } function prepareKeyOrSecret(key, isSecret) { From e895a87eaea7c9a10bf743126311e21071a54dd2 Mon Sep 17 00:00:00 2001 From: Matteo Pietro Dazzi Date: Wed, 13 Nov 2024 22:55:54 +0100 Subject: [PATCH 06/11] chore: unique if --- src/verifier.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/verifier.js b/src/verifier.js index 849d003..4405bb1 100644 --- a/src/verifier.js +++ b/src/verifier.js @@ -187,10 +187,8 @@ function verifyToken( validateAlgorithmAndSignature(input, header, signature, key, allowedAlgorithms) // Verify typ - if (checkTyp) { - if (typeof header.typ !== 'string' || checkTyp !== header.typ.toLowerCase().replace(/^application\//, '')) { - throw new TokenError(TokenError.codes.invalidType, 'Invalid typ.') - } + if (checkTyp && (typeof header.typ !== 'string' || checkTyp !== header.typ.toLowerCase().replace(/^application\//, ''))) { + throw new TokenError(TokenError.codes.invalidType, 'Invalid typ.') } // Verify the payload From 087cba1a1feb4195dd1b55fe92bd6a97a5516b7f Mon Sep 17 00:00:00 2001 From: Matteo Pietro Dazzi Date: Wed, 13 Nov 2024 22:57:19 +0100 Subject: [PATCH 07/11] chore: move now closer to where is used --- src/verifier.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/verifier.js b/src/verifier.js index 4405bb1..a506d5b 100644 --- a/src/verifier.js +++ b/src/verifier.js @@ -191,9 +191,6 @@ function verifyToken( throw new TokenError(TokenError.codes.invalidType, 'Invalid typ.') } - // Verify the payload - const now = clockTimestamp || Date.now() - if (requiredClaims) { for (const claim of requiredClaims) { if (!(claim in payload)) { @@ -202,6 +199,9 @@ function verifyToken( } } + // Verify the payload + const now = clockTimestamp || Date.now() + for (const validator of validators) { const { type, claim, allowed, array, modifier, greater, errorCode, errorVerb } = validator const value = payload[claim] From 9a481fcc68e7bc42ee6828442a0494a885abf237 Mon Sep 17 00:00:00 2001 From: Matteo Pietro Dazzi Date: Wed, 13 Nov 2024 22:58:23 +0100 Subject: [PATCH 08/11] chore: better use of destructuring --- src/verifier.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/verifier.js b/src/verifier.js index a506d5b..256d52d 100644 --- a/src/verifier.js +++ b/src/verifier.js @@ -202,8 +202,7 @@ function verifyToken( // Verify the payload const now = clockTimestamp || Date.now() - for (const validator of validators) { - const { type, claim, allowed, array, modifier, greater, errorCode, errorVerb } = validator + for (const { type, claim, allowed, array, modifier, greater, errorCode, errorVerb } of validators) { const value = payload[claim] const arrayValue = Array.isArray(value) const values = arrayValue ? value : [value] From 0ab5fec7123af9695267b6f9bec8d0240ae35765 Mon Sep 17 00:00:00 2001 From: Matteo Pietro Dazzi Date: Wed, 13 Nov 2024 23:01:52 +0100 Subject: [PATCH 09/11] chore: move cache context object where needed --- src/verifier.js | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/verifier.js b/src/verifier.js index 256d52d..0849943 100644 --- a/src/verifier.js +++ b/src/verifier.js @@ -248,19 +248,6 @@ function verify( ) { const [callback, promise] = isAsync ? ensurePromiseCallback(cb) : [] - const cacheContext = { - cache, - token, - cacheTTL, - errorCacheTTL, - payload: undefined, - ignoreExpiration, - ignoreNotBefore, - maxAge, - clockTimestamp, - clockTolerance - } - // Check the cache if (cache) { const [value, min, max] = cache.get(hashToken(token)) || [undefined, 0, 0] @@ -297,7 +284,18 @@ function verify( } const { header, payload, signature } = decoded - cacheContext.payload = payload + const cacheContext = { + cache, + token, + cacheTTL, + errorCacheTTL, + ignoreExpiration, + ignoreNotBefore, + maxAge, + clockTimestamp, + clockTolerance, + payload + } const validationContext = { validators, allowedAlgorithms, checkTyp, clockTimestamp, clockTolerance, requiredClaims } // We have the key From 47d2e3d06e4ba425516800fd6aabba19821e29e5 Mon Sep 17 00:00:00 2001 From: Matteo Pietro Dazzi Date: Wed, 13 Nov 2024 23:04:21 +0100 Subject: [PATCH 10/11] chore: numeric separator --- src/verifier.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/verifier.js b/src/verifier.js index 0849943..f7d36fb 100644 --- a/src/verifier.js +++ b/src/verifier.js @@ -374,7 +374,7 @@ module.exports = function createVerifier(options) { allowedSub, allowedNonce, requiredClaims - } = { cacheTTL: 600000, clockTolerance: 0, errorCacheTTL: -1, ...options } + } = { cacheTTL: 600_000, clockTolerance: 0, errorCacheTTL: -1, ...options } // Validate options if (!Array.isArray(allowedAlgorithms)) { From 6738ab416fff9f3a8d7ec49bfd7cca6f41d7fc0f Mon Sep 17 00:00:00 2001 From: Matteo Pietro Dazzi Date: Wed, 13 Nov 2024 23:05:17 +0100 Subject: [PATCH 11/11] fix: prefer const --- src/verifier.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/verifier.js b/src/verifier.js index f7d36fb..a6b0823 100644 --- a/src/verifier.js +++ b/src/verifier.js @@ -476,10 +476,7 @@ module.exports = function createVerifier(options) { validators.push({ type: 'string', claim: 'nonce', allowed: ensureStringClaimMatcher(allowedNonce) }) } - let normalizedTyp = null - if (checkTyp) { - normalizedTyp = checkTyp.toLowerCase().replace(/^application\//, '') - } + const normalizedTyp = checkTyp ? checkTyp.toLowerCase().replace(/^application\//, '') : null const context = { key,