import { type MlKemBackend, type MlKemParameterSet, type MlKemSuiteDescriptor } from "./mlkem";
import type { LibCrypto } from "./libcrypto";
/**
* Sparse post-quantum healing state machine.
*
* This module deliberately does not own transport or persistence. Callers must:
*
* 1. authenticate/decrypt every inbound record with the current outer channel
* before calling an `acceptAuthenticated*` method;
* 2. persist prepared state before putting its record on the network;
* 3. encrypt an ADVANCE under its `fromEpoch` key before committing it; and
* 4. gate application traffic while `trafficBlocked` is true.
*
* There is no classical or lower-parameter fallback. The room-selected suite
* supplied to the constructor is checked against both FIPS 203 sizes and the
* backend. Every ADVANCE embeds the complete OFFER it answers. That makes the
* exact suite, channel binding, epochs, counters, public key, and ciphertext a
* single deterministic KDF transcript and gives the state machine an exact
* byte string with which to reject replays and forks.
*/
export declare const PQ_HEALING_BINDING_BYTES = 32;
export declare const PQ_HEALING_ROOT_BYTES = 32;
export declare const PQ_HEALING_RECORD_HEADER_BYTES = 64;
export declare const PQ_HEALING_ACK_BYTES = 64;
export declare const PQ_HEALING_SNAPSHOT_FORMAT_VERSION = 1;
export type PqHealingTurn = "local" | "remote";
export type PqHealingPhase = "idle" | "outbound-offer-prepared" | "outbound-offer-dispatched" | "inbound-offer" | "outbound-advance-prepared" | "outbound-advance-awaiting-ack" | "inbound-advance-prepared" | "inbound-advance-awaiting-ack-dispatch" | "destroyed";
export type PqHealingErrorCode = "binding-mismatch" | "counter-gap" | "destroyed" | "epoch-gap" | "fork" | "invalid-record" | "invalid-state" | "overflow" | "replay" | "suite-mismatch" | "wrong-turn";
export declare class PqHealingError extends Error {
readonly code: PqHealingErrorCode;
constructor(code: PqHealingErrorCode, message: string);
}
export interface PqHealingAdvanceAcknowledgement {
/**
* The newly committed epoch. The transport acknowledgement must be
* authenticated under this epoch, proving that the offerer decapsulated.
*/
readonly epoch: bigint;
/** Sender counter of the ADVANCE being acknowledged. */
readonly advanceCounter: bigint;
}
export interface PqHealingOfferRecord {
readonly type: "offer";
readonly parameterSet: MlKemParameterSet;
readonly binding: Uint8Array;
readonly senderCounter: bigint;
readonly fromEpoch: bigint;
readonly toEpoch: bigint;
readonly publicKey: Uint8Array;
}
export interface PqHealingAdvanceRecord {
readonly type: "advance";
readonly parameterSet: MlKemParameterSet;
readonly binding: Uint8Array;
readonly senderCounter: bigint;
readonly fromEpoch: bigint;
readonly toEpoch: bigint;
/** The complete, canonical OFFER answered by this record. */
readonly offerBytes: Uint8Array;
readonly offer: PqHealingOfferRecord;
readonly ciphertext: Uint8Array;
}
export type PqHealingRecord = PqHealingOfferRecord | PqHealingAdvanceRecord;
export interface PqHealingMachineOptions
{
readonly module: LibCrypto;
readonly backend: MlKemBackend
;
/**
* Authenticated room-selected suite. This is an input, never a negotiated
* result. It must exactly match the backend's parameter set and FIPS sizes.
*/
readonly suite: Readonly>;
/**
* Non-secret, transcript-derived edge binding (for example a truncation of
* the authenticated handshake/channel-input hash).
*/
readonly binding: Uint8Array;
/** Current, separate PQ root. The constructor takes an owned copy. */
readonly rootKey: Uint8Array;
/** Which side is allowed to emit the first/next OFFER. */
readonly nextOfferer: PqHealingTurn;
readonly epoch?: bigint;
readonly localCounter?: bigint;
readonly remoteCounter?: bigint;
}
export interface PqHealingRestoreOptions {
readonly module: LibCrypto;
readonly backend: MlKemBackend
;
readonly suite: Readonly>;
/** Authenticated expected edge binding; snapshots from another edge fail. */
readonly binding: Uint8Array;
}
export type PqHealingSnapshotPhase = {
readonly kind: "idle";
} | {
readonly kind: "outbound-offer-prepared" | "outbound-offer-dispatched";
readonly offer: Uint8Array;
readonly secretKey: Uint8Array;
} | {
readonly kind: "inbound-offer";
readonly offer: Uint8Array;
} | {
readonly kind: "outbound-advance-prepared";
readonly advance: Uint8Array;
readonly nextRoot: Uint8Array;
} | {
readonly kind: "outbound-advance-awaiting-ack";
readonly advance: Uint8Array;
} | {
readonly kind: "inbound-advance-prepared";
readonly advance: Uint8Array;
readonly secretKey: Uint8Array;
readonly nextRoot: Uint8Array;
} | {
readonly kind: "inbound-advance-awaiting-ack-dispatch";
readonly advance: Uint8Array;
};
/**
* Owned, plaintext checkpoint of the complete store-free PQ machine.
*
* Durable integration normally checkpoints only phases whose corresponding
* sealed outbox frame is committed in the same transaction. The two
* `*-prepared` phases are intentionally representable for clone/rollback and
* fault-injection tests, but are transaction-local and must never be persisted
* alone or dispatched after restoration without the exact sealed frame.
*/
export interface PqHealingSnapshot {
readonly formatVersion: typeof PQ_HEALING_SNAPSHOT_FORMAT_VERSION;
readonly parameterSet: P;
readonly binding: Uint8Array;
readonly rootKey: Uint8Array;
readonly epoch: bigint;
readonly localCounter: bigint;
readonly remoteCounter: bigint;
readonly nextOfferer: PqHealingTurn;
readonly phase: PqHealingSnapshotPhase;
readonly lastInboundOffer: Uint8Array | null;
readonly lastInboundAdvance: Uint8Array | null;
}
export declare const getPqHealingRecordLengths: (suite: Readonly) => {
readonly offer: number;
readonly advance: number;
};
/**
* Encode the sole canonical PQ acknowledgement representation:
*
* `magic(4) | version(1) | type(1) | suite(1) | flags(1) | binding(32) |
* advanceCounter(u64 BE) | epoch(u64 BE) | reserved(u64=0)`.
*/
export declare const encodePqHealingAck: (acknowledgement: unknown, suite: Readonly, binding: Uint8Array) => Uint8Array;
/** Decode and validate an exact, edge- and suite-bound 64-byte ACK. */
export declare const decodePqHealingAck: (record: Uint8Array, suite: Readonly, binding: Uint8Array) => PqHealingAdvanceAcknowledgement;
/**
* Parse a canonical record without changing state. Integration code can use
* this for routing and audit logs, but MUST still call the stateful
* `acceptAuthenticated*` method only after outer authentication.
*/
export declare const inspectPqHealingRecord: (record: Uint8Array, suite: Readonly) => PqHealingRecord;
/**
* One directional-turn PQ healing machine for a single authenticated peer
* edge. A room mesh owns one instance per edge; a room-wide policy supplies
* the same exact ML-KEM parameter set to every instance.
*/
export declare class PqHealingMachine {
#private;
constructor(options: PqHealingMachineOptions
);
static restore(snapshot: unknown, options: PqHealingRestoreOptions): PqHealingMachine;
get suite(): Readonly>;
get phase(): PqHealingPhase;
get epoch(): bigint;
get localCounter(): bigint;
get remoteCounter(): bigint;
get nextOfferer(): PqHealingTurn;
/**
* New-epoch messages must not race the control exchange across independent
* WebRTC data channels. The integration must hold application traffic while
* this is true.
*/
get trafficBlocked(): boolean;
/** Explicit owned copy for the message-key combiner or encrypted checkpoint. */
copyRootKey(): Uint8Array;
/**
* Deep-copy the complete checkpoint, including pending ML-KEM secret keys.
* See `PqHealingSnapshot`: prepared phases are transaction-local snapshots.
*/
snapshot(): PqHealingSnapshot;
/** Deep-clone this machine into independently owned secret buffers. */
clone(): PqHealingMachine
;
/**
* Consume an independently authenticated successor. Superseded live secrets
* are wiped and `next` becomes destroyed without wiping the moved secrets.
*/
adopt(next: PqHealingMachine
): void;
/**
* Return the exact public record that should be sent/retransmitted. A caller
* must never reconstruct a replacement after the original may have escaped.
*/
copyPendingOutboundRecord(): Uint8Array;
prepareOffer(): Promise;
/**
* Mark the persisted OFFER as having possibly escaped to the network. After
* this point it cannot be aborted or replaced; only its exact bytes may be
* retransmitted.
*/
markOfferDispatched(): void;
/**
* Safe only while the caller can prove the OFFER has never been sent.
* The pending ML-KEM secret key is wiped.
*/
abortUnsentOffer(): void;
/**
* Accept a full OFFER only after the outer AEAD has authenticated it.
*/
acceptAuthenticatedOffer(record: Uint8Array): void;
/**
* Prepare an ADVANCE and its candidate root. The caller must outer-encrypt
* this exact record under `fromEpoch` before committing, then persist the
* committed state and sealed record before sending.
*/
prepareAdvance(): Promise;
/**
* Commit the prepared root after the exact ADVANCE has already been sealed
* under the old epoch. The returned record is the only record that may be
* sent. Application traffic remains blocked until a new-epoch authenticated
* acknowledgement is accepted.
*/
commitPreparedAdvance(): Uint8Array;
/**
* Close the responder side only after an acknowledgement authenticated under
* the new epoch has been received. Exact epoch/counter binding prevents an
* unrelated transport receipt from unlocking the next OFFER.
*/
acceptAuthenticatedAdvanceAcknowledgement(acknowledgement: unknown): void;
/**
* Accept a full ADVANCE only after its outer AEAD has authenticated it under
* the old epoch.
*/
acceptAuthenticatedAdvance(record: Uint8Array): Promise;
/**
* Commit a successfully decapsulated ADVANCE. The returned binding must be
* carried by an acknowledgement authenticated under the new epoch. Traffic
* remains blocked until `markAdvanceAcknowledgementDispatched` is called.
*/
commitAcceptedAdvance(): PqHealingAdvanceAcknowledgement;
/**
* Mark the new-epoch acknowledgement as having escaped to the network.
* Integration should persist that fact or retain a transport receipt cache
* so an exact ADVANCE replay can be answered without rolling state back.
*/
markAdvanceAcknowledgementDispatched(acknowledgement: unknown): void;
/**
* Idempotent terminal cleanup. All live roots, candidate roots, and pending
* ML-KEM secret keys are wiped. Public record copies may remain with callers.
*/
destroy(): void;
}
/** Store-free checkpoint helper with an owned result. */
export declare const snapshotPqHealing: (machine: PqHealingMachine
) => PqHealingSnapshot
;
/** Restore only after validating suite, binding, records, and phase invariants. */
export declare const restorePqHealing:
(snapshot: unknown, options: PqHealingRestoreOptions
) => PqHealingMachine
;
/** Deep-clone for mutate/persist/adopt transaction flows. */
export declare const clonePqHealing:
(machine: PqHealingMachine
) => PqHealingMachine
;
/** Consume `next` as the independently authenticated successor of `live`. */
export declare const adoptPqHealing:
(live: PqHealingMachine
, next: PqHealingMachine
) => void;
/**
* Wipe every byte buffer owned by a plaintext checkpoint, including the PQ
* root, pending KEM secret key/candidate root, binding, and public records.
*/
export declare const wipePqHealingSnapshot: (snapshot: PqHealingSnapshot) => void;