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