Files
ChatApp/packages/shared/src/crypto/backend.ts
T
2026-04-18 23:11:35 +02:00

70 lines
2.3 KiB
TypeScript

// Platform-agnostic crypto backend contract. Each host (desktop / mobile)
// registers its own implementation at boot via `setCryptoBackend`.
//
// Why an interface?
// - Desktop: libsodium-wrappers (WASM, needs async ready-gate).
// - Mobile: react-native-libsodium (native bindings, different API shape).
// - Tests: deterministic backend with seeded RNG.
//
// Backends are expected to be *synchronous* after registration. Any async
// initialisation (e.g. libsodium's WASM warm-up) happens before the backend
// object is handed to `setCryptoBackend`.
export interface KeyPair {
publicKey: Uint8Array;
privateKey: Uint8Array;
}
export interface CryptoBackend {
readonly name: string;
readonly nonceLength: number; // crypto_box_NONCEBYTES — 24
readonly publicKeyLength: number; // 32
readonly privateKeyLength: number; // 32
readonly secretboxKeyLength: number; // crypto_secretbox_KEYBYTES — 32
readonly secretboxNonceLength: number; // crypto_secretbox_NONCEBYTES — 24
randomBytes(n: number): Uint8Array;
generateKeyPair(): KeyPair;
// Authenticated encryption (X25519 + XSalsa20-Poly1305, AKA crypto_box).
box(
plaintext: Uint8Array,
nonce: Uint8Array,
recipientPublicKey: Uint8Array,
senderPrivateKey: Uint8Array,
): Uint8Array;
// Must throw on authentication failure.
boxOpen(
ciphertext: Uint8Array,
nonce: Uint8Array,
senderPublicKey: Uint8Array,
recipientPrivateKey: Uint8Array,
): Uint8Array;
// Symmetric authenticated encryption (XSalsa20-Poly1305, crypto_secretbox).
// Used for large blobs (attachments) — one key per blob, key itself is
// distributed per recipient via crypto_box envelopes.
secretbox(plaintext: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array;
secretboxOpen(ciphertext: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array;
}
let current: CryptoBackend | null = null;
export function setCryptoBackend(backend: CryptoBackend): void {
current = backend;
}
export function getCryptoBackend(): CryptoBackend {
if (!current) {
throw new Error(
'Crypto backend not configured. Call setCryptoBackend() at app boot before any crypto operation.',
);
}
return current;
}
export function isCryptoBackendReady(): boolean {
return current !== null;
}