forked from pool/s390-tools
* s390-tools-08-rust-pvimg-Fix-flag-parsing-for-allowing-dump.patch * s390-tools-09-rust-pvimg-Document-the-change-from--comm-key-to--cck.patch OBS-URL: https://build.opensuse.org/package/show/Base:System/s390-tools?expand=0&rev=247
388 lines
13 KiB
Diff
388 lines
13 KiB
Diff
From ff04f76257791593c8f92374f295a0c478e3b0f7 Mon Sep 17 00:00:00 2001
|
|
From: Steffen Eiden <seiden@linux.ibm.com>
|
|
Date: Mon, 5 Aug 2024 09:34:47 +0200
|
|
Subject: [PATCH] rust/pv*: Allow the use of non-hashes secret IDs
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
Secret IDs identify a secret in the store. Tooling (pvsecret) calculates
|
|
them by hashing a user-defined string. With this patch it is now
|
|
possible to skip the hash step and directly use the input string as the
|
|
ID. Up to the first 31 bytes of the input ASCII-string are used. The last byte
|
|
is the NUL char. During list pvsecret tries to interpret the secret
|
|
as ASCII string and if possible displays the ASCII characters alongside
|
|
the hex number.
|
|
|
|
Also, use the Upper/Lower Hex formatters for the hexstring formatting of
|
|
SecretId. Display will, additionally show the ASCII representation if
|
|
applicable.
|
|
|
|
While at it, use Self wherever possible.
|
|
|
|
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
|
|
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
|
|
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
|
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
|
---
|
|
rust/pv/src/uvsecret/guest_secret.rs | 16 ++-
|
|
rust/pv_core/src/uvdevice/secret_list.rs | 163 ++++++++++++++++++++---
|
|
rust/pvsecret/src/cli.rs | 8 ++
|
|
rust/pvsecret/src/cmd/create.rs | 4 +-
|
|
rust/pvsecret/src/cmd/retr.rs | 14 +-
|
|
5 files changed, 184 insertions(+), 21 deletions(-)
|
|
|
|
diff --git a/rust/pv/src/uvsecret/guest_secret.rs b/rust/pv/src/uvsecret/guest_secret.rs
|
|
index 3bad6d3c..87b58bb8 100644
|
|
--- a/rust/pv/src/uvsecret/guest_secret.rs
|
|
+++ b/rust/pv/src/uvsecret/guest_secret.rs
|
|
@@ -92,7 +92,8 @@ macro_rules! retr_constructor {
|
|
}
|
|
|
|
impl GuestSecret {
|
|
- fn name_to_id(name: &str) -> Result<SecretId> {
|
|
+ /// Hashes the name with sha256
|
|
+ pub fn name_to_id(name: &str) -> Result<SecretId> {
|
|
let id: [u8; SecretId::ID_SIZE] = hash(MessageDigest::sha256(), name.as_bytes())?
|
|
.to_vec()
|
|
.try_into()
|
|
@@ -135,6 +136,19 @@ impl GuestSecret {
|
|
retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the curve is invalid"]
|
|
| #[doc = r"EC PRIVATE Key"] => PKey<Private>, ec);
|
|
|
|
+ /// Use the name as ID, do not hash it
|
|
+ pub fn no_hash_name(&mut self) {
|
|
+ match self {
|
|
+ Self::Null => (),
|
|
+ Self::Association {
|
|
+ name, ref mut id, ..
|
|
+ }
|
|
+ | Self::Retrievable {
|
|
+ name, ref mut id, ..
|
|
+ } => id.clone_from(&SecretId::from_string(name)),
|
|
+ }
|
|
+ }
|
|
+
|
|
/// Reference to the confidential data
|
|
pub fn confidential(&self) -> &[u8] {
|
|
match &self {
|
|
diff --git a/rust/pv_core/src/uvdevice/secret_list.rs b/rust/pv_core/src/uvdevice/secret_list.rs
|
|
index d7c268c9..7c7e63b5 100644
|
|
--- a/rust/pv_core/src/uvdevice/secret_list.rs
|
|
+++ b/rust/pv_core/src/uvdevice/secret_list.rs
|
|
@@ -11,7 +11,9 @@ use crate::{
|
|
use byteorder::{BigEndian, ByteOrder};
|
|
use serde::{Deserialize, Serialize, Serializer};
|
|
use std::{
|
|
- fmt::Display,
|
|
+ cmp::min,
|
|
+ ffi::CStr,
|
|
+ fmt::{Debug, Display, LowerHex, UpperHex},
|
|
io::{Cursor, Read, Seek, Write},
|
|
mem::size_of,
|
|
slice::Iter,
|
|
@@ -35,6 +37,33 @@ impl SecretId {
|
|
pub fn from(buf: [u8; Self::ID_SIZE]) -> Self {
|
|
buf.into()
|
|
}
|
|
+
|
|
+ /// Create a Id from a string
|
|
+ ///
|
|
+ /// Uses the first 31 bytes from `name` as id
|
|
+ /// Does not hash anything. Byte 32 is the NUL char
|
|
+ pub fn from_string(name: &str) -> Self {
|
|
+ let len = min(name.len(), Self::ID_SIZE - 1);
|
|
+ let mut res = Self::default();
|
|
+ res.0[0..len].copy_from_slice(&name.as_bytes()[0..len]);
|
|
+ res
|
|
+ }
|
|
+
|
|
+ /// Tries to represent the Id as printable-ASCII string
|
|
+ pub fn as_ascii(&self) -> Option<&str> {
|
|
+ if let Ok(t) = CStr::from_bytes_until_nul(&self.0) {
|
|
+ if let Ok(t) = t.to_str() {
|
|
+ if !t.is_empty()
|
|
+ && t.chars()
|
|
+ .all(|c| c.is_ascii_whitespace() | c.is_ascii_graphic())
|
|
+ && self.0[t.len()..].iter().all(|b| *b == 0)
|
|
+ {
|
|
+ return Some(t);
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+ None
|
|
+ }
|
|
}
|
|
|
|
impl Serialize for SecretId {
|
|
@@ -42,8 +71,8 @@ impl Serialize for SecretId {
|
|
where
|
|
S: Serializer,
|
|
{
|
|
- // calls Display at one point
|
|
- ser.serialize_str(&self.to_string())
|
|
+ // calls LowerHex at one point
|
|
+ ser.serialize_str(&format!("{self:#x}"))
|
|
}
|
|
}
|
|
|
|
@@ -56,12 +85,36 @@ impl<'de> Deserialize<'de> for SecretId {
|
|
}
|
|
}
|
|
|
|
+impl UpperHex for SecretId {
|
|
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
+ if f.alternate() {
|
|
+ write!(f, "0x")?;
|
|
+ }
|
|
+ for b in self.0 {
|
|
+ write!(f, "{b:02X}")?;
|
|
+ }
|
|
+ Ok(())
|
|
+ }
|
|
+}
|
|
+
|
|
+impl LowerHex for SecretId {
|
|
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
+ if f.alternate() {
|
|
+ write!(f, "0x")?;
|
|
+ }
|
|
+ for b in self.0 {
|
|
+ write!(f, "{b:02x}")?;
|
|
+ }
|
|
+ Ok(())
|
|
+ }
|
|
+}
|
|
+
|
|
impl Display for SecretId {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
- let mut s = String::with_capacity(32 * 2 + 2);
|
|
- s.push_str("0x");
|
|
- let s = self.0.iter().fold(s, |acc, e| acc + &format!("{e:02x}"));
|
|
- write!(f, "{s}")
|
|
+ if let Some(s) = self.as_ascii() {
|
|
+ write!(f, "{s} | ")?;
|
|
+ }
|
|
+ write!(f, "{self:#x}")
|
|
}
|
|
}
|
|
|
|
@@ -79,7 +132,7 @@ impl AsRef<[u8]> for SecretId {
|
|
|
|
/// A secret in a [`SecretList`]
|
|
#[repr(C)]
|
|
-#[derive(Debug, PartialEq, Eq, AsBytes, FromZeroes, FromBytes, Serialize)]
|
|
+#[derive(Debug, Clone, PartialEq, Eq, AsBytes, FromZeroes, FromBytes, Serialize)]
|
|
pub struct SecretEntry {
|
|
#[serde(serialize_with = "ser_u16")]
|
|
index: U16<BigEndian>,
|
|
@@ -153,11 +206,7 @@ impl Display for SecretEntry {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
let stype: ListableSecretType = self.stype.get().into();
|
|
writeln!(f, "{} {}:", self.index, stype)?;
|
|
- write!(f, " ")?;
|
|
- for b in self.id.as_ref() {
|
|
- write!(f, "{b:02x}")?;
|
|
- }
|
|
- Ok(())
|
|
+ write!(f, " {}", self.id)
|
|
}
|
|
}
|
|
|
|
@@ -269,6 +318,11 @@ impl SecretList {
|
|
self.hdr.total_num_secrets.get() as usize
|
|
}
|
|
|
|
+ /// Find the first [`SecretEntry`] that has the provided [`SecretId`]
|
|
+ pub fn find(&self, id: &SecretId) -> Option<SecretEntry> {
|
|
+ self.iter().find(|e| e.id() == id.as_ref()).cloned()
|
|
+ }
|
|
+
|
|
/// Encodes the list in the same binary format the UV would do
|
|
pub fn encode<T: Write>(&self, w: &mut T) -> Result<()> {
|
|
w.write_all(self.hdr.as_bytes())?;
|
|
@@ -456,7 +510,7 @@ where
|
|
type Value = [u8; SecretId::ID_SIZE];
|
|
|
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
- formatter.write_str("a `32 bytes long hexstring` prepended with 0x")
|
|
+ formatter.write_str("a `32 bytes (=64 character) long hexstring` prepended with 0x")
|
|
}
|
|
|
|
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
|
|
@@ -464,7 +518,10 @@ where
|
|
E: serde::de::Error,
|
|
{
|
|
if s.len() != SecretId::ID_SIZE * 2 + "0x".len() {
|
|
- return Err(serde::de::Error::invalid_length(s.len() - 2, &self));
|
|
+ return Err(serde::de::Error::invalid_length(
|
|
+ s.len().saturating_sub("0x".len()),
|
|
+ &self,
|
|
+ ));
|
|
}
|
|
let nb = s.strip_prefix("0x").ok_or_else(|| {
|
|
serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self)
|
|
@@ -655,4 +712,80 @@ mod test {
|
|
],
|
|
)
|
|
}
|
|
+
|
|
+ #[test]
|
|
+ fn secret_id_display() {
|
|
+ let text = "Fancy secret ID";
|
|
+ let id = SecretId::from_string(text);
|
|
+
|
|
+ let exp =
|
|
+ "Fancy secret ID | 0x46616e6379207365637265742049440000000000000000000000000000000000";
|
|
+ assert_eq!(id.to_string(), exp);
|
|
+ }
|
|
+
|
|
+ #[test]
|
|
+ fn secret_id_long_name() {
|
|
+ let text = "the most fanciest secret ID you ever seen in the time the universe exists";
|
|
+ let id = SecretId::from_string(text);
|
|
+ let exp =
|
|
+ "the most fanciest secret ID you | 0x746865206d6f73742066616e63696573742073656372657420494420796f7500";
|
|
+ assert_eq!(id.to_string(), exp);
|
|
+ }
|
|
+
|
|
+ #[test]
|
|
+ fn secret_id_no_ascii_name() {
|
|
+ let text = [0; 32];
|
|
+ let id = SecretId::from(text);
|
|
+
|
|
+ let exp = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
+ assert_eq!(id.to_string(), exp);
|
|
+ }
|
|
+
|
|
+ #[test]
|
|
+ fn secret_id_no_ascii_name2() {
|
|
+ let text = [
|
|
+ 0x25, 0x55, 3, 4, 50, 0, 6, 0, 8, 0, 0, 0, 0, 0, 0, 0, 90, 0, 0xa, 0, 0, 0, 0, 0xf, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0,
|
|
+ ];
|
|
+ let id = SecretId::from(text);
|
|
+ assert_eq!(id.as_ascii(), None);
|
|
+ }
|
|
+
|
|
+ #[test]
|
|
+ fn secret_id_no_ascii_name3() {
|
|
+ let text = [
|
|
+ 0x25, 0x55, 0, 4, 50, 0, 6, 0, 8, 0, 0, 0, 0, 0, 0, 0, 90, 0, 0xa, 0, 0, 0, 0, 0xf, 0,
|
|
+ 0, 0, 0, 0, 0, 0, 0,
|
|
+ ];
|
|
+ let id = SecretId::from(text);
|
|
+ assert_eq!(id.as_ascii(), None);
|
|
+ }
|
|
+
|
|
+ #[test]
|
|
+ fn secret_id_hex() {
|
|
+ let id_str = "Nice Test 123";
|
|
+ let id = SecretId::from_string(id_str);
|
|
+
|
|
+ let s = format!("{id:#x}");
|
|
+ assert_eq!(
|
|
+ s,
|
|
+ "0x4e69636520546573742031323300000000000000000000000000000000000000"
|
|
+ );
|
|
+ let s = format!("{id:x}");
|
|
+ assert_eq!(
|
|
+ s,
|
|
+ "4e69636520546573742031323300000000000000000000000000000000000000"
|
|
+ );
|
|
+ let s = format!("{id:#X}");
|
|
+ assert_eq!(
|
|
+ s,
|
|
+ "0x4E69636520546573742031323300000000000000000000000000000000000000"
|
|
+ );
|
|
+
|
|
+ let s = format!("{id:X}");
|
|
+ assert_eq!(
|
|
+ s,
|
|
+ "4E69636520546573742031323300000000000000000000000000000000000000"
|
|
+ );
|
|
+ }
|
|
}
|
|
diff --git a/rust/pvsecret/src/cli.rs b/rust/pvsecret/src/cli.rs
|
|
index 4e747682..d858fc29 100644
|
|
--- a/rust/pvsecret/src/cli.rs
|
|
+++ b/rust/pvsecret/src/cli.rs
|
|
@@ -141,6 +141,12 @@ pub struct CreateSecretOpt {
|
|
/// by default.
|
|
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath,)]
|
|
pub user_sign_key: Option<String>,
|
|
+
|
|
+ /// Do not hash the name, use it directly as secret ID.
|
|
+ ///
|
|
+ /// Ignored for meta-secrets.
|
|
+ #[arg(long)]
|
|
+ pub use_name: bool,
|
|
}
|
|
|
|
#[derive(Subcommand, Debug)]
|
|
@@ -342,6 +348,8 @@ pub enum RetrInpFmt {
|
|
Yaml,
|
|
/// Use a hex string.
|
|
Hex,
|
|
+ /// Use a name-string. Will hash it if no secret with the name found.
|
|
+ Name,
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Default)]
|
|
diff --git a/rust/pvsecret/src/cmd/create.rs b/rust/pvsecret/src/cmd/create.rs
|
|
index 73089a12..fab37e67 100644
|
|
--- a/rust/pvsecret/src/cmd/create.rs
|
|
+++ b/rust/pvsecret/src/cmd/create.rs
|
|
@@ -94,7 +94,7 @@ fn read_private_key(buf: &[u8]) -> Result<PKey<Private>> {
|
|
fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
|
|
debug!("Build add-secret request");
|
|
|
|
- let secret = match &opt.secret {
|
|
+ let mut secret = match &opt.secret {
|
|
AddSecretType::Meta => GuestSecret::Null,
|
|
AddSecretType::Association {
|
|
name,
|
|
@@ -112,6 +112,8 @@ fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
|
|
};
|
|
trace!("AddSecret: {secret:x?}");
|
|
|
|
+ opt.use_name.then(|| secret.no_hash_name());
|
|
+
|
|
let mut flags = match &opt.pcf {
|
|
Some(v) => (&try_parse_u64(v, "pcf")?).into(),
|
|
None => AddSecretFlags::default(),
|
|
diff --git a/rust/pvsecret/src/cmd/retr.rs b/rust/pvsecret/src/cmd/retr.rs
|
|
index 7f7704cc..ad3e91c3 100644
|
|
--- a/rust/pvsecret/src/cmd/retr.rs
|
|
+++ b/rust/pvsecret/src/cmd/retr.rs
|
|
@@ -17,12 +17,17 @@ use utils::get_writer_from_cli_file_arg;
|
|
fn retrieve(id: &SecretId) -> Result<RetrievedSecret> {
|
|
let uv = UvDevice::open()?;
|
|
let secrets = list_uvc(&uv)?;
|
|
- let secret = secrets
|
|
- .into_iter()
|
|
- .find(|s| s.id() == id.as_ref())
|
|
+ let secret = match secrets.find(id) {
|
|
+ Some(s) => s,
|
|
+ // hash it + try again if it is ASCII-representable
|
|
+ None => match id.as_ascii() {
|
|
+ Some(s) => secrets.find(&GuestSecret::name_to_id(s)?),
|
|
+ None => None,
|
|
+ }
|
|
.ok_or(anyhow!(
|
|
"The UV secret-store has no secret with the ID {id}"
|
|
- ))?;
|
|
+ ))?,
|
|
+ };
|
|
|
|
info!("Try to retrieve secret at index: {}", secret.index());
|
|
debug!("Try to retrieve: {secret:?}");
|
|
@@ -43,6 +48,7 @@ pub fn retr(opt: &RetrSecretOptions) -> Result<()> {
|
|
RetrInpFmt::Hex => {
|
|
serde_yaml::from_str(&opt.input).context("Cannot parse SecretId information")?
|
|
}
|
|
+ RetrInpFmt::Name => SecretId::from_string(&opt.input),
|
|
};
|
|
|
|
let retr_secret =
|