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