mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
pvimg: info: Add '--format text' support
Add human-readable output format to 'pvimg info' command. The format 'text:normal' shows only basic information about the Secure Execution header, but skips the keys and other binary data; the format 'text:full' shows everything. Reviewed-by: Steffen Eiden <seiden@linux.ibm.com> Signed-off-by: Marc Hartmayer <marc@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
committed by
Jan Höppner
parent
1d2a89b387
commit
126ba7e336
@@ -8,8 +8,8 @@ use std::string::ToString;
|
||||
use std::{env, ffi::OsStr, path::PathBuf};
|
||||
|
||||
use clap::{
|
||||
builder::PossibleValue, Arg, ArgGroup, Args, Command, CommandFactory, Parser, ValueEnum,
|
||||
ValueHint,
|
||||
builder::{PossibleValue, TypedValueParser},
|
||||
Arg, ArgGroup, Args, Command, CommandFactory, Parser, ValueEnum, ValueHint,
|
||||
};
|
||||
use log::warn;
|
||||
use utils::{CertificateOptions, DeprecatedVerbosityOptions};
|
||||
@@ -214,6 +214,8 @@ pub struct CreateBootImageLegacyFlags {
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
|
||||
pub enum OutputFormatKind {
|
||||
/// Human-readable, unstable text format
|
||||
Text,
|
||||
/// JSON format.
|
||||
Json,
|
||||
}
|
||||
@@ -221,6 +223,7 @@ pub enum OutputFormatKind {
|
||||
impl Display for OutputFormatKind {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Text => write!(f, "human-readable"),
|
||||
Self::Json => write!(f, "JSON"),
|
||||
}
|
||||
}
|
||||
@@ -231,6 +234,7 @@ impl FromStr for OutputFormatKind {
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"text" => Ok(Self::Text),
|
||||
"json" => Ok(Self::Json),
|
||||
_ => Err(format!("Invalid output format: {s}")),
|
||||
}
|
||||
@@ -241,6 +245,8 @@ impl FromStr for OutputFormatKind {
|
||||
pub enum OutputFormatVariant {
|
||||
/// Default
|
||||
Default,
|
||||
/// Full
|
||||
Full,
|
||||
/// Minified
|
||||
Minify,
|
||||
/// Pretty
|
||||
@@ -256,7 +262,7 @@ pub struct OutputFormatSpec {
|
||||
#[derive(Clone, Default)]
|
||||
struct OutputFormatSpecParser;
|
||||
|
||||
impl clap::builder::TypedValueParser for OutputFormatSpecParser {
|
||||
impl TypedValueParser for OutputFormatSpecParser {
|
||||
type Value = OutputFormatSpec;
|
||||
|
||||
fn parse_ref(
|
||||
@@ -288,14 +294,15 @@ impl clap::builder::TypedValueParser for OutputFormatSpecParser {
|
||||
(_, None) => OutputFormatVariant::Default,
|
||||
(_, Some("default")) => OutputFormatVariant::Default,
|
||||
|
||||
(OutputFormatKind::Text, Some("full")) => OutputFormatVariant::Full,
|
||||
(OutputFormatKind::Json, Some("pretty")) => OutputFormatVariant::Pretty,
|
||||
(OutputFormatKind::Json, Some("minify")) => OutputFormatVariant::Minify,
|
||||
(OutputFormatKind::Json, Some(other)) => {
|
||||
(_, Some(other)) => {
|
||||
let mut err =
|
||||
clap::error::Error::new(clap::error::ErrorKind::ValueValidation).with_cmd(cmd);
|
||||
err.insert(
|
||||
clap::error::ContextKind::InvalidArg,
|
||||
clap::error::ContextValue::String(arg_name),
|
||||
clap::error::ContextValue::String(arg_name.clone()),
|
||||
);
|
||||
err.insert(
|
||||
clap::error::ContextKind::InvalidValue,
|
||||
|
||||
@@ -51,6 +51,14 @@ pub fn info(opt: &InfoArgs) -> Result<OwnExitCode> {
|
||||
};
|
||||
|
||||
match opt.format {
|
||||
OutputFormatSpec {
|
||||
kind: OutputFormatKind::Text,
|
||||
variant: OutputFormatVariant::Default,
|
||||
} => write!(output, "{se_hdr}").context("Cannot generate the human readable output")?,
|
||||
OutputFormatSpec {
|
||||
kind: OutputFormatKind::Text,
|
||||
variant: OutputFormatVariant::Full,
|
||||
} => write!(output, "{se_hdr:#}").context("Cannot generate the human readable output")?,
|
||||
OutputFormatSpec {
|
||||
kind: OutputFormatKind::Json,
|
||||
variant,
|
||||
@@ -63,10 +71,14 @@ pub fn info(opt: &InfoArgs) -> Result<OwnExitCode> {
|
||||
OutputFormatVariant::Default | OutputFormatVariant::Pretty => {
|
||||
serde_json::to_writer_pretty(&mut output, &doc)?;
|
||||
}
|
||||
OutputFormatVariant::Full => {
|
||||
unreachable!("we already validate the variant in the outer match")
|
||||
}
|
||||
}
|
||||
// Make sure the output ends with a new line
|
||||
writeln!(&mut output)?
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
output.flush()?;
|
||||
|
||||
|
||||
@@ -2,10 +2,30 @@
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
use pv::PvCoreError;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
|
||||
pub fn display_indented<T: Display>(
|
||||
f: &mut std::fmt::Formatter<'_>,
|
||||
s: &T,
|
||||
width: usize,
|
||||
) -> String {
|
||||
let indentation = " ".repeat(width);
|
||||
let value = if f.alternate() {
|
||||
format!("{s:#}")
|
||||
} else {
|
||||
format!("{s}")
|
||||
};
|
||||
value
|
||||
.lines()
|
||||
.map(|l| format!("{indentation}{l}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
/// Rounds up the given `value` to a multiple of `multiple`.
|
||||
///
|
||||
/// # Errors
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
use deku::{ctx::Endian, DekuRead, DekuWrite};
|
||||
use pv::request::Zeroize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -23,6 +25,13 @@ pub struct PSW {
|
||||
pub addr: u64,
|
||||
}
|
||||
|
||||
impl Display for PSW {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "address: {:#016x}", self.addr)?;
|
||||
writeln!(f, "mask: {:#016x}", self.mask)
|
||||
}
|
||||
}
|
||||
|
||||
impl Zeroize for PSW {
|
||||
fn zeroize(&mut self) {
|
||||
self.mask.zeroize();
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
use std::{
|
||||
fmt::Display,
|
||||
io::{Read, Seek, SeekFrom},
|
||||
mem::size_of,
|
||||
};
|
||||
@@ -24,6 +25,7 @@ use crate::{
|
||||
misc::PAGESIZE,
|
||||
pv_utils::{
|
||||
error::{Error, Result},
|
||||
misc::display_indented,
|
||||
serializing::{serde_hex_array, serialize_to_bytes},
|
||||
uvdata::{
|
||||
AeadCipherTrait, AeadDataTrait, AeadPlainDataTrait, KeyExchangeTrait, UvDataPlainTrait,
|
||||
@@ -69,6 +71,18 @@ pub enum SeHdrVersion {
|
||||
V1 = 0x100,
|
||||
}
|
||||
|
||||
impl Display for SeHdrVersion {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
SeHdrVersion::V1 => "1",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[serde(tag = "kind")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
@@ -85,6 +99,37 @@ pub enum SeH {
|
||||
},
|
||||
}
|
||||
|
||||
impl Display for SeH {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match &self {
|
||||
SeH::DecryptedSeHdr { verified, se_hdr } => {
|
||||
let verified_s = if *verified {
|
||||
", integrity and authenticity verified"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
if f.alternate() {
|
||||
write!(f, "decrypted{verified_s} {se_hdr:#}")
|
||||
} else {
|
||||
write!(f, "decrypted{verified_s} {se_hdr}")
|
||||
}
|
||||
}
|
||||
SeH::SeHdr { verified, se_hdr } => {
|
||||
let verified_s = if *verified {
|
||||
"integrity and authenticity verified "
|
||||
} else {
|
||||
""
|
||||
};
|
||||
if f.alternate() {
|
||||
write!(f, "{verified_s}{se_hdr:#}")
|
||||
} else {
|
||||
write!(f, "{verified_s}{se_hdr}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize, Deserialize)]
|
||||
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
|
||||
@@ -118,6 +163,12 @@ impl SeHdrCommon {
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for SeHdrCommon {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "version: {}", self.version)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, DekuRead, DekuWrite, Serialize, Deserialize)]
|
||||
#[deku(endian = "Endian::Big")]
|
||||
/// Secure Execution header structure
|
||||
@@ -130,6 +181,17 @@ pub struct SeHdr {
|
||||
pub data: SeHdrVersioned,
|
||||
}
|
||||
|
||||
impl Display for SeHdr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "Secure Execution header")?;
|
||||
// Support verbose mode if the `alternate` (`{:#}`) flag is used.
|
||||
let value = display_indented(f, &self.common, 2);
|
||||
writeln!(f, "{value}")?;
|
||||
let data = display_indented(f, &self.data, 2);
|
||||
writeln!(f, "{data}")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, DekuRead, DekuWrite, Serialize, Deserialize)]
|
||||
#[deku(endian = "Endian::Big")]
|
||||
/// Plain data Secure Execution header structure
|
||||
@@ -141,6 +203,16 @@ pub struct SeHdrPlain {
|
||||
pub data: SeHdrData,
|
||||
}
|
||||
|
||||
impl Display for SeHdrPlain {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "Secure Execution header:")?;
|
||||
let value = display_indented(f, &self.common, 2);
|
||||
writeln!(f, "{value}")?;
|
||||
let value = display_indented(f, &self.data, 2);
|
||||
writeln!(f, "{value}")
|
||||
}
|
||||
}
|
||||
|
||||
#[enum_dispatch(AeadCipherTrait, AeadDataTrait, KeyExchangeTrait)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, DekuRead, DekuWrite, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
@@ -150,6 +222,20 @@ pub enum SeHdrVersioned {
|
||||
SeHdrBinV1(SeHdrBinV1),
|
||||
}
|
||||
|
||||
impl Display for SeHdrVersioned {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
SeHdrVersioned::SeHdrBinV1(se_hdr_bin_v1) => {
|
||||
if f.alternate() {
|
||||
write!(f, "{se_hdr_bin_v1:#}")
|
||||
} else {
|
||||
write!(f, "{se_hdr_bin_v1}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[enum_dispatch(
|
||||
AeadCipherTrait,
|
||||
AeadPlainDataTrait,
|
||||
@@ -164,6 +250,20 @@ pub enum SeHdrData {
|
||||
SeHdrDataV1(SeHdrDataV1),
|
||||
}
|
||||
|
||||
impl Display for SeHdrData {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::SeHdrDataV1(data_v1) => {
|
||||
if f.alternate() {
|
||||
write!(f, "{data_v1:#}")
|
||||
} else {
|
||||
write!(f, "{data_v1}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AeadCipherBuilderTrait for SeHdrData {
|
||||
fn set_iv(&mut self, iv: &[u8]) -> Result<()> {
|
||||
match self {
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
use std::mem::{size_of, size_of_val};
|
||||
use std::{
|
||||
fmt::Display,
|
||||
mem::{size_of, size_of_val},
|
||||
};
|
||||
|
||||
use base64::prelude::*;
|
||||
use deku::{ctx::Endian, prelude::*};
|
||||
use openssl::{
|
||||
nid::Nid,
|
||||
@@ -15,6 +19,7 @@ use pv::request::{
|
||||
Zeroize, SHA_512_HASH_LEN,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utils::HexSlice;
|
||||
|
||||
use super::keys::phkh_v1;
|
||||
use crate::{
|
||||
@@ -22,6 +27,7 @@ use crate::{
|
||||
misc::PAGESIZE,
|
||||
pv_utils::{
|
||||
error::Result,
|
||||
misc::display_indented,
|
||||
se_hdr::{
|
||||
brb::{
|
||||
ComponentMetadata, ComponentMetadataV1, SeHdrCommon, SeHdrConfBuilderTrait,
|
||||
@@ -81,6 +87,34 @@ impl SeHdrAadV1 {
|
||||
const KEY_TYPE: SymKeyType = SymKeyType::Aes256Gcm;
|
||||
}
|
||||
|
||||
impl Display for SeHdrAadV1 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// Support verbose mode if the `alternate` (`{:#}`) flag is used.
|
||||
if f.alternate() {
|
||||
writeln!(f, "size: {} bytes", self.sehs)?;
|
||||
writeln!(f, "number of key slots: {}", self.nks)?;
|
||||
}
|
||||
writeln!(f, "key slots:")?;
|
||||
for s in &self.keyslots {
|
||||
writeln!(f, " - {s}")?;
|
||||
}
|
||||
if f.alternate() {
|
||||
let value = display_indented(f, &self.cust_pub_key, 2);
|
||||
writeln!(f, "customer public key:\n{value}",)?;
|
||||
writeln!(f, "number of component pages: {}", self.nep)?;
|
||||
writeln!(f, "components content hash: {:}", HexSlice::from(&self.pld))?;
|
||||
writeln!(f, "components address hash: {:}", HexSlice::from(&self.ald))?;
|
||||
writeln!(f, "components tweak hash: {:}", HexSlice::from(&self.tld))?;
|
||||
}
|
||||
writeln!(
|
||||
f,
|
||||
"plaintext control flags:\n{}",
|
||||
PlaintextControlFlagsV1::from(self.pcf)
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyExchangeTrait for SeHdrAadV1 {
|
||||
fn contains<K: AsRef<PKeyRef<Public>>>(&self, key: K) -> Result<bool> {
|
||||
let phkh = phkh_v1(key)?;
|
||||
@@ -146,6 +180,25 @@ impl Zeroize for SeHdrConfV1 {
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for SeHdrConfV1 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(
|
||||
f,
|
||||
"secret control flags:\n{}",
|
||||
SecretControlFlagsV1::from(self.scf)
|
||||
)?;
|
||||
|
||||
// Support verbose mode if the `alternate` (`{:#}`) flag is used.
|
||||
if f.alternate() {
|
||||
writeln!(f, "CCK: {:}", HexSlice::from(self.cck.value()))?;
|
||||
writeln!(f, "XTS key: {:}", HexSlice::from(self.xts.value()))?;
|
||||
let psw = display_indented(f, &self.psw, 2);
|
||||
writeln!(f, "PSW:\n{psw}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Eq, Debug, Clone, DekuRead, DekuWrite, Serialize, Deserialize)]
|
||||
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
|
||||
pub struct SeHdrTagV1 {
|
||||
@@ -153,6 +206,12 @@ pub struct SeHdrTagV1 {
|
||||
tag: [u8; SymKeyType::AES_256_GCM_TAG_LEN],
|
||||
}
|
||||
|
||||
impl Display for SeHdrTagV1 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:}", HexSlice::from(&self.tag))
|
||||
}
|
||||
}
|
||||
|
||||
mod ser_confidential_confv1 {
|
||||
use pv::request::Confidential;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
@@ -190,6 +249,21 @@ pub struct SeHdrDataV1 {
|
||||
tag: SeHdrTagV1,
|
||||
}
|
||||
|
||||
impl Display for SeHdrDataV1 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// Support verbose mode if the `alternate` (`{:#}`) flag is used.
|
||||
if f.alternate() {
|
||||
write!(f, "{:#}", self.aad)?;
|
||||
write!(f, "{:#}", self.data.value())?;
|
||||
writeln!(f, "GCM tag: {}", self.tag)?;
|
||||
} else {
|
||||
write!(f, "{}", self.aad)?;
|
||||
write!(f, "{}", self.data.value())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads from a `reader` and creates a confidential `SeHdrConfV1`.
|
||||
///
|
||||
/// # Errors
|
||||
@@ -481,6 +555,24 @@ impl SeHdrBinV1 {
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for SeHdrBinV1 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// Support verbose mode if the `alternate` (`{:#}`) flag is used.
|
||||
if f.alternate() {
|
||||
write!(f, "{:#}", self.aad)?;
|
||||
writeln!(
|
||||
f,
|
||||
"encrypted data: {:#}",
|
||||
BASE64_STANDARD.encode(&self.cipher_data)
|
||||
)?;
|
||||
writeln!(f, "GCM tag: {:#}", self.tag)?;
|
||||
} else {
|
||||
write!(f, "{}", self.aad)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl UvDataTrait for SeHdrBinV1 {
|
||||
type P = SeHdrDataV1;
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
use std::mem::size_of;
|
||||
use std::{fmt::Display, mem::size_of};
|
||||
|
||||
use base64::prelude::*;
|
||||
use deku::{ctx::Endian, DekuRead, DekuWrite};
|
||||
use openssl::{
|
||||
hash::{hash, MessageDigest},
|
||||
@@ -11,6 +12,7 @@ use openssl::{
|
||||
};
|
||||
use pv::{request::EcPubKeyCoord, static_assert};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utils::HexSlice;
|
||||
|
||||
use crate::{
|
||||
error::{Error, Result},
|
||||
@@ -35,6 +37,12 @@ pub struct EcPubKeyCoordV1 {
|
||||
pub coord: [u8; 160],
|
||||
}
|
||||
|
||||
impl Display for EcPubKeyCoordV1 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "coordinate: {}", BASE64_STANDARD.encode(self.coord))
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::fallible_impl_from)]
|
||||
impl From<EcPubKeyCoord> for EcPubKeyCoordV1 {
|
||||
fn from(value: EcPubKeyCoord) -> Self {
|
||||
@@ -84,6 +92,12 @@ pub struct BinaryKeySlotV1 {
|
||||
}
|
||||
static_assert!(size_of::<BinaryKeySlotV1>() == 80);
|
||||
|
||||
impl Display for BinaryKeySlotV1 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "target key hash: {:}", HexSlice::from(&self.phkh))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<u8>> for BinaryKeySlotV1 {
|
||||
type Error = Error;
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ use crate::pv_utils::{
|
||||
|
||||
/// Operation mode for component preparation.
|
||||
#[allow(unused)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum Mode {
|
||||
/// Encrypt the component data
|
||||
Encrypt,
|
||||
|
||||
Reference in New Issue
Block a user