#[cfg(feature = "signing")]
use hmac::{Hmac, Mac};
#[cfg(feature = "signing")]
use sha2::Sha256;
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SignatureError {
InvalidSignature,
VerificationFailed,
InvalidKeyLength,
}
impl fmt::Display for SignatureError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SignatureError::InvalidSignature => write!(f, "Invalid signature format"),
SignatureError::VerificationFailed => write!(f, "Signature verification failed"),
SignatureError::InvalidKeyLength => write!(f, "Invalid key length"),
}
}
}
impl std::error::Error for SignatureError {}
#[cfg(feature = "signing")]
type HmacSha256 = Hmac<Sha256>;
#[cfg(feature = "signing")]
pub struct MessageSigner {
key: Vec<u8>,
}
#[cfg(feature = "signing")]
impl MessageSigner {
pub fn new(key: &[u8]) -> Self {
Self { key: key.to_vec() }
}
pub fn sign(&self, message: &[u8]) -> Result<Vec<u8>, SignatureError> {
let mut mac =
HmacSha256::new_from_slice(&self.key).map_err(|_| SignatureError::InvalidKeyLength)?;
mac.update(message);
Ok(mac.finalize().into_bytes().to_vec())
}
pub fn verify(&self, message: &[u8], signature: &[u8]) -> Result<(), SignatureError> {
let mut mac =
HmacSha256::new_from_slice(&self.key).map_err(|_| SignatureError::InvalidKeyLength)?;
mac.update(message);
mac.verify_slice(signature)
.map_err(|_| SignatureError::VerificationFailed)
}
pub fn sign_hex(&self, message: &[u8]) -> Result<String, SignatureError> {
let sig = self.sign(message)?;
Ok(hex::encode(sig))
}
pub fn verify_hex(&self, message: &[u8], signature_hex: &str) -> Result<(), SignatureError> {
let signature_bytes =
hex::decode(signature_hex).map_err(|_| SignatureError::InvalidSignature)?;
self.verify(message, &signature_bytes)
}
}
#[cfg(not(feature = "signing"))]
pub struct MessageSigner;
#[cfg(not(feature = "signing"))]
impl MessageSigner {
pub fn new(_key: &[u8]) -> Self {
Self
}
}
#[cfg(all(test, feature = "signing"))]
mod tests {
use super::*;
#[test]
fn test_sign_and_verify() {
let secret = b"my-secret-key";
let signer = MessageSigner::new(secret);
let message = b"test message";
let signature = signer.sign(message).expect("signing should not fail");
assert!(signer.verify(message, &signature).is_ok());
}
#[test]
fn test_verify_invalid_signature() {
let secret = b"my-secret-key";
let signer = MessageSigner::new(secret);
let message = b"test message";
let wrong_signature = vec![0u8; 32];
assert!(signer.verify(message, &wrong_signature).is_err());
}
#[test]
fn test_verify_wrong_message() {
let secret = b"my-secret-key";
let signer = MessageSigner::new(secret);
let message = b"test message";
let signature = signer.sign(message).expect("signing should not fail");
let wrong_message = b"different message";
assert!(signer.verify(wrong_message, &signature).is_err());
}
#[test]
fn test_sign_hex() {
let secret = b"my-secret-key";
let signer = MessageSigner::new(secret);
let message = b"test message";
let signature_hex = signer.sign_hex(message).expect("signing should not fail");
assert!(hex::decode(&signature_hex).is_ok());
assert!(signer.verify_hex(message, &signature_hex).is_ok());
}
#[test]
fn test_verify_hex_invalid() {
let secret = b"my-secret-key";
let signer = MessageSigner::new(secret);
let message = b"test message";
let invalid_hex = "not-valid-hex!!";
assert_eq!(
signer.verify_hex(message, invalid_hex),
Err(SignatureError::InvalidSignature)
);
}
#[test]
fn test_signature_deterministic() {
let secret = b"my-secret-key";
let signer = MessageSigner::new(secret);
let message = b"test message";
let sig1 = signer.sign(message).expect("signing should not fail");
let sig2 = signer.sign(message).expect("signing should not fail");
assert_eq!(sig1, sig2);
}
#[test]
fn test_different_keys_different_signatures() {
let message = b"test message";
let signer1 = MessageSigner::new(b"key1");
let signer2 = MessageSigner::new(b"key2");
let sig1 = signer1.sign(message).expect("signing should not fail");
let sig2 = signer2.sign(message).expect("signing should not fail");
assert_ne!(sig1, sig2);
}
#[test]
fn test_signature_error_display() {
assert_eq!(
SignatureError::InvalidSignature.to_string(),
"Invalid signature format"
);
assert_eq!(
SignatureError::VerificationFailed.to_string(),
"Signature verification failed"
);
assert_eq!(
SignatureError::InvalidKeyLength.to_string(),
"Invalid key length"
);
}
}