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