use crate::kdf::{hkdf_expand_sha512, hkdf_extract_sha512};
use crate::traits::Kem;
use zeroize::Zeroize;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Role { Initiator, Responder }
pub struct AnubisDeriveInput<'a> {
pub pake_secret: &'a [u8],
pub transcript_hash: &'a [u8],
}
#[derive(Clone, Debug, Zeroize)]
#[zeroize(drop)]
pub struct AnubisDerivedKeys {
pub k_ctrl: Vec<u8>,
pub k_data: Vec<u8>,
pub k_dilate: Vec<u8>,
pub k_verify: Vec<u8>,
}
pub fn derive_keys(pake: &[u8], kem_ss: &[u8], transcript_hash: &[u8]) -> AnubisDerivedKeys {
let mut ikm = Vec::with_capacity(pake.len() + kem_ss.len());
ikm.extend_from_slice(pake);
ikm.extend_from_slice(kem_ss);
let prk = hkdf_extract_sha512(transcript_hash, &ikm);
let k_ctrl = hkdf_expand_sha512(&prk, b"anubis-ctrl", 32);
let k_data = hkdf_expand_sha512(&prk, b"anubis-data", 32);
let k_dilate = hkdf_expand_sha512(&prk, b"anubis-dilate", 32);
let k_verify = hkdf_expand_sha512(&prk, b"anubis-verify", 32);
AnubisDerivedKeys { k_ctrl, k_data, k_dilate, k_verify }
}
pub struct InitiatorState<K: Kem> {
kem: K,
sk: K::SecretKey,
_pubkey_sent: bool,
_derive: Vec<u8>, pake: Vec<u8>,
th: Vec<u8>,
}
impl<K: Kem> InitiatorState<K> {
pub fn start(kem: K, input: AnubisDeriveInput) -> (Self, K::PublicKey) {
let (pk, sk) = kem.keypair();
let st = InitiatorState {
kem,
sk,
_pubkey_sent: true,
_derive: Vec::new(),
pake: input.pake_secret.to_vec(),
th: input.transcript_hash.to_vec(),
};
(st, pk)
}
pub fn finish(self, ct_b: &K::Ciphertext) -> AnubisDerivedKeys {
let ss = self.kem.decapsulate(&self.sk, ct_b);
derive_keys(&self.pake, ss.as_ref(), &self.th)
}
}
impl<K: Kem> Drop for InitiatorState<K> {
fn drop(&mut self) {
use zeroize::Zeroize;
self.pake.zeroize();
self.th.zeroize();
}
}
pub struct ResponderOutput<K: Kem> {
pub ct: K::Ciphertext,
pub keys: AnubisDerivedKeys,
}
pub fn responder_process<K: Kem>(kem: &K, input: AnubisDeriveInput, pk_a: &K::PublicKey) -> ResponderOutput<K> {
let (ct, ss) = kem.encapsulate(pk_a);
let keys = derive_keys(input.pake_secret, ss.as_ref(), input.transcript_hash);
ResponderOutput { ct, keys }
}