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

VLEK: Adding ASVK Support #43

Merged
merged 1 commit into from
Mar 4, 2024
Merged
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
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!")),
larrydewey marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
#[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