mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
rust: Refactoring and reduce API surface
Prepare pv & pv_core crates to be released on crates.io: * Remove any unused API to stay flexible * Remove utils dependency * Move cli, tmpfile and version utilities to local utils crate * Use the new utilities in the pv tools * Rename Secret into Confidential to avoid confusion of Secret (now Confidential) and AddSecret requests. * Move the uvsecret module out of the request module and change the name to secret. * Cleanup dependencies * Precise and correct minimal dependency versions * Inline `Aes256Key::from_digest` The cleanup ensures that the code also compiles with the dependencies resolved to their minimal versions using: $ cargo +nightly -Z minimal-versions update $ cargo build For more information refer to this blog post: https://users.rust-lang.org/t/psa-please-specify-precise-dependency-versions-in-cargo-toml/71277/8 Signed-off-by: Marc Hartmayer <mhartmay@de.ibm.com> Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
This commit is contained in:
+6
-22
@@ -8,19 +8,16 @@
|
||||
//! This library is intened to be used by tools and libraries that
|
||||
//! are used for creating and managing IBM Secure Execution guests.
|
||||
//! `pv_core` provides abstraction layers for secure memory management,
|
||||
//! logging, and accessing the uvdevice.
|
||||
//! and accessing the uvdevice.
|
||||
//!
|
||||
//! It does not provide any cryptographic operations through OpenSSL.
|
||||
//! For this use `pv` which reexports all symbos from this crate.
|
||||
mod error;
|
||||
mod log;
|
||||
mod macros;
|
||||
mod tmpfile;
|
||||
mod utils;
|
||||
mod uvdevice;
|
||||
mod uvsecret;
|
||||
|
||||
pub use crate::log::PvLogger;
|
||||
pub use error::{Error, FileAccessErrorType, FileIoErrorType, Result};
|
||||
|
||||
/// Miscellaneous functions and definitions
|
||||
@@ -30,27 +27,17 @@ pub mod misc {
|
||||
pub use crate::utils::{parse_hex, to_u16, to_u32, try_parse_u128, try_parse_u64};
|
||||
pub use crate::utils::{read, write};
|
||||
pub use crate::utils::{Flags, Lsb0Flags64, Msb0Flags64};
|
||||
|
||||
pub use crate::tmpfile::TemporaryDirectory;
|
||||
}
|
||||
|
||||
/// Definitions and functions for interacting with the Ultravisor
|
||||
pub mod uv {
|
||||
pub use crate::uvdevice::secret::{AddCmd, ListCmd, LockCmd};
|
||||
pub use crate::uvdevice::secret_list::{ListableSecretType, SecretEntry, SecretId, SecretList};
|
||||
pub use crate::uvdevice::{
|
||||
uv_ioctl, ConfigUid, UvCmd, UvDevice, UvDeviceInfo, UvFlags, UvcSuccess,
|
||||
};
|
||||
pub use crate::uvdevice::{ConfigUid, UvCmd, UvDevice, UvDeviceInfo, UvFlags, UvcSuccess};
|
||||
}
|
||||
|
||||
/// Functionalities to verify UV requests
|
||||
pub mod request {
|
||||
/// Functionalities for reading add-secret requests
|
||||
pub mod uvsecret {
|
||||
pub use crate::uvsecret::AddSecretMagic;
|
||||
pub use crate::uvsecret::UserDataType;
|
||||
}
|
||||
|
||||
/// Version number of the request in system-endian
|
||||
pub type RequestVersion = u32;
|
||||
/// Request magic value
|
||||
@@ -73,14 +60,11 @@ pub mod request {
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides cargo version Info about this crate.
|
||||
///
|
||||
/// Produces `pv_core-crate <version>`
|
||||
pub const fn crate_info() -> &'static str {
|
||||
concat!(env!("CARGO_PKG_NAME"), "-crate ", env!("CARGO_PKG_VERSION"))
|
||||
/// Functionalities for reading add-secret requests
|
||||
pub mod secret {
|
||||
pub use crate::uvsecret::AddSecretMagic;
|
||||
pub use crate::uvsecret::UserDataType;
|
||||
}
|
||||
|
||||
// Internal definitions/ imports
|
||||
const PAGESIZE: usize = 0x1000;
|
||||
use ::utils::assert_size;
|
||||
use ::utils::static_assert;
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2023
|
||||
|
||||
use log::{self, Level, LevelFilter, Log, Metadata, Record};
|
||||
|
||||
/// A simple Logger that prints to stderr if the verbosity level is high enough.
|
||||
/// Prints log-level for Debug+Trace
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct PvLogger;
|
||||
|
||||
fn to_level(verbosity: u8) -> LevelFilter {
|
||||
match verbosity {
|
||||
// Error and Warn on by default
|
||||
0 => LevelFilter::Warn,
|
||||
1 => LevelFilter::Info,
|
||||
2 => LevelFilter::Debug,
|
||||
_ => LevelFilter::Trace,
|
||||
}
|
||||
}
|
||||
|
||||
impl PvLogger {
|
||||
/// Set self as the logger for this application.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// An error is returned if a logger has already been set.
|
||||
pub fn start(&'static self, verbosity: u8) -> Result<(), log::SetLoggerError> {
|
||||
log::set_logger(self).map(|()| log::set_max_level(to_level(verbosity)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Log for PvLogger {
|
||||
fn enabled(&self, _metadata: &Metadata) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn log(&self, record: &Record) {
|
||||
if self.enabled(record.metadata()) {
|
||||
if record.level() > Level::Info {
|
||||
eprintln!("{}: {}", record.level(), record.args());
|
||||
} else {
|
||||
eprintln!("{}", record.args());
|
||||
}
|
||||
}
|
||||
}
|
||||
fn flush(&self) {}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2023
|
||||
// Copyright IBM Corp. 2023, 2024
|
||||
|
||||
macro_rules! path_to_str {
|
||||
($path: expr) => {
|
||||
@@ -28,14 +28,35 @@ macro_rules! bail_spec {
|
||||
}
|
||||
pub(crate) use bail_spec;
|
||||
|
||||
#[doc(hidden)]
|
||||
/// Asserts a constant expression evaluates to `true`.
|
||||
///
|
||||
/// If the expression is not evaluated to `true` the compilation will fail.
|
||||
#[macro_export]
|
||||
macro_rules! file_acc_error {
|
||||
($ty: tt, $path:expr, $src: expr) => {
|
||||
$crate::Error::FileAccess {
|
||||
ty: $crate::FileAccessErrorType::$ty,
|
||||
path: $path.to_string(),
|
||||
source: $src,
|
||||
}
|
||||
macro_rules! static_assert {
|
||||
($condition:expr) => {
|
||||
const _: () = core::assert!($condition);
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts that a type has a specific size.
|
||||
///
|
||||
/// Useful to validate structs that are passed to C code.
|
||||
/// If the size has not the expected value the compilation will fail.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// # use pv_core::assert_size;
|
||||
/// # fn main() {}
|
||||
/// #[repr(C)]
|
||||
/// struct c_struct {
|
||||
/// v: u64,
|
||||
/// }
|
||||
/// assert_size!(c_struct, 8);
|
||||
/// // assert_size!(c_struct, 7);//won't compile
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! assert_size {
|
||||
($t:ty, $sz:expr ) => {
|
||||
$crate::static_assert!(::std::mem::size_of::<$t>() == $sz);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
use std::{
|
||||
ffi::{CString, OsStr},
|
||||
os::unix::prelude::OsStrExt,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
/// Rust wrapper for `libc::mkdtemp`
|
||||
fn mkdtemp<P: AsRef<Path>>(template: P) -> Result<PathBuf, std::io::Error> {
|
||||
let template_cstr = CString::new(template.as_ref().as_os_str().as_bytes())?;
|
||||
let template_raw = template_cstr.into_raw();
|
||||
unsafe {
|
||||
// SAFETY: template_raw is a valid CString because it was generated by
|
||||
// the `CString::new`.
|
||||
let ret = libc::mkdtemp(template_raw);
|
||||
|
||||
if ret.is_null() {
|
||||
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);
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This type creates a temporary directory that is automatically removed when
|
||||
/// it goes out of scope. It utilizes the `mkdtemp` function and its semantics,
|
||||
/// with the addition of automatically including the template characters
|
||||
/// `XXXXXX`.
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub struct TemporaryDirectory {
|
||||
path: Box<Path>,
|
||||
}
|
||||
|
||||
impl TemporaryDirectory {
|
||||
/// Creates a temporary directory using `prefix` as directory prefix.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// An error is returned if the temporary directory could not be created.
|
||||
pub fn new<P: AsRef<Path>>(prefix: P) -> Result<Self, std::io::Error> {
|
||||
let mut template = prefix.as_ref().to_owned();
|
||||
let mut template_os_string = template.as_mut_os_string();
|
||||
template_os_string.push("XXXXXX");
|
||||
|
||||
let temp_dir = mkdtemp(template_os_string)?;
|
||||
Ok(Self {
|
||||
path: temp_dir.into_boxed_path(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the path of the created temporary directory.
|
||||
pub fn path(&self) -> &Path {
|
||||
self.path.as_ref()
|
||||
}
|
||||
|
||||
fn forget(mut self) {
|
||||
self.path = PathBuf::new().into_boxed_path();
|
||||
std::mem::forget(self);
|
||||
}
|
||||
|
||||
/// Removes the created temporary directory and it's contents.
|
||||
pub fn close(mut self) -> std::io::Result<()> {
|
||||
let ret = std::fs::remove_dir_all(&self.path);
|
||||
self.forget();
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Path> for TemporaryDirectory {
|
||||
fn as_ref(&self) -> &Path {
|
||||
self.path()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TemporaryDirectory {
|
||||
fn drop(&mut self) {
|
||||
let _ = std::fs::remove_dir_all(&self.path);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::{mkdtemp, TemporaryDirectory};
|
||||
|
||||
#[test]
|
||||
fn mkdtemp_test() {
|
||||
let template_inv_not_last_characters = "XXXXXXyay";
|
||||
let template_inv_too_less_x = "yayXXXXX";
|
||||
let template_inv_path_does_not_exist = "../NA-yay/XXXXXX";
|
||||
|
||||
let template = "yayXXXXXX";
|
||||
|
||||
let err = mkdtemp(template_inv_not_last_characters).expect_err("invalid template");
|
||||
let err = mkdtemp(template_inv_too_less_x).expect_err("invalid template");
|
||||
let err =
|
||||
mkdtemp(template_inv_path_does_not_exist).expect_err("path does not exist template");
|
||||
|
||||
let path = mkdtemp(template).expect("mkdtemp should work");
|
||||
assert!(path.exists());
|
||||
assert!(path.as_os_str().to_str().expect("works").starts_with("yay"));
|
||||
std::fs::remove_dir(path);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn temporary_directory_empty_name_test() {
|
||||
let temp_dir = TemporaryDirectory::new("").expect("should work");
|
||||
let path = temp_dir.path().to_owned();
|
||||
assert!(path.exists());
|
||||
|
||||
// Test that close removes the directory
|
||||
temp_dir.close();
|
||||
assert!(!path.exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn temporary_directory_drop_test() {
|
||||
let temp_dir = TemporaryDirectory::new("").expect("should work");
|
||||
let path = temp_dir.path().to_owned();
|
||||
assert!(path.exists());
|
||||
|
||||
// Test that the destructor removes the directory
|
||||
drop(temp_dir);
|
||||
assert!(!path.exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn temporary_directory_close_test() {
|
||||
let temp_dir = TemporaryDirectory::new("yay").expect("should work");
|
||||
|
||||
let path = temp_dir.path().to_owned();
|
||||
assert!(path.exists());
|
||||
assert!(path.as_os_str().to_str().expect("works").starts_with("yay"));
|
||||
|
||||
// Test that close() removes the directory
|
||||
temp_dir.close();
|
||||
assert!(!path.exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn temporary_directory_as_ref_test() {
|
||||
let temp_dir = TemporaryDirectory::new("").expect("should work");
|
||||
|
||||
assert_eq!(temp_dir.path(), temp_dir.as_ref());
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ use test::mock_libc::ioctl;
|
||||
mod ffi;
|
||||
mod info;
|
||||
mod test;
|
||||
pub use ffi::uv_ioctl;
|
||||
pub(crate) use ffi::uv_ioctl;
|
||||
pub mod secret;
|
||||
pub mod secret_list;
|
||||
|
||||
@@ -82,6 +82,11 @@ fn rc_fmt<C: UvCmd>(rc: u16, rrc: u16, cmd: &mut C) -> &'static str {
|
||||
}
|
||||
|
||||
/// Ultravisor Command.
|
||||
|
||||
///
|
||||
/// Implementers provide information on the specific Ultravisor command metadata and content.
|
||||
/// API users do not need to interact directly with any functions provided by this trait and refer
|
||||
/// to the specialized access and tweaking functionalities of the specivic command.
|
||||
pub trait UvCmd {
|
||||
/// The UV IOCTL number of the UV call
|
||||
const UV_IOCTL_NR: u8;
|
||||
|
||||
@@ -107,7 +107,7 @@ impl uvio_attest {
|
||||
}
|
||||
|
||||
/// corresponds to the UV_IOCTL macro
|
||||
pub const fn uv_ioctl(nr: u8) -> u64 {
|
||||
pub(crate) const fn uv_ioctl(nr: u8) -> u64 {
|
||||
iowr(UVIO_TYPE_UVC, nr, std::mem::size_of::<uvio_ioctl_cb>())
|
||||
}
|
||||
static_assert!(uv_ioctl(UVIO_IOCTL_ATT_NR) == 0xc0407501);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
use super::ffi::{self, uvio_uvdev_info};
|
||||
use crate::{
|
||||
misc::{Flags, Lsb0Flags64},
|
||||
uv::{uv_ioctl, UvCmd, UvDevice},
|
||||
uv::{UvCmd, UvDevice},
|
||||
Result,
|
||||
};
|
||||
use std::fmt::Display;
|
||||
@@ -22,8 +22,8 @@ use zerocopy::{AsBytes, FromZeroes};
|
||||
/// If the bit is set in both, `supp_uvio_cmds` and `supp_uv_cmds`,
|
||||
/// the uvdevice and the Ultravisor support that call.
|
||||
///
|
||||
/// Note that bit 0 ([`UvDevice::INFO_NR`]) is always zero for `supp_uv_cmds`
|
||||
/// as there is no corresponding UV-call.
|
||||
/// Note that bit 0 is always zero for `supp_uv_cmds`
|
||||
/// as there is no corresponding Info UV-call.
|
||||
///
|
||||
#[derive(Debug)]
|
||||
pub struct UvDeviceInfo {
|
||||
@@ -38,7 +38,7 @@ impl UvDeviceInfo {
|
||||
///
|
||||
/// This function will return an error if the ioctl fails and the error code is not
|
||||
/// [`libc::ENOTTY`].
|
||||
/// `ENOTTY` is most likely because the uvdevice does not support the info IOCTL.
|
||||
/// `ENOTTY` is most likely because older uvdevices does not support the info IOCTL.
|
||||
/// In that case one can safely assume that the device only supports the Attestation IOCTL.
|
||||
/// Therefore this is what this function returns IOCTL support for Attestation and _Data not
|
||||
/// available_ for the UV Attestation facility.
|
||||
|
||||
@@ -6,8 +6,9 @@ use super::ffi;
|
||||
use crate::{
|
||||
assert_size,
|
||||
misc::to_u16,
|
||||
request::{uvsecret::AddSecretMagic, MagicValue},
|
||||
uv::{uv_ioctl, UvCmd, UvDevice},
|
||||
request::MagicValue,
|
||||
uv::{UvCmd, UvDevice},
|
||||
uvsecret::AddSecretMagic,
|
||||
Error, Result, PAGESIZE,
|
||||
};
|
||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
//
|
||||
// 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 serde::{Deserialize, Serialize, Serializer};
|
||||
use std::{
|
||||
@@ -10,11 +12,8 @@ use std::{
|
||||
slice::Iter,
|
||||
vec::IntoIter,
|
||||
};
|
||||
use utils::assert_size;
|
||||
use zerocopy::{AsBytes, FromBytes, FromZeroes, U16, U32};
|
||||
|
||||
use crate::{misc::to_u16, uv::ListCmd, uvdevice::UvCmd, Error, Result};
|
||||
|
||||
/// The 32 byte long ID of an UV secret
|
||||
///
|
||||
/// (de)serializes itself in/from a hex-string
|
||||
@@ -94,7 +93,7 @@ impl SecretEntry {
|
||||
|
||||
/// Create a new entry for a [`SecretList`].
|
||||
///
|
||||
/// The content of this entry will very liekly not represent the status of the guest in the
|
||||
/// 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.
|
||||
pub fn new(index: u16, stype: ListableSecretType, id: SecretId, secret_len: u32) -> Self {
|
||||
Self {
|
||||
@@ -180,7 +179,7 @@ impl FromIterator<SecretEntry> for SecretList {
|
||||
impl SecretList {
|
||||
/// Creates a new SecretList.
|
||||
///
|
||||
/// The content of this list will very liekly not represent the status of the guest in the
|
||||
/// 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 {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
//
|
||||
// Copyright IBM Corp. 2023
|
||||
|
||||
use crate::{assert_size, static_assert};
|
||||
use crate::{
|
||||
misc::to_u16,
|
||||
request::{MagicValue, RequestMagic},
|
||||
@@ -14,14 +15,13 @@ use std::{
|
||||
io::{Cursor, Read, Seek, Write},
|
||||
mem::size_of,
|
||||
};
|
||||
use utils::{assert_size, static_assert};
|
||||
use zerocopy::{AsBytes, FromBytes, U16, U32};
|
||||
|
||||
/// The magic value used to identify an add-secret request`]
|
||||
///
|
||||
/// The magic value is ASCII:
|
||||
/// ```rust
|
||||
/// # use pv_core::request::uvsecret::AddSecretMagic;
|
||||
/// # use pv_core::secret::AddSecretMagic;
|
||||
/// # use pv_core::request::MagicValue;
|
||||
/// # fn main() {
|
||||
/// # let magic =
|
||||
@@ -56,7 +56,7 @@ impl AddSecretMagic {
|
||||
|
||||
/// Try to convert from a byte slice.
|
||||
///
|
||||
/// Retuns [`None`] if the byte slice does not contain a valid magic value variant.
|
||||
/// Returns [`None`] if the byte slice does not contain a valid magic value variant.
|
||||
pub fn try_from_bytes(bytes: &[u8]) -> Result<Self> {
|
||||
if !Self::starts_with_magic(bytes) || bytes.len() < size_of::<AddSecretMagic>() {
|
||||
return Err(Error::NoAsrcb);
|
||||
@@ -70,8 +70,8 @@ impl AddSecretMagic {
|
||||
|
||||
/// Returns the [`UserDataType`] of this [`AddSecretMagic`].
|
||||
pub fn kind(&self) -> UserDataType {
|
||||
// Panic: Will never panic. The value is cheched during construcion of the object for
|
||||
// beeing one of the enum values.
|
||||
// Panic: Will never panic. The value is checked during construction of
|
||||
// the object for being one of the enum values.
|
||||
self.kind.get().try_into().unwrap()
|
||||
}
|
||||
}
|
||||
@@ -153,10 +153,8 @@ impl From<UserDataType> for AddSecretMagic {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{
|
||||
request::{
|
||||
uvsecret::{AddSecretMagic, UserDataType},
|
||||
MagicValue,
|
||||
},
|
||||
request::MagicValue,
|
||||
secret::{AddSecretMagic, UserDataType},
|
||||
Error,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user