Files
s390-tools/rust/pvimg/examples/tamper_pvimg/main.rs
T
Marc Hartmayer b766d4a53c rust: Apply clippy fixes to format strings
Makes the code easier to read.

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-11-07 14:34:48 +01:00

156 lines
5.0 KiB
Rust

#![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 =
<SeHdrVersioned as TryInto<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 =
<SeHdrData as TryInto<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(())
}