Skip to content

Commit

Permalink
Consolidate certificate generation
Browse files Browse the repository at this point in the history
DRY up some of the licate logic for generating x509 certificates in the
`build.rs` of the `mc-attest-verify` crate.
  • Loading branch information
nick-mobilecoin committed Aug 14, 2023
1 parent d3485e1 commit 429059a
Showing 1 changed file with 159 additions and 117 deletions.
276 changes: 159 additions & 117 deletions attest/verifier/build.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright (c) 2018-2022 The MobileCoin Foundation

use chrono::{offset::Utc, Datelike, Duration, Timelike};
use chrono::{offset::Utc, DateTime, Datelike, Duration, Timelike};
use core::{ptr::null_mut, slice::from_raw_parts_mut};
use mbedtls::{
hash::Type as HashType,
Expand Down Expand Up @@ -98,6 +98,43 @@ const CHAIN_FILENAME: &str = "chain.pem";

const GENERATED_FILENAMES: &[&str] = &[ROOT_ANCHOR_FILENAME, SIGNER_KEY_FILENAME, CHAIN_FILENAME];

/// An issuer of a certificate. This is the one that signs a certificate.
struct Issuer<'a> {
/// The key used to sign issued certificates
key: SigningKey,
/// A description of the issuer, in the form of a Distinguished Name (DN)
description: &'a str,
}

/// The subject of a certificate.
struct Subject<'a> {
/// The serial number of the certificate. Since this is only used in sim
/// environments we can limit this to 1 byte.
serial: u8,
/// The description of the subject, in the form of a Distinguished Name (DN)
description: &'a str,
/// Is this a CA certificate?
is_ca: bool,
/// The validity times of the certificate
validity: Validity,
}

/// Validity times
#[derive(Debug, Clone)]
struct Validity {
not_before: DateTime<Utc>,
not_after: DateTime<Utc>,
}

impl Validity {
/// Returns mbedtls times, `(not_before, not_after)`.
fn to_mbedtls_times(&self) -> (Time, Time) {
let not_before = to_mbdetls_time(self.not_before);
let not_after = to_mbdetls_time(self.not_after);
(not_before, not_after)
}
}

fn generate_sim_files(data_path: impl AsRef<Path>) {
let data_path = data_path.as_ref();

Expand All @@ -108,125 +145,37 @@ fn generate_sim_files(data_path: impl AsRef<Path>) {
const ROOT_SUBJECT: &str = "C=US,ST=CA,L=Santa Clara,O=Intel Corporation,CN=Simulation Intel SGX Attestation Report Signing CA\0";
const SIGNER_SUBJECT: &str = "C=US,ST=CA,L=Santa Clara,O=Intel Corporation,CN=Simulation Intel SGX Attestation Report Signer\0";

let mut serial: [u8; 1] = [1u8];

let now = Utc::now();
let start_time = now - Duration::hours(1);
let end_time = now + Duration::days(30);

let mut csprng = RngForMbedTls {};

// ROOT CERTIFICATE
let private_key = SigningKey::random(&mut *RNG.lock().expect("mutex poisoned"));
let der_private_key = private_key
.to_pkcs8_der()
.expect("Could not export privkey to DER");
let mut root_subject_key = Pk::from_private_key(der_private_key.as_bytes(), None)
.expect("Could not parse privkey from DER");
let mut root_issuer_key = Pk::from_private_key(der_private_key.as_bytes(), None)
.expect("Could not parse privkey from DER");
// Intermediate authority will be signed by this key.
let mut signer_issuer_key = Pk::from_private_key(der_private_key.as_bytes(), None)
.expect("Could not parse privkey from DER");

let root_not_before = Time::new(
u16::try_from(start_time.year()).expect("Year not a u16"),
u8::try_from(start_time.month()).expect("Month not a u8"),
u8::try_from(start_time.day()).expect("Day not a u8"),
u8::try_from(start_time.hour()).expect("Hour not a u8"),
u8::try_from(start_time.minute()).expect("Minute not a u8"),
u8::try_from(start_time.second()).expect("Second not a u8"),
)
.expect("Could not create a not_before time");
let root_not_after = Time::new(
u16::try_from(end_time.year()).expect("Year not a u16"),
u8::try_from(end_time.month()).expect("Month not a u8"),
u8::try_from(end_time.day()).expect("Day not a u8"),
u8::try_from(end_time.hour()).expect("Hour not a u8"),
u8::try_from(end_time.minute()).expect("Minute not a u8"),
u8::try_from(end_time.second()).expect("Second not a u8"),
)
.expect("Could not create a not_after time");

let mut root_builder = Builder::new();
let root_cert_pem = root_builder
.subject_with_nul(ROOT_SUBJECT)
.expect("Could not set subject")
.issuer_with_nul(ROOT_SUBJECT)
.expect("Could not set issuer")
.basic_constraints(true, Some(0))
.expect("Could not set basic constraints")
.key_usage(KeyUsage::CRL_SIGN | KeyUsage::KEY_CERT_SIGN)
.expect("Could not set key usage")
.validity(root_not_before, root_not_after)
.expect("Could not set time validity range")
.serial(&serial[..])
.expect("Could not set serial number")
.subject_key(&mut root_subject_key)
.issuer_key(&mut root_issuer_key)
.signature_hash(HashType::Sha256)
.write_pem_string(&mut csprng)
.expect("Could not create PEM string of certificate");
let not_before = now - Duration::hours(1);
let not_after = not_before + Duration::days(30);
let validity = Validity {
not_before,
not_after,
};

let subject = Subject {
serial: 1,
description: ROOT_SUBJECT,
is_ca: true,
validity: validity.clone(),
};
let (root_cert_pem, root_key) = create_certificate(None, &subject);
write(root_anchor_path, &root_cert_pem).expect("Unable to write root anchor");

// IAS SIGNER CERT
let signer_key = SigningKey::random(&mut *RNG.lock().expect("mutex poisoned"));
let der_signer_key = signer_key
.to_pkcs8_der()
.expect("Could not export privkey to DER");
write(
signer_key_path,
der_signer_key
.to_pem("PRIVATE KEY", LineEnding::LF)
.expect("Could not encode signer key to PEM"),
)
.expect("Could not write signer key PEM to file");

let mut signer_subject_key = Pk::from_private_key(der_signer_key.as_bytes(), None)
.expect("Could not parse privkey from DER");

let signer_not_before = Time::new(
u16::try_from(start_time.year()).expect("Year not a u16"),
u8::try_from(start_time.month()).expect("Month not a u8"),
u8::try_from(start_time.day()).expect("Day not a u8"),
u8::try_from(start_time.hour()).expect("Hour not a u8"),
u8::try_from(start_time.minute()).expect("Minute not a u8"),
u8::try_from(start_time.second()).expect("Second not a u8"),
)
.expect("Could not create a not_before time");
let signer_not_after = Time::new(
u16::try_from(end_time.year()).expect("Year not a u16"),
u8::try_from(end_time.month()).expect("Month not a u8"),
u8::try_from(end_time.day()).expect("Day not a u8"),
u8::try_from(end_time.hour()).expect("Hour not a u8"),
u8::try_from(end_time.minute()).expect("Minute not a u8"),
u8::try_from(end_time.second()).expect("Second not a u8"),
)
.expect("Could not create a not_after time");

serial[0] += 1;

let mut builder = Builder::new();
let signer_cert_pem = builder
.subject_with_nul(SIGNER_SUBJECT)
.expect("Could not set subject")
.issuer_with_nul(ROOT_SUBJECT)
.expect("Could not set issuer")
.basic_constraints(false, None)
.expect("Could not set basic constraints")
.key_usage(KeyUsage::DIGITAL_SIGNATURE | KeyUsage::NON_REPUDIATION)
.expect("Could not set key usage")
.validity(signer_not_before, signer_not_after)
.expect("Could not set time validity range")
.serial(&serial[..])
.expect("Could not set serial number")
.subject_key(&mut signer_subject_key)
.issuer_key(&mut signer_issuer_key)
.signature_hash(HashType::Sha256)
.write_pem_string(&mut csprng)
.expect("Could not create PEM string of certificate");

write(chain_path, root_cert_pem + &signer_cert_pem).expect("Unable to write cert chain");
let issuer = Issuer {
key: root_key,
description: ROOT_SUBJECT,
};
let subject = Subject {
serial: 2,
description: SIGNER_SUBJECT,
is_ca: false,
validity,
};
let (signer_cert_pem, signer_key) = create_certificate(issuer, &subject);
write(chain_path, signer_cert_pem + &root_cert_pem).expect("Unable to write cert chain");

write_signing_key(signer_key_path, &signer_key);
}

/// Returns true of the build script should generate the sim files
Expand Down Expand Up @@ -265,3 +214,96 @@ fn should_generating_sim_files(data_path: impl AsRef<Path>) -> bool {
// - The third build cargo should skip this build script.
env::var("MC_SEED").is_ok()
}

/// Create a certificate
///
/// # Aruments:
/// * `issuer` - The issuer of the certificate. If `None` then the certificate
/// is self signed.
/// * `subject` - The subject of the certificate
///
/// # Returns:
/// The PEM encoded certificate and the signing key of the certificate
fn create_certificate<'a>(
issuer: impl Into<Option<Issuer<'a>>>,
subject: &Subject,
) -> (String, SigningKey) {
let subject_signing_key = SigningKey::random(&mut *RNG.lock().expect("mutex poisoned"));

// No issuer means it's self signed
let issuer = issuer.into().unwrap_or_else(|| Issuer {
key: subject_signing_key.clone(),
description: subject.description,
});

let mut issuer_key = to_mbedtls_key_context(&issuer.key);
let mut subject_key = to_mbedtls_key_context(&subject_signing_key);
let (not_before, not_after) = subject.validity.to_mbedtls_times();

let key_usage = if subject.is_ca {
KeyUsage::CRL_SIGN | KeyUsage::KEY_CERT_SIGN
} else {
KeyUsage::DIGITAL_SIGNATURE | KeyUsage::NON_REPUDIATION
};

let mut csprng = RngForMbedTls {};
let mut builder = Builder::new();
builder
.subject_with_nul(subject.description)
.expect("Could not set subject")
.issuer_with_nul(issuer.description)
.expect("Could not set issuer")
.basic_constraints(subject.is_ca, Some(0))
.expect("Could not set basic constraints")
.key_usage(key_usage)
.expect("Could not set key usage")
.validity(not_before, not_after)
.expect("Could not set time validity range")
.serial(&[subject.serial])
.expect("Could not set serial number")
.subject_key(&mut subject_key)
.issuer_key(&mut issuer_key)
.signature_hash(HashType::Sha256);
let cert_pem = builder
.write_pem_string(&mut csprng)
.expect("Could not create PEM string of certificate");
(cert_pem, subject_signing_key)
}

/// Convert the provided `key` into an mbedtls key context.
///
/// The mbedtls key context contains both the public and private keys.
fn to_mbedtls_key_context(key: &SigningKey) -> Pk {
let der_key = key
.to_pkcs8_der()
.expect("Could not export private key to DER");
let key_context = Pk::from_private_key(der_key.as_bytes(), None)
.expect("Could not parse private key from DER");
key_context
}

fn to_mbdetls_time(time: DateTime<Utc>) -> Time {
Time::new(
u16::try_from(time.year()).expect("Year not a u16"),
u8::try_from(time.month()).expect("Month not a u8"),
u8::try_from(time.day()).expect("Day not a u8"),
u8::try_from(time.hour()).expect("Hour not a u8"),
u8::try_from(time.minute()).expect("Minute not a u8"),
u8::try_from(time.second()).expect("Second not a u8"),
)
.expect("Could not create a valid mbedtls time")
}

fn write_signing_key(signer_key_path: impl AsRef<Path>, signer_key: &SigningKey) {
let signer_key_path = signer_key_path.as_ref();
let der_signer_key = signer_key
.to_pkcs8_der()
.expect("Could not export privkey to DER");
write(
signer_key_path,
der_signer_key
.to_pem("PRIVATE KEY", LineEnding::LF)
.expect("Could not encode signer key to PEM"),
)
.expect("Could not write signer key PEM to file");
}

0 comments on commit 429059a

Please sign in to comment.