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 §ion_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(§ion_config, §ions[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