From 38ec517f4e93f5e6f080a3ebddb91f91dd0563e5 Mon Sep 17 00:00:00 2001 From: Nick Santana Date: Tue, 2 May 2023 12:23:35 -0700 Subject: [PATCH] Convert PKITS tests to integration tests --- verifier/data/tests/README.md | 4 +- .../data/tests/pkits/crls/BadSignedCACRL.crl | Bin 452 -> 0 bytes verifier/src/lib.rs | 4 +- verifier/src/x509.rs | 7 +- verifier/src/x509/algorithm.rs | 4 + verifier/src/x509/certs.rs | 2 + verifier/src/x509/chain.rs | 119 +----------------- verifier/src/x509/error.rs | 5 + verifier/tests/data/README.md | 4 + .../tests => tests/data}/pkits/README.md | 0 .../data}/pkits/certs/BadSignedCACert.crt | Bin .../data}/pkits/certs/GoodCACert.crt | Bin .../pkits/certs/InvalidCASignatureTest2EE.crt | Bin .../pkits/certs/InvalidEESignatureTest3EE.crt | Bin .../certs/TrustAnchorRootCertificate.crt | Bin .../certs/ValidCertificatePathTest1EE.crt | Bin .../data}/pkits/crls/GoodCACRL.crl | Bin .../data}/pkits/crls/TrustAnchorRootCRL.crl | Bin verifier/tests/pkits-4-1.rs | 111 ++++++++++++++++ 19 files changed, 141 insertions(+), 119 deletions(-) delete mode 100644 verifier/data/tests/pkits/crls/BadSignedCACRL.crl create mode 100644 verifier/tests/data/README.md rename verifier/{data/tests => tests/data}/pkits/README.md (100%) rename verifier/{data/tests => tests/data}/pkits/certs/BadSignedCACert.crt (100%) rename verifier/{data/tests => tests/data}/pkits/certs/GoodCACert.crt (100%) rename verifier/{data/tests => tests/data}/pkits/certs/InvalidCASignatureTest2EE.crt (100%) rename verifier/{data/tests => tests/data}/pkits/certs/InvalidEESignatureTest3EE.crt (100%) rename verifier/{data/tests => tests/data}/pkits/certs/TrustAnchorRootCertificate.crt (100%) rename verifier/{data/tests => tests/data}/pkits/certs/ValidCertificatePathTest1EE.crt (100%) rename verifier/{data/tests => tests/data}/pkits/crls/GoodCACRL.crl (100%) rename verifier/{data/tests => tests/data}/pkits/crls/TrustAnchorRootCRL.crl (100%) create mode 100644 verifier/tests/pkits-4-1.rs diff --git a/verifier/data/tests/README.md b/verifier/data/tests/README.md index 004dd03..6f1e962 100644 --- a/verifier/data/tests/README.md +++ b/verifier/data/tests/README.md @@ -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. @@ -14,6 +14,4 @@ * `processor_crl.pem` - CRL for the processor CA in a certificate chain. This was retrieved from . -* The `pkits` directory contains test data from the NIST PKITS test suite. - See for more information. * The `pub_keys` directory contains some pre-canned public keys. diff --git a/verifier/data/tests/pkits/crls/BadSignedCACRL.crl b/verifier/data/tests/pkits/crls/BadSignedCACRL.crl deleted file mode 100644 index f7d0c8030a775c01d0d81fb12b638dec12fa39ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 452 zcmXqLVmx5bxRQyH(SVnYQ>)FR?K>|cBR4C9ftw+>0Vf-CC<~h~Q)sZEyn!r;!zC;h zl3HA%;G9}il9`s7oLG`ttYBndXlN*AAOcdrEX?ban4%DznVy%LqTuW(&TD93U1opgvh;76}8f29fH!0@hv+PTX~A_3N;S>OMPDY4Uml zE|3Bd7G_LWGB+_YGC1UZnepi7s%(biFJk2;+LO=HlNLN%_Tbe27`@!r9+Mk*Cv14tworso zI$f)86Z?6`mZC4t^O}!bDPG9GE-cY+TXB7+!VL57mpQ@blkUy$ty~cPOYOi}gYE2| zoXqFaldQ$Wd3ZNG^YCA9`lVRx%(AplH5HYOmpYrjxJhXBs)kD*v}#BRF7^ud+%I>3 zCG!a{cdm2gm;W9&=B~SN>%qDuJ#7z9zU>tEICqo1V+pI9$CM+@0?d_0_wFysuZ-lY bEDLAsF3Oy->zz>1sqP<-zoc0erz{2lO{1zK diff --git a/verifier/src/lib.rs b/verifier/src/lib.rs index ec4407f..3330f58 100644 --- a/verifier/src/lib.rs +++ b/verifier/src/lib.rs @@ -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, diff --git a/verifier/src/x509.rs b/verifier/src/x509.rs index 05bf1b7..b0032ed 100644 --- a/verifier/src/x509.rs +++ b/verifier/src/x509.rs @@ -9,5 +9,8 @@ mod chain; mod crl; mod error; -pub use error::Error; -pub type Result = core::result::Result; +pub use algorithm::{PublicKey, Signature}; +pub use certs::{UnverifiedCertificate, VerifiedCertificate}; +pub use chain::CertificateChain; +pub use crl::UnverifiedCrl; +pub use error::{Error, Result}; diff --git a/verifier/src/x509/algorithm.rs b/verifier/src/x509/algorithm.rs index 4108331..a32dcc2 100644 --- a/verifier/src/x509/algorithm.rs +++ b/verifier/src/x509/algorithm.rs @@ -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), } @@ -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), } diff --git a/verifier/src/x509/certs.rs b/verifier/src/x509/certs.rs index 4426e42..9926a94 100644 --- a/verifier/src/x509/certs.rs +++ b/verifier/src/x509/certs.rs @@ -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 { self.verify(&self.key, unix_time) } diff --git a/verifier/src/x509/chain.rs b/verifier/src/x509/chain.rs index e04018d..0f06271 100644 --- a/verifier/src/x509/chain.rs +++ b/verifier/src/x509/chain.rs @@ -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. @@ -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, Duration) { - let crls = der_crls - .iter() - .map(|crl| UnverifiedCrl::try_from(*crl).expect("Failed decoding CRL")) - .collect::>(); - - 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 -} diff --git a/verifier/src/x509/error.rs b/verifier/src/x509/error.rs index e7e42bb..bb3450a 100644 --- a/verifier/src/x509/error.rs +++ b/verifier/src/x509/error.rs @@ -1,3 +1,8 @@ +// Copyright (c) 2023 The MobileCoin Foundation + +/// Result type for certificate processing +pub type Result = core::result::Result; + /// Error type for decoding and verifying certificates. #[derive(Debug, displaydoc::Display, PartialEq, Eq)] pub enum Error { diff --git a/verifier/tests/data/README.md b/verifier/tests/data/README.md new file mode 100644 index 0000000..489a588 --- /dev/null +++ b/verifier/tests/data/README.md @@ -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 for more information. diff --git a/verifier/data/tests/pkits/README.md b/verifier/tests/data/pkits/README.md similarity index 100% rename from verifier/data/tests/pkits/README.md rename to verifier/tests/data/pkits/README.md diff --git a/verifier/data/tests/pkits/certs/BadSignedCACert.crt b/verifier/tests/data/pkits/certs/BadSignedCACert.crt similarity index 100% rename from verifier/data/tests/pkits/certs/BadSignedCACert.crt rename to verifier/tests/data/pkits/certs/BadSignedCACert.crt diff --git a/verifier/data/tests/pkits/certs/GoodCACert.crt b/verifier/tests/data/pkits/certs/GoodCACert.crt similarity index 100% rename from verifier/data/tests/pkits/certs/GoodCACert.crt rename to verifier/tests/data/pkits/certs/GoodCACert.crt diff --git a/verifier/data/tests/pkits/certs/InvalidCASignatureTest2EE.crt b/verifier/tests/data/pkits/certs/InvalidCASignatureTest2EE.crt similarity index 100% rename from verifier/data/tests/pkits/certs/InvalidCASignatureTest2EE.crt rename to verifier/tests/data/pkits/certs/InvalidCASignatureTest2EE.crt diff --git a/verifier/data/tests/pkits/certs/InvalidEESignatureTest3EE.crt b/verifier/tests/data/pkits/certs/InvalidEESignatureTest3EE.crt similarity index 100% rename from verifier/data/tests/pkits/certs/InvalidEESignatureTest3EE.crt rename to verifier/tests/data/pkits/certs/InvalidEESignatureTest3EE.crt diff --git a/verifier/data/tests/pkits/certs/TrustAnchorRootCertificate.crt b/verifier/tests/data/pkits/certs/TrustAnchorRootCertificate.crt similarity index 100% rename from verifier/data/tests/pkits/certs/TrustAnchorRootCertificate.crt rename to verifier/tests/data/pkits/certs/TrustAnchorRootCertificate.crt diff --git a/verifier/data/tests/pkits/certs/ValidCertificatePathTest1EE.crt b/verifier/tests/data/pkits/certs/ValidCertificatePathTest1EE.crt similarity index 100% rename from verifier/data/tests/pkits/certs/ValidCertificatePathTest1EE.crt rename to verifier/tests/data/pkits/certs/ValidCertificatePathTest1EE.crt diff --git a/verifier/data/tests/pkits/crls/GoodCACRL.crl b/verifier/tests/data/pkits/crls/GoodCACRL.crl similarity index 100% rename from verifier/data/tests/pkits/crls/GoodCACRL.crl rename to verifier/tests/data/pkits/crls/GoodCACRL.crl diff --git a/verifier/data/tests/pkits/crls/TrustAnchorRootCRL.crl b/verifier/tests/data/pkits/crls/TrustAnchorRootCRL.crl similarity index 100% rename from verifier/data/tests/pkits/crls/TrustAnchorRootCRL.crl rename to verifier/tests/data/pkits/crls/TrustAnchorRootCRL.crl diff --git a/verifier/tests/pkits-4-1.rs b/verifier/tests/pkits-4-1.rs new file mode 100644 index 0000000..fd9d700 --- /dev/null +++ b/verifier/tests/pkits-4-1.rs @@ -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, Duration) { + let crls = der_crls + .iter() + .map(|crl| UnverifiedCrl::try_from(*crl).expect("Failed decoding CRL")) + .collect::>(); + + 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) + ); +}