Skip to content

Commit

Permalink
Convert PKITS tests to integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
nick-mobilecoin committed May 3, 2023
1 parent 702c986 commit 38ec517
Show file tree
Hide file tree
Showing 19 changed files with 141 additions and 119 deletions.
4 changes: 1 addition & 3 deletions verifier/data/tests/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Test data for `mc-attestation-verifier`
# Test data for `mc-attestation-verifier` unit tests

* `root_ca.pem` - Root CA of a certificate chain. This is a copy of an Intel
root CA which was in an actual hardware quote.
Expand All @@ -14,6 +14,4 @@
* `processor_crl.pem` - CRL for the processor CA in a certificate chain. This
was retrieved from
<https://api.trustedservices.intel.com/sgx/certification/v4/pckcrl?ca=processor>.
* The `pkits` directory contains test data from the NIST PKITS test suite.
See <https://csrc.nist.gov/projects/pki-testing> for more information.
* The `pub_keys` directory contains some pre-canned public keys.
Binary file removed verifier/data/tests/pkits/crls/BadSignedCACRL.crl
Binary file not shown.
4 changes: 3 additions & 1 deletion verifier/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@

mod report_body;
mod struct_name;

/// X509 certificate processing
#[cfg(feature = "alloc")]
mod x509;
pub mod x509;

pub use report_body::{
AttributesVerifier, ConfigIdVerifier, ConfigSvnVerifier, CpuSvnVerifier,
Expand Down
7 changes: 5 additions & 2 deletions verifier/src/x509.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@ mod chain;
mod crl;
mod error;

pub use error::Error;
pub type Result<T> = core::result::Result<T, Error>;
pub use algorithm::{PublicKey, Signature};
pub use certs::{UnverifiedCertificate, VerifiedCertificate};
pub use chain::CertificateChain;
pub use crl::UnverifiedCrl;
pub use error::{Error, Result};
4 changes: 4 additions & 0 deletions verifier/src/x509/algorithm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ const OID_SIG_ECDSA_WITH_SHA256: ObjectIdentifier =
/// Public key used in PKI signature verification
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum PublicKey {
/// Elliptic curve public key
Ecdsa(ecdsa::VerifyingKey),
/// RSA public key
Rsa(rsa::RsaPublicKey),
}

Expand Down Expand Up @@ -81,7 +83,9 @@ impl TryFrom<&SubjectPublicKeyInfoOwned> for PublicKey {
/// Signature used in PKI verification
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Signature {
/// Elliptic curve signature
Ecdsa(ecdsa::Signature),
/// RSA signature
Rsa(Vec<u8>),
}

Expand Down
2 changes: 2 additions & 0 deletions verifier/src/x509/certs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ pub struct UnverifiedCertificate {
}

impl UnverifiedCertificate {
/// Verify a self signed certificate has its own signature and that the
/// `unix_time` is valid.
pub fn verify_self_signed(&self, unix_time: Duration) -> Result<VerifiedCertificate> {
self.verify(&self.key, unix_time)
}
Expand Down
119 changes: 6 additions & 113 deletions verifier/src/x509/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ impl CertificateChain {
}
}

impl AsRef<[UnverifiedCertificate]> for CertificateChain {
fn as_ref(&self) -> &[UnverifiedCertificate] {
&self.certificates
}
}

/// Convert a series PEM-encoded certificates into a [`CertificateChain`].
///
/// The PEMs must be in order from the trust anchor to the leaf certificate.
Expand Down Expand Up @@ -271,116 +277,3 @@ mod test {
);
}
}

#[cfg(test)]
mod pkits {
use super::*;

use x509_cert::crl::CertificateList;
use x509_cert::der::Decode;
use x509_cert::Certificate as X509Certificate;

const TRUST_ANCHOR_ROOT_CERTIFICATE: &[u8] =
include_bytes!("../../data/tests/pkits/certs/TrustAnchorRootCertificate.crt");
const GOOD_CA_CERT: &[u8] = include_bytes!("../../data/tests/pkits/certs/GoodCACert.crt");
const VALID_CERTIFICATE_PATH_TEST_1EE: &[u8] =
include_bytes!("../../data/tests/pkits/certs/ValidCertificatePathTest1EE.crt");
const TRUST_ANCHOR_ROOT_CRL: &[u8] =
include_bytes!("../../data/tests/pkits/crls/TrustAnchorRootCRL.crl");
const GOOD_CA_CRL: &[u8] = include_bytes!("../../data/tests/pkits/crls/GoodCACRL.crl");
const BAD_SIGNED_CA_CERT: &[u8] =
include_bytes!("../../data/tests/pkits/certs/BadSignedCACert.crt");
const BAD_SIGNED_CA_CRL: &[u8] =
include_bytes!("../../data/tests/pkits/crls/BadSignedCACRL.crl");
const INVALID_CA_SIGNATURE_TEST_2EE: &[u8] =
include_bytes!("../../data/tests/pkits/certs/InvalidCASignatureTest2EE.crt");
const INVALID_EE_SIGNATURE_TEST_3EE: &[u8] =
include_bytes!("../../data/tests/pkits/certs/InvalidEESignatureTest3EE.crt");

fn chain_and_leaf_key(certs: &[&[u8]]) -> (CertificateChain, PublicKey) {
let chain = CertificateChain::try_from(certs).expect("Failed decoding certs");

let leaf_der = certs.last().expect("Should be at least one cert");
let leaf_cert = X509Certificate::from_der(leaf_der).expect("Falied decoding DER");
let key = PublicKey::try_from(&leaf_cert.tbs_certificate.subject_public_key_info)
.expect("Failed decoding key");

(chain, key)
}

fn crls_and_time(der_crls: &[&[u8]]) -> (Vec<UnverifiedCrl>, Duration) {
let crls = der_crls
.iter()
.map(|crl| UnverifiedCrl::try_from(*crl).expect("Failed decoding CRL"))
.collect::<Vec<_>>();

let last_der = der_crls.last().expect("Should be at least one CRL");
let crl = CertificateList::from_der(last_der).expect("Falied decoding DER");
let unix_time = crl.tbs_cert_list.this_update.to_unix_duration();
(crls, unix_time)
}

#[test]
fn valid_signatures_test_4_1_1() {
let (chain, expected_key) = chain_and_leaf_key(
[
TRUST_ANCHOR_ROOT_CERTIFICATE,
GOOD_CA_CERT,
VALID_CERTIFICATE_PATH_TEST_1EE,
]
.as_slice(),
);
let (crls, unix_time) = crls_and_time([TRUST_ANCHOR_ROOT_CRL, GOOD_CA_CRL].as_slice());

let root = chain.certificates[0]
.verify_self_signed(unix_time)
.expect("Failed verifying root");

let signing_key = chain
.signing_key(&root, unix_time, crls.as_slice())
.expect("Failed getting signing key");

assert_eq!(signing_key, expected_key);
}

#[test]
fn invalid_ca_signature_test_4_1_2() {
let certs = [
TRUST_ANCHOR_ROOT_CERTIFICATE,
BAD_SIGNED_CA_CERT,
INVALID_CA_SIGNATURE_TEST_2EE,
];

// Building the chain parses the signatures of each certificate. The
// invalid signature causes a parsing error.
assert_eq!(
CertificateChain::try_from(certs.as_slice()),
Err(Error::SignatureDecoding)
);
}

#[test]
fn invalid_ee_signature_test_4_1_3() {
let (chain, _) = chain_and_leaf_key(
[
TRUST_ANCHOR_ROOT_CERTIFICATE,
GOOD_CA_CERT,
INVALID_EE_SIGNATURE_TEST_3EE,
]
.as_slice(),
);
let (crls, unix_time) = crls_and_time([TRUST_ANCHOR_ROOT_CRL, GOOD_CA_CRL].as_slice());

let root = chain.certificates[0]
.verify_self_signed(unix_time)
.expect("Failed verifying root");

assert_eq!(
chain.signing_key(&root, unix_time, crls.as_slice()),
Err(Error::SignatureVerification)
);
}

// Tests 4.1.4 - 4.1.6 are not implemented because DSA has been deprecated, see
// https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5-draft.pdf#%5B%7B%22num%22%3A72%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C72%2C721%2Cnull%5D
}
5 changes: 5 additions & 0 deletions verifier/src/x509/error.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
// Copyright (c) 2023 The MobileCoin Foundation

/// Result type for certificate processing
pub type Result<T> = core::result::Result<T, Error>;

/// Error type for decoding and verifying certificates.
#[derive(Debug, displaydoc::Display, PartialEq, Eq)]
pub enum Error {
Expand Down
4 changes: 4 additions & 0 deletions verifier/tests/data/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Test data for `mc-attestation-verifier` integration tests

* The `pkits` directory contains test data from the NIST PKITS test suite.
See <https://csrc.nist.gov/projects/pki-testing> for more information.
File renamed without changes.
111 changes: 111 additions & 0 deletions verifier/tests/pkits-4-1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright (c) 2023 The MobileCoin Foundation

//! Test for section 4.1 of the PKI test suite.
//! https://csrc.nist.gov/projects/pki-testing
//!
//! Tests 4.1.4 - 4.1.6 are not implemented because DSA has been deprecated, see
//! https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5-draft.pdf#%5B%7B%22num%22%3A72%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C72%2C721%2Cnull%5D

use mc_attestation_verifier::x509::PublicKey;
use mc_attestation_verifier::x509::{CertificateChain, Error, UnverifiedCrl};
use std::time::Duration;
use x509_cert::crl::CertificateList;
use x509_cert::der::Decode;
use x509_cert::Certificate as X509Certificate;

const TRUST_ANCHOR_ROOT_CERTIFICATE: &[u8] =
include_bytes!("data/pkits/certs/TrustAnchorRootCertificate.crt");
const GOOD_CA_CERT: &[u8] = include_bytes!("data/pkits/certs/GoodCACert.crt");
const VALID_CERTIFICATE_PATH_TEST_1EE: &[u8] =
include_bytes!("data/pkits/certs/ValidCertificatePathTest1EE.crt");
const TRUST_ANCHOR_ROOT_CRL: &[u8] = include_bytes!("data/pkits/crls/TrustAnchorRootCRL.crl");
const GOOD_CA_CRL: &[u8] = include_bytes!("data/pkits/crls/GoodCACRL.crl");
const BAD_SIGNED_CA_CERT: &[u8] = include_bytes!("data/pkits/certs/BadSignedCACert.crt");
const INVALID_CA_SIGNATURE_TEST_2EE: &[u8] =
include_bytes!("data/pkits/certs/InvalidCASignatureTest2EE.crt");
const INVALID_EE_SIGNATURE_TEST_3EE: &[u8] =
include_bytes!("data/pkits/certs/InvalidEESignatureTest3EE.crt");

fn chain_and_leaf_key(certs: &[&[u8]]) -> (CertificateChain, PublicKey) {
let chain = CertificateChain::try_from(certs).expect("Failed decoding certs");

let leaf_der = certs.last().expect("Should be at least one cert");
let leaf_cert = X509Certificate::from_der(leaf_der).expect("Failed decoding DER");
let key = PublicKey::try_from(&leaf_cert.tbs_certificate.subject_public_key_info)
.expect("Failed decoding key");

(chain, key)
}

fn crls_and_time(der_crls: &[&[u8]]) -> (Vec<UnverifiedCrl>, Duration) {
let crls = der_crls
.iter()
.map(|crl| UnverifiedCrl::try_from(*crl).expect("Failed decoding CRL"))
.collect::<Vec<_>>();

let last_der = der_crls.last().expect("Should be at least one CRL");
let crl = CertificateList::from_der(last_der).expect("Failed decoding DER");
let unix_time = crl.tbs_cert_list.this_update.to_unix_duration();
(crls, unix_time)
}

#[test]
fn valid_signatures_test_4_1_1() {
let (chain, expected_key) = chain_and_leaf_key(
[
TRUST_ANCHOR_ROOT_CERTIFICATE,
GOOD_CA_CERT,
VALID_CERTIFICATE_PATH_TEST_1EE,
]
.as_slice(),
);
let (crls, unix_time) = crls_and_time([TRUST_ANCHOR_ROOT_CRL, GOOD_CA_CRL].as_slice());

let root = chain.as_ref()[0]
.verify_self_signed(unix_time)
.expect("Failed verifying root");

let signing_key = chain
.signing_key(&root, unix_time, crls.as_slice())
.expect("Failed getting signing key");

assert_eq!(signing_key, expected_key);
}

#[test]
fn invalid_ca_signature_test_4_1_2() {
let certs = [
TRUST_ANCHOR_ROOT_CERTIFICATE,
BAD_SIGNED_CA_CERT,
INVALID_CA_SIGNATURE_TEST_2EE,
];

// Building the chain parses the signatures of each certificate. The
// invalid signature causes a parsing error.
assert_eq!(
CertificateChain::try_from(certs.as_slice()),
Err(Error::SignatureDecoding)
);
}

#[test]
fn invalid_ee_signature_test_4_1_3() {
let (chain, _) = chain_and_leaf_key(
[
TRUST_ANCHOR_ROOT_CERTIFICATE,
GOOD_CA_CERT,
INVALID_EE_SIGNATURE_TEST_3EE,
]
.as_slice(),
);
let (crls, unix_time) = crls_and_time([TRUST_ANCHOR_ROOT_CRL, GOOD_CA_CRL].as_slice());

let root = chain.as_ref()[0]
.verify_self_signed(unix_time)
.expect("Failed verifying root");

assert_eq!(
chain.signing_key(&root, unix_time, crls.as_slice()),
Err(Error::SignatureVerification)
);
}

0 comments on commit 38ec517

Please sign in to comment.