Skip to main content

licenz_core/
verifier.rs

1//! License verification functionality (client-side)
2//!
3//! This module provides license verification using the pluggable cryptographic
4//! architecture. It automatically selects the appropriate signature algorithm
5//! based on the algorithm identifier stored in the license.
6//!
7//! # Example
8//!
9//! ```rust,ignore
10//! use licenz_core::verifier::{CryptoVerifier, LicenseVerifier};
11//!
12//! // New algorithm-agnostic verifier (recommended)
13//! let verifier = CryptoVerifier::from_pem(public_key_pem).unwrap();
14//! let result = verifier.validate(&license);
15//!
16//! // Legacy RSA-only verifier (backward compatible)
17//! let verifier = LicenseVerifier::from_pem(public_key_pem).unwrap();
18//! ```
19
20use crate::crypto::{algorithm_ids, CryptoRegistry, SignatureAlgorithm};
21use crate::error::{LicenseError, Result};
22use crate::hardware::{
23    default_hardware_environment, verify_hardware_binding, FixedHardwareEnvironment,
24    HardwareBindingError, HardwareEnvironment, HardwareInfo,
25};
26use crate::keys::parse_public_key;
27use crate::license::{LicenseFormat, SignedLicense, BINARY_MAGIC, BINARY_VERSION};
28use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
29use chrono::Utc;
30use rsa::pkcs1v15::VerifyingKey;
31use rsa::signature::Verifier;
32use rsa::RsaPublicKey;
33use sha2::Sha256;
34use std::path::Path;
35use std::sync::Arc;
36
37/// License verifier for validating licenses
38#[derive(Clone)]
39pub struct LicenseVerifier {
40    public_key: RsaPublicKey,
41    hardware_env: Arc<dyn HardwareEnvironment>,
42}
43
44impl LicenseVerifier {
45    /// Create a new license verifier with a public key
46    pub fn new(public_key: RsaPublicKey) -> Self {
47        Self {
48            public_key,
49            hardware_env: default_hardware_environment(),
50        }
51    }
52
53    /// Create a new license verifier from a PEM string
54    pub fn from_pem(pem: &str) -> Result<Self> {
55        let public_key = parse_public_key(pem)?;
56        Ok(Self::new(public_key))
57    }
58
59    /// Create a new license verifier from a PEM file
60    pub fn from_pem_file(path: &Path) -> Result<Self> {
61        let pem = std::fs::read_to_string(path)?;
62        Self::from_pem(&pem)
63    }
64
65    /// Set custom hardware info (useful for testing or manual override)
66    pub fn with_hardware_info(mut self, info: HardwareInfo) -> Self {
67        self.hardware_env = Arc::new(FixedHardwareEnvironment(info));
68        self
69    }
70
71    /// Use a custom [`HardwareEnvironment`] (e.g. TPM-backed probe in your crate).
72    pub fn with_hardware_environment(mut self, env: Arc<dyn HardwareEnvironment>) -> Self {
73        self.hardware_env = env;
74        self
75    }
76
77    fn get_hardware_info(&self) -> HardwareInfo {
78        self.hardware_env.snapshot()
79    }
80
81    /// Load a license from a file (auto-detects format)
82    pub fn load_license(&self, path: &Path) -> Result<SignedLicense> {
83        let bytes = std::fs::read(path)?;
84        self.parse_license(&bytes)
85    }
86
87    /// Parse a license from bytes (auto-detects format)
88    pub fn parse_license(&self, data: &[u8]) -> Result<SignedLicense> {
89        let format = detect_license_format(data);
90
91        match format {
92            LicenseFormat::Binary => self.parse_binary_license(data),
93            LicenseFormat::Json => self.parse_json_license(data),
94        }
95    }
96
97    /// Parse a binary format license
98    fn parse_binary_license(&self, data: &[u8]) -> Result<SignedLicense> {
99        if data.len() < 9 {
100            return Err(LicenseError::InvalidLicenseFormat(
101                "Binary license too short".into(),
102            ));
103        }
104
105        // Verify magic header
106        if &data[0..4] != BINARY_MAGIC {
107            return Err(LicenseError::InvalidLicenseFormat(
108                "Invalid magic header".into(),
109            ));
110        }
111
112        // Check version
113        let version = data[4];
114        if version > BINARY_VERSION {
115            return Err(LicenseError::InvalidLicenseFormat(format!(
116                "Unsupported license version: {}",
117                version
118            )));
119        }
120
121        // Read length
122        let len = u32::from_le_bytes([data[5], data[6], data[7], data[8]]) as usize;
123
124        if data.len() < 9 + len {
125            return Err(LicenseError::InvalidLicenseFormat(
126                "Binary license data truncated".into(),
127            ));
128        }
129
130        // Deserialize the license from JSON
131        serde_json::from_slice(&data[9..9 + len])
132            .map_err(|e| LicenseError::InvalidLicenseFormat(e.to_string()))
133    }
134
135    /// Parse a JSON format license
136    fn parse_json_license(&self, data: &[u8]) -> Result<SignedLicense> {
137        serde_json::from_slice(data).map_err(|e| LicenseError::InvalidLicenseFormat(e.to_string()))
138    }
139
140    /// Verify the cryptographic signature of a license
141    pub fn verify_signature(&self, license: &SignedLicense) -> Result<()> {
142        // Serialize the data the same way it was signed
143        let data_bytes = serde_json::to_vec(&license.data)
144            .map_err(|e| LicenseError::SerializationError(e.to_string()))?;
145
146        // Decode the signature
147        let signature_bytes = BASE64.decode(&license.signature).map_err(|e| {
148            LicenseError::InvalidLicenseFormat(format!("Invalid signature encoding: {}", e))
149        })?;
150
151        // Create verifying key
152        let verifying_key = VerifyingKey::<Sha256>::new(self.public_key.clone());
153
154        // Parse signature
155        let signature =
156            rsa::pkcs1v15::Signature::try_from(signature_bytes.as_slice()).map_err(|e| {
157                LicenseError::VerificationFailed(format!("Invalid signature format: {}", e))
158            })?;
159
160        // Verify
161        verifying_key.verify(&data_bytes, &signature).map_err(|e| {
162            LicenseError::VerificationFailed(format!("Signature verification failed: {}", e))
163        })
164    }
165
166    /// Verify that the license has not expired
167    pub fn verify_expiration(&self, license: &SignedLicense) -> Result<()> {
168        let now = Utc::now();
169
170        if now < license.data.valid_from {
171            return Err(LicenseError::NotYetValid(
172                license
173                    .data
174                    .valid_from
175                    .format("%Y-%m-%d %H:%M:%S UTC")
176                    .to_string(),
177            ));
178        }
179
180        if now > license.data.valid_until {
181            return Err(LicenseError::LicenseExpired(
182                license
183                    .data
184                    .valid_until
185                    .format("%Y-%m-%d %H:%M:%S UTC")
186                    .to_string(),
187            ));
188        }
189
190        Ok(())
191    }
192
193    /// Verify hardware binding
194    pub fn verify_hardware(&self, license: &SignedLicense) -> Result<()> {
195        let hardware = self.get_hardware_info();
196
197        verify_hardware_binding(&license.data.hardware_binding, &hardware).map_err(|e| {
198            tracing::debug!("Hardware binding check failed: {}", e);
199            match e {
200                HardwareBindingError::MacAddressMismatch { .. } => {
201                    LicenseError::HardwareBindingMismatch {
202                        field: "mac_address".to_string(),
203                    }
204                }
205                HardwareBindingError::HostnameMismatch { .. } => {
206                    LicenseError::HardwareBindingMismatch {
207                        field: "hostname".to_string(),
208                    }
209                }
210                HardwareBindingError::DiskIdMismatch { .. } => {
211                    LicenseError::HardwareBindingMismatch {
212                        field: "disk_id".to_string(),
213                    }
214                }
215                HardwareBindingError::CustomMismatch { key, .. } => {
216                    LicenseError::HardwareBindingMismatch { field: key }
217                }
218            }
219        })
220    }
221
222    /// Perform full license validation
223    ///
224    /// This verifies:
225    /// 1. Cryptographic signature
226    /// 2. Expiration date
227    /// 3. Hardware binding (if enabled)
228    pub fn validate(&self, license: &SignedLicense) -> Result<()> {
229        self.verify_signature(license)?;
230        self.verify_expiration(license)?;
231        self.verify_hardware(license)?;
232        Ok(())
233    }
234
235    /// Load and validate a license from a file
236    pub fn load_and_validate(&self, path: &Path) -> Result<SignedLicense> {
237        let license = self.load_license(path)?;
238        self.validate(&license)?;
239        Ok(license)
240    }
241}
242
243/// Detect the format of a license file
244pub fn detect_license_format(data: &[u8]) -> LicenseFormat {
245    if data.len() >= 4 && &data[0..4] == BINARY_MAGIC {
246        LicenseFormat::Binary
247    } else {
248        LicenseFormat::Json
249    }
250}
251
252/// Validation result with detailed status
253#[derive(Debug, Clone)]
254pub struct ValidationResult {
255    /// Is the license valid?
256    pub is_valid: bool,
257
258    /// Signature verification status
259    pub signature_valid: bool,
260
261    /// Expiration status
262    pub expiration_valid: bool,
263
264    /// Hardware binding status
265    pub hardware_valid: bool,
266
267    /// Days remaining until expiration
268    pub days_remaining: i64,
269
270    /// Error message if invalid
271    pub error: Option<String>,
272}
273
274impl LicenseVerifier {
275    /// Perform detailed validation and return a result
276    pub fn validate_detailed(&self, license: &SignedLicense) -> ValidationResult {
277        let mut result = ValidationResult {
278            is_valid: false,
279            signature_valid: false,
280            expiration_valid: false,
281            hardware_valid: false,
282            days_remaining: license.data.days_remaining(),
283            error: None,
284        };
285
286        // Check signature
287        match self.verify_signature(license) {
288            Ok(_) => result.signature_valid = true,
289            Err(e) => {
290                result.error = Some(e.to_string());
291                return result;
292            }
293        }
294
295        // Check expiration
296        match self.verify_expiration(license) {
297            Ok(_) => result.expiration_valid = true,
298            Err(e) => {
299                result.error = Some(e.to_string());
300                return result;
301            }
302        }
303
304        // Check hardware
305        match self.verify_hardware(license) {
306            Ok(_) => result.hardware_valid = true,
307            Err(e) => {
308                result.error = Some(e.to_string());
309                return result;
310            }
311        }
312
313        result.is_valid = true;
314        result
315    }
316}
317
318/// Algorithm-agnostic license verifier that automatically selects the correct algorithm
319///
320/// This verifier uses the algorithm identifier stored in the license to select
321/// the appropriate signature verification algorithm. This enables support for
322/// multiple signature algorithms (RSA-SHA256, Ed25519, etc.) with automatic detection.
323///
324/// # Example
325///
326/// ```rust,ignore
327/// use licenz_core::verifier::CryptoVerifier;
328/// use std::collections::HashMap;
329///
330/// // Create verifier with multiple public keys
331/// let mut keys = HashMap::new();
332/// keys.insert("RSA-SHA256".to_string(), rsa_public_key_pem.to_string());
333/// keys.insert("Ed25519".to_string(), ed25519_public_key_pem.to_string());
334///
335/// let verifier = CryptoVerifier::new(keys);
336/// let result = verifier.validate(&license);
337/// ```
338#[derive(Clone)]
339pub struct CryptoVerifier {
340    /// Map of algorithm ID to public key PEM
341    public_keys: std::collections::HashMap<String, String>,
342    hardware_env: Arc<dyn HardwareEnvironment>,
343}
344
345impl CryptoVerifier {
346    /// Create a new crypto verifier with multiple public keys
347    ///
348    /// # Arguments
349    /// * `public_keys` - Map of algorithm ID to public key PEM
350    pub fn new(public_keys: std::collections::HashMap<String, String>) -> Self {
351        Self {
352            public_keys,
353            hardware_env: default_hardware_environment(),
354        }
355    }
356
357    /// Create a verifier from a single PEM string, auto-detecting the algorithm
358    ///
359    /// This tries to parse the key as RSA first (for backward compatibility),
360    /// then falls back to Ed25519.
361    pub fn from_pem(pem: &str) -> Result<Self> {
362        let mut keys = std::collections::HashMap::new();
363
364        // Try RSA first (most common, backward compatible)
365        if parse_public_key(pem).is_ok() {
366            keys.insert(algorithm_ids::RSA_SHA256.to_string(), pem.to_string());
367            return Ok(Self::new(keys));
368        }
369
370        // Try Ed25519 by checking if the key can be parsed
371        // We check by looking at the PEM structure and attempting verification
372        let ed25519_signer = crate::crypto::ed25519::Ed25519Signer::new();
373        let verify_result: Result<()> = ed25519_signer.verify(b"test", &[0u8; 64], pem);
374
375        // If the error message mentions "verification failed" it means the key parsed OK
376        // but the signature was invalid (as expected with a dummy signature)
377        match verify_result {
378            Ok(()) => {
379                // Unlikely but possible - key worked
380                keys.insert(algorithm_ids::ED25519.to_string(), pem.to_string());
381                return Ok(Self::new(keys));
382            }
383            Err(e) => {
384                let err_str = e.to_string();
385                if err_str.contains("verification failed")
386                    || err_str.contains("Signature verification failed")
387                {
388                    // Key parsed successfully, just signature was wrong (expected)
389                    keys.insert(algorithm_ids::ED25519.to_string(), pem.to_string());
390                    return Ok(Self::new(keys));
391                }
392                // Key parsing failed, continue to fallback
393            }
394        }
395
396        // Fallback: if the PEM contains PUBLIC KEY marker, default to RSA
397        let pem_normalized = pem.replace("\\n", "\n");
398        if pem_normalized.contains("PUBLIC KEY") {
399            keys.insert(algorithm_ids::RSA_SHA256.to_string(), pem.to_string());
400            return Ok(Self::new(keys));
401        }
402
403        Err(LicenseError::InvalidKeyFormat(
404            "Could not determine public key type".into(),
405        ))
406    }
407
408    /// Create a verifier from a PEM file
409    pub fn from_pem_file(path: &Path) -> Result<Self> {
410        let pem = std::fs::read_to_string(path)?;
411        Self::from_pem(&pem)
412    }
413
414    /// Add a public key for a specific algorithm
415    pub fn with_public_key(mut self, algorithm_id: &str, pem: &str) -> Self {
416        self.public_keys
417            .insert(algorithm_id.to_string(), pem.to_string());
418        self
419    }
420
421    /// Set custom hardware info (useful for testing or manual override)
422    pub fn with_hardware_info(mut self, info: HardwareInfo) -> Self {
423        self.hardware_env = Arc::new(FixedHardwareEnvironment(info));
424        self
425    }
426
427    /// Use a custom [`HardwareEnvironment`].
428    pub fn with_hardware_environment(mut self, env: Arc<dyn HardwareEnvironment>) -> Self {
429        self.hardware_env = env;
430        self
431    }
432
433    fn get_hardware_info(&self) -> HardwareInfo {
434        self.hardware_env.snapshot()
435    }
436
437    /// Get the public key for a specific algorithm
438    fn get_public_key(&self, algorithm_id: &str) -> Result<&str> {
439        self.public_keys
440            .get(algorithm_id)
441            .map(|s| s.as_str())
442            .ok_or_else(|| {
443                LicenseError::InvalidKeyFormat(format!(
444                    "No public key configured for algorithm: {}",
445                    algorithm_id
446                ))
447            })
448    }
449
450    /// Load a license from a file (auto-detects format)
451    pub fn load_license(&self, path: &Path) -> Result<SignedLicense> {
452        let bytes = std::fs::read(path)?;
453        self.parse_license(&bytes)
454    }
455
456    /// Parse a license from bytes (auto-detects format)
457    pub fn parse_license(&self, data: &[u8]) -> Result<SignedLicense> {
458        let format = detect_license_format(data);
459
460        match format {
461            LicenseFormat::Binary => self.parse_binary_license(data),
462            LicenseFormat::Json => self.parse_json_license(data),
463        }
464    }
465
466    /// Parse a binary format license
467    fn parse_binary_license(&self, data: &[u8]) -> Result<SignedLicense> {
468        if data.len() < 9 {
469            return Err(LicenseError::InvalidLicenseFormat(
470                "Binary license too short".into(),
471            ));
472        }
473
474        // Verify magic header
475        if &data[0..4] != BINARY_MAGIC {
476            return Err(LicenseError::InvalidLicenseFormat(
477                "Invalid magic header".into(),
478            ));
479        }
480
481        // Check version
482        let version = data[4];
483        if version > BINARY_VERSION {
484            return Err(LicenseError::InvalidLicenseFormat(format!(
485                "Unsupported license version: {}",
486                version
487            )));
488        }
489
490        // Read length
491        let len = u32::from_le_bytes([data[5], data[6], data[7], data[8]]) as usize;
492
493        if data.len() < 9 + len {
494            return Err(LicenseError::InvalidLicenseFormat(
495                "Binary license data truncated".into(),
496            ));
497        }
498
499        // Deserialize the license from JSON
500        serde_json::from_slice(&data[9..9 + len])
501            .map_err(|e| LicenseError::InvalidLicenseFormat(e.to_string()))
502    }
503
504    /// Parse a JSON format license
505    fn parse_json_license(&self, data: &[u8]) -> Result<SignedLicense> {
506        serde_json::from_slice(data).map_err(|e| LicenseError::InvalidLicenseFormat(e.to_string()))
507    }
508
509    /// Verify the cryptographic signature of a license
510    ///
511    /// Automatically selects the algorithm based on the license's algorithm field.
512    pub fn verify_signature(&self, license: &SignedLicense) -> Result<()> {
513        // Serialize the data the same way it was signed
514        let data_bytes = serde_json::to_vec(&license.data)
515            .map_err(|e| LicenseError::SerializationError(e.to_string()))?;
516
517        // Decode the signature
518        let signature_bytes = BASE64.decode(&license.signature).map_err(|e| {
519            LicenseError::InvalidLicenseFormat(format!("Invalid signature encoding: {}", e))
520        })?;
521
522        // Get the algorithm and public key
523        let algorithm = CryptoRegistry::get_signature_algorithm(&license.algorithm)?;
524        let public_key_pem = self.get_public_key(&license.algorithm)?;
525
526        // Verify using the appropriate algorithm
527        algorithm.verify(&data_bytes, &signature_bytes, public_key_pem)
528    }
529
530    /// Verify that the license has not expired
531    pub fn verify_expiration(&self, license: &SignedLicense) -> Result<()> {
532        let now = Utc::now();
533
534        if now < license.data.valid_from {
535            return Err(LicenseError::NotYetValid(
536                license
537                    .data
538                    .valid_from
539                    .format("%Y-%m-%d %H:%M:%S UTC")
540                    .to_string(),
541            ));
542        }
543
544        if now > license.data.valid_until {
545            return Err(LicenseError::LicenseExpired(
546                license
547                    .data
548                    .valid_until
549                    .format("%Y-%m-%d %H:%M:%S UTC")
550                    .to_string(),
551            ));
552        }
553
554        Ok(())
555    }
556
557    /// Verify hardware binding
558    pub fn verify_hardware(&self, license: &SignedLicense) -> Result<()> {
559        let hardware = self.get_hardware_info();
560
561        verify_hardware_binding(&license.data.hardware_binding, &hardware).map_err(|e| {
562            tracing::debug!("Hardware binding check failed: {}", e);
563            match e {
564                HardwareBindingError::MacAddressMismatch { .. } => {
565                    LicenseError::HardwareBindingMismatch {
566                        field: "mac_address".to_string(),
567                    }
568                }
569                HardwareBindingError::HostnameMismatch { .. } => {
570                    LicenseError::HardwareBindingMismatch {
571                        field: "hostname".to_string(),
572                    }
573                }
574                HardwareBindingError::DiskIdMismatch { .. } => {
575                    LicenseError::HardwareBindingMismatch {
576                        field: "disk_id".to_string(),
577                    }
578                }
579                HardwareBindingError::CustomMismatch { key, .. } => {
580                    LicenseError::HardwareBindingMismatch { field: key }
581                }
582            }
583        })
584    }
585
586    /// Perform full license validation
587    ///
588    /// This verifies:
589    /// 1. Cryptographic signature (algorithm auto-selected from license)
590    /// 2. Expiration date
591    /// 3. Hardware binding (if enabled)
592    pub fn validate(&self, license: &SignedLicense) -> Result<()> {
593        self.verify_signature(license)?;
594        self.verify_expiration(license)?;
595        self.verify_hardware(license)?;
596        Ok(())
597    }
598
599    /// Load and validate a license from a file
600    pub fn load_and_validate(&self, path: &Path) -> Result<SignedLicense> {
601        let license = self.load_license(path)?;
602        self.validate(&license)?;
603        Ok(license)
604    }
605
606    /// Perform detailed validation and return a result
607    pub fn validate_detailed(&self, license: &SignedLicense) -> ValidationResult {
608        let mut result = ValidationResult {
609            is_valid: false,
610            signature_valid: false,
611            expiration_valid: false,
612            hardware_valid: false,
613            days_remaining: license.data.days_remaining(),
614            error: None,
615        };
616
617        // Check signature
618        match self.verify_signature(license) {
619            Ok(_) => result.signature_valid = true,
620            Err(e) => {
621                result.error = Some(e.to_string());
622                return result;
623            }
624        }
625
626        // Check expiration
627        match self.verify_expiration(license) {
628            Ok(_) => result.expiration_valid = true,
629            Err(e) => {
630                result.error = Some(e.to_string());
631                return result;
632            }
633        }
634
635        // Check hardware
636        match self.verify_hardware(license) {
637            Ok(_) => result.hardware_valid = true,
638            Err(e) => {
639                result.error = Some(e.to_string());
640                return result;
641            }
642        }
643
644        result.is_valid = true;
645        result
646    }
647}
648
649#[cfg(test)]
650mod tests {
651    use super::*;
652    use crate::generator::LicenseGenerator;
653    use crate::keys::{KeyPair, KeySize};
654    use crate::license::LicenseData;
655
656    fn create_test_keypair() -> KeyPair {
657        KeyPair::generate(KeySize::Bits2048).unwrap()
658    }
659
660    #[test]
661    fn test_license_verification() {
662        let keypair = create_test_keypair();
663        let generator = LicenseGenerator::new(keypair.private_key().clone());
664        let verifier = LicenseVerifier::new(keypair.public_key);
665
666        let data = LicenseData::builder()
667            .id("TEST-001")
668            .serial("SN-12345")
669            .customer_id("CUST-001")
670            .product_id("PROD-001")
671            .valid_days(365)
672            .build()
673            .unwrap();
674
675        let signed = generator.generate(data).unwrap();
676
677        assert!(verifier.verify_signature(&signed).is_ok());
678        assert!(verifier.verify_expiration(&signed).is_ok());
679        assert!(verifier.validate(&signed).is_ok());
680    }
681
682    #[test]
683    fn test_binary_round_trip() {
684        let keypair = create_test_keypair();
685        let generator = LicenseGenerator::new(keypair.private_key().clone());
686        let verifier = LicenseVerifier::new(keypair.public_key);
687
688        let data = LicenseData::builder()
689            .id("TEST-001")
690            .serial("SN-12345")
691            .customer_id("CUST-001")
692            .product_id("PROD-001")
693            .valid_days(365)
694            .feature("basic")
695            .feature("premium")
696            .build()
697            .unwrap();
698
699        let signed = generator.generate(data).unwrap();
700        let binary = generator.export_binary(&signed).unwrap();
701
702        let parsed = verifier.parse_license(&binary).unwrap();
703
704        assert_eq!(parsed.data.id, signed.data.id);
705        assert_eq!(parsed.data.serial, signed.data.serial);
706        assert_eq!(parsed.data.features.len(), 2);
707    }
708
709    #[test]
710    fn test_json_round_trip() {
711        let keypair = create_test_keypair();
712        let generator = LicenseGenerator::new(keypair.private_key().clone());
713        let verifier = LicenseVerifier::new(keypair.public_key);
714
715        let data = LicenseData::builder()
716            .id("TEST-001")
717            .serial("SN-12345")
718            .customer_id("CUST-001")
719            .product_id("PROD-001")
720            .valid_days(365)
721            .build()
722            .unwrap();
723
724        let signed = generator.generate(data).unwrap();
725        let json = generator.export_json(&signed).unwrap();
726
727        let parsed = verifier.parse_license(json.as_bytes()).unwrap();
728
729        assert_eq!(parsed.data.id, signed.data.id);
730        assert!(verifier.validate(&parsed).is_ok());
731    }
732
733    // CryptoVerifier tests
734
735    #[test]
736    fn test_crypto_verifier_rsa() {
737        use crate::generator::CryptoGenerator;
738        use crate::keys::CryptoKeyPair;
739
740        let keypair = CryptoKeyPair::generate(algorithm_ids::RSA_SHA256).unwrap();
741        let generator = CryptoGenerator::from_keypair(&keypair);
742
743        let mut keys = std::collections::HashMap::new();
744        keys.insert(
745            algorithm_ids::RSA_SHA256.to_string(),
746            keypair.public_key_pem.clone(),
747        );
748        let verifier = CryptoVerifier::new(keys);
749
750        let data = LicenseData::builder()
751            .id("CRYPTO-RSA-001")
752            .serial("SN-CRYPTO-RSA")
753            .customer_id("CUST-001")
754            .product_id("PROD-001")
755            .valid_days(365)
756            .build()
757            .unwrap();
758
759        let signed = generator.generate(data).unwrap();
760        assert_eq!(signed.algorithm, algorithm_ids::RSA_SHA256);
761
762        assert!(verifier.verify_signature(&signed).is_ok());
763        assert!(verifier.verify_expiration(&signed).is_ok());
764        assert!(verifier.validate(&signed).is_ok());
765    }
766
767    #[test]
768    fn test_crypto_verifier_ed25519() {
769        use crate::generator::CryptoGenerator;
770        use crate::keys::CryptoKeyPair;
771
772        let keypair = CryptoKeyPair::generate(algorithm_ids::ED25519).unwrap();
773        let generator = CryptoGenerator::from_keypair(&keypair);
774
775        let mut keys = std::collections::HashMap::new();
776        keys.insert(
777            algorithm_ids::ED25519.to_string(),
778            keypair.public_key_pem.clone(),
779        );
780        let verifier = CryptoVerifier::new(keys);
781
782        let data = LicenseData::builder()
783            .id("CRYPTO-ED25519-001")
784            .serial("SN-CRYPTO-ED25519")
785            .customer_id("CUST-001")
786            .product_id("PROD-001")
787            .valid_days(365)
788            .build()
789            .unwrap();
790
791        let signed = generator.generate(data).unwrap();
792        assert_eq!(signed.algorithm, algorithm_ids::ED25519);
793
794        assert!(verifier.verify_signature(&signed).is_ok());
795        assert!(verifier.verify_expiration(&signed).is_ok());
796        assert!(verifier.validate(&signed).is_ok());
797    }
798
799    #[test]
800    fn test_crypto_verifier_multi_algorithm() {
801        use crate::generator::CryptoGenerator;
802        use crate::keys::CryptoKeyPair;
803
804        // Generate keys for both algorithms
805        let rsa_keypair = CryptoKeyPair::generate(algorithm_ids::RSA_SHA256).unwrap();
806        let ed25519_keypair = CryptoKeyPair::generate(algorithm_ids::ED25519).unwrap();
807
808        // Create generators
809        let rsa_generator = CryptoGenerator::from_keypair(&rsa_keypair);
810        let ed25519_generator = CryptoGenerator::from_keypair(&ed25519_keypair);
811
812        // Create verifier with both public keys
813        let mut keys = std::collections::HashMap::new();
814        keys.insert(
815            algorithm_ids::RSA_SHA256.to_string(),
816            rsa_keypair.public_key_pem.clone(),
817        );
818        keys.insert(
819            algorithm_ids::ED25519.to_string(),
820            ed25519_keypair.public_key_pem.clone(),
821        );
822        let verifier = CryptoVerifier::new(keys);
823
824        // Generate RSA license
825        let rsa_data = LicenseData::builder()
826            .id("MULTI-RSA-001")
827            .serial("SN-MULTI-RSA")
828            .customer_id("CUST-001")
829            .product_id("PROD-001")
830            .valid_days(365)
831            .build()
832            .unwrap();
833        let rsa_license = rsa_generator.generate(rsa_data).unwrap();
834
835        // Generate Ed25519 license
836        let ed25519_data = LicenseData::builder()
837            .id("MULTI-ED25519-001")
838            .serial("SN-MULTI-ED25519")
839            .customer_id("CUST-001")
840            .product_id("PROD-001")
841            .valid_days(365)
842            .build()
843            .unwrap();
844        let ed25519_license = ed25519_generator.generate(ed25519_data).unwrap();
845
846        // Verify both licenses with the same verifier
847        assert!(verifier.validate(&rsa_license).is_ok());
848        assert!(verifier.validate(&ed25519_license).is_ok());
849
850        // Ensure algorithm detection works correctly
851        assert_eq!(rsa_license.algorithm, algorithm_ids::RSA_SHA256);
852        assert_eq!(ed25519_license.algorithm, algorithm_ids::ED25519);
853    }
854
855    #[test]
856    fn test_crypto_verifier_binary_round_trip() {
857        use crate::generator::CryptoGenerator;
858        use crate::keys::CryptoKeyPair;
859
860        let keypair = CryptoKeyPair::generate(algorithm_ids::ED25519).unwrap();
861        let generator = CryptoGenerator::from_keypair(&keypair);
862
863        let mut keys = std::collections::HashMap::new();
864        keys.insert(
865            algorithm_ids::ED25519.to_string(),
866            keypair.public_key_pem.clone(),
867        );
868        let verifier = CryptoVerifier::new(keys);
869
870        let data = LicenseData::builder()
871            .id("BINARY-ED25519-001")
872            .serial("SN-BINARY-ED25519")
873            .customer_id("CUST-001")
874            .product_id("PROD-001")
875            .valid_days(365)
876            .feature("basic")
877            .feature("premium")
878            .build()
879            .unwrap();
880
881        let signed = generator.generate(data).unwrap();
882        let binary = generator.export_binary(&signed).unwrap();
883
884        let parsed = verifier.parse_license(&binary).unwrap();
885
886        assert_eq!(parsed.data.id, signed.data.id);
887        assert_eq!(parsed.data.serial, signed.data.serial);
888        assert_eq!(parsed.data.features.len(), 2);
889        assert_eq!(parsed.algorithm, algorithm_ids::ED25519);
890        assert!(verifier.validate(&parsed).is_ok());
891    }
892
893    #[test]
894    fn test_crypto_verifier_wrong_key() {
895        use crate::generator::CryptoGenerator;
896        use crate::keys::CryptoKeyPair;
897
898        let keypair = CryptoKeyPair::generate(algorithm_ids::ED25519).unwrap();
899        let wrong_keypair = CryptoKeyPair::generate(algorithm_ids::ED25519).unwrap();
900
901        let generator = CryptoGenerator::from_keypair(&keypair);
902
903        // Use wrong public key
904        let mut keys = std::collections::HashMap::new();
905        keys.insert(
906            algorithm_ids::ED25519.to_string(),
907            wrong_keypair.public_key_pem.clone(),
908        );
909        let verifier = CryptoVerifier::new(keys);
910
911        let data = LicenseData::builder()
912            .id("WRONG-KEY-001")
913            .serial("SN-WRONG-KEY")
914            .customer_id("CUST-001")
915            .product_id("PROD-001")
916            .valid_days(365)
917            .build()
918            .unwrap();
919
920        let signed = generator.generate(data).unwrap();
921
922        // Should fail verification with wrong key
923        assert!(verifier.verify_signature(&signed).is_err());
924        assert!(verifier.validate(&signed).is_err());
925    }
926
927    #[test]
928    fn test_crypto_verifier_missing_algorithm_key() {
929        use crate::generator::CryptoGenerator;
930        use crate::keys::CryptoKeyPair;
931
932        let keypair = CryptoKeyPair::generate(algorithm_ids::ED25519).unwrap();
933        let generator = CryptoGenerator::from_keypair(&keypair);
934
935        // Only configure RSA key, but license uses Ed25519
936        let rsa_keypair = CryptoKeyPair::generate(algorithm_ids::RSA_SHA256).unwrap();
937        let mut keys = std::collections::HashMap::new();
938        keys.insert(
939            algorithm_ids::RSA_SHA256.to_string(),
940            rsa_keypair.public_key_pem.clone(),
941        );
942        let verifier = CryptoVerifier::new(keys);
943
944        let data = LicenseData::builder()
945            .id("MISSING-ALG-001")
946            .serial("SN-MISSING-ALG")
947            .customer_id("CUST-001")
948            .product_id("PROD-001")
949            .valid_days(365)
950            .build()
951            .unwrap();
952
953        let signed = generator.generate(data).unwrap();
954
955        // Should fail because verifier doesn't have Ed25519 key
956        let result = verifier.verify_signature(&signed);
957        assert!(result.is_err());
958        assert!(result
959            .unwrap_err()
960            .to_string()
961            .contains("No public key configured"));
962    }
963
964    #[test]
965    fn test_crypto_verifier_detailed_validation() {
966        use crate::generator::CryptoGenerator;
967        use crate::keys::CryptoKeyPair;
968
969        let keypair = CryptoKeyPair::generate(algorithm_ids::ED25519).unwrap();
970        let generator = CryptoGenerator::from_keypair(&keypair);
971
972        let mut keys = std::collections::HashMap::new();
973        keys.insert(
974            algorithm_ids::ED25519.to_string(),
975            keypair.public_key_pem.clone(),
976        );
977        let verifier = CryptoVerifier::new(keys);
978
979        let data = LicenseData::builder()
980            .id("DETAILED-001")
981            .serial("SN-DETAILED")
982            .customer_id("CUST-001")
983            .product_id("PROD-001")
984            .valid_days(365)
985            .build()
986            .unwrap();
987
988        let signed = generator.generate(data).unwrap();
989
990        let result = verifier.validate_detailed(&signed);
991
992        assert!(result.is_valid);
993        assert!(result.signature_valid);
994        assert!(result.expiration_valid);
995        assert!(result.hardware_valid);
996        assert!(result.days_remaining > 0);
997        assert!(result.error.is_none());
998    }
999
1000    // Post-quantum verifier tests (feature-gated)
1001    #[cfg(feature = "post-quantum")]
1002    mod pq_tests {
1003        use super::*;
1004        use crate::crypto::algorithm_ids;
1005        use crate::generator::CryptoGenerator;
1006        use crate::keys::CryptoKeyPair;
1007
1008        #[test]
1009        fn test_crypto_verifier_ml_dsa_65() {
1010            let keypair = CryptoKeyPair::generate(algorithm_ids::ML_DSA_65).unwrap();
1011            let generator = CryptoGenerator::from_keypair(&keypair);
1012
1013            let mut keys = std::collections::HashMap::new();
1014            keys.insert(
1015                algorithm_ids::ML_DSA_65.to_string(),
1016                keypair.public_key_pem.clone(),
1017            );
1018            let verifier = CryptoVerifier::new(keys);
1019
1020            let data = LicenseData::builder()
1021                .id("PQ-VERIFY-001")
1022                .serial("SN-PQ-VERIFY")
1023                .customer_id("CUST-001")
1024                .product_id("PROD-001")
1025                .valid_days(365)
1026                .feature("quantum-safe")
1027                .build()
1028                .unwrap();
1029
1030            let signed = generator.generate(data).unwrap();
1031            assert_eq!(signed.algorithm, algorithm_ids::ML_DSA_65);
1032
1033            assert!(verifier.verify_signature(&signed).is_ok());
1034            assert!(verifier.verify_expiration(&signed).is_ok());
1035            assert!(verifier.validate(&signed).is_ok());
1036        }
1037
1038        #[test]
1039        fn test_crypto_verifier_hybrid_ed25519_ml_dsa() {
1040            let keypair = CryptoKeyPair::generate(algorithm_ids::HYBRID_ED25519_ML_DSA_65).unwrap();
1041            let generator = CryptoGenerator::from_keypair(&keypair);
1042
1043            let mut keys = std::collections::HashMap::new();
1044            keys.insert(
1045                algorithm_ids::HYBRID_ED25519_ML_DSA_65.to_string(),
1046                keypair.public_key_pem.clone(),
1047            );
1048            let verifier = CryptoVerifier::new(keys);
1049
1050            let data = LicenseData::builder()
1051                .id("PQ-HYBRID-VERIFY-001")
1052                .serial("SN-PQ-HYBRID-VERIFY")
1053                .customer_id("CUST-001")
1054                .product_id("PROD-001")
1055                .valid_days(365)
1056                .feature("hybrid-security")
1057                .build()
1058                .unwrap();
1059
1060            let signed = generator.generate(data).unwrap();
1061            assert_eq!(signed.algorithm, algorithm_ids::HYBRID_ED25519_ML_DSA_65);
1062
1063            assert!(verifier.verify_signature(&signed).is_ok());
1064            assert!(verifier.validate(&signed).is_ok());
1065        }
1066
1067        #[test]
1068        fn test_crypto_verifier_hybrid_rsa_ml_dsa() {
1069            let keypair = CryptoKeyPair::generate(algorithm_ids::HYBRID_RSA_ML_DSA_65).unwrap();
1070            let generator = CryptoGenerator::from_keypair(&keypair);
1071
1072            let mut keys = std::collections::HashMap::new();
1073            keys.insert(
1074                algorithm_ids::HYBRID_RSA_ML_DSA_65.to_string(),
1075                keypair.public_key_pem.clone(),
1076            );
1077            let verifier = CryptoVerifier::new(keys);
1078
1079            let data = LicenseData::builder()
1080                .id("PQ-HYBRID-RSA-VERIFY-001")
1081                .serial("SN-PQ-HYBRID-RSA-VERIFY")
1082                .customer_id("CUST-001")
1083                .product_id("PROD-001")
1084                .valid_days(365)
1085                .build()
1086                .unwrap();
1087
1088            let signed = generator.generate(data).unwrap();
1089            assert_eq!(signed.algorithm, algorithm_ids::HYBRID_RSA_ML_DSA_65);
1090
1091            assert!(verifier.verify_signature(&signed).is_ok());
1092            assert!(verifier.validate(&signed).is_ok());
1093        }
1094
1095        #[test]
1096        fn test_pq_verifier_binary_round_trip() {
1097            let keypair = CryptoKeyPair::generate(algorithm_ids::ML_DSA_65).unwrap();
1098            let generator = CryptoGenerator::from_keypair(&keypair);
1099
1100            let mut keys = std::collections::HashMap::new();
1101            keys.insert(
1102                algorithm_ids::ML_DSA_65.to_string(),
1103                keypair.public_key_pem.clone(),
1104            );
1105            let verifier = CryptoVerifier::new(keys);
1106
1107            let data = LicenseData::builder()
1108                .id("PQ-BINARY-VERIFY-001")
1109                .serial("SN-PQ-BINARY-VERIFY")
1110                .customer_id("CUST-001")
1111                .product_id("PROD-001")
1112                .valid_days(365)
1113                .feature("quantum-safe")
1114                .feature("binary-format")
1115                .build()
1116                .unwrap();
1117
1118            let signed = generator.generate(data).unwrap();
1119            let binary = generator.export_binary(&signed).unwrap();
1120
1121            let parsed = verifier.parse_license(&binary).unwrap();
1122
1123            assert_eq!(parsed.data.id, signed.data.id);
1124            assert_eq!(parsed.algorithm, algorithm_ids::ML_DSA_65);
1125            assert!(verifier.validate(&parsed).is_ok());
1126        }
1127
1128        #[test]
1129        fn test_pq_verifier_wrong_key() {
1130            let keypair = CryptoKeyPair::generate(algorithm_ids::ML_DSA_65).unwrap();
1131            let wrong_keypair = CryptoKeyPair::generate(algorithm_ids::ML_DSA_65).unwrap();
1132
1133            let generator = CryptoGenerator::from_keypair(&keypair);
1134
1135            // Use wrong public key
1136            let mut keys = std::collections::HashMap::new();
1137            keys.insert(
1138                algorithm_ids::ML_DSA_65.to_string(),
1139                wrong_keypair.public_key_pem.clone(),
1140            );
1141            let verifier = CryptoVerifier::new(keys);
1142
1143            let data = LicenseData::builder()
1144                .id("PQ-WRONG-KEY-001")
1145                .serial("SN-PQ-WRONG-KEY")
1146                .customer_id("CUST-001")
1147                .product_id("PROD-001")
1148                .valid_days(365)
1149                .build()
1150                .unwrap();
1151
1152            let signed = generator.generate(data).unwrap();
1153
1154            // Should fail verification with wrong key
1155            assert!(verifier.verify_signature(&signed).is_err());
1156        }
1157
1158        #[test]
1159        fn test_pq_verifier_multi_algorithm() {
1160            // Generate keys for multiple algorithms including PQ
1161            let rsa_keypair = CryptoKeyPair::generate(algorithm_ids::RSA_SHA256).unwrap();
1162            let ed25519_keypair = CryptoKeyPair::generate(algorithm_ids::ED25519).unwrap();
1163            let ml_dsa_keypair = CryptoKeyPair::generate(algorithm_ids::ML_DSA_65).unwrap();
1164            let hybrid_keypair =
1165                CryptoKeyPair::generate(algorithm_ids::HYBRID_ED25519_ML_DSA_65).unwrap();
1166
1167            // Create verifier with all public keys
1168            let mut keys = std::collections::HashMap::new();
1169            keys.insert(
1170                algorithm_ids::RSA_SHA256.to_string(),
1171                rsa_keypair.public_key_pem.clone(),
1172            );
1173            keys.insert(
1174                algorithm_ids::ED25519.to_string(),
1175                ed25519_keypair.public_key_pem.clone(),
1176            );
1177            keys.insert(
1178                algorithm_ids::ML_DSA_65.to_string(),
1179                ml_dsa_keypair.public_key_pem.clone(),
1180            );
1181            keys.insert(
1182                algorithm_ids::HYBRID_ED25519_ML_DSA_65.to_string(),
1183                hybrid_keypair.public_key_pem.clone(),
1184            );
1185            let verifier = CryptoVerifier::new(keys);
1186
1187            // Generate and verify licenses with each algorithm
1188            for (keypair, alg_name) in [
1189                (&rsa_keypair, "RSA"),
1190                (&ed25519_keypair, "Ed25519"),
1191                (&ml_dsa_keypair, "ML-DSA-65"),
1192                (&hybrid_keypair, "Hybrid"),
1193            ] {
1194                let generator = CryptoGenerator::from_keypair(keypair);
1195                let data = LicenseData::builder()
1196                    .id(format!("MULTI-{}-001", alg_name))
1197                    .serial(format!("SN-MULTI-{}", alg_name))
1198                    .customer_id("CUST-001")
1199                    .product_id("PROD-001")
1200                    .valid_days(365)
1201                    .build()
1202                    .unwrap();
1203
1204                let signed = generator.generate(data).unwrap();
1205                assert!(
1206                    verifier.validate(&signed).is_ok(),
1207                    "Failed to verify {} license",
1208                    alg_name
1209                );
1210            }
1211        }
1212
1213        #[test]
1214        fn test_pq_verifier_detailed_validation() {
1215            let keypair = CryptoKeyPair::generate(algorithm_ids::HYBRID_ED25519_ML_DSA_65).unwrap();
1216            let generator = CryptoGenerator::from_keypair(&keypair);
1217
1218            let mut keys = std::collections::HashMap::new();
1219            keys.insert(
1220                algorithm_ids::HYBRID_ED25519_ML_DSA_65.to_string(),
1221                keypair.public_key_pem.clone(),
1222            );
1223            let verifier = CryptoVerifier::new(keys);
1224
1225            let data = LicenseData::builder()
1226                .id("PQ-DETAILED-001")
1227                .serial("SN-PQ-DETAILED")
1228                .customer_id("CUST-001")
1229                .product_id("PROD-001")
1230                .valid_days(365)
1231                .build()
1232                .unwrap();
1233
1234            let signed = generator.generate(data).unwrap();
1235            let result = verifier.validate_detailed(&signed);
1236
1237            assert!(result.is_valid);
1238            assert!(result.signature_valid);
1239            assert!(result.expiration_valid);
1240            assert!(result.hardware_valid);
1241            assert!(result.days_remaining > 0);
1242            assert!(result.error.is_none());
1243        }
1244    }
1245}