Skip to content

Release 1.1.37 (versionCode 40): Disconnect ends this phone's Cloud access - #109

Merged
adibhanna merged 5 commits into
mainfrom
release/1.1.37
Oct 12, 2026
Merged

adibhanna merged 5 commits into
mainfrom
release/1.1.37

Conversation

@adibhanna

Copy link
Copy Markdown
Contributor

1.1.37 on core 2.67.0.

  • 322f60a: ZenDirectUpload's native ZNE1 file loop (encryptFile, decryptFile, readHeader) for end-to-end encrypted vaults, with JVM tests on the vectors the TypeScript and Swift implementations share, plus tamper cases.
  • f20d04d: ids come from src/bridge/uuid.ts (getRandomValues fallback, byte-identical to the iPhone's), and bootstrap installs crypto.randomUUID where it is missing. No change on Android's supported WebViews.
  • 03161ae: end-to-end encrypted vaults wired through sync (native sealed uploads, the shared staging's native decrypt), the Keystore account key, the Settings encryption panel, and Disconnect revoking this device on ZenNotes Cloud. Encryption ships dark: the service has it off, so Settings shows nothing new. cloud-sync-staging.ts and its test are byte-identical to the iPhone's.
  • 54ff002: core-2.67.0-core.hb828dd60259bc376 (CI-built from ZenNotes/zennotes 58ba24ac, boundary run 38278029411); the lockfile moves the three @ZenNotes pins and adds hash-wasm 4.12.0, @scure/bip39 2.4.0 and @noble/hashes 2.4.0.
  • 6b6f393: 1.1.37, versionCode 40.

Local: typecheck clean, 271 tests pass; testDebugUnitTest (21 Java tests), lintDebug (0 errors) and assembleDebug pass. bundleRelease with the upload key: zennotes-1.1.37-vc40.aab, 31,618,163 bytes, SHA-256 7d213b07…ee55, built from tree 62a19254, the tree of 6b6f393. On an emulator against a local service with encryption on: a wrong passphrase was refused, the right one unlocked, the desktop's encrypted vault came down byte-identical (a 6.2 MB file included), and an edit, a new note and a 6 MB attachment went up; lock and unlock worked from Settings.

…iles

End-to-end encrypted vaults (docs/specs/cloud-e2ee.md in the desktop
repo) store every file as a ZNE1 object: a 74-byte header, then 64 KiB
chunks sealed with AES-256-GCM. Large files never cross the WebView
bridge on Android, so the chunk loop has to run natively too.

ZenDirectUpload gains encryptFile and decryptFile, which stream a vault
file through javax.crypto 64 KiB at a time and hash both sides, and
readHeader, which returns a file's first bytes so JavaScript can check a
downloaded object's key commitment before anything is opened. JavaScript
derives the per-object key, builds the header and computes the AAD; the
native side never sees a parent key. Decryption holds one chunk back to
know which chunk is last, refuses truncation, reordering, appended bytes
and an empty last chunk after a full one, and deletes its partial output
on any failure.

Zne1FileCipher has no Capacitor dependency, so JVM tests run it against
the vectors the TypeScript and Swift implementations share, plus tamper
cases. The vectors are copied into the unit-test resources, and org.json
joins as a test-only dependency to read them.
…ypto.randomUUID

The iPhone app runs on WebViews without crypto.randomUUID (iOS 15.0 to
15.3), and core 2.67.0 routes its own ids through a getRandomValues
fallback. Android's oldest supported WebView (Chrome 92, which the
bundle's syntax needs) already has randomUUID, so nothing changes on a
phone today; this puts Android on the same footing, so the shared files
both apps carry can call one helper.

Sync, backup downloads, dropped files and server profiles take their ids
from src/bridge/uuid.ts, which falls back to getRandomValues, and
bootstrap.ts installs crypto.randomUUID before anything else loads, for
code that calls it directly. uuid.ts and its test are byte-identical to
the iPhone app's.
Core 2.67.0's encryption layer seals inline content and item metadata;
this wires up the rest of the phone.

- Files too large to send inline are sealed natively into the vault's
  private sync folder, checked against the plaintext hash sync took, and
  sent by the native PUT. An encrypted vault decides inline or direct by
  the size of its ciphertext, and the sealed copy is deleted however the
  upload ends.
- Downloads go through the shared staging (cloud-sync-staging.ts and its
  test, byte-identical to the iPhone's): the ciphertext is verified,
  opened natively with the key the vault cipher derives from its header,
  and only the checked plaintext is published.
- The account key lives in the Keystore-backed secure storage, one entry
  per account. A missing or unreadable entry reads as locked, so a
  restored install asks for the passphrase instead of failing. Signing
  out forgets every key, and another account's key never outlives a
  switch.
- Settings serves the shared encryption panel: set up, unlock, change
  the passphrase, a new recovery key, lock, and creating an encrypted
  vault. Linking an encrypted vault on a locked device stops at the
  link, its real name shows once unlocked, its backups stay unlabeled,
  and its backup archive downloads only on the desktop.
- Disconnect now asks ZenNotes Cloud to end this phone's access (DELETE
  /api/v1/devices/current) while the credential still exists to ask
  with. The phone used to stay listed under Connected apps.

The service keeps end-to-end encryption off, so nothing of it shows yet:
Settings offers encryption only when the account's capabilities include
it. On an emulator against a local service with it on, the phone
refused a wrong passphrase, unlocked with the right one, pulled an
encrypted vault the desktop made (every file byte-identical, a 6.2 MB
one included) and pushed an edit, a new note and a 6 MB attachment.
…d access

Core core-2.67.0-core.hb828dd60259bc376, built by CI from
ZenNotes/zennotes 58ba24ac (boundary run 38278029411), adopted with
core:adopt (SHA-256, SHA-512, packed versions and clean-source
provenance checked). It carries the sign-out revoke the previous
commit's Disconnect uses, the end-to-end encryption layer (ZNE1 objects,
keys, vault ciphers, path ids) and randomUUID through getRandomValues
where a WebView lacks it.

The lockfile moves the three @ZenNotes pins and adds the encryption
layer's pinned dependencies: hash-wasm 4.12.0 (Argon2id) and
@scure/bip39 2.4.0 with its @noble/hashes 2.4.0 (recovery words).
Typecheck and 271 tests pass.
@adibhanna
adibhanna merged commit b863e42 into main Oct 12, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant