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

Replace struct SignEnclaveInfo with trait PcrSigner #30

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
tab_spaces = 4
25 changes: 18 additions & 7 deletions eif_build/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ use std::path::Path;

use aws_nitro_enclaves_image_format::defs::{EifBuildInfo, EifIdentityInfo, EIF_HDR_ARCH_ARM64};
use aws_nitro_enclaves_image_format::utils::identity::parse_custom_metadata;
use aws_nitro_enclaves_image_format::utils::{PcrSigner, PrivateKeyPcrSigner, SignaturePcrSigner};
use aws_nitro_enclaves_image_format::{
generate_build_info,
utils::{get_pcrs, EifBuilder, SignEnclaveInfo},
utils::{get_pcrs, EifBuilder},
};
use chrono::offset::Utc;
use clap::{App, Arg, ValueSource};
Expand Down Expand Up @@ -174,12 +175,22 @@ fn main() {

let private_key = matches.value_of("private-key");

let sign_info = match (signing_certificate, private_key) {
(None, None) => None,
(Some(cert_path), Some(key_path)) => {
Some(SignEnclaveInfo::new(cert_path, key_path).expect("Could not read signing info"))
let signature = matches.value_of("signature");

let sign_info: Option<Box<dyn PcrSigner>> = match (signing_certificate, private_key, signature)
{
(None, None, None) => None,
(Some(cert_path), Some(key_path), None) => Some(Box::new(
PrivateKeyPcrSigner::new(cert_path, key_path)
.expect("Could not read certificate and private key"),
)),
(Some(cert_path), None, Some(sig_path)) => Some(Box::new(
SignaturePcrSigner::new(cert_path, sig_path)
.expect("Could not read certificate and signature"),
)),
_ => {
panic!("Both signing-certificate and private-key/signature parameters must be provided")
}
_ => panic!("Both signing-certificate and private-key parameters must be provided"),
};

let img_name = matches.value_of("image_name").map(|val| val.to_string());
Expand Down Expand Up @@ -286,7 +297,7 @@ pub fn build_eif(
cmdline: &str,
ramdisks: Vec<&str>,
output_path: &str,
sign_info: Option<SignEnclaveInfo>,
sign_info: Option<Box<dyn PcrSigner>>,
eif_info: EifIdentityInfo,
arch: &str,
) {
Expand Down
162 changes: 139 additions & 23 deletions src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,17 @@ use std::path::Path;

const DEFAULT_SECTIONS_COUNT: u16 = 3;

#[derive(Clone, Debug)]
pub struct SignEnclaveInfo {
pub signing_certificate: Vec<u8>,
pub private_key: Vec<u8>,
pub trait PcrSigner {
fn get_signing_certificate(&self) -> &[u8];
fn sign(&self, pcr_info: &PcrInfo) -> Result<Vec<u8>, String>;
}

impl SignEnclaveInfo {
pub struct PrivateKeyPcrSigner {
signing_certificate: Vec<u8>,
pcr_cose_sign1: PcrCoseSign1,
}

impl PrivateKeyPcrSigner {
pub fn new(cert_path: &str, key_path: &str) -> Result<Self, String> {
let mut certificate_file = File::open(cert_path)
.map_err(|err| format!("Could not open the certificate file: {:?}", err))?;
Expand All @@ -53,17 +57,125 @@ impl SignEnclaveInfo {
.read_to_end(&mut signing_certificate)
.map_err(|err| format!("Could not read the certificate file: {:?}", err))?;

let pcr_cose_sign1 = PcrCoseSign1::new(key_path)?;
Ok(Self {
signing_certificate,
pcr_cose_sign1,
})
}
}

impl PcrSigner for PrivateKeyPcrSigner {
fn get_signing_certificate(&self) -> &[u8] {
&self.signing_certificate
}

fn sign(&self, pcr_info: &PcrInfo) -> Result<Vec<u8>, String> {
self.pcr_cose_sign1.generate_signature(pcr_info)
}
}

pub struct SignaturePcrSigner {
signing_certificate: Vec<u8>,
signature: Vec<u8>,
}

impl SignaturePcrSigner {
pub fn new(cert_path: &str, sig_path: &str) -> Result<Self, String> {
let mut sig_file = File::open(sig_path)
.map_err(|err| format!("Could not open the signature file: {:?}", err))?;
let mut sig_hex = String::new();
sig_file
.read_to_string(&mut sig_hex)
.map_err(|err| format!("Could not read the signature file: {:?}", err))?;
let signature = hex::decode(sig_hex.trim())
.map_err(|err| format!("Could not decode signature: {:?}", err))?;

let mut certificate_file = File::open(cert_path)
.map_err(|err| format!("Could not open the certificate file: {:?}", err))?;
let mut signing_certificate = Vec::new();
certificate_file
.read_to_end(&mut signing_certificate)
.map_err(|err| format!("Could not read the certificate file: {:?}", err))?;

Ok(Self {
signing_certificate,
signature,
})
}
}

impl PcrSigner for SignaturePcrSigner {
fn get_signing_certificate(&self) -> &[u8] {
&self.signing_certificate
}

fn sign(&self, pcr_info: &PcrInfo) -> Result<Vec<u8>, String> {
let expected =
to_vec(&pcr_info).map_err(|err| format!("Could not serialize PCR info: {:?}", err))?;

let signature = CoseSign1::from_bytes(&self.signature[..])
.map_err(|err| format!("Could not deserialize the signature: {:?}", err))?;
let cert = openssl::x509::X509::from_pem(&self.signing_certificate[..])
.map_err(|_| "Could not deserialize the signing certificate".to_string())?;
let public_key = cert
.public_key()
.map_err(|_| "Could not get the public key from the signing certificate".to_string())?;

let payload = signature
.get_payload::<Openssl>(Some(public_key.as_ref()))
.map_err(|_| "Could not get the payload from the signature".to_string())?;

if payload.eq(&expected) {
Ok(self.signature.clone())
} else {
Err("Signature does not match the PCR info".to_string())
}
}
}

pub struct PcrCoseSign1 {
private_key: Vec<u8>,
}

impl PcrCoseSign1 {
pub fn new(key_path: &str) -> Result<Self, String> {
let mut key_file = File::open(key_path)
.map_err(|err| format!("Could not open the key file: {:?}", err))?;
let mut private_key = Vec::new();
key_file
.read_to_end(&mut private_key)
.map_err(|err| format!("Could not read the key file: {:?}", err))?;
Ok(Self { private_key })
}

Ok(SignEnclaveInfo {
signing_certificate,
private_key,
})
pub fn generate_signature(&self, pcr_info: &PcrInfo) -> Result<Vec<u8>, String> {
let payload =
to_vec(&pcr_info).map_err(|err| format!("Could not serialize PCR info: {:?}", err))?;

let private_key = PKey::private_key_from_pem(&self.private_key)
.expect("Could not deserialize the PEM-formatted private key");

let signature =
CoseSign1::new::<Openssl>(&payload, &HeaderMap::new(), private_key.as_ref())
.unwrap()
.as_bytes(false)
.unwrap();

Ok(signature)
}

pub fn write_signature(
&self,
pcr_info: &PcrInfo,
output_file: &mut File,
) -> Result<(), String> {
let signature = self.generate_signature(pcr_info)?;
let sig_hex = hex::encode(&signature);
output_file
.write_all(sig_hex.as_bytes())
.map_err(|err| format!("Could not write signature to file: {err:?}"))?;
Ok(())
}
}

Expand Down Expand Up @@ -119,7 +231,7 @@ pub struct EifBuilder<T: Digest + Debug + Write + Clone> {
kernel: File,
cmdline: Vec<u8>,
ramdisks: Vec<File>,
sign_info: Option<SignEnclaveInfo>,
sign_info: Option<Box<dyn PcrSigner>>,
signature: Option<Vec<u8>>,
signature_size: u64,
metadata: Vec<u8>,
Expand All @@ -143,7 +255,7 @@ impl<T: Digest + Debug + Write + Clone> EifBuilder<T> {
pub fn new(
kernel_path: &Path,
cmdline: String,
sign_info: Option<SignEnclaveInfo>,
sign_info: Option<Box<dyn PcrSigner>>,
hasher: T,
flags: u16,
eif_info: EifIdentityInfo,
Expand Down Expand Up @@ -287,18 +399,9 @@ impl<T: Digest + Debug + Write + Clone> EifBuilder<T> {
register_value: Vec<u8>,
) -> PcrSignature {
let sign_info = self.sign_info.as_ref().unwrap();
let signing_certificate = sign_info.signing_certificate.clone();
let signing_certificate = sign_info.get_signing_certificate().to_vec();
let pcr_info = PcrInfo::new(register_index, register_value);

let payload = to_vec(&pcr_info).expect("Could not serialize PCR info");
let private_key = PKey::private_key_from_pem(&sign_info.private_key)
.expect("Could not deserialize the PEM-formatted private key");

let signature =
CoseSign1::new::<Openssl>(&payload, &HeaderMap::new(), private_key.as_ref())
.unwrap()
.as_bytes(false)
.unwrap();
let signature = sign_info.sign(&pcr_info).expect("Could not sign the PCR");

PcrSignature {
signing_certificate,
Expand Down Expand Up @@ -585,6 +688,19 @@ impl<T: Digest + Debug + Write + Clone> EifBuilder<T> {
measurements
}

pub fn get_measurements(&mut self) -> BTreeMap<String, String> {
self.measure();
get_pcrs(
&mut self.image_hasher,
&mut self.bootstrap_hasher,
&mut self.customer_app_hasher,
&mut self.certificate_hasher,
self.hasher_template.clone(),
self.sign_info.is_some(),
)
.expect("Failed to get measurements")
}

pub fn measure(&mut self) {
let mut kernel_file = &self.kernel;
kernel_file
Expand Down Expand Up @@ -619,7 +735,7 @@ impl<T: Digest + Debug + Write + Clone> EifBuilder<T> {
}

if let Some(sign_info) = self.sign_info.as_ref() {
let cert = openssl::x509::X509::from_pem(&sign_info.signing_certificate[..]).unwrap();
let cert = openssl::x509::X509::from_pem(&sign_info.get_signing_certificate()).unwrap();
let cert_der = cert.to_der().unwrap();
// This is equivalent to extend(cert.digest(sha384)), since hasher is going to
// hash the DER certificate (cert.digest()) and then tpm_extend_finalize_reset
Expand Down