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.
32 lines
1.0 KiB
TypeScript
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");
|
|
};
|