use crate::error::{Error, Result};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum SignatureAlgorithm {
RsaPkcs1v15,
RsaPss,
Ecdsa,
EdDsa,
Dsa,
}
impl SignatureAlgorithm {
pub fn as_str(&self) -> &'static str {
match self {
SignatureAlgorithm::RsaPkcs1v15 => "rsa-pkcs1v15",
SignatureAlgorithm::RsaPss => "rsa-pss",
SignatureAlgorithm::Ecdsa => "ecdsa",
SignatureAlgorithm::EdDsa => "eddsa",
SignatureAlgorithm::Dsa => "dsa",
}
}
pub fn min_key_size(&self) -> usize {
match self {
SignatureAlgorithm::RsaPkcs1v15 => 2048,
SignatureAlgorithm::RsaPss => 2048,
SignatureAlgorithm::Ecdsa => 256,
SignatureAlgorithm::EdDsa => 256,
SignatureAlgorithm::Dsa => 2048,
}
}
pub fn is_secure(&self) -> bool {
match self {
SignatureAlgorithm::RsaPkcs1v15 => true,
SignatureAlgorithm::RsaPss => true,
SignatureAlgorithm::Ecdsa => true,
SignatureAlgorithm::EdDsa => true,
SignatureAlgorithm::Dsa => false, }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum SignatureFormat {
Raw,
Base64,
Hex,
Pem,
Der,
}
impl SignatureFormat {
pub fn as_str(&self) -> &'static str {
match self {
SignatureFormat::Raw => "raw",
SignatureFormat::Base64 => "base64",
SignatureFormat::Hex => "hex",
SignatureFormat::Pem => "pem",
SignatureFormat::Der => "der",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PublicKeyFormat {
Pem,
Der,
Raw,
Jwk,
Ssh,
}
impl PublicKeyFormat {
pub fn as_str(&self) -> &'static str {
match self {
PublicKeyFormat::Pem => "pem",
PublicKeyFormat::Der => "der",
PublicKeyFormat::Raw => "raw",
PublicKeyFormat::Jwk => "jwk",
PublicKeyFormat::Ssh => "ssh",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PublicKey {
pub algorithm: SignatureAlgorithm,
pub format: PublicKeyFormat,
pub data: Vec<u8>,
pub key_id: Option<String>,
pub usage: Option<KeyUsage>,
}
impl PublicKey {
pub fn new(algorithm: SignatureAlgorithm, format: PublicKeyFormat, data: Vec<u8>) -> Self {
Self {
algorithm,
format,
data,
key_id: None,
usage: None,
}
}
pub fn with_key_id(mut self, key_id: String) -> Self {
self.key_id = Some(key_id);
self
}
pub fn with_usage(mut self, usage: KeyUsage) -> Self {
self.usage = Some(usage);
self
}
pub fn key_size(&self) -> usize {
self.data.len() * 8
}
pub fn validate(&self) -> Result<()> {
if !self.algorithm.is_secure() {
return Err(Error::InvalidState(format!(
"Algorithm {:?} is not considered secure",
self.algorithm
)));
}
if self.key_size() < self.algorithm.min_key_size() {
return Err(Error::InvalidState(format!(
"Key size {} is below minimum {} for algorithm {:?}",
self.key_size(),
self.algorithm.min_key_size(),
self.algorithm
)));
}
Ok(())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KeyUsage {
pub purposes: Vec<KeyPurpose>,
pub expires_at: Option<u64>,
pub not_before: Option<u64>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum KeyPurpose {
CodeSigning,
DocumentSigning,
TimestampSigning,
CertificateSigning,
Custom(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Signature {
pub algorithm: SignatureAlgorithm,
pub format: SignatureFormat,
pub data: Vec<u8>,
pub key_id: Option<String>,
pub created_at: Option<u64>,
pub expires_at: Option<u64>,
}
impl Signature {
pub fn new(algorithm: SignatureAlgorithm, format: SignatureFormat, data: Vec<u8>) -> Self {
Self {
algorithm,
format,
data,
key_id: None,
created_at: None,
expires_at: None,
}
}
pub fn with_key_id(mut self, key_id: String) -> Self {
self.key_id = Some(key_id);
self
}
pub fn with_created_at(mut self, created_at: u64) -> Self {
self.created_at = Some(created_at);
self
}
pub fn with_expires_at(mut self, expires_at: u64) -> Self {
self.expires_at = Some(expires_at);
self
}
pub fn is_expired(&self) -> bool {
if let Some(expires_at) = self.expires_at {
let now = 0; now > expires_at
} else {
false
}
}
pub fn decode_data(&self) -> Result<Vec<u8>> {
match self.format {
SignatureFormat::Raw => Ok(self.data.clone()),
SignatureFormat::Base64 => {
use base64::{Engine as _, engine::general_purpose};
general_purpose::STANDARD
.decode(&self.data)
.map_err(|e| Error::InvalidState(format!("Invalid base64 signature: {}", e)))
}
SignatureFormat::Hex => hex::decode(&self.data)
.map_err(|e| Error::InvalidState(format!("Invalid hex signature: {}", e))),
SignatureFormat::Pem | SignatureFormat::Der => {
Err(Error::InvalidState(
"PEM/DER signature parsing not implemented".to_string(),
))
}
}
}
}
#[derive(Debug, Clone)]
pub struct SignatureConfig {
pub public_key: PublicKey,
pub signature: Signature,
pub signed_data: Option<Vec<u8>>,
pub ignore_expired: bool,
}
impl SignatureConfig {
pub fn new(public_key: PublicKey, signature: Signature) -> Self {
Self {
public_key,
signature,
signed_data: None,
ignore_expired: false,
}
}
pub fn with_signed_data(mut self, data: Vec<u8>) -> Self {
self.signed_data = Some(data);
self
}
pub fn with_ignore_expired(mut self, ignore: bool) -> Self {
self.ignore_expired = ignore;
self
}
pub fn validate(&self) -> Result<()> {
self.public_key.validate()?;
if self.public_key.algorithm != self.signature.algorithm {
return Err(Error::InvalidState(format!(
"Algorithm mismatch: key is {:?}, signature is {:?}",
self.public_key.algorithm, self.signature.algorithm
)));
}
if !self.ignore_expired && self.signature.is_expired() {
return Err(Error::InvalidState("Signature has expired".to_string()));
}
if let (Some(key_key_id), Some(sig_key_id)) =
(&self.public_key.key_id, &self.signature.key_id)
&& key_key_id != sig_key_id
{
return Err(Error::InvalidState("Key ID mismatch".to_string()));
}
Ok(())
}
}
pub trait SignatureVerifier: Send + Sync {
fn verify(&self, data: &[u8], config: &SignatureConfig) -> Result<bool>;
fn algorithm(&self) -> SignatureAlgorithm;
}
pub struct MockVerifier {
algorithm: SignatureAlgorithm,
should_verify: bool,
}
impl MockVerifier {
pub fn new(algorithm: SignatureAlgorithm, should_verify: bool) -> Self {
Self {
algorithm,
should_verify,
}
}
pub fn set_should_verify(&mut self, should_verify: bool) {
self.should_verify = should_verify;
}
}
impl SignatureVerifier for MockVerifier {
fn verify(&self, _data: &[u8], config: &SignatureConfig) -> Result<bool> {
config.validate()?;
Ok(self.should_verify)
}
fn algorithm(&self) -> SignatureAlgorithm {
self.algorithm
}
}
pub struct SignatureManager {
verifiers: HashMap<SignatureAlgorithm, Box<dyn SignatureVerifier>>,
}
impl SignatureManager {
pub fn new() -> Self {
let mut manager = Self {
verifiers: HashMap::new(),
};
manager.add_mock_verifiers();
manager
}
fn add_mock_verifiers(&mut self) {
use SignatureAlgorithm::*;
self.verifiers
.insert(RsaPkcs1v15, Box::new(MockVerifier::new(RsaPkcs1v15, true)));
self.verifiers
.insert(RsaPss, Box::new(MockVerifier::new(RsaPss, true)));
self.verifiers
.insert(Ecdsa, Box::new(MockVerifier::new(Ecdsa, true)));
self.verifiers
.insert(EdDsa, Box::new(MockVerifier::new(EdDsa, true)));
self.verifiers
.insert(Dsa, Box::new(MockVerifier::new(Dsa, true)));
}
pub fn add_verifier(&mut self, verifier: Box<dyn SignatureVerifier>) {
self.verifiers.insert(verifier.algorithm(), verifier);
}
pub fn verify(&self, data: &[u8], config: &SignatureConfig) -> Result<bool> {
if let Some(verifier) = self.verifiers.get(&config.signature.algorithm) {
verifier.verify(data, config)
} else {
Err(Error::InvalidState(format!(
"No verifier available for algorithm {:?}",
config.signature.algorithm
)))
}
}
pub fn supports_algorithm(&self, algorithm: SignatureAlgorithm) -> bool {
self.verifiers.contains_key(&algorithm)
}
pub fn supported_algorithms(&self) -> Vec<SignatureAlgorithm> {
self.verifiers.keys().copied().collect()
}
}
impl Default for SignatureManager {
fn default() -> Self {
Self::new()
}
}
pub fn verify_signature(data: &[u8], config: &SignatureConfig) -> Result<bool> {
let manager = SignatureManager::new();
manager.verify(data, config)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_signature_algorithm() {
assert_eq!(SignatureAlgorithm::RsaPkcs1v15.as_str(), "rsa-pkcs1v15");
assert_eq!(SignatureAlgorithm::RsaPkcs1v15.min_key_size(), 2048);
assert!(SignatureAlgorithm::RsaPkcs1v15.is_secure());
assert!(!SignatureAlgorithm::Dsa.is_secure());
}
#[test]
fn test_signature_format() {
assert_eq!(SignatureFormat::Base64.as_str(), "base64");
}
#[test]
fn test_public_key_format() {
assert_eq!(PublicKeyFormat::Pem.as_str(), "pem");
}
#[test]
fn test_public_key_creation() {
let key = PublicKey::new(
SignatureAlgorithm::RsaPkcs1v15,
PublicKeyFormat::Pem,
b"mock_key_data".to_vec(),
)
.with_key_id("test-key".to_string());
assert_eq!(key.algorithm, SignatureAlgorithm::RsaPkcs1v15);
assert_eq!(key.format, PublicKeyFormat::Pem);
assert_eq!(key.data, b"mock_key_data");
assert_eq!(key.key_id, Some("test-key".to_string()));
}
#[test]
fn test_public_key_validation() {
let key = PublicKey::new(
SignatureAlgorithm::RsaPkcs1v15,
PublicKeyFormat::Pem,
vec![0u8; 256], );
assert!(key.validate().is_ok());
let key = PublicKey::new(
SignatureAlgorithm::Dsa,
PublicKeyFormat::Pem,
vec![0u8; 256],
);
assert!(key.validate().is_err());
let key = PublicKey::new(
SignatureAlgorithm::RsaPkcs1v15,
PublicKeyFormat::Pem,
vec![0u8; 128], );
assert!(key.validate().is_err());
}
#[test]
fn test_signature_creation() {
let sig = Signature::new(
SignatureAlgorithm::RsaPkcs1v15,
SignatureFormat::Base64,
b"mock_signature".to_vec(),
)
.with_key_id("test-key".to_string())
.with_created_at(1234567890)
.with_expires_at(1234567990);
assert_eq!(sig.algorithm, SignatureAlgorithm::RsaPkcs1v15);
assert_eq!(sig.format, SignatureFormat::Base64);
assert_eq!(sig.data, b"mock_signature");
assert_eq!(sig.key_id, Some("test-key".to_string()));
assert_eq!(sig.created_at, Some(1234567890));
assert_eq!(sig.expires_at, Some(1234567990));
}
#[test]
fn test_signature_decode_data() {
let sig = Signature::new(
SignatureAlgorithm::RsaPkcs1v15,
SignatureFormat::Raw,
b"raw_data".to_vec(),
);
assert_eq!(sig.decode_data().unwrap(), b"raw_data");
let sig = Signature::new(
SignatureAlgorithm::RsaPkcs1v15,
SignatureFormat::Hex,
b"48656c6c6f".to_vec(), );
assert_eq!(sig.decode_data().unwrap(), b"Hello");
}
#[test]
fn test_signature_config() {
let key = PublicKey::new(
SignatureAlgorithm::RsaPkcs1v15,
PublicKeyFormat::Pem,
vec![0u8; 256],
);
let sig = Signature::new(
SignatureAlgorithm::RsaPkcs1v15,
SignatureFormat::Raw,
b"signature".to_vec(),
);
let config = SignatureConfig::new(key, sig)
.with_signed_data(b"data".to_vec())
.with_ignore_expired(true);
assert!(config.validate().is_ok());
}
#[test]
fn test_signature_config_algorithm_mismatch() {
let key = PublicKey::new(
SignatureAlgorithm::RsaPkcs1v15,
PublicKeyFormat::Pem,
vec![0u8; 256],
);
let sig = Signature::new(
SignatureAlgorithm::Ecdsa,
SignatureFormat::Raw,
b"signature".to_vec(),
);
let config = SignatureConfig::new(key, sig);
assert!(config.validate().is_err());
}
#[test]
fn test_mock_verifier() {
let verifier = MockVerifier::new(SignatureAlgorithm::RsaPkcs1v15, true);
assert_eq!(verifier.algorithm(), SignatureAlgorithm::RsaPkcs1v15);
let key = PublicKey::new(
SignatureAlgorithm::RsaPkcs1v15,
PublicKeyFormat::Pem,
vec![0u8; 256],
);
let sig = Signature::new(
SignatureAlgorithm::RsaPkcs1v15,
SignatureFormat::Raw,
b"signature".to_vec(),
);
let config = SignatureConfig::new(key, sig);
assert!(verifier.verify(b"data", &config).unwrap());
let verifier = MockVerifier::new(SignatureAlgorithm::RsaPkcs1v15, false);
assert!(!verifier.verify(b"data", &config).unwrap());
}
#[test]
fn test_signature_manager() {
let manager = SignatureManager::new();
assert!(manager.supports_algorithm(SignatureAlgorithm::RsaPkcs1v15));
assert!(manager.supports_algorithm(SignatureAlgorithm::Ecdsa));
let algorithms = manager.supported_algorithms();
assert!(algorithms.contains(&SignatureAlgorithm::RsaPkcs1v15));
assert!(algorithms.contains(&SignatureAlgorithm::Ecdsa));
let key = PublicKey::new(
SignatureAlgorithm::RsaPkcs1v15,
PublicKeyFormat::Pem,
vec![0u8; 256],
);
let sig = Signature::new(
SignatureAlgorithm::RsaPkcs1v15,
SignatureFormat::Raw,
b"signature".to_vec(),
);
let config = SignatureConfig::new(key, sig);
assert!(manager.verify(b"data", &config).unwrap());
}
#[test]
fn test_convenience_function() {
let key = PublicKey::new(
SignatureAlgorithm::RsaPkcs1v15,
PublicKeyFormat::Pem,
vec![0u8; 256],
);
let sig = Signature::new(
SignatureAlgorithm::RsaPkcs1v15,
SignatureFormat::Raw,
b"signature".to_vec(),
);
let config = SignatureConfig::new(key, sig);
assert!(verify_signature(b"data", &config).unwrap());
}
}