import type { LibCrypto } from "../cryptography/libcrypto"; import type { RatchetState } from "../cryptography/ratchet"; import type { RoomPqMode } from "../roomPolicy"; export { HANDSHAKE_PAYLOAD_LENGTHS, HANDSHAKE_STEP, MAX_HANDSHAKE_PAYLOAD_LENGTH, handshakePayloadLengthForStep, isHandshakePayloadForStep, } from "./handshakeFrame"; export type { HandshakeStep } from "./handshakeFrame"; export interface ChannelInputParams { channelId: Uint8Array; ikInitiator: Uint8Array; ikResponder: Uint8Array; fpInitiator: Uint8Array; fpResponder: Uint8Array; /** Defaults to the 0.10 ML-KEM-768 suite for source compatibility. */ pqMode?: RoomPqMode; } /** * CPace channel-input transcript (spec §5): CI = channel-id ‖ IK_a ‖ IK_b ‖ * fp_a ‖ fp_b ‖ PQ_TAG, with a = initiator, b = responder. Both peers build a * byte-identical CI because they agree on the initiator role (Task 4). Binding * both identity keys + both DTLS fingerprints is what makes a swapped-cert * MITM fail the key-confirmation MAC. * * Deliberately synchronous (unlike the project's async concatUint8Arrays * helper in utils/uint8array.ts, which yields to the microtask queue for * parity with other WASM-backed helpers): CI construction is pure * byte-copying with no WASM/IndexedDB involved, and callers build it inline * while assembling the CPace transcript. */ export declare const buildChannelInput: (p: ChannelInputParams) => Uint8Array; export interface HandshakeTransport { send(bytes: Uint8Array): void | Promise; recv(): Promise; } export interface HandshakeCoreParams { mode: "pin" | "nopin"; /** Exact preselected suite; omission selects the documented 768 default. */ pqMode?: RoomPqMode; pin: Uint8Array | null; channelInput: Uint8Array; amInitiator: boolean; idSelfSec: Uint8Array; selfIdentityX25519Pub: Uint8Array; selfIdentityCrossSignature: Uint8Array; peerIdentityEd25519Pub: Uint8Array; } export interface PqHealingBootstrap { /** Secret root dedicated to sparse post-quantum healing. Caller-owned. */ rootKey: Uint8Array; /** Non-secret transcript/edge binding shared by both authenticated peers. */ binding: Uint8Array; /** Stable Ed25519 role ordering chooses the first OFFER turn. */ nextOfferer: "local" | "remote"; } /** * Three-flight key-confirmed handshake core (spec §5), decoupled from * RTCDataChannel so it is * unit-testable with two linked in-memory transports: * R1 initiator sends HELLO {sid, EK, Y, ML-KEM pk}; responder encapsulates, * replies with HELLO {sid, EK, Y, ML-KEM ct}; both derive the 32-byte * hybrid root from interactive-3DH || ML-KEM (no-PIN) or * CPace-ISK || interactive-3DH || ML-KEM (PIN). * R2 responder initRatchet(false,null) → dhPubR and sends * CONFIRM{dhPubR, mac_R}; initiator seeds against dhPubR and sends * CONFIRM{dhPubI, mac_I}, where mac_I commits to the received mac_R. * R3 after verifying mac_I and priming both ratchet chains, responder sends * FINISH{mac_F}, which commits to both earlier confirmation MACs. * Initiator returns only after verifying mac_F. * * The room-selected ML-KEM suite is mandatory: this function constructs that * exact backend itself and fails before sending if its WASM exports are * missing. There is deliberately no classical fallback or negotiation bit. * The three chained MACs cover the full ordered HELLO transcript and dhPubR * under distinct role domains; mac_I additionally covers dhPubI + mac_R, and * mac_F covers both earlier proofs. A swapped cert/key, altered KEM field, * ratchet-key tamper, or wrong PIN makes key confirmation fail (or is rejected * as malformed). * Persistence + gate-open happen ONLY in runHandshake, after this resolves, so * a throw here leaves nothing persisted. * * NOTE on the R2 ordering: the initiator publishes its mac_I BEFORE separately * checking mac_R so a wrong-root peer still receives a terminal proof instead * of hanging forever. This is safe because mac_I itself authenticates the exact * mac_R bytes the initiator received: changing mac_R makes the responder reject * mac_I. A valid responder FINISH is therefore required before the initiator * can return an established state. */ export declare const performHandshakeCore: (transport: HandshakeTransport, params: HandshakeCoreParams, module: LibCrypto) => Promise<{ state: RatchetState; secret: Uint8Array; pqHealing: PqHealingBootstrap; }>;