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 #![allow(clippy::indexing_slicing, clippy::unwrap_used, clippy::panic, clippy::expect_used)]
15
16 extern crate alloc;
17
18 use crate::{
19 build_np_adv_decrypter_from_key_seed, salt_padder, AuthenticatedNpLdtDecryptCipher,
20 LdtAdvDecryptError, NpLdtDecryptCipher, NpLdtEncryptCipher, V0IdentityToken, V0Salt,
21 LDT_XTS_AES_MAX_LEN, V0_IDENTITY_TOKEN_LEN,
22 };
23 use alloc::vec::Vec;
24 use crypto_provider::{CryptoProvider, CryptoRng};
25 use crypto_provider_default::CryptoProviderImpl;
26 use ldt::{DefaultPadder, LdtCipher, LdtError, LdtKey, XorPadder};
27 use np_hkdf::NpKeySeedHkdf;
28 use rand::Rng;
29 use rand_ext::{random_bytes, random_vec, seeded_rng};
30
31 extern crate std;
32
33 use crypto_provider::aes::BLOCK_SIZE;
34 use std::vec;
35
36 #[test]
decrypt_matches_correct_ciphertext()37 fn decrypt_matches_correct_ciphertext() {
38 let mut rng = CryptoRng::new();
39 for _ in 0..1_000 {
40 let test_state = make_test_components::<CryptoProviderImpl>(&mut rng);
41
42 let cipher = build_np_adv_decrypter_from_key_seed(&test_state.hkdf, test_state.hmac);
43 let (token, plaintext) =
44 cipher.decrypt_and_verify(&test_state.ciphertext, &test_state.padder).unwrap();
45
46 assert_eq!(test_state.identity_token, token);
47 assert_eq!(&test_state.remaining_plaintext, plaintext.as_ref());
48 }
49 }
50
51 #[test]
decrypt_doesnt_match_when_ciphertext_mangled()52 fn decrypt_doesnt_match_when_ciphertext_mangled() {
53 let mut rng = CryptoRng::new();
54 for _ in 0..1_000 {
55 let mut test_state = make_test_components::<CryptoProviderImpl>(&mut rng);
56
57 // mangle the ciphertext
58 test_state.ciphertext[0] ^= 0xAA;
59
60 let cipher = build_np_adv_decrypter_from_key_seed(&test_state.hkdf, test_state.hmac);
61 assert_eq!(
62 Err(LdtAdvDecryptError::MacMismatch),
63 cipher.decrypt_and_verify(&test_state.ciphertext, &test_state.padder)
64 );
65 }
66 }
67
68 #[test]
decrypt_doesnt_match_when_plaintext_doesnt_match_mac()69 fn decrypt_doesnt_match_when_plaintext_doesnt_match_mac() {
70 let mut rng = CryptoRng::new();
71 for _ in 0..1_000 {
72 let mut test_state = make_test_components::<CryptoProviderImpl>(&mut rng);
73
74 // mangle the mac
75 test_state.hmac[0] ^= 0xAA;
76
77 let cipher = build_np_adv_decrypter_from_key_seed(&test_state.hkdf, test_state.hmac);
78 assert_eq!(
79 Err(LdtAdvDecryptError::MacMismatch),
80 cipher.decrypt_and_verify(&test_state.ciphertext, &test_state.padder)
81 );
82 }
83 }
84
85 #[test]
86 #[allow(deprecated)]
encrypt_works()87 fn encrypt_works() {
88 let mut rng = CryptoRng::new();
89 for _ in 0..1_000 {
90 let test_state = make_test_components::<CryptoProviderImpl>(&mut rng);
91
92 let cipher = test_state.ldt_enc;
93
94 let mut plaintext_copy = test_state.all_plaintext.clone();
95 cipher.encrypt(&mut plaintext_copy[..], &test_state.padder).unwrap();
96
97 assert_eq!(test_state.ciphertext, plaintext_copy);
98 }
99 }
100
101 #[test]
102 #[allow(deprecated)]
encrypt_too_short_err()103 fn encrypt_too_short_err() {
104 let ldt_enc =
105 NpLdtEncryptCipher::<CryptoProviderImpl>::new(&LdtKey::from_concatenated(&[0; 64]));
106
107 let mut payload = vec![0; BLOCK_SIZE - 1];
108 assert_eq!(
109 Err(LdtError::InvalidLength(BLOCK_SIZE - 1)),
110 ldt_enc.encrypt(&mut payload, &DefaultPadder)
111 );
112 }
113
114 #[test]
115 #[allow(deprecated)]
encrypt_too_long_err()116 fn encrypt_too_long_err() {
117 let ldt_enc =
118 NpLdtEncryptCipher::<CryptoProviderImpl>::new(&LdtKey::from_concatenated(&[0; 64]));
119
120 let mut payload = vec![0; BLOCK_SIZE * 2];
121 assert_eq!(
122 Err(LdtError::InvalidLength(BLOCK_SIZE * 2)),
123 ldt_enc.encrypt(&mut payload, &DefaultPadder)
124 );
125 }
126
127 #[test]
decrypt_too_short_err()128 fn decrypt_too_short_err() {
129 let adv_cipher: AuthenticatedNpLdtDecryptCipher<CryptoProviderImpl> =
130 AuthenticatedNpLdtDecryptCipher {
131 ldt_decrypter: NpLdtDecryptCipher::<CryptoProviderImpl>::new(
132 &LdtKey::from_concatenated(&[0; 64]),
133 ),
134 metadata_key_tag: [0; 32],
135 metadata_key_hmac_key: np_hkdf::NpHmacSha256Key::from([0; 32]),
136 };
137
138 // 1 byte less than a full block
139 let payload = vec![0; BLOCK_SIZE - 1];
140 assert_eq!(
141 Err(LdtAdvDecryptError::InvalidLength(BLOCK_SIZE - 1)),
142 adv_cipher.decrypt_and_verify(&payload, &XorPadder::from([0_u8; BLOCK_SIZE]))
143 );
144 }
145
146 #[test]
decrypt_too_long_err()147 fn decrypt_too_long_err() {
148 let adv_cipher: AuthenticatedNpLdtDecryptCipher<CryptoProviderImpl> =
149 AuthenticatedNpLdtDecryptCipher {
150 ldt_decrypter: NpLdtDecryptCipher::<CryptoProviderImpl>::new(
151 &LdtKey::from_concatenated(&[0; 64]),
152 ),
153 metadata_key_tag: [0; 32],
154 metadata_key_hmac_key: np_hkdf::NpHmacSha256Key::from([0; 32]),
155 };
156
157 // 2 full blocks
158 let payload = [0; BLOCK_SIZE * 2];
159 assert_eq!(
160 Err(LdtAdvDecryptError::InvalidLength(BLOCK_SIZE * 2)),
161 adv_cipher.decrypt_and_verify(&payload, &XorPadder::from([0; BLOCK_SIZE]))
162 );
163 }
164
165 #[test]
verify_input_len_const()166 fn verify_input_len_const() {
167 // make sure it matches the authoritative upstream
168 assert_eq!(crate::VALID_INPUT_LEN, NpLdtEncryptCipher::<CryptoProviderImpl>::VALID_INPUT_LEN);
169 }
170
171 #[test]
ldt_ffi_test_scenario()172 fn ldt_ffi_test_scenario() {
173 // used in ldt_ffi_tests.cc and LdtNpJniTests
174 let key_seed: [u8; 32] = [
175 204, 219, 36, 137, 233, 252, 172, 66, 179, 147, 72, 184, 148, 30, 209, 154, 29, 54, 14,
176 117, 224, 152, 200, 193, 94, 107, 28, 194, 182, 32, 205, 57,
177 ];
178
179 let salt: V0Salt = [12, 15].into();
180 let plaintext = [
181 205_u8, 104, 63, 225, 161, 209, 248, 70, 84, 61, 10, 19, 212, 174, 164, 0, 64, 200, 214,
182 123,
183 ];
184
185 let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
186 let ldt_key = hkdf.v0_ldt_key();
187 let identity_token_hmac = hkdf
188 .v0_identity_token_hmac_key()
189 .calculate_hmac::<CryptoProviderImpl>(&plaintext[..V0_IDENTITY_TOKEN_LEN]);
190 let decrypter = build_np_adv_decrypter_from_key_seed(&hkdf, identity_token_hmac);
191
192 let padder = salt_padder::<CryptoProviderImpl>(salt);
193 let mut ciphertext = plaintext;
194 NpLdtEncryptCipher::<CryptoProviderImpl>::new(&ldt_key)
195 .encrypt(&mut ciphertext, &padder)
196 .unwrap();
197
198 let (token, contents) = decrypter.decrypt_and_verify(&ciphertext, &padder).unwrap();
199 assert_eq!(&plaintext[..V0_IDENTITY_TOKEN_LEN], token.as_slice());
200 assert_eq!(&plaintext[V0_IDENTITY_TOKEN_LEN..], contents.as_slice());
201
202 // Uncomment if updating the FFI test
203 // {
204 // use std::println;
205 // use test_helper::hex_bytes;
206 // println!("Key seed:\n{}\n{}", hex_bytes(&key_seed), hex::encode_upper(&key_seed));
207 // println!("Identity token HMAC:\n{}\n{}", hex_bytes(&identity_token_hmac), hex::encode_upper(&identity_token_hmac));
208 // println!("Plaintext:\n{}\n{}", hex_bytes(&plaintext), hex::encode_upper(&plaintext));
209 // println!("Salt:\n{}\n{}", hex_bytes(salt.bytes()), hex::encode_upper(salt.bytes()));
210 // println!("Ciphertext:\n{}\n{}", hex_bytes(&ciphertext), hex::encode_upper(&ciphertext));
211 // panic!();
212 // }
213 }
214
215 /// Returns (plaintext, ciphertext, padder, hmac key, MAC, ldt)
make_test_components<C: crypto_provider::CryptoProvider>( rng: &mut C::CryptoRng, ) -> LdtAdvTestComponents<C>216 fn make_test_components<C: crypto_provider::CryptoProvider>(
217 rng: &mut C::CryptoRng,
218 ) -> LdtAdvTestComponents<C> {
219 // [1, 2) blocks of XTS-AES
220 let mut rc_rng = seeded_rng();
221 let payload_len = rc_rng.gen_range(BLOCK_SIZE..=(BLOCK_SIZE * 2 - 1));
222 let all_plaintext = random_vec::<C>(rng, payload_len);
223
224 let salt = V0Salt { bytes: random_bytes::<2, C>(rng) };
225 let padder = salt_padder::<C>(salt);
226
227 let key_seed: [u8; 32] = random_bytes::<32, C>(rng);
228 let hkdf = np_hkdf::NpKeySeedHkdf::new(&key_seed);
229 let ldt_key = hkdf.v0_ldt_key();
230 let hmac_key = hkdf.v0_identity_token_hmac_key();
231 let identity_token: [u8; V0_IDENTITY_TOKEN_LEN] =
232 all_plaintext[..V0_IDENTITY_TOKEN_LEN].try_into().unwrap();
233 let hmac: [u8; 32] = hmac_key.calculate_hmac::<C>(&identity_token);
234
235 let ldt_enc = NpLdtEncryptCipher::<C>::new(&ldt_key);
236
237 let mut ciphertext = [0_u8; LDT_XTS_AES_MAX_LEN];
238 ciphertext[..all_plaintext.len()].copy_from_slice(&all_plaintext);
239 ldt_enc.encrypt(&mut ciphertext[..all_plaintext.len()], &padder).unwrap();
240 let remaining_plaintext = all_plaintext[V0_IDENTITY_TOKEN_LEN..].to_vec();
241 LdtAdvTestComponents {
242 all_plaintext,
243 identity_token: identity_token.into(),
244 remaining_plaintext,
245 ciphertext: ciphertext[..payload_len].to_vec(),
246 padder,
247 hmac,
248 ldt_enc,
249 hkdf,
250 }
251 }
252
253 struct LdtAdvTestComponents<C: CryptoProvider> {
254 all_plaintext: Vec<u8>,
255 identity_token: V0IdentityToken,
256 /// Plaintext after the identity token
257 remaining_plaintext: Vec<u8>,
258 ciphertext: Vec<u8>,
259 padder: XorPadder<{ BLOCK_SIZE }>,
260 hmac: [u8; 32],
261 ldt_enc: NpLdtEncryptCipher<C>,
262 hkdf: NpKeySeedHkdf<C>,
263 }
264
265 mod coverage_gaming {
266 extern crate std;
267
268 use crate::{V0IdentityToken, V0_IDENTITY_TOKEN_LEN};
269 use crypto_provider::{CryptoProvider, CryptoRng};
270 use crypto_provider_default::CryptoProviderImpl;
271 use std::{collections, format};
272
273 #[test]
legacy_identity_token()274 fn legacy_identity_token() {
275 let token = V0IdentityToken::from([0; V0_IDENTITY_TOKEN_LEN]);
276 // debug
277 let _ = format!("{:?}", token);
278 // hash
279 let _ = collections::HashSet::new().insert(&token);
280 // bytes
281 assert_eq!(token.0, token.bytes());
282 // AsRef
283 assert_eq!(token.0.as_slice(), token.as_ref());
284 // FromCryptoRng
285 let mut rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
286 let _: V0IdentityToken = rng.gen();
287 }
288 }
289