diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..b6f799d --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1 @@ +tab_spaces = 4 diff --git a/eif_build/src/main.rs b/eif_build/src/main.rs index 016bad9..1837a7f 100644 --- a/eif_build/src/main.rs +++ b/eif_build/src/main.rs @@ -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}; @@ -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> = 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()); @@ -286,7 +297,7 @@ pub fn build_eif( cmdline: &str, ramdisks: Vec<&str>, output_path: &str, - sign_info: Option, + sign_info: Option>, eif_info: EifIdentityInfo, arch: &str, ) { diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 8a9aa05..9707f0d 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -38,13 +38,17 @@ use std::path::Path; const DEFAULT_SECTIONS_COUNT: u16 = 3; -#[derive(Clone, Debug)] -pub struct SignEnclaveInfo { - pub signing_certificate: Vec, - pub private_key: Vec, +pub trait PcrSigner { + fn get_signing_certificate(&self) -> &[u8]; + fn sign(&self, pcr_info: &PcrInfo) -> Result, String>; } -impl SignEnclaveInfo { +pub struct PrivateKeyPcrSigner { + signing_certificate: Vec, + pcr_cose_sign1: PcrCoseSign1, +} + +impl PrivateKeyPcrSigner { pub fn new(cert_path: &str, key_path: &str) -> Result { let mut certificate_file = File::open(cert_path) .map_err(|err| format!("Could not open the certificate file: {:?}", err))?; @@ -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, String> { + self.pcr_cose_sign1.generate_signature(pcr_info) + } +} + +pub struct SignaturePcrSigner { + signing_certificate: Vec, + signature: Vec, +} + +impl SignaturePcrSigner { + pub fn new(cert_path: &str, sig_path: &str) -> Result { + 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, 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::(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, +} + +impl PcrCoseSign1 { + pub fn new(key_path: &str) -> Result { 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, 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::(&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(()) } } @@ -119,7 +231,7 @@ pub struct EifBuilder { kernel: File, cmdline: Vec, ramdisks: Vec, - sign_info: Option, + sign_info: Option>, signature: Option>, signature_size: u64, metadata: Vec, @@ -143,7 +255,7 @@ impl EifBuilder { pub fn new( kernel_path: &Path, cmdline: String, - sign_info: Option, + sign_info: Option>, hasher: T, flags: u16, eif_info: EifIdentityInfo, @@ -287,18 +399,9 @@ impl EifBuilder { register_value: Vec, ) -> 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::(&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, @@ -585,6 +688,19 @@ impl EifBuilder { measurements } + pub fn get_measurements(&mut self) -> BTreeMap { + 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 @@ -619,7 +735,7 @@ impl EifBuilder { } 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