mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
rust: pvattest-Rust
Add a CLI compatible Rust implementation of pvattest-C. - All (non-experimental) options are supported and work exactly as in the C implementation. For some options/parameters new variants are available. - `perform` now also accepts positional arguments, while keep accepting -i and -o that was mandatory in the C implementation. - `version` may also be a command instead of an option now. - -V is deprecated - -v increases verbosity instead of showing the version - all experimental options are dropped Acked-by: Qi Feng Huo <huoqif@cn.ibm.com> Acked-by: Marc Hartmayer <mhartmay@linux.ibm.com> Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
use crate::{
|
||||
cli::{AttAddFlags, CreateAttOpt},
|
||||
exchange::{ExchangeFormatRequest, ExchangeFormatVersion},
|
||||
};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use log::{debug, warn};
|
||||
use pv::{
|
||||
attest::{AttestationFlags, AttestationMeasAlg, AttestationRequest, AttestationVersion},
|
||||
misc::{create_file, write_file},
|
||||
request::{ReqEncrCtx, Request, SymKey, SymKeyType},
|
||||
};
|
||||
use std::process::ExitCode;
|
||||
|
||||
fn flags(cli_flags: &[AttAddFlags]) -> AttestationFlags {
|
||||
let mut att_flags = AttestationFlags::default();
|
||||
for flag in cli_flags {
|
||||
match flag {
|
||||
AttAddFlags::PhkhImg => att_flags.set_image_phkh(),
|
||||
AttAddFlags::PhkhAtt => att_flags.set_attest_phkh(),
|
||||
}
|
||||
}
|
||||
att_flags
|
||||
}
|
||||
|
||||
pub fn create(opt: &CreateAttOpt) -> Result<ExitCode> {
|
||||
let att_version = AttestationVersion::One;
|
||||
let meas_alg = AttestationMeasAlg::HmacSha512;
|
||||
|
||||
let mut arcb = AttestationRequest::new(att_version, meas_alg, flags(&opt.add_data))?;
|
||||
debug!("Generated Attestation request");
|
||||
|
||||
// Add host-key documents
|
||||
opt.certificate_args
|
||||
.get_verified_hkds("attestation request")?
|
||||
.into_iter()
|
||||
.for_each(|k| arcb.add_hostkey(k));
|
||||
debug!("Added all host-keys");
|
||||
|
||||
let encr_ctx =
|
||||
ReqEncrCtx::random(SymKeyType::Aes256).context("Failed to generate random input")?;
|
||||
let ser_arcb = arcb.encrypt(&encr_ctx)?;
|
||||
warn!("Successfully generated the request");
|
||||
|
||||
let mut output = create_file(&opt.output)?;
|
||||
let exch_ctx = ExchangeFormatRequest::new(
|
||||
ser_arcb,
|
||||
meas_alg.exp_size(),
|
||||
arcb.flags().expected_additional_size(),
|
||||
)?;
|
||||
exch_ctx.write(&mut output, ExchangeFormatVersion::One)?;
|
||||
|
||||
let arpk = match encr_ctx.prot_key() {
|
||||
SymKey::Aes256(k) => k,
|
||||
_ => bail!("Unexpected key type"),
|
||||
};
|
||||
write_file(
|
||||
&opt.arpk,
|
||||
arpk.value(),
|
||||
"Attestation request Protection Key",
|
||||
)?;
|
||||
|
||||
Ok(ExitCode::SUCCESS)
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
use crate::{
|
||||
cli::PerformAttOptComb,
|
||||
exchange::{ExchangeFormatRequest, ExchangeFormatResponse, ExchangeFormatVersion},
|
||||
};
|
||||
use anyhow::Result;
|
||||
use pv::{
|
||||
misc::{create_file, open_file, read_file},
|
||||
uv::{AttestationCmd, UvDevice},
|
||||
};
|
||||
use std::process::ExitCode;
|
||||
|
||||
pub fn perform<'a, P>(opt: P) -> Result<ExitCode>
|
||||
where
|
||||
P: Into<PerformAttOptComb<'a>>,
|
||||
{
|
||||
let opt = opt.into();
|
||||
let mut input = open_file(opt.input)?;
|
||||
let mut output = create_file(opt.output)?;
|
||||
let uvdevice = UvDevice::open()?;
|
||||
|
||||
let ex_in = ExchangeFormatRequest::read(&mut input)?;
|
||||
let user_data = opt
|
||||
.user_data
|
||||
.map(|u| read_file(u, "user-data"))
|
||||
.transpose()?;
|
||||
|
||||
let mut cmd = AttestationCmd::new_request(
|
||||
ex_in.arcb.clone().into(),
|
||||
user_data.clone(),
|
||||
ex_in.exp_measurement,
|
||||
ex_in.exp_additional,
|
||||
)?;
|
||||
|
||||
uvdevice.send_cmd(&mut cmd)?;
|
||||
|
||||
let measurement = cmd.measurement();
|
||||
let additional = cmd.additional_owned();
|
||||
let cuid = cmd.cuid();
|
||||
|
||||
let ex_out = ExchangeFormatResponse::new(
|
||||
ex_in.arcb,
|
||||
measurement.to_owned(),
|
||||
additional,
|
||||
user_data,
|
||||
cuid.to_owned(),
|
||||
)?;
|
||||
ex_out.write(&mut output, ExchangeFormatVersion::One)?;
|
||||
|
||||
Ok(ExitCode::SUCCESS)
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
use anyhow::Result;
|
||||
use log::{debug, warn};
|
||||
use pv::{
|
||||
attest::{
|
||||
AdditionalData, AttestationFlags, AttestationItems, AttestationMeasurement,
|
||||
AttestationRequest,
|
||||
},
|
||||
misc::{create_file, open_file, read_exact_file, write_file},
|
||||
request::{openssl::pkey::PKey, BootHdrTags, Confidential, SymKey},
|
||||
};
|
||||
use serde::Serialize;
|
||||
use std::{fmt::Display, process::ExitCode};
|
||||
use utils::HexSlice;
|
||||
|
||||
use crate::{
|
||||
cli::{VerifyOpt, VerifyOutputType},
|
||||
exchange::ExchangeFormatResponse,
|
||||
EXIT_CODE_ATTESTATION_FAIL,
|
||||
};
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct VerifyOutput<'a> {
|
||||
cuid: HexSlice<'a>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
add: Option<HexSlice<'a>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
add_fields: Option<AdditionalData<HexSlice<'a>>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
user_data: Option<HexSlice<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> VerifyOutput<'a> {
|
||||
fn from_exchange(resp: &'a ExchangeFormatResponse, flags: &AttestationFlags) -> Result<Self> {
|
||||
let additional_data_fields = resp
|
||||
.additional()
|
||||
.map(|a| AdditionalData::from_slice(a, flags))
|
||||
.transpose()?;
|
||||
let user_data = resp.user().map(|u| u.into());
|
||||
|
||||
Ok(Self {
|
||||
cuid: resp.config_uid().into(),
|
||||
add: resp.additional().map(|a| a.into()),
|
||||
add_fields: additional_data_fields.map(AdditionalData::from_other),
|
||||
user_data,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for VerifyOutput<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "Config UID:")?;
|
||||
writeln!(f, "{:#}", self.cuid)?;
|
||||
if let Some(data) = &self.add {
|
||||
writeln!(f, "Additional-data:")?;
|
||||
writeln!(f, "{:#}", data)?;
|
||||
}
|
||||
if let Some(data) = &self.add_fields {
|
||||
writeln!(f, "Additional-data content:")?;
|
||||
writeln!(f, "{:#}", data)?;
|
||||
}
|
||||
if let Some(data) = &self.user_data {
|
||||
writeln!(f, "user-data:")?;
|
||||
writeln!(f, "{:#}", data)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verify(opt: &VerifyOpt) -> Result<ExitCode> {
|
||||
let mut input = open_file(&opt.input)?;
|
||||
let mut img = open_file(&opt.hdr)?;
|
||||
let output = opt.output.as_ref().map(create_file).transpose()?;
|
||||
let arpk = SymKey::Aes256(
|
||||
read_exact_file(&opt.arpk, "Attestation request protection key").map(Confidential::new)?,
|
||||
);
|
||||
let tags = BootHdrTags::from_se_image(&mut img)?;
|
||||
let exchange = ExchangeFormatResponse::read(&mut input)?;
|
||||
|
||||
let (auth, conf) = AttestationRequest::decrypt_bin(exchange.arcb(), &arpk)?;
|
||||
let meas_key = PKey::hmac(conf.measurement_key())?;
|
||||
let items = AttestationItems::new(
|
||||
&tags,
|
||||
exchange.config_uid(),
|
||||
exchange.user(),
|
||||
conf.nonce().as_ref().map(|v| v.value()),
|
||||
exchange.additional(),
|
||||
);
|
||||
|
||||
let measurement = AttestationMeasurement::calculate(items, auth.mai(), &meas_key)?;
|
||||
|
||||
let uv_meas = exchange.measurement();
|
||||
if !measurement.eq_secure(uv_meas) {
|
||||
debug!("Measurement values:");
|
||||
debug!("Recieved: {}", HexSlice::from(uv_meas));
|
||||
debug!("Calculated: {}", HexSlice::from(measurement.as_ref()));
|
||||
warn!("Attestation measurement verification failed. Calculated and received attestation measurement are not equal.");
|
||||
return Ok(ExitCode::from(EXIT_CODE_ATTESTATION_FAIL));
|
||||
}
|
||||
warn!("Attestation measurement verified");
|
||||
// Error impossible CUID is present Attestation verified
|
||||
let pr_data = VerifyOutput::from_exchange(&exchange, auth.flags())?;
|
||||
|
||||
warn!("{pr_data}");
|
||||
if let Some(mut output) = output {
|
||||
match opt.format {
|
||||
VerifyOutputType::Yaml => serde_yaml::to_writer(&mut output, &pr_data)?,
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(user_data) = &opt.user_data {
|
||||
match exchange.user() {
|
||||
Some(data) => write_file(user_data, data, "user-data")?,
|
||||
None => {
|
||||
warn!("Location for `user-data` specified, but respose does not contain any user-data")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(ExitCode::SUCCESS)
|
||||
}
|
||||
Reference in New Issue
Block a user