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,