Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ias certchain #415

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 11 additions & 12 deletions intel-sgx/ias/examples/attestation-tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ const IAS_DEV_URL: &'static str = "https://api.trustedservices.intel.com/sgx/dev

const REPORT_SIZE_TRUNCATED: usize = 384; // without KEYID, MAC

lazy_static::lazy_static!{
// This is the IAS report signing certificate.
static ref IAS_REPORT_SIGNING_CERTIFICATE: Vec<u8> =
pem::pem_to_der(include_str!("../tests/data/reports/test_report_signing_cert"),
lazy_static::lazy_static! {
// This is the IAS report signing CA certificate.
static ref IAS_REPORT_SIGNING_CA: Vec<u8> =
pem::pem_to_der(include_str!("../tests/data/intel_sgx_attestation_report_signing_ca"),
Some(PEM_CERTIFICATE)).unwrap();
}

Expand Down Expand Up @@ -137,14 +137,13 @@ async fn main() {

match ias_client.verify_quote(quote.quote()).await {
Ok(response) => {
let report = match response
.verify::<Mbedtls>(&[IAS_REPORT_SIGNING_CERTIFICATE.as_slice()]) {
Ok(report) => report,
Err(_) => {
eprintln!("Unable to verify signature on IAS report");
process::exit(1);
}
};
let report = match response.verify::<Mbedtls>(&[IAS_REPORT_SIGNING_CA.as_slice()]) {
Ok(report) => report,
Err(_) => {
eprintln!("Unable to verify signature on IAS report");
process::exit(1);
}
};

let pstatus = report.platform_info_blob().as_ref()
.map(|v| v.parse::<PlatformStatus>().map_err(|_| v.to_owned()));
Expand Down
96 changes: 68 additions & 28 deletions intel-sgx/ias/src/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ pub enum ErrorKind {
Asn1InvalidBitstring,
Asn1NotBytes,
CaNotConfigured,
InvalidCaCertificate,
MissingIasReport,
ReportAsn1Parse,
ReportJsonParse,
Expand All @@ -82,6 +83,7 @@ impl fmt::Display for ErrorKind {
Asn1InvalidBitstring => f.write_fmt(format_args!("ASN.1 bitstring is not a multiple of bytes")),
Asn1NotBytes => f.write_fmt(format_args!("ASN.1 value is not a bitstring or octetstring")),
CaNotConfigured => f.write_fmt(format_args!("at least one CA certificate is required")),
InvalidCaCertificate => f.write_fmt(format_args!("One of the provided CA certificates cannot be parsed")),
MissingIasReport => f.write_fmt(format_args!("no IAS report extension in certificate")),
ReportAsn1Parse => f.write_fmt(format_args!("unable to parse report ASN.1")),
ReportJsonParse => f.write_fmt(format_args!("unable to parse report JSON")),
Expand Down Expand Up @@ -117,12 +119,12 @@ impl fmt::Display for ErrorKind {
}
}

pub(crate) fn verify_raw_report<C: Crypto>(raw_report: &[u8], report_sig: &[u8], cert_chain: &Vec<DerSequence>, ca_certificates: &[&[u8]]) -> Result<(), Error> {
// TODO: check the validity of the chain, and use the CA as the trust
// anchor rather than the leaf. Chain verification outside the context
// of TLS connection establishment does not seem to be exposed by
// either the rust openssl or mbedtls bindings.

pub(crate) fn verify_raw_report<C: Crypto>(
raw_report: &[u8],
report_sig: &[u8],
cert_chain: &Vec<DerSequence>,
ca_certificates: &[&[u8]],
) -> Result<(), Error> {
let leaf_cert = match cert_chain.first() {
None => return Err(Error::enclave_certificate(ErrorKind::ReportNoCertificate, None::<Error>)),
Some(cert) => GenericCertificate::from_der(cert)
Expand All @@ -135,14 +137,10 @@ pub(crate) fn verify_raw_report<C: Crypto>(raw_report: &[u8], report_sig: &[u8],
return Err(Error::enclave_certificate(ErrorKind::CaNotConfigured, None::<Error>));
}

// Verify that the leaf cert appears in our list of trusted certificates
if !ca_certificates.iter().map(GenericCertificate::from_der)
.any(|c| match c {
Ok(ref c) if c == &leaf_cert => true,
_ => false
}) {
return Err(Error::enclave_certificate(ErrorKind::ReportUntrustedCertificate, None::<Error>));
}
// Verify the validity of the certificate chain
let mut ca_certs = Vec::new();
ca_certificates.iter().for_each(|c| ca_certs.push(DerSequence::from(*c)));
C::x509_verify_chain(cert_chain, &ca_certs)?;

C::rsa_sha256_verify(leaf_cert.tbscert.spki.as_ref(), &raw_report, report_sig)
.map_err(|e| Error::enclave_certificate(ErrorKind::ReportBadSignature, Some(e)))?;
Expand Down Expand Up @@ -183,7 +181,13 @@ mod tests {
const verify_report: for<'a> fn(ca_certificates: &[&[u8]], report: &AttestationEmbeddedIasReport<'a, 'a, 'a>) -> Result<(), Error>
= super::verify_report::<super::crypto::Mbedtls>;

lazy_static::lazy_static!{
lazy_static::lazy_static! {

//This is the IAS report signing root CA
static ref TEST_REPORT_SIGNING_ROOT_CA: Vec<u8> =
pem_to_der(include_str!("../tests/data/intel_sgx_attestation_report_signing_ca"),
Some(PEM_CERTIFICATE)).unwrap();

// This is the IAS report signing certificate.
static ref TEST_REPORT_SIGNING_CERT: Vec<u8> =
pem_to_der(include_str!("../tests/data/reports/test_report_signing_cert"),
Expand Down Expand Up @@ -214,7 +218,7 @@ mod tests {

#[test]
fn verify_report_success() {
verify_report(&[&TEST_REPORT_SIGNING_CERT], &TEST_REPORT_EXT).unwrap();
verify_report(&[&TEST_REPORT_SIGNING_ROOT_CA], &TEST_REPORT_EXT).unwrap();
}

macro_rules! assert_match {
Expand All @@ -228,30 +232,54 @@ mod tests {
fn verify_report_missing_cert() {
let mut report = TEST_REPORT_EXT.clone();
report.certificates = vec![];
let result = verify_report(&[&TEST_REPORT_SIGNING_CERT], &report).unwrap_err();
assert_match!(result, Error { error_kind: ErrorKind::ReportNoCertificate, cause: _ });
let result = verify_report(&[&TEST_REPORT_SIGNING_ROOT_CA], &report).unwrap_err();
assert_match!(
result,
Error {
error_kind: ErrorKind::ReportNoCertificate,
cause: _
}
);
}

#[test]
fn verify_report_bad_cert() {
let mut report = TEST_REPORT_EXT.clone();
report.certificates = vec![vec![0x30, 0x00].into()];
let result = verify_report(&[&TEST_REPORT_SIGNING_CERT], &report).unwrap_err();
assert_match!(result, Error { error_kind: ErrorKind::ReportInvalidCertificate, cause: _ });
let result = verify_report(&[&TEST_REPORT_SIGNING_ROOT_CA], &report).unwrap_err();
assert_match!(
result,
Error {
error_kind: ErrorKind::ReportInvalidCertificate,
cause: _
}
);
}

#[test]
fn verify_report_wrong_cert() {
let mut cert = GenericCertificate::from_der(TEST_REPORT_SIGNING_CERT.as_slice()).unwrap();
cert.tbscert.spki.value.to_mut()[0] ^= 0x80;
let mut cert = GenericCertificate::from_der(TEST_REPORT_SIGNING_ROOT_CA.as_slice()).unwrap();
cert.tbscert.spki.value.to_mut()[50] ^= 0x80;
let result = verify_report(&[&cert.to_der()], &TEST_REPORT_EXT).unwrap_err();
assert_match!(result, Error { error_kind: ErrorKind::ReportUntrustedCertificate, cause: _ });
assert_match!(
result,
Error {
error_kind: ErrorKind::ReportUntrustedCertificate,
cause: _
}
);
}

#[test]
fn verify_report_bad_ca_cert() {
let result = verify_report(&[&vec![0x30, 0x00]], &TEST_REPORT_EXT).unwrap_err();
assert_match!(result, Error { error_kind: ErrorKind::ReportUntrustedCertificate, cause: _ });
assert_match!(
result,
Error {
error_kind: ErrorKind::InvalidCaCertificate,
cause: _
}
);
}

#[test]
Expand All @@ -265,16 +293,28 @@ mod tests {
// Signature corrupted
let mut report = TEST_REPORT_EXT.clone();
report.report_sig.to_mut()[0] ^= 0x80;
let result = verify_report(&[&TEST_REPORT_SIGNING_CERT], &report).unwrap_err();
assert_match!(result, Error { error_kind: ErrorKind::ReportBadSignature, cause: _ });
let result = verify_report(&[&TEST_REPORT_SIGNING_ROOT_CA], &report).unwrap_err();
assert_match!(
result,
Error {
error_kind: ErrorKind::ReportBadSignature,
cause: _
}
);
}

#[test]
fn verify_report_bad_signature2() {
// Message (report JSON) corrupted
let mut report = TEST_REPORT_EXT.clone();
report.http_body.to_mut()[0] ^= 0x80;
let result = verify_report(&[&TEST_REPORT_SIGNING_CERT], &report).unwrap_err();
assert_match!(result, Error { error_kind: ErrorKind::ReportBadSignature, cause: _ });
let result = verify_report(&[&TEST_REPORT_SIGNING_ROOT_CA], &report).unwrap_err();
assert_match!(
result,
Error {
error_kind: ErrorKind::ReportBadSignature,
cause: _
}
);
}
}
8 changes: 8 additions & 0 deletions intel-sgx/ias/src/verifier/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ pub use self::mbedtls::Mbedtls;
pub const SHA256_DIGEST_LEN: usize = 32;

pub(super) mod private {
use pkix::types::DerSequence;

pub trait Crypto {
type Error: std::error::Error + Send + Sync + 'static;

Expand All @@ -24,5 +26,11 @@ pub(super) mod private {
///
/// Returns `Ok(())` if the signature is correct, an error otherwise.
fn rsa_sha256_verify(public_key: &[u8], message: &[u8], signature: &[u8]) -> ::std::result::Result<(), Self::Error>;

//TODO add support for CRLs
/// Verify an x.509 certificate chain `cert_chain`, given a list of trusted CAs `ca_certs`
///
/// Returns `Ok(())` if the certificate chain is valid, an error otherwise.
fn x509_verify_chain(cert_chain: &Vec<DerSequence>, ca_certs: &Vec<DerSequence>) -> ::std::result::Result<(), crate::verifier::Error>;
}
}
31 changes: 28 additions & 3 deletions intel-sgx/ias/src/verifier/crypto/mbedtls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use mbedtls::Error;
use mbedtls::hash::{Type,Md};
use mbedtls::pk::Pk;
use crate::verifier::{Error as VerifierError, ErrorKind};
use mbedtls;
use mbedtls::alloc::List as MbedtlsList;
use mbedtls::hash::{Md, Type};
use mbedtls::pk::Pk;
use mbedtls::x509::Certificate;
use mbedtls::Error;
use pkix::types::DerSequence;

use super::{private::Crypto, SHA256_DIGEST_LEN};

Expand All @@ -31,4 +35,25 @@ impl Crypto for Mbedtls {

Ok(())
}

fn x509_verify_chain(
cert_chain: &Vec<DerSequence>,
ca_certs: &Vec<DerSequence>,
) -> ::std::result::Result<(), VerifierError> {
let cert_chain = der_to_mbedtls_cert_list(cert_chain)
.map_err(|e| VerifierError::enclave_certificate(ErrorKind::ReportInvalidCertificate, Some(e)))?;
let ca_certs = der_to_mbedtls_cert_list(ca_certs)
.map_err(|e| VerifierError::enclave_certificate(ErrorKind::InvalidCaCertificate, Some(e)))?;
Certificate::verify(&cert_chain, &ca_certs, None)
.map_err(|e| VerifierError::enclave_certificate(ErrorKind::ReportUntrustedCertificate, Some(e)))?;
Ok(())
}
}

fn der_to_mbedtls_cert_list(certificates: &Vec<DerSequence>) -> Result<MbedtlsList<Certificate>, Error> {
let mut list = MbedtlsList::new();
for c in certificates {
list.push(Certificate::from_der(c.value.as_ref())?);
}
Ok(list)
}
31 changes: 31 additions & 0 deletions intel-sgx/ias/tests/data/intel_sgx_attestation_report_signing_ca
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFSzCCA7OgAwIBAgIJANEHdl0yo7CUMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNV
BAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwLU2FudGEgQ2xhcmExGjAYBgNV
BAoMEUludGVsIENvcnBvcmF0aW9uMTAwLgYDVQQDDCdJbnRlbCBTR1ggQXR0ZXN0
YXRpb24gUmVwb3J0IFNpZ25pbmcgQ0EwIBcNMTYxMTE0MTUzNzMxWhgPMjA0OTEy
MzEyMzU5NTlaMH4xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwL
U2FudGEgQ2xhcmExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMTAwLgYDVQQD
DCdJbnRlbCBTR1ggQXR0ZXN0YXRpb24gUmVwb3J0IFNpZ25pbmcgQ0EwggGiMA0G
CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCfPGR+tXc8u1EtJzLA10Feu1Wg+p7e
LmSRmeaCHbkQ1TF3Nwl3RmpqXkeGzNLd69QUnWovYyVSndEMyYc3sHecGgfinEeh
rgBJSEdsSJ9FpaFdesjsxqzGRa20PYdnnfWcCTvFoulpbFR4VBuXnnVLVzkUvlXT
L/TAnd8nIZk0zZkFJ7P5LtePvykkar7LcSQO85wtcQe0R1Raf/sQ6wYKaKmFgCGe
NpEJUmg4ktal4qgIAxk+QHUxQE42sxViN5mqglB0QJdUot/o9a/V/mMeH8KvOAiQ
byinkNndn+Bgk5sSV5DFgF0DffVqmVMblt5p3jPtImzBIH0QQrXJq39AT8cRwP5H
afuVeLHcDsRp6hol4P+ZFIhu8mmbI1u0hH3W/0C2BuYXB5PC+5izFFh/nP0lc2Lf
6rELO9LZdnOhpL1ExFOq9H/B8tPQ84T3Sgb4nAifDabNt/zu6MmCGo5U8lwEFtGM
RoOaX4AS+909x00lYnmtwsDVWv9vBiJCXRsCAwEAAaOByTCBxjBgBgNVHR8EWTBX
MFWgU6BRhk9odHRwOi8vdHJ1c3RlZHNlcnZpY2VzLmludGVsLmNvbS9jb250ZW50
L0NSTC9TR1gvQXR0ZXN0YXRpb25SZXBvcnRTaWduaW5nQ0EuY3JsMB0GA1UdDgQW
BBR4Q3t2pn680K9+QjfrNXw7hwFRPDAfBgNVHSMEGDAWgBR4Q3t2pn680K9+Qjfr
NXw7hwFRPDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkq
hkiG9w0BAQsFAAOCAYEAeF8tYMXICvQqeXYQITkV2oLJsp6J4JAqJabHWxYJHGir
IEqucRiJSSx+HjIJEUVaj8E0QjEud6Y5lNmXlcjqRXaCPOqK0eGRz6hi+ripMtPZ
sFNaBwLQVV905SDjAzDzNIDnrcnXyB4gcDFCvwDFKKgLRjOB/WAqgscDUoGq5ZVi
zLUzTqiQPmULAQaB9c6Oti6snEFJiCQ67JLyW/E83/frzCmO5Ru6WjU4tmsmy8Ra
Ud4APK0wZTGtfPXU7w+IBdG5Ez0kE1qzxGQaL4gINJ1zMyleDnbuS8UicjJijvqA
152Sq049ESDz+1rRGc2NVEqh1KaGXmtXvqxXcTB+Ljy5Bw2ke0v8iGngFBPqCTVB
3op5KBG3RjbF6RRSzwzuWfL7QErNC8WEy5yDVARzTA5+xmBc388v9Dm21HGfcC8O
DD+gT9sSpssq0ascmvH49MOgjt1yoysLtdCtJW/9FZpoOypaHx0R+mJTLwPXVMrv
DaVzWh5aiEx+idkSGMnX
-----END CERTIFICATE-----