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 #![allow(clippy::unwrap_used)]
16 
17 extern crate std;
18 
19 use super::super::*;
20 use crate::{
21     deserialization_arena,
22     extended::{
23         data_elements::TxPowerDataElement,
24         deserialize::{
25             encrypted_section::tests::first_section_identity_token,
26             section::intermediate::{
27                 parse_sections, tests::IntermediateSectionExt, CiphertextSection,
28             },
29             DataElement, DataElementParseError, Section,
30         },
31         salt::{ShortV1Salt, SHORT_SALT_LEN},
32         serialize::{
33             AdvBuilder, AdvertisementType, CapacityLimitedVec, MicEncryptedSectionEncoder,
34             WriteDataElement,
35         },
36         V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN,
37         V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN,
38     },
39     shared_data::TxPower,
40     NpVersionHeader,
41 };
42 use crypto_provider::ed25519;
43 use crypto_provider_default::CryptoProviderImpl;
44 use np_hkdf::{v1_salt::EXTENDED_SALT_LEN, DerivedSectionKeys};
45 use sink::Sink;
46 
47 type Ed25519ProviderImpl = <CryptoProviderImpl as CryptoProvider>::Ed25519;
48 
49 #[test]
deserialize_mic_encrypted_correct_keys_extended_salt()50 fn deserialize_mic_encrypted_correct_keys_extended_salt() {
51     deserialize_mic_encrypted_correct_keys(ExtendedV1Salt::from([3; EXTENDED_SALT_LEN]).into())
52 }
53 
54 #[test]
deserialize_mic_encrypted_correct_keys_short_salt()55 fn deserialize_mic_encrypted_correct_keys_short_salt() {
56     deserialize_mic_encrypted_correct_keys(ShortV1Salt::from([3; SHORT_SALT_LEN]).into())
57 }
58 
deserialize_mic_encrypted_correct_keys(salt: MultiSalt)59 fn deserialize_mic_encrypted_correct_keys(salt: MultiSalt) {
60     let identity_token = V1IdentityToken::from([1; 16]);
61     let key_seed = [2; 32];
62     let broadcast_cm = V1BroadcastCredential::new(
63         key_seed,
64         identity_token,
65         ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
66     );
67 
68     let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
69     let mut section_builder = adv_builder
70         .section_builder(MicEncryptedSectionEncoder::<_>::new::<CryptoProviderImpl>(
71             salt,
72             &broadcast_cm,
73         ))
74         .unwrap();
75 
76     let txpower_de = TxPowerDataElement::from(TxPower::try_from(5).unwrap());
77     section_builder.add_de(|_| txpower_de.clone()).unwrap();
78     section_builder.add_to_advertisement::<CryptoProviderImpl>();
79     let adv = adv_builder.into_advertisement();
80 
81     let (remaining, header) = NpVersionHeader::parse(adv.as_slice()).unwrap();
82 
83     let adv_header = if let NpVersionHeader::V1(h) = header {
84         h
85     } else {
86         panic!("incorrect header");
87     };
88 
89     let sections = parse_sections(adv_header, remaining).unwrap();
90     assert_eq!(1, sections.len());
91 
92     let section = sections.into_iter().next().unwrap();
93     let enc_section = section.as_ciphertext().unwrap();
94 
95     let contents = if let CiphertextSection::MicEncrypted(contents) = &enc_section {
96         contents
97     } else {
98         panic!("incorrect flavor");
99     };
100 
101     let private_key = ed25519::PrivateKey::generate::<Ed25519ProviderImpl>();
102     // deserializing to Section works
103     let discovery_credential = V1BroadcastCredential::new(key_seed, identity_token, private_key)
104         .derive_discovery_credential::<CryptoProviderImpl>();
105 
106     let arena = deserialization_arena!();
107     let mut allocator = arena.into_allocator();
108     let section = contents
109         .try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
110             &mut allocator,
111             &discovery_credential,
112         )
113         .unwrap();
114 
115     assert_eq!(
116         DecryptedSection::new(
117             VerificationMode::Mic,
118             salt,
119             identity_token,
120             &[txpower_de.de_header().serialize().as_slice(), &[5],].concat(),
121         ),
122         section
123     );
124     let data_elements = section.collect_data_elements().unwrap();
125     assert_eq!(data_elements, &[DataElement::new(0.into(), 0x05_u8.into(), &[5])]);
126 
127     let (_header, contents_bytes) =
128         remaining.split_at(1 + 1 + salt.as_slice().len() + V1_IDENTITY_TOKEN_LEN);
129     assert_eq!(
130         &MicEncryptedSection {
131             contents: EncryptedSectionContents {
132                 adv_header,
133                 format_bytes: &[match salt {
134                     MultiSalt::Short(_) => {
135                         V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN
136                     }
137                     MultiSalt::Extended(_) => {
138                         V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN
139                     }
140                 }],
141                 salt,
142                 identity_token: first_section_identity_token(salt, remaining),
143                 section_contents: &contents_bytes
144                     [..contents_bytes.len() - SectionMic::CONTENTS_LEN],
145                 total_section_contents_len: contents_bytes.len().try_into().unwrap(),
146             },
147             mic: SectionMic {
148                 mic: contents_bytes[contents_bytes.len() - SectionMic::CONTENTS_LEN..]
149                     .try_into()
150                     .unwrap()
151             },
152         },
153         contents
154     );
155 
156     // plaintext is correct
157     {
158         let identity_resolution_contents =
159             contents.contents.compute_identity_resolution_contents::<CryptoProviderImpl>();
160         let identity_match = identity_resolution_contents
161             .try_match::<CryptoProviderImpl>(&match salt {
162                 MultiSalt::Short(_) => discovery_credential
163                     .mic_short_salt_identity_resolution_material::<CryptoProviderImpl>()
164                     .into_raw_resolution_material(),
165                 MultiSalt::Extended(_) => discovery_credential
166                     .mic_extended_salt_identity_resolution_material::<CryptoProviderImpl>()
167                     .into_raw_resolution_material(),
168             })
169             .unwrap();
170         let arena = deserialization_arena!();
171         let mut allocator = arena.into_allocator();
172         let decrypted = contents
173             .contents
174             .decrypt_ciphertext::<CryptoProviderImpl>(&mut allocator, identity_match)
175             .unwrap();
176 
177         let mut expected = Vec::new();
178         // battery de
179         expected.extend_from_slice(txpower_de.clone().de_header().serialize().as_slice());
180         let _ = txpower_de.write_de_contents(&mut expected);
181 
182         assert_eq!(identity_token, decrypted.identity_token);
183         assert_eq!(&expected, decrypted.plaintext_contents);
184     }
185 }
186 
187 #[test]
deserialize_mic_encrypted_short_salt_incorrect_aes_key_error()188 fn deserialize_mic_encrypted_short_salt_incorrect_aes_key_error() {
189     // bad aes key -> bad metadata key plaintext
190     do_bad_deserialize_params::<CryptoProviderImpl>(
191         ShortV1Salt::from([3; SHORT_SALT_LEN]).into(),
192         IdentityResolutionOrDeserializationError::IdentityMatchingError,
193         |cm| {
194             cm.mic_short_salt_identity_resolution_material
195                 .as_mut_raw_resolution_material()
196                 .aes_key = [0xFF; 16].into();
197         },
198     );
199 }
200 
201 #[test]
deserialize_mic_encrypted_short_salt_incorrect_identity_token_hmac_key_error()202 fn deserialize_mic_encrypted_short_salt_incorrect_identity_token_hmac_key_error() {
203     // bad metadata key hmac key -> bad calculated metadata key mac
204     do_bad_deserialize_params::<CryptoProviderImpl>(
205         ShortV1Salt::from([3; SHORT_SALT_LEN]).into(),
206         IdentityResolutionOrDeserializationError::IdentityMatchingError,
207         |cm| {
208             cm.mic_short_salt_identity_resolution_material
209                 .as_mut_raw_resolution_material()
210                 .identity_token_hmac_key = [0xFF; 32];
211         },
212     );
213 }
214 
215 #[test]
deserialize_mic_encrypted_short_salt_incorrect_mic_hmac_key_error()216 fn deserialize_mic_encrypted_short_salt_incorrect_mic_hmac_key_error() {
217     // bad mic hmac key -> bad calculated mic
218     do_bad_deserialize_params::<CryptoProviderImpl>(
219         ShortV1Salt::from([3; SHORT_SALT_LEN]).into(),
220         MicVerificationError::MicMismatch.into(),
221         |cm| {
222             cm.mic_short_salt_verification_material.mic_hmac_key = [0xFF; 32];
223         },
224     );
225 }
226 
227 #[test]
deserialize_mic_encrypted_short_salt_incorrect_expected_identity_token_hmac_error()228 fn deserialize_mic_encrypted_short_salt_incorrect_expected_identity_token_hmac_error() {
229     // bad expected metadata key mac
230     do_bad_deserialize_params::<CryptoProviderImpl>(
231         ShortV1Salt::from([3; SHORT_SALT_LEN]).into(),
232         IdentityResolutionOrDeserializationError::IdentityMatchingError,
233         |cm| {
234             cm.mic_short_salt_identity_resolution_material
235                 .as_mut_raw_resolution_material()
236                 .expected_identity_token_hmac = [0xFF; 32];
237         },
238     );
239 }
240 
241 #[test]
deserialize_mic_encrypted_extended_salt_incorrect_aes_key_error()242 fn deserialize_mic_encrypted_extended_salt_incorrect_aes_key_error() {
243     // bad aes key -> bad metadata key plaintext
244     do_bad_deserialize_params::<CryptoProviderImpl>(
245         ExtendedV1Salt::from([3; EXTENDED_SALT_LEN]).into(),
246         IdentityResolutionOrDeserializationError::IdentityMatchingError,
247         |cm| {
248             cm.mic_extended_salt_identity_resolution_material
249                 .as_mut_raw_resolution_material()
250                 .aes_key = [0xFF; 16].into();
251         },
252     );
253 }
254 
255 #[test]
deserialize_mic_encrypted_extended_salt_incorrect_identity_token_hmac_key_error()256 fn deserialize_mic_encrypted_extended_salt_incorrect_identity_token_hmac_key_error() {
257     // bad metadata key hmac key -> bad calculated metadata key mac
258     do_bad_deserialize_params::<CryptoProviderImpl>(
259         ExtendedV1Salt::from([3; EXTENDED_SALT_LEN]).into(),
260         IdentityResolutionOrDeserializationError::IdentityMatchingError,
261         |cm| {
262             cm.mic_extended_salt_identity_resolution_material
263                 .as_mut_raw_resolution_material()
264                 .identity_token_hmac_key = [0xFF; 32];
265         },
266     );
267 }
268 
269 #[test]
deserialize_mic_encrypted_extended_salt_incorrect_mic_hmac_key_error()270 fn deserialize_mic_encrypted_extended_salt_incorrect_mic_hmac_key_error() {
271     // bad mic hmac key -> bad calculated mic
272     do_bad_deserialize_params::<CryptoProviderImpl>(
273         ExtendedV1Salt::from([3; EXTENDED_SALT_LEN]).into(),
274         MicVerificationError::MicMismatch.into(),
275         |cm| {
276             cm.mic_extended_salt_verification_material.mic_hmac_key = [0xFF; 32];
277         },
278     );
279 }
280 
281 #[test]
deserialize_mic_encrypted_extended_salt_incorrect_expected_identity_token_hmac_error()282 fn deserialize_mic_encrypted_extended_salt_incorrect_expected_identity_token_hmac_error() {
283     // bad expected metadata key mac
284     do_bad_deserialize_params::<CryptoProviderImpl>(
285         ExtendedV1Salt::from([3; EXTENDED_SALT_LEN]).into(),
286         IdentityResolutionOrDeserializationError::IdentityMatchingError,
287         |cm| {
288             cm.mic_extended_salt_identity_resolution_material
289                 .as_mut_raw_resolution_material()
290                 .expected_identity_token_hmac = [0xFF; 32];
291         },
292     );
293 }
294 
295 #[test]
deserialize_mic_encrypted_extended_salt_incorrect_salt_error()296 fn deserialize_mic_encrypted_extended_salt_incorrect_salt_error() {
297     // bad salt -> bad iv -> bad metadata key plaintext
298     do_bad_deserialize_tampered(
299         ExtendedV1Salt::from([3; EXTENDED_SALT_LEN]).into(),
300         DeserializeError::IdentityResolutionOrDeserializationError(
301             IdentityResolutionOrDeserializationError::IdentityMatchingError,
302         ),
303         |_| {},
304         // replace the extended salt bytes
305         |adv| adv[2..18].fill(0xFF),
306     );
307 }
308 
309 #[test]
deserialize_mic_encrypted_extended_salt_de_that_wont_parse()310 fn deserialize_mic_encrypted_extended_salt_de_that_wont_parse() {
311     // add an extra byte to the section, leading it to try to parse a DE that doesn't exist
312     do_bad_deserialize_tampered(
313         ExtendedV1Salt::from([3; EXTENDED_SALT_LEN]).into(),
314         DeserializeError::DataElementParseError(DataElementParseError::UnexpectedDataAfterEnd),
315         |sec| sec.try_push(0xFF).unwrap(),
316         |_| {},
317     );
318 }
319 
320 #[test]
deserialize_mic_encrypted_extended_salt_tampered_mic_error()321 fn deserialize_mic_encrypted_extended_salt_tampered_mic_error() {
322     // flip a bit in the first MIC byte
323     do_bad_deserialize_tampered(
324         ExtendedV1Salt::from([3; EXTENDED_SALT_LEN]).into(),
325         DeserializeError::IdentityResolutionOrDeserializationError(
326             MicVerificationError::MicMismatch.into(),
327         ),
328         |_| {},
329         |adv| {
330             let mic_start = adv.len() - 16;
331             adv[mic_start] ^= 0x01
332         },
333     );
334 }
335 
336 #[test]
deserialize_mic_encrypted_extended_salt_tampered_payload_error()337 fn deserialize_mic_encrypted_extended_salt_tampered_payload_error() {
338     // flip the last payload bit
339     do_bad_deserialize_tampered(
340         ExtendedV1Salt::from([3; EXTENDED_SALT_LEN]).into(),
341         DeserializeError::IdentityResolutionOrDeserializationError(
342             MicVerificationError::MicMismatch.into(),
343         ),
344         |_| {},
345         |adv| {
346             let before_mic = adv.len() - 17;
347             adv[before_mic] ^= 0x01
348         },
349     );
350 }
351 #[test]
deserialize_mic_encrypted_short_salt_incorrect_salt_error()352 fn deserialize_mic_encrypted_short_salt_incorrect_salt_error() {
353     // bad salt -> bad iv -> bad metadata key plaintext
354     do_bad_deserialize_tampered(
355         ShortV1Salt::from([3; SHORT_SALT_LEN]).into(),
356         DeserializeError::IdentityResolutionOrDeserializationError(
357             IdentityResolutionOrDeserializationError::IdentityMatchingError,
358         ),
359         |_| {},
360         // replace the extended salt bytes
361         |adv| adv[2..4].fill(0xFF),
362     );
363 }
364 
365 #[test]
deserialize_mic_encrypted_short_salt_de_that_wont_parse()366 fn deserialize_mic_encrypted_short_salt_de_that_wont_parse() {
367     // add an extra byte to the section, leading it to try to parse a DE that doesn't exist
368     do_bad_deserialize_tampered(
369         ShortV1Salt::from([3; SHORT_SALT_LEN]).into(),
370         DeserializeError::DataElementParseError(DataElementParseError::UnexpectedDataAfterEnd),
371         |sec| sec.try_push(0xFF).unwrap(),
372         |_| {},
373     );
374 }
375 
376 #[test]
deserialize_mic_encrypted_short_salt_tampered_mic_error()377 fn deserialize_mic_encrypted_short_salt_tampered_mic_error() {
378     // flip a bit in the first MIC byte
379     do_bad_deserialize_tampered(
380         ShortV1Salt::from([3; SHORT_SALT_LEN]).into(),
381         DeserializeError::IdentityResolutionOrDeserializationError(
382             MicVerificationError::MicMismatch.into(),
383         ),
384         |_| {},
385         |adv| {
386             let mic_start = adv.len() - 16;
387             adv[mic_start] ^= 0x01
388         },
389     );
390 }
391 
392 #[test]
deserialize_mic_encrypted_short_salt_tampered_payload_error()393 fn deserialize_mic_encrypted_short_salt_tampered_payload_error() {
394     // flip the last payload bit
395     do_bad_deserialize_tampered(
396         ShortV1Salt::from([3; SHORT_SALT_LEN]).into(),
397         DeserializeError::IdentityResolutionOrDeserializationError(
398             MicVerificationError::MicMismatch.into(),
399         ),
400         |_| {},
401         |adv| {
402             let before_mic = adv.len() - 17;
403             adv[before_mic] ^= 0x01
404         },
405     );
406 }
407 
408 #[test]
arena_out_of_space_on_mic_verify()409 fn arena_out_of_space_on_mic_verify() {
410     let identity_token = V1IdentityToken::from([1; 16]);
411     let key_seed = [2; 32];
412     let section_salt = ExtendedV1Salt::from([3; 16]);
413     let broadcast_cm = V1BroadcastCredential::new(
414         key_seed,
415         identity_token,
416         ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
417     );
418 
419     let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
420     let mut section_builder = adv_builder
421         .section_builder(MicEncryptedSectionEncoder::<_>::new::<CryptoProviderImpl>(
422             section_salt,
423             &broadcast_cm,
424         ))
425         .unwrap();
426 
427     let txpower_de = TxPowerDataElement::from(TxPower::try_from(5).unwrap());
428     section_builder.add_de(|_| txpower_de.clone()).unwrap();
429     section_builder.add_to_advertisement::<CryptoProviderImpl>();
430     let adv = adv_builder.into_advertisement();
431 
432     let (remaining, header) = NpVersionHeader::parse(adv.as_slice()).unwrap();
433 
434     let adv_header = if let NpVersionHeader::V1(h) = header {
435         h
436     } else {
437         panic!("incorrect header");
438     };
439 
440     let sections = parse_sections(adv_header, remaining).unwrap();
441     assert_eq!(1, sections.len());
442 
443     let section = sections.into_iter().next().unwrap();
444     let enc_section = section.as_ciphertext().unwrap();
445 
446     let contents = if let CiphertextSection::MicEncrypted(contents) = &enc_section {
447         contents
448     } else {
449         panic!("incorrect flavor");
450     };
451 
452     let private_key = ed25519::PrivateKey::generate::<Ed25519ProviderImpl>();
453     // deserializing to Section works
454     let discovery_credential = V1BroadcastCredential::new(key_seed, identity_token, private_key)
455         .derive_discovery_credential::<CryptoProviderImpl>();
456 
457     let arena = deserialization_arena!();
458     let mut allocator = arena.into_allocator();
459     let _ = allocator.allocate(250).unwrap();
460     let res = contents.try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
461         &mut allocator,
462         &discovery_credential,
463     );
464     assert_eq!(
465         Err(IdentityResolutionOrDeserializationError::DeserializationError(
466             DeserializationError::ArenaOutOfSpace
467         )),
468         res
469     );
470 }
471 
472 /// Attempt a decryption that will fail when using the provided parameters for decryption only.
473 /// `None` means use the correct value for that parameter.
do_bad_deserialize_params<C: CryptoProvider>( salt: MultiSalt, error: IdentityResolutionOrDeserializationError<MicVerificationError>, mut mangle_crypto_material: impl FnMut(&mut PrecalculatedV1DiscoveryCryptoMaterial), )474 fn do_bad_deserialize_params<C: CryptoProvider>(
475     salt: MultiSalt,
476     error: IdentityResolutionOrDeserializationError<MicVerificationError>,
477     mut mangle_crypto_material: impl FnMut(&mut PrecalculatedV1DiscoveryCryptoMaterial),
478 ) {
479     let identity_token = V1IdentityToken([1; 16]);
480     let key_seed = [2; 32];
481     let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&key_seed);
482 
483     let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
484 
485     let broadcast_cm = V1BroadcastCredential::new(
486         key_seed,
487         identity_token,
488         ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
489     );
490 
491     let mut section_builder = adv_builder
492         .section_builder(MicEncryptedSectionEncoder::<_>::new::<CryptoProviderImpl>(
493             salt,
494             &broadcast_cm,
495         ))
496         .unwrap();
497 
498     section_builder.add_de(|_| TxPowerDataElement::from(TxPower::try_from(7).unwrap())).unwrap();
499 
500     section_builder.add_to_advertisement::<CryptoProviderImpl>();
501 
502     let adv = adv_builder.into_advertisement();
503 
504     let (remaining, header) = NpVersionHeader::parse(adv.as_slice()).unwrap();
505 
506     let v1_header = if let NpVersionHeader::V1(h) = header {
507         h
508     } else {
509         panic!("incorrect header");
510     };
511 
512     let sections = parse_sections(v1_header, remaining).unwrap();
513     assert_eq!(1, sections.len());
514 
515     let section = sections.into_iter().next().unwrap();
516     let enc_section = section.as_ciphertext().unwrap();
517     let contents = if let CiphertextSection::MicEncrypted(contents) = &enc_section {
518         contents
519     } else {
520         panic!("incorrect flavor");
521     };
522 
523     // start with correct crypto material
524     let mut crypto_material = V1DiscoveryCredential::new(
525         key_seed,
526         key_seed_hkdf
527             .v1_mic_short_salt_keys()
528             .identity_token_hmac_key()
529             .calculate_hmac::<C>(&identity_token.0),
530         key_seed_hkdf
531             .v1_mic_extended_salt_keys()
532             .identity_token_hmac_key()
533             .calculate_hmac::<C>(&identity_token.0),
534         key_seed_hkdf
535             .v1_signature_keys()
536             .identity_token_hmac_key()
537             .calculate_hmac::<C>(&identity_token.0),
538         crypto_provider::ed25519::PrivateKey::generate::<C::Ed25519>()
539             .derive_public_key::<C::Ed25519>(),
540     )
541     .to_precalculated::<C>();
542 
543     // then break it per the test case
544     mangle_crypto_material(&mut crypto_material);
545 
546     assert_eq!(
547         error,
548         contents
549             .try_resolve_identity_and_deserialize::<C>(
550                 &mut deserialization_arena!().into_allocator(),
551                 &crypto_material
552             )
553             .unwrap_err()
554     );
555 }
556 
557 #[derive(Debug, PartialEq)]
558 enum DeserializeError {
559     IdentityResolutionOrDeserializationError(
560         IdentityResolutionOrDeserializationError<MicVerificationError>,
561     ),
562     DataElementParseError(DataElementParseError),
563 }
564 
do_bad_deserialize_tampered( salt: MultiSalt, expected_error: DeserializeError, mangle_section: impl Fn(&mut CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>), mangle_adv: impl Fn(&mut Vec<u8>), )565 fn do_bad_deserialize_tampered(
566     salt: MultiSalt,
567     expected_error: DeserializeError,
568     mangle_section: impl Fn(&mut CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>),
569     mangle_adv: impl Fn(&mut Vec<u8>),
570 ) {
571     let metadata_key = V1IdentityToken([1; 16]);
572     let key_seed = [2; 32];
573 
574     let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
575 
576     let broadcast_cm = V1BroadcastCredential::new(
577         key_seed,
578         metadata_key,
579         ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
580     );
581 
582     let mut section_builder = adv_builder
583         .section_builder(MicEncryptedSectionEncoder::<_>::new::<CryptoProviderImpl>(
584             salt,
585             &broadcast_cm,
586         ))
587         .unwrap();
588 
589     section_builder.add_de(|_| TxPowerDataElement::from(TxPower::try_from(7).unwrap())).unwrap();
590 
591     mangle_section(&mut section_builder.section);
592 
593     section_builder.add_to_advertisement::<CryptoProviderImpl>();
594 
595     let adv = adv_builder.into_advertisement();
596     let mut adv_mut = adv.as_slice().to_vec();
597     mangle_adv(&mut adv_mut);
598 
599     let (remaining, header) = NpVersionHeader::parse(&adv_mut).unwrap();
600 
601     let v1_header = if let NpVersionHeader::V1(h) = header {
602         h
603     } else {
604         panic!("incorrect header");
605     };
606 
607     let sections = parse_sections(v1_header, remaining).unwrap();
608     assert_eq!(1, sections.len());
609 
610     let section = sections.into_iter().next().unwrap();
611     let enc_section = section.as_ciphertext().unwrap();
612     let contents = if let CiphertextSection::MicEncrypted(contents) = &enc_section {
613         contents
614     } else {
615         panic!("incorrect flavor");
616     };
617 
618     // generate a random key pair since we need _some_ public key in our discovery
619     // credential, even if it winds up going unused
620     let private_key = ed25519::PrivateKey::generate::<Ed25519ProviderImpl>();
621 
622     let discovery_credential = V1BroadcastCredential::new(key_seed, metadata_key, private_key)
623         .derive_discovery_credential::<CryptoProviderImpl>();
624 
625     match contents.try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
626         &mut deserialization_arena!().into_allocator(),
627         &discovery_credential,
628     ) {
629         Ok(section) => {
630             assert_eq!(
631                 expected_error,
632                 DeserializeError::DataElementParseError(
633                     section.collect_data_elements().unwrap_err()
634                 )
635             );
636         }
637         Err(e) => assert_eq!(
638             expected_error,
639             DeserializeError::IdentityResolutionOrDeserializationError(e),
640         ),
641     };
642 }
643