use super::*;
use std::{
fs,
fs::File,
io::{BufWriter, Read, Write},
path::{Path, PathBuf},
str::FromStr,
};
use certs::{identify_cert, CertFormat};
use rand::{thread_rng, RngCore};
use sev::firmware::{
guest::{AttestationReport, Firmware},
host::CertType,
};
pub fn read_report(att_report_path: PathBuf) -> Result<AttestationReport, anyhow::Error> {
let attestation_file = fs::File::open(att_report_path)?;
let attestation_report = bincode::deserialize_from(attestation_file)
.context("Could not parse attestation report.")?;
Ok(attestation_report)
}
pub fn create_random_request() -> [u8; 64] {
let mut data = [0u8; 64];
thread_rng().fill_bytes(&mut data);
return data;
}
pub fn write_hex<W: Write>(file: &mut BufWriter<W>, data: &[u8]) -> Result<()> {
let mut line_counter = 0;
for val in data {
if line_counter.eq(&16) {
write!(file, "\n").context("Failed to write data to file")?;
line_counter = 0;
}
write!(file, "{:02x}", val).context("Failed to write data to file")?;
line_counter += 1;
}
Ok(())
}
#[derive(StructOpt)]
pub struct ReportArgs {
#[structopt(
long = "extended",
short,
help = "Request an extended report instead of the regular report"
)]
pub extended_report: bool,
#[structopt(
long = "random",
short,
help = "Generate a random request file for the attestation report. Defaults to ./random-request-file.txt"
)]
pub random: bool,
#[structopt(
long = "vmpl",
short,
help = "VMPL level the Guest is running on. Defaults to 1"
)]
pub vmpl: Option<u32>,
#[structopt(
long = "request",
help = "Path pointing to were the request-file location. If provided with random flag, then a random request file will be generated at that location"
)]
pub request_file: Option<PathBuf>,
#[structopt(
long = "att-report",
short,
help = "File to write the attestation report to. Defaults to ./attestation_report.bin"
)]
pub att_report_path: Option<PathBuf>,
#[structopt(
long = "certs",
short,
help = "Directory to store certificates. Defaults to ./certs"
)]
pub certs_path: Option<PathBuf>,
}
pub fn get_report(args: ReportArgs) -> Result<()> {
let mut sev_fw: Firmware = Firmware::open().context("failed to open SEV firmware device.")?;
let request_data = match args.request_file {
Some(path) => {
let request_data = if args.random {
let request_buf = create_random_request();
let file = File::create(path)
.context("Failed to create a random request file for report request")?;
write_hex(&mut BufWriter::new(file), &request_buf)
.context("Failed to write request data in request file")?;
request_buf
} else {
let mut request_file =
File::open(path).context("Could not open the report request file.")?;
let mut request_buf: [u8; 64] = [0; 64];
request_file
.read(&mut request_buf)
.context("Could not read report request file.")?;
request_buf
};
request_data
}
None => {
let request_data = if args.random {
let request_buf = create_random_request();
let file = File::create("./random-request-file.txt")
.context("Failed to create a random request file for report request")?;
write_hex(&mut BufWriter::new(file), &request_buf)
.context("Failed to write request data in request file")?;
request_buf
} else {
return Err(anyhow::anyhow!("Please provide a request-file or use --random flag to create one in order request attestation report."));
};
request_data
}
};
if !args.extended_report {
let att_report_path = match args.att_report_path {
Some(path) => path,
None => PathBuf::from_str("./attestation_report.bin")
.context("unable to create default path")?,
};
let att_report = sev_fw
.get_report(None, Some(request_data), args.vmpl)
.context("Failed to get report.")?;
let mut attestation_file =
File::create(att_report_path).context("Failed to create Attestation Report File")?;
bincode::serialize_into(&mut attestation_file, &att_report)
.context("Could not serialize attestation report into file.")?;
} else {
let att_report_path = match args.att_report_path {
Some(path) => path,
None => PathBuf::from_str("./attestation_report.bin")
.context("unable to create attestation report default path")?,
};
let (att_report, certificates) = sev_fw
.get_ext_report(None, Some(request_data), args.vmpl)
.context("Failed to get extended report.")?;
let mut attestation_file =
File::create(att_report_path).context("Failed to create Attestation Report File")?;
bincode::serialize_into(&mut attestation_file, &att_report)
.context("Could not serialize attestation report into file.")?;
if certificates.is_empty() {
return Err(anyhow::anyhow!(
"The certificate chain is empty! Certificates probably not loaded by the host."
));
}
if args.certs_path.is_none() {
if !Path::new("./certs").is_dir() {
fs::create_dir("./certs").context("Could not create certs folder")?;
}
}
for cert in certificates.iter() {
let mut path = match args.certs_path.clone() {
Some(path) => path,
None => PathBuf::from("./certs"),
};
let mut f = match cert.cert_type {
CertType::ARK => {
match identify_cert(&cert.data[0..27]) {
CertFormat::PEM => path.push("ark.pem"),
CertFormat::DER => path.push("ark.der"),
};
fs::File::create(path).context("unable to create/open ARK file")?
}
CertType::ASK => {
match identify_cert(&cert.data[0..27]) {
CertFormat::PEM => path.push("ask.pem"),
CertFormat::DER => path.push("ask.der"),
};
fs::File::create(path).context("unable to create/open VCEK file")?
}
CertType::VCEK => {
match identify_cert(&cert.data[0..27]) {
CertFormat::PEM => path.push("vcek.pem"),
CertFormat::DER => path.push("vcek.der"),
};
fs::File::create(path).context("unable to create/open VCEK file")?
}
_ => continue,
};
f.write(&cert.data)
.context(format!("unable to write data to file {:?}", f))?;
}
}
Ok(())
}