From f8bfccfd4b6b35fd7adacd4cdc2f048779961424 Mon Sep 17 00:00:00 2001 From: Richard Barnes Date: Thu, 11 Jul 2024 17:00:16 -0400 Subject: [PATCH] Add tests for leaf node epoch logic --- mls-rs/src/group/proposal_cache.rs | 78 +++++++++++++++++++++--------- mls-rs/src/tree_kem/leaf_node.rs | 6 ++- 2 files changed, 61 insertions(+), 23 deletions(-) diff --git a/mls-rs/src/group/proposal_cache.rs b/mls-rs/src/group/proposal_cache.rs index 5737743f..957aaebb 100644 --- a/mls-rs/src/group/proposal_cache.rs +++ b/mls-rs/src/group/proposal_cache.rs @@ -661,7 +661,8 @@ mod tests { use crate::group::proposal_ref::test_utils::auth_content_from_proposal; use crate::group::proposal_ref::ProposalRef; use crate::group::{ - AddProposal, AuthenticatedContent, Content, ExternalInit, GroupContext, Proposal, + AddProposal, AuthenticatedContent, Content, ExternalInit, GroupContext, + LeafNodeEpochExt, Proposal, ProposalOrRef, ReInitProposal, RemoveProposal, Roster, Sender, UpdateProposal, }; use crate::key_package::test_utils::test_key_package_with_signer; @@ -778,15 +779,40 @@ mod tests { .unwrap()[0] } + #[cfg(feature = "replace_proposal")] #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] - async fn update_leaf_node(name: &str, leaf_index: u32) -> LeafNode { + async fn update_member(tree: &mut TreeKemPublic, leaf_index: u32, leaf_node: LeafNode) { + tree.update_leaf( + leaf_index, + leaf_node, + &BasicIdentityProvider, + &test_cipher_suite_provider(TEST_CIPHER_SUITE), + ) + .await + .unwrap(); + } + + #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] + async fn update_leaf_node(name: &str, leaf_index: u32, epoch: Option) -> LeafNode { let (mut leaf, _, signer) = get_basic_test_node_sig_key(TEST_CIPHER_SUITE, name).await; + let properties = default_properties(); + + #[cfg(feature = "replace_proposal")] + let mut properties = properties; + + #[cfg(feature = "replace_proposal")] + if let Some(epoch) = epoch { + properties + .extensions + .set(LeafNodeEpochExt::new(epoch).into_extension().unwrap()); + } + leaf.update( &test_cipher_suite_provider(TEST_CIPHER_SUITE), TEST_GROUP, leaf_index, - Some(default_properties()), + properties, None, &signer, ) @@ -3089,21 +3115,18 @@ mod tests { assert_eq!(processed_proposals.1.unused_proposals, vec![replace_info]); } - // TODO(RLB) Scenario - // * Alice creates the group - // * Bob joins - // * Bob creates a LeafNode - // * Bob commits - // * Alice sends Replace {1, bob_leaf_node} - #[cfg(feature = "replace_proposal")] #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))] async fn receiving_replace_for_old_epoch_fails() { - /* let (alice, mut tree) = new_tree("alice").await; - let _bob = add_member(&mut tree, "bob").await; + add_member(&mut tree, "bob").await; - let replace = Proposal::Replace(make_replace_proposal(1, "carol")); + // Fast-forward Bob to epoch 42 + let bob_leaf_new_epoch = update_leaf_node("bob", 1, Some(42)); + update_member(&mut tree, 1, bob_leaf_new_epoch); + + // Try to replace Bob with a LeafNode from epoch 21 + let replace = Proposal::Replace(make_replace_proposal_custom(1, "bob", 21)); let replace_ref = make_proposal_ref(&replace, alice).await; let res = CommitReceiver::new( @@ -3117,17 +3140,20 @@ mod tests { .await; assert_matches!(res, Err(MlsError::InvalidSuccessor)); - */ } #[cfg(feature = "replace_proposal")] #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))] async fn sending_replace_for_old_epoch_filters_it_out() { - /* let (alice, mut tree) = new_tree("alice").await; - let _bob = add_member(&mut tree, "bob").await; + add_member(&mut tree, "bob").await; - let replace = Proposal::Replace(make_replace_proposal(1, "carol")); + // Fast-forward Bob to epoch 42 + let bob_leaf_new_epoch = update_leaf_node("bob", 1, Some(42)); + update_member(&mut tree, 1, bob_leaf_new_epoch); + + // Try to replace Bob with a LeafNode from epoch 21 + let replace = Proposal::Replace(make_replace_proposal_custom(1, "bob", 21)); let replace_info = make_proposal_info(&replace, alice).await; let processed_proposals = @@ -3141,7 +3167,6 @@ mod tests { #[cfg(feature = "state_update")] assert_eq!(processed_proposals.1.unused_proposals, vec![replace_info]); - */ } #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))] @@ -3814,7 +3839,7 @@ mod tests { .await .unwrap(); - let bob_new_leaf = update_leaf_node("bob", 1).await; + let bob_new_leaf = update_leaf_node("bob", 1, None).await; let pk1_to_pk2 = Proposal::Update(UpdateProposal { leaf_node: alice_new_leaf.clone(), @@ -4560,7 +4585,7 @@ mod tests { #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] async fn make_update_proposal(name: &str) -> UpdateProposal { UpdateProposal { - leaf_node: update_leaf_node(name, 1).await, + leaf_node: update_leaf_node(name, 1, None).await, } } @@ -4569,14 +4594,23 @@ mod tests { async fn make_replace_proposal(index: u32, name: &str) -> ReplaceProposal { ReplaceProposal { to_replace: LeafIndex(index), - leaf_node: update_leaf_node(name, index).await, + leaf_node: update_leaf_node(name, index, None).await, + } + } + + #[cfg(feature = "replace_proposal")] + #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] + async fn make_replace_proposal_custom(index: u32, name: &str, epoch: u64) -> ReplaceProposal { + ReplaceProposal { + to_replace: LeafIndex(index), + leaf_node: update_leaf_node(name, index, Some(epoch)).await, } } #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] async fn make_update_proposal_custom(name: &str, leaf_index: u32) -> UpdateProposal { UpdateProposal { - leaf_node: update_leaf_node(name, leaf_index).await, + leaf_node: update_leaf_node(name, leaf_index, None).await, } } diff --git a/mls-rs/src/tree_kem/leaf_node.rs b/mls-rs/src/tree_kem/leaf_node.rs index d3785d64..b0a5b49d 100644 --- a/mls-rs/src/tree_kem/leaf_node.rs +++ b/mls-rs/src/tree_kem/leaf_node.rs @@ -277,7 +277,7 @@ pub(crate) mod test_utils { identity::test_utils::{get_test_signing_identity, BasicWithCustomProvider}, }; - use crate::extension::ApplicationIdExt; + use crate::extension::{ApplicationIdExt, ExtensionType}; use super::*; @@ -392,6 +392,10 @@ pub(crate) mod test_utils { CredentialType::from(BasicWithCustomProvider::CUSTOM_CREDENTIAL_TYPE), ], cipher_suites: TestCryptoProvider::all_supported_cipher_suites(), + + #[cfg(feature = "replace_proposal")] + extensions: vec![ExtensionType::LEAF_NODE_EPOCH], + ..Default::default() } }