/** * The uniform rejection detail for a challenge that cannot be consumed: * never issued, expired, already consumed, or issued under a different * session binding. Deliberately identical across those cases so a * rejection's public detail does not distinguish them. verifyBundle * normalizes EVERY store failure — custom-store error strings and thrown * exceptions included — to this text in the public result, so * implementations cannot leak record state even accidentally. */ export declare const UNKNOWN_CHALLENGE = "challenge was not issued by this verifier or has already been used"; /** * Tracks verifier-issued challenges so each is accepted at most once * within its freshness window (SPEC §10). * * The bundled implementation is in-memory (MemoryChallengeStore). * Deployments that need issuance state to survive restarts or span * verifier replicas implement this interface over shared storage; consume * MUST remain atomic (compare-and-set) so two concurrent presentations of * one challenge cannot both succeed. */ export interface ChallengeStore { /** * Generate a fresh challenge bound to sessionContext (which must be * empty/undefined or exactly 32 bytes; absent = unbound), valid for * ttlSeconds (which must be positive). Returns the challenge and its * expiry (unix seconds). */ issue(sessionContext: Uint8Array | undefined, ttlSeconds: number): Promise<{ challenge: Uint8Array; expires_at: number; }>; /** * Report whether challenge could be consumed right now — issued, * unexpired, unconsumed, and bound to sessionContext — WITHOUT * consuming it. Returns null when consumable, or a rejection reason. * verifyBundle calls this before any signature work. */ validate(challenge: Uint8Array, sessionContext: Uint8Array | undefined, now: number): Promise; /** * Atomically remove the challenge's issuance record. Exactly one * consume of a given challenge may ever succeed; all later calls (and * calls with a mismatched sessionContext, which do NOT remove the * record) return a rejection reason. Returns null on success. Removing * the record keeps the store's capacity a count of PENDING challenges — * a consumed challenge frees its slot immediately. */ consume(challenge: Uint8Array, sessionContext: Uint8Array | undefined, now: number): Promise; } /** * In-memory ChallengeStore: map keyed by the challenge with lazy expiry * and a capacity cap. Single-process only — state does not survive * restarts (an unconsumed challenge dies with the process, which fails * closed), and replicas sharing verification traffic would each accept * the same challenge once. Deployments spanning processes or hosts need * a ChallengeStore over shared storage whose consume is atomic (e.g. a * single-row DELETE ... RETURNING). */ export declare class MemoryChallengeStore implements ChallengeStore { private readonly maxSize; private readonly records; constructor(maxSize?: number); issue(sessionContext: Uint8Array | undefined, ttlSeconds: number): Promise<{ challenge: Uint8Array; expires_at: number; }>; validate(challenge: Uint8Array, sessionContext: Uint8Array | undefined, now: number): Promise; consume(challenge: Uint8Array, sessionContext: Uint8Array | undefined, now: number): Promise; private lookup; private expire; } //# sourceMappingURL=challenge_store.d.ts.map