1 // Copyright 2015-2016 Brian Smith.
2 //
3 // Permission to use, copy, modify, and/or distribute this software for any
4 // purpose with or without fee is hereby granted, provided that the above
5 // copyright notice and this permission notice appear in all copies.
6 //
7 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
8 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
10 // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15 #![cfg(any(not(target_arch = "wasm32"), feature = "wasm32_c"))]
16
17 #[cfg(target_arch = "wasm32")]
18 use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure};
19
20 #[cfg(target_arch = "wasm32")]
21 wasm_bindgen_test_configure!(run_in_browser);
22
23 use core::ops::RangeFrom;
24 use ring::{aead, error, test, test_file};
25
26 #[test]
27 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
aead_aes_gcm_128()28 fn aead_aes_gcm_128() {
29 test_aead(
30 &aead::AES_128_GCM,
31 seal_with_key,
32 open_with_key,
33 test_file!("aead_aes_128_gcm_tests.txt"),
34 );
35 test_aead(
36 &aead::AES_128_GCM,
37 seal_with_less_safe_key,
38 open_with_less_safe_key,
39 test_file!("aead_aes_128_gcm_tests.txt"),
40 );
41 }
42
43 #[test]
44 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
aead_aes_gcm_256()45 fn aead_aes_gcm_256() {
46 test_aead(
47 &aead::AES_256_GCM,
48 seal_with_key,
49 open_with_key,
50 test_file!("aead_aes_256_gcm_tests.txt"),
51 );
52 test_aead(
53 &aead::AES_256_GCM,
54 seal_with_less_safe_key,
55 open_with_less_safe_key,
56 test_file!("aead_aes_256_gcm_tests.txt"),
57 );
58 }
59
60 #[cfg(any(
61 target_arch = "aarch64",
62 target_arch = "arm",
63 target_arch = "x86_64",
64 target_arch = "x86"
65 ))]
66 #[test]
aead_chacha20_poly1305()67 fn aead_chacha20_poly1305() {
68 test_aead(
69 &aead::CHACHA20_POLY1305,
70 seal_with_key,
71 open_with_key,
72 test_file!("aead_chacha20_poly1305_tests.txt"),
73 );
74 test_aead(
75 &aead::CHACHA20_POLY1305,
76 seal_with_less_safe_key,
77 open_with_less_safe_key,
78 test_file!("aead_chacha20_poly1305_tests.txt"),
79 );
80 }
81
test_aead<Seal, Open>( aead_alg: &'static aead::Algorithm, seal: Seal, open: Open, test_file: test::File, ) where Seal: Fn( &'static aead::Algorithm, &[u8], aead::Nonce, aead::Aad<&[u8]>, &mut Vec<u8>, ) -> Result<(), error::Unspecified>, Open: for<'a> Fn( &'static aead::Algorithm, &[u8], aead::Nonce, aead::Aad<&[u8]>, &'a mut [u8], RangeFrom<usize>, ) -> Result<&'a mut [u8], error::Unspecified>,82 fn test_aead<Seal, Open>(
83 aead_alg: &'static aead::Algorithm,
84 seal: Seal,
85 open: Open,
86 test_file: test::File,
87 ) where
88 Seal: Fn(
89 &'static aead::Algorithm,
90 &[u8],
91 aead::Nonce,
92 aead::Aad<&[u8]>,
93 &mut Vec<u8>,
94 ) -> Result<(), error::Unspecified>,
95 Open: for<'a> Fn(
96 &'static aead::Algorithm,
97 &[u8],
98 aead::Nonce,
99 aead::Aad<&[u8]>,
100 &'a mut [u8],
101 RangeFrom<usize>,
102 ) -> Result<&'a mut [u8], error::Unspecified>,
103 {
104 test_aead_key_sizes(aead_alg);
105
106 test::run(test_file, |section, test_case| {
107 assert_eq!(section, "");
108 let key_bytes = test_case.consume_bytes("KEY");
109 let nonce_bytes = test_case.consume_bytes("NONCE");
110 let plaintext = test_case.consume_bytes("IN");
111 let aad = test_case.consume_bytes("AD");
112 let mut ct = test_case.consume_bytes("CT");
113 let tag = test_case.consume_bytes("TAG");
114 let error = test_case.consume_optional_string("FAILS");
115
116 match &error {
117 Some(err) if err == "WRONG_NONCE_LENGTH" => {
118 assert!(aead::Nonce::try_assume_unique_for_key(&nonce_bytes).is_err());
119 return Ok(());
120 }
121 _ => (),
122 };
123
124 let mut s_in_out = plaintext.clone();
125 let nonce = aead::Nonce::try_assume_unique_for_key(&nonce_bytes).unwrap();
126 let s_result = seal(
127 aead_alg,
128 &key_bytes[..],
129 nonce,
130 aead::Aad::from(&aad[..]),
131 &mut s_in_out,
132 );
133
134 ct.extend(tag);
135
136 if s_result.is_ok() {
137 assert_eq!(&ct, &s_in_out);
138 }
139
140 // In release builds, test all prefix lengths from 0 to 4096 bytes.
141 // Debug builds are too slow for this, so for those builds, only
142 // test a smaller subset.
143
144 // TLS record headers are 5 bytes long.
145 // TLS explicit nonces for AES-GCM are 8 bytes long.
146 static MINIMAL_IN_PREFIX_LENS: [usize; 36] = [
147 // No input prefix to overwrite; i.e. the opening is exactly
148 // "in place."
149 0,
150 1,
151 2,
152 // Proposed TLS 1.3 header (no explicit nonce).
153 5,
154 8,
155 // Probably the most common use of a non-zero `in_prefix_len`
156 // would be to write a decrypted TLS record over the top of the
157 // TLS header and nonce.
158 5 /* record header */ + 8, /* explicit nonce */
159 // The stitched AES-GCM x86-64 code works on 6-block (96 byte)
160 // units. Some of the ChaCha20 code is even weirder.
161 15, // The maximum partial AES block.
162 16, // One AES block.
163 17, // One byte more than a full AES block.
164 31, // 2 AES blocks or 1 ChaCha20 block, minus 1.
165 32, // Two AES blocks, one ChaCha20 block.
166 33, // 2 AES blocks or 1 ChaCha20 block, plus 1.
167 47, // Three AES blocks - 1.
168 48, // Three AES blocks.
169 49, // Three AES blocks + 1.
170 63, // Four AES blocks or two ChaCha20 blocks, minus 1.
171 64, // Four AES blocks or two ChaCha20 blocks.
172 65, // Four AES blocks or two ChaCha20 blocks, plus 1.
173 79, // Five AES blocks, minus 1.
174 80, // Five AES blocks.
175 81, // Five AES blocks, plus 1.
176 95, // Six AES blocks or three ChaCha20 blocks, minus 1.
177 96, // Six AES blocks or three ChaCha20 blocks.
178 97, // Six AES blocks or three ChaCha20 blocks, plus 1.
179 111, // Seven AES blocks, minus 1.
180 112, // Seven AES blocks.
181 113, // Seven AES blocks, plus 1.
182 127, // Eight AES blocks or four ChaCha20 blocks, minus 1.
183 128, // Eight AES blocks or four ChaCha20 blocks.
184 129, // Eight AES blocks or four ChaCha20 blocks, plus 1.
185 143, // Nine AES blocks, minus 1.
186 144, // Nine AES blocks.
187 145, // Nine AES blocks, plus 1.
188 255, // 16 AES blocks or 8 ChaCha20 blocks, minus 1.
189 256, // 16 AES blocks or 8 ChaCha20 blocks.
190 257, // 16 AES blocks or 8 ChaCha20 blocks, plus 1.
191 ];
192
193 let mut more_comprehensive_in_prefix_lengths = [0; 4096];
194 let in_prefix_lengths = if cfg!(debug_assertions) {
195 &MINIMAL_IN_PREFIX_LENS[..]
196 } else {
197 #[allow(clippy::needless_range_loop)]
198 for b in 0..more_comprehensive_in_prefix_lengths.len() {
199 more_comprehensive_in_prefix_lengths[b] = b;
200 }
201 &more_comprehensive_in_prefix_lengths[..]
202 };
203 let mut o_in_out = vec![123u8; 4096];
204
205 for &in_prefix_len in in_prefix_lengths.iter() {
206 o_in_out.truncate(0);
207 o_in_out.resize(in_prefix_len, 123);
208 o_in_out.extend_from_slice(&ct[..]);
209
210 let nonce = aead::Nonce::try_assume_unique_for_key(&nonce_bytes).unwrap();
211 let o_result = open(
212 aead_alg,
213 &key_bytes,
214 nonce,
215 aead::Aad::from(&aad[..]),
216 &mut o_in_out,
217 in_prefix_len..,
218 );
219 match error {
220 None => {
221 assert!(s_result.is_ok());
222 assert_eq!(&plaintext[..], o_result.unwrap());
223 }
224 Some(ref error) if error == "WRONG_NONCE_LENGTH" => {
225 assert_eq!(Err(error::Unspecified), s_result);
226 assert_eq!(Err(error::Unspecified), o_result);
227 }
228 Some(error) => {
229 unreachable!("Unexpected error test case: {}", error);
230 }
231 };
232 }
233
234 Ok(())
235 });
236 }
237
seal_with_key( algorithm: &'static aead::Algorithm, key: &[u8], nonce: aead::Nonce, aad: aead::Aad<&[u8]>, in_out: &mut Vec<u8>, ) -> Result<(), error::Unspecified>238 fn seal_with_key(
239 algorithm: &'static aead::Algorithm,
240 key: &[u8],
241 nonce: aead::Nonce,
242 aad: aead::Aad<&[u8]>,
243 in_out: &mut Vec<u8>,
244 ) -> Result<(), error::Unspecified> {
245 let mut s_key: aead::SealingKey<OneNonceSequence> = make_key(algorithm, key, nonce);
246 s_key.seal_in_place_append_tag(aad, in_out)
247 }
248
open_with_key<'a>( algorithm: &'static aead::Algorithm, key: &[u8], nonce: aead::Nonce, aad: aead::Aad<&[u8]>, in_out: &'a mut [u8], ciphertext_and_tag: RangeFrom<usize>, ) -> Result<&'a mut [u8], error::Unspecified>249 fn open_with_key<'a>(
250 algorithm: &'static aead::Algorithm,
251 key: &[u8],
252 nonce: aead::Nonce,
253 aad: aead::Aad<&[u8]>,
254 in_out: &'a mut [u8],
255 ciphertext_and_tag: RangeFrom<usize>,
256 ) -> Result<&'a mut [u8], error::Unspecified> {
257 let mut o_key: aead::OpeningKey<OneNonceSequence> = make_key(algorithm, key, nonce);
258 o_key.open_within(aad, in_out, ciphertext_and_tag)
259 }
260
seal_with_less_safe_key( algorithm: &'static aead::Algorithm, key: &[u8], nonce: aead::Nonce, aad: aead::Aad<&[u8]>, in_out: &mut Vec<u8>, ) -> Result<(), error::Unspecified>261 fn seal_with_less_safe_key(
262 algorithm: &'static aead::Algorithm,
263 key: &[u8],
264 nonce: aead::Nonce,
265 aad: aead::Aad<&[u8]>,
266 in_out: &mut Vec<u8>,
267 ) -> Result<(), error::Unspecified> {
268 let key = make_less_safe_key(algorithm, key);
269 key.seal_in_place_append_tag(nonce, aad, in_out)
270 }
271
open_with_less_safe_key<'a>( algorithm: &'static aead::Algorithm, key: &[u8], nonce: aead::Nonce, aad: aead::Aad<&[u8]>, in_out: &'a mut [u8], ciphertext_and_tag: RangeFrom<usize>, ) -> Result<&'a mut [u8], error::Unspecified>272 fn open_with_less_safe_key<'a>(
273 algorithm: &'static aead::Algorithm,
274 key: &[u8],
275 nonce: aead::Nonce,
276 aad: aead::Aad<&[u8]>,
277 in_out: &'a mut [u8],
278 ciphertext_and_tag: RangeFrom<usize>,
279 ) -> Result<&'a mut [u8], error::Unspecified> {
280 let key = make_less_safe_key(algorithm, key);
281 key.open_within(nonce, aad, in_out, ciphertext_and_tag)
282 }
283
284 #[allow(clippy::range_plus_one)]
test_aead_key_sizes(aead_alg: &'static aead::Algorithm)285 fn test_aead_key_sizes(aead_alg: &'static aead::Algorithm) {
286 let key_len = aead_alg.key_len();
287 let key_data = vec![0u8; key_len * 2];
288
289 // Key is the right size.
290 assert!(aead::UnboundKey::new(aead_alg, &key_data[..key_len]).is_ok());
291
292 // Key is one byte too small.
293 assert!(aead::UnboundKey::new(aead_alg, &key_data[..(key_len - 1)]).is_err());
294
295 // Key is one byte too large.
296 assert!(aead::UnboundKey::new(aead_alg, &key_data[..(key_len + 1)]).is_err());
297
298 // Key is half the required size.
299 assert!(aead::UnboundKey::new(aead_alg, &key_data[..(key_len / 2)]).is_err());
300
301 // Key is twice the required size.
302 assert!(aead::UnboundKey::new(aead_alg, &key_data[..(key_len * 2)]).is_err());
303
304 // Key is empty.
305 assert!(aead::UnboundKey::new(aead_alg, &[]).is_err());
306
307 // Key is one byte.
308 assert!(aead::UnboundKey::new(aead_alg, &[0]).is_err());
309 }
310
311 // Test that we reject non-standard nonce sizes.
312 #[allow(clippy::range_plus_one)]
313 #[test]
test_aead_nonce_sizes() -> Result<(), error::Unspecified>314 fn test_aead_nonce_sizes() -> Result<(), error::Unspecified> {
315 let nonce_len = aead::NONCE_LEN;
316 let nonce = vec![0u8; nonce_len * 2];
317
318 assert!(aead::Nonce::try_assume_unique_for_key(&nonce[..nonce_len]).is_ok());
319 assert!(aead::Nonce::try_assume_unique_for_key(&nonce[..(nonce_len - 1)]).is_err());
320 assert!(aead::Nonce::try_assume_unique_for_key(&nonce[..(nonce_len + 1)]).is_err());
321 assert!(aead::Nonce::try_assume_unique_for_key(&nonce[..(nonce_len / 2)]).is_err());
322 assert!(aead::Nonce::try_assume_unique_for_key(&nonce[..(nonce_len * 2)]).is_err());
323 assert!(aead::Nonce::try_assume_unique_for_key(&[]).is_err());
324 assert!(aead::Nonce::try_assume_unique_for_key(&nonce[..1]).is_err());
325 assert!(aead::Nonce::try_assume_unique_for_key(&nonce[..16]).is_err()); // 128 bits.
326
327 Ok(())
328 }
329
330 #[cfg(any(
331 target_arch = "aarch64",
332 target_arch = "arm",
333 target_arch = "x86_64",
334 target_arch = "x86"
335 ))]
336 #[allow(clippy::range_plus_one)]
337 #[test]
aead_chacha20_poly1305_openssh()338 fn aead_chacha20_poly1305_openssh() {
339 // TODO: test_aead_key_sizes(...);
340
341 test::run(
342 test_file!("aead_chacha20_poly1305_openssh_tests.txt"),
343 |section, test_case| {
344 assert_eq!(section, "");
345
346 // XXX: `polyfill::convert` isn't available here.
347 let key_bytes = {
348 let as_vec = test_case.consume_bytes("KEY");
349 let mut as_array = [0u8; aead::chacha20_poly1305_openssh::KEY_LEN];
350 as_array.copy_from_slice(&as_vec);
351 as_array
352 };
353
354 let sequence_number = test_case.consume_usize("SEQUENCE_NUMBER");
355 assert_eq!(sequence_number as u32 as usize, sequence_number);
356 let sequence_num = sequence_number as u32;
357 let plaintext = test_case.consume_bytes("IN");
358 let ct = test_case.consume_bytes("CT");
359 let expected_tag = test_case.consume_bytes("TAG");
360
361 // TODO: Add some tests for when things fail.
362 //let error = test_case.consume_optional_string("FAILS");
363
364 let mut tag = [0u8; aead::chacha20_poly1305_openssh::TAG_LEN];
365 let mut s_in_out = plaintext.clone();
366 let s_key = aead::chacha20_poly1305_openssh::SealingKey::new(&key_bytes);
367 s_key.seal_in_place(sequence_num, &mut s_in_out[..], &mut tag);
368 assert_eq!(&ct, &s_in_out);
369 assert_eq!(&expected_tag, &tag);
370 let o_key = aead::chacha20_poly1305_openssh::OpeningKey::new(&key_bytes);
371
372 {
373 let o_result = o_key.open_in_place(sequence_num, &mut s_in_out[..], &tag);
374 assert_eq!(o_result, Ok(&plaintext[4..]));
375 }
376 assert_eq!(&s_in_out[..4], &ct[..4]);
377 assert_eq!(&s_in_out[4..], &plaintext[4..]);
378
379 Ok(())
380 },
381 );
382 }
383
384 #[test]
385 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
test_tag_traits()386 fn test_tag_traits() {
387 test::compile_time_assert_send::<aead::Tag>();
388 test::compile_time_assert_sync::<aead::Tag>();
389 }
390
391 #[test]
392 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
test_aead_key_debug()393 fn test_aead_key_debug() {
394 let key_bytes = [0; 32];
395 let nonce = [0; aead::NONCE_LEN];
396
397 let key = aead::UnboundKey::new(&aead::AES_256_GCM, &key_bytes).unwrap();
398 assert_eq!(
399 "UnboundKey { algorithm: AES_256_GCM }",
400 format!("{:?}", key)
401 );
402
403 let sealing_key: aead::SealingKey<OneNonceSequence> = make_key(
404 &aead::AES_256_GCM,
405 &key_bytes,
406 aead::Nonce::try_assume_unique_for_key(&nonce).unwrap(),
407 );
408 assert_eq!(
409 "SealingKey { algorithm: AES_256_GCM }",
410 format!("{:?}", sealing_key)
411 );
412
413 let opening_key: aead::OpeningKey<OneNonceSequence> = make_key(
414 &aead::AES_256_GCM,
415 &key_bytes,
416 aead::Nonce::try_assume_unique_for_key(&nonce).unwrap(),
417 );
418 assert_eq!(
419 "OpeningKey { algorithm: AES_256_GCM }",
420 format!("{:?}", opening_key)
421 );
422
423 let key: aead::LessSafeKey = make_less_safe_key(&aead::AES_256_GCM, &key_bytes);
424 assert_eq!(
425 "LessSafeKey { algorithm: AES_256_GCM }",
426 format!("{:?}", key)
427 );
428 }
429
make_key<K: aead::BoundKey<OneNonceSequence>>( algorithm: &'static aead::Algorithm, key: &[u8], nonce: aead::Nonce, ) -> K430 fn make_key<K: aead::BoundKey<OneNonceSequence>>(
431 algorithm: &'static aead::Algorithm,
432 key: &[u8],
433 nonce: aead::Nonce,
434 ) -> K {
435 let key = aead::UnboundKey::new(algorithm, key).unwrap();
436 let nonce_sequence = OneNonceSequence::new(nonce);
437 K::new(key, nonce_sequence)
438 }
439
make_less_safe_key(algorithm: &'static aead::Algorithm, key: &[u8]) -> aead::LessSafeKey440 fn make_less_safe_key(algorithm: &'static aead::Algorithm, key: &[u8]) -> aead::LessSafeKey {
441 let key = aead::UnboundKey::new(algorithm, key).unwrap();
442 aead::LessSafeKey::new(key)
443 }
444
445 struct OneNonceSequence(Option<aead::Nonce>);
446
447 impl OneNonceSequence {
448 /// Constructs the sequence allowing `advance()` to be called
449 /// `allowed_invocations` times.
new(nonce: aead::Nonce) -> Self450 fn new(nonce: aead::Nonce) -> Self {
451 Self(Some(nonce))
452 }
453 }
454
455 impl aead::NonceSequence for OneNonceSequence {
advance(&mut self) -> Result<aead::Nonce, error::Unspecified>456 fn advance(&mut self) -> Result<aead::Nonce, error::Unspecified> {
457 self.0.take().ok_or(error::Unspecified)
458 }
459 }
460