1 // Copyright 2023 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 //      http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 //! NP Rust FFI structures and methods for V1 advertisement serialization.
15 
16 use crate::common::*;
17 use crate::credentials::V1BroadcastCredential;
18 use crate::serialize::AdvertisementBuilderKind;
19 use crate::utils::FfiEnum;
20 use crate::v1::V1VerificationMode;
21 use crypto_provider_default::CryptoProviderImpl;
22 use handle_map::{declare_handle_map, HandleLike, HandleMapFullError};
23 
24 /// A handle to a builder for V1 advertisements.
25 #[derive(Clone, Copy)]
26 #[repr(C)]
27 pub struct V1AdvertisementBuilder {
28     kind: AdvertisementBuilderKind,
29     handle: V1AdvertisementBuilderHandle,
30 }
31 
32 impl V1AdvertisementBuilder {
33     /// Attempts to create a builder for a new public section within this advertisement, returning
34     /// an owned handle to the newly-created section builder if successful.
35     ///
36     /// This method may fail if there is another currently-active section builder for the same
37     /// advertisement builder, if the kind of section being added does not match the advertisement
38     /// type (public/encrypted), or if the section would not manage to fit within the enclosing
39     /// advertisement.
public_section_builder(&self) -> CreateV1SectionBuilderResult40     pub fn public_section_builder(&self) -> CreateV1SectionBuilderResult {
41         self.section_builder_internals(|internals| internals.public_section_builder())
42     }
43 
44     /// Gets the kind of advertisement builder (public/encrypted)
kind(&self) -> AdvertisementBuilderKind45     pub fn kind(&self) -> AdvertisementBuilderKind {
46         self.kind
47     }
48 
49     /// Attempts to create a builder for a new encrypted section within this advertisement,
50     /// returning an owned handle to the newly-created section builder if successful.
51     ///
52     /// The identity details for the new section builder may be specified
53     /// via providing the broadcast credential data, the kind of encrypted
54     /// identity being broadcast (private/trusted/provisioned), and the
55     /// verification mode (MIC/Signature) to be used for the encrypted section.
56     ///
57     /// This method may fail if there is another currently-active
58     /// section builder for the same advertisement builder, if the
59     /// kind of section being added does not match the advertisement
60     /// type (public/encrypted), or if the section would not manage
61     /// to fit within the enclosing advertisement.
encrypted_section_builder( &self, broadcast_cred: V1BroadcastCredential, verification_mode: V1VerificationMode, ) -> CreateV1SectionBuilderResult62     pub fn encrypted_section_builder(
63         &self,
64         broadcast_cred: V1BroadcastCredential,
65         verification_mode: V1VerificationMode,
66     ) -> CreateV1SectionBuilderResult {
67         self.section_builder_internals(move |internals| {
68             internals.encrypted_section_builder(broadcast_cred, verification_mode)
69         })
70     }
71 
72     /// Attempts to serialize the contents of the advertisement builder behind this handle to
73     /// bytes. Assuming that the handle is valid, this operation will always take ownership of the
74     /// handle and result in the contents behind the advertisement builder handle being
75     /// deallocated.
into_advertisement(self) -> SerializeV1AdvertisementResult76     pub fn into_advertisement(self) -> SerializeV1AdvertisementResult {
77         match self.handle.deallocate() {
78             Ok(adv_builder) => adv_builder.into_advertisement(),
79             Err(_) => SerializeV1AdvertisementResult::InvalidAdvertisementBuilderHandle,
80         }
81     }
82 
section_builder_internals( &self, builder_supplier: impl FnOnce( &mut V1AdvertisementBuilderInternals, ) -> Result<usize, SectionBuilderError>, ) -> CreateV1SectionBuilderResult83     fn section_builder_internals(
84         &self,
85         builder_supplier: impl FnOnce(
86             &mut V1AdvertisementBuilderInternals,
87         ) -> Result<usize, SectionBuilderError>,
88     ) -> CreateV1SectionBuilderResult {
89         match self.handle.get_mut() {
90             Ok(mut adv_builder_write_guard) => {
91                 match builder_supplier(&mut adv_builder_write_guard) {
92                     Ok(section_index) => CreateV1SectionBuilderResult::Success(V1SectionBuilder {
93                         adv_builder: *self,
94                         section_index: section_index as u8,
95                     }),
96                     Err(e) => e.into(),
97                 }
98             }
99             Err(_) => CreateV1SectionBuilderResult::InvalidAdvertisementBuilderHandle,
100         }
101     }
102 }
103 
104 /// Discriminant for `CreateV1AdvertisementBuilderResult`
105 #[derive(Copy, Clone)]
106 #[repr(u8)]
107 pub enum CreateV1AdvertisementBuilderResultKind {
108     /// The attempt to create a new advertisement builder
109     /// failed since there are no more available
110     /// slots for V1 advertisement builders in their handle-map.
111     NoSpaceLeft = 0,
112     /// The attempt succeeded. The wrapped advertisement builder
113     /// may be obtained via
114     /// `CreateV1AdvertisementBuilderResult#into_success`.
115     Success = 1,
116 }
117 
118 /// The result of attempting to create a new V1 advertisement builder.
119 #[repr(C)]
120 #[allow(missing_docs)]
121 pub enum CreateV1AdvertisementBuilderResult {
122     NoSpaceLeft,
123     Success(V1AdvertisementBuilder),
124 }
125 
126 impl From<Result<V1AdvertisementBuilder, HandleMapFullError>>
127     for CreateV1AdvertisementBuilderResult
128 {
from(result: Result<V1AdvertisementBuilder, HandleMapFullError>) -> Self129     fn from(result: Result<V1AdvertisementBuilder, HandleMapFullError>) -> Self {
130         match result {
131             Ok(builder) => CreateV1AdvertisementBuilderResult::Success(builder),
132             Err(_) => CreateV1AdvertisementBuilderResult::NoSpaceLeft,
133         }
134     }
135 }
136 
137 impl FfiEnum for CreateV1AdvertisementBuilderResult {
138     type Kind = CreateV1AdvertisementBuilderResultKind;
kind(&self) -> Self::Kind139     fn kind(&self) -> Self::Kind {
140         match self {
141             CreateV1AdvertisementBuilderResult::NoSpaceLeft => {
142                 CreateV1AdvertisementBuilderResultKind::NoSpaceLeft
143             }
144             CreateV1AdvertisementBuilderResult::Success(_) => {
145                 CreateV1AdvertisementBuilderResultKind::Success
146             }
147         }
148     }
149 }
150 
151 impl CreateV1AdvertisementBuilderResult {
152     declare_enum_cast! {into_success, Success, V1AdvertisementBuilder }
153 }
154 
155 /// Creates a new V1 advertisement builder for the given advertisement
156 /// kind (public/encrypted).
create_v1_advertisement_builder( kind: AdvertisementBuilderKind, ) -> CreateV1AdvertisementBuilderResult157 pub fn create_v1_advertisement_builder(
158     kind: AdvertisementBuilderKind,
159 ) -> CreateV1AdvertisementBuilderResult {
160     V1AdvertisementBuilderHandle::allocate(move || V1AdvertisementBuilderInternals::new(kind))
161         .map(|handle| V1AdvertisementBuilder { kind, handle })
162         .into()
163 }
164 
165 pub(crate) enum V1AdvertisementBuilderState {
166     /// Internal state for when we have an active advertisement
167     /// builder, but no currently-active section builder.
168     Advertisement(np_adv_dynamic::extended::BoxedAdvBuilder),
169     /// Internal state for when we have both an active advertisement
170     /// builder and an active section builder.
171     Section(np_adv_dynamic::extended::BoxedSectionBuilder<np_adv::extended::serialize::AdvBuilder>),
172 }
173 
174 /// Internal version of errors which may be raised when
175 /// attempting to derive a new section builder from an
176 /// advertisement builder.
177 #[derive(Debug, Eq, PartialEq)]
178 pub(crate) enum SectionBuilderError {
179     /// We're currently in the middle of building a section.
180     UnclosedActiveSection,
181     /// We're attempting to build a section with an identity
182     /// kind (public/encrypted) which doesn't match the kind
183     /// for the entire advertisement.
184     IdentityKindMismatch,
185     /// There isn't enough space for a new section, either
186     /// because the maximum section count has been exceeded
187     /// or because the advertisement is almost full, and
188     /// the minimum size of a section wouldn't fit.
189     NoSpaceLeft,
190 }
191 
192 impl From<np_adv_dynamic::extended::BoxedAddSectionError> for SectionBuilderError {
from(err: np_adv_dynamic::extended::BoxedAddSectionError) -> Self193     fn from(err: np_adv_dynamic::extended::BoxedAddSectionError) -> Self {
194         use np_adv::extended::serialize::AddSectionError;
195         use np_adv_dynamic::extended::BoxedAddSectionError;
196         match err {
197             BoxedAddSectionError::IdentityRequiresSaltError
198             | BoxedAddSectionError::Underlying(AddSectionError::IncompatibleSectionType) => {
199                 SectionBuilderError::IdentityKindMismatch
200             }
201             BoxedAddSectionError::Underlying(AddSectionError::InsufficientAdvSpace)
202             | BoxedAddSectionError::Underlying(AddSectionError::MaxSectionCountExceeded) => {
203                 SectionBuilderError::NoSpaceLeft
204             }
205         }
206     }
207 }
208 
209 /// Discriminant for `SerializeV1AdvertisementResult`.
210 #[repr(u8)]
211 pub enum SerializeV1AdvertisementResultKind {
212     /// Serializing the advertisement to bytes was successful.
213     Success = 0,
214     /// The state of the advertisement builder was invalid
215     /// for the builder to be closed for serialization, likely
216     /// because there was an unclosed section builder.
217     InvalidBuilderState = 1,
218     /// The advertisement builder handle was invalid.
219     InvalidAdvertisementBuilderHandle = 2,
220 }
221 
222 /// The result of attempting to serialize the contents
223 /// of a V1 advertisement builder to raw bytes.
224 #[repr(C)]
225 #[allow(missing_docs, clippy::large_enum_variant)]
226 pub enum SerializeV1AdvertisementResult {
227     Success(ByteBuffer<250>),
228     InvalidBuilderState,
229     InvalidAdvertisementBuilderHandle,
230 }
231 
232 impl SerializeV1AdvertisementResult {
233     declare_enum_cast! { into_success, Success, ByteBuffer<250> }
234 }
235 
236 impl FfiEnum for SerializeV1AdvertisementResult {
237     type Kind = SerializeV1AdvertisementResultKind;
kind(&self) -> SerializeV1AdvertisementResultKind238     fn kind(&self) -> SerializeV1AdvertisementResultKind {
239         match self {
240             Self::Success(_) => SerializeV1AdvertisementResultKind::Success,
241             Self::InvalidBuilderState => SerializeV1AdvertisementResultKind::InvalidBuilderState,
242             Self::InvalidAdvertisementBuilderHandle => {
243                 SerializeV1AdvertisementResultKind::InvalidAdvertisementBuilderHandle
244             }
245         }
246     }
247 }
248 
249 /// Internal, Rust-side implementation of a V1 advertisement builder.
250 pub struct V1AdvertisementBuilderInternals {
251     // Note: This is actually always populated from an external
252     // perspective in the absence of panics. We only need
253     // the `Option` in order to be able to take ownership
254     // and perform a state transition when needed.
255     state: Option<V1AdvertisementBuilderState>,
256 }
257 
258 impl V1AdvertisementBuilderInternals {
new(kind: AdvertisementBuilderKind) -> Self259     pub(crate) fn new(kind: AdvertisementBuilderKind) -> Self {
260         let adv_type = kind.as_internal_v1();
261         let builder = np_adv::extended::serialize::AdvBuilder::new(adv_type);
262         let builder = builder.into();
263         let state = Some(V1AdvertisementBuilderState::Advertisement(builder));
264         Self { state }
265     }
266     /// Internals of section_builder-type routines. Upon success, yields the index
267     /// of the newly-added section builder.
section_builder_internal( &mut self, identity: np_adv_dynamic::extended::BoxedEncoder, ) -> Result<usize, SectionBuilderError>268     pub(crate) fn section_builder_internal(
269         &mut self,
270         identity: np_adv_dynamic::extended::BoxedEncoder,
271     ) -> Result<usize, SectionBuilderError> {
272         let state = self.state.take();
273         match state {
274             Some(V1AdvertisementBuilderState::Advertisement(adv_builder)) => {
275                 match adv_builder.into_section_builder(identity) {
276                     Ok(section_builder) => {
277                         let section_index = section_builder.section_index();
278                         self.state = Some(V1AdvertisementBuilderState::Section(section_builder));
279                         Ok(section_index)
280                     }
281                     Err((adv_builder, err)) => {
282                         self.state = Some(V1AdvertisementBuilderState::Advertisement(adv_builder));
283                         Err(err.into())
284                     }
285                 }
286             }
287             x => {
288                 // Note: Technically, this case also would leave the `None` state
289                 // if we ever entered into it, but we never transition to that
290                 // state during normal operation.
291                 self.state = x;
292                 Err(SectionBuilderError::UnclosedActiveSection)
293             }
294         }
295     }
296 
public_section_builder(&mut self) -> Result<usize, SectionBuilderError>297     pub(crate) fn public_section_builder(&mut self) -> Result<usize, SectionBuilderError> {
298         let identity = np_adv_dynamic::extended::BoxedEncoder::Unencrypted;
299         self.section_builder_internal(identity)
300     }
encrypted_section_builder( &mut self, broadcast_cred: V1BroadcastCredential, verification_mode: V1VerificationMode, ) -> Result<usize, SectionBuilderError>301     pub(crate) fn encrypted_section_builder(
302         &mut self,
303         broadcast_cred: V1BroadcastCredential,
304         verification_mode: V1VerificationMode,
305     ) -> Result<usize, SectionBuilderError> {
306         let mut rng = get_global_crypto_rng();
307         let rng = rng.get_rng();
308         let internal_broadcast_cred = broadcast_cred.into_internal();
309         let encoder = match verification_mode {
310             V1VerificationMode::Mic => {
311                 let encoder =
312                     np_adv::extended::serialize::MicEncryptedSectionEncoder::<_>::new_wrapped_salt::<
313                         CryptoProviderImpl,
314                     >(rng, &internal_broadcast_cred);
315                 np_adv_dynamic::extended::BoxedEncoder::MicEncrypted(encoder)
316             }
317             V1VerificationMode::Signature => {
318                 let encoder =
319                     np_adv::extended::serialize::SignedEncryptedSectionEncoder::new_random_salt::<
320                         CryptoProviderImpl,
321                     >(rng, &internal_broadcast_cred);
322                 np_adv_dynamic::extended::BoxedEncoder::SignedEncrypted(encoder)
323             }
324         };
325         self.section_builder_internal(encoder)
326     }
into_advertisement(self) -> SerializeV1AdvertisementResult327     fn into_advertisement(self) -> SerializeV1AdvertisementResult {
328         match self.state {
329             Some(V1AdvertisementBuilderState::Advertisement(adv_builder)) => {
330                 let array_view = adv_builder.into_advertisement().into_array_view();
331                 SerializeV1AdvertisementResult::Success(ByteBuffer::from_array_view(array_view))
332             }
333             _ => SerializeV1AdvertisementResult::InvalidBuilderState,
334         }
335     }
336 }
337 
338 /// A `#[repr(C)]` handle to a value of type `V1AdvertisementBuilderInternals`
339 #[repr(C)]
340 #[derive(Clone, Copy, PartialEq, Eq)]
341 struct V1AdvertisementBuilderHandle {
342     handle_id: u64,
343 }
344 
345 declare_handle_map!(
346     advertisement_builder,
347     crate::common::default_handle_map_dimensions(),
348     super::V1AdvertisementBuilderHandle,
349     super::V1AdvertisementBuilderInternals
350 );
351 
352 /// Discriminant for `CreateV1SectionBuilderResult`
353 #[derive(Copy, Clone)]
354 #[repr(u8)]
355 pub enum CreateV1SectionBuilderResultKind {
356     /// The attempt to create a new section builder succeeded.
357     Success = 0,
358     /// We're currently in the middle of building a section.
359     UnclosedActiveSection = 1,
360     /// The advertisement builder handle was invalid.
361     InvalidAdvertisementBuilderHandle = 2,
362     /// We're attempting to build a section with an identity
363     /// kind (public/encrypted) which doesn't match the kind
364     /// for the entire advertisement.
365     IdentityKindMismatch = 3,
366     /// There isn't enough space for a new section, either
367     /// because the maximum section count has been exceeded
368     /// or because the advertisement is almost full, and
369     /// the minimum size of a section wouldn't fit.
370     NoSpaceLeft = 4,
371 }
372 
373 /// The result of attempting to create a new V1 section builder.
374 #[repr(C)]
375 #[allow(missing_docs)]
376 pub enum CreateV1SectionBuilderResult {
377     Success(V1SectionBuilder),
378     UnclosedActiveSection,
379     InvalidAdvertisementBuilderHandle,
380     IdentityKindMismatch,
381     NoSpaceLeft,
382 }
383 
384 impl FfiEnum for CreateV1SectionBuilderResult {
385     type Kind = CreateV1SectionBuilderResultKind;
kind(&self) -> Self::Kind386     fn kind(&self) -> Self::Kind {
387         match self {
388             Self::Success(_) => CreateV1SectionBuilderResultKind::Success,
389             Self::UnclosedActiveSection => CreateV1SectionBuilderResultKind::UnclosedActiveSection,
390             Self::InvalidAdvertisementBuilderHandle => {
391                 CreateV1SectionBuilderResultKind::InvalidAdvertisementBuilderHandle
392             }
393             Self::IdentityKindMismatch => CreateV1SectionBuilderResultKind::IdentityKindMismatch,
394             Self::NoSpaceLeft => CreateV1SectionBuilderResultKind::NoSpaceLeft,
395         }
396     }
397 }
398 
399 impl CreateV1SectionBuilderResult {
400     declare_enum_cast! {into_success, Success, V1SectionBuilder}
401 }
402 
403 impl From<SectionBuilderError> for CreateV1SectionBuilderResult {
from(err: SectionBuilderError) -> Self404     fn from(err: SectionBuilderError) -> Self {
405         match err {
406             SectionBuilderError::UnclosedActiveSection => Self::UnclosedActiveSection,
407             SectionBuilderError::IdentityKindMismatch => Self::IdentityKindMismatch,
408             SectionBuilderError::NoSpaceLeft => Self::NoSpaceLeft,
409         }
410     }
411 }
412 
413 /// Result code for [`V1SectionBuilder#add_to_advertisement`].
414 #[derive(Clone, Copy)]
415 #[repr(u8)]
416 pub enum AddV1SectionToAdvertisementResult {
417     /// The section referenced by the given handle
418     /// couldn't be added to the containing advertisement,
419     /// possibly because the handle is invalid or the section
420     /// has already been added to the containing section.
421     Error = 0,
422     /// The section referenced by the given handle
423     /// was successfully added to the containing advertisement.
424     /// After obtaining this result code, the section
425     /// handle will no longer be valid.
426     Success = 1,
427 }
428 
429 /// Result code for operations adding DEs to a section builder.
430 #[derive(Clone, Copy)]
431 #[repr(u8)]
432 pub enum AddV1DEResult {
433     /// The DE was successfully added to the section builder
434     /// behind the given handle.
435     Success = 0,
436     /// The handle for the section builder was invalid.
437     InvalidSectionHandle = 1,
438     /// There was no more space left in the advertisement
439     /// to fit the DE in the containing section.
440     InsufficientSectionSpace = 2,
441     /// The data element itself had invalid characteristics,
442     /// most likely a length above 127.
443     InvalidDataElement = 3,
444 }
445 
446 /// Discriminant for `NextV1DE16ByteSaltResult`.
447 #[derive(Clone, Copy)]
448 #[repr(u8)]
449 pub enum NextV1DE16ByteSaltResultKind {
450     /// We couldn't return a 16-byte DE salt, possibly
451     /// because the handle to the section builder
452     /// was invalid, or possibly because the section
453     /// builder was for a public section.
454     Error = 0,
455     /// A 16-byte DE salt was returned successfully.
456     Success = 1,
457 }
458 
459 /// The result of attempting to get the derived V1 DE
460 /// 16-byte salt for the next-added DE to the section
461 /// builder behind the given handle.
462 #[derive(Clone)]
463 #[repr(C)]
464 #[allow(missing_docs)]
465 pub enum NextV1DE16ByteSaltResult {
466     Error,
467     Success(FixedSizeArray<16>),
468 }
469 
470 impl FfiEnum for NextV1DE16ByteSaltResult {
471     type Kind = NextV1DE16ByteSaltResultKind;
kind(&self) -> Self::Kind472     fn kind(&self) -> Self::Kind {
473         match self {
474             Self::Error => NextV1DE16ByteSaltResultKind::Error,
475             Self::Success(_) => NextV1DE16ByteSaltResultKind::Success,
476         }
477     }
478 }
479 
480 impl NextV1DE16ByteSaltResult {
new_from_de_salt(salt: Option<np_adv::extended::serialize::DeSalt>) -> Self481     fn new_from_de_salt(salt: Option<np_adv::extended::serialize::DeSalt>) -> Self {
482         match salt.and_then(|salt| salt.derive::<16, CryptoProviderImpl>()) {
483             Some(salt) => NextV1DE16ByteSaltResult::Success(FixedSizeArray::from_array(salt)),
484             None => NextV1DE16ByteSaltResult::Error,
485         }
486     }
487 
488     declare_enum_cast! {into_success, Success, FixedSizeArray<16> }
489 }
490 
491 /// A handle to a builder for V1 sections. This is not a unique handle; it is the same handle as
492 /// the advertisement builder the section builder was originated from.
493 #[derive(Clone, Copy)]
494 #[repr(C)]
495 pub struct V1SectionBuilder {
496     adv_builder: V1AdvertisementBuilder,
497     section_index: u8,
498 }
499 
500 impl V1SectionBuilder {
501     /// Attempts to add the section constructed behind this handle
502     /// to a section builder to the containing advertisement it
503     /// originated from.
add_to_advertisement(self) -> AddV1SectionToAdvertisementResult504     pub fn add_to_advertisement(self) -> AddV1SectionToAdvertisementResult {
505         match self.adv_builder.handle.get_mut() {
506             Ok(mut adv_builder) => {
507                 let state = adv_builder.state.take();
508                 match state {
509                     Some(V1AdvertisementBuilderState::Section(section_builder)) => {
510                         // Make sure the index of the section we're trying to close
511                         // matches the index of the section currently under construction.
512                         let actual_section_index = section_builder.section_index() as u8;
513                         if self.section_index == actual_section_index {
514                             let updated_adv_builder =
515                                 section_builder.add_to_advertisement::<CryptoProviderImpl>();
516                             adv_builder.state = Some(V1AdvertisementBuilderState::Advertisement(
517                                 updated_adv_builder,
518                             ));
519                             AddV1SectionToAdvertisementResult::Success
520                         } else {
521                             adv_builder.state =
522                                 Some(V1AdvertisementBuilderState::Section(section_builder));
523                             AddV1SectionToAdvertisementResult::Error
524                         }
525                     }
526                     x => {
527                         adv_builder.state = x;
528                         AddV1SectionToAdvertisementResult::Error
529                     }
530                 }
531             }
532             Err(_) => AddV1SectionToAdvertisementResult::Error,
533         }
534     }
535 
536     /// Attempts to get the derived 16-byte V1 DE salt for the next
537     /// DE to be added to this section builder. May fail if this
538     /// section builder handle is invalid, or if the section
539     /// is a public section.
next_de_salt(&self) -> NextV1DE16ByteSaltResult540     pub fn next_de_salt(&self) -> NextV1DE16ByteSaltResult {
541         self.try_apply_to_internals(
542             |section_builder| {
543                 NextV1DE16ByteSaltResult::new_from_de_salt(section_builder.next_de_salt())
544             },
545             NextV1DE16ByteSaltResult::Error,
546         )
547     }
548 
549     /// Attempts to add the given DE to the section builder behind
550     /// this handle. The passed DE may have a payload of up to 127
551     /// bytes, the maximum for a V1 DE.
add_127_byte_buffer_de(&self, de: V1DE127ByteBuffer) -> AddV1DEResult552     pub fn add_127_byte_buffer_de(&self, de: V1DE127ByteBuffer) -> AddV1DEResult {
553         match de.into_internal() {
554             Some(generic_de) => self
555                 .add_de_internals(np_adv_dynamic::extended::BoxedWriteDataElement::new(generic_de)),
556             None => AddV1DEResult::InvalidDataElement,
557         }
558     }
559 
add_de_internals( &self, de: np_adv_dynamic::extended::BoxedWriteDataElement, ) -> AddV1DEResult560     fn add_de_internals(
561         &self,
562         de: np_adv_dynamic::extended::BoxedWriteDataElement,
563     ) -> AddV1DEResult {
564         self.try_apply_to_internals(
565             move |section_builder| match section_builder.add_de(move |_| de) {
566                 Ok(_) => AddV1DEResult::Success,
567                 Err(_) => AddV1DEResult::InsufficientSectionSpace,
568             },
569             AddV1DEResult::InvalidSectionHandle,
570         )
571     }
572 
try_apply_to_internals<R>( &self, func: impl FnOnce( &mut np_adv_dynamic::extended::BoxedSectionBuilder< np_adv::extended::serialize::AdvBuilder, >, ) -> R, invalid_handle_error: R, ) -> R573     fn try_apply_to_internals<R>(
574         &self,
575         func: impl FnOnce(
576             &mut np_adv_dynamic::extended::BoxedSectionBuilder<
577                 np_adv::extended::serialize::AdvBuilder,
578             >,
579         ) -> R,
580         invalid_handle_error: R,
581     ) -> R {
582         match self.adv_builder.handle.get_mut() {
583             Ok(mut adv_builder) => {
584                 match adv_builder.state.as_mut() {
585                     Some(V1AdvertisementBuilderState::Section(ref mut section_builder)) => {
586                         // Check to make sure that the section index matches, otherwise
587                         // we have an invalid handle.
588                         let current_section_index = section_builder.section_index() as u8;
589                         if current_section_index == self.section_index {
590                             func(section_builder)
591                         } else {
592                             invalid_handle_error
593                         }
594                     }
595                     Some(V1AdvertisementBuilderState::Advertisement(_)) => invalid_handle_error,
596                     None => invalid_handle_error,
597                 }
598             }
599             Err(_) => invalid_handle_error,
600         }
601     }
602 }
603 
604 /// Represents the contents of a V1 DE whose payload
605 /// is stored in a buffer which may contain up to 127 bytes,
606 /// which is the maximum for any V1 DE.
607 ///
608 /// This representation is stable, and so you may directly
609 /// reference this struct's fields if you wish.
610 #[repr(C)]
611 //TODO: Partial unification with `deserialize::v1::GenericV1DataElement`?
612 pub struct V1DE127ByteBuffer {
613     /// The DE type code of this generic data-element.
614     pub de_type: u32,
615     /// The raw data-element byte payload, up to
616     /// 127 bytes in length.
617     pub payload: ByteBuffer<127>,
618 }
619 
620 impl V1DE127ByteBuffer {
621     /// Attempts to convert this FFI-friendly DE with a byte-buffer size of 127
622     /// to the internal representation of a generic DE. May fail in the case
623     /// where the underlying payload byte-buffer has an invalid length above 127.
into_internal(self) -> Option<np_adv::extended::data_elements::GenericDataElement>624     fn into_internal(self) -> Option<np_adv::extended::data_elements::GenericDataElement> {
625         let de_type = np_adv::extended::de_type::DeType::from(self.de_type);
626         self.payload.as_slice().and_then(move |payload_slice| {
627             np_adv::extended::data_elements::GenericDataElement::try_from(de_type, payload_slice)
628                 .ok()
629         })
630     }
631 }
632 
633 #[cfg(test)]
634 mod tests {
635     use super::*;
636 
state_is_advertisement_building(adv_builder_state: &V1AdvertisementBuilderState) -> bool637     fn state_is_advertisement_building(adv_builder_state: &V1AdvertisementBuilderState) -> bool {
638         matches!(adv_builder_state, V1AdvertisementBuilderState::Advertisement(_))
639     }
640 
641     #[allow(clippy::expect_used)]
642     #[test]
test_build_section_fails_with_outstanding_section()643     fn test_build_section_fails_with_outstanding_section() {
644         let mut adv_builder =
645             V1AdvertisementBuilderInternals::new(AdvertisementBuilderKind::Encrypted);
646 
647         let adv_builder_state =
648             adv_builder.state.as_ref().expect("Adv builder state should be present.");
649         assert!(state_is_advertisement_building(adv_builder_state));
650         let section_index = adv_builder
651             .encrypted_section_builder(empty_broadcast_cred(), V1VerificationMode::Mic)
652             .expect("Should be able to build the first section.");
653         assert_eq!(section_index, 0);
654 
655         assert!(adv_builder.state.is_some());
656         let adv_builder_state =
657             adv_builder.state.as_ref().expect("Adv builder state should be present.");
658         assert!(!state_is_advertisement_building(adv_builder_state));
659 
660         let double_build_error = adv_builder
661             .encrypted_section_builder(empty_broadcast_cred(), V1VerificationMode::Mic)
662             .expect_err("Shouldn't be able to start a new section builder with an unclosed one.");
663         assert_eq!(double_build_error, SectionBuilderError::UnclosedActiveSection);
664     }
665 
empty_broadcast_cred() -> V1BroadcastCredential666     fn empty_broadcast_cred() -> V1BroadcastCredential {
667         V1BroadcastCredential::new([0; 32], [0; 16].into(), [0; 32])
668     }
669 }
670