Files
jose/test/jwe/compact.encrypt.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

146 lines
4.5 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import test from 'ava'
import * as crypto from 'node:crypto'
import { CompactEncrypt, compactDecrypt } from '../../src/index.js'
const rsa = crypto.generateKeyPairSync('rsa', { modulusLength: 2048 })
const keyObjectTest = typeof rsa.publicKey.toCryptoKey === 'function' ? test : test.skip
test.before(async (t) => {
const encode = TextEncoder.prototype.encode.bind(new TextEncoder())
t.context.plaintext = encode('It’s a dangerous business, Frodo, going out your door.')
t.context.initializationVector = new Uint8Array(12)
t.context.secret = new Uint8Array(16)
})
test('CompactEncrypt', async (t) => {
const jwe = await new CompactEncrypt(t.context.plaintext)
.setInitializationVector(t.context.initializationVector)
.setProtectedHeader({ alg: 'dir', enc: 'A128GCM' })
.encrypt(t.context.secret)
t.deepEqual(
jwe,
'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIn0..AAAAAAAAAAAAAAAA.Svw4TvnFg_PTTKPXFteMF4Lmisk8ODBNko7607TNs49EbT0BKRz9tEep2dmks9KPvD-CfX7hW1M.Y5cdeOSFYNyxcPWQlrVFzw',
)
})
test('CompactEncrypt.prototype.setProtectedHeader', (t) => {
t.throws(
() => new CompactEncrypt(t.context.plaintext).setProtectedHeader({}).setProtectedHeader({}),
{
instanceOf: TypeError,
message: 'setProtectedHeader can only be called once',
},
)
})
test('CompactEncrypt.prototype.setKeyManagementParameters', (t) => {
t.throws(
() =>
new CompactEncrypt(t.context.plaintext)
.setKeyManagementParameters({})
.setKeyManagementParameters({}),
{
instanceOf: TypeError,
message: 'setKeyManagementParameters can only be called once',
},
)
})
test('CompactEncrypt.prototype.setInitializationVector', (t) => {
t.throws(
() =>
new CompactEncrypt(t.context.plaintext)
.setInitializationVector(t.context.initializationVector)
.setInitializationVector(t.context.initializationVector),
{
instanceOf: TypeError,
message: 'setInitializationVector can only be called once',
},
)
})
test('CompactEncrypt.prototype.setContentEncryptionKey', (t) => {
t.throws(
() =>
new CompactEncrypt(t.context.plaintext)
.setContentEncryptionKey(t.context.secret)
.setContentEncryptionKey(t.context.secret),
{
instanceOf: TypeError,
message: 'setContentEncryptionKey can only be called once',
},
)
})
test('CompactEncrypt.prototype.encrypt must have a JOSE header', async (t) => {
await t.throwsAsync(new CompactEncrypt(t.context.plaintext).encrypt(t.context.secret), {
code: 'ERR_JWE_INVALID',
message:
'either setProtectedHeader, setUnprotectedHeader, or sharedUnprotectedHeader must be called before #encrypt()',
})
})
test('CompactEncrypt.prototype.encrypt JOSE header have an alg', async (t) => {
await t.throwsAsync(
new CompactEncrypt(t.context.plaintext)
.setProtectedHeader({ enc: 'A128GCM' })
.encrypt(t.context.secret),
{
code: 'ERR_JWE_INVALID',
message: 'JWE "alg" (Algorithm) Header Parameter missing or invalid',
},
)
})
test('CompactEncrypt.prototype.encrypt JOSE header have an enc', async (t) => {
await t.throwsAsync(
new CompactEncrypt(t.context.plaintext)
.setProtectedHeader({ alg: 'dir' })
.encrypt(t.context.secret),
{
code: 'ERR_JWE_INVALID',
message: 'JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid',
},
)
})
keyObjectTest('ECDH-ES validates KeyObject compatibility', async (t) => {
await t.throwsAsync(
new CompactEncrypt(t.context.plaintext)
.setProtectedHeader({ alg: 'ECDH-ES', enc: 'A128GCM' })
.encrypt(rsa.publicKey),
{
instanceOf: TypeError,
code: 'ERR_MISSING_OPTION',
message: /'namedCurve' is required/,
},
)
const x25519 = crypto.generateKeyPairSync('x25519')
const jwe = await new CompactEncrypt(t.context.plaintext)
.setProtectedHeader({ alg: 'ECDH-ES', enc: 'A128GCM' })
.encrypt(x25519.publicKey)
const { plaintext } = await compactDecrypt(jwe, x25519.privateKey)
t.deepEqual(plaintext, t.context.plaintext)
})
keyObjectTest('KeyObject conversion errors do not trigger a JWK fallback', async (t) => {
const sentinel = new RangeError('sentinel')
const { publicKey } = crypto.generateKeyPairSync('x25519')
Object.defineProperty(publicKey, 'toCryptoKey', {
value() {
throw sentinel
},
})
const error = await t.throwsAsync(
new CompactEncrypt(t.context.plaintext)
.setProtectedHeader({ alg: 'ECDH-ES', enc: 'A128GCM' })
.encrypt(publicKey),
)
t.is(error, sentinel)
})