rust/pv/test: Code + Certificate refactoring

* Get rid of Mockito
* create certs with AKID
* simplify things in the `create_certs.py` script

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
This commit is contained in:
Marc Hartmayer
2024-03-21 09:56:54 +00:00
committed by Steffen Eiden
parent 966e67a252
commit f6c6f0cc71
56 changed files with 1549 additions and 595 deletions
+12 -4
View File
@@ -7,8 +7,12 @@ use log::debug;
use openssl::stack::Stack;
use openssl::x509::store::X509Store;
use openssl::x509::{CrlStatus, X509Ref, X509StoreContext, X509};
use openssl_extensions::crl::StackableX509Crl;
use openssl_extensions::crl::X509StoreContextExtension;
use openssl_extensions::crl::{StackableX509Crl, X509StoreContextExtension, X509StoreExtension};
#[cfg(not(test))]
use helper::download_first_crl_from_x509;
#[cfg(test)]
use test::download_first_crl_from_x509;
use crate::error::bail_hkd_verify;
use crate::misc::{read_certs, read_file};
@@ -113,7 +117,7 @@ impl CertVerifier {
if !self.offline {
// Try to download a CRL if defined in the HKD
if let Some(crl) = helper::download_first_crl_from_x509(hkd)? {
if let Some(crl) = download_first_crl_from_x509(hkd)? {
crl.into_iter().try_for_each(|c| crls.push(c.into()))?;
}
}
@@ -143,7 +147,11 @@ impl CertVerifier {
for path in cert_paths {
let mut crt = read_certs(&read_file(path, "certificate")?)?;
if !offline {
helper::download_crls_into_store(&mut store, &crt)?;
for c in &crt {
if let Some(crl) = download_first_crl_from_x509(c)? {
crl.iter().try_for_each(|c| store.add_crl(c))?;
}
}
}
untr_certs.append(&mut crt);
}
+10 -41
View File
@@ -3,10 +3,8 @@
// Copyright IBM Corp. 2023
use crate::error::bail_hkd_verify;
use crate::misc::read_crls;
use crate::HkdVerifyErrorType::*;
use crate::{Error, Result};
use curl::easy::{Easy2, Handler, WriteError};
use log::debug;
use openssl::{
asn1::{Asn1Time, Asn1TimeRef},
@@ -15,17 +13,14 @@ use openssl::{
ssl::SslFiletype,
stack::{Stack, Stackable},
x509::{
store::{File, X509Lookup, X509StoreBuilder, X509StoreBuilderRef, X509StoreRef},
store::{File, X509Lookup, X509StoreBuilder, X509StoreRef},
verify::{X509VerifyFlags, X509VerifyParam},
X509Crl, X509CrlRef, X509Name, X509NameRef, X509PurposeId, X509Ref, X509StoreContext,
X509CrlRef, X509Name, X509NameRef, X509PurposeId, X509Ref, X509StoreContext,
X509StoreContextRef, X509VerifyResult, X509,
},
};
use openssl_extensions::{
akid::{AkidCheckResult, AkidExtension},
crl::X509StoreExtension,
};
use std::{cmp::Ordering, ffi::c_int, time::Duration, usize};
use openssl_extensions::akid::{AkidCheckResult, AkidExtension};
use std::{cmp::Ordering, ffi::c_int, usize};
/// 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
@@ -192,17 +187,6 @@ pub fn extract_ibm_sign_key(certs: Vec<X509>) -> Result<(X509, Stack<X509>)> {
Ok((ibm_z_sign_key, chain))
}
/// for all certs load the first CRL specified into our store
pub fn download_crls_into_store(store: &mut X509StoreBuilderRef, crts: &[X509]) -> Result<()> {
for crt in crts {
debug!("Download crls for {crt:?}");
if let Some(crl) = download_first_crl_from_x509(crt)? {
crl.iter().try_for_each(|c| store.add_crl(c))?;
}
}
Ok(())
}
// Name Entry values of an IBM Z key signing cert
//Asn1StringRef::as_slice aka ASN1_STRING_get0_data gives a string without \0 delimiter
const IBM_Z_COMMON_NAME: &[u8; 43usize] = b"International Business Machines Corporation";
@@ -319,14 +303,17 @@ pub fn x509_dist_points(cert: &X509Ref) -> Vec<String> {
res
}
const CRL_TIMEOUT_MAX: Duration = Duration::from_secs(3);
/// Searches for CRL Distribution points and downloads the CRL. Stops after the first successful
/// download.
///
/// Error if sth bad(=unexpected) happens (not bad: crl not available at link, unexpected format)
/// Other issues are mapped to Ok(None)
pub fn download_first_crl_from_x509(cert: &X509Ref) -> Result<Option<Vec<X509Crl>>> {
#[cfg(not(test))]
pub fn download_first_crl_from_x509(cert: &X509Ref) -> Result<Option<Vec<openssl::x509::X509Crl>>> {
use crate::misc::read_crls;
use curl::easy::{Easy2, Handler, WriteError};
use std::time::Duration;
const CRL_TIMEOUT_MAX: Duration = Duration::from_secs(3);
struct Buf(Vec<u8>);
impl Handler for Buf {
@@ -420,8 +407,6 @@ pub fn stack_err_hlp<T: Stackable>(
/// tests for some private functions
mod test {
use openssl_extensions::x509_crl_eq;
use super::*;
use crate::test_utils::*;
use std::time::{Duration, SystemTime};
@@ -497,20 +482,4 @@ mod test {
));
assert!(super::get_ibm_z_sign_key(&[ibm_crt, no_sign_crt]).is_ok(),);
}
#[test]
fn download_first_crl_from_x509() {
let ibm_crt = load_gen_cert("ibm.crt");
let inter_crl = load_gen_crl("inter_ca.crl");
let _m_inter = super::super::test::mock_endpt("inter_ca.crl");
let crl_d = super::download_first_crl_from_x509(&ibm_crt)
.unwrap()
.unwrap();
assert_eq!(crl_d.len(), 1);
assert!(x509_crl_eq(
crl_d.first().unwrap().as_ref(),
inter_crl.as_ref()
));
}
}
+46 -90
View File
@@ -5,32 +5,30 @@
#![cfg(test)]
use super::{helper, helper::*, *};
use crate::{Error, HkdVerifyErrorType::*};
use core::slice;
use once_cell::sync::OnceCell;
use openssl::stack::Stack;
use std::sync::Mutex;
use crate::{misc::read_crls, Error, HkdVerifyErrorType::*};
use openssl::{stack::Stack, x509::X509Crl};
use std::path::Path;
use crate::test_utils::*;
pub fn mock_endpt(res: &str) -> mockito::Mock {
static MOCK_SERVER: OnceCell<Mutex<mockito::Server>> = OnceCell::new();
//mock function
pub fn download_first_crl_from_x509(cert: &X509Ref) -> Result<Option<Vec<X509Crl>>> {
fn mock_download<P: AsRef<Path>>(path: P) -> Result<Vec<X509Crl>> {
read_crls(&std::fs::read(path)?)
}
let res_path = get_cert_asset_path(res);
MOCK_SERVER
.get_or_init(|| mockito::Server::new_with_port(1234).into())
.lock()
.expect("COULD NOT GET THE MOCK_SERVER LOCK")
.mock("GET", format!("/crl/{res}").as_str())
.with_header("content-type", "application/pkix-crl")
.with_body_from_file(res_path)
.create()
}
#[test]
fn mockito_server_available() {
let _mock = mock_endpt("ibm.crt");
for dist_point in x509_dist_points(cert) {
{
let path = get_cert_asset_path(&dist_point);
let crls = if let Ok(buf) = mock_download(&path) {
buf
} else {
continue;
};
return Ok(Some(crls));
}
}
Ok(None)
}
#[test]
@@ -44,22 +42,12 @@ fn store_setup() {
#[test]
fn verify_chain_online() {
let ibm_crt = load_gen_cert("ibm.crt");
let inter_crt = load_gen_cert("inter_ca.crt");
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 mock_inter = mock_endpt("inter_ca.crl");
let mut store = helper::store_setup(&Some(root_crt), &[], &[]).unwrap();
download_crls_into_store(&mut store, slice::from_ref(&ibm_crt)).unwrap();
let store = store.build();
mock_inter.assert();
let mut sk = Stack::<X509>::new().unwrap();
sk.push(inter_crt).unwrap();
verify_chain(&store, &sk, &[ibm_crt.clone()]).unwrap();
assert!(verify_chain(&store, &sk, &[ibm_crt]).is_ok());
let ret = CertVerifier::new(&[ibm_crt, inter_crt], &[], &root_crt.into(), false);
assert!(ret.is_ok(), "CertVerifier::new failed: {ret:?}");
}
#[test]
@@ -79,33 +67,36 @@ fn verify_chain_offline() {
}
#[test]
fn verify_online() {
fn dist_points() {
let crt = load_gen_cert("ibm.crt");
let res = x509_dist_points(&crt);
let exp = vec!["inter_ca.crl"];
assert_eq!(res, exp);
}
fn verify(offline: bool, ibm_crt: &'static str, ibm_crl: &'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 ibm_crt = get_cert_asset_path_string("ibm.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 hkd_revoked = load_gen_cert("host_rev.crt");
let hkd_inv = load_gen_cert("host_invalid_signing_key.crt");
let hkd_exp = load_gen_cert("host_crt_expired.crt");
let hkd = load_gen_cert("host.crt");
let mock_inter = mock_endpt("inter_ca.crl");
let mock_ibm = mock_endpt("ibm.crl");
let inter_crl = get_cert_asset_path_string("inter_ca.crl");
let ibm_crl = get_cert_asset_path_string("ibm.crl");
let crls = &[ibm_crl, inter_crl];
let verifier = CertVerifier::new(
&[ibm_crt, inter_crt],
&[ibm_crl, inter_crl],
if offline { crls } else { &[] },
&Some(root_crt),
false,
offline,
)
.unwrap();
mock_inter.assert();
let res = verifier.verify(&hkd);
assert!(res.is_ok(), "Verify failed: res: {res:?}");
verifier.verify(&hkd).unwrap();
mock_ibm.assert();
assert!(matches!(
verifier.verify(&hkd_revoked),
Err(Error::HkdVerify(HdkRevoked))
@@ -122,47 +113,12 @@ fn verify_online() {
));
}
#[test]
fn verify_online() {
verify(false, "ibm.crt", "ibm.crl")
}
#[test]
fn verify_offline() {
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 hkd_revoked = load_gen_cert("host_rev.crt");
let hkd_inv = load_gen_cert("host_invalid_signing_key.crt");
let hkd_exp = load_gen_cert("host_crt_expired.crt");
let hkd = load_gen_cert("host.crt");
let verifier = CertVerifier::new(
&[ibm_crt, inter_crt],
&[ibm_crl, inter_crl],
&Some(root_crt),
true,
)
.unwrap();
verifier.verify(&hkd).unwrap();
assert!(matches!(
verifier.verify(&hkd_revoked),
Err(Error::HkdVerify(HdkRevoked))
));
assert!(matches!(
verifier.verify(&hkd_inv),
Err(Error::HkdVerify(IssuerMismatch))
));
assert!(matches!(
verifier.verify(&hkd_exp),
Err(Error::HkdVerify(AfterValidity))
));
}
#[test]
fn dist_points() {
let crt = load_gen_cert("ibm.crt");
let res = x509_dist_points(&crt);
let exp = vec!["http://127.0.0.1:1234/crl/inter_ca.crl"];
assert_eq!(res, exp);
verify(true, "ibm.crt", "ibm.crl")
}