use super::cipher::Cipher;
use super::cipher_state::CipherState;
use super::error::HandshakeError;
use super::hash::Hash;
use crate::zeroize::{zeroize_array, zeroize_bytes};
use std::marker::PhantomData;
fn noise_hkdf_2<H: Hash>(ck: &[u8], ikm: &[u8]) -> (Vec<u8>, Vec<u8>) {
let mut temp_key = H::hmac(ck, ikm);
let output1 = H::hmac(&temp_key, &[0x01]);
let mut input2 = Vec::with_capacity(output1.len() + 1);
input2.extend_from_slice(&output1);
input2.push(0x02);
let output2 = H::hmac(&temp_key, &input2);
zeroize_bytes(&mut temp_key);
zeroize_bytes(&mut input2);
(output1, output2)
}
fn noise_hkdf_3<H: Hash>(ck: &[u8], ikm: &[u8]) -> (Vec<u8>, Vec<u8>, Vec<u8>) {
let mut temp_key = H::hmac(ck, ikm);
let output1 = H::hmac(&temp_key, &[0x01]);
let mut input2 = Vec::with_capacity(output1.len() + 1);
input2.extend_from_slice(&output1);
input2.push(0x02);
let output2 = H::hmac(&temp_key, &input2);
let mut input3 = Vec::with_capacity(output2.len() + 1);
input3.extend_from_slice(&output2);
input3.push(0x03);
let output3 = H::hmac(&temp_key, &input3);
zeroize_bytes(&mut temp_key);
zeroize_bytes(&mut input2);
zeroize_bytes(&mut input3);
(output1, output2, output3)
}
pub struct SymmetricState<Ci: Cipher, H: Hash> {
ck: Vec<u8>,
h: Vec<u8>,
cipher_state: CipherState<Ci>,
_hash: PhantomData<H>,
}
impl<Ci: Cipher, H: Hash> SymmetricState<Ci, H> {
pub fn initialize(protocol_name: &str) -> Self {
let h = if protocol_name.len() <= H::HASH_LEN {
let mut h = vec![0u8; H::HASH_LEN];
h[..protocol_name.len()].copy_from_slice(protocol_name.as_bytes());
h
} else {
H::hash(protocol_name.as_bytes())
};
let ck = h.clone();
Self {
ck,
h,
cipher_state: CipherState::empty(),
_hash: PhantomData,
}
}
pub fn has_key(&self) -> bool {
self.cipher_state.has_key()
}
pub(crate) fn mix_key(&mut self, input_key_material: &[u8]) {
let (new_ck, mut temp_k) = noise_hkdf_2::<H>(&self.ck, input_key_material);
zeroize_bytes(&mut self.ck);
self.ck = new_ck;
let mut key = [0u8; 32];
key.copy_from_slice(&temp_k[..32]);
self.cipher_state = CipherState::from_key(key);
zeroize_array(&mut key);
zeroize_bytes(&mut temp_k);
}
pub(crate) fn mix_hash(&mut self, data: &[u8]) {
self.h = H::hash_two(&self.h, data);
}
pub(crate) fn mix_key_and_hash(&mut self, input_key_material: &[u8]) {
let (new_ck, mut temp_h, mut temp_k) = noise_hkdf_3::<H>(&self.ck, input_key_material);
zeroize_bytes(&mut self.ck);
self.ck = new_ck;
self.mix_hash(&temp_h);
let mut key = [0u8; 32];
key.copy_from_slice(&temp_k[..32]);
self.cipher_state = CipherState::from_key(key);
zeroize_array(&mut key);
zeroize_bytes(&mut temp_h);
zeroize_bytes(&mut temp_k);
}
pub(crate) fn encrypt_and_hash(
&mut self,
plaintext: &[u8],
output: &mut [u8],
) -> Result<usize, HandshakeError> {
let len = self
.cipher_state
.encrypt_with_ad(&self.h, plaintext, output)?;
self.mix_hash(&output[..len]);
Ok(len)
}
pub(crate) fn decrypt_and_hash(
&mut self,
ciphertext: &[u8],
output: &mut [u8],
) -> Result<usize, HandshakeError> {
let len = self
.cipher_state
.decrypt_with_ad(&self.h, ciphertext, output)?;
self.mix_hash(ciphertext);
Ok(len)
}
pub(crate) fn split(mut self) -> (CipherState<Ci>, CipherState<Ci>) {
let (mut temp_k1, mut temp_k2) = noise_hkdf_2::<H>(&self.ck, &[]);
let mut key1 = [0u8; 32];
key1.copy_from_slice(&temp_k1[..32]);
let mut key2 = [0u8; 32];
key2.copy_from_slice(&temp_k2[..32]);
let result = (CipherState::from_key(key1), CipherState::from_key(key2));
zeroize_array(&mut key1);
zeroize_array(&mut key2);
zeroize_bytes(&mut temp_k1);
zeroize_bytes(&mut temp_k2);
zeroize_bytes(&mut self.ck);
result
}
pub fn handshake_hash(&self) -> &[u8] {
&self.h
}
}
impl<Ci: Cipher, H: Hash> Drop for SymmetricState<Ci, H> {
fn drop(&mut self) {
zeroize_bytes(&mut self.ck);
}
}