• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 for V0 advertisements.
16 //!
17 //! # Examples
18 //!
19 //! Serializing a plaintext advertisement:
20 //!
21 //! ```
22 //! use np_adv::{legacy::{data_elements::tx_power::TxPowerDataElement, serialize::*}, shared_data::*};
23 //!
24 //! let mut builder = AdvBuilder::new(UnencryptedEncoder);
25 //! builder
26 //!     .add_data_element(TxPowerDataElement::from(TxPower::try_from(3).expect("3 is a valid TxPower value")))
27 //!     .unwrap();
28 //! let packet = builder.into_advertisement().unwrap();
29 //! assert_eq!(
30 //!     &[
31 //!         0x00, // Adv Header
32 //!         0x15, 0x03, // tx power de
33 //!     ],
34 //!     packet.as_slice()
35 //! );
36 //! ```
37 //!
38 //! Serializing an encrypted advertisement:
39 //!
40 //! ```
41 //! use np_adv::{shared_data::*, legacy::{data_elements::de_type::*, data_elements::tx_power::TxPowerDataElement, serialize::*, *}};
42 //! use np_adv::credential::{v0::{V0, V0BroadcastCredential}};
43 //! use crypto_provider::CryptoProvider;
44 //! use crypto_provider_default::CryptoProviderImpl;
45 //! use ldt_np_adv::{V0Salt, V0IdentityToken};
46 //!
47 //! // Generate these from proper CSPRNGs -- using fixed data here
48 //! let metadata_key = V0IdentityToken::from([0x33; 14]);
49 //! let salt = V0Salt::from([0x01, 0x02]);
50 //! let key_seed = [0x44; 32];
51 //!
52 //! let broadcast_cred = V0BroadcastCredential::new(
53 //!     key_seed,
54 //!     metadata_key,
55 //! );
56 //!
57 //! let mut builder = AdvBuilder::new(LdtEncoder::<CryptoProviderImpl>::new(
58 //!     salt,
59 //!     &broadcast_cred,
60 //! ));
61 //!
62 //! builder
63 //!     .add_data_element(TxPowerDataElement::from(TxPower::try_from(3).expect("3 is a valid TxPower value")))
64 //!     .unwrap();
65 //!
66 //! let packet = builder.into_advertisement().unwrap();
67 //! ```
68 use core::fmt;
69 
70 use array_view::ArrayView;
71 use crypto_provider::CryptoProvider;
72 use ldt::LdtCipher;
73 use ldt_np_adv::{V0IdentityToken, V0_IDENTITY_TOKEN_LEN};
74 use sink::Sink;
75 
76 use crate::credential::v0::V0BroadcastCredential;
77 use crate::{
78     extended::to_array_view,
79     header::{VERSION_HEADER_V0_LDT, VERSION_HEADER_V0_UNENCRYPTED},
80     legacy::{
81         data_elements::{
82             de_type::{DeActualLength, DeEncodedLength, DeTypeCode},
83             DataElementSerializationBuffer, DataElementSerializeError, SerializeDataElement,
84         },
85         Ciphertext, PacketFlavor, Plaintext, BLE_4_ADV_SVC_MAX_CONTENT_LEN, NP_MAX_ADV_CONTENT_LEN,
86         NP_MIN_ADV_CONTENT_LEN,
87     },
88 };
89 
90 mod header;
91 
92 #[cfg(test)]
93 pub(crate) mod tests;
94 
95 /// An encoder used in serializing an advertisement.
96 pub trait AdvEncoder: fmt::Debug {
97     /// The flavor of packet this identity produces
98     type Flavor: PacketFlavor;
99     /// The error returned if postprocessing fails
100     type Error: fmt::Debug;
101 
102     /// The version header to put at the start of the advertisement.
103     const VERSION_HEADER: u8;
104 
105     /// The V0-specific header to be written at the start of the advertisement
106     /// immediately after the version header.
header(&self) -> header::V0Header107     fn header(&self) -> header::V0Header;
108 
109     /// Perform identity-specific manipulation to the serialized DEs to produce the final
110     /// advertisement format.
111     ///
112     /// `buf` has the contents of [Self::header] written at the start, followed
113     /// by all of the DEs added to a packet. It does not include the NP top level header.
114     ///
115     /// The first `header_len` bytes of `buf` are the bytes produced by the call to [Self::header].
116     ///
117     /// Returns `Ok` if postprocessing was successful and the packet is finished, or `Err` if
118     /// postprocessing failed and the packet should be discarded.
postprocess(&self, header_len: usize, buf: &mut [u8]) -> Result<(), Self::Error>119     fn postprocess(&self, header_len: usize, buf: &mut [u8]) -> Result<(), Self::Error>;
120 }
121 
122 /// An unencrypted encoder with no associated identity.
123 #[derive(Debug)]
124 pub struct UnencryptedEncoder;
125 
126 impl AdvEncoder for UnencryptedEncoder {
127     type Flavor = Plaintext;
128     type Error = UnencryptedEncodeError;
129     const VERSION_HEADER: u8 = VERSION_HEADER_V0_UNENCRYPTED;
130 
header(&self) -> header::V0Header131     fn header(&self) -> header::V0Header {
132         header::V0Header::unencrypted()
133     }
134 
postprocess(&self, _header_len: usize, buf: &mut [u8]) -> Result<(), Self::Error>135     fn postprocess(&self, _header_len: usize, buf: &mut [u8]) -> Result<(), Self::Error> {
136         if buf.len() < NP_MIN_ADV_CONTENT_LEN {
137             Err(UnencryptedEncodeError::InvalidLength)
138         } else {
139             Ok(())
140         }
141     }
142 }
143 
144 /// Unencrypted encoding errors
145 #[derive(Debug, PartialEq, Eq)]
146 pub enum UnencryptedEncodeError {
147     /// The advertisement content was outside the valid range
148     InvalidLength,
149 }
150 
151 /// Encoder used for encrypted packets (private, trusted, provisioned) that encrypts other DEs
152 /// (as well as the metadata key).
153 pub struct LdtEncoder<C: CryptoProvider> {
154     salt: ldt_np_adv::V0Salt,
155     identity_token: V0IdentityToken,
156     ldt_enc: ldt_np_adv::NpLdtEncryptCipher<C>,
157 }
158 
159 // Exclude sensitive members
160 impl<C: CryptoProvider> fmt::Debug for LdtEncoder<C> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result161     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162         write!(f, "LdtEncoder {{ salt: {:X?} }}", self.salt)
163     }
164 }
165 
166 impl<C: CryptoProvider> LdtEncoder<C> {
167     /// Build an `LdtIdentity` for the provided identity type, salt, and
168     /// broadcast crypto-materials.
new(salt: ldt_np_adv::V0Salt, broadcast_cred: &V0BroadcastCredential) -> Self169     pub fn new(salt: ldt_np_adv::V0Salt, broadcast_cred: &V0BroadcastCredential) -> Self {
170         let identity_token = broadcast_cred.identity_token();
171         let key_seed = broadcast_cred.key_seed();
172         let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&key_seed);
173         let ldt_key = key_seed_hkdf.v0_ldt_key();
174         let ldt_enc = ldt_np_adv::NpLdtEncryptCipher::<C>::new(&ldt_key);
175 
176         Self { salt, identity_token, ldt_enc }
177     }
178 }
179 
180 impl<C: CryptoProvider> AdvEncoder for LdtEncoder<C> {
181     type Flavor = Ciphertext;
182     type Error = LdtEncodeError;
183     const VERSION_HEADER: u8 = VERSION_HEADER_V0_LDT;
184 
header(&self) -> header::V0Header185     fn header(&self) -> header::V0Header {
186         header::V0Header::ldt_short_salt(self.salt, self.identity_token)
187     }
188 
postprocess(&self, header_len: usize, buf: &mut [u8]) -> Result<(), LdtEncodeError>189     fn postprocess(&self, header_len: usize, buf: &mut [u8]) -> Result<(), LdtEncodeError> {
190         // encrypt everything after v0 format and salt
191         self.ldt_enc
192             .encrypt(
193                 &mut buf[header_len - V0_IDENTITY_TOKEN_LEN..],
194                 &ldt_np_adv::salt_padder::<C>(self.salt),
195             )
196             .map_err(|e| match e {
197                 // too short, not enough DEs
198                 ldt::LdtError::InvalidLength(_) => LdtEncodeError::InvalidLength,
199             })
200     }
201 }
202 
203 /// LDT encoding errors
204 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
205 pub enum LdtEncodeError {
206     /// The minimum size (2 bytes) or maximum size (7 bytes for BLE 4.2
207     /// advertisements) of DE data was not met
208     InvalidLength,
209 }
210 
211 /// The serialized form of a V0 adv, suitable for setting as the value for the NP UUID svc data.
212 pub type SerializedAdv = ArrayView<u8, BLE_4_ADV_SVC_MAX_CONTENT_LEN>;
213 
214 /// Accumulates DEs, then massages the serialized DEs with the configured identity to produce
215 /// the final advertisement.
216 #[derive(Debug)]
217 pub struct AdvBuilder<E: AdvEncoder> {
218     /// The first byte is for the adv header, then the next I::OVERHEAD_LEN bytes are set aside for
219     /// use by [AdvEncoder::postprocess].
220     buffer: [u8; BLE_4_ADV_SVC_MAX_CONTENT_LEN],
221     /// How much of the buffer is consumed.
222     /// Always <= buffer length, and at least 2
223     len: usize,
224     /// How long the header was
225     header_len: usize,
226     encoder: E,
227 }
228 
229 impl<E: AdvEncoder> AdvBuilder<E> {
230     /// Create an empty AdvBuilder with the provided identity.
new(encoder: E) -> AdvBuilder<E>231     pub fn new(encoder: E) -> AdvBuilder<E> {
232         let mut buffer = [0; BLE_4_ADV_SVC_MAX_CONTENT_LEN];
233         buffer[0] = E::VERSION_HEADER;
234 
235         // encode the rest of the V0 header
236         let header = encoder.header();
237         let header_len = header.as_slice().len();
238         // len will be at least 1, important for max_de_len safety in add_data_element
239         let len = 1 + header_len;
240         // check for broken identities
241         debug_assert!(len < buffer.len());
242         buffer[1..=header_len].copy_from_slice(header.as_slice());
243 
244         AdvBuilder {
245             // conveniently the first byte is already 0, which is the correct header for v0.
246             // 3 bit version (000 since this is version 0), 5 bit reserved (also all 0)
247             buffer,
248             len,
249             header_len,
250             encoder,
251         }
252     }
253 
254     /// Add the data element to the packet buffer, if there is space.
add_data_element<D: SerializeDataElement<E::Flavor>>( &mut self, data_element: D, ) -> Result<(), AddDataElementError>255     pub fn add_data_element<D: SerializeDataElement<E::Flavor>>(
256         &mut self,
257         data_element: D,
258     ) -> Result<(), AddDataElementError> {
259         // invariant: self.len <= buffer length
260         debug_assert!(self.len <= self.buffer.len());
261         let dest = &mut self.buffer[self.len..];
262         // because self.len is at least 1, the dest will be no more than
263         // `[BLE_ADV_SVC_CONTENT_LEN] - 1 = [NP_MAX_ADV_CONTENT_LEN]`
264         let de_buf = serialize_de(&data_element, dest.len())?;
265         dest[..de_buf.len()].copy_from_slice(de_buf.as_slice());
266         // invariant: de_buf fit in the remaining space, so adding its length is safe
267         self.len += de_buf.len();
268         debug_assert!(self.len <= self.buffer.len());
269 
270         Ok(())
271     }
272 
273     /// Return the finished advertisement (version header || V0 header || DEs),
274     /// or `None` if the adv could not be built.
into_advertisement(mut self) -> Result<SerializedAdv, E::Error>275     pub fn into_advertisement(mut self) -> Result<SerializedAdv, E::Error> {
276         // encrypt, if applicable
277         self.encoder
278             // skip NP version header for postprocessing
279             .postprocess(self.header_len, &mut self.buffer[1..self.len])
280             .map(|_| ArrayView::try_from_array(self.buffer, self.len).expect("len is always valid"))
281     }
282 }
283 
284 /// Errors that can occur for [AdvBuilder.add_data_element].
285 #[derive(Debug, PartialEq, Eq)]
286 pub enum AddDataElementError {
287     /// The provided DE can't fit in the remaining space
288     InsufficientAdvSpace,
289 }
290 
291 impl From<DataElementSerializeError> for AddDataElementError {
from(value: DataElementSerializeError) -> Self292     fn from(value: DataElementSerializeError) -> Self {
293         match value {
294             DataElementSerializeError::InsufficientSpace => Self::InsufficientAdvSpace,
295         }
296     }
297 }
298 
299 /// Encode a DE type and length into a DE header byte.
encode_de_header(code: DeTypeCode, header_len: DeEncodedLength) -> u8300 pub(crate) fn encode_de_header(code: DeTypeCode, header_len: DeEncodedLength) -> u8 {
301     // 4 high bits are length, 4 low bits are type
302     (header_len.as_u8() << 4) | code.as_u8()
303 }
304 
305 /// Encode a DE into a buffer.
306 ///
307 /// The buffer will contain the DE header and DE contents, if any, and will
308 /// not exceed `max_de_len`.
309 ///
310 /// # Panics
311 /// `max_de_len` must be no larger than [NP_MAX_ADV_CONTENT_LEN].
serialize_de<F: PacketFlavor, D: SerializeDataElement<F>>( data_element: &D, max_de_len: usize, ) -> Result<SerializedDataElement, AddDataElementError>312 fn serialize_de<F: PacketFlavor, D: SerializeDataElement<F>>(
313     data_element: &D,
314     max_de_len: usize,
315 ) -> Result<SerializedDataElement, AddDataElementError> {
316     let mut de_buf = DataElementSerializationBuffer::new(max_de_len)
317         .expect("max_de_len must not exceed NP_MAX_DE_CONTENT_LEN");
318 
319     // placeholder for header
320     de_buf.try_push(0).ok_or(AddDataElementError::InsufficientAdvSpace)?;
321     data_element.serialize_contents(&mut de_buf)?;
322 
323     let encoded_len = data_element.map_actual_len_to_encoded_len(
324         DeActualLength::try_from(de_buf.len() - 1)
325             .expect("DE fit in buffer after header, so it should be a valid size"),
326     );
327 
328     let mut vec = de_buf.into_inner().into_inner();
329     vec[0] = encode_de_header(data_element.de_type_code(), encoded_len);
330 
331     debug_assert!(vec.len() <= max_de_len);
332 
333     Ok(SerializedDataElement::new(to_array_view(vec)))
334 }
335 
336 /// The serialized form of a data element, including its header.
337 pub(crate) struct SerializedDataElement {
338     data: ArrayView<u8, NP_MAX_ADV_CONTENT_LEN>,
339 }
340 
341 impl SerializedDataElement {
new(data: ArrayView<u8, NP_MAX_ADV_CONTENT_LEN>) -> Self342     fn new(data: ArrayView<u8, NP_MAX_ADV_CONTENT_LEN>) -> Self {
343         Self { data }
344     }
345 
346     /// The serialized DE, starting with the DE header byte
as_slice(&self) -> &[u8]347     pub(crate) fn as_slice(&self) -> &[u8] {
348         self.data.as_slice()
349     }
350 
len(&self) -> usize351     pub(crate) fn len(&self) -> usize {
352         self.data.len()
353     }
354 }
355