Names, key wrapping, chunked content, the KDF and the identity record —
everything needed to open a Files vault written by any other FlameNet
client.
Held to vectors/files-v1.json, produced by the JAVASCRIPT engine in
the browser. Not to the Swift engine. Checking Kotlin against Swift would
establish only that the two agree, and two implementations agreeing on
the wrong answer is how somebody's files stop opening when they change
phones. The file is copied byte for byte from the iOS repository rather
than regenerated, because a vector file regenerated by the implementation
under test proves nothing.
Two vectors were recomputed from scratch in Python before any Kotlin ran
against them — struct.pack('>hQ') for the label encoding and hmac.new(...).digest() for the name digests. The café case matches
under NFC and NOT under NFD, which is what pins the normalisation form
rather than assuming it: a platform normalising differently does not
error, it silently fails to find a file filed on another device.
Each guard was proven by breaking the code and watching the right test go
red: normalisation removed → the composed/decomposed digest test; the
wrap salt halves swapped → the browser's label-key grant; the end-flag or
the chunk index dropped from the authenticated data → the browser's file
stops decrypting at all.
The truncation and reordering tests each go on to show the surviving
bytes are still authentic under their ORIGINAL index and end-flag. Without
that second half "it threw" is all they would establish, and any bug at
all makes a mangled blob throw.
Two notes on the platform:
PBKDF2 is hand-written on Mac, as Bip39.pbkdf2HmacSha512 already is.
The JCA path takes a CharArray and lets the provider decide the
encoding; the reference implementations take bytes. A passphrase outside
ASCII would otherwise stretch differently under a different provider and
the record simply would not open, with nothing to say why.
The public half of an identity is recovered by agreeing against the
X25519 base point, because the JCA will not derive one from an XDH
private key. That it returns 32 bytes proves nothing, so it is pinned to
the vector file's own identityPublicKey.
43 tests. FileContent rather than Content: the messenger's Content is
a message payload and the two are not interchangeable.
Names, key wrapping, chunked content, the KDF and the identity record —
everything needed to open a Files vault written by any other FlameNet
client.
Held to `vectors/files-v1.json`, produced by the JAVASCRIPT engine in
the browser. Not to the Swift engine. Checking Kotlin against Swift would
establish only that the two agree, and two implementations agreeing on
the wrong answer is how somebody's files stop opening when they change
phones. The file is copied byte for byte from the iOS repository rather
than regenerated, because a vector file regenerated by the implementation
under test proves nothing.
Two vectors were recomputed from scratch in Python before any Kotlin ran
against them — `struct.pack('>hQ')` for the label encoding and
`hmac.new(...).digest()` for the name digests. The `café` case matches
under NFC and NOT under NFD, which is what pins the normalisation form
rather than assuming it: a platform normalising differently does not
error, it silently fails to find a file filed on another device.
Each guard was proven by breaking the code and watching the right test go
red: normalisation removed → the composed/decomposed digest test; the
wrap salt halves swapped → the browser's label-key grant; the end-flag or
the chunk index dropped from the authenticated data → the browser's file
stops decrypting at all.
The truncation and reordering tests each go on to show the surviving
bytes are still authentic under their ORIGINAL index and end-flag. Without
that second half "it threw" is all they would establish, and any bug at
all makes a mangled blob throw.
Two notes on the platform:
- PBKDF2 is hand-written on `Mac`, as `Bip39.pbkdf2HmacSha512` already is.
The JCA path takes a `CharArray` and lets the provider decide the
encoding; the reference implementations take bytes. A passphrase outside
ASCII would otherwise stretch differently under a different provider and
the record simply would not open, with nothing to say why.
- The public half of an identity is recovered by agreeing against the
X25519 base point, because the JCA will not derive one from an XDH
private key. That it returns 32 bytes proves nothing, so it is pinned to
the vector file's own `identityPublicKey`.
43 tests. `FileContent` rather than `Content`: the messenger's `Content` is
a message payload and the two are not interchangeable.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Names, key wrapping, chunked content, the KDF and the identity record —
everything needed to open a Files vault written by any other FlameNet
client.
Held to
vectors/files-v1.json, produced by the JAVASCRIPT engine inthe browser. Not to the Swift engine. Checking Kotlin against Swift would
establish only that the two agree, and two implementations agreeing on
the wrong answer is how somebody's files stop opening when they change
phones. The file is copied byte for byte from the iOS repository rather
than regenerated, because a vector file regenerated by the implementation
under test proves nothing.
Two vectors were recomputed from scratch in Python before any Kotlin ran
against them —
struct.pack('>hQ')for the label encoding andhmac.new(...).digest()for the name digests. Thecafécase matchesunder NFC and NOT under NFD, which is what pins the normalisation form
rather than assuming it: a platform normalising differently does not
error, it silently fails to find a file filed on another device.
Each guard was proven by breaking the code and watching the right test go
red: normalisation removed → the composed/decomposed digest test; the
wrap salt halves swapped → the browser's label-key grant; the end-flag or
the chunk index dropped from the authenticated data → the browser's file
stops decrypting at all.
The truncation and reordering tests each go on to show the surviving
bytes are still authentic under their ORIGINAL index and end-flag. Without
that second half "it threw" is all they would establish, and any bug at
all makes a mangled blob throw.
Two notes on the platform:
Mac, asBip39.pbkdf2HmacSha512already is.The JCA path takes a
CharArrayand lets the provider decide theencoding; the reference implementations take bytes. A passphrase outside
ASCII would otherwise stretch differently under a different provider and
the record simply would not open, with nothing to say why.
X25519 base point, because the JCA will not derive one from an XDH
private key. That it returns 32 bytes proves nothing, so it is pinned to
the vector file's own
identityPublicKey.43 tests.
FileContentrather thanContent: the messenger'sContentisa message payload and the two are not interchangeable.
Co-Authored-By: Claude Opus 5 noreply@anthropic.com
assigned to @paulhitt
deleted the
feat/files-label-latticebranch. This merge request now targets themainbranchmentioned in commit
54f9a1e0fachanged the description