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