Skip to content

Commit 8b28a8e

Browse files
committed
System test OpenSSL integration
1 parent 44af02b commit 8b28a8e

3 files changed

Lines changed: 172 additions & 2 deletions

File tree

revoke-test/tests/api/openssl.rs

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
use core::ffi::{c_int, c_long, c_void};
2+
use core::{mem, ptr};
3+
use std::ffi::CString;
4+
5+
use base64::{Engine, prelude::BASE64_STANDARD};
6+
use openssl_sys::{
7+
OPENSSL_STACK, OPENSSL_sk_free, OPENSSL_sk_new_null, OPENSSL_sk_push, SSL, SSL_CTX,
8+
SSL_CTX_free, SSL_CTX_new, SSL_free, SSL_get_ex_data_X509_STORE_CTX_idx, SSL_new,
9+
TLS_client_method, X509_STORE_CTX, X509_STORE_CTX_free, X509_STORE_CTX_get_error,
10+
X509_STORE_CTX_new, X509_V_ERR_CERT_REVOKED, d2i_X509, stack_st_X509,
11+
};
12+
use rustls_pki_types::CertificateDer;
13+
use upki_ffi::{upki_config_new, upki_result};
14+
use upkiopenssl::{upki_openssl_set_config, upki_openssl_verify_callback};
15+
16+
use super::{TEST_CONFIG_PATH, TestResult};
17+
use revoke_test::CertificateDetail;
18+
19+
pub(super) fn openssl(detail: &CertificateDetail) -> TestResult {
20+
let mut chain = Chain::new();
21+
22+
for cert in [&detail.end_entity_cert]
23+
.into_iter()
24+
.chain(detail.intermediates.iter())
25+
.map(|c| {
26+
CertificateDer::from(
27+
BASE64_STANDARD
28+
.decode(c)
29+
.expect("cannot decode cert"),
30+
)
31+
})
32+
{
33+
chain.push(&cert);
34+
}
35+
36+
let mut config = ptr::null_mut();
37+
assert!(matches!(
38+
unsafe {
39+
upki_config_new(
40+
CString::new(TEST_CONFIG_PATH)
41+
.unwrap()
42+
.as_ptr(),
43+
&mut config,
44+
)
45+
},
46+
upki_result::UPKI_OK
47+
));
48+
49+
let ssl_ctx = SslCtx::new();
50+
unsafe { upki_openssl_set_config(ssl_ctx.0, config) };
51+
52+
let ssl = ssl_ctx.new_ssl();
53+
54+
let mut store_ctx = StoreCtx::new();
55+
store_ctx.attach_ssl(ssl.0);
56+
store_ctx.set_error_depth(0);
57+
store_ctx.set_verified_chain(chain);
58+
59+
let rc = unsafe { upki_openssl_verify_callback(1, store_ctx.0) };
60+
61+
drop(ssl); // extend lifetime over store_ctx
62+
63+
match rc {
64+
1 => TestResult::IncorrectlyNotRevoked,
65+
0 if store_ctx.error() == X509_V_ERR_CERT_REVOKED => TestResult::CorrectlyRevoked,
66+
_ => panic!(
67+
"upki_openssl_verify_callback failed with rc={rc:?} store_ctx.error={:?}",
68+
store_ctx.error()
69+
),
70+
}
71+
}
72+
73+
struct SslCtx(*mut SSL_CTX);
74+
75+
impl SslCtx {
76+
fn new() -> Self {
77+
Self(unsafe { SSL_CTX_new(TLS_client_method()) })
78+
}
79+
80+
fn new_ssl(&self) -> Ssl {
81+
Ssl(unsafe { SSL_new(self.0) })
82+
}
83+
}
84+
85+
impl Drop for SslCtx {
86+
fn drop(&mut self) {
87+
unsafe { SSL_CTX_free(self.0) };
88+
}
89+
}
90+
91+
struct Ssl(*mut SSL);
92+
93+
impl Drop for Ssl {
94+
fn drop(&mut self) {
95+
unsafe { SSL_free(self.0) };
96+
}
97+
}
98+
99+
struct StoreCtx(*mut X509_STORE_CTX);
100+
101+
impl StoreCtx {
102+
fn new() -> Self {
103+
Self(unsafe { X509_STORE_CTX_new() })
104+
}
105+
106+
fn error(&self) -> c_int {
107+
unsafe { X509_STORE_CTX_get_error(self.0) }
108+
}
109+
110+
fn set_error_depth(&mut self, depth: i32) {
111+
unsafe { X509_STORE_CTX_set_error_depth(self.0, depth) };
112+
}
113+
114+
fn set_verified_chain(&mut self, mut chain: Chain) {
115+
unsafe { X509_STORE_CTX_set0_verified_chain(self.0, chain.steal()) };
116+
}
117+
118+
fn attach_ssl(&mut self, ssl: *mut SSL) {
119+
unsafe {
120+
X509_STORE_CTX_set_ex_data(self.0, SSL_get_ex_data_X509_STORE_CTX_idx(), ssl.cast())
121+
};
122+
}
123+
}
124+
125+
impl Drop for StoreCtx {
126+
fn drop(&mut self) {
127+
unsafe { X509_STORE_CTX_free(self.0) }
128+
}
129+
}
130+
131+
struct Chain(*mut stack_st_X509);
132+
133+
impl Chain {
134+
fn new() -> Self {
135+
Self(unsafe { OPENSSL_sk_new_null() as *mut stack_st_X509 })
136+
}
137+
138+
fn push(&mut self, der_bytes: &[u8]) {
139+
unsafe {
140+
let mut ptr = der_bytes.as_ptr();
141+
let x509 = d2i_X509(ptr::null_mut(), &mut ptr, der_bytes.len() as c_long);
142+
assert!(!x509.is_null());
143+
OPENSSL_sk_push(self.0 as *mut OPENSSL_STACK, x509 as *mut c_void);
144+
}
145+
}
146+
147+
fn steal(&mut self) -> *mut stack_st_X509 {
148+
mem::replace(&mut self.0, ptr::null_mut())
149+
}
150+
}
151+
152+
impl Drop for Chain {
153+
fn drop(&mut self) {
154+
unsafe { OPENSSL_sk_free(self.0 as *mut OPENSSL_STACK) }
155+
}
156+
}
157+
158+
unsafe extern "C" {
159+
fn X509_STORE_CTX_set_error_depth(ctx: *mut X509_STORE_CTX, depth: c_int);
160+
fn X509_STORE_CTX_set0_verified_chain(ctx: *mut X509_STORE_CTX, chain: *mut stack_st_X509);
161+
fn X509_STORE_CTX_set_ex_data(ctx: *mut X509_STORE_CTX, idx: c_int, ptr: *mut c_void);
162+
}

revoke-test/tests/system_tests.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ use rustls_upki::{Policy, ServerVerifier};
1919

2020
#[path = "api/ffi.rs"]
2121
mod ffi;
22+
#[path = "api/openssl.rs"]
23+
mod openssl;
2224

2325
#[ignore]
2426
#[test]
@@ -50,6 +52,7 @@ fn real_world_system_tests() {
5052

5153
let high_level_cli = test_each_site(tests.sites.iter(), high_level_cli, "cli");
5254
let ffi = test_each_site(tests.sites.iter(), ffi::ffi, "ffi");
55+
let openssl = test_each_site(tests.sites.iter(), openssl::openssl, "openssl");
5356

5457
let verifier = ServerVerifier::new(
5558
Policy::default(),
@@ -63,12 +66,13 @@ fn real_world_system_tests() {
6366

6467
let rustls_results = test_each_site(tests.sites.iter(), verifier, "rustls");
6568

66-
for (((site, high), rustls), ffi) in tests
69+
for ((((site, high), rustls), ffi), openssl) in tests
6770
.sites
6871
.iter()
6972
.zip(high_level_cli.iter())
7073
.zip(rustls_results.iter())
7174
.zip(ffi.iter())
75+
.zip(openssl.iter())
7276
{
7377
assert!(
7478
high == rustls || *high == rustls.expired_as_revoked(),
@@ -78,6 +82,10 @@ fn real_world_system_tests() {
7882
high == ffi,
7983
"site {site:?} revocation result disagrees between high-level API ({high:?}) and FFI API ({ffi:?})"
8084
);
85+
assert!(
86+
high == openssl,
87+
"site {site:?} revocation result disagrees between high-level API ({high:?}) and OpenSSL API ({openssl:?})"
88+
);
8189
}
8290
}
8391

upki-openssl/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ repository.workspace = true
88

99
[lib]
1010
name = "upkiopenssl"
11-
crate-type = ["cdylib"]
11+
crate-type = ["cdylib", "lib"]
1212

1313
[dependencies]
1414
openssl-sys = { workspace = true }

0 commit comments

Comments
 (0)