1use crate::signing::{KeyPair, PublicKey, SignatureBytes, verify};
43use serde::{Deserialize, Serialize};
44use std::collections::HashMap;
45use std::time::SystemTime;
46use thiserror::Error;
47
48mod 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
69mod 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#[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
121pub type CertResult<T> = Result<T, CertError>;
123
124#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
126pub enum RevocationReason {
127 KeyCompromise,
129 CaCompromise,
131 AffiliationChanged,
133 Superseded,
135 CessationOfOperation,
137 CertificateHold,
139 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#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct CertificateMetadata {
160 pub validity_days: u64,
162 pub extensions: HashMap<String, String>,
164 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 pub fn with_validity_days(mut self, days: u64) -> Self {
181 self.validity_days = days;
182 self
183 }
184
185 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 pub fn with_key_usage(mut self, usage: Vec<KeyUsage>) -> Self {
193 self.key_usage = usage;
194 self
195 }
196}
197
198#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
200pub enum KeyUsage {
201 DigitalSignature,
203 KeyEncipherment,
205 DataEncipherment,
207 KeyAgreement,
209 CertSign,
211 CrlSign,
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize)]
217pub struct Certificate {
218 pub version: u32,
220 pub serial_number: String,
222 pub issuer: String,
224 pub subject: String,
226 #[serde(with = "serde_bytes_32")]
228 pub subject_public_key: PublicKey,
229 pub not_before: u64,
231 pub not_after: u64,
233 pub extensions: HashMap<String, String>,
235 pub key_usage: Vec<KeyUsage>,
237 #[serde(with = "serde_bytes_64")]
239 pub signature: SignatureBytes,
240}
241
242impl Certificate {
243 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 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 pub fn is_valid_at(&self, timestamp: u64) -> bool {
267 timestamp >= self.not_before && timestamp <= self.not_after
268 }
269
270 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 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 pub fn is_expired(&self) -> bool {
290 self.time_until_expiry() == 0
291 }
292}
293
294#[derive(Debug, Clone, Serialize, Deserialize)]
296pub struct RevocationEntry {
297 pub serial_number: String,
299 pub revoked_at: u64,
301 pub reason: RevocationReason,
303}
304
305#[derive(Debug, Clone, Serialize, Deserialize)]
307pub struct CertificateRevocationList {
308 pub version: u32,
310 pub issuer: String,
312 pub this_update: u64,
314 pub next_update: u64,
316 pub revoked_certificates: Vec<RevocationEntry>,
318 #[serde(with = "serde_bytes_64")]
320 pub signature: SignatureBytes,
321}
322
323pub struct CertificateAuthority {
325 keypair: KeyPair,
327 ca_name: String,
329 certificates: HashMap<String, Certificate>,
331 crl: HashMap<String, RevocationEntry>,
333 next_serial: u64,
335}
336
337impl CertificateAuthority {
338 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 pub fn name(&self) -> &str {
351 &self.ca_name
352 }
353
354 pub fn public_key(&self) -> PublicKey {
356 self.keypair.public_key()
357 }
358
359 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 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 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 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], };
396
397 let signable_data = cert.signable_data();
399 cert.signature = self.keypair.sign(&signable_data);
400
401 self.certificates.insert(serial_number, cert.clone());
403
404 Ok(cert)
405 }
406
407 pub fn verify_certificate(&self, cert: &Certificate) -> CertResult<()> {
409 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 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 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 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 pub fn revoke_certificate(
450 &mut self,
451 serial_number: &str,
452 reason: RevocationReason,
453 ) -> CertResult<()> {
454 if !self.certificates.contains_key(serial_number) {
456 return Err(CertError::NotFound(serial_number.to_string()));
457 }
458
459 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 pub fn is_revoked(&self, serial_number: &str) -> bool {
478 self.crl.contains_key(serial_number)
479 }
480
481 pub fn get_revocation_info(&self, serial_number: &str) -> Option<&RevocationEntry> {
483 self.crl.get(serial_number)
484 }
485
486 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], };
501
502 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 pub fn list_certificates(&self) -> Vec<&Certificate> {
519 self.certificates.values().collect()
520 }
521
522 pub fn get_certificate(&self, serial_number: &str) -> Option<&Certificate> {
524 self.certificates.get(serial_number)
525 }
526
527 pub fn count_active_certificates(&self) -> usize {
529 self.certificates
530 .keys()
531 .filter(|sn| !self.is_revoked(sn))
532 .count()
533 }
534
535 pub fn count_revoked_certificates(&self) -> usize {
537 self.crl.len()
538 }
539
540 pub fn renew_certificate(
542 &mut self,
543 old_cert: &Certificate,
544 metadata: CertificateMetadata,
545 ) -> CertResult<Certificate> {
546 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 let new_cert = self.issue_certificate(
555 old_cert.subject_public_key,
556 old_cert.subject.clone(),
557 metadata,
558 )?;
559
560 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 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 assert!(!ca.is_revoked(&cert.serial_number));
624 assert!(ca.verify_certificate(&cert).is_ok());
625
626 ca.revoke_certificate(&cert.serial_number, RevocationReason::KeyCompromise)
628 .unwrap();
629
630 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 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 let crl = ca.generate_crl(24);
659
660 assert_eq!(crl.issuer, "Test CA");
661 assert_eq!(crl.revoked_certificates.len(), 2); }
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 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 assert!(cert.time_until_expiry() <= 1);
682
683 let future = cert.not_after + 1;
685 assert!(!cert.is_valid_at(future));
686
687 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 let new_cert = ca
708 .renew_certificate(
709 &old_cert,
710 CertificateMetadata::default().with_validity_days(730),
711 )
712 .unwrap();
713
714 assert!(ca.is_revoked(&old_cert.serial_number));
716
717 assert!(!ca.is_revoked(&new_cert.serial_number));
719 assert!(ca.verify_certificate(&new_cert).is_ok());
720
721 assert_eq!(old_cert.subject, new_cert.subject);
723
724 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 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 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 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 let found = ca.get_certificate(&cert.serial_number).unwrap();
875 assert_eq!(found.subject, "peer-001");
876
877 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 assert!(ca.get_revocation_info(&cert.serial_number).is_none());
897
898 ca.revoke_certificate(&cert.serial_number, RevocationReason::KeyCompromise)
900 .unwrap();
901
902 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}