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 //! Deserialization for V1 advertisement contents
16 #[cfg(any(test, feature = "alloc"))]
17 use alloc::vec::Vec;
18 
19 use core::{array::TryFromSliceError, fmt::Debug};
20 
21 use crate::{
22     array_vec::ArrayVecOption,
23     credential::{
24         book::CredentialBook,
25         matched::{HasIdentityMatch, MatchedCredential, WithMatchedCredential},
26         v1::{V1DiscoveryCryptoMaterial, V1},
27     },
28     deserialization_arena::{ArenaOutOfSpace, DeserializationArena, DeserializationArenaAllocator},
29     extended::{
30         deserialize::{
31             data_element::{DataElement, DataElementParseError, DataElementParsingIterator},
32             encrypted_section::{
33                 DeserializationError, MicVerificationError, SectionIdentityResolutionContents,
34                 SignatureVerificationError,
35             },
36             section::intermediate::{
37                 parse_sections, CiphertextSection, IntermediateSection, PlaintextSection,
38             },
39         },
40         salt::MultiSalt,
41         V1IdentityToken, NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT,
42     },
43     header::V1AdvHeader,
44     AdvDeserializationError, AdvDeserializationErrorDetailsHazmat,
45 };
46 use crypto_provider::CryptoProvider;
47 
48 #[cfg(test)]
49 mod tests;
50 
51 pub mod data_element;
52 pub(crate) mod encrypted_section;
53 pub(crate) mod section;
54 
55 /// Provides deserialization APIs which expose more of the internals, suitable for use only in
56 /// dev tools.
57 #[cfg(feature = "devtools")]
58 pub mod dev_tools;
59 
60 /// Deserialize and decrypt the contents of a v1 adv after the version header
deser_decrypt_v1<'adv, 'cred, B, P>( arena: DeserializationArena<'adv>, cred_book: &'cred B, remaining: &'adv [u8], header: V1AdvHeader, ) -> Result<V1AdvertisementContents<'adv, B::Matched>, AdvDeserializationError> where B: CredentialBook<'cred>, P: CryptoProvider,61 pub(crate) fn deser_decrypt_v1<'adv, 'cred, B, P>(
62     arena: DeserializationArena<'adv>,
63     cred_book: &'cred B,
64     remaining: &'adv [u8],
65     header: V1AdvHeader,
66 ) -> Result<V1AdvertisementContents<'adv, B::Matched>, AdvDeserializationError>
67 where
68     B: CredentialBook<'cred>,
69     P: CryptoProvider,
70 {
71     let mut sections_in_processing =
72         SectionsInProcessing::<'_, B::Matched>::from_advertisement_contents::<P>(
73             header, remaining,
74         )?;
75 
76     let mut allocator = arena.into_allocator();
77 
78     // Hot loop
79     // We assume that iterating credentials is more expensive than iterating sections
80     for (crypto_material, match_data) in cred_book.v1_iter() {
81         sections_in_processing
82             .try_decrypt_with_credential::<_, P>(&mut allocator, crypto_material, match_data)
83             .expect(concat!(
84                 "Should not run out of space because DeserializationArenaAllocator is big ",
85                 "enough to hold a single advertisement, and we exit immediately upon ",
86                 "successful decryption",
87             ));
88         if sections_in_processing.resolved_all_identities() {
89             // No need to consider the other credentials
90             break;
91         }
92     }
93     Ok(sections_in_processing.finished_with_decryption_attempts())
94 }
95 
96 /// A section deserialized from a V1 advertisement.
97 pub trait Section<'adv, E: Debug> {
98     /// The iterator type used to iterate over data elements
99     type Iterator: Iterator<Item = Result<DataElement<'adv>, E>>;
100 
101     /// Iterator over the data elements in a section, except for any DEs related to resolving the
102     /// identity or otherwise validating the payload (e.g. MIC, Signature, any identity DEs like
103     /// Private Identity).
iter_data_elements(&self) -> Self::Iterator104     fn iter_data_elements(&self) -> Self::Iterator;
105 
106     /// Collects the data elements into a vector, eagerly catching and resolving any errors during
107     /// parsing.
108     #[cfg(any(test, feature = "alloc"))]
collect_data_elements(&self) -> Result<Vec<DataElement<'adv>>, E> where Self: Sized,109     fn collect_data_elements(&self) -> Result<Vec<DataElement<'adv>>, E>
110     where
111         Self: Sized,
112     {
113         self.iter_data_elements().collect()
114     }
115 }
116 
117 /// Fully-parsed and verified decrypted contents from an encrypted section.
118 #[derive(Debug, PartialEq, Eq)]
119 pub struct DecryptedSection<'adv> {
120     verification_mode: VerificationMode,
121     identity_token: V1IdentityToken,
122     salt: MultiSalt,
123     /// Decrypted DE data, excluding any encoder suffix
124     plaintext: &'adv [u8],
125 }
126 
127 impl<'adv> DecryptedSection<'adv> {
new( verification_mode: VerificationMode, salt: MultiSalt, identity_token: V1IdentityToken, plaintext: &'adv [u8], ) -> Self128     fn new(
129         verification_mode: VerificationMode,
130         salt: MultiSalt,
131         identity_token: V1IdentityToken,
132         plaintext: &'adv [u8],
133     ) -> Self {
134         Self { verification_mode, identity_token, salt, plaintext }
135     }
136 
137     /// The verification mode used in the section.
verification_mode(&self) -> VerificationMode138     pub fn verification_mode(&self) -> VerificationMode {
139         self.verification_mode
140     }
141 
142     /// The identity token extracted from the section
identity_token(&self) -> &V1IdentityToken143     pub fn identity_token(&self) -> &V1IdentityToken {
144         &self.identity_token
145     }
146 
147     /// The salt used for decryption of this section.
salt(&self) -> &MultiSalt148     pub fn salt(&self) -> &MultiSalt {
149         &self.salt
150     }
151 
152     #[cfg(test)]
plaintext(&self) -> &'adv [u8]153     pub(crate) fn plaintext(&self) -> &'adv [u8] {
154         self.plaintext
155     }
156 }
157 
158 impl<'adv> HasIdentityMatch for DecryptedSection<'adv> {
159     type Version = V1;
identity_token(&self) -> V1IdentityToken160     fn identity_token(&self) -> V1IdentityToken {
161         self.identity_token
162     }
163 }
164 
165 impl<'adv> Section<'adv, DataElementParseError> for DecryptedSection<'adv> {
166     type Iterator = DataElementParsingIterator<'adv>;
167 
iter_data_elements(&self) -> Self::Iterator168     fn iter_data_elements(&self) -> Self::Iterator {
169         DataElementParsingIterator::new(self.plaintext)
170     }
171 }
172 
173 /// Errors that can occur when deserializing a section
174 /// of a V1 advertisement.
175 #[derive(Debug, PartialEq, Eq)]
176 pub enum SectionDeserializeError {
177     /// The credential supplied did not match the section's identity data
178     IncorrectCredential,
179     /// Section data is malformed
180     ParseError,
181     /// The given arena is not large enough to hold the decrypted contents
182     ArenaOutOfSpace,
183 }
184 
185 impl From<DeserializationError<MicVerificationError>> for SectionDeserializeError {
from(mic_deserialization_error: DeserializationError<MicVerificationError>) -> Self186     fn from(mic_deserialization_error: DeserializationError<MicVerificationError>) -> Self {
187         match mic_deserialization_error {
188             DeserializationError::VerificationError(MicVerificationError::MicMismatch) => {
189                 Self::IncorrectCredential
190             }
191             DeserializationError::ArenaOutOfSpace => Self::ArenaOutOfSpace,
192         }
193     }
194 }
195 
196 impl From<DeserializationError<SignatureVerificationError>> for SectionDeserializeError {
from( signature_deserialization_error: DeserializationError<SignatureVerificationError>, ) -> Self197     fn from(
198         signature_deserialization_error: DeserializationError<SignatureVerificationError>,
199     ) -> Self {
200         match signature_deserialization_error {
201             DeserializationError::VerificationError(
202                 SignatureVerificationError::SignatureMissing,
203             ) => Self::ParseError,
204             DeserializationError::VerificationError(
205                 SignatureVerificationError::SignatureMismatch,
206             ) => Self::IncorrectCredential,
207             DeserializationError::ArenaOutOfSpace => Self::ArenaOutOfSpace,
208         }
209     }
210 }
211 
212 /// A ciphertext section which has not yet been
213 /// resolved to an identity, but for which some
214 /// `SectionIdentityResolutionContents` have been
215 /// pre-computed for speedy identity-resolution.
216 struct ResolvableCiphertextSection<'a> {
217     identity_resolution_contents: SectionIdentityResolutionContents,
218     ciphertext_section: CiphertextSection<'a>,
219 }
220 
221 /// A collection of possibly-deserialized sections which are separated according
222 /// to whether/not they're intermediate encrypted sections (of either type)
223 /// or fully-deserialized, with a running count of the number of malformed sections.
224 /// Each potentially-valid section is tagged with a 0-based index derived from the original
225 /// section ordering as they appeared within the original advertisement to ensure
226 /// that the fully-deserialized advertisement may be correctly reconstructed.
227 struct SectionsInProcessing<'adv, M: MatchedCredential> {
228     deserialized_sections: ArrayVecOption<
229         (usize, V1DeserializedSection<'adv, M>),
230         { NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT },
231     >,
232     encrypted_sections: ArrayVecOption<
233         (usize, ResolvableCiphertextSection<'adv>),
234         { NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT },
235     >,
236     malformed_sections_count: usize,
237 }
238 
239 impl<'adv, M: MatchedCredential> SectionsInProcessing<'adv, M> {
240     /// Attempts to parse a V1 advertisement's contents after the version header
241     /// into a collection of not-yet-fully-deserialized sections which may
242     /// require credentials to be decrypted.
from_advertisement_contents<C: CryptoProvider>( header: V1AdvHeader, remaining: &'adv [u8], ) -> Result<Self, AdvDeserializationError>243     fn from_advertisement_contents<C: CryptoProvider>(
244         header: V1AdvHeader,
245         remaining: &'adv [u8],
246     ) -> Result<Self, AdvDeserializationError> {
247         let int_sections =
248             parse_sections(header, remaining).map_err(|_| AdvDeserializationError::ParseError {
249                 details_hazmat: AdvDeserializationErrorDetailsHazmat::AdvertisementDeserializeError,
250             })?;
251         let mut deserialized_sections = ArrayVecOption::default();
252         let mut encrypted_sections = ArrayVecOption::default();
253         // keep track of ordering for later sorting during `self.finished_with_decryption_attempts()`.
254         for (idx, s) in int_sections.into_iter().enumerate() {
255             match s {
256                 IntermediateSection::Plaintext(p) => {
257                     deserialized_sections.push((idx, V1DeserializedSection::Plaintext(p)))
258                 }
259                 IntermediateSection::Ciphertext(ciphertext_section) => {
260                     let identity_resolution_contents =
261                         ciphertext_section.identity_resolution_contents::<C>();
262                     let resolvable_ciphertext_section = ResolvableCiphertextSection {
263                         identity_resolution_contents,
264                         ciphertext_section,
265                     };
266                     encrypted_sections.push((idx, resolvable_ciphertext_section));
267                 }
268             }
269         }
270         Ok(Self { deserialized_sections, encrypted_sections, malformed_sections_count: 0 })
271     }
272 
273     /// Returns true iff we have resolved all sections to identities.
resolved_all_identities(&self) -> bool274     fn resolved_all_identities(&self) -> bool {
275         self.encrypted_sections.is_empty()
276     }
277 
278     /// Runs through all of the encrypted sections in processing, and attempts
279     /// to use the given credential to decrypt them. Suitable for situations
280     /// where iterating over credentials is relatively slow compared to
281     /// the cost of iterating over sections-in-memory.
try_decrypt_with_credential<C: V1DiscoveryCryptoMaterial, P: CryptoProvider>( &mut self, arena: &mut DeserializationArenaAllocator<'adv>, crypto_material: C, match_data: M, ) -> Result<(), ArenaOutOfSpace>282     fn try_decrypt_with_credential<C: V1DiscoveryCryptoMaterial, P: CryptoProvider>(
283         &mut self,
284         arena: &mut DeserializationArenaAllocator<'adv>,
285         crypto_material: C,
286         match_data: M,
287     ) -> Result<(), ArenaOutOfSpace> {
288         let mut i = 0;
289         while i < self.encrypted_sections.len() {
290             let (section_idx, section): &(usize, ResolvableCiphertextSection) =
291                 &self.encrypted_sections[i];
292             // Fast-path: Check for an identity match, ignore if there's no identity match.
293             let identity_resolution_contents = &section.identity_resolution_contents;
294             let identity_resolution_material = match &section.ciphertext_section {
295                 CiphertextSection::MicEncrypted(m) => match m.contents.salt {
296                     MultiSalt::Short(_) => crypto_material
297                         .mic_short_salt_identity_resolution_material::<P>()
298                         .into_raw_resolution_material(),
299                     MultiSalt::Extended(_) => crypto_material
300                         .mic_extended_salt_identity_resolution_material::<P>()
301                         .into_raw_resolution_material(),
302                 },
303 
304                 CiphertextSection::SignatureEncrypted(_) => crypto_material
305                     .signed_identity_resolution_material::<P>()
306                     .into_raw_resolution_material(),
307             };
308             match identity_resolution_contents.try_match::<P>(&identity_resolution_material) {
309                 None => {
310                     // Try again with another section
311                     i += 1;
312                     continue;
313                 }
314                 Some(identity_match) => {
315                     // The identity matched, so now we need to more closely scrutinize
316                     // the provided ciphertext. Try to decrypt and parse the section.
317                     let deserialization_result = match &section.ciphertext_section {
318                         CiphertextSection::SignatureEncrypted(c) => c
319                             .try_deserialize(
320                                 arena,
321                                 identity_match,
322                                 &crypto_material.signed_verification_material::<P>(),
323                             )
324                             .map_err(SectionDeserializeError::from),
325                         CiphertextSection::MicEncrypted(c) => c
326                             .try_deserialize(arena, identity_match, &crypto_material)
327                             .map_err(SectionDeserializeError::from),
328                     };
329                     match deserialization_result {
330                         Ok(s) => {
331                             self.deserialized_sections.push((
332                                 *section_idx,
333                                 V1DeserializedSection::Decrypted(WithMatchedCredential::new(
334                                     match_data.clone(),
335                                     crypto_material.metadata_nonce::<P>(),
336                                     s,
337                                 )),
338                             ));
339                         }
340                         Err(e) => match e {
341                             SectionDeserializeError::IncorrectCredential => {
342                                 // keep it around to try with another credential
343                                 i += 1;
344                                 continue;
345                             }
346                             SectionDeserializeError::ParseError => {
347                                 // the credential worked, but the section itself was bogus
348                                 self.malformed_sections_count += 1;
349                             }
350                             SectionDeserializeError::ArenaOutOfSpace => {
351                                 return Err(ArenaOutOfSpace);
352                             }
353                         },
354                     }
355                     // By default, if we have an identity match, assume that decrypting the section worked,
356                     // or that the section was somehow invalid.
357                     // We don't care about maintaining order, so use O(1) remove
358                     let _ = self.encrypted_sections.swap_remove(i);
359                     // don't advance i -- it now points to a new element
360                 }
361             }
362         }
363         Ok(())
364     }
365 
366     /// Packages the current state of the deserialization process into a
367     /// `V1AdvertisementContents` representing a fully-deserialized V1 advertisement.
368     ///
369     /// This method should only be called after all sections were either successfully
370     /// decrypted or have had all relevant credentials checked against
371     /// them without obtaining a successful identity-match and/or subsequent
372     /// cryptographic verification of the section contents.
finished_with_decryption_attempts(mut self) -> V1AdvertisementContents<'adv, M>373     fn finished_with_decryption_attempts(mut self) -> V1AdvertisementContents<'adv, M> {
374         // Invalid sections = malformed sections + number of encrypted sections
375         // which we could not manage to decrypt with any of our credentials
376         let invalid_sections_count = self.malformed_sections_count + self.encrypted_sections.len();
377 
378         // Put the deserialized sections back into the original ordering for
379         // the returned `V1AdvertisementContents`
380         // (Note: idx is unique, so unstable sort is ok)
381         self.deserialized_sections.sort_unstable_by_key(|(idx, _section)| *idx);
382         let ordered_sections = self.deserialized_sections.into_iter().map(|(_idx, s)| s).collect();
383         V1AdvertisementContents::new(ordered_sections, invalid_sections_count)
384     }
385 }
386 
387 /// The contents of a deserialized and decrypted V1 advertisement.
388 #[derive(Debug, PartialEq, Eq)]
389 pub struct V1AdvertisementContents<'adv, M: MatchedCredential> {
390     sections: ArrayVecOption<V1DeserializedSection<'adv, M>, NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT>,
391     invalid_sections: usize,
392 }
393 
394 impl<'adv, M: MatchedCredential> V1AdvertisementContents<'adv, M> {
new( sections: ArrayVecOption< V1DeserializedSection<'adv, M>, NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT, >, invalid_sections: usize, ) -> Self395     fn new(
396         sections: ArrayVecOption<
397             V1DeserializedSection<'adv, M>,
398             NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT,
399         >,
400         invalid_sections: usize,
401     ) -> Self {
402         Self { sections, invalid_sections }
403     }
404 
405     /// Destructures this V1 advertisement into just the sections
406     /// which could be successfully deserialized and decrypted
into_sections( self, ) -> ArrayVecOption<V1DeserializedSection<'adv, M>, NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT>407     pub fn into_sections(
408         self,
409     ) -> ArrayVecOption<V1DeserializedSection<'adv, M>, NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT> {
410         self.sections
411     }
412 
413     /// The sections that could be successfully deserialized and decrypted
sections(&self) -> impl ExactSizeIterator<Item = &V1DeserializedSection<M>>414     pub fn sections(&self) -> impl ExactSizeIterator<Item = &V1DeserializedSection<M>> {
415         self.sections.iter()
416     }
417 
418     /// The number of sections that could not be parsed or decrypted.
invalid_sections_count(&self) -> usize419     pub fn invalid_sections_count(&self) -> usize {
420         self.invalid_sections
421     }
422 }
423 
424 /// Advertisement content that was either already plaintext or has been decrypted.
425 #[derive(Debug, PartialEq, Eq)]
426 pub enum V1DeserializedSection<'adv, M: MatchedCredential> {
427     /// Section that was plaintext in the original advertisement
428     Plaintext(PlaintextSection<'adv>),
429     /// Section that was ciphertext in the original advertisement, and has been decrypted
430     /// with the credential in the [MatchedCredential]
431     Decrypted(WithMatchedCredential<M, DecryptedSection<'adv>>),
432 }
433 
434 /// The level of integrity protection in an encrypted section
435 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
436 pub enum VerificationMode {
437     /// A symmetric MIC (message integrity code aka message authentication code) was verified.
438     ///
439     /// Since this is a symmetric operation, holders of the key material needed to verify a MIC
440     /// can also forge MICs.
441     Mic,
442     /// An asymmetric signature was verified.
443     ///
444     /// Since this is an asymmetric operation, only the holder of the private key can generate
445     /// signatures, so it offers a stronger level of authenticity protection than [Self::Mic].
446     Signature,
447 }
448 
449 #[derive(PartialEq, Eq, Debug)]
450 pub(crate) struct SectionMic {
451     mic: [u8; Self::CONTENTS_LEN],
452 }
453 
454 impl SectionMic {
455     /// 16-byte MIC
456     pub(crate) const CONTENTS_LEN: usize = 16;
457 }
458 
459 impl From<[u8; 16]> for SectionMic {
from(value: [u8; 16]) -> Self460     fn from(value: [u8; 16]) -> Self {
461         SectionMic { mic: value }
462     }
463 }
464 
465 impl TryFrom<&[u8]> for SectionMic {
466     type Error = TryFromSliceError;
467 
try_from(value: &[u8]) -> Result<Self, Self::Error>468     fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
469         let fixed_bytes: [u8; SectionMic::CONTENTS_LEN] = value.try_into()?;
470         Ok(Self { mic: fixed_bytes })
471     }
472 }
473 
hi_bit_set(b: u8) -> bool474 fn hi_bit_set(b: u8) -> bool {
475     b & 0x80 > 0
476 }
477