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 //! Covers the first half of section parsing before decryption, if relevant, is
16 //! attempted.
17 
18 use crate::{
19     array_vec::ArrayVecOption,
20     extended::{
21         deserialize::{
22             encrypted_section::{
23                 EncryptedSectionContents, MicEncryptedSection, SectionIdentityResolutionContents,
24                 SignatureEncryptedSection,
25             },
26             section::header::{
27                 CiphertextExtendedIdentityToken, EncryptedSectionHeader, SectionHeader,
28             },
29             DataElementParsingIterator, Section, SectionMic,
30         },
31         salt::MultiSalt,
32         NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT, NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT,
33     },
34     header::V1AdvHeader,
35 };
36 use crypto_provider::CryptoProvider;
37 use nom::{branch, bytes, combinator, error, multi};
38 
39 use crate::extended::deserialize::data_element::DataElementParseError;
40 #[cfg(feature = "devtools")]
41 use crate::{
42     credential::v1::V1DiscoveryCryptoMaterial, deserialization_arena::DeserializationArenaAllocator,
43 };
44 #[cfg(feature = "devtools")]
45 use crate::{deserialization_arena::ArenaOutOfSpace, extended::NP_ADV_MAX_SECTION_LEN};
46 #[cfg(feature = "devtools")]
47 use array_view::ArrayView;
48 
49 #[cfg(test)]
50 pub(crate) mod tests;
51 
52 /// Parse into [IntermediateSection]s, exposing the underlying parsing errors.
53 /// Consumes all of `adv_body`.
parse_sections( adv_header: V1AdvHeader, adv_body: &[u8], ) -> Result< ArrayVecOption<IntermediateSection, NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT>, nom::Err<error::Error<&[u8]>>, >54 pub(crate) fn parse_sections(
55     adv_header: V1AdvHeader,
56     adv_body: &[u8],
57 ) -> Result<
58     ArrayVecOption<IntermediateSection, NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT>,
59     nom::Err<error::Error<&[u8]>>,
60 > {
61     combinator::all_consuming(branch::alt((
62         // Public advertisement
63         multi::fold_many_m_n(
64             1,
65             NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT,
66             IntermediateSection::parser_unencrypted_section,
67             ArrayVecOption::default,
68             |mut acc, item| {
69                 acc.push(item);
70                 acc
71             },
72         ),
73         // Encrypted advertisement
74         multi::fold_many_m_n(
75             1,
76             NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT,
77             IntermediateSection::parser_encrypted_with_header(adv_header),
78             ArrayVecOption::default,
79             |mut acc, item| {
80                 acc.push(item);
81                 acc
82             },
83         ),
84     )))(adv_body)
85     .map(|(_rem, sections)| sections)
86 }
87 
88 /// A partially processed section that hasn't been decrypted (if applicable) yet.
89 #[derive(PartialEq, Eq, Debug)]
90 pub(crate) enum IntermediateSection<'a> {
91     /// A section that was not encrypted, e.g. a public identity or no-identity section.
92     Plaintext(PlaintextSection<'a>),
93     /// A section whose contents were encrypted, e.g. a private identity section.
94     Ciphertext(CiphertextSection<'a>),
95 }
96 
97 impl<'a> IntermediateSection<'a> {
parser_unencrypted_section( np_adv_body: &'a [u8], ) -> nom::IResult<&'a [u8], IntermediateSection<'a>>98     fn parser_unencrypted_section(
99         np_adv_body: &'a [u8],
100     ) -> nom::IResult<&'a [u8], IntermediateSection<'a>> {
101         combinator::map_opt(SectionContents::parse, |sc| match sc.header {
102             SectionHeader::Unencrypted => {
103                 Some(IntermediateSection::Plaintext(PlaintextSection::new(sc.contents)))
104             }
105             SectionHeader::Encrypted(_) => None,
106         })(np_adv_body)
107     }
108 
parser_encrypted_with_header( adv_header: V1AdvHeader, ) -> impl Fn(&'a [u8]) -> nom::IResult<&[u8], IntermediateSection>109     pub(crate) fn parser_encrypted_with_header(
110         adv_header: V1AdvHeader,
111     ) -> impl Fn(&'a [u8]) -> nom::IResult<&[u8], IntermediateSection> {
112         move |adv_body| {
113             fn split_at_mic(contents: &[u8]) -> Option<(&[u8], SectionMic)> {
114                 contents.len().checked_sub(SectionMic::CONTENTS_LEN).map(|len_before_mic| {
115                     let (before_mic, mic) = contents.split_at(len_before_mic);
116                     let mic = SectionMic::try_from(mic).expect("MIC length checked above");
117 
118                     (before_mic, mic)
119                 })
120             }
121 
122             fn build_mic_section<'a>(
123                 adv_header: V1AdvHeader,
124                 format_bytes: &'a [u8],
125                 salt: MultiSalt,
126                 token: CiphertextExtendedIdentityToken,
127                 contents_len: u8,
128                 contents: &'a [u8],
129             ) -> Option<IntermediateSection<'a>> {
130                 split_at_mic(contents).map(|(before_mic, mic)| {
131                     IntermediateSection::Ciphertext(CiphertextSection::MicEncrypted(
132                         MicEncryptedSection {
133                             contents: EncryptedSectionContents::new(
134                                 adv_header,
135                                 format_bytes,
136                                 salt,
137                                 token,
138                                 contents_len,
139                                 before_mic,
140                             ),
141                             mic,
142                         },
143                     ))
144                 })
145             }
146 
147             combinator::map_opt(
148                 combinator::map_opt(SectionContents::parse, |sc| match sc.header {
149                     SectionHeader::Unencrypted => None,
150                     SectionHeader::Encrypted(e) => {
151                         Some((sc.format_bytes, sc.contents, sc.contents_len, e))
152                     }
153                 }),
154                 move |(format_bytes, contents, contents_len, header)| match header {
155                     EncryptedSectionHeader::MicShortSalt { salt, token } => build_mic_section(
156                         adv_header,
157                         format_bytes,
158                         salt.into(),
159                         token,
160                         contents_len,
161                         contents,
162                     ),
163                     EncryptedSectionHeader::MicExtendedSalt { salt, token } => build_mic_section(
164                         adv_header,
165                         format_bytes,
166                         salt.into(),
167                         token,
168                         contents_len,
169                         contents,
170                     ),
171                     EncryptedSectionHeader::SigExtendedSalt { salt, token } => {
172                         Some(IntermediateSection::Ciphertext(
173                             CiphertextSection::SignatureEncrypted(SignatureEncryptedSection {
174                                 contents: EncryptedSectionContents::new(
175                                     adv_header,
176                                     format_bytes,
177                                     salt,
178                                     token,
179                                     contents_len,
180                                     contents,
181                                 ),
182                             }),
183                         ))
184                     }
185                 },
186             )(adv_body)
187         }
188     }
189 }
190 
191 /// Components of a section after header decode, but before decryption or DE parsing.
192 ///
193 /// This is just the first stage of parsing sections, followed by [IntermediateSection].
194 #[derive(PartialEq, Eq, Debug)]
195 pub(crate) struct SectionContents<'adv> {
196     /// 1-2 bytes of the format saved for later use in verification
197     pub(crate) format_bytes: &'adv [u8],
198     /// Section header contents which includes salt + identity token
199     pub(crate) header: SectionHeader,
200     /// Contents of the section after the header.
201     /// No validation is performed on the contents.
202     pub(crate) contents: &'adv [u8],
203     /// The length of the contents stored as an u8.
204     pub(crate) contents_len: u8,
205 }
206 
207 impl<'adv> SectionContents<'adv> {
parse(input: &'adv [u8]) -> nom::IResult<&'adv [u8], Self>208     pub(crate) fn parse(input: &'adv [u8]) -> nom::IResult<&'adv [u8], Self> {
209         let (input, (format_bytes, header, contents_len)) = SectionHeader::parse(input)?;
210         let (input, contents) = bytes::complete::take(contents_len)(input)?;
211 
212         Ok((input, Self { format_bytes, header, contents, contents_len }))
213     }
214 }
215 
216 /// A plaintext section deserialized from a V1 advertisement.
217 #[derive(PartialEq, Eq, Debug)]
218 pub struct PlaintextSection<'adv> {
219     contents: &'adv [u8],
220 }
221 
222 impl<'adv> PlaintextSection<'adv> {
new(contents: &'adv [u8]) -> Self223     pub(crate) fn new(contents: &'adv [u8]) -> Self {
224         Self { contents }
225     }
226 }
227 
228 impl<'adv> Section<'adv, DataElementParseError> for PlaintextSection<'adv> {
229     type Iterator = DataElementParsingIterator<'adv>;
230 
iter_data_elements(&self) -> Self::Iterator231     fn iter_data_elements(&self) -> Self::Iterator {
232         DataElementParsingIterator::new(self.contents)
233     }
234 }
235 
236 #[derive(PartialEq, Eq, Debug)]
237 pub(crate) enum CiphertextSection<'a> {
238     SignatureEncrypted(SignatureEncryptedSection<'a>),
239     MicEncrypted(MicEncryptedSection<'a>),
240 }
241 
242 impl<'a> CiphertextSection<'a> {
243     /// Try decrypting into some raw bytes given some raw unsigned crypto-material.
244     #[cfg(feature = "devtools")]
try_resolve_identity_and_decrypt< C: V1DiscoveryCryptoMaterial, P: CryptoProvider, >( &self, allocator: &mut DeserializationArenaAllocator<'a>, crypto_material: &C, ) -> Option<Result<ArrayView<u8,245     pub(crate) fn try_resolve_identity_and_decrypt<
246         C: V1DiscoveryCryptoMaterial,
247         P: CryptoProvider,
248     >(
249         &self,
250         allocator: &mut DeserializationArenaAllocator<'a>,
251         crypto_material: &C,
252     ) -> Option<Result<ArrayView<u8, { NP_ADV_MAX_SECTION_LEN }>, ArenaOutOfSpace>> {
253         match self {
254             CiphertextSection::SignatureEncrypted(x) => {
255                 let identity_resolution_material =
256                     crypto_material.signed_identity_resolution_material::<P>();
257                 x.try_resolve_identity_and_decrypt::<P>(allocator, &identity_resolution_material)
258             }
259             CiphertextSection::MicEncrypted(x) => match x.contents.salt {
260                 MultiSalt::Short(_) => x.try_resolve_short_salt_identity_and_decrypt::<P>(
261                     allocator,
262                     &crypto_material.mic_short_salt_identity_resolution_material::<P>(),
263                 ),
264                 MultiSalt::Extended(_) => x.try_resolve_extended_salt_identity_and_decrypt::<P>(
265                     allocator,
266                     &crypto_material.mic_extended_salt_identity_resolution_material::<P>(),
267                 ),
268             },
269         }
270     }
271 
272     /// Return the data needed to resolve identities.
273     ///
274     /// In the typical case of trying many identities across a few sections,
275     /// these should be calculated once for all relevant sections, then re-used
276     /// for all identity match attempts.
identity_resolution_contents<C: CryptoProvider>( &self, ) -> SectionIdentityResolutionContents277     pub(crate) fn identity_resolution_contents<C: CryptoProvider>(
278         &self,
279     ) -> SectionIdentityResolutionContents {
280         match self {
281             CiphertextSection::SignatureEncrypted(x) => {
282                 x.contents.compute_identity_resolution_contents::<C>()
283             }
284             CiphertextSection::MicEncrypted(x) => {
285                 x.contents.compute_identity_resolution_contents::<C>()
286             }
287         }
288     }
289 }
290