Files
quak/src/crypto/encoding.ts
sneak 8aecf977e9 Phase 2 green: implement crypto primitives
Each stub is replaced with a thin wrapper over libsodium-wrappers-sumo:

  * init() awaits sodium.ready
  * toBase64 / toBase64URL / fromBase64 use sodium's base64 variants;
    fromBase64 tries all four (standard, standard-no-pad, URL-safe,
    URL-safe-no-pad) so callers don't have to know which form Ente
    delivered
  * deriveKEK is sodium.crypto_pwhash with ALG_ARGON2ID13 and 32-byte
    output
  * deriveLoginSubkey is sodium.crypto_kdf_derive_from_key(32, 1,
    'loginctx', kek).slice(0, 16) per the upstream Ente clients
  * decryptBox is sodium.crypto_secretbox_open_easy
  * decryptSealed is sodium.crypto_box_seal_open
  * initStreamPull / pullStreamChunk wrap the secretstream pull API,
    throwing on authentication failure rather than returning false

All 32 tests pass; make check is green.
2026-05-09 12:44:59 -07:00

32 lines
1.0 KiB
TypeScript

import sodium from "libsodium-wrappers-sumo";
export const toBase64 = (b: Uint8Array): string =>
sodium.to_base64(b, sodium.base64_variants.ORIGINAL);
export const toBase64URL = (b: Uint8Array): string =>
sodium.to_base64(b, sodium.base64_variants.URLSAFE_NO_PADDING);
// Ente uses standard base64 for most fields, URL-safe (with padding stripped)
// for the auth token. Rather than make callers specify, fromBase64 accepts
// any of the four variants libsodium understands and returns the bytes.
const VARIANTS = [
sodium.base64_variants.ORIGINAL,
sodium.base64_variants.ORIGINAL_NO_PADDING,
sodium.base64_variants.URLSAFE,
sodium.base64_variants.URLSAFE_NO_PADDING,
] as const;
export const fromBase64 = (s: string): Uint8Array => {
let lastError: unknown;
for (const variant of VARIANTS) {
try {
return sodium.from_base64(s, variant);
} catch (err) {
lastError = err;
}
}
throw lastError instanceof Error
? lastError
: new Error("invalid base64 input");
};