use hmac::{Hmac, Mac};
use sha2::Sha256;
type HmacSha256 = Hmac<Sha256>;
pub fn kdf(key: &[u8], s: &[u8]) -> [u8; 32] {
let mut mac =
HmacSha256::new_from_slice(key).expect("HMAC-SHA-256 accepts any key size per RFC 2104");
mac.update(s);
mac.finalize().into_bytes().into()
}
pub fn build_s(fc: u8, params: &[&[u8]]) -> Vec<u8> {
let mut s = vec![fc];
for p in params {
s.extend_from_slice(p);
assert!(p.len() <= 0xFFFF, "KDF parameter exceeds 65535 bytes");
let len = p.len() as u16;
s.extend_from_slice(&len.to_be_bytes());
}
s
}
pub fn derive_kausf(
ck: &[u8; 16],
ik: &[u8; 16],
sn_name: &[u8],
sqn_xor_ak: &[u8; 6],
fc: u8,
) -> [u8; 32] {
let ck_ik: Vec<u8> = ck.iter().chain(ik.iter()).copied().collect();
let s = build_s(fc, &[sn_name, sqn_xor_ak]);
kdf(&ck_ik, &s)
}
pub fn derive_kseaf(kausf: &[u8; 32], sn_name: &[u8]) -> [u8; 32] {
let s = build_s(0x6C, &[sn_name]);
kdf(kausf, &s)
}
pub fn derive_kamf(kseaf: &[u8; 32], supi_digits: &str, abba: &[u8]) -> [u8; 32] {
let s = build_s(0x6D, &[supi_digits.as_bytes(), abba]);
kdf(kseaf, &s)
}
pub fn derive_nas_key(kamf: &[u8; 32], algo_type: u8, algo_id: u8) -> [u8; 32] {
let s = build_s(0x69, &[&[algo_type], &[algo_id]]);
kdf(kamf, &s)
}
pub fn extract_128(full: &[u8; 32]) -> [u8; 16] {
full[16..]
.try_into()
.expect("32-byte array yields 16-byte tail")
}
pub fn compute_hres_star(rand: &[u8; 16], res_star: &[u8]) -> [u8; 16] {
debug_assert!(res_star.len() == 16, "RES* must be 16 bytes");
use sha2::{Digest, Sha256};
let mut h = Sha256::new();
h.update(rand.as_ref());
h.update(res_star);
h.finalize()[16..]
.try_into()
.expect("SHA-256 output yields 16-byte tail")
}
pub fn derive_kgnb(kamf: &[u8; 32], ul_nas_count: u32) -> [u8; 32] {
let count_bytes = ul_nas_count.to_be_bytes();
let access_type: &[u8] = &[0x01]; let s = build_s(0x6E, &[&count_bytes, access_type]);
kdf(kamf, &s)
}
pub fn derive_nh(kamf: &[u8; 32], sync_input: &[u8; 32]) -> [u8; 32] {
let s = build_s(0x6F, &[sync_input.as_ref()]);
kdf(kamf, &s)
}
pub fn compute_xres_star(
ck: &[u8; 16],
ik: &[u8; 16],
sn_name: &[u8],
rand: &[u8; 16],
xres: &[u8],
) -> Vec<u8> {
debug_assert!(!xres.is_empty(), "XRES must not be empty");
let ck_ik: Vec<u8> = ck.iter().chain(ik.iter()).copied().collect();
let s = build_s(0x6B, &[sn_name, rand.as_ref(), xres]);
let full = kdf(&ck_ik, &s);
full[16..].to_vec()
}