fix: skip public key detection for HSxxx algorithms#576
fix: skip public key detection for HSxxx algorithms#576hf wants to merge 7 commits intonearform:masterfrom
Conversation
|
Additionally, it would be great to have a test for this. |
|
@hf can you please add tests? |
Co-authored-by: Matteo Pietro Dazzi <matteopietro.dazzi@gmail.com>
|
Added a test. Tried to fix this for the verifier also, but decided not to. I'm new to this codebase and it's hard to find my way around it. |
There was a problem hiding this comment.
Pull request overview
This PR fixes an issue where HMAC secrets (used with HS256/HS384/HS512 algorithms) that contain patterns resembling PEM-encoded public keys would incorrectly trigger a "Public keys are not supported for signing" error. The fix adds an early return in the private key detection logic to skip public key validation when an HS algorithm is explicitly provided.
Key changes:
- Modified
performDetectPrivateKeyAlgorithmto bypass public key detection when the provided algorithm starts with 'H' - Added test to verify public key patterns in HMAC secrets are accepted when HS256 is specified
- Updated existing HS algorithm tests to use a shared key constant from
privateKeys.HS
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/crypto.js | Added early return in performDetectPrivateKeyAlgorithm to skip public key validation for algorithms starting with 'H', preventing false rejection of HMAC secrets |
| test/crypto.spec.js | Added test case verifying HMAC secrets with public key patterns are accepted, and standardized key usage in HS algorithm round-trip tests |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@SociableSteve can you take a look? |
|
@hf Many thanks for this and apologies for the delay getting to this over the holiday period. If you can update to cover HS384 and HS512 tests for completeness, and restrict the check to the defined algorithms in case of future clashes (pretty much as CoPilot has suggested) then we will get this merged in. |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
Sorry about the delay, it's now ready. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| test('detectPrivateKeyAlgorithm - public keys should be accepted if HS256, HS384, HS512 is used', t => { | ||
| ;['HS256', 'HS384', 'HS512'].forEach(algorithm => { | ||
| t.assert.equal(detectPrivateKeyAlgorithm(`-----BEGIN PUBLIC KEY-----\nUSED IN ${algorithm}`, algorithm), algorithm) | ||
| }) | ||
| }) |
There was a problem hiding this comment.
This test doesn't adequately test the caching behavior with the new changes. Each iteration uses a different key string (by including the algorithm in the key), so it doesn't test whether the same key can be correctly used with different HS algorithms.
Consider adding a test that calls detectPrivateKeyAlgorithm with the same key but different algorithms to ensure the function returns the correct algorithm for each call. For example:
- Call with a key and HS256
- Call with the same key and HS384
- Verify both calls return the correct algorithm respectively
| const hsAlgorithm = `HS${bits}` | ||
| const key = privateKeys.HS | ||
|
|
||
| // HS256, HS512, HS512 |
There was a problem hiding this comment.
The comment has a typo: "HS256, HS512, HS512" should be "HS256, HS384, HS512" to correctly reflect the three hash algorithms being tested.
| // HS256, HS512, HS512 | |
| // HS256, HS384, HS512 |
|
It looks like this fix works for signing tokens but not for verifying them. Simple strings work in both cases, but a token formatted like the following doesn't work when creating a verifier: The code used to test was: const { createSigner, createVerifier } = require('fast-jwt')
// This is a valid HMAC secret - it just happens to look like a PEM public key.
// This can occur when secrets are base64-encoded or generated from key material.
const secret = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo
4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u
-----END PUBLIC KEY-----`
// Signing works (fixed by PR #576)
const signer = createSigner({ algorithm: 'HS256', key: secret })
const token = signer({ userId: 123, role: 'admin' })
console.log('Token created:', token)
// Verification fails - even though we explicitly specify HS256
const verifier = createVerifier({ algorithms: ['HS256'], key: secret })
const payload = verifier(token)
console.log('Payload:', payload)Verification uses the public key methods for validation of the key. function: https://github.com/hf/fast-jwt/blob/bafb8f42aaeeb30fcb66960224e4722a34d5dba0/src/verifier.js#L367 function: https://github.com/hf/fast-jwt/blob/bafb8f42aaeeb30fcb66960224e4722a34d5dba0/src/crypto.js#L209 function: https://github.com/hf/fast-jwt/blob/bafb8f42aaeeb30fcb66960224e4722a34d5dba0/src/crypto.js#L131 This needs the same fix applying that you have in the private key checker to ensure that generated tokens can be verified. There is an additional complexity that the algorithms is an array instead of a single entry since the library can be used to verify against multiple different signing keys in a single call. |
SociableSteve
left a comment
There was a problem hiding this comment.
As per previous comment
We use fast-jwt at Supabase and are very grateful for this library.
We ran into a problem recently where some HS256 keys used to sign JWTs look quite a lot like a public/private key. The library ignores the
algorithm: 'HS256'indication and errors out with:Instead, it should not try to parse the string if the developer has indicated the string or buffer is a HMAC secret only, regardless of what it contains.
Fixes #577