chie_crypto/
cert_manager.rs

1//! Certificate management and key revocation system.
2//!
3//! This module provides a comprehensive certificate and key revocation infrastructure
4//! for managing trust relationships in the CHIE protocol. It includes:
5//!
6//! - Certificate issuance and lifecycle management
7//! - Certificate Revocation Lists (CRL)
8//! - Certificate chain validation
9//! - OCSP-like status checking
10//! - Time-based certificate expiration
11//! - Certificate renewal and rotation
12//!
13//! # Example
14//!
15//! ```
16//! use chie_crypto::cert_manager::*;
17//! use chie_crypto::KeyPair;
18//!
19//! // Create a certificate authority
20//! let ca_keypair = KeyPair::generate();
21//! let mut ca = CertificateAuthority::new(ca_keypair, "CHIE Root CA".to_string());
22//!
23//! // Issue a certificate
24//! let peer_keypair = KeyPair::generate();
25//! let cert = ca.issue_certificate(
26//!     peer_keypair.public_key(),
27//!     "peer-001".to_string(),
28//!     CertificateMetadata::default()
29//!         .with_validity_days(365)
30//! ).unwrap();
31//!
32//! // Verify the certificate
33//! assert!(ca.verify_certificate(&cert).is_ok());
34//!
35//! // Revoke the certificate
36//! ca.revoke_certificate(&cert.serial_number, RevocationReason::KeyCompromise).unwrap();
37//!
38//! // Check revocation status
39//! assert!(ca.is_revoked(&cert.serial_number));
40//! ```
41
42use crate::signing::{KeyPair, PublicKey, SignatureBytes, verify};
43use serde::{Deserialize, Serialize};
44use std::collections::HashMap;
45use std::time::SystemTime;
46use thiserror::Error;
47
48// Serde helper for [u8; 32] (PublicKey)
49mod serde_bytes_32 {
50    use serde::{Deserialize, Deserializer, Serializer};
51
52    pub fn serialize<S>(bytes: &[u8; 32], serializer: S) -> Result<S::Ok, S::Error>
53    where
54        S: Serializer,
55    {
56        serializer.serialize_bytes(bytes)
57    }
58
59    pub fn deserialize<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error>
60    where
61        D: Deserializer<'de>,
62    {
63        let vec = <Vec<u8>>::deserialize(deserializer)?;
64        vec.try_into()
65            .map_err(|_| serde::de::Error::custom("Invalid length for [u8; 32]"))
66    }
67}
68
69// Serde helper for [u8; 64] (SignatureBytes)
70mod serde_bytes_64 {
71    use serde::{Deserialize, Deserializer, Serializer};
72
73    pub fn serialize<S>(bytes: &[u8; 64], serializer: S) -> Result<S::Ok, S::Error>
74    where
75        S: Serializer,
76    {
77        serializer.serialize_bytes(bytes)
78    }
79
80    pub fn deserialize<'de, D>(deserializer: D) -> Result<[u8; 64], D::Error>
81    where
82        D: Deserializer<'de>,
83    {
84        let vec = <Vec<u8>>::deserialize(deserializer)?;
85        vec.try_into()
86            .map_err(|_| serde::de::Error::custom("Invalid length for [u8; 64]"))
87    }
88}
89
90/// Errors that can occur in certificate management.
91#[derive(Debug, Error)]
92pub enum CertError {
93    #[error("Certificate verification failed: {0}")]
94    VerificationFailed(String),
95
96    #[error("Certificate expired at {0}")]
97    Expired(u64),
98
99    #[error("Certificate not yet valid (valid from {0})")]
100    NotYetValid(u64),
101
102    #[error("Certificate revoked: {0}")]
103    Revoked(String),
104
105    #[error("Certificate not found: {0}")]
106    NotFound(String),
107
108    #[error("Invalid certificate chain: {0}")]
109    InvalidChain(String),
110
111    #[error("Signing error: {0}")]
112    SigningError(String),
113
114    #[error("Serial number collision: {0}")]
115    SerialCollision(String),
116
117    #[error("Invalid metadata: {0}")]
118    InvalidMetadata(String),
119}
120
121/// Result type for certificate operations.
122pub type CertResult<T> = Result<T, CertError>;
123
124/// Reason for certificate revocation.
125#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
126pub enum RevocationReason {
127    /// Key has been compromised
128    KeyCompromise,
129    /// Certificate authority compromised
130    CaCompromise,
131    /// Affiliation changed (e.g., peer left network)
132    AffiliationChanged,
133    /// Certificate superseded by newer one
134    Superseded,
135    /// Operations ceased
136    CessationOfOperation,
137    /// Certificate on hold (temporary)
138    CertificateHold,
139    /// Reason not specified
140    Unspecified,
141}
142
143impl std::fmt::Display for RevocationReason {
144    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145        match self {
146            Self::KeyCompromise => write!(f, "keyCompromise"),
147            Self::CaCompromise => write!(f, "caCompromise"),
148            Self::AffiliationChanged => write!(f, "affiliationChanged"),
149            Self::Superseded => write!(f, "superseded"),
150            Self::CessationOfOperation => write!(f, "cessationOfOperation"),
151            Self::CertificateHold => write!(f, "certificateHold"),
152            Self::Unspecified => write!(f, "unspecified"),
153        }
154    }
155}
156
157/// Certificate metadata and attributes.
158#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct CertificateMetadata {
160    /// Validity period in days (default: 365)
161    pub validity_days: u64,
162    /// Custom extensions/attributes
163    pub extensions: HashMap<String, String>,
164    /// Certificate usage flags
165    pub key_usage: Vec<KeyUsage>,
166}
167
168impl Default for CertificateMetadata {
169    fn default() -> Self {
170        Self {
171            validity_days: 365,
172            extensions: HashMap::new(),
173            key_usage: vec![KeyUsage::DigitalSignature],
174        }
175    }
176}
177
178impl CertificateMetadata {
179    /// Set validity period in days
180    pub fn with_validity_days(mut self, days: u64) -> Self {
181        self.validity_days = days;
182        self
183    }
184
185    /// Add an extension
186    pub fn with_extension(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
187        self.extensions.insert(key.into(), value.into());
188        self
189    }
190
191    /// Set key usage
192    pub fn with_key_usage(mut self, usage: Vec<KeyUsage>) -> Self {
193        self.key_usage = usage;
194        self
195    }
196}
197
198/// Key usage flags for certificates.
199#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
200pub enum KeyUsage {
201    /// Digital signature
202    DigitalSignature,
203    /// Key encipherment
204    KeyEncipherment,
205    /// Data encipherment
206    DataEncipherment,
207    /// Key agreement
208    KeyAgreement,
209    /// Certificate signing
210    CertSign,
211    /// CRL signing
212    CrlSign,
213}
214
215/// Digital certificate for peer identity.
216#[derive(Debug, Clone, Serialize, Deserialize)]
217pub struct Certificate {
218    /// Version number
219    pub version: u32,
220    /// Unique serial number
221    pub serial_number: String,
222    /// Issuer identifier (CA name)
223    pub issuer: String,
224    /// Subject identifier (peer/entity name)
225    pub subject: String,
226    /// Subject's public key
227    #[serde(with = "serde_bytes_32")]
228    pub subject_public_key: PublicKey,
229    /// Validity period start (Unix timestamp)
230    pub not_before: u64,
231    /// Validity period end (Unix timestamp)
232    pub not_after: u64,
233    /// Certificate extensions
234    pub extensions: HashMap<String, String>,
235    /// Key usage flags
236    pub key_usage: Vec<KeyUsage>,
237    /// Issuer's signature over certificate data
238    #[serde(with = "serde_bytes_64")]
239    pub signature: SignatureBytes,
240}
241
242impl Certificate {
243    /// Get the certificate data to be signed
244    fn signable_data(&self) -> Vec<u8> {
245        let mut data = Vec::new();
246        data.extend_from_slice(&self.version.to_le_bytes());
247        data.extend_from_slice(self.serial_number.as_bytes());
248        data.extend_from_slice(self.issuer.as_bytes());
249        data.extend_from_slice(self.subject.as_bytes());
250        data.extend_from_slice(&self.subject_public_key);
251        data.extend_from_slice(&self.not_before.to_le_bytes());
252        data.extend_from_slice(&self.not_after.to_le_bytes());
253
254        // Include extensions in deterministic order
255        let mut ext_keys: Vec<_> = self.extensions.keys().collect();
256        ext_keys.sort();
257        for key in ext_keys {
258            data.extend_from_slice(key.as_bytes());
259            data.extend_from_slice(self.extensions[key].as_bytes());
260        }
261
262        data
263    }
264
265    /// Check if certificate is currently valid (time-wise)
266    pub fn is_valid_at(&self, timestamp: u64) -> bool {
267        timestamp >= self.not_before && timestamp <= self.not_after
268    }
269
270    /// Check if certificate is currently valid
271    pub fn is_currently_valid(&self) -> bool {
272        let now = SystemTime::now()
273            .duration_since(SystemTime::UNIX_EPOCH)
274            .unwrap_or_default()
275            .as_secs();
276        self.is_valid_at(now)
277    }
278
279    /// Get time until expiration in seconds (0 if expired)
280    pub fn time_until_expiry(&self) -> u64 {
281        let now = SystemTime::now()
282            .duration_since(SystemTime::UNIX_EPOCH)
283            .unwrap_or_default()
284            .as_secs();
285        self.not_after.saturating_sub(now)
286    }
287
288    /// Check if certificate has expired
289    pub fn is_expired(&self) -> bool {
290        self.time_until_expiry() == 0
291    }
292}
293
294/// Certificate Revocation List entry.
295#[derive(Debug, Clone, Serialize, Deserialize)]
296pub struct RevocationEntry {
297    /// Serial number of revoked certificate
298    pub serial_number: String,
299    /// Revocation timestamp
300    pub revoked_at: u64,
301    /// Reason for revocation
302    pub reason: RevocationReason,
303}
304
305/// Certificate Revocation List (CRL).
306#[derive(Debug, Clone, Serialize, Deserialize)]
307pub struct CertificateRevocationList {
308    /// CRL version
309    pub version: u32,
310    /// Issuer (CA) name
311    pub issuer: String,
312    /// CRL issue timestamp
313    pub this_update: u64,
314    /// Next CRL update timestamp
315    pub next_update: u64,
316    /// Revoked certificates
317    pub revoked_certificates: Vec<RevocationEntry>,
318    /// CRL signature
319    #[serde(with = "serde_bytes_64")]
320    pub signature: SignatureBytes,
321}
322
323/// Certificate Authority for issuing and managing certificates.
324pub struct CertificateAuthority {
325    /// CA's signing keypair
326    keypair: KeyPair,
327    /// CA identifier
328    ca_name: String,
329    /// Issued certificates (keyed by serial number)
330    certificates: HashMap<String, Certificate>,
331    /// Certificate Revocation List
332    crl: HashMap<String, RevocationEntry>,
333    /// Next serial number to issue
334    next_serial: u64,
335}
336
337impl CertificateAuthority {
338    /// Create a new Certificate Authority
339    pub fn new(keypair: KeyPair, ca_name: String) -> Self {
340        Self {
341            keypair,
342            ca_name,
343            certificates: HashMap::new(),
344            crl: HashMap::new(),
345            next_serial: 1,
346        }
347    }
348
349    /// Get CA name
350    pub fn name(&self) -> &str {
351        &self.ca_name
352    }
353
354    /// Get CA public key
355    pub fn public_key(&self) -> PublicKey {
356        self.keypair.public_key()
357    }
358
359    /// Generate next serial number
360    fn next_serial_number(&mut self) -> String {
361        let serial = format!("{:016x}", self.next_serial);
362        self.next_serial += 1;
363        serial
364    }
365
366    /// Issue a new certificate
367    pub fn issue_certificate(
368        &mut self,
369        subject_public_key: PublicKey,
370        subject: String,
371        metadata: CertificateMetadata,
372    ) -> CertResult<Certificate> {
373        let serial_number = self.next_serial_number();
374
375        // Calculate validity period
376        let now = SystemTime::now()
377            .duration_since(SystemTime::UNIX_EPOCH)
378            .unwrap_or_default()
379            .as_secs();
380        let not_before = now;
381        let not_after = now + (metadata.validity_days * 24 * 3600);
382
383        // Create certificate (without signature first)
384        let mut cert = Certificate {
385            version: 1,
386            serial_number: serial_number.clone(),
387            issuer: self.ca_name.clone(),
388            subject,
389            subject_public_key,
390            not_before,
391            not_after,
392            extensions: metadata.extensions,
393            key_usage: metadata.key_usage,
394            signature: [0u8; 64], // Placeholder
395        };
396
397        // Sign the certificate
398        let signable_data = cert.signable_data();
399        cert.signature = self.keypair.sign(&signable_data);
400
401        // Store certificate
402        self.certificates.insert(serial_number, cert.clone());
403
404        Ok(cert)
405    }
406
407    /// Verify a certificate
408    pub fn verify_certificate(&self, cert: &Certificate) -> CertResult<()> {
409        // Check if issued by this CA
410        if cert.issuer != self.ca_name {
411            return Err(CertError::VerificationFailed(format!(
412                "Certificate not issued by this CA (expected {}, got {})",
413                self.ca_name, cert.issuer
414            )));
415        }
416
417        // Verify signature
418        let signable_data = cert.signable_data();
419        verify(&self.keypair.public_key(), &signable_data, &cert.signature)
420            .map_err(|e| CertError::VerificationFailed(e.to_string()))?;
421
422        // Check validity period
423        let now = SystemTime::now()
424            .duration_since(SystemTime::UNIX_EPOCH)
425            .unwrap_or_default()
426            .as_secs();
427
428        if now < cert.not_before {
429            return Err(CertError::NotYetValid(cert.not_before));
430        }
431
432        if now > cert.not_after {
433            return Err(CertError::Expired(cert.not_after));
434        }
435
436        // Check revocation status
437        if self.is_revoked(&cert.serial_number) {
438            let entry = self.crl.get(&cert.serial_number).unwrap();
439            return Err(CertError::Revoked(format!(
440                "Certificate revoked: {}",
441                entry.reason
442            )));
443        }
444
445        Ok(())
446    }
447
448    /// Revoke a certificate
449    pub fn revoke_certificate(
450        &mut self,
451        serial_number: &str,
452        reason: RevocationReason,
453    ) -> CertResult<()> {
454        // Check if certificate exists
455        if !self.certificates.contains_key(serial_number) {
456            return Err(CertError::NotFound(serial_number.to_string()));
457        }
458
459        // Add to CRL
460        let revoked_at = SystemTime::now()
461            .duration_since(SystemTime::UNIX_EPOCH)
462            .unwrap_or_default()
463            .as_secs();
464
465        let entry = RevocationEntry {
466            serial_number: serial_number.to_string(),
467            revoked_at,
468            reason,
469        };
470
471        self.crl.insert(serial_number.to_string(), entry);
472
473        Ok(())
474    }
475
476    /// Check if a certificate is revoked
477    pub fn is_revoked(&self, serial_number: &str) -> bool {
478        self.crl.contains_key(serial_number)
479    }
480
481    /// Get revocation information
482    pub fn get_revocation_info(&self, serial_number: &str) -> Option<&RevocationEntry> {
483        self.crl.get(serial_number)
484    }
485
486    /// Generate a Certificate Revocation List
487    pub fn generate_crl(&self, validity_hours: u64) -> CertificateRevocationList {
488        let now = SystemTime::now()
489            .duration_since(SystemTime::UNIX_EPOCH)
490            .unwrap_or_default()
491            .as_secs();
492
493        let mut crl = CertificateRevocationList {
494            version: 1,
495            issuer: self.ca_name.clone(),
496            this_update: now,
497            next_update: now + (validity_hours * 3600),
498            revoked_certificates: self.crl.values().cloned().collect(),
499            signature: [0u8; 64], // Placeholder
500        };
501
502        // Sign the CRL
503        let mut data = Vec::new();
504        data.extend_from_slice(&crl.version.to_le_bytes());
505        data.extend_from_slice(crl.issuer.as_bytes());
506        data.extend_from_slice(&crl.this_update.to_le_bytes());
507        data.extend_from_slice(&crl.next_update.to_le_bytes());
508        for entry in &crl.revoked_certificates {
509            data.extend_from_slice(entry.serial_number.as_bytes());
510            data.extend_from_slice(&entry.revoked_at.to_le_bytes());
511        }
512
513        crl.signature = self.keypair.sign(&data);
514        crl
515    }
516
517    /// List all issued certificates
518    pub fn list_certificates(&self) -> Vec<&Certificate> {
519        self.certificates.values().collect()
520    }
521
522    /// Get certificate by serial number
523    pub fn get_certificate(&self, serial_number: &str) -> Option<&Certificate> {
524        self.certificates.get(serial_number)
525    }
526
527    /// Count active (non-revoked) certificates
528    pub fn count_active_certificates(&self) -> usize {
529        self.certificates
530            .keys()
531            .filter(|sn| !self.is_revoked(sn))
532            .count()
533    }
534
535    /// Count revoked certificates
536    pub fn count_revoked_certificates(&self) -> usize {
537        self.crl.len()
538    }
539
540    /// Renew a certificate (issue new one with same subject)
541    pub fn renew_certificate(
542        &mut self,
543        old_cert: &Certificate,
544        metadata: CertificateMetadata,
545    ) -> CertResult<Certificate> {
546        // Verify old certificate was issued by us
547        if old_cert.issuer != self.ca_name {
548            return Err(CertError::VerificationFailed(
549                "Cannot renew certificate from different CA".to_string(),
550            ));
551        }
552
553        // Issue new certificate
554        let new_cert = self.issue_certificate(
555            old_cert.subject_public_key,
556            old_cert.subject.clone(),
557            metadata,
558        )?;
559
560        // Revoke old certificate
561        self.revoke_certificate(&old_cert.serial_number, RevocationReason::Superseded)?;
562
563        Ok(new_cert)
564    }
565}
566
567#[cfg(test)]
568mod tests {
569    use super::*;
570
571    #[test]
572    fn test_certificate_issuance() {
573        let ca_keypair = KeyPair::generate();
574        let mut ca = CertificateAuthority::new(ca_keypair, "Test CA".to_string());
575
576        let peer_keypair = KeyPair::generate();
577        let cert = ca
578            .issue_certificate(
579                peer_keypair.public_key(),
580                "peer-001".to_string(),
581                CertificateMetadata::default(),
582            )
583            .unwrap();
584
585        assert_eq!(cert.subject, "peer-001");
586        assert_eq!(cert.issuer, "Test CA");
587        assert!(cert.is_currently_valid());
588    }
589
590    #[test]
591    fn test_certificate_verification() {
592        let ca_keypair = KeyPair::generate();
593        let mut ca = CertificateAuthority::new(ca_keypair, "Test CA".to_string());
594
595        let peer_keypair = KeyPair::generate();
596        let cert = ca
597            .issue_certificate(
598                peer_keypair.public_key(),
599                "peer-001".to_string(),
600                CertificateMetadata::default(),
601            )
602            .unwrap();
603
604        // Should verify successfully
605        assert!(ca.verify_certificate(&cert).is_ok());
606    }
607
608    #[test]
609    fn test_certificate_revocation() {
610        let ca_keypair = KeyPair::generate();
611        let mut ca = CertificateAuthority::new(ca_keypair, "Test CA".to_string());
612
613        let peer_keypair = KeyPair::generate();
614        let cert = ca
615            .issue_certificate(
616                peer_keypair.public_key(),
617                "peer-001".to_string(),
618                CertificateMetadata::default(),
619            )
620            .unwrap();
621
622        // Initially not revoked
623        assert!(!ca.is_revoked(&cert.serial_number));
624        assert!(ca.verify_certificate(&cert).is_ok());
625
626        // Revoke certificate
627        ca.revoke_certificate(&cert.serial_number, RevocationReason::KeyCompromise)
628            .unwrap();
629
630        // Now revoked
631        assert!(ca.is_revoked(&cert.serial_number));
632        assert!(ca.verify_certificate(&cert).is_err());
633    }
634
635    #[test]
636    fn test_crl_generation() {
637        let ca_keypair = KeyPair::generate();
638        let mut ca = CertificateAuthority::new(ca_keypair, "Test CA".to_string());
639
640        // Issue and revoke some certificates
641        for i in 0..3 {
642            let peer_keypair = KeyPair::generate();
643            let cert = ca
644                .issue_certificate(
645                    peer_keypair.public_key(),
646                    format!("peer-{:03}", i),
647                    CertificateMetadata::default(),
648                )
649                .unwrap();
650
651            if i % 2 == 0 {
652                ca.revoke_certificate(&cert.serial_number, RevocationReason::Unspecified)
653                    .unwrap();
654            }
655        }
656
657        // Generate CRL
658        let crl = ca.generate_crl(24);
659
660        assert_eq!(crl.issuer, "Test CA");
661        assert_eq!(crl.revoked_certificates.len(), 2); // Revoked 2 out of 3
662    }
663
664    #[test]
665    fn test_certificate_expiration() {
666        let ca_keypair = KeyPair::generate();
667        let mut ca = CertificateAuthority::new(ca_keypair, "Test CA".to_string());
668
669        let peer_keypair = KeyPair::generate();
670
671        // Issue certificate with 0 day validity
672        let cert = ca
673            .issue_certificate(
674                peer_keypair.public_key(),
675                "peer-001".to_string(),
676                CertificateMetadata::default().with_validity_days(0),
677            )
678            .unwrap();
679
680        // Certificate should have 0 or very small time until expiry
681        assert!(cert.time_until_expiry() <= 1);
682
683        // Check validity at future timestamp (cert should be expired)
684        let future = cert.not_after + 1;
685        assert!(!cert.is_valid_at(future));
686
687        // Check validity at past timestamp (cert should be valid)
688        let past = cert.not_before;
689        assert!(cert.is_valid_at(past));
690    }
691
692    #[test]
693    fn test_certificate_renewal() {
694        let ca_keypair = KeyPair::generate();
695        let mut ca = CertificateAuthority::new(ca_keypair, "Test CA".to_string());
696
697        let peer_keypair = KeyPair::generate();
698        let old_cert = ca
699            .issue_certificate(
700                peer_keypair.public_key(),
701                "peer-001".to_string(),
702                CertificateMetadata::default(),
703            )
704            .unwrap();
705
706        // Renew certificate
707        let new_cert = ca
708            .renew_certificate(
709                &old_cert,
710                CertificateMetadata::default().with_validity_days(730),
711            )
712            .unwrap();
713
714        // Old certificate should be revoked
715        assert!(ca.is_revoked(&old_cert.serial_number));
716
717        // New certificate should be valid
718        assert!(!ca.is_revoked(&new_cert.serial_number));
719        assert!(ca.verify_certificate(&new_cert).is_ok());
720
721        // Subjects should match
722        assert_eq!(old_cert.subject, new_cert.subject);
723
724        // Serial numbers should differ
725        assert_ne!(old_cert.serial_number, new_cert.serial_number);
726    }
727
728    #[test]
729    fn test_metadata_extensions() {
730        let ca_keypair = KeyPair::generate();
731        let mut ca = CertificateAuthority::new(ca_keypair, "Test CA".to_string());
732
733        let peer_keypair = KeyPair::generate();
734        let cert = ca
735            .issue_certificate(
736                peer_keypair.public_key(),
737                "peer-001".to_string(),
738                CertificateMetadata::default()
739                    .with_extension("node_type", "storage")
740                    .with_extension("region", "us-west"),
741            )
742            .unwrap();
743
744        assert_eq!(
745            cert.extensions.get("node_type"),
746            Some(&"storage".to_string())
747        );
748        assert_eq!(cert.extensions.get("region"), Some(&"us-west".to_string()));
749    }
750
751    #[test]
752    fn test_key_usage() {
753        let ca_keypair = KeyPair::generate();
754        let mut ca = CertificateAuthority::new(ca_keypair, "Test CA".to_string());
755
756        let peer_keypair = KeyPair::generate();
757        let cert = ca
758            .issue_certificate(
759                peer_keypair.public_key(),
760                "peer-001".to_string(),
761                CertificateMetadata::default()
762                    .with_key_usage(vec![KeyUsage::DigitalSignature, KeyUsage::KeyEncipherment]),
763            )
764            .unwrap();
765
766        assert_eq!(cert.key_usage.len(), 2);
767        assert!(cert.key_usage.contains(&KeyUsage::DigitalSignature));
768        assert!(cert.key_usage.contains(&KeyUsage::KeyEncipherment));
769    }
770
771    #[test]
772    fn test_revocation_reasons() {
773        let ca_keypair = KeyPair::generate();
774        let mut ca = CertificateAuthority::new(ca_keypair, "Test CA".to_string());
775
776        let reasons = [
777            RevocationReason::KeyCompromise,
778            RevocationReason::CaCompromise,
779            RevocationReason::AffiliationChanged,
780            RevocationReason::Superseded,
781            RevocationReason::CessationOfOperation,
782        ];
783
784        for (i, reason) in reasons.iter().enumerate() {
785            let peer_keypair = KeyPair::generate();
786            let cert = ca
787                .issue_certificate(
788                    peer_keypair.public_key(),
789                    format!("peer-{:03}", i),
790                    CertificateMetadata::default(),
791                )
792                .unwrap();
793
794            ca.revoke_certificate(&cert.serial_number, *reason).unwrap();
795
796            let info = ca.get_revocation_info(&cert.serial_number).unwrap();
797            assert_eq!(info.reason, *reason);
798        }
799    }
800
801    #[test]
802    fn test_certificate_counting() {
803        let ca_keypair = KeyPair::generate();
804        let mut ca = CertificateAuthority::new(ca_keypair, "Test CA".to_string());
805
806        // Issue 5 certificates
807        for i in 0..5 {
808            let peer_keypair = KeyPair::generate();
809            ca.issue_certificate(
810                peer_keypair.public_key(),
811                format!("peer-{:03}", i),
812                CertificateMetadata::default(),
813            )
814            .unwrap();
815        }
816
817        assert_eq!(ca.list_certificates().len(), 5);
818        assert_eq!(ca.count_active_certificates(), 5);
819        assert_eq!(ca.count_revoked_certificates(), 0);
820
821        // Revoke 2 certificates
822        let certs: Vec<_> = ca
823            .list_certificates()
824            .iter()
825            .take(2)
826            .map(|c| c.serial_number.clone())
827            .collect();
828        for serial in &certs {
829            ca.revoke_certificate(serial, RevocationReason::Unspecified)
830                .unwrap();
831        }
832
833        assert_eq!(ca.count_active_certificates(), 3);
834        assert_eq!(ca.count_revoked_certificates(), 2);
835    }
836
837    #[test]
838    fn test_serial_number_uniqueness() {
839        let ca_keypair = KeyPair::generate();
840        let mut ca = CertificateAuthority::new(ca_keypair, "Test CA".to_string());
841
842        let mut serials = std::collections::HashSet::new();
843
844        // Issue 100 certificates and check for unique serials
845        for i in 0..100 {
846            let peer_keypair = KeyPair::generate();
847            let cert = ca
848                .issue_certificate(
849                    peer_keypair.public_key(),
850                    format!("peer-{:03}", i),
851                    CertificateMetadata::default(),
852                )
853                .unwrap();
854
855            assert!(serials.insert(cert.serial_number.clone()));
856        }
857    }
858
859    #[test]
860    fn test_certificate_lookup() {
861        let ca_keypair = KeyPair::generate();
862        let mut ca = CertificateAuthority::new(ca_keypair, "Test CA".to_string());
863
864        let peer_keypair = KeyPair::generate();
865        let cert = ca
866            .issue_certificate(
867                peer_keypair.public_key(),
868                "peer-001".to_string(),
869                CertificateMetadata::default(),
870            )
871            .unwrap();
872
873        // Lookup by serial number
874        let found = ca.get_certificate(&cert.serial_number).unwrap();
875        assert_eq!(found.subject, "peer-001");
876
877        // Lookup non-existent
878        assert!(ca.get_certificate("nonexistent").is_none());
879    }
880
881    #[test]
882    fn test_revocation_info() {
883        let ca_keypair = KeyPair::generate();
884        let mut ca = CertificateAuthority::new(ca_keypair, "Test CA".to_string());
885
886        let peer_keypair = KeyPair::generate();
887        let cert = ca
888            .issue_certificate(
889                peer_keypair.public_key(),
890                "peer-001".to_string(),
891                CertificateMetadata::default(),
892            )
893            .unwrap();
894
895        // No revocation info initially
896        assert!(ca.get_revocation_info(&cert.serial_number).is_none());
897
898        // Revoke
899        ca.revoke_certificate(&cert.serial_number, RevocationReason::KeyCompromise)
900            .unwrap();
901
902        // Should have revocation info now
903        let info = ca.get_revocation_info(&cert.serial_number).unwrap();
904        assert_eq!(info.reason, RevocationReason::KeyCompromise);
905        assert!(info.revoked_at > 0);
906    }
907}