Skip to content

Commit

Permalink
Add support for caculating kernel eventlog digist when using OVMF+QEMU
Browse files Browse the repository at this point in the history
Signed-off-by: OuyangHang33 <hank.ouyang@intel.com>
  • Loading branch information
OuyangHang33 committed Feb 22, 2024
1 parent 9b4e454 commit a807299
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 4 deletions.
16 changes: 14 additions & 2 deletions td-shim-tools/src/bin/td-payload-reference-calculator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ wget https://github.com/Xynnn007/td-payload-reference-provider/raw/main/tests/bz

Test with the example bzImage
```
cargo run -- kernel -k bzImage -s 0x10000000
cargo run -p td-shim-tools --bin td-payload-reference-calculator -- kernel -k bzImage -s 0x10000000
```

The `kernel-size` parameter here means `KERNEL_SIZE` defined in guest firmware, s.t. [TD-SHIM](https://github.com/confidential-containers/td-shim)
Expand All @@ -25,11 +25,23 @@ Will get the result

which is from https://github.com/confidential-containers/attestation-service/pull/33/files#diff-1a4e5ad4c3b043c019c00bc3b3072fd6e1e5b03a5ce8c498e1c0acaf697d9d3fR265

When using [TDVF](https://github.com/tianocore/edk2-staging/tree/2024-tdvf-ww01.4/OvmfPkg) + [QEMU](https://github.com/qemu/qemu) Kernel Direct Boot, the kernel eventlog digest will be pre-processed by QEMU Kernel Direct Boot logic. Which is from https://github.com/confidential-containers/td-shim/issues/633

Test with the example bzImage
```
cargo run -p td-shim-tools --bin td-payload-reference-calculator -- kernel -k bzImage -q
```

Will get the result
```
a5e921ae5bde7ab989216da059057741688eae9114b854ce60733824f93ade8a848f19c719f3fdd5c4f0d7178164a5e2
```

### Kernel Parameter

Test
```
cargo run -- param -p "root=/dev/vda1 console=hvc0 rw" -s 0x1000
cargo run -p td-shim-tools --bin td-payload-reference-calculator -- param -p "root=/dev/vda1 console=hvc0 rw" -s 0x1000
```

Will get the result
Expand Down
122 changes: 120 additions & 2 deletions td-shim-tools/src/bin/td-payload-reference-calculator/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@ use std::{convert::TryFrom, path::Path};
pub const KERNEL_SIZE: &str = "0x2000000";
pub const KERNEL_PARAM_SIZE: &str = "0x1000";

// Refer to https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#signature-image-only,
// file offset specified at offset 0x3c,
// size of PE signature is 4: "PE\0\0"
const IMAGE_PE_OFFSET: usize = 0x003c;
const PE_SIGNATURE_SIZE: u32 = 4;
const IMGAE_BEGIN_CHECKSUM_ADDR: usize = 0x0000;
const IMGAE_BEGIN_CHECKSUM_SIZE: usize = 0x00da;
const IMGAE_CERT_TABLE_ADDR: usize = 0x00de;
const IMGAE_CERT_TABLE_SIZE: usize = 0x004c;
const IMGAE_HEADERS_ADDR: usize = 0x0132;
const IMGAE_HEADERS_SIZE: usize = 0x00ce;

fn kernel(path: &str, size: &str) -> Result<String> {
let path = Path::new(path).to_path_buf();
let siz = parse::<u64>(size)?;
Expand All @@ -31,6 +43,106 @@ fn param(param: &str, size: &str) -> Result<String> {
padding_digest(param, siz)
}

fn qemu(path: &str, size: &str) -> Result<String> {
let path = Path::new(path).to_path_buf();
let siz = parse::<u64>(size)?;
let file_size = std::fs::metadata(&path)?.len();
if file_size > siz {
bail!("File size should be less than `kernel-size`");
}
let buf = std::fs::read(path)?;
qemu_patch(buf)
}

fn qemu_patch(mut buf: Vec<u8>) -> Result<String> {
// refer to https://github.com/qemu/qemu/blob/f48c205fb42be48e2e47b7e1cd9a2802e5ca17b0/hw/i386/x86.c#L999
// patching type_of_loader @0x210
buf[0x210] = 0xb0;

// refer to https://github.com/qemu/qemu/blob/f48c205fb42be48e2e47b7e1cd9a2802e5ca17b0/hw/i386/x86.c#L1003
// patching loadflags @0x211
buf[0x211] = 0x81;

// refer to https://github.com/qemu/qemu/blob/9c74490bff6c8886a922008d0c9ce6cae70dd17e/hw/i386/x86.c#L1004
// patching heap_end_ptr @0x224 cmdline_addr - real_addr - 0x200 = 0xfe00
buf[0x224] = 0x00;
buf[0x225] = 0xfe;

// refer to https://github.com/qemu/qemu/blob/9c74490bff6c8886a922008d0c9ce6cae70dd17e/hw/i386/x86.c#L962
// patching cmd_line_ptr @0x228 cmdline_addr = 0x20000
buf[0x228] = 0x00;
buf[0x229] = 0x00;
buf[0x22A] = 0x02;
buf[0x22B] = 0x00;

let mut hasher = sha2::Sha384::new();
let (number_of_region_entry, regions_base, regions_size) = get_image_regions(&buf);

for index in 0..number_of_region_entry {
hasher.update(&buf[regions_base[index]..regions_base[index] + regions_size[index]]);
}

let res = hasher.finalize();
Ok(hex::encode(res))
}

fn get_image_regions(buf: &[u8]) -> (usize, Vec<usize>, Vec<usize>) {
// These 3 regions are known.
let mut number_of_region_entry = 3;
let mut regions_base = vec![
IMGAE_BEGIN_CHECKSUM_ADDR,
IMGAE_CERT_TABLE_ADDR,
IMGAE_HEADERS_ADDR,
];
let mut regions_size = vec![
IMGAE_BEGIN_CHECKSUM_SIZE,
IMGAE_CERT_TABLE_SIZE,
IMGAE_HEADERS_SIZE,
];

// Refer to https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#coff-file-header-object-and-image,
// After the signature of an image file is COFF File Header, size is 20 bytes.
// the NumberOfSections' offset is 2 and size is 2 bytes.
let size_of_coff_file_header: u32 = 20;

let coff_file_header_offset = ((buf[IMAGE_PE_OFFSET + 3] as u32) << 24)
| ((buf[IMAGE_PE_OFFSET + 2] as u32) << 16)
| ((buf[IMAGE_PE_OFFSET + 1] as u32) << 8)
| (buf[IMAGE_PE_OFFSET] as u32) + PE_SIGNATURE_SIZE;

let number_of_pecoff_entry = ((buf[coff_file_header_offset as usize + 3] as u16) << 8)
| buf[coff_file_header_offset as usize + 2] as u16;
number_of_region_entry += number_of_pecoff_entry as usize;

// the SizeOfOptionalHeader's offset is 16 and size is 2 bytes
let size_of_optional_header = ((buf[coff_file_header_offset as usize + 17] as u16) << 8)
| buf[coff_file_header_offset as usize + 16] as u16;

// Refer to https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#section-table-section-headers
// Size Of each Section is 40 bytes
// SizeOfRawData Offset: 16 Size:4
// PointerToRawData Offset: 20 Size:4
let mut p = (coff_file_header_offset
+ size_of_coff_file_header
+ size_of_optional_header as u32) as usize;
for _i in 0..number_of_pecoff_entry {
p += 16;
let size = ((buf[p + 3] as u32) << 24)
| ((buf[p + 2] as u32) << 16)
| ((buf[p + 1] as u32) << 8)
| buf[p] as u32;
p += 4;
let base = ((buf[p + 3] as u32) << 24)
| ((buf[p + 2] as u32) << 16)
| ((buf[p + 1] as u32) << 8)
| buf[p] as u32;
regions_base.push(base as usize);
regions_size.push(size as usize);
p += 20;
}
(number_of_region_entry, regions_base, regions_size)
}

fn padding_digest(mut buf: Vec<u8>, len: usize) -> Result<String> {
let diff = len - buf.len();

Expand All @@ -56,7 +168,8 @@ fn main() {
.required(false)
.default_value(KERNEL_SIZE)
.action(ArgAction::Set),
),
)
.arg(arg!(-q --"qemu" "QEMU Kernel Direct Boot patch string").required(false)),
)
.subcommand(
command!("param")
Expand All @@ -78,7 +191,12 @@ fn main() {
Some(("kernel", args)) => {
let path = args.get_one::<String>("kernel").unwrap();
let siz = args.get_one::<String>("size").unwrap();
kernel(path, siz)
// let qflag = args.get_one::<String>("qemu").unwrap();
if args.get_flag("qemu") {
qemu(path, siz)
} else {
kernel(path, siz)
}
}
Some(("param", args)) => {
let parameter = args.get_one::<String>("parameter").unwrap();
Expand Down

0 comments on commit a807299

Please sign in to comment.