Skip to content

Commit

Permalink
Add initial CRL logic to cert chain verification
Browse files Browse the repository at this point in the history
  • Loading branch information
nick-mobilecoin committed May 2, 2023
1 parent e3ab151 commit 2bf5d6f
Show file tree
Hide file tree
Showing 4 changed files with 272 additions and 80 deletions.
116 changes: 98 additions & 18 deletions verifier/src/x509/certs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,35 +10,24 @@ use core::time::Duration;
use p256::ecdsa::signature::Verifier;
use p256::ecdsa::{Signature, VerifyingKey};
use x509_cert::der::{Decode, Encode};
use x509_cert::name::Name;
use x509_cert::serial_number::SerialNumber;
use x509_cert::Certificate as X509Certificate;

/// A certificate whose signature has not been verified.
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct UnverifiedCertificate {
// In order to verify the signature, we need to access the original DER
// bytes
der_bytes: Vec<u8>,
pub(crate) certificate: X509Certificate,
certificate: X509Certificate,
// The signature and key are persisted here since they are fallible
// operations and it's more ergonomic to fail fast than fail later for a
// bad key or signature
signature: Signature,
key: VerifyingKey,
}

/// A certificate whose signature has been verified.
#[derive(Debug, PartialEq, Eq)]
pub struct VerifiedCertificate {
_certificate: X509Certificate,
key: VerifyingKey,
}

impl VerifiedCertificate {
pub(crate) fn public_key(&self) -> VerifyingKey {
self.key
}
}

impl UnverifiedCertificate {
pub fn verify_self_signed(&self, unix_time: Duration) -> Result<VerifiedCertificate> {
self.verify(&self.key, unix_time)
Expand All @@ -47,8 +36,8 @@ impl UnverifiedCertificate {
/// Verify the certificate signature and time are valid.
///
/// # Arguments
/// - `key` - The public key to verify the certificate signature with
/// - `unix_time` - The duration since
/// * `key` - The public key to verify the certificate signature with
/// * `unix_time` - The duration since
/// [`UNIX_EPOCH`](https://doc.rust-lang.org/std/time/constant.UNIX_EPOCH.html).
/// This is expected to be generated by the caller using:
/// ```ignore
Expand All @@ -60,7 +49,7 @@ impl UnverifiedCertificate {
self.verify_signature(key)?;

Ok(VerifiedCertificate {
_certificate: self.certificate.clone(),
certificate: self.certificate.clone(),
key: self.key,
})
}
Expand Down Expand Up @@ -136,6 +125,46 @@ impl TryFrom<&[u8]> for UnverifiedCertificate {
}
}

/// A certificate whose signature has been verified.
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct VerifiedCertificate {
certificate: X509Certificate,
key: VerifyingKey,
}

impl VerifiedCertificate {
/// Try to convert and verify a self signed PEM-encoded certificate
///
/// # Arguments
/// * `pem` - The PEM-encoded certificate
/// * `unix_time` - The duration since
/// [`UNIX_EPOCH`](https://doc.rust-lang.org/std/time/constant.UNIX_EPOCH.html).
/// This is expected to be generated by the caller using:
/// ```ignore
/// SystemTime::now().duration_since(UNIX_EPOCH)
/// ```
/// or equivalent
pub fn try_from_self_signed_pem(pem: &str, unix_time: Duration) -> Result<Self> {
let unverified_cert = UnverifiedCertificate::try_from(pem)?;
unverified_cert.verify_self_signed(unix_time)
}

/// Get the public signing key of the certificate
pub fn public_key(&self) -> VerifyingKey {
self.key
}

/// Get the subject name of the certificate
pub fn subject_name(&self) -> &Name {
&self.certificate.tbs_certificate.subject
}

/// Get the serial number of the certificate
pub fn serial_number(&self) -> &SerialNumber {
&self.certificate.tbs_certificate.serial_number
}
}

#[cfg(test)]
mod test {
use super::*;
Expand Down Expand Up @@ -450,4 +479,55 @@ mod test {
Err(Error::CertificateExpired)
);
}

#[test]
fn verified_from_self_signed_pem() {
let pem = ROOT_CA;
let root_cert =
UnverifiedCertificate::try_from(pem).expect("Failed to decode certificate from PEM");
let unix_time = root_cert
.certificate
.tbs_certificate
.validity
.not_after
.to_unix_duration();
assert!(VerifiedCertificate::try_from_self_signed_pem(pem, unix_time).is_ok());
}

#[test]
fn verified_from_self_signed_pem_fails_for_wrong_time() {
let pem = ROOT_CA;
let root_cert =
UnverifiedCertificate::try_from(pem).expect("Failed to decode certificate from PEM");
let mut unix_time = root_cert
.certificate
.tbs_certificate
.validity
.not_after
.to_unix_duration();
unix_time += Duration::from_nanos(1);
assert_eq!(
VerifiedCertificate::try_from_self_signed_pem(pem, unix_time),
Err(Error::CertificateExpired)
);
}

#[test]
fn verified_from_self_signed_pem_fails_for_decoding_error() {
let pem = ROOT_CA;
let root_cert =
UnverifiedCertificate::try_from(pem).expect("Failed to decode certificate from PEM");
let unix_time = root_cert
.certificate
.tbs_certificate
.validity
.not_after
.to_unix_duration();

let bad_pem = pem.replace("-----END CERTIFICATE-----", "");
assert!(matches!(
VerifiedCertificate::try_from_self_signed_pem(bad_pem.as_str(), unix_time),
Err(Error::PemDecoding(_))
));
}
}
Loading

0 comments on commit 2bf5d6f

Please sign in to comment.