/** Shared test helpers (hex codec + Ed25519 host stub). */ import { ed25519 } from '@noble/curves/ed25519' import type { AeadAlg, DeviceAuthProofProvider } from 'relay-contracts' import { createHostHandshake, type HostHandshake } from '../src/handshake.js' import { nobleEd25519Signer } from '../src/ed25519.js' export function hexToBytes(hex: string): Uint8Array { const out = new Uint8Array(hex.length / 2) for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16) return out } export function bytesToHex(bytes: Uint8Array): string { return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('') } export const utf8 = (s: string): Uint8Array => new TextEncoder().encode(s) export const fromUtf8 = (b: Uint8Array): string => new TextDecoder().decode(b) /** A deterministic Ed25519 host identity for handshake tests. */ export function makeHostIdentity(): { privateKey: Uint8Array; agentPubkey: Uint8Array } { const privateKey = ed25519.utils.randomPrivateKey() return { privateKey, agentPubkey: ed25519.getPublicKey(privateKey) } } /** A proof provider whose proofs are bound to the ephemeral binding material. */ export function boundProofProvider(): DeviceAuthProofProvider { return { async proofFor(hostId, binding) { return `proof:${hostId}:${bytesToHex(binding.clientEphPub)}:${bytesToHex(binding.clientNonce)}` }, } } /** Verify a bound proof matches the presented ephemeral binding (anti-replay). */ export function verifyBoundProof( proof: string, binding: { clientEphPub: Uint8Array; clientNonce: Uint8Array }, ): boolean { const expected = `${bytesToHex(binding.clientEphPub)}:${bytesToHex(binding.clientNonce)}` return proof.startsWith('proof:') && proof.endsWith(expected) } export function makeHost( privateKey: Uint8Array, agentPubkey: Uint8Array, supported: readonly AeadAlg[] = ['xchacha20-poly1305', 'aes-256-gcm'], ): HostHandshake { return createHostHandshake({ signer: nobleEd25519Signer(privateKey), agentPubkey, supported, verifyDeviceProof: async (proof, binding) => verifyBoundProof(proof, binding), }) }