Commit Graph
56 Commits
Author SHA1 Message Date
sneak ef3f10fecc Phase 4 red: ApiClient tests and stub
19 tests covering ApiClient's full public surface: default and custom
origins, X-Client-Package and X-Auth-Token headers, getJSON with query
params, postJSON with JSON body, ApiError on 4xx/5xx, streaming file
and thumbnail downloads, and self-hosted origin routing.

Tests inject a recording fetch via the constructor, so nothing hits the
network. The test file is documented to serve as canonical usage
reference per the development workflow.
2026-05-11 01:01:34 -07:00
sneak 78fdabe54a Phase 3a green: implement auth.unwrapAuth
The implementation is exactly the decryption chain documented in the
test file: deriveKEK -> decryptBox(masterKey) -> decryptBox(secretKey)
-> decryptSealed(token) -> toBase64URL. Errors from the underlying
crypto primitives propagate; the only added validation is the up-front
check that the response actually contains both keyAttributes and
encryptedToken (caller bug if not).

Also re-exports the auth/unwrap and auth/types public surface from
src/index.ts.

All 38 tests pass; make check and make docker are green.
2026-05-11 00:59:43 -07:00
sneak 6386a0ec9f Phase 3a red: auth.unwrapAuth tests and stub
Tests for the password-only decryption chain that follows a successful
login (SRP or email OTP, with or without 2FA). The unwrap covers:
  password -> KEK (Argon2id) -> masterKey (secretbox) ->
  secretKey (secretbox) -> tokenBytes (sealed box) -> base64url token

Each test builds a synthetic AuthorizationResponse using libsodium
directly and asserts unwrapAuth recovers the inputs byte for byte. The
test file also functions as the canonical description of the protocol.

Adds src/auth/types.ts with KeyAttributes, SRPAttributes,
AuthorizationResponse, and LoginChallenge declarations matching the
README's API reference. src/auth/unwrap.ts is the throwing stub; the
real implementation lands next.
2026-05-11 00:58:27 -07:00
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
sneak 676d42c5eb Phase 2 red: crypto primitive tests and stub modules
Tests for the entire crypto/ public surface, written against the API
shape declared in the README. The accompanying src/crypto/ modules are
stubs that throw 'not implemented' so the test files compile and tests
fail with clear errors rather than module-not-found.

Tests cover:
  * init() resolves and is idempotent
  * fromBase64 / toBase64 / toBase64URL round-trips, including URL-safe
    input with stripped padding (the form Ente uses for auth tokens)
  * deriveKEK matches sodium.crypto_pwhash with Argon2id parameters
  * deriveLoginSubkey matches sodium.crypto_kdf_derive_from_key with
    subkey id 1 and ctx 'loginctx', truncated to 16 bytes
  * decryptBox round-trips, rejects tampering, wrong key, wrong nonce
  * decryptSealed round-trips, rejects wrong keypair and tampering
  * Secretstream pull decrypts multi-chunk streams in order, exposes
    per-chunk tags, rejects tampering, wrong key, and out-of-order chunks
  * Constants STREAM_CHUNK_SIZE (4 MiB) and STREAM_CHUNK_OVERHEAD (17)

Tests are commented to serve as the canonical API documentation per the
README development workflow policy. Verified: 29 tests fail (red), 3
trivial constant tests pass; lint and fmt-check are green.

eslint.config.mjs is updated to honour the leading-underscore convention
for intentionally unused parameters (the stubs).
2026-05-09 12:43:52 -07:00
sneak 47cdb5ca8b Add JS toolchain: TypeScript, ESLint, Prettier, Vitest
package.json declares the project as ESM with NodeNext module resolution,
exposing dist/index.js as the library entry and dist/bin/quack.js as the
CLI binary. Dev dependencies are pinned to exact versions (yarn.lock holds
the integrity hashes per repo policy on hash-pinned external references).
Adds a placeholder src/index.ts and a single smoke test so make check is
not a no-op.
2026-05-09 21:29:13 +02:00