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