import { describe, expect, it } from 'vitest' import type { AeadAlg, E2EEnvelope } from 'relay-contracts' import { importAeadKey } from '../src/aead.js' import { MAX_FRAME_BYTES, decodeEnvelope, encodeEnvelope, nonceForSeq } from '../src/envelope.js' import { EnvelopeFormatError } from '../src/errors.js' import { sealFrame } from '../src/session.js' import { bytesToHex, hexToBytes } from './helpers.js' import envVector from './vectors/envelope.json' with { type: 'json' } function sample(seq: bigint): E2EEnvelope { return { seq, nonce: new Uint8Array([1, 2, 3, 4]), ciphertext: new Uint8Array([9, 8, 7]), tag: new Uint8Array(16).fill(0xbb), } } describe('T3 envelope codec (frozen relay-contracts shape, re-exported)', () => { it('KAT: frozen vector encodes to exact wire bytes and decodes back', () => { const key = importAeadKey(new Uint8Array(32).fill(5), 'aes-256-gcm', 'c2h') const env = sealFrame(key, BigInt(envVector.seq), new TextEncoder().encode('wire codec vector')) expect(bytesToHex(encodeEnvelope(env))).toBe(envVector.wire) const decoded = decodeEnvelope(hexToBytes(envVector.wire)) expect(bytesToHex(decoded.nonce)).toBe(envVector.nonce) expect(bytesToHex(decoded.ciphertext)).toBe(envVector.ciphertext) expect(bytesToHex(decoded.tag)).toBe(envVector.tag) }) it('round-trips and preserves seq as bigint past 2^53 (no float truncation)', () => { const env = sample(2n ** 63n - 1n) const decoded = decodeEnvelope(encodeEnvelope(env)) expect(decoded.seq).toBe(2n ** 63n - 1n) expect(decoded).toEqual(env) }) it('deterministic nonce = left-zero-padded big-endian seq', () => { const gcm = nonceForSeq(1n, 'aes-256-gcm' as AeadAlg) expect(gcm.length).toBe(12) expect(gcm[11]).toBe(1) expect(gcm.slice(0, 11).every((b) => b === 0)).toBe(true) const xchacha = nonceForSeq(258n, 'xchacha20-poly1305' as AeadAlg) expect(xchacha.length).toBe(24) expect(xchacha[23]).toBe(2) expect(xchacha[22]).toBe(1) }) it('negative: truncated buffer / length mismatch → EnvelopeFormatError', () => { expect(() => decodeEnvelope(new Uint8Array(5))).toThrow(EnvelopeFormatError) const wire = encodeEnvelope(sample(0n)) expect(() => decodeEnvelope(wire.slice(0, wire.length - 1))).toThrow(EnvelopeFormatError) const trailing = new Uint8Array(wire.length + 1) trailing.set(wire) expect(() => decodeEnvelope(trailing)).toThrow(EnvelopeFormatError) }) it('security: a frame larger than the hard cap is rejected before allocation', () => { expect(() => decodeEnvelope(new Uint8Array(MAX_FRAME_BYTES + 1))).toThrow(EnvelopeFormatError) }) })