Files
jose/test/jwk/key_input.test.ts
Filip Skokan 98996e04f4 refactor: fold single-consumer modules into their consumers
Move key-management algorithms into key_management.ts, key export
conversion into key/export.ts, and key validation and conversion into
key.ts. Freeze mutable asymmetric JWK inputs and key_ops before cached
import.

Signed-off-by: Filip Skokan <[email protected]>
2026-07-29 18:52:03 +02:00

60 lines
2.0 KiB
TypeScript

import test from 'ava'
import { CompactSign, compactVerify, exportJWK, generateKeyPair } from '../../src/index.js'
const payload = new TextEncoder().encode('You step into the Road, and if you do not keep your feet')
test.serial('JWK inputs are frozen before import and cached afterward', async (t) => {
const { privateKey, publicKey } = await generateKeyPair('ES256', { extractable: true })
const jwk = await exportJWK(privateKey)
jwk.key_ops = ['sign']
const subtle = crypto.subtle
const descriptor = Object.getOwnPropertyDescriptor(subtle, 'importKey')
const importKey = subtle.importKey
let imports = 0
let release!: () => void
const blocked = new Promise<void>((resolve) => {
release = resolve
})
Object.defineProperty(subtle, 'importKey', {
configurable: true,
async value(...args: Parameters<SubtleCrypto['importKey']>) {
if (args[0] === 'jwk') {
imports++
await blocked
}
return Reflect.apply(importKey, subtle, args)
},
})
let firstPending: Promise<string> | undefined
try {
// The cache key must become immutable before the import promise can yield.
firstPending = new CompactSign(payload).setProtectedHeader({ alg: 'ES256' }).sign(jwk)
t.is(imports, 1)
t.true(Object.isFrozen(jwk))
t.true(Object.isFrozen(jwk.key_ops))
t.throws(() => Object.assign(jwk, { kid: 'changed' }), { instanceOf: TypeError })
t.throws(() => jwk.key_ops!.push('verify'), { instanceOf: TypeError })
release()
const first = await firstPending
const second = await new CompactSign(payload).setProtectedHeader({ alg: 'ES256' }).sign(jwk)
t.is(imports, 1)
await t.notThrowsAsync(compactVerify(first, publicKey))
await t.notThrowsAsync(compactVerify(second, publicKey))
} finally {
release()
await firstPending?.catch(() => {})
if (descriptor) {
Object.defineProperty(subtle, 'importKey', descriptor)
} else {
Reflect.deleteProperty(subtle, 'importKey')
}
}
})