70 lines
2.3 KiB
TypeScript
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;
|
|
}
|