use crate::error::{AcmeError, Result};
use hmac::{Hmac, KeyInit, Mac};
use sha2::Sha256;
#[derive(Debug, Clone)]
pub struct Signature {
pub data: Vec<u8>,
pub algorithm: String,
}
impl Signature {
pub fn new(data: Vec<u8>, algorithm: String) -> Self {
Self { data, algorithm }
}
pub fn to_base64(&self) -> String {
use base64::Engine;
base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(&self.data)
}
}
pub trait Signer: Send + Sync {
fn sign(&self, data: &[u8]) -> Result<Signature>;
fn algorithm(&self) -> &str;
fn verify(&self, _data: &[u8], _signature: &[u8]) -> Result<bool> {
Ok(false)
}
}
pub struct HmacSigner {
key: Vec<u8>,
algorithm: String,
}
impl HmacSigner {
pub fn new(key: Vec<u8>, algorithm: String) -> Self {
Self { key, algorithm }
}
pub fn hs256(key: Vec<u8>) -> Self {
Self::new(key, "HS256".to_string())
}
}
impl Signer for HmacSigner {
fn sign(&self, data: &[u8]) -> Result<Signature> {
tracing::debug!("Signing data with HMAC algorithm: {}", self.algorithm);
match self.algorithm.as_str() {
"HS256" | "HMAC-SHA256" => {
let mac = Hmac::<Sha256>::new_from_slice(&self.key).map_err(|e| {
tracing::error!("Invalid HMAC key: {}", e);
AcmeError::crypto(format!("HMAC key error: {}", e))
})?;
let mut mac = mac;
mac.update(data);
let result = mac.finalize().into_bytes().to_vec();
Ok(Signature::new(result, self.algorithm.clone()))
}
_ => {
tracing::error!("Unsupported HMAC algorithm requested: {}", self.algorithm);
Err(AcmeError::crypto(format!(
"Unsupported HMAC algorithm: {}",
self.algorithm
)))
}
}
}
fn algorithm(&self) -> &str {
&self.algorithm
}
fn verify(&self, data: &[u8], signature: &[u8]) -> Result<bool> {
tracing::debug!(
"Verifying HMAC signature with algorithm: {}",
self.algorithm
);
match self.algorithm.as_str() {
"HS256" | "HMAC-SHA256" => {
let mac = Hmac::<Sha256>::new_from_slice(&self.key).map_err(|e| {
tracing::error!("Invalid HMAC key during verification: {}", e);
AcmeError::crypto(format!("HMAC key error: {}", e))
})?;
let mut mac = mac;
mac.update(data);
Ok(mac.verify_slice(signature).is_ok())
}
_ => {
tracing::warn!(
"Verification not supported for algorithm: {}",
self.algorithm
);
Ok(false)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_signature_base64() {
let sig = Signature::new(vec![1, 2, 3, 4], "test".to_string());
let base64 = sig.to_base64();
assert!(!base64.is_empty());
}
#[test]
fn test_hmac_signer() {
let key = b"secret-key".to_vec();
let signer = HmacSigner::hs256(key);
let data = b"hello world";
let sig = signer.sign(data).unwrap();
assert_eq!(sig.algorithm, "HS256");
assert_eq!(sig.data.len(), 32);
let verified = signer.verify(data, &sig.data).unwrap();
assert!(verified);
let wrong_data = b"wrong data";
let verified_wrong = signer.verify(wrong_data, &sig.data).unwrap();
assert!(!verified_wrong);
}
}