use std::fmt;
use hkdf::SimpleHkdf;
use sha3::Sha3_512;
use zeroize::ZeroizeOnDrop;
use crate::crypto::kdf::MasterKey;
const INFO_ENC_KEY: &[u8] = b"STENOXIDE-v1-enc-key";
const INFO_NONCE: &[u8] = b"STENOXIDE-v1-nonce";
const INFO_STC_SEED: &[u8] = b"STENOXIDE-v1-stc-seed";
#[derive(Debug)]
pub enum ExpandError {
HkdfError(String),
}
impl fmt::Display for ExpandError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ExpandError::HkdfError(message) => {
write!(f, "hkdf-sha3-512 key expansion failed: {message}")
}
}
}
}
impl std::error::Error for ExpandError {}
#[derive(ZeroizeOnDrop)]
pub struct DerivedKeys {
pub(crate) enc_key: [u8; 32],
pub(crate) nonce: [u8; 24],
pub(crate) stc_seed: [u8; 32],
}
impl DerivedKeys {
pub fn enc_key(&self) -> &[u8; 32] {
&self.enc_key
}
pub fn nonce(&self) -> &[u8; 24] {
&self.nonce
}
pub fn stc_seed(&self) -> &[u8; 32] {
&self.stc_seed
}
}
pub fn expand_master_key(mk: &MasterKey) -> Result<DerivedKeys, ExpandError> {
let hkdf = SimpleHkdf::<Sha3_512>::new(None, mk.as_bytes());
let mut keys = DerivedKeys {
enc_key: [0u8; 32],
nonce: [0u8; 24],
stc_seed: [0u8; 32],
};
hkdf.expand(INFO_ENC_KEY, &mut keys.enc_key)
.map_err(|err| ExpandError::HkdfError(err.to_string()))?;
hkdf.expand(INFO_NONCE, &mut keys.nonce)
.map_err(|err| ExpandError::HkdfError(err.to_string()))?;
hkdf.expand(INFO_STC_SEED, &mut keys.stc_seed)
.map_err(|err| ExpandError::HkdfError(err.to_string()))?;
Ok(keys)
}
#[cfg(test)]
mod tests {
#![allow(clippy::expect_used)]
use super::*;
#[test]
fn expansion_matches_pinned_vectors() {
const ENC_KEY: [u8; 32] = [
0x9a, 0x09, 0x5f, 0x87, 0xbf, 0x45, 0x5d, 0x1c, 0x30, 0x61, 0x94, 0xd1, 0x58, 0xdb,
0x7c, 0xfa, 0x6b, 0x10, 0xd9, 0xe6, 0x29, 0xd9, 0xb1, 0x43, 0xcd, 0x3b, 0xb6, 0x76,
0x89, 0xd5, 0xb9, 0x36,
];
const NONCE: [u8; 24] = [
0x34, 0x83, 0xe6, 0x2d, 0x0b, 0xae, 0x7f, 0xae, 0x8d, 0x13, 0x77, 0x3a, 0x98, 0x97,
0x89, 0x3b, 0x97, 0xcb, 0x56, 0x66, 0x0f, 0x49, 0xee, 0x3f,
];
const STC_SEED: [u8; 32] = [
0x35, 0x52, 0xd3, 0x1e, 0x7e, 0x52, 0xdb, 0xa7, 0x77, 0xf8, 0x75, 0xd4, 0xa4, 0x86,
0xb2, 0xea, 0x5f, 0x38, 0x08, 0xaa, 0xa1, 0x4d, 0x0d, 0xeb, 0x21, 0x31, 0x4e, 0x62,
0x42, 0x90, 0x8e, 0x11,
];
let keys = expand_master_key(&MasterKey::new([7u8; 32])).expect("expansion must succeed");
assert_eq!(keys.enc_key(), &ENC_KEY);
assert_eq!(keys.nonce(), &NONCE);
assert_eq!(keys.stc_seed(), &STC_SEED);
}
#[test]
fn subkeys_are_domain_separated() {
let keys = expand_master_key(&MasterKey::new([1u8; 32])).expect("expansion must succeed");
assert_ne!(keys.enc_key().as_slice(), keys.stc_seed().as_slice());
assert_ne!(&keys.enc_key()[..24], keys.nonce().as_slice());
}
}