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