1 // Copyright 2023 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 #![allow(clippy::unwrap_used)]
16 
17 extern crate std;
18 
19 use std::{prelude::rust_2021::*, vec};
20 
21 use rand::{
22     distributions::{Distribution, Standard},
23     random,
24     rngs::StdRng,
25     Rng, SeedableRng as _,
26 };
27 
28 use array_view::ArrayView;
29 use crypto_provider::{ed25519, CryptoRng};
30 use crypto_provider_default::CryptoProviderImpl;
31 use np_hkdf::v1_salt::ExtendedV1Salt;
32 
33 use crate::{
34     credential::{book::*, matched::*, v0::*, v1::*, *},
35     extended::{
36         data_elements::GenericDataElement,
37         deserialize::{data_element::DataElement, section::intermediate::PlaintextSection, *},
38         salt::MultiSalt,
39         serialize::*,
40         *,
41     },
42     *,
43 };
44 
45 mod happy_path;
46 
47 mod error_condition;
48 
49 impl<'adv, M: MatchedCredential> V1DeserializedSection<'adv, M> {
as_plaintext_section(&self) -> &PlaintextSection50     fn as_plaintext_section(&self) -> &PlaintextSection {
51         match self {
52             V1DeserializedSection::Plaintext(c) => c,
53             V1DeserializedSection::Decrypted(_) => {
54                 panic!("Casting into invalid enum variant")
55             }
56         }
57     }
58 
as_ciphertext_section(&self) -> &WithMatchedCredential<M, DecryptedSection<'adv>>59     fn as_ciphertext_section(&self) -> &WithMatchedCredential<M, DecryptedSection<'adv>> {
60         match self {
61             V1DeserializedSection::Plaintext(_) => panic!("Casting into invalid enum variant"),
62             V1DeserializedSection::Decrypted(wmc) => wmc,
63         }
64     }
65 }
66 
assert_section_equals( section_config: &SectionConfig, section: &V1DeserializedSection< ReferencedMatchedCredential<MetadataMatchedCredential<Vec<u8>>>, >, )67 fn assert_section_equals(
68     section_config: &SectionConfig,
69     section: &V1DeserializedSection<
70         ReferencedMatchedCredential<MetadataMatchedCredential<Vec<u8>>>,
71     >,
72 ) {
73     match &section_config.identity_kind {
74         IdentityKind::Plaintext => {
75             let plaintext_section = section.as_plaintext_section();
76             assert_eq!(
77                 section_config.data_elements,
78                 plaintext_section
79                     .iter_data_elements()
80                     .map(|de| (&de.unwrap()).into())
81                     .collect::<Vec<_>>()
82             )
83         }
84         IdentityKind::Encrypted { verification_mode, identity } => {
85             let enc_section = section.as_ciphertext_section();
86 
87             let decrypted_metadata = enc_section.decrypt_metadata::<CryptoProviderImpl>().unwrap();
88             assert_eq!(&identity.plaintext_metadata, &decrypted_metadata);
89 
90             let expected_contents =
91                 section_config.data_elements.clone().iter().fold(Vec::new(), |mut buf, de| {
92                     buf.extend_from_slice(de.de_header().serialize().as_slice());
93                     de.write_de_contents(&mut buf).unwrap();
94                     buf
95                 });
96             let contents = enc_section.contents();
97             assert_eq!(&expected_contents, contents.plaintext());
98 
99             assert_eq!(
100                 section_config.data_elements,
101                 contents
102                     .iter_data_elements()
103                     .map(|de| (&de.unwrap()).into())
104                     .collect::<Vec<GenericDataElement>>()
105             );
106             assert_eq!(&identity.identity_token, enc_section.contents().identity_token());
107             assert_eq!(verification_mode, &enc_section.contents().verification_mode());
108         }
109     }
110 }
111 
deser_v1_error<'a, B, P>( arena: DeserializationArena<'a>, adv: &'a [u8], cred_book: &'a B, ) -> AdvDeserializationError where B: CredentialBook<'a>, P: CryptoProvider,112 fn deser_v1_error<'a, B, P>(
113     arena: DeserializationArena<'a>,
114     adv: &'a [u8],
115     cred_book: &'a B,
116 ) -> AdvDeserializationError
117 where
118     B: CredentialBook<'a>,
119     P: CryptoProvider,
120 {
121     let v1_contents = match crate::deserialize_advertisement::<_, P>(arena, adv, cred_book) {
122         Err(e) => e,
123         _ => panic!("Expecting an error!"),
124     };
125     v1_contents
126 }
127 
deser_v1<'adv, B, P>( arena: DeserializationArena<'adv>, adv: &'adv [u8], cred_book: &'adv B, ) -> V1AdvertisementContents<'adv, B::Matched> where B: CredentialBook<'adv>, P: CryptoProvider,128 fn deser_v1<'adv, B, P>(
129     arena: DeserializationArena<'adv>,
130     adv: &'adv [u8],
131     cred_book: &'adv B,
132 ) -> V1AdvertisementContents<'adv, B::Matched>
133 where
134     B: CredentialBook<'adv>,
135     P: CryptoProvider,
136 {
137     crate::deserialize_advertisement::<_, P>(arena, adv, cred_book)
138         .expect("Should be a valid advertisement")
139         .into_v1()
140         .expect("Should be V1")
141 }
142 
build_empty_cred_book() -> PrecalculatedOwnedCredentialBook<EmptyMatchedCredential>143 fn build_empty_cred_book() -> PrecalculatedOwnedCredentialBook<EmptyMatchedCredential> {
144     PrecalculatedOwnedCredentialBook::new(
145         PrecalculatedOwnedCredentialSource::<V0, EmptyMatchedCredential>::new::<CryptoProviderImpl>(
146             Vec::new(),
147         ),
148         PrecalculatedOwnedCredentialSource::<V1, EmptyMatchedCredential>::new::<CryptoProviderImpl>(
149             Vec::new(),
150         ),
151     )
152 }
153 
154 /// Populate a random number of sections with randomly chosen identities and random DEs
fill_plaintext_adv<'a, R: rand::Rng>( rng: &mut R, adv_builder: &mut AdvBuilder, ) -> SectionConfig<'a>155 fn fill_plaintext_adv<'a, R: rand::Rng>(
156     rng: &mut R,
157     adv_builder: &mut AdvBuilder,
158 ) -> SectionConfig<'a> {
159     adv_builder
160         .section_builder(UnencryptedSectionEncoder)
161         .map(|mut s| {
162             let mut sink = Vec::new();
163             // plaintext sections cannot be empty so we need to be sure at least 1 DE is generated
164             let (_, des) = fill_section_random_des::<_, _, 1>(rng, &mut sink, &mut s);
165             s.add_to_advertisement::<CryptoProviderImpl>();
166             SectionConfig::new(IdentityKind::Plaintext, des)
167         })
168         .unwrap()
169 }
170 
171 impl Distribution<VerificationMode> for Standard {
sample<R: Rng + ?Sized>(&self, rng: &mut R) -> VerificationMode172     fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> VerificationMode {
173         match rng.gen_range(0..=2) {
174             0 => VerificationMode::Signature,
175             _ => VerificationMode::Mic,
176         }
177     }
178 }
179 
add_sig_rand_salt_to_adv<'a, R: rand::Rng, C: CryptoProvider, const M: usize>( rng: &mut R, identity: &'a TestIdentity, adv_builder: &mut AdvBuilder, ) -> Result<SectionConfig<'a>, AddSectionError>180 pub(crate) fn add_sig_rand_salt_to_adv<'a, R: rand::Rng, C: CryptoProvider, const M: usize>(
181     rng: &mut R,
182     identity: &'a TestIdentity,
183     adv_builder: &mut AdvBuilder,
184 ) -> Result<SectionConfig<'a>, AddSectionError> {
185     let salt: ExtendedV1Salt = rng.gen::<[u8; 16]>().into();
186     add_sig_with_salt_to_adv::<_, C, M>(rng, identity, adv_builder, salt.into())
187 }
188 
add_sig_with_salt_to_adv<'a, R: rand::Rng, C: CryptoProvider, const M: usize>( rng: &mut R, identity: &'a crate::tests::deser_v1_tests::TestIdentity, adv_builder: &mut AdvBuilder, salt: MultiSalt, ) -> Result<crate::tests::deser_v1_tests::SectionConfig<'a>, AddSectionError>189 fn add_sig_with_salt_to_adv<'a, R: rand::Rng, C: CryptoProvider, const M: usize>(
190     rng: &mut R,
191     identity: &'a crate::tests::deser_v1_tests::TestIdentity,
192     adv_builder: &mut AdvBuilder,
193     salt: MultiSalt,
194 ) -> Result<crate::tests::deser_v1_tests::SectionConfig<'a>, AddSectionError> {
195     let broadcast_cred = identity.broadcast_credential();
196     let salt = match salt {
197         MultiSalt::Short(_) => {
198             panic!("Invalid salt type for signature encrpted adv")
199         }
200         MultiSalt::Extended(e) => e,
201     };
202     adv_builder.section_builder(SignedEncryptedSectionEncoder::new::<C>(salt, &broadcast_cred)).map(
203         |mut s| {
204             let mut sink = Vec::new();
205             let (_, des) = fill_section_random_des::<_, _, M>(rng, &mut sink, &mut s);
206             s.add_to_advertisement::<C>();
207             SectionConfig::new(
208                 IdentityKind::Encrypted {
209                     verification_mode: VerificationMode::Signature,
210                     identity,
211                 },
212                 des,
213             )
214         },
215     )
216 }
217 
add_mic_rand_salt_to_adv<'a, R: rand::Rng, C: CryptoProvider, const M: usize>( rng: &mut R, identity: &'a TestIdentity, adv_builder: &mut AdvBuilder, ) -> Result<SectionConfig<'a>, AddSectionError>218 pub(crate) fn add_mic_rand_salt_to_adv<'a, R: rand::Rng, C: CryptoProvider, const M: usize>(
219     rng: &mut R,
220     identity: &'a TestIdentity,
221     adv_builder: &mut AdvBuilder,
222 ) -> Result<SectionConfig<'a>, AddSectionError> {
223     let salt = if rng.gen_bool(0.5) {
224         MultiSalt::Short(rng.gen::<[u8; 2]>().into())
225     } else {
226         MultiSalt::Extended(rng.gen::<[u8; 16]>().into())
227     };
228 
229     add_mic_with_salt_to_adv::<_, C, M>(rng, identity, adv_builder, salt)
230 }
231 
add_mic_with_salt_to_adv<'a, R: rand::Rng, C: CryptoProvider, const M: usize>( rng: &mut R, identity: &'a TestIdentity, adv_builder: &mut AdvBuilder, salt: MultiSalt, ) -> Result<crate::tests::deser_v1_tests::SectionConfig<'a>, AddSectionError>232 pub(crate) fn add_mic_with_salt_to_adv<'a, R: rand::Rng, C: CryptoProvider, const M: usize>(
233     rng: &mut R,
234     identity: &'a TestIdentity,
235     adv_builder: &mut AdvBuilder,
236     salt: MultiSalt,
237 ) -> Result<crate::tests::deser_v1_tests::SectionConfig<'a>, AddSectionError> {
238     let broadcast_cred = identity.broadcast_credential();
239     adv_builder
240         .section_builder(MicEncryptedSectionEncoder::<_>::new_with_salt::<C>(salt, &broadcast_cred))
241         .map(|mut s| {
242             let mut sink = Vec::new();
243             let (_, des) = fill_section_random_des::<_, _, M>(rng, &mut sink, &mut s);
244             s.add_to_advertisement::<C>();
245             SectionConfig::new(
246                 IdentityKind::Encrypted { verification_mode: VerificationMode::Mic, identity },
247                 des,
248             )
249         })
250 }
251 
252 /// Populate a random number of sections with randomly chosen identities and random DEs
fill_with_encrypted_sections<'a, R: rand::Rng, C: CryptoProvider>( mut rng: &mut R, identities: &'a TestIdentities, adv_builder: &mut AdvBuilder, ) -> Vec<SectionConfig<'a>>253 fn fill_with_encrypted_sections<'a, R: rand::Rng, C: CryptoProvider>(
254     mut rng: &mut R,
255     identities: &'a TestIdentities,
256     adv_builder: &mut AdvBuilder,
257 ) -> Vec<SectionConfig<'a>> {
258     let mut expected = Vec::new();
259     for _ in 0..rng.gen_range(1..=NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT) {
260         let identity = identities.pick_random_identity(&mut rng);
261         let mode: VerificationMode = random();
262         let res = match mode {
263             VerificationMode::Signature => {
264                 add_sig_rand_salt_to_adv::<_, C, 0>(&mut rng, identity, adv_builder)
265             }
266             VerificationMode::Mic => {
267                 add_mic_rand_salt_to_adv::<_, C, 0>(&mut rng, identity, adv_builder)
268             }
269         };
270         match res {
271             Ok(tuple) => expected.push(tuple),
272             Err(_) => {
273                 // couldn't fit that section; maybe another smaller section will fit
274                 continue;
275             }
276         }
277     }
278     expected
279 }
280 
281 #[derive(Clone)]
282 pub(crate) struct TestIdentity {
283     key_seed: [u8; 32],
284     identity_token: V1IdentityToken,
285     private_key: ed25519::PrivateKey,
286     plaintext_metadata: Vec<u8>,
287 }
288 
289 impl TestIdentity {
290     /// Generate a new identity with random crypto material
random<R: rand::Rng, C: CryptoProvider>(rng: &mut R) -> Self291     fn random<R: rand::Rng, C: CryptoProvider>(rng: &mut R) -> Self {
292         Self {
293             key_seed: rng.gen(),
294             identity_token: rng.gen(),
295             private_key: ed25519::PrivateKey::generate::<C::Ed25519>(),
296             // varying length vec of random bytes
297             plaintext_metadata: (0..rng.gen_range(50..200)).map(|_| rng.gen()).collect(),
298         }
299     }
300     /// Returns a (simple, signed) broadcast credential using crypto material from this identity
broadcast_credential(&self) -> V1BroadcastCredential301     fn broadcast_credential(&self) -> V1BroadcastCredential {
302         V1BroadcastCredential::new(self.key_seed, self.identity_token, self.private_key.clone())
303     }
304     /// Returns a discovery credential using crypto material from this identity
discovery_credential<C: CryptoProvider>(&self) -> V1DiscoveryCredential305     fn discovery_credential<C: CryptoProvider>(&self) -> V1DiscoveryCredential {
306         self.broadcast_credential().derive_discovery_credential::<C>()
307     }
308 }
309 
310 pub(crate) struct SectionConfig<'a> {
311     identity_kind: IdentityKind<'a>,
312     data_elements: Vec<GenericDataElement>,
313 }
314 
315 pub(crate) enum IdentityKind<'a> {
316     Plaintext,
317     Encrypted { verification_mode: VerificationMode, identity: &'a TestIdentity },
318 }
319 
320 impl<'a> SectionConfig<'a> {
new(identity_kind: IdentityKind<'a>, data_elements: Vec<GenericDataElement>) -> Self321     pub fn new(identity_kind: IdentityKind<'a>, data_elements: Vec<GenericDataElement>) -> Self {
322         Self { identity_kind, data_elements }
323     }
324 }
325 
326 /// Returns the DEs created in both deserialized form ([DataElement]) and
327 /// input form ([GenericDataElement]) where `M` is the minimum number of DE's
328 /// generated
fill_section_random_des<'adv, R: rand::Rng, I: SectionEncoder, const M: usize>( mut rng: &mut R, sink: &'adv mut Vec<u8>, section_builder: &mut SectionBuilder<&mut AdvBuilder, I>, ) -> (Vec<DataElement<'adv>>, Vec<GenericDataElement>)329 fn fill_section_random_des<'adv, R: rand::Rng, I: SectionEncoder, const M: usize>(
330     mut rng: &mut R,
331     sink: &'adv mut Vec<u8>,
332     section_builder: &mut SectionBuilder<&mut AdvBuilder, I>,
333 ) -> (Vec<DataElement<'adv>>, Vec<GenericDataElement>) {
334     let mut expected_des = vec![];
335     let mut orig_des = vec![];
336     let mut de_ranges = vec![];
337 
338     for _ in 0..rng.gen_range(M..=5) {
339         let de = random_de::<MAX_DE_LEN, _>(&mut rng);
340 
341         let de_clone = de.clone();
342         if section_builder.add_de(|_| de_clone).is_err() {
343             break;
344         }
345 
346         let orig_len = sink.len();
347         de.write_de_contents(sink).unwrap();
348         let contents_len = sink.len() - orig_len;
349         de_ranges.push(orig_len..orig_len + contents_len);
350         orig_des.push(de);
351     }
352 
353     for (index, (de, range)) in orig_des.iter().zip(de_ranges).enumerate() {
354         expected_des.push(DataElement::new(
355             u8::try_from(index).unwrap().into(),
356             de.de_header().de_type,
357             &sink[range],
358         ));
359     }
360     (expected_des, orig_des)
361 }
362 
363 /// generates a random DE, where `N` is the max length size of the DE
random_de<const N: usize, R: rand::Rng>(rng: &mut R) -> GenericDataElement364 fn random_de<const N: usize, R: rand::Rng>(rng: &mut R) -> GenericDataElement {
365     let mut array = [0_u8; MAX_DE_LEN];
366     rng.fill(&mut array[..]);
367     let data: ArrayView<u8, MAX_DE_LEN> =
368         ArrayView::try_from_array(array, rng.gen_range(0..=MAX_DE_LEN)).unwrap();
369     // skip the first few DEs that Google uses
370     GenericDataElement::try_from(rng.gen_range(0_u32..1000).into(), data.as_slice()).unwrap()
371 }
372 
373 #[derive(Clone)]
374 pub(crate) struct TestIdentities(pub(crate) Vec<TestIdentity>);
375 
376 impl TestIdentities {
generate<const N: usize, R: rand::Rng, C: CryptoProvider>( rng: &mut R, ) -> TestIdentities377     pub(crate) fn generate<const N: usize, R: rand::Rng, C: CryptoProvider>(
378         rng: &mut R,
379     ) -> TestIdentities {
380         TestIdentities((0..N).map(|_| TestIdentity::random::<_, C>(rng)).collect::<Vec<_>>())
381     }
382 
pick_random_identity<R: rand::Rng>(&self, rng: &mut R) -> &TestIdentity383     fn pick_random_identity<R: rand::Rng>(&self, rng: &mut R) -> &TestIdentity {
384         let chosen_index = rng.gen_range(0..self.0.len());
385         &self.0[chosen_index]
386     }
387 
build_cred_book<C: CryptoProvider>( &self, ) -> PrecalculatedOwnedCredentialBook<MetadataMatchedCredential<Vec<u8>>>388     pub(crate) fn build_cred_book<C: CryptoProvider>(
389         &self,
390     ) -> PrecalculatedOwnedCredentialBook<MetadataMatchedCredential<Vec<u8>>> {
391         let creds = self
392             .0
393             .iter()
394             .map(|identity| {
395                 let match_data = MetadataMatchedCredential::<Vec<u8>>::encrypt_from_plaintext::<
396                     V1,
397                     CryptoProviderImpl,
398                 >(
399                     &np_hkdf::NpKeySeedHkdf::new(&identity.key_seed),
400                     identity.identity_token,
401                     &identity.plaintext_metadata,
402                 );
403                 let discovery_credential = identity.discovery_credential::<C>();
404                 MatchableCredential { discovery_credential, match_data }
405             })
406             .collect::<Vec<_>>();
407         let cred_source = PrecalculatedOwnedCredentialSource::new::<CryptoProviderImpl>(creds);
408         PrecalculatedOwnedCredentialBook::new(
409             PrecalculatedOwnedCredentialSource::<V0, MetadataMatchedCredential<Vec<u8>>>::new::<
410                 CryptoProviderImpl,
411             >(Vec::new()),
412             cred_source,
413         )
414     }
415 }
416 
deserialize_rand_identities_finds_correct_one<E, F>( build_encoder: F, expected_mode: VerificationMode, ) where E: SectionEncoder, F: Fn(&mut <CryptoProviderImpl as CryptoProvider>::CryptoRng, &V1BroadcastCredential) -> E,417 fn deserialize_rand_identities_finds_correct_one<E, F>(
418     build_encoder: F,
419     expected_mode: VerificationMode,
420 ) where
421     E: SectionEncoder,
422     F: Fn(&mut <CryptoProviderImpl as CryptoProvider>::CryptoRng, &V1BroadcastCredential) -> E,
423 {
424     let mut rng = StdRng::from_entropy();
425     let mut crypto_rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
426 
427     for _ in 0..100 {
428         let identities = TestIdentities::generate::<100, _, CryptoProviderImpl>(&mut rng);
429         let identity = identities.pick_random_identity(&mut rng);
430         let book = identities.build_cred_book::<CryptoProviderImpl>();
431 
432         let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
433 
434         let broadcast_cm = identity.broadcast_credential();
435         let mut section_builder =
436             adv_builder.section_builder(build_encoder(&mut crypto_rng, &broadcast_cm)).unwrap();
437 
438         let mut expected_de_data = vec![];
439         let (expected_des, orig_des) = fill_section_random_des::<_, _, 0>(
440             &mut rng,
441             &mut expected_de_data,
442             &mut section_builder,
443         );
444         section_builder.add_to_advertisement::<CryptoProviderImpl>();
445 
446         let section_config = SectionConfig::new(
447             IdentityKind::Encrypted { verification_mode: expected_mode, identity },
448             orig_des.clone(),
449         );
450 
451         let adv = adv_builder.into_advertisement();
452 
453         let arena = deserialization_arena!();
454         let decrypted_contents = deser_v1::<_, CryptoProviderImpl>(arena, adv.as_slice(), &book);
455         assert_eq!(0, decrypted_contents.invalid_sections_count());
456         let sections = decrypted_contents.into_sections();
457         assert_eq!(1, sections.len());
458 
459         assert_section_equals(&section_config, &sections[0]);
460 
461         // verify data elements match original after collecting them from the section
462         let data_elements =
463             sections[0].as_ciphertext_section().contents().collect_data_elements().unwrap();
464         assert_eq!(expected_des, data_elements);
465     }
466 }
467 
add_plaintext_section<R: rand::Rng>( rng: &mut R, builder: &mut AdvBuilder, ) -> Result<(), AddSectionError>468 fn add_plaintext_section<R: rand::Rng>(
469     rng: &mut R,
470     builder: &mut AdvBuilder,
471 ) -> Result<(), AddSectionError> {
472     builder.section_builder(UnencryptedSectionEncoder).map(|mut sb| {
473         // use an upper bound on the De size to leave room for another section
474         sb.add_de(|_| random_de::<20, _>(rng)).unwrap();
475         sb.add_to_advertisement::<CryptoProviderImpl>();
476     })
477 }
478 
append_mock_encrypted_section(adv: &mut Vec<u8>)479 fn append_mock_encrypted_section(adv: &mut Vec<u8>) {
480     adv.push(0b0000_0001u8); // format
481     adv.extend_from_slice(&[0xAA; 2]); // short salt
482     adv.extend_from_slice(&[0xBB; 16]); // identity token
483     adv.push(3); // payload length
484     adv.extend_from_slice(&[0xCC; 3]); // payload contents
485 }
486