Files
amethyst/quartz
Claude 236ebfe4d6 fix(marmot): apply RFC 9420 PrivateMessageContent framing to app messages
MlsGroup.encrypt / decrypt used to pass raw application bytes as the
AEAD plaintext — no PrivateMessageContent framing, no FramedContentTBS
signature, no padding. Amethyst ↔ Amethyst round-trips worked (both
sides skipped it the same way) but every cross-implementation message
was unreadable.

Per RFC 9420 §6.3.1 the AEAD plaintext for an application PrivateMessage
is the serialized PrivateMessageContent:

    opaque application_data<V>;     // varint-prefixed payload
    opaque signature<V>;            // FramedContentAuthData.signature
    opaque padding[N];              // zero padding (N may be 0)

where the signature is `SignWithLabel(., "FramedContentTBS",
FramedContentTBS)` computed over (version || wire_format ||
FramedContent || GroupContext). This change wires both sides up
end-to-end:

* encrypt now builds FramedContentTBS via the new
  buildApplicationFramedContentTbs helper, signs it with the caller's
  signature private key, and writes application_data<V> || signature<V>
  (zero-length padding) as the AEAD plaintext.
* decrypt parses application_data<V> and signature<V> from the AEAD
  plaintext, asserts all remaining bytes are zero padding, rebuilds
  FramedContentTBS for the sender's leaf, and verifies the Ed25519
  signature against that leaf's signatureKey before returning the
  application payload. Non-application PrivateMessages now error
  (commit/proposal-inside-PrivateMessage was never correctly handled
  by the old raw-bytes path either; leaving that for a follow-up).

Un-Ignore MdkWelcomeInteropTest.testBobDecryptsMdkApplicationMessagesFromAlice
— Amethyst now decrypts and verifies each of the three openmls-authored
application messages against Alice's committer leaf.

https://claude.ai/code/session_01HfHdd5S5rvxUW2ihEpLGJr
2026-04-21 12:49:01 +00:00
..