crypto/payload_encryption library

Payload encryption compatible with the Go client's bound NaCl box (go-ricochet pkg/client/encryption.go).

Wire format sent: "RCE2" ‖ 24-byte nonce ‖ box(header ‖ payload), where the box is NaCl box (X25519 agreement between the two identity keys, XSalsa20-Poly1305) and the header is the PayloadBinding as three big-endian u16-length-prefixed fields. The binding ties the ciphertext to the message that carries it, so a ciphertext the server moved to another folder, or presented under another id or to another recipient, is refused on decrypt. The legacy unbound format (nonce ‖ box(payload)), which old Go clients sent, still opens and is reported as unbound.

What this layer is not: it has no forward secrecy (one static key pair per identity, by design; a ratchet belongs above it, as OverNode's Signal sessions are) and it does not detect a replay of the same ciphertext into the same folder.

Classes

DecryptedPayload
A decrypted payload and whether its ciphertext carried a binding. A caller that wants the replay protection should treat bound == false as a downgrade.
PayloadBinding
What a ciphertext is tied to: the message it must arrive as.
PayloadEncryptor
Seals and opens payloads with one identity's key.

Constants

boundMagic → const List<int>
The four bytes that open a bound ciphertext.

Functions

ed25519PublicKeyToX25519(Uint8List edPublic) → Uint8List
Converts an Ed25519 public key to X25519 by the birational map from Edwards to Montgomery form, as the Go client does.
ed25519SeedToX25519(Uint8List seed) → Uint8List
Converts an Ed25519 seed (the 32-byte private key) to the X25519 secret the Go client derives: SHA-512 of the seed, first 32 bytes, clamped.
openIfEncrypted(SFMessage message, PayloadEncryptor? encryptor) → Future<SFMessage>
Returns message with its payload opened when it is flagged encrypted, checked against the envelope it arrived in; otherwise unchanged.
peerIdToX25519PublicKey(PeerId id) → Future<Uint8List>
The X25519 public key of a peer, from the Ed25519 key its id embeds.

Exceptions / Errors

BoundToAnotherMessageException
The ciphertext opened, but it was sealed for a different message.
PayloadDecryptException
Decryption failed at the Ricochet payload layer, as opposed to any end-to-end layer an application runs above it.