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:
10
rust/Cargo.lock
generated
10
rust/Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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<R>(img: &mut R) -> Result<Self>
|
||||
where
|
||||
R: Read + Seek,
|
||||
@@ -125,18 +118,18 @@ impl BootHdrTags {
|
||||
img.seek(Current(
|
||||
hdr_head.size.get() as i64
|
||||
- size_of::<BootHdrHead>() 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,
|
||||
],
|
||||
|
||||
@@ -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<const COUNT: usize> 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<u8> {
|
||||
/// 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<u8> {
|
||||
|
||||
/// 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: Zeroize>(C);
|
||||
impl<C: Zeroize> Secret<C> {
|
||||
pub struct Confidential<C: Zeroize>(C);
|
||||
impl<C: Zeroize> Confidential<C> {
|
||||
/// 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<C: Zeroize> Secret<C> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Zeroize + Debug> Debug for Secret<C> {
|
||||
impl<C: Zeroize + Debug> Debug for Confidential<C> {
|
||||
#[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<C: Zeroize> From<C> for Secret<C> {
|
||||
fn from(v: C) -> Secret<C> {
|
||||
Secret(v)
|
||||
impl<C: Zeroize> From<C> for Confidential<C> {
|
||||
fn from(v: C) -> Confidential<C> {
|
||||
Confidential(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Zeroize> Zeroize for Secret<C> {
|
||||
impl<C: Zeroize> Zeroize for Confidential<C> {
|
||||
fn zeroize(&mut self) {
|
||||
self.0.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Zeroize> Drop for Secret<C> {
|
||||
impl<C: Zeroize> Drop for Confidential<C> {
|
||||
fn drop(&mut self) {
|
||||
self.0.zeroize();
|
||||
}
|
||||
@@ -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<Aes256Key> for SymKey {
|
||||
fn from(value: Aes256Key) -> Self {
|
||||
Self::Aes256(value)
|
||||
@@ -90,7 +75,7 @@ impl From<Aes256Key> for SymKey {
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an OpenSSL error if the key could not be generated.
|
||||
pub fn hkdf_rfc_5869<const COUNT: usize>(
|
||||
pub(crate) fn hkdf_rfc_5869<const COUNT: usize>(
|
||||
md: &MdRef,
|
||||
ikm: &[u8],
|
||||
salt: &[u8],
|
||||
@@ -114,17 +99,20 @@ pub fn hkdf_rfc_5869<const COUNT: usize>(
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if something went bad in OpenSSL.
|
||||
pub fn derive_key(k1: &PKey<Private>, k2: &PKey<Public>) -> Result<Aes256Key> {
|
||||
pub(crate) fn derive_key(k1: &PKeyRef<Private>, k2: &PKeyRef<Public>) -> Result<Aes256Key> {
|
||||
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<Private>, k2: &PKey<Public>) -> Result<Aes256Key> {
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the entropy source fails or is not available.
|
||||
pub fn random_array<const COUNT: usize>() -> Result<[u8; COUNT]> {
|
||||
pub(crate) fn random_array<const COUNT: usize>() -> Result<[u8; COUNT]> {
|
||||
let mut rand = [0; COUNT];
|
||||
rand_bytes(&mut rand)?;
|
||||
Ok(rand)
|
||||
@@ -143,14 +131,15 @@ pub fn random_array<const COUNT: usize>() -> 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<PKey<Private>> {
|
||||
pub(crate) fn gen_ec_key() -> Result<PKey<Private>> {
|
||||
let group = EcGroup::from_curve_name(Nid::SECP521R1)?;
|
||||
let key: EcKey<Private> = 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<u8>`] with the following content:
|
||||
@@ -163,12 +152,14 @@ pub struct AesGcmResult {
|
||||
/// The position of the encrypted data in [`Self::buf`]
|
||||
pub encr_range: Range<usize>,
|
||||
/// The position of the tag in [`Self::buf`]
|
||||
#[allow(unused)]
|
||||
// here for completeness
|
||||
pub tag_range: Range<usize>,
|
||||
}
|
||||
|
||||
impl AesGcmResult {
|
||||
/// Deconstruct the result to just the resulting data w/o ranges.
|
||||
pub fn data(self) -> Vec<u8> {
|
||||
pub(crate) fn data(self) -> Vec<u8> {
|
||||
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<AesGcmResult> {
|
||||
pub(crate) fn encrypt_aes_gcm(
|
||||
key: &SymKey,
|
||||
iv: &[u8],
|
||||
aad: &[u8],
|
||||
conf: &[u8],
|
||||
) -> Result<AesGcmResult> {
|
||||
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<DigestBytes> {
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if OpenSSL could not compute the signature.
|
||||
pub fn sign_msg(skey: &PKeyRef<Private>, dgst: MessageDigest, msg: &[u8]) -> Result<Vec<u8>> {
|
||||
pub(crate) fn sign_msg(
|
||||
skey: &PKeyRef<Private>,
|
||||
dgst: MessageDigest,
|
||||
msg: &[u8],
|
||||
) -> Result<Vec<u8>> {
|
||||
match skey.id() {
|
||||
Id::EC => {
|
||||
let mut sgn = Signer::new(dgst, skey)?;
|
||||
@@ -265,7 +265,7 @@ pub fn sign_msg(skey: &PKeyRef<Private>, dgst: MessageDigest, msg: &[u8]) -> Res
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if OpenSSL could not compute the signature.
|
||||
pub fn verify_signature<T: HasPublic>(
|
||||
pub(crate) fn verify_signature<T: HasPublic>(
|
||||
skey: &PKeyRef<T>,
|
||||
dgst: MessageDigest,
|
||||
msg: &[u8],
|
||||
|
||||
@@ -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<T, E = Error> = std::result::Result<T, E>;
|
||||
@@ -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;
|
||||
|
||||
@@ -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 <version>`
|
||||
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::*;
|
||||
}
|
||||
|
||||
@@ -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<Private>) -> Result<Vec<u8>> {
|
||||
fn encrypt(&self, secret: &[u8], priv_key: &PKeyRef<Private>) -> Result<Vec<u8>> {
|
||||
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<Private>, to: &mut Vec<u8>) -> Result<()>;
|
||||
fn encrypt_to(
|
||||
&self,
|
||||
secret: &[u8],
|
||||
priv_key: &PKeyRef<Private>,
|
||||
to: &mut Vec<u8>,
|
||||
) -> Result<()>;
|
||||
}
|
||||
|
||||
/// Types of Authenticated Data
|
||||
@@ -91,7 +98,7 @@ impl Encrypt for Keyslot {
|
||||
fn encrypt_to(
|
||||
&self,
|
||||
prot_key: &[u8],
|
||||
priv_key: &PKey<Private>,
|
||||
priv_key: &PKeyRef<Private>,
|
||||
to: &mut Vec<u8>,
|
||||
) -> 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<AesGcmResult> {
|
||||
pub(crate) fn encrypt_aead(&self, aad: &[u8], conf: &[u8]) -> Result<AesGcmResult> {
|
||||
encrypt_aes_gcm(&self.prot_key, &self.iv, aad, conf)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,12 +39,6 @@ pub fn get_cert_asset_path<P: AsRef<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
|
||||
|
||||
@@ -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<Vec<X509Crl>> {
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the underlying OpenSSL implementation cannot parse `buf`
|
||||
|
||||
pub fn read_certs(buf: &[u8]) -> Result<Vec<X509>> {
|
||||
pub fn read_certs(buf: &[u8]) -> Result<Vec<X509>, 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>> {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Vec<u8>> {
|
||||
fn to_bytes(&self) -> Confidential<Vec<u8>> {
|
||||
let secret = self.secret.confidential();
|
||||
|
||||
let mut v = vec![0; secret.len() + 32];
|
||||
@@ -110,7 +108,7 @@ impl From<AddSecretVersion> 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<u8>, skey: Option<PKey<Private>>) -> Result<()> {
|
||||
self.user_data = UserData::new(skey, msg)?;
|
||||
pub fn set_user_data<T: Into<Vec<u8>>>(
|
||||
&mut self,
|
||||
msg: T,
|
||||
skey: Option<PKey<Private>>,
|
||||
) -> Result<()> {
|
||||
self.user_data = UserData::new(skey, msg.into())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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<String>,
|
||||
cert_paths: &[&Path],
|
||||
crl_paths: &[&Path],
|
||||
root_ca_path: Option<&Path>,
|
||||
offline: bool,
|
||||
) -> Result<Self> {
|
||||
let mut store = helper::store_setup(root_ca_path, crl_paths, cert_paths)?;
|
||||
|
||||
@@ -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<String>,
|
||||
crl_paths: &[String],
|
||||
cert_w_crl_paths: &[String],
|
||||
root_ca_path: Option<&Path>,
|
||||
crl_paths: &[&Path],
|
||||
cert_w_crl_paths: &[&Path],
|
||||
) -> Result<X509StoreBuilder> {
|
||||
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<X509> {
|
||||
}
|
||||
}
|
||||
|
||||
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>::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>::file())?;
|
||||
@@ -306,7 +307,7 @@ pub fn x509_dist_points(cert: &X509Ref) -> Vec<String> {
|
||||
/// Other issues are mapped to Ok(None)
|
||||
#[cfg(not(test))]
|
||||
pub fn download_first_crl_from_x509(cert: &X509Ref) -> Result<Option<Vec<openssl::x509::X509Crl>>> {
|
||||
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);
|
||||
|
||||
@@ -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<Option<Vec<X509Crl
|
||||
|
||||
#[test]
|
||||
fn store_setup() {
|
||||
let ibm_str = get_cert_asset_path_string("ibm.crt");
|
||||
let inter_str = get_cert_asset_path_string("inter.crt");
|
||||
let ibm_path = get_cert_asset_path("ibm.crt");
|
||||
let inter_path = get_cert_asset_path("inter.crt");
|
||||
|
||||
let store = helper::store_setup(&None, &[], &[ibm_str, inter_str]);
|
||||
let store = helper::store_setup(None, &[], &[&ibm_path, &inter_path]);
|
||||
assert!(store.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_chain_online() {
|
||||
let ibm_crt = get_cert_asset_path_string("ibm.crt");
|
||||
let inter_crt = get_cert_asset_path_string("inter_ca.crt");
|
||||
let root_crt = get_cert_asset_path_string("root_ca.chained.crt");
|
||||
let ibm_crt = get_cert_asset_path("ibm.crt");
|
||||
let inter_crt = get_cert_asset_path("inter_ca.crt");
|
||||
let root_crt = get_cert_asset_path("root_ca.chained.crt");
|
||||
|
||||
let ret = CertVerifier::new(&[ibm_crt, inter_crt], &[], &root_crt.into(), false);
|
||||
let ret = CertVerifier::new(&[&ibm_crt, &inter_crt], &[], Some(&root_crt), false);
|
||||
assert!(ret.is_ok(), "CertVerifier::new failed: {ret:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_chain_offline() {
|
||||
let ibm_crt = load_gen_cert("ibm.crt");
|
||||
let inter_crl = get_cert_asset_path_string("inter_ca.crl");
|
||||
let inter_crl = get_cert_asset_path("inter_ca.crl");
|
||||
let inter_crt = load_gen_cert("inter_ca.crt");
|
||||
let root_crt = get_cert_asset_path_string("root_ca.chained.crt");
|
||||
let root_crt = get_cert_asset_path("root_ca.chained.crt");
|
||||
|
||||
let store = helper::store_setup(&Some(root_crt), &[inter_crl], &[])
|
||||
let store = helper::store_setup(Some(&root_crt), &[&inter_crl], &[])
|
||||
.unwrap()
|
||||
.build();
|
||||
|
||||
@@ -75,20 +75,20 @@ fn dist_points() {
|
||||
}
|
||||
|
||||
fn verify(offline: bool, ibm_crt: &'static str, ibm_crl: &'static str, hkd: &'static str) {
|
||||
let root_crt = get_cert_asset_path_string("root_ca.chained.crt");
|
||||
let inter_crt = get_cert_asset_path_string("inter_ca.crt");
|
||||
let inter_crl = get_cert_asset_path_string("inter_ca.crl");
|
||||
let ibm_crt = get_cert_asset_path_string(ibm_crt);
|
||||
let ibm_crl = get_cert_asset_path_string(ibm_crl);
|
||||
let root_crt = get_cert_asset_path("root_ca.chained.crt");
|
||||
let inter_crt = get_cert_asset_path("inter_ca.crt");
|
||||
let inter_crl = get_cert_asset_path("inter_ca.crl");
|
||||
let ibm_crt = get_cert_asset_path(ibm_crt);
|
||||
let ibm_crl = get_cert_asset_path(ibm_crl);
|
||||
let hkd_revoked = load_gen_cert("host_rev.crt");
|
||||
let hkd_exp = load_gen_cert("host_crt_expired.crt");
|
||||
let hkd = load_gen_cert(hkd);
|
||||
|
||||
let crls = &[ibm_crl, inter_crl];
|
||||
let crls = &[ibm_crl.as_path(), inter_crl.as_path()];
|
||||
let verifier = CertVerifier::new(
|
||||
&[ibm_crt, inter_crt],
|
||||
&[&ibm_crt, &inter_crt],
|
||||
if offline { crls } else { &[] },
|
||||
&Some(root_crt),
|
||||
Some(&root_crt),
|
||||
offline,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -98,7 +98,7 @@ fn verify(offline: bool, ibm_crt: &'static str, ibm_crl: &'static str, hkd: &'st
|
||||
|
||||
assert!(matches!(
|
||||
verifier.verify(&hkd_revoked),
|
||||
Err(Error::HkdVerify(HdkRevoked))
|
||||
Err(Error::HkdVerify(HkdRevoked))
|
||||
));
|
||||
|
||||
assert!(matches!(
|
||||
|
||||
@@ -10,12 +10,12 @@ use pv::{
|
||||
get_test_asset,
|
||||
request::{
|
||||
openssl::pkey::{PKey, Public},
|
||||
uvsecret::{
|
||||
verify_asrcb_and_get_user_data, AddSecretFlags, AddSecretRequest, AddSecretVersion,
|
||||
ExtSecret, GuestSecret,
|
||||
},
|
||||
BootHdrTags, ReqEncrCtx, Request, SymKey,
|
||||
},
|
||||
secret::{
|
||||
verify_asrcb_and_get_user_data, AddSecretFlags, AddSecretRequest, AddSecretVersion,
|
||||
ExtSecret, GuestSecret,
|
||||
},
|
||||
test_utils::get_test_keys,
|
||||
uv::ConfigUid,
|
||||
Result,
|
||||
|
||||
@@ -30,90 +30,80 @@ fn verify_sign_error_slice(exp_raw: &[c_int], obs: Error) {
|
||||
|
||||
#[test]
|
||||
fn verifier_new() {
|
||||
let root_chn_crt = get_cert_asset_path_string("root_ca.chained.crt");
|
||||
let root_crt = get_cert_asset_path_string("root_ca.crt");
|
||||
let inter_crt = get_cert_asset_path_string("inter_ca.crt");
|
||||
let inter_fake_crt = get_cert_asset_path_string("fake_inter_ca.crt");
|
||||
let inter_fake_crl = get_cert_asset_path_string("fake_inter_ca.crl");
|
||||
let inter_crl = get_cert_asset_path_string("inter_ca.crl");
|
||||
let ibm_crt = get_cert_asset_path_string("ibm.crt");
|
||||
let ibm_early_crt = get_cert_asset_path_string("ibm_outdated_early.crl");
|
||||
let ibm_late_crt = get_cert_asset_path_string("ibm_outdated_late.crl");
|
||||
let ibm_rev_crt = get_cert_asset_path_string("ibm_rev.crt");
|
||||
let root_chn_crt = get_cert_asset_path("root_ca.chained.crt");
|
||||
let root_crt = get_cert_asset_path("root_ca.crt");
|
||||
let inter_crt = get_cert_asset_path("inter_ca.crt");
|
||||
let inter_fake_crt = get_cert_asset_path("fake_inter_ca.crt");
|
||||
let inter_fake_crl = get_cert_asset_path("fake_inter_ca.crl");
|
||||
let inter_crl = get_cert_asset_path("inter_ca.crl");
|
||||
let ibm_crt = get_cert_asset_path("ibm.crt");
|
||||
let ibm_early_crt = get_cert_asset_path("ibm_outdated_early.crl");
|
||||
let ibm_late_crt = get_cert_asset_path("ibm_outdated_late.crl");
|
||||
let ibm_rev_crt = get_cert_asset_path("ibm_rev.crt");
|
||||
|
||||
// Too many signing keys
|
||||
let verifier = CertVerifier::new(&[ibm_crt.clone(), ibm_rev_crt.clone()], &[], &None, true);
|
||||
let verifier = CertVerifier::new(&[&ibm_crt, &ibm_rev_crt], &[], None, true);
|
||||
assert!(matches!(verifier, Err(Error::HkdVerify(ManyIbmSignKeys))));
|
||||
|
||||
// No CRL for each X509
|
||||
let verifier = CertVerifier::new(
|
||||
&[inter_crt.clone(), ibm_crt.clone()],
|
||||
&[inter_crl.clone()],
|
||||
&Some(root_crt),
|
||||
&[&inter_crt, &ibm_crt],
|
||||
&[&inter_crl],
|
||||
Some(&root_crt),
|
||||
false,
|
||||
);
|
||||
verify_sign_error(3, verifier.unwrap_err());
|
||||
let verifier = CertVerifier::new(
|
||||
&[inter_crt.clone(), ibm_crt.clone()],
|
||||
&[],
|
||||
&Some(root_chn_crt.clone()),
|
||||
false,
|
||||
);
|
||||
let verifier = CertVerifier::new(&[&inter_crt, &ibm_crt], &[], Some(&root_chn_crt), false);
|
||||
verify_sign_error(3, verifier.unwrap_err());
|
||||
|
||||
// Wrong intermediate (or ibm key)
|
||||
let verifier = CertVerifier::new(
|
||||
&[inter_fake_crt, ibm_crt.clone()],
|
||||
&[inter_fake_crl],
|
||||
&Some(root_chn_crt.clone()),
|
||||
&[&inter_fake_crt, &ibm_crt],
|
||||
&[&inter_fake_crl],
|
||||
Some(&root_chn_crt),
|
||||
true,
|
||||
);
|
||||
// Depending on the OpenSSL version different error codes can appear
|
||||
verify_sign_error_slice(&[20, 30], verifier.unwrap_err());
|
||||
|
||||
// Wrong root ca
|
||||
let verifier = CertVerifier::new(
|
||||
&[inter_crt.clone(), ibm_crt.clone()],
|
||||
&[inter_crl.clone()],
|
||||
&None,
|
||||
true,
|
||||
);
|
||||
let verifier = CertVerifier::new(&[&inter_crt, &ibm_crt], &[&inter_crl], None, true);
|
||||
verify_sign_error(20, verifier.unwrap_err());
|
||||
|
||||
// Correct signing key + intermediate cert
|
||||
let _verifier = CertVerifier::new(
|
||||
&[inter_crt.clone(), ibm_crt.clone()],
|
||||
&[inter_crl.clone()],
|
||||
&Some(root_chn_crt.clone()),
|
||||
&[&inter_crt, &ibm_crt],
|
||||
&[&inter_crl],
|
||||
Some(&root_chn_crt),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// No intermediate key
|
||||
let verifier = CertVerifier::new(&[ibm_crt], &[], &Some(root_chn_crt.clone()), false);
|
||||
let verifier = CertVerifier::new(&[&ibm_crt], &[], Some(&root_chn_crt), false);
|
||||
verify_sign_error(20, verifier.unwrap_err());
|
||||
|
||||
// IBM Sign outdated
|
||||
let verifier = CertVerifier::new(
|
||||
&[inter_crt.clone(), ibm_early_crt],
|
||||
&[inter_crl.clone()],
|
||||
&Some(root_chn_crt.clone()),
|
||||
&[&inter_crt, &ibm_early_crt],
|
||||
&[&inter_crl],
|
||||
Some(&root_chn_crt),
|
||||
false,
|
||||
);
|
||||
assert!(matches!(verifier, Err(Error::HkdVerify(NoIbmSignKey))));
|
||||
let verifier = CertVerifier::new(
|
||||
&[inter_crt.clone(), ibm_late_crt],
|
||||
&[inter_crl.clone()],
|
||||
&Some(root_chn_crt.clone()),
|
||||
&[&inter_crt, &ibm_late_crt],
|
||||
&[&inter_crl],
|
||||
Some(&root_chn_crt),
|
||||
false,
|
||||
);
|
||||
assert!(matches!(verifier, Err(Error::HkdVerify(NoIbmSignKey))));
|
||||
|
||||
// Revoked
|
||||
let verifier = CertVerifier::new(
|
||||
&[inter_crt, ibm_rev_crt],
|
||||
&[inter_crl],
|
||||
&Some(root_chn_crt),
|
||||
&[&inter_crt, &ibm_rev_crt],
|
||||
&[&inter_crl],
|
||||
Some(&root_chn_crt),
|
||||
false,
|
||||
);
|
||||
verify_sign_error(23, verifier.unwrap_err());
|
||||
|
||||
@@ -10,11 +10,10 @@ license.workspace = true
|
||||
libc = "0.2.49"
|
||||
log = { version = "0.4.6", features = ["std", "release_max_level_debug"] }
|
||||
thiserror = "1.0.33"
|
||||
utils = {path = "../utils"}
|
||||
zerocopy = {version = "0.7", features = ["derive"]}
|
||||
serde = { version = "1.0.139", features = ["derive"]}
|
||||
byteorder = "1.3"
|
||||
|
||||
[dev-dependencies]
|
||||
lazy_static = "1.4.0"
|
||||
serde_test = "1"
|
||||
serde_test = "1.0.139"
|
||||
lazy_static = "1.1"
|
||||
|
||||
@@ -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,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);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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.
|
||||
///
|
||||
|
||||
@@ -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<()> {
|
||||
|
||||
@@ -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<D: AsRef<[u8]>>(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>> {
|
||||
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<AddSecretRequest> {
|
||||
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<String>,
|
||||
verifier: Box<dyn HkdVerifier>,
|
||||
) -> Result<Vec<PKey<Public>>> {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<()> {
|
||||
|
||||
@@ -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<PKey<Public>> {
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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<Box<dyn crate::verify::HkdVerifier>> {
|
||||
use crate::verify::{CertVerifier, NoVerifyHkd};
|
||||
fn verifier(&self, protectee: &'static str) -> Result<Box<dyn HkdVerifier>> {
|
||||
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::<Vec<_>>(),
|
||||
&self.crls.iter().map(Path::new).collect::<Vec<_>>(),
|
||||
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<Vec<PKey<Public>>> {
|
||||
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<Box<dyn Read>> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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: &E, verbosity: u8) -> ExitCode
|
||||
where
|
||||
// Error trait is not required, but here to limit the usage to errors
|
||||
E: AsRef<dyn std::error::Error> + 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;
|
||||
@@ -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.
|
||||
|
||||
@@ -44,5 +44,6 @@ impl Log for PvLogger {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&self) {}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ impl TemporaryDirectory {
|
||||
/// 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();
|
||||
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());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user