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.
This commit is contained in:
2026-05-09 12:44:59 -07:00
parent 676d42c5eb
commit 8aecf977e9
5 changed files with 101 additions and 48 deletions

View File

@@ -1,13 +1,31 @@
// Stub: see the README "Development workflow" section for TDD policy.
import sodium from "libsodium-wrappers-sumo";
export const fromBase64 = (_s: string): Uint8Array => {
throw new Error("crypto.fromBase64 not implemented");
};
export const toBase64 = (b: Uint8Array): string =>
sodium.to_base64(b, sodium.base64_variants.ORIGINAL);
export const toBase64 = (_b: Uint8Array): string => {
throw new Error("crypto.toBase64 not implemented");
};
export const toBase64URL = (b: Uint8Array): string =>
sodium.to_base64(b, sodium.base64_variants.URLSAFE_NO_PADDING);
export const toBase64URL = (_b: Uint8Array): string => {
throw new Error("crypto.toBase64URL not implemented");
// 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");
};