| // Copyright 2018 Brian Smith. |
| // |
| // Permission to use, copy, modify, and/or distribute this software for any |
| // purpose with or without fee is hereby granted, provided that the above |
| // copyright notice and this permission notice appear in all copies. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES |
| // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY |
| // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
| // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
| // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| |
| use super::{ |
| nonce::{self, Iv}, |
| Block, Direction, BLOCK_LEN, |
| }; |
| |
| #[cfg(not(target_arch = "aarch64"))] |
| use super::shift; |
| |
| use crate::{bits::BitLength, c, cpu, endian::*, error, polyfill}; |
| |
| pub(crate) struct Key { |
| inner: AES_KEY, |
| cpu_features: cpu::Features, |
| } |
| |
| impl Key { |
| #[inline] |
| pub fn new( |
| bytes: &[u8], |
| variant: Variant, |
| cpu_features: cpu::Features, |
| ) -> Result<Self, error::Unspecified> { |
| let key_bits = match variant { |
| Variant::AES_128 => BitLength::from_usize_bits(128), |
| Variant::AES_256 => BitLength::from_usize_bits(256), |
| }; |
| if BitLength::from_usize_bytes(bytes.len())? != key_bits { |
| return Err(error::Unspecified); |
| } |
| |
| let mut key = AES_KEY { |
| rd_key: [0u32; 4 * (MAX_ROUNDS + 1)], |
| rounds: 0, |
| }; |
| |
| match detect_implementation(cpu_features) { |
| Implementation::HWAES => { |
| extern "C" { |
| fn GFp_aes_hw_set_encrypt_key( |
| user_key: *const u8, |
| bits: c::uint, |
| key: &mut AES_KEY, |
| ) -> ZeroMeansSuccess; |
| } |
| Result::from(unsafe { |
| GFp_aes_hw_set_encrypt_key( |
| bytes.as_ptr(), |
| key_bits.as_usize_bits() as c::uint, |
| &mut key, |
| ) |
| })?; |
| } |
| |
| #[cfg(any(target_arch = "aarch64", target_arch = "x86_64", target_arch = "x86"))] |
| Implementation::VPAES => { |
| extern "C" { |
| fn GFp_vpaes_set_encrypt_key( |
| user_key: *const u8, |
| bits: c::uint, |
| key: &mut AES_KEY, |
| ) -> ZeroMeansSuccess; |
| } |
| Result::from(unsafe { |
| GFp_vpaes_set_encrypt_key( |
| bytes.as_ptr(), |
| key_bits.as_usize_bits() as c::uint, |
| &mut key, |
| ) |
| })?; |
| } |
| |
| #[cfg(not(target_arch = "aarch64"))] |
| _ => { |
| extern "C" { |
| fn GFp_aes_nohw_set_encrypt_key( |
| user_key: *const u8, |
| bits: c::uint, |
| key: &mut AES_KEY, |
| ) -> ZeroMeansSuccess; |
| } |
| Result::from(unsafe { |
| GFp_aes_nohw_set_encrypt_key( |
| bytes.as_ptr(), |
| key_bits.as_usize_bits() as c::uint, |
| &mut key, |
| ) |
| })?; |
| } |
| }; |
| |
| Ok(Self { |
| inner: key, |
| cpu_features, |
| }) |
| } |
| |
| #[inline] |
| pub fn encrypt_block(&self, mut a: Block) -> Block { |
| let aliasing_const: *const Block = &a; |
| let aliasing_mut: *mut Block = &mut a; |
| |
| match detect_implementation(self.cpu_features) { |
| Implementation::HWAES => { |
| extern "C" { |
| fn GFp_aes_hw_encrypt(a: *const Block, r: *mut Block, key: &AES_KEY); |
| } |
| unsafe { |
| GFp_aes_hw_encrypt(aliasing_const, aliasing_mut, &self.inner); |
| } |
| } |
| |
| #[cfg(any(target_arch = "aarch64", target_arch = "x86_64", target_arch = "x86"))] |
| Implementation::VPAES => { |
| extern "C" { |
| fn GFp_vpaes_encrypt(a: *const Block, r: *mut Block, key: &AES_KEY); |
| } |
| unsafe { |
| GFp_vpaes_encrypt(aliasing_const, aliasing_mut, &self.inner); |
| } |
| } |
| |
| #[cfg(not(target_arch = "aarch64"))] |
| _ => { |
| extern "C" { |
| fn GFp_aes_nohw_encrypt(a: *const Block, r: *mut Block, key: &AES_KEY); |
| } |
| unsafe { |
| GFp_aes_nohw_encrypt(aliasing_const, aliasing_mut, &self.inner); |
| } |
| } |
| } |
| |
| a |
| } |
| |
| #[inline] |
| pub fn encrypt_iv_xor_block(&self, iv: Iv, input: Block) -> Block { |
| let mut output = self.encrypt_block(iv.into_block_less_safe()); |
| output.bitxor_assign(input); |
| output |
| } |
| |
| #[inline] |
| pub(super) fn ctr32_encrypt_blocks( |
| &self, |
| in_out: &mut [u8], |
| direction: Direction, |
| ctr: &mut Counter, |
| ) { |
| let output: *mut u8 = in_out.as_mut_ptr(); |
| let in_prefix_len = match direction { |
| Direction::Opening { in_prefix_len } => in_prefix_len, |
| Direction::Sealing => 0, |
| }; |
| let input: *const u8 = in_out[in_prefix_len..].as_ptr(); |
| |
| let in_out_len = in_out.len().checked_sub(in_prefix_len).unwrap(); |
| |
| assert_eq!(in_out_len % BLOCK_LEN, 0); |
| let blocks = in_out_len / BLOCK_LEN; |
| let blocks_u32 = blocks as u32; |
| assert_eq!(blocks, polyfill::usize_from_u32(blocks_u32)); |
| |
| match detect_implementation(self.cpu_features) { |
| Implementation::HWAES => { |
| extern "C" { |
| fn GFp_aes_hw_ctr32_encrypt_blocks( |
| input: *const u8, |
| output: *mut u8, |
| blocks: c::size_t, |
| key: &AES_KEY, |
| ivec: &Counter, |
| ); |
| } |
| unsafe { |
| GFp_aes_hw_ctr32_encrypt_blocks(input, output, blocks, &self.inner, ctr); |
| } |
| ctr.increment_by_less_safe(blocks_u32); |
| } |
| |
| #[cfg(target_arch = "aarch64")] |
| Implementation::VPAES => { |
| extern "C" { |
| fn GFp_vpaes_ctr32_encrypt_blocks( |
| input: *const u8, |
| output: *mut u8, |
| blocks: c::size_t, |
| key: &AES_KEY, |
| ivec: &Counter, |
| ); |
| } |
| unsafe { |
| GFp_vpaes_ctr32_encrypt_blocks(input, output, blocks, &self.inner, ctr); |
| } |
| ctr.increment_by_less_safe(blocks_u32); |
| } |
| |
| #[cfg(target_arch = "arm")] |
| Implementation::BSAES => { |
| extern "C" { |
| fn GFp_bsaes_ctr32_encrypt_blocks( |
| input: *const u8, |
| output: *mut u8, |
| blocks: c::size_t, |
| key: &AES_KEY, |
| ivec: &Counter, |
| ); |
| } |
| unsafe { |
| GFp_bsaes_ctr32_encrypt_blocks(input, output, blocks, &self.inner, ctr); |
| } |
| ctr.increment_by_less_safe(blocks_u32); |
| } |
| |
| #[cfg(not(target_arch = "aarch64"))] |
| _ => { |
| shift::shift_full_blocks(in_out, in_prefix_len, |input| { |
| self.encrypt_iv_xor_block(ctr.increment(), Block::from(input)) |
| }); |
| } |
| } |
| } |
| |
| pub fn new_mask(&self, sample: Block) -> [u8; 5] { |
| let block = self.encrypt_block(sample); |
| |
| let mut out: [u8; 5] = [0; 5]; |
| out.copy_from_slice(&block.as_ref()[..5]); |
| |
| out |
| } |
| |
| #[cfg(target_arch = "x86_64")] |
| #[must_use] |
| pub fn is_aes_hw(&self) -> bool { |
| match detect_implementation(self.cpu_features) { |
| Implementation::HWAES => true, |
| _ => false, |
| } |
| } |
| |
| #[cfg(target_arch = "x86_64")] |
| #[must_use] |
| pub(super) fn inner_less_safe(&self) -> &AES_KEY { |
| &self.inner |
| } |
| } |
| |
| // Keep this in sync with AES_KEY in aes.h. |
| #[repr(C)] |
| pub(super) struct AES_KEY { |
| pub rd_key: [u32; 4 * (MAX_ROUNDS + 1)], |
| pub rounds: c::uint, |
| } |
| |
| // Keep this in sync with `AES_MAXNR` in aes.h. |
| const MAX_ROUNDS: usize = 14; |
| |
| pub enum Variant { |
| AES_128, |
| AES_256, |
| } |
| |
| pub type Counter = nonce::Counter<BigEndian<u32>>; |
| |
| #[repr(C)] // Only so `Key` can be `#[repr(C)]` |
| #[derive(Clone, Copy)] |
| pub enum Implementation { |
| HWAES = 1, |
| |
| #[cfg(any(target_arch = "aarch64", target_arch = "x86_64", target_arch = "x86"))] |
| VPAES = 2, |
| |
| #[cfg(target_arch = "arm")] |
| BSAES = 3, |
| |
| #[cfg(not(target_arch = "aarch64"))] |
| Fallback = 4, |
| } |
| |
| fn detect_implementation(cpu_features: cpu::Features) -> Implementation { |
| if cpu::intel::AES.available(cpu_features) || cpu::arm::AES.available(cpu_features) { |
| return Implementation::HWAES; |
| } |
| |
| #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] |
| { |
| if cpu::intel::SSSE3.available(cpu_features) { |
| return Implementation::VPAES; |
| } |
| } |
| |
| #[cfg(target_arch = "arm")] |
| { |
| if cpu::arm::NEON.available(cpu_features) { |
| return Implementation::BSAES; |
| } |
| } |
| |
| #[cfg(target_arch = "aarch64")] |
| { |
| Implementation::VPAES |
| } |
| |
| #[cfg(not(target_arch = "aarch64"))] |
| { |
| Implementation::Fallback |
| } |
| } |
| |
| #[must_use] |
| #[repr(transparent)] |
| pub struct ZeroMeansSuccess(c::int); |
| |
| impl From<ZeroMeansSuccess> for Result<(), error::Unspecified> { |
| fn from(ZeroMeansSuccess(value): ZeroMeansSuccess) -> Self { |
| if value == 0 { |
| Ok(()) |
| } else { |
| Err(error::Unspecified) |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::{super::BLOCK_LEN, *}; |
| use crate::test; |
| use core::convert::TryInto; |
| |
| #[test] |
| pub fn test_aes() { |
| test::run(test_file!("aes_tests.txt"), |section, test_case| { |
| assert_eq!(section, ""); |
| let key = consume_key(test_case, "Key"); |
| let input = test_case.consume_bytes("Input"); |
| let input: &[u8; BLOCK_LEN] = input.as_slice().try_into()?; |
| let expected_output = test_case.consume_bytes("Output"); |
| |
| let block = Block::from(input); |
| let output = key.encrypt_block(block); |
| assert_eq!(output.as_ref(), &expected_output[..]); |
| |
| Ok(()) |
| }) |
| } |
| |
| fn consume_key(test_case: &mut test::TestCase, name: &str) -> Key { |
| let key = test_case.consume_bytes(name); |
| let variant = match key.len() { |
| 16 => Variant::AES_128, |
| 32 => Variant::AES_256, |
| _ => unreachable!(), |
| }; |
| Key::new(&key[..], variant, cpu::features()).unwrap() |
| } |
| } |