Skip to content

Commit

Permalink
VLEK: Adding ASVK Support
Browse files Browse the repository at this point in the history
With the release of the VLEK, there is a separate
api endpoint for requesting VLEK signing keys which
are known as an AMD Signing VLEK Key (ASVK). This
adds logic to handle these special use-cases. As
the GHCB does not offer a unique UUID, assumptive
logic must be applied to when the file should be
named appropriately ASVK or ASK.

Signed-off-by: Larry Dewey <[email protected]>
  • Loading branch information
larrydewey committed Mar 1, 2024
1 parent c68b63b commit 766192f
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 35 deletions.
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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"
Expand Down
52 changes: 37 additions & 15 deletions src/certs.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand Down Expand Up @@ -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<u8>;

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<u8> = match encoding {
CertFormat::Pem => cert.to_pem()?,
CertFormat::Der => cert.to_der()?,
};

let cert_path: PathBuf = path.join(format!("{cert_str}.{encoding}"));
Expand Down Expand Up @@ -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.")?;

Expand All @@ -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...");
}
Expand Down
54 changes: 49 additions & 5 deletions src/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self, Self::Err> {
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,
Expand Down Expand Up @@ -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<Vec<X509>, anyhow::Error> {
pub fn request_ca_kds(
processor_model: ProcType,
endorser: &Endorsement,
) -> Result<Vec<X509>, 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()
);

Expand All @@ -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() {
Expand All @@ -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(())
Expand Down Expand Up @@ -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(())
}
Expand Down
34 changes: 24 additions & 10 deletions src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, vek_type)?,
find_cert_in_dir(&args.certs_dir, sign_type)?,
),
};

// Get a cert chain from directory
Expand Down Expand Up @@ -97,12 +99,18 @@ mod certificate_chain {
match (&ark, &ask).verify() {
Ok(()) => {
if !quiet {
println!("The AMD ASK was signed by the AMD ARK!");
println!(
"The AMD {} was signed by the AMD ARK!",
sign_type.to_uppercase()
);
}
}
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 {} was not signed by the AMD ARK!",
sign_type.to_uppercase()
))
}
_ => return Err(anyhow::anyhow!("Failed to verify ASK certificate: {:?}", e)),
},
Expand All @@ -111,13 +119,19 @@ mod certificate_chain {
match (&ask, &vek).verify() {
Ok(()) => {
if !quiet {
println!("The {vek_type} was signed by the AMD ASK!");
println!(
"The {} was signed by the AMD {}!",
vek_type.to_uppercase(),
sign_type.to_uppercase()
);
}
}
Err(e) => match e.kind() {
ErrorKind::Other => {
return Err(anyhow::anyhow!(
"The {vek_type} was not signed by the AMD ASK!"
"The {} was not signed by the AMD {}!",
vek_type.to_uppercase(),
sign_type.to_uppercase(),
))
}
_ => return Err(anyhow::anyhow!("Failed to verify VEK certificate: {:?}", e)),
Expand Down

0 comments on commit 766192f

Please sign in to comment.