/** * P2 Protocol Conformance Tests * * Additional test vectors for JWT (RS256, ES256), HTTP Signature edge cases, * and X.509/SPIFFE strictness beyond the base protocol-extensions.test.ts. */ import { test } from 'node:test' import assert from 'node:assert' import { createSign, generateKeyPairSync } from 'node:crypto' import { jwtExtension } from '../../extensions/jwt.js' import { httpSignatureExtension } from '../../extensions/http-signature.js' import type { PredicateContext } from '../../extension/types.js' const makeCtx = (overrides: Partial = {}): PredicateContext['evalContext'] => ({ request: { body: undefined, headers: {}, query: {}, params: {}, }, response: { body: undefined, headers: {}, statusCode: 200, }, ...overrides, } as PredicateContext['evalContext']) const makeRoute = () => ({ path: '/test', method: 'GET' as const, category: 'observer' as const, requires: [], ensures: [], invariants: [], regexPatterns: {}, validateRuntime: true, }) // ============================================================================ // JWT: RS256 and ES256 verification vectors // ============================================================================ test('jwt: validates RS256 signature with RSA public key', () => { const { privateKey, publicKey } = generateKeyPairSync('rsa', { modulusLength: 2048 }) const payload = { sub: 'user-123', iat: Math.floor(Date.now() / 1000) } const header = { alg: 'RS256', typ: 'JWT' } const encodedHeader = Buffer.from(JSON.stringify(header)).toString('base64url') const encodedPayload = Buffer.from(JSON.stringify(payload)).toString('base64url') const signingInput = `${encodedHeader}.${encodedPayload}` const signer = createSign('RSA-SHA256') signer.update(signingInput) signer.end() const signature = signer.sign(privateKey).toString('base64url') const token = `${signingInput}.${signature}` const ext = jwtExtension({ keys: { default: publicKey.export({ type: 'spki', format: 'pem' }).toString() }, verify: true, }) const state = ext.onSuiteStart!({}) as Record const ctx: PredicateContext = { route: makeRoute(), evalContext: makeCtx({ request: { body: undefined, headers: { authorization: `Bearer ${token}` }, query: {}, params: {}, }, }), accessor: [], extensionState: state, } const result = ext.predicates!.jwt_valid!(ctx) assert.ok(result.success) assert.strictEqual(result.value, true) }) test('jwt: rejects RS256 token with wrong public key', () => { const { privateKey } = generateKeyPairSync('rsa', { modulusLength: 2048 }) const { publicKey: wrongPublicKey } = generateKeyPairSync('rsa', { modulusLength: 2048 }) const payload = { sub: 'user-123', iat: Math.floor(Date.now() / 1000) } const header = { alg: 'RS256', typ: 'JWT' } const encodedHeader = Buffer.from(JSON.stringify(header)).toString('base64url') const encodedPayload = Buffer.from(JSON.stringify(payload)).toString('base64url') const signingInput = `${encodedHeader}.${encodedPayload}` const signer = createSign('RSA-SHA256') signer.update(signingInput) signer.end() const signature = signer.sign(privateKey).toString('base64url') const token = `${signingInput}.${signature}` const ext = jwtExtension({ keys: { default: wrongPublicKey.export({ type: 'spki', format: 'pem' }).toString() }, verify: true, }) const state = ext.onSuiteStart!({}) as Record const ctx: PredicateContext = { route: makeRoute(), evalContext: makeCtx({ request: { body: undefined, headers: { authorization: `Bearer ${token}` }, query: {}, params: {}, }, }), accessor: [], extensionState: state, } const result = ext.predicates!.jwt_valid!(ctx) assert.ok(result.success) assert.strictEqual(result.value, false) }) test('jwt: validates ES256 signature with EC public key', () => { const { privateKey, publicKey } = generateKeyPairSync('ec', { namedCurve: 'P-256' }) const payload = { sub: 'user-123', iat: Math.floor(Date.now() / 1000) } const header = { alg: 'ES256', typ: 'JWT' } const encodedHeader = Buffer.from(JSON.stringify(header)).toString('base64url') const encodedPayload = Buffer.from(JSON.stringify(payload)).toString('base64url') const signingInput = `${encodedHeader}.${encodedPayload}` const signer = createSign('SHA256') signer.update(signingInput) signer.end() const signature = signer.sign(privateKey).toString('base64url') const token = `${signingInput}.${signature}` const ext = jwtExtension({ keys: { default: publicKey.export({ type: 'spki', format: 'pem' }).toString() }, verify: true, }) const state = ext.onSuiteStart!({}) as Record const ctx: PredicateContext = { route: makeRoute(), evalContext: makeCtx({ request: { body: undefined, headers: { authorization: `Bearer ${token}` }, query: {}, params: {}, }, }), accessor: [], extensionState: state, } const result = ext.predicates!.jwt_valid!(ctx) assert.ok(result.success) assert.strictEqual(result.value, true) }) test('jwt: rejects ES256 token with wrong public key', () => { const { privateKey } = generateKeyPairSync('ec', { namedCurve: 'P-256' }) const { publicKey: wrongPublicKey } = generateKeyPairSync('ec', { namedCurve: 'P-256' }) const payload = { sub: 'user-123', iat: Math.floor(Date.now() / 1000) } const header = { alg: 'ES256', typ: 'JWT' } const encodedHeader = Buffer.from(JSON.stringify(header)).toString('base64url') const encodedPayload = Buffer.from(JSON.stringify(payload)).toString('base64url') const signingInput = `${encodedHeader}.${encodedPayload}` const signer = createSign('SHA256') signer.update(signingInput) signer.end() const signature = signer.sign(privateKey).toString('base64url') const token = `${signingInput}.${signature}` const ext = jwtExtension({ keys: { default: wrongPublicKey.export({ type: 'spki', format: 'pem' }).toString() }, verify: true, }) const state = ext.onSuiteStart!({}) as Record const ctx: PredicateContext = { route: makeRoute(), evalContext: makeCtx({ request: { body: undefined, headers: { authorization: `Bearer ${token}` }, query: {}, params: {}, }, }), accessor: [], extensionState: state, } const result = ext.predicates!.jwt_valid!(ctx) assert.ok(result.success) assert.strictEqual(result.value, false) }) // ============================================================================ // HTTP Signature: negative corpus and edge cases // ============================================================================ test('httpSignature: rejects unsupported signature algorithm', () => { const { privateKey } = generateKeyPairSync('rsa', { modulusLength: 2048 }) const signatureInput = 'sig1=("@method")' const signer = createSign('SHA512') signer.update('dummy') signer.end() const signature = signer.sign(privateKey).toString('base64') const ext = httpSignatureExtension() const ctx: PredicateContext = { route: makeRoute(), evalContext: makeCtx({ request: { body: undefined, headers: { signature: `sig1=:${signature}:`, 'signature-input': signatureInput, }, query: {}, params: {}, }, }), accessor: [], extensionState: {}, } const result = ext.predicates!.signature_valid!(ctx) assert.ok(result.success) assert.strictEqual(result.value, false) }) test('httpSignature: rejects signature with mismatched label', () => { const { privateKey, publicKey } = generateKeyPairSync('rsa', { modulusLength: 2048 }) const ext = httpSignatureExtension({ publicKey: publicKey.export({ type: 'spki', format: 'pem' }).toString() }) const signatureInput = 'sig1=("@method")' const signer = createSign('SHA256') signer.update('dummy') signer.end() const signature = signer.sign(privateKey).toString('base64') const ctx: PredicateContext = { route: makeRoute(), evalContext: makeCtx({ request: { body: undefined, headers: { signature: `sig2=:${signature}:`, 'signature-input': signatureInput, }, query: {}, params: {}, }, }), accessor: [], extensionState: {}, } const result = ext.predicates!.signature_valid!(ctx) assert.ok(result.success) assert.strictEqual(result.value, false) })