use core::{marker::PhantomData, ops::Sub};
use aead::{
KeySizeUser,
array::{Array, ArraySize},
};
use digest::{KeyInit, OutputSizeUser};
use zerocopy::IntoBytes;
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
use super::epoch_key::EpochKey;
use crate::{
FloeAead, FloeKdf,
keys::FloeKdfKey,
types::{FloeIv, Parameters},
};
#[cfg_attr(feature = "zeroize", derive(zeroize::ZeroizeOnDrop))]
pub(crate) struct MessageKey<A, H>
where
A: FloeAead,
H: FloeKdf,
{
pub(super) key: FloeKdfKey<H>,
pub(super) _phantom_aead: PhantomData<A>,
pub(super) _phantom: PhantomData<H>,
}
impl<A, K> MessageKey<A, K>
where
A: FloeAead,
K: FloeKdf,
{
pub(crate) fn derive_epoch_key<const N: usize, const S: u32>(
&self,
floe_iv: &FloeIv<N>,
associated_data: &[u8],
segment_number: u64,
is_final: bool,
) -> EpochKey<A>
where
<K as OutputSizeUser>::OutputSize: Sub<<A as KeySizeUser>::KeySize>,
<<K as OutputSizeUser>::OutputSize as Sub<<A as KeySizeUser>::KeySize>>::Output: ArraySize,
{
let masked_counter = segment_number & A::AEAD_ROTATION_MASK;
let mut purpose = [0u8; 12];
purpose[..4].copy_from_slice(b"DEK:");
purpose[4..].copy_from_slice(&masked_counter.to_be_bytes());
let parameters = Parameters::new::<A, K, N, S>();
#[allow(clippy::expect_used)]
let output = <K as KeyInit>::new_from_slice(&self.key)
.expect(
"the KDF input key material should be big enough as this is determined \
by KDF_KEY_LEN parameter",
)
.chain_update(parameters.as_bytes())
.chain_update(floe_iv.as_array())
.chain_update(purpose)
.chain_update(associated_data)
.chain_update([1])
.finalize();
let (key, mut _rest) = Array::split::<<A as KeySizeUser>::KeySize>(output.into_bytes());
#[cfg(feature = "zeroize")]
_rest.zeroize();
EpochKey { key, segment_number, is_final }
}
}