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(§ion_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>(§ion_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