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