• 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 //! The first stage of deserialization: just header data (if any) and the bulk contents of the
16 //! advertisement, with no disaggregation into individual data elements.
17 
18 use crate::header::V0Encoding;
19 use crate::helpers::parse_byte_array;
20 use crate::legacy::deserialize::{
21     AdvDeserializeError, DeIterator, DecryptError, DecryptedAdvContents,
22 };
23 #[cfg(test)]
24 use crate::legacy::deserialize::{DataElementDeserializer, GenericDeIterator};
25 use crate::legacy::{Plaintext, NP_MIN_ADV_CONTENT_LEN};
26 use crypto_provider::CryptoProvider;
27 use ldt_np_adv::V0Salt;
28 use nom::combinator;
29 
30 #[cfg(test)]
31 mod tests;
32 
33 /// The header components, if any, and the bytes that will later be decrypted and/or parsed into DEs.
34 #[derive(Debug, PartialEq, Eq)]
35 pub(crate) enum IntermediateAdvContents<'d> {
36     /// Plaintext advertisements
37     Unencrypted(UnencryptedAdvContents<'d>),
38     /// Ciphertext advertisements
39     Ldt(LdtAdvContents<'d>),
40 }
41 
42 impl<'d> IntermediateAdvContents<'d> {
43     #[cfg(test)]
as_unencrypted(&self) -> Option<&UnencryptedAdvContents<'d>>44     pub(crate) fn as_unencrypted(&self) -> Option<&UnencryptedAdvContents<'d>> {
45         match self {
46             IntermediateAdvContents::Unencrypted(c) => Some(c),
47             IntermediateAdvContents::Ldt(_) => None,
48         }
49     }
50 
51     #[cfg(test)]
as_ldt(&self) -> Option<&LdtAdvContents<'d>>52     pub(crate) fn as_ldt(&self) -> Option<&LdtAdvContents<'d>> {
53         match self {
54             IntermediateAdvContents::Unencrypted(_) => None,
55             IntermediateAdvContents::Ldt(c) => Some(c),
56         }
57     }
58 
59     /// Performs basic structural checks on header and content, but doesn't deserialize DEs or
60     /// decrypt.
deserialize<C: CryptoProvider>( encoding: V0Encoding, input: &[u8], ) -> Result<IntermediateAdvContents<'_>, AdvDeserializeError>61     pub(crate) fn deserialize<C: CryptoProvider>(
62         encoding: V0Encoding,
63         input: &[u8],
64     ) -> Result<IntermediateAdvContents<'_>, AdvDeserializeError> {
65         match encoding {
66             V0Encoding::Unencrypted => {
67                 if input.len() < NP_MIN_ADV_CONTENT_LEN {
68                     return Err(AdvDeserializeError::NoDataElements);
69                 }
70                 Ok(IntermediateAdvContents::Unencrypted(UnencryptedAdvContents { data: input }))
71             }
72             V0Encoding::Ldt => {
73                 let (ciphertext, salt) =
74                     parse_v0_salt(input).map_err(|_| AdvDeserializeError::InvalidStructure)?;
75                 LdtAdvContents::new::<C>(salt, ciphertext)
76                     .ok_or(AdvDeserializeError::InvalidStructure)
77                     .map(IntermediateAdvContents::Ldt)
78             }
79         }
80     }
81 }
82 
83 /// The contents of a plaintext advertisement.
84 #[derive(Debug, PartialEq, Eq)]
85 pub struct UnencryptedAdvContents<'d> {
86     /// Contents of the advertisement after the version header.
87     ///
88     /// Contents are at least [NP_MIN_ADV_CONTENT_LEN].
89     data: &'d [u8],
90 }
91 
92 impl<'d> UnencryptedAdvContents<'d> {
93     /// Returns an iterator over the v0 data elements
data_elements(&self) -> DeIterator<'d, Plaintext>94     pub fn data_elements(&self) -> DeIterator<'d, Plaintext> {
95         DeIterator::new(self.data)
96     }
97 
98     #[cfg(test)]
generic_data_elements<D: DataElementDeserializer>( &self, ) -> GenericDeIterator<Plaintext, D>99     pub(in crate::legacy) fn generic_data_elements<D: DataElementDeserializer>(
100         &self,
101     ) -> GenericDeIterator<Plaintext, D> {
102         GenericDeIterator::new(self.data)
103     }
104 }
105 
106 /// Contents of an encrypted advertisement before decryption.
107 #[derive(Debug, PartialEq, Eq)]
108 pub(crate) struct LdtAdvContents<'d> {
109     /// Salt from the advertisement, converted into a padder.
110     /// Pre-calculated so it's only derived once across multiple decrypt attempts.
111     salt_padder: ldt::XorPadder<{ crypto_provider::aes::BLOCK_SIZE }>,
112     /// The salt instance used for encryption of this advertisement.
113     salt: V0Salt,
114     /// Ciphertext containing the identity token and any data elements.
115     /// Must be a valid length for LDT.
116     ciphertext: &'d [u8],
117 }
118 
119 impl<'d> LdtAdvContents<'d> {
120     /// Returns `None` if `ciphertext` is not a valid LDT-XTS-AES ciphertext length.
new<C: CryptoProvider>(salt: V0Salt, ciphertext: &'d [u8]) -> Option<Self>121     pub(crate) fn new<C: CryptoProvider>(salt: V0Salt, ciphertext: &'d [u8]) -> Option<Self> {
122         if !ldt_np_adv::VALID_INPUT_LEN.contains(&ciphertext.len()) {
123             return None;
124         }
125         Some(Self { salt_padder: ldt_np_adv::salt_padder::<C>(salt), salt, ciphertext })
126     }
127 
128     /// Try decrypting with an identity's LDT cipher and deserializing the resulting data elements.
129     ///
130     /// Returns the decrypted data if decryption and verification succeeded and the resulting DEs could be parsed
131     /// successfully, otherwise `Err`.
try_decrypt<C: CryptoProvider>( &self, cipher: &ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C>, ) -> Result<DecryptedAdvContents, DecryptError>132     pub(crate) fn try_decrypt<C: CryptoProvider>(
133         &self,
134         cipher: &ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C>,
135     ) -> Result<DecryptedAdvContents, DecryptError> {
136         let (identity_token, plaintext) = cipher
137             .decrypt_and_verify(self.ciphertext, &self.salt_padder)
138             .map_err(|_e| DecryptError::DecryptOrVerifyError)?;
139 
140         Ok(DecryptedAdvContents::new(identity_token, self.salt, plaintext))
141     }
142 }
143 
parse_v0_salt(input: &[u8]) -> nom::IResult<&[u8], V0Salt>144 fn parse_v0_salt(input: &[u8]) -> nom::IResult<&[u8], V0Salt> {
145     combinator::map(parse_byte_array::<2>, V0Salt::from)(input)
146 }
147