AEAD internals: Have each AEAD key type remember the CPU features.

Move the `cpu_features` field from `KeyInner` to the (inner) inner key
types. This makes the AES-GCM code clearer.
This commit is contained in:
Brian Smith 2021-03-05 12:11:19 -08:00
parent 4a0c4830af
commit 2be8976f70
8 changed files with 60 additions and 69 deletions

View File

@ -144,20 +144,13 @@ impl hkdf::KeyType for &'static Algorithm {
pub struct Algorithm {
init: fn(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified>,
seal: fn(
key: &KeyInner,
nonce: Nonce,
aad: Aad<&[u8]>,
in_out: &mut [u8],
cpu_features: cpu::Features,
) -> Tag,
seal: fn(key: &KeyInner, nonce: Nonce, aad: Aad<&[u8]>, in_out: &mut [u8]) -> Tag,
open: fn(
key: &KeyInner,
nonce: Nonce,
aad: Aad<&[u8]>,
in_out: &mut [u8],
src: RangeFrom<usize>,
cpu_features: cpu::Features,
) -> Tag,
key_len: usize,

View File

@ -65,13 +65,7 @@ fn init(
const CHUNK_BLOCKS: usize = 3 * 1024 / 16;
fn aes_gcm_seal(
key: &aead::KeyInner,
nonce: Nonce,
aad: Aad<&[u8]>,
in_out: &mut [u8],
cpu_features: cpu::Features,
) -> Tag {
fn aes_gcm_seal(key: &aead::KeyInner, nonce: Nonce, aad: Aad<&[u8]>, in_out: &mut [u8]) -> Tag {
let Key { aes_key, gcm_key } = match key {
aead::KeyInner::AesGcm(key) => key,
_ => unreachable!(),
@ -82,7 +76,7 @@ fn aes_gcm_seal(
let total_in_out_len = in_out.len();
let aad_len = aad.0.len();
let mut auth = gcm::Context::new(gcm_key, aad, cpu_features);
let mut auth = gcm::Context::new(gcm_key, aad);
#[cfg(target_arch = "x86_64")]
let in_out = {
@ -144,7 +138,6 @@ fn aes_gcm_open(
aad: Aad<&[u8]>,
in_out: &mut [u8],
src: RangeFrom<usize>,
cpu_features: cpu::Features,
) -> Tag {
let Key { aes_key, gcm_key } = match key {
aead::KeyInner::AesGcm(key) => key,
@ -155,7 +148,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, cpu_features);
let mut auth = gcm::Context::new(gcm_key, aad);
let in_prefix_len = src.start;

View File

@ -14,7 +14,10 @@
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
use super::{quic::Sample, Nonce};
use crate::polyfill::{array_map::Map, ChunksFixed};
use crate::{
cpu,
polyfill::{array_map::Map, ChunksFixed},
};
#[cfg(any(
test,
@ -30,13 +33,22 @@ mod fallback;
use core::ops::RangeFrom;
#[repr(transparent)]
pub struct Key([u32; KEY_LEN / 4]);
pub struct Key {
words: [u32; KEY_LEN / 4],
cpu_features: cpu::Features,
}
impl From<[u8; KEY_LEN]> for Key {
#[inline]
fn from(value: [u8; KEY_LEN]) -> Self {
impl Key {
pub(super) fn new(value: [u8; KEY_LEN], cpu_features: cpu::Features) -> Self {
let value: &[[u8; 4]; KEY_LEN / 4] = value.chunks_fixed();
Self(value.array_map(u32::from_le_bytes))
Self {
words: value.array_map(u32::from_le_bytes),
cpu_features,
}
}
pub(super) fn cpu_features(&self) -> cpu::Features {
self.cpu_features
}
}
@ -140,7 +152,7 @@ impl Key {
))]
#[inline]
pub(super) fn words_less_safe(&self) -> &[u32; KEY_LEN / 4] {
&self.0
&self.words
}
}
@ -261,7 +273,7 @@ mod tests {
let key = test_case.consume_bytes("Key");
let key: &[u8; KEY_LEN] = key.as_slice().try_into()?;
let key = Key::from(*key);
let key = Key::new(*key, cpu::features());
let ctr = test_case.consume_usize("Ctr");
let nonce = test_case.consume_bytes("Nonce");

View File

@ -36,10 +36,13 @@ pub static CHACHA20_POLY1305: aead::Algorithm = aead::Algorithm {
/// Copies |key| into |ctx_buf|.
fn chacha20_poly1305_init(
key: &[u8],
_todo: cpu::Features,
cpu_features: cpu::Features,
) -> Result<aead::KeyInner, error::Unspecified> {
let key: [u8; chacha::KEY_LEN] = key.try_into()?;
Ok(aead::KeyInner::ChaCha20Poly1305(chacha::Key::from(key)))
Ok(aead::KeyInner::ChaCha20Poly1305(chacha::Key::new(
key,
cpu_features,
)))
}
fn chacha20_poly1305_seal(
@ -47,7 +50,6 @@ 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,
@ -56,7 +58,7 @@ fn chacha20_poly1305_seal(
#[cfg(target_arch = "x86_64")]
{
if cpu::intel::SSE41.available(cpu_features) {
if cpu::intel::SSE41.available(chacha20_key.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.
@ -113,7 +115,7 @@ fn chacha20_poly1305_seal(
let mut counter = Counter::zero(nonce);
let mut auth = {
let key = derive_poly1305_key(chacha20_key, counter.increment(), cpu_features);
let key = derive_poly1305_key(chacha20_key, counter.increment());
poly1305::Context::from_key(key)
};
@ -129,7 +131,6 @@ fn chacha20_poly1305_open(
aad: Aad<&[u8]>,
in_out: &mut [u8],
src: RangeFrom<usize>,
cpu_features: cpu::Features,
) -> Tag {
let chacha20_key = match key {
aead::KeyInner::ChaCha20Poly1305(key) => key,
@ -138,7 +139,7 @@ fn chacha20_poly1305_open(
#[cfg(target_arch = "x86_64")]
{
if cpu::intel::SSE41.available(cpu_features) {
if cpu::intel::SSE41.available(chacha20_key.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.
@ -191,7 +192,7 @@ fn chacha20_poly1305_open(
let mut counter = Counter::zero(nonce);
let mut auth = {
let key = derive_poly1305_key(chacha20_key, counter.increment(), cpu_features);
let key = derive_poly1305_key(chacha20_key, counter.increment());
poly1305::Context::from_key(key)
};
@ -250,14 +251,10 @@ fn poly1305_update_padded_16(ctx: &mut poly1305::Context, input: &[u8]) {
}
// Also used by chacha20_poly1305_openssh.
pub(super) fn derive_poly1305_key(
chacha_key: &chacha::Key,
iv: Iv,
cpu_features: cpu::Features,
) -> poly1305::Key {
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, cpu_features)
poly1305::Key::new(key_bytes, chacha_key.cpu_features())
}
#[cfg(test)]

View File

@ -65,8 +65,7 @@ impl SealingKey {
tag_out: &mut [u8; TAG_LEN],
) {
let mut counter = make_counter(sequence_number);
let poly_key =
derive_poly1305_key(&self.key.k_2, counter.increment(), self.key.cpu_features);
let poly_key = derive_poly1305_key(&self.key.k_2, counter.increment());
{
let (len_in_out, data_and_padding_in_out) =
@ -133,8 +132,7 @@ impl OpeningKey {
// We must verify the tag before decrypting so that
// `ciphertext_in_plaintext_out` is unmodified if verification fails.
// This is beyond what we guarantee.
let poly_key =
derive_poly1305_key(&self.key.k_2, counter.increment(), self.key.cpu_features);
let poly_key = derive_poly1305_key(&self.key.k_2, counter.increment());
verify(poly_key, ciphertext_in_plaintext_out, tag)?;
let plaintext_in_ciphertext_out = &mut ciphertext_in_plaintext_out[PACKET_LENGTH_LEN..];
@ -149,7 +147,6 @@ impl OpeningKey {
struct Key {
k_1: chacha::Key,
k_2: chacha::Key,
cpu_features: cpu::Features,
}
impl Key {
@ -157,9 +154,8 @@ impl Key {
// The first half becomes K_2 and the second half becomes K_1.
let &[k_2, k_1]: &[[u8; chacha::KEY_LEN]; 2] = key_material.chunks_fixed();
Self {
k_1: k_1.into(),
k_2: k_2.into(),
cpu_features,
k_1: chacha::Key::new(k_1, cpu_features),
k_2: chacha::Key::new(k_2, cpu_features),
}
}
}

View File

@ -22,16 +22,22 @@ use core::ops::BitXorAssign;
#[cfg(not(target_arch = "aarch64"))]
mod gcm_nohw;
pub struct Key(HTable);
pub struct Key {
h_table: HTable,
cpu_features: cpu::Features,
}
impl Key {
pub(super) fn new(h_be: Block, cpu_features: cpu::Features) -> Self {
let h: [u64; 2] = h_be.into();
let mut key = Self(HTable {
Htable: [u128 { hi: 0, lo: 0 }; HTABLE_LEN],
});
let h_table = &mut key.0;
let mut key = Self {
h_table: HTable {
Htable: [u128 { hi: 0, lo: 0 }; HTABLE_LEN],
},
cpu_features,
};
let h_table = &mut key.h_table;
match detect_implementation(cpu_features) {
#[cfg(target_arch = "x86_64")]
@ -85,14 +91,14 @@ pub struct Context {
}
impl Context {
pub(crate) fn new(key: &Key, aad: Aad<&[u8]>, cpu_features: cpu::Features) -> Self {
pub(crate) fn new(key: &Key, aad: Aad<&[u8]>) -> Self {
let mut ctx = Self {
inner: ContextInner {
Xi: Xi(Block::zero()),
_unused: Block::zero(),
Htable: key.0.clone(),
Htable: key.h_table.clone(),
},
cpu_features,
cpu_features: key.cpu_features,
};
for ad in aad.0.chunks(BLOCK_LEN) {

View File

@ -23,7 +23,6 @@ use core::ops::RangeFrom;
pub struct LessSafeKey {
inner: KeyInner,
algorithm: &'static Algorithm,
cpu_features: cpu::Features,
}
impl LessSafeKey {
@ -41,7 +40,6 @@ impl LessSafeKey {
Ok(Self {
inner: (algorithm.init)(key_bytes, cpu_features)?,
algorithm,
cpu_features,
})
}
@ -153,8 +151,7 @@ fn open_within_<'in_out>(
.ok_or(error::Unspecified)?;
check_per_nonce_max_bytes(key.algorithm, ciphertext_len)?;
let (in_out, received_tag) = in_out.split_at_mut(src.start + ciphertext_len);
let Tag(calculated_tag) =
(key.algorithm.open)(&key.inner, nonce, aad, in_out, src, key.cpu_features);
let Tag(calculated_tag) = (key.algorithm.open)(&key.inner, nonce, aad, in_out, src);
if constant_time::verify_slices_are_equal(calculated_tag.as_ref(), received_tag).is_err() {
// Zero out the plaintext so that it isn't accidentally leaked or used
// after verification fails. It would be safest if we could check the
@ -177,13 +174,7 @@ pub(super) fn seal_in_place_separate_tag_(
in_out: &mut [u8],
) -> Result<Tag, error::Unspecified> {
check_per_nonce_max_bytes(key.algorithm(), in_out.len())?;
Ok((key.algorithm.seal)(
&key.inner,
nonce,
aad,
in_out,
key.cpu_features,
))
Ok((key.algorithm.seal)(&key.inner, nonce, aad, in_out))
}
fn check_per_nonce_max_bytes(alg: &Algorithm, in_out_len: usize) -> Result<(), error::Unspecified> {

View File

@ -171,9 +171,12 @@ pub static CHACHA20: Algorithm = Algorithm {
id: AlgorithmID::CHACHA20,
};
fn chacha20_init(key: &[u8], _todo: cpu::Features) -> Result<KeyInner, error::Unspecified> {
fn chacha20_init(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified> {
let chacha20_key: [u8; chacha::KEY_LEN] = key.try_into()?;
Ok(KeyInner::ChaCha20(chacha::Key::from(chacha20_key)))
Ok(KeyInner::ChaCha20(chacha::Key::new(
chacha20_key,
cpu_features,
)))
}
fn chacha20_new_mask(key: &KeyInner, sample: Sample) -> [u8; 5] {