use super::{Error, HmacSha256Snafu};
use arc_swap::ArcSwap;
use hex::encode;
use hmac::{Hmac, KeyInit, Mac};
use sha2::Sha256;
use snafu::ResultExt;
use std::sync::Arc;
type Result<T> = std::result::Result<T, Error>;
type HmacSha256 = Hmac<Sha256>;
#[derive(Debug)]
pub struct KeyGrip {
keys: ArcSwap<Vec<Vec<u8>>>,
}
fn sign_with_key(data: &[u8], key: &[u8]) -> Result<String> {
let mut mac = HmacSha256::new_from_slice(key).context(HmacSha256Snafu)?;
mac.update(data);
Ok(encode(mac.finalize().into_bytes()))
}
fn verify_with_key(data: &[u8], key: &[u8], expected: &[u8]) -> Result<bool> {
let mut mac = HmacSha256::new_from_slice(key).context(HmacSha256Snafu)?;
mac.update(data);
Ok(mac.verify_slice(expected).is_ok())
}
impl KeyGrip {
pub fn new(keys: Vec<Vec<u8>>) -> Result<Self> {
if keys.is_empty() {
return Err(Error::KeyGripEmpty);
}
Ok(Self {
keys: ArcSwap::from_pointee(keys),
})
}
pub fn update_keys(&self, new_keys: Vec<Vec<u8>>) -> Result<()> {
if new_keys.is_empty() {
return Err(Error::KeyGripEmpty);
}
self.keys.store(Arc::new(new_keys));
Ok(())
}
pub fn sign(&self, data: &[u8]) -> Result<String> {
let keys = self.keys.load();
let key = keys.first().ok_or(Error::KeyGripEmpty)?;
sign_with_key(data, key)
}
pub fn verify(&self, data: &[u8], digest: &str) -> Result<(bool, bool)> {
let Ok(expected) = hex::decode(digest) else {
return Ok((false, false));
};
let keys = self.keys.load();
for (index, key) in keys.iter().enumerate() {
if verify_with_key(data, key, &expected)? {
return Ok((true, index == 0));
}
}
Ok((false, false))
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
fn keys(primary: &[u8], rest: &[&[u8]]) -> Vec<Vec<u8>> {
let mut v = vec![primary.to_vec()];
v.extend(rest.iter().map(|k| k.to_vec()));
v
}
#[test]
fn empty_keys_rejected() {
assert!(matches!(
KeyGrip::new(Vec::new()).unwrap_err(),
Error::KeyGripEmpty
));
}
#[test]
fn sign_then_verify_with_primary() {
let kg = KeyGrip::new(keys(b"primary", &[])).unwrap();
let sig = kg.sign(b"hello").unwrap();
assert_eq!(sig.len(), 64); assert_eq!(kg.verify(b"hello", &sig).unwrap(), (true, true));
}
#[test]
fn verify_with_rotated_key_marks_not_current() {
let kg = KeyGrip::new(keys(b"primary", &[b"legacy"])).unwrap();
let legacy_sig = sign_with_key(b"hello", b"legacy").unwrap();
assert_eq!(
kg.verify(b"hello", &legacy_sig).unwrap(),
(true, false),
"签名匹配历史密钥应返回 is_valid=true, is_current=false(提示调用方重新签名)"
);
}
#[test]
fn verify_unknown_signature_returns_invalid() {
let kg = KeyGrip::new(keys(b"primary", &[])).unwrap();
let foreign_sig = sign_with_key(b"hello", b"someone-else").unwrap();
assert_eq!(kg.verify(b"hello", &foreign_sig).unwrap(), (false, false));
}
#[test]
fn verify_malformed_hex_returns_invalid_not_error() {
let kg = KeyGrip::new(keys(b"primary", &[])).unwrap();
assert_eq!(kg.verify(b"hello", "zzz").unwrap(), (false, false));
assert_eq!(kg.verify(b"hello", "").unwrap(), (false, false));
}
#[test]
fn update_keys_atomically_rotates_primary() {
let kg = KeyGrip::new(keys(b"v1", &[])).unwrap();
let old_sig = kg.sign(b"payload").unwrap();
assert_eq!(kg.verify(b"payload", &old_sig).unwrap(), (true, true));
kg.update_keys(keys(b"v2", &[b"v1"])).unwrap();
let new_sig = kg.sign(b"payload").unwrap();
assert_ne!(new_sig, old_sig, "新主密钥应产生不同签名");
assert_eq!(
kg.verify(b"payload", &new_sig).unwrap(),
(true, true),
"新签名应匹配主密钥"
);
assert_eq!(
kg.verify(b"payload", &old_sig).unwrap(),
(true, false),
"旧签名应仍有效但标记为非当前"
);
}
#[test]
fn update_keys_rejects_empty() {
let kg = KeyGrip::new(keys(b"v1", &[])).unwrap();
assert!(matches!(
kg.update_keys(Vec::new()).unwrap_err(),
Error::KeyGripEmpty
));
let sig = kg.sign(b"x").unwrap();
assert_eq!(kg.verify(b"x", &sig).unwrap(), (true, true));
}
#[test]
fn binary_payload_is_supported() {
let kg = KeyGrip::new(keys(&[0xDE, 0xAD, 0xBE, 0xEF], &[])).unwrap();
let data = &[0x00, 0xFF, 0x42, 0x00, 0x7F];
let sig = kg.sign(data).unwrap();
assert_eq!(kg.verify(data, &sig).unwrap(), (true, true));
assert_eq!(
kg.verify(&[0x00, 0xFF, 0x42, 0x00, 0x80], &sig).unwrap(),
(false, false)
);
}
}