mirror of
https://github.com/hanzoai/jose.git
synced 2026-09-24 17:00:01 +00:00
Resolve JWKS and embedded-JWK imports against the JWS registry and import with jwkToKey. Use registry metadata for candidate filtering and apply RSA oth rejection in the shared import path. Signed-off-by: Filip Skokan <[email protected]>
93 lines
3.0 KiB
TypeScript
93 lines
3.0 KiB
TypeScript
import test from 'ava'
|
||
|
||
import {
|
||
importJWK,
|
||
EmbeddedJWK,
|
||
FlattenedSign,
|
||
flattenedVerify,
|
||
exportJWK,
|
||
generateKeyPair,
|
||
} from '../../src/index.js'
|
||
|
||
const mlDsaTest = SubtleCrypto.supports?.('generateKey', 'ML-DSA-65') === true ? test : test.skip
|
||
|
||
function pubjwk(jwk) {
|
||
const { d, p, q, dp, dq, qi, ext, alg, ...publicJwk } = jwk
|
||
return publicJwk
|
||
}
|
||
|
||
test.before(async (t) => {
|
||
const encode = TextEncoder.prototype.encode.bind(new TextEncoder())
|
||
t.context.key = {
|
||
crv: 'P-256',
|
||
alg: 'ES256',
|
||
ext: false,
|
||
x: 'Sp3KpzPjwcCF04_W2GvSSf-vGDvp3Iv2kQYqAjnMB-Y',
|
||
y: 'lZmecT2quXe0i9f7b4qHvDAFDpxs0oxCoJx4tOOqsks',
|
||
d: 'hRVo5TGE_d_4tQC1KEQIlCdo9rteZmLSmaMPpFOjeDI',
|
||
kty: 'EC',
|
||
}
|
||
|
||
const privateKey = await importJWK(t.context.key)
|
||
t.context.token = await new FlattenedSign(
|
||
encode('It’s a dangerous business, Frodo, going out your door.'),
|
||
)
|
||
.setProtectedHeader({ alg: 'ES256', jwk: pubjwk(t.context.key) })
|
||
.sign(privateKey)
|
||
t.context.tokenMissingJwk = await new FlattenedSign(
|
||
encode('It’s a dangerous business, Frodo, going out your door.'),
|
||
)
|
||
.setProtectedHeader({ alg: 'ES256' })
|
||
.sign(privateKey)
|
||
t.context.tokenInvalidJWK = await new FlattenedSign(
|
||
encode('It’s a dangerous business, Frodo, going out your door.'),
|
||
)
|
||
.setProtectedHeader({ alg: 'ES256', jwk: null })
|
||
.sign(privateKey)
|
||
t.context.tokenPrivateJWK = await new FlattenedSign(
|
||
encode('It’s a dangerous business, Frodo, going out your door.'),
|
||
)
|
||
.setProtectedHeader({ alg: 'ES256', jwk: t.context.key })
|
||
.sign(privateKey)
|
||
})
|
||
|
||
test('EmbeddedJWK', async (t) => {
|
||
await t.notThrowsAsync(async () => {
|
||
const { key: resolvedKey } = await flattenedVerify(t.context.token, EmbeddedJWK)
|
||
t.truthy(resolvedKey)
|
||
t.is(resolvedKey.type, 'public')
|
||
})
|
||
})
|
||
|
||
test('EmbeddedJWK requires "jwk" to be an object', async (t) => {
|
||
await t.throwsAsync(flattenedVerify(t.context.tokenMissingJwk, EmbeddedJWK), {
|
||
code: 'ERR_JWS_INVALID',
|
||
message: '"jwk" (JSON Web Key) Header Parameter must be a JSON object',
|
||
})
|
||
await t.throwsAsync(flattenedVerify(t.context.tokenInvalidJWK, EmbeddedJWK), {
|
||
code: 'ERR_JWS_INVALID',
|
||
message: '"jwk" (JSON Web Key) Header Parameter must be a JSON object',
|
||
})
|
||
})
|
||
|
||
test('EmbeddedJWK requires "jwk" to be a public one', async (t) => {
|
||
await t.throwsAsync(flattenedVerify(t.context.tokenPrivateJWK, EmbeddedJWK), {
|
||
code: 'ERR_JWS_INVALID',
|
||
message: '"jwk" (JSON Web Key) Header Parameter must be a public key',
|
||
})
|
||
})
|
||
|
||
mlDsaTest('EmbeddedJWK defers AKP "alg" validation to WebCrypto', async (t) => {
|
||
const { publicKey } = await generateKeyPair('ML-DSA-65', { extractable: true })
|
||
const { alg, ...jwk } = await exportJWK(publicKey)
|
||
|
||
for (const protectedHeader of [
|
||
{ alg: 'ML-DSA-65', jwk },
|
||
{ alg: 'ML-DSA-44', jwk: { ...jwk, alg } },
|
||
]) {
|
||
const error = await t.throwsAsync(EmbeddedJWK(protectedHeader))
|
||
t.true(error instanceof DOMException)
|
||
t.is(error.name, 'DataError')
|
||
}
|
||
})
|