Skip to content

[_]: feature/user mail keystroke - #47

Merged
xabg2 merged 5 commits into
mainfrom
feature/mail-feature
Sep 22, 2026
Merged

xabg2 merged 5 commits into
mainfrom
feature/mail-feature

Conversation

@xabg2

@xabg2 xabg2 commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

We call the Mail API at GET /users/me/mail-account/keys to fetch the user's mail keystore, which holds both halves of the account's key pair:

  • encryptionPrivateKey decrypts incoming mail — the bodies of the messages in Inbox and every other folder. It arrives encrypted, and unwrapping it is what this PR is about.
  • publicKey encrypts outgoing mail for the account itself, so the user can still read their own Sent messages and drafts. Recipients' keys come from a separate lookup; this one is only ever the user's own.

The Mail API returns the account's private key encrypted, and the bridge daemon expects it decrypted. This adds the unwrapping, which is the client's job by design so the daemon never sees the mnemonic.

The chain mirrors openEncryptionKeystore in internxt/crypto, the library the web client uses to create the keystore — it has to match byte for byte or the key will not open:

blob   = base64(encryptionPrivateKey)              → 60 bytes
layout = ciphertext(32) ‖ GCM tag(16) ‖ IV(12)     ← IV is appended, not prepended
key    = BLAKE3_derive_key(context, BIP39_entropy(mnemonic))
AAD    = utf8(address + "Encryption" + publicKeyBase64)
→ AES-256-GCM open → 32 bytes

Why the BLAKE3 C sources are vendored

The keystore key is derived with BLAKE3 in derive_key mode. That is a separate mode with its own internal flags, not a hash of the context concatenated with the material, so it cannot be emulated. CryptoKit does not ship BLAKE3, so we vendor the reference implementation written by the algorithm's own authors instead.

Files are unmodified from https://github.com/BLAKE3-team/BLAKE3 at tag 1.8.7:

Tests - validation

12 tests, no hand-written expectations: the 35 official BLAKE3 vectors in both modes, the 24 official BIP-0039 vectors, and a keystore generated by internxt-crypto that this code unwraps to the same 32 bytes — which validates BIP39, BLAKE3, the AAD and the IV layout in one assertion.

@xabg2 xabg2 self-assigned this Sep 14, 2026
@xabg2 xabg2 added the enhancement New feature or request label Sep 14, 2026
Comment thread Sources/InternxtSwiftCore/Services/Crypto/Blake3.swift Outdated
Comment thread Tests/InternxtSwiftCoreTests/MailKeystoreTests.swift
@xabg2
xabg2 requested a review from sg-gs September 22, 2026 11:53
@xabg2
xabg2 merged commit 5d690c4 into main Sep 22, 2026
1 check passed
@xabg2
xabg2 deleted the feature/mail-feature branch September 22, 2026 13:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants