Skip to content

Commit

Permalink
Add measurement build --firmware --kernel --initrd --cmdline
Browse files Browse the repository at this point in the history
--firmware can be specified independently.
--kernel --initrd --cmdline expect OVMF firmware and must
be specified as a trio
  • Loading branch information
crobinso committed Oct 20, 2022
1 parent 0aa137b commit d2ae085
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 1 deletion.
101 changes: 100 additions & 1 deletion src/measurement.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// SPDX-License-Identifier: Apache-2.0

use std::path::{Path, PathBuf};

use structopt::StructOpt;

use anyhow::Context;
Expand Down Expand Up @@ -32,15 +34,112 @@ pub struct BuildArgs {
#[structopt(long, help = "Launch digest in base64")]
pub launch_digest: Option<String>,

#[structopt(long, help = "Path to firmware/OVMF binary")]
pub firmware: Option<PathBuf>,
#[structopt(long, help = "Path to kernel")]
pub kernel: Option<PathBuf>,
#[structopt(long, help = "Path to initrd")]
pub initrd: Option<PathBuf>,
#[structopt(long, help = "Kernel commandline")]
pub cmdline: Option<String>,

#[structopt(long, help = "Optionally write binary content to filename")]
pub outfile: Option<String>,
}

fn sha256_bytes(content: &[u8], logname: &str) -> super::Result<Vec<u8>> {
#![allow(clippy::unnecessary_wraps)]
let mut hash = openssl::sha::Sha256::new();
hash.update(content);
let out = hash.finish();

let mut shastr = String::new();
for c in out.iter() {
shastr.push_str(format!("{:02x}", c).as_str());
}
log::debug!("{} len={} sha256: {}", logname, content.len(), shastr);
Ok(out.to_vec())
}

fn sha256_path(path: &Path) -> super::Result<Vec<u8>> {
let content =
std::fs::read(path).context(format!("failed to read file: {}", path.display()))?;
let logname = path.file_name().unwrap().to_str().unwrap();
sha256_bytes(&content, logname)
}

fn build_entry(guid: uuid::Uuid, payload: Vec<u8>) -> super::Result<Vec<u8>> {
let mut entry: Vec<u8> = Vec::new();
let header = guid.to_bytes_le();
let len = header.len() + payload.len() + 2;

entry.extend(&header);
entry.extend(&(len as u16).to_le_bytes());
entry.extend(&payload);

sha256_bytes(&entry, "entry")?;
Ok(entry)
}

fn build_kernel_table(kernel: &Path, initrd: &Path, cmdline: &str) -> super::Result<Vec<u8>> {
let table_uuid = uuid::Uuid::parse_str("9438d606-4f22-4cc9-b479-a793d411fd21")?;
let kernel_uuid = uuid::Uuid::parse_str("4de79437-abd2-427f-b835-d5b172d2045b")?;
let initrd_uuid = uuid::Uuid::parse_str("44baf731-3a2f-4bd7-9af1-41e29169781d")?;
let cmdline_uuid = uuid::Uuid::parse_str("97d02dd8-bd20-4c94-aa78-e7714d36ab2a")?;

// cmdline needs a trailing NUL byte
let mut cmdline_bytes = cmdline.to_owned().into_bytes();
cmdline_bytes.push(0);

let mut payload: Vec<u8> = Vec::new();
payload.extend(build_entry(
cmdline_uuid,
sha256_bytes(&cmdline_bytes, "cmdline")?,
)?);
payload.extend(build_entry(initrd_uuid, sha256_path(initrd)?)?);
payload.extend(build_entry(kernel_uuid, sha256_path(kernel)?)?);
sha256_bytes(&payload, "table payload")?;

let mut table = build_entry(table_uuid, payload)?;
let pad = 16 - (table.len() % 16);
table.extend(vec![0; pad]);

sha256_bytes(&table, "table")?;
Ok(table)
}

fn build_digest(args: &BuildArgs) -> super::Result<Vec<u8>> {
if let Some(ld) = &args.launch_digest {
return base64::decode(ld).context("failed to base64 decode --launch-digest");
}
Err(anyhow::anyhow!("--launch-digest must be specified."))

if args.firmware.is_none() {
return Err(anyhow::anyhow!(
"One of --firmware or --launch-digest must be specified."
));
}

let firmware = args.firmware.as_ref().unwrap();

if args.kernel.is_none() {
return sha256_path(firmware);
}

if args.initrd.is_none() || args.cmdline.is_none() {
return Err(anyhow::anyhow!(
"--kernel, --initrd, --cmdline must be specified together"
));
}

let kernel = args.kernel.as_ref().unwrap();
let initrd = args.initrd.as_ref().unwrap();
let cmdline = args.cmdline.as_ref().unwrap();

let mut content = std::fs::read(firmware)
.context(format!("failed to read file: {}", &firmware.display()))?;
content.extend(build_kernel_table(kernel, initrd, cmdline)?);

sha256_bytes(&content, "firmware + table")
}

fn parse_hex_or_int(argname: &str, val: &str) -> super::Result<u32> {
Expand Down
1 change: 1 addition & 0 deletions tests/data/measurement/initrd-fake
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fake initrd
1 change: 1 addition & 0 deletions tests/data/measurement/vmlinuz-fake
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fake vmlinuz
45 changes: 45 additions & 0 deletions tests/measurementbuild.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ struct BuildArgs<'a> {

launch_digest: Option<&'a str>,

firmware: Option<&'a str>,
kernel: Option<&'a str>,
initrd: Option<&'a str>,
cmdline: Option<&'a str>,

outfile: Option<&'a str>,
}

Expand Down Expand Up @@ -37,6 +42,24 @@ fn run_build(args: &BuildArgs) -> String {
sevctl_args.push("--launch-digest");
sevctl_args.push(ld);
}

if let Some(firmware) = args.firmware {
sevctl_args.push("--firmware");
sevctl_args.push(firmware);
}
if let Some(kernel) = args.kernel {
sevctl_args.push("--kernel");
sevctl_args.push(kernel);
}
if let Some(initrd) = args.initrd {
sevctl_args.push("--initrd");
sevctl_args.push(initrd);
}
if let Some(cmdline) = args.cmdline {
sevctl_args.push("--cmdline");
sevctl_args.push(cmdline);
}

if let Some(val) = &args.outfile {
sevctl_args.push("--outfile");
sevctl_args.push(val);
Expand All @@ -63,6 +86,10 @@ fn measurement_build() {
nonce: "wxP6tRHCFrFQWxsuqZA8QA==",
tik: "tests/data/measurement/tik1.bin",
launch_digest: None,
firmware: None,
kernel: None,
initrd: None,
cmdline: None,
outfile: None,
};

Expand All @@ -82,4 +109,22 @@ fn measurement_build() {
..stdargs
};
test_build(expected, args_outfile);

// Test --firmware PATH
let args_firmware = BuildArgs {
firmware: Some("tests/data/OVMF.amdsev.fd_trimmed_edk2-ovmf-20220126gitbb1bba3d77-4.el9"),
..stdargs
};
let expected = "oMDewIouJSpbpNRHj7Mk3p08H2dPZQdsZMU14qIymBk=";
test_build(expected, BuildArgs { ..args_firmware });

// Test --firmware + --kernel everything
let args_kernel = BuildArgs {
kernel: Some("tests/data/measurement/vmlinuz-fake"),
initrd: Some("tests/data/measurement/initrd-fake"),
cmdline: Some("foo bar baz fake kernel=cmdline"),
..args_firmware
};
let expected = "h3auYbWQnVW7EGLWN4Hf9SN0oEYMPU2sK4bLnefWPws=";
test_build(expected, args_kernel);
}

0 comments on commit d2ae085

Please sign in to comment.