use crate::crypto::{algorithm_ids, CryptoRegistry, SignatureAlgorithm};
use crate::error::{LicenseError, Result};
use crate::hardware::{
default_hardware_environment, verify_hardware_binding, FixedHardwareEnvironment,
HardwareBindingError, HardwareEnvironment, HardwareInfo,
};
use crate::keys::parse_public_key;
use crate::license::{LicenseFormat, SignedLicense, BINARY_MAGIC, BINARY_VERSION};
use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
use chrono::Utc;
use rsa::pkcs1v15::VerifyingKey;
use rsa::signature::Verifier;
use rsa::RsaPublicKey;
use sha2::Sha256;
use std::path::Path;
use std::sync::Arc;
#[derive(Clone)]
pub struct LicenseVerifier {
public_key: RsaPublicKey,
hardware_env: Arc<dyn HardwareEnvironment>,
}
impl LicenseVerifier {
pub fn new(public_key: RsaPublicKey) -> Self {
Self {
public_key,
hardware_env: default_hardware_environment(),
}
}
pub fn from_pem(pem: &str) -> Result<Self> {
let public_key = parse_public_key(pem)?;
Ok(Self::new(public_key))
}
pub fn from_pem_file(path: &Path) -> Result<Self> {
let pem = std::fs::read_to_string(path)?;
Self::from_pem(&pem)
}
pub fn with_hardware_info(mut self, info: HardwareInfo) -> Self {
self.hardware_env = Arc::new(FixedHardwareEnvironment(info));
self
}
pub fn with_hardware_environment(mut self, env: Arc<dyn HardwareEnvironment>) -> Self {
self.hardware_env = env;
self
}
fn get_hardware_info(&self) -> HardwareInfo {
self.hardware_env.snapshot()
}
pub fn load_license(&self, path: &Path) -> Result<SignedLicense> {
let bytes = std::fs::read(path)?;
self.parse_license(&bytes)
}
pub fn parse_license(&self, data: &[u8]) -> Result<SignedLicense> {
let format = detect_license_format(data);
match format {
LicenseFormat::Binary => self.parse_binary_license(data),
LicenseFormat::Json => self.parse_json_license(data),
}
}
fn parse_binary_license(&self, data: &[u8]) -> Result<SignedLicense> {
if data.len() < 9 {
return Err(LicenseError::InvalidLicenseFormat(
"Binary license too short".into(),
));
}
if &data[0..4] != BINARY_MAGIC {
return Err(LicenseError::InvalidLicenseFormat(
"Invalid magic header".into(),
));
}
let version = data[4];
if version > BINARY_VERSION {
return Err(LicenseError::InvalidLicenseFormat(format!(
"Unsupported license version: {}",
version
)));
}
let len = u32::from_le_bytes([data[5], data[6], data[7], data[8]]) as usize;
if data.len() < 9 + len {
return Err(LicenseError::InvalidLicenseFormat(
"Binary license data truncated".into(),
));
}
serde_json::from_slice(&data[9..9 + len])
.map_err(|e| LicenseError::InvalidLicenseFormat(e.to_string()))
}
fn parse_json_license(&self, data: &[u8]) -> Result<SignedLicense> {
serde_json::from_slice(data).map_err(|e| LicenseError::InvalidLicenseFormat(e.to_string()))
}
pub fn verify_signature(&self, license: &SignedLicense) -> Result<()> {
let data_bytes = serde_json::to_vec(&license.data)
.map_err(|e| LicenseError::SerializationError(e.to_string()))?;
let signature_bytes = BASE64.decode(&license.signature).map_err(|e| {
LicenseError::InvalidLicenseFormat(format!("Invalid signature encoding: {}", e))
})?;
let verifying_key = VerifyingKey::<Sha256>::new(self.public_key.clone());
let signature =
rsa::pkcs1v15::Signature::try_from(signature_bytes.as_slice()).map_err(|e| {
LicenseError::VerificationFailed(format!("Invalid signature format: {}", e))
})?;
verifying_key.verify(&data_bytes, &signature).map_err(|e| {
LicenseError::VerificationFailed(format!("Signature verification failed: {}", e))
})
}
pub fn verify_expiration(&self, license: &SignedLicense) -> Result<()> {
let now = Utc::now();
if now < license.data.valid_from {
return Err(LicenseError::NotYetValid(
license
.data
.valid_from
.format("%Y-%m-%d %H:%M:%S UTC")
.to_string(),
));
}
if now > license.data.valid_until {
return Err(LicenseError::LicenseExpired(
license
.data
.valid_until
.format("%Y-%m-%d %H:%M:%S UTC")
.to_string(),
));
}
Ok(())
}
pub fn verify_hardware(&self, license: &SignedLicense) -> Result<()> {
let hardware = self.get_hardware_info();
verify_hardware_binding(&license.data.hardware_binding, &hardware).map_err(|e| {
tracing::debug!("Hardware binding check failed: {}", e);
match e {
HardwareBindingError::MacAddressMismatch { .. } => {
LicenseError::HardwareBindingMismatch {
field: "mac_address".to_string(),
}
}
HardwareBindingError::HostnameMismatch { .. } => {
LicenseError::HardwareBindingMismatch {
field: "hostname".to_string(),
}
}
HardwareBindingError::DiskIdMismatch { .. } => {
LicenseError::HardwareBindingMismatch {
field: "disk_id".to_string(),
}
}
HardwareBindingError::CustomMismatch { key, .. } => {
LicenseError::HardwareBindingMismatch { field: key }
}
}
})
}
pub fn validate(&self, license: &SignedLicense) -> Result<()> {
self.verify_signature(license)?;
self.verify_expiration(license)?;
self.verify_hardware(license)?;
Ok(())
}
pub fn load_and_validate(&self, path: &Path) -> Result<SignedLicense> {
let license = self.load_license(path)?;
self.validate(&license)?;
Ok(license)
}
}
pub fn detect_license_format(data: &[u8]) -> LicenseFormat {
if data.len() >= 4 && &data[0..4] == BINARY_MAGIC {
LicenseFormat::Binary
} else {
LicenseFormat::Json
}
}
#[derive(Debug, Clone)]
pub struct ValidationResult {
pub is_valid: bool,
pub signature_valid: bool,
pub expiration_valid: bool,
pub hardware_valid: bool,
pub days_remaining: i64,
pub error: Option<String>,
}
impl LicenseVerifier {
pub fn validate_detailed(&self, license: &SignedLicense) -> ValidationResult {
let mut result = ValidationResult {
is_valid: false,
signature_valid: false,
expiration_valid: false,
hardware_valid: false,
days_remaining: license.data.days_remaining(),
error: None,
};
match self.verify_signature(license) {
Ok(_) => result.signature_valid = true,
Err(e) => {
result.error = Some(e.to_string());
return result;
}
}
match self.verify_expiration(license) {
Ok(_) => result.expiration_valid = true,
Err(e) => {
result.error = Some(e.to_string());
return result;
}
}
match self.verify_hardware(license) {
Ok(_) => result.hardware_valid = true,
Err(e) => {
result.error = Some(e.to_string());
return result;
}
}
result.is_valid = true;
result
}
}
#[derive(Clone)]
pub struct CryptoVerifier {
public_keys: std::collections::HashMap<String, String>,
hardware_env: Arc<dyn HardwareEnvironment>,
}
impl CryptoVerifier {
pub fn new(public_keys: std::collections::HashMap<String, String>) -> Self {
Self {
public_keys,
hardware_env: default_hardware_environment(),
}
}
pub fn from_pem(pem: &str) -> Result<Self> {
let mut keys = std::collections::HashMap::new();
if parse_public_key(pem).is_ok() {
keys.insert(algorithm_ids::RSA_SHA256.to_string(), pem.to_string());
return Ok(Self::new(keys));
}
let ed25519_signer = crate::crypto::ed25519::Ed25519Signer::new();
let verify_result: Result<()> = ed25519_signer.verify(b"test", &[0u8; 64], pem);
match verify_result {
Ok(()) => {
keys.insert(algorithm_ids::ED25519.to_string(), pem.to_string());
return Ok(Self::new(keys));
}
Err(e) => {
let err_str = e.to_string();
if err_str.contains("verification failed")
|| err_str.contains("Signature verification failed")
{
keys.insert(algorithm_ids::ED25519.to_string(), pem.to_string());
return Ok(Self::new(keys));
}
}
}
let pem_normalized = pem.replace("\\n", "\n");
if pem_normalized.contains("PUBLIC KEY") {
keys.insert(algorithm_ids::RSA_SHA256.to_string(), pem.to_string());
return Ok(Self::new(keys));
}
Err(LicenseError::InvalidKeyFormat(
"Could not determine public key type".into(),
))
}
pub fn from_pem_file(path: &Path) -> Result<Self> {
let pem = std::fs::read_to_string(path)?;
Self::from_pem(&pem)
}
pub fn with_public_key(mut self, algorithm_id: &str, pem: &str) -> Self {
self.public_keys
.insert(algorithm_id.to_string(), pem.to_string());
self
}
pub fn with_hardware_info(mut self, info: HardwareInfo) -> Self {
self.hardware_env = Arc::new(FixedHardwareEnvironment(info));
self
}
pub fn with_hardware_environment(mut self, env: Arc<dyn HardwareEnvironment>) -> Self {
self.hardware_env = env;
self
}
fn get_hardware_info(&self) -> HardwareInfo {
self.hardware_env.snapshot()
}
fn get_public_key(&self, algorithm_id: &str) -> Result<&str> {
self.public_keys
.get(algorithm_id)
.map(|s| s.as_str())
.ok_or_else(|| {
LicenseError::InvalidKeyFormat(format!(
"No public key configured for algorithm: {}",
algorithm_id
))
})
}
pub fn load_license(&self, path: &Path) -> Result<SignedLicense> {
let bytes = std::fs::read(path)?;
self.parse_license(&bytes)
}
pub fn parse_license(&self, data: &[u8]) -> Result<SignedLicense> {
let format = detect_license_format(data);
match format {
LicenseFormat::Binary => self.parse_binary_license(data),
LicenseFormat::Json => self.parse_json_license(data),
}
}
fn parse_binary_license(&self, data: &[u8]) -> Result<SignedLicense> {
if data.len() < 9 {
return Err(LicenseError::InvalidLicenseFormat(
"Binary license too short".into(),
));
}
if &data[0..4] != BINARY_MAGIC {
return Err(LicenseError::InvalidLicenseFormat(
"Invalid magic header".into(),
));
}
let version = data[4];
if version > BINARY_VERSION {
return Err(LicenseError::InvalidLicenseFormat(format!(
"Unsupported license version: {}",
version
)));
}
let len = u32::from_le_bytes([data[5], data[6], data[7], data[8]]) as usize;
if data.len() < 9 + len {
return Err(LicenseError::InvalidLicenseFormat(
"Binary license data truncated".into(),
));
}
serde_json::from_slice(&data[9..9 + len])
.map_err(|e| LicenseError::InvalidLicenseFormat(e.to_string()))
}
fn parse_json_license(&self, data: &[u8]) -> Result<SignedLicense> {
serde_json::from_slice(data).map_err(|e| LicenseError::InvalidLicenseFormat(e.to_string()))
}
pub fn verify_signature(&self, license: &SignedLicense) -> Result<()> {
let data_bytes = serde_json::to_vec(&license.data)
.map_err(|e| LicenseError::SerializationError(e.to_string()))?;
let signature_bytes = BASE64.decode(&license.signature).map_err(|e| {
LicenseError::InvalidLicenseFormat(format!("Invalid signature encoding: {}", e))
})?;
let algorithm = CryptoRegistry::get_signature_algorithm(&license.algorithm)?;
let public_key_pem = self.get_public_key(&license.algorithm)?;
algorithm.verify(&data_bytes, &signature_bytes, public_key_pem)
}
pub fn verify_expiration(&self, license: &SignedLicense) -> Result<()> {
let now = Utc::now();
if now < license.data.valid_from {
return Err(LicenseError::NotYetValid(
license
.data
.valid_from
.format("%Y-%m-%d %H:%M:%S UTC")
.to_string(),
));
}
if now > license.data.valid_until {
return Err(LicenseError::LicenseExpired(
license
.data
.valid_until
.format("%Y-%m-%d %H:%M:%S UTC")
.to_string(),
));
}
Ok(())
}
pub fn verify_hardware(&self, license: &SignedLicense) -> Result<()> {
let hardware = self.get_hardware_info();
verify_hardware_binding(&license.data.hardware_binding, &hardware).map_err(|e| {
tracing::debug!("Hardware binding check failed: {}", e);
match e {
HardwareBindingError::MacAddressMismatch { .. } => {
LicenseError::HardwareBindingMismatch {
field: "mac_address".to_string(),
}
}
HardwareBindingError::HostnameMismatch { .. } => {
LicenseError::HardwareBindingMismatch {
field: "hostname".to_string(),
}
}
HardwareBindingError::DiskIdMismatch { .. } => {
LicenseError::HardwareBindingMismatch {
field: "disk_id".to_string(),
}
}
HardwareBindingError::CustomMismatch { key, .. } => {
LicenseError::HardwareBindingMismatch { field: key }
}
}
})
}
pub fn validate(&self, license: &SignedLicense) -> Result<()> {
self.verify_signature(license)?;
self.verify_expiration(license)?;
self.verify_hardware(license)?;
Ok(())
}
pub fn load_and_validate(&self, path: &Path) -> Result<SignedLicense> {
let license = self.load_license(path)?;
self.validate(&license)?;
Ok(license)
}
pub fn validate_detailed(&self, license: &SignedLicense) -> ValidationResult {
let mut result = ValidationResult {
is_valid: false,
signature_valid: false,
expiration_valid: false,
hardware_valid: false,
days_remaining: license.data.days_remaining(),
error: None,
};
match self.verify_signature(license) {
Ok(_) => result.signature_valid = true,
Err(e) => {
result.error = Some(e.to_string());
return result;
}
}
match self.verify_expiration(license) {
Ok(_) => result.expiration_valid = true,
Err(e) => {
result.error = Some(e.to_string());
return result;
}
}
match self.verify_hardware(license) {
Ok(_) => result.hardware_valid = true,
Err(e) => {
result.error = Some(e.to_string());
return result;
}
}
result.is_valid = true;
result
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::generator::LicenseGenerator;
use crate::keys::{KeyPair, KeySize};
use crate::license::LicenseData;
fn create_test_keypair() -> KeyPair {
KeyPair::generate(KeySize::Bits2048).unwrap()
}
#[test]
fn test_license_verification() {
let keypair = create_test_keypair();
let generator = LicenseGenerator::new(keypair.private_key().clone());
let verifier = LicenseVerifier::new(keypair.public_key);
let data = LicenseData::builder()
.id("TEST-001")
.serial("SN-12345")
.customer_id("CUST-001")
.product_id("PROD-001")
.valid_days(365)
.build()
.unwrap();
let signed = generator.generate(data).unwrap();
assert!(verifier.verify_signature(&signed).is_ok());
assert!(verifier.verify_expiration(&signed).is_ok());
assert!(verifier.validate(&signed).is_ok());
}
#[test]
fn test_binary_round_trip() {
let keypair = create_test_keypair();
let generator = LicenseGenerator::new(keypair.private_key().clone());
let verifier = LicenseVerifier::new(keypair.public_key);
let data = LicenseData::builder()
.id("TEST-001")
.serial("SN-12345")
.customer_id("CUST-001")
.product_id("PROD-001")
.valid_days(365)
.feature("basic")
.feature("premium")
.build()
.unwrap();
let signed = generator.generate(data).unwrap();
let binary = generator.export_binary(&signed).unwrap();
let parsed = verifier.parse_license(&binary).unwrap();
assert_eq!(parsed.data.id, signed.data.id);
assert_eq!(parsed.data.serial, signed.data.serial);
assert_eq!(parsed.data.features.len(), 2);
}
#[test]
fn test_json_round_trip() {
let keypair = create_test_keypair();
let generator = LicenseGenerator::new(keypair.private_key().clone());
let verifier = LicenseVerifier::new(keypair.public_key);
let data = LicenseData::builder()
.id("TEST-001")
.serial("SN-12345")
.customer_id("CUST-001")
.product_id("PROD-001")
.valid_days(365)
.build()
.unwrap();
let signed = generator.generate(data).unwrap();
let json = generator.export_json(&signed).unwrap();
let parsed = verifier.parse_license(json.as_bytes()).unwrap();
assert_eq!(parsed.data.id, signed.data.id);
assert!(verifier.validate(&parsed).is_ok());
}
#[test]
fn test_crypto_verifier_rsa() {
use crate::generator::CryptoGenerator;
use crate::keys::CryptoKeyPair;
let keypair = CryptoKeyPair::generate(algorithm_ids::RSA_SHA256).unwrap();
let generator = CryptoGenerator::from_keypair(&keypair);
let mut keys = std::collections::HashMap::new();
keys.insert(
algorithm_ids::RSA_SHA256.to_string(),
keypair.public_key_pem.clone(),
);
let verifier = CryptoVerifier::new(keys);
let data = LicenseData::builder()
.id("CRYPTO-RSA-001")
.serial("SN-CRYPTO-RSA")
.customer_id("CUST-001")
.product_id("PROD-001")
.valid_days(365)
.build()
.unwrap();
let signed = generator.generate(data).unwrap();
assert_eq!(signed.algorithm, algorithm_ids::RSA_SHA256);
assert!(verifier.verify_signature(&signed).is_ok());
assert!(verifier.verify_expiration(&signed).is_ok());
assert!(verifier.validate(&signed).is_ok());
}
#[test]
fn test_crypto_verifier_ed25519() {
use crate::generator::CryptoGenerator;
use crate::keys::CryptoKeyPair;
let keypair = CryptoKeyPair::generate(algorithm_ids::ED25519).unwrap();
let generator = CryptoGenerator::from_keypair(&keypair);
let mut keys = std::collections::HashMap::new();
keys.insert(
algorithm_ids::ED25519.to_string(),
keypair.public_key_pem.clone(),
);
let verifier = CryptoVerifier::new(keys);
let data = LicenseData::builder()
.id("CRYPTO-ED25519-001")
.serial("SN-CRYPTO-ED25519")
.customer_id("CUST-001")
.product_id("PROD-001")
.valid_days(365)
.build()
.unwrap();
let signed = generator.generate(data).unwrap();
assert_eq!(signed.algorithm, algorithm_ids::ED25519);
assert!(verifier.verify_signature(&signed).is_ok());
assert!(verifier.verify_expiration(&signed).is_ok());
assert!(verifier.validate(&signed).is_ok());
}
#[test]
fn test_crypto_verifier_multi_algorithm() {
use crate::generator::CryptoGenerator;
use crate::keys::CryptoKeyPair;
let rsa_keypair = CryptoKeyPair::generate(algorithm_ids::RSA_SHA256).unwrap();
let ed25519_keypair = CryptoKeyPair::generate(algorithm_ids::ED25519).unwrap();
let rsa_generator = CryptoGenerator::from_keypair(&rsa_keypair);
let ed25519_generator = CryptoGenerator::from_keypair(&ed25519_keypair);
let mut keys = std::collections::HashMap::new();
keys.insert(
algorithm_ids::RSA_SHA256.to_string(),
rsa_keypair.public_key_pem.clone(),
);
keys.insert(
algorithm_ids::ED25519.to_string(),
ed25519_keypair.public_key_pem.clone(),
);
let verifier = CryptoVerifier::new(keys);
let rsa_data = LicenseData::builder()
.id("MULTI-RSA-001")
.serial("SN-MULTI-RSA")
.customer_id("CUST-001")
.product_id("PROD-001")
.valid_days(365)
.build()
.unwrap();
let rsa_license = rsa_generator.generate(rsa_data).unwrap();
let ed25519_data = LicenseData::builder()
.id("MULTI-ED25519-001")
.serial("SN-MULTI-ED25519")
.customer_id("CUST-001")
.product_id("PROD-001")
.valid_days(365)
.build()
.unwrap();
let ed25519_license = ed25519_generator.generate(ed25519_data).unwrap();
assert!(verifier.validate(&rsa_license).is_ok());
assert!(verifier.validate(&ed25519_license).is_ok());
assert_eq!(rsa_license.algorithm, algorithm_ids::RSA_SHA256);
assert_eq!(ed25519_license.algorithm, algorithm_ids::ED25519);
}
#[test]
fn test_crypto_verifier_binary_round_trip() {
use crate::generator::CryptoGenerator;
use crate::keys::CryptoKeyPair;
let keypair = CryptoKeyPair::generate(algorithm_ids::ED25519).unwrap();
let generator = CryptoGenerator::from_keypair(&keypair);
let mut keys = std::collections::HashMap::new();
keys.insert(
algorithm_ids::ED25519.to_string(),
keypair.public_key_pem.clone(),
);
let verifier = CryptoVerifier::new(keys);
let data = LicenseData::builder()
.id("BINARY-ED25519-001")
.serial("SN-BINARY-ED25519")
.customer_id("CUST-001")
.product_id("PROD-001")
.valid_days(365)
.feature("basic")
.feature("premium")
.build()
.unwrap();
let signed = generator.generate(data).unwrap();
let binary = generator.export_binary(&signed).unwrap();
let parsed = verifier.parse_license(&binary).unwrap();
assert_eq!(parsed.data.id, signed.data.id);
assert_eq!(parsed.data.serial, signed.data.serial);
assert_eq!(parsed.data.features.len(), 2);
assert_eq!(parsed.algorithm, algorithm_ids::ED25519);
assert!(verifier.validate(&parsed).is_ok());
}
#[test]
fn test_crypto_verifier_wrong_key() {
use crate::generator::CryptoGenerator;
use crate::keys::CryptoKeyPair;
let keypair = CryptoKeyPair::generate(algorithm_ids::ED25519).unwrap();
let wrong_keypair = CryptoKeyPair::generate(algorithm_ids::ED25519).unwrap();
let generator = CryptoGenerator::from_keypair(&keypair);
let mut keys = std::collections::HashMap::new();
keys.insert(
algorithm_ids::ED25519.to_string(),
wrong_keypair.public_key_pem.clone(),
);
let verifier = CryptoVerifier::new(keys);
let data = LicenseData::builder()
.id("WRONG-KEY-001")
.serial("SN-WRONG-KEY")
.customer_id("CUST-001")
.product_id("PROD-001")
.valid_days(365)
.build()
.unwrap();
let signed = generator.generate(data).unwrap();
assert!(verifier.verify_signature(&signed).is_err());
assert!(verifier.validate(&signed).is_err());
}
#[test]
fn test_crypto_verifier_missing_algorithm_key() {
use crate::generator::CryptoGenerator;
use crate::keys::CryptoKeyPair;
let keypair = CryptoKeyPair::generate(algorithm_ids::ED25519).unwrap();
let generator = CryptoGenerator::from_keypair(&keypair);
let rsa_keypair = CryptoKeyPair::generate(algorithm_ids::RSA_SHA256).unwrap();
let mut keys = std::collections::HashMap::new();
keys.insert(
algorithm_ids::RSA_SHA256.to_string(),
rsa_keypair.public_key_pem.clone(),
);
let verifier = CryptoVerifier::new(keys);
let data = LicenseData::builder()
.id("MISSING-ALG-001")
.serial("SN-MISSING-ALG")
.customer_id("CUST-001")
.product_id("PROD-001")
.valid_days(365)
.build()
.unwrap();
let signed = generator.generate(data).unwrap();
let result = verifier.verify_signature(&signed);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("No public key configured"));
}
#[test]
fn test_crypto_verifier_detailed_validation() {
use crate::generator::CryptoGenerator;
use crate::keys::CryptoKeyPair;
let keypair = CryptoKeyPair::generate(algorithm_ids::ED25519).unwrap();
let generator = CryptoGenerator::from_keypair(&keypair);
let mut keys = std::collections::HashMap::new();
keys.insert(
algorithm_ids::ED25519.to_string(),
keypair.public_key_pem.clone(),
);
let verifier = CryptoVerifier::new(keys);
let data = LicenseData::builder()
.id("DETAILED-001")
.serial("SN-DETAILED")
.customer_id("CUST-001")
.product_id("PROD-001")
.valid_days(365)
.build()
.unwrap();
let signed = generator.generate(data).unwrap();
let result = verifier.validate_detailed(&signed);
assert!(result.is_valid);
assert!(result.signature_valid);
assert!(result.expiration_valid);
assert!(result.hardware_valid);
assert!(result.days_remaining > 0);
assert!(result.error.is_none());
}
#[cfg(feature = "post-quantum")]
mod pq_tests {
use super::*;
use crate::crypto::algorithm_ids;
use crate::generator::CryptoGenerator;
use crate::keys::CryptoKeyPair;
#[test]
fn test_crypto_verifier_ml_dsa_65() {
let keypair = CryptoKeyPair::generate(algorithm_ids::ML_DSA_65).unwrap();
let generator = CryptoGenerator::from_keypair(&keypair);
let mut keys = std::collections::HashMap::new();
keys.insert(
algorithm_ids::ML_DSA_65.to_string(),
keypair.public_key_pem.clone(),
);
let verifier = CryptoVerifier::new(keys);
let data = LicenseData::builder()
.id("PQ-VERIFY-001")
.serial("SN-PQ-VERIFY")
.customer_id("CUST-001")
.product_id("PROD-001")
.valid_days(365)
.feature("quantum-safe")
.build()
.unwrap();
let signed = generator.generate(data).unwrap();
assert_eq!(signed.algorithm, algorithm_ids::ML_DSA_65);
assert!(verifier.verify_signature(&signed).is_ok());
assert!(verifier.verify_expiration(&signed).is_ok());
assert!(verifier.validate(&signed).is_ok());
}
#[test]
fn test_crypto_verifier_hybrid_ed25519_ml_dsa() {
let keypair = CryptoKeyPair::generate(algorithm_ids::HYBRID_ED25519_ML_DSA_65).unwrap();
let generator = CryptoGenerator::from_keypair(&keypair);
let mut keys = std::collections::HashMap::new();
keys.insert(
algorithm_ids::HYBRID_ED25519_ML_DSA_65.to_string(),
keypair.public_key_pem.clone(),
);
let verifier = CryptoVerifier::new(keys);
let data = LicenseData::builder()
.id("PQ-HYBRID-VERIFY-001")
.serial("SN-PQ-HYBRID-VERIFY")
.customer_id("CUST-001")
.product_id("PROD-001")
.valid_days(365)
.feature("hybrid-security")
.build()
.unwrap();
let signed = generator.generate(data).unwrap();
assert_eq!(signed.algorithm, algorithm_ids::HYBRID_ED25519_ML_DSA_65);
assert!(verifier.verify_signature(&signed).is_ok());
assert!(verifier.validate(&signed).is_ok());
}
#[test]
fn test_crypto_verifier_hybrid_rsa_ml_dsa() {
let keypair = CryptoKeyPair::generate(algorithm_ids::HYBRID_RSA_ML_DSA_65).unwrap();
let generator = CryptoGenerator::from_keypair(&keypair);
let mut keys = std::collections::HashMap::new();
keys.insert(
algorithm_ids::HYBRID_RSA_ML_DSA_65.to_string(),
keypair.public_key_pem.clone(),
);
let verifier = CryptoVerifier::new(keys);
let data = LicenseData::builder()
.id("PQ-HYBRID-RSA-VERIFY-001")
.serial("SN-PQ-HYBRID-RSA-VERIFY")
.customer_id("CUST-001")
.product_id("PROD-001")
.valid_days(365)
.build()
.unwrap();
let signed = generator.generate(data).unwrap();
assert_eq!(signed.algorithm, algorithm_ids::HYBRID_RSA_ML_DSA_65);
assert!(verifier.verify_signature(&signed).is_ok());
assert!(verifier.validate(&signed).is_ok());
}
#[test]
fn test_pq_verifier_binary_round_trip() {
let keypair = CryptoKeyPair::generate(algorithm_ids::ML_DSA_65).unwrap();
let generator = CryptoGenerator::from_keypair(&keypair);
let mut keys = std::collections::HashMap::new();
keys.insert(
algorithm_ids::ML_DSA_65.to_string(),
keypair.public_key_pem.clone(),
);
let verifier = CryptoVerifier::new(keys);
let data = LicenseData::builder()
.id("PQ-BINARY-VERIFY-001")
.serial("SN-PQ-BINARY-VERIFY")
.customer_id("CUST-001")
.product_id("PROD-001")
.valid_days(365)
.feature("quantum-safe")
.feature("binary-format")
.build()
.unwrap();
let signed = generator.generate(data).unwrap();
let binary = generator.export_binary(&signed).unwrap();
let parsed = verifier.parse_license(&binary).unwrap();
assert_eq!(parsed.data.id, signed.data.id);
assert_eq!(parsed.algorithm, algorithm_ids::ML_DSA_65);
assert!(verifier.validate(&parsed).is_ok());
}
#[test]
fn test_pq_verifier_wrong_key() {
let keypair = CryptoKeyPair::generate(algorithm_ids::ML_DSA_65).unwrap();
let wrong_keypair = CryptoKeyPair::generate(algorithm_ids::ML_DSA_65).unwrap();
let generator = CryptoGenerator::from_keypair(&keypair);
let mut keys = std::collections::HashMap::new();
keys.insert(
algorithm_ids::ML_DSA_65.to_string(),
wrong_keypair.public_key_pem.clone(),
);
let verifier = CryptoVerifier::new(keys);
let data = LicenseData::builder()
.id("PQ-WRONG-KEY-001")
.serial("SN-PQ-WRONG-KEY")
.customer_id("CUST-001")
.product_id("PROD-001")
.valid_days(365)
.build()
.unwrap();
let signed = generator.generate(data).unwrap();
assert!(verifier.verify_signature(&signed).is_err());
}
#[test]
fn test_pq_verifier_multi_algorithm() {
let rsa_keypair = CryptoKeyPair::generate(algorithm_ids::RSA_SHA256).unwrap();
let ed25519_keypair = CryptoKeyPair::generate(algorithm_ids::ED25519).unwrap();
let ml_dsa_keypair = CryptoKeyPair::generate(algorithm_ids::ML_DSA_65).unwrap();
let hybrid_keypair =
CryptoKeyPair::generate(algorithm_ids::HYBRID_ED25519_ML_DSA_65).unwrap();
let mut keys = std::collections::HashMap::new();
keys.insert(
algorithm_ids::RSA_SHA256.to_string(),
rsa_keypair.public_key_pem.clone(),
);
keys.insert(
algorithm_ids::ED25519.to_string(),
ed25519_keypair.public_key_pem.clone(),
);
keys.insert(
algorithm_ids::ML_DSA_65.to_string(),
ml_dsa_keypair.public_key_pem.clone(),
);
keys.insert(
algorithm_ids::HYBRID_ED25519_ML_DSA_65.to_string(),
hybrid_keypair.public_key_pem.clone(),
);
let verifier = CryptoVerifier::new(keys);
for (keypair, alg_name) in [
(&rsa_keypair, "RSA"),
(&ed25519_keypair, "Ed25519"),
(&ml_dsa_keypair, "ML-DSA-65"),
(&hybrid_keypair, "Hybrid"),
] {
let generator = CryptoGenerator::from_keypair(keypair);
let data = LicenseData::builder()
.id(format!("MULTI-{}-001", alg_name))
.serial(format!("SN-MULTI-{}", alg_name))
.customer_id("CUST-001")
.product_id("PROD-001")
.valid_days(365)
.build()
.unwrap();
let signed = generator.generate(data).unwrap();
assert!(
verifier.validate(&signed).is_ok(),
"Failed to verify {} license",
alg_name
);
}
}
#[test]
fn test_pq_verifier_detailed_validation() {
let keypair = CryptoKeyPair::generate(algorithm_ids::HYBRID_ED25519_ML_DSA_65).unwrap();
let generator = CryptoGenerator::from_keypair(&keypair);
let mut keys = std::collections::HashMap::new();
keys.insert(
algorithm_ids::HYBRID_ED25519_ML_DSA_65.to_string(),
keypair.public_key_pem.clone(),
);
let verifier = CryptoVerifier::new(keys);
let data = LicenseData::builder()
.id("PQ-DETAILED-001")
.serial("SN-PQ-DETAILED")
.customer_id("CUST-001")
.product_id("PROD-001")
.valid_days(365)
.build()
.unwrap();
let signed = generator.generate(data).unwrap();
let result = verifier.validate_detailed(&signed);
assert!(result.is_valid);
assert!(result.signature_valid);
assert!(result.expiration_valid);
assert!(result.hardware_valid);
assert!(result.days_remaining > 0);
assert!(result.error.is_none());
}
}
}