1 // Copyright 2015-2021 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 #![allow(missing_docs)]
15 
16 use criterion::criterion_main;
17 use ring::{aead, error};
18 
19 // All the AEADs we're testing use 96-bit nonces.
20 pub const NONCE: [u8; 96 / 8] = [0u8; 96 / 8];
21 
22 // A TLS 1.2 finished message is always 12 bytes long.
23 const TLS12_FINISHED_LEN: usize = 12;
24 
25 // A TLS 1.3 finished message is "[t]he size of the HMAC output for the
26 // Hash used for the handshake," which is usually SHA-256.
27 const TLS13_FINISHED_LEN: usize = 32;
28 
29 // In TLS 1.2, 13 bytes of additional data are used for AEAD cipher suites.
30 const TLS12_AD: [u8; 13] = [
31     23, // Type: application_data
32     3, 3, // Version = TLS 1.2.
33     0x12, 0x34, // Length = 0x1234.
34     0, 0, 0, 0, 0, 0, 0, 1, // Record #1
35 ];
36 
37 // In TLS 1.3, 5 bytes of additional data are used for AEAD cihper suites.
38 const TLS13_AD: [u8; 5] = [
39     0x17, // app data type
40     0x3,  // version
41     0x3,  // ..
42     0x0,  // Length
43     0x7,  // ..
44 ];
45 
46 struct NonceSequence(u64);
47 impl NonceSequence {
new() -> Self48     const fn new() -> Self {
49         Self(0)
50     }
51 }
52 
53 impl aead::NonceSequence for NonceSequence {
advance(&mut self) -> Result<aead::Nonce, error::Unspecified>54     fn advance(&mut self) -> Result<aead::Nonce, error::Unspecified> {
55         let mut result = [0u8; aead::NONCE_LEN];
56         result[4..].copy_from_slice(&u64::to_be_bytes(self.0));
57         self.0 = self.0.checked_add(1).ok_or(error::Unspecified)?;
58         Ok(aead::Nonce::assume_unique_for_key(result))
59     }
60 }
61 
62 macro_rules! function_bench_name {
63     ( $benchmark_name:ident, $algorithm:expr, $operation:ident) => {{
64         const FUNC_NAME_BASE: &str = concat!(
65             "aead_",
66             stringify!($operation),
67             "_",
68             stringify!($benchmark_name),
69             "_",
70             stringify!($algorithm)
71         );
72 
73         FUNC_NAME_BASE.replace("&aead::", "")
74     }};
75 }
76 
77 macro_rules! bench {
78     ( $benchmark_name:ident, $algorithm:expr, $chunk_len:expr, $ad:expr ) => {
79         pub(super) fn $benchmark_name(c: &mut criterion::Criterion) {
80             use super::{NonceSequence, NONCE};
81             use criterion::{black_box, BatchSize};
82             use ring::{
83                 aead::{self, BoundKey},
84                 rand::{SecureRandom, SystemRandom},
85             };
86 
87             let rng = SystemRandom::new();
88 
89             let mut key_bytes = vec![0u8; $algorithm.key_len()];
90             rng.fill(&mut key_bytes).unwrap();
91 
92             {
93                 let key = aead::UnboundKey::new($algorithm, &key_bytes).unwrap();
94                 let mut key = aead::SealingKey::new(key, NonceSequence::new());
95 
96                 let mut in_out = black_box(vec![0u8; $chunk_len + $algorithm.tag_len()]);
97 
98                 c.bench_function(
99                     &function_bench_name!($benchmark_name, $algorithm, seal),
100                     |b| {
101                         b.iter(|| -> Result<(), ring::error::Unspecified> {
102                             let aad = aead::Aad::from(black_box($ad));
103                             let _tag = key.seal_in_place_separate_tag(aad, &mut in_out)?;
104                             Ok(())
105                         })
106                     },
107                 );
108             }
109 
110             {
111                 let key =
112                     aead::LessSafeKey::new(aead::UnboundKey::new($algorithm, &key_bytes).unwrap());
113 
114                 let ciphertext = {
115                     let nonce = aead::Nonce::assume_unique_for_key(NONCE);
116                     let mut in_out = vec![0u8; $chunk_len + $algorithm.tag_len()];
117                     let aad = aead::Aad::from($ad);
118                     key.seal_in_place_append_tag(nonce, aad, &mut in_out)
119                         .unwrap();
120                     in_out
121                 };
122 
123                 let num_batches = (std::cmp::max(1, 8192 / ciphertext.len()) * 10) as u64;
124 
125                 c.bench_function(
126                     &function_bench_name!($benchmark_name, $algorithm, open),
127                     |b| {
128                         b.iter_batched(
129                             || ciphertext.clone(),
130                             |mut ciphertext| -> Result<(), ring::error::Unspecified> {
131                                 // Optimizes out
132                                 let nonce = aead::Nonce::assume_unique_for_key(NONCE);
133 
134                                 let aad = aead::Aad::from(black_box($ad));
135                                 let _result = key.open_in_place(nonce, aad, &mut ciphertext)?;
136 
137                                 Ok(())
138                             },
139                             BatchSize::NumBatches(num_batches),
140                         )
141                     },
142                 );
143             }
144         }
145     };
146 }
147 
148 macro_rules! benches {
149     ( $name:ident, $algorithm:expr ) => {
150         mod $name {
151             use criterion::criterion_group;
152 
153             // A TLS 1.2 finished message.
154             bench!(
155                 tls12_finished,
156                 $algorithm,
157                 super::TLS12_FINISHED_LEN,
158                 super::TLS12_AD
159             );
160 
161             // A TLS 1.3 finished message.
162             bench!(
163                 tls13_finished,
164                 $algorithm,
165                 super::TLS13_FINISHED_LEN,
166                 super::TLS13_AD
167             );
168 
169             // For comparison with BoringSSL.
170             bench!(tls12_16, $algorithm, 16, super::TLS12_AD);
171 
172             // ~1 packet of data in TLS.
173             bench!(tls12_1350, $algorithm, 1350, super::TLS12_AD);
174             bench!(tls13_1350, $algorithm, 1350, super::TLS13_AD);
175 
176             // For comparison with BoringSSL.
177             bench!(tls12_8192, $algorithm, 8192, super::TLS12_AD);
178             bench!(tls13_8192, $algorithm, 8192, super::TLS13_AD);
179 
180             criterion_group!(
181                 $name,
182                 tls12_finished,
183                 tls13_finished,
184                 tls12_16,
185                 tls12_1350,
186                 tls13_1350,
187                 tls12_8192,
188                 tls13_8192
189             );
190         }
191 
192         // Export Criterion benchmark groups
193         pub use $name::*;
194     };
195 }
196 
197 benches!(aes_128_gcm, &aead::AES_128_GCM);
198 benches!(aes_256_gcm, &aead::AES_256_GCM);
199 benches!(chacha20_poly1305, &aead::CHACHA20_POLY1305);
200 
201 criterion_main!(aes_128_gcm, aes_256_gcm, chacha20_poly1305);
202