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