mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
pvimg: Add '--print-schema <FORMAT>' option to 'pvimg info'
Add a new '--print-schema <FORMAT>' option to the 'pvimg info' command that prints the schema, if available, describing the given output format. For example, 'pvimg info --print-schema json' prints the JSON schema for the 'pvimg info' command output. Acked-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
8d40b5c97a
commit
3b0fc8cfda
321
rust/pvimg/schema/info-v1.schema.json
Normal file
321
rust/pvimg/schema/info-v1.schema.json
Normal file
@@ -0,0 +1,321 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "'pvimg info' JSON output schema",
|
||||
"description": "JSON schema for the 'pvimg info' command output. This schema describes the structure of IBM Secure Execution (SE) image header information in three states: decrypted (with secrets), encrypted (verified), and encrypted (not verified).",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"type": "object",
|
||||
"description": "Metadata about the s390-tools version and execution environment.",
|
||||
"required": [
|
||||
"api_level",
|
||||
"version",
|
||||
"host",
|
||||
"time_epoch",
|
||||
"time"
|
||||
],
|
||||
"properties": {
|
||||
"api_level": {
|
||||
"type": "integer",
|
||||
"const": 1,
|
||||
"description": "API level version, currently always 1."
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "Version of s390-tools."
|
||||
},
|
||||
"host": {
|
||||
"type": "string",
|
||||
"description": "Hostname where the command was executed."
|
||||
},
|
||||
"time_epoch": {
|
||||
"type": "integer",
|
||||
"description": "Unix timestamp (seconds since epoch) when the command was executed."
|
||||
},
|
||||
"time": {
|
||||
"type": "string",
|
||||
"description": "Human-readable timestamp in format 'YYYY-MM-DD HH:MM:SS+ZZZZ'."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"description": "Contains the Secure Execution header information.",
|
||||
"required": [
|
||||
"kind",
|
||||
"verified",
|
||||
"magic_hex",
|
||||
"version",
|
||||
"sehs",
|
||||
"iv_hex",
|
||||
"nks",
|
||||
"sea",
|
||||
"nep",
|
||||
"pcf_hex",
|
||||
"cust_pub_key",
|
||||
"pld_hex",
|
||||
"ald_hex",
|
||||
"tld_hex",
|
||||
"keyslots",
|
||||
"tag_hex"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"decrypted_se_hdr",
|
||||
"se_hdr"
|
||||
],
|
||||
"description": "Type of SE header."
|
||||
},
|
||||
"verified": {
|
||||
"type": "boolean",
|
||||
"description": "Indicates whether the SE header authenticity was verified."
|
||||
},
|
||||
"magic_hex": {
|
||||
"$ref": "#/definitions/hexString",
|
||||
"description": "Magic value identifying the SE header."
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "SE header version string."
|
||||
},
|
||||
"sehs": {
|
||||
"type": "integer",
|
||||
"description": "SE header size in bytes."
|
||||
},
|
||||
"iv_hex": {
|
||||
"$ref": "#/definitions/hexString",
|
||||
"description": "Initialization vector for SE header encryption."
|
||||
},
|
||||
"nks": {
|
||||
"type": "integer",
|
||||
"description": "Number of key slots."
|
||||
},
|
||||
"sea": {
|
||||
"type": "integer",
|
||||
"description": "Size of encrypted area of the SE header in bytes."
|
||||
},
|
||||
"nep": {
|
||||
"type": "integer",
|
||||
"description": "Number of (encrypted) component pages."
|
||||
},
|
||||
"pcf_hex": {
|
||||
"$ref": "#/definitions/hexString",
|
||||
"description": "Plaintext control flags."
|
||||
},
|
||||
"cust_pub_key": {
|
||||
"$ref": "#/definitions/custPubKey",
|
||||
"description": "Customer public key."
|
||||
},
|
||||
"pld_hex": {
|
||||
"$ref": "#/definitions/hexString",
|
||||
"description": "PLD (Page content List Digest) hash."
|
||||
},
|
||||
"ald_hex": {
|
||||
"$ref": "#/definitions/hexString",
|
||||
"description": "ALD (Address List Digest) hash."
|
||||
},
|
||||
"tld_hex": {
|
||||
"$ref": "#/definitions/hexString",
|
||||
"description": "TLD (Tweak List Digest) hash."
|
||||
},
|
||||
"keyslots": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"description": "Array of key slot entries.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/keyslot"
|
||||
}
|
||||
},
|
||||
"cipher_data_b64": {
|
||||
"$ref": "#/definitions/base64String",
|
||||
"description": "Encrypted SE header data (only for encrypted headers)."
|
||||
},
|
||||
"psw": {
|
||||
"$ref": "#/definitions/psw",
|
||||
"description": "Program Status Word (only for decrypted headers)."
|
||||
},
|
||||
"scf_hex": {
|
||||
"$ref": "#/definitions/hexString",
|
||||
"description": "Secret control flags (only for decrypted headers)."
|
||||
},
|
||||
"cck_hex": {
|
||||
"$ref": "#/definitions/hexString",
|
||||
"description": "Customer communication key (CCK) (only for decrypted headers)."
|
||||
},
|
||||
"xts_hex": {
|
||||
"$ref": "#/definitions/hexString",
|
||||
"description": "Components encryption key (only for decrypted headers)."
|
||||
},
|
||||
"tag_hex": {
|
||||
"$ref": "#/definitions/hexString",
|
||||
"description": "SE Header authentication tag."
|
||||
}
|
||||
},
|
||||
"oneOf": [
|
||||
{
|
||||
"title": "Decrypted SE Header",
|
||||
"description": "SE header with decrypted secrets.",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"const": "decrypted_se_hdr"
|
||||
},
|
||||
"verified": {
|
||||
"const": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"kind",
|
||||
"verified",
|
||||
"psw",
|
||||
"scf_hex",
|
||||
"cck_hex",
|
||||
"xts_hex"
|
||||
],
|
||||
"not": {
|
||||
"anyOf": [
|
||||
{
|
||||
"required": [
|
||||
"cipher_data_b64"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Encrypted SE Header (Verified)",
|
||||
"description": "Encrypted SE header integrity and authenticity checked with --hdr-key.",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"const": "se_hdr"
|
||||
},
|
||||
"verified": {
|
||||
"const": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"kind",
|
||||
"verified",
|
||||
"cipher_data_b64"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Encrypted SE Header (Not Verified)",
|
||||
"description": "Encrypted SE header without integrity and authenticity verification.",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"const": "se_hdr"
|
||||
},
|
||||
"verified": {
|
||||
"const": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"kind",
|
||||
"verified"
|
||||
],
|
||||
"not": {
|
||||
"anyOf": [
|
||||
{
|
||||
"required": [
|
||||
"psw"
|
||||
]
|
||||
},
|
||||
{
|
||||
"required": [
|
||||
"scf_hex"
|
||||
]
|
||||
},
|
||||
{
|
||||
"required": [
|
||||
"cck_hex"
|
||||
]
|
||||
},
|
||||
{
|
||||
"required": [
|
||||
"xts_hex"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"hexString": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9a-fA-F]+$",
|
||||
"description": "Hexadecimal string without 0x prefix."
|
||||
},
|
||||
"base64String": {
|
||||
"type": "string",
|
||||
"pattern": "^[A-Za-z0-9+/]+={0,2}$",
|
||||
"description": "Standard Base64-encoded string."
|
||||
},
|
||||
"custPubKey": {
|
||||
"type": "object",
|
||||
"description": "Customer public key.",
|
||||
"required": [
|
||||
"coord_hex"
|
||||
],
|
||||
"properties": {
|
||||
"coord_hex": {
|
||||
"$ref": "#/definitions/hexString",
|
||||
"description": "EC public key coordinates in hex."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"keyslot": {
|
||||
"type": "object",
|
||||
"description": "Wrapped SE header protection key for a specific target key.",
|
||||
"required": [
|
||||
"phkh_hex",
|
||||
"wrpk_hex",
|
||||
"kst_hex"
|
||||
],
|
||||
"properties": {
|
||||
"phkh_hex": {
|
||||
"$ref": "#/definitions/hexString",
|
||||
"description": "Hash of the public target key."
|
||||
},
|
||||
"wrpk_hex": {
|
||||
"$ref": "#/definitions/hexString",
|
||||
"description": "Wrapped SE header protection key."
|
||||
},
|
||||
"kst_hex": {
|
||||
"$ref": "#/definitions/hexString",
|
||||
"description": "Key slot authentication tag."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"psw": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"mask_hex",
|
||||
"addr_hex"
|
||||
],
|
||||
"description": "Program Status Word (PSW) defining the initial CPU state in the protected mode.",
|
||||
"properties": {
|
||||
"mask_hex": {
|
||||
"$ref": "#/definitions/hexString",
|
||||
"description": "Initial PSW mask."
|
||||
},
|
||||
"addr_hex": {
|
||||
"$ref": "#/definitions/hexString",
|
||||
"description": "Initial PSW address."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -354,15 +354,16 @@ pub struct SeImgInputArgs {
|
||||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
#[command(group(ArgGroup::new("info-mode").required(true).args(["path", "print_schema"])))]
|
||||
pub struct InfoArgs {
|
||||
#[clap(flatten)]
|
||||
pub input: SeImgInputArgs,
|
||||
pub input: Option<SeImgInputArgs>,
|
||||
|
||||
/// Output format for the Secure Execution image information.
|
||||
///
|
||||
/// If not specified, the format is automatically determined based on
|
||||
/// whether stdout is connected to a terminal.
|
||||
#[arg(long, value_parser=OutputFormatSpecParser::default())]
|
||||
#[arg(long, value_parser=OutputFormatSpecParser::default(), conflicts_with = "print_schema")]
|
||||
pub format: Option<OutputFormatSpec>,
|
||||
|
||||
/// Use the key in FILE to verify the Secure Execution header and optionally
|
||||
@@ -376,7 +377,7 @@ pub struct InfoArgs {
|
||||
/// Without this option, the information is displayed, but NOT verified, and
|
||||
/// a warning is printed. The displayed data should not be trusted without
|
||||
/// verification.
|
||||
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath, alias = "key", verbatim_doc_comment)]
|
||||
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath, alias = "key", verbatim_doc_comment, conflicts_with = "print_schema")]
|
||||
pub hdr_key: Option<PathBuf>,
|
||||
|
||||
/// This option reveals sensitive information that is normally encrypted in
|
||||
@@ -389,8 +390,22 @@ pub struct InfoArgs {
|
||||
/// The decrypted secrets should never be exposed in untrusted systems.
|
||||
///
|
||||
/// This option requires '--hdr-key' to decrypt the header.
|
||||
#[arg(long, requires = "hdr_key", verbatim_doc_comment)]
|
||||
#[arg(
|
||||
long,
|
||||
requires = "hdr_key",
|
||||
verbatim_doc_comment,
|
||||
conflicts_with = "print_schema"
|
||||
)]
|
||||
pub show_secrets: bool,
|
||||
|
||||
/// Print the schema for the 'info' subcommand and exit.
|
||||
///
|
||||
/// This outputs the schema that describes the structure of the given output FORMAT
|
||||
/// produced by the 'info' subcommand. The schema can be used for:
|
||||
/// - Validating output
|
||||
/// - Building tools that parse the output
|
||||
#[arg(value_name = "FORMAT", long, verbatim_doc_comment)]
|
||||
pub print_schema: Option<OutputFormatKind>,
|
||||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
@@ -984,17 +999,57 @@ mod test {
|
||||
CliOption::new("verbose", ["-VVV"]),
|
||||
],
|
||||
)),
|
||||
// --print-schema json works standalone
|
||||
flat_map_collect(insert(
|
||||
args.clone(),
|
||||
vec![CliOption::new(
|
||||
"print-json-schema",
|
||||
["--print-schema", "json"],
|
||||
)],
|
||||
)),
|
||||
];
|
||||
|
||||
let invalid_test_args = [
|
||||
// No default defined for --format
|
||||
flat_map_collect(insert(
|
||||
args,
|
||||
args.clone(),
|
||||
vec![
|
||||
CliOption::new("format", ["--format"]),
|
||||
CliOption::new("image", ["--", "/dev/null"]),
|
||||
],
|
||||
)),
|
||||
// --print-json-schema conflicts with input
|
||||
flat_map_collect(insert(
|
||||
args.clone(),
|
||||
vec![
|
||||
CliOption::new("print-json-schema", ["--print-schema", "json"]),
|
||||
CliOption::new("image", ["/dev/null"]),
|
||||
],
|
||||
)),
|
||||
// --print-json-schema conflicts with --format
|
||||
flat_map_collect(insert(
|
||||
args.clone(),
|
||||
vec![
|
||||
CliOption::new("print-json-schema", ["--print-schema", "json"]),
|
||||
CliOption::new("format", ["--format=json"]),
|
||||
],
|
||||
)),
|
||||
// --print-json-schema conflicts with --hdr-key
|
||||
flat_map_collect(insert(
|
||||
args.clone(),
|
||||
vec![
|
||||
CliOption::new("print-json-schema", ["--print-schema", "json"]),
|
||||
CliOption::new("hdr-key", ["--hdr-key", "/dev/null"]),
|
||||
],
|
||||
)),
|
||||
// --print-json-schema conflicts with --show-secrets
|
||||
flat_map_collect(insert(
|
||||
args,
|
||||
vec![
|
||||
CliOption::new("print-json-schema", ["--print-schema", "json"]),
|
||||
CliOption::new("show-secrets", ["--show-secrets"]),
|
||||
],
|
||||
)),
|
||||
];
|
||||
|
||||
let mut pvimg_valid_args = vec![];
|
||||
|
||||
@@ -17,13 +17,23 @@ use pvimg::{
|
||||
|
||||
use crate::cli::{InfoArgs, OutputFormatKind, OutputFormatSpec, OutputFormatVariant};
|
||||
|
||||
pub const INFO_V1_JSON_SCHEMA: &str = include_str!("../../schema/info-v1.schema.json");
|
||||
|
||||
pub fn info(opt: &InfoArgs) -> Result<OwnExitCode> {
|
||||
info!(
|
||||
"Reading Secure Execution header {}",
|
||||
opt.input.path.display()
|
||||
);
|
||||
let mut img = open_file(&opt.input.path)?;
|
||||
let mut output = std::io::stdout();
|
||||
if let Some(format_kind) = opt.print_schema {
|
||||
match format_kind {
|
||||
OutputFormatKind::Text => {
|
||||
writeln!(output, "No schema for {format_kind} format available!")?
|
||||
}
|
||||
OutputFormatKind::Json => write!(output, "{INFO_V1_JSON_SCHEMA}")?,
|
||||
}
|
||||
return Ok(OwnExitCode::Success);
|
||||
}
|
||||
|
||||
let input_path = opt.input.as_ref().unwrap().path.clone();
|
||||
info!("Reading Secure Execution header {}", input_path.display(),);
|
||||
let mut img = open_file(&input_path)?;
|
||||
|
||||
SeHdr::seek_sehdr(&mut img, None)?;
|
||||
let hdr = SeHdr::try_from_io(&mut img)?;
|
||||
|
||||
Reference in New Issue
Block a user