diff --git a/Cargo.lock b/Cargo.lock index 8ac099ab..f5683635 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1890,6 +1890,7 @@ dependencies = [ "tokio", "tokio-rustls", "upki", + "upki-ffi", "webpki-ccadb", "webpki-roots", "x509-parser", diff --git a/revoke-test/Cargo.toml b/revoke-test/Cargo.toml index 6d624f54..cb5eaaa3 100644 --- a/revoke-test/Cargo.toml +++ b/revoke-test/Cargo.toml @@ -26,6 +26,7 @@ insta-cmd.workspace = true rustls-pki-types.workspace = true rustls-upki.workspace = true upki = { path = "../upki" } +upki-ffi = { path = "../upki-ffi" } [features] __bench_codspeed = ["dep:codspeed-criterion-compat"] diff --git a/revoke-test/src/lib.rs b/revoke-test/src/lib.rs index 3478c8d9..91fbe5a3 100644 --- a/revoke-test/src/lib.rs +++ b/revoke-test/src/lib.rs @@ -94,6 +94,23 @@ impl CertificateDetail { scts, }) } + + pub fn end_entity_cert_der(&self) -> Result> { + Ok(CertificateDer::from( + BASE64_STANDARD + .decode(&self.end_entity_cert) + .map_err(|e| eyre::eyre!("cannot base64-decode certificate {e:?}"))?, + )) + } + + pub fn intermediates_der(&self) -> impl Iterator>> { + self.intermediates.iter().map(|pem| { + BASE64_STANDARD + .decode(pem) + .map_err(|e| eyre::eyre!("cannot base64-decode certificate {e:?}")) + .map(CertificateDer::from) + }) + } } fn parse_octet_string(data: &[u8]) -> Result<&[u8]> { diff --git a/revoke-test/tests/api/ffi.rs b/revoke-test/tests/api/ffi.rs new file mode 100644 index 00000000..447b0965 --- /dev/null +++ b/revoke-test/tests/api/ffi.rs @@ -0,0 +1,64 @@ +use core::ptr; +use std::ffi::CString; + +use revoke_test::CertificateDetail; +use rustls_pki_types::CertificateDer; +use upki_ffi::{ + upki_certificate_der, upki_check_revocation, upki_config, upki_config_free, upki_config_new, + upki_result, +}; + +use super::{TEST_CONFIG_PATH, TestResult}; + +pub(super) fn ffi(detail: &CertificateDetail) -> TestResult { + let certs = [detail.end_entity_cert_der().unwrap()] + .into_iter() + .chain( + detail + .intermediates_der() + .collect::>>>() + .unwrap(), + ) + .collect::>(); + + let mut cert_pointers = Vec::new(); + for c in &certs { + cert_pointers.push(upki_certificate_der { + data: c.as_ptr(), + len: c.len(), + }); + } + + let mut config = OwnedConfig(ptr::null_mut()); + assert!(matches!( + unsafe { + upki_config_new( + CString::new(TEST_CONFIG_PATH) + .unwrap() + .as_ptr(), + &mut config.0, + ) + }, + upki_result::UPKI_OK + )); + let rc = + unsafe { upki_check_revocation(config.0, cert_pointers.as_ptr(), cert_pointers.len()) }; + + drop(certs); // extend lifetime for benefit of cert_pointers + + match rc { + upki_result::UPKI_REVOCATION_REVOKED => TestResult::CorrectlyRevoked, + upki_result::UPKI_REVOCATION_NOT_COVERED | upki_result::UPKI_REVOCATION_NOT_REVOKED => { + TestResult::IncorrectlyNotRevoked + } + e => panic!("upki_check_revocation() failed with {:?}", e as usize), + } +} + +struct OwnedConfig(*mut upki_config); + +impl Drop for OwnedConfig { + fn drop(&mut self) { + unsafe { upki_config_free(self.0) }; + } +} diff --git a/revoke-test/tests/system_tests.rs b/revoke-test/tests/system_tests.rs index 413c5ebc..2d232503 100644 --- a/revoke-test/tests/system_tests.rs +++ b/revoke-test/tests/system_tests.rs @@ -9,14 +9,17 @@ use std::process::{Command, Stdio}; use std::sync::Arc; use std::time::SystemTime; -use base64::prelude::*; use insta_cmd::get_cargo_bin; use revoke_test::{CertificateDetail, RevocationTestSite, RevocationTestSites}; use rustls::client::danger::ServerCertVerifier; -use rustls::pki_types::{CertificateDer, ServerName, UnixTime}; +use rustls::pki_types::{ServerName, UnixTime}; use rustls::{CertificateError, Error, RootCertStore}; +use rustls_pki_types::CertificateDer; use rustls_upki::{Policy, ServerVerifier}; +#[path = "api/ffi.rs"] +mod ffi; + #[ignore] #[test] fn real_world_system_tests() { @@ -46,6 +49,7 @@ fn real_world_system_tests() { ); let high_level_cli = test_each_site(tests.sites.iter(), high_level_cli, "cli"); + let ffi = test_each_site(tests.sites.iter(), ffi::ffi, "ffi"); let verifier = ServerVerifier::new( Policy::default(), @@ -59,38 +63,34 @@ fn real_world_system_tests() { let rustls_results = test_each_site(tests.sites.iter(), verifier, "rustls"); - for ((site, high), rustls) in tests + for (((site, high), rustls), ffi) in tests .sites .iter() .zip(high_level_cli.iter()) .zip(rustls_results.iter()) + .zip(ffi.iter()) { assert!( high == rustls || *high == rustls.expired_as_revoked(), "site {site:?} revocation result disagrees between high-level API ({high:?}) and rustls verifier ({rustls:?})" ); + assert!( + high == ffi, + "site {site:?} revocation result disagrees between high-level API ({high:?}) and FFI API ({ffi:?})" + ); } } impl TestCase for ServerVerifier { fn run(&self, detail: &CertificateDetail, test: &RevocationTestSite) -> TestResult { // Decode certificates from base64 - let end_entity = CertificateDer::from( - BASE64_STANDARD - .decode(&detail.end_entity_cert) - .expect("cannot decode end_entity_cert"), - ); + let end_entity = detail + .end_entity_cert_der() + .expect("cannot decode end_entity_cert"); let intermediates = detail - .intermediates - .iter() - .map(|c| { - CertificateDer::from( - BASE64_STANDARD - .decode(c) - .expect("cannot decode issuer_cert"), - ) - }) - .collect::>(); + .intermediates_der() + .collect::>>>() + .expect("cannot decode issuer_cert"); let url = &test.test_website_revoked; let host = url diff --git a/upki-ffi/Cargo.toml b/upki-ffi/Cargo.toml index 2ed0fec2..6e42da04 100644 --- a/upki-ffi/Cargo.toml +++ b/upki-ffi/Cargo.toml @@ -8,9 +8,7 @@ license.workspace = true description = "C FFI bindings for upki" [lib] -name = "upki" -crate-type = ["cdylib"] -doc = false # Can't document this and `upki` because they have the same name +crate-type = ["cdylib", "lib"] [dependencies] rustls-pki-types.workspace = true diff --git a/upki-ffi/example/Makefile b/upki-ffi/example/Makefile index e7c1234b..5389a302 100644 --- a/upki-ffi/example/Makefile +++ b/upki-ffi/example/Makefile @@ -1,7 +1,7 @@ LIBDIR = ../../target/release upki-demo: upki-demo.c demo-certs.h - $(CC) -o $@ $< -I.. -L$(LIBDIR) -lupki -Wl,-rpath,$(LIBDIR) + $(CC) -o $@ $< -I.. -L$(LIBDIR) -lupki_ffi -Wl,-rpath,$(LIBDIR) clean: rm -f upki-demo