1 // Copyright 2022 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 use crate::{ 16 credential::v1::*, 17 extended::{ 18 deserialize::{DecryptedSection, SectionMic, VerificationMode}, 19 section_signature_payload::*, 20 V1IdentityToken, NP_ADV_MAX_SECTION_LEN, V1_IDENTITY_TOKEN_LEN, 21 }, 22 NP_SVC_UUID, 23 }; 24 25 use crate::deserialization_arena::DeserializationArenaAllocator; 26 27 #[cfg(any(feature = "devtools", test))] 28 extern crate alloc; 29 30 use crate::{ 31 extended::{ 32 deserialize::section::header::CiphertextExtendedIdentityToken, 33 salt::{MultiSalt, V1Salt}, 34 }, 35 header::V1AdvHeader, 36 }; 37 #[cfg(any(feature = "devtools", test))] 38 use alloc::vec::Vec; 39 #[cfg(feature = "devtools")] 40 use array_view::ArrayView; 41 use core::fmt::Debug; 42 use crypto_provider::{ 43 aes::ctr::{AesCtr, AesCtrNonce, NonceAndCounter}, 44 hmac::Hmac, 45 CryptoProvider, 46 }; 47 use np_hkdf::v1_salt::ExtendedV1Salt; 48 49 #[cfg(test)] 50 use crate::extended::deserialize::encrypted_section::tests::IdentityResolutionOrDeserializationError; 51 52 use super::ArenaOutOfSpace; 53 54 #[cfg(test)] 55 mod tests; 56 57 /// Represents the contents of an encrypted section 58 /// which are directly employed in identity resolution. 59 /// This does not incorporate any information about credentials. 60 /// 61 /// Should be re-used for multiple identity resolution attempts, if applicable, to amortize the 62 /// cost of calculating this data. 63 #[derive(PartialEq, Eq, Debug)] 64 pub(crate) struct SectionIdentityResolutionContents { 65 /// The ciphertext for the identity token 66 pub(crate) identity_token: CiphertextExtendedIdentityToken, 67 /// The 12-byte cryptographic nonce which is derived from the salt for a 68 /// particular section. 69 pub(crate) nonce: AesCtrNonce, 70 } 71 72 impl SectionIdentityResolutionContents { 73 /// Decrypt the contained metadata-key ciphertext buffer whose bytes of plaintext are, maybe, the 74 /// metadata key for an NP identity, as specified via pre-calculated cryptographic materials 75 /// stored in some [`SectionIdentityResolutionMaterial`]. 76 /// 77 /// This method does not decrypt an entire section's ciphertext aside from the metadata key, 78 /// and so verification (MIC or signature) needs to be done elsewhere. 79 /// 80 /// Returns `Some` if decrypting the metadata-key ciphertext produces plaintext whose HMAC 81 /// matches the expected MAC. Otherwise, returns `None`. try_match<C: CryptoProvider>( &self, identity_resolution_material: &SectionIdentityResolutionMaterial, ) -> Option<IdentityMatch<C>>82 pub(crate) fn try_match<C: CryptoProvider>( 83 &self, 84 identity_resolution_material: &SectionIdentityResolutionMaterial, 85 ) -> Option<IdentityMatch<C>> { 86 let mut decrypt_buf = self.identity_token.0; 87 let mut cipher = C::AesCtr128::new( 88 &identity_resolution_material.aes_key, 89 NonceAndCounter::from_nonce(self.nonce), 90 ); 91 cipher.apply_keystream(&mut decrypt_buf[..]); 92 93 let identity_token_hmac_key: np_hkdf::NpHmacSha256Key = 94 identity_resolution_material.identity_token_hmac_key.into(); 95 identity_token_hmac_key 96 .verify_hmac::<C>( 97 &decrypt_buf[..], 98 identity_resolution_material.expected_identity_token_hmac, 99 ) 100 .ok() 101 .map(move |_| IdentityMatch { 102 cipher, 103 identity_token: V1IdentityToken(decrypt_buf), 104 nonce: self.nonce, 105 }) 106 } 107 } 108 109 /// Carries data about an identity "match" for a particular section 110 /// against some particular V1 identity-resolution crypto-materials. 111 pub(crate) struct IdentityMatch<C: CryptoProvider> { 112 /// Decrypted identity token 113 identity_token: V1IdentityToken, 114 /// The AES-Ctr nonce to be used in section decryption and verification 115 nonce: AesCtrNonce, 116 /// The state of the AES-Ctr cipher after successfully decrypting 117 /// the metadata key ciphertext. May be used to decrypt the remainder 118 /// of the section ciphertext. 119 cipher: C::AesCtr128, 120 } 121 122 /// Maximum length of a section's contents, after the metadata-key. 123 #[allow(unused)] 124 const MAX_SECTION_CONTENTS_LEN: usize = NP_ADV_MAX_SECTION_LEN - V1_IDENTITY_TOKEN_LEN; 125 126 /// Bare, decrypted contents from an encrypted section, 127 /// including the decrypted metadata key and the decrypted section ciphertext. 128 /// At this point, verification of the plaintext contents has not yet been performed. 129 pub(crate) struct RawDecryptedSection<'a> { 130 // Only used with feature = "devtools" 131 #[allow(unused)] 132 pub(crate) identity_token: V1IdentityToken, 133 pub(crate) nonce: AesCtrNonce, 134 pub(crate) plaintext_contents: &'a [u8], 135 } 136 137 #[cfg(feature = "devtools")] 138 impl<'a> RawDecryptedSection<'a> { to_raw_bytes(&self) -> ArrayView<u8, NP_ADV_MAX_SECTION_LEN>139 pub(crate) fn to_raw_bytes(&self) -> ArrayView<u8, NP_ADV_MAX_SECTION_LEN> { 140 let mut result = Vec::new(); 141 result.extend_from_slice(self.identity_token.as_slice()); 142 result.extend_from_slice(self.plaintext_contents); 143 ArrayView::try_from_slice(&result).expect("Won't panic because of the involved lengths") 144 } 145 } 146 147 /// Represents the contents of an encrypted section, 148 /// independent of the encryption type. 149 #[derive(PartialEq, Eq, Debug)] 150 pub(crate) struct EncryptedSectionContents<'adv, S> { 151 adv_header: V1AdvHeader, 152 format_bytes: &'adv [u8], 153 pub(crate) salt: S, 154 /// Ciphertext of identity token (part of section header) 155 identity_token: CiphertextExtendedIdentityToken, 156 /// The portion of the ciphertext that has been encrypted. 157 /// Length must be in `[0, NP_ADV_MAX_SECTION_LEN]`. 158 section_contents: &'adv [u8], 159 // The length byte exactly as it appears in the adv. This is the length of the encrypted 160 // contents plus any additional bytes of suffix 161 total_section_contents_len: u8, 162 } 163 164 impl<'adv, S: V1Salt> EncryptedSectionContents<'adv, S> { 165 /// Constructs a representation of the contents of an encrypted V1 section 166 /// from the advertisement header, the section header, information about 167 /// the encryption used for identity verification, identity metadata, 168 /// and the entire section contents as an undecrypted ciphertext. 169 /// 170 /// # Panics 171 /// If `all_ciphertext` is greater than `NP_ADV_MAX_SECTION_LEN` bytes, 172 /// or less than `IDENTITY_TOKEN_LEN` bytes. new( adv_header: V1AdvHeader, format_bytes: &'adv [u8], salt: S, identity_token: CiphertextExtendedIdentityToken, section_contents_len: u8, section_contents: &'adv [u8], ) -> Self173 pub(crate) fn new( 174 adv_header: V1AdvHeader, 175 format_bytes: &'adv [u8], 176 salt: S, 177 identity_token: CiphertextExtendedIdentityToken, 178 section_contents_len: u8, 179 section_contents: &'adv [u8], 180 ) -> Self { 181 assert!(section_contents.len() <= NP_ADV_MAX_SECTION_LEN - V1_IDENTITY_TOKEN_LEN); 182 Self { 183 adv_header, 184 format_bytes, 185 salt, 186 identity_token, 187 total_section_contents_len: section_contents_len, 188 section_contents, 189 } 190 } 191 192 /// Gets the salt for this encrypted section salt(&self) -> MultiSalt193 pub(crate) fn salt(&self) -> MultiSalt { 194 self.salt.into() 195 } 196 197 /// Constructs some cryptographic contents for section identity-resolution 198 /// out of the entire contents of this encrypted section. compute_identity_resolution_contents<C: CryptoProvider>( &self, ) -> SectionIdentityResolutionContents199 pub(crate) fn compute_identity_resolution_contents<C: CryptoProvider>( 200 &self, 201 ) -> SectionIdentityResolutionContents { 202 let nonce = self.salt.compute_nonce::<C>(); 203 SectionIdentityResolutionContents { nonce, identity_token: self.identity_token } 204 } 205 206 /// Given an identity-match, decrypts the ciphertext in this encrypted section 207 /// and returns the raw bytes of the decrypted plaintext. decrypt_ciphertext<C: CryptoProvider>( &self, arena: &mut DeserializationArenaAllocator<'adv>, mut identity_match: IdentityMatch<C>, ) -> Result<RawDecryptedSection<'adv>, ArenaOutOfSpace>208 pub(crate) fn decrypt_ciphertext<C: CryptoProvider>( 209 &self, 210 arena: &mut DeserializationArenaAllocator<'adv>, 211 mut identity_match: IdentityMatch<C>, 212 ) -> Result<RawDecryptedSection<'adv>, ArenaOutOfSpace> { 213 // Fill decrypt_buf with the ciphertext after the section length 214 let decrypt_buf = 215 arena 216 .allocate(u8::try_from(self.section_contents.len()).expect( 217 "section_contents.len() must be in [0, NP_ADV_MAX_SECTION_CONTENTS_LEN - EXTENDED_IDENTITY_TOKEN_LEN]", 218 ))?; 219 decrypt_buf.copy_from_slice(self.section_contents); 220 221 // Decrypt everything after the metadata key 222 identity_match.cipher.apply_keystream(decrypt_buf); 223 224 Ok(RawDecryptedSection { 225 identity_token: identity_match.identity_token, 226 nonce: identity_match.nonce, 227 plaintext_contents: decrypt_buf, 228 }) 229 } 230 231 /// Try decrypting into some raw bytes given some raw identity-resolution material. 232 #[cfg(feature = "devtools")] try_resolve_identity_and_decrypt<P: CryptoProvider>( &self, allocator: &mut DeserializationArenaAllocator<'adv>, identity_resolution_material: &SectionIdentityResolutionMaterial, ) -> Option<Result<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>, ArenaOutOfSpace>>233 pub(crate) fn try_resolve_identity_and_decrypt<P: CryptoProvider>( 234 &self, 235 allocator: &mut DeserializationArenaAllocator<'adv>, 236 identity_resolution_material: &SectionIdentityResolutionMaterial, 237 ) -> Option<Result<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>, ArenaOutOfSpace>> { 238 self.compute_identity_resolution_contents::<P>() 239 .try_match(identity_resolution_material) 240 .map(|identity_match| { 241 Ok(self.decrypt_ciphertext::<P>(allocator, identity_match)?.to_raw_bytes()) 242 }) 243 } 244 } 245 246 /// An encrypted section which is verified using a ed25519 signature 247 #[derive(PartialEq, Eq, Debug)] 248 pub(crate) struct SignatureEncryptedSection<'a> { 249 pub(crate) contents: EncryptedSectionContents<'a, ExtendedV1Salt>, 250 } 251 252 impl<'a> SignatureEncryptedSection<'a> { 253 /// Try deserializing into a [`DecryptedSection`] given an identity-match 254 /// with some paired verification material for the matched identity. try_deserialize<P>( &self, arena: &mut DeserializationArenaAllocator<'a>, identity_match: IdentityMatch<P>, verification_material: &SignedSectionVerificationMaterial, ) -> Result<DecryptedSection<'a>, DeserializationError<SignatureVerificationError>> where P: CryptoProvider,255 pub(crate) fn try_deserialize<P>( 256 &self, 257 arena: &mut DeserializationArenaAllocator<'a>, 258 identity_match: IdentityMatch<P>, 259 verification_material: &SignedSectionVerificationMaterial, 260 ) -> Result<DecryptedSection<'a>, DeserializationError<SignatureVerificationError>> 261 where 262 P: CryptoProvider, 263 { 264 let identity_token = identity_match.identity_token; 265 let raw_decrypted = self.contents.decrypt_ciphertext(arena, identity_match)?; 266 let nonce = raw_decrypted.nonce; 267 let remaining = raw_decrypted.plaintext_contents; 268 269 let (plaintext_des, sig) = remaining 270 .split_last_chunk::<{ crypto_provider::ed25519::SIGNATURE_LENGTH }>() 271 .ok_or(SignatureVerificationError::SignatureMissing)?; 272 273 let expected_signature = crypto_provider::ed25519::Signature::from(*sig); 274 275 let section_signature_payload = SectionSignaturePayload::new( 276 self.contents.format_bytes, 277 self.contents.salt.bytes(), 278 &nonce, 279 identity_token.as_slice(), 280 self.contents.total_section_contents_len, 281 plaintext_des, 282 ); 283 284 let public_key = verification_material.signature_verification_public_key(); 285 286 section_signature_payload.verify::<P::Ed25519>(expected_signature, &public_key).map_err( 287 |e| { 288 // Length of the payload should fit in the signature verification buffer. 289 debug_assert!(e != np_ed25519::SignatureVerificationError::PayloadTooBig); 290 SignatureVerificationError::SignatureMismatch 291 }, 292 )?; 293 294 Ok(DecryptedSection::new( 295 VerificationMode::Signature, 296 self.contents.salt(), 297 identity_token, 298 plaintext_des, 299 )) 300 } 301 302 /// Try decrypting into some raw bytes given some raw signed crypto-material. 303 #[cfg(feature = "devtools")] try_resolve_identity_and_decrypt<P: CryptoProvider>( &self, allocator: &mut DeserializationArenaAllocator<'a>, identity_resolution_material: &SignedSectionIdentityResolutionMaterial, ) -> Option<Result<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>, ArenaOutOfSpace>>304 pub(crate) fn try_resolve_identity_and_decrypt<P: CryptoProvider>( 305 &self, 306 allocator: &mut DeserializationArenaAllocator<'a>, 307 identity_resolution_material: &SignedSectionIdentityResolutionMaterial, 308 ) -> Option<Result<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>, ArenaOutOfSpace>> { 309 self.contents.try_resolve_identity_and_decrypt::<P>( 310 allocator, 311 identity_resolution_material.as_raw_resolution_material(), 312 ) 313 } 314 315 /// Try deserializing into a [Section] given some raw signed crypto-material. 316 /// 317 /// A less-efficient, one-shot way of getting 318 /// [EncryptedSectionContents::compute_identity_resolution_contents] and then attempting 319 /// deserialization. 320 /// 321 /// Normally, id resolution contents would be calculated for a bunch of sections, and then have 322 /// many identities tried on them. This just works for one identity. 323 #[cfg(test)] try_resolve_identity_and_deserialize<P: CryptoProvider>( &self, allocator: &mut DeserializationArenaAllocator<'a>, identity_resolution_material: &SignedSectionIdentityResolutionMaterial, verification_material: &SignedSectionVerificationMaterial, ) -> Result< DecryptedSection, IdentityResolutionOrDeserializationError<SignatureVerificationError>, >324 pub(crate) fn try_resolve_identity_and_deserialize<P: CryptoProvider>( 325 &self, 326 allocator: &mut DeserializationArenaAllocator<'a>, 327 identity_resolution_material: &SignedSectionIdentityResolutionMaterial, 328 verification_material: &SignedSectionVerificationMaterial, 329 ) -> Result< 330 DecryptedSection, 331 IdentityResolutionOrDeserializationError<SignatureVerificationError>, 332 > { 333 match self 334 .contents 335 .compute_identity_resolution_contents::<P>() 336 .try_match::<P>(identity_resolution_material.as_raw_resolution_material()) 337 { 338 Some(identity_match) => self 339 .try_deserialize(allocator, identity_match, verification_material) 340 .map_err(|e| e.into()), 341 None => Err(IdentityResolutionOrDeserializationError::IdentityMatchingError), 342 } 343 } 344 } 345 346 /// An error when attempting to deserialize an encrypted advertisement, 347 /// assuming that we already have an identity-match. 348 /// 349 /// This should not be exposed publicly, since it's too 350 /// detailed. 351 #[derive(Debug, PartialEq, Eq)] 352 pub(crate) enum DeserializationError<V: VerificationError> { 353 /// Verification failed 354 VerificationError(V), 355 /// The given arena ran out of space 356 ArenaOutOfSpace, 357 } 358 359 impl<V: VerificationError> From<ArenaOutOfSpace> for DeserializationError<V> { from(_: ArenaOutOfSpace) -> Self360 fn from(_: ArenaOutOfSpace) -> Self { 361 Self::ArenaOutOfSpace 362 } 363 } 364 365 impl<V: VerificationError> From<V> for DeserializationError<V> { from(verification_error: V) -> Self366 fn from(verification_error: V) -> Self { 367 Self::VerificationError(verification_error) 368 } 369 } 370 371 /// Common trait bound for errors which arise during 372 /// verification of encrypted section contents. 373 /// 374 /// Implementors should not be exposed publicly, since it's too 375 /// detailed. 376 pub(crate) trait VerificationError: Debug + PartialEq + Eq {} 377 378 /// An error when attempting to verify a signature 379 #[derive(Debug, PartialEq, Eq)] 380 pub(crate) enum SignatureVerificationError { 381 /// The provided signature did not match the calculated signature 382 SignatureMismatch, 383 /// The provided signature is missing 384 SignatureMissing, 385 } 386 387 impl VerificationError for SignatureVerificationError {} 388 389 /// An encrypted section whose contents are verified to match a message integrity code (MIC) 390 #[derive(PartialEq, Eq, Debug)] 391 pub(crate) struct MicEncryptedSection<'a> { 392 pub(crate) contents: EncryptedSectionContents<'a, MultiSalt>, 393 pub(crate) mic: SectionMic, 394 } 395 396 impl<'a> MicEncryptedSection<'a> { 397 /// Try deserializing into a [`DecryptedSection`]. 398 /// 399 /// Returns an error if the credential is incorrect or if the section data is malformed. try_deserialize<P>( &self, allocator: &mut DeserializationArenaAllocator<'a>, identity_match: IdentityMatch<P>, crypto_material: &impl V1DiscoveryCryptoMaterial, ) -> Result<DecryptedSection<'a>, DeserializationError<MicVerificationError>> where P: CryptoProvider,400 pub(crate) fn try_deserialize<P>( 401 &self, 402 allocator: &mut DeserializationArenaAllocator<'a>, 403 identity_match: IdentityMatch<P>, 404 crypto_material: &impl V1DiscoveryCryptoMaterial, 405 ) -> Result<DecryptedSection<'a>, DeserializationError<MicVerificationError>> 406 where 407 P: CryptoProvider, 408 { 409 let hmac_key = match self.contents.salt { 410 MultiSalt::Short(_) => { 411 crypto_material.mic_short_salt_verification_material::<P>().mic_hmac_key() 412 } 413 MultiSalt::Extended(_) => { 414 crypto_material.mic_extended_salt_verification_material::<P>().mic_hmac_key() 415 } 416 }; 417 418 let mut mic_hmac = hmac_key.build_hmac::<P>(); 419 // if mic is ok, the section was generated by someone holding at least the shared credential 420 mic_hmac.update(&NP_SVC_UUID); 421 mic_hmac.update(&[self.contents.adv_header.contents()]); 422 // section format 423 mic_hmac.update(self.contents.format_bytes); 424 // salt bytes 425 mic_hmac.update(self.contents.salt.as_slice()); 426 // nonce 427 mic_hmac.update(identity_match.nonce.as_slice()); 428 // ciphertext identity token 429 mic_hmac.update(self.contents.identity_token.0.as_slice()); 430 // section payload len 431 mic_hmac.update(&[self.contents.total_section_contents_len]); 432 // rest of encrypted contents 433 mic_hmac.update(self.contents.section_contents); 434 mic_hmac 435 // adv only contains first 16 bytes of HMAC 436 .verify_truncated_left(&self.mic.mic) 437 .map_err(|_e| MicVerificationError::MicMismatch)?; 438 439 // plaintext identity token, already decrypted during identity match 440 let identity_token = identity_match.identity_token; 441 let raw_decrypted = self.contents.decrypt_ciphertext(allocator, identity_match)?; 442 Ok(DecryptedSection::new( 443 VerificationMode::Mic, 444 self.contents.salt(), 445 identity_token, 446 raw_decrypted.plaintext_contents, 447 )) 448 } 449 450 /// Try decrypting into some raw bytes given some raw unsigned crypto-material. 451 #[cfg(feature = "devtools")] try_resolve_short_salt_identity_and_decrypt<P: CryptoProvider>( &self, allocator: &mut DeserializationArenaAllocator<'a>, identity_resolution_material: &MicShortSaltSectionIdentityResolutionMaterial, ) -> Option<Result<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>, ArenaOutOfSpace>>452 pub(crate) fn try_resolve_short_salt_identity_and_decrypt<P: CryptoProvider>( 453 &self, 454 allocator: &mut DeserializationArenaAllocator<'a>, 455 identity_resolution_material: &MicShortSaltSectionIdentityResolutionMaterial, 456 ) -> Option<Result<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>, ArenaOutOfSpace>> { 457 self.contents.try_resolve_identity_and_decrypt::<P>( 458 allocator, 459 identity_resolution_material.as_raw_resolution_material(), 460 ) 461 } 462 463 /// Try decrypting into some raw bytes given some raw unsigned crypto-material. 464 #[cfg(feature = "devtools")] try_resolve_extended_salt_identity_and_decrypt<P: CryptoProvider>( &self, allocator: &mut DeserializationArenaAllocator<'a>, identity_resolution_material: &MicExtendedSaltSectionIdentityResolutionMaterial, ) -> Option<Result<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>, ArenaOutOfSpace>>465 pub(crate) fn try_resolve_extended_salt_identity_and_decrypt<P: CryptoProvider>( 466 &self, 467 allocator: &mut DeserializationArenaAllocator<'a>, 468 identity_resolution_material: &MicExtendedSaltSectionIdentityResolutionMaterial, 469 ) -> Option<Result<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>, ArenaOutOfSpace>> { 470 self.contents.try_resolve_identity_and_decrypt::<P>( 471 allocator, 472 identity_resolution_material.as_raw_resolution_material(), 473 ) 474 } 475 476 /// Try deserializing into a [Section] given some raw unsigned crypto-material. 477 #[cfg(test)] try_resolve_identity_and_deserialize<P: CryptoProvider>( &self, allocator: &mut DeserializationArenaAllocator<'a>, crypto_material: &impl V1DiscoveryCryptoMaterial, ) -> Result<DecryptedSection, IdentityResolutionOrDeserializationError<MicVerificationError>>478 pub(crate) fn try_resolve_identity_and_deserialize<P: CryptoProvider>( 479 &self, 480 allocator: &mut DeserializationArenaAllocator<'a>, 481 crypto_material: &impl V1DiscoveryCryptoMaterial, 482 ) -> Result<DecryptedSection, IdentityResolutionOrDeserializationError<MicVerificationError>> 483 { 484 let section_identity_resolution_contents = 485 self.contents.compute_identity_resolution_contents::<P>(); 486 487 let identity_match = match self.contents.salt { 488 MultiSalt::Short(_) => section_identity_resolution_contents.try_match::<P>( 489 crypto_material 490 .mic_short_salt_identity_resolution_material::<P>() 491 .as_raw_resolution_material(), 492 ), 493 MultiSalt::Extended(_) => section_identity_resolution_contents.try_match::<P>( 494 crypto_material 495 .mic_extended_salt_identity_resolution_material::<P>() 496 .as_raw_resolution_material(), 497 ), 498 } 499 .ok_or(IdentityResolutionOrDeserializationError::IdentityMatchingError)?; 500 501 self.try_deserialize(allocator, identity_match, crypto_material).map_err(|e| e.into()) 502 } 503 } 504 505 #[derive(Debug, PartialEq, Eq)] 506 pub(crate) enum MicVerificationError { 507 /// Calculated MIC did not match MIC from section 508 MicMismatch, 509 } 510 511 impl VerificationError for MicVerificationError {} 512