1 // Copyright 2023 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 //! Core NP Rust FFI structures and methods for v1 advertisement deserialization. 15 16 use super::DeserializeAdvertisementError; 17 use crate::{ 18 common::*, 19 credentials::{CredentialBook, MatchedCredential}, 20 deserialize::{allocate_decrypted_metadata_handle, DecryptMetadataResult}, 21 utils::*, 22 v1::V1VerificationMode, 23 }; 24 use array_view::ArrayView; 25 use crypto_provider::CryptoProvider; 26 use crypto_provider_default::CryptoProviderImpl; 27 use handle_map::{declare_handle_map, HandleLike}; 28 use np_adv::{ 29 credential::matched::WithMatchedCredential, 30 extended::{ 31 deserialize::{ 32 data_element::DataElementParseError, V1AdvertisementContents, V1DeserializedSection, 33 }, 34 salt::MultiSalt, 35 }, 36 }; 37 38 /// Representation of a deserialized V1 advertisement 39 #[repr(C)] 40 pub struct DeserializedV1Advertisement { 41 /// The number of legible sections 42 pub num_legible_sections: u8, 43 /// The number of sections that were unable to be decrypted 44 pub num_undecryptable_sections: u8, 45 /// A handle to the set of legible (plain or decrypted) sections 46 pub legible_sections: LegibleV1Sections, 47 } 48 49 impl DeserializedV1Advertisement { 50 /// Gets the number of legible sections in this deserialized V1 advertisement. num_legible_sections(&self) -> u851 pub fn num_legible_sections(&self) -> u8 { 52 self.num_legible_sections 53 } 54 55 /// Gets the number of undecryptable sections in this deserialized V1 advertisement. num_undecryptable_sections(&self) -> u856 pub fn num_undecryptable_sections(&self) -> u8 { 57 self.num_undecryptable_sections 58 } 59 60 /// Gets the legible section with the given index (which is bounded in 61 /// `0..self.num_legible_sections()`). This uses the internal handle but does not take 62 /// ownership of it. get_section(&self, legible_section_index: u8) -> GetV1SectionResult63 pub fn get_section(&self, legible_section_index: u8) -> GetV1SectionResult { 64 match self.legible_sections.get() { 65 Ok(sections_read_guard) => { 66 sections_read_guard.get_section(self.legible_sections, legible_section_index) 67 } 68 Err(_) => GetV1SectionResult::Error, 69 } 70 } 71 72 /// Attempts to deallocate memory utilized internally by this V1 advertisement (which contains 73 /// a handle to actual advertisement contents behind-the-scenes). This function takes ownership 74 /// of the internal handle. deallocate(self) -> DeallocateResult75 pub fn deallocate(self) -> DeallocateResult { 76 self.legible_sections.deallocate().map(|_| ()).into() 77 } 78 allocate_with_contents( contents: V1AdvertisementContents< np_adv::credential::matched::ReferencedMatchedCredential<MatchedCredential>, >, ) -> Result<Self, DeserializeAdvertisementError>79 pub(crate) fn allocate_with_contents( 80 contents: V1AdvertisementContents< 81 np_adv::credential::matched::ReferencedMatchedCredential<MatchedCredential>, 82 >, 83 ) -> Result<Self, DeserializeAdvertisementError> { 84 // 16-section limit enforced by np_adv 85 let num_undecryptable_sections = contents.invalid_sections_count() as u8; 86 let legible_sections = contents.into_sections(); 87 let num_legible_sections = legible_sections.len() as u8; 88 let legible_sections = 89 LegibleV1Sections::allocate_with_contents(legible_sections.into_vec())?; 90 Ok(Self { num_undecryptable_sections, num_legible_sections, legible_sections }) 91 } 92 } 93 94 /// Internal, Rust-side implementation of a listing of legible sections 95 /// in a deserialized V1 advertisement 96 pub struct LegibleV1SectionsInternals { 97 sections: Vec<DeserializedV1SectionInternals>, 98 } 99 100 impl LegibleV1SectionsInternals { get_section_internals( &self, legible_section_index: u8, ) -> Option<&DeserializedV1SectionInternals>101 fn get_section_internals( 102 &self, 103 legible_section_index: u8, 104 ) -> Option<&DeserializedV1SectionInternals> { 105 self.sections.get(legible_section_index as usize) 106 } get_section( &self, legible_sections_handle: LegibleV1Sections, legible_section_index: u8, ) -> GetV1SectionResult107 fn get_section( 108 &self, 109 legible_sections_handle: LegibleV1Sections, 110 legible_section_index: u8, 111 ) -> GetV1SectionResult { 112 match self.get_section_internals(legible_section_index) { 113 Some(section_ref) => { 114 // Determine whether the section is plaintext 115 // or decrypted to report back to the caller, 116 // and also determine the number of contained DEs. 117 let num_des = section_ref.num_des(); 118 let identity_tag = section_ref.identity_kind(); 119 GetV1SectionResult::Success(DeserializedV1Section { 120 legible_sections_handle, 121 legible_section_index, 122 num_des, 123 identity_tag, 124 }) 125 } 126 None => GetV1SectionResult::Error, 127 } 128 } 129 } 130 131 impl<'adv> 132 TryFrom< 133 Vec< 134 V1DeserializedSection< 135 'adv, 136 np_adv::credential::matched::ReferencedMatchedCredential<'adv, MatchedCredential>, 137 >, 138 >, 139 > for LegibleV1SectionsInternals 140 { 141 type Error = DataElementParseError; 142 try_from( contents: Vec< V1DeserializedSection< 'adv, np_adv::credential::matched::ReferencedMatchedCredential<'adv, MatchedCredential>, >, >, ) -> Result<Self, Self::Error>143 fn try_from( 144 contents: Vec< 145 V1DeserializedSection< 146 'adv, 147 np_adv::credential::matched::ReferencedMatchedCredential<'adv, MatchedCredential>, 148 >, 149 >, 150 ) -> Result<Self, Self::Error> { 151 let sections = contents 152 .into_iter() 153 .map(DeserializedV1SectionInternals::try_from) 154 .collect::<Result<Vec<_>, _>>()?; 155 Ok(Self { sections }) 156 } 157 } 158 159 /// A `#[repr(C)]` handle to a value of type `LegibleV1SectionsInternals` 160 #[repr(C)] 161 #[derive(Clone, Copy, PartialEq, Eq)] 162 pub struct LegibleV1Sections { 163 handle_id: u64, 164 } 165 166 declare_handle_map!( 167 legible_v1_sections, 168 crate::common::default_handle_map_dimensions(), 169 super::LegibleV1Sections, 170 super::LegibleV1SectionsInternals 171 ); 172 173 impl LocksLongerThan<LegibleV1Sections> for CredentialBook {} 174 175 impl LegibleV1Sections { allocate_with_contents( contents: Vec< V1DeserializedSection< np_adv::credential::matched::ReferencedMatchedCredential<MatchedCredential>, >, >, ) -> Result<Self, DeserializeAdvertisementError>176 pub(crate) fn allocate_with_contents( 177 contents: Vec< 178 V1DeserializedSection< 179 np_adv::credential::matched::ReferencedMatchedCredential<MatchedCredential>, 180 >, 181 >, 182 ) -> Result<Self, DeserializeAdvertisementError> { 183 let section = LegibleV1SectionsInternals::try_from(contents) 184 .map_err(|_| DeserializeAdvertisementError)?; 185 Self::allocate(move || section).map_err(|e| e.into()) 186 } 187 188 /// Gets the legible section with the given index (which is bounded in 189 /// `0..self.num_legible_sections()`). This function uses this handle but does not take 190 /// ownership of it. get_section(&self, legible_section_index: u8) -> GetV1SectionResult191 pub fn get_section(&self, legible_section_index: u8) -> GetV1SectionResult { 192 match self.get() { 193 Ok(sections_read_guard) => { 194 sections_read_guard.get_section(*self, legible_section_index) 195 } 196 Err(_) => GetV1SectionResult::Error, 197 } 198 } 199 200 /// Get a data element by section index and de index. Similar to `get_section().get_de()` but 201 /// will only lock the HandleMap once. This function uses this handle but does not take 202 /// ownership of it. get_section_de(&self, legible_section_index: u8, de_index: u8) -> GetV1DEResult203 pub fn get_section_de(&self, legible_section_index: u8, de_index: u8) -> GetV1DEResult { 204 let Ok(sections) = self.get() else { 205 return GetV1DEResult::Error; 206 }; 207 let Some(section) = sections.get_section_internals(legible_section_index) else { 208 return GetV1DEResult::Error; 209 }; 210 section.get_de(de_index) 211 } 212 } 213 214 /// Discriminant for `GetV1SectionResult` 215 #[derive(Clone, Copy)] 216 #[repr(u8)] 217 pub enum GetV1SectionResultKind { 218 /// The attempt to get the section failed, 219 /// possibly due to the section index being 220 /// out-of-bounds or due to the underlying 221 /// advertisement having already been deallocated. 222 Error = 0, 223 /// The attempt to get the section succeeded. 224 /// The wrapped section may be obtained via 225 /// `GetV1SectionResult#into_success`. 226 Success = 1, 227 } 228 229 /// The result of attempting to get a particular V1 section 230 /// from its' index within the list of legible sections 231 /// via `DeserializedV1Advertisement::get_section`. 232 #[repr(C)] 233 #[allow(missing_docs)] 234 pub enum GetV1SectionResult { 235 Error, 236 Success(DeserializedV1Section), 237 } 238 239 impl FfiEnum for GetV1SectionResult { 240 type Kind = GetV1SectionResultKind; kind(&self) -> Self::Kind241 fn kind(&self) -> Self::Kind { 242 match self { 243 GetV1SectionResult::Error => GetV1SectionResultKind::Error, 244 GetV1SectionResult::Success(_) => GetV1SectionResultKind::Success, 245 } 246 } 247 } 248 249 impl GetV1SectionResult { 250 declare_enum_cast! {into_success, Success, DeserializedV1Section} 251 } 252 253 /// Discriminant for `GetV1DE16ByteSaltResult`. 254 #[derive(Clone, Copy)] 255 #[repr(u8)] 256 pub enum GetV1DE16ByteSaltResultKind { 257 /// The attempt to get the derived salt failed, possibly 258 /// because the passed DE offset was invalid (==255), 259 /// or because there was no salt included for the 260 /// referenced advertisement section (i.e: it was 261 /// a public advertisement section, or it was deallocated.) 262 Error = 0, 263 /// A 16-byte salt for the given DE offset was successfully 264 /// derived. 265 Success = 1, 266 } 267 268 /// The result of attempting to get a derived 16-byte salt 269 /// for a given DE within a section. 270 #[repr(C)] 271 #[allow(missing_docs)] 272 pub enum GetV1DE16ByteSaltResult { 273 Error, 274 Success(FixedSizeArray<16>), 275 } 276 277 impl GetV1DE16ByteSaltResult { 278 declare_enum_cast! {into_success, Success, FixedSizeArray<16>} 279 } 280 281 impl FfiEnum for GetV1DE16ByteSaltResult { 282 type Kind = GetV1DE16ByteSaltResultKind; kind(&self) -> Self::Kind283 fn kind(&self) -> Self::Kind { 284 match self { 285 GetV1DE16ByteSaltResult::Error => GetV1DE16ByteSaltResultKind::Error, 286 GetV1DE16ByteSaltResult::Success(_) => GetV1DE16ByteSaltResultKind::Success, 287 } 288 } 289 } 290 291 /// The internal FFI-friendly representation of a deserialized v1 section 292 pub struct DeserializedV1SectionInternals { 293 des: Vec<V1DataElement>, 294 identity: Option<DeserializedV1IdentityInternals>, 295 } 296 297 impl DeserializedV1SectionInternals { 298 /// Gets the number of data-elements in this section. num_des(&self) -> u8299 fn num_des(&self) -> u8 { 300 self.des.len() as u8 301 } 302 303 /// Gets the enum tag of the identity used for this section. identity_kind(&self) -> DeserializedV1IdentityKind304 fn identity_kind(&self) -> DeserializedV1IdentityKind { 305 if self.identity.is_some() { 306 DeserializedV1IdentityKind::Decrypted 307 } else { 308 DeserializedV1IdentityKind::Plaintext 309 } 310 } 311 312 /// Attempts to get the DE with the given index in this section. get_de(&self, index: u8) -> GetV1DEResult313 fn get_de(&self, index: u8) -> GetV1DEResult { 314 match self.des.get(index as usize) { 315 Some(de) => GetV1DEResult::Success(de.clone()), 316 None => GetV1DEResult::Error, 317 } 318 } 319 320 /// Attempts to get the directly-transmissible details about 321 /// the deserialized V1 identity for this section. Does 322 /// not include decrypted metadata bytes nor the section salt. get_identity_details(&self) -> GetV1IdentityDetailsResult323 pub(crate) fn get_identity_details(&self) -> GetV1IdentityDetailsResult { 324 match &self.identity { 325 Some(identity) => GetV1IdentityDetailsResult::Success(identity.details()), 326 None => GetV1IdentityDetailsResult::Error, 327 } 328 } 329 330 /// Attempts to decrypt the metadata for the matched 331 /// credential for this V1 section (if any). decrypt_metadata(&self) -> DecryptMetadataResult332 pub(crate) fn decrypt_metadata(&self) -> DecryptMetadataResult { 333 match &self.identity { 334 None => DecryptMetadataResult::Error, 335 Some(identity) => match identity.decrypt_metadata() { 336 None => DecryptMetadataResult::Error, 337 Some(metadata) => allocate_decrypted_metadata_handle(metadata), 338 }, 339 } 340 } 341 342 /// Attempts to derive a 16-byte DE salt for a DE in this section 343 /// with the given DE offset. This operation may fail if the 344 /// passed offset is 255 (causes overflow) or if the section 345 /// is leveraging a public identity, and hence, doesn't have 346 /// an associated salt. derive_16_byte_salt_for_offset<C: CryptoProvider>( &self, de_offset: u8, ) -> GetV1DE16ByteSaltResult347 pub(crate) fn derive_16_byte_salt_for_offset<C: CryptoProvider>( 348 &self, 349 de_offset: u8, 350 ) -> GetV1DE16ByteSaltResult { 351 self.identity 352 .as_ref() 353 .and_then(|x| x.derive_16_byte_salt_for_offset::<C>(de_offset)) 354 .map_or(GetV1DE16ByteSaltResult::Error, GetV1DE16ByteSaltResult::Success) 355 } 356 } 357 358 impl<'adv> 359 TryFrom< 360 V1DeserializedSection< 361 'adv, 362 np_adv::credential::matched::ReferencedMatchedCredential<'adv, MatchedCredential>, 363 >, 364 > for DeserializedV1SectionInternals 365 { 366 type Error = DataElementParseError; 367 try_from( section: V1DeserializedSection< np_adv::credential::matched::ReferencedMatchedCredential<'adv, MatchedCredential>, >, ) -> Result<Self, Self::Error>368 fn try_from( 369 section: V1DeserializedSection< 370 np_adv::credential::matched::ReferencedMatchedCredential<'adv, MatchedCredential>, 371 >, 372 ) -> Result<Self, Self::Error> { 373 use np_adv::extended::deserialize::Section; 374 match section { 375 V1DeserializedSection::Plaintext(section) => { 376 let des = section 377 .iter_data_elements() 378 .map(|r| r.map(|de| V1DataElement::from(&de))) 379 .collect::<Result<Vec<_>, _>>()?; 380 let identity = None; 381 Ok(Self { des, identity }) 382 } 383 V1DeserializedSection::Decrypted(with_matched) => { 384 let section = with_matched.contents(); 385 let des = section 386 .iter_data_elements() 387 .map(|r| r.map(|de| V1DataElement::from(&de))) 388 .collect::<Result<Vec<_>, _>>()?; 389 390 let verification_mode = section.verification_mode(); 391 let salt = *section.salt(); 392 393 let match_data = with_matched.clone_match_data(); 394 let match_data = match_data.map(|x| *x.identity_token()); 395 396 let identity = 397 Some(DeserializedV1IdentityInternals::new(verification_mode, salt, match_data)); 398 Ok(Self { des, identity }) 399 } 400 } 401 } 402 } 403 /// Discriminant for `DeserializedV1Identity`. 404 #[derive(Clone, Copy)] 405 #[repr(u8)] 406 pub enum DeserializedV1IdentityKind { 407 /// The deserialized v1 identity was plaintext 408 Plaintext = 0, 409 /// The deserialized v1 identity corresponded 410 /// to some kind of decrypted identity. 411 Decrypted = 1, 412 } 413 414 /// Internals for the representation of a decrypted 415 /// V1 section identity. 416 pub(crate) struct DeserializedV1IdentityInternals { 417 /// The details about the identity, suitable 418 /// for direct communication over FFI 419 details: DeserializedV1IdentityDetails, 420 /// The metadata key, together with the matched 421 /// credential and enough information to decrypt 422 /// the credential metadata, if desired. 423 match_data: WithMatchedCredential<MatchedCredential, np_adv::extended::V1IdentityToken>, 424 /// The 16-byte section salt 425 salt: MultiSalt, 426 } 427 428 impl DeserializedV1IdentityInternals { new( verification_mode: np_adv::extended::deserialize::VerificationMode, salt: MultiSalt, match_data: WithMatchedCredential<MatchedCredential, np_adv::extended::V1IdentityToken>, ) -> Self429 pub(crate) fn new( 430 verification_mode: np_adv::extended::deserialize::VerificationMode, 431 salt: MultiSalt, 432 match_data: WithMatchedCredential<MatchedCredential, np_adv::extended::V1IdentityToken>, 433 ) -> Self { 434 let cred_id = match_data.matched_credential().id(); 435 let identity_token = match_data.contents(); 436 let details = 437 DeserializedV1IdentityDetails::new(cred_id, verification_mode, *identity_token); 438 Self { details, match_data, salt } 439 } 440 /// Gets the directly-transmissible details about 441 /// this deserialized V1 identity. Does not include 442 /// decrypted metadata bytes nor the section salt. details(&self) -> DeserializedV1IdentityDetails443 pub(crate) fn details(&self) -> DeserializedV1IdentityDetails { 444 self.details 445 } 446 /// Attempts to decrypt the metadata associated 447 /// with this identity. decrypt_metadata(&self) -> Option<Vec<u8>>448 pub(crate) fn decrypt_metadata(&self) -> Option<Vec<u8>> { 449 self.match_data.decrypt_metadata::<CryptoProviderImpl>().ok() 450 } 451 /// For a given data-element offset, derives a 16-byte DE salt 452 /// for a DE in that position within this section. derive_16_byte_salt_for_offset<C: CryptoProvider>( &self, de_offset: u8, ) -> Option<FixedSizeArray<16>>453 pub(crate) fn derive_16_byte_salt_for_offset<C: CryptoProvider>( 454 &self, 455 de_offset: u8, 456 ) -> Option<FixedSizeArray<16>> { 457 let de_offset = np_hkdf::v1_salt::DataElementOffset::from(de_offset); 458 459 match self.salt { 460 MultiSalt::Short(_) => None, 461 MultiSalt::Extended(s) => { 462 s.derive::<16, C>(Some(de_offset)).map(FixedSizeArray::from_array) 463 } 464 } 465 } 466 } 467 468 /// Discriminant for `GetV1IdentityDetailsResult` 469 #[derive(Clone, Copy)] 470 #[repr(u8)] 471 pub enum GetV1IdentityDetailsResultKind { 472 /// The attempt to get the identity details 473 /// for the section failed, possibly 474 /// due to the section being a public 475 /// section, or the underlying 476 /// advertisement has already been deallocated. 477 Error = 0, 478 /// The attempt to get the identity details succeeded. 479 /// The wrapped identity details may be obtained via 480 /// `GetV1IdentityDetailsResult#into_success`. 481 Success = 1, 482 } 483 484 /// The result of attempting to get the identity details 485 /// for a V1 advertisement section via 486 /// `DeserializedV1Advertisement#get_identity_details`. 487 #[repr(C)] 488 #[allow(missing_docs)] 489 pub enum GetV1IdentityDetailsResult { 490 Error, 491 Success(DeserializedV1IdentityDetails), 492 } 493 494 impl FfiEnum for GetV1IdentityDetailsResult { 495 type Kind = GetV1IdentityDetailsResultKind; kind(&self) -> Self::Kind496 fn kind(&self) -> Self::Kind { 497 match self { 498 GetV1IdentityDetailsResult::Error => GetV1IdentityDetailsResultKind::Error, 499 GetV1IdentityDetailsResult::Success(_) => GetV1IdentityDetailsResultKind::Success, 500 } 501 } 502 } 503 504 impl GetV1IdentityDetailsResult { 505 declare_enum_cast! {into_success, Success, DeserializedV1IdentityDetails} 506 } 507 508 /// Information about the identity which matched 509 /// a decrypted V1 section. 510 #[derive(Clone, Copy)] 511 #[repr(C)] 512 pub struct DeserializedV1IdentityDetails { 513 /// The verification mode (MIC/Signature) which 514 /// was used to verify the decrypted adv contents. 515 verification_mode: V1VerificationMode, 516 /// The ID of the credential which 517 /// matched the deserialized section. 518 cred_id: u32, 519 /// The 16-byte metadata key. 520 identity_token: [u8; 16], 521 } 522 523 impl DeserializedV1IdentityDetails { new( cred_id: u32, verification_mode: np_adv::extended::deserialize::VerificationMode, identity_token: np_adv::extended::V1IdentityToken, ) -> Self524 pub(crate) fn new( 525 cred_id: u32, 526 verification_mode: np_adv::extended::deserialize::VerificationMode, 527 identity_token: np_adv::extended::V1IdentityToken, 528 ) -> Self { 529 let verification_mode = verification_mode.into(); 530 Self { cred_id, verification_mode, identity_token: identity_token.into_bytes() } 531 } 532 /// Returns the ID of the credential which matched the deserialized section. cred_id(&self) -> u32533 pub fn cred_id(&self) -> u32 { 534 self.cred_id 535 } 536 /// Returns the verification mode (MIC/Signature) employed for the decrypted section. verification_mode(&self) -> V1VerificationMode537 pub fn verification_mode(&self) -> V1VerificationMode { 538 self.verification_mode 539 } 540 /// Returns the 16-byte section identity token. identity_token(&self) -> [u8; 16]541 pub fn identity_token(&self) -> [u8; 16] { 542 self.identity_token 543 } 544 } 545 546 /// Handle to a deserialized V1 section 547 #[repr(C)] 548 pub struct DeserializedV1Section { 549 legible_sections_handle: LegibleV1Sections, 550 legible_section_index: u8, 551 num_des: u8, 552 identity_tag: DeserializedV1IdentityKind, 553 } 554 555 impl DeserializedV1Section { 556 /// Gets the number of data elements contained in this section. 557 /// Suitable as an iteration bound on `Self::get_de`. num_des(&self) -> u8558 pub fn num_des(&self) -> u8 { 559 self.num_des 560 } 561 562 /// Gets the enum tag of the identity employed by this deserialized section. identity_kind(&self) -> DeserializedV1IdentityKind563 pub fn identity_kind(&self) -> DeserializedV1IdentityKind { 564 self.identity_tag 565 } 566 567 /// Gets the DE with the given index in this section. get_de(&self, de_index: u8) -> GetV1DEResult568 pub fn get_de(&self, de_index: u8) -> GetV1DEResult { 569 self.apply_to_section_internals( 570 move |section_ref| section_ref.get_de(de_index), 571 GetV1DEResult::Error, 572 ) 573 } 574 /// Attempts to get the details of the identity employed for the section referenced by this 575 /// handle. May fail if the handle is invalid, or if the advertisement section leverages a 576 /// public identity. This function does not take ownership of the handle. get_identity_details(&self) -> GetV1IdentityDetailsResult577 pub fn get_identity_details(&self) -> GetV1IdentityDetailsResult { 578 self.apply_to_section_internals( 579 DeserializedV1SectionInternals::get_identity_details, 580 GetV1IdentityDetailsResult::Error, 581 ) 582 } 583 /// Attempts to decrypt the metadata for the matched credential for the V1 section referenced 584 /// by this handle (if any). This uses but does not take ownership of the handle. decrypt_metadata(&self) -> DecryptMetadataResult585 pub fn decrypt_metadata(&self) -> DecryptMetadataResult { 586 self.apply_to_section_internals( 587 DeserializedV1SectionInternals::decrypt_metadata, 588 DecryptMetadataResult::Error, 589 ) 590 } 591 /// Attempts to derive a 16-byte DE salt for a DE in this section with the given DE offset. 592 /// This operation may fail if the passed offset is 255 (causes overflow) or if the section is 593 /// leveraging a public identity, and hence, doesn't have an associated salt. derive_16_byte_salt_for_offset(&self, de_offset: u8) -> GetV1DE16ByteSaltResult594 pub fn derive_16_byte_salt_for_offset(&self, de_offset: u8) -> GetV1DE16ByteSaltResult { 595 self.apply_to_section_internals( 596 move |section_ref| { 597 section_ref.derive_16_byte_salt_for_offset::<CryptoProviderImpl>(de_offset) 598 }, 599 GetV1DE16ByteSaltResult::Error, 600 ) 601 } 602 apply_to_section_internals<R>( &self, func: impl FnOnce(&DeserializedV1SectionInternals) -> R, lookup_failure_result: R, ) -> R603 fn apply_to_section_internals<R>( 604 &self, 605 func: impl FnOnce(&DeserializedV1SectionInternals) -> R, 606 lookup_failure_result: R, 607 ) -> R { 608 // TODO: Once the `FromResidual` trait is stabilized, this can be simplified. 609 match self.legible_sections_handle.get() { 610 Ok(legible_sections_read_guard) => { 611 match legible_sections_read_guard.get_section_internals(self.legible_section_index) 612 { 613 Some(section_ref) => func(section_ref), 614 None => lookup_failure_result, 615 } 616 } 617 Err(_) => lookup_failure_result, 618 } 619 } 620 } 621 622 /// Discriminant for the `GetV1DEResult` enum. 623 #[derive(Clone, Copy)] 624 #[repr(u8)] 625 pub enum GetV1DEResultKind { 626 /// Attempting to get the DE at the given position failed, 627 /// possibly due to the index being out-of-bounds or due 628 /// to the whole advertisement having been previously deallocated. 629 Error = 0, 630 /// Attempting to get the DE at the given position succeeded. 631 /// The underlying DE may be extracted with `GetV1DEResult#into_success`. 632 Success = 1, 633 } 634 635 /// Represents the result of the `DeserializedV1Section#get_de` operation. 636 #[repr(C)] 637 #[allow(missing_docs)] 638 pub enum GetV1DEResult { 639 Error, 640 Success(V1DataElement), 641 } 642 643 impl FfiEnum for GetV1DEResult { 644 type Kind = GetV1DEResultKind; kind(&self) -> Self::Kind645 fn kind(&self) -> Self::Kind { 646 match self { 647 GetV1DEResult::Error => GetV1DEResultKind::Error, 648 GetV1DEResult::Success(_) => GetV1DEResultKind::Success, 649 } 650 } 651 } 652 653 impl GetV1DEResult { 654 declare_enum_cast! {into_success, Success, V1DataElement} 655 } 656 657 /// FFI-transmissible representation of a V1 data-element 658 #[derive(Clone)] 659 #[repr(C)] 660 pub enum V1DataElement { 661 /// A "generic" V1 data-element, for which we have no 662 /// particular information about its schema (just 663 /// a DE type code and a byte payload.) 664 Generic(GenericV1DataElement), 665 } 666 667 impl V1DataElement { 668 // Note: not using declare_enum_cast! for this one, because if V1DataElement 669 // gets more variants, this will have a different internal implementation 670 /// Converts a `V1DataElement` to a `GenericV1DataElement` which 671 /// only maintains information about the DE's type-code and payload. to_generic(self) -> GenericV1DataElement672 pub fn to_generic(self) -> GenericV1DataElement { 673 match self { 674 V1DataElement::Generic(x) => x, 675 } 676 } 677 } 678 679 impl<'a> From<&'a np_adv::extended::deserialize::data_element::DataElement<'a>> for V1DataElement { from(de: &'a np_adv::extended::deserialize::data_element::DataElement<'a>) -> Self680 fn from(de: &'a np_adv::extended::deserialize::data_element::DataElement<'a>) -> Self { 681 let offset = de.offset().as_u8(); 682 let de_type = V1DEType::from(de.de_type()); 683 let contents_as_slice = de.contents(); 684 //Guaranteed not to panic due DE size limit. 685 #[allow(clippy::unwrap_used)] 686 let array_view: ArrayView<u8, 127> = ArrayView::try_from_slice(contents_as_slice).unwrap(); 687 let payload = ByteBuffer::from_array_view(array_view); 688 Self::Generic(GenericV1DataElement { de_type, offset, payload }) 689 } 690 } 691 692 /// FFI-transmissible representation of a generic V1 data-element. 693 /// This representation is stable, and so you may directly 694 /// reference this struct's fields if you wish. 695 #[derive(Clone)] 696 #[repr(C)] 697 pub struct GenericV1DataElement { 698 /// The offset of this generic data-element. 699 pub offset: u8, 700 /// The DE type code of this generic data-element. 701 pub de_type: V1DEType, 702 /// The raw data-element byte payload, up to 703 /// 127 bytes in length. 704 pub payload: ByteBuffer<127>, 705 } 706 707 impl GenericV1DataElement { 708 /// Gets the offset for this generic V1 data element. offset(&self) -> u8709 pub fn offset(&self) -> u8 { 710 self.offset 711 } 712 /// Gets the DE-type of this generic V1 data element. de_type(&self) -> V1DEType713 pub fn de_type(&self) -> V1DEType { 714 self.de_type 715 } 716 /// Destructures this `GenericV1DataElement` into just the DE payload byte-buffer. into_payload(self) -> ByteBuffer<127>717 pub fn into_payload(self) -> ByteBuffer<127> { 718 self.payload 719 } 720 } 721 722 /// Representation of the data-element type tag 723 /// of a V1 data element. 724 #[derive(Clone, Copy)] 725 #[repr(C)] 726 pub struct V1DEType { 727 code: u32, 728 } 729 730 impl From<np_adv::extended::de_type::DeType> for V1DEType { from(de_type: np_adv::extended::de_type::DeType) -> Self731 fn from(de_type: np_adv::extended::de_type::DeType) -> Self { 732 let code = de_type.as_u32(); 733 Self { code } 734 } 735 } 736 737 impl V1DEType { 738 /// Yields this V1 DE type code as a u32. to_u32(&self) -> u32739 pub fn to_u32(&self) -> u32 { 740 self.code 741 } 742 } 743