mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
rust: Add toc support for ebc to pvsecret
Add two new command-line options to pvsecret create for Early Boot
Customization (EBC) Table of Contents (TOC) support:
1. --policy FILE
Links an Add-Secret-Request (ASR) to a policy file by embedding a
PolicyReference in the ASR's user data field. The PolicyReference
contains the relative file path and SHA512 hash of the policy file,
enabling integrity verification of the policy. This option conflicts
with --user-data as both use the same user data field in the ASR
structure.
2. --toc-policy FILE
Appends the AES-GCM authentication tag (MAC tag - last 16 bytes of
the encrypted ASR) to the specified TOC policy file. This enables
the TOC policy to maintain a list of all ASR MAC tags for
completeness verification during boot. The TOC can verify that all
expected ASRs are present and unmodified by checking their MAC tags
against this list. This option also conflicts with --user-data.
Both options support the EBC multi-party workflow where an ISV/CSP builds
a generic SEL image and customers customize it with their own secrets. The
TOC mechanism ensures the integrity and completeness of all EBC resources
during the boot process.
Assisted-by: IBM Bob:1.0.1
Acked-by: Holger Dengler <dengler@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
committed by
Jan Höppner
parent
1741ecff96
commit
c803cb925e
@@ -13,6 +13,7 @@ anyhow = { version = "1.0.95", features = ["std"] }
|
||||
clap = { version ="4.5", features = ["derive", "wrap_help"]}
|
||||
log = { version = "0.4.25", features = ["std", "release_max_level_debug"] }
|
||||
serde_yaml = "0.9"
|
||||
zerocopy = "0.8"
|
||||
|
||||
pv = { path = "../pv" , package = "s390_pv" }
|
||||
utils = { path = "../utils"}
|
||||
|
||||
@@ -194,6 +194,28 @@ Optional. No user\-data by default.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
\-\-policy <FILE>
|
||||
.RS 4
|
||||
Links an add\-secret request to a policy file.
|
||||
This option embeds a reference to a policy in the add\-secret request user data field. The
|
||||
reference includes the relative file path and the SHA-512 hash of the
|
||||
policy file, enabling verification of the policy file’s integrity.
|
||||
This option conflicts with \fB\-\-user\-data\fR, because both options use the
|
||||
same user data field in the add\-secret request structure.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
\-\-toc\-policy <FILE>
|
||||
.RS 4
|
||||
Adds the AES\-GCM authentication tag to a table-of-contents (TOC) policy file.
|
||||
This option appends the AES\-GCM authentication tag to the specified TOC policy
|
||||
file. This allows the TOC policy to maintain a list of all add\-secret request MAC tags for
|
||||
completeness verification during boot. During verification, the TOC checks the
|
||||
AES\-GCM tags against this list to ensure that all expected add\-secret request are present and
|
||||
unmodified.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
\-\-user\-sign\-key <FILE>
|
||||
.RS 4
|
||||
Use the content of FILE as user signing key. Adds a signature calculated from
|
||||
|
||||
@@ -130,6 +130,30 @@ pub struct CreateSecretOpt {
|
||||
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath,)]
|
||||
pub user_data: Option<String>,
|
||||
|
||||
/// Links an Add‑Secret-Request (ASR) to a policy file.
|
||||
///
|
||||
/// This option embeds a PolicyReference in the ASR user data field. The PolicyReference
|
||||
/// includes the relative file path and the SHA‑512 hash of the policy file, allowing the
|
||||
/// policy’s integrity to be verified.
|
||||
///
|
||||
/// This option conflicts with --user-data, because both options use the same user data field in
|
||||
/// the ASR structure.
|
||||
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath, conflicts_with("user_data"))]
|
||||
pub policy: Option<String>,
|
||||
|
||||
/// Adds the AES‑GCM authentication tag to a TOC policy file.
|
||||
///
|
||||
/// This option appends the AES‑GCM authentication tag to the specified TOC policy file. This
|
||||
/// allows the TOC policy to maintain a list of all ASR MAC tags for completeness verification
|
||||
/// during boot. During verification, the TOC checks the MAC tags against this list to ensure
|
||||
/// that all expected ASRs are present and unmodified.
|
||||
#[arg(
|
||||
long = "toc-policy",
|
||||
value_name = "FILE",
|
||||
value_hint = ValueHint::FilePath,
|
||||
)]
|
||||
pub tocpolicy: Option<String>,
|
||||
|
||||
/// Use the content of FILE as user signing key.
|
||||
///
|
||||
/// Adds a signature calculated from the key in FILE to the add-secret request. The
|
||||
|
||||
@@ -2,24 +2,30 @@
|
||||
//
|
||||
// Copyright IBM Corp. 2023, 2024
|
||||
|
||||
use std::path::Path;
|
||||
use std::{
|
||||
fs::OpenOptions,
|
||||
io::{Read, Write},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, Context, Error, Result};
|
||||
use log::{debug, info, trace, warn};
|
||||
use pv::request::openssl;
|
||||
use pv::{
|
||||
misc::{
|
||||
decode_hex, open_file, pv_guest_bit_set, read_exact_file, read_file, try_parse_u128,
|
||||
try_parse_u64, write,
|
||||
decode_hex, encode_hex, open_file, pv_guest_bit_set, read_exact_file, read_file,
|
||||
try_parse_u128, try_parse_u64, write,
|
||||
},
|
||||
request::{
|
||||
openssl::pkey::{PKey, Private},
|
||||
BootHdrTags, ReqEncrCtx, Request, SymKeyType,
|
||||
BootHdrTags, PolicyReference, ReqEncrCtx, Request, SymKeyType,
|
||||
},
|
||||
secret::{AddSecretFlags, AddSecretRequest, AddSecretVersion, ExtSecret, GuestSecret},
|
||||
uv::ConfigUid,
|
||||
};
|
||||
use serde_yaml::Value;
|
||||
use utils::get_writer_from_cli_file_arg;
|
||||
use zerocopy::IntoBytes;
|
||||
|
||||
use crate::cli::{AddSecretType, CreateSecretFlags, CreateSecretOpt, RetrieveableSecretInpKind};
|
||||
|
||||
@@ -33,6 +39,38 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Computes the SHA-256 hash of data from a reader.
|
||||
///
|
||||
/// Reads data from the provided reader in 4096-byte chunks and computes
|
||||
/// the SHA-256 hash of the entire content.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `r` - A reader providing the data to hash
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns a `Vec<u8>` containing the 32-byte SHA-256 hash, or an error
|
||||
/// if reading fails.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if reading from the reader fails.
|
||||
pub fn sha256_hash<R: Read>(mut r: R) -> Result<Vec<u8>, pv::PvCoreError> {
|
||||
let mut hasher = openssl::Sha256::new();
|
||||
let mut buf: [u8; 4096] = [0; 4096];
|
||||
|
||||
loop {
|
||||
let read = r.read(&mut buf)?;
|
||||
if read == 0 {
|
||||
break;
|
||||
}
|
||||
hasher.update(&buf[..read]);
|
||||
}
|
||||
|
||||
Ok(hasher.finish().to_vec())
|
||||
}
|
||||
|
||||
fn retrievable(name: &str, secret: &str, kind: &RetrieveableSecretInpKind) -> Result<GuestSecret> {
|
||||
let secret_data = read_file(secret, &format!("retrievable {kind}"))?.into();
|
||||
|
||||
@@ -76,6 +114,14 @@ pub fn create(opt: &CreateSecretOpt) -> Result<()> {
|
||||
let rq =
|
||||
ReqEncrCtx::random(SymKeyType::Aes256Gcm).context("Failed to generate random input")?;
|
||||
let ser_asrbc = asrcb.encrypt(&rq)?;
|
||||
|
||||
if let Some(path) = &opt.tocpolicy {
|
||||
let mac_tag = encode_hex(&ser_asrbc[(ser_asrbc.len() - 16)..]);
|
||||
let mut file = OpenOptions::new().create(true).append(true).open(path)?;
|
||||
|
||||
writeln!(file, "{mac_tag}")?;
|
||||
}
|
||||
|
||||
warn!("Successfully generated the request");
|
||||
write_out(&opt.output, ser_asrbc, "add-secret request")?;
|
||||
info!("Successfully wrote the request to '{}'", &opt.output);
|
||||
@@ -157,6 +203,17 @@ fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
|
||||
warn!("Added empty user-data file.");
|
||||
}
|
||||
|
||||
let supplied_ref = opt
|
||||
.policy
|
||||
.as_ref()
|
||||
.map(|s| -> Result<PolicyReference> {
|
||||
let p = Path::new(s);
|
||||
let reference = PolicyReference::new(p, sha256_hash)?;
|
||||
println!("{}", encode_hex(reference.hash));
|
||||
Ok(reference)
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let user_key = opt
|
||||
.user_sign_key
|
||||
.as_ref()
|
||||
@@ -169,6 +226,8 @@ fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
|
||||
|
||||
if user_data.is_some() || user_key.is_some() {
|
||||
asrcb.set_user_data(user_data.unwrap_or_default(), user_key)?;
|
||||
} else if let Some(ref_val) = supplied_ref {
|
||||
asrcb.set_user_data(ref_val.as_bytes(), None)?;
|
||||
}
|
||||
Ok(asrcb)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ use utils::{print_cli_error, print_error, print_version, PvLogger};
|
||||
|
||||
static LOGGER: PvLogger = PvLogger;
|
||||
static EXIT_LOGGER: u8 = 3;
|
||||
const FEATURES: &[&[&str]] = &[cmd::CMD_FN, cmd::UV_CMD_FN];
|
||||
const FEATURES: &[&[&str]] = &[cmd::CMD_FN, cmd::UV_CMD_FN, &["+ebc"]];
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let cli: CliOptions = match CliOptions::try_parse() {
|
||||
|
||||
Reference in New Issue
Block a user