pub mod ed25519;
pub mod rsa;
#[cfg(feature = "post-quantum")]
pub mod hybrid;
#[cfg(feature = "post-quantum")]
pub mod ml_dsa;
#[cfg(feature = "post-quantum")]
pub mod ml_kem;
#[cfg(all(test, feature = "post-quantum"))]
mod pq_tests;
use crate::error::{LicenseError, Result};
use std::collections::HashMap;
use std::sync::LazyLock;
pub mod algorithm_ids {
pub const RSA_SHA256: &str = "RSA-SHA256";
pub const ED25519: &str = "Ed25519";
#[cfg(feature = "post-quantum")]
pub const ML_DSA_65: &str = "ML-DSA-65";
#[cfg(feature = "post-quantum")]
pub const ML_KEM_768: &str = "ML-KEM-768";
#[cfg(feature = "post-quantum")]
pub const HYBRID_RSA_ML_DSA_65: &str = "Hybrid-RSA-ML-DSA-65";
#[cfg(feature = "post-quantum")]
pub const HYBRID_ED25519_ML_DSA_65: &str = "Hybrid-Ed25519-ML-DSA-65";
}
pub trait SignatureAlgorithm: Send + Sync {
fn algorithm_id(&self) -> &'static str;
fn sign(&self, data: &[u8], private_key_pem: &str) -> Result<Vec<u8>>;
fn verify(&self, data: &[u8], signature: &[u8], public_key_pem: &str) -> Result<()>;
fn generate_keypair(&self) -> Result<(String, String)>;
fn extract_public_key(&self, private_key_pem: &str) -> Result<String>;
}
pub trait EncryptionAlgorithm: Send + Sync {
fn algorithm_id(&self) -> &'static str;
fn encrypt(&self, data: &[u8], key: &[u8]) -> Result<Vec<u8>>;
fn decrypt(&self, data: &[u8], key: &[u8]) -> Result<Vec<u8>>;
fn key_size(&self) -> usize;
fn nonce_size(&self) -> usize;
}
pub type BoxedSignatureAlgorithm = Box<dyn SignatureAlgorithm>;
pub type BoxedEncryptionAlgorithm = Box<dyn EncryptionAlgorithm>;
pub struct CryptoRegistry;
static SIGNATURE_ALGORITHMS: LazyLock<HashMap<&'static str, BoxedSignatureAlgorithm>> =
LazyLock::new(|| {
let mut map: HashMap<&'static str, BoxedSignatureAlgorithm> = HashMap::new();
map.insert(algorithm_ids::RSA_SHA256, Box::new(rsa::RsaSigner::new()));
map.insert(
algorithm_ids::ED25519,
Box::new(ed25519::Ed25519Signer::new()),
);
#[cfg(feature = "post-quantum")]
{
map.insert(
algorithm_ids::ML_DSA_65,
Box::new(ml_dsa::MlDsa65Signer::new()),
);
map.insert(
algorithm_ids::HYBRID_RSA_ML_DSA_65,
Box::new(hybrid::HybridRsaMlDsaSigner::new()),
);
map.insert(
algorithm_ids::HYBRID_ED25519_ML_DSA_65,
Box::new(hybrid::HybridEd25519MlDsaSigner::new()),
);
}
map
});
impl CryptoRegistry {
pub fn get_signature_algorithm(algorithm_id: &str) -> Result<&'static dyn SignatureAlgorithm> {
SIGNATURE_ALGORITHMS
.get(algorithm_id)
.map(|boxed| boxed.as_ref())
.ok_or_else(|| {
LicenseError::InvalidKeyFormat(format!(
"Unknown signature algorithm: {}. Supported: {:?}",
algorithm_id,
Self::supported_signature_algorithms()
))
})
}
pub fn supported_signature_algorithms() -> Vec<&'static str> {
SIGNATURE_ALGORITHMS.keys().copied().collect()
}
pub fn is_signature_algorithm_supported(algorithm_id: &str) -> bool {
SIGNATURE_ALGORITHMS.contains_key(algorithm_id)
}
pub fn default_signature_algorithm() -> &'static dyn SignatureAlgorithm {
SIGNATURE_ALGORITHMS
.get(algorithm_ids::RSA_SHA256)
.map(|boxed| boxed.as_ref())
.expect("RSA-SHA256 should always be available")
}
pub fn is_post_quantum_available() -> bool {
#[cfg(feature = "post-quantum")]
{
true
}
#[cfg(not(feature = "post-quantum"))]
{
false
}
}
#[cfg(feature = "post-quantum")]
pub fn post_quantum_signature_algorithms() -> Vec<&'static str> {
vec![
algorithm_ids::ML_DSA_65,
algorithm_ids::HYBRID_RSA_ML_DSA_65,
algorithm_ids::HYBRID_ED25519_ML_DSA_65,
]
}
#[cfg(not(feature = "post-quantum"))]
pub fn post_quantum_signature_algorithms() -> Vec<&'static str> {
vec![]
}
pub fn classical_signature_algorithms() -> Vec<&'static str> {
vec![algorithm_ids::RSA_SHA256, algorithm_ids::ED25519]
}
#[cfg(feature = "post-quantum")]
pub fn hybrid_signature_algorithms() -> Vec<&'static str> {
vec![
algorithm_ids::HYBRID_RSA_ML_DSA_65,
algorithm_ids::HYBRID_ED25519_ML_DSA_65,
]
}
#[cfg(not(feature = "post-quantum"))]
pub fn hybrid_signature_algorithms() -> Vec<&'static str> {
vec![]
}
pub fn recommended_algorithm() -> &'static dyn SignatureAlgorithm {
#[cfg(feature = "post-quantum")]
{
SIGNATURE_ALGORITHMS
.get(algorithm_ids::HYBRID_ED25519_ML_DSA_65)
.map(|boxed| boxed.as_ref())
.expect("Hybrid-Ed25519-ML-DSA-65 should be available with post-quantum feature")
}
#[cfg(not(feature = "post-quantum"))]
{
SIGNATURE_ALGORITHMS
.get(algorithm_ids::ED25519)
.map(|boxed| boxed.as_ref())
.expect("Ed25519 should always be available")
}
}
}
pub use crate::keys::CryptoKeyPair;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_registry_get_rsa() {
let alg = CryptoRegistry::get_signature_algorithm(algorithm_ids::RSA_SHA256).unwrap();
assert_eq!(alg.algorithm_id(), algorithm_ids::RSA_SHA256);
}
#[test]
fn test_registry_get_ed25519() {
let alg = CryptoRegistry::get_signature_algorithm(algorithm_ids::ED25519).unwrap();
assert_eq!(alg.algorithm_id(), algorithm_ids::ED25519);
}
#[test]
fn test_registry_unknown_algorithm() {
let result = CryptoRegistry::get_signature_algorithm("UNKNOWN-ALG");
assert!(result.is_err());
}
#[test]
fn test_supported_algorithms() {
let supported = CryptoRegistry::supported_signature_algorithms();
assert!(supported.contains(&algorithm_ids::RSA_SHA256));
assert!(supported.contains(&algorithm_ids::ED25519));
}
#[test]
fn test_default_algorithm() {
let alg = CryptoRegistry::default_signature_algorithm();
assert_eq!(alg.algorithm_id(), algorithm_ids::RSA_SHA256);
}
#[test]
fn test_crypto_keypair_rsa() {
let keypair = CryptoKeyPair::generate(algorithm_ids::RSA_SHA256).unwrap();
assert_eq!(keypair.algorithm_id, algorithm_ids::RSA_SHA256);
assert!(keypair.private_key_pem().contains("PRIVATE KEY"));
assert!(keypair.public_key_pem.contains("PUBLIC KEY"));
let data = b"test message";
let signature = keypair.sign(data).unwrap();
assert!(keypair.verify(data, &signature).is_ok());
}
#[test]
fn test_crypto_keypair_ed25519() {
let keypair = CryptoKeyPair::generate(algorithm_ids::ED25519).unwrap();
assert_eq!(keypair.algorithm_id, algorithm_ids::ED25519);
assert!(keypair.private_key_pem().contains("PRIVATE KEY"));
assert!(keypair.public_key_pem.contains("PUBLIC KEY"));
let data = b"test message";
let signature = keypair.sign(data).unwrap();
assert!(keypair.verify(data, &signature).is_ok());
}
}