use gbp_mls::MlsContext;
use hkdf::Hkdf;
use sha2::Sha256;
use crate::error::SFrameError;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CipherSuite {
Aes128Gcm,
Aes256Gcm,
}
impl CipherSuite {
pub(crate) fn key_len(self) -> usize {
match self {
Self::Aes128Gcm => 16,
Self::Aes256Gcm => 32,
}
}
pub fn from_u8(v: u8) -> Option<Self> {
match v {
0 => Some(Self::Aes128Gcm),
1 => Some(Self::Aes256Gcm),
_ => None,
}
}
pub fn as_u8(self) -> u8 {
match self {
Self::Aes128Gcm => 0,
Self::Aes256Gcm => 1,
}
}
}
pub(crate) struct ParticipantKeys {
pub key: Vec<u8>,
pub base_nonce: [u8; 12],
}
pub fn derive_base_key(mls: &MlsContext, label: &str, epoch: u64) -> Result<[u8; 32], SFrameError> {
let context = epoch.to_be_bytes();
let raw = mls
.export_raw(label, &context, 32)
.map_err(|e| SFrameError::MlsExport(e.to_string()))?;
let mut out = [0u8; 32];
out.copy_from_slice(&raw);
Ok(out)
}
const HKDF_LABEL_KEY: &[u8] = b"gbp sframe key ";
const HKDF_LABEL_NONCE: &[u8] = b"gbp sframe salt ";
pub(crate) fn derive_participant(
base_key: &[u8; 32],
leaf_index: u32,
suite: CipherSuite,
) -> ParticipantKeys {
let hk =
Hkdf::<Sha256>::from_prk(base_key).expect("base_key is exactly SHA-256 HashLen (32 bytes)");
let leaf_be = leaf_index.to_be_bytes();
let mut label = HKDF_LABEL_KEY.to_vec();
label.extend_from_slice(&leaf_be);
let mut key = vec![0u8; suite.key_len()];
hk.expand(&label, &mut key)
.expect("key length is well within 255 * HashLen");
let mut label = HKDF_LABEL_NONCE.to_vec();
label.extend_from_slice(&leaf_be);
let mut base_nonce: [u8; 12] = Default::default();
hk.expand(&label, &mut base_nonce)
.expect("nonce length (12) is well within 255 * HashLen");
ParticipantKeys { key, base_nonce }
}
pub(crate) fn make_nonce(salt: &[u8; 12], ctr: u64) -> [u8; 12] {
let mut nonce = *salt;
let ctr_le = ctr.to_le_bytes(); for i in 0..8 {
nonce[i] ^= ctr_le[i];
}
nonce
}