quantcrypt/asn1/
certificate.rs

1use crate::{
2    dsa::{common::dsa_trait::Dsa, dsa_manager::DsaManager},
3    kem::{common::kem_trait::Kem, kem_manager::KemManager},
4    keys::PublicKey,
5};
6use chrono::{DateTime, Utc};
7use cms::enveloped_data::RecipientIdentifier;
8use der::{Decode, DecodePem, Encode, EncodePem};
9use spki::ObjectIdentifier;
10use x509_cert::{
11    ext::pkix::{AuthorityKeyIdentifier, KeyUsage, SubjectKeyIdentifier},
12    name::RdnSequence,
13    serial_number::SerialNumber,
14};
15
16use crate::errors::QuantCryptError;
17
18type Result<T> = std::result::Result<T, QuantCryptError>;
19
20/// A certificate
21///
22/// # Example
23/// ```
24/// use quantcrypt::certificates::Certificate;
25/// use quantcrypt::is_ipd_mode_enabled;
26/// let cert_path = if is_ipd_mode_enabled() {
27///    "test/data_ipd/MlDsa44EcdsaP256SHA256-2.16.840.1.114027.80.8.1.4_ta.pem"
28/// } else {
29///   "test/data/MlDsa44EcdsaP256SHA256-2.16.840.1.114027.80.8.1.4_ta.pem"
30/// };
31/// let cert = Certificate::from_file(cert_path).unwrap();
32/// assert!(cert.verify_self_signed().unwrap());
33/// ```
34#[derive(Clone)]
35pub struct Certificate {
36    cert: x509_cert::Certificate,
37}
38
39impl Certificate {
40    /// Create a new certificate
41    ///
42    /// # Arguments
43    ///
44    /// * `cert` - The certificate
45    ///
46    /// # Returns
47    ///
48    /// The new certificate
49    pub(crate) fn new(cert: x509_cert::Certificate) -> Certificate {
50        Certificate { cert }
51    }
52
53    /// Convert the certificate to DER format bytes
54    ///
55    /// # Returns
56    ///
57    /// The DER format bytes
58    pub fn to_der(&self) -> Result<Vec<u8>> {
59        let result = self
60            .cert
61            .to_der()
62            .map_err(|_| QuantCryptError::InvalidCertificate)?;
63        Ok(result)
64    }
65
66    /// Convert the certificate to PEM format
67    ///
68    /// # Returns
69    ///
70    /// The PEM format certificate as a string
71    pub fn to_pem(&self) -> Result<String> {
72        let result = self
73            .cert
74            .to_pem(pkcs8::LineEnding::LF)
75            .map_err(|_| QuantCryptError::InvalidCertificate)?;
76        Ok(result)
77    }
78
79    /// Create a certificate from DER format bytes
80    ///
81    /// # Arguments
82    ///
83    /// * `der` - The DER format bytes
84    ///
85    /// # Returns
86    ///
87    /// The new certificate
88    ///
89    /// # Errors
90    ///
91    /// `CertificateError::InvalidCertificate` will be returned if the certificate is invalid
92    pub fn from_der(der: &[u8]) -> Result<Certificate> {
93        let mut cert = x509_cert::Certificate::from_der(der)
94            .map_err(|_| QuantCryptError::InvalidCertificate)?;
95        // Map old OIDs to new OIDs
96        let original_oid = cert
97            .tbs_certificate
98            .subject_public_key_info
99            .algorithm
100            .oid
101            .to_string();
102        let new_oid: ObjectIdentifier = original_oid
103            .parse()
104            .map_err(|_| QuantCryptError::InvalidCertificate)?;
105        cert.tbs_certificate.subject_public_key_info.algorithm.oid = new_oid;
106        Ok(Certificate::new(cert))
107    }
108
109    /// Create a certificate from a PEM format string
110    ///
111    /// # Arguments
112    ///
113    /// * `pem` - The PEM format string
114    ///
115    /// # Returns
116    ///
117    /// The new certificate
118    ///
119    /// # Errors
120    ///
121    /// `CertificateError::InvalidCertificate` will be returned if the certificate is invalid
122    pub fn from_pem(pem: &str) -> Result<Certificate> {
123        let cert = x509_cert::Certificate::from_pem(pem)
124            .map_err(|_| QuantCryptError::InvalidCertificate)?;
125        Ok(Certificate::new(cert))
126    }
127
128    /// Get the subject name
129    ///
130    /// # Returns
131    ///
132    /// The subject name
133    pub fn get_subject(&self) -> RdnSequence {
134        self.cert.tbs_certificate.subject.clone()
135    }
136
137    /// Get the issuer name
138    ///
139    /// # Returns
140    ///
141    /// The issuer name
142    pub fn get_issuer(&self) -> RdnSequence {
143        self.cert.tbs_certificate.issuer.clone()
144    }
145
146    /// Get the serial number
147    ///
148    /// # Returns
149    ///
150    /// The serial number
151    pub fn get_serial_number(&self) -> SerialNumber {
152        self.cert.tbs_certificate.serial_number.clone()
153    }
154
155    /// Get the subject key identifier
156    ///
157    /// # Returns
158    ///
159    /// The subject key identifier
160    pub fn get_subject_key_identifier(&self) -> Result<SubjectKeyIdentifier> {
161        if let Some(exts) = self.cert.tbs_certificate.extensions.clone() {
162            for ext in exts {
163                if ext.extn_id == const_oid::db::rfc5280::ID_CE_SUBJECT_KEY_IDENTIFIER {
164                    let ski_raw_bytes = ext.extn_value.as_bytes();
165                    let ski = SubjectKeyIdentifier::from_der(ski_raw_bytes)
166                        .map_err(|_| QuantCryptError::InvalidCertificate)?;
167                    return Ok(ski);
168                }
169            }
170        }
171        Err(QuantCryptError::SkidNotFound)
172    }
173
174    /// Verify that the certificate is self-signed
175    ///
176    /// # Returns
177    ///
178    /// True if the certificate is self-signed, false otherwise
179    pub fn verify_self_signed(&self) -> Result<bool> {
180        // The certificate must contain basic constraints with cA set to true
181        if let Some(exts) = self.cert.tbs_certificate.extensions.clone() {
182            for ext in exts {
183                if ext.extn_id == const_oid::db::rfc5280::ID_CE_BASIC_CONSTRAINTS {
184                    if let Ok(bc) = ext.to_der() {
185                        if let Ok(bc) = x509_cert::ext::pkix::BasicConstraints::from_der(&bc) {
186                            if bc.ca {
187                                break;
188                            }
189                        }
190                    }
191                }
192            }
193        } else {
194            return Ok(false);
195        }
196
197        // The subject and issuer must be the same
198        if self.get_subject() != self.get_issuer() {
199            return Ok(false);
200        }
201
202        let msg = self
203            .cert
204            .tbs_certificate
205            .to_der()
206            .map_err(|_| QuantCryptError::InvalidCertificate)?;
207
208        let sig = self.cert.signature.raw_bytes();
209
210        let pk = self.get_public_key()?;
211
212        let result = pk.verify(&msg, sig).unwrap_or(false);
213
214        Ok(result)
215    }
216
217    /// Get the public key
218    ///
219    /// # Returns
220    ///
221    /// The public key
222    pub fn get_public_key(&self) -> Result<PublicKey> {
223        let pk_der = self
224            .cert
225            .tbs_certificate
226            .subject_public_key_info
227            .to_der()
228            .map_err(|_| QuantCryptError::InvalidCertificate)?;
229
230        let pk = PublicKey::from_der(&pk_der).map_err(|_| QuantCryptError::InvalidCertificate)?;
231
232        Ok(pk)
233    }
234
235    /// Verify that the specified certificate is a child of this certificate.
236    ///
237    /// This checks that the specified child certificate has the same issuer as this certificate's subject,
238    /// that the child's Subject Key Identifier matches the Authority Key Identifier of this certificate,
239    /// and that the child's signature is valid.
240    ///
241    /// # Arguments
242    ///
243    /// * `child` - The child certificate
244    ///
245    /// # Returns
246    ///
247    /// True if the child certificate is a child of this certificate, false otherwise
248    pub fn verify_child(&self, child: &Certificate) -> Result<bool> {
249        // If the child has a different issuer than the parent's subject, it cannot be a child
250        if self.get_subject() != child.get_issuer() {
251            return Ok(false);
252        }
253
254        // If AKID is present in child, it should match the SKID of the parent
255        if let Ok(parent_skid) = self.get_subject_key_identifier() {
256            if let Some(exts) = child.cert.tbs_certificate.extensions.clone() {
257                for ext in exts {
258                    if ext.extn_id == const_oid::db::rfc5280::ID_CE_AUTHORITY_KEY_IDENTIFIER {
259                        let akid = AuthorityKeyIdentifier::from_der(ext.extn_value.as_bytes())
260                            .map_err(|_| QuantCryptError::InvalidCertificate)?;
261
262                        let akid = if let Some(akid) = akid.key_identifier {
263                            akid
264                        } else {
265                            return Ok(false);
266                        };
267
268                        if akid != parent_skid.0 {
269                            return Ok(false);
270                        }
271                    }
272                }
273            }
274        }
275
276        // Verify the signature of the child
277        let msg = child
278            .cert
279            .tbs_certificate
280            .to_der()
281            .map_err(|_| QuantCryptError::InvalidCertificate)?;
282        let sig = child.cert.signature.raw_bytes();
283        let pk = self.get_public_key()?;
284
285        let result = pk
286            .verify(&msg, sig)
287            .map_err(|_| QuantCryptError::InvalidCertificate)?;
288
289        Ok(result)
290    }
291
292    /// Load a certificate from the specified file. The file can be in either DER or PEM format.
293    ///
294    /// # Arguments
295    ///
296    /// * `path` - The path to the file
297    ///
298    /// # Returns
299    ///
300    /// The certificate
301    pub fn from_file(path: &str) -> Result<Certificate> {
302        // Read the contents of the file as bytes
303        let contents = std::fs::read(path).map_err(|_| QuantCryptError::FileReadError)?;
304
305        // Try to interpret as DER
306        let result = Certificate::from_der(&contents);
307
308        if let Ok(cert) = result {
309            Ok(cert)
310        } else {
311            // Try to interpret as PEM
312            let pem =
313                std::str::from_utf8(&contents).map_err(|_| QuantCryptError::InvalidCertificate)?;
314            if let Ok(cert) = Certificate::from_pem(pem) {
315                Ok(cert)
316            } else {
317                Err(QuantCryptError::InvalidCertificate)
318            }
319        }
320    }
321
322    /// Save the certificate to the specified file in DER format
323    ///
324    /// # Arguments
325    ///
326    /// * `path` - The path to the file
327    pub fn to_der_file(&self, path: &str) -> Result<()> {
328        let der = self.to_der()?;
329        std::fs::write(path, der).map_err(|_| QuantCryptError::InvalidCertificate)?;
330        Ok(())
331    }
332
333    /// Save the certificate to the specified file in PEM format
334    ///
335    /// # Arguments
336    ///
337    /// * `path` - The path to the file
338    pub fn to_pem_file(&self, path: &str) -> Result<()> {
339        let pem = self.to_pem()?;
340        std::fs::write(path, pem).map_err(|_| QuantCryptError::InvalidCertificate)?;
341        Ok(())
342    }
343
344    /// Check if this certificate is identified by the specified recipient identifier
345    ///
346    /// This could match by either issuer and serial number or subject key identifier
347    ///
348    /// # Arguments
349    ///
350    /// * `rid` - The recipient identifier
351    ///
352    /// # Returns
353    ///
354    /// True if the certificate is identified by the recipient identifier, false otherwise
355    pub fn is_identified_by(&self, rid: &RecipientIdentifier) -> bool {
356        match rid {
357            cms::enveloped_data::RecipientIdentifier::IssuerAndSerialNumber(issuer) => {
358                if self.get_issuer() == issuer.issuer
359                    && self.get_serial_number() == issuer.serial_number
360                {
361                    return true;
362                }
363            }
364            cms::enveloped_data::RecipientIdentifier::SubjectKeyIdentifier(ski) => {
365                if let Ok(cert_ski) = self.get_subject_key_identifier() {
366                    if cert_ski == *ski {
367                        return true;
368                    }
369                }
370            }
371        }
372        false
373    }
374
375    /// Check if this certificate is valid
376    ///
377    /// # Returns
378    ///
379    /// True if the certificate is valid, false otherwise
380    pub fn is_valid(&self) -> bool {
381        // Get the notBefore and notAfter fields as DateTime
382        let not_before = self.cert.tbs_certificate.validity.not_before.to_date_time();
383        let not_after = self.cert.tbs_certificate.validity.not_after.to_date_time();
384
385        // Interpret the times as UTC
386        let not_before: DateTime<Utc> = not_before.to_system_time().into();
387        let not_after: DateTime<Utc> = not_after.to_system_time().into();
388
389        // Get the current time
390        let now = chrono::Utc::now();
391
392        // Check if the current time is within the validity period
393        let result = now >= not_before && now <= not_after;
394
395        // Certificate sig oid must match the expected sig oid
396        let oid = self.cert.signature_algorithm.oid;
397        let expected_oid = self.cert.tbs_certificate.signature.oid;
398        if oid != expected_oid {
399            return false;
400        }
401
402        result
403    }
404
405    /// Check if key encipherment is enabled
406    ///
407    /// # Returns
408    ///
409    /// True if key encipherment is enabled, false otherwise
410    pub fn is_key_encipherment_enabled(&self) -> bool {
411        if let Some(exts) = self.cert.tbs_certificate.extensions.clone() {
412            for ext in exts {
413                if ext.extn_id == const_oid::db::rfc5280::ID_CE_KEY_USAGE {
414                    if let Ok(ku) = KeyUsage::from_der(ext.extn_value.as_bytes()) {
415                        return ku.key_encipherment();
416                    }
417                }
418            }
419        }
420        false
421    }
422
423    /// Get the OID of algorithm used for the public key
424    ///
425    /// # Returns
426    ///
427    /// The OID of the algorithm used for the public key
428    pub fn get_public_key_oid(&self) -> String {
429        self.cert
430            .tbs_certificate
431            .subject_public_key_info
432            .algorithm
433            .oid
434            .to_string()
435    }
436
437    /// Get the OID of algorithm used for the signature
438    ///
439    /// # Returns
440    ///
441    /// The OID of the algorithm used for the signature
442    pub fn get_signature_oid(&self) -> String {
443        self.cert.tbs_certificate.signature.oid.to_string()
444    }
445
446    /// Get the friendly name of the algorithm used for the public key
447    ///
448    /// # Returns
449    ///
450    /// The friendly name of the algorithm used for the public key
451    pub fn get_public_key_oid_friendly_name(&self) -> String {
452        let oid = self.get_public_key_oid();
453        if let Ok(man) = DsaManager::new_from_oid(&oid) {
454            let info = man.get_dsa_info();
455            format!("{:?}", info.dsa_type)
456        } else if let Ok(man) = KemManager::new_from_oid(&oid) {
457            let info = man.get_kem_info();
458            format!("{:?}", info.kem_type)
459        } else {
460            "Unknown".to_string()
461        }
462    }
463}
464
465#[cfg(test)]
466mod tests {
467    use crate::{certificates::CertValidity, certificates::Certificate};
468
469    //const USE_OLD_VERSION: bool = true;
470
471    #[test]
472    fn test_ml_dsa44_ecdsa_p256_sha256_self_signed_cert() {
473        // let pem_bytes = include_bytes!("../../test/data/MlDsa44EcdsaP256SHA256-2.16.840.1.114027.80.8.1.4_ta.pem");
474        #[cfg(feature = "ipd")]
475        let pem_bytes = include_bytes!(
476            "../../test/data_ipd/MlDsa44EcdsaP256SHA256-2.16.840.1.114027.80.8.1.4_ta.pem"
477        );
478
479        #[cfg(not(feature = "ipd"))]
480        let pem_bytes = include_bytes!(
481            "../../test/data/MlDsa44EcdsaP256SHA256-2.16.840.1.114027.80.8.1.4_ta.pem"
482        );
483
484        let pem = std::str::from_utf8(pem_bytes).unwrap().trim();
485        let cert = Certificate::from_pem(pem).unwrap();
486        assert!(cert.verify_self_signed().unwrap());
487    }
488
489    #[test]
490    fn test_ml_dsa_44_rsa2048_pss_sha256_self_signed_cert() {
491        // let pem_bytes = include_bytes!("../../test/data/MlDsa44Rsa2048PssSha256-2.16.840.1.114027.80.8.1.1_ta.pem");
492        #[cfg(feature = "ipd")]
493        let pem_bytes = include_bytes!(
494            "../../test/data_ipd/MlDsa44Rsa2048PssSha256-2.16.840.1.114027.80.8.1.1_ta.pem"
495        );
496
497        #[cfg(not(feature = "ipd"))]
498        let pem_bytes = include_bytes!(
499            "../../test/data/MlDsa44Rsa2048PssSha256-2.16.840.1.114027.80.8.1.1_ta.pem"
500        );
501        let pem = std::str::from_utf8(pem_bytes).unwrap().trim();
502        let cert = Certificate::from_pem(&pem).unwrap();
503        assert!(cert.verify_self_signed().unwrap());
504    }
505
506    #[test]
507    fn test_ml_dsa_44_rsa2048_pkcs15_sha256_self_signed_cert() {
508        #[cfg(not(feature = "ipd"))]
509        let pem_bytes = include_bytes!(
510            "../../test/data/MlDsa44Rsa2048Pkcs15Sha256-2.16.840.1.114027.80.8.1.2_ta.pem"
511        );
512        #[cfg(feature = "ipd")]
513        let pem_bytes = include_bytes!(
514            "../../test/data_ipd/MlDsa44Rsa2048Pkcs15Sha256-2.16.840.1.114027.80.8.1.2_ta.pem"
515        );
516        let pem = std::str::from_utf8(pem_bytes).unwrap().trim();
517        let cert = Certificate::from_pem(&pem).unwrap();
518        assert!(cert.verify_self_signed().unwrap());
519    }
520
521    #[test]
522    fn test_akid_skid() {
523        // First generate a TA cert
524        let (pk, sk) = crate::dsas::DsaKeyGenerator::new(crate::dsas::DsaAlgorithm::MlDsa44)
525            .generate()
526            .unwrap();
527
528        let validity = CertValidity::new(None, "2035-01-01T00:00:00Z").unwrap();
529
530        let cert = crate::certificates::CertificateBuilder::new(
531            crate::certificates::Profile::Root,
532            None,
533            validity,
534            "CN=example.com".to_string(),
535            pk,
536            &sk,
537        )
538        .unwrap()
539        .build()
540        .unwrap();
541
542        // Next generate a leaf KEM cert
543        let (pk_kem, _) = crate::kems::KemKeyGenerator::new(crate::kems::KemAlgorithm::MlKem512)
544            .generate()
545            .unwrap();
546
547        let validity = CertValidity::new(None, "2035-01-01T00:00:00Z").unwrap();
548
549        let cert_kem = crate::certificates::CertificateBuilder::new(
550            crate::certificates::Profile::Leaf {
551                issuer: cert.get_subject(),
552                enable_key_agreement: false,
553                enable_key_encipherment: true,
554            },
555            None,
556            validity,
557            "CN=example.com".to_string(),
558            pk_kem,
559            &sk,
560        )
561        .unwrap()
562        .build()
563        .unwrap();
564
565        assert!(!cert_kem.verify_self_signed().unwrap());
566        assert!(cert.verify_child(&cert_kem).unwrap());
567    }
568
569    #[test]
570    fn test_certificate_expiry() {
571        // Get now plus 2 secs as UTC String
572        let now = chrono::Utc::now();
573        let not_before = now + chrono::Duration::seconds(2);
574        let not_after = now + chrono::Duration::seconds(5);
575
576        let validity =
577            CertValidity::new(Some(&not_before.to_rfc3339()), &not_after.to_rfc3339()).unwrap();
578
579        let (pk, sk) = crate::dsas::DsaKeyGenerator::new(crate::dsas::DsaAlgorithm::MlDsa44)
580            .generate()
581            .unwrap();
582        let cert = crate::certificates::CertificateBuilder::new(
583            crate::certificates::Profile::Root,
584            None,
585            validity,
586            "CN=example.com".to_string(),
587            pk,
588            &sk,
589        )
590        .unwrap()
591        .build()
592        .unwrap();
593        assert!(!cert.is_valid());
594        // sleep for 1 second
595        std::thread::sleep(std::time::Duration::from_secs(3));
596        assert!(cert.is_valid());
597        // sleep for 3 seconds
598        std::thread::sleep(std::time::Duration::from_secs(5));
599        assert!(!cert.is_valid());
600    }
601}