1 // Copyright 2023 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 //     http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 #![allow(clippy::unwrap_used, clippy::expect_used, clippy::indexing_slicing, clippy::panic)]
15 
16 use crypto_provider_default::CryptoProviderImpl;
17 use ldt_np_adv::*;
18 use np_adv::credential::matched::{
19     EmptyMatchedCredential, HasIdentityMatch, MetadataMatchedCredential,
20 };
21 use np_adv::legacy::serialize::{AdvBuilder, LdtEncoder};
22 use np_adv::{
23     credential::{
24         book::CredentialBookBuilder,
25         v0::{V0BroadcastCredential, V0DiscoveryCredential, V0},
26         MatchableCredential,
27     },
28     legacy::{
29         data_elements::tx_power::TxPowerDataElement, deserialize::*, V0AdvertisementContents,
30     },
31     shared_data::*,
32     *,
33 };
34 use serde::{Deserialize, Serialize};
35 
36 #[rustfmt::skip]
37 #[test]
v0_deser_plaintext()38 fn v0_deser_plaintext() {
39     let cred_book = CredentialBookBuilder::<EmptyMatchedCredential>::build_cached_slice_book::<
40         0,
41         0,
42         CryptoProviderImpl,
43     >(&[], &[]);
44     let arena = deserialization_arena!();
45     let adv = deserialize_advertisement::<_, CryptoProviderImpl>(
46         arena,
47         &[
48             0x00, // version header
49             0x15, 0x03, // Length 1 Tx Power DE with value 3
50         ],
51         &cred_book,
52     )
53         .expect("Should be a valid advertisement")
54         .into_v0()
55         .expect("Should be V0");
56 
57     match adv {
58         V0AdvertisementContents::Plaintext(p) => {
59             assert_eq!(
60                 vec![DeserializedDataElement::TxPower(TxPowerDataElement::from(
61                     TxPower::try_from(3).unwrap()
62                 ))],
63                 p.data_elements().collect::<Result<Vec<_>, _>>().unwrap()
64             );
65         }
66         _ => panic!("this example is plaintext"),
67     }
68 }
69 
70 /// Sample contents for some encrypted identity metadata
71 /// which includes just a name and an e-mail address.
72 #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
73 struct IdentityMetadata {
74     name: String,
75     email: String,
76 }
77 
78 impl IdentityMetadata {
79     /// Serialize this identity metadata to a JSON byte-string.
to_bytes(&self) -> Vec<u8>80     fn to_bytes(&self) -> Vec<u8> {
81         serde_json::to_vec(&self).expect("serialization should always succeed")
82     }
83     /// Attempt to deserialize identity metadata from a JSON byte-string.
try_from_bytes(serialized: &[u8]) -> Option<Self>84     fn try_from_bytes(serialized: &[u8]) -> Option<Self> {
85         serde_json::from_slice(serialized).ok()
86     }
87 }
88 
89 #[rustfmt::skip]
90 #[test]
v0_deser_ciphertext()91 fn v0_deser_ciphertext() {
92     // These are kept fixed in this example for reproducibility.
93     // In practice, these should instead be derived from a cryptographically-secure
94     // random number generator.
95     let key_seed = [0x11_u8; 32];
96     let identity_token = V0IdentityToken::from([0x33; V0_IDENTITY_TOKEN_LEN]);
97 
98     let broadcast_cred = V0BroadcastCredential::new(key_seed, identity_token);
99 
100     let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
101     let identity_token_hmac: [u8; 32] =
102         hkdf.v0_identity_token_hmac_key().calculate_hmac::<CryptoProviderImpl>(&identity_token.bytes());
103 
104     // Serialize and encrypt some identity metadata (sender-side)
105     let sender_metadata =
106         IdentityMetadata { name: "Alice".to_string(), email: "[email protected]".to_string() };
107     let sender_metadata_bytes = sender_metadata.to_bytes();
108     let encrypted_sender_metadata = MetadataMatchedCredential::<Vec<u8>>::
109     encrypt_from_plaintext::<V0, CryptoProviderImpl>(&hkdf,
110       identity_token,
111       &sender_metadata_bytes);
112 
113     // output of building a packet using AdvBuilder
114     let adv = &[
115         0x04, // version header
116         0x22, 0x22, // salt
117         // ciphertext for metadata key & txpower DE
118         0xD8, 0x22, 0x12, 0xEF, 0x16, 0xDB, 0xF8, 0x72, 0xF2, 0xA3, 0xA7,
119         0xC0, 0xFA, 0x52, 0x48, 0xEC
120     ];
121 
122     // make sure output hasn't changed
123     let mut builder = AdvBuilder::new(LdtEncoder::<CryptoProviderImpl>::new([0x22; 2].into(), &broadcast_cred));
124     builder.add_data_element(TxPowerDataElement::from(TxPower::try_from(3).unwrap())).unwrap();
125     assert_eq!(adv, builder.into_advertisement().unwrap().as_slice());
126 
127     let discovery_credential = V0DiscoveryCredential::new(key_seed, identity_token_hmac);
128 
129     let credentials: [MatchableCredential<V0, MetadataMatchedCredential<_>>; 1] =
130         [MatchableCredential { discovery_credential, match_data: encrypted_sender_metadata }];
131 
132     let cred_book = CredentialBookBuilder::build_cached_slice_book::<0, 0, CryptoProviderImpl>(
133         &credentials,
134         &[],
135     );
136 
137     let matched = match deserialize_advertisement::<_, CryptoProviderImpl>(
138         deserialization_arena!(),
139         adv,
140         &cred_book,
141     )
142         .expect("Should be a valid advertisement")
143         .into_v0()
144         .expect("Should be V0")
145     {
146         V0AdvertisementContents::Decrypted(c) => c,
147         _ => panic!("this example is ciphertext"),
148     };
149 
150     let decrypted_metadata_bytes = matched
151         .decrypt_metadata::<CryptoProviderImpl>()
152         .expect("Sender metadata should be decryptable");
153     let decrypted_metadata = IdentityMetadata::try_from_bytes(&decrypted_metadata_bytes)
154         .expect("Sender metadata should be deserializable");
155 
156     assert_eq!(sender_metadata, decrypted_metadata);
157 
158     let decrypted = matched.contents();
159 
160     assert_eq!(identity_token, decrypted.identity_token());
161 
162     assert_eq!(
163         vec![DeserializedDataElement::TxPower(TxPowerDataElement::from(TxPower::try_from(3).unwrap())),],
164         decrypted.data_elements().collect::<Result<Vec<_>, _>>().unwrap(),
165     );
166 }
167