Skip to content

0.0.10 — cipher-suite binding, last-resort key packages, total session archive (first release off main)

Choose a tag to compare

@germ-mark germ-mark released this 10 Jul 01:10
030197e

Integration build for downstream (AbstractTwoMLS, germDM feat/PQ) — built straight from main at 030197e (all PRs merged; #41, #42/#37, #43, #44 and the provider-agnostic landing #36). Unlike 0.0.8/0.0.9 (cut from throwaway integration branches), this is the first release off main now that every open PR has landed.

Built with Apple CryptoKit backing both halves (--features cryptokit), Xcode 27 beta 3 toolchain.

⚠️ FFI surface changed — binding and binary MUST pair

Take the attached two_mls_pq.swift from this release; a 0.0.9-era binding against this binary fails fast. AbstractTwoMLS's bindingContractVersion() assertion at first client/invitation construction catches a stale pairing immediately, and uniffi's own load-time checksum rejects a mismatched binding outright.

Binding contract 3 → 5 (two increments since 0.0.9):

  • v4 (#41)TwoMlsPqError gained CipherSuiteMismatch (a peer key package/welcome carries a suite pair that doesn't match the session's fixed suite); MlsCipherSuite.is_supported renamed to is_combiner_pq (the name always meant "is the PQ combiner half", not "is a supported suite"); AgentState renamed to PrincipalState (part of #44).
  • v5 (#43)TwoMlsPqError gained InvitationSpent (a single-use invitation whose key package was already consumed rejects every further receive, from any remote — distinct from the per-remote DuplicateWelcome); generate_invitation gained a last_resort flag (that signature change is also caught by uniffi's load-time checksum).

⚠️ Persisted state is not portable across pre-release builds

Archive layouts changed again (total/self-contained session archive, retained key packages folded in). Per the pre-release policy the archive version bytes are intentionally not bumped (still pinned to 1), so old archives are not auto-detected — regenerate all persisted sessions and invitations from this release rather than loading 0.0.9-era archives.

What's new since 0.0.9

  • Cipher-suite binding, hardened (#41): the classical+PQ cipher-suite pair is now a fixed, validated property of a session — established once at construction and enforced on every peer key package and welcome. A wrong-suite peer is rejected with CipherSuiteMismatch, distinct from PqNotAvailable (peer offers no PQ half) and UnsupportedCipherSuite (a local provider gap). The suite classifier now covers all RFC 9420 suites and the PqNotAvailable diagnostic is corrected.
  • Last-resort key packages + invitation key-package lifecycle (#43): invitations are explicitly single-use or last-resort (generate_invitation(last_resort:)). A single-use invitation's key package is consumed on the first accepted session and every later receive fails with InvitationSpent; a last-resort invitation never spends. Session key packages self-prune on use (mls-rs delete + store honors it); invitation key packages are erased explicitly. The session archive now carries the client's retained key packages so restore is self-contained.
  • Total, self-contained session archive/restore (#37, #42): archive/restore is parameterless and total — no external state threaded in at restore, id-based stage_rotation, retained key packages included.
  • Principal terminology (#44): the Identity/Agent concept is renamed to Principal, CommProtocol-agnostic (AgentStatePrincipalState; a terminology bridge is documented on the client).
  • Session rendezvous (#38) and a rotation listen-window regression test (#39, guarding the 0.0.9 pruning fix).
  • Provider-agnostic core fully landed on main (#36) — both halves run on Apple CryptoKit in this binary (awslc remains the portable/Linux-CI configuration); plus doc-staleness fixes across README/book/benches (#40).

Carried over from 0.0.9: crypto-provider-agnostic core (both halves on CryptoKit), session persistence groundwork, A.5 PQ rekey, invitation forward table (spawn-token routing), raw-digest FFI convention, honest epoch reporting, bindingContractVersion() canary.

Integration

.binaryTarget(
    name: "TwoMLSPQrs",
    url: "https://github.com/germ-network/TwoMLSPQ/releases/download/0.0.10/TwoMLSPQ.xcframework.zip",
    checksum: "f83f43d1d35afadcfc9adbadae290b6c83209c9c53e6680fcaa4d243108a3acf"
)

Copy the attached two_mls_pq.swift into your wrapper target from this same release, and bump the consumer's expectedBindingContract to 5.

🤖 Generated with Claude Code