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 use array_view::ArrayView;
16 use crypto_provider::CryptoProvider;
17 use np_hkdf::v1_salt::DataElementOffset;
18 use sink::Sink as _;
19 
20 use crate::extended::{
21     serialize::{
22         section::encoder::SectionEncoder, AdvBuilder, CapacityLimitedVec, WriteDataElement,
23     },
24     to_array_view, NP_ADV_MAX_SECTION_LEN,
25 };
26 
27 pub(crate) mod encoder;
28 pub(crate) mod header;
29 
30 /// Accumulates data elements and encodes them into a section.
31 #[derive(Debug)]
32 pub struct SectionBuilder<R: AsMut<AdvBuilder>, SE: SectionEncoder> {
33     /// The length of the header produced by `section_encoder`
34     pub(crate) header_len: usize,
35     /// Contains the section header, the identity-specified overhead, and any DEs added
36     pub(crate) section: CapacityLimitedVec<u8, { NP_ADV_MAX_SECTION_LEN }>,
37     pub(crate) section_encoder: SE,
38     /// mut ref-able to enforce only one active section builder at a time
39     pub(crate) adv_builder: R,
40     next_de_offset: DataElementOffset,
41 }
42 
43 impl<'a, SE: SectionEncoder> SectionBuilder<&'a mut AdvBuilder, SE> {
44     /// Add this builder to the advertisement that created it.
add_to_advertisement<C: CryptoProvider>(self)45     pub fn add_to_advertisement<C: CryptoProvider>(self) {
46         let _ = self.add_to_advertisement_internal::<C>();
47     }
48 }
49 
50 impl<SE: SectionEncoder> SectionBuilder<AdvBuilder, SE> {
51     /// Gets the 0-based index of the section currently under construction
52     /// in the context of the containing advertisement.
section_index(&self) -> usize53     pub fn section_index(&self) -> usize {
54         self.adv_builder.section_count()
55     }
56     /// Add this builder to the advertisement that created it,
57     /// and returns the containing advertisement back to the caller.
add_to_advertisement<C: CryptoProvider>(self) -> AdvBuilder58     pub fn add_to_advertisement<C: CryptoProvider>(self) -> AdvBuilder {
59         self.add_to_advertisement_internal::<C>()
60     }
61 }
62 
63 impl<R: AsMut<AdvBuilder>, SE: SectionEncoder> SectionBuilder<R, SE> {
new( header_len: usize, section: CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>, section_encoder: SE, adv_builder: R, ) -> Self64     pub(crate) fn new(
65         header_len: usize,
66         section: CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>,
67         section_encoder: SE,
68         adv_builder: R,
69     ) -> Self {
70         Self {
71             header_len,
72             section,
73             section_encoder,
74             adv_builder,
75             next_de_offset: DataElementOffset::ZERO,
76         }
77     }
78 
79     /// Add this builder to the advertisement that created it.
80     /// Returns the mut-refable to the advertisement builder
81     /// which the contents of this section builder were added to.
82     //TODO: make this fallible, if the section being added is invalid, right now it is possible to
83     // create invalid adv's that don't parse
add_to_advertisement_internal<C: CryptoProvider>(mut self) -> R84     fn add_to_advertisement_internal<C: CryptoProvider>(mut self) -> R {
85         let adv_builder = self.adv_builder.as_mut();
86         adv_builder.add_section(Self::build_section::<C>(
87             self.header_len,
88             self.section.into_inner(),
89             self.section_encoder,
90         ));
91         self.adv_builder
92     }
93 
94     /// Gets the derived salt which will be employed for the next DE offset.
95     ///
96     /// Suitable for scenarios (like FFI) where a closure would be inappropriate
97     /// for DE construction, and interaction with the client is preferred.
next_de_salt(&self) -> SE::DerivedSalt98     pub fn next_de_salt(&self) -> SE::DerivedSalt {
99         self.section_encoder.de_salt(self.next_de_offset)
100     }
101 
102     /// Add a data element to the section with a closure that returns a `Result`.
103     ///
104     /// The provided `build_de` closure will be invoked with the derived salt for this DE.
add_de_res<W: WriteDataElement, E, F: FnOnce(SE::DerivedSalt) -> Result<W, E>>( &mut self, build_de: F, ) -> Result<(), AddDataElementError<E>>105     pub fn add_de_res<W: WriteDataElement, E, F: FnOnce(SE::DerivedSalt) -> Result<W, E>>(
106         &mut self,
107         build_de: F,
108     ) -> Result<(), AddDataElementError<E>> {
109         let writer = build_de(self.next_de_salt()).map_err(AddDataElementError::BuildDeError)?;
110 
111         let orig_len = self.section.len();
112         // since we own the writer, and it's immutable, no race risk writing header w/ len then
113         // the contents as long as it's not simply an incorrect impl
114         let de_header = writer.de_header();
115         let content_len = self
116             .section
117             .try_extend_from_slice(de_header.serialize().as_slice())
118             .ok_or(AddDataElementError::InsufficientSectionSpace)
119             .and_then(|_| {
120                 let after_header_len = self.section.len();
121                 writer
122                     .write_de_contents(&mut self.section)
123                     .ok_or(AddDataElementError::InsufficientSectionSpace)
124                     .map(|_| self.section.len() - after_header_len)
125             })
126             .map_err(|e| {
127                 // if anything went wrong, truncate any partial writes (e.g. just the header)
128                 self.section.truncate(orig_len);
129                 e
130             })?;
131 
132         if content_len != usize::from(de_header.len.as_u8()) {
133             // TODO eliminate this possibility by keeping a 127-byte buffer
134             // to write DEs into, then calculating the written length, so the
135             // DE impl doesn't have to do it
136             panic!(
137                 "Buggy WriteDataElement impl: header len {}, actual written len {}",
138                 de_header.len.as_u8(),
139                 content_len
140             );
141         }
142 
143         self.next_de_offset = self.next_de_offset.incremented();
144 
145         Ok(())
146     }
147 
148     /// Add a data element to the section with a closure that returns the data element directly.
149     ///
150     /// The provided `build_de` closure will be invoked with the derived salt for this DE.
add_de<W: WriteDataElement, F: FnOnce(SE::DerivedSalt) -> W>( &mut self, build_de: F, ) -> Result<(), AddDataElementError<()>>151     pub fn add_de<W: WriteDataElement, F: FnOnce(SE::DerivedSalt) -> W>(
152         &mut self,
153         build_de: F,
154     ) -> Result<(), AddDataElementError<()>> {
155         self.add_de_res(|derived_salt| Ok::<_, ()>(build_de(derived_salt)))
156     }
157 
158     /// Convert a section builder's contents into an encoded section.
159     ///
160     /// `section_contents` must have size > 0.
161     ///
162     /// `header_len` is the length of the prefix of `section_contents` that has been populated
163     /// with the data returned from [SectionEncoder::header()] which does NOT include the length byte.
164     ///
165     /// Implemented without self to avoid partial-move issues.
build_section<C: CryptoProvider>( header_len: usize, mut section_contents: tinyvec::ArrayVec<[u8; NP_ADV_MAX_SECTION_LEN]>, mut section_encoder: SE, ) -> EncodedSection166     pub(crate) fn build_section<C: CryptoProvider>(
167         header_len: usize,
168         mut section_contents: tinyvec::ArrayVec<[u8; NP_ADV_MAX_SECTION_LEN]>,
169         mut section_encoder: SE,
170     ) -> EncodedSection {
171         // there is space because the capacity for DEs was restricted to allow it
172         section_contents.resize(section_contents.len() + SE::SUFFIX_LEN, 0);
173 
174         let (format_and_salt_and_identity_token, rest_of_contents) =
175             section_contents.split_at_mut(header_len);
176 
177         let (section_length_byte, rest_of_contents) = rest_of_contents.split_at_mut(1);
178 
179         let section_len = rest_of_contents.len().try_into().expect(
180             "section length will always fit into a u8 and has been validated by the section builder",
181         );
182         // set the section length byte
183         section_length_byte[0] = section_len;
184         section_encoder.postprocess::<C>(
185             format_and_salt_and_identity_token,
186             section_len,
187             rest_of_contents,
188         );
189 
190         to_array_view(section_contents)
191     }
192 }
193 
194 /// Errors for adding a DE to a section
195 #[derive(Debug, PartialEq, Eq)]
196 pub enum AddDataElementError<E> {
197     /// An error occurred when invoking the DE builder closure.
198     BuildDeError(E),
199     /// Too much data to fit into the section
200     InsufficientSectionSpace,
201 }
202 
203 /// The encoded form of an advertisement section
204 pub(crate) type EncodedSection = ArrayView<u8, NP_ADV_MAX_SECTION_LEN>;
205