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::leaf_node::{LeafNode, LeafNodeSigningContext, LeafNodeSource};
6 use crate::client::MlsError;
7 use crate::CipherSuiteProvider;
8 use crate::{signer::Signable, time::MlsTime};
9 use mls_rs_core::{error::IntoAnyError, extension::ExtensionList, identity::IdentityProvider};
10 
11 use crate::extension::RequiredCapabilitiesExt;
12 
13 #[cfg(feature = "by_ref_proposal")]
14 use crate::extension::ExternalSendersExt;
15 
16 pub enum ValidationContext<'a> {
17     Add(Option<MlsTime>),
18     Update((&'a [u8], u32, Option<MlsTime>)),
19     Commit((&'a [u8], u32, Option<MlsTime>)),
20 }
21 
22 impl<'a> ValidationContext<'a> {
signing_context(&self) -> LeafNodeSigningContext23     fn signing_context(&self) -> LeafNodeSigningContext {
24         match *self {
25             ValidationContext::Add(_) => Default::default(),
26             ValidationContext::Update((group_id, leaf_index, _)) => (group_id, leaf_index).into(),
27             ValidationContext::Commit((group_id, leaf_index, _)) => (group_id, leaf_index).into(),
28         }
29     }
30 
generation_time(&self) -> Option<MlsTime>31     fn generation_time(&self) -> Option<MlsTime> {
32         match *self {
33             ValidationContext::Add(t) => t,
34             ValidationContext::Update((_, _, t)) => t,
35             ValidationContext::Commit((_, _, t)) => t,
36         }
37     }
38 }
39 
40 #[derive(Clone, Debug)]
41 pub struct LeafNodeValidator<'a, C, CP>
42 where
43     C: IdentityProvider,
44     CP: CipherSuiteProvider,
45 {
46     cipher_suite_provider: &'a CP,
47     identity_provider: &'a C,
48     group_context_extensions: Option<&'a ExtensionList>,
49 }
50 
51 impl<'a, C: IdentityProvider, CP: CipherSuiteProvider> LeafNodeValidator<'a, C, CP> {
new( cipher_suite_provider: &'a CP, identity_provider: &'a C, group_context_extensions: Option<&'a ExtensionList>, ) -> Self52     pub fn new(
53         cipher_suite_provider: &'a CP,
54         identity_provider: &'a C,
55         group_context_extensions: Option<&'a ExtensionList>,
56     ) -> Self {
57         Self {
58             cipher_suite_provider,
59             identity_provider,
60             group_context_extensions,
61         }
62     }
63 
check_context( &self, leaf_node: &LeafNode, context: &ValidationContext, ) -> Result<(), MlsError>64     fn check_context(
65         &self,
66         leaf_node: &LeafNode,
67         context: &ValidationContext,
68     ) -> Result<(), MlsError> {
69         // Context specific checks
70         match context {
71             ValidationContext::Add(time) => {
72                 // If the context is add, and we specified a time to check for lifetime, verify it
73                 if let LeafNodeSource::KeyPackage(lifetime) = &leaf_node.leaf_node_source {
74                     if let Some(current_time) = time {
75                         if !lifetime.within_lifetime(*current_time) {
76                             return Err(MlsError::InvalidLifetime);
77                         }
78                     }
79                 } else {
80                     // If the leaf_node_source is anything other than Add it is invalid
81                     return Err(MlsError::InvalidLeafNodeSource);
82                 }
83             }
84             ValidationContext::Update(_) => {
85                 // If the leaf_node_source is anything other than Update it is invalid
86                 if !matches!(leaf_node.leaf_node_source, LeafNodeSource::Update) {
87                     return Err(MlsError::InvalidLeafNodeSource);
88                 }
89             }
90             ValidationContext::Commit(_) => {
91                 // If the leaf_node_source is anything other than Commit it is invalid
92                 if !matches!(leaf_node.leaf_node_source, LeafNodeSource::Commit(_)) {
93                     return Err(MlsError::InvalidLeafNodeSource);
94                 }
95             }
96         }
97 
98         Ok(())
99     }
100 
101     #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
revalidate( &self, leaf_node: &LeafNode, group_id: &[u8], leaf_index: u32, ) -> Result<(), MlsError>102     pub async fn revalidate(
103         &self,
104         leaf_node: &LeafNode,
105         group_id: &[u8],
106         leaf_index: u32,
107     ) -> Result<(), MlsError> {
108         let context = match leaf_node.leaf_node_source {
109             LeafNodeSource::KeyPackage(_) => ValidationContext::Add(None),
110             LeafNodeSource::Update => ValidationContext::Update((group_id, leaf_index, None)),
111             LeafNodeSource::Commit(_) => ValidationContext::Commit((group_id, leaf_index, None)),
112         };
113 
114         self.check_if_valid(leaf_node, context).await
115     }
116 
validate_required_capabilities(&self, leaf_node: &LeafNode) -> Result<(), MlsError>117     pub fn validate_required_capabilities(&self, leaf_node: &LeafNode) -> Result<(), MlsError> {
118         let Some(required_capabilities) = self
119             .group_context_extensions
120             .and_then(|exts| exts.get_as::<RequiredCapabilitiesExt>().transpose())
121             .transpose()?
122         else {
123             return Ok(());
124         };
125 
126         for extension in &required_capabilities.extensions {
127             if !leaf_node.capabilities.extensions.contains(extension) {
128                 return Err(MlsError::RequiredExtensionNotFound(*extension));
129             }
130         }
131 
132         for proposal in &required_capabilities.proposals {
133             if !leaf_node.capabilities.proposals.contains(proposal) {
134                 return Err(MlsError::RequiredProposalNotFound(*proposal));
135             }
136         }
137 
138         for credential in &required_capabilities.credentials {
139             if !leaf_node.capabilities.credentials.contains(credential) {
140                 return Err(MlsError::RequiredCredentialNotFound(*credential));
141             }
142         }
143 
144         Ok(())
145     }
146 
147     #[cfg(feature = "by_ref_proposal")]
validate_external_senders_ext_credentials( &self, leaf_node: &LeafNode, ) -> Result<(), MlsError>148     pub fn validate_external_senders_ext_credentials(
149         &self,
150         leaf_node: &LeafNode,
151     ) -> Result<(), MlsError> {
152         let Some(ext) = self
153             .group_context_extensions
154             .and_then(|exts| exts.get_as::<ExternalSendersExt>().transpose())
155             .transpose()?
156         else {
157             return Ok(());
158         };
159 
160         ext.allowed_senders.iter().try_for_each(|sender| {
161             let cred_type = sender.credential.credential_type();
162             leaf_node
163                 .capabilities
164                 .credentials
165                 .contains(&cred_type)
166                 .then_some(())
167                 .ok_or(MlsError::RequiredCredentialNotFound(cred_type))
168         })
169     }
170 
171     #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
check_if_valid( &self, leaf_node: &LeafNode, context: ValidationContext<'_>, ) -> Result<(), MlsError>172     pub(crate) async fn check_if_valid(
173         &self,
174         leaf_node: &LeafNode,
175         context: ValidationContext<'_>,
176     ) -> Result<(), MlsError> {
177         // Check that we are validating within the proper context
178         self.check_context(leaf_node, &context)?;
179 
180         // Verify the credential
181         self.identity_provider
182             .validate_member(
183                 &leaf_node.signing_identity,
184                 context.generation_time(),
185                 self.group_context_extensions,
186             )
187             .await
188             .map_err(|e| MlsError::IdentityProviderError(e.into_any_error()))?;
189 
190         // Verify that the credential signed the leaf node
191         leaf_node
192             .verify(
193                 self.cipher_suite_provider,
194                 &leaf_node.signing_identity.signature_key,
195                 &context.signing_context(),
196             )
197             .await?;
198 
199         // If required capabilities are specified, verify the leaf node meets the requirements
200         self.validate_required_capabilities(leaf_node)?;
201 
202         // If there are extensions, make sure they are referenced in the capabilities field
203         for one_ext in &*leaf_node.extensions {
204             if !leaf_node
205                 .capabilities
206                 .extensions
207                 .contains(&one_ext.extension_type)
208             {
209                 return Err(MlsError::ExtensionNotInCapabilities(one_ext.extension_type));
210             }
211         }
212 
213         // Verify that group extensions are supported by the leaf
214         self.group_context_extensions
215             .into_iter()
216             .flat_map(|exts| &**exts)
217             .map(|ext| ext.extension_type)
218             .find(|ext_type| {
219                 !ext_type.is_default() && !leaf_node.capabilities.extensions.contains(ext_type)
220             })
221             .map(MlsError::UnsupportedGroupExtension)
222             .map_or(Ok(()), Err)?;
223 
224         #[cfg(feature = "by_ref_proposal")]
225         self.validate_external_senders_ext_credentials(leaf_node)?;
226 
227         Ok(())
228     }
229 }
230 
231 #[cfg(test)]
232 mod tests {
233     use crate::crypto::test_utils::try_test_cipher_suite_provider;
234     use crate::extension::MlsExtension;
235     use alloc::vec;
236     use assert_matches::assert_matches;
237     #[cfg(feature = "std")]
238     use core::time::Duration;
239     use mls_rs_core::crypto::CipherSuite;
240     use mls_rs_core::group::ProposalType;
241 
242     use super::*;
243 
244     use crate::client::test_utils::TEST_CIPHER_SUITE;
245     use crate::crypto::test_utils::test_cipher_suite_provider;
246     use crate::crypto::test_utils::TestCryptoProvider;
247     use crate::crypto::SignatureSecretKey;
248     use crate::extension::test_utils::TestExtension;
249     use crate::group::test_utils::random_bytes;
250     use crate::identity::basic::BasicCredential;
251     use crate::identity::basic::BasicIdentityProvider;
252     use crate::identity::test_utils::get_test_signing_identity;
253     use crate::tree_kem::leaf_node::test_utils::*;
254     use crate::tree_kem::leaf_node_validator::test_utils::FailureIdentityProvider;
255     use crate::tree_kem::Capabilities;
256     use crate::ExtensionList;
257 
258     #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
get_test_add_node() -> (LeafNode, SignatureSecretKey)259     async fn get_test_add_node() -> (LeafNode, SignatureSecretKey) {
260         let (signing_identity, secret) = get_test_signing_identity(TEST_CIPHER_SUITE, b"foo").await;
261 
262         let (leaf_node, _) =
263             get_test_node(TEST_CIPHER_SUITE, signing_identity, &secret, None, None).await;
264 
265         (leaf_node, secret)
266     }
267 
268     #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))]
test_basic_add_validation()269     async fn test_basic_add_validation() {
270         let cipher_suite_provider = test_cipher_suite_provider(TEST_CIPHER_SUITE);
271 
272         let (leaf_node, _) = get_test_add_node().await;
273 
274         let test_validator =
275             LeafNodeValidator::new(&cipher_suite_provider, &BasicIdentityProvider, None);
276 
277         let res = test_validator
278             .check_if_valid(&leaf_node, ValidationContext::Add(None))
279             .await;
280 
281         assert_matches!(res, Ok(_));
282     }
283 
284     #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))]
test_failed_validation()285     async fn test_failed_validation() {
286         let cipher_suite_provider = test_cipher_suite_provider(TEST_CIPHER_SUITE);
287         let (leaf_node, _) = get_test_add_node().await;
288 
289         let fail_test_validator =
290             LeafNodeValidator::new(&cipher_suite_provider, &FailureIdentityProvider, None);
291 
292         let res = fail_test_validator
293             .check_if_valid(&leaf_node, ValidationContext::Add(None))
294             .await;
295 
296         assert_matches!(res, Err(MlsError::IdentityProviderError(_)));
297     }
298 
299     #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))]
test_basic_update_validation()300     async fn test_basic_update_validation() {
301         let cipher_suite_provider = test_cipher_suite_provider(TEST_CIPHER_SUITE);
302         let group_id = b"group_id";
303 
304         let (mut leaf_node, secret) = get_test_add_node().await;
305 
306         leaf_node
307             .update(
308                 &cipher_suite_provider,
309                 group_id,
310                 0,
311                 // TODO remove identity from input
312                 default_properties(),
313                 None,
314                 &secret,
315             )
316             .await
317             .unwrap();
318 
319         let test_validator =
320             LeafNodeValidator::new(&cipher_suite_provider, &BasicIdentityProvider, None);
321 
322         let res = test_validator
323             .check_if_valid(&leaf_node, ValidationContext::Update((group_id, 0, None)))
324             .await;
325 
326         assert_matches!(res, Ok(_));
327     }
328 
329     #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))]
test_basic_commit_validation()330     async fn test_basic_commit_validation() {
331         let cipher_suite_provider = test_cipher_suite_provider(TEST_CIPHER_SUITE);
332         let group_id = b"group_id";
333 
334         let (mut leaf_node, secret) = get_test_add_node().await;
335 
336         leaf_node.leaf_node_source = LeafNodeSource::Commit(hex!("f00d").into());
337 
338         leaf_node
339             .commit(
340                 &cipher_suite_provider,
341                 group_id,
342                 0,
343                 default_properties(),
344                 None,
345                 &secret,
346             )
347             .await
348             .unwrap();
349 
350         let test_validator =
351             LeafNodeValidator::new(&cipher_suite_provider, &BasicIdentityProvider, None);
352 
353         let res = test_validator
354             .check_if_valid(&leaf_node, ValidationContext::Commit((group_id, 0, None)))
355             .await;
356 
357         assert_matches!(res, Ok(_));
358     }
359 
360     #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))]
test_incorrect_context()361     async fn test_incorrect_context() {
362         let cipher_suite_provider = test_cipher_suite_provider(TEST_CIPHER_SUITE);
363 
364         let test_validator =
365             LeafNodeValidator::new(&cipher_suite_provider, &BasicIdentityProvider, None);
366 
367         let (mut leaf_node, secret) = get_test_add_node().await;
368 
369         let res = test_validator
370             .check_if_valid(&leaf_node, ValidationContext::Update((b"foo", 0, None)))
371             .await;
372 
373         assert_matches!(res, Err(MlsError::InvalidLeafNodeSource));
374 
375         let res = test_validator
376             .check_if_valid(&leaf_node, ValidationContext::Commit((b"foo", 0, None)))
377             .await;
378 
379         assert_matches!(res, Err(MlsError::InvalidLeafNodeSource));
380 
381         leaf_node
382             .update(
383                 &cipher_suite_provider,
384                 b"foo",
385                 0,
386                 default_properties(),
387                 None,
388                 &secret,
389             )
390             .await
391             .unwrap();
392 
393         let res = test_validator
394             .check_if_valid(&leaf_node, ValidationContext::Add(None))
395             .await;
396 
397         assert_matches!(res, Err(MlsError::InvalidLeafNodeSource));
398 
399         let res = test_validator
400             .check_if_valid(&leaf_node, ValidationContext::Commit((b"foo", 0, None)))
401             .await;
402 
403         assert_matches!(res, Err(MlsError::InvalidLeafNodeSource));
404 
405         leaf_node.leaf_node_source = LeafNodeSource::Commit(hex!("f00d").into());
406 
407         leaf_node
408             .commit(
409                 &cipher_suite_provider,
410                 b"foo",
411                 0,
412                 default_properties(),
413                 None,
414                 &secret,
415             )
416             .await
417             .unwrap();
418 
419         let res = test_validator
420             .check_if_valid(&leaf_node, ValidationContext::Add(None))
421             .await;
422 
423         assert_matches!(res, Err(MlsError::InvalidLeafNodeSource));
424 
425         let res = test_validator
426             .check_if_valid(&leaf_node, ValidationContext::Update((b"foo", 0, None)))
427             .await;
428 
429         assert_matches!(res, Err(MlsError::InvalidLeafNodeSource));
430     }
431 
432     #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))]
test_bad_signature()433     async fn test_bad_signature() {
434         for cipher_suite in TestCryptoProvider::all_supported_cipher_suites() {
435             let cipher_suite_provider = test_cipher_suite_provider(cipher_suite);
436 
437             let (signing_identity, secret) = get_test_signing_identity(cipher_suite, b"foo").await;
438 
439             let (mut leaf_node, _) =
440                 get_test_node(cipher_suite, signing_identity, &secret, None, None).await;
441 
442             leaf_node.signature = random_bytes(leaf_node.signature.len());
443 
444             let test_validator =
445                 LeafNodeValidator::new(&cipher_suite_provider, &BasicIdentityProvider, None);
446 
447             let res = test_validator
448                 .check_if_valid(&leaf_node, ValidationContext::Add(None))
449                 .await;
450 
451             assert_matches!(res, Err(MlsError::InvalidSignature));
452         }
453     }
454 
455     #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))]
test_capabilities_mismatch()456     async fn test_capabilities_mismatch() {
457         let (signing_identity, secret) = get_test_signing_identity(TEST_CIPHER_SUITE, b"foo").await;
458 
459         let mut extensions = ExtensionList::new();
460 
461         extensions.set_from(TestExtension::from(0)).unwrap();
462 
463         let capabilities = Capabilities {
464             credentials: vec![BasicCredential::credential_type()],
465             ..Default::default()
466         };
467 
468         let (leaf_node, _) = get_test_node(
469             TEST_CIPHER_SUITE,
470             signing_identity,
471             &secret,
472             Some(capabilities),
473             Some(extensions),
474         )
475         .await;
476 
477         let cipher_suite_provider = test_cipher_suite_provider(TEST_CIPHER_SUITE);
478 
479         let test_validator =
480             LeafNodeValidator::new(&cipher_suite_provider, &BasicIdentityProvider, None);
481 
482         let res = test_validator
483             .check_if_valid(&leaf_node, ValidationContext::Add(None))
484             .await;
485 
486         assert_matches!(res,
487             Err(MlsError::ExtensionNotInCapabilities(ext)) if ext == 42.into());
488     }
489 
490     #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))]
test_cipher_suite_mismatch()491     async fn test_cipher_suite_mismatch() {
492         for another_cipher_suite in CipherSuite::all().filter(|cs| cs != &TEST_CIPHER_SUITE) {
493             if let Some(cs) = try_test_cipher_suite_provider(*another_cipher_suite) {
494                 let (leaf_node, _) = get_test_add_node().await;
495 
496                 let test_validator = LeafNodeValidator::new(&cs, &BasicIdentityProvider, None);
497 
498                 let res = test_validator
499                     .check_if_valid(&leaf_node, ValidationContext::Add(None))
500                     .await;
501 
502                 assert_matches!(res, Err(MlsError::InvalidSignature));
503             }
504         }
505     }
506 
507     #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))]
test_required_extension()508     async fn test_required_extension() {
509         let required_capabilities = RequiredCapabilitiesExt {
510             extensions: vec![43.into()],
511             ..Default::default()
512         };
513 
514         let (leaf_node, _) = get_test_add_node().await;
515 
516         let cipher_suite_provider = test_cipher_suite_provider(TEST_CIPHER_SUITE);
517 
518         let group_context_extensions =
519             core::iter::once(required_capabilities.into_extension().unwrap()).collect();
520 
521         let test_validator = LeafNodeValidator::new(
522             &cipher_suite_provider,
523             &BasicIdentityProvider,
524             Some(&group_context_extensions),
525         );
526 
527         let res = test_validator
528             .check_if_valid(&leaf_node, ValidationContext::Add(None))
529             .await;
530 
531         assert_matches!(
532             res,
533             Err(MlsError::RequiredExtensionNotFound(v)) if v == 43.into()
534         );
535     }
536 
537     #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))]
test_required_proposal()538     async fn test_required_proposal() {
539         let required_capabilities = RequiredCapabilitiesExt {
540             proposals: vec![42.into()],
541             ..Default::default()
542         };
543 
544         let cipher_suite_provider = test_cipher_suite_provider(TEST_CIPHER_SUITE);
545 
546         let (leaf_node, _) = get_test_add_node().await;
547 
548         let group_context_extensions =
549             core::iter::once(required_capabilities.into_extension().unwrap()).collect();
550 
551         let test_validator = LeafNodeValidator::new(
552             &cipher_suite_provider,
553             &BasicIdentityProvider,
554             Some(&group_context_extensions),
555         );
556 
557         let res = test_validator
558             .check_if_valid(&leaf_node, ValidationContext::Add(None))
559             .await;
560 
561         assert_matches!(
562             res,
563             Err(MlsError::RequiredProposalNotFound(p)) if p == ProposalType::new(42)
564         );
565     }
566 
567     #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))]
test_required_credential()568     async fn test_required_credential() {
569         let required_capabilities = RequiredCapabilitiesExt {
570             credentials: vec![0.into()],
571             ..Default::default()
572         };
573 
574         let (leaf_node, _) = get_test_add_node().await;
575 
576         let cipher_suite_provider = test_cipher_suite_provider(TEST_CIPHER_SUITE);
577 
578         let group_context_extensions =
579             core::iter::once(required_capabilities.into_extension().unwrap()).collect();
580 
581         let test_validator = LeafNodeValidator::new(
582             &cipher_suite_provider,
583             &BasicIdentityProvider,
584             Some(&group_context_extensions),
585         );
586 
587         let res = test_validator
588             .check_if_valid(&leaf_node, ValidationContext::Add(None))
589             .await;
590 
591         assert_matches!(res,
592             Err(MlsError::RequiredCredentialNotFound(ext)) if ext == 0.into()
593         );
594     }
595 
596     #[cfg(feature = "std")]
597     #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))]
test_add_lifetime()598     async fn test_add_lifetime() {
599         let (leaf_node, _) = get_test_add_node().await;
600 
601         let cipher_suite_provider = test_cipher_suite_provider(TEST_CIPHER_SUITE);
602 
603         let test_validator =
604             LeafNodeValidator::new(&cipher_suite_provider, &BasicIdentityProvider, None);
605 
606         let good_lifetime = MlsTime::now();
607 
608         let over_one_year = good_lifetime.seconds_since_epoch() + (86400 * 366);
609 
610         let bad_lifetime = MlsTime::from_duration_since_epoch(Duration::from_secs(over_one_year));
611 
612         let res = test_validator
613             .check_if_valid(&leaf_node, ValidationContext::Add(Some(good_lifetime)))
614             .await;
615 
616         assert_matches!(res, Ok(()));
617 
618         let res = test_validator
619             .check_if_valid(&leaf_node, ValidationContext::Add(Some(bad_lifetime)))
620             .await;
621 
622         assert_matches!(res, Err(MlsError::InvalidLifetime));
623     }
624 }
625 
626 #[cfg(test)]
627 pub(crate) mod test_utils {
628     use alloc::vec;
629     use alloc::{boxed::Box, vec::Vec};
630     use mls_rs_codec::MlsEncode;
631     use mls_rs_core::{
632         error::IntoAnyError,
633         extension::ExtensionList,
634         identity::{BasicCredential, IdentityProvider},
635     };
636 
637     use crate::{identity::SigningIdentity, time::MlsTime};
638 
639     #[derive(Clone, Debug, Default)]
640     pub struct FailureIdentityProvider;
641 
642     #[cfg(feature = "by_ref_proposal")]
643     impl FailureIdentityProvider {
new() -> Self644         pub fn new() -> Self {
645             Self
646         }
647     }
648 
649     #[derive(Debug)]
650     #[cfg_attr(feature = "std", derive(thiserror::Error))]
651     #[cfg_attr(feature = "std", error("test error"))]
652     pub struct TestFailureError;
653 
654     impl IntoAnyError for TestFailureError {
655         #[cfg(feature = "std")]
into_dyn_error(self) -> Result<Box<dyn std::error::Error + Send + Sync>, Self>656         fn into_dyn_error(self) -> Result<Box<dyn std::error::Error + Send + Sync>, Self> {
657             Ok(self.into())
658         }
659     }
660 
661     #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
662     #[cfg_attr(mls_build_async, maybe_async::must_be_async)]
663     impl IdentityProvider for FailureIdentityProvider {
664         type Error = TestFailureError;
665 
validate_member( &self, _signing_identity: &SigningIdentity, _timestamp: Option<MlsTime>, _extensions: Option<&ExtensionList>, ) -> Result<(), Self::Error>666         async fn validate_member(
667             &self,
668             _signing_identity: &SigningIdentity,
669             _timestamp: Option<MlsTime>,
670             _extensions: Option<&ExtensionList>,
671         ) -> Result<(), Self::Error> {
672             Err(TestFailureError)
673         }
674 
validate_external_sender( &self, _signing_identity: &SigningIdentity, _timestamp: Option<MlsTime>, _extensions: Option<&ExtensionList>, ) -> Result<(), Self::Error>675         async fn validate_external_sender(
676             &self,
677             _signing_identity: &SigningIdentity,
678             _timestamp: Option<MlsTime>,
679             _extensions: Option<&ExtensionList>,
680         ) -> Result<(), Self::Error> {
681             Err(TestFailureError)
682         }
683 
684         #[cfg_attr(coverage_nightly, coverage(off))]
identity( &self, signing_id: &SigningIdentity, _extensions: &ExtensionList, ) -> Result<Vec<u8>, Self::Error>685         async fn identity(
686             &self,
687             signing_id: &SigningIdentity,
688             _extensions: &ExtensionList,
689         ) -> Result<Vec<u8>, Self::Error> {
690             Ok(signing_id.credential.mls_encode_to_vec().unwrap())
691         }
692 
693         #[cfg_attr(coverage_nightly, coverage(off))]
valid_successor( &self, _predecessor: &SigningIdentity, _successor: &SigningIdentity, _extensions: &ExtensionList, ) -> Result<bool, Self::Error>694         async fn valid_successor(
695             &self,
696             _predecessor: &SigningIdentity,
697             _successor: &SigningIdentity,
698             _extensions: &ExtensionList,
699         ) -> Result<bool, Self::Error> {
700             Err(TestFailureError)
701         }
702 
703         #[cfg_attr(coverage_nightly, coverage(off))]
supported_types(&self) -> Vec<crate::identity::CredentialType>704         fn supported_types(&self) -> Vec<crate::identity::CredentialType> {
705             vec![BasicCredential::credential_type()]
706         }
707     }
708 }
709