// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // Copyright by contributors to this project. // SPDX-License-Identifier: (Apache-2.0 OR MIT)
usecrate::client::MlsError; usecrate::crypto::{CipherSuiteProvider, SignatureSecretKey}; usecrate::group::GroupContext; usecrate::identity::SigningIdentity; usecrate::iter::wrap_iter; usecrate::tree_kem::math as tree_math; use alloc::vec; use alloc::vec::Vec; use itertools::Itertools; use mls_rs_codec::MlsEncode; use tree_math::{CopathNode, TreeIndex};
#[cfg(all(not(mls_build_async), feature = "rayon"))] use {crate::iter::ParallelIteratorExt, rayon::prelude::*};
#[cfg(mls_build_async)] use futures::{StreamExt, TryStreamExt};
#[cfg(feature = "std")] use std::collections::HashSet;
// Tree modifications are all done so we can update the tree hash and encrypt with the new context self.tree_kem_public
.update_hashes(&[self_index], cipher_suite_provider)
.await?;
let lca_index =
tree_math::leaf_lca_level(self_index.into(), sender_index.into()) as usize - 2;
letmut path = self.tree_kem_public.nodes.direct_copath(self_index); let leaf = CopathNode::new(self_index.into(), 0);
path.insert(0, leaf); let resolved_pos = self.find_resolved_pos(&path, lca_index)?;
let ct_pos = self.find_ciphertext_pos(path[lca_index].path, path[resolved_pos].path, added_leaves)?;
let lca_node = update_path.nodes[lca_index]
.as_ref()
.ok_or(MlsError::LcaNotFoundInDirectPath)?;
let ct = lca_node
.encrypted_path_secret
.get(ct_pos)
.ok_or(MlsError::LcaNotFoundInDirectPath)?;
let secret = self.private_key.secret_keys[resolved_pos]
.as_ref()
.ok_or(MlsError::UpdateErrorNoSecretKey)?;
let public = self
.tree_kem_public
.nodes
.borrow_node(path[resolved_pos].path)?
.as_ref()
.ok_or(MlsError::UpdateErrorNoSecretKey)?
.public_key();
let lca_path_secret =
PathSecret::decrypt(cipher_suite_provider, secret, public, context_bytes, ct).await?;
// Derive the rest of the secrets for the tree and assign to the proper nodes letmut node_secret_gen =
PathSecretGenerator::starting_with(cipher_suite_provider, lca_path_secret);
// Update secrets based on the decrypted path secret in the update self.private_key.secret_keys.resize(path.len() + 1, None);
for (i, update) in update_path.nodes.iter().enumerate().skip(lca_index) { iflet Some(update) = update { let secret = node_secret_gen.next_secret().await?;
// Verify the private key we calculated properly matches the public key we inserted into the tree. This guarantees // that we will be able to decrypt later. let (hpke_private, hpke_public) =
secret.to_hpke_key_pair(cipher_suite_provider).await?;
if hpke_public != update.public_key { return Err(MlsError::PubKeyMismatch);
}
// If we don't have the key, we should be an unmerged leaf at the resolved node. (If // we're not, an error will be thrown later.) ifself.private_key.secret_keys[lca_index].is_none() {
lca_index = 0;
}
#[cfg(test)] mod tests { usesuper::{tree_math, TreeKem}; usecrate::{
cipher_suite::CipherSuite,
client::test_utils::TEST_CIPHER_SUITE,
crypto::test_utils::{test_cipher_suite_provider, TestCryptoProvider},
extension::test_utils::TestExtension,
group::test_utils::{get_test_group_context, random_bytes},
identity::basic::BasicIdentityProvider,
tree_kem::{
leaf_node::{
test_utils::{get_basic_test_node_sig_key, get_test_capabilities},
ConfigProperties,
},
node::LeafIndex,
Capabilities, TreeKemPrivate, TreeKemPublic, UpdatePath, ValidatedUpdatePath,
},
ExtensionList,
}; use alloc::{format, vec, vec::Vec}; use mls_rs_codec::MlsEncode; use mls_rs_core::crypto::CipherSuiteProvider; use tree_math::TreeIndex;
// Verify that the tree is in the correct state after generating an update path fn verify_tree_update_path(
tree: &TreeKemPublic,
update_path: &UpdatePath,
index: LeafIndex,
capabilities: Option<Capabilities>,
extensions: Option<ExtensionList>,
) { // Make sure the update path is based on the direct path of the sender let direct_path = tree.nodes.direct_copath(index);
for (i, n) in direct_path.iter().enumerate() {
assert_eq!(
*tree
.nodes
.borrow_node(n.path)
.unwrap()
.as_ref()
.unwrap()
.public_key(),
update_path.nodes[i].public_key
);
}
// Verify that the leaf from the update path has been installed
assert_eq!(
tree.nodes.borrow_as_leaf(index).unwrap(),
&update_path.leaf_node
);
// Verify that updated capabilities were installed iflet Some(capabilities) = capabilities {
assert_eq!(update_path.leaf_node.capabilities, capabilities);
}
// Verify that update extensions were installed iflet Some(extensions) = extensions {
assert_eq!(update_path.leaf_node.extensions, extensions);
}
// Verify that we have a public keys up to the root let root = tree.total_leaf_count().root();
assert!(tree.nodes.borrow_node(root).unwrap().is_some());
}
// Make sure we have private values along the direct path, and the public keys match let path_iter = public_tree
.nodes
.direct_copath(index)
.into_iter()
.enumerate();
for (i, n) in path_iter { let secret_key = private_tree.secret_keys[i + 1].as_ref().unwrap();
let public_key = public_tree
.nodes
.borrow_node(n.path)
.unwrap()
.as_ref()
.unwrap()
.public_key();
let test_data = random_bytes(32);
let sealed = provider
.hpke_seal(public_key, &[], None, &test_data)
.await
.unwrap();
let opened = provider
.hpke_open(&sealed, secret_key, public_key, &[], None)
.await
.unwrap();
let (encap_node, encap_hpke_secret, encap_signer) =
get_basic_test_node_sig_key(cipher_suite, "encap").await;
// Build a test tree we can clone for all leaf nodes let (mut test_tree, mut encap_private_key) = TreeKemPublic::derive(
encap_node,
encap_hpke_secret,
&BasicIdentityProvider,
&Default::default(),
)
.await
.unwrap();
// Clone the tree for the first leaf, generate a new key package for that leaf letmut encap_tree = test_tree.clone();
let update_leaf_properties = ConfigProperties {
capabilities: capabilities.clone().unwrap_or_else(get_test_capabilities),
extensions: extensions.clone().unwrap_or_default(),
};
// Perform the encap function let encap_gen = TreeKem::new(&mut encap_tree, &mut encap_private_key)
.encap(
&mut get_test_group_context(42, cipher_suite).await,
&[],
&encap_signer,
update_leaf_properties,
None,
&cipher_suite_provider, #[cfg(test)]
&Default::default(),
)
.await
.unwrap();
// Verify that the state of the tree matches the produced update path
verify_tree_update_path(
&encap_tree,
&encap_gen.update_path,
LeafIndex(0),
capabilities,
extensions,
);
// Verify that the private key matches the data in the public key
verify_tree_private_path(&cipher_suite, &encap_tree, &encap_private_key, LeafIndex(0))
.await;
// Apply the update path to the rest of the leaf nodes using the decap function let validated_update_path = ValidatedUpdatePath {
leaf_node: encap_gen.update_path.leaf_node,
nodes: unfiltered_nodes,
};
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.