use openssl::sha::sha256;
use std::fs::File;
use std::io::Write;
use std::{
convert::{TryFrom, TryInto},
io::Read,
mem::size_of,
path::PathBuf,
str::FromStr,
};
use uuid::{uuid, Uuid};
use crate::error::*;
use crate::parser::{ByteParser, Decoder, Encoder};
use crate::util::parser_helper::{ReadExt, WriteExt};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
type Sha256Hash = [u8; 32];
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, Default)]
struct GuidLe {
_data: [u8; 16],
}
impl Encoder<()> for GuidLe {
fn encode(&self, writer: &mut impl Write, _: ()) -> Result<(), std::io::Error> {
writer.write_bytes(self._data, ())?;
Ok(())
}
}
impl Decoder<()> for GuidLe {
fn decode(reader: &mut impl Read, _: ()) -> Result<Self, std::io::Error> {
let data = reader.read_bytes()?;
Ok(Self { _data: data })
}
}
impl ByteParser<()> for GuidLe {
type Bytes = [u8; 16];
const EXPECTED_LEN: Option<usize> = Some(16);
}
impl TryFrom<&Uuid> for GuidLe {
type Error = MeasurementError;
fn try_from(value: &Uuid) -> Result<Self, Self::Error> {
let guid = value.to_bytes_le();
let guid = guid.as_slice();
Ok(Self {
_data: guid.try_into()?,
})
}
}
impl FromStr for GuidLe {
type Err = MeasurementError;
fn from_str(guid: &str) -> Result<Self, MeasurementError> {
let guid = Uuid::try_from(guid)?;
let guid = guid.to_bytes_le();
let guid = guid.as_slice();
Ok(Self {
_data: guid.try_into()?,
})
}
}
#[repr(C)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, Default)]
struct SevHashTableEntry {
guid: GuidLe,
length: u16,
hash: Sha256Hash,
}
impl Encoder<()> for SevHashTableEntry {
fn encode(&self, writer: &mut impl Write, _: ()) -> Result<(), std::io::Error> {
writer.write_bytes(self.guid, ())?;
writer.write_bytes(self.length, ())?;
writer.write_bytes(self.hash, ())?;
Ok(())
}
}
impl Decoder<()> for SevHashTableEntry {
fn decode(reader: &mut impl Read, _: ()) -> Result<Self, std::io::Error> {
let guid = reader.read_bytes()?;
let length = reader.read_bytes()?;
let hash = reader.read_bytes()?;
Ok(Self { guid, length, hash })
}
}
impl ByteParser<()> for SevHashTableEntry {
type Bytes = [u8; 50];
const EXPECTED_LEN: Option<usize> = Some(50);
}
impl SevHashTableEntry {
fn new(guid: &Uuid, hash: Sha256Hash) -> Result<Self, MeasurementError> {
Ok(Self {
guid: GuidLe::try_from(guid)?,
length: std::mem::size_of::<SevHashTableEntry>() as u16,
hash,
})
}
}
#[repr(C)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, Default)]
struct SevHashTable {
guid: GuidLe,
length: u16,
cmdline: SevHashTableEntry,
initrd: SevHashTableEntry,
kernel: SevHashTableEntry,
}
impl Encoder<()> for SevHashTable {
fn encode(&self, writer: &mut impl Write, _: ()) -> Result<(), std::io::Error> {
writer.write_bytes(self.guid, ())?;
writer.write_bytes(self.length, ())?;
writer.write_bytes(self.cmdline, ())?;
writer.write_bytes(self.initrd, ())?;
writer.write_bytes(self.kernel, ())?;
Ok(())
}
}
impl Decoder<()> for SevHashTable {
fn decode(reader: &mut impl Read, _: ()) -> Result<Self, std::io::Error> {
let guid = reader.read_bytes()?;
let length = reader.read_bytes()?;
let cmdline = reader.read_bytes()?;
let initrd = reader.read_bytes()?;
let kernel = reader.read_bytes()?;
Ok(Self {
guid,
length,
cmdline,
initrd,
kernel,
})
}
}
impl ByteParser<()> for SevHashTable {
type Bytes = [u8; 168];
const EXPECTED_LEN: Option<usize> = Some(168);
}
impl SevHashTable {
fn new(
guid: &str,
cmdline: SevHashTableEntry,
initrd: SevHashTableEntry,
kernel: SevHashTableEntry,
) -> Result<Self, MeasurementError> {
Ok(Self {
guid: GuidLe::from_str(guid)?,
length: std::mem::size_of::<SevHashTable>() as u16,
cmdline,
initrd,
kernel,
})
}
}
#[repr(C)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, Default)]
struct PaddedSevHashTable {
ht: SevHashTable,
padding: [u8; PaddedSevHashTable::PADDING_SIZE],
}
impl Encoder<()> for PaddedSevHashTable {
fn encode(&self, writer: &mut impl Write, _: ()) -> Result<(), std::io::Error> {
writer.write_bytes(self.ht, ())?;
writer.write_bytes(self.padding, ())?;
Ok(())
}
}
impl Decoder<()> for PaddedSevHashTable {
fn decode(reader: &mut impl Read, _: ()) -> Result<Self, std::io::Error> {
let ht = reader.read_bytes()?;
let padding = reader.read_bytes()?;
Ok(Self { ht, padding })
}
}
impl ByteParser<()> for PaddedSevHashTable {
type Bytes = [u8; 168 + PaddedSevHashTable::PADDING_SIZE];
const EXPECTED_LEN: Option<usize> = Some(168 + PaddedSevHashTable::PADDING_SIZE);
}
impl PaddedSevHashTable {
const PADDING_SIZE: usize =
((size_of::<SevHashTable>() + 15) & !15) - size_of::<SevHashTable>();
fn new(hash_table: SevHashTable) -> Self {
PaddedSevHashTable {
ht: hash_table,
padding: [0; Self::PADDING_SIZE],
}
}
}
const SEV_HASH_TABLE_HEADER_GUID: Uuid = uuid!("9438d606-4f22-4cc9-b479-a793d411fd21");
const SEV_KERNEL_ENTRY_GUID: Uuid = uuid!("4de79437-abd2-427f-b835-d5b172d2045b");
const SEV_INITRD_ENTRY_GUID: Uuid = uuid!("44baf731-3a2f-4bd7-9af1-41e29169781d");
const SEV_CMDLINE_ENTRY_GUID: Uuid = uuid!("97d02dd8-bd20-4c94-aa78-e7714d36ab2a");
pub struct SevHashes {
kernel_hash: Sha256Hash,
initrd_hash: Sha256Hash,
cmdline_hash: Sha256Hash,
}
impl SevHashes {
pub fn new(
kernel: PathBuf,
initrd: Option<PathBuf>,
append: Option<&str>,
) -> Result<Self, MeasurementError> {
let mut kernel_file = File::open(kernel)?;
let mut kernel_data = Vec::new();
kernel_file.read_to_end(&mut kernel_data)?;
let kernel_hash = sha256(&kernel_data);
let initrd_data = match initrd {
Some(path) => {
let mut initrd_file = File::open(path)?;
let mut data = Vec::new();
initrd_file.read_to_end(&mut data)?;
data
}
None => Vec::new(),
};
let initrd_hash = sha256(&initrd_data);
let cmdline_hash = match append {
Some(append_str) => {
let mut append_bytes = append_str.trim().as_bytes().to_vec();
append_bytes.extend_from_slice(b"\x00");
sha256(&append_bytes)
}
None => sha256(b"\x00"),
};
Ok(SevHashes {
kernel_hash,
initrd_hash,
cmdline_hash,
})
}
pub fn construct_table(
&self,
) -> Result<[u8; 168 + PaddedSevHashTable::PADDING_SIZE], MeasurementError> {
let sev_hash_table = SevHashTable::new(
SEV_HASH_TABLE_HEADER_GUID.to_string().as_str(),
SevHashTableEntry::new(&SEV_CMDLINE_ENTRY_GUID, self.cmdline_hash)?,
SevHashTableEntry::new(&SEV_INITRD_ENTRY_GUID, self.initrd_hash)?,
SevHashTableEntry::new(&SEV_KERNEL_ENTRY_GUID, self.kernel_hash)?,
)?;
let padded_hash_table = PaddedSevHashTable::new(sev_hash_table);
let bytes = padded_hash_table.to_bytes()?;
Ok(bytes)
}
pub fn construct_page(&self, offset: usize) -> Result<Vec<u8>, MeasurementError> {
if offset >= 4096 {
return Err(SevHashError::InvalidOffset(offset, 4096))?;
}
let hashes_table = self.construct_table()?;
let mut page = Vec::with_capacity(4096);
page.resize(offset, 0);
page.extend_from_slice(&hashes_table[..]);
page.resize(4096, 0);
if page.len() != 4096 {
return Err(SevHashError::InvalidSize(page.len(), 4096))?;
}
Ok(page)
}
}