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