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