/** * SFrame E2EE adapter for @oxpulse/chat-sdk. * * Thin wrapper around sframe-ratchet/chat that converts ArrayBuffer↔Uint8Array * at the boundary and exposes a CryptoProvider interface consumed by SDKChatClient. * * ## Key contract * `getKey(roomId)` MUST return a CryptoKey with usages `['deriveKey','deriveBits']` * (HKDF base-key). Do NOT pass an AES-GCM key — the library will throw. * * ## Threat model (see design doc § C) * - Defends: message confidentiality, integrity, in-session sender auth, replay — * including cross-reload replay when IndexedDB is available (SEC-CR-003, see * `durableReplay` below and `sframe-replay.ts`). * - Does NOT defend: forward secrecy, post-compromise security, sender deniability * (symmetric key — any room member can forge messages from any other member). * Document loudly in production SDKs. * * @example * ```ts * // Demo: shared HKDF key imported from a 32-byte constant. * // Production: key is delivered out-of-band (X25519 + HKDF, or KMS). * const sharedHkdfKey = await crypto.subtle.importKey( * 'raw', sharedSecret32Bytes, 'HKDF', false, ['deriveKey', 'deriveBits'] * ); * const provider = createSFrameProvider({ * getKey: async (_roomId) => sharedHkdfKey, * }); * ``` */ import type { CryptoProvider } from './types.js'; export interface SFrameProviderOptions { /** * Return an HKDF base-key with usages `['deriveKey', 'deriveBits']`. * Called once per room on first seal/unseal; result is cached by sframe-ratchet. * * CRITICAL: must NOT return an AES-GCM key — the library derives its own * per-sender AES-128-GCM key via HKDF internally. */ getKey: (roomId: string) => Promise; /** * CTR allocation strategy passed through to sframe-ratchet. Default `'random-64'`. * * `'monotonic-idb'` persists the SENDER's per-frame counter to IndexedDB (requires * `ctrKeyspace`) and avoids the random-64 birthday bound. NOTE: it does NOT by itself * protect the RECEIVER across reloads — that is what `durableReplay` (below) does. */ ctrStrategy?: 'random-64' | 'monotonic-idb'; /** Required by sframe-ratchet when `ctrStrategy` is `'monotonic-idb'`; namespaces its CTR store. */ ctrKeyspace?: string; /** * In-memory replay window size passed through to sframe-ratchet (recent CTRs tracked per * sender per room within a session). Default 1024. `0` disables the library's in-memory * window (debug only) — it does NOT disable the durable window; set `durableReplay: false` * for that. */ replayWindow?: number; /** * SEC-CR-003: durable, cross-reload receiver-side anti-replay. Default ON when IndexedDB is * available, graceful no-op (with a one-time warning) when it is not (SSR / Node / private * mode). Set `false` to opt out entirely (reverts to the library's session-scoped in-memory * window only). */ durableReplay?: boolean; /** Namespace for the durable replay IDB store (isolate independent key-spaces). Default `'default'`. */ durableReplayNamespace?: string; /** * Durable replay window size (distinct recent CTRs per sender per room). Default 1024 * (equals `replayWindow`'s default, so both windows are configured EQUAL out of the box). * * SEC-CR-189-02: recommend `durableReplayWindow <= replayWindow`. `DurableReplayGuard`'s * in-memory cache can — for a bounded stretch while a persist write is still in flight * (`sframe-replay.ts`'s `persisting` guard) — release a not-yet-durable CTR slightly earlier * than the window would normally evict it. This is harmless at equal windows: sframe-ratchet's * OWN in-memory `replayWindow` (same CTR span) still rejects that in-session replay as a * backstop. Setting `durableReplayWindow` LARGER than `replayWindow` removes that backstop for * the extra span and reopens a narrow in-session replay window. Both currently default to * 1024 and no caller in this repo sets them independently. */ durableReplayWindow?: number; } /** * Create a CryptoProvider backed by sframe-ratchet v0.5 chat-mode. * * The returned provider is stateful (key cache + replay window). * Create one instance per application lifetime and share it across * SDKChatClient instances that share the same key space. */ export declare function createSFrameProvider(opts: SFrameProviderOptions): CryptoProvider; //# sourceMappingURL=sframe.d.ts.map