pv: Rewrite CRL download tests

Replace the existing mocks with a trait-based test infrastructure. This
allows testing download_first_crl_from_x509() functionally while
avoiding actual network access, resulting in more comprehensive and
realistic test coverage.

Assisted-by: IBM Bob:1.0.5
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Acked-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
Marc Hartmayer
2026-07-03 14:36:16 +02:00
committed by Jan Höppner
parent 2565a4ca99
commit 692af4058c
4 changed files with 186 additions and 27 deletions

View File

@@ -175,6 +175,8 @@ pub enum HkdVerifyErrorType {
NoCrlDP,
#[error("The IBM Z signing key could not be verified. Error occurred at level {1}")]
IbmSignInvalid(#[source] openssl::x509::X509VerifyResult, u32),
#[error("CRL download failed")]
CrlDownloadFailed,
}
macro_rules! bail_hkd_verify {

View File

@@ -5,15 +5,12 @@
use core::slice;
use std::path::Path;
#[cfg(not(test))]
use helper::download_first_crl_from_x509;
use log::{debug, trace};
use openssl::error::ErrorStack;
use openssl::stack::Stack;
use openssl::x509::store::X509Store;
use openssl::x509::{CrlStatus, X509NameRef, X509Ref, X509StoreContext, X509StoreContextRef, X509};
#[cfg(test)]
use test::download_first_crl_from_x509;
use crate::error::bail_hkd_verify;
use crate::misc::{read_certs, read_file};

View File

@@ -22,6 +22,8 @@ use openssl::x509::{
};
#[cfg(not(test))]
pub(crate) use prod_client::download_first_crl_from_x509;
#[cfg(test)]
pub(crate) use tests::download_first_crl_from_x509;
use crate::error::bail_hkd_verify;
use crate::openssl_extensions::{AkidCheckResult, AkidExtension};
@@ -545,4 +547,186 @@ mod tests {
));
assert!(super::get_ibm_z_sign_key(&[ibm_crt, no_sign_crt]).is_ok(),);
}
use std::collections::HashMap;
use openssl::bn::{BigNum, MsbOption};
use openssl::hash::MessageDigest;
use openssl::pkey::PKey;
use openssl::rsa::Rsa;
use openssl::x509::{X509Builder, X509Crl, X509Extension, X509NameBuilder};
use crate::test_utils::get_cert_asset_path;
/// Mock HTTP response for testing
pub struct MockResponse {
pub data: Vec<u8>,
pub should_fail: bool,
}
/// Mock HTTP client for testing
pub struct MockHttpClient {
url: String,
responses: HashMap<String, MockResponse>,
perform_count: usize,
}
impl MockHttpClient {
pub fn new(responses: HashMap<String, MockResponse>) -> Self {
Self {
url: String::new(),
responses,
perform_count: 0,
}
}
}
impl HttpClient for MockHttpClient {
fn url(&mut self, url: &str) -> Result<()> {
self.url = url.to_string();
Ok(())
}
fn get(&mut self, _enable: bool) -> Result<()> {
Ok(())
}
fn follow_location(&mut self, _enable: bool) -> Result<()> {
Ok(())
}
fn timeout(&mut self, _timeout: Duration) -> Result<()> {
Ok(())
}
fn useragent(&mut self, _agent: &str) -> Result<()> {
Ok(())
}
fn perform(&mut self) -> Result<()> {
self.perform_count += 1;
if let Some(response) = self.responses.get(&self.url) {
if response.should_fail {
bail_hkd_verify!(CrlDownloadFailed);
}
Ok(())
} else {
bail_hkd_verify!(CrlDownloadFailed);
}
}
fn get_ref(&self) -> &[u8] {
if let Some(response) = self.responses.get(&self.url) {
&response.data
} else {
&[]
}
}
}
/// Helper to create mock response with data
fn mock_response(data: Vec<u8>) -> MockResponse {
MockResponse {
data,
should_fail: false,
}
}
/// Helper to create mock failure response
fn mock_failure() -> MockResponse {
MockResponse {
data: vec![],
should_fail: true,
}
}
/// Helper to create test certificate with CRL distribution points
fn create_cert_with_crl_dps(crl_uris: &[&str]) -> X509 {
// Generate a key pair
let rsa = Rsa::generate(2048).unwrap();
let key_pair = PKey::from_rsa(rsa).unwrap();
let mut builder = X509Builder::new().unwrap();
builder.set_version(2).unwrap();
// Set serial number
let serial = {
let mut num = BigNum::new().unwrap();
num.rand(159, MsbOption::MAYBE_ZERO, false).unwrap();
num.to_asn1_integer().unwrap()
};
builder.set_serial_number(&serial).unwrap();
// Set subject name
let mut name_builder = X509NameBuilder::new().unwrap();
name_builder.append_entry_by_text("C", "US").unwrap();
name_builder
.append_entry_by_text("O", "Test Organization")
.unwrap();
name_builder
.append_entry_by_text("CN", "Test Certificate")
.unwrap();
let name = name_builder.build();
builder.set_subject_name(&name).unwrap();
builder.set_issuer_name(&name).unwrap();
builder.set_pubkey(&key_pair).unwrap();
// Set validity
builder
.set_not_before(&Asn1Time::days_from_now(0).unwrap())
.unwrap();
builder
.set_not_after(&Asn1Time::days_from_now(365).unwrap())
.unwrap();
// Add CRL Distribution Points if provided
if !crl_uris.is_empty() {
// Create a single extension with all URIs
let crl_dp_value = crl_uris
.iter()
.map(|uri| format!("URI:{}", uri))
.collect::<Vec<_>>()
.join(",");
#[allow(deprecated)]
let crl_ext =
X509Extension::new_nid(None, None, Nid::CRL_DISTRIBUTION_POINTS, &crl_dp_value)
.unwrap();
builder.append_extension(crl_ext).unwrap();
}
// Sign the certificate
builder.sign(&key_pair, MessageDigest::sha256()).unwrap();
builder.build()
}
/// Helper to test with mock client
fn download_with_mock(
cert: &X509Ref,
responses: HashMap<String, MockResponse>,
) -> Result<Option<Vec<X509Crl>>> {
download_first_crl_from_x509_impl(cert, MockHttpClient::new(responses))
}
// Mock function
pub(crate) fn download_first_crl_from_x509(cert: &X509Ref) -> Result<Option<Vec<X509Crl>>> {
use std::collections::HashMap;
let dist_points = x509_dist_points(cert);
// Build mock responses for each distribution point
let mut responses = HashMap::new();
for dist_point in dist_points {
// Treat distribution point as filename
let path = get_cert_asset_path(&dist_point);
if let Ok(crl_data) = std::fs::read(&path) {
responses.insert(dist_point, mock_response(crl_data));
}
// If file doesn't exist, skip this distribution point
}
download_with_mock(cert, responses)
}
}

View File

@@ -4,38 +4,14 @@
#![cfg(test)]
use std::path::Path;
use openssl::stack::Stack;
use openssl::x509::X509Crl;
use super::helper::*;
use super::{helper, *};
use crate::test_utils::*;
use crate::utils::read_crls;
use crate::Error;
use crate::HkdVerifyErrorType::*;
// 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)?)
}
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]
fn store_setup() {
let ibm_path = get_cert_asset_path("ibm.crt");