import test from 'node:test'; import assert from 'node:assert/strict'; import {generateKeyPairSync,randomBytes} from 'node:crypto'; import policy from '../packages/core/policy-channel.cjs'; const channel=policy.evidenceChannel; const ed=generateKeyPairSync('ed25519'),pq=generateKeyPairSync('ml-dsa-87'); const edPublic=ed.publicKey.export({type:'spki',format:'pem'}),pqPublic=pq.publicKey.export({type:'spki',format:'pem'}); const make=()=>channel.challenge('synthetic-device',ed.privateKey,pq.privateKey); function exchange(){const challenge=make(),client=channel.prepare(challenge.bundle,{content:'synthetic confidential evidence',sourceEventId:'fixture-event'}),server=channel.receive(client.request,challenge.bundle,challenge.privateKey);return {...challenge,client,server};} test('real evidence KEM encrypts payload and authenticates signed identity and response',()=>{ const identity=channel.identity(ed.privateKey,pq.privateKey,pqPublic);assert.equal(channel.verifyIdentity(identity,edPublic,pqPublic),pqPublic); const x=exchange();channel.verifyChallenge(x.bundle,'synthetic-device',edPublic,pqPublic); assert.equal(JSON.stringify(x.client.request).includes('synthetic confidential'),false);assert.equal(x.server.payload.content,'synthetic confidential evidence'); const response=channel.response({accepted:true},x.bundle,x.server);assert.deepEqual(channel.finish(response,x.bundle,x.client),{accepted:true}); assert.throws(()=>channel.finish({...response,encrypted:x.client.request.encrypted},x.bundle,x.client)); x.client.responseKey.fill(0);x.server.responseKey.fill(0); }); test('policy and evidence protocols cannot substitute identity, challenge or ciphertext',()=>{ const x=exchange(),other=policy.challenge('synthetic-device',ed.privateKey,pq.privateKey); assert.throws(()=>policy.verifyIdentity(channel.identity(ed.privateKey,pq.privateKey,pqPublic),edPublic,pqPublic)); assert.throws(()=>policy.verifyChallenge(x.bundle,'synthetic-device',edPublic,pqPublic)); assert.throws(()=>channel.verifyChallenge(other.bundle,'synthetic-device',edPublic,pqPublic)); assert.throws(()=>channel.prepare(other.bundle,{content:'fixture'})); assert.throws(()=>policy.receive(x.client.request,x.bundle,x.privateKey)); const p=policy.prepare(other.bundle);assert.throws(()=>channel.receive(p.request,other.bundle,other.privateKey)); p.responseKey.fill(0);x.client.responseKey.fill(0);x.server.responseKey.fill(0); }); test('evidence refuses tampering, altered device, challenge, response key and request identity',()=>{ const x=exchange(),other=make(); assert.throws(()=>channel.verifyChallenge({...x.bundle,deviceId:'other'},'other',edPublic,pqPublic)); assert.throws(()=>channel.receive({...x.client.request,deviceId:'other'},x.bundle,x.privateKey)); assert.throws(()=>channel.receive({...x.client.request,id:other.bundle.id},x.bundle,x.privateKey)); const altered=structuredClone(x.client.request);altered.encrypted.tag=randomBytes(16).toString('base64url');assert.throws(()=>channel.receive(altered,x.bundle,x.privateKey)); const response=channel.response({accepted:true},x.bundle,x.server); assert.throws(()=>channel.finish(response,x.bundle,{...x.client,responseKey:randomBytes(32)})); assert.throws(()=>channel.finish(response,x.bundle,{...x.client,requestId:'other'})); assert.throws(()=>channel.finish({...response,id:other.bundle.id},x.bundle,x.client)); x.client.responseKey.fill(0);x.server.responseKey.fill(0); }); test('evidence payload size and strict envelope are bounded; replay consumption belongs to server',()=>{ const x=make();assert.throws(()=>channel.prepare(x.bundle,{content:'x'.repeat(65536)}));assert.throws(()=>channel.prepare(x.bundle,[])); const client=channel.prepare(x.bundle,{content:'x'.repeat(65500)}),first=channel.receive(client.request,x.bundle,x.privateKey); assert.equal(first.payload.content.length,65500); assert.throws(()=>channel.receive({...client.request,extra:true},x.bundle,x.privateKey)); assert.throws(()=>channel.receive(client.request,{...x.bundle,expiresAt:Date.now()-1},x.privateKey)); client.responseKey.fill(0);first.responseKey.fill(0); });