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 crate::CipherSuiteProvider; 6 use crate::{client::MlsError, group::transcript_hash::ConfirmedTranscriptHash}; 7 use alloc::vec::Vec; 8 use core::{ 9 fmt::{self, Debug}, 10 ops::Deref, 11 }; 12 use mls_rs_codec::{MlsDecode, MlsEncode, MlsSize}; 13 use mls_rs_core::error::IntoAnyError; 14 15 #[derive(Clone, PartialEq, MlsSize, MlsEncode, MlsDecode)] 16 #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] 17 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 18 pub struct ConfirmationTag( 19 #[mls_codec(with = "mls_rs_codec::byte_vec")] 20 #[cfg_attr(feature = "serde", serde(with = "mls_rs_core::vec_serde"))] 21 Vec<u8>, 22 ); 23 24 impl Debug for ConfirmationTag { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 26 mls_rs_core::debug::pretty_bytes(&self.0) 27 .named("ConfirmationTag") 28 .fmt(f) 29 } 30 } 31 32 impl Deref for ConfirmationTag { 33 type Target = Vec<u8>; deref(&self) -> &Self::Target34 fn deref(&self) -> &Self::Target { 35 &self.0 36 } 37 } 38 39 impl ConfirmationTag { 40 #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] create<P: CipherSuiteProvider>( confirmation_key: &[u8], confirmed_transcript_hash: &ConfirmedTranscriptHash, cipher_suite_provider: &P, ) -> Result<Self, MlsError>41 pub(crate) async fn create<P: CipherSuiteProvider>( 42 confirmation_key: &[u8], 43 confirmed_transcript_hash: &ConfirmedTranscriptHash, 44 cipher_suite_provider: &P, 45 ) -> Result<Self, MlsError> { 46 cipher_suite_provider 47 .mac(confirmation_key, confirmed_transcript_hash) 48 .await 49 .map(ConfirmationTag) 50 .map_err(|e| MlsError::CryptoProviderError(e.into_any_error())) 51 } 52 53 #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] matches<P: CipherSuiteProvider>( &self, confirmation_key: &[u8], confirmed_transcript_hash: &ConfirmedTranscriptHash, cipher_suite_provider: &P, ) -> Result<bool, MlsError>54 pub(crate) async fn matches<P: CipherSuiteProvider>( 55 &self, 56 confirmation_key: &[u8], 57 confirmed_transcript_hash: &ConfirmedTranscriptHash, 58 cipher_suite_provider: &P, 59 ) -> Result<bool, MlsError> { 60 let tag = ConfirmationTag::create( 61 confirmation_key, 62 confirmed_transcript_hash, 63 cipher_suite_provider, 64 ) 65 .await?; 66 67 Ok(&tag == self) 68 } 69 } 70 71 #[cfg(test)] 72 impl ConfirmationTag { 73 #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] empty<P: CipherSuiteProvider>(cipher_suite_provider: &P) -> Self74 pub(crate) async fn empty<P: CipherSuiteProvider>(cipher_suite_provider: &P) -> Self { 75 Self( 76 cipher_suite_provider 77 .mac( 78 &alloc::vec![0; cipher_suite_provider.kdf_extract_size()], 79 &[], 80 ) 81 .await 82 .unwrap(), 83 ) 84 } 85 } 86 87 #[cfg(test)] 88 mod tests { 89 use super::*; 90 use crate::crypto::test_utils::{test_cipher_suite_provider, TestCryptoProvider}; 91 92 #[cfg(target_arch = "wasm32")] 93 use wasm_bindgen_test::wasm_bindgen_test as test; 94 95 #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))] test_confirmation_tag_matching()96 async fn test_confirmation_tag_matching() { 97 for cipher_suite in TestCryptoProvider::all_supported_cipher_suites() { 98 let cipher_suite_provider = test_cipher_suite_provider(cipher_suite); 99 100 let confirmed_hash_a = ConfirmedTranscriptHash::from(b"foo_a".to_vec()); 101 102 let confirmation_key_a = b"bar_a".to_vec(); 103 104 let confirmed_hash_b = ConfirmedTranscriptHash::from(b"foo_b".to_vec()); 105 106 let confirmation_key_b = b"bar_b".to_vec(); 107 108 let confirmation_tag = ConfirmationTag::create( 109 &confirmation_key_a, 110 &confirmed_hash_a, 111 &cipher_suite_provider, 112 ) 113 .await 114 .unwrap(); 115 116 let matches = confirmation_tag 117 .matches( 118 &confirmation_key_a, 119 &confirmed_hash_a, 120 &cipher_suite_provider, 121 ) 122 .await 123 .unwrap(); 124 125 assert!(matches); 126 127 let matches = confirmation_tag 128 .matches( 129 &confirmation_key_b, 130 &confirmed_hash_a, 131 &cipher_suite_provider, 132 ) 133 .await 134 .unwrap(); 135 136 assert!(!matches); 137 138 let matches = confirmation_tag 139 .matches( 140 &confirmation_key_a, 141 &confirmed_hash_b, 142 &cipher_suite_provider, 143 ) 144 .await 145 .unwrap(); 146 147 assert!(!matches); 148 } 149 } 150 } 151