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