Skip to content

Commit

Permalink
feat: deprecate use new crypto (#508)
Browse files Browse the repository at this point in the history
  • Loading branch information
ilteoood authored Nov 14, 2024
1 parent 72762fd commit 5844024
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 265 deletions.
18 changes: 1 addition & 17 deletions src/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = { '=': '', '+': '-', '/': '_' }

Expand All @@ -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(),
Expand Down Expand Up @@ -351,7 +336,6 @@ function verifySignature(algorithm, key, input, signature) {
}

module.exports = {
useNewCrypto,
base64UrlMatcher,
base64UrlReplacer,
hsAlgorithms,
Expand Down
9 changes: 1 addition & 8 deletions src/signer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
const {
base64UrlMatcher,
base64UrlReplacer,
useNewCrypto,
hsAlgorithms,
esAlgorithms,
rsaAlgorithms,
Expand Down Expand Up @@ -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(
Expand Down
10 changes: 2 additions & 8 deletions src/verifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -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) {
Expand Down
35 changes: 16 additions & 19 deletions test/compatibility.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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 })
})
}
43 changes: 20 additions & 23 deletions test/compliance.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand Down Expand Up @@ -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)
})
71 changes: 34 additions & 37 deletions test/crypto.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ const { resolve } = require('node:path')

const { createVerifier, createSigner } = require('../src')
const {
useNewCrypto,
hsAlgorithms,
rsaAlgorithms,
detectPrivateKeyAlgorithm,
Expand Down Expand Up @@ -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.'
})
}
})
}
Loading

0 comments on commit 5844024

Please sign in to comment.