From 2a272eaf18788e63306ee2a930ffe854657da91e Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Tue, 5 Dec 2023 21:48:14 -0800 Subject: [PATCH] AEAD: Don't store cpu::Features in the key. Have the inner AEAD API take `cpu::features()` for all operations. Then we will be able to write CPU-capability-based tests using (a variation of) the inner API, which will (when implemented) eliminate the need to use SDE and the other various hacks we use for testing all the implementations. --- src/aead.rs | 9 ++++- src/aead/aes.rs | 31 +++++++---------- src/aead/aes_gcm.rs | 50 ++++++++++++++++++++------- src/aead/chacha.rs | 11 ++---- src/aead/chacha20_poly1305.rs | 23 +++++------- src/aead/chacha20_poly1305_openssh.rs | 15 ++++---- src/aead/gcm.rs | 6 ++-- src/aead/less_safe_key.rs | 11 ++++-- src/aead/poly1305.rs | 23 ++++-------- src/aead/quic.rs | 7 ++-- 10 files changed, 98 insertions(+), 88 deletions(-) diff --git a/src/aead.rs b/src/aead.rs index dd1999832e..be059c7fca 100644 --- a/src/aead.rs +++ b/src/aead.rs @@ -134,13 +134,20 @@ impl hkdf::KeyType for &'static Algorithm { pub struct Algorithm { init: fn(key: &[u8], cpu_features: cpu::Features) -> Result, - seal: fn(key: &KeyInner, nonce: Nonce, aad: Aad<&[u8]>, in_out: &mut [u8]) -> Tag, + seal: fn( + key: &KeyInner, + nonce: Nonce, + aad: Aad<&[u8]>, + in_out: &mut [u8], + cpu_features: cpu::Features, + ) -> Tag, open: fn( key: &KeyInner, nonce: Nonce, aad: Aad<&[u8]>, in_out: &mut [u8], src: RangeFrom, + cpu_features: cpu::Features, ) -> Tag, key_len: usize, diff --git a/src/aead/aes.rs b/src/aead/aes.rs index f87427b584..fdacbfbb27 100644 --- a/src/aead/aes.rs +++ b/src/aead/aes.rs @@ -29,7 +29,6 @@ use core::ops::RangeFrom; #[derive(Clone)] pub(super) struct Key { inner: AES_KEY, - cpu_features: cpu::Features, } macro_rules! set_encrypt_key { @@ -172,15 +171,12 @@ impl Key { } }; - Ok(Self { - inner: key, - cpu_features, - }) + Ok(Self { inner: key }) } #[inline] - pub fn encrypt_block(&self, a: Block) -> Block { - match detect_implementation(self.cpu_features) { + pub fn encrypt_block(&self, a: Block, cpu_features: cpu::Features) -> Block { + match detect_implementation(cpu_features) { #[cfg(any( target_arch = "aarch64", target_arch = "arm", @@ -203,8 +199,8 @@ impl Key { } #[inline] - pub fn encrypt_iv_xor_block(&self, iv: Iv, input: Block) -> Block { - let encrypted_iv = self.encrypt_block(iv.into_block_less_safe()); + pub fn encrypt_iv_xor_block(&self, iv: Iv, input: Block, cpu_features: cpu::Features) -> Block { + let encrypted_iv = self.encrypt_block(iv.into_block_less_safe(), cpu_features); encrypted_iv ^ input } @@ -214,12 +210,13 @@ impl Key { in_out: &mut [u8], src: RangeFrom, ctr: &mut Counter, + cpu_features: cpu::Features, ) { let in_out_len = in_out[src.clone()].len(); assert_eq!(in_out_len % BLOCK_LEN, 0); - match detect_implementation(self.cpu_features) { + match detect_implementation(cpu_features) { #[cfg(any( target_arch = "aarch64", target_arch = "arm", @@ -271,7 +268,7 @@ impl Key { #[cfg(target_arch = "x86")] Implementation::VPAES_BSAES => { super::shift::shift_full_blocks(in_out, src, |input| { - self.encrypt_iv_xor_block(ctr.increment(), Block::from(input)) + self.encrypt_iv_xor_block(ctr.increment(), Block::from(input), cpu_features) }); } @@ -283,7 +280,7 @@ impl Key { } pub fn new_mask(&self, sample: Sample) -> [u8; 5] { - let block = self.encrypt_block(Block::from(&sample)); + let block = self.encrypt_block(Block::from(&sample), cpu::features()); let mut out: [u8; 5] = [0; 5]; out.copy_from_slice(&block.as_ref()[..5]); @@ -293,11 +290,8 @@ impl Key { #[cfg(target_arch = "x86_64")] #[must_use] - pub fn is_aes_hw(&self) -> bool { - matches!( - detect_implementation(self.cpu_features), - Implementation::HWAES - ) + pub fn is_aes_hw(&self, cpu_features: cpu::Features) -> bool { + matches!(detect_implementation(cpu_features), Implementation::HWAES) } #[cfg(target_arch = "x86_64")] @@ -445,6 +439,7 @@ mod tests { #[test] pub fn test_aes() { + let cpu_features = cpu::features(); test::run(test_file!("aes_tests.txt"), |section, test_case| { assert_eq!(section, ""); let key = consume_key(test_case, "Key"); @@ -453,7 +448,7 @@ mod tests { let expected_output = test_case.consume_bytes("Output"); let block = Block::from(input); - let output = key.encrypt_block(block); + let output = key.encrypt_block(block, cpu_features); assert_eq!(output.as_ref(), &expected_output[..]); Ok(()) diff --git a/src/aead/aes_gcm.rs b/src/aead/aes_gcm.rs index ca2548506b..73e4ef6cef 100644 --- a/src/aead/aes_gcm.rs +++ b/src/aead/aes_gcm.rs @@ -63,13 +63,22 @@ fn init( cpu_features: cpu::Features, ) -> Result { let aes_key = aes::Key::new(key, variant, cpu_features)?; - let gcm_key = gcm::Key::new(aes_key.encrypt_block(Block::zero()), cpu_features); + let gcm_key = gcm::Key::new( + aes_key.encrypt_block(Block::zero(), cpu_features), + cpu_features, + ); Ok(aead::KeyInner::AesGcm(Key { gcm_key, aes_key })) } const CHUNK_BLOCKS: usize = 3 * 1024 / 16; -fn aes_gcm_seal(key: &aead::KeyInner, nonce: Nonce, aad: Aad<&[u8]>, in_out: &mut [u8]) -> Tag { +fn aes_gcm_seal( + key: &aead::KeyInner, + nonce: Nonce, + aad: Aad<&[u8]>, + in_out: &mut [u8], + cpu_features: cpu::Features, +) -> Tag { let Key { gcm_key, aes_key } = match key { aead::KeyInner::AesGcm(key) => key, _ => unreachable!(), @@ -80,11 +89,11 @@ fn aes_gcm_seal(key: &aead::KeyInner, nonce: Nonce, aad: Aad<&[u8]>, in_out: &mu let total_in_out_len = in_out.len(); let aad_len = aad.0.len(); - let mut auth = gcm::Context::new(gcm_key, aad); + let mut auth = gcm::Context::new(gcm_key, aad, cpu_features); #[cfg(target_arch = "x86_64")] let in_out = { - if !aes_key.is_aes_hw() || !auth.is_avx() { + if !aes_key.is_aes_hw(cpu_features) || !auth.is_avx() { in_out } else { use crate::c; @@ -124,20 +133,27 @@ fn aes_gcm_seal(key: &aead::KeyInner, nonce: Nonce, aad: Aad<&[u8]>, in_out: &mu }; for chunk in whole.chunks_mut(CHUNK_BLOCKS * BLOCK_LEN) { - aes_key.ctr32_encrypt_within(chunk, 0.., &mut ctr); + aes_key.ctr32_encrypt_within(chunk, 0.., &mut ctr, cpu_features); auth.update_blocks(chunk); } if !remainder.is_empty() { let mut input = Block::zero(); input.overwrite_part_at(0, remainder); - let mut output = aes_key.encrypt_iv_xor_block(ctr.into(), input); + let mut output = aes_key.encrypt_iv_xor_block(ctr.into(), input, cpu_features); output.zero_from(remainder.len()); auth.update_block(output); remainder.copy_from_slice(&output.as_ref()[..remainder.len()]); } - finish(aes_key, auth, tag_iv, aad_len, total_in_out_len) + finish( + aes_key, + auth, + tag_iv, + aad_len, + total_in_out_len, + cpu_features, + ) } fn aes_gcm_open( @@ -146,6 +162,7 @@ fn aes_gcm_open( aad: Aad<&[u8]>, in_out: &mut [u8], src: RangeFrom, + cpu_features: cpu::Features, ) -> Tag { let Key { gcm_key, aes_key } = match key { aead::KeyInner::AesGcm(key) => key, @@ -156,7 +173,7 @@ fn aes_gcm_open( let tag_iv = ctr.increment(); let aad_len = aad.0.len(); - let mut auth = gcm::Context::new(gcm_key, aad); + let mut auth = gcm::Context::new(gcm_key, aad, cpu_features); let in_prefix_len = src.start; @@ -164,7 +181,7 @@ fn aes_gcm_open( #[cfg(target_arch = "x86_64")] let in_out = { - if !aes_key.is_aes_hw() || !auth.is_avx() { + if !aes_key.is_aes_hw(cpu_features) || !auth.is_avx() { in_out } else { use crate::c; @@ -218,6 +235,7 @@ fn aes_gcm_open( &mut in_out[output..][..(chunk_len + in_prefix_len)], in_prefix_len.., &mut ctr, + cpu_features, ); output += chunk_len; input += chunk_len; @@ -229,10 +247,17 @@ fn aes_gcm_open( let mut input = Block::zero(); input.overwrite_part_at(0, remainder); auth.update_block(input); - aes_key.encrypt_iv_xor_block(ctr.into(), input) + aes_key.encrypt_iv_xor_block(ctr.into(), input, cpu_features) }); - finish(aes_key, auth, tag_iv, aad_len, total_in_out_len) + finish( + aes_key, + auth, + tag_iv, + aad_len, + total_in_out_len, + cpu_features, + ) } fn finish( @@ -241,6 +266,7 @@ fn finish( tag_iv: aes::Iv, aad_len: usize, in_out_len: usize, + cpu_features: cpu::Features, ) -> Tag { // Authenticate the final block containing the input lengths. let aad_bits = polyfill::u64_from_usize(aad_len) << 3; @@ -251,7 +277,7 @@ fn finish( // Finalize the tag and return it. gcm_ctx.pre_finish(|pre_tag| { - let encrypted_iv = aes_key.encrypt_block(tag_iv.into_block_less_safe()); + let encrypted_iv = aes_key.encrypt_block(tag_iv.into_block_less_safe(), cpu_features); let tag = pre_tag ^ encrypted_iv; Tag(*tag.as_ref()) }) diff --git a/src/aead/chacha.rs b/src/aead/chacha.rs index 660cf34c2d..a8771b4a9c 100644 --- a/src/aead/chacha.rs +++ b/src/aead/chacha.rs @@ -14,7 +14,6 @@ // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. use super::{quic::Sample, Nonce}; -use crate::cpu; #[cfg(any( test, @@ -33,20 +32,14 @@ use core::ops::RangeFrom; #[derive(Clone)] pub struct Key { words: [u32; KEY_LEN / 4], - cpu_features: cpu::Features, } impl Key { - pub(super) fn new(value: [u8; KEY_LEN], cpu_features: cpu::Features) -> Self { + pub(super) fn new(value: [u8; KEY_LEN]) -> Self { Self { words: value.array_split_map(u32::from_le_bytes), - cpu_features, } } - - pub(super) fn cpu_features(&self) -> cpu::Features { - self.cpu_features - } } impl Key { @@ -261,7 +254,7 @@ mod tests { let key = test_case.consume_bytes("Key"); let key: &[u8; KEY_LEN] = key.as_slice().try_into()?; - let key = Key::new(*key, cpu::features()); + let key = Key::new(*key); let ctr = test_case.consume_usize("Ctr"); let nonce = test_case.consume_bytes("Nonce"); diff --git a/src/aead/chacha20_poly1305.rs b/src/aead/chacha20_poly1305.rs index 10c707bf3a..1c9359fc0c 100644 --- a/src/aead/chacha20_poly1305.rs +++ b/src/aead/chacha20_poly1305.rs @@ -39,13 +39,10 @@ pub static CHACHA20_POLY1305: aead::Algorithm = aead::Algorithm { /// Copies |key| into |ctx_buf|. fn chacha20_poly1305_init( key: &[u8], - cpu_features: cpu::Features, + _cpu_features: cpu::Features, ) -> Result { let key: [u8; chacha::KEY_LEN] = key.try_into()?; - Ok(aead::KeyInner::ChaCha20Poly1305(chacha::Key::new( - key, - cpu_features, - ))) + Ok(aead::KeyInner::ChaCha20Poly1305(chacha::Key::new(key))) } fn chacha20_poly1305_seal( @@ -53,6 +50,7 @@ fn chacha20_poly1305_seal( nonce: Nonce, aad: Aad<&[u8]>, in_out: &mut [u8], + cpu_features: cpu::Features, ) -> Tag { let chacha20_key = match key { aead::KeyInner::ChaCha20Poly1305(key) => key, @@ -61,9 +59,7 @@ fn chacha20_poly1305_seal( #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] { - if cpu::intel::SSE41.available(chacha20_key.cpu_features()) - || cpu::arm::NEON.available(chacha20_key.cpu_features()) - { + if cpu::intel::SSE41.available(cpu_features) || cpu::arm::NEON.available(cpu_features) { // XXX: BoringSSL uses `alignas(16)` on `key` instead of on the // structure, but Rust can't do that yet; see // https://github.com/rust-lang/rust/issues/73557. @@ -121,7 +117,7 @@ fn chacha20_poly1305_seal( let mut counter = Counter::zero(nonce); let mut auth = { let key = derive_poly1305_key(chacha20_key, counter.increment()); - poly1305::Context::from_key(key) + poly1305::Context::from_key(key, cpu_features) }; poly1305_update_padded_16(&mut auth, aad.as_ref()); @@ -136,6 +132,7 @@ fn chacha20_poly1305_open( aad: Aad<&[u8]>, in_out: &mut [u8], src: RangeFrom, + cpu_features: cpu::Features, ) -> Tag { let chacha20_key = match key { aead::KeyInner::ChaCha20Poly1305(key) => key, @@ -144,9 +141,7 @@ fn chacha20_poly1305_open( #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] { - if cpu::intel::SSE41.available(chacha20_key.cpu_features()) - || cpu::arm::NEON.available(chacha20_key.cpu_features()) - { + if cpu::intel::SSE41.available(cpu_features) || cpu::arm::NEON.available(cpu_features) { // XXX: BoringSSL uses `alignas(16)` on `key` instead of on the // structure, but Rust can't do that yet; see // https://github.com/rust-lang/rust/issues/73557. @@ -200,7 +195,7 @@ fn chacha20_poly1305_open( let mut counter = Counter::zero(nonce); let mut auth = { let key = derive_poly1305_key(chacha20_key, counter.increment()); - poly1305::Context::from_key(key) + poly1305::Context::from_key(key, cpu_features) }; poly1305_update_padded_16(&mut auth, aad.as_ref()); @@ -258,7 +253,7 @@ fn poly1305_update_padded_16(ctx: &mut poly1305::Context, input: &[u8]) { pub(super) fn derive_poly1305_key(chacha_key: &chacha::Key, iv: Iv) -> poly1305::Key { let mut key_bytes = [0u8; poly1305::KEY_LEN]; chacha_key.encrypt_iv_xor_in_place(iv, &mut key_bytes); - poly1305::Key::new(key_bytes, chacha_key.cpu_features()) + poly1305::Key::new(key_bytes) } #[cfg(test)] diff --git a/src/aead/chacha20_poly1305_openssh.rs b/src/aead/chacha20_poly1305_openssh.rs index 015d41787d..306c9a89ad 100644 --- a/src/aead/chacha20_poly1305_openssh.rs +++ b/src/aead/chacha20_poly1305_openssh.rs @@ -45,7 +45,7 @@ impl SealingKey { /// Constructs a new `SealingKey`. pub fn new(key_material: &[u8; KEY_LEN]) -> Self { Self { - key: Key::new(key_material, cpu::features()), + key: Key::new(key_material), } } @@ -62,6 +62,7 @@ impl SealingKey { plaintext_in_ciphertext_out: &mut [u8], tag_out: &mut [u8; TAG_LEN], ) { + let cpu_features = cpu::features(); let mut counter = make_counter(sequence_number); let poly_key = derive_poly1305_key(&self.key.k_2, counter.increment()); @@ -77,7 +78,7 @@ impl SealingKey { .encrypt_in_place(counter, data_and_padding_in_out); } - let Tag(tag) = poly1305::sign(poly_key, plaintext_in_ciphertext_out); + let Tag(tag) = poly1305::sign(poly_key, plaintext_in_ciphertext_out, cpu_features); tag_out.copy_from_slice(tag.as_ref()); } } @@ -91,7 +92,7 @@ impl OpeningKey { /// Constructs a new `OpeningKey`. pub fn new(key_material: &[u8; KEY_LEN]) -> Self { Self { - key: Key::new(key_material, cpu::features()), + key: Key::new(key_material), } } @@ -148,12 +149,12 @@ struct Key { } impl Key { - fn new(key_material: &[u8; KEY_LEN], cpu_features: cpu::Features) -> Self { + fn new(key_material: &[u8; KEY_LEN]) -> Self { // The first half becomes K_2 and the second half becomes K_1. let (k_2, k_1) = key_material.split_at(chacha::KEY_LEN); Self { - k_1: chacha::Key::new(k_1.try_into().unwrap(), cpu_features), - k_2: chacha::Key::new(k_2.try_into().unwrap(), cpu_features), + k_1: chacha::Key::new(k_1.try_into().unwrap()), + k_2: chacha::Key::new(k_2.try_into().unwrap()), } } } @@ -174,6 +175,6 @@ pub const PACKET_LENGTH_LEN: usize = 4; // 32 bits pub const TAG_LEN: usize = super::TAG_LEN; fn verify(key: poly1305::Key, msg: &[u8], tag: &[u8; TAG_LEN]) -> Result<(), error::Unspecified> { - let Tag(calculated_tag) = poly1305::sign(key, msg); + let Tag(calculated_tag) = poly1305::sign(key, msg, cpu::features()); constant_time::verify_slices_are_equal(calculated_tag.as_ref(), tag) } diff --git a/src/aead/gcm.rs b/src/aead/gcm.rs index 9fb20bc7e5..2771113a15 100644 --- a/src/aead/gcm.rs +++ b/src/aead/gcm.rs @@ -25,7 +25,6 @@ mod gcm_nohw; #[derive(Clone)] pub struct Key { h_table: HTable, - cpu_features: cpu::Features, } impl Key { @@ -36,7 +35,6 @@ impl Key { h_table: HTable { Htable: [u128 { hi: 0, lo: 0 }; HTABLE_LEN], }, - cpu_features, }; let h_table = &mut key.h_table; @@ -92,13 +90,13 @@ pub struct Context { } impl Context { - pub(crate) fn new(key: &Key, aad: Aad<&[u8]>) -> Self { + pub(crate) fn new(key: &Key, aad: Aad<&[u8]>, cpu_features: cpu::Features) -> Self { let mut ctx = Self { inner: ContextInner { Xi: Xi(Block::zero()), Htable: key.h_table.clone(), }, - cpu_features: key.cpu_features, + cpu_features, }; for ad in aad.0.chunks(BLOCK_LEN) { diff --git a/src/aead/less_safe_key.rs b/src/aead/less_safe_key.rs index 009bb3559c..1762b5c90e 100644 --- a/src/aead/less_safe_key.rs +++ b/src/aead/less_safe_key.rs @@ -171,7 +171,8 @@ fn open_within_<'in_out>( let ciphertext_len = in_out.get(src.clone()).ok_or(error::Unspecified)?.len(); check_per_nonce_max_bytes(key.algorithm, ciphertext_len)?; - let Tag(calculated_tag) = (key.algorithm.open)(&key.inner, nonce, aad, in_out, src); + let Tag(calculated_tag) = + (key.algorithm.open)(&key.inner, nonce, aad, in_out, src, cpu::features()); if constant_time::verify_slices_are_equal(calculated_tag.as_ref(), received_tag.as_ref()) .is_err() @@ -198,7 +199,13 @@ pub(super) fn seal_in_place_separate_tag_( in_out: &mut [u8], ) -> Result { check_per_nonce_max_bytes(key.algorithm(), in_out.len())?; - Ok((key.algorithm.seal)(&key.inner, nonce, aad, in_out)) + Ok((key.algorithm.seal)( + &key.inner, + nonce, + aad, + in_out, + cpu::features(), + )) } fn check_per_nonce_max_bytes(alg: &Algorithm, in_out_len: usize) -> Result<(), error::Unspecified> { diff --git a/src/aead/poly1305.rs b/src/aead/poly1305.rs index f7c202c49d..3817e1d2bc 100644 --- a/src/aead/poly1305.rs +++ b/src/aead/poly1305.rs @@ -21,7 +21,6 @@ use crate::{c, cpu}; /// A Poly1305 key. pub(super) struct Key { key_and_nonce: [u8; KEY_LEN], - cpu_features: cpu::Features, } pub(super) const BLOCK_LEN: usize = 16; @@ -29,11 +28,8 @@ pub(super) const KEY_LEN: usize = 2 * BLOCK_LEN; impl Key { #[inline] - pub(super) fn new(key_and_nonce: [u8; KEY_LEN], cpu_features: cpu::Features) -> Self { - Self { - key_and_nonce, - cpu_features, - } + pub(super) fn new(key_and_nonce: [u8; KEY_LEN]) -> Self { + Self { key_and_nonce } } } @@ -79,12 +75,7 @@ macro_rules! dispatch { impl Context { #[inline] - pub(super) fn from_key( - Key { - key_and_nonce, - cpu_features, - }: Key, - ) -> Self { + pub(super) fn from_key(Key { key_and_nonce }: Key, cpu_features: cpu::Features) -> Self { let mut ctx = Self { state: poly1305_state([0u8; OPAQUE_LEN]), cpu_features, @@ -123,8 +114,8 @@ impl Context { /// /// This is used by chacha20_poly1305_openssh and the standalone /// poly1305 test vectors. -pub(super) fn sign(key: Key, input: &[u8]) -> Tag { - let mut ctx = Context::from_key(key); +pub(super) fn sign(key: Key, input: &[u8], cpu_features: cpu::Features) -> Tag { + let mut ctx = Context::from_key(key, cpu_features); ctx.update(input); ctx.finish() } @@ -144,8 +135,8 @@ mod tests { let key: &[u8; KEY_LEN] = key.as_slice().try_into().unwrap(); let input = test_case.consume_bytes("Input"); let expected_mac = test_case.consume_bytes("MAC"); - let key = Key::new(*key, cpu_features); - let Tag(actual_mac) = sign(key, &input); + let key = Key::new(*key); + let Tag(actual_mac) = sign(key, &input, cpu_features); assert_eq!(expected_mac, actual_mac.as_ref()); Ok(()) diff --git a/src/aead/quic.rs b/src/aead/quic.rs index 9af4771ff0..a5d2d738d2 100644 --- a/src/aead/quic.rs +++ b/src/aead/quic.rs @@ -170,12 +170,9 @@ pub static CHACHA20: Algorithm = Algorithm { id: AlgorithmID::CHACHA20, }; -fn chacha20_init(key: &[u8], cpu_features: cpu::Features) -> Result { +fn chacha20_init(key: &[u8], _cpu_features: cpu::Features) -> Result { let chacha20_key: [u8; chacha::KEY_LEN] = key.try_into()?; - Ok(KeyInner::ChaCha20(chacha::Key::new( - chacha20_key, - cpu_features, - ))) + Ok(KeyInner::ChaCha20(chacha::Key::new(chacha20_key))) } fn chacha20_new_mask(key: &KeyInner, sample: Sample) -> [u8; 5] {