Files
s390-tools/rust/pvsecret/src/cmd/retr.rs
T
Steffen Eiden 080a6678fb pvsecret: Add -i -o option variants
All pvattest subcommands use the command line option -i <input> and -o
<output> to specify file input and output respectively. pvsecret however
uses mostly positional arguments for <input> and <output> exclusively,
e.g. pvattest check input.bin output.yaml

$ pvsecret add secret.bin

This provides an inconsistent user interface within the Secure
Execution tools and may confuse users.

Add the -i and -o option to the subcommands if applicable.
Input/output can then be specified like so:

$ pvsecret add -i secret.bin
$ pvsecret list -o list.yaml
$ pvsecret verify -i FILE -o out.yaml
$ pvsecret retrieve -i ID -o id.yaml

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-05-21 13:20:06 +02:00

124 lines
3.8 KiB
Rust

// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2024
use std::{collections::VecDeque, fmt::Display};
use anyhow::{anyhow, bail, Context, Result};
use log::{debug, info, warn};
use pv::{
misc::open_file,
misc::write,
secret::{GuestSecret, RetrievedSecret},
uv::{RetrieveCmd, SecretEntry, SecretId, SecretList, UvDevice},
};
use utils::get_writer_from_cli_file_arg;
use super::list::list_uvc;
use crate::cli::{RetrInpFmt, RetrOutFmt, RetrSecretOptions, RetrSecretOptionsComb};
enum Value {
Id(SecretId),
Idx(u16),
}
impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Value::Id(id) => write!(f, "ID {id}"),
Value::Idx(idx) => write!(f, "index {idx}"),
}
}
}
impl TryFrom<&RetrSecretOptionsComb<'_>> for Value {
type Error = anyhow::Error;
fn try_from(opt: &RetrSecretOptionsComb) -> Result<Self> {
match opt.inform {
RetrInpFmt::Yaml => match serde_yaml::from_reader(&mut open_file(opt.input)?)? {
GuestSecret::Retrievable { id, .. } => Ok(Self::Id(id)),
gs => bail!("The file contains a {gs}-secret, which is not retrievable."),
},
RetrInpFmt::Hex => serde_yaml::from_str(opt.input)
.context("Cannot parse SecretId information")
.map(Self::Id),
RetrInpFmt::Name => Ok(Self::Id(SecretId::from_string(opt.input))),
RetrInpFmt::Idx => opt
.input
.parse()
.context("Invalid index value")
.map(Self::Idx),
}
}
}
fn find_secret_by_id(secrets: &SecretList, id: &SecretId) -> Option<SecretEntry> {
let mut secrets: VecDeque<_> = secrets
.into_iter()
.filter(|s| s.id() == id.as_ref())
.collect();
let secret = secrets.pop_front();
if !secrets.is_empty() {
warn!(
"There are multiple secrets in the secret store with that id. Indices: {}",
secrets
.iter()
.fold(format!("{}", secret.unwrap().index()), |acc, e| {
format!("{acc}, {}", e.index())
})
);
}
secret.cloned()
}
fn retrieve(value: Value) -> Result<RetrievedSecret> {
let uv = UvDevice::open()?;
let secrets = list_uvc(&uv)?;
let entry = match &value {
Value::Id(id) => {
match find_secret_by_id(&secrets, id) {
Some(s) => Some(s),
// hash it + try again if it is ASCII-representable
None => match id.as_ascii() {
Some(s) => find_secret_by_id(&secrets, &GuestSecret::name_to_id(s)?),
None => None,
},
}
}
Value::Idx(idx) => secrets.into_iter().find(|e| &e.index() == idx),
}
.ok_or(anyhow!(
"The UV secret-store has no secret with the {value}"
))?;
info!("Try to retrieve secret at index: {}", entry.index());
debug!("Try to retrieve: {entry:?}");
let mut uv_cmd = RetrieveCmd::from_entry(entry)?;
uv.send_cmd(&mut uv_cmd)?;
Ok(RetrievedSecret::from_cmd(uv_cmd))
}
pub fn retr(opt: &RetrSecretOptions) -> Result<()> {
let opt_comb = RetrSecretOptionsComb::from(opt);
let mut output = get_writer_from_cli_file_arg(opt_comb.output)?;
let retr_secret = retrieve((&opt_comb).try_into()?)
.context("Could not retrieve the secret from the UV secret store.")?;
let out_data = match opt.outform {
RetrOutFmt::Bin => retr_secret.into_bytes(),
RetrOutFmt::Pem => retr_secret.to_pem()?.into_bytes(),
};
write(
&mut output,
out_data.value(),
opt_comb.output,
"IBM Protected Key",
)?;
Ok(())
}