diff --git a/Cargo.lock b/Cargo.lock index 4a3e9ca..9148765 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1346,9 +1346,9 @@ dependencies = [ [[package]] name = "sev" -version = "3.0.0" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "210c2ddcae6f3ca89e9f78449479e4e2f0d8b9ea68e4415e4d5daeb3e6fcedb8" +checksum = "b2890179f8ef689340f441ba05f0b268bc14f672ae4b36d629cc2266d0d747ab" dependencies = [ "base64 0.21.7", "bincode", @@ -1380,7 +1380,7 @@ dependencies = [ [[package]] name = "snpguest" -version = "0.3.1" +version = "0.4.0" dependencies = [ "anyhow", "asn1-rs", diff --git a/Cargo.toml b/Cargo.toml index b5b5de9..5fb9602 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "snpguest" -version = "0.3.1" +version = "0.4.0" authors = ["The VirTEE Project Developers"] edition = "2021" license = "Apache-2.0" @@ -22,7 +22,7 @@ hyperv = ["tss-esapi"] structopt = "0.3" env_logger = "0.10.0" anyhow = "1.0.69" -sev = { version = "^3.0.0", default-features = false, features = ['openssl','snp']} +sev = { version = "^3.1.1", default-features = false, features = ['openssl','snp']} nix = "^0.23" serde = { version = "1.0", features = ["derive"] } bincode = "^1.2.1" diff --git a/src/certs.rs b/src/certs.rs index f219b71..4a9212c 100644 --- a/src/certs.rs +++ b/src/certs.rs @@ -1,6 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 // This file contains code related to managing certificates. It defines a structure for managing certificate paths (`CertPaths`) and functions for obtaining extended certificates from the AMD Secure Processor. +use crate::fetch::Endorsement; + use super::*; use std::{ @@ -120,19 +122,27 @@ pub fn write_cert( cert_type: &CertType, data: &[u8], encoding: CertFormat, + endorser: &Endorsement, ) -> Result<()> { // Get cert type into str let cert: Certificate = Certificate::from_bytes(data)?; - let cert_str: String = cert_type.to_string(); - let bytes: Vec; - match encoding { - CertFormat::Pem => { - bytes = cert.to_pem()?; - } - CertFormat::Der => { - bytes = cert.to_der()?; - } + let cert_str: String = match (cert_type, endorser) { + (CertType::ASK, Endorsement::Vlek) => "asvk".to_string(), + (_, _) => match cert_type { + CertType::Empty => "empty".to_string(), + CertType::ARK => "ark".to_string(), + CertType::ASK => "ask".to_string(), + CertType::VCEK => "vcek".to_string(), + CertType::VLEK => "vlek".to_string(), + CertType::CRL => "crl".to_string(), + CertType::OTHER(uuid) => format!("other-{uuid}"), + }, + }; + + let bytes: Vec = match encoding { + CertFormat::Pem => cert.to_pem()?, + CertFormat::Der => cert.to_der()?, }; let cert_path: PathBuf = path.join(format!("{cert_str}.{encoding}")); @@ -169,10 +179,10 @@ pub fn get_ext_certs(args: CertificatesArgs) -> Result<()> { let mut sev_fw: Firmware = Firmware::open().context("failed to open SEV firmware device.")?; // Generate random request data - let request_data = report::create_random_request(); + let request_data: [u8; 64] = report::create_random_request(); // Request extended attestation report - let (_, certificates) = sev_fw + let (_, mut certificates) = sev_fw .get_ext_report(None, Some(request_data), None) .context("Failed to get extended report.")?; @@ -182,10 +192,22 @@ pub fn get_ext_certs(args: CertificatesArgs) -> Result<()> { }; // If certificates are present, write certs into directory - if let Some(ref certificates) = certificates { - for cert in certificates.iter() { - write_cert(&args.certs_dir, &cert.cert_type, &cert.data, args.encoding)?; - } + if let Some(ref mut certificates) = certificates { + // Unless VLEK is encountered, assume VCEK style endorsement with ASK. + let mut endorsement: Endorsement = Endorsement::Vcek; + + certificates.iter().try_for_each(|cert| { + if cert.cert_type == CertType::VLEK { + endorsement = Endorsement::Vlek; + } + write_cert( + &args.certs_dir, + &cert.cert_type, + &cert.data, + args.encoding, + &endorsement, + ) + })?; } else { eprintln!("No certificates were loaded by the host..."); } diff --git a/src/fetch.rs b/src/fetch.rs index 3a69529..a1143eb 100644 --- a/src/fetch.rs +++ b/src/fetch.rs @@ -22,6 +22,32 @@ pub enum FetchCmd { Vcek(vcek::Args), } +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Endorsement { + Vcek, + Vlek, +} + +impl fmt::Display for Endorsement { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Endorsement::Vcek => write!(f, "VCEK"), + Endorsement::Vlek => write!(f, "VLEK"), + } + } +} + +impl FromStr for Endorsement { + type Err = anyhow::Error; + + fn from_str(s: &str) -> std::prelude::v1::Result { + match s.to_lowercase().as_str() { + "vcek" => Ok(Self::Vcek), + "vlek" => Ok(Self::Vlek), + _ => Err(anyhow::anyhow!("Endorsement type not found!")), + } + } +} #[derive(Debug, Clone)] pub enum ProcType { Milan, @@ -88,17 +114,27 @@ mod cert_authority { #[structopt(help = "Directory to store the certificates in.")] pub certs_dir: PathBuf, + + #[structopt( + long, + short, + default_value = "vcek", + help = "Specify to pull the VLEK certificate chain instead of VCEK." + )] + pub endorser: Endorsement, } // Function to build kds request for ca chain and return a vector with the 2 certs (ASK & ARK) - pub fn request_ca_kds(processor_model: ProcType) -> Result, anyhow::Error> { + pub fn request_ca_kds( + processor_model: ProcType, + endorser: &Endorsement, + ) -> Result, anyhow::Error> { const KDS_CERT_SITE: &str = "https://kdsintf.amd.com"; - const KDS_VCEK: &str = "/vcek/v1"; const KDS_CERT_CHAIN: &str = "cert_chain"; // Should make -> https://kdsintf.amd.com/vcek/v1/{SEV_PROD_NAME}/cert_chain let url: String = format!( - "{KDS_CERT_SITE}{KDS_VCEK}/{}/{KDS_CERT_CHAIN}", + "{KDS_CERT_SITE}{endorser}/v1/{}/{KDS_CERT_CHAIN}", processor_model.to_kds_url() ); @@ -123,7 +159,7 @@ mod cert_authority { // Fetch the ca from the kds and write it into the certs directory pub fn fetch_ca(args: Args) -> Result<()> { // Get certs from kds - let certificates = request_ca_kds(args.processor_model)?; + let certificates = request_ca_kds(args.processor_model, &args.endorser)?; // Create certs directory if missing if !args.certs_dir.exists() { @@ -138,12 +174,14 @@ mod cert_authority { &CertType::ARK, &ark_cert.to_pem()?, args.encoding, + &args.endorser, )?; write_cert( &args.certs_dir, &CertType::ASK, &ask_cert.to_pem()?, args.encoding, + &args.endorser, )?; Ok(()) @@ -223,7 +261,13 @@ mod vcek { fs::create_dir(&args.certs_dir).context("Could not create certs folder")?; } - write_cert(&args.certs_dir, &CertType::VCEK, &vcek, args.encoding)?; + write_cert( + &args.certs_dir, + &CertType::VCEK, + &vcek, + args.encoding, + &Endorsement::Vcek, + )?; Ok(()) } diff --git a/src/verify.rs b/src/verify.rs index 2cfdd60..7357e3c 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -54,14 +54,16 @@ mod certificate_chain { // Function to validate certificate chain pub fn validate_cc(args: Args, quiet: bool) -> Result<()> { let ark_path = find_cert_in_dir(&args.certs_dir, "ark")?; - let ask_path = find_cert_in_dir(&args.certs_dir, "ask")?; - let mut vek_type: &str = "vcek"; - let vek_path = match find_cert_in_dir(&args.certs_dir, "vlek") { + let (mut vek_type, mut sign_type): (&str, &str) = ("vcek", "ask"); + let (vek_path, ask_path) = match find_cert_in_dir(&args.certs_dir, "vlek") { Ok(vlek_path) => { - vek_type = "vlek"; - vlek_path + (vek_type, sign_type) = ("vlek", "asvk"); + (vlek_path, find_cert_in_dir(&args.certs_dir, sign_type)?) } - Err(_) => find_cert_in_dir(&args.certs_dir, "vcek")?, + Err(_) => ( + find_cert_in_dir(&args.certs_dir, "vcek")?, + find_cert_in_dir(&args.certs_dir, "ask")?, + ), }; // Get a cert chain from directory @@ -97,12 +99,14 @@ mod certificate_chain { match (&ark, &ask).verify() { Ok(()) => { if !quiet { - println!("The AMD ASK was signed by the AMD ARK!"); + println!("The AMD {sign_type} was signed by the AMD ARK!"); } } Err(e) => match e.kind() { ErrorKind::Other => { - return Err(anyhow::anyhow!("The AMD ASK ws not signed by the AMD ARK!")) + return Err(anyhow::anyhow!( + "The AMD {sign_type} was not signed by the AMD ARK!" + )) } _ => return Err(anyhow::anyhow!("Failed to verify ASK certificate: {:?}", e)), }, @@ -111,13 +115,13 @@ mod certificate_chain { match (&ask, &vek).verify() { Ok(()) => { if !quiet { - println!("The {vek_type} was signed by the AMD ASK!"); + println!("The {vek_type} was signed by the AMD {sign_type}!"); } } Err(e) => match e.kind() { ErrorKind::Other => { return Err(anyhow::anyhow!( - "The {vek_type} was not signed by the AMD ASK!" + "The {vek_type} was not signed by the AMD {sign_type}!" )) } _ => return Err(anyhow::anyhow!("Failed to verify VEK certificate: {:?}", e)),