• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(
16     missing_docs,
17     unused_results,
18     clippy::unwrap_used,
19     clippy::expect_used,
20     clippy::indexing_slicing,
21     clippy::panic
22 )]
23 
24 use criterion::{black_box, criterion_group, criterion_main, Bencher, Criterion};
25 use crypto_provider::{ed25519, CryptoProvider, CryptoRng};
26 use crypto_provider_default::CryptoProviderImpl;
27 use ldt_np_adv::{V0IdentityToken, V0Salt, V0_IDENTITY_TOKEN_LEN};
28 
29 use np_adv::credential::matched::EmptyMatchedCredential;
30 use np_adv::deserialization_arena;
31 use np_adv::extended::serialize::AdvertisementType;
32 use np_adv::extended::V1IdentityToken;
33 use np_adv::legacy::serialize::UnencryptedEncoder;
34 use np_adv::{
35     credential::{book::*, v0::*, v1::*, *},
36     deserialize_advertisement,
37     extended::{
38         data_elements::{GenericDataElement, TxPowerDataElement},
39         deserialize::VerificationMode,
40         serialize::{
41             AdvBuilder as ExtendedAdvBuilder, MicEncryptedSectionEncoder, SectionBuilder,
42             SectionEncoder, SignedEncryptedSectionEncoder, UnencryptedSectionEncoder,
43         },
44     },
45     legacy::{
46         data_elements::actions::{ActionBits, ActionsDataElement},
47         serialize::{AdvBuilder as LegacyAdvBuilder, LdtEncoder},
48     },
49     shared_data::TxPower,
50 };
51 use np_hkdf::{DerivedSectionKeys, NpKeySeedHkdf};
52 use rand::{Rng as _, SeedableRng as _};
53 use strum::IntoEnumIterator;
54 
deser_adv_v1_encrypted(c: &mut Criterion)55 pub fn deser_adv_v1_encrypted(c: &mut Criterion) {
56     let mut crypto_rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
57 
58     for crypto_type in CryptoMaterialType::iter() {
59         for &identity_type in &[VerificationMode::Mic, VerificationMode::Signature] {
60             for &num_identities in &[10, 100, 1000] {
61                 for &num_sections in &[1, 2] {
62                     // measure worst-case performance -- the correct identities will be the last
63                     // num_sections of the identities to be tried
64                     c.bench_function(
65                         &format!(
66                             "Deser V1 encrypted: crypto={crypto_type:?}/mode={identity_type:?}/ids={num_identities}/sections={num_sections}"
67                         ),
68                         |b| {
69                             let identities = (0..num_identities)
70                                 .map(|_| V1Identity::random::<CryptoProviderImpl>(&mut crypto_rng))
71                                 .collect::<Vec<_>>();
72 
73                             let mut adv_builder = ExtendedAdvBuilder::new(AdvertisementType::Encrypted);
74 
75                             // take the first n identities, one section per identity
76                             for identity in identities.iter().take(num_sections) {
77                                 let broadcast_cm = V1BroadcastCredential::new(
78                                     identity.key_seed,
79                                     identity.identity_token,
80                                     identity.private_key.clone(),
81                                 );
82                                 match identity_type {
83                                     VerificationMode::Mic => {
84                                         let mut sb = adv_builder
85                                             .section_builder(MicEncryptedSectionEncoder::<_>::new_random_salt::<CryptoProviderImpl>(
86                                                 &mut crypto_rng,
87                                                 &broadcast_cm,
88                                             ))
89                                             .unwrap();
90 
91                                         add_des(&mut sb);
92                                         sb.add_to_advertisement::<CryptoProviderImpl>();
93                                     }
94                                     VerificationMode::Signature => {
95                                         let mut sb = adv_builder
96                                             .section_builder(SignedEncryptedSectionEncoder::new_random_salt::<CryptoProviderImpl>(
97                                                 &mut crypto_rng,
98                                                 &broadcast_cm,
99                                             ))
100                                             .unwrap();
101 
102                                         add_des(&mut sb);
103                                         sb.add_to_advertisement::<CryptoProviderImpl>();
104                                     }
105                                 }
106                             }
107 
108                             let adv = adv_builder.into_advertisement();
109 
110                             run_with_v1_creds::<
111                                 CryptoProviderImpl
112                             >(
113                                 b, crypto_type, identities, adv.as_slice(),
114                             )
115                         },
116                     );
117                 }
118             }
119         }
120     }
121 }
122 
deser_adv_v1_plaintext(c: &mut Criterion)123 pub fn deser_adv_v1_plaintext(c: &mut Criterion) {
124     c.bench_function("Deser V1 plaintext: sections=1", |b| {
125         let mut adv_builder = ExtendedAdvBuilder::new(AdvertisementType::Plaintext);
126 
127         let mut sb = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
128 
129         add_des(&mut sb);
130         sb.add_to_advertisement::<CryptoProviderImpl>();
131 
132         let adv = adv_builder.into_advertisement();
133 
134         run_with_v1_creds::<CryptoProviderImpl>(
135             b,
136             CryptoMaterialType::MinFootprint,
137             vec![],
138             adv.as_slice(),
139         )
140     });
141 }
142 
deser_adv_v0_encrypted(c: &mut Criterion)143 pub fn deser_adv_v0_encrypted(c: &mut Criterion) {
144     let mut rng = rand::rngs::StdRng::from_entropy();
145     for crypto_type in CryptoMaterialType::iter() {
146         for &num_identities in &[10, 100, 1000] {
147             // measure worst-case performance -- the correct identities will be the last
148             // num_sections of the identities to be tried
149             c.bench_function(
150                 &format!("Deser V0 encrypted: crypto={crypto_type:?}/ids={num_identities}"),
151                 |b| {
152                     let identities = (0..num_identities)
153                         .map(|_| V0Identity::random(&mut rng))
154                         .collect::<Vec<_>>();
155 
156                     let identity = &identities[0];
157 
158                     let broadcast_cm =
159                         V0BroadcastCredential::new(identity.key_seed, identity.identity_token);
160 
161                     let mut adv_builder =
162                         LegacyAdvBuilder::new(LdtEncoder::<CryptoProviderImpl>::new(
163                             V0Salt::from(rng.gen::<[u8; 2]>()),
164                             &broadcast_cm,
165                         ));
166 
167                     let action_bits = ActionBits::default();
168                     adv_builder.add_data_element(ActionsDataElement::from(action_bits)).unwrap();
169 
170                     let adv = adv_builder.into_advertisement().unwrap();
171 
172                     run_with_v0_creds::<CryptoProviderImpl>(
173                         b,
174                         crypto_type,
175                         identities,
176                         adv.as_slice(),
177                     )
178                 },
179             );
180         }
181     }
182 }
183 
deser_adv_v0_plaintext(c: &mut Criterion)184 pub fn deser_adv_v0_plaintext(c: &mut Criterion) {
185     let mut adv_builder = LegacyAdvBuilder::new(UnencryptedEncoder);
186 
187     let action_bits = ActionBits::default();
188     adv_builder.add_data_element(ActionsDataElement::from(action_bits)).unwrap();
189     let adv = adv_builder.into_advertisement().unwrap();
190 
191     let cred_book = CredentialBookBuilder::<EmptyMatchedCredential>::build_cached_slice_book::<
192         0,
193         0,
194         CryptoProviderImpl,
195     >(&[], &[]);
196 
197     for &num_advs in &[1, 10, 100, 1000] {
198         c.bench_function(
199             format!("Deser V0 plaintext with {num_advs} advertisements").as_str(),
200             |b| {
201                 b.iter(|| {
202                     for _ in 0..num_advs {
203                         black_box(
204                             deserialize_advertisement::<_, CryptoProviderImpl>(
205                                 deserialization_arena!(),
206                                 black_box(adv.as_slice()),
207                                 black_box(&cred_book),
208                             )
209                             .expect("Should succeed"),
210                         );
211                     }
212                 })
213             },
214         );
215     }
216 }
217 
218 /// Benchmark decrypting a V0 advertisement with credentials built from the reversed list of
219 /// identities
run_with_v0_creds<C>( b: &mut Bencher, crypto_material_type: CryptoMaterialType, identities: Vec<V0Identity>, adv: &[u8], ) where C: CryptoProvider,220 fn run_with_v0_creds<C>(
221     b: &mut Bencher,
222     crypto_material_type: CryptoMaterialType,
223     identities: Vec<V0Identity>,
224     adv: &[u8],
225 ) where
226     C: CryptoProvider,
227 {
228     let mut creds = identities
229         .into_iter()
230         .map(|identity| identity.into_discovery_credential::<C>())
231         .map(|crypto_material| MatchableCredential {
232             discovery_credential: crypto_material,
233             match_data: EmptyMatchedCredential,
234         })
235         .collect::<Vec<_>>();
236 
237     // reverse the identities so that we're scanning to the end of the
238     // cred source for predictably bad performance
239     creds.reverse();
240 
241     match crypto_material_type {
242         CryptoMaterialType::MinFootprint => {
243             // Cache size of 0 => only min-footprint creds
244             let cred_book = CredentialBookBuilder::<_>::build_cached_slice_book::<
245                 0,
246                 0,
247                 CryptoProviderImpl,
248             >(&creds, &[]);
249 
250             b.iter(|| {
251                 black_box(
252                     deserialize_advertisement::<_, C>(deserialization_arena!(), adv, &cred_book)
253                         .map(|_| 0_u8)
254                         .unwrap(),
255                 )
256             });
257         }
258         CryptoMaterialType::Precalculated => {
259             let cred_book = CredentialBookBuilder::<_>::build_precalculated_owned_book::<C>(
260                 creds,
261                 core::iter::empty(),
262             );
263             b.iter(|| {
264                 black_box(
265                     deserialize_advertisement::<_, C>(deserialization_arena!(), adv, &cred_book)
266                         .map(|_| 0_u8)
267                         .unwrap(),
268                 )
269             });
270         }
271     }
272 }
273 
274 /// Benchmark decrypting a V1 advertisement with credentials built from the reversed list of
275 /// identities
run_with_v1_creds<C>( b: &mut Bencher, crypto_material_type: CryptoMaterialType, identities: Vec<V1Identity>, adv: &[u8], ) where C: CryptoProvider,276 fn run_with_v1_creds<C>(
277     b: &mut Bencher,
278     crypto_material_type: CryptoMaterialType,
279     identities: Vec<V1Identity>,
280     adv: &[u8],
281 ) where
282     C: CryptoProvider,
283 {
284     let mut creds = identities
285         .into_iter()
286         .map(|identity| identity.into_discovery_credential::<C>())
287         .map(|crypto_material| MatchableCredential {
288             discovery_credential: crypto_material,
289             match_data: EmptyMatchedCredential,
290         })
291         .collect::<Vec<_>>();
292 
293     // reverse the identities so that we're scanning to the end of the
294     // cred source for predictably bad performance
295     creds.reverse();
296 
297     match crypto_material_type {
298         CryptoMaterialType::MinFootprint => {
299             // Cache size of 0 => only min-footprint creds
300             let cred_book = CredentialBookBuilder::<_>::build_cached_slice_book::<
301                 0,
302                 0,
303                 CryptoProviderImpl,
304             >(&[], &creds);
305 
306             b.iter(|| {
307                 black_box(
308                     deserialize_advertisement::<_, C>(deserialization_arena!(), adv, &cred_book)
309                         .map(|_| 0_u8)
310                         .unwrap(),
311                 )
312             });
313         }
314         CryptoMaterialType::Precalculated => {
315             let cred_book = CredentialBookBuilder::<_>::build_precalculated_owned_book::<C>(
316                 core::iter::empty(),
317                 creds,
318             );
319             b.iter(|| {
320                 black_box(
321                     deserialize_advertisement::<_, C>(deserialization_arena!(), adv, &cred_book)
322                         .map(|_| 0_u8)
323                         .unwrap(),
324                 )
325             });
326         }
327     }
328 }
329 
add_des<I: SectionEncoder>( sb: &mut SectionBuilder<&mut np_adv::extended::serialize::AdvBuilder, I>, )330 fn add_des<I: SectionEncoder>(
331     sb: &mut SectionBuilder<&mut np_adv::extended::serialize::AdvBuilder, I>,
332 ) {
333     sb.add_de_res(|_| TxPower::try_from(17).map(TxPowerDataElement::from)).unwrap();
334     sb.add_de_res(|_| GenericDataElement::try_from(100_u32.into(), &[0; 10])).unwrap();
335 }
336 criterion_group!(
337     benches,
338     deser_adv_v1_encrypted,
339     deser_adv_v1_plaintext,
340     deser_adv_v0_encrypted,
341     deser_adv_v0_plaintext
342 );
343 criterion_main!(benches);
344 
345 struct V0Identity {
346     key_seed: [u8; 32],
347     identity_token: V0IdentityToken,
348 }
349 
350 impl V0Identity {
351     /// Generate a new identity with random crypto material
random<R: rand::Rng + rand::CryptoRng>(rng: &mut R) -> Self352     fn random<R: rand::Rng + rand::CryptoRng>(rng: &mut R) -> Self {
353         Self {
354             key_seed: rng.gen(),
355             identity_token: V0IdentityToken::from(rng.gen::<[u8; V0_IDENTITY_TOKEN_LEN]>()),
356         }
357     }
358     /// Convert this `V0Identity` into a V0 discovery credential.
into_discovery_credential<C: CryptoProvider>(self) -> V0DiscoveryCredential359     fn into_discovery_credential<C: CryptoProvider>(self) -> V0DiscoveryCredential {
360         let hkdf = NpKeySeedHkdf::<C>::new(&self.key_seed);
361         V0DiscoveryCredential::new(
362             self.key_seed,
363             hkdf.v0_identity_token_hmac_key().calculate_hmac::<C>(self.identity_token.as_slice()),
364         )
365     }
366 }
367 
368 struct V1Identity {
369     key_seed: [u8; 32],
370     identity_token: V1IdentityToken,
371     private_key: ed25519::PrivateKey,
372 }
373 
374 impl V1Identity {
375     /// Generate a new identity with random crypto material
random<C: CryptoProvider>(rng: &mut C::CryptoRng) -> Self376     fn random<C: CryptoProvider>(rng: &mut C::CryptoRng) -> Self {
377         Self {
378             key_seed: rng.gen(),
379             identity_token: rng.gen(),
380             private_key: ed25519::PrivateKey::generate::<C::Ed25519>(),
381         }
382     }
383     /// Convert this `V1Identity` into a `V1DiscoveryCredential`.
into_discovery_credential<C: CryptoProvider>(self) -> V1DiscoveryCredential384     fn into_discovery_credential<C: CryptoProvider>(self) -> V1DiscoveryCredential {
385         let hkdf = NpKeySeedHkdf::<C>::new(&self.key_seed);
386 
387         V1DiscoveryCredential::new(
388             self.key_seed,
389             hkdf.v1_mic_short_salt_keys()
390                 .identity_token_hmac_key()
391                 .calculate_hmac::<C>(self.identity_token.as_slice()),
392             hkdf.v1_mic_extended_salt_keys()
393                 .identity_token_hmac_key()
394                 .calculate_hmac::<C>(self.identity_token.as_slice()),
395             hkdf.v1_signature_keys()
396                 .identity_token_hmac_key()
397                 .calculate_hmac::<C>(self.identity_token.as_slice()),
398             self.private_key.derive_public_key::<C::Ed25519>(),
399         )
400     }
401 }
402 
403 #[derive(strum_macros::EnumIter, Clone, Copy, Debug)]
404 enum CryptoMaterialType {
405     MinFootprint,
406     Precalculated,
407 }
408