use super::funejson::Value; use super::crypto_sha256::sha256; const BLOCK: usize = 64; fn check_bytes(value: &[i64], name: &str) { if value.iter().any(|b| !(0..=255).contains(b)) { panic!("{} must be a list of integers from 0 to 255", name); } } /// HMAC-SHA256 (RFC 2104): H((K ^ opad) || H((K ^ ipad) || message)), with a /// key longer than the 64-byte block hashed down to 32 bytes first. /// /// # Panics /// Panics if any key or message value is outside 0-255. pub fn hmac_sha256(key: &[i64], message: &[i64]) -> Vec { check_bytes(key, "key"); check_bytes(message, "message"); let hashed; let k: &[i64] = if key.len() > BLOCK { hashed = sha256(key); &hashed } else { key }; let mut inner: Vec = Vec::with_capacity(BLOCK + message.len()); let mut outer: Vec = Vec::with_capacity(BLOCK + 32); for i in 0..BLOCK { let b = if i < k.len() { k[i] } else { 0 }; inner.push(b ^ 0x36); outer.push(b ^ 0x5c); } inner.extend_from_slice(message); outer.extend(sha256(&inner)); sha256(&outer) } fn bytes_from_value(value: &Value, name: &str) -> Vec { match value { Value::Arr(items) => items .iter() .map(|item| match item { Value::Int(i) => *i, _ => panic!("{} must be a list of integers from 0 to 255", name), }) .collect(), _ => panic!("{} must be a list of integers from 0 to 255", name), } } pub fn fune_vector(args: &[Value]) -> Value { let key = bytes_from_value(&args[0], "key"); let message = bytes_from_value(&args[1], "message"); Value::Arr(hmac_sha256(&key, &message).into_iter().map(Value::Int).collect()) }