#![allow(missing_docs)] use std::{fs::File, io::Write, path::PathBuf}; use anyhow::Context; use clap::{Parser, ValueEnum, ValueHint}; use log::info; use pv::{ misc::{open_file, parse_hex, read_file}, request::SymKey, }; use pvimg::{ error::Result, uvdata::{ KeyExchangeTrait, SeHdr, SeHdrBinV1, SeHdrData, SeHdrDataV1, SeHdrVersioned, UvDataPlainTrait, UvDataTrait, }, }; use utils::{PvLogger, VerbosityOptions}; #[derive(Parser, Debug)] struct Cli { /// Use INPUT as the Secure Execution image. #[arg(short, long, value_name = "INPUT", value_hint = ValueHint::FilePath,)] infile: PathBuf, /// Use INPUT as the Secure Execution image. #[arg(short, long, value_name = "OUTPUT", value_hint = ValueHint::FilePath,)] outfile: PathBuf, /// Use the key in FILE to decrypt the Secure Execution header. /// It is the key that was specified with the command line option /// '--hdr-key' at the Secure Execution image creation. #[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath, alias = "key")] hdr_key: PathBuf, #[clap(flatten)] verbosity: VerbosityOptions, /// Hdr Value to tamper with #[arg(long, value_enum)] tamp: TampVal, } #[non_exhaustive] #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)] pub enum TampVal { /// ALD. AddressListDigest, /// PLD. PageListDigest, /// TLD. TweakListDigest, /// Change lenght of secure header HdrSize, /// customer ECDH key CustomerPublicKey, /// host key hash HostKey, /// Secret control flags SecretControlFlag, /// Plaintext control flags PlaintextControlFlag, /// Number of keyslots NumKeySlots, /// Size encrypted area SizeEncArea, /// Number of encrypted pages NumEncPages, } static LOGGER: PvLogger = PvLogger; fn main() -> anyhow::Result<()> { let opt = Cli::parse(); LOGGER .start(opt.verbosity.to_level_filter()) .with_context(|| "Failed to set-up logger")?; info!("Reading Secure Execution header {}", opt.infile.display()); let mut input = open_file(&opt.infile)?; SeHdr::seek_sehdr(&mut input, None)?; let hdr = SeHdr::try_from_io(&mut input)?; let mut hdr_encr_v1: SeHdrBinV1 = >::try_into(hdr.clone().data).expect("SE-header V1"); let mut decryption_required = false; let mut hdr_encr: SeHdr; // Tamper with parts of SE header that doesn't require decryption match &opt.tamp { TampVal::HdrSize => hdr_encr_v1.aad.sehs += 1, TampVal::HostKey => hdr_encr_v1.aad.keyslots[0].phkh[..32].copy_from_slice(&[0; 32]), TampVal::PlaintextControlFlag => { let hex_str = String::from_utf8( read_file("/sys/firmware/uv/query/supp_se_hdr_pcf", "input file").unwrap(), ) .expect("PCF support not found"); let hex = parse_hex(&hex_str); let mut hex_pad = [0u8; 8]; hex_pad[(8 - hex.len())..].copy_from_slice(&hex); let supported_pcf: u64 = u64::from_be_bytes(hex_pad); hdr_encr_v1.aad.pcf = 0xFFFF_FFFF_FFFF_D5FF - supported_pcf; } TampVal::NumKeySlots => hdr_encr_v1.aad.nks = 0, TampVal::SizeEncArea => hdr_encr_v1.aad.sea = 0, _ => decryption_required = true, } if decryption_required { let key = SymKey::try_from_data( hdr.key_type(), read_file(&opt.hdr_key, "Reading key")?.into(), )?; let mut hdr_plain = hdr.decrypt(&key)?; let mut hdr_v1: SeHdrDataV1 = >::try_into(hdr_plain.clone().data) .expect("SE-header V1"); // Tamper with the SE header data match &opt.tamp { TampVal::AddressListDigest => hdr_v1.aad.ald[..8].copy_from_slice(&[0; 8]), TampVal::PageListDigest => hdr_v1.aad.pld[..8].copy_from_slice(&[0; 8]), TampVal::TweakListDigest => hdr_v1.aad.tld[..8].copy_from_slice(&[0; 8]), TampVal::CustomerPublicKey => { hdr_v1.aad.cust_pub_key.coord[..160].copy_from_slice(&[0; 160]) } TampVal::SecretControlFlag => hdr_v1.data.value_mut().scf = 0xFFFF_FFFF_FFFF_FFFF, TampVal::NumEncPages => hdr_v1.aad.nep = 0, _ => {} } hdr_plain.data = hdr_v1.into(); hdr_encr = hdr_plain.encrypt(&key)?; } else { hdr_encr = hdr.clone(); hdr_encr.data = hdr_encr_v1.clone().into(); } std::fs::copy(&opt.infile, &opt.outfile)?; match tamper_image_file(&opt.outfile, hdr_encr) { Ok(_) => (), Err(err) => { std::fs::remove_file(&opt.outfile)?; panic!("Could not seek SE header: {err}"); } }; Ok(()) } fn tamper_image_file(outfile: &PathBuf, hdr_encr: SeHdr) -> Result<()> { let mut output = File::options().read(true).write(true).open(outfile)?; SeHdr::seek_sehdr(&mut output, None)?; output.write_all(&hdr_encr.as_bytes()?)?; Ok(()) }