1 // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 // Copyright by contributors to this project. 3 // SPDX-License-Identifier: (Apache-2.0 OR MIT) 4 5 use super::{parent_hash::ParentHash, Capabilities, Lifetime}; 6 use crate::client::MlsError; 7 use crate::crypto::{CipherSuiteProvider, HpkePublicKey, HpkeSecretKey, SignatureSecretKey}; 8 use crate::{identity::SigningIdentity, signer::Signable, ExtensionList}; 9 use alloc::vec::Vec; 10 use core::fmt::{self, Debug}; 11 use mls_rs_codec::{MlsDecode, MlsEncode, MlsSize}; 12 use mls_rs_core::error::IntoAnyError; 13 14 #[derive(Debug, Clone, MlsSize, MlsEncode, MlsDecode, PartialEq, Eq)] 15 #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] 16 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 17 #[repr(u8)] 18 pub enum LeafNodeSource { 19 KeyPackage(Lifetime) = 1u8, 20 Update = 2u8, 21 Commit(ParentHash) = 3u8, 22 } 23 24 #[derive(Clone, MlsSize, MlsEncode, MlsDecode, PartialEq)] 25 #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] 26 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 27 #[non_exhaustive] 28 pub struct LeafNode { 29 pub public_key: HpkePublicKey, 30 pub signing_identity: SigningIdentity, 31 pub capabilities: Capabilities, 32 pub leaf_node_source: LeafNodeSource, 33 pub extensions: ExtensionList, 34 #[mls_codec(with = "mls_rs_codec::byte_vec")] 35 #[cfg_attr(feature = "serde", serde(with = "mls_rs_core::vec_serde"))] 36 pub signature: Vec<u8>, 37 } 38 39 impl Debug for LeafNode { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 41 f.debug_struct("LeafNode") 42 .field("public_key", &self.public_key) 43 .field("signing_identity", &self.signing_identity) 44 .field("capabilities", &self.capabilities) 45 .field("leaf_node_source", &self.leaf_node_source) 46 .field("extensions", &self.extensions) 47 .field( 48 "signature", 49 &mls_rs_core::debug::pretty_bytes(&self.signature), 50 ) 51 .finish() 52 } 53 } 54 55 #[derive(Clone, Debug)] 56 pub struct ConfigProperties { 57 pub capabilities: Capabilities, 58 pub extensions: ExtensionList, 59 } 60 61 impl LeafNode { 62 #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] generate<CSP>( cipher_suite_provider: &CSP, properties: ConfigProperties, signing_identity: SigningIdentity, signer: &SignatureSecretKey, lifetime: Lifetime, ) -> Result<(Self, HpkeSecretKey), MlsError> where CSP: CipherSuiteProvider,63 pub async fn generate<CSP>( 64 cipher_suite_provider: &CSP, 65 properties: ConfigProperties, 66 signing_identity: SigningIdentity, 67 signer: &SignatureSecretKey, 68 lifetime: Lifetime, 69 ) -> Result<(Self, HpkeSecretKey), MlsError> 70 where 71 CSP: CipherSuiteProvider, 72 { 73 let (secret_key, public_key) = cipher_suite_provider 74 .kem_generate() 75 .await 76 .map_err(|e| MlsError::CryptoProviderError(e.into_any_error()))?; 77 78 let mut leaf_node = LeafNode { 79 public_key, 80 signing_identity, 81 capabilities: properties.capabilities, 82 leaf_node_source: LeafNodeSource::KeyPackage(lifetime), 83 extensions: properties.extensions, 84 signature: Default::default(), 85 }; 86 87 leaf_node.grease(cipher_suite_provider)?; 88 89 leaf_node 90 .sign( 91 cipher_suite_provider, 92 signer, 93 &LeafNodeSigningContext::default(), 94 ) 95 .await?; 96 97 Ok((leaf_node, secret_key)) 98 } 99 100 #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] update<P: CipherSuiteProvider>( &mut self, cipher_suite_provider: &P, group_id: &[u8], leaf_index: u32, new_properties: ConfigProperties, signing_identity: Option<SigningIdentity>, signer: &SignatureSecretKey, ) -> Result<HpkeSecretKey, MlsError>101 pub async fn update<P: CipherSuiteProvider>( 102 &mut self, 103 cipher_suite_provider: &P, 104 group_id: &[u8], 105 leaf_index: u32, 106 new_properties: ConfigProperties, 107 signing_identity: Option<SigningIdentity>, 108 signer: &SignatureSecretKey, 109 ) -> Result<HpkeSecretKey, MlsError> { 110 let (secret, public) = cipher_suite_provider 111 .kem_generate() 112 .await 113 .map_err(|e| MlsError::CryptoProviderError(e.into_any_error()))?; 114 115 self.public_key = public; 116 self.capabilities = new_properties.capabilities; 117 self.extensions = new_properties.extensions; 118 self.leaf_node_source = LeafNodeSource::Update; 119 120 self.grease(cipher_suite_provider)?; 121 122 if let Some(signing_identity) = signing_identity { 123 self.signing_identity = signing_identity; 124 } 125 126 self.sign( 127 cipher_suite_provider, 128 signer, 129 &(group_id, leaf_index).into(), 130 ) 131 .await?; 132 133 Ok(secret) 134 } 135 136 #[allow(clippy::too_many_arguments)] 137 #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] commit<P: CipherSuiteProvider>( &mut self, cipher_suite_provider: &P, group_id: &[u8], leaf_index: u32, new_properties: ConfigProperties, new_signing_identity: Option<SigningIdentity>, signer: &SignatureSecretKey, ) -> Result<HpkeSecretKey, MlsError>138 pub async fn commit<P: CipherSuiteProvider>( 139 &mut self, 140 cipher_suite_provider: &P, 141 group_id: &[u8], 142 leaf_index: u32, 143 new_properties: ConfigProperties, 144 new_signing_identity: Option<SigningIdentity>, 145 signer: &SignatureSecretKey, 146 ) -> Result<HpkeSecretKey, MlsError> { 147 let (secret, public) = cipher_suite_provider 148 .kem_generate() 149 .await 150 .map_err(|e| MlsError::CryptoProviderError(e.into_any_error()))?; 151 152 self.public_key = public; 153 self.capabilities = new_properties.capabilities; 154 self.extensions = new_properties.extensions; 155 156 if let Some(new_signing_identity) = new_signing_identity { 157 self.signing_identity = new_signing_identity; 158 } 159 160 self.sign( 161 cipher_suite_provider, 162 signer, 163 &(group_id, leaf_index).into(), 164 ) 165 .await?; 166 167 Ok(secret) 168 } 169 } 170 171 #[derive(Debug)] 172 struct LeafNodeTBS<'a> { 173 public_key: &'a HpkePublicKey, 174 signing_identity: &'a SigningIdentity, 175 capabilities: &'a Capabilities, 176 leaf_node_source: &'a LeafNodeSource, 177 extensions: &'a ExtensionList, 178 group_id: Option<&'a [u8]>, 179 leaf_index: Option<u32>, 180 } 181 182 impl<'a> MlsSize for LeafNodeTBS<'a> { mls_encoded_len(&self) -> usize183 fn mls_encoded_len(&self) -> usize { 184 self.public_key.mls_encoded_len() 185 + self.signing_identity.mls_encoded_len() 186 + self.capabilities.mls_encoded_len() 187 + self.leaf_node_source.mls_encoded_len() 188 + self.extensions.mls_encoded_len() 189 + self 190 .group_id 191 .as_ref() 192 .map_or(0, mls_rs_codec::byte_vec::mls_encoded_len) 193 + self.leaf_index.map_or(0, |i| i.mls_encoded_len()) 194 } 195 } 196 197 impl<'a> MlsEncode for LeafNodeTBS<'a> { mls_encode(&self, writer: &mut Vec<u8>) -> Result<(), mls_rs_codec::Error>198 fn mls_encode(&self, writer: &mut Vec<u8>) -> Result<(), mls_rs_codec::Error> { 199 self.public_key.mls_encode(writer)?; 200 self.signing_identity.mls_encode(writer)?; 201 self.capabilities.mls_encode(writer)?; 202 self.leaf_node_source.mls_encode(writer)?; 203 self.extensions.mls_encode(writer)?; 204 205 if let Some(ref group_id) = self.group_id { 206 mls_rs_codec::byte_vec::mls_encode(group_id, writer)?; 207 } 208 209 if let Some(leaf_index) = self.leaf_index { 210 leaf_index.mls_encode(writer)?; 211 } 212 213 Ok(()) 214 } 215 } 216 217 #[derive(Clone, Debug, Default)] 218 pub(crate) struct LeafNodeSigningContext<'a> { 219 pub group_id: Option<&'a [u8]>, 220 pub leaf_index: Option<u32>, 221 } 222 223 impl<'a> From<(&'a [u8], u32)> for LeafNodeSigningContext<'a> { from((group_id, leaf_index): (&'a [u8], u32)) -> Self224 fn from((group_id, leaf_index): (&'a [u8], u32)) -> Self { 225 Self { 226 group_id: Some(group_id), 227 leaf_index: Some(leaf_index), 228 } 229 } 230 } 231 232 impl<'a> Signable<'a> for LeafNode { 233 const SIGN_LABEL: &'static str = "LeafNodeTBS"; 234 235 type SigningContext = LeafNodeSigningContext<'a>; 236 signature(&self) -> &[u8]237 fn signature(&self) -> &[u8] { 238 &self.signature 239 } 240 signable_content( &self, context: &Self::SigningContext, ) -> Result<Vec<u8>, mls_rs_codec::Error>241 fn signable_content( 242 &self, 243 context: &Self::SigningContext, 244 ) -> Result<Vec<u8>, mls_rs_codec::Error> { 245 LeafNodeTBS { 246 public_key: &self.public_key, 247 signing_identity: &self.signing_identity, 248 capabilities: &self.capabilities, 249 leaf_node_source: &self.leaf_node_source, 250 extensions: &self.extensions, 251 group_id: context.group_id, 252 leaf_index: context.leaf_index, 253 } 254 .mls_encode_to_vec() 255 } 256 write_signature(&mut self, signature: Vec<u8>)257 fn write_signature(&mut self, signature: Vec<u8>) { 258 self.signature = signature 259 } 260 } 261 262 #[cfg(test)] 263 pub(crate) mod test_utils { 264 use alloc::vec; 265 use mls_rs_core::identity::{BasicCredential, CredentialType}; 266 267 use crate::{ 268 cipher_suite::CipherSuite, 269 crypto::test_utils::{test_cipher_suite_provider, TestCryptoProvider}, 270 identity::test_utils::{get_test_signing_identity, BasicWithCustomProvider}, 271 }; 272 273 use crate::extension::ApplicationIdExt; 274 275 use super::*; 276 277 #[allow(unused)] 278 #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] get_test_node( cipher_suite: CipherSuite, signing_identity: SigningIdentity, secret: &SignatureSecretKey, capabilities: Option<Capabilities>, extensions: Option<ExtensionList>, ) -> (LeafNode, HpkeSecretKey)279 pub async fn get_test_node( 280 cipher_suite: CipherSuite, 281 signing_identity: SigningIdentity, 282 secret: &SignatureSecretKey, 283 capabilities: Option<Capabilities>, 284 extensions: Option<ExtensionList>, 285 ) -> (LeafNode, HpkeSecretKey) { 286 get_test_node_with_lifetime( 287 cipher_suite, 288 signing_identity, 289 secret, 290 capabilities.unwrap_or_else(get_test_capabilities), 291 extensions.unwrap_or_default(), 292 Lifetime::years(1).unwrap(), 293 ) 294 .await 295 } 296 297 #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] get_test_node_with_lifetime( cipher_suite: CipherSuite, signing_identity: SigningIdentity, secret: &SignatureSecretKey, capabilities: Capabilities, extensions: ExtensionList, lifetime: Lifetime, ) -> (LeafNode, HpkeSecretKey)298 pub async fn get_test_node_with_lifetime( 299 cipher_suite: CipherSuite, 300 signing_identity: SigningIdentity, 301 secret: &SignatureSecretKey, 302 capabilities: Capabilities, 303 extensions: ExtensionList, 304 lifetime: Lifetime, 305 ) -> (LeafNode, HpkeSecretKey) { 306 let properties = ConfigProperties { 307 capabilities, 308 extensions, 309 }; 310 311 LeafNode::generate( 312 &test_cipher_suite_provider(cipher_suite), 313 properties, 314 signing_identity, 315 secret, 316 lifetime, 317 ) 318 .await 319 .unwrap() 320 } 321 322 #[allow(unused)] 323 #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] get_basic_test_node(cipher_suite: CipherSuite, id: &str) -> LeafNode324 pub async fn get_basic_test_node(cipher_suite: CipherSuite, id: &str) -> LeafNode { 325 get_basic_test_node_sig_key(cipher_suite, id).await.0 326 } 327 328 #[allow(unused)] default_properties() -> ConfigProperties329 pub fn default_properties() -> ConfigProperties { 330 ConfigProperties { 331 capabilities: get_test_capabilities(), 332 extensions: Default::default(), 333 } 334 } 335 336 #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] get_basic_test_node_capabilities( cipher_suite: CipherSuite, id: &str, capabilities: Capabilities, ) -> (LeafNode, HpkeSecretKey, SignatureSecretKey)337 pub async fn get_basic_test_node_capabilities( 338 cipher_suite: CipherSuite, 339 id: &str, 340 capabilities: Capabilities, 341 ) -> (LeafNode, HpkeSecretKey, SignatureSecretKey) { 342 let (signing_identity, signature_key) = 343 get_test_signing_identity(cipher_suite, id.as_bytes()).await; 344 345 LeafNode::generate( 346 &test_cipher_suite_provider(cipher_suite), 347 ConfigProperties { 348 capabilities, 349 extensions: Default::default(), 350 }, 351 signing_identity, 352 &signature_key, 353 Lifetime::years(1).unwrap(), 354 ) 355 .await 356 .map(|(leaf, hpke_secret_key)| (leaf, hpke_secret_key, signature_key)) 357 .unwrap() 358 } 359 360 #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] get_basic_test_node_sig_key( cipher_suite: CipherSuite, id: &str, ) -> (LeafNode, HpkeSecretKey, SignatureSecretKey)361 pub async fn get_basic_test_node_sig_key( 362 cipher_suite: CipherSuite, 363 id: &str, 364 ) -> (LeafNode, HpkeSecretKey, SignatureSecretKey) { 365 get_basic_test_node_capabilities(cipher_suite, id, get_test_capabilities()).await 366 } 367 368 #[allow(unused)] get_test_extensions() -> ExtensionList369 pub fn get_test_extensions() -> ExtensionList { 370 let mut extension_list = ExtensionList::new(); 371 372 extension_list 373 .set_from(ApplicationIdExt { 374 identifier: b"identifier".to_vec(), 375 }) 376 .unwrap(); 377 378 extension_list 379 } 380 get_test_capabilities() -> Capabilities381 pub fn get_test_capabilities() -> Capabilities { 382 Capabilities { 383 credentials: vec![ 384 BasicCredential::credential_type(), 385 CredentialType::from(BasicWithCustomProvider::CUSTOM_CREDENTIAL_TYPE), 386 ], 387 cipher_suites: TestCryptoProvider::all_supported_cipher_suites(), 388 ..Default::default() 389 } 390 } 391 392 #[allow(unused)] get_test_client_identity(leaf: &LeafNode) -> Vec<u8>393 pub fn get_test_client_identity(leaf: &LeafNode) -> Vec<u8> { 394 leaf.signing_identity 395 .credential 396 .mls_encode_to_vec() 397 .unwrap() 398 } 399 } 400 401 #[cfg(test)] 402 mod tests { 403 use super::test_utils::*; 404 use super::*; 405 406 use crate::client::test_utils::TEST_CIPHER_SUITE; 407 use crate::crypto::test_utils::test_cipher_suite_provider; 408 use crate::crypto::test_utils::TestCryptoProvider; 409 use crate::group::test_utils::random_bytes; 410 use crate::identity::test_utils::get_test_signing_identity; 411 use assert_matches::assert_matches; 412 413 #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))] test_node_generation()414 async fn test_node_generation() { 415 let capabilities = get_test_capabilities(); 416 let extensions = get_test_extensions(); 417 let lifetime = Lifetime::years(1).unwrap(); 418 419 for cipher_suite in TestCryptoProvider::all_supported_cipher_suites() { 420 let (signing_identity, secret) = get_test_signing_identity(cipher_suite, b"foo").await; 421 422 let (leaf_node, secret_key) = get_test_node_with_lifetime( 423 cipher_suite, 424 signing_identity.clone(), 425 &secret, 426 capabilities.clone(), 427 extensions.clone(), 428 lifetime.clone(), 429 ) 430 .await; 431 432 assert_eq!(leaf_node.ungreased_capabilities(), capabilities); 433 assert_eq!(leaf_node.ungreased_extensions(), extensions); 434 assert_eq!(leaf_node.signing_identity, signing_identity); 435 436 assert_matches!( 437 &leaf_node.leaf_node_source, 438 LeafNodeSource::KeyPackage(lt) if lt == &lifetime, 439 "Expected {:?}, got {:?}", LeafNodeSource::KeyPackage(lifetime), 440 leaf_node.leaf_node_source 441 ); 442 443 let provider = test_cipher_suite_provider(cipher_suite); 444 445 // Verify that the hpke key pair generated will work 446 let test_data = random_bytes(32); 447 448 let sealed = provider 449 .hpke_seal(&leaf_node.public_key, &[], None, &test_data) 450 .await 451 .unwrap(); 452 453 let opened = provider 454 .hpke_open(&sealed, &secret_key, &leaf_node.public_key, &[], None) 455 .await 456 .unwrap(); 457 458 assert_eq!(opened, test_data); 459 460 leaf_node 461 .verify( 462 &test_cipher_suite_provider(cipher_suite), 463 &signing_identity.signature_key, 464 &LeafNodeSigningContext::default(), 465 ) 466 .await 467 .unwrap(); 468 } 469 } 470 471 #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))] test_node_generation_randomness()472 async fn test_node_generation_randomness() { 473 let cipher_suite = TEST_CIPHER_SUITE; 474 475 let (signing_identity, secret) = get_test_signing_identity(cipher_suite, b"foo").await; 476 477 let (first_leaf, first_secret) = 478 get_test_node(cipher_suite, signing_identity.clone(), &secret, None, None).await; 479 480 for _ in 0..100 { 481 let (next_leaf, next_secret) = 482 get_test_node(cipher_suite, signing_identity.clone(), &secret, None, None).await; 483 484 assert_ne!(first_secret, next_secret); 485 assert_ne!(first_leaf.public_key, next_leaf.public_key); 486 } 487 } 488 489 #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))] test_node_update_no_meta_changes()490 async fn test_node_update_no_meta_changes() { 491 for cipher_suite in TestCryptoProvider::all_supported_cipher_suites() { 492 let cipher_suite_provider = test_cipher_suite_provider(cipher_suite); 493 494 let (signing_identity, secret) = get_test_signing_identity(cipher_suite, b"foo").await; 495 496 let (mut leaf, leaf_secret) = 497 get_test_node(cipher_suite, signing_identity.clone(), &secret, None, None).await; 498 499 let original_leaf = leaf.clone(); 500 501 let new_secret = leaf 502 .update( 503 &cipher_suite_provider, 504 b"group", 505 0, 506 default_properties(), 507 None, 508 &secret, 509 ) 510 .await 511 .unwrap(); 512 513 assert_ne!(new_secret, leaf_secret); 514 assert_ne!(original_leaf.public_key, leaf.public_key); 515 516 assert_eq!( 517 leaf.ungreased_capabilities(), 518 original_leaf.ungreased_capabilities() 519 ); 520 521 assert_eq!( 522 leaf.ungreased_extensions(), 523 original_leaf.ungreased_extensions() 524 ); 525 526 assert_eq!(leaf.signing_identity, original_leaf.signing_identity); 527 assert_matches!(&leaf.leaf_node_source, LeafNodeSource::Update); 528 529 leaf.verify( 530 &cipher_suite_provider, 531 &signing_identity.signature_key, 532 &(b"group".as_slice(), 0).into(), 533 ) 534 .await 535 .unwrap(); 536 } 537 } 538 539 #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))] test_node_update_meta_changes()540 async fn test_node_update_meta_changes() { 541 let cipher_suite = TEST_CIPHER_SUITE; 542 543 let (signing_identity, secret) = get_test_signing_identity(cipher_suite, b"foo").await; 544 545 let new_properties = ConfigProperties { 546 capabilities: get_test_capabilities(), 547 extensions: get_test_extensions(), 548 }; 549 550 let (mut leaf, _) = 551 get_test_node(cipher_suite, signing_identity, &secret, None, None).await; 552 553 leaf.update( 554 &test_cipher_suite_provider(cipher_suite), 555 b"group", 556 0, 557 new_properties.clone(), 558 None, 559 &secret, 560 ) 561 .await 562 .unwrap(); 563 564 assert_eq!(leaf.ungreased_capabilities(), new_properties.capabilities); 565 assert_eq!(leaf.ungreased_extensions(), new_properties.extensions); 566 } 567 568 #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))] test_node_commit_no_meta_changes()569 async fn test_node_commit_no_meta_changes() { 570 for cipher_suite in TestCryptoProvider::all_supported_cipher_suites() { 571 let cipher_suite_provider = test_cipher_suite_provider(cipher_suite); 572 573 let (signing_identity, secret) = get_test_signing_identity(cipher_suite, b"foo").await; 574 575 let (mut leaf, leaf_secret) = 576 get_test_node(cipher_suite, signing_identity.clone(), &secret, None, None).await; 577 578 let original_leaf = leaf.clone(); 579 580 let new_secret = leaf 581 .commit( 582 &cipher_suite_provider, 583 b"group", 584 0, 585 default_properties(), 586 None, 587 &secret, 588 ) 589 .await 590 .unwrap(); 591 592 assert_ne!(new_secret, leaf_secret); 593 assert_ne!(original_leaf.public_key, leaf.public_key); 594 595 assert_eq!( 596 leaf.ungreased_capabilities(), 597 original_leaf.ungreased_capabilities() 598 ); 599 600 assert_eq!( 601 leaf.ungreased_extensions(), 602 original_leaf.ungreased_extensions() 603 ); 604 605 assert_eq!(leaf.signing_identity, original_leaf.signing_identity); 606 607 leaf.verify( 608 &cipher_suite_provider, 609 &signing_identity.signature_key, 610 &(b"group".as_slice(), 0).into(), 611 ) 612 .await 613 .unwrap(); 614 } 615 } 616 617 #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))] test_node_commit_meta_changes()618 async fn test_node_commit_meta_changes() { 619 let cipher_suite = TEST_CIPHER_SUITE; 620 621 let (signing_identity, secret) = get_test_signing_identity(cipher_suite, b"foo").await; 622 let (mut leaf, _) = 623 get_test_node(cipher_suite, signing_identity, &secret, None, None).await; 624 625 let new_properties = ConfigProperties { 626 capabilities: get_test_capabilities(), 627 extensions: get_test_extensions(), 628 }; 629 630 // The new identity has a fresh public key 631 let new_signing_identity = get_test_signing_identity(cipher_suite, b"foo").await.0; 632 633 leaf.commit( 634 &test_cipher_suite_provider(cipher_suite), 635 b"group", 636 0, 637 new_properties.clone(), 638 Some(new_signing_identity.clone()), 639 &secret, 640 ) 641 .await 642 .unwrap(); 643 644 assert_eq!(leaf.capabilities, new_properties.capabilities); 645 assert_eq!(leaf.extensions, new_properties.extensions); 646 assert_eq!(leaf.signing_identity, new_signing_identity); 647 } 648 649 #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))] context_is_signed()650 async fn context_is_signed() { 651 let provider = test_cipher_suite_provider(TEST_CIPHER_SUITE); 652 653 let (signing_identity, secret) = get_test_signing_identity(TEST_CIPHER_SUITE, b"foo").await; 654 655 let (mut leaf, _) = get_test_node( 656 TEST_CIPHER_SUITE, 657 signing_identity.clone(), 658 &secret, 659 None, 660 None, 661 ) 662 .await; 663 664 leaf.sign(&provider, &secret, &(b"foo".as_slice(), 0).into()) 665 .await 666 .unwrap(); 667 668 let res = leaf 669 .verify( 670 &provider, 671 &signing_identity.signature_key, 672 &(b"foo".as_slice(), 1).into(), 673 ) 674 .await; 675 676 assert_matches!(res, Err(MlsError::InvalidSignature)); 677 678 let res = leaf 679 .verify( 680 &provider, 681 &signing_identity.signature_key, 682 &(b"bar".as_slice(), 0).into(), 683 ) 684 .await; 685 686 assert_matches!(res, Err(MlsError::InvalidSignature)); 687 } 688 } 689