use std::{ffi::CString, ptr};
mod ossl_store_ffi {
use std::os::raw::{c_char, c_int, c_void};
pub const OSSL_STORE_INFO_PKEY: c_int = 4;
pub enum OsslStoreCtx {}
pub enum OsslStoreInfo {}
extern "C" {
pub fn OSSL_STORE_open_ex(
uri: *const c_char,
libctx: *const c_void,
propq: *const c_char,
ui_method: *const c_void,
ui_data: *const c_void,
params: *const c_void,
post_cb: *const c_void,
post_cb_data: *const c_void,
) -> *mut OsslStoreCtx;
pub fn OSSL_STORE_eof(ctx: *mut OsslStoreCtx) -> c_int;
pub fn OSSL_STORE_error(ctx: *mut OsslStoreCtx) -> c_int;
pub fn OSSL_STORE_load(ctx: *mut OsslStoreCtx) -> *mut OsslStoreInfo;
pub fn OSSL_STORE_close(ctx: *mut OsslStoreCtx) -> c_int;
pub fn OSSL_STORE_INFO_get_type(info: *const OsslStoreInfo) -> c_int;
pub fn OSSL_STORE_INFO_get1_PKEY(info: *const OsslStoreInfo) -> *mut std::ffi::c_void;
pub fn OSSL_STORE_INFO_free(info: *mut OsslStoreInfo);
}
}
struct StoreGuard(*mut ossl_store_ffi::OsslStoreCtx);
impl Drop for StoreGuard {
fn drop(&mut self) {
unsafe { ossl_store_ffi::OSSL_STORE_close(self.0) };
}
}
fn load_pkcs11_pkey(
uri: &str,
) -> Result<native_ossl::pkey::Pkey<native_ossl::pkey::Private>, super::OpensslKeyError> {
use native_ossl::pkey::Pkey;
use ossl_store_ffi::*;
let uri_c = CString::new(uri).map_err(|e| super::OpensslKeyError(e.to_string()))?;
let store_ctx = unsafe {
OSSL_STORE_open_ex(
uri_c.as_ptr(),
ptr::null(), ptr::null(), ptr::null(), ptr::null(), ptr::null(), ptr::null(), ptr::null(), )
};
if store_ctx.is_null() {
return Err(super::OpensslKeyError(format!(
"OSSL_STORE_open_ex('{}') failed: {}",
uri,
native_ossl::error::ErrorStack::drain()
)));
}
let _guard = StoreGuard(store_ctx);
let mut found: Option<native_ossl::pkey::Pkey<native_ossl::pkey::Private>> = None;
while found.is_none() && unsafe { OSSL_STORE_eof(store_ctx) } == 0 {
let info = unsafe { OSSL_STORE_load(store_ctx) };
if info.is_null() {
if unsafe { OSSL_STORE_error(store_ctx) } != 0 {
return Err(super::OpensslKeyError(format!(
"OSSL_STORE_load('{}') failed: {}",
uri,
native_ossl::error::ErrorStack::drain()
)));
}
break;
}
let ty = unsafe { OSSL_STORE_INFO_get_type(info) };
if ty == OSSL_STORE_INFO_PKEY {
let raw = unsafe { OSSL_STORE_INFO_get1_PKEY(info) };
unsafe { OSSL_STORE_INFO_free(info) };
if !raw.is_null() {
found = Some(unsafe { Pkey::from_ptr(raw as *mut _) });
}
} else {
unsafe { OSSL_STORE_INFO_free(info) };
}
}
found.ok_or_else(|| {
super::OpensslKeyError(format!("no private key found at PKCS#11 URI '{}'", uri))
})
}
pub(crate) fn priv_load_from_pkcs11_uri(
uri: &str,
) -> Result<crate::crypto::BackendPrivateKey, super::OpensslKeyError> {
let pkey = load_pkcs11_pkey(uri)?;
let spki_der = pkey
.public_key_to_der()
.map_err(|e| super::OpensslKeyError(e.to_string()))?;
let pkcs11 = crate::pkcs11_uri::Pkcs11Uri::parse(uri)
.expect("URI passed OSSL_STORE_open_ex but failed pkcs11_uri::parse");
Ok(crate::crypto::BackendPrivateKey {
pkcs8_der: std::sync::OnceLock::new(),
spki_cache: Some(spki_der),
pkey: Some(pkey),
pkcs11: Some(pkcs11),
})
}