Skip to main content

P5. Cascade wire encoding

Work in progress — not audited

Normative protocol documentation for the current implementation. Not independently audited.

P5.1 Scope

Defines the cascadedPayload object inside encryptedMessage for EnkryptedChat-Profile-v0. Cryptographic primitives of each layer are defined by Signal, MLS, ML-KEM, and Web Crypto — this chapter defines composition and serialization only.

P5.2 Layer processing order

CascadingCipherManager applies layers in registration order:

plaintext: Uint8Array
→ layer[0] MLS encrypt
→ layer[1] Signal encrypt (input: base64 decode of prior output)
→ layer[2] ML-KEM encrypt
→ layer[3] AES-GCM encrypt
→ finalCiphertext: Uint8Array

Between layers, intermediate ciphertext is represented as base64 strings internally; finalCiphertext is raw bytes.

Decrypt MUST reverse layer order.

P5.3 cascadedPayload schema

FieldJSON typeRequiredDescription
finalCiphertextarray of numbersMUST (unless chunked)Each element 0–255
layersarray of objectsMUSTPer-layer metadata
layerParametersarray of objectsMUSTPer-layer decrypt params
totalProcessingTimenumberMUSTMilliseconds
originalSizenumberMUSTPlaintext byte length
finalSizenumberMUSTCiphertext byte length
timestampnumberSHOULDUnix ms; informative for interop debugging

Decrypt MUST use finalCiphertext and layerParameters. layers[] statistics are informative but SHOULD be included for diagnostics.

P5.3.1 layers[] entry (LayerMetadata)

FieldTypeDescription
algorithmstringe.g. MLS, Signal, ML-KEM, AES-GCM-256
versionstringLayer implementation version
timestampnumberLayer processing time
inputSizenumberBytes in
outputSizenumberBytes out
processingTimenumberms
metadataobjectMAY — algorithm-specific

P5.3.2 layerParameters[]

Opaque per-layer decryption parameters (keys, nonces, Signal state handles). Structure is layer-specific; auditors SHOULD inspect cryptography module layer implementations.

P5.4 Key material (implementation notes)

For each encrypted message, MLSProvider builds CipherKeys:

Layer key nameSource
MLSmlsManager + groupId
SignalWASM session after handshake
ML-KEM-768peerMLKEMKeyPairsRef for target peer
AES-GCM-256Password derived from sorted peer IDs + optional rotation suffix

AES password format (normative for v0 implementation):

1:1 chat: "{sortedPeerA}-{sortedPeerB}-aes-key" or with "-{rotationTimestamp}" suffix
Group: "{localPeerId}-group-aes-key" (+ suffix)

P5.5 Chunking cascaded ciphertext

ConstantValue
CHUNK_SIZE8192 bytes (8 KiB)

If finalCiphertext.lengthCHUNK_SIZE:

  • Send single PDU with full cascadedPayload.

If larger:

  1. Set chunked: true, messageId, totalChunks.
  2. Omit finalCiphertext from inline payload; send chunks[] arrays.
  3. Optionally use __mls_chunk__ transport (P4).

Receivers MUST reassemble chunks in order before decrypt.

P5.6 Failure behavior

FailureBehavior
Missing layer keysThrow CascadingCipherError; encrypt path falls back to MLS-only
Layer decrypt/auth failMUST NOT emit plaintext; abort message (P6)
Zero layers configuredMUST NOT call encrypt

P5.7 Reference implementation