use std::{marker::PhantomData, mem::replace};
use crate::{CipherSuite, crypto::key_derivation::Ratcheting, error::Result};
use super::ratcheting_key_id::RatchetingKeyId;
pub struct RatchetingBaseKey<D>
where
D: Ratcheting,
{
cipher_suite: CipherSuite,
current_material: Vec<u8>,
key_id: RatchetingKeyId,
_ratcheting: PhantomData<D>,
}
impl<D> RatchetingBaseKey<D>
where
D: Ratcheting,
{
pub fn ratchet_forward<K, M>(
key_id: K,
key_material: M,
cipher_suite: CipherSuite,
) -> Result<Self>
where
K: Into<RatchetingKeyId>,
M: AsRef<[u8]>,
{
let mut base_key = Self {
cipher_suite,
current_material: key_material.as_ref().into(),
key_id: key_id.into(),
_ratcheting: PhantomData,
};
base_key.ratchet()?;
Ok(base_key)
}
pub fn next_base_key(&mut self) -> Result<(RatchetingKeyId, Vec<u8>)> {
let key_id = self.key_id;
let key_material = self.ratchet()?;
Ok((key_id, key_material))
}
pub fn key_id(&self) -> RatchetingKeyId {
self.key_id
}
fn ratchet(&mut self) -> Result<Vec<u8>> {
self.key_id.inc_ratchet_step();
let new_material = D::ratchet(self.cipher_suite, &self.current_material)?;
Ok(replace(&mut self.current_material, new_material))
}
}
#[cfg(all(test, crypto_backend))]
mod test {
use crate::crypto::Kdf;
use crate::ratchet::ratcheting_key_id::RatchetingKeyId;
use pretty_assertions::assert_eq;
type RatchetingBaseKey = super::RatchetingBaseKey<Kdf>;
#[test]
fn should_ratchet_forward() {
const N_RATCHET_BITS: u8 = 8;
let expected_key_id = RatchetingKeyId::new(42u8, N_RATCHET_BITS);
let secret = b"SuperSecret";
let mut base_key = RatchetingBaseKey::ratchet_forward(
expected_key_id,
secret,
crate::CipherSuite::AesGcm128Sha256,
)
.unwrap();
let (first_key_id, first_material) = base_key.next_base_key().unwrap();
assert_eq!(expected_key_id.generation(), first_key_id.generation());
assert_eq!(first_key_id.ratchet_step(), 1);
assert_ne!(secret, first_material.as_slice());
let (second_key_id, second_material) = base_key.next_base_key().unwrap();
assert_eq!(second_key_id.ratchet_step(), 2);
assert_ne!(secret, second_material.as_slice());
}
}