1 // Copyright 2022 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Serialization support for V1 advertisements.
16 //!
17 //! # Examples
18 //!
19 //! Serialize some DEs without an adv salt:
20 //!
21 //! ```
22 //! use crypto_provider_default::CryptoProviderImpl;
23 //! use np_adv::{
24 //!     extended::{data_elements::*, serialize::*, de_type::DeType, V1_ENCODING_UNENCRYPTED},
25 //!     shared_data::TxPower
26 //! };
27 //!
28 //! // no section identities or DEs need salt in this example
29 //! let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
30 //! let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
31 //!
32 //! section_builder.add_de(|_salt| TxPowerDataElement::from(TxPower::try_from(3).unwrap())).unwrap();
33 //!
34 //! // add some other DE with type = 1000
35 //! section_builder.add_de_res(|_salt|
36 //!     GenericDataElement::try_from( DeType::from(1000_u32), &[10, 11, 12, 13])
37 //! ).unwrap();
38 //!
39 //! section_builder.add_to_advertisement::<CryptoProviderImpl>();
40 //!
41 //! assert_eq!(
42 //!     &[
43 //!         0x20, // version header
44 //!         V1_ENCODING_UNENCRYPTED, //section format
45 //!         0x09, // section length
46 //!         0x15, 3, // tx power
47 //!         0x84, 0x87, 0x68, 10, 11, 12, 13, // other DE
48 //!     ],
49 //!     adv_builder.into_advertisement().as_slice()
50 //! );
51 //! ```
52 //!
53 //! Serialize some DEs in an adv with an encrypted section:
54 //!
55 //! ```
56 //! use np_adv::{
57 //!     credential::{ v1::{V1, V1BroadcastCredential}},
58 //!     extended::{data_elements::*, serialize::*, de_type::DeType, V1IdentityToken },
59 //! };
60 //! use rand::{Rng as _, SeedableRng as _};
61 //! use crypto_provider::{CryptoProvider, CryptoRng, ed25519};
62 //! use crypto_provider_default::CryptoProviderImpl;
63 //! use np_adv::shared_data::TxPower;
64 //!
65 //! let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
66 //!
67 //! // these would come from the credential
68 //!
69 //! let mut rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
70 //! let identity_token = rng.gen();
71 //! let key_seed: [u8; 32] = rng.gen();
72 //! // use your preferred crypto impl
73 //! let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
74 //!
75 //! let broadcast_cm = V1BroadcastCredential::new(
76 //!     key_seed,
77 //!     identity_token,
78 //!     ed25519::PrivateKey::generate::<<CryptoProviderImpl as CryptoProvider>::Ed25519>(),
79 //! );
80 //!
81 //! let mut section_builder = adv_builder.section_builder(MicEncryptedSectionEncoder::<_>::new_random_salt::<CryptoProviderImpl>(
82 //!     &mut rng,
83 //!     &broadcast_cm,
84 //! )).unwrap();
85 //!
86 //! section_builder.add_de(|_salt| TxPowerDataElement::from(TxPower::try_from(3).unwrap())).unwrap();
87 //!
88 //! // add some other DE with type = 1000
89 //! section_builder.add_de_res(|salt|
90 //!     GenericDataElement::try_from(
91 //!         DeType::from(1000_u32),
92 //!         &do_fancy_crypto(salt.derive::<16, CryptoProviderImpl>().expect("16 is a valid HKDF length")))
93 //! ).unwrap();
94 //!
95 //! section_builder.add_to_advertisement::<CryptoProviderImpl>();
96 //!
97 //! // can't assert much about this since most of it is random
98 //! assert_eq!(
99 //!     0x20, // adv header
100 //!     adv_builder.into_advertisement().as_slice()[0]
101 //! );
102 //!
103 //! // A hypothetical function that uses the per-DE derived salt to do something like encrypt or
104 //! // otherwise scramble data
105 //! fn do_fancy_crypto(derived_salt: [u8; 16]) -> [u8; 16] {
106 //!     // flipping bits is just a nonsense example, do something real here
107 //!     derived_salt.iter().map(|b| !b)
108 //!         .collect::<Vec<_>>()
109 //!         .try_into().expect("array sizes match")
110 //! }
111 //! ```
112 use core::fmt::{self, Display};
113 
114 use array_view::ArrayView;
115 use crypto_provider::CryptoProvider;
116 use np_hkdf::v1_salt::{DataElementOffset, ExtendedV1Salt};
117 use sink::Sink;
118 
119 use crate::extended::{
120     de_requires_extended_bit, de_type::DeType, serialize::section::EncodedSection, to_array_view,
121     DeLength, BLE_5_ADV_SVC_MAX_CONTENT_LEN, NP_ADV_MAX_SECTION_LEN,
122     NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT, NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT,
123 };
124 
125 mod section;
126 
127 use crate::header::VERSION_HEADER_V1;
128 pub use section::{
129     encoder::{
130         MicEncryptedSectionEncoder, SectionEncoder, SignedEncryptedSectionEncoder,
131         UnencryptedSectionEncoder,
132     },
133     AddDataElementError, SectionBuilder,
134 };
135 
136 #[cfg(test)]
137 use crate::header::V1AdvHeader;
138 
139 #[cfg(test)]
140 pub(crate) mod adv_tests;
141 #[cfg(test)]
142 mod de_header_tests;
143 #[cfg(test)]
144 pub(crate) mod section_tests;
145 #[cfg(test)]
146 mod test_vectors;
147 
148 /// Builder for V1 advertisements.
149 #[derive(Debug)]
150 pub struct AdvBuilder {
151     // TODO make this configurable, and test making sections whose length is not restricted by BLE limitations
152     /// Contains the adv header byte
153     adv: tinyvec::ArrayVec<[u8; BLE_5_ADV_SVC_MAX_CONTENT_LEN]>,
154     /// To track the number of sections that are in the advertisement
155     section_count: usize,
156     /// Advertisement type: Public or Encrypted
157     advertisement_type: AdvertisementType,
158 }
159 
160 impl AsMut<AdvBuilder> for AdvBuilder {
as_mut(&mut self) -> &mut AdvBuilder161     fn as_mut(&mut self) -> &mut AdvBuilder {
162         self
163     }
164 }
165 
166 impl AdvBuilder {
167     /// Build an [AdvBuilder].
new(advertisement_type: AdvertisementType) -> Self168     pub fn new(advertisement_type: AdvertisementType) -> Self {
169         let mut adv = tinyvec::ArrayVec::new();
170         adv.push(VERSION_HEADER_V1);
171         Self { adv, section_count: 0, advertisement_type }
172     }
173 
174     /// Create a section builder whose contents may be added to this advertisement.
175     ///
176     /// The builder will not accept more DEs than can fit given the space already used in the
177     /// advertisement by previous sections, if any.
178     ///
179     /// Once the builder is populated, add it to the originating advertisement with
180     /// [SectionBuilder.add_to_advertisement].
section_builder<SE: SectionEncoder>( &mut self, section_encoder: SE, ) -> Result<SectionBuilder<&mut AdvBuilder, SE>, AddSectionError>181     pub fn section_builder<SE: SectionEncoder>(
182         &mut self,
183         section_encoder: SE,
184     ) -> Result<SectionBuilder<&mut AdvBuilder, SE>, AddSectionError> {
185         let (header_len, contents) = self.prepare_section_builder_buffer(&section_encoder)?;
186         Ok(SectionBuilder::new(header_len, contents, section_encoder, self))
187     }
188 
189     /// Create a section builder which actually takes ownership of this advertisement builder.
190     ///
191     /// This is unlike `AdvertisementBuilder#section_builder` in that the returned section
192     /// builder will take ownership of this advertisement builder, if the operation was
193     /// successful. Otherwise, this advertisement builder will be returned back to the
194     /// caller unaltered as part of the `Err` arm.
195     #[allow(clippy::result_large_err)]
into_section_builder<SE: SectionEncoder>( self, section_encoder: SE, ) -> Result<SectionBuilder<AdvBuilder, SE>, (AdvBuilder, AddSectionError)>196     pub fn into_section_builder<SE: SectionEncoder>(
197         self,
198         section_encoder: SE,
199     ) -> Result<SectionBuilder<AdvBuilder, SE>, (AdvBuilder, AddSectionError)> {
200         match self.prepare_section_builder_buffer::<SE>(&section_encoder) {
201             Ok((header_len, section)) => {
202                 Ok(SectionBuilder::new(header_len, section, section_encoder, self))
203             }
204             Err(err) => Err((self, err)),
205         }
206     }
207 
208     /// Convert the builder into an encoded advertisement.
into_advertisement(self) -> EncodedAdvertisement209     pub fn into_advertisement(self) -> EncodedAdvertisement {
210         EncodedAdvertisement { adv: to_array_view(self.adv) }
211     }
212 
213     /// Gets the current number of sections added to this advertisement
214     /// builder, not counting any outstanding SectionBuilders.
section_count(&self) -> usize215     pub fn section_count(&self) -> usize {
216         self.section_count
217     }
218 
219     /// Returns the length of the header (excluding the leading length byte),
220     /// and a buffer already populated with a placeholder section length byte and the rest
221     /// of the header.
prepare_section_builder_buffer<SE: SectionEncoder>( &self, section_encoder: &SE, ) -> Result<(usize, CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>), AddSectionError>222     fn prepare_section_builder_buffer<SE: SectionEncoder>(
223         &self,
224         section_encoder: &SE,
225     ) -> Result<(usize, CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>), AddSectionError> {
226         if self.section_count >= self.advertisement_type.max_sections() {
227             return Err(AddSectionError::MaxSectionCountExceeded);
228         }
229         if self.advertisement_type != SE::ADVERTISEMENT_TYPE {
230             return Err(AddSectionError::IncompatibleSectionType);
231         }
232 
233         // The header contains all the header bytes except for the final length byte.
234         let header = section_encoder.header();
235         let header_slice = header.as_slice();
236 
237         // the max overall len available to the section
238         let available_len = self.adv.capacity() - self.adv.len();
239 
240         let mut prefix = available_len
241             .checked_sub(SE::SUFFIX_LEN)
242             .and_then(CapacityLimitedVec::new)
243             .ok_or(AddSectionError::InsufficientAdvSpace)?;
244         prefix.try_extend_from_slice(header_slice).ok_or(AddSectionError::InsufficientAdvSpace)?;
245         // Placeholder for section length, which we do not know yet
246         prefix.try_push(0).ok_or(AddSectionError::InsufficientAdvSpace)?;
247         Ok((header_slice.len(), prefix))
248     }
249 
250     /// Add the section, which must have come from a SectionBuilder generated from this, into this
251     /// advertisement.
add_section(&mut self, section: EncodedSection)252     fn add_section(&mut self, section: EncodedSection) {
253         self.adv
254             .try_extend_from_slice(section.as_slice())
255             .expect("section capacity enforced in the section builder");
256         self.section_count += 1;
257     }
258 
259     #[cfg(test)]
adv_header(&self) -> V1AdvHeader260     fn adv_header(&self) -> V1AdvHeader {
261         V1AdvHeader::new(self.adv[0])
262     }
263 }
264 
265 /// Errors that can occur when adding a section to an advertisement
266 #[derive(Debug, PartialEq, Eq)]
267 pub enum AddSectionError {
268     /// The advertisement doesn't have enough space to hold the minimum size of the section
269     InsufficientAdvSpace,
270     /// The advertisement can only hold a maximum of NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT number of sections
271     MaxSectionCountExceeded,
272     /// An incompatible section trying to be added
273     IncompatibleSectionType,
274 }
275 
276 impl Display for AddSectionError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result277     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
278         match self {
279             AddSectionError::InsufficientAdvSpace => {
280                 write!(f, "The advertisement (max {BLE_5_ADV_SVC_MAX_CONTENT_LEN} bytes) doesn't have enough remaining space to hold the section")
281             }
282             AddSectionError::MaxSectionCountExceeded => {
283                 write!(f, "The advertisement can only hold a maximum of {NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT} number of sections")
284             }
285             AddSectionError::IncompatibleSectionType => {
286                 write!(f, "Public and Encrypted sections cannot be mixed in the same advertisement")
287             }
288         }
289     }
290 }
291 
292 /// An encoded NP V1 advertisement, starting with the NP advertisement header byte.
293 #[derive(Debug, PartialEq, Eq)]
294 pub struct EncodedAdvertisement {
295     adv: ArrayView<u8, BLE_5_ADV_SVC_MAX_CONTENT_LEN>,
296 }
297 
298 impl EncodedAdvertisement {
299     /// Returns the advertisement as a slice.
as_slice(&self) -> &[u8]300     pub fn as_slice(&self) -> &[u8] {
301         self.adv.as_slice()
302     }
303     /// Converts this encoded advertisement into
304     /// a raw byte-array.
into_array_view(self) -> ArrayView<u8, BLE_5_ADV_SVC_MAX_CONTENT_LEN>305     pub fn into_array_view(self) -> ArrayView<u8, BLE_5_ADV_SVC_MAX_CONTENT_LEN> {
306         self.adv
307     }
308 }
309 
310 /// The advertisement type, which dictates what sections can exist
311 #[derive(Debug, PartialEq, Eq)]
312 pub enum AdvertisementType {
313     /// Plaintext advertisement with only plaintext sections
314     Plaintext,
315     /// Encrypted advertisement with only encrypted sections
316     Encrypted,
317 }
318 
319 impl AdvertisementType {
max_sections(&self) -> usize320     fn max_sections(&self) -> usize {
321         match self {
322             AdvertisementType::Plaintext => NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT,
323             AdvertisementType::Encrypted => NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT,
324         }
325     }
326 }
327 
328 /// Derived salt for an individual data element.
329 pub struct DeSalt {
330     salt: ExtendedV1Salt,
331     de_offset: DataElementOffset,
332 }
333 
334 impl DeSalt {
335     /// Derive salt of the requested length.
336     ///
337     /// The length must be a valid HKDF-SHA256 length.
derive<const N: usize, C: CryptoProvider>(&self) -> Option<[u8; N]>338     pub fn derive<const N: usize, C: CryptoProvider>(&self) -> Option<[u8; N]> {
339         self.salt.derive::<N, C>(Some(self.de_offset))
340     }
341 }
342 
343 /// For DE structs that only implement one DE type, rather than multi-type impls.
344 pub trait SingleTypeDataElement {
345     /// The DE type for the DE.
346     const DE_TYPE: DeType;
347 }
348 
349 /// Writes data for a V1 DE into a provided buffer.
350 ///
351 /// V1 data elements can be hundreds of bytes, so we ideally wouldn't even stack allocate a buffer
352 /// big enough for that, hence an abstraction that writes into an existing buffer.
353 pub trait WriteDataElement {
354     /// Returns the DE header that will be serialized into the section.
de_header(&self) -> DeHeader355     fn de_header(&self) -> DeHeader;
356     /// Write just the contents of the DE, returning `Some` if all contents could be written and
357     /// `None` otherwise.
write_de_contents<S: Sink<u8>>(&self, sink: &mut S) -> Option<()>358     fn write_de_contents<S: Sink<u8>>(&self, sink: &mut S) -> Option<()>;
359 }
360 
361 // convenience impl for &W
362 impl<W: WriteDataElement> WriteDataElement for &W {
de_header(&self) -> DeHeader363     fn de_header(&self) -> DeHeader {
364         (*self).de_header()
365     }
366 
write_de_contents<S: Sink<u8>>(&self, sink: &mut S) -> Option<()>367     fn write_de_contents<S: Sink<u8>>(&self, sink: &mut S) -> Option<()> {
368         (*self).write_de_contents(sink)
369     }
370 }
371 
372 /// Serialization-specific representation of a DE header
373 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
374 pub struct DeHeader {
375     /// The length of the content of the DE
376     len: DeLength,
377     pub(crate) de_type: DeType,
378 }
379 
380 impl DeHeader {
381     /// Build a DeHeader from the provided type and length
new(de_type: DeType, len: DeLength) -> Self382     pub fn new(de_type: DeType, len: DeLength) -> Self {
383         DeHeader { de_type, len }
384     }
385 
386     /// Serialize the DE header as per the V1 DE header format:
387     /// - 1 byte form for length <= 3 bits, type <= 4 bits: `0LLLTTTT`
388     /// - multi byte form: `0b1LLLLLLL [0b1TTTTTTT ...] 0b0TTTTTTT`
389     ///   - the shortest possible encoding must be used (no empty prefix type bytes)
390     ///
391     /// We assume that a 32-bit de type is sufficient, which would take at most 5 7-bit chunks to
392     /// encode, resulting in a total length of 6 bytes with the initial length byte.
serialize(&self) -> ArrayView<u8, 6>393     pub(crate) fn serialize(&self) -> ArrayView<u8, 6> {
394         let mut buffer = [0; 6];
395         let de_type = self.de_type.as_u32();
396         let hi_bit = 0x80_u8;
397         let len = self.len.len;
398         if !de_requires_extended_bit(de_type, len) {
399             buffer[0] = len << 4 | de_type as u8;
400             ArrayView::try_from_array(buffer, 1).expect("1 is a valid length")
401         } else {
402             // length w/ extended bit
403             buffer[0] = hi_bit | len;
404 
405             // expand to a u64 so we can represent all 5 7-bit chunks of a u32, shifted so that
406             // it fills the top 5 * 7 = 35 bits after the high bit, which is left unset so that
407             // the MSB can be interpreted as a 7-bit chunk with an unset high bit.
408             let mut type64 = (de_type as u64) << (64 - 35 - 1);
409             let mut remaining_chunks = 5;
410             let mut chunks_written = 0;
411             // write 7 bit chunks, skipping leading 0 chunks
412             while remaining_chunks > 0 {
413                 let chunk = type64.to_be_bytes()[0];
414                 remaining_chunks -= 1;
415 
416                 // shift 7 more bits up, leaving the high bit unset
417                 type64 = (type64 << 7) & (u64::MAX >> 1);
418 
419                 if chunks_written == 0 && chunk == 0 {
420                     // skip leading all-zero chunks
421                     continue;
422                 }
423 
424                 buffer[1 + chunks_written] = chunk;
425                 chunks_written += 1;
426             }
427             if chunks_written > 0 {
428                 // fill in high bits for all but the last
429                 for byte in buffer[1..chunks_written].iter_mut() {
430                     *byte |= hi_bit;
431                 }
432 
433                 ArrayView::try_from_array(buffer, 1 + chunks_written).expect("length is at most 6")
434             } else {
435                 // type byte is a leading 0 bit w/ 0 type, so use the existing 0 byte
436                 ArrayView::try_from_array(buffer, 2).expect("2 is a valid length")
437             }
438         }
439     }
440 }
441 
442 /// A wrapper around a fixed-size tinyvec that can have its capacity further constrained to handle
443 /// dynamic size limits.
444 #[derive(Debug)]
445 pub(crate) struct CapacityLimitedVec<T, const N: usize>
446 where
447     T: fmt::Debug + Clone,
448     [T; N]: tinyvec::Array + fmt::Debug,
449     <[T; N] as tinyvec::Array>::Item: fmt::Debug + Clone,
450 {
451     /// constraint on the occupied space in `vec`.
452     /// Invariant: `vec.len() <= capacity` and `vec.capacity() >= capacity`.
453     capacity: usize,
454     vec: tinyvec::ArrayVec<[T; N]>,
455 }
456 
457 impl<T, const N: usize> CapacityLimitedVec<T, N>
458 where
459     T: fmt::Debug + Clone,
460     [T; N]: tinyvec::Array + fmt::Debug,
461     <[T; N] as tinyvec::Array>::Item: fmt::Debug + Clone,
462 {
463     /// Returns `None` if `capacity > N`
new(capacity: usize) -> Option<Self>464     pub(crate) fn new(capacity: usize) -> Option<Self> {
465         if capacity <= N {
466             Some(Self { capacity, vec: tinyvec::ArrayVec::new() })
467         } else {
468             None
469         }
470     }
471 
len(&self) -> usize472     pub(crate) fn len(&self) -> usize {
473         self.vec.len()
474     }
475 
capacity(&self) -> usize476     fn capacity(&self) -> usize {
477         self.capacity
478     }
479 
truncate(&mut self, len: usize)480     fn truncate(&mut self, len: usize) {
481         self.vec.truncate(len);
482     }
483 
into_inner(self) -> tinyvec::ArrayVec<[T; N]>484     pub(crate) fn into_inner(self) -> tinyvec::ArrayVec<[T; N]> {
485         self.vec
486     }
487 }
488 
489 impl<T, const N: usize> Sink<<[T; N] as tinyvec::Array>::Item> for CapacityLimitedVec<T, N>
490 where
491     T: fmt::Debug + Clone,
492     [T; N]: tinyvec::Array + fmt::Debug,
493     <[T; N] as tinyvec::Array>::Item: fmt::Debug + Clone,
494 {
try_extend_from_slice(&mut self, items: &[<[T; N] as tinyvec::Array>::Item]) -> Option<()>495     fn try_extend_from_slice(&mut self, items: &[<[T; N] as tinyvec::Array>::Item]) -> Option<()> {
496         if items.len() > (self.capacity() - self.len()) {
497             return None;
498         }
499         // won't panic: just checked the length
500         self.vec.extend_from_slice(items);
501         Some(())
502     }
503 
try_push(&mut self, item: <[T; N] as tinyvec::Array>::Item) -> Option<()>504     fn try_push(&mut self, item: <[T; N] as tinyvec::Array>::Item) -> Option<()> {
505         if self.len() == self.capacity() {
506             // already full
507             None
508         } else {
509             self.vec.push(item);
510             Some(())
511         }
512     }
513 }
514