From 548160a322b2c1297770017031e79da1e28ed7fe Mon Sep 17 00:00:00 2001 From: "Mark (dev)" Date: Tue, 28 Jul 2026 11:32:11 -0700 Subject: [PATCH] Bump mls-rs to germ-integration; keep APP_DATA_UPDATE pathless MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moves the pin from ec69dc25 (germ-shadow-safe-exporter) to b43703fb (germ-integration): upstream main resynced to 09761fa, plus the Germ crypto providers, the recovered cryptokit rpath build fix, the Safe Extensions exporter tree, and attachment CEK derivation. Five pins plus the [patch.crates-io] redirect move together. That commit is CI-green on the fork across ubuntu-latest, macos-26, each-feature, no_std/thumbv6m, mls_build_async, lint and audit. The resync requires one code change. awslabs/mls-rs#364 added MlsRules::custom_proposal_requires_update_path with a default of `true`, so APP_DATA_UPDATE — an attestation that changes no membership — started forcing an updatePath. On the PQ half that means an ML-KEM updatePath rides a commit that must be pathless: the bind commit grew to 4042 B against a 1088 B bound, failing test_bind_pq_commit_is_pathless. TwoMlsRules now overrides the hook to `false`, restoring the behaviour apq was written against. Where a path IS wanted — the FULL commit discharging an owed bind on the classical half — commit_options still pins it explicitly, which is where that decision belongs. Also drops serde entirely: the mls-rs feature and the unused [workspace.dependencies] entry. No .rs file in the workspace references serde and no member crate declares it. mls-rs's serde derives are cfg_attr-attached and never participate in MlsEncode/MlsDecode, so this cannot change the stored format — archiving goes through the crate's own wire structs, untouched here. Verified: apq 51+8+9, two-mls-pq 307 (cryptokit), fmt, taplo and clippy -D warnings clean. Group state written by the old pin still loads (fixtures in mls-rs). Co-Authored-By: Claude Opus 5 --- .changeset/cyan-donkeys-shave.md | 29 +++++++++++++++++++++++++++++ rust/Cargo.toml | 12 +++++------- rust/apq/src/rules.rs | 25 ++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 8 deletions(-) create mode 100644 .changeset/cyan-donkeys-shave.md diff --git a/.changeset/cyan-donkeys-shave.md b/.changeset/cyan-donkeys-shave.md new file mode 100644 index 0000000..a50be3d --- /dev/null +++ b/.changeset/cyan-donkeys-shave.md @@ -0,0 +1,29 @@ +--- +"@germ-network/two-mls-pq": patch +--- + +Bump mls-rs to the `germ-integration` pin, and keep `APP_DATA_UPDATE` pathless + +The fork's `main` was resynced with upstream `awslabs/mls-rs`, and the Germ changes were +recomposed on top of it as `germ-integration`. The pin moves from `ec69dc25` to +`c6ede1ce`, picking up the crypto providers, a recovered cryptokit build fix, the Safe +Extensions exporter tree, and attachment CEK derivation. + +The resync requires one behavioural fix. Upstream added +`MlsRules::custom_proposal_requires_update_path` with a default of `true`, so +`APP_DATA_UPDATE` — an attestation that changes no group membership — began forcing an +updatePath. On the PQ half that put an ML-KEM updatePath on a commit that must stay +pathless: the bind commit grew past a whole ML-KEM-768 ciphertext. `TwoMlsRules` now +overrides the hook to `false`, restoring the behaviour `apq` was written against. Where a +path _is_ wanted — the FULL commit discharging an owed bind on the classical half — +`commit_options` still pins it explicitly, which is where that decision belongs. + +Also drops `serde` — both the `mls-rs` feature and the unused `[workspace.dependencies]` +entry. Nothing in the workspace consumes either: no `.rs` file references serde and no +member crate declares it. mls-rs's serde derives are `cfg_attr`-attached, so they never +participate in `MlsEncode`/`MlsDecode`; removing the feature cannot move a byte of the +stored format. Archiving goes through the crate's own `MlsEncode`/`MlsDecode` wire +structs, which are untouched. + +No wire, FFI, or error-variant change. Group state written by the previous pin still +loads. diff --git a/rust/Cargo.toml b/rust/Cargo.toml index ff664e1..0b4c50e 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -36,22 +36,20 @@ strip = "symbols" mls-rs = { version = "0.55", features = [ "psk", "private_message", - "serde", "safe_extensions", ] } -mls-rs-core = { version = "0.27", git = "https://github.com/germ-network/mls-rs", rev = "ec69dc251db66ae6eb117079564814998bb55dec" } -mls-rs-crypto-awslc = { version = "0.25", git = "https://github.com/germ-network/mls-rs", rev = "ec69dc251db66ae6eb117079564814998bb55dec", features = [ +mls-rs-core = { version = "0.27", git = "https://github.com/germ-network/mls-rs", rev = "b43703fbf1244140fe9e5e43e41c373e34191758" } +mls-rs-crypto-awslc = { version = "0.25", git = "https://github.com/germ-network/mls-rs", rev = "b43703fbf1244140fe9e5e43e41c373e34191758", features = [ "post-quantum", ] } -mls-rs-crypto-cryptokit = { version = "0.11", git = "https://github.com/germ-network/mls-rs", rev = "ec69dc251db66ae6eb117079564814998bb55dec", features = [ +mls-rs-crypto-cryptokit = { version = "0.11", git = "https://github.com/germ-network/mls-rs", rev = "b43703fbf1244140fe9e5e43e41c373e34191758", features = [ "post-quantum", ] } -mls-rs-crypto-traits = { version = "0.22", git = "https://github.com/germ-network/mls-rs", rev = "ec69dc251db66ae6eb117079564814998bb55dec" } -serde = { version = "1", features = ["derive"] } +mls-rs-crypto-traits = { version = "0.22", git = "https://github.com/germ-network/mls-rs", rev = "b43703fbf1244140fe9e5e43e41c373e34191758" } sha2 = "0.10" thiserror = "1" uniffi = "0.31" zeroize = { version = "1", features = ["derive"] } [patch.crates-io] -mls-rs = { git = "https://github.com/germ-network/mls-rs", rev = "ec69dc251db66ae6eb117079564814998bb55dec" } +mls-rs = { git = "https://github.com/germ-network/mls-rs", rev = "b43703fbf1244140fe9e5e43e41c373e34191758" } diff --git a/rust/apq/src/rules.rs b/rust/apq/src/rules.rs index 4c68a08..815b9a2 100644 --- a/rust/apq/src/rules.rs +++ b/rust/apq/src/rules.rs @@ -18,7 +18,7 @@ use mls_rs::{ error::IntoAnyError, - group::{GroupContext, Roster, Sender}, + group::{proposal::ProposalType, GroupContext, Roster, Sender}, mls_rules::{CommitDirection, CommitOptions, CommitSource, EncryptionOptions, ProposalBundle}, MlsRules, }; @@ -74,6 +74,29 @@ pub struct TwoMlsRules; impl MlsRules for TwoMlsRules { type Error = RuleError; + /// `APP_DATA_UPDATE` never forces an updatePath. + /// + /// It carries an attestation only — it does not change group membership — so RFC 9420 + /// §12.4 leaves the path at the committer's discretion. The PQ half depends on that: + /// a bind commit must stay pathless, or it carries an ML-KEM updatePath and grows past + /// a whole ML-KEM-768 ciphertext (`test_bind_pq_commit_is_pathless`). + /// + /// Where a path IS wanted on the classical half — the FULL commit discharging an owed + /// bind — [`Self::commit_options`] pins it explicitly. That is the one place this + /// decision belongs. + /// + /// mls-rs added this hook in awslabs/mls-rs#364 with a default of `true`, which would + /// silently put a path on every custom proposal. Overriding it restores the behaviour + /// this crate was written against; it is not a new policy. + fn custom_proposal_requires_update_path(&self, custom_proposal_type: ProposalType) -> bool { + debug_assert_eq!( + custom_proposal_type, APP_DATA_UPDATE, + "filter_proposals rejects every other custom proposal type" + ); + let _ = custom_proposal_type; + false + } + fn filter_proposals( &self, _direction: CommitDirection,