1 // Copyright 2018 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 use super::{
16     block::{Block, BLOCK_LEN},
17     nonce::Nonce,
18     quic::Sample,
19 };
20 use crate::{
21     bits::BitLength,
22     c, cpu,
23     endian::{ArrayEncoding, BigEndian},
24     error,
25     polyfill::{self, ChunksFixed},
26 };
27 use core::ops::RangeFrom;
28 
29 #[derive(Clone)]
30 pub(super) struct Key {
31     inner: AES_KEY,
32     cpu_features: cpu::Features,
33 }
34 
35 macro_rules! set_encrypt_key {
36     ( $name:ident, $bytes:expr, $key_bits:expr, $key:expr ) => {{
37         prefixed_extern! {
38             fn $name(user_key: *const u8, bits: c::uint, key: &mut AES_KEY) -> c::int;
39         }
40         set_encrypt_key($name, $bytes, $key_bits, $key)
41     }};
42 }
43 
44 #[inline]
set_encrypt_key( f: unsafe extern "C" fn(*const u8, c::uint, &mut AES_KEY) -> c::int, bytes: &[u8], key_bits: BitLength, key: &mut AES_KEY, ) -> Result<(), error::Unspecified>45 fn set_encrypt_key(
46     f: unsafe extern "C" fn(*const u8, c::uint, &mut AES_KEY) -> c::int,
47     bytes: &[u8],
48     key_bits: BitLength,
49     key: &mut AES_KEY,
50 ) -> Result<(), error::Unspecified> {
51     // Unusually, in this case zero means success and non-zero means failure.
52     if 0 == unsafe { f(bytes.as_ptr(), key_bits.as_usize_bits() as c::uint, key) } {
53         Ok(())
54     } else {
55         Err(error::Unspecified)
56     }
57 }
58 
59 macro_rules! encrypt_block {
60     ($name:ident, $block:expr, $key:expr) => {{
61         prefixed_extern! {
62             fn $name(a: &Block, r: *mut Block, key: &AES_KEY);
63         }
64         encrypt_block_($name, $block, $key)
65     }};
66 }
67 
68 #[inline]
encrypt_block_( f: unsafe extern "C" fn(&Block, *mut Block, &AES_KEY), a: Block, key: &Key, ) -> Block69 fn encrypt_block_(
70     f: unsafe extern "C" fn(&Block, *mut Block, &AES_KEY),
71     a: Block,
72     key: &Key,
73 ) -> Block {
74     let mut result = core::mem::MaybeUninit::uninit();
75     unsafe {
76         f(&a, result.as_mut_ptr(), &key.inner);
77         result.assume_init()
78     }
79 }
80 
81 macro_rules! ctr32_encrypt_blocks {
82     ($name:ident, $in_out:expr, $src:expr, $key:expr, $ivec:expr ) => {{
83         prefixed_extern! {
84             fn $name(
85                 input: *const [u8; BLOCK_LEN],
86                 output: *mut [u8; BLOCK_LEN],
87                 blocks: c::size_t,
88                 key: &AES_KEY,
89                 ivec: &Counter,
90             );
91         }
92         ctr32_encrypt_blocks_($name, $in_out, $src, $key, $ivec)
93     }};
94 }
95 
96 #[inline]
ctr32_encrypt_blocks_( f: unsafe extern "C" fn( input: *const [u8; BLOCK_LEN], output: *mut [u8; BLOCK_LEN], blocks: c::size_t, key: &AES_KEY, ivec: &Counter, ), in_out: &mut [u8], src: RangeFrom<usize>, key: &AES_KEY, ctr: &mut Counter, )97 fn ctr32_encrypt_blocks_(
98     f: unsafe extern "C" fn(
99         input: *const [u8; BLOCK_LEN],
100         output: *mut [u8; BLOCK_LEN],
101         blocks: c::size_t,
102         key: &AES_KEY,
103         ivec: &Counter,
104     ),
105     in_out: &mut [u8],
106     src: RangeFrom<usize>,
107     key: &AES_KEY,
108     ctr: &mut Counter,
109 ) {
110     let in_out_len = in_out[src.clone()].len();
111     assert_eq!(in_out_len % BLOCK_LEN, 0);
112 
113     let blocks = in_out_len / BLOCK_LEN;
114     let blocks_u32 = blocks as u32;
115     assert_eq!(blocks, polyfill::usize_from_u32(blocks_u32));
116 
117     let input = in_out[src].as_ptr() as *const [u8; BLOCK_LEN];
118     let output = in_out.as_mut_ptr() as *mut [u8; BLOCK_LEN];
119 
120     unsafe {
121         f(input, output, blocks, key, ctr);
122     }
123     ctr.increment_by_less_safe(blocks_u32);
124 }
125 
126 impl Key {
127     #[inline]
new( bytes: &[u8], variant: Variant, cpu_features: cpu::Features, ) -> Result<Self, error::Unspecified>128     pub fn new(
129         bytes: &[u8],
130         variant: Variant,
131         cpu_features: cpu::Features,
132     ) -> Result<Self, error::Unspecified> {
133         let key_bits = match variant {
134             Variant::AES_128 => BitLength::from_usize_bits(128),
135             Variant::AES_256 => BitLength::from_usize_bits(256),
136         };
137         if BitLength::from_usize_bytes(bytes.len())? != key_bits {
138             return Err(error::Unspecified);
139         }
140 
141         let mut key = AES_KEY {
142             rd_key: [0u32; 4 * (MAX_ROUNDS + 1)],
143             rounds: 0,
144         };
145 
146         match detect_implementation(cpu_features) {
147             #[cfg(any(
148                 target_arch = "aarch64",
149                 target_arch = "arm",
150                 target_arch = "x86_64",
151                 target_arch = "x86"
152             ))]
153             Implementation::HWAES => {
154                 set_encrypt_key!(aes_hw_set_encrypt_key, bytes, key_bits, &mut key)?
155             }
156 
157             #[cfg(any(
158                 target_arch = "aarch64",
159                 target_arch = "arm",
160                 target_arch = "x86_64",
161                 target_arch = "x86"
162             ))]
163             Implementation::VPAES_BSAES => {
164                 set_encrypt_key!(vpaes_set_encrypt_key, bytes, key_bits, &mut key)?
165             }
166 
167             #[cfg(not(target_arch = "aarch64"))]
168             Implementation::NOHW => {
169                 set_encrypt_key!(aes_nohw_set_encrypt_key, bytes, key_bits, &mut key)?
170             }
171         };
172 
173         Ok(Self {
174             inner: key,
175             cpu_features,
176         })
177     }
178 
179     #[inline]
encrypt_block(&self, a: Block) -> Block180     pub fn encrypt_block(&self, a: Block) -> Block {
181         match detect_implementation(self.cpu_features) {
182             #[cfg(any(
183                 target_arch = "aarch64",
184                 target_arch = "arm",
185                 target_arch = "x86_64",
186                 target_arch = "x86"
187             ))]
188             Implementation::HWAES => encrypt_block!(aes_hw_encrypt, a, self),
189 
190             #[cfg(any(
191                 target_arch = "aarch64",
192                 target_arch = "arm",
193                 target_arch = "x86_64",
194                 target_arch = "x86"
195             ))]
196             Implementation::VPAES_BSAES => encrypt_block!(vpaes_encrypt, a, self),
197 
198             #[cfg(not(target_arch = "aarch64"))]
199             Implementation::NOHW => encrypt_block!(aes_nohw_encrypt, a, self),
200         }
201     }
202 
203     #[inline]
encrypt_iv_xor_block(&self, iv: Iv, input: Block) -> Block204     pub fn encrypt_iv_xor_block(&self, iv: Iv, input: Block) -> Block {
205         let encrypted_iv = self.encrypt_block(Block::from(iv.as_bytes_less_safe()));
206         encrypted_iv ^ input
207     }
208 
209     #[inline]
ctr32_encrypt_within( &self, in_out: &mut [u8], src: RangeFrom<usize>, ctr: &mut Counter, )210     pub(super) fn ctr32_encrypt_within(
211         &self,
212         in_out: &mut [u8],
213         src: RangeFrom<usize>,
214         ctr: &mut Counter,
215     ) {
216         let in_out_len = in_out[src.clone()].len();
217 
218         assert_eq!(in_out_len % BLOCK_LEN, 0);
219 
220         match detect_implementation(self.cpu_features) {
221             #[cfg(any(
222                 target_arch = "aarch64",
223                 target_arch = "arm",
224                 target_arch = "x86_64",
225                 target_arch = "x86"
226             ))]
227             Implementation::HWAES => {
228                 ctr32_encrypt_blocks!(aes_hw_ctr32_encrypt_blocks, in_out, src, &self.inner, ctr)
229             }
230 
231             #[cfg(any(target_arch = "aarch64", target_arch = "arm", target_arch = "x86_64"))]
232             Implementation::VPAES_BSAES => {
233                 // 8 blocks is the cut-off point where it's faster to use BSAES.
234                 #[cfg(target_arch = "arm")]
235                 let in_out = if in_out_len >= 8 * BLOCK_LEN {
236                     let remainder = in_out_len % (8 * BLOCK_LEN);
237                     let bsaes_in_out_len = if remainder < (4 * BLOCK_LEN) {
238                         in_out_len - remainder
239                     } else {
240                         in_out_len
241                     };
242 
243                     let mut bsaes_key = AES_KEY {
244                         rd_key: [0u32; 4 * (MAX_ROUNDS + 1)],
245                         rounds: 0,
246                     };
247                     prefixed_extern! {
248                         fn vpaes_encrypt_key_to_bsaes(bsaes_key: &mut AES_KEY, vpaes_key: &AES_KEY);
249                     }
250                     unsafe {
251                         vpaes_encrypt_key_to_bsaes(&mut bsaes_key, &self.inner);
252                     }
253                     ctr32_encrypt_blocks!(
254                         bsaes_ctr32_encrypt_blocks,
255                         &mut in_out[..(src.start + bsaes_in_out_len)],
256                         src.clone(),
257                         &bsaes_key,
258                         ctr
259                     );
260 
261                     &mut in_out[bsaes_in_out_len..]
262                 } else {
263                     in_out
264                 };
265 
266                 ctr32_encrypt_blocks!(vpaes_ctr32_encrypt_blocks, in_out, src, &self.inner, ctr)
267             }
268 
269             #[cfg(target_arch = "x86")]
270             Implementation::VPAES_BSAES => {
271                 super::shift::shift_full_blocks(in_out, src, |input| {
272                     self.encrypt_iv_xor_block(ctr.increment(), Block::from(input))
273                 });
274             }
275 
276             #[cfg(not(target_arch = "aarch64"))]
277             Implementation::NOHW => {
278                 ctr32_encrypt_blocks!(aes_nohw_ctr32_encrypt_blocks, in_out, src, &self.inner, ctr)
279             }
280         }
281     }
282 
new_mask(&self, sample: Sample) -> [u8; 5]283     pub fn new_mask(&self, sample: Sample) -> [u8; 5] {
284         let block = self.encrypt_block(Block::from(&sample));
285 
286         let mut out: [u8; 5] = [0; 5];
287         out.copy_from_slice(&block.as_ref()[..5]);
288 
289         out
290     }
291 
292     #[cfg(target_arch = "x86_64")]
293     #[must_use]
is_aes_hw(&self) -> bool294     pub fn is_aes_hw(&self) -> bool {
295         matches!(
296             detect_implementation(self.cpu_features),
297             Implementation::HWAES
298         )
299     }
300 
301     #[cfg(target_arch = "x86_64")]
302     #[must_use]
inner_less_safe(&self) -> &AES_KEY303     pub(super) fn inner_less_safe(&self) -> &AES_KEY {
304         &self.inner
305     }
306 }
307 
308 // Keep this in sync with AES_KEY in aes.h.
309 #[repr(C)]
310 #[derive(Clone)]
311 pub(super) struct AES_KEY {
312     pub rd_key: [u32; 4 * (MAX_ROUNDS + 1)],
313     pub rounds: c::uint,
314 }
315 
316 // Keep this in sync with `AES_MAXNR` in aes.h.
317 const MAX_ROUNDS: usize = 14;
318 
319 pub enum Variant {
320     AES_128,
321     AES_256,
322 }
323 
324 /// Nonce || Counter, all big-endian.
325 #[repr(transparent)]
326 pub(super) struct Counter([BigEndian<u32>; 4]);
327 
328 impl Counter {
one(nonce: Nonce) -> Self329     pub fn one(nonce: Nonce) -> Self {
330         let nonce = nonce.as_ref().chunks_fixed();
331         Self([nonce[0].into(), nonce[1].into(), nonce[2].into(), 1.into()])
332     }
333 
increment(&mut self) -> Iv334     pub fn increment(&mut self) -> Iv {
335         let iv = Iv(self.0);
336         self.increment_by_less_safe(1);
337         iv
338     }
339 
increment_by_less_safe(&mut self, increment_by: u32)340     fn increment_by_less_safe(&mut self, increment_by: u32) {
341         let old_value: u32 = self.0[3].into();
342         self.0[3] = (old_value + increment_by).into();
343     }
344 }
345 
346 /// The IV for a single block encryption.
347 ///
348 /// Intentionally not `Clone` to ensure each is used only once.
349 pub struct Iv([BigEndian<u32>; 4]);
350 
351 impl From<Counter> for Iv {
from(counter: Counter) -> Self352     fn from(counter: Counter) -> Self {
353         Self(counter.0)
354     }
355 }
356 
357 impl Iv {
as_bytes_less_safe(&self) -> &[u8; 16]358     pub(super) fn as_bytes_less_safe(&self) -> &[u8; 16] {
359         self.0.as_byte_array()
360     }
361 }
362 
363 #[repr(C)] // Only so `Key` can be `#[repr(C)]`
364 #[derive(Clone, Copy)]
365 pub enum Implementation {
366     #[cfg(any(
367         target_arch = "aarch64",
368         target_arch = "arm",
369         target_arch = "x86_64",
370         target_arch = "x86"
371     ))]
372     HWAES = 1,
373 
374     // On "arm" only, this indicates that the bsaes implementation may be used.
375     #[cfg(any(
376         target_arch = "aarch64",
377         target_arch = "arm",
378         target_arch = "x86_64",
379         target_arch = "x86"
380     ))]
381     VPAES_BSAES = 2,
382 
383     #[cfg(not(target_arch = "aarch64"))]
384     NOHW = 3,
385 }
386 
detect_implementation(cpu_features: cpu::Features) -> Implementation387 fn detect_implementation(cpu_features: cpu::Features) -> Implementation {
388     // `cpu_features` is only used for specific platforms.
389     #[cfg(not(any(
390         target_arch = "aarch64",
391         target_arch = "arm",
392         target_arch = "x86_64",
393         target_arch = "x86"
394     )))]
395     let _cpu_features = cpu_features;
396 
397     #[cfg(any(
398         target_arch = "aarch64",
399         target_arch = "arm",
400         target_arch = "x86_64",
401         target_arch = "x86"
402     ))]
403     {
404         if cpu::intel::AES.available(cpu_features) || cpu::arm::AES.available(cpu_features) {
405             return Implementation::HWAES;
406         }
407     }
408 
409     #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
410     {
411         if cpu::intel::SSSE3.available(cpu_features) {
412             return Implementation::VPAES_BSAES;
413         }
414     }
415 
416     #[cfg(target_arch = "arm")]
417     {
418         if cpu::arm::NEON.available(cpu_features) {
419             return Implementation::VPAES_BSAES;
420         }
421     }
422 
423     #[cfg(target_arch = "aarch64")]
424     {
425         Implementation::VPAES_BSAES
426     }
427 
428     #[cfg(not(target_arch = "aarch64"))]
429     {
430         Implementation::NOHW
431     }
432 }
433 
434 #[cfg(test)]
435 mod tests {
436     use super::*;
437     use crate::test;
438 
439     #[test]
test_aes()440     pub fn test_aes() {
441         test::run(test_file!("aes_tests.txt"), |section, test_case| {
442             assert_eq!(section, "");
443             let key = consume_key(test_case, "Key");
444             let input = test_case.consume_bytes("Input");
445             let input: &[u8; BLOCK_LEN] = input.as_slice().try_into()?;
446             let expected_output = test_case.consume_bytes("Output");
447 
448             let block = Block::from(input);
449             let output = key.encrypt_block(block);
450             assert_eq!(output.as_ref(), &expected_output[..]);
451 
452             Ok(())
453         })
454     }
455 
consume_key(test_case: &mut test::TestCase, name: &str) -> Key456     fn consume_key(test_case: &mut test::TestCase, name: &str) -> Key {
457         let key = test_case.consume_bytes(name);
458         let variant = match key.len() {
459             16 => Variant::AES_128,
460             32 => Variant::AES_256,
461             _ => unreachable!(),
462         };
463         Key::new(&key[..], variant, cpu::features()).unwrap()
464     }
465 }
466