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:
+15
-22
@@ -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,166 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2023
|
||||
|
||||
use crate::misc::{create_file, open_file};
|
||||
use crate::Result;
|
||||
use clap::{ArgGroup, Args, ValueHint};
|
||||
use std::io::{Read, Write};
|
||||
|
||||
/// CLI Argument collection for handling certificates.
|
||||
#[derive(Args, Debug, PartialEq, Eq, Default)]
|
||||
#[command(
|
||||
group(ArgGroup::new("pv_verify").required(true).args(["no_verify", "certs"])),
|
||||
)]
|
||||
pub struct CertificateOptions {
|
||||
/// Use FILE as a host-key document.
|
||||
///
|
||||
/// Can be specified multiple times and must be used at least once.
|
||||
#[arg(
|
||||
short = 'k',
|
||||
long = "host-key-document",
|
||||
value_name = "FILE",
|
||||
required = true,
|
||||
value_hint = ValueHint::FilePath,
|
||||
use_value_delimiter = true,
|
||||
value_delimiter = ',',
|
||||
)]
|
||||
pub host_key_documents: Vec<String>,
|
||||
|
||||
/// Disable the host-key document verification.
|
||||
///
|
||||
/// Does not require the host-key documents to be valid.
|
||||
/// Do not use for a production request unless you verified the host-key document beforehand.
|
||||
#[arg(long)]
|
||||
pub no_verify: bool,
|
||||
|
||||
/// 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
|
||||
/// the intermediate CA certificate (signed by the root CA).
|
||||
#[arg(
|
||||
short= 'C',
|
||||
long = "cert",
|
||||
value_name = "FILE",
|
||||
alias("crt"),
|
||||
value_hint = ValueHint::FilePath,
|
||||
use_value_delimiter = true,
|
||||
value_delimiter = ',',
|
||||
)]
|
||||
pub certs: Vec<String>,
|
||||
|
||||
/// Use FILE as a certificate revocation list.
|
||||
///
|
||||
/// The list is used to check whether a certificate of the chain of
|
||||
/// trust is revoked. Specify this option multiple times to use multiple CRLs.
|
||||
#[arg(
|
||||
long = "crl",
|
||||
requires("certs"),
|
||||
value_name = "FILE",
|
||||
value_hint = ValueHint::FilePath,
|
||||
use_value_delimiter = true,
|
||||
value_delimiter = ',',
|
||||
)]
|
||||
pub crls: Vec<String>,
|
||||
|
||||
/// Make no attempt to download CRLs.
|
||||
#[arg(long, requires("certs"))]
|
||||
pub offline: bool,
|
||||
|
||||
/// Use FILE as the root-CA certificate for the verification.
|
||||
///
|
||||
/// If omitted, the system wide-root CAs installed on the system are used.
|
||||
/// Use this only if you trust the specified certificate.
|
||||
#[arg(long, requires("certs"))]
|
||||
pub root_ca: Option<String>,
|
||||
}
|
||||
|
||||
impl CertificateOptions {
|
||||
/// Returns the verifier of this [`CertificateOptions`] based on the given CLI options.
|
||||
///
|
||||
/// # 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};
|
||||
match self.no_verify {
|
||||
true => {
|
||||
log::warn!(
|
||||
"Host-key document verification is disabled. The secret may not be protected."
|
||||
);
|
||||
Ok(Box::new(NoVerifyHkd))
|
||||
}
|
||||
false => Ok(Box::new(CertVerifier::new(
|
||||
&self.certs,
|
||||
&self.crls,
|
||||
&self.root_ca,
|
||||
self.offline,
|
||||
)?)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// stdout
|
||||
pub const STDOUT: &str = "-";
|
||||
/// stdin
|
||||
pub const STDIN: &str = "-";
|
||||
|
||||
/// Converts an argument value into a Writer.
|
||||
pub fn get_writer_from_cli_file_arg(path: &str) -> Result<Box<dyn Write>> {
|
||||
if path == STDOUT {
|
||||
Ok(Box::new(std::io::stdout()))
|
||||
} else {
|
||||
Ok(Box::new(create_file(path)?))
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts an argument value into a Reader.
|
||||
pub fn get_reader_from_cli_file_arg(path: &str) -> Result<Box<dyn Read>> {
|
||||
if path == STDIN {
|
||||
Ok(Box::new(std::io::stdin()))
|
||||
} else {
|
||||
Ok(Box::new(open_file(path)?))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use clap::Parser;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn cli_args() {
|
||||
//Verify only that some arguments are optional, we do not want to test clap, only the
|
||||
//configuration
|
||||
let valid_args = [vec!["pgr", "-k", "hkd.crt", "--no-verify"], vec!["pgr", "-k", "hkd.crt", "--crt", "abc.crt"]];
|
||||
// Test for the minimal amount of flags to yield an invalid combination
|
||||
let invalid_args = [
|
||||
vec!["pgr", "-k", "hkd.crt"],
|
||||
vec!["pgr", "--no-verify", "--crt", "abc.crt"],
|
||||
vec!["pgr", "--no-verify", "--crt", "abc.crt", "--offline"],
|
||||
vec!["pgr", "--no-verify", "--crt", "abc.crt", "--crl", "abc.crl"],
|
||||
vec!["pgr", "--no-verify", "--crt", "abc.crt", "--root-ca", "root.crt"],
|
||||
vec!["pgr", "--offline"],
|
||||
vec!["pgr", "--crl", "abc.crl"],
|
||||
vec!["pgr", "--root-ca", "root.crt"],
|
||||
];
|
||||
#[derive(Parser, Debug)]
|
||||
struct TestParser {
|
||||
#[command(flatten)]
|
||||
pub verify_args: CertificateOptions,
|
||||
}
|
||||
|
||||
for arg in valid_args {
|
||||
let res = TestParser::try_parse_from(&arg);
|
||||
assert!(res.is_ok());
|
||||
}
|
||||
|
||||
for arg in invalid_args {
|
||||
let res = TestParser::try_parse_from(&arg);
|
||||
assert!(res.is_err());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
+32
-32
@@ -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],
|
||||
|
||||
+13
-4
@@ -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;
|
||||
|
||||
+22
-41
@@ -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::*;
|
||||
}
|
||||
|
||||
+15
-14
@@ -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
-31
@@ -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);
|
||||
|
||||
+20
-20
@@ -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!(
|
||||
|
||||
Reference in New Issue
Block a user