From 381fecfc4461ea6f3aa54a09a9fb8962550fa27e Mon Sep 17 00:00:00 2001 From: Steffen Eiden Date: Tue, 21 May 2024 16:26:51 +0200 Subject: [PATCH] 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 Signed-off-by: Steffen Eiden --- rust/Cargo.lock | 10 ++- rust/pv/Cargo.toml | 8 +- rust/pv/openssl_extensions/Cargo.toml | 4 +- rust/pv/src/brcb.rs | 37 ++++----- rust/pv/src/{secret.rs => confidential.rs} | 45 +++++----- rust/pv/src/crypto.rs | 64 +++++++------- rust/pv/src/error.rs | 17 +++- rust/pv/src/lib.rs | 63 +++++--------- rust/pv/src/req.rs | 29 +++---- rust/pv/src/test_utils.rs | 6 -- rust/pv/src/utils.rs | 34 +------- rust/pv/src/uvsecret/asrcb.rs | 36 ++++---- rust/pv/src/uvsecret/ext_secret.rs | 8 +- rust/pv/src/uvsecret/guest_secret.rs | 9 +- rust/pv/src/uvsecret/user_data.rs | 17 ++-- rust/pv/src/verify.rs | 11 +-- rust/pv/src/verify/helper.rs | 21 ++--- rust/pv/src/verify/test.rs | 40 ++++----- rust/pv/tests/add_secret_request.rs | 8 +- rust/pv/tests/cert_verifier.rs | 74 +++++++---------- rust/pv_core/Cargo.toml | 5 +- rust/pv_core/src/lib.rs | 28 ++----- rust/pv_core/src/macros.rs | 39 +++++++-- rust/pv_core/src/uvdevice.rs | 7 +- rust/pv_core/src/uvdevice/ffi.rs | 2 +- rust/pv_core/src/uvdevice/info.rs | 8 +- rust/pv_core/src/uvdevice/secret.rs | 5 +- rust/pv_core/src/uvdevice/secret_list.rs | 9 +- rust/pv_core/src/uvsecret.rs | 16 ++-- rust/pvapconfig/Cargo.toml | 5 +- rust/pvapconfig/src/helper.rs | 2 +- rust/pvapconfig/src/main.rs | 8 +- rust/pvsecret/Cargo.toml | 2 +- rust/pvsecret/src/cli.rs | 3 +- rust/pvsecret/src/cmd/add.rs | 2 +- rust/pvsecret/src/cmd/create.rs | 76 ++++++++--------- rust/pvsecret/src/cmd/list.rs | 6 +- rust/pvsecret/src/cmd/verify.rs | 12 ++- rust/pvsecret/src/main.rs | 52 ++---------- rust/utils/Cargo.toml | 6 ++ rust/{pv => utils}/src/cli.rs | 97 +++++++++++++++++++--- rust/utils/src/lib.rs | 39 ++++++++- rust/{pv_core => utils}/src/log.rs | 1 + rust/{pv_core => utils}/src/tmpfile.rs | 18 ++-- 44 files changed, 499 insertions(+), 490 deletions(-) rename rust/pv/src/{secret.rs => confidential.rs} (72%) rename rust/{pv => utils}/src/cli.rs (61%) rename rust/{pv_core => utils}/src/log.rs (99%) rename rust/{pv_core => utils}/src/tmpfile.rs (90%) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index f263c5f1..ef56d7da 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -414,7 +414,6 @@ name = "pv" version = "1.0.0" dependencies = [ "byteorder", - "clap", "curl", "log", "openssl", @@ -423,7 +422,6 @@ dependencies = [ "serde", "serde_test", "thiserror", - "utils", "zerocopy", ] @@ -438,7 +436,6 @@ dependencies = [ "serde", "serde_test", "thiserror", - "utils", "zerocopy", ] @@ -449,7 +446,6 @@ dependencies = [ "clap", "lazy_static", "openssl", - "openssl-sys", "pv_core", "rand", "regex", @@ -687,6 +683,12 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "utils" version = "0.1.0" +dependencies = [ + "clap", + "libc", + "log", + "pv", +] [[package]] name = "vcpkg" diff --git a/rust/pv/Cargo.toml b/rust/pv/Cargo.toml index e202121e..cc212875 100644 --- a/rust/pv/Cargo.toml +++ b/rust/pv/Cargo.toml @@ -6,17 +6,15 @@ license.workspace = true [dependencies] byteorder = "1.3" -clap = { version ="4", features = ["derive", "wrap_help"] } -curl = "0.4.7" +curl = "0.4.44" log = { version = "0.4.6", features = ["std", "release_max_level_debug"] } -openssl = "0.10.49" +openssl = "0.10.57" serde = { version = "1.0.139", features = ["derive"] } thiserror = "1.0.33" -utils = {path = "../utils"} zerocopy = { version="0.7", features = ["derive"] } openssl_extensions = { path = "openssl_extensions" } pv_core = { path = "../pv_core" } [dev-dependencies] -serde_test = "1" +serde_test = "1.0.139" diff --git a/rust/pv/openssl_extensions/Cargo.toml b/rust/pv/openssl_extensions/Cargo.toml index 7bdeece6..0ed2dd69 100644 --- a/rust/pv/openssl_extensions/Cargo.toml +++ b/rust/pv/openssl_extensions/Cargo.toml @@ -8,5 +8,5 @@ license.workspace = true foreign-types = "0.3.1" libc = {version = "0.2.49", features = [ "extra_traits"] } log = { version = "0.4.6", features = ["std", "release_max_level_debug"] } -openssl = "0.10.49" -openssl-sys = "0.9.85" +openssl = "0.10.57" +openssl-sys = "0.9.92" diff --git a/rust/pv/src/brcb.rs b/rust/pv/src/brcb.rs index 8886238e..3d3a398f 100644 --- a/rust/pv/src/brcb.rs +++ b/rust/pv/src/brcb.rs @@ -8,9 +8,8 @@ use std::{ }; // (SE) boot request control block aka SE header -use crate::{assert_size, static_assert, Error, Result, PAGESIZE}; +use crate::{assert_size, request::MagicValue, static_assert, Error, Result, PAGESIZE}; use log::debug; -use pv_core::request::MagicValue; use zerocopy::{AsBytes, BigEndian, FromBytes, FromZeroes, U32, U64}; /// Struct containing all SE-header tags. @@ -19,15 +18,14 @@ use zerocopy::{AsBytes, BigEndian, FromBytes, FromZeroes, U32, U64}; /// Page List Digest (pld) /// Address List Digest (ald) /// Tweak List Digest (tld) -/// SE Header Tag (seht) -/// +/// SE Header Tag (tag) #[repr(C)] #[derive(Debug, Clone, Copy, AsBytes, PartialEq, Eq)] pub struct BootHdrTags { pld: [u8; BootHdrHead::DIGEST_SIZE], ald: [u8; BootHdrHead::DIGEST_SIZE], tld: [u8; BootHdrHead::DIGEST_SIZE], - seht: [u8; BootHdrHead::SEHT_SIZE], + tag: [u8; BootHdrHead::TAG_SIZE], } /// Magiv value for a SE-(boot)header @@ -38,19 +36,14 @@ impl MagicValue<8> for BootHdrMagic { impl BootHdrTags { /// Returns a reference to the SE-hdr tag of this [`BootHdrTags`]. - pub fn seht(&self) -> &[u8; 16] { - &self.seht + pub fn tag(&self) -> &[u8; 16] { + &self.tag } /// Creates a new [`BootHdrTags`]. Useful for writing tests. #[doc(hidden)] - pub const fn new(pld: [u8; 64], ald: [u8; 64], tld: [u8; 64], seht: [u8; 16]) -> Self { - Self { - ald, - tld, - pld, - seht, - } + pub const fn new(pld: [u8; 64], ald: [u8; 64], tld: [u8; 64], tag: [u8; 16]) -> Self { + Self { ald, tld, pld, tag } } /// returns false if no hdr found, true otherwise @@ -93,8 +86,8 @@ impl BootHdrTags { /// /// # Errors /// - /// This function will return an error if `hdr` is not at least as long as the header specifies - /// in bytes 12-15 or the first 8 bytes do not contain the magic value. + /// This function will return an error if the header could not be found in + /// `img` or is invalid. pub fn from_se_image(img: &mut R) -> Result where R: Read + Seek, @@ -125,18 +118,18 @@ impl BootHdrTags { img.seek(Current( hdr_head.size.get() as i64 - size_of::() as i64 - - BootHdrHead::SEHT_SIZE as i64, + - BootHdrHead::TAG_SIZE as i64, ))?; // read in the tag - let mut seht = [0u8; BootHdrHead::SEHT_SIZE]; - img.read_exact(seht.as_mut_slice())?; + let mut tag = [0u8; BootHdrHead::TAG_SIZE]; + img.read_exact(tag.as_mut_slice())?; Ok(BootHdrTags { pld: hdr_head.pld, ald: hdr_head.ald, tld: hdr_head.tld, - seht, + tag, }) } } @@ -161,7 +154,7 @@ struct BootHdrHead { assert_size!(BootHdrHead, 0x1A0); impl BootHdrHead { const DIGEST_SIZE: usize = 0x40; - const SEHT_SIZE: usize = 0x10; + const TAG_SIZE: usize = 0x10; } #[cfg(test)] @@ -194,7 +187,7 @@ mod tests { 0x8f, 0x9b, 0xe0, 0xa5, 0x49, 0xd8, 0xd7, 0xa9, 0x4a, 0xe7, 0x20, 0xe5, 0xc0, 0x76, 0x0a, 0x82, 0x5d, 0x47, 0x9f, 0xe6, 0x7a, 0xf5, ], - seht: [ + tag: [ 0x92, 0x30, 0x9d, 0x45, 0x89, 0xb9, 0xa8, 0x5b, 0x42, 0x7f, 0x87, 0x53, 0x17, 0x1d, 0x15, 0x20, ], diff --git a/rust/pv/src/secret.rs b/rust/pv/src/confidential.rs similarity index 72% rename from rust/pv/src/secret.rs rename to rust/pv/src/confidential.rs index 88287c8e..31993d60 100644 --- a/rust/pv/src/secret.rs +++ b/rust/pv/src/confidential.rs @@ -1,18 +1,18 @@ // SPDX-License-Identifier: MIT // -// Copyright IBM Corp. 2023 +// Copyright IBM Corp. 2023, 2024 use std::fmt::Debug; /// Trait for securely zeroizing memory. /// -/// To be used with [`Secret`] +/// To be used with [`Confidential`] pub trait Zeroize { /// Reliably overwrites the given buffer with zeros, fn zeroize(&mut self); } -/* Automatically impl Zeroize for u8 arrays */ +// Automatically impl Zeroize for u8 arrays impl Zeroize for [u8; COUNT] { /// Reliably overwrites the given buffer with zeros, /// by performing a volatile write followed by a memory barrier @@ -27,7 +27,7 @@ impl Zeroize for Vec { /// Reliably overwrites the given buffer with zeros, /// by overwriting the whole vector's capacity with zeros. fn zeroize(&mut self) { - //TODO use `volatile_set_memory` when stabilized + // TODO use `volatile_set_memory` when stabilized let mut dst = self.as_mut_ptr(); for _ in 0..self.capacity() { // SAFETY: @@ -44,38 +44,39 @@ impl Zeroize for Vec { /// Thin wrapper around an type implementing Zeroize. /// -/// A `Secret` represents a confidential value that must be securely overwritten during drop. +/// A `Confidential` represents a confidential value that must be securely overwritten during drop. /// Will never leak its wrapped value during [`Debug`] /// /// ```rust -/// use pv::request::Secret; -/// fn foo(value: Secret<[u8; 2]>) { +/// use pv::request::Confidential; +/// fn foo(value: Confidential<[u8; 2]>) { /// println!("value: {value:?}"); /// } /// # fn main() { -/// foo([1,2].into()); +/// foo([1, 2].into()); /// // prints: /// // in debug builds: -/// // value: Secret([1, 2]) +/// // value: Confidential([1, 2]) /// // in release builds: -/// // value: Secret(***) +/// // value: Confidential(***) /// # } /// ``` #[derive(Clone, PartialEq, Eq, Default)] -pub struct Secret(C); -impl Secret { +pub struct Confidential(C); +impl Confidential { /// Convert a type into a self overwriting one. /// /// Prefer using [`Into`] pub fn new(v: C) -> Self { - Secret(v) + Confidential(v) } /// Get a reference to the contained value pub fn value(&self) -> &C { &self.0 } - /// Get a imutable reference to the contained value + + /// Get an immutable reference to the contained value /// /// NOTE that modifications to a mutable reference can trigger reallocation. /// e.g. a [`Vec`] might expand if more space needed. -> preallocate enough space @@ -85,32 +86,32 @@ impl Secret { } } -impl Debug for Secret { +impl Debug for Confidential { #[allow(unreachable_code)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // do NOT leak secrets in production builds #[cfg(not(debug_assertions))] - return write!(f, "Secret(***)"); + return write!(f, "Confidential(***)"); - let mut b = f.debug_tuple("Secret"); + let mut b = f.debug_tuple("Confidential"); b.field(&self.0); b.finish() } } -impl From for Secret { - fn from(v: C) -> Secret { - Secret(v) +impl From for Confidential { + fn from(v: C) -> Confidential { + Confidential(v) } } -impl Zeroize for Secret { +impl Zeroize for Confidential { fn zeroize(&mut self) { self.0.zeroize(); } } -impl Drop for Secret { +impl Drop for Confidential { fn drop(&mut self) { self.0.zeroize(); } diff --git a/rust/pv/src/crypto.rs b/rust/pv/src/crypto.rs index 680d011f..8d5eb8bb 100644 --- a/rust/pv/src/crypto.rs +++ b/rust/pv/src/crypto.rs @@ -2,7 +2,7 @@ // // Copyright IBM Corp. 2023 -use crate::{error::Result, secret::Secret, Error}; +use crate::{confidential::Confidential, error::Result, Error}; use openssl::{ derive::Deriver, ec::{EcGroup, EcKey}, @@ -20,7 +20,7 @@ use std::{convert::TryInto, ops::Range}; /// An AES256-key that will purge itself out of the memory when going out of scope /// -pub type Aes256Key = Secret<[u8; 32]>; +pub type Aes256Key = Confidential<[u8; 32]>; pub(crate) const AES_256_GCM_TAG_SIZE: usize = 16; /// Types of symmetric keys, to specify during construction. @@ -63,21 +63,6 @@ impl SymKey { } } -impl Aes256Key { - /// Generates an AES256 key from an digest (hash). - /// - /// # Panics - /// - /// Panics if `digset` is not 32 bytes long. - fn from_digest(digest: DigestBytes) -> Self { - let key: [u8; 32] = digest - .as_ref() - .try_into() - .expect("Unexpected OpenSSl Error. Sha256 hash not 32 bytes long"); - key.into() - } -} - impl From for SymKey { fn from(value: Aes256Key) -> Self { Self::Aes256(value) @@ -90,7 +75,7 @@ impl From for SymKey { /// # Errors /// /// This function will return an OpenSSL error if the key could not be generated. -pub fn hkdf_rfc_5869( +pub(crate) fn hkdf_rfc_5869( md: &MdRef, ikm: &[u8], salt: &[u8], @@ -114,17 +99,20 @@ pub fn hkdf_rfc_5869( /// # Errors /// /// This function will return an error if something went bad in OpenSSL. -pub fn derive_key(k1: &PKey, k2: &PKey) -> Result { +pub(crate) fn derive_key(k1: &PKeyRef, k2: &PKeyRef) -> Result { let mut der = Deriver::new(k1)?; der.set_peer(k2)?; let mut key = der.derive_to_vec()?; key.extend([0, 0, 0, 1]); - let secr = Secret::new(key); + let secr = Confidential::new(key); - Ok(Aes256Key::from_digest(hash( - MessageDigest::sha256(), - secr.value(), - )?)) + // Panic: does not panic as SHA256 digest is 32 bytes long + Ok(Aes256Key::new( + hash(MessageDigest::sha256(), secr.value())? + .as_ref() + .try_into() + .unwrap(), + )) } /// Generate a random array. @@ -132,7 +120,7 @@ pub fn derive_key(k1: &PKey, k2: &PKey) -> Result { /// # Errors /// /// This function will return an error if the entropy source fails or is not available. -pub fn random_array() -> Result<[u8; COUNT]> { +pub(crate) fn random_array() -> Result<[u8; COUNT]> { let mut rand = [0; COUNT]; rand_bytes(&mut rand)?; Ok(rand) @@ -143,14 +131,15 @@ pub fn random_array() -> Result<[u8; COUNT]> { /// # Errors /// /// This function will return an error if the key could not be generated by OpenSSL. -pub fn gen_ec_key() -> Result> { +pub(crate) fn gen_ec_key() -> Result> { let group = EcGroup::from_curve_name(Nid::SECP521R1)?; let key: EcKey = EcKey::generate(&group)?; PKey::from_ec_key(key).map_err(Error::Crypto) } -/// Result type for [`encrypt_aes_gcm`]. -pub struct AesGcmResult { +/// Result type for an AES encryption in GCM mode.. +#[derive(Debug)] +pub(crate) struct AesGcmResult { /// The result. /// /// [`Vec`] with the following content: @@ -163,12 +152,14 @@ pub struct AesGcmResult { /// The position of the encrypted data in [`Self::buf`] pub encr_range: Range, /// The position of the tag in [`Self::buf`] + #[allow(unused)] + // here for completeness pub tag_range: Range, } impl AesGcmResult { /// Deconstruct the result to just the resulting data w/o ranges. - pub fn data(self) -> Vec { + pub(crate) fn data(self) -> Vec { let Self { buf, .. } = self; buf } @@ -184,7 +175,12 @@ impl AesGcmResult { /// # Errors /// /// This function will return an error if the data could not be encrypted by OpenSSL. -pub fn encrypt_aes_gcm(key: &SymKey, iv: &[u8], aad: &[u8], conf: &[u8]) -> Result { +pub(crate) fn encrypt_aes_gcm( + key: &SymKey, + iv: &[u8], + aad: &[u8], + conf: &[u8], +) -> Result { let mut tag = vec![0xff; AES_256_GCM_TAG_SIZE]; let encr = match key { SymKey::Aes256(key) => encrypt_aead( @@ -239,7 +235,11 @@ pub fn hash(t: MessageDigest, data: &[u8]) -> Result { /// # Errors /// /// This function will return an error if OpenSSL could not compute the signature. -pub fn sign_msg(skey: &PKeyRef, dgst: MessageDigest, msg: &[u8]) -> Result> { +pub(crate) fn sign_msg( + skey: &PKeyRef, + dgst: MessageDigest, + msg: &[u8], +) -> Result> { match skey.id() { Id::EC => { let mut sgn = Signer::new(dgst, skey)?; @@ -265,7 +265,7 @@ pub fn sign_msg(skey: &PKeyRef, dgst: MessageDigest, msg: &[u8]) -> Res /// # Errors /// /// This function will return an error if OpenSSL could not compute the signature. -pub fn verify_signature( +pub(crate) fn verify_signature( skey: &PKeyRef, dgst: MessageDigest, msg: &[u8], diff --git a/rust/pv/src/error.rs b/rust/pv/src/error.rs index 359c6bdd..1e7c735f 100644 --- a/rust/pv/src/error.rs +++ b/rust/pv/src/error.rs @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MIT // -// Copyright IBM Corp. 2023 +// Copyright IBM Corp. 2023, 2024 + +use crate::secret::UserDataType; /// Result type for this crate pub type Result = std::result::Result; @@ -68,6 +70,15 @@ pub enum Error { )] AsrcbUserDataSgnFail, + #[error("The provided Host Key Document in '{hkd}' is not in PEM or DER format")] + HkdNotPemOrDer { + hkd: String, + source: openssl::error::ErrorStack, + }, + + #[error("The provided host key document in {0} contains no certificate!")] + NoHkdInFile(String), + // errors from other crates #[error(transparent)] PvCore(#[from] pv_core::Error), @@ -97,7 +108,7 @@ pub enum HkdVerifyErrorType { #[error("No valid CRL found")] NoCrl, #[error("Host-key document is revoked.")] - HdkRevoked, + HkdRevoked, #[error("Not enough bits of security. ({0}, {1} expected)")] SecurityBits(u32, u32), #[error("Authority Key Id mismatch")] @@ -126,5 +137,3 @@ macro_rules! bail_hkd_verify { }; } pub(crate) use bail_hkd_verify; - -use crate::request::uvsecret::UserDataType; diff --git a/rust/pv/src/lib.rs b/rust/pv/src/lib.rs index bd839620..4ad408e7 100644 --- a/rust/pv/src/lib.rs +++ b/rust/pv/src/lib.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT // -// Copyright IBM Corp. 2023 +// Copyright IBM Corp. 2023, 2024 #![deny(missing_docs)] //! pv - library for pv-tools @@ -8,28 +8,27 @@ //! This library is intened to be used by tools and libraries that //! are used for creating and managing IBM Secure Execution guests. //! `pv` provides abstraction layers for encryption, secure memory management, -//! logging, and accessing the uvdevice. +//! and accessing the uvdevice. //! //! If you do not need any OpenSSL features use `pv_core`. //! This crate reexports all symbols from `pv_core` mod brcb; -mod cli; +mod confidential; mod crypto; mod error; mod req; -mod secret; mod utils; mod uvsecret; mod verify; /// utility functions for writing TESTS!!! -//hide any test helpers on docs! +// hide any test helpers on docs! #[doc(hidden)] #[allow(dead_code)] pub mod test_utils; -pub use ::utils::assert_size; -pub use ::utils::static_assert; +pub use pv_core::assert_size; +pub use pv_core::static_assert; const PAGESIZE: usize = 0x1000; @@ -40,13 +39,8 @@ pub mod uv { /// Miscellaneous functions and definitions pub mod misc { - pub use crate::cli::{ - get_reader_from_cli_file_arg, get_writer_from_cli_file_arg, CertificateOptions, STDIN, - STDOUT, - }; - pub use crate::utils::{read_certs, read_crls, read_private_key}; + pub use crate::utils::read_certs; pub use pv_core::misc::*; - pub use pv_core::PvLogger; } pub use crate::error::HkdVerifyErrorType; @@ -54,42 +48,29 @@ pub use error::{Error, Result}; /// Functionalities to build UV requests pub mod request { - pub use crate::brcb::{BootHdrMagic, BootHdrTags}; - pub use crate::crypto::derive_key; - pub use crate::crypto::random_array; - pub use crate::crypto::{encrypt_aes_gcm, gen_ec_key, AesGcmResult}; - pub use crate::crypto::{hash, hkdf_rfc_5869}; - pub use crate::crypto::{sign_msg, verify_signature}; - pub use crate::crypto::{Aes256Key, SymKey, SymKeyType}; - pub use crate::req::{Aad, BinReqValues, Encrypt, Keyslot, ReqEncrCtx, Request}; - pub use crate::secret::{Secret, Zeroize}; + pub use crate::brcb::BootHdrTags; + pub use crate::confidential::{Confidential, Zeroize}; + pub use crate::crypto::{SymKey, SymKeyType}; + pub use crate::req::{Keyslot, ReqEncrCtx, Request}; pub use crate::verify::{CertVerifier, HkdVerifier, NoVerifyHkd}; /// Reexports some useful OpenSSL symbols pub mod openssl { pub use openssl::error::ErrorStack; - pub use openssl::hash::MessageDigest; - pub use openssl::md::Md; pub use openssl::pkey; + pub use openssl::x509; } - /// Functionalities for creating add-secret requests - pub mod uvsecret { - pub use crate::uvsecret::{ - asrcb::{AddSecretFlags, AddSecretRequest, AddSecretVersion}, - ext_secret::ExtSecret, - guest_secret::GuestSecret, - user_data::verify_asrcb_and_get_user_data, - }; - pub use pv_core::request::uvsecret::AddSecretMagic; - pub use pv_core::request::uvsecret::UserDataType; - } - pub use pv_core::request::RequestMagic; + pub use pv_core::request::*; } -/// Provides cargo version Info about this crate. -/// -/// Produces `pv-crate ` -pub const fn crate_info() -> &'static str { - concat!(env!("CARGO_PKG_NAME"), "-crate ", env!("CARGO_PKG_VERSION")) +/// Functionalities for creating add-secret requests +pub mod secret { + pub use crate::uvsecret::{ + asrcb::{AddSecretFlags, AddSecretRequest, AddSecretVersion}, + ext_secret::ExtSecret, + guest_secret::GuestSecret, + user_data::verify_asrcb_and_get_user_data, + }; + pub use pv_core::secret::*; } diff --git a/rust/pv/src/req.rs b/rust/pv/src/req.rs index dd7d4dd4..59b2765a 100644 --- a/rust/pv/src/req.rs +++ b/rust/pv/src/req.rs @@ -2,9 +2,12 @@ // // Copyright IBM Corp. 2023 -use crate::crypto::{AesGcmResult, AES_256_GCM_TAG_SIZE}; +use crate::assert_size; +use crate::crypto::{ + derive_key, encrypt_aes_gcm, gen_ec_key, random_array, AesGcmResult, SymKey, SymKeyType, + AES_256_GCM_TAG_SIZE, +}; use crate::misc::to_u32; -use crate::request::{derive_key, encrypt_aes_gcm, gen_ec_key, random_array, SymKey, SymKeyType}; use crate::{Error, Result}; use openssl::bn::{BigNum, BigNumContext}; use openssl::ec::{EcGroupRef, EcPointRef}; @@ -14,7 +17,6 @@ use openssl::pkey::{PKey, PKeyRef, Private, Public}; use pv_core::request::{RequestMagic, RequestVersion}; use std::convert::TryInto; use std::mem::size_of; -use utils::assert_size; use zerocopy::{AsBytes, BigEndian, FromBytes, FromZeroes, U32}; /// Encrypt a _secret_ using self and a given private key. @@ -27,7 +29,7 @@ pub trait Encrypt { /// # Errors /// /// This function will return an error if OpenSSL could not encrypt the secret. - fn encrypt(&self, secret: &[u8], priv_key: &PKey) -> Result> { + fn encrypt(&self, secret: &[u8], priv_key: &PKeyRef) -> Result> { let mut res = Vec::with_capacity(80); self.encrypt_to(secret, priv_key, &mut res)?; Ok(res) @@ -42,7 +44,12 @@ pub trait Encrypt { /// # Errors /// /// This function will return an error if OpenSSL could not encrypt the secret. - fn encrypt_to(&self, secret: &[u8], priv_key: &PKey, to: &mut Vec) -> Result<()>; + fn encrypt_to( + &self, + secret: &[u8], + priv_key: &PKeyRef, + to: &mut Vec, + ) -> Result<()>; } /// Types of Authenticated Data @@ -91,7 +98,7 @@ impl Encrypt for Keyslot { fn encrypt_to( &self, prot_key: &[u8], - priv_key: &PKey, + priv_key: &PKeyRef, to: &mut Vec, ) -> Result<()> { let derived_key = derive_key(priv_key, &self.0)?; @@ -106,7 +113,7 @@ impl Encrypt for Keyslot { } } -/// Context used to mange the encryption of requests. +/// Context used to manage the encryption of requests. /// Intended to be used by [`Request`] implementations #[derive(Debug)] pub struct ReqEncrCtx { @@ -155,12 +162,6 @@ impl ReqEncrCtx { } } - ///Panics if data does not fit into bin_aad+offs - // #[track_caller] - // pub fn copy_to_bin_aad(_bin_aad: &mut [u8], _aad_offs: usize, _data: &[u8]) { - // todo!(); - // } - /// Build the authenticated data for a request. /// # Returns /// ```none @@ -252,7 +253,7 @@ impl ReqEncrCtx { /// # Errors /// /// This function will return an error if the data could not be encrypted by OpenSSL. - pub fn encrypt_aead(&self, aad: &[u8], conf: &[u8]) -> Result { + pub(crate) fn encrypt_aead(&self, aad: &[u8], conf: &[u8]) -> Result { encrypt_aes_gcm(&self.prot_key, &self.iv, aad, conf) } } diff --git a/rust/pv/src/test_utils.rs b/rust/pv/src/test_utils.rs index 3859950c..2db26659 100644 --- a/rust/pv/src/test_utils.rs +++ b/rust/pv/src/test_utils.rs @@ -39,12 +39,6 @@ pub fn get_cert_asset_path>(path: P) -> PathBuf { p } -pub fn get_cert_asset_path_string(path: &'static str) -> String { - get_cert_asset_path(path) - .into_os_string() - .into_string() - .unwrap() -} /// TEST ONLY! Load an cert /// /// panic on errors diff --git a/rust/pv/src/utils.rs b/rust/pv/src/utils.rs index 1a2ab9e4..1b479c08 100644 --- a/rust/pv/src/utils.rs +++ b/rust/pv/src/utils.rs @@ -3,7 +3,7 @@ // Copyright IBM Corp. 2023 use crate::{Error, Result}; use openssl::{ - pkey::{PKey, Private}, + error::ErrorStack, x509::{X509Crl, X509}, }; @@ -26,29 +26,15 @@ pub fn read_crls(buf: &[u8]) -> Result> { /// # Errors /// /// This function will return an error if the underlying OpenSSL implementation cannot parse `buf` - -pub fn read_certs(buf: &[u8]) -> Result> { +pub fn read_certs(buf: &[u8]) -> Result, ErrorStack> { X509::from_der(buf) .map(|crt| vec![crt]) .or_else(|_| X509::stack_from_pem(buf)) - .map_err(Error::Crypto) -} - -/// Read+parse the first key from the buffer. -/// -/// # Errors -/// -/// This function will return an error if the underlying OpenSSL implementation cannot parse `buf` -/// as `DER` or `PEM`. -pub fn read_private_key(buf: &[u8]) -> Result> { - PKey::private_key_from_der(buf) - .or_else(|_| PKey::private_key_from_pem(buf)) - .map_err(Error::Crypto) } #[cfg(test)] mod tests { - use crate::{get_test_asset, test_utils::*}; + use crate::test_utils::*; #[test] fn read_crls() { @@ -69,18 +55,4 @@ mod tests { assert_eq!(super::read_certs(&crt_der).unwrap().len(), 1); assert_eq!(super::read_certs(&fail).unwrap().len(), 0); } - - #[test] - fn read_private_key() { - let key = get_test_asset!("keys/rsa3072key.pem"); - let key = super::read_private_key(key).unwrap(); - assert_eq!(key.rsa().unwrap().size(), 384); - } - - #[test] - fn read_private_key_fail() { - let key = get_test_asset!("exp/secure_guest.hdr"); - let key = super::read_private_key(key); - assert!(key.is_err()); - } } diff --git a/rust/pv/src/uvsecret/asrcb.rs b/rust/pv/src/uvsecret/asrcb.rs index d6ff38bb..1df2fabc 100644 --- a/rust/pv/src/uvsecret/asrcb.rs +++ b/rust/pv/src/uvsecret/asrcb.rs @@ -5,20 +5,18 @@ use super::user_data::UserData; use crate::{ assert_size, - crypto::AesGcmResult, + crypto::{hkdf_rfc_5869, AesGcmResult}, misc::Flags, - request::{ - hkdf_rfc_5869, - openssl::{ - pkey::{PKey, Private, Public}, - Md, - }, - uvsecret::{ExtSecret, GuestSecret}, - Aad, BootHdrTags, Keyslot, ReqEncrCtx, Request, Secret, - }, + req::{Aad, Keyslot, ReqEncrCtx}, + request::{BootHdrTags, Confidential, Request}, + secret::{ExtSecret, GuestSecret}, uv::{ConfigUid, UvFlags}, Result, }; +use openssl::{ + md::Md, + pkey::{PKey, Private, Public}, +}; use pv_core::request::RequestVersion; use zerocopy::AsBytes; @@ -47,11 +45,11 @@ impl ReqAuthData { #[derive(Debug)] struct ReqConfData { secret: GuestSecret, - extension_secret: Secret<[u8; 32]>, + extension_secret: Confidential<[u8; 32]>, } impl ReqConfData { - fn to_bytes(&self) -> Secret> { + fn to_bytes(&self) -> Confidential> { let secret = self.secret.confidential(); let mut v = vec![0; secret.len() + 32]; @@ -110,7 +108,7 @@ impl From for RequestVersion { /// Add-secret request Control Block /// -/// An ASRCB wraps a secret to transport it securely to the Ultravisor. +/// An ASRCB wraps a secret to securely transport it to the Ultravisor. /// /// Layout: ///```none @@ -159,7 +157,7 @@ impl AddSecretRequest { ) -> Self { AddSecretRequest { conf: ReqConfData { - extension_secret: Secret::new([0; 32]), + extension_secret: Confidential::new([0; 32]), secret, }, aad: ReqAuthData::new(boot_tags, flags), @@ -186,7 +184,7 @@ impl AddSecretRequest { ExtSecret::Derived(cck) => hkdf_rfc_5869( Md::sha512(), cck.value(), - self.aad.boot_tags.seht(), + self.aad.boot_tags.tag(), DER_EXT_SECRET_INFO, )? .into(), @@ -210,8 +208,12 @@ impl AddSecretRequest { /// - RSA 3072 bit (up to 128 byte message) /// /// The signature can be verified during the verification of the secret-request on the target machine. - pub fn set_user_data(&mut self, msg: Vec, skey: Option>) -> Result<()> { - self.user_data = UserData::new(skey, msg)?; + pub fn set_user_data>>( + &mut self, + msg: T, + skey: Option>, + ) -> Result<()> { + self.user_data = UserData::new(skey, msg.into())?; Ok(()) } diff --git a/rust/pv/src/uvsecret/ext_secret.rs b/rust/pv/src/uvsecret/ext_secret.rs index 0c9410d1..83afa994 100644 --- a/rust/pv/src/uvsecret/ext_secret.rs +++ b/rust/pv/src/uvsecret/ext_secret.rs @@ -2,13 +2,13 @@ // // Copyright IBM Corp. 2023 -use crate::request::Secret; +use crate::request::Confidential; -/// Extension Secret for [`crate::request::uvsecret::AddSecretRequest`] +/// Extension Secret for [`crate::secret::AddSecretRequest`] #[derive(Debug, Clone)] pub enum ExtSecret { /// A bytepattern that must be equal for each request targeting the same SE-guest instance - Simple(Secret<[u8; 32]>), // contains the secret + Simple(Confidential<[u8; 32]>), // contains the secret /// A secret that is derived from the Customer communication key from the SE-header - Derived(Secret<[u8; 32]>), // contains the cck + Derived(Confidential<[u8; 32]>), // contains the cck } diff --git a/rust/pv/src/uvsecret/guest_secret.rs b/rust/pv/src/uvsecret/guest_secret.rs index 2c3e5589..0c53c328 100644 --- a/rust/pv/src/uvsecret/guest_secret.rs +++ b/rust/pv/src/uvsecret/guest_secret.rs @@ -4,15 +4,17 @@ #[allow(unused_imports)] //used for more convenient docstring use super::asrcb::AddSecretRequest; +use crate::assert_size; use crate::{ - request::{hash, openssl::MessageDigest, random_array, Secret}, + crypto::{hash, random_array}, + request::Confidential, Result, }; use byteorder::BigEndian; +use openssl::hash::MessageDigest; use pv_core::uv::{ListableSecretType, SecretId}; use serde::{Deserialize, Serialize}; use std::{convert::TryInto, fmt::Display}; -use utils::assert_size; use zerocopy::{AsBytes, U16, U32}; const ASSOC_SECRET_SIZE: usize = 32; @@ -32,7 +34,7 @@ pub enum GuestSecret { id: SecretId, /// Confidential actual association secret (32 bytes) #[serde(skip)] - secret: Secret<[u8; ASSOC_SECRET_SIZE]>, + secret: Confidential<[u8; ASSOC_SECRET_SIZE]>, }, } @@ -166,7 +168,6 @@ mod test { use super::*; use serde_test::{assert_tokens, Token}; - //todo test GuestSecret::association #[test] fn association() { let secret_value = [0x11; 32]; diff --git a/rust/pv/src/uvsecret/user_data.rs b/rust/pv/src/uvsecret/user_data.rs index 9f650291..ee54e423 100644 --- a/rust/pv/src/uvsecret/user_data.rs +++ b/rust/pv/src/uvsecret/user_data.rs @@ -1,23 +1,16 @@ +use crate::assert_size; use crate::{ crypto::{sign_msg, verify_signature}, req::BinReqValues, request::{ - openssl::{ - pkey::{PKey, Private}, - MessageDigest, - }, - uvsecret::{AddSecretRequest, AddSecretVersion}, + openssl::pkey::{HasParams, HasPublic, Id, PKey, PKeyRef, Private, Public}, RequestMagic, }, + secret::{AddSecretMagic, AddSecretRequest, AddSecretVersion, UserDataType}, Error, Result, }; -use openssl::{ - nid::Nid, - pkey::{HasParams, HasPublic, Id, PKeyRef, Public}, -}; -use pv_core::request::uvsecret::AddSecretMagic; -use pv_core::request::uvsecret::UserDataType; -use utils::assert_size; +use openssl::hash::MessageDigest; +use openssl::nid::Nid; use zerocopy::{AsBytes, BigEndian, FromBytes, FromZeroes, U16}; /// User data. diff --git a/rust/pv/src/verify.rs b/rust/pv/src/verify.rs index ffa6e691..53d2e8d8 100644 --- a/rust/pv/src/verify.rs +++ b/rust/pv/src/verify.rs @@ -9,6 +9,7 @@ use openssl::stack::Stack; use openssl::x509::store::X509Store; use openssl::x509::{CrlStatus, X509NameRef, X509Ref, X509StoreContext, X509StoreContextRef, X509}; use openssl_extensions::crl::{StackableX509Crl, X509StoreContextExtension, X509StoreExtension}; +use std::path::Path; #[cfg(not(test))] use helper::download_first_crl_from_x509; @@ -90,7 +91,7 @@ impl HkdVerifier for CertVerifier { for crl in verified_crls { match crl.get_by_serial(hkd.serial_number()) { CrlStatus::NotRevoked => (), - _ => bail_hkd_verify!(HdkRevoked), + _ => bail_hkd_verify!(HkdRevoked), } } debug!("HKD: verified"); @@ -162,7 +163,7 @@ impl CertVerifier { impl CertVerifier { /// Create a `CertVerifier`. /// - /// * `cert_paths` - Paths to Cerificates for the chain of trust + /// * `cert_paths` - Paths to certificates for the chain of trust /// * `crl_paths` - Paths to certificate revocation lists for the chain of trust /// * `root_ca_path` - Path to the root of trust /// * `offline` - if set to true the verification process will not try to download CRLs from the @@ -171,9 +172,9 @@ impl CertVerifier { /// /// This function will return an error if the chain of trust could not be established. pub fn new( - cert_paths: &[String], - crl_paths: &[String], - root_ca_path: &Option, + cert_paths: &[&Path], + crl_paths: &[&Path], + root_ca_path: Option<&Path>, offline: bool, ) -> Result { let mut store = helper::store_setup(root_ca_path, crl_paths, cert_paths)?; diff --git a/rust/pv/src/verify/helper.rs b/rust/pv/src/verify/helper.rs index 4dd08441..3b450c76 100644 --- a/rust/pv/src/verify/helper.rs +++ b/rust/pv/src/verify/helper.rs @@ -20,8 +20,9 @@ use openssl::{ }, }; use openssl_extensions::akid::{AkidCheckResult, AkidExtension}; +use std::path::Path; use std::str::from_utf8; -use std::{cmp::Ordering, ffi::c_int, usize}; +use std::{cmp::Ordering, ffi::c_int}; /// Minimum security level for the keys/certificates used to establish a chain of /// trust (see https://www.openssl.org/docs/man1.1.1/man3/X509_VERIFY_PARAM_set_auth_level.html @@ -75,9 +76,9 @@ pub fn verify_crl(crl: &X509CrlRef, issuer: &X509Ref) -> Option<()> { /// Setup the x509Store such that it can be used it for verifying certificates pub fn store_setup( - root_ca_path: &Option, - crl_paths: &[String], - cert_w_crl_paths: &[String], + root_ca_path: Option<&Path>, + crl_paths: &[&Path], + cert_w_crl_paths: &[&Path], ) -> Result { let mut x509store = X509StoreBuilder::new()?; @@ -88,7 +89,7 @@ pub fn store_setup( for crl in crl_paths { load_crl_to_store(&mut x509store, crl, true).map_err(|source| Error::X509Load { - path: crl.to_owned(), + path: crl.display().to_string(), ty: Error::CRL, source, })?; @@ -96,7 +97,7 @@ pub fn store_setup( for crl in cert_w_crl_paths { load_crl_to_store(&mut x509store, crl, false).map_err(|source| Error::X509Load { - path: crl.to_owned(), + path: crl.display().to_string(), ty: Error::CRL, source, })?; @@ -235,7 +236,7 @@ fn get_ibm_z_sign_key(certs: &[X509]) -> Result { } } -fn load_root_ca(path: &str, x509_store: &mut X509StoreBuilder) -> Result<()> { +fn load_root_ca(path: &Path, x509_store: &mut X509StoreBuilder) -> Result<()> { let lu = x509_store.add_lookup(X509Lookup::::file())?; // Try to load cert as PEM file @@ -249,7 +250,7 @@ fn load_root_ca(path: &str, x509_store: &mut X509StoreBuilder) -> Result<()> { .load_cert_file(path, SslFiletype::ASN1) .map(|_| ()) .map_err(|source| Error::X509Load { - path: path.to_string(), + path: path.display().to_string(), ty: Error::CERT, source, }), @@ -258,7 +259,7 @@ fn load_root_ca(path: &str, x509_store: &mut X509StoreBuilder) -> Result<()> { fn load_crl_to_store( x509_store: &mut X509StoreBuilder, - path: &str, + path: &Path, err_out_empty_crl: bool, ) -> std::result::Result<(), openssl::error::ErrorStack> { let lu = x509_store.add_lookup(X509Lookup::::file())?; @@ -306,7 +307,7 @@ pub fn x509_dist_points(cert: &X509Ref) -> Vec { /// Other issues are mapped to Ok(None) #[cfg(not(test))] pub fn download_first_crl_from_x509(cert: &X509Ref) -> Result>> { - use crate::misc::read_crls; + use crate::utils::read_crls; use curl::easy::{Easy2, Handler, WriteError}; use std::time::Duration; const CRL_TIMEOUT_MAX: Duration = Duration::from_secs(3); diff --git a/rust/pv/src/verify/test.rs b/rust/pv/src/verify/test.rs index 2fd6bc34..798fa1cf 100644 --- a/rust/pv/src/verify/test.rs +++ b/rust/pv/src/verify/test.rs @@ -5,7 +5,7 @@ #![cfg(test)] use super::{helper, helper::*, *}; -use crate::{misc::read_crls, Error, HkdVerifyErrorType::*}; +use crate::{utils::read_crls, Error, HkdVerifyErrorType::*}; use openssl::{stack::Stack, x509::X509Crl}; use std::path::Path; @@ -33,31 +33,31 @@ pub fn download_first_crl_from_x509(cert: &X509Ref) -> Result` -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; diff --git a/rust/pv_core/src/macros.rs b/rust/pv_core/src/macros.rs index 019b5f3b..c904f544 100644 --- a/rust/pv_core/src/macros.rs +++ b/rust/pv_core/src/macros.rs @@ -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); }; } diff --git a/rust/pv_core/src/uvdevice.rs b/rust/pv_core/src/uvdevice.rs index 0272a653..0f6baf79 100644 --- a/rust/pv_core/src/uvdevice.rs +++ b/rust/pv_core/src/uvdevice.rs @@ -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(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; diff --git a/rust/pv_core/src/uvdevice/ffi.rs b/rust/pv_core/src/uvdevice/ffi.rs index d70e7d19..0b4a8514 100644 --- a/rust/pv_core/src/uvdevice/ffi.rs +++ b/rust/pv_core/src/uvdevice/ffi.rs @@ -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::()) } static_assert!(uv_ioctl(UVIO_IOCTL_ATT_NR) == 0xc0407501); diff --git a/rust/pv_core/src/uvdevice/info.rs b/rust/pv_core/src/uvdevice/info.rs index 154b71d2..b3b753b6 100644 --- a/rust/pv_core/src/uvdevice/info.rs +++ b/rust/pv_core/src/uvdevice/info.rs @@ -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. diff --git a/rust/pv_core/src/uvdevice/secret.rs b/rust/pv_core/src/uvdevice/secret.rs index f5ea24ed..235a39a2 100644 --- a/rust/pv_core/src/uvdevice/secret.rs +++ b/rust/pv_core/src/uvdevice/secret.rs @@ -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}; diff --git a/rust/pv_core/src/uvdevice/secret_list.rs b/rust/pv_core/src/uvdevice/secret_list.rs index 95bed2dd..dbfba321 100644 --- a/rust/pv_core/src/uvdevice/secret_list.rs +++ b/rust/pv_core/src/uvdevice/secret_list.rs @@ -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 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) -> Self { Self { diff --git a/rust/pv_core/src/uvsecret.rs b/rust/pv_core/src/uvsecret.rs index a90bb964..9ddbbc0d 100644 --- a/rust/pv_core/src/uvsecret.rs +++ b/rust/pv_core/src/uvsecret.rs @@ -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 { if !Self::starts_with_magic(bytes) || bytes.len() < size_of::() { 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 for AddSecretMagic { #[cfg(test)] mod test { use crate::{ - request::{ - uvsecret::{AddSecretMagic, UserDataType}, - MagicValue, - }, + request::MagicValue, + secret::{AddSecretMagic, UserDataType}, Error, }; diff --git a/rust/pvapconfig/Cargo.toml b/rust/pvapconfig/Cargo.toml index ce3bf8d8..1c244852 100644 --- a/rust/pvapconfig/Cargo.toml +++ b/rust/pvapconfig/Cargo.toml @@ -10,10 +10,9 @@ license.workspace = true clap = { version ="4.1", features = ["derive", "wrap_help"]} lazy_static = "1.1" openssl = { version = "0.10" } -openssl-sys = { version = "0.9" } pv_core = { path = "../pv_core"} rand = "0.8" -regex = "1" -serde = { version = "1.0", features = ["derive"] } +regex = "1.7" +serde = { version = "1.0.139", features = ["derive"] } serde_yaml = "0.9" utils = { path = "../utils" } diff --git a/rust/pvapconfig/src/helper.rs b/rust/pvapconfig/src/helper.rs index ae1b1b23..88e09bc6 100644 --- a/rust/pvapconfig/src/helper.rs +++ b/rust/pvapconfig/src/helper.rs @@ -199,7 +199,7 @@ impl Drop for LockFile { #[cfg(test)] mod tests { use super::*; - use pv_core::misc::TemporaryDirectory; + use utils::TemporaryDirectory; // Only very simple tests diff --git a/rust/pvapconfig/src/main.rs b/rust/pvapconfig/src/main.rs index 9c67901f..f480c69e 100644 --- a/rust/pvapconfig/src/main.rs +++ b/rust/pvapconfig/src/main.rs @@ -18,7 +18,7 @@ use config::{ApConfigEntry, ApConfigList}; use helper::{LockFile, PATH_PVAPCONFIG_LOCK}; use pv_core::uv::{ListableSecretType, SecretList}; use std::process::ExitCode; -use utils::release_string; +use utils::print_version; /// Simple macro for /// if Cli::verbose() { @@ -58,11 +58,7 @@ macro_rules! on_error_print_and_exit { fn main() -> ExitCode { // handle version option if cli::ARGS.version { - println!( - "{} version {}\nCopyright IBM Corp. 2023", - env!("CARGO_PKG_NAME"), - release_string!() - ); + print_version!(0, "2023"); return ExitCode::SUCCESS; } diff --git a/rust/pvsecret/Cargo.toml b/rust/pvsecret/Cargo.toml index 5b0823fc..43934d16 100644 --- a/rust/pvsecret/Cargo.toml +++ b/rust/pvsecret/Cargo.toml @@ -6,7 +6,7 @@ license.workspace = true [dependencies] anyhow = { version = "1.0.70", features = ["std"] } -clap = { version ="4", features = ["derive", "wrap_help"]} +clap = { version ="4.1", features = ["derive", "wrap_help"]} log = { version = "0.4.6", features = ["std", "release_max_level_debug"] } serde_yaml = "0.9" diff --git a/rust/pvsecret/src/cli.rs b/rust/pvsecret/src/cli.rs index 7808f2da..60ec4958 100644 --- a/rust/pvsecret/src/cli.rs +++ b/rust/pvsecret/src/cli.rs @@ -3,8 +3,7 @@ // Copyright IBM Corp. 2023 use clap::{ArgGroup, Args, CommandFactory, Parser, Subcommand, ValueEnum, ValueHint}; -use pv::misc::CertificateOptions; -use pv::misc::STDOUT; +use utils::{CertificateOptions, STDOUT}; /// Manage secrets for IBM Secure Execution guests. /// diff --git a/rust/pvsecret/src/cmd/add.rs b/rust/pvsecret/src/cmd/add.rs index 947604e4..3a9d8f2a 100644 --- a/rust/pvsecret/src/cmd/add.rs +++ b/rust/pvsecret/src/cmd/add.rs @@ -5,8 +5,8 @@ use crate::cli::AddSecretOpt; use anyhow::{Context, Result}; use log::warn; -use pv::misc::get_reader_from_cli_file_arg; use pv::uv::{AddCmd, UvDevice}; +use utils::get_reader_from_cli_file_arg; /// Do an Add Secret UVC pub fn add(opt: &AddSecretOpt) -> Result<()> { diff --git a/rust/pvsecret/src/cmd/create.rs b/rust/pvsecret/src/cmd/create.rs index 2c6fbe32..ab56fb93 100644 --- a/rust/pvsecret/src/cmd/create.rs +++ b/rust/pvsecret/src/cmd/create.rs @@ -1,23 +1,24 @@ // SPDX-License-Identifier: MIT // -// Copyright IBM Corp. 2023 +// Copyright IBM Corp. 2023, 2024 use crate::cli::{AddSecretType, CreateSecretFlags, CreateSecretOpt}; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{anyhow, bail, Context, Error, Result}; use log::{debug, info, trace, warn}; use pv::{ misc::{ - get_writer_from_cli_file_arg, open_file, parse_hex, pv_guest_bit_set, read_certs, - read_exact_file, read_file, read_private_key, try_parse_u128, try_parse_u64, write, + open_file, parse_hex, pv_guest_bit_set, read_exact_file, read_file, try_parse_u128, + try_parse_u64, write, }, request::{ - openssl::pkey::{PKey, Public}, - uvsecret::{AddSecretFlags, AddSecretRequest, AddSecretVersion, ExtSecret, GuestSecret}, - BootHdrTags, HkdVerifier, ReqEncrCtx, Request, SymKeyType, + openssl::pkey::{PKey, Private}, + BootHdrTags, ReqEncrCtx, Request, SymKeyType, }, + secret::{AddSecretFlags, AddSecretRequest, AddSecretVersion, ExtSecret, GuestSecret}, uv::ConfigUid, }; use serde_yaml::Value; +use utils::get_writer_from_cli_file_arg; fn write_out>(path: &str, data: D, ctx: &str) -> pv::Result<()> { let mut wr = get_writer_from_cli_file_arg(path)?; @@ -40,8 +41,8 @@ pub fn create(opt: &CreateSecretOpt) -> Result<()> { debug!("Generated Add-secret request"); // Add host-key documents - let verifier = opt.certificate_args.verifier()?; - read_and_verify_hkds(&opt.certificate_args.host_key_documents, verifier)? + opt.certificate_args + .get_verified_hkds("secret")? .into_iter() .for_each(|k| asrcb.add_hostkey(k)); @@ -57,6 +58,13 @@ pub fn create(opt: &CreateSecretOpt) -> Result<()> { write_secret(&opt.secret, &asrcb) } +/// Read+parse the first key from the buffer. +fn read_private_key(buf: &[u8]) -> Result> { + PKey::private_key_from_der(buf) + .or_else(|_| PKey::private_key_from_pem(buf)) + .map_err(Error::new) +} + /// Set-up the `add-secret request` from command-line arguments fn build_asrcb(opt: &CreateSecretOpt) -> Result { debug!("Build add-secret request"); @@ -191,38 +199,6 @@ fn read_cuid(asrcb: &mut AddSecretRequest, opt: &CreateSecretOpt) -> Result<()> Ok(()) } -/// reads HKDs into memory, verifies them with the provided HKD verifier. -/// returns list of public keys or Err -/// Aborts on first error -fn read_and_verify_hkds( - hkds: &Vec, - verifier: Box, -) -> Result>> { - let mut res = Vec::with_capacity(hkds.len()); - for hkd in hkds { - let hk = read_file(hkd, "host-key document")?; - let certs = read_certs(&hk).with_context(|| { - format!("The provided Host Key Document in '{hkd}' is not in PEM or DER format") - })?; - if certs.is_empty() { - let msg = format!( - "The provided host key document in {} contains no certificate!", - hkd - ); - return Err(anyhow!(msg)); - } - if certs.len() > 1 { - warn!("The host key document in '{hkd}' contains more than one certificate! Only the first certificate will be used.") - } - - // len is >= 1 -> unwrap will succeed - let c = certs.first().unwrap(); - verifier.verify(c)?; - res.push(c.public_key()?); - info!("Use host-key document at '{hkd}'"); - } - Ok(res) -} /// Write the generated secret (if any) to the specified output stream fn write_secret(secret: &AddSecretType, asrcb: &AddSecretRequest) -> Result<()> { if let AddSecretType::Association { @@ -262,3 +238,21 @@ fn write_secret(secret: &AddSecretType, asrcb: &AddSecretRequest) -> Result<()> }; Ok(()) } + +#[cfg(test)] +mod test { + + #[test] + fn read_private_key() { + let key = include_bytes!("../../../pv/tests/assets/keys/rsa3072key.pem"); + let key = super::read_private_key(key).unwrap(); + assert_eq!(key.rsa().unwrap().size(), 384); + } + + #[test] + fn read_private_key_fail() { + let key = include_bytes!("create.rs"); + let key = super::read_private_key(key); + assert!(key.is_err()); + } +} diff --git a/rust/pvsecret/src/cmd/list.rs b/rust/pvsecret/src/cmd/list.rs index e3d193ed..f7e3a72b 100644 --- a/rust/pvsecret/src/cmd/list.rs +++ b/rust/pvsecret/src/cmd/list.rs @@ -5,10 +5,8 @@ use crate::cli::{ListSecretOpt, ListSecretOutputType}; use anyhow::{Context, Result}; use log::warn; -use pv::{ - misc::{get_writer_from_cli_file_arg, STDOUT}, - uv::{ListCmd, SecretList, UvDevice, UvcSuccess}, -}; +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<()> { diff --git a/rust/pvsecret/src/cmd/verify.rs b/rust/pvsecret/src/cmd/verify.rs index f665ad17..d96ac8cf 100644 --- a/rust/pvsecret/src/cmd/verify.rs +++ b/rust/pvsecret/src/cmd/verify.rs @@ -1,14 +1,12 @@ +use crate::cli::VerifyOpt; use anyhow::{anyhow, Context, Result}; use log::warn; +use pv::misc::{read_certs, read_file}; use pv::{ - misc::{get_reader_from_cli_file_arg, get_writer_from_cli_file_arg, read_certs, read_file}, - request::{ - openssl::pkey::{PKey, Public}, - uvsecret::verify_asrcb_and_get_user_data, - }, + request::openssl::pkey::{PKey, Public}, + secret::verify_asrcb_and_get_user_data, }; - -use crate::cli::VerifyOpt; +use utils::{get_reader_from_cli_file_arg, get_writer_from_cli_file_arg}; /// read the content of a DER or PEM x509 and return the public key fn read_sgn_key(path: &str) -> Result> { diff --git a/rust/pvsecret/src/main.rs b/rust/pvsecret/src/main.rs index 82d45cc3..20ff61cf 100644 --- a/rust/pvsecret/src/main.rs +++ b/rust/pvsecret/src/main.rs @@ -1,17 +1,15 @@ // SPDX-License-Identifier: MIT // -// Copyright IBM Corp. 2023 +// Copyright IBM Corp. 2023, 2024 mod cli; mod cmd; -use clap::CommandFactory; -use clap::Parser; +use clap::{CommandFactory, Parser}; use cli::{CliOptions, Command}; use log::trace; -use pv::misc::PvLogger; use std::process::ExitCode; -use utils::release_string; +use utils::{print_cli_error, print_error, print_version, PvLogger}; use crate::cli::validate_cli; @@ -19,44 +17,8 @@ static LOGGER: PvLogger = PvLogger; static EXIT_LOGGER: u8 = 3; const FEATURES: &[&[&str]] = &[cmd::CMD_FN, cmd::UV_CMD_FN]; -fn print_error(e: anyhow::Error, verbosity: u8) -> ExitCode { - if verbosity > 0 { - // Debug formatter also prints the whole error stack - // So only print it when on verbose - eprintln!("error: {e:?}") - } else { - eprintln!("error: {e}") - }; - ExitCode::FAILURE -} - -fn print_cli_error(e: clap::Error) -> ExitCode { - let ret = if e.use_stderr() { - ExitCode::FAILURE - } else { - ExitCode::SUCCESS - }; - //Ignore any errors during printing of the error - let _ = e.format(&mut CliOptions::command()).print(); - ret -} - fn print_version(verbosity: u8) -> anyhow::Result<()> { - println!( - "{} version {}\nCopyright IBM Corp. 2023", - env!("CARGO_PKG_NAME"), - release_string!() - ); - if verbosity > 0 { - FEATURES.concat().iter().for_each(|f| print!("{f} ")); - println!("(compiled)"); - println!( - "\n{}-crate {}", - env!("CARGO_PKG_NAME"), - env!("CARGO_PKG_VERSION") - ); - println!("{}", pv::crate_info()); - } + print_version!(verbosity, "2024", FEATURES.concat()); Ok(()) } @@ -64,9 +26,9 @@ fn main() -> ExitCode { let cli: CliOptions = match CliOptions::try_parse() { Ok(cli) => match validate_cli(&cli) { Ok(_) => cli, - Err(e) => return print_cli_error(e), + Err(e) => return print_cli_error(e, CliOptions::command()), }, - Err(e) => return print_cli_error(e), + Err(e) => return print_cli_error(e, CliOptions::command()), }; // set up logger/std(out,err) @@ -97,6 +59,6 @@ fn main() -> ExitCode { match res { Ok(_) => ExitCode::SUCCESS, - Err(e) => print_error(e, cli.verbose), + Err(e) => print_error(&e, cli.verbose), } } diff --git a/rust/utils/Cargo.toml b/rust/utils/Cargo.toml index 215381be..9ab410f1 100644 --- a/rust/utils/Cargo.toml +++ b/rust/utils/Cargo.toml @@ -3,3 +3,9 @@ name = "utils" version = "0.1.0" edition.workspace = true license.workspace = true + +[dependencies] +clap = { version ="4.1", features = ["derive", "wrap_help"] } +libc = "0.2.49" +log = { version = "0.4.6", features = ["std", "release_max_level_debug"] } +pv = { path = "../pv" } diff --git a/rust/pv/src/cli.rs b/rust/utils/src/cli.rs similarity index 61% rename from rust/pv/src/cli.rs rename to rust/utils/src/cli.rs index 3c9a0883..e2dba376 100644 --- a/rust/pv/src/cli.rs +++ b/rust/utils/src/cli.rs @@ -1,11 +1,21 @@ // SPDX-License-Identifier: MIT // -// Copyright IBM Corp. 2023 +// Copyright IBM Corp. 2023, 2024 -use crate::misc::{create_file, open_file}; -use crate::Result; -use clap::{ArgGroup, Args, ValueHint}; +use clap::{ArgGroup, Args, Command, ValueHint}; +use log::{info, warn}; +use pv::misc::read_file; +use pv::{ + misc::{create_file, open_file, read_certs}, + request::{ + openssl::pkey::{PKey, Public}, + HkdVerifier, + }, + Error, Result, +}; use std::io::{Read, Write}; +use std::path::Path; +use std::process::ExitCode; /// CLI Argument collection for handling certificates. #[derive(Args, Debug, PartialEq, Eq, Default)] @@ -34,7 +44,7 @@ pub struct CertificateOptions { #[arg(long)] pub no_verify: bool, - /// Use FILE as a certificate to verify the host key or keys. + /// Use FILE as a certificate to verify the host-key or keys. /// /// The certificates are used to establish a chain of trust for the verification /// of the host-key documents. Specify this option twice to specify the IBM Z signing key and @@ -79,26 +89,63 @@ pub struct CertificateOptions { impl CertificateOptions { /// Returns the verifier of this [`CertificateOptions`] based on the given CLI options. /// + /// - `protectee`: what you want to create. e.g. add-secret request or SE-image + /// /// # Errors /// /// This function will return an error if [`crate::request::HkdVerifier`] cannot be created. - pub fn verifier(&self) -> Result> { - use crate::verify::{CertVerifier, NoVerifyHkd}; + fn verifier(&self, protectee: &'static str) -> Result> { + use pv::request::{CertVerifier, NoVerifyHkd}; match self.no_verify { true => { log::warn!( - "Host-key document verification is disabled. The secret may not be protected." + "Host-key document verification is disabled. The {protectee} may not be protected." ); Ok(Box::new(NoVerifyHkd)) } false => Ok(Box::new(CertVerifier::new( - &self.certs, - &self.crls, - &self.root_ca, + &self.certs.iter().map(Path::new).collect::>(), + &self.crls.iter().map(Path::new).collect::>(), + self.root_ca.as_ref().map(Path::new), self.offline, )?)), } } + + /// Read the host-keys specified and verifies them if required + /// + /// - `protectee`: what you want to create. e.g. add-secret request or SE-image + /// + /// # Error + /// Returns an error if something went wrong during parsing the HKDs, the verification chain + /// could not built, or when the verification + /// failed. + pub fn get_verified_hkds(&self, protectee: &'static str) -> Result>> { + let hkds = &self.host_key_documents; + let verifier = self.verifier(protectee)?; + + let mut res = Vec::with_capacity(hkds.len()); + for hkd in hkds { + let hk = read_file(hkd, "host-key document")?; + let certs = read_certs(&hk).map_err(|source| Error::HkdNotPemOrDer { + hkd: hkd.to_string(), + source, + })?; + if certs.is_empty() { + return Err(Error::NoHkdInFile(hkd.to_string())); + } + if certs.len() != 1 { + warn!("The host-key document in '{hkd}' contains more than one certificate!") + } + + // Panic: len is == 1 -> unwrap will succeed/not panic + let c = certs.first().unwrap(); + verifier.verify(c)?; + res.push(c.public_key()?); + info!("Use host-key document at '{hkd}'"); + } + Ok(res) + } } /// stdout @@ -124,6 +171,34 @@ pub fn get_reader_from_cli_file_arg(path: &str) -> Result> { } } +/// Print an error that occured during CLI parsing +pub fn print_cli_error(e: clap::Error, mut cmd: Command) -> ExitCode { + let ret = if e.use_stderr() { + ExitCode::FAILURE + } else { + ExitCode::SUCCESS + }; + // Ignore any errors during printing of the error + let _ = e.format(&mut cmd).print(); + ret +} + +/// Print an error to stderr +pub fn print_error(e: &E, verbosity: u8) -> ExitCode +where + // Error trait is not required, but here to limit the usage to errors + E: AsRef + std::fmt::Debug + std::fmt::Display, +{ + if verbosity > 0 { + // Debug formatter also prints the whole error stack + // So only print it when on verbose + eprintln!("error: {e:?}") + } else { + eprintln!("error: {e}") + }; + ExitCode::FAILURE +} + #[cfg(test)] mod test { use clap::Parser; diff --git a/rust/utils/src/lib.rs b/rust/utils/src/lib.rs index c8a29940..83e8114f 100644 --- a/rust/utils/src/lib.rs +++ b/rust/utils/src/lib.rs @@ -2,7 +2,17 @@ //! Utils for s390-tools written in rust. //! Not intened to be used outside of s390-tools. //! -//! Copyright IBM Corp. 2023 +//! Copyright IBM Corp. 2023, 2024 +mod cli; +mod log; +mod tmpfile; + +pub use crate::cli::CertificateOptions; +pub use crate::cli::{get_reader_from_cli_file_arg, get_writer_from_cli_file_arg}; +pub use crate::cli::{print_cli_error, print_error}; +pub use crate::cli::{STDIN, STDOUT}; +pub use crate::log::PvLogger; +pub use crate::tmpfile::TemporaryDirectory; /// Get the s390-tools release string /// @@ -28,6 +38,33 @@ macro_rules! release_string { }}; } +#[macro_export] +/// Print the version to stdout +/// +/// verbosity: integer if >0 more and more details printed +/// feat: (optional) list of features +/// rel_str: a string containig the release name +macro_rules! print_version { + ($verbosity: expr, $year: expr $( ,$feat: expr)?) => {{ + println!( + "{} version {}\nCopyright IBM Corp. {}", + env!("CARGO_PKG_NAME"), + $crate::release_string!(), + $year, + ); + if $verbosity > 0 { + $($feat.iter().for_each(|f| print!("{f} ")); println!("(compiled)");)? + } + if $verbosity > 1 { + println!( + "\n{}-crate {}", + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_VERSION"), + ); + } + }}; +} + /// Asserts a constant expression evaluates to `true`. /// /// If the expression is not evaluated to `true` the compilation will fail. diff --git a/rust/pv_core/src/log.rs b/rust/utils/src/log.rs similarity index 99% rename from rust/pv_core/src/log.rs rename to rust/utils/src/log.rs index 127b95db..70204e8c 100644 --- a/rust/pv_core/src/log.rs +++ b/rust/utils/src/log.rs @@ -44,5 +44,6 @@ impl Log for PvLogger { } } } + fn flush(&self) {} } diff --git a/rust/pv_core/src/tmpfile.rs b/rust/utils/src/tmpfile.rs similarity index 90% rename from rust/pv_core/src/tmpfile.rs rename to rust/utils/src/tmpfile.rs index 24dafbf7..6e660326 100644 --- a/rust/pv_core/src/tmpfile.rs +++ b/rust/utils/src/tmpfile.rs @@ -44,7 +44,7 @@ impl TemporaryDirectory { /// An error is returned if the temporary directory could not be created. pub fn new>(prefix: P) -> Result { let mut template = prefix.as_ref().to_owned(); - let mut template_os_string = template.as_mut_os_string(); + let template_os_string = template.as_mut_os_string(); template_os_string.push("XXXXXX"); let temp_dir = mkdtemp(template_os_string)?; @@ -64,7 +64,7 @@ impl TemporaryDirectory { } /// Removes the created temporary directory and it's contents. - pub fn close(mut self) -> std::io::Result<()> { + pub fn close(self) -> std::io::Result<()> { let ret = std::fs::remove_dir_all(&self.path); self.forget(); ret @@ -85,8 +85,6 @@ impl Drop for TemporaryDirectory { #[cfg(test)] mod tests { - use std::path::PathBuf; - use super::{mkdtemp, TemporaryDirectory}; #[test] @@ -97,15 +95,15 @@ mod tests { 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 = + 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); + std::fs::remove_dir(path).unwrap(); } #[test] @@ -115,7 +113,7 @@ mod tests { assert!(path.exists()); // Test that close removes the directory - temp_dir.close(); + temp_dir.close().unwrap(); assert!(!path.exists()); } @@ -139,7 +137,7 @@ mod tests { assert!(path.as_os_str().to_str().expect("works").starts_with("yay")); // Test that close() removes the directory - temp_dir.close(); + temp_dir.close().unwrap(); assert!(!path.exists()); }