Accepting request 1236146 from Base:System
OBS-URL: https://build.opensuse.org/request/show/1236146 OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/s390-tools?expand=0&rev=88
This commit is contained in:
commit
201c253b02
147
s390-tools-General-update-01.patch
Normal file
147
s390-tools-General-update-01.patch
Normal file
@ -0,0 +1,147 @@
|
||||
From 1e44ace41de3cbd744b22a8f9835473b091186e0 Mon Sep 17 00:00:00 2001
|
||||
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Date: Thu, 18 Jul 2024 10:55:45 +0200
|
||||
Subject: [PATCH] rust/pvsecret: Refactor writing secret
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Refactor the writing of secret-type dependent output files to ease
|
||||
extensions.
|
||||
|
||||
Reviewed-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 | 2 +-
|
||||
rust/pvsecret/src/cmd/create.rs | 89 +++++++++++++++-------------
|
||||
2 files changed, 48 insertions(+), 43 deletions(-)
|
||||
|
||||
diff --git a/rust/pv/src/uvsecret/guest_secret.rs b/rust/pv/src/uvsecret/guest_secret.rs
|
||||
index 509691fa..4f1db31c 100644
|
||||
--- a/rust/pv/src/uvsecret/guest_secret.rs
|
||||
+++ b/rust/pv/src/uvsecret/guest_secret.rs
|
||||
@@ -68,7 +68,7 @@ impl GuestSecret {
|
||||
}
|
||||
|
||||
/// Reference to the confidential data
|
||||
- pub(crate) fn confidential(&self) -> &[u8] {
|
||||
+ pub fn confidential(&self) -> &[u8] {
|
||||
match &self {
|
||||
Self::Null => &[],
|
||||
Self::Association { secret, .. } => secret.value().as_slice(),
|
||||
diff --git a/rust/pvsecret/src/cmd/create.rs b/rust/pvsecret/src/cmd/create.rs
|
||||
index 808b29e1..9251c38c 100644
|
||||
--- a/rust/pvsecret/src/cmd/create.rs
|
||||
+++ b/rust/pvsecret/src/cmd/create.rs
|
||||
@@ -62,7 +62,7 @@ pub fn create(opt: &CreateSecretOpt) -> Result<()> {
|
||||
write_out(&opt.output, ser_asrbc, "add-secret request")?;
|
||||
info!("Successfully wrote the request to '{}'", &opt.output);
|
||||
|
||||
- write_secret(&opt.secret, &asrcb, &opt.output)
|
||||
+ write_secret(&opt.secret, asrcb.guest_secret(), &opt.output)
|
||||
}
|
||||
|
||||
/// Read+parse the first key from the buffer.
|
||||
@@ -206,54 +206,59 @@ fn read_cuid(asrcb: &mut AddSecretRequest, opt: &CreateSecretOpt) -> Result<()>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+// Write non confidential data (=name+id) to a yaml stdout
|
||||
+fn write_yaml<P: AsRef<Path>>(
|
||||
+ name: &str,
|
||||
+ guest_secret: &GuestSecret,
|
||||
+ stdout: &bool,
|
||||
+ outp_path: P,
|
||||
+) -> Result<()> {
|
||||
+ debug!("Non-confidential secret information: {guest_secret:x?}");
|
||||
+
|
||||
+ let secret_info = serde_yaml::to_string(guest_secret)?;
|
||||
+ if stdout.to_owned() {
|
||||
+ println!("{secret_info}");
|
||||
+ return Ok(());
|
||||
+ }
|
||||
+
|
||||
+ let gen_name: String = name
|
||||
+ .chars()
|
||||
+ .map(|c| if c.is_whitespace() { '_' } else { c })
|
||||
+ .collect();
|
||||
+ let mut yaml_path = outp_path
|
||||
+ .as_ref()
|
||||
+ .parent()
|
||||
+ .with_context(|| format!("Cannot open directory of {:?}", outp_path.as_ref()))?
|
||||
+ .to_owned();
|
||||
+ yaml_path.push(gen_name);
|
||||
+ yaml_path.set_extension("yaml");
|
||||
+ write_out(&yaml_path, secret_info, "secret information")?;
|
||||
+ warn!(
|
||||
+ "Successfully wrote secret info to '{}'",
|
||||
+ yaml_path.display().to_string()
|
||||
+ );
|
||||
+ Ok(())
|
||||
+}
|
||||
+
|
||||
/// Write the generated secret (if any) to the specified output stream
|
||||
fn write_secret<P: AsRef<Path>>(
|
||||
secret: &AddSecretType,
|
||||
- asrcb: &AddSecretRequest,
|
||||
+ guest_secret: &GuestSecret,
|
||||
outp_path: P,
|
||||
) -> Result<()> {
|
||||
- if let AddSecretType::Association {
|
||||
- name,
|
||||
- stdout,
|
||||
- output_secret: secret_out,
|
||||
- ..
|
||||
- } = secret
|
||||
- {
|
||||
- let gen_name: String = name
|
||||
- .chars()
|
||||
- .map(|c| if c.is_whitespace() { '_' } else { c })
|
||||
- .collect();
|
||||
- let mut gen_path = outp_path
|
||||
- .as_ref()
|
||||
- .parent()
|
||||
- .with_context(|| format!("Cannot open directory of {:?}", outp_path.as_ref()))?
|
||||
- .to_owned();
|
||||
- gen_path.push(format!("{gen_name}.yaml"));
|
||||
-
|
||||
- // write non confidential data (=name+id) to a yaml
|
||||
- let secret_info = serde_yaml::to_string(asrcb.guest_secret())?;
|
||||
- if stdout.to_owned() {
|
||||
- println!("{secret_info}");
|
||||
- } else {
|
||||
- write_out(&gen_path, secret_info, "association secret info")?;
|
||||
- debug!(
|
||||
- "Non-confidential secret information: {:x?}",
|
||||
- asrcb.guest_secret()
|
||||
- );
|
||||
- warn!(
|
||||
- "Successfully wrote association info to '{}'",
|
||||
- gen_path.display()
|
||||
- );
|
||||
- }
|
||||
-
|
||||
- if let Some(path) = secret_out {
|
||||
- if let GuestSecret::Association { secret, .. } = asrcb.guest_secret() {
|
||||
- write_out(path, secret.value(), "Association secret")?
|
||||
- } else {
|
||||
- unreachable!("The secret type has to be `association` at this point (bug)!")
|
||||
+ match secret {
|
||||
+ AddSecretType::Association {
|
||||
+ name,
|
||||
+ stdout,
|
||||
+ output_secret,
|
||||
+ ..
|
||||
+ } => {
|
||||
+ write_yaml(name, guest_secret, stdout, outp_path)?;
|
||||
+ if let Some(path) = output_secret {
|
||||
+ write_out(path, guest_secret.confidential(), "Association secret")?
|
||||
}
|
||||
- info!("Successfully wrote generated association secret to '{path}'");
|
||||
}
|
||||
+ _ => (),
|
||||
};
|
||||
Ok(())
|
||||
}
|
467
s390-tools-General-update-02.patch
Normal file
467
s390-tools-General-update-02.patch
Normal file
@ -0,0 +1,467 @@
|
||||
From d1636168b26cc842bc0766235c8a4f2da9663f20 Mon Sep 17 00:00:00 2001
|
||||
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Date: Tue, 5 Mar 2024 10:46:29 +0100
|
||||
Subject: [PATCH] rust/pv: Support for writing data in PEM format
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Use existing OpenSSL functionalities to create PEM files containing
|
||||
arbitrary data.
|
||||
|
||||
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
|
||||
Acked-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/error.rs | 3 +
|
||||
rust/pv/src/lib.rs | 6 +
|
||||
rust/pv/src/openssl_extensions/bio.rs | 85 +++++++
|
||||
rust/pv/src/openssl_extensions/mod.rs | 2 +
|
||||
.../src/openssl_extensions/stackable_crl.rs | 41 +---
|
||||
rust/pv/src/pem_utils.rs | 222 ++++++++++++++++++
|
||||
6 files changed, 321 insertions(+), 38 deletions(-)
|
||||
create mode 100644 rust/pv/src/openssl_extensions/bio.rs
|
||||
create mode 100644 rust/pv/src/pem_utils.rs
|
||||
|
||||
diff --git a/rust/pv/src/error.rs b/rust/pv/src/error.rs
|
||||
index af85e93e..3ba808f2 100644
|
||||
--- a/rust/pv/src/error.rs
|
||||
+++ b/rust/pv/src/error.rs
|
||||
@@ -106,6 +106,9 @@ pub enum Error {
|
||||
)]
|
||||
AddDataMissing(&'static str),
|
||||
|
||||
+ #[error("An ASCII string was expected, but non-ASCII characters were received.")]
|
||||
+ NonAscii,
|
||||
+
|
||||
// errors from other crates
|
||||
#[error(transparent)]
|
||||
PvCore(#[from] pv_core::Error),
|
||||
diff --git a/rust/pv/src/lib.rs b/rust/pv/src/lib.rs
|
||||
index 7a33210c..ec31b9a4 100644
|
||||
--- a/rust/pv/src/lib.rs
|
||||
+++ b/rust/pv/src/lib.rs
|
||||
@@ -37,6 +37,7 @@ mod brcb;
|
||||
mod crypto;
|
||||
mod error;
|
||||
mod openssl_extensions;
|
||||
+mod pem_utils;
|
||||
mod req;
|
||||
mod utils;
|
||||
mod uvattest;
|
||||
@@ -71,6 +72,11 @@ pub mod attest {
|
||||
};
|
||||
}
|
||||
|
||||
+/// Definitions and functions to write objects in PEM format
|
||||
+pub mod pem {
|
||||
+ pub use crate::pem_utils::Pem;
|
||||
+}
|
||||
+
|
||||
/// Miscellaneous functions and definitions
|
||||
pub mod misc {
|
||||
pub use pv_core::misc::*;
|
||||
diff --git a/rust/pv/src/openssl_extensions/bio.rs b/rust/pv/src/openssl_extensions/bio.rs
|
||||
new file mode 100644
|
||||
index 00000000..73528eed
|
||||
--- /dev/null
|
||||
+++ b/rust/pv/src/openssl_extensions/bio.rs
|
||||
@@ -0,0 +1,85 @@
|
||||
+// SPDX-License-Identifier: MIT
|
||||
+//
|
||||
+// Copyright IBM Corp. 2024
|
||||
+
|
||||
+use core::slice;
|
||||
+use openssl::error::ErrorStack;
|
||||
+use openssl_sys::BIO_new_mem_buf;
|
||||
+use std::ffi::c_int;
|
||||
+use std::{marker::PhantomData, ptr};
|
||||
+
|
||||
+pub struct BioMem(*mut openssl_sys::BIO);
|
||||
+
|
||||
+impl Drop for BioMem {
|
||||
+ fn drop(&mut self) {
|
||||
+ // SAFETY: Pointer is valid. The pointer value is dropped after the free.
|
||||
+ unsafe {
|
||||
+ openssl_sys::BIO_free_all(self.0);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl BioMem {
|
||||
+ pub fn new() -> Result<Self, ErrorStack> {
|
||||
+ openssl_sys::init();
|
||||
+
|
||||
+ // SAFETY: Returns a valid pointer or null. null-case is tested right after this.
|
||||
+ let bio = unsafe { openssl_sys::BIO_new(openssl_sys::BIO_s_mem()) };
|
||||
+ match bio.is_null() {
|
||||
+ true => Err(ErrorStack::get()),
|
||||
+ false => Ok(Self(bio)),
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pub fn as_ptr(&self) -> *mut openssl_sys::BIO {
|
||||
+ self.0
|
||||
+ }
|
||||
+
|
||||
+ /// Copies the content of this slice into a Vec
|
||||
+ pub fn to_vec(&self) -> Vec<u8> {
|
||||
+ let buf;
|
||||
+ // SAFTEY: BIO provides a continuous memory that can be used to build a slice.
|
||||
+ unsafe {
|
||||
+ let mut ptr = ptr::null_mut();
|
||||
+ let len = openssl_sys::BIO_get_mem_data(self.0, &mut ptr);
|
||||
+ buf = slice::from_raw_parts(ptr as *const _ as *const _, len as usize)
|
||||
+ }
|
||||
+ buf.to_vec()
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+pub struct BioMemSlice<'a>(*mut openssl_sys::BIO, PhantomData<&'a [u8]>);
|
||||
+impl Drop for BioMemSlice<'_> {
|
||||
+ fn drop(&mut self) {
|
||||
+ // SAFETY: Pointer is valid. The pointer value is dropped after the free.
|
||||
+ unsafe {
|
||||
+ openssl_sys::BIO_free_all(self.0);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl<'a> BioMemSlice<'a> {
|
||||
+ pub fn new(buf: &'a [u8]) -> Result<BioMemSlice<'a>, ErrorStack> {
|
||||
+ openssl_sys::init();
|
||||
+
|
||||
+ // SAFETY: `buf` is a slice (i.e. pointer+size) pointing to a valid memory region.
|
||||
+ // So the resulting bio is valid. Lifetime of the slice is connected by this Rust
|
||||
+ // structure.
|
||||
+ assert!(buf.len() <= c_int::MAX as usize);
|
||||
+ let bio = unsafe {
|
||||
+ {
|
||||
+ let r = BIO_new_mem_buf(buf.as_ptr() as *const _, buf.len() as c_int);
|
||||
+ match r.is_null() {
|
||||
+ true => Err(ErrorStack::get()),
|
||||
+ false => Ok(r),
|
||||
+ }
|
||||
+ }?
|
||||
+ };
|
||||
+
|
||||
+ Ok(BioMemSlice(bio, PhantomData))
|
||||
+ }
|
||||
+
|
||||
+ pub fn as_ptr(&self) -> *mut openssl_sys::BIO {
|
||||
+ self.0
|
||||
+ }
|
||||
+}
|
||||
diff --git a/rust/pv/src/openssl_extensions/mod.rs b/rust/pv/src/openssl_extensions/mod.rs
|
||||
index fab26638..f6234e5d 100644
|
||||
--- a/rust/pv/src/openssl_extensions/mod.rs
|
||||
+++ b/rust/pv/src/openssl_extensions/mod.rs
|
||||
@@ -6,8 +6,10 @@
|
||||
|
||||
/// Extensions to the rust-openssl crate
|
||||
mod akid;
|
||||
+mod bio;
|
||||
mod crl;
|
||||
mod stackable_crl;
|
||||
|
||||
pub use akid::*;
|
||||
+pub use bio::*;
|
||||
pub use crl::*;
|
||||
diff --git a/rust/pv/src/openssl_extensions/stackable_crl.rs b/rust/pv/src/openssl_extensions/stackable_crl.rs
|
||||
index aef7cf86..12a9f9de 100644
|
||||
--- a/rust/pv/src/openssl_extensions/stackable_crl.rs
|
||||
+++ b/rust/pv/src/openssl_extensions/stackable_crl.rs
|
||||
@@ -2,16 +2,14 @@
|
||||
//
|
||||
// Copyright IBM Corp. 2023
|
||||
|
||||
-use std::{marker::PhantomData, ptr};
|
||||
-
|
||||
+use crate::openssl_extensions::bio::BioMemSlice;
|
||||
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||
use openssl::{
|
||||
error::ErrorStack,
|
||||
stack::Stackable,
|
||||
x509::{X509Crl, X509CrlRef},
|
||||
};
|
||||
-use openssl_sys::BIO_new_mem_buf;
|
||||
-use std::ffi::c_int;
|
||||
+use std::ptr;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StackableX509Crl(*mut openssl_sys::X509_CRL);
|
||||
@@ -62,44 +60,11 @@ impl Stackable for StackableX509Crl {
|
||||
type StackType = openssl_sys::stack_st_X509_CRL;
|
||||
}
|
||||
|
||||
-pub struct MemBioSlice<'a>(*mut openssl_sys::BIO, PhantomData<&'a [u8]>);
|
||||
-impl Drop for MemBioSlice<'_> {
|
||||
- fn drop(&mut self) {
|
||||
- unsafe {
|
||||
- openssl_sys::BIO_free_all(self.0);
|
||||
- }
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-impl<'a> MemBioSlice<'a> {
|
||||
- pub fn new(buf: &'a [u8]) -> Result<MemBioSlice<'a>, ErrorStack> {
|
||||
- openssl_sys::init();
|
||||
-
|
||||
- assert!(buf.len() <= c_int::MAX as usize);
|
||||
- let bio = unsafe {
|
||||
- {
|
||||
- let r = BIO_new_mem_buf(buf.as_ptr() as *const _, buf.len() as c_int);
|
||||
- if r.is_null() {
|
||||
- Err(ErrorStack::get())
|
||||
- } else {
|
||||
- Ok(r)
|
||||
- }
|
||||
- }?
|
||||
- };
|
||||
-
|
||||
- Ok(MemBioSlice(bio, PhantomData))
|
||||
- }
|
||||
-
|
||||
- pub fn as_ptr(&self) -> *mut openssl_sys::BIO {
|
||||
- self.0
|
||||
- }
|
||||
-}
|
||||
-
|
||||
impl StackableX509Crl {
|
||||
pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509Crl>, ErrorStack> {
|
||||
unsafe {
|
||||
openssl_sys::init();
|
||||
- let bio = MemBioSlice::new(pem)?;
|
||||
+ let bio = BioMemSlice::new(pem)?;
|
||||
|
||||
let mut crls = vec![];
|
||||
loop {
|
||||
diff --git a/rust/pv/src/pem_utils.rs b/rust/pv/src/pem_utils.rs
|
||||
new file mode 100644
|
||||
index 00000000..e6462519
|
||||
--- /dev/null
|
||||
+++ b/rust/pv/src/pem_utils.rs
|
||||
@@ -0,0 +1,222 @@
|
||||
+// SPDX-License-Identifier: MIT
|
||||
+//
|
||||
+// Copyright IBM Corp. 2024
|
||||
+
|
||||
+use crate::Result;
|
||||
+use crate::{openssl_extensions::BioMem, Error};
|
||||
+use openssl::error::ErrorStack;
|
||||
+use pv_core::request::Confidential;
|
||||
+use std::{
|
||||
+ ffi::{c_char, CString},
|
||||
+ fmt::Display,
|
||||
+};
|
||||
+
|
||||
+mod ffi {
|
||||
+ use openssl_sys::BIO;
|
||||
+ use std::ffi::{c_char, c_int, c_long, c_uchar};
|
||||
+ extern "C" {
|
||||
+ pub fn PEM_write_bio(
|
||||
+ bio: *mut BIO,
|
||||
+ name: *const c_char,
|
||||
+ header: *const c_char,
|
||||
+ data: *const c_uchar,
|
||||
+ len: c_long,
|
||||
+ ) -> c_int;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// Thin wrapper around [`CString`] only containing ASCII chars.
|
||||
+#[derive(Debug)]
|
||||
+struct AsciiCString(CString);
|
||||
+
|
||||
+impl AsciiCString {
|
||||
+ /// Convert from string
|
||||
+ ///
|
||||
+ /// # Returns
|
||||
+ /// Error if string is not ASCII or contains null chars
|
||||
+ pub(crate) fn from_str(s: &str) -> Result<Self> {
|
||||
+ match s.is_ascii() {
|
||||
+ true => Ok(Self(CString::new(s).map_err(|_| Error::NonAscii)?)),
|
||||
+ false => Err(Error::NonAscii),
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn as_ptr(&self) -> *const c_char {
|
||||
+ self.0.as_ptr()
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// Helper struct to construct the PEM format
|
||||
+#[derive(Debug)]
|
||||
+struct InnerPem<'d> {
|
||||
+ name: AsciiCString,
|
||||
+ header: Option<AsciiCString>,
|
||||
+ data: &'d [u8],
|
||||
+}
|
||||
+
|
||||
+impl<'d> InnerPem<'d> {
|
||||
+ fn new(name: &str, header: Option<String>, data: &'d [u8]) -> Result<Self> {
|
||||
+ Ok(Self {
|
||||
+ name: AsciiCString::from_str(name)?,
|
||||
+ header: match header {
|
||||
+ Some(h) => Some(AsciiCString::from_str(&h)?),
|
||||
+ None => None,
|
||||
+ },
|
||||
+ data,
|
||||
+ })
|
||||
+ }
|
||||
+
|
||||
+ /// Generate PEM representation of the data
|
||||
+ fn to_pem(&self) -> Result<Vec<u8>> {
|
||||
+ let bio = BioMem::new()?;
|
||||
+ let hdr_ptr = match self.header {
|
||||
+ // avoid moving variable -> use reference
|
||||
+ Some(ref h) => h.as_ptr(),
|
||||
+ None => std::ptr::null(),
|
||||
+ };
|
||||
+
|
||||
+ // SAFETY:
|
||||
+ // All pointers point to valid C strings or memory regions
|
||||
+ let rc = unsafe {
|
||||
+ ffi::PEM_write_bio(
|
||||
+ bio.as_ptr(),
|
||||
+ self.name.as_ptr(),
|
||||
+ hdr_ptr,
|
||||
+ self.data.as_ptr(),
|
||||
+ self.data.len() as std::ffi::c_long,
|
||||
+ )
|
||||
+ };
|
||||
+
|
||||
+ match rc {
|
||||
+ 1 => Err(Error::InternalSsl("Could not write PEM", ErrorStack::get())),
|
||||
+ _ => Ok(bio.to_vec()),
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// Data in PEM format
|
||||
+///
|
||||
+/// Displays into a printable PEM structure.
|
||||
+/// Must be constructed from another structure in this library.
|
||||
+///
|
||||
+/// ```rust,ignore
|
||||
+/// let pem: Pem = ...;
|
||||
+/// println!("PEM {pem}");
|
||||
+/// ```
|
||||
+/// ```PEM
|
||||
+///-----BEGIN <name>-----
|
||||
+///<header>
|
||||
+///
|
||||
+///<Base64 formatted binary data>
|
||||
+///-----END <name>-----
|
||||
+
|
||||
+#[derive(Debug)]
|
||||
+pub struct Pem {
|
||||
+ pem: Confidential<String>,
|
||||
+}
|
||||
+
|
||||
+#[allow(unused)]
|
||||
+impl Pem {
|
||||
+ /// Create a new PEM structure.
|
||||
+ ///
|
||||
+ /// # Errors
|
||||
+ ///
|
||||
+ /// This function will return an error if name or header contain non-ASCII chars, or OpenSSL
|
||||
+ /// could not generate the PEM (very likely due to OOM).
|
||||
+ pub(crate) fn new<D, H>(name: &str, header: H, data: D) -> Result<Self>
|
||||
+ where
|
||||
+ D: AsRef<[u8]>,
|
||||
+ H: Into<Option<String>>,
|
||||
+ {
|
||||
+ let mut header = header.into();
|
||||
+ let header = match header {
|
||||
+ Some(h) if h.ends_with('\n') => Some(h),
|
||||
+ Some(h) if h.is_empty() => None,
|
||||
+ Some(mut h) => {
|
||||
+ h.push('\n');
|
||||
+ Some(h)
|
||||
+ }
|
||||
+ None => None,
|
||||
+ };
|
||||
+
|
||||
+ let inner_pem = InnerPem::new(name, header, data.as_ref())?;
|
||||
+
|
||||
+ // Create the PEM format eagerly so that to_string/display cannot fail because of ASCII or OpenSSL Errors
|
||||
+ // Both error should be very unlikely
|
||||
+ // OpenSSL should be able to create PEM if there is enough memory and produce a non-null
|
||||
+ // terminated ASCII-string
|
||||
+ // Unwrap succeeds it's all ASCII
|
||||
+ // Std lib implements all the conversations without a copy
|
||||
+ let pem = CString::new(inner_pem.to_pem()?)
|
||||
+ .map_err(|_| Error::NonAscii)?
|
||||
+ .into_string()
|
||||
+ .unwrap()
|
||||
+ .into();
|
||||
+
|
||||
+ Ok(Self { pem })
|
||||
+ }
|
||||
+
|
||||
+ /// Converts the PEM-data into a byte vector.
|
||||
+ ///
|
||||
+ /// This consumes the `PEM`.
|
||||
+ #[inline]
|
||||
+ #[must_use = "`self` will be dropped if the result is not used"]
|
||||
+ pub fn into_bytes(self) -> Confidential<Vec<u8>> {
|
||||
+ self.pem.into_inner().into_bytes().into()
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl Display for Pem {
|
||||
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
+ self.pem.value().fmt(f)
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#[cfg(test)]
|
||||
+mod test {
|
||||
+ use super::*;
|
||||
+
|
||||
+ #[test]
|
||||
+ fn no_data() {
|
||||
+ const EXP: &str =
|
||||
+ "-----BEGIN PEM test-----\ntest hdr value: 17\n\n-----END PEM test-----\n";
|
||||
+ let test_pem = Pem::new("PEM test", "test hdr value: 17".to_string(), []).unwrap();
|
||||
+ let pem_str = test_pem.to_string();
|
||||
+ assert_eq!(pem_str, EXP);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn no_hdr() {
|
||||
+ const EXP: &str =
|
||||
+ "-----BEGIN PEM test-----\ndmVyeSBzZWNyZXQga2V5\n-----END PEM test-----\n";
|
||||
+ let test_pem = Pem::new("PEM test", None, "very secret key").unwrap();
|
||||
+ let pem_str = test_pem.to_string();
|
||||
+ assert_eq!(pem_str, EXP);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn some_data() {
|
||||
+ const EXP: &str= "-----BEGIN PEM test-----\ntest hdr value: 17\n\ndmVyeSBzZWNyZXQga2V5\n-----END PEM test-----\n";
|
||||
+ let test_pem = Pem::new(
|
||||
+ "PEM test",
|
||||
+ "test hdr value: 17".to_string(),
|
||||
+ "very secret key",
|
||||
+ )
|
||||
+ .unwrap();
|
||||
+ let pem_str = test_pem.to_string();
|
||||
+ assert_eq!(pem_str, EXP);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn data_linebreak() {
|
||||
+ const EXP: &str= "-----BEGIN PEM test-----\ntest hdr value: 17\n\ndmVyeSBzZWNyZXQga2V5\n-----END PEM test-----\n";
|
||||
+ let test_pem = Pem::new(
|
||||
+ "PEM test",
|
||||
+ "test hdr value: 17\n".to_string(),
|
||||
+ "very secret key",
|
||||
+ )
|
||||
+ .unwrap();
|
||||
+ let pem_str = test_pem.to_string();
|
||||
+ assert_eq!(pem_str, EXP);
|
||||
+ }
|
||||
+}
|
57
s390-tools-General-update-03.patch
Normal file
57
s390-tools-General-update-03.patch
Normal file
@ -0,0 +1,57 @@
|
||||
From 69eb06f39e5134565babfe96c66a3786c0a571cf Mon Sep 17 00:00:00 2001
|
||||
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Date: Tue, 20 Feb 2024 14:50:47 +0100
|
||||
Subject: [PATCH] rust/pv_core: Update ffi.rs to linux/uvdevice.h v6.13
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
While at it, add a file global #[allow(dead_code)].
|
||||
The file is a rustified copy of linux/arch/s390/include/uapi/asm/uvdevice.h
|
||||
and there might be things that are not needed here but are defined in that header.
|
||||
|
||||
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_core/src/uvdevice/ffi.rs | 11 +++++++++--
|
||||
1 file changed, 9 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/rust/pv_core/src/uvdevice/ffi.rs b/rust/pv_core/src/uvdevice/ffi.rs
|
||||
index bbcc5867..3d9998db 100644
|
||||
--- a/rust/pv_core/src/uvdevice/ffi.rs
|
||||
+++ b/rust/pv_core/src/uvdevice/ffi.rs
|
||||
@@ -2,6 +2,13 @@
|
||||
//
|
||||
// Copyright IBM Corp. 2023
|
||||
|
||||
+// This file is a rustified copy of linux/arch/s390/include/uapi/asm/uvdevice.h
|
||||
+// There might be things that are not needed here but nontheless defined in that header.
|
||||
+// Those two files should be in sync -> there might be unused/dead code.
|
||||
+//
|
||||
+// The `UVIO_IOCTL_*` and `UVIO_SUPP_*` macros
|
||||
+#![allow(dead_code)]
|
||||
+
|
||||
use std::mem::size_of;
|
||||
|
||||
use crate::{assert_size, static_assert};
|
||||
@@ -11,9 +18,8 @@ pub const UVIO_ATT_ARCB_MAX_LEN: usize = 0x100000;
|
||||
pub const UVIO_ATT_MEASUREMENT_MAX_LEN: usize = 0x8000;
|
||||
pub const UVIO_ATT_ADDITIONAL_MAX_LEN: usize = 0x8000;
|
||||
pub const UVIO_ADD_SECRET_MAX_LEN: usize = 0x100000;
|
||||
-#[allow(unused)]
|
||||
-// here for completeness
|
||||
pub const UVIO_LIST_SECRETS_LEN: usize = 0x1000;
|
||||
+pub const UVIO_RETR_SECRET_MAX_LEN: usize = 0x2000;
|
||||
|
||||
// equal to ascii 'u'
|
||||
pub const UVIO_TYPE_UVC: u8 = 117u8;
|
||||
@@ -23,6 +29,7 @@ pub const UVIO_IOCTL_ATT_NR: u8 = 1;
|
||||
pub const UVIO_IOCTL_ADD_SECRET_NR: u8 = 2;
|
||||
pub const UVIO_IOCTL_LIST_SECRETS_NR: u8 = 3;
|
||||
pub const UVIO_IOCTL_LOCK_SECRETS_NR: u8 = 4;
|
||||
+pub const UVIO_IOCTL_RETR_SECRET_NR: u8 = 5;
|
||||
|
||||
/// Uvdevice IOCTL control block
|
||||
/// Programs can use this struct to communicate with the uvdevice via IOCTLs
|
204
s390-tools-General-update-04.patch
Normal file
204
s390-tools-General-update-04.patch
Normal file
@ -0,0 +1,204 @@
|
||||
From 01cd81ecf5d1a7e1e504ae1b67692cf63cd4b51d Mon Sep 17 00:00:00 2001
|
||||
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Date: Tue, 5 Mar 2024 11:56:57 +0100
|
||||
Subject: [PATCH] rust/pv_core: Retrieve Secret UVC
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Create the uvdevice-IOCTL functionality for the new Retrieve Secret UVC.
|
||||
|
||||
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
|
||||
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
|
||||
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
rust/pv_core/src/error.rs | 7 ++
|
||||
rust/pv_core/src/lib.rs | 2 +-
|
||||
rust/pv_core/src/uvdevice/secret.rs | 97 +++++++++++++++++++++++-
|
||||
rust/pv_core/src/uvdevice/secret_list.rs | 15 ++++
|
||||
4 files changed, 118 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/rust/pv_core/src/error.rs b/rust/pv_core/src/error.rs
|
||||
index 20fca24d..ba7b7e26 100644
|
||||
--- a/rust/pv_core/src/error.rs
|
||||
+++ b/rust/pv_core/src/error.rs
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
+use crate::uv::SecretId;
|
||||
+
|
||||
/// Result type for this crate
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
@@ -70,6 +72,11 @@ pub enum Error {
|
||||
#[error("The attestation request does not specify a measurement size or measurement data.")]
|
||||
BinArcbNoMeasurement,
|
||||
|
||||
+ #[error(
|
||||
+ "The secret with the ID {id} cannot be retrieved. The requested size is too large ({size})"
|
||||
+ )]
|
||||
+ InvalidRetrievableSecretType { id: SecretId, size: usize },
|
||||
+
|
||||
// errors from other crates
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
diff --git a/rust/pv_core/src/lib.rs b/rust/pv_core/src/lib.rs
|
||||
index 349c0b28..5922211f 100644
|
||||
--- a/rust/pv_core/src/lib.rs
|
||||
+++ b/rust/pv_core/src/lib.rs
|
||||
@@ -32,7 +32,7 @@ pub mod misc {
|
||||
/// [`crate::uv::UvCmd`]
|
||||
pub mod uv {
|
||||
pub use crate::uvdevice::attest::AttestationCmd;
|
||||
- pub use crate::uvdevice::secret::{AddCmd, ListCmd, LockCmd};
|
||||
+ pub use crate::uvdevice::secret::{AddCmd, ListCmd, LockCmd, RetrieveCmd};
|
||||
pub use crate::uvdevice::secret_list::{ListableSecretType, SecretEntry, SecretId, SecretList};
|
||||
pub use crate::uvdevice::{ConfigUid, UvCmd, UvDevice, UvDeviceInfo, UvFlags, UvcSuccess};
|
||||
}
|
||||
diff --git a/rust/pv_core/src/uvdevice/secret.rs b/rust/pv_core/src/uvdevice/secret.rs
|
||||
index 6c22b6ed..263f17d5 100644
|
||||
--- a/rust/pv_core/src/uvdevice/secret.rs
|
||||
+++ b/rust/pv_core/src/uvdevice/secret.rs
|
||||
@@ -3,8 +3,15 @@
|
||||
// Copyright IBM Corp. 2023
|
||||
|
||||
use super::ffi;
|
||||
-use crate::{request::MagicValue, uv::UvCmd, uvsecret::AddSecretMagic, Error, Result, PAGESIZE};
|
||||
-use std::io::Read;
|
||||
+use crate::{
|
||||
+ request::{Confidential, MagicValue},
|
||||
+ uv::{SecretEntry, UvCmd},
|
||||
+ uvsecret::AddSecretMagic,
|
||||
+ Error, Result, PAGESIZE,
|
||||
+};
|
||||
+use log::debug;
|
||||
+use std::{io::Read, mem::size_of_val};
|
||||
+use zerocopy::AsBytes;
|
||||
|
||||
/// _List Secrets_ Ultravisor command.
|
||||
///
|
||||
@@ -116,3 +123,89 @@ impl UvCmd for LockCmd {
|
||||
}
|
||||
}
|
||||
}
|
||||
+
|
||||
+/// Retrieve a secret value from UV store
|
||||
+#[derive(Debug)]
|
||||
+pub struct RetrieveCmd {
|
||||
+ entry: SecretEntry,
|
||||
+ key: Confidential<Vec<u8>>,
|
||||
+}
|
||||
+
|
||||
+impl RetrieveCmd {
|
||||
+ /// Maximum size of a retrieved key (=2 pages)
|
||||
+ pub const MAX_SIZE: usize = ffi::UVIO_RETR_SECRET_MAX_LEN;
|
||||
+
|
||||
+ /// Create a retrieve-secret UVC from a [`SecretEntry`].
|
||||
+ ///
|
||||
+ /// This uses the index of the secret entry for the UVC.
|
||||
+ pub fn from_entry(entry: SecretEntry) -> Result<Self> {
|
||||
+ entry.try_into()
|
||||
+ }
|
||||
+
|
||||
+ /// Transform a [`RetrieveCmd`] into a key-vector.
|
||||
+ ///
|
||||
+ /// Only makes sense to call after a successful UVC execution.
|
||||
+ pub fn into_key(self) -> Confidential<Vec<u8>> {
|
||||
+ self.key
|
||||
+ }
|
||||
+
|
||||
+ /// Get the secret entry
|
||||
+ ///
|
||||
+ /// Get the secret entry that is used as metadata to retrieve the secret
|
||||
+ pub fn meta_data(&self) -> &SecretEntry {
|
||||
+ &self.entry
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl TryFrom<SecretEntry> for RetrieveCmd {
|
||||
+ type Error = Error;
|
||||
+
|
||||
+ fn try_from(entry: SecretEntry) -> Result<Self> {
|
||||
+ let len = entry.secret_size() as usize;
|
||||
+
|
||||
+ // Next to impossible if the secret entry is a valid response from UV
|
||||
+ if len > Self::MAX_SIZE {
|
||||
+ return Err(Error::InvalidRetrievableSecretType {
|
||||
+ id: entry.secret_id().to_owned(),
|
||||
+ size: len,
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ // Ensure that an u16 fits into the buffer.
|
||||
+ let size = std::cmp::max(size_of_val(&entry.index()), len);
|
||||
+ debug!("Create a buf with {} elements", size);
|
||||
+ let mut buf = vec![0; size];
|
||||
+ // The IOCTL expects the secret index in the first two bytes of the buffer. They will be
|
||||
+ // overwritten in the response
|
||||
+ entry.index_be().write_to_prefix(&mut buf).unwrap();
|
||||
+ Ok(Self {
|
||||
+ entry,
|
||||
+ key: buf.into(),
|
||||
+ })
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl UvCmd for RetrieveCmd {
|
||||
+ const UV_IOCTL_NR: u8 = ffi::UVIO_IOCTL_RETR_SECRET_NR;
|
||||
+
|
||||
+ fn rc_fmt(&self, rc: u16, _: u16) -> Option<&'static str> {
|
||||
+ match rc {
|
||||
+ // should not appear (TM), software creates request from a list item
|
||||
+ 0x0009 => Some("the allocated buffer is to small to store the secret"),
|
||||
+ // should not appear (TM), kernel allocates the memory
|
||||
+ 0x0102 => {
|
||||
+ Some("access exception recognized when accessing retrieved secret storage area")
|
||||
+ }
|
||||
+ // should not appear (TM), software creates request from a list item
|
||||
+ 0x010f => Some("the Secret Store is empty"),
|
||||
+ // should not appear (TM), software creates request from a list item
|
||||
+ 0x0110 => Some("the Secret Store does not contain a secret with the specified index"),
|
||||
+ 0x0111 => Some("the secret is not retrievable"),
|
||||
+ _ => None,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn data(&mut self) -> Option<&mut [u8]> {
|
||||
+ Some(self.key.value_mut())
|
||||
+ }
|
||||
+}
|
||||
diff --git a/rust/pv_core/src/uvdevice/secret_list.rs b/rust/pv_core/src/uvdevice/secret_list.rs
|
||||
index d20928b5..0a8af504 100644
|
||||
--- a/rust/pv_core/src/uvdevice/secret_list.rs
|
||||
+++ b/rust/pv_core/src/uvdevice/secret_list.rs
|
||||
@@ -110,6 +110,11 @@ impl SecretEntry {
|
||||
self.index.get()
|
||||
}
|
||||
|
||||
+ /// Returns the index of this [`SecretEntry`] in BE.
|
||||
+ pub(crate) fn index_be(&self) -> &U16<BigEndian> {
|
||||
+ &self.index
|
||||
+ }
|
||||
+
|
||||
/// Returns the secret type of this [`SecretEntry`].
|
||||
pub fn stype(&self) -> ListableSecretType {
|
||||
self.stype.into()
|
||||
@@ -127,6 +132,16 @@ impl SecretEntry {
|
||||
pub fn id(&self) -> &[u8] {
|
||||
self.id.as_ref()
|
||||
}
|
||||
+
|
||||
+ /// Get the id as [`SecretId`] reference
|
||||
+ pub(crate) fn secret_id(&self) -> &SecretId {
|
||||
+ &self.id
|
||||
+ }
|
||||
+
|
||||
+ /// Returns the secret size of this [`SecretEntry`].
|
||||
+ pub fn secret_size(&self) -> u32 {
|
||||
+ self.len.get()
|
||||
+ }
|
||||
}
|
||||
|
||||
impl Display for SecretEntry {
|
710
s390-tools-General-update-05.patch
Normal file
710
s390-tools-General-update-05.patch
Normal file
@ -0,0 +1,710 @@
|
||||
From 4af137f4fad8638169ccf0ddcb6dc4b0fe8fb1c1 Mon Sep 17 00:00:00 2001
|
||||
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Date: Tue, 5 Mar 2024 12:16:44 +0100
|
||||
Subject: [PATCH] rust/pv_core: Support for listing Retrievable Secrets
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Add support for listing retrievable secrets in the List Secrets UVC.
|
||||
|
||||
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_core/src/lib.rs | 2 +
|
||||
rust/pv_core/src/uvdevice.rs | 1 +
|
||||
rust/pv_core/src/uvdevice/retr_secret.rs | 399 +++++++++++++++++++++++
|
||||
rust/pv_core/src/uvdevice/secret_list.rs | 157 +++++++--
|
||||
4 files changed, 536 insertions(+), 23 deletions(-)
|
||||
create mode 100644 rust/pv_core/src/uvdevice/retr_secret.rs
|
||||
|
||||
diff --git a/rust/pv_core/src/lib.rs b/rust/pv_core/src/lib.rs
|
||||
index 5922211f..caebfcea 100644
|
||||
--- a/rust/pv_core/src/lib.rs
|
||||
+++ b/rust/pv_core/src/lib.rs
|
||||
@@ -32,6 +32,8 @@ pub mod misc {
|
||||
/// [`crate::uv::UvCmd`]
|
||||
pub mod uv {
|
||||
pub use crate::uvdevice::attest::AttestationCmd;
|
||||
+ pub use crate::uvdevice::retr_secret::RetrievableSecret;
|
||||
+ pub use crate::uvdevice::retr_secret::{AesSizes, AesXtsSizes, EcCurves, HmacShaSizes};
|
||||
pub use crate::uvdevice::secret::{AddCmd, ListCmd, LockCmd, RetrieveCmd};
|
||||
pub use crate::uvdevice::secret_list::{ListableSecretType, SecretEntry, SecretId, SecretList};
|
||||
pub use crate::uvdevice::{ConfigUid, UvCmd, UvDevice, UvDeviceInfo, UvFlags, UvcSuccess};
|
||||
diff --git a/rust/pv_core/src/uvdevice.rs b/rust/pv_core/src/uvdevice.rs
|
||||
index d4176815..e9848243 100644
|
||||
--- a/rust/pv_core/src/uvdevice.rs
|
||||
+++ b/rust/pv_core/src/uvdevice.rs
|
||||
@@ -25,6 +25,7 @@ mod info;
|
||||
mod test;
|
||||
pub(crate) use ffi::uv_ioctl;
|
||||
pub mod attest;
|
||||
+pub mod retr_secret;
|
||||
pub mod secret;
|
||||
pub mod secret_list;
|
||||
|
||||
diff --git a/rust/pv_core/src/uvdevice/retr_secret.rs b/rust/pv_core/src/uvdevice/retr_secret.rs
|
||||
new file mode 100644
|
||||
index 00000000..490152b4
|
||||
--- /dev/null
|
||||
+++ b/rust/pv_core/src/uvdevice/retr_secret.rs
|
||||
@@ -0,0 +1,399 @@
|
||||
+// SPDX-License-Identifier: MIT
|
||||
+//
|
||||
+// Copyright IBM Corp. 2024
|
||||
+
|
||||
+use crate::uv::{ListableSecretType, RetrieveCmd};
|
||||
+use serde::{Deserialize, Serialize, Serializer};
|
||||
+use std::fmt::Display;
|
||||
+
|
||||
+/// Allowed sizes for AES keys
|
||||
+#[non_exhaustive]
|
||||
+#[derive(PartialEq, Eq, Debug)]
|
||||
+pub enum AesSizes {
|
||||
+ /// 128 bit key
|
||||
+ Bits128,
|
||||
+ /// 192 bit key
|
||||
+ Bits192,
|
||||
+ /// 256 bit key
|
||||
+ Bits256,
|
||||
+}
|
||||
+
|
||||
+impl AesSizes {
|
||||
+ /// Construct the key-size from the bit-size.
|
||||
+ ///
|
||||
+ /// Returns [`None`] if the bit-size is not supported.
|
||||
+ pub fn from_bits(bits: u32) -> Option<Self> {
|
||||
+ match bits {
|
||||
+ 128 => Some(Self::Bits128),
|
||||
+ 192 => Some(Self::Bits192),
|
||||
+ 256 => Some(Self::Bits256),
|
||||
+ _ => None,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /// Returns the bit-size for the key-type
|
||||
+ const fn bit_size(&self) -> u32 {
|
||||
+ match self {
|
||||
+ Self::Bits128 => 128,
|
||||
+ Self::Bits192 => 192,
|
||||
+ Self::Bits256 => 256,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl Display for AesSizes {
|
||||
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
+ write!(f, "{}", self.bit_size())
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// Allowed sizes for AES-XTS keys
|
||||
+#[non_exhaustive]
|
||||
+#[derive(PartialEq, Eq, Debug)]
|
||||
+pub enum AesXtsSizes {
|
||||
+ /// Two AES 128 bit keys
|
||||
+ Bits128,
|
||||
+ /// Two AES 256 bit keys
|
||||
+ Bits256,
|
||||
+}
|
||||
+
|
||||
+impl AesXtsSizes {
|
||||
+ /// Construct the key-size from the bit-size.
|
||||
+ ///
|
||||
+ /// It's a key containing two keys; bit-size is half the number of bits it has
|
||||
+ /// Returns [`None`] if the bit-size is not supported.
|
||||
+ pub fn from_bits(bits: u32) -> Option<Self> {
|
||||
+ match bits {
|
||||
+ 128 => Some(Self::Bits128),
|
||||
+ 256 => Some(Self::Bits256),
|
||||
+ _ => None,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /// Returns the bit-size for the key-type
|
||||
+ ///
|
||||
+ /// It's a key containing two keys: bit-size is half the number of bits it has
|
||||
+ const fn bit_size(&self) -> u32 {
|
||||
+ match self {
|
||||
+ Self::Bits128 => 128,
|
||||
+ Self::Bits256 => 256,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl Display for AesXtsSizes {
|
||||
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
+ write!(f, "{}", self.bit_size())
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// Allowed sizes for HMAC-SHA keys
|
||||
+#[non_exhaustive]
|
||||
+#[derive(PartialEq, Eq, Debug)]
|
||||
+pub enum HmacShaSizes {
|
||||
+ /// SHA 256 bit
|
||||
+ Sha256,
|
||||
+ /// SHA 512 bit
|
||||
+ Sha512,
|
||||
+}
|
||||
+
|
||||
+impl HmacShaSizes {
|
||||
+ /// Construct the key-size from the sha-size.
|
||||
+ ///
|
||||
+ /// FW expects maximum resistance keys (double the SHA size).
|
||||
+ /// The `sha_size` is half of the number of bits in the key
|
||||
+ /// Returns [`None`] if the `sha_size` is not supported.
|
||||
+ pub fn from_sha_size(sha_size: u32) -> Option<Self> {
|
||||
+ match sha_size {
|
||||
+ 256 => Some(Self::Sha256),
|
||||
+ 512 => Some(Self::Sha512),
|
||||
+ _ => None,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /// Returns the sha-size for the key-type
|
||||
+ ///
|
||||
+ /// FW expects maximum resistance keys (double the SHA size).
|
||||
+ /// The `sha_size` is half of the number of bits in the key
|
||||
+ const fn sha_size(&self) -> u32 {
|
||||
+ match self {
|
||||
+ Self::Sha256 => 256,
|
||||
+ Self::Sha512 => 512,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl Display for HmacShaSizes {
|
||||
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
+ write!(f, "{}", self.sha_size())
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// Allowed curves for EC private keys
|
||||
+#[non_exhaustive]
|
||||
+#[derive(PartialEq, Eq, Debug)]
|
||||
+pub enum EcCurves {
|
||||
+ /// secp256r1 or prime256v1 curve
|
||||
+ Secp256R1,
|
||||
+ /// secp384p1 curve
|
||||
+ Secp384R1,
|
||||
+ /// secp521r1 curve
|
||||
+ Secp521R1,
|
||||
+ /// ed25519 curve
|
||||
+ Ed25519,
|
||||
+ /// ed448 curve
|
||||
+ Ed448,
|
||||
+}
|
||||
+
|
||||
+impl EcCurves {
|
||||
+ const fn exp_size(&self) -> usize {
|
||||
+ match self {
|
||||
+ Self::Secp256R1 => 32,
|
||||
+ Self::Secp384R1 => 48,
|
||||
+ Self::Secp521R1 => 80,
|
||||
+ Self::Ed25519 => 32,
|
||||
+ Self::Ed448 => 64,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /// Resizes the raw key to the expected size.
|
||||
+ ///
|
||||
+ /// See [`Vec::resize`]
|
||||
+ pub fn resize_raw_key(&self, mut raw: Vec<u8>) -> Vec<u8> {
|
||||
+ raw.resize(self.exp_size(), 0);
|
||||
+ raw
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// The names have to stay constant, otherwise the PEM contains invalid types
|
||||
+impl Display for EcCurves {
|
||||
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
+ match self {
|
||||
+ Self::Secp256R1 => write!(f, "SECP256R1"),
|
||||
+ Self::Secp384R1 => write!(f, "SECP384R1"),
|
||||
+ Self::Secp521R1 => write!(f, "SECP521R1"),
|
||||
+ Self::Ed25519 => write!(f, "ED25519"),
|
||||
+ Self::Ed448 => write!(f, "ED448"),
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// Retrievable Secret types
|
||||
+#[non_exhaustive]
|
||||
+#[derive(PartialEq, Eq, Debug)]
|
||||
+pub enum RetrievableSecret {
|
||||
+ /// Plain-text secret
|
||||
+ PlainText,
|
||||
+ /// Protected AES key
|
||||
+ Aes(AesSizes),
|
||||
+ /// Protected AES-XTS key
|
||||
+ AesXts(AesXtsSizes),
|
||||
+ /// Protected HMAC-SHA key
|
||||
+ HmacSha(HmacShaSizes),
|
||||
+ /// Protected EC-private key
|
||||
+ Ec(EcCurves),
|
||||
+}
|
||||
+
|
||||
+// The names have to stay constant, otherwise the PEM contains invalid/unknown types
|
||||
+impl Display for RetrievableSecret {
|
||||
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
+ // Alternate representation: Omit sizes/curves
|
||||
+ if f.alternate() {
|
||||
+ match self {
|
||||
+ Self::PlainText => write!(f, "PLAINTEXT"),
|
||||
+ Self::Aes(_) => write!(f, "AES-KEY"),
|
||||
+ Self::AesXts(_) => write!(f, "AES-XTS-KEY"),
|
||||
+ Self::HmacSha(_) => write!(f, "HMAC-SHA-KEY"),
|
||||
+ Self::Ec(_) => write!(f, "EC-PRIVATE-KEY"),
|
||||
+ }
|
||||
+ } else {
|
||||
+ match self {
|
||||
+ Self::PlainText => write!(f, "PLAINTEXT"),
|
||||
+ Self::Aes(s) => write!(f, "AES-{s}-KEY"),
|
||||
+ Self::AesXts(s) => write!(f, "AES-XTS-{s}-KEY"),
|
||||
+ Self::HmacSha(s) => write!(f, "HMAC-SHA-{s}-KEY"),
|
||||
+ Self::Ec(c) => write!(f, "EC-{c}-PRIVATE-KEY"),
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl RetrievableSecret {
|
||||
+ /// Report expected input types
|
||||
+ pub fn expected(&self) -> String {
|
||||
+ match self {
|
||||
+ Self::PlainText => format!("less than {}", RetrieveCmd::MAX_SIZE),
|
||||
+ Self::Aes(_) => "128, 192, or 256".to_string(),
|
||||
+ Self::AesXts(_) => "128 or 256".to_string(),
|
||||
+ Self::HmacSha(_) => "256 or 512".to_string(),
|
||||
+ Self::Ec(_) => "secp256r1, secp384r1, secp521r1, ed25519, or ed448".to_string(),
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl From<&RetrievableSecret> for u16 {
|
||||
+ fn from(value: &RetrievableSecret) -> Self {
|
||||
+ match value {
|
||||
+ RetrievableSecret::PlainText => ListableSecretType::PLAINTEXT,
|
||||
+ RetrievableSecret::Aes(AesSizes::Bits128) => ListableSecretType::AES_128_KEY,
|
||||
+ RetrievableSecret::Aes(AesSizes::Bits192) => ListableSecretType::AES_192_KEY,
|
||||
+ RetrievableSecret::Aes(AesSizes::Bits256) => ListableSecretType::AES_256_KEY,
|
||||
+ RetrievableSecret::AesXts(AesXtsSizes::Bits128) => ListableSecretType::AES_128_XTS_KEY,
|
||||
+ RetrievableSecret::AesXts(AesXtsSizes::Bits256) => ListableSecretType::AES_256_XTS_KEY,
|
||||
+ RetrievableSecret::HmacSha(HmacShaSizes::Sha256) => {
|
||||
+ ListableSecretType::HMAC_SHA_256_KEY
|
||||
+ }
|
||||
+ RetrievableSecret::HmacSha(HmacShaSizes::Sha512) => {
|
||||
+ ListableSecretType::HMAC_SHA_512_KEY
|
||||
+ }
|
||||
+ RetrievableSecret::Ec(EcCurves::Secp256R1) => ListableSecretType::ECDSA_P256_KEY,
|
||||
+ RetrievableSecret::Ec(EcCurves::Secp384R1) => ListableSecretType::ECDSA_P384_KEY,
|
||||
+ RetrievableSecret::Ec(EcCurves::Secp521R1) => ListableSecretType::ECDSA_P521_KEY,
|
||||
+ RetrievableSecret::Ec(EcCurves::Ed25519) => ListableSecretType::ECDSA_ED25519_KEY,
|
||||
+ RetrievableSecret::Ec(EcCurves::Ed448) => ListableSecretType::ECDSA_ED448_KEY,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// serializes to: <secret type nb> (String name)
|
||||
+impl Serialize for RetrievableSecret {
|
||||
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
+ where
|
||||
+ S: Serializer,
|
||||
+ {
|
||||
+ let id: u16 = self.into();
|
||||
+ serializer.serialize_str(&format!("{id} ({self})"))
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// deserializes from the secret type nb only
|
||||
+impl<'de> Deserialize<'de> for RetrievableSecret {
|
||||
+ fn deserialize<D>(de: D) -> Result<Self, D::Error>
|
||||
+ where
|
||||
+ D: serde::Deserializer<'de>,
|
||||
+ {
|
||||
+ struct RetrSecretVisitor;
|
||||
+ impl<'de> serde::de::Visitor<'de> for RetrSecretVisitor {
|
||||
+ type Value = RetrievableSecret;
|
||||
+
|
||||
+ fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
+ fmt.write_str(
|
||||
+ "a retrievable secret type: `<number> (String name)` number in [3,10]|[17,21]",
|
||||
+ )
|
||||
+ }
|
||||
+ fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
|
||||
+ where
|
||||
+ E: serde::de::Error,
|
||||
+ {
|
||||
+ let (n, _) = s.split_once(' ').ok_or(serde::de::Error::invalid_value(
|
||||
+ serde::de::Unexpected::Str(s),
|
||||
+ &self,
|
||||
+ ))?;
|
||||
+ let id: u16 = n.parse().map_err(|_| {
|
||||
+ serde::de::Error::invalid_value(serde::de::Unexpected::Str(n), &self)
|
||||
+ })?;
|
||||
+ let listable: ListableSecretType = id.into();
|
||||
+ match listable {
|
||||
+ ListableSecretType::Retrievable(r) => Ok(r),
|
||||
+ _ => Err(serde::de::Error::invalid_value(
|
||||
+ serde::de::Unexpected::Unsigned(id.into()),
|
||||
+ &self,
|
||||
+ )),
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ de.deserialize_str(RetrSecretVisitor)
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#[cfg(test)]
|
||||
+mod test {
|
||||
+ use serde_test::{assert_tokens, Token};
|
||||
+
|
||||
+ use super::*;
|
||||
+
|
||||
+ #[test]
|
||||
+ fn retr_serde_plain() {
|
||||
+ let retr = RetrievableSecret::PlainText;
|
||||
+ assert_tokens(&retr, &[Token::Str("3 (PLAINTEXT)")]);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn retr_serde_aes() {
|
||||
+ let retr = RetrievableSecret::Aes(AesSizes::Bits192);
|
||||
+ assert_tokens(&retr, &[Token::Str("5 (AES-192-KEY)")]);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn retr_serde_aes_xts() {
|
||||
+ let retr = RetrievableSecret::AesXts(AesXtsSizes::Bits128);
|
||||
+ assert_tokens(&retr, &[Token::Str("7 (AES-XTS-128-KEY)")]);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn retr_serde_hmac() {
|
||||
+ let retr = RetrievableSecret::HmacSha(HmacShaSizes::Sha256);
|
||||
+ assert_tokens(&retr, &[Token::Str("9 (HMAC-SHA-256-KEY)")]);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn retr_serde_es() {
|
||||
+ let retr = RetrievableSecret::Ec(EcCurves::Secp521R1);
|
||||
+ assert_tokens(&retr, &[Token::Str("19 (EC-SECP521R1-PRIVATE-KEY)")]);
|
||||
+ }
|
||||
+
|
||||
+ // Ensure that the string representation of the retrievable types stay constant, or PEM will have
|
||||
+ // different, incompatible types
|
||||
+ #[test]
|
||||
+ fn stable_type_names() {
|
||||
+ assert_eq!("PLAINTEXT", RetrievableSecret::PlainText.to_string());
|
||||
+ assert_eq!(
|
||||
+ "AES-128-KEY",
|
||||
+ RetrievableSecret::Aes(AesSizes::Bits128).to_string()
|
||||
+ );
|
||||
+ assert_eq!(
|
||||
+ "AES-192-KEY",
|
||||
+ RetrievableSecret::Aes(AesSizes::Bits192).to_string()
|
||||
+ );
|
||||
+ assert_eq!(
|
||||
+ "AES-256-KEY",
|
||||
+ RetrievableSecret::Aes(AesSizes::Bits256).to_string()
|
||||
+ );
|
||||
+ assert_eq!(
|
||||
+ "AES-XTS-128-KEY",
|
||||
+ RetrievableSecret::AesXts(AesXtsSizes::Bits128).to_string()
|
||||
+ );
|
||||
+ assert_eq!(
|
||||
+ "AES-XTS-256-KEY",
|
||||
+ RetrievableSecret::AesXts(AesXtsSizes::Bits256).to_string()
|
||||
+ );
|
||||
+ assert_eq!(
|
||||
+ "HMAC-SHA-256-KEY",
|
||||
+ RetrievableSecret::HmacSha(HmacShaSizes::Sha256).to_string()
|
||||
+ );
|
||||
+ assert_eq!(
|
||||
+ "HMAC-SHA-512-KEY",
|
||||
+ RetrievableSecret::HmacSha(HmacShaSizes::Sha512).to_string()
|
||||
+ );
|
||||
+ assert_eq!(
|
||||
+ "EC-SECP256R1-PRIVATE-KEY",
|
||||
+ RetrievableSecret::Ec(EcCurves::Secp256R1).to_string()
|
||||
+ );
|
||||
+ assert_eq!(
|
||||
+ "EC-SECP384R1-PRIVATE-KEY",
|
||||
+ RetrievableSecret::Ec(EcCurves::Secp384R1).to_string()
|
||||
+ );
|
||||
+ assert_eq!(
|
||||
+ "EC-SECP521R1-PRIVATE-KEY",
|
||||
+ RetrievableSecret::Ec(EcCurves::Secp521R1).to_string()
|
||||
+ );
|
||||
+ assert_eq!(
|
||||
+ "EC-ED25519-PRIVATE-KEY",
|
||||
+ RetrievableSecret::Ec(EcCurves::Ed25519).to_string()
|
||||
+ );
|
||||
+ assert_eq!(
|
||||
+ "EC-ED448-PRIVATE-KEY",
|
||||
+ RetrievableSecret::Ec(EcCurves::Ed448).to_string()
|
||||
+ );
|
||||
+ }
|
||||
+}
|
||||
diff --git a/rust/pv_core/src/uvdevice/secret_list.rs b/rust/pv_core/src/uvdevice/secret_list.rs
|
||||
index 0a8af504..4e955010 100644
|
||||
--- a/rust/pv_core/src/uvdevice/secret_list.rs
|
||||
+++ b/rust/pv_core/src/uvdevice/secret_list.rs
|
||||
@@ -2,9 +2,14 @@
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
-use crate::assert_size;
|
||||
-use crate::{misc::to_u16, uv::ListCmd, uvdevice::UvCmd, Error, Result};
|
||||
-use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
+use crate::{
|
||||
+ assert_size,
|
||||
+ misc::to_u16,
|
||||
+ uv::{AesSizes, AesXtsSizes, EcCurves, HmacShaSizes, ListCmd, RetrievableSecret},
|
||||
+ uvdevice::UvCmd,
|
||||
+ Error, Result,
|
||||
+};
|
||||
+use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt};
|
||||
use serde::{Deserialize, Serialize, Serializer};
|
||||
use std::{
|
||||
fmt::Display,
|
||||
@@ -18,7 +23,7 @@ use zerocopy::{AsBytes, FromBytes, FromZeroes, U16, U32};
|
||||
///
|
||||
/// (de)serializes itself in/from a hex-string
|
||||
#[repr(C)]
|
||||
-#[derive(PartialEq, Eq, AsBytes, FromZeroes, FromBytes, Debug, Clone)]
|
||||
+#[derive(PartialEq, Eq, AsBytes, FromZeroes, FromBytes, Debug, Clone, Default)]
|
||||
pub struct SecretId([u8; Self::ID_SIZE]);
|
||||
assert_size!(SecretId, SecretId::ID_SIZE);
|
||||
|
||||
@@ -94,11 +99,11 @@ impl SecretEntry {
|
||||
/// Create a new entry for a [`SecretList`].
|
||||
///
|
||||
/// The content of this entry will very likely not represent the status of the guest in the
|
||||
- /// Ultravisor. Use of [`SecretList::decode`] in any non-test environments is encuraged.
|
||||
+ /// Ultravisor. Use of [`SecretList::decode`] in any non-test environments is encouraged.
|
||||
pub fn new(index: u16, stype: ListableSecretType, id: SecretId, secret_len: u32) -> Self {
|
||||
Self {
|
||||
index: index.into(),
|
||||
- stype: stype.into(),
|
||||
+ stype: U16::new(stype.into()),
|
||||
len: secret_len.into(),
|
||||
res_8: 0,
|
||||
id,
|
||||
@@ -117,7 +122,7 @@ impl SecretEntry {
|
||||
|
||||
/// Returns the secret type of this [`SecretEntry`].
|
||||
pub fn stype(&self) -> ListableSecretType {
|
||||
- self.stype.into()
|
||||
+ self.stype.get().into()
|
||||
}
|
||||
|
||||
/// Returns a reference to the id of this [`SecretEntry`].
|
||||
@@ -146,7 +151,7 @@ impl SecretEntry {
|
||||
|
||||
impl Display for SecretEntry {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
- let stype: ListableSecretType = self.stype.into();
|
||||
+ let stype: ListableSecretType = self.stype.get().into();
|
||||
writeln!(f, "{} {}:", self.index, stype)?;
|
||||
write!(f, " ")?;
|
||||
for b in self.id.as_ref() {
|
||||
@@ -298,51 +303,115 @@ fn ser_u16<S: Serializer>(v: &U16<BigEndian>, ser: S) -> Result<S::Ok, S::Error>
|
||||
pub enum ListableSecretType {
|
||||
/// Association Secret
|
||||
Association,
|
||||
+ /// Retrievable key
|
||||
+ Retrievable(RetrievableSecret),
|
||||
+
|
||||
/// Invalid secret type, that should never appear in a list
|
||||
///
|
||||
/// 0 is reserved
|
||||
- /// 1 is Null secret, with no id and not listable
|
||||
+ /// 1 is Null secret, with no id and not list-able
|
||||
Invalid(u16),
|
||||
/// Unknown secret type
|
||||
Unknown(u16),
|
||||
}
|
||||
|
||||
impl ListableSecretType {
|
||||
- /// UV type id for an association secret
|
||||
- pub const ASSOCIATION: u16 = 0x0002;
|
||||
- /// UV type id for a null secret
|
||||
- pub const NULL: u16 = 0x0001;
|
||||
const RESERVED_0: u16 = 0x0000;
|
||||
+ /// UV secret-type id for a null secret
|
||||
+ pub const NULL: u16 = 0x0001;
|
||||
+ /// UV secret-type id for an association secret
|
||||
+ pub const ASSOCIATION: u16 = 0x0002;
|
||||
+ /// UV secret-type id for a plain text secret
|
||||
+ pub const PLAINTEXT: u16 = 0x0003;
|
||||
+ /// UV secret-type id for an aes-128-key secret
|
||||
+ pub const AES_128_KEY: u16 = 0x0004;
|
||||
+ /// UV secret-type id for an aes-192-key secret
|
||||
+ pub const AES_192_KEY: u16 = 0x0005;
|
||||
+ /// UV secret-type id for an aes-256-key secret
|
||||
+ pub const AES_256_KEY: u16 = 0x0006;
|
||||
+ /// UV secret-type id for an aes-xts-128-key secret
|
||||
+ pub const AES_128_XTS_KEY: u16 = 0x0007;
|
||||
+ /// UV secret-type id for an aes-xts-256-key secret
|
||||
+ pub const AES_256_XTS_KEY: u16 = 0x0008;
|
||||
+ /// UV secret-type id for an hmac-sha-256-key secret
|
||||
+ pub const HMAC_SHA_256_KEY: u16 = 0x0009;
|
||||
+ /// UV secret-type id for an hmac-sha-512-key secret
|
||||
+ pub const HMAC_SHA_512_KEY: u16 = 0x000a;
|
||||
+ // 0x000b - 0x0010 reserved
|
||||
+ /// UV secret-type id for an ecdsa-p256-private-key secret
|
||||
+ pub const ECDSA_P256_KEY: u16 = 0x0011;
|
||||
+ /// UV secret-type id for an ecdsa-p384-private-key secret
|
||||
+ pub const ECDSA_P384_KEY: u16 = 0x0012;
|
||||
+ /// UV secret-type id for an ecdsa-p521-private-key secret
|
||||
+ pub const ECDSA_P521_KEY: u16 = 0x0013;
|
||||
+ /// UV secret-type id for an ed25519-private-key secret
|
||||
+ pub const ECDSA_ED25519_KEY: u16 = 0x0014;
|
||||
+ /// UV secret-type id for an ed448-private-key secret
|
||||
+ pub const ECDSA_ED448_KEY: u16 = 0x0015;
|
||||
}
|
||||
|
||||
impl Display for ListableSecretType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Association => write!(f, "Association"),
|
||||
- Self::Invalid(n) => write!(f, "Invalid({n})"),
|
||||
- Self::Unknown(n) => write!(f, "Unknown({n})"),
|
||||
+ Self::Invalid(n) => write!(f, "Invalid(0x{n:04x})"),
|
||||
+ Self::Unknown(n) => write!(f, "Unknown(0x{n:04x})"),
|
||||
+ Self::Retrievable(r) => write!(f, "{r}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-impl From<U16<BigEndian>> for ListableSecretType {
|
||||
- fn from(value: U16<BigEndian>) -> Self {
|
||||
- match value.get() {
|
||||
+impl<O: ByteOrder> From<U16<O>> for ListableSecretType {
|
||||
+ fn from(value: U16<O>) -> Self {
|
||||
+ value.get().into()
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl From<u16> for ListableSecretType {
|
||||
+ fn from(value: u16) -> Self {
|
||||
+ match value {
|
||||
Self::RESERVED_0 => Self::Invalid(Self::RESERVED_0),
|
||||
Self::NULL => Self::Invalid(Self::NULL),
|
||||
Self::ASSOCIATION => Self::Association,
|
||||
+ Self::PLAINTEXT => Self::Retrievable(RetrievableSecret::PlainText),
|
||||
+ Self::AES_128_KEY => Self::Retrievable(RetrievableSecret::Aes(AesSizes::Bits128)),
|
||||
+ Self::AES_192_KEY => Self::Retrievable(RetrievableSecret::Aes(AesSizes::Bits192)),
|
||||
+ Self::AES_256_KEY => Self::Retrievable(RetrievableSecret::Aes(AesSizes::Bits256)),
|
||||
+ Self::AES_128_XTS_KEY => {
|
||||
+ Self::Retrievable(RetrievableSecret::AesXts(AesXtsSizes::Bits128))
|
||||
+ }
|
||||
+ Self::AES_256_XTS_KEY => {
|
||||
+ Self::Retrievable(RetrievableSecret::AesXts(AesXtsSizes::Bits256))
|
||||
+ }
|
||||
+ Self::HMAC_SHA_256_KEY => {
|
||||
+ Self::Retrievable(RetrievableSecret::HmacSha(HmacShaSizes::Sha256))
|
||||
+ }
|
||||
+ Self::HMAC_SHA_512_KEY => {
|
||||
+ Self::Retrievable(RetrievableSecret::HmacSha(HmacShaSizes::Sha512))
|
||||
+ }
|
||||
+ Self::ECDSA_P256_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Secp256R1)),
|
||||
+ Self::ECDSA_P384_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Secp384R1)),
|
||||
+ Self::ECDSA_P521_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Secp521R1)),
|
||||
+ Self::ECDSA_ED25519_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Ed25519)),
|
||||
+ Self::ECDSA_ED448_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Ed448)),
|
||||
n => Self::Unknown(n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-impl From<ListableSecretType> for U16<BigEndian> {
|
||||
+impl<O: ByteOrder> From<ListableSecretType> for U16<O> {
|
||||
+ fn from(value: ListableSecretType) -> Self {
|
||||
+ Self::new(value.into())
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl From<ListableSecretType> for u16 {
|
||||
fn from(value: ListableSecretType) -> Self {
|
||||
match value {
|
||||
ListableSecretType::Association => ListableSecretType::ASSOCIATION,
|
||||
ListableSecretType::Invalid(n) | ListableSecretType::Unknown(n) => n,
|
||||
+ ListableSecretType::Retrievable(r) => (&r).into(),
|
||||
}
|
||||
- .into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,8 +432,8 @@ where
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
- if s.len() != SecretId::ID_SIZE * 2 + 2 {
|
||||
- return Err(serde::de::Error::invalid_length(s.len(), &self));
|
||||
+ if s.len() != SecretId::ID_SIZE * 2 + "0x".len() {
|
||||
+ return Err(serde::de::Error::invalid_length(s.len() - 2, &self));
|
||||
}
|
||||
let nb = s.strip_prefix("0x").ok_or_else(|| {
|
||||
serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self)
|
||||
@@ -385,7 +454,6 @@ mod test {
|
||||
|
||||
use super::*;
|
||||
use std::io::{BufReader, BufWriter, Cursor};
|
||||
-
|
||||
#[test]
|
||||
fn dump_secret_entry() {
|
||||
const EXP: &[u8] = &[
|
||||
@@ -516,4 +584,47 @@ mod test {
|
||||
)],
|
||||
)
|
||||
}
|
||||
+
|
||||
+ #[test]
|
||||
+ fn secret_list_ser() {
|
||||
+ let list = SecretList {
|
||||
+ total_num_secrets: 0x112,
|
||||
+ secrets: vec![SecretEntry {
|
||||
+ index: 1.into(),
|
||||
+ stype: 2.into(),
|
||||
+ len: 32.into(),
|
||||
+ res_8: 0,
|
||||
+ id: SecretId::from([0; 32]),
|
||||
+ }],
|
||||
+ };
|
||||
+
|
||||
+ assert_ser_tokens(
|
||||
+ &list,
|
||||
+ &[
|
||||
+ Token::Struct {
|
||||
+ name: "SecretList",
|
||||
+ len: 2,
|
||||
+ },
|
||||
+ Token::String("total_num_secrets"),
|
||||
+ Token::U64(0x112),
|
||||
+ Token::String("secrets"),
|
||||
+ Token::Seq { len: Some(1) },
|
||||
+ Token::Struct {
|
||||
+ name: "SecretEntry",
|
||||
+ len: (4),
|
||||
+ },
|
||||
+ Token::String("index"),
|
||||
+ Token::U16(1),
|
||||
+ Token::String("stype"),
|
||||
+ Token::U16(2),
|
||||
+ Token::String("len"),
|
||||
+ Token::U32(32),
|
||||
+ Token::String("id"),
|
||||
+ Token::String("0x0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
+ Token::StructEnd,
|
||||
+ Token::SeqEnd,
|
||||
+ Token::StructEnd,
|
||||
+ ],
|
||||
+ )
|
||||
+ }
|
||||
}
|
877
s390-tools-General-update-06.patch
Normal file
877
s390-tools-General-update-06.patch
Normal file
@ -0,0 +1,877 @@
|
||||
From fd024387d710887bd2016658c44d4762a08c791c Mon Sep 17 00:00:00 2001
|
||||
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Date: Tue, 5 Mar 2024 12:19:22 +0100
|
||||
Subject: [PATCH] rust/pv: Retrievable secrets support
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Support retrievable secret for Add-Secret requests.
|
||||
|
||||
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/crypto.rs | 3 +-
|
||||
rust/pv/src/error.rs | 8 +
|
||||
rust/pv/src/lib.rs | 8 +-
|
||||
rust/pv/src/uvsecret.rs | 1 +
|
||||
rust/pv/src/uvsecret/guest_secret.rs | 399 +++++++++++++++++++++++++--
|
||||
rust/pv/src/uvsecret/retr_secret.rs | 234 ++++++++++++++++
|
||||
6 files changed, 631 insertions(+), 22 deletions(-)
|
||||
create mode 100644 rust/pv/src/uvsecret/retr_secret.rs
|
||||
|
||||
diff --git a/rust/pv/src/crypto.rs b/rust/pv/src/crypto.rs
|
||||
index 8f11d2b4..ebc85f72 100644
|
||||
--- a/rust/pv/src/crypto.rs
|
||||
+++ b/rust/pv/src/crypto.rs
|
||||
@@ -29,7 +29,6 @@ pub type Aes256XtsKey = Confidential<[u8; SymKeyType::AES_256_XTS_KEY_LEN]>;
|
||||
|
||||
/// SHA-512 digest length (in bytes)
|
||||
pub const SHA_512_HASH_LEN: usize = 64;
|
||||
-
|
||||
#[allow(dead_code)]
|
||||
pub(crate) const SHA_256_HASH_LEN: u32 = 32;
|
||||
#[allow(dead_code)]
|
||||
@@ -60,6 +59,8 @@ impl SymKeyType {
|
||||
pub const AES_256_XTS_KEY_LEN: usize = 64;
|
||||
/// AES256-XTS tweak length (in bytes)
|
||||
pub const AES_256_XTS_TWEAK_LEN: usize = 16;
|
||||
+ /// AES256 GCM Block length
|
||||
+ pub const AES_256_GCM_BLOCK_LEN: usize = 16;
|
||||
|
||||
/// Returns the tag length of the [`SymKeyType`] if it is an AEAD key
|
||||
pub const fn tag_len(&self) -> Option<usize> {
|
||||
diff --git a/rust/pv/src/error.rs b/rust/pv/src/error.rs
|
||||
index 3ba808f2..601b40f0 100644
|
||||
--- a/rust/pv/src/error.rs
|
||||
+++ b/rust/pv/src/error.rs
|
||||
@@ -109,6 +109,14 @@ pub enum Error {
|
||||
#[error("An ASCII string was expected, but non-ASCII characters were received.")]
|
||||
NonAscii,
|
||||
|
||||
+ #[error("Incorrect {what} for a {kind}. Is: {value}; expected: {exp}")]
|
||||
+ RetrInvKey {
|
||||
+ what: &'static str,
|
||||
+ kind: String,
|
||||
+ value: String,
|
||||
+ exp: String,
|
||||
+ },
|
||||
+
|
||||
// errors from other crates
|
||||
#[error(transparent)]
|
||||
PvCore(#[from] pv_core::Error),
|
||||
diff --git a/rust/pv/src/lib.rs b/rust/pv/src/lib.rs
|
||||
index ec31b9a4..43375669 100644
|
||||
--- a/rust/pv/src/lib.rs
|
||||
+++ b/rust/pv/src/lib.rs
|
||||
@@ -104,7 +104,12 @@ pub mod request {
|
||||
|
||||
/// Reexports some useful OpenSSL symbols
|
||||
pub mod openssl {
|
||||
- pub use openssl::{error::ErrorStack, hash::DigestBytes, pkey, x509};
|
||||
+ pub use openssl::{error::ErrorStack, hash::DigestBytes, nid::Nid, pkey, x509};
|
||||
+ // rust-OpenSSL does not define these NIDs
|
||||
+ #[allow(missing_docs)]
|
||||
+ pub const NID_ED25519: Nid = Nid::from_raw(openssl_sys::NID_ED25519);
|
||||
+ #[allow(missing_docs)]
|
||||
+ pub const NID_ED448: Nid = Nid::from_raw(openssl_sys::NID_ED448);
|
||||
}
|
||||
|
||||
pub use pv_core::request::*;
|
||||
@@ -118,6 +123,7 @@ pub mod secret {
|
||||
asrcb::{AddSecretFlags, AddSecretRequest, AddSecretVersion},
|
||||
ext_secret::ExtSecret,
|
||||
guest_secret::GuestSecret,
|
||||
+ retr_secret::{IbmProtectedKey, RetrievedSecret},
|
||||
user_data::verify_asrcb_and_get_user_data,
|
||||
};
|
||||
}
|
||||
diff --git a/rust/pv/src/uvsecret.rs b/rust/pv/src/uvsecret.rs
|
||||
index 343e4b05..c3b43bba 100644
|
||||
--- a/rust/pv/src/uvsecret.rs
|
||||
+++ b/rust/pv/src/uvsecret.rs
|
||||
@@ -10,4 +10,5 @@
|
||||
pub mod asrcb;
|
||||
pub mod ext_secret;
|
||||
pub mod guest_secret;
|
||||
+pub mod retr_secret;
|
||||
pub mod user_data;
|
||||
diff --git a/rust/pv/src/uvsecret/guest_secret.rs b/rust/pv/src/uvsecret/guest_secret.rs
|
||||
index 4f1db31c..3bad6d3c 100644
|
||||
--- a/rust/pv/src/uvsecret/guest_secret.rs
|
||||
+++ b/rust/pv/src/uvsecret/guest_secret.rs
|
||||
@@ -4,20 +4,34 @@
|
||||
|
||||
#[allow(unused_imports)] // used for more convenient docstring
|
||||
use super::asrcb::AddSecretRequest;
|
||||
-use crate::assert_size;
|
||||
use crate::{
|
||||
- crypto::{hash, random_array},
|
||||
- request::Confidential,
|
||||
- Result,
|
||||
+ assert_size,
|
||||
+ crypto::{hash, random_array, SymKeyType},
|
||||
+ request::{
|
||||
+ openssl::{NID_ED25519, NID_ED448},
|
||||
+ Confidential,
|
||||
+ },
|
||||
+ uv::{
|
||||
+ AesSizes, AesXtsSizes, EcCurves, HmacShaSizes, ListableSecretType, RetrievableSecret,
|
||||
+ RetrieveCmd, SecretId,
|
||||
+ },
|
||||
+ Error, Result,
|
||||
};
|
||||
use byteorder::BigEndian;
|
||||
-use openssl::hash::MessageDigest;
|
||||
-use pv_core::uv::{ListableSecretType, SecretId};
|
||||
+use openssl::{
|
||||
+ hash::MessageDigest,
|
||||
+ nid::Nid,
|
||||
+ pkey::{Id, PKey, Private},
|
||||
+};
|
||||
+use pv_core::static_assert;
|
||||
use serde::{Deserialize, Serialize};
|
||||
-use std::{convert::TryInto, fmt::Display};
|
||||
+use std::fmt::Display;
|
||||
use zerocopy::{AsBytes, U16, U32};
|
||||
|
||||
const ASSOC_SECRET_SIZE: usize = 32;
|
||||
+/// Maximum size of a plain-text secret payload (8190)
|
||||
+pub(crate) const MAX_SIZE_PLAIN_PAYLOAD: usize = RetrieveCmd::MAX_SIZE - 2;
|
||||
+static_assert!(MAX_SIZE_PLAIN_PAYLOAD == 8190);
|
||||
|
||||
/// A Secret to be added in [`AddSecretRequest`]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
@@ -36,13 +50,60 @@ pub enum GuestSecret {
|
||||
#[serde(skip)]
|
||||
secret: Confidential<[u8; ASSOC_SECRET_SIZE]>,
|
||||
},
|
||||
+ /// Retrievable key
|
||||
+ ///
|
||||
+ /// Create Retrievables using [`GuestSecret::retrievable`]
|
||||
+ /// Secret size is always valid for the type/kind
|
||||
+ Retrievable {
|
||||
+ /// Retrievable secret type
|
||||
+ kind: RetrievableSecret,
|
||||
+ /// Name of the secret
|
||||
+ name: String,
|
||||
+ /// SHA256 hash of [`GuestSecret::RetrievableKey::name`]
|
||||
+ id: SecretId,
|
||||
+ /// Confidential actual retrievable secret (32 bytes)
|
||||
+ #[serde(skip)]
|
||||
+ secret: Confidential<Vec<u8>>,
|
||||
+ },
|
||||
+}
|
||||
+
|
||||
+macro_rules! retr_constructor {
|
||||
+ ($(#[$err:meta])* | $(#[$kind:meta])* => $type: ty, $func: ident) => {
|
||||
+ /// Create a new
|
||||
+ $(#[$kind])*
|
||||
+ /// [`GuestSecret::Retrievable`] secret.
|
||||
+ ///
|
||||
+ /// * `name` - Name of the secret. Will be hashed into a 32 byte id
|
||||
+ /// * `secret` - the secret value
|
||||
+ ///
|
||||
+ /// # Errors
|
||||
+ ///
|
||||
+ $(#[$err])*
|
||||
+ pub fn $func(name: &str, secret: $type) -> Result<Self> {
|
||||
+ let (kind, secret) = $func(secret)?;
|
||||
+ Ok(Self::Retrievable {
|
||||
+ kind,
|
||||
+ name: name.to_string(),
|
||||
+ id: Self::name_to_id(name)?,
|
||||
+ secret,
|
||||
+ })
|
||||
+ }
|
||||
+ };
|
||||
}
|
||||
|
||||
impl GuestSecret {
|
||||
+ fn name_to_id(name: &str) -> Result<SecretId> {
|
||||
+ let id: [u8; SecretId::ID_SIZE] = hash(MessageDigest::sha256(), name.as_bytes())?
|
||||
+ .to_vec()
|
||||
+ .try_into()
|
||||
+ .unwrap();
|
||||
+ Ok(id.into())
|
||||
+ }
|
||||
+
|
||||
/// Create a new [`GuestSecret::Association`].
|
||||
///
|
||||
/// * `name` - Name of the secret. Will be hashed into a 32 byte id
|
||||
- /// * `secret` - Value of the secret. Ranom if [`Option::None`]
|
||||
+ /// * `secret` - Value of the secret. Random if [`Option::None`]
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
@@ -51,10 +112,6 @@ impl GuestSecret {
|
||||
where
|
||||
O: Into<Option<[u8; ASSOC_SECRET_SIZE]>>,
|
||||
{
|
||||
- let id: [u8; SecretId::ID_SIZE] = hash(MessageDigest::sha256(), name.as_bytes())?
|
||||
- .to_vec()
|
||||
- .try_into()
|
||||
- .unwrap();
|
||||
let secret = match secret.into() {
|
||||
Some(s) => s,
|
||||
None => random_array()?,
|
||||
@@ -62,16 +119,28 @@ impl GuestSecret {
|
||||
|
||||
Ok(Self::Association {
|
||||
name: name.to_string(),
|
||||
- id: id.into(),
|
||||
+ id: Self::name_to_id(name)?,
|
||||
secret: secret.into(),
|
||||
})
|
||||
}
|
||||
|
||||
+ retr_constructor!(#[doc = r"This function will return an error if the secret is larger than 8 pages"]
|
||||
+ | #[doc = r"plaintext"] => Confidential<Vec<u8>>, plaintext);
|
||||
+ retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the secret size is invalid"]
|
||||
+ | #[doc = r"AES Key"] => Confidential<Vec<u8>>, aes);
|
||||
+ retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the secret size is invalid"]
|
||||
+ | #[doc = r"AES-XTS Key"] => Confidential<Vec<u8>>, aes_xts);
|
||||
+ retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the secret size is invalid"]
|
||||
+ | #[doc = r"HMAC-SHA Key"] => Confidential<Vec<u8>>, hmac_sha);
|
||||
+ 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);
|
||||
+
|
||||
/// Reference to the confidential data
|
||||
pub fn confidential(&self) -> &[u8] {
|
||||
match &self {
|
||||
Self::Null => &[],
|
||||
Self::Association { secret, .. } => secret.value().as_slice(),
|
||||
+ Self::Retrievable { secret, .. } => secret.value(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +148,7 @@ impl GuestSecret {
|
||||
pub(crate) fn auth(&self) -> SecretAuth {
|
||||
match &self {
|
||||
Self::Null => SecretAuth::Null,
|
||||
- // Panic: every non null secret type is listable -> no panic
|
||||
+ // Panic: every non null secret type is list-able -> no panic
|
||||
listable => {
|
||||
SecretAuth::Listable(ListableSecretHdr::from_guest_secret(listable).unwrap())
|
||||
}
|
||||
@@ -92,6 +161,7 @@ impl GuestSecret {
|
||||
// Null is not listable, but the ListableSecretType provides the type constant (1)
|
||||
Self::Null => ListableSecretType::NULL,
|
||||
Self::Association { .. } => ListableSecretType::ASSOCIATION,
|
||||
+ Self::Retrievable { kind, .. } => kind.into(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +170,7 @@ impl GuestSecret {
|
||||
match self {
|
||||
Self::Null => 0,
|
||||
Self::Association { secret, .. } => secret.value().len() as u32,
|
||||
+ Self::Retrievable { secret, .. } => secret.value().len() as u32,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,18 +178,157 @@ impl GuestSecret {
|
||||
fn id(&self) -> Option<SecretId> {
|
||||
match self {
|
||||
Self::Null => None,
|
||||
- Self::Association { id, .. } => Some(id.to_owned()),
|
||||
+ Self::Association { id, .. } | Self::Retrievable { id, .. } => Some(id.to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+type RetrKeyInfo = (RetrievableSecret, Confidential<Vec<u8>>);
|
||||
+
|
||||
+fn extend_to_multiple(mut key: Vec<u8>, multiple: usize) -> Confidential<Vec<u8>> {
|
||||
+ match key.len().checked_rem(multiple) {
|
||||
+ Some(0) | None => key,
|
||||
+ Some(m) => {
|
||||
+ key.resize(key.len() + multiple - m, 0);
|
||||
+ key
|
||||
+ }
|
||||
+ }
|
||||
+ .into()
|
||||
+}
|
||||
+
|
||||
+/// Get a plain-text key
|
||||
+///
|
||||
+/// ```none
|
||||
+/// size U16<BigEndian> | payload (0-8190) bytes
|
||||
+/// ```
|
||||
+fn plaintext(inp: Confidential<Vec<u8>>) -> Result<RetrKeyInfo> {
|
||||
+ let key_len = inp.value().len();
|
||||
+ if key_len > RetrieveCmd::MAX_SIZE {
|
||||
+ return Err(Error::RetrInvKey {
|
||||
+ what: "key size",
|
||||
+ value: key_len.to_string(),
|
||||
+ kind: RetrievableSecret::PlainText.to_string(),
|
||||
+ exp: RetrievableSecret::PlainText.expected(),
|
||||
+ });
|
||||
+ }
|
||||
+ let mut key = Vec::with_capacity(2 + inp.value().len());
|
||||
+ let key_len: U16<BigEndian> = (key_len as u16).into();
|
||||
+ key.extend_from_slice(key_len.as_bytes());
|
||||
+ key.extend_from_slice(inp.value());
|
||||
+ let key = extend_to_multiple(key, SymKeyType::AES_256_GCM_BLOCK_LEN);
|
||||
+
|
||||
+ Ok((RetrievableSecret::PlainText, key))
|
||||
+}
|
||||
+
|
||||
+/// Get an AES-key
|
||||
+fn aes(key: Confidential<Vec<u8>>) -> Result<RetrKeyInfo> {
|
||||
+ let key_len = key.value().len() as u32;
|
||||
+ let bit_size = bitsize(key_len);
|
||||
+ match AesSizes::from_bits(bit_size) {
|
||||
+ Some(size) => Ok((RetrievableSecret::Aes(size), key)),
|
||||
+ None => {
|
||||
+ // Use some AES type to get exp sizes and name
|
||||
+ let kind = RetrievableSecret::Aes(AesSizes::Bits128);
|
||||
+ Err(Error::RetrInvKey {
|
||||
+ what: "key size",
|
||||
+ value: bit_size.to_string(),
|
||||
+ kind: format!("{kind:#}"),
|
||||
+ exp: kind.expected(),
|
||||
+ })
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// Get an AES-XTS-key
|
||||
+fn aes_xts(key: Confidential<Vec<u8>>) -> Result<RetrKeyInfo> {
|
||||
+ let key_len = key.value().len() as u32;
|
||||
+ let bit_size = bitsize(key_len / 2);
|
||||
+ match AesXtsSizes::from_bits(bit_size) {
|
||||
+ Some(size) => Ok((RetrievableSecret::AesXts(size), key)),
|
||||
+ None => {
|
||||
+ // Use some AES-XTS type to get exp sizes and name
|
||||
+ let kind = RetrievableSecret::AesXts(AesXtsSizes::Bits128);
|
||||
+ Err(Error::RetrInvKey {
|
||||
+ what: "key size",
|
||||
+ value: bit_size.to_string(),
|
||||
+ kind: format!("{kind:#}"),
|
||||
+ exp: kind.expected(),
|
||||
+ })
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// Get an HMAC-SHA-key
|
||||
+fn hmac_sha(key: Confidential<Vec<u8>>) -> Result<RetrKeyInfo> {
|
||||
+ let key_len = key.value().len() as u32;
|
||||
+ let size = bitsize(key_len / 2);
|
||||
+ match HmacShaSizes::from_sha_size(size) {
|
||||
+ Some(size) => Ok((RetrievableSecret::HmacSha(size), key)),
|
||||
+ None => {
|
||||
+ // Use some HMAC type to get exp sizes and name
|
||||
+ let kind = RetrievableSecret::HmacSha(HmacShaSizes::Sha256);
|
||||
+ Err(Error::RetrInvKey {
|
||||
+ what: "key size",
|
||||
+ value: size.to_string(),
|
||||
+ kind: format!("{kind:#}"),
|
||||
+ exp: kind.expected(),
|
||||
+ })
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// Get an EC-private-key
|
||||
+fn ec(key: PKey<Private>) -> Result<RetrKeyInfo> {
|
||||
+ let (key, nid) = match key.id() {
|
||||
+ Id::EC => {
|
||||
+ let ec_key = key.ec_key()?;
|
||||
+ let key = ec_key.private_key().to_vec();
|
||||
+ let nid = ec_key.group().curve_name().unwrap_or(Nid::UNDEF);
|
||||
+ (key, nid)
|
||||
+ }
|
||||
+ // ED keys are not handled via the EC struct in OpenSSL.
|
||||
+ id @ (Id::ED25519 | Id::ED448) => {
|
||||
+ let key = key.raw_private_key()?;
|
||||
+ let nid = Nid::from_raw(id.as_raw());
|
||||
+ (key, nid)
|
||||
+ }
|
||||
+ _ => (vec![], Nid::UNDEF),
|
||||
+ };
|
||||
+
|
||||
+ let kind = match nid {
|
||||
+ Nid::X9_62_PRIME256V1 => EcCurves::Secp256R1,
|
||||
+ Nid::SECP384R1 => EcCurves::Secp384R1,
|
||||
+ Nid::SECP521R1 => EcCurves::Secp521R1,
|
||||
+ NID_ED25519 => EcCurves::Ed25519,
|
||||
+ NID_ED448 => EcCurves::Ed448,
|
||||
+ nid => {
|
||||
+ // Use some EC type to get exp sizes and name
|
||||
+ let ec = RetrievableSecret::Ec(EcCurves::Secp521R1);
|
||||
+ return Err(Error::RetrInvKey {
|
||||
+ what: "curve or format",
|
||||
+ kind: format!("{ec:#}"),
|
||||
+ value: nid.long_name()?.to_string(),
|
||||
+ exp: ec.expected(),
|
||||
+ });
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ let key = kind.resize_raw_key(key);
|
||||
+ Ok((RetrievableSecret::Ec(kind), key.into()))
|
||||
+}
|
||||
+
|
||||
+#[inline(always)]
|
||||
+const fn bitsize(bytesize: u32) -> u32 {
|
||||
+ bytesize * 8
|
||||
+}
|
||||
+
|
||||
impl Display for GuestSecret {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Null => write!(f, "Meta"),
|
||||
gs => {
|
||||
let kind: U16<BigEndian> = gs.kind().into();
|
||||
- let st: ListableSecretType = kind.into();
|
||||
+ let st: ListableSecretType = kind.get().into();
|
||||
write!(f, "{st}")
|
||||
}
|
||||
}
|
||||
@@ -153,20 +363,24 @@ assert_size!(ListableSecretHdr, 0x30);
|
||||
|
||||
impl ListableSecretHdr {
|
||||
fn from_guest_secret(gs: &GuestSecret) -> Option<Self> {
|
||||
- let id = gs.id()?;
|
||||
Some(Self {
|
||||
res0: 0,
|
||||
kind: gs.kind().into(),
|
||||
secret_len: gs.secret_len().into(),
|
||||
res8: 0,
|
||||
- id,
|
||||
+ id: gs.id()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
+
|
||||
+ use super::HmacShaSizes as HmacSizes;
|
||||
+ use super::RetrievableSecret::*;
|
||||
use super::*;
|
||||
+ use openssl::ec::{EcGroup, EcKey};
|
||||
+ use pv_core::uv::AesSizes;
|
||||
use serde_test::{assert_tokens, Token};
|
||||
|
||||
#[test]
|
||||
@@ -187,8 +401,103 @@ mod test {
|
||||
assert_eq!(secret, exp);
|
||||
}
|
||||
|
||||
+ macro_rules! retr_test {
|
||||
+ ($name: ident, $func: ident, $size: expr, $exp_kind: expr) => {
|
||||
+ #[test]
|
||||
+ fn $name() {
|
||||
+ let secret_value = vec![0x11; $size];
|
||||
+ let name = "test retr secret".to_string();
|
||||
+ let secret = GuestSecret::$func(&name, secret_value.clone().into()).unwrap();
|
||||
+ let exp_id = [
|
||||
+ 0x61, 0x2c, 0xd6, 0x3e, 0xa8, 0xf2, 0xc1, 0x15, 0xc1, 0xe, 0x15, 0xb8, 0x8a,
|
||||
+ 0x90, 0x16, 0xc1, 0x55, 0xef, 0x9c, 0x7c, 0x2c, 0x8e, 0x56, 0xd0, 0x78, 0x4c,
|
||||
+ 0x8a, 0x1d, 0xc9, 0x3a, 0x80, 0xba,
|
||||
+ ];
|
||||
+ let exp = GuestSecret::Retrievable {
|
||||
+ kind: $exp_kind,
|
||||
+ name,
|
||||
+ id: exp_id.into(),
|
||||
+ secret: secret_value.into(),
|
||||
+ };
|
||||
+ assert_eq!(exp, secret);
|
||||
+ }
|
||||
+ };
|
||||
+ }
|
||||
+
|
||||
+ retr_test!(retr_aes_128, aes, 16, Aes(AesSizes::Bits128));
|
||||
+ retr_test!(retr_aes_192, aes, 24, Aes(AesSizes::Bits192));
|
||||
+ retr_test!(retr_aes_256, aes, 32, Aes(AesSizes::Bits256));
|
||||
+ retr_test!(retr_aes_xts_128, aes_xts, 32, AesXts(AesXtsSizes::Bits128));
|
||||
+ retr_test!(retr_aes_xts_256, aes_xts, 64, AesXts(AesXtsSizes::Bits256));
|
||||
+ retr_test!(retr_aes_hmac_256, hmac_sha, 64, HmacSha(HmacSizes::Sha256));
|
||||
+ retr_test!(retr_aes_hmac_512, hmac_sha, 128, HmacSha(HmacSizes::Sha512));
|
||||
+
|
||||
+ #[test]
|
||||
+ fn plaintext_no_pad() {
|
||||
+ let key = vec![0, 14, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7];
|
||||
+ let name = "PLAINTEXT_PAD".to_string();
|
||||
+ let secret = GuestSecret::plaintext(&name, key[2..].to_vec().into()).unwrap();
|
||||
+ let exp_id = [
|
||||
+ 15, 123, 176, 210, 135, 231, 220, 232, 148, 93, 198, 195, 165, 212, 214, 129, 45, 1,
|
||||
+ 94, 11, 167, 18, 151, 15, 120, 254, 13, 109, 173, 186, 37, 74,
|
||||
+ ];
|
||||
+ let exp = GuestSecret::Retrievable {
|
||||
+ kind: PlainText,
|
||||
+ name,
|
||||
+ id: exp_id.into(),
|
||||
+ secret: key.into(),
|
||||
+ };
|
||||
+
|
||||
+ assert_eq!(secret, exp);
|
||||
+ }
|
||||
+
|
||||
#[test]
|
||||
- fn ap_asc_parse() {
|
||||
+ fn plaintext_pad() {
|
||||
+ let key = vec![0, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0];
|
||||
+ let name = "PLAINTEXT_PAD".to_string();
|
||||
+ let secret = GuestSecret::plaintext(&name, key[2..12].to_vec().into()).unwrap();
|
||||
+ let exp_id = [
|
||||
+ 15, 123, 176, 210, 135, 231, 220, 232, 148, 93, 198, 195, 165, 212, 214, 129, 45, 1,
|
||||
+ 94, 11, 167, 18, 151, 15, 120, 254, 13, 109, 173, 186, 37, 74,
|
||||
+ ];
|
||||
+ let exp = GuestSecret::Retrievable {
|
||||
+ kind: PlainText,
|
||||
+ name,
|
||||
+ id: exp_id.into(),
|
||||
+ secret: key.into(),
|
||||
+ };
|
||||
+
|
||||
+ assert_eq!(secret, exp);
|
||||
+ }
|
||||
+
|
||||
+ #[track_caller]
|
||||
+ fn test_ec(grp: Nid, exp_kind: EcCurves, exp_len: usize) {
|
||||
+ let key = match grp {
|
||||
+ NID_ED25519 => PKey::generate_ed25519().unwrap(),
|
||||
+ NID_ED448 => PKey::generate_ed448().unwrap(),
|
||||
+ nid => {
|
||||
+ let group = EcGroup::from_curve_name(nid).unwrap();
|
||||
+ let key = EcKey::generate(&group).unwrap();
|
||||
+ PKey::from_ec_key(key).unwrap()
|
||||
+ }
|
||||
+ };
|
||||
+ let (kind, key) = ec(key).unwrap();
|
||||
+
|
||||
+ assert_eq!(kind, Ec(exp_kind));
|
||||
+ assert_eq!(key.value().len(), exp_len);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn retr_ec() {
|
||||
+ test_ec(Nid::X9_62_PRIME256V1, EcCurves::Secp256R1, 32);
|
||||
+ test_ec(Nid::SECP384R1, EcCurves::Secp384R1, 48);
|
||||
+ test_ec(Nid::SECP521R1, EcCurves::Secp521R1, 80);
|
||||
+ test_ec(NID_ED25519, EcCurves::Ed25519, 32);
|
||||
+ test_ec(NID_ED448, EcCurves::Ed448, 64);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn asc_parse() {
|
||||
let id = [
|
||||
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab,
|
||||
0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67,
|
||||
@@ -217,6 +526,39 @@ mod test {
|
||||
);
|
||||
}
|
||||
|
||||
+ #[test]
|
||||
+ fn retrievable_parse() {
|
||||
+ let id = [
|
||||
+ 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab,
|
||||
+ 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67,
|
||||
+ 0x89, 0xab, 0xcd, 0xef,
|
||||
+ ];
|
||||
+ let asc = GuestSecret::Retrievable {
|
||||
+ kind: PlainText,
|
||||
+ name: "test123".to_string(),
|
||||
+ id: id.into(),
|
||||
+ secret: vec![].into(),
|
||||
+ };
|
||||
+
|
||||
+ assert_tokens(
|
||||
+ &asc,
|
||||
+ &[
|
||||
+ Token::StructVariant {
|
||||
+ name: "GuestSecret",
|
||||
+ variant: "Retrievable",
|
||||
+ len: 3,
|
||||
+ },
|
||||
+ Token::String("kind"),
|
||||
+ Token::String("3 (PLAINTEXT)"),
|
||||
+ Token::String("name"),
|
||||
+ Token::String("test123"),
|
||||
+ Token::String("id"),
|
||||
+ Token::String("0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"),
|
||||
+ Token::StructVariantEnd,
|
||||
+ ],
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
#[test]
|
||||
fn guest_secret_bin_null() {
|
||||
let gs = GuestSecret::Null;
|
||||
@@ -228,7 +570,7 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
- fn guest_secret_bin_ap() {
|
||||
+ fn guest_secret_bin_asoc() {
|
||||
let gs = GuestSecret::Association {
|
||||
name: "test".to_string(),
|
||||
id: [1; 32].into(),
|
||||
@@ -241,4 +583,21 @@ mod test {
|
||||
assert_eq!(exp, gs_bytes_auth.get());
|
||||
assert_eq!(&[2; 32], gs.confidential());
|
||||
}
|
||||
+
|
||||
+ #[test]
|
||||
+ fn guest_secret_bin_retr() {
|
||||
+ let gs = GuestSecret::Retrievable {
|
||||
+ kind: PlainText,
|
||||
+ name: "test".to_string(),
|
||||
+ id: [1; 32].into(),
|
||||
+ secret: vec![2; 32].into(),
|
||||
+ };
|
||||
+ let auth = gs.auth();
|
||||
+ let gs_bytes_auth = auth.get();
|
||||
+ let mut exp = vec![0u8, 0, 0, 3, 0, 0, 0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
+ exp.extend([1; 32]);
|
||||
+
|
||||
+ assert_eq!(exp, gs_bytes_auth);
|
||||
+ assert_eq!(&[2; 32], gs.confidential());
|
||||
+ }
|
||||
}
|
||||
diff --git a/rust/pv/src/uvsecret/retr_secret.rs b/rust/pv/src/uvsecret/retr_secret.rs
|
||||
new file mode 100644
|
||||
index 00000000..5fad016f
|
||||
--- /dev/null
|
||||
+++ b/rust/pv/src/uvsecret/retr_secret.rs
|
||||
@@ -0,0 +1,234 @@
|
||||
+// SPDX-License-Identifier: MIT
|
||||
+//
|
||||
+// Copyright IBM Corp. 2024
|
||||
+
|
||||
+use crate::{pem::Pem, uvsecret::guest_secret::MAX_SIZE_PLAIN_PAYLOAD, Result};
|
||||
+
|
||||
+use byteorder::BigEndian;
|
||||
+use log::warn;
|
||||
+use pv_core::{
|
||||
+ request::Confidential,
|
||||
+ uv::{ListableSecretType, RetrievableSecret, RetrieveCmd},
|
||||
+};
|
||||
+use zerocopy::{FromBytes, U16};
|
||||
+
|
||||
+/// An IBM Protected Key
|
||||
+///
|
||||
+/// A protected key, writeable as pem.
|
||||
+///
|
||||
+/// Will convert into PEM as:
|
||||
+/// ```PEM
|
||||
+///-----BEGIN IBM PROTECTED KEY-----
|
||||
+///kind: <name>
|
||||
+///
|
||||
+///<protected key in base64>
|
||||
+///-----END IBM PROTECTED KEY-----
|
||||
+/// ```
|
||||
+#[derive(Debug, PartialEq, Eq)]
|
||||
+pub struct IbmProtectedKey {
|
||||
+ kind: ListableSecretType,
|
||||
+ key: Confidential<Vec<u8>>,
|
||||
+}
|
||||
+
|
||||
+impl IbmProtectedKey {
|
||||
+ /// Get the binary representation of the key.
|
||||
+ pub fn data(&self) -> &[u8] {
|
||||
+ self.key.value()
|
||||
+ }
|
||||
+
|
||||
+ /// Converts a [`IbmProtectedKey`] into a vector.
|
||||
+ pub fn into_bytes(self) -> Confidential<Vec<u8>> {
|
||||
+ self.key
|
||||
+ }
|
||||
+
|
||||
+ /// Get the data in PEM format.
|
||||
+ ///
|
||||
+ /// # Errors
|
||||
+ ///
|
||||
+ /// This function will return an error if the PEM conversion failed (very unlikely).
|
||||
+ pub fn to_pem(&self) -> Result<Pem> {
|
||||
+ Pem::new(
|
||||
+ "IBM PROTECTED KEY",
|
||||
+ format!("kind: {}", self.kind),
|
||||
+ self.key.value(),
|
||||
+ )
|
||||
+ }
|
||||
+
|
||||
+ fn new<K>(kind: ListableSecretType, key: K) -> Self
|
||||
+ where
|
||||
+ K: Into<Confidential<Vec<u8>>>,
|
||||
+ {
|
||||
+ Self {
|
||||
+ kind,
|
||||
+ key: key.into(),
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl From<RetrieveCmd> for RetrievedSecret {
|
||||
+ fn from(value: RetrieveCmd) -> Self {
|
||||
+ let kind = value.meta_data().stype();
|
||||
+ let key = value.into_key();
|
||||
+
|
||||
+ match kind {
|
||||
+ ListableSecretType::Retrievable(RetrievableSecret::PlainText) => {
|
||||
+ // Will not run into default, retrieve has a granularity of 16 bytes and 16 bytes is the
|
||||
+ // minimum size
|
||||
+ let len = U16::<BigEndian>::read_from_prefix(key.value())
|
||||
+ .unwrap_or_default()
|
||||
+ .get() as usize;
|
||||
+
|
||||
+ // Test if the plain text secret has a size:
|
||||
+ // 1. len <= 8190
|
||||
+ // 2. first two bytes are max 15 less than buffer-size+2
|
||||
+ // 3. bytes after len + 2 are zero
|
||||
+ match len <= MAX_SIZE_PLAIN_PAYLOAD
|
||||
+ && key.value().len() - (len + 2) < 15
|
||||
+ && key.value()[len + 2..].iter().all(|c| *c == 0)
|
||||
+ {
|
||||
+ false => Self::Plaintext(key),
|
||||
+ true => Self::Plaintext(key.value()[2..len + 2].to_vec().into()),
|
||||
+ }
|
||||
+ }
|
||||
+ kind => {
|
||||
+ match kind {
|
||||
+ ListableSecretType::Retrievable(_) => (),
|
||||
+ _ => warn!("Retrieved an unretrievable Secret! Will continue; interpreting it as a protected key."),
|
||||
+ }
|
||||
+ Self::ProtectedKey(IbmProtectedKey::new(kind, key))
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// A retrieved Secret.
|
||||
+#[derive(Debug, PartialEq, Eq)]
|
||||
+pub enum RetrievedSecret {
|
||||
+ /// A plaintext secret
|
||||
+ Plaintext(Confidential<Vec<u8>>),
|
||||
+ /// An [`IbmProtectedKey`]
|
||||
+ ProtectedKey(IbmProtectedKey),
|
||||
+}
|
||||
+
|
||||
+impl RetrievedSecret {
|
||||
+ /// Create a new IBM PROTECTED KEY object
|
||||
+ pub fn from_cmd(cmd: RetrieveCmd) -> Self {
|
||||
+ cmd.into()
|
||||
+ }
|
||||
+
|
||||
+ /// Get the binary representation of the key.
|
||||
+ pub fn data(&self) -> &[u8] {
|
||||
+ match self {
|
||||
+ RetrievedSecret::Plaintext(p) => p.value(),
|
||||
+ RetrievedSecret::ProtectedKey(p) => p.data(),
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /// Converts a [`IbmProtectedKey`] into a vector.
|
||||
+ pub fn into_bytes(self) -> Confidential<Vec<u8>> {
|
||||
+ match self {
|
||||
+ RetrievedSecret::Plaintext(p) => p,
|
||||
+ RetrievedSecret::ProtectedKey(p) => p.into_bytes(),
|
||||
+ }
|
||||
+ }
|
||||
+ /// Get the data in PEM format.
|
||||
+ ///
|
||||
+ /// # Errors
|
||||
+ ///
|
||||
+ /// This function will return an error if the PEM conversion failed (very unlikely).
|
||||
+ pub fn to_pem(&self) -> Result<Pem> {
|
||||
+ match self {
|
||||
+ RetrievedSecret::Plaintext(p) => Pem::new("PLAINTEXT SECRET", None, p.value()),
|
||||
+ RetrievedSecret::ProtectedKey(p) => p.to_pem(),
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#[cfg(test)]
|
||||
+mod test {
|
||||
+ use super::*;
|
||||
+ use pv_core::uv::*;
|
||||
+
|
||||
+ fn mk_retr(secret: &[u8]) -> RetrievedSecret {
|
||||
+ let entry = SecretEntry::new(
|
||||
+ 0,
|
||||
+ ListableSecretType::Retrievable(RetrievableSecret::PlainText),
|
||||
+ SecretId::default(),
|
||||
+ secret.len() as u32,
|
||||
+ );
|
||||
+ let mut cmd = RetrieveCmd::from_entry(entry).unwrap();
|
||||
+ cmd.data().unwrap().copy_from_slice(secret);
|
||||
+ RetrievedSecret::from_cmd(cmd)
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn from_retr_cmd() {
|
||||
+ let secret = vec![0, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0, 0, 0, 0];
|
||||
+ let prot_key = mk_retr(&secret);
|
||||
+ let exp = RetrievedSecret::Plaintext(secret[2..12].to_vec().into());
|
||||
+ assert_eq!(prot_key, exp);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn from_retr_inv_size() {
|
||||
+ let secret = vec![0x20; 32];
|
||||
+ let prot_key = mk_retr(&secret);
|
||||
+ let exp = RetrievedSecret::Plaintext(secret.into());
|
||||
+ assert_eq!(prot_key, exp);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn from_retr_inv_no_zero_after_end() {
|
||||
+ let secret = vec![0, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 1, 0, 0, 0];
|
||||
+ let prot_key = mk_retr(&secret);
|
||||
+ let exp = RetrievedSecret::Plaintext(secret.into());
|
||||
+ assert_eq!(prot_key, exp);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn from_retr_inv_to_much_padding() {
|
||||
+ let secret = vec![
|
||||
+ 0, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
+ 0, 0, 0, 0,
|
||||
+ ];
|
||||
+ let prot_key = mk_retr(&secret);
|
||||
+ let exp = RetrievedSecret::Plaintext(secret.into());
|
||||
+ assert_eq!(prot_key, exp);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn from_retr_0_size() {
|
||||
+ let secret = vec![0x00; 32];
|
||||
+ let prot_key = mk_retr(&secret);
|
||||
+ let exp = RetrievedSecret::Plaintext(secret.into());
|
||||
+ assert_eq!(prot_key, exp);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn plain_text_pem() {
|
||||
+ let exp = "\
|
||||
+ -----BEGIN PLAINTEXT SECRET-----\n\
|
||||
+ ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER\n\
|
||||
+ -----END PLAINTEXT SECRET-----\n";
|
||||
+ let prot = RetrievedSecret::Plaintext(vec![17; 48].into());
|
||||
+ let pem = prot.to_pem().unwrap();
|
||||
+ let pem_str = pem.to_string();
|
||||
+ assert_eq!(pem_str, exp);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn prot_key_pem() {
|
||||
+ let exp = "\
|
||||
+ -----BEGIN IBM PROTECTED KEY-----\n\
|
||||
+ kind: AES-128-KEY\n\n\
|
||||
+ ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER\n\
|
||||
+ -----END IBM PROTECTED KEY-----\n";
|
||||
+ let prot = IbmProtectedKey::new(
|
||||
+ ListableSecretType::Retrievable(RetrievableSecret::Aes(AesSizes::Bits128)),
|
||||
+ vec![17; 48],
|
||||
+ );
|
||||
+ let pem = prot.to_pem().unwrap();
|
||||
+ let pem_str = pem.to_string();
|
||||
+ assert_eq!(pem_str, exp);
|
||||
+ }
|
||||
+}
|
95
s390-tools-General-update-07.patch
Normal file
95
s390-tools-General-update-07.patch
Normal file
@ -0,0 +1,95 @@
|
||||
From a14f9d4edcc5db0d54e4fbe3ec3d98c7c270bf8e Mon Sep 17 00:00:00 2001
|
||||
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Date: Fri, 13 Dec 2024 15:04:02 +0100
|
||||
Subject: [PATCH] rust/pvsecret: Improve CLI
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Improve the wording of the help/man text/
|
||||
|
||||
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/pvsecret/src/cli.rs | 26 +++++++++++++-------------
|
||||
1 file changed, 13 insertions(+), 13 deletions(-)
|
||||
|
||||
diff --git a/rust/pvsecret/src/cli.rs b/rust/pvsecret/src/cli.rs
|
||||
index 6deaaebd..c4b9f2b3 100644
|
||||
--- a/rust/pvsecret/src/cli.rs
|
||||
+++ b/rust/pvsecret/src/cli.rs
|
||||
@@ -37,8 +37,8 @@ pub struct CreateSecretOpt {
|
||||
|
||||
/// Specifies the header of the guest image.
|
||||
///
|
||||
- /// Can be an IBM Secure Execution image created by genprotimg or an extracted IBM Secure
|
||||
- /// Execution header. The header must start at a page boundary.
|
||||
+ /// Can be an IBM Secure Execution image created by 'pvimg/genprotimg' or an
|
||||
+ /// extracted IBM Secure Execution header.
|
||||
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath)]
|
||||
pub hdr: String,
|
||||
|
||||
@@ -150,12 +150,12 @@ pub enum AddSecretType {
|
||||
|
||||
/// Create an association secret.
|
||||
///
|
||||
- /// Use an association secret to connect a trusted I/O device to a guest. The `pvapconfig` tool
|
||||
+ /// Use an association secret to connect a trusted I/O device to a guest. The 'pvapconfig' tool
|
||||
/// provides more information about association secrets.
|
||||
Association {
|
||||
- /// String to identify the new secret.
|
||||
+ /// String that identifies the new secret.
|
||||
///
|
||||
- /// The actual secret is set with --input-secret. The name is saved in `NAME.yaml` with
|
||||
+ /// The actual secret is set with '--input-secret'. The name is saved in `NAME.yaml` with
|
||||
/// white-spaces mapped to `_`.
|
||||
name: String,
|
||||
|
||||
@@ -166,15 +166,15 @@ pub enum AddSecretType {
|
||||
stdout: bool,
|
||||
|
||||
/// Path from which to read the plaintext secret. Uses a random secret if not specified.
|
||||
- #[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath, conflicts_with("output_secret"))]
|
||||
+ #[arg(long, value_name = "SECRET-FILE", value_hint = ValueHint::FilePath, conflicts_with("output_secret"))]
|
||||
input_secret: Option<String>,
|
||||
|
||||
- /// Save the generated secret as plaintext in FILE.
|
||||
+ /// Save the generated secret as plaintext in SECRET-FILE.
|
||||
///
|
||||
/// The generated secret can be used to generate add-secret requests for a different guest
|
||||
- /// with the same secret using --input-secret. Destroy the secret when it is not used
|
||||
+ /// with the same secret using '--input-secret'. Destroy the secret when it is not used
|
||||
/// anymore.
|
||||
- #[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath,)]
|
||||
+ #[arg(long, value_name = "SECRET-FILE", value_hint = ValueHint::FilePath,)]
|
||||
output_secret: Option<String>,
|
||||
},
|
||||
}
|
||||
@@ -243,13 +243,13 @@ pub enum Command {
|
||||
/// Create a new add-secret request.
|
||||
///
|
||||
/// Create add-secret requests for IBM Secure Execution guests. Only create these requests in a
|
||||
- /// trusted environment, such as your workstation. The `pvattest create` command creates a
|
||||
+ /// trusted environment, such as your workstation. The 'pvattest create' command creates a
|
||||
/// randomly generated key to protect the request. The generated requests can then be added on
|
||||
- /// an IBM Secure Execution guest using `pvsecret add`. The guest can then use the secrets with
|
||||
+ /// an IBM Secure Execution guest using 'pvsecret add'. The guest can then use the secrets with
|
||||
/// the use case depending on the secret type.
|
||||
Create(Box<CreateSecretOpt>),
|
||||
|
||||
- /// Perform an add-secret request (s390x only).
|
||||
+ /// Submit an add-secret request to the Ultravisor (s390x only).
|
||||
///
|
||||
/// Perform an add-secret request using a previously generated add-secret request. Only
|
||||
/// available on s390x.
|
||||
@@ -258,7 +258,7 @@ pub enum Command {
|
||||
/// Lock the secret-store (s390x only).
|
||||
///
|
||||
/// Lock the secret store (s390x only). After this command executed successfully, all
|
||||
- /// add-secret requests will fail. Only available on s390x.
|
||||
+ /// subsequent add-secret requests will fail. Only available on s390x.
|
||||
Lock,
|
||||
|
||||
/// List all ultravisor secrets (s390x only).
|
423
s390-tools-General-update-08.patch
Normal file
423
s390-tools-General-update-08.patch
Normal file
@ -0,0 +1,423 @@
|
||||
From 93da795520ca2f0a73cfbfc951a9b16437a1b95b Mon Sep 17 00:00:00 2001
|
||||
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Date: Mon, 19 Feb 2024 15:15:16 +0100
|
||||
Subject: [PATCH] rust/pvsecret: Add support for retrievable secrets
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Support for creating and retrieving retrievable secrets.
|
||||
|
||||
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/pvsecret/src/cli.rs | 129 +++++++++++++++++++++++++++++++-
|
||||
rust/pvsecret/src/cmd.rs | 6 +-
|
||||
rust/pvsecret/src/cmd/create.rs | 30 +++++++-
|
||||
rust/pvsecret/src/cmd/list.rs | 12 ++-
|
||||
rust/pvsecret/src/cmd/retr.rs | 62 +++++++++++++++
|
||||
rust/pvsecret/src/main.rs | 1 +
|
||||
6 files changed, 230 insertions(+), 10 deletions(-)
|
||||
create mode 100644 rust/pvsecret/src/cmd/retr.rs
|
||||
|
||||
diff --git a/rust/pvsecret/src/cli.rs b/rust/pvsecret/src/cli.rs
|
||||
index c4b9f2b3..4e747682 100644
|
||||
--- a/rust/pvsecret/src/cli.rs
|
||||
+++ b/rust/pvsecret/src/cli.rs
|
||||
@@ -1,7 +1,10 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
-// Copyright IBM Corp. 2023
|
||||
+// Copyright IBM Corp. 2023, 2024
|
||||
|
||||
+use std::fmt::Display;
|
||||
+
|
||||
+use clap::error::ErrorKind::ValueValidation;
|
||||
use clap::{ArgGroup, Args, CommandFactory, Parser, Subcommand, ValueEnum, ValueHint};
|
||||
use utils::{CertificateOptions, DeprecatedVerbosityOptions, STDOUT};
|
||||
|
||||
@@ -177,6 +180,72 @@ pub enum AddSecretType {
|
||||
#[arg(long, value_name = "SECRET-FILE", value_hint = ValueHint::FilePath,)]
|
||||
output_secret: Option<String>,
|
||||
},
|
||||
+
|
||||
+ /// Create a retrievable secret.
|
||||
+ ///
|
||||
+ /// A retrievable secret is stored in the per-guest storage of the Ultravisor. A SE-guest can
|
||||
+ /// retrieve the secret at runtime and use it. All retrievable secrets, but the plaintext
|
||||
+ /// secret, are retrieved as wrapped/protected key objects and only usable inside the current,
|
||||
+ /// running SE-guest instance.
|
||||
+ #[command(visible_alias = "retr")]
|
||||
+ Retrievable {
|
||||
+ /// String that identifies the new secret.
|
||||
+ ///
|
||||
+ /// The actual secret is set with '--secret'. The name is saved in `NAME.yaml` with
|
||||
+ /// white-spaces mapped to `_`.
|
||||
+ name: String,
|
||||
+
|
||||
+ /// Print the hashed name to stdout.
|
||||
+ ///
|
||||
+ /// The hashed name is not written to `NAME.yaml`
|
||||
+ #[arg(long)]
|
||||
+ stdout: bool,
|
||||
+
|
||||
+ /// Use SECRET-FILE as retrievable secret
|
||||
+ #[arg(long, value_name = "SECRET-FILE", value_hint = ValueHint::FilePath)]
|
||||
+ secret: String,
|
||||
+
|
||||
+ /// Specify the secret type.
|
||||
+ ///
|
||||
+ /// Limitations to the input data apply depending on the secret type.
|
||||
+ #[arg(long = "type", value_name = "TYPE")]
|
||||
+ kind: RetrieveableSecretInpKind,
|
||||
+ },
|
||||
+}
|
||||
+
|
||||
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
|
||||
+pub enum RetrieveableSecretInpKind {
|
||||
+ /// A plaintext secret.
|
||||
+ /// Can be any file up to 8190 bytes long
|
||||
+ Plain,
|
||||
+ /// An AES key.
|
||||
+ /// Must be a plain byte file 128, 192, or 256 bit long.
|
||||
+ Aes,
|
||||
+ /// An AES-XTS key.
|
||||
+ /// Must be a plain byte file 512, or 1024 bit long.
|
||||
+ AesXts,
|
||||
+ /// A HMAC-SHA key.
|
||||
+ /// Must be a plain byte file 512, or 1024 bit long.
|
||||
+ HmacSha,
|
||||
+ /// An elliptic curve private key.
|
||||
+ /// Must be a PEM or DER file.
|
||||
+ Ec,
|
||||
+}
|
||||
+
|
||||
+impl Display for RetrieveableSecretInpKind {
|
||||
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
+ write!(
|
||||
+ f,
|
||||
+ "{}",
|
||||
+ match self {
|
||||
+ Self::Plain => "PLAINTEXT",
|
||||
+ Self::Aes => "AES KEY",
|
||||
+ Self::AesXts => "AES-XTS KEY",
|
||||
+ Self::HmacSha => "HMAC-SHA KEY",
|
||||
+ Self::Ec => "EC PRIVATE KEY",
|
||||
+ }
|
||||
+ )
|
||||
+ }
|
||||
}
|
||||
|
||||
// all members s390x only
|
||||
@@ -238,6 +307,56 @@ pub struct VerifyOpt {
|
||||
pub output: String,
|
||||
}
|
||||
|
||||
+// all members s390x only
|
||||
+#[derive(Args, Debug)]
|
||||
+pub struct RetrSecretOptions {
|
||||
+ /// Specify the secret ID to be retrieved.
|
||||
+ ///
|
||||
+ /// Input type depends on '--inform'. If `yaml` (default) is specified, it must be a yaml
|
||||
+ /// created by the create subcommand of this tool. If `hex` is specified, it must be a hex
|
||||
+ /// 32-byte unsigned big endian number string. Leading zeros are required.
|
||||
+ #[cfg(target_arch = "s390x")]
|
||||
+ #[arg(value_name = "ID", value_hint = ValueHint::FilePath)]
|
||||
+ pub input: String,
|
||||
+
|
||||
+ /// Specify the output path to place the secret value
|
||||
+ #[cfg(target_arch = "s390x")]
|
||||
+ #[arg(short, long, value_name = "FILE", default_value = STDOUT, value_hint = ValueHint::FilePath)]
|
||||
+ pub output: String,
|
||||
+
|
||||
+ /// Define input type for the Secret ID
|
||||
+ #[cfg(target_arch = "s390x")]
|
||||
+ #[arg(long, value_enum, default_value_t)]
|
||||
+ pub inform: RetrInpFmt,
|
||||
+
|
||||
+ /// Define the output format for the retrieved secret
|
||||
+ #[cfg(target_arch = "s390x")]
|
||||
+ #[arg(long, value_enum, default_value_t)]
|
||||
+ pub outform: RetrOutFmt,
|
||||
+}
|
||||
+
|
||||
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Default)]
|
||||
+pub enum RetrInpFmt {
|
||||
+ /// Use a yaml file
|
||||
+ #[default]
|
||||
+ Yaml,
|
||||
+ /// Use a hex string.
|
||||
+ Hex,
|
||||
+}
|
||||
+
|
||||
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Default)]
|
||||
+pub enum RetrOutFmt {
|
||||
+ /// Write the secret as PEM.
|
||||
+ ///
|
||||
+ /// File starts with `-----BEGIN IBM PROTECTED KEY----` and `-----BEGIN
|
||||
+ /// PLAINTEXT SECRET-----` for plaintext secrets it contains one header
|
||||
+ /// line with the type information and the base64 protected key
|
||||
+ #[default]
|
||||
+ Pem,
|
||||
+ /// Write the secret in binary.
|
||||
+ Bin,
|
||||
+}
|
||||
+
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub enum Command {
|
||||
/// Create a new add-secret request.
|
||||
@@ -274,6 +393,10 @@ pub enum Command {
|
||||
/// provided key. Outputs the arbitrary user-data.
|
||||
Verify(VerifyOpt),
|
||||
|
||||
+ /// Retrieve a secret from the UV secret store (s390x only).
|
||||
+ #[command(visible_alias = "retr")]
|
||||
+ Retrieve(RetrSecretOptions),
|
||||
+
|
||||
/// Print version information and exit.
|
||||
#[command(aliases(["--version"]), hide(true))]
|
||||
Version,
|
||||
@@ -294,13 +417,13 @@ pub fn validate_cli(cli: &CliOptions) -> Result<(), clap::Error> {
|
||||
}
|
||||
if secret_out == &Some(format!("{name}.yaml")) {
|
||||
return Err(CliOptions::command().error(
|
||||
- clap::error::ErrorKind::ValueValidation,
|
||||
+ ValueValidation,
|
||||
format!("Secret output file and the secret name '{name}.yaml' are the same."),
|
||||
));
|
||||
}
|
||||
if format!("{name}.yaml") == opt.output {
|
||||
return Err(CliOptions::command().error(
|
||||
- clap::error::ErrorKind::ValueValidation,
|
||||
+ ValueValidation,
|
||||
format!(
|
||||
"output file and the secret name '{}' are the same.",
|
||||
&opt.output
|
||||
diff --git a/rust/pvsecret/src/cmd.rs b/rust/pvsecret/src/cmd.rs
|
||||
index a826fb31..10d99a5b 100644
|
||||
--- a/rust/pvsecret/src/cmd.rs
|
||||
+++ b/rust/pvsecret/src/cmd.rs
|
||||
@@ -16,6 +16,8 @@ mod add;
|
||||
mod list;
|
||||
#[cfg(target_arch = "s390x")]
|
||||
mod lock;
|
||||
+#[cfg(target_arch = "s390x")]
|
||||
+mod retr;
|
||||
|
||||
// Commands (directly) related to UVCs are only available on s389x
|
||||
#[cfg(target_arch = "s390x")]
|
||||
@@ -24,12 +26,13 @@ mod uv_cmd {
|
||||
pub use add::add;
|
||||
pub use list::list;
|
||||
pub use lock::lock;
|
||||
+ pub use retr::retr;
|
||||
pub const UV_CMD_FN: &[&str] = &["+add", "+lock", "+list"];
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "s390x"))]
|
||||
mod uv_cmd {
|
||||
- use crate::cli::{AddSecretOpt, ListSecretOpt};
|
||||
+ use crate::cli::{AddSecretOpt, ListSecretOpt, RetrSecretOptions};
|
||||
use anyhow::{bail, Result};
|
||||
macro_rules! not_supp {
|
||||
($name: ident $( ,$opt: ty )?) => {
|
||||
@@ -40,6 +43,7 @@ mod uv_cmd {
|
||||
}
|
||||
not_supp!(add, AddSecretOpt);
|
||||
not_supp!(list, ListSecretOpt);
|
||||
+ not_supp!(retr, RetrSecretOptions);
|
||||
not_supp!(lock);
|
||||
pub const UV_CMD_FN: &[&str] = &[];
|
||||
}
|
||||
diff --git a/rust/pvsecret/src/cmd/create.rs b/rust/pvsecret/src/cmd/create.rs
|
||||
index 9251c38c..73089a12 100644
|
||||
--- a/rust/pvsecret/src/cmd/create.rs
|
||||
+++ b/rust/pvsecret/src/cmd/create.rs
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
-use crate::cli::{AddSecretType, CreateSecretFlags, CreateSecretOpt};
|
||||
use anyhow::{anyhow, bail, Context, Error, Result};
|
||||
use log::{debug, info, trace, warn};
|
||||
use pv::{
|
||||
@@ -22,6 +21,8 @@ use pv::{
|
||||
use serde_yaml::Value;
|
||||
use utils::get_writer_from_cli_file_arg;
|
||||
|
||||
+use crate::cli::{AddSecretType, CreateSecretFlags, CreateSecretOpt, RetrieveableSecretInpKind};
|
||||
+
|
||||
fn write_out<P, D>(path: &P, data: D, ctx: &str) -> pv::Result<()>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
@@ -32,6 +33,23 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+fn retrievable(name: &str, secret: &str, kind: &RetrieveableSecretInpKind) -> Result<GuestSecret> {
|
||||
+ let secret_data = read_file(secret, &format!("retrievable {kind}"))?.into();
|
||||
+
|
||||
+ match kind {
|
||||
+ RetrieveableSecretInpKind::Plain => GuestSecret::plaintext(name, secret_data),
|
||||
+ RetrieveableSecretInpKind::Aes => GuestSecret::aes(name, secret_data),
|
||||
+ RetrieveableSecretInpKind::AesXts => GuestSecret::aes_xts(name, secret_data),
|
||||
+ RetrieveableSecretInpKind::HmacSha => GuestSecret::hmac_sha(name, secret_data),
|
||||
+ RetrieveableSecretInpKind::Ec => GuestSecret::ec(
|
||||
+ name,
|
||||
+ read_private_key(secret_data.value())
|
||||
+ .with_context(|| format!("Cannot read {secret} as {kind} from PEM or DER"))?,
|
||||
+ ),
|
||||
+ }
|
||||
+ .map_err(Error::from)
|
||||
+}
|
||||
+
|
||||
/// Prepare an add-secret request
|
||||
pub fn create(opt: &CreateSecretOpt) -> Result<()> {
|
||||
if pv_guest_bit_set() {
|
||||
@@ -88,6 +106,9 @@ fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
|
||||
input_secret: None,
|
||||
..
|
||||
} => GuestSecret::association(name, None)?,
|
||||
+ AddSecretType::Retrievable {
|
||||
+ name, secret, kind, ..
|
||||
+ } => retrievable(name, secret, kind)?,
|
||||
};
|
||||
trace!("AddSecret: {secret:x?}");
|
||||
|
||||
@@ -136,7 +157,9 @@ fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
|
||||
.as_ref()
|
||||
.map(|p| read_file(p, "User-signing key"))
|
||||
.transpose()?
|
||||
- .map(|buf| read_private_key(&buf))
|
||||
+ .map(|buf| {
|
||||
+ read_private_key(&buf).context("Cannot read {secret} as private key from PEM or DER")
|
||||
+ })
|
||||
.transpose()?;
|
||||
|
||||
if user_data.is_some() || user_key.is_some() {
|
||||
@@ -258,6 +281,9 @@ fn write_secret<P: AsRef<Path>>(
|
||||
write_out(path, guest_secret.confidential(), "Association secret")?
|
||||
}
|
||||
}
|
||||
+ AddSecretType::Retrievable { name, stdout, .. } => {
|
||||
+ write_yaml(name, guest_secret, stdout, outp_path)?
|
||||
+ }
|
||||
_ => (),
|
||||
};
|
||||
Ok(())
|
||||
diff --git a/rust/pvsecret/src/cmd/list.rs b/rust/pvsecret/src/cmd/list.rs
|
||||
index f7e3a72b..0bd9eca4 100644
|
||||
--- a/rust/pvsecret/src/cmd/list.rs
|
||||
+++ b/rust/pvsecret/src/cmd/list.rs
|
||||
@@ -3,21 +3,25 @@
|
||||
// Copyright IBM Corp. 2023
|
||||
|
||||
use crate::cli::{ListSecretOpt, ListSecretOutputType};
|
||||
-use anyhow::{Context, Result};
|
||||
+use anyhow::{Context, Error, Result};
|
||||
use log::warn;
|
||||
use pv::uv::{ListCmd, SecretList, UvDevice, UvcSuccess};
|
||||
use utils::{get_writer_from_cli_file_arg, STDOUT};
|
||||
|
||||
/// Do a List Secrets UVC
|
||||
-pub fn list(opt: &ListSecretOpt) -> Result<()> {
|
||||
- let uv = UvDevice::open()?;
|
||||
+pub fn list_uvc(uv: &UvDevice) -> Result<SecretList> {
|
||||
let mut cmd = ListCmd::default();
|
||||
match uv.send_cmd(&mut cmd)? {
|
||||
UvcSuccess::RC_SUCCESS => (),
|
||||
UvcSuccess::RC_MORE_DATA => warn!("There is more data available than expected"),
|
||||
};
|
||||
+ cmd.try_into().map_err(Error::new)
|
||||
+}
|
||||
|
||||
- let secret_list: SecretList = cmd.try_into()?;
|
||||
+/// Do a List Secrets UVC and output the list in the requested format
|
||||
+pub fn list(opt: &ListSecretOpt) -> Result<()> {
|
||||
+ let uv = UvDevice::open()?;
|
||||
+ let secret_list = list_uvc(&uv)?;
|
||||
let mut wr_out = get_writer_from_cli_file_arg(&opt.output)?;
|
||||
|
||||
match &opt.format {
|
||||
diff --git a/rust/pvsecret/src/cmd/retr.rs b/rust/pvsecret/src/cmd/retr.rs
|
||||
new file mode 100644
|
||||
index 00000000..7f7704cc
|
||||
--- /dev/null
|
||||
+++ b/rust/pvsecret/src/cmd/retr.rs
|
||||
@@ -0,0 +1,62 @@
|
||||
+// SPDX-License-Identifier: MIT
|
||||
+//
|
||||
+// Copyright IBM Corp. 2024
|
||||
+
|
||||
+use super::list::list_uvc;
|
||||
+use crate::cli::{RetrInpFmt, RetrOutFmt, RetrSecretOptions};
|
||||
+use anyhow::{anyhow, bail, Context, Result};
|
||||
+use log::{debug, info};
|
||||
+use pv::{
|
||||
+ misc::open_file,
|
||||
+ misc::write,
|
||||
+ secret::{GuestSecret, RetrievedSecret},
|
||||
+ uv::{RetrieveCmd, SecretId, UvDevice},
|
||||
+};
|
||||
+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())
|
||||
+ .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:?}");
|
||||
+
|
||||
+ let mut uv_cmd = RetrieveCmd::from_entry(secret)?;
|
||||
+ uv.send_cmd(&mut uv_cmd)?;
|
||||
+
|
||||
+ Ok(RetrievedSecret::from_cmd(uv_cmd))
|
||||
+}
|
||||
+
|
||||
+pub fn retr(opt: &RetrSecretOptions) -> Result<()> {
|
||||
+ let mut output = get_writer_from_cli_file_arg(&opt.output)?;
|
||||
+ let id = match &opt.inform {
|
||||
+ RetrInpFmt::Yaml => match serde_yaml::from_reader(&mut open_file(&opt.input)?)? {
|
||||
+ GuestSecret::Retrievable { 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")?
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ let retr_secret =
|
||||
+ retrieve(&id).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.output,
|
||||
+ "IBM Protected Key",
|
||||
+ )?;
|
||||
+ Ok(())
|
||||
+}
|
||||
diff --git a/rust/pvsecret/src/main.rs b/rust/pvsecret/src/main.rs
|
||||
index 502a6ea0..883a3ee2 100644
|
||||
--- a/rust/pvsecret/src/main.rs
|
||||
+++ b/rust/pvsecret/src/main.rs
|
||||
@@ -45,6 +45,7 @@ fn main() -> ExitCode {
|
||||
Command::Create(opt) => cmd::create(opt),
|
||||
Command::Version => Ok(print_version!("2024", log_level; FEATURES.concat())),
|
||||
Command::Verify(opt) => cmd::verify(opt),
|
||||
+ Command::Retrieve(opt) => cmd::retr(opt),
|
||||
};
|
||||
|
||||
match res {
|
313
s390-tools-General-update-09.patch
Normal file
313
s390-tools-General-update-09.patch
Normal file
@ -0,0 +1,313 @@
|
||||
From 256289a30aa5d3f6a4d2631dea69d1dc47205150 Mon Sep 17 00:00:00 2001
|
||||
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Date: Wed, 12 Jun 2024 16:23:31 +0200
|
||||
Subject: [PATCH] rust/pv_core: Refactor secret list
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Improve the secret list implementation. Use structs+{As,From}Bytes
|
||||
instead of arbitrary seeks and reads/writes to parse the secret list.
|
||||
|
||||
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_core/src/uvdevice.rs | 10 ++
|
||||
rust/pv_core/src/uvdevice/secret_list.rs | 124 ++++++++++++++---------
|
||||
2 files changed, 86 insertions(+), 48 deletions(-)
|
||||
|
||||
diff --git a/rust/pv_core/src/uvdevice.rs b/rust/pv_core/src/uvdevice.rs
|
||||
index e9848243..e701366d 100644
|
||||
--- a/rust/pv_core/src/uvdevice.rs
|
||||
+++ b/rust/pv_core/src/uvdevice.rs
|
||||
@@ -163,6 +163,16 @@ pub enum UvcSuccess {
|
||||
RC_MORE_DATA = UvDevice::RC_MORE_DATA,
|
||||
}
|
||||
|
||||
+impl UvcSuccess {
|
||||
+ /// Returns true if there is more data available
|
||||
+ pub fn more_data(&self) -> bool {
|
||||
+ match self {
|
||||
+ Self::RC_SUCCESS => false,
|
||||
+ Self::RC_MORE_DATA => true,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
/// The `UvDevice` is a (virtual) device on s390 machines to send Ultravisor commands(UVCs) from
|
||||
/// userspace.
|
||||
///
|
||||
diff --git a/rust/pv_core/src/uvdevice/secret_list.rs b/rust/pv_core/src/uvdevice/secret_list.rs
|
||||
index 4e955010..d7c268c9 100644
|
||||
--- a/rust/pv_core/src/uvdevice/secret_list.rs
|
||||
+++ b/rust/pv_core/src/uvdevice/secret_list.rs
|
||||
@@ -4,16 +4,16 @@
|
||||
|
||||
use crate::{
|
||||
assert_size,
|
||||
- misc::to_u16,
|
||||
uv::{AesSizes, AesXtsSizes, EcCurves, HmacShaSizes, ListCmd, RetrievableSecret},
|
||||
uvdevice::UvCmd,
|
||||
Error, Result,
|
||||
};
|
||||
-use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt};
|
||||
+use byteorder::{BigEndian, ByteOrder};
|
||||
use serde::{Deserialize, Serialize, Serializer};
|
||||
use std::{
|
||||
fmt::Display,
|
||||
io::{Cursor, Read, Seek, Write},
|
||||
+ mem::size_of,
|
||||
slice::Iter,
|
||||
vec::IntoIter,
|
||||
};
|
||||
@@ -31,7 +31,7 @@ impl SecretId {
|
||||
/// Size in bytes of the [`SecretId`]
|
||||
pub const ID_SIZE: usize = 32;
|
||||
|
||||
- /// Create a [`SecretId`] forom a buffer.
|
||||
+ /// Create a [`SecretId`] from a buffer.
|
||||
pub fn from(buf: [u8; Self::ID_SIZE]) -> Self {
|
||||
buf.into()
|
||||
}
|
||||
@@ -120,7 +120,7 @@ impl SecretEntry {
|
||||
&self.index
|
||||
}
|
||||
|
||||
- /// Returns the secret type of this [`SecretEntry`].
|
||||
+ /// Returns the secret type of this [`SecretEntry`]
|
||||
pub fn stype(&self) -> ListableSecretType {
|
||||
self.stype.get().into()
|
||||
}
|
||||
@@ -161,12 +161,45 @@ impl Display for SecretEntry {
|
||||
}
|
||||
}
|
||||
|
||||
+#[repr(C)]
|
||||
+#[derive(Debug, FromBytes, AsBytes, FromZeroes, Clone, PartialEq, Eq, Default, Serialize)]
|
||||
+struct SecretListHdr {
|
||||
+ #[serde(skip)]
|
||||
+ num_secrets_stored: U16<BigEndian>,
|
||||
+ #[serde(serialize_with = "ser_u16")]
|
||||
+ total_num_secrets: U16<BigEndian>,
|
||||
+ #[serde(skip)]
|
||||
+ next_secret_idx: U16<BigEndian>,
|
||||
+ #[serde(skip)]
|
||||
+ reserved_06: u16,
|
||||
+ #[serde(skip)]
|
||||
+ reserved_08: u64,
|
||||
+}
|
||||
+
|
||||
+impl SecretListHdr {
|
||||
+ fn new(num_secrets_stored: u16, total_num_secrets: u16, next_secret_idx: u16) -> Self {
|
||||
+ Self {
|
||||
+ num_secrets_stored: num_secrets_stored.into(),
|
||||
+ total_num_secrets: total_num_secrets.into(),
|
||||
+ next_secret_idx: next_secret_idx.into(),
|
||||
+ reserved_06: 0,
|
||||
+ reserved_08: 0,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+assert_size!(SecretListHdr, 16);
|
||||
+
|
||||
/// List of secrets used to parse the [`crate::uv::ListCmd`] result.
|
||||
///
|
||||
-/// The list should not hold more than 0xffffffff elements
|
||||
-#[derive(Debug, PartialEq, Eq, Serialize)]
|
||||
+/// The list should ONLY be created from an UV-Call result using either:
|
||||
+/// - [`TryInto::try_into`] from [`ListCmd`]
|
||||
+/// - [`SecretList::decode`]
|
||||
+/// Any other ways can create invalid lists that do not represent the UV secret store.
|
||||
+/// The list must not hold more than [`u32::MAX`] elements
|
||||
+#[derive(Debug, PartialEq, Eq, Serialize, Default)]
|
||||
pub struct SecretList {
|
||||
- total_num_secrets: usize,
|
||||
+ #[serde(flatten)]
|
||||
+ hdr: SecretListHdr,
|
||||
secrets: Vec<SecretEntry>,
|
||||
}
|
||||
|
||||
@@ -202,10 +235,14 @@ impl SecretList {
|
||||
/// The content of this list will very likely not represent the status of the guest in the
|
||||
/// Ultravisor. Use of [`SecretList::decode`] in any non-test environments is encuraged.
|
||||
pub fn new(total_num_secrets: u16, secrets: Vec<SecretEntry>) -> Self {
|
||||
- Self {
|
||||
- total_num_secrets: total_num_secrets as usize,
|
||||
+ Self::new_with_hdr(
|
||||
+ SecretListHdr::new(total_num_secrets, total_num_secrets, 0),
|
||||
secrets,
|
||||
- }
|
||||
+ )
|
||||
+ }
|
||||
+
|
||||
+ fn new_with_hdr(hdr: SecretListHdr, secrets: Vec<SecretEntry>) -> Self {
|
||||
+ Self { hdr, secrets }
|
||||
}
|
||||
|
||||
/// Returns an iterator over the slice.
|
||||
@@ -229,19 +266,12 @@ impl SecretList {
|
||||
///
|
||||
/// This number may be not equal to the provided number of [`SecretEntry`]
|
||||
pub fn total_num_secrets(&self) -> usize {
|
||||
- self.total_num_secrets
|
||||
+ self.hdr.total_num_secrets.get() as usize
|
||||
}
|
||||
|
||||
/// Encodes the list in the same binary format the UV would do
|
||||
pub fn encode<T: Write>(&self, w: &mut T) -> Result<()> {
|
||||
- let num_s = to_u16(self.secrets.len()).ok_or(Error::ManySecrets)?;
|
||||
- w.write_u16::<BigEndian>(num_s)?;
|
||||
- w.write_u16::<BigEndian>(
|
||||
- self.total_num_secrets
|
||||
- .try_into()
|
||||
- .map_err(|_| Error::ManySecrets)?,
|
||||
- )?;
|
||||
- w.write_all(&[0u8; 12])?;
|
||||
+ w.write_all(self.hdr.as_bytes())?;
|
||||
for secret in &self.secrets {
|
||||
w.write_all(secret.as_bytes())?;
|
||||
}
|
||||
@@ -250,19 +280,20 @@ impl SecretList {
|
||||
|
||||
/// Decodes the list from the binary format of the UV into this internal representation
|
||||
pub fn decode<R: Read + Seek>(r: &mut R) -> std::io::Result<Self> {
|
||||
- let num_s = r.read_u16::<BigEndian>()?;
|
||||
- let total_num_secrets = r.read_u16::<BigEndian>()? as usize;
|
||||
- let mut v: Vec<SecretEntry> = Vec::with_capacity(num_s as usize);
|
||||
- r.seek(std::io::SeekFrom::Current(12))?; // skip reserved bytes
|
||||
+ let mut buf = [0u8; size_of::<SecretListHdr>()];
|
||||
+ r.read_exact(&mut buf)?;
|
||||
+ let hdr = SecretListHdr::ref_from(&buf).unwrap();
|
||||
+
|
||||
let mut buf = [0u8; SecretEntry::STRUCT_SIZE];
|
||||
- for _ in 0..num_s {
|
||||
+ let mut v = Vec::with_capacity(hdr.num_secrets_stored.get() as usize);
|
||||
+ for _ in 0..hdr.num_secrets_stored.get() {
|
||||
r.read_exact(&mut buf)?;
|
||||
// cannot fail. buffer has the same size as the secret entry
|
||||
let secr = SecretEntry::read_from(buf.as_slice()).unwrap();
|
||||
v.push(secr);
|
||||
}
|
||||
Ok(Self {
|
||||
- total_num_secrets,
|
||||
+ hdr: hdr.clone(),
|
||||
secrets: v,
|
||||
})
|
||||
}
|
||||
@@ -278,7 +309,7 @@ impl TryFrom<ListCmd> for SecretList {
|
||||
|
||||
impl Display for SecretList {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
- writeln!(f, "Total number of secrets: {}", self.total_num_secrets)?;
|
||||
+ writeln!(f, "Total number of secrets: {}", self.total_num_secrets())?;
|
||||
if !self.secrets.is_empty() {
|
||||
writeln!(f)?;
|
||||
}
|
||||
@@ -481,8 +512,8 @@ mod test {
|
||||
let buf = [
|
||||
0x00u8, 0x01, // num secr stored
|
||||
0x01, 0x12, // total num secrets
|
||||
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
- 0x00, // reserved
|
||||
+ 0x01, 0x01, // next valid idx
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
|
||||
// secret
|
||||
0x00, 0x01, 0x00, 0x02, // idx + type
|
||||
0x00, 0x00, 0x00, 0x20, // len
|
||||
@@ -493,16 +524,16 @@ mod test {
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
];
|
||||
|
||||
- let exp = SecretList {
|
||||
- total_num_secrets: 0x112,
|
||||
- secrets: vec![SecretEntry {
|
||||
+ let exp = SecretList::new_with_hdr(
|
||||
+ SecretListHdr::new(0x001, 0x112, 0x101),
|
||||
+ vec![SecretEntry {
|
||||
index: 1.into(),
|
||||
stype: 2.into(),
|
||||
len: 32.into(),
|
||||
res_8: 0,
|
||||
id: SecretId::from([0; 32]),
|
||||
}],
|
||||
- };
|
||||
+ );
|
||||
|
||||
let mut br = BufReader::new(Cursor::new(buf));
|
||||
let sl = SecretList::decode(&mut br).unwrap();
|
||||
@@ -514,8 +545,8 @@ mod test {
|
||||
const EXP: &[u8] = &[
|
||||
0x00, 0x01, // num secr stored
|
||||
0x01, 0x12, // total num secrets
|
||||
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
- 0x00, // reserved
|
||||
+ 0x01, 0x01, // next valid idx
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
|
||||
// secret
|
||||
0x00, 0x01, 0x00, 0x02, // idx + type
|
||||
0x00, 0x00, 0x00, 0x20, // len
|
||||
@@ -526,16 +557,16 @@ mod test {
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
];
|
||||
|
||||
- let sl = SecretList {
|
||||
- total_num_secrets: 0x112,
|
||||
- secrets: vec![SecretEntry {
|
||||
+ let sl = SecretList::new_with_hdr(
|
||||
+ SecretListHdr::new(0x001, 0x112, 0x101),
|
||||
+ vec![SecretEntry {
|
||||
index: 1.into(),
|
||||
stype: 2.into(),
|
||||
len: 32.into(),
|
||||
res_8: 0,
|
||||
id: SecretId::from([0; 32]),
|
||||
}],
|
||||
- };
|
||||
+ );
|
||||
|
||||
let mut buf = [0u8; 0x40];
|
||||
{
|
||||
@@ -587,26 +618,23 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn secret_list_ser() {
|
||||
- let list = SecretList {
|
||||
- total_num_secrets: 0x112,
|
||||
- secrets: vec![SecretEntry {
|
||||
+ let list = SecretList::new_with_hdr(
|
||||
+ SecretListHdr::new(0x001, 0x112, 0x101),
|
||||
+ vec![SecretEntry {
|
||||
index: 1.into(),
|
||||
stype: 2.into(),
|
||||
len: 32.into(),
|
||||
res_8: 0,
|
||||
id: SecretId::from([0; 32]),
|
||||
}],
|
||||
- };
|
||||
+ );
|
||||
|
||||
assert_ser_tokens(
|
||||
&list,
|
||||
&[
|
||||
- Token::Struct {
|
||||
- name: "SecretList",
|
||||
- len: 2,
|
||||
- },
|
||||
+ Token::Map { len: None },
|
||||
Token::String("total_num_secrets"),
|
||||
- Token::U64(0x112),
|
||||
+ Token::U16(0x112),
|
||||
Token::String("secrets"),
|
||||
Token::Seq { len: Some(1) },
|
||||
Token::Struct {
|
||||
@@ -623,7 +651,7 @@ mod test {
|
||||
Token::String("0x0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
Token::StructEnd,
|
||||
Token::SeqEnd,
|
||||
- Token::StructEnd,
|
||||
+ Token::MapEnd,
|
||||
],
|
||||
)
|
||||
}
|
111
s390-tools-General-update-10.patch
Normal file
111
s390-tools-General-update-10.patch
Normal file
@ -0,0 +1,111 @@
|
||||
From 93216d916c479ee1292aa1d598ac9c0e7f585bd8 Mon Sep 17 00:00:00 2001
|
||||
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Date: Wed, 12 Jun 2024 16:35:15 +0200
|
||||
Subject: [PATCH] rust/pv*: Support longer secret lists
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Make use of the enhanced list secrets UAPI for the uvdevice in the latest kernel
|
||||
version. This allows fetching secret lists with more than 85 entries via
|
||||
reserving more userspace memory in the IOCTL argument.
|
||||
|
||||
While at it, move the errno readout next to the ioctl-syscall.
|
||||
|
||||
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_core/src/uvdevice.rs | 6 ++++--
|
||||
rust/pv_core/src/uvdevice/secret.rs | 11 +++++++++++
|
||||
rust/pvsecret/src/cmd/list.rs | 28 +++++++++++++++++++++-------
|
||||
3 files changed, 36 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/rust/pv_core/src/uvdevice.rs b/rust/pv_core/src/uvdevice.rs
|
||||
index e701366d..689748a1 100644
|
||||
--- a/rust/pv_core/src/uvdevice.rs
|
||||
+++ b/rust/pv_core/src/uvdevice.rs
|
||||
@@ -59,11 +59,13 @@ fn ioctl_raw(raw_fd: RawFd, cmd: c_ulong, cb: &mut IoctlCb) -> Result<()> {
|
||||
rc = ioctl(raw_fd, cmd, cb.as_ptr_mut());
|
||||
}
|
||||
|
||||
+ // NOTE io::Error handles all errnos ioctl uses
|
||||
+ let errno = std::io::Error::last_os_error();
|
||||
+
|
||||
debug!("ioctl resulted with {cb:?}");
|
||||
match rc {
|
||||
0 => Ok(()),
|
||||
- // NOTE io::Error handles all errnos ioctl uses
|
||||
- _ => Err(std::io::Error::last_os_error().into()),
|
||||
+ _ => Err(errno.into()),
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/rust/pv_core/src/uvdevice/secret.rs b/rust/pv_core/src/uvdevice/secret.rs
|
||||
index 263f17d5..cb5b7233 100644
|
||||
--- a/rust/pv_core/src/uvdevice/secret.rs
|
||||
+++ b/rust/pv_core/src/uvdevice/secret.rs
|
||||
@@ -24,6 +24,17 @@ impl ListCmd {
|
||||
Self(vec![0; size])
|
||||
}
|
||||
|
||||
+ /// Create a new list secrets command with `pages` capacity.
|
||||
+ ///
|
||||
+ /// * `pages` - number pf pages to allocate for this IOCTL
|
||||
+ ///
|
||||
+ /// # Panic
|
||||
+ /// This function will trigger a panic if the allocation size is larger than [`usize::MAX`].
|
||||
+ /// Very likely an OOM situation occurs way before this!
|
||||
+ pub fn with_pages(pages: usize) -> Self {
|
||||
+ Self::with_size(pages * PAGESIZE)
|
||||
+ }
|
||||
+
|
||||
/// Create a new list secrets command with a one page capacity
|
||||
pub fn new() -> Self {
|
||||
Self::with_size(PAGESIZE)
|
||||
diff --git a/rust/pvsecret/src/cmd/list.rs b/rust/pvsecret/src/cmd/list.rs
|
||||
index 0bd9eca4..56294cac 100644
|
||||
--- a/rust/pvsecret/src/cmd/list.rs
|
||||
+++ b/rust/pvsecret/src/cmd/list.rs
|
||||
@@ -2,19 +2,33 @@
|
||||
//
|
||||
// Copyright IBM Corp. 2023
|
||||
|
||||
+use std::io::ErrorKind;
|
||||
+
|
||||
use crate::cli::{ListSecretOpt, ListSecretOutputType};
|
||||
use anyhow::{Context, Error, Result};
|
||||
-use log::warn;
|
||||
-use pv::uv::{ListCmd, SecretList, UvDevice, UvcSuccess};
|
||||
+use log::{info, warn};
|
||||
+use pv::uv::{ListCmd, SecretList, UvDevice};
|
||||
use utils::{get_writer_from_cli_file_arg, STDOUT};
|
||||
|
||||
+const SECRET_LIST_BUF_SIZE: usize = 4;
|
||||
+
|
||||
/// Do a List Secrets UVC
|
||||
pub fn list_uvc(uv: &UvDevice) -> Result<SecretList> {
|
||||
- let mut cmd = ListCmd::default();
|
||||
- match uv.send_cmd(&mut cmd)? {
|
||||
- UvcSuccess::RC_SUCCESS => (),
|
||||
- UvcSuccess::RC_MORE_DATA => warn!("There is more data available than expected"),
|
||||
- };
|
||||
+ let mut cmd = ListCmd::with_pages(SECRET_LIST_BUF_SIZE);
|
||||
+ let more_data = match uv.send_cmd(&mut cmd) {
|
||||
+ Ok(v) => Ok(v),
|
||||
+ Err(pv::PvCoreError::Io(e)) if e.kind() == ErrorKind::InvalidInput => {
|
||||
+ info!("Uvdevice does not suport longer list. Fallback to one page list.");
|
||||
+ cmd = ListCmd::default();
|
||||
+ uv.send_cmd(&mut cmd)
|
||||
+ }
|
||||
+ Err(e) => Err(e),
|
||||
+ }?
|
||||
+ .more_data();
|
||||
+ if more_data {
|
||||
+ warn!("The secret list contains more data but the uvdevice cannot show all.");
|
||||
+ }
|
||||
+
|
||||
cmd.try_into().map_err(Error::new)
|
||||
}
|
||||
|
387
s390-tools-General-update-11.patch
Normal file
387
s390-tools-General-update-11.patch
Normal file
@ -0,0 +1,387 @@
|
||||
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 =
|
1207
s390-tools-General-update-12.patch
Normal file
1207
s390-tools-General-update-12.patch
Normal file
File diff suppressed because it is too large
Load Diff
167
s390-tools-pvimg-additional-01.patch
Normal file
167
s390-tools-pvimg-additional-01.patch
Normal file
@ -0,0 +1,167 @@
|
||||
From 5b6d7a467dc342c9c25a0af72b2d5546798cdc94 Mon Sep 17 00:00:00 2001
|
||||
From: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Date: Thu, 12 Dec 2024 20:19:56 +0100
|
||||
Subject: [PATCH] rust/pvimg: Add '--cck <FILE>' command line option and make
|
||||
'--comm-key' an alias
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Add '--cck <FILE>' as an command line option and make '--comm-key' an
|
||||
alias of it. This makes the command line more similar to the other
|
||||
Secure Execution related PV-tools (e.g. pvattest and pvsecret).
|
||||
|
||||
Suggested-by: Reinhard Bündgen <buendgen@de.ibm.com>
|
||||
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
rust/pvimg/man/genprotimg.1 | 11 +++++------
|
||||
rust/pvimg/man/pvimg-create.1 | 11 +++++------
|
||||
rust/pvimg/src/cli.rs | 14 ++++++++------
|
||||
rust/pvimg/src/cmd/create.rs | 3 +--
|
||||
4 files changed, 19 insertions(+), 20 deletions(-)
|
||||
|
||||
Index: s390-tools-2.36.0/rust/pvimg/man/genprotimg.1
|
||||
===================================================================
|
||||
--- s390-tools-2.36.0.orig/rust/pvimg/man/genprotimg.1
|
||||
+++ s390-tools-2.36.0/rust/pvimg/man/genprotimg.1
|
||||
@@ -123,7 +123,7 @@ Overwrite an existing Secure Execution b
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
-\-\-comm\-key <FILE>
|
||||
+\-\-cck, \-\-comm\-key <FILE>
|
||||
.RS 4
|
||||
Use the content of FILE as the customer\-communication key (CCK). The file must
|
||||
contain exactly 32 bytes of data.
|
||||
@@ -133,7 +133,7 @@ contain exactly 32 bytes of data.
|
||||
\-\-enable\-dump
|
||||
.RS 4
|
||||
Enable Secure Execution guest dump support. This option requires the
|
||||
-\fB\-\-comm\-key\fR option.
|
||||
+\fB\-\-cck\fR option.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
@@ -146,8 +146,7 @@ Disable Secure Execution guest dump supp
|
||||
\-\-enable\-cck\-extension\-secret
|
||||
.RS 4
|
||||
Add\-secret requests must provide an extension secret that matches the
|
||||
-CCK\-derived extension secret. This option requires the \fB\-\-comm\-key\fR
|
||||
-option.
|
||||
+CCK\-derived extension secret. This option requires the \fB\-\-cck\fR option.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
@@ -268,7 +267,7 @@ Generate an IBM Secure Execution image:
|
||||
|
||||
Generate an IBM Secure Execution image with Secure Execution guest dump support:
|
||||
.PP
|
||||
-.B genprotimg \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/secure-linux\/\fR \-\-enable\-dump \-\-comm\-key \fI\,comm-key\fR
|
||||
+.B genprotimg \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/secure-linux\/\fR \-\-enable\-dump \-\-cck \fI\,comm-key\fR
|
||||
.SH NOTES
|
||||
.IP "1." 4
|
||||
The \fBgenprotimg\fR(1) command is a symbolic link to the \fBpvimg-create\fR(1) command.
|
||||
Index: s390-tools-2.36.0/rust/pvimg/man/pvimg-create.1
|
||||
===================================================================
|
||||
--- s390-tools-2.36.0.orig/rust/pvimg/man/pvimg-create.1
|
||||
+++ s390-tools-2.36.0/rust/pvimg/man/pvimg-create.1
|
||||
@@ -122,7 +122,7 @@ Overwrite an existing Secure Execution b
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
-\-\-comm\-key <FILE>
|
||||
+\-\-cck, \-\-comm\-key <FILE>
|
||||
.RS 4
|
||||
Use the content of FILE as the customer\-communication key (CCK). The file must
|
||||
contain exactly 32 bytes of data.
|
||||
@@ -132,7 +132,7 @@ contain exactly 32 bytes of data.
|
||||
\-\-enable\-dump
|
||||
.RS 4
|
||||
Enable Secure Execution guest dump support. This option requires the
|
||||
-\fB\-\-comm\-key\fR option.
|
||||
+\fB\-\-cck\fR option.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
@@ -145,8 +145,7 @@ Disable Secure Execution guest dump supp
|
||||
\-\-enable\-cck\-extension\-secret
|
||||
.RS 4
|
||||
Add\-secret requests must provide an extension secret that matches the
|
||||
-CCK\-derived extension secret. This option requires the \fB\-\-comm\-key\fR
|
||||
-option.
|
||||
+CCK\-derived extension secret. This option requires the \fB\-\-cck\fR option.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
@@ -249,7 +248,7 @@ Generate an IBM Secure Execution image:
|
||||
|
||||
Generate an IBM Secure Execution image with Secure Execution guest dump support:
|
||||
.PP
|
||||
-.B pvimg create \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/secure-linux\/\fR \-\-enable\-dump \-\-comm\-key \fI\,comm-key\fR
|
||||
+.B pvimg create \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/secure-linux\/\fR \-\-enable\-dump \-\-cck \fI\,comm-key\fR
|
||||
.SH NOTES
|
||||
.IP "1." 4
|
||||
The \fBgenprotimg\fR(1) command is a symbolic link to the \fBpvimg-create\fR(1) command.
|
||||
Index: s390-tools-2.36.0/rust/pvimg/src/cli.rs
|
||||
===================================================================
|
||||
--- s390-tools-2.36.0.orig/rust/pvimg/src/cli.rs
|
||||
+++ s390-tools-2.36.0/rust/pvimg/src/cli.rs
|
||||
@@ -96,8 +96,8 @@ pub struct ComponentPaths {
|
||||
#[command(group(ArgGroup::new("header-flags").multiple(true).conflicts_with_all(["x_pcf", "x_scf"])))]
|
||||
pub struct CreateBootImageLegacyFlags {
|
||||
/// Enable Secure Execution guest dump support. This option requires the
|
||||
- /// '--comm-key' option.
|
||||
- #[arg(long, action = clap::ArgAction::SetTrue, requires="comm_key", group="header-flags")]
|
||||
+ /// '--cck' option.
|
||||
+ #[arg(long, action = clap::ArgAction::SetTrue, requires="cck", group="header-flags")]
|
||||
pub enable_dump: Option<bool>,
|
||||
|
||||
/// Disable Secure Execution guest dump support (default).
|
||||
@@ -105,9 +105,9 @@ pub struct CreateBootImageLegacyFlags {
|
||||
pub disable_dump: Option<bool>,
|
||||
|
||||
/// Add-secret requests must provide an extension secret that matches the
|
||||
- /// CCK-derived extension secret. This option requires the '--comm-key'
|
||||
+ /// CCK-derived extension secret. This option requires the '--cck'
|
||||
/// option.
|
||||
- #[arg(long, action = clap::ArgAction::SetTrue, requires="comm_key", group="header-flags")]
|
||||
+ #[arg(long, action = clap::ArgAction::SetTrue, requires="cck", group="header-flags")]
|
||||
pub enable_cck_extension_secret: Option<bool>,
|
||||
|
||||
/// Add-secret requests don't have to provide the CCK-derived extension
|
||||
@@ -328,8 +328,8 @@ pub struct CreateBootImageArgs {
|
||||
/// Use the content of FILE as the customer-communication key (CCK).
|
||||
///
|
||||
/// The file must contain exactly 32 bytes of data.
|
||||
- #[arg(long, value_name = "FILE")]
|
||||
- pub comm_key: Option<PathBuf>,
|
||||
+ #[arg(long, value_name = "FILE", visible_alias = "comm-key")]
|
||||
+ pub cck: Option<PathBuf>,
|
||||
|
||||
#[clap(flatten)]
|
||||
pub legacy_flags: CreateBootImageLegacyFlags,
|
||||
@@ -482,6 +482,8 @@ mod test {
|
||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"]),
|
||||
CliOption::new("comm-key", ["--comm-key", "/dev/null"])])),
|
||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"]),
|
||||
+ CliOption::new("comm-key", ["--cck", "/dev/null"])])),
|
||||
+ flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"]),
|
||||
CliOption::new("comm-key", ["--comm-key", "/dev/null"])])),
|
||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-pcf", ["--x-pcf", "0x0"]),
|
||||
CliOption::new("x-scf", ["--x-scf", "0x0"])])),
|
||||
Index: s390-tools-2.36.0/rust/pvimg/src/cmd/create.rs
|
||||
===================================================================
|
||||
--- s390-tools-2.36.0.orig/rust/pvimg/src/cmd/create.rs
|
||||
+++ s390-tools-2.36.0/rust/pvimg/src/cmd/create.rs
|
||||
@@ -137,8 +137,7 @@ pub fn create(opt: &CreateBootImageArgs)
|
||||
let verified_host_keys = opt
|
||||
.certificate_args
|
||||
.get_verified_hkds("Secure Execution image")?;
|
||||
- let user_provided_keys =
|
||||
- read_user_provided_keys(opt.comm_key.as_deref(), &opt.experimental_args)?;
|
||||
+ let user_provided_keys = read_user_provided_keys(opt.cck.as_deref(), &opt.experimental_args)?;
|
||||
let (plaintext_flags, secret_flags) = parse_flags(opt)?;
|
||||
|
||||
if plaintext_flags.is_set(PcfV1::NoComponentEncryption) {
|
58
s390-tools-pvimg-info-command-01.patch
Normal file
58
s390-tools-pvimg-info-command-01.patch
Normal file
@ -0,0 +1,58 @@
|
||||
From 560b276f7e9938475af921c8ebd4cd05910dbf31 Mon Sep 17 00:00:00 2001
|
||||
From: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Date: Fri, 6 Dec 2024 20:45:36 +0100
|
||||
Subject: [PATCH] rust/pvimg: Fix possible 'range start index out of range for
|
||||
slice' error
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Fix possible 'range start index 16 out of range for slice of length 0'
|
||||
error by adding a check of the slice data length.
|
||||
|
||||
Fixes: f4cf4ae6ebb1 ("rust: Add a new tool called 'pvimg'")
|
||||
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
rust/pvimg/src/pv_utils/se_hdr/brb.rs | 23 +++++++++++++++++++++++
|
||||
1 file changed, 23 insertions(+)
|
||||
|
||||
diff --git a/rust/pvimg/src/pv_utils/se_hdr/brb.rs b/rust/pvimg/src/pv_utils/se_hdr/brb.rs
|
||||
index f7ae1bc9..ac3a2e6e 100644
|
||||
--- a/rust/pvimg/src/pv_utils/se_hdr/brb.rs
|
||||
+++ b/rust/pvimg/src/pv_utils/se_hdr/brb.rs
|
||||
@@ -259,6 +259,10 @@ impl SeHdr {
|
||||
return Err(Error::InvalidSeHdr);
|
||||
}
|
||||
|
||||
+ if sehs <= common_size {
|
||||
+ return Err(Error::InvalidSeHdr);
|
||||
+ }
|
||||
+
|
||||
data.resize(sehs, 0);
|
||||
reader.read_exact(&mut data[common_size..])?;
|
||||
Self::try_from_data(&data)
|
||||
@@ -366,3 +370,22 @@ impl AeadCipherTrait for SeHdrPlain {
|
||||
self.data.aead_tag_size()
|
||||
}
|
||||
}
|
||||
+
|
||||
+#[cfg(test)]
|
||||
+mod tests {
|
||||
+ use std::io::Cursor;
|
||||
+
|
||||
+ use super::SeHdr;
|
||||
+ use crate::error::Error;
|
||||
+
|
||||
+ #[test]
|
||||
+ fn test_sehdr_try_from_io() {
|
||||
+ // Invalid SeHdr as `sehs` is set to 0
|
||||
+ assert!(matches!(
|
||||
+ SeHdr::try_from_io(Cursor::new([
|
||||
+ 73, 66, 77, 83, 101, 99, 69, 120, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 8
|
||||
+ ])),
|
||||
+ Err(Error::InvalidSeHdr)
|
||||
+ ));
|
||||
+ }
|
||||
+}
|
51
s390-tools-pvimg-info-command-02.patch
Normal file
51
s390-tools-pvimg-info-command-02.patch
Normal file
@ -0,0 +1,51 @@
|
||||
From 3f6572e901ddcc654021c4302cb2a99999acb87a Mon Sep 17 00:00:00 2001
|
||||
From: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Date: Wed, 18 Dec 2024 13:41:13 +0100
|
||||
Subject: [PATCH] rust/utils: mkdtemp: fix memory leak
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Fix memory leak of @template_raw. The documentation of CString::into_raw
|
||||
reads:
|
||||
|
||||
"Consumes the CString and transfers ownership of the string to a C
|
||||
caller.
|
||||
...
|
||||
Failure to call CString::from_raw will lead to a memory leak." [1]
|
||||
|
||||
Let's fix the memory leak by always calling `CString::from_raw` and
|
||||
therefore reclaim the ownership.
|
||||
|
||||
[1] https://doc.rust-lang.org/std/ffi/struct.CString.html#method.into_raw
|
||||
|
||||
Fixes: e56acf4f14b0 ("pv_core: add `TemporaryDirectory`")
|
||||
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
rust/utils/src/tmpfile.rs | 7 ++++---
|
||||
1 file changed, 4 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/rust/utils/src/tmpfile.rs b/rust/utils/src/tmpfile.rs
|
||||
index 07acdba8..883d5586 100644
|
||||
--- a/rust/utils/src/tmpfile.rs
|
||||
+++ b/rust/utils/src/tmpfile.rs
|
||||
@@ -16,13 +16,14 @@ fn mkdtemp<P: AsRef<Path>>(template: P) -> Result<PathBuf, std::io::Error> {
|
||||
// SAFETY: template_raw is a valid CString because it was generated by
|
||||
// the `CString::new`.
|
||||
let ret = libc::mkdtemp(template_raw);
|
||||
+ // SAFETY: `template_raw` is still a valid CString because it was
|
||||
+ // generated by `CString::new` and modified by `libc::mkdtemp`.
|
||||
+ let path_cstr = std::ffi::CString::from_raw(template_raw);
|
||||
|
||||
if ret.is_null() {
|
||||
+ drop(path_cstr);
|
||||
Err(std::io::Error::last_os_error())
|
||||
} else {
|
||||
- // SAFETY: `template_raw` is still a valid CString because it was
|
||||
- // generated by `CString::new` and modified by `libc::mkdtemp`.
|
||||
- let path_cstr = std::ffi::CString::from_raw(template_raw);
|
||||
let path = OsStr::from_bytes(path_cstr.as_bytes());
|
||||
let path = std::path::PathBuf::from(path);
|
||||
|
334
s390-tools-pvimg-info-command-03.patch
Normal file
334
s390-tools-pvimg-info-command-03.patch
Normal file
@ -0,0 +1,334 @@
|
||||
From 944581eaefe4c6887790f2b8ed39c9ee76146c55 Mon Sep 17 00:00:00 2001
|
||||
From: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Date: Tue, 17 Dec 2024 11:58:01 +0100
|
||||
Subject: [PATCH] rust/pvimg: Add upper estimates for the Secure Execution
|
||||
header
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
A Secure Execution header V1 can be at maximum two pages large, optional
|
||||
items are not supported, and the size of the encrypted part cannot be
|
||||
larger than the total size of the Secure Execution header add this as
|
||||
Deku assertions and additional conditions to the code. In addition, add
|
||||
a check for the number of key slots.
|
||||
|
||||
Fixes: f4cf4ae6ebb1 ("rust: Add a new tool called 'pvimg'")
|
||||
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
rust/pvimg/src/pv_utils/error.rs | 3 +
|
||||
rust/pvimg/src/pv_utils/se_hdr/brb.rs | 50 +++++++++++++---
|
||||
rust/pvimg/src/pv_utils/se_hdr/builder.rs | 10 +++-
|
||||
rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs | 71 ++++++++++++++++++++---
|
||||
rust/pvimg/src/pv_utils/uvdata.rs | 18 ++++--
|
||||
5 files changed, 130 insertions(+), 22 deletions(-)
|
||||
|
||||
diff --git a/rust/pvimg/src/pv_utils/error.rs b/rust/pvimg/src/pv_utils/error.rs
|
||||
index 2a176276..a12c4a22 100644
|
||||
--- a/rust/pvimg/src/pv_utils/error.rs
|
||||
+++ b/rust/pvimg/src/pv_utils/error.rs
|
||||
@@ -30,6 +30,9 @@ pub enum Error {
|
||||
#[error("Invalid Secure Execution header")]
|
||||
InvalidSeHdr,
|
||||
|
||||
+ #[error("Secure Execution header size {given} is larger than the maximum of {maximum} bytes")]
|
||||
+ InvalidSeHdrTooLarge { given: usize, maximum: usize },
|
||||
+
|
||||
#[error("Invalid component metadata.")]
|
||||
InvalidComponentMetadata,
|
||||
|
||||
diff --git a/rust/pvimg/src/pv_utils/se_hdr/brb.rs b/rust/pvimg/src/pv_utils/se_hdr/brb.rs
|
||||
index ac3a2e6e..b8dadba1 100644
|
||||
--- a/rust/pvimg/src/pv_utils/se_hdr/brb.rs
|
||||
+++ b/rust/pvimg/src/pv_utils/se_hdr/brb.rs
|
||||
@@ -171,8 +171,8 @@ impl AeadCipherTrait for SeHdr {
|
||||
}
|
||||
|
||||
impl AeadDataTrait for SeHdr {
|
||||
- fn aad(&self) -> Vec<u8> {
|
||||
- [serialize_to_bytes(&self.common).unwrap(), self.data.aad()].concat()
|
||||
+ fn aad(&self) -> Result<Vec<u8>> {
|
||||
+ Ok([serialize_to_bytes(&self.common)?, self.data.aad()?].concat())
|
||||
}
|
||||
|
||||
fn data(&self) -> Vec<u8> {
|
||||
@@ -265,7 +265,7 @@ impl SeHdr {
|
||||
|
||||
data.resize(sehs, 0);
|
||||
reader.read_exact(&mut data[common_size..])?;
|
||||
- Self::try_from_data(&data)
|
||||
+ Self::try_from_data(&data).map_err(|_| Error::InvalidSeHdr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,13 +342,13 @@ impl UvDataPlainTrait for SeHdrPlain {
|
||||
}
|
||||
|
||||
impl AeadPlainDataTrait for SeHdrPlain {
|
||||
- fn aad(&self) -> Vec<u8> {
|
||||
- let data_aad = self.data.aad();
|
||||
+ fn aad(&self) -> Result<Vec<u8>> {
|
||||
+ let data_aad = self.data.aad()?;
|
||||
|
||||
- [serialize_to_bytes(&self.common).unwrap(), data_aad].concat()
|
||||
+ Ok([serialize_to_bytes(&self.common)?, data_aad].concat())
|
||||
}
|
||||
|
||||
- fn data(&self) -> Confidential<Vec<u8>> {
|
||||
+ fn data(&self) -> Result<Confidential<Vec<u8>>> {
|
||||
self.data.data()
|
||||
}
|
||||
|
||||
@@ -387,5 +387,41 @@ mod tests {
|
||||
])),
|
||||
Err(Error::InvalidSeHdr)
|
||||
));
|
||||
+
|
||||
+ // Invalid SeHdr as the `sehs` is too large.
|
||||
+ assert!(matches!(
|
||||
+ SeHdr::try_from_io(Cursor::new([
|
||||
+ 73, 66, 77, 83, 101, 99, 69, 120, 0, 0, 1, 0, 0, 0, 1, 255, 65, 65, 65, 65, 67, 0,
|
||||
+ 65, 17, 65, 0, 65, 65, 65, 65, 65, 65, 91, 91, 180, 91, 91, 91, 91, 91, 91, 91, 91,
|
||||
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 241, 241,
|
||||
+ 241, 241, 241, 91, 91, 91, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112,
|
||||
+ 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 80,
|
||||
+ 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112,
|
||||
+ 112, 112, 112, 112, 91, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112,
|
||||
+ 112, 112, 112, 112, 112, 112, 112, 0, 0, 0, 0, 101, 99, 255, 255, 255, 255, 255,
|
||||
+ 255, 255, 255, 255, 255, 255, 65, 65, 65, 65, 67, 0, 65, 17, 65, 0, 65, 65, 65, 65,
|
||||
+ 65, 65, 91, 91, 180, 91, 91, 91, 91, 91, 91, 91, 91, 255, 255, 255, 255, 255, 255,
|
||||
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
+ 255, 255, 255, 255, 255, 255, 255, 255, 241, 241, 241, 241, 241, 91, 91, 91, 112,
|
||||
+ 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112,
|
||||
+ 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 80, 112, 112, 112, 112, 112, 112,
|
||||
+ 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 91, 112, 112,
|
||||
+ 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 73, 66, 77, 83, 101, 99, 69, 120,
|
||||
+ 0, 112, 112, 0, 1, 0, 0, 0, 0, 101, 99, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
+ 255, 255, 255, 65, 65, 65, 65, 67, 0, 65, 17, 65, 0, 65, 65, 65, 65, 65, 65, 91,
|
||||
+ 91, 180, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
|
||||
+ 91, 91, 112, 112, 112, 112, 112, 73, 66, 77, 83, 101, 99, 69, 120, 0, 0, 1, 0, 0,
|
||||
+ 0, 0, 48, 53, 53, 53, 53, 53, 53, 53, 91, 91, 91, 241, 241, 46, 49, 49, 0, 49, 49,
|
||||
+ 0, 0, 112, 112, 112, 91, 0, 0, 0, 0, 9, 0, 49, 50, 22, 241, 241, 241, 241, 241,
|
||||
+ 241, 241, 241, 241, 241, 241, 91, 91, 91, 91, 91, 255, 251, 0, 0, 91, 91, 91, 91,
|
||||
+ 91, 91, 91, 91, 91, 91, 91, 0, 0, 91, 0, 0, 10, 91, 91, 91, 65, 65, 65, 65
|
||||
+ ])),
|
||||
+ Err(Error::InvalidSeHdr)
|
||||
+ ));
|
||||
}
|
||||
}
|
||||
diff --git a/rust/pvimg/src/pv_utils/se_hdr/builder.rs b/rust/pvimg/src/pv_utils/se_hdr/builder.rs
|
||||
index ba6de898..93bcc7af 100644
|
||||
--- a/rust/pvimg/src/pv_utils/se_hdr/builder.rs
|
||||
+++ b/rust/pvimg/src/pv_utils/se_hdr/builder.rs
|
||||
@@ -230,8 +230,14 @@ mod tests {
|
||||
|
||||
let decrypted = bin.decrypt(&prot_key).expect("BUG");
|
||||
assert_eq!(bin.common, decrypted.common);
|
||||
- assert_eq!(bin.aad(), decrypted.aad());
|
||||
- assert_ne!(&bin.data(), decrypted.data().value());
|
||||
+ assert_eq!(
|
||||
+ bin.aad().expect("should not fail"),
|
||||
+ decrypted.aad().expect("should not fail")
|
||||
+ );
|
||||
+ assert_ne!(
|
||||
+ &bin.data(),
|
||||
+ decrypted.data().expect("should not fail").value()
|
||||
+ );
|
||||
let _decrypted_hdrv1: SeHdrDataV1 = decrypted.data.try_into().expect("BUG");
|
||||
}
|
||||
|
||||
diff --git a/rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs b/rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs
|
||||
index a7f2f609..b179d50d 100644
|
||||
--- a/rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs
|
||||
+++ b/rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs
|
||||
@@ -19,6 +19,7 @@ use serde::{Serialize, Serializer};
|
||||
use super::keys::phkh_v1;
|
||||
use crate::{
|
||||
error::Error,
|
||||
+ misc::PAGESIZE,
|
||||
pv_utils::{
|
||||
error::Result,
|
||||
se_hdr::{
|
||||
@@ -51,11 +52,14 @@ struct HdrSizesV1 {
|
||||
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)]
|
||||
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
|
||||
struct SeHdrAadV1 {
|
||||
+ #[deku(assert = "*sehs <= SeHdrDataV1::MAX_SIZE.try_into().unwrap()")]
|
||||
sehs: u32,
|
||||
#[serde(serialize_with = "ser_hex")]
|
||||
iv: [u8; SymKeyType::AES_256_GCM_IV_LEN],
|
||||
res1: u32,
|
||||
+ #[deku(assert = "*nks <= (*sehs).into()", update = "self.keyslots.len()")]
|
||||
nks: u64,
|
||||
+ #[deku(assert = "*sea <= (*sehs).into()")]
|
||||
sea: u64,
|
||||
nep: u64,
|
||||
#[serde(serialize_with = "ser_lower_hex")]
|
||||
@@ -118,6 +122,7 @@ pub struct SeHdrConfV1 {
|
||||
psw: PSW,
|
||||
#[serde(serialize_with = "ser_lower_hex")]
|
||||
scf: u64,
|
||||
+ #[deku(assert_eq = "0")]
|
||||
noi: u32,
|
||||
res2: u32,
|
||||
#[deku(count = "noi")]
|
||||
@@ -200,6 +205,7 @@ where
|
||||
}
|
||||
|
||||
impl SeHdrDataV1 {
|
||||
+ const MAX_SIZE: usize = 2 * PAGESIZE;
|
||||
const PCF_DEFAULT: u64 = 0x0;
|
||||
const SCF_DEFAULT: u64 = 0x0;
|
||||
|
||||
@@ -241,7 +247,14 @@ impl SeHdrDataV1 {
|
||||
tag: SeHdrTagV1::default(),
|
||||
};
|
||||
let hdr_size = ret.size()?;
|
||||
- ret.aad.sehs = hdr_size.phs.try_into()?;
|
||||
+ let phs = hdr_size.phs.try_into()?;
|
||||
+ if phs > Self::MAX_SIZE {
|
||||
+ return Err(Error::InvalidSeHdrTooLarge {
|
||||
+ given: phs,
|
||||
+ maximum: Self::MAX_SIZE,
|
||||
+ });
|
||||
+ }
|
||||
+ ret.aad.sehs = phs.try_into()?;
|
||||
ret.aad.sea = hdr_size.sea;
|
||||
Ok(ret)
|
||||
}
|
||||
@@ -494,8 +507,8 @@ impl KeyExchangeTrait for SeHdrBinV1 {
|
||||
}
|
||||
|
||||
impl AeadDataTrait for SeHdrBinV1 {
|
||||
- fn aad(&self) -> Vec<u8> {
|
||||
- serialize_to_bytes(&self.aad).unwrap()
|
||||
+ fn aad(&self) -> Result<Vec<u8>> {
|
||||
+ serialize_to_bytes(&self.aad)
|
||||
}
|
||||
|
||||
fn data(&self) -> Vec<u8> {
|
||||
@@ -508,12 +521,12 @@ impl AeadDataTrait for SeHdrBinV1 {
|
||||
}
|
||||
|
||||
impl AeadPlainDataTrait for SeHdrDataV1 {
|
||||
- fn aad(&self) -> Vec<u8> {
|
||||
- serialize_to_bytes(&self.aad).unwrap()
|
||||
+ fn aad(&self) -> Result<Vec<u8>> {
|
||||
+ serialize_to_bytes(&self.aad)
|
||||
}
|
||||
|
||||
- fn data(&self) -> Confidential<Vec<u8>> {
|
||||
- serialize_to_bytes(self.data.value()).unwrap().into()
|
||||
+ fn data(&self) -> Result<Confidential<Vec<u8>>> {
|
||||
+ Ok(serialize_to_bytes(self.data.value())?.into())
|
||||
}
|
||||
|
||||
fn tag(&self) -> Vec<u8> {
|
||||
@@ -610,4 +623,48 @@ mod tests {
|
||||
assert_eq!(psw, hdr_data_v1.data.value().psw);
|
||||
assert_eq!(cck.value(), hdr_data_v1.data.value().cck.value());
|
||||
}
|
||||
+
|
||||
+ #[test]
|
||||
+ fn max_size_sehdr_test() {
|
||||
+ const MAX_HOST_KEYS: usize = 95;
|
||||
+
|
||||
+ let (_, host_key) = get_test_key_and_cert();
|
||||
+ let pub_key = host_key.public_key().unwrap();
|
||||
+ let host_keys_max: Vec<_> = (0..MAX_HOST_KEYS).map(|_| pub_key.clone()).collect();
|
||||
+ let too_many_host_keys: Vec<_> = (0..MAX_HOST_KEYS + 1).map(|_| pub_key.clone()).collect();
|
||||
+ let xts_key = Confidential::new([0x3; SymKeyType::AES_256_XTS_KEY_LEN]);
|
||||
+ let meta = ComponentMetadataV1 {
|
||||
+ ald: [0x1; SHA_512_HASH_LEN],
|
||||
+ pld: [0x2; SHA_512_HASH_LEN],
|
||||
+ tld: [0x3; SHA_512_HASH_LEN],
|
||||
+ nep: 3,
|
||||
+ key: xts_key,
|
||||
+ };
|
||||
+ let psw = PSW {
|
||||
+ addr: 1234,
|
||||
+ mask: 5678,
|
||||
+ };
|
||||
+
|
||||
+ let mut builder = SeHdrBuilder::new(SeHdrVersion::V1, psw.clone(), meta.clone())
|
||||
+ .expect("should not fail");
|
||||
+ builder
|
||||
+ .add_hostkeys(&host_keys_max)
|
||||
+ .expect("should not fail")
|
||||
+ .with_components(meta.clone())
|
||||
+ .expect("should not fail");
|
||||
+ let bin = builder.build().expect("should not fail");
|
||||
+ assert_eq!(bin.common.version, SeHdrVersion::V1);
|
||||
+ let hdr_v1: SeHdrBinV1 = bin.data.try_into().expect("should not fail");
|
||||
+ assert_eq!(hdr_v1.aad.sehs, 8160);
|
||||
+
|
||||
+ let mut builder = SeHdrBuilder::new(SeHdrVersion::V1, psw.clone(), meta.clone())
|
||||
+ .expect("should not fail");
|
||||
+
|
||||
+ builder
|
||||
+ .add_hostkeys(&too_many_host_keys)
|
||||
+ .expect("should not fail")
|
||||
+ .with_components(meta)
|
||||
+ .expect("should not fail");
|
||||
+ assert!(matches!(builder.build(), Err(Error::InvalidSeHdr)));
|
||||
+ }
|
||||
}
|
||||
diff --git a/rust/pvimg/src/pv_utils/uvdata.rs b/rust/pvimg/src/pv_utils/uvdata.rs
|
||||
index b0ec355a..c6ed9567 100644
|
||||
--- a/rust/pvimg/src/pv_utils/uvdata.rs
|
||||
+++ b/rust/pvimg/src/pv_utils/uvdata.rs
|
||||
@@ -34,7 +34,7 @@ pub trait AeadCipherTrait {
|
||||
#[enum_dispatch]
|
||||
pub trait AeadDataTrait {
|
||||
/// Returns the authenticated associated data.
|
||||
- fn aad(&self) -> Vec<u8>;
|
||||
+ fn aad(&self) -> Result<Vec<u8>>;
|
||||
|
||||
/// Returns the encrypted data.
|
||||
fn data(&self) -> Vec<u8>;
|
||||
@@ -47,10 +47,10 @@ pub trait AeadDataTrait {
|
||||
#[enum_dispatch]
|
||||
pub trait AeadPlainDataTrait {
|
||||
/// Returns the authenticated associated data.
|
||||
- fn aad(&self) -> Vec<u8>;
|
||||
+ fn aad(&self) -> Result<Vec<u8>>;
|
||||
|
||||
/// Returns the unencrypted data.
|
||||
- fn data(&self) -> Confidential<Vec<u8>>;
|
||||
+ fn data(&self) -> Result<Confidential<Vec<u8>>>;
|
||||
|
||||
/// Returns the tag data.
|
||||
fn tag(&self) -> Vec<u8>;
|
||||
@@ -124,8 +124,14 @@ pub trait UvDataPlainTrait:
|
||||
expected: self.aead_key_type().to_string(),
|
||||
});
|
||||
}
|
||||
- let aad = self.aad();
|
||||
- let unecrypted_data = self.data();
|
||||
+ let aad = self.aad().map_err(|err| match err {
|
||||
+ Error::Deku(_) => Error::InvalidSeHdr,
|
||||
+ err => err,
|
||||
+ })?;
|
||||
+ let unecrypted_data = self.data().map_err(|err| match err {
|
||||
+ Error::Deku(_) => Error::InvalidSeHdr,
|
||||
+ err => err,
|
||||
+ })?;
|
||||
let iv = self.iv();
|
||||
let result = encrypt_aead(key, iv, &aad, unecrypted_data.value())?;
|
||||
Self::C::try_from_data(&result.into_buf())
|
||||
@@ -169,7 +175,7 @@ pub trait UvDataTrait: AeadDataTrait + AeadCipherTrait + KeyExchangeTrait + Clon
|
||||
}
|
||||
|
||||
let tag_size = self.aead_tag_size();
|
||||
- let aad = self.aad();
|
||||
+ let aad = self.aad()?;
|
||||
let unecrypted_data = self.data();
|
||||
let iv = self.iv();
|
||||
let tag = self.tag();
|
101
s390-tools-pvimg-info-command-04.patch
Normal file
101
s390-tools-pvimg-info-command-04.patch
Normal file
@ -0,0 +1,101 @@
|
||||
From 6e48c5ebaa26c6bd2a1bc33ccf36ed8bd6946358 Mon Sep 17 00:00:00 2001
|
||||
From: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Date: Tue, 17 Dec 2024 18:13:31 +0100
|
||||
Subject: [PATCH] pvimg: info: Rename '--key' into '--hdr-key' and use '--key'
|
||||
as an alias
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Rename '--key' into '--hdr-key' and use '--key' as an (non-visible)
|
||||
alias for '--hdr-key' in order to keep the command line backwards
|
||||
compatible. The chances of someone using '--key' are very low, as this
|
||||
version has not yet been released by any OS distribution.
|
||||
|
||||
This change makes the command line options for the different subcommands
|
||||
more consistent and therefore easier to use.
|
||||
|
||||
Suggested-by: Reinhard Bündgen <buendgen@de.ibm.com>
|
||||
Acked-by: Hendrik Brueckner <brueckner@linux.ibm.com>
|
||||
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
rust/pvimg/man/pvimg-info.1 | 2 +-
|
||||
rust/pvimg/src/cli.rs | 22 +++++++++++++++++++---
|
||||
rust/pvimg/src/cmd/info.rs | 2 +-
|
||||
3 files changed, 21 insertions(+), 5 deletions(-)
|
||||
|
||||
Index: s390-tools-2.36.0/rust/pvimg/man/pvimg-info.1
|
||||
===================================================================
|
||||
--- s390-tools-2.36.0.orig/rust/pvimg/man/pvimg-info.1
|
||||
+++ s390-tools-2.36.0/rust/pvimg/man/pvimg-info.1
|
||||
@@ -37,7 +37,7 @@ Possible values:
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
-\-\-key <FILE>
|
||||
+\-\-hdr\-key <FILE>
|
||||
.RS 4
|
||||
Use the key in FILE to decrypt the Secure Execution header.
|
||||
.RE
|
||||
Index: s390-tools-2.36.0/rust/pvimg/src/cli.rs
|
||||
===================================================================
|
||||
--- s390-tools-2.36.0.orig/rust/pvimg/src/cli.rs
|
||||
+++ s390-tools-2.36.0/rust/pvimg/src/cli.rs
|
||||
@@ -192,8 +192,8 @@ pub struct InfoArgs {
|
||||
pub format: OutputFormat,
|
||||
|
||||
/// Use the key in FILE to decrypt the Secure Execution header.
|
||||
- #[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath,)]
|
||||
- pub key: Option<PathBuf>,
|
||||
+ #[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath, alias = "key")]
|
||||
+ pub hdr_key: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
@@ -710,6 +710,22 @@ mod test {
|
||||
CliOption::new("image", ["/dev/null"]),
|
||||
],
|
||||
)),
|
||||
+ flat_map_collect(insert(
|
||||
+ args.clone(),
|
||||
+ vec![
|
||||
+ CliOption::new("hdr-key", ["--hdr-key", "/dev/null"]),
|
||||
+ CliOption::new("format", ["--format=json"]),
|
||||
+ CliOption::new("image", ["/dev/null"]),
|
||||
+ ],
|
||||
+ )),
|
||||
+ flat_map_collect(insert(
|
||||
+ args.clone(),
|
||||
+ vec![
|
||||
+ CliOption::new("hdr-key", ["--key", "/dev/null"]),
|
||||
+ CliOption::new("format", ["--format=json"]),
|
||||
+ CliOption::new("image", ["/dev/null"]),
|
||||
+ ],
|
||||
+ )),
|
||||
// separation between keyword and positional args works
|
||||
flat_map_collect(insert(
|
||||
args.clone(),
|
||||
@@ -750,7 +766,7 @@ mod test {
|
||||
|
||||
// Test for invalid combinations
|
||||
// Input is missing
|
||||
- let mut pvimg_invalid_args = vec![vec!["pvimg", "test"]];
|
||||
+ let mut pvimg_invalid_args = vec![vec!["pvimg", "info"]];
|
||||
|
||||
for create_args in &valid_test_args {
|
||||
pvimg_valid_args.push(
|
||||
Index: s390-tools-2.36.0/rust/pvimg/src/cmd/info.rs
|
||||
===================================================================
|
||||
--- s390-tools-2.36.0.orig/rust/pvimg/src/cmd/info.rs
|
||||
+++ s390-tools-2.36.0/rust/pvimg/src/cmd/info.rs
|
||||
@@ -27,7 +27,7 @@ pub fn info(opt: &InfoArgs) -> Result<Ow
|
||||
|
||||
SeHdr::seek_sehdr(&mut input, None)?;
|
||||
let hdr = SeHdr::try_from_io(input)?;
|
||||
- if let Some(key_path) = &opt.key {
|
||||
+ if let Some(key_path) = &opt.hdr_key {
|
||||
let key =
|
||||
SymKey::try_from_data(hdr.key_type(), read_file(key_path, "Reading key")?.into())?;
|
||||
serde_json::to_writer_pretty(&mut output, &hdr.decrypt(&key)?)?;
|
@ -1,3 +1,43 @@
|
||||
-------------------------------------------------------------------
|
||||
Thu Jan 9 07:05:53 UTC 2025 - Nikolay Gueorguiev <nikolay.gueorguiev@suse.com>
|
||||
|
||||
- Applied backport patches from s390-tools 2.37 to 2.36 ( jsc#PED-11870 )
|
||||
( jsc#IBM-1447, jsc#IBM-1062 )
|
||||
* s390-tools-General-update-01.patch
|
||||
* s390-tools-General-update-02.patch
|
||||
* s390-tools-General-update-03.patch
|
||||
* s390-tools-General-update-04.patch
|
||||
* s390-tools-General-update-05.patch
|
||||
* s390-tools-General-update-06.patch
|
||||
* s390-tools-General-update-07.patch
|
||||
* s390-tools-General-update-08.patch
|
||||
* s390-tools-General-update-09.patch
|
||||
* s390-tools-General-update-10.patch
|
||||
* s390-tools-General-update-11.patch
|
||||
* s390-tools-General-update-12.patch
|
||||
* s390-tools-Additional-update-01.patch
|
||||
* s390-tools-Additional-update-02.patch
|
||||
( jsc#IBM-1570, jsc#IBM-1571 )
|
||||
* s390-tools-Support-unencrypted-SE-images-01.patch
|
||||
( jsc#IBM-1572, jsc#IBM-1573 )
|
||||
* s390-tools-pvimg-info-command-01.patch
|
||||
* s390-tools-pvimg-info-command-02.patch
|
||||
* s390-tools-pvimg-info-command-03.patch
|
||||
* s390-tools-pvimg-info-command-04.patch
|
||||
( jsc#IBM-1576, jsc#IBM-1577 )
|
||||
* s390-tools-pvimg-additional-01.patch
|
||||
- Renamed patches from - to
|
||||
* s390-tools-01-opticsmon-Fix-runaway-loop-in-on_link_change.patch
|
||||
to
|
||||
s390-tools-Additional-update-01.patch
|
||||
* s390-tools-02-libzpci-opticsmon-Refactor-on_link_change-using-new.patch
|
||||
to
|
||||
s390-tools-Additional-update-02.patch
|
||||
* s390-tools-03-rust-pvimg-Add-enable-disable-image-encryption-flags-to-pvimg-create.patch
|
||||
to
|
||||
s390-tools-Support-unencrypted-SE-images-01.patch
|
||||
- Revendored vendor.tar.gz
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Tue Jan 7 08:59:16 UTC 2025 - Nikolay Gueorguiev <nikolay.gueorguiev@suse.com>
|
||||
|
||||
|
@ -153,14 +153,35 @@ Patch910: s390-tools-sles15sp1-11-zdev-Do-not-call-zipl-on-initrd-update.p
|
||||
Patch911: s390-tools-sles15sp5-remove-no-pie-link-arguments.patch
|
||||
Patch912: s390-tools-ALP-zdev-live.patch
|
||||
Patch913: s390-tools-sles15sp6-kdump-initrd-59-zfcp-compat-rules.patch
|
||||
###
|
||||
Patch914: s390-tools-01-zipl_helper.device-mapper-add-missed-step-in-logical.patch
|
||||
Patch915: s390-tools-02-zipl-src-fix-imprecise-check-that-file-is-on-specifi.patch
|
||||
###
|
||||
Patch916: s390-tools-01-opticsmon-Fix-runaway-loop-in-on_link_change.patch
|
||||
Patch917: s390-tools-02-libzpci-opticsmon-Refactor-on_link_change-using-new.patch
|
||||
Patch918: s390-tools-03-rust-pvimg-Add-enable-disable-image-encryption-flags-to-pvimg-create.patch
|
||||
Patch920: s390-tools-General-update-01.patch
|
||||
Patch921: s390-tools-General-update-02.patch
|
||||
Patch922: s390-tools-General-update-03.patch
|
||||
Patch923: s390-tools-General-update-04.patch
|
||||
Patch924: s390-tools-General-update-05.patch
|
||||
Patch925: s390-tools-General-update-06.patch
|
||||
Patch926: s390-tools-General-update-07.patch
|
||||
Patch927: s390-tools-General-update-08.patch
|
||||
Patch928: s390-tools-General-update-09.patch
|
||||
Patch929: s390-tools-General-update-10.patch
|
||||
Patch930: s390-tools-General-update-11.patch
|
||||
Patch931: s390-tools-General-update-12.patch
|
||||
###
|
||||
Patch920: s390-tools-slfo-01-parse-ipl-device-for-activation.patch
|
||||
Patch935: s390-tools-Additional-update-01.patch
|
||||
Patch936: s390-tools-Additional-update-02.patch
|
||||
###
|
||||
Patch950: s390-tools-pvimg-info-command-01.patch
|
||||
Patch951: s390-tools-pvimg-info-command-02.patch
|
||||
Patch952: s390-tools-pvimg-info-command-03.patch
|
||||
###
|
||||
Patch960: s390-tools-Support-unencrypted-SE-images-01.patch
|
||||
Patch961: s390-tools-pvimg-info-command-04.patch
|
||||
Patch962: s390-tools-pvimg-additional-01.patch
|
||||
###
|
||||
Patch990: s390-tools-slfo-01-parse-ipl-device-for-activation.patch
|
||||
###
|
||||
|
||||
BuildRequires: curl-devel
|
||||
@ -809,7 +830,7 @@ done
|
||||
%prep
|
||||
%autosetup -p1
|
||||
|
||||
install -D -m 0644 %{SOURCE200} .cargo/config
|
||||
install -D -m 0644 %{SOURCE200} .cargo/config.toml
|
||||
tar -xzf %{SOURCE201}
|
||||
|
||||
%build
|
||||
|
BIN
vendor.tar.gz
(Stored with Git LFS)
BIN
vendor.tar.gz
(Stored with Git LFS)
Binary file not shown.
Loading…
Reference in New Issue
Block a user