qubip_aurora 0.11.0

A framework to build OpenSSL Providers tailored for the transition to post-quantum cryptography
Documentation
#![deny(unexpected_cfgs)]

pub type Error = anyhow::Error;

#[macro_use]
extern crate log;

#[macro_use]
mod helpers;
pub(crate) use helpers::{concat_cstr, handleResult, log_target, named};

pub(crate) mod adapters;
pub(crate) mod forge;

mod init;
mod query;
pub(crate) mod random;

pub(crate) mod asn_definitions;

#[cfg(test)]
pub(crate) mod tests;

use std::ffi::{CStr, CString};

use bindings::dispatch_table_entry;
use bindings::OSSL_PARAM;
use bindings::{
    OSSL_FUNC_provider_get_capabilities_fn, OSSL_FUNC_provider_get_params_fn,
    OSSL_FUNC_provider_gettable_params_fn, OSSL_FUNC_provider_query_operation_fn,
    OSSL_FUNC_provider_teardown_fn, OSSL_DISPATCH, OSSL_FUNC_PROVIDER_GETTABLE_PARAMS,
    OSSL_FUNC_PROVIDER_GET_CAPABILITIES, OSSL_FUNC_PROVIDER_GET_PARAMS,
    OSSL_FUNC_PROVIDER_QUERY_OPERATION, OSSL_FUNC_PROVIDER_TEARDOWN, OSSL_PROV_PARAM_BUILDINFO,
    OSSL_PROV_PARAM_NAME, OSSL_PROV_PARAM_VERSION,
};
use forge::{bindings, osslparams, upcalls};
use osslparams::{OSSLParam, OSSLParamData, Utf8PtrData, OSSL_PARAM_END};
use upcalls::traits::{CoreUpcaller, CoreUpcallerWithCoreHandle};
use upcalls::OSSL_CORE_HANDLE;
pub use upcalls::{CoreDispatch, CoreDispatchWithCoreHandle};

/// This is an abstract representation of one Provider instance.
/// Remember that a single provider module could be loaded multiple
/// times within the same process, either in the same OpenSSL libctx or
/// within different libctx's.
///
/// At the moment a single instance holds nothing of relevance, but in
/// the future all the context which is specific to an instance should
/// be encapsulated within it, so that different instances could have
/// different configurations, and their own separate state.
#[derive(Debug)]
pub struct ProviderInstance<'a> {
    core_handle: *const OSSL_CORE_HANDLE,
    core_dispatch: CoreDispatch<'a>,
    pub name: &'a str,
    pub version: &'a str,
    params: Vec<OSSLParam<'a>>,
    param_array_ptr: Option<*mut [OSSL_PARAM]>,
    pub(crate) adapters_ctx: adapters::FinalizedAdaptersHandle,
}

/// We implement the Drop trait to make it explicit when a provider
/// instance is dropped: this should only happen after `teardown()` has
/// been called.
impl<'a> Drop for ProviderInstance<'a> {
    #[named]
    fn drop(&mut self) {
        let tname = std::any::type_name_of_val(self);
        let name = self.name;
        trace!(
            target: log_target!(),
            "🗑️\tDropping {tname} named {name}",
        )
    }
}

pub static PROV_NAME: &str = env!("CARGO_PKG_NAME");
pub static PROV_VER: &str = env!("CARGO_PKG_VERSION");
pub static PROV_BUILDINFO: &str = env!("CARGO_GIT_DESCRIBE");

const PROPERTY_DEFINITION: &CStr = c"provider=aurora,x.author=QUBIP";

impl<'a> ProviderInstance<'a> {
    #[named]
    pub fn new(handle: *const OSSL_CORE_HANDLE, core_dispatch: CoreDispatch<'a>) -> Self {
        trace!(target: log_target!(), "Called");

        let upcaller: CoreDispatchWithCoreHandle<'a> = (core_dispatch, handle).into();

        #[cfg(not(test))]
        helpers::examine_core_parameters(&upcaller).expect("Error while examining core parameters");

        let adapters_ctx = { adapters::FinalizedAdaptersHandle::new(&upcaller) };

        let core_dispatch: CoreDispatch = upcaller.into();

        Self {
            core_handle: handle,
            core_dispatch,
            name: PROV_NAME,
            version: PROV_VER,
            param_array_ptr: None,
            params: vec![
                OSSLParam::Utf8Ptr(Utf8PtrData::new_null(OSSL_PROV_PARAM_NAME)),
                OSSLParam::Utf8Ptr(Utf8PtrData::new_null(OSSL_PROV_PARAM_VERSION)),
                OSSLParam::Utf8Ptr(Utf8PtrData::new_null(OSSL_PROV_PARAM_BUILDINFO)),
            ],
            adapters_ctx,
        }
    }

    /// Retrieve a heap allocated `OSSL_DISPATCH` table associated with this provider instance.
    pub fn get_provider_dispatch(&mut self) -> *const OSSL_DISPATCH {
        let ret = Box::new([
            dispatch_table_entry!(
                OSSL_FUNC_PROVIDER_TEARDOWN,
                OSSL_FUNC_provider_teardown_fn,
                crate::init::provider_teardown
            ),
            dispatch_table_entry!(
                OSSL_FUNC_PROVIDER_GETTABLE_PARAMS,
                OSSL_FUNC_provider_gettable_params_fn,
                crate::init::gettable_params
            ),
            dispatch_table_entry!(
                OSSL_FUNC_PROVIDER_GET_PARAMS,
                OSSL_FUNC_provider_get_params_fn,
                crate::init::get_params
            ),
            dispatch_table_entry!(
                OSSL_FUNC_PROVIDER_QUERY_OPERATION,
                OSSL_FUNC_provider_query_operation_fn,
                crate::query::query_operation
            ),
            dispatch_table_entry!(
                OSSL_FUNC_PROVIDER_GET_CAPABILITIES,
                OSSL_FUNC_provider_get_capabilities_fn,
                crate::query::get_capabilities
            ),
            OSSL_DISPATCH::END,
        ]);
        Box::into_raw(ret).cast()
    }

    fn get_params_array(&mut self) -> *const OSSL_PARAM {
        // This is kind of like a poor man's std::sync::Once
        let raw_ptr = match self.param_array_ptr {
            Some(raw_ptr) => raw_ptr,
            None => {
                let slice = self
                    .params
                    .iter_mut()
                    .map(|p| unsafe { *p.get_c_struct() })
                    .chain(std::iter::once(OSSL_PARAM_END))
                    .collect::<Vec<_>>()
                    .into_boxed_slice();
                let raw_ptr = Box::into_raw(slice);
                self.param_array_ptr = Some(raw_ptr);
                raw_ptr
            }
        };
        raw_ptr.cast()
    }

    pub fn c_prov_name(&self) -> &CStr {
        use std::sync::OnceLock;

        static CELL: OnceLock<CString> = OnceLock::new();

        let l = CELL.get_or_init(|| CString::new(self.name).expect("Error parsing self.name"));
        l.as_ref()
    }

    pub fn c_prov_version(&self) -> &CStr {
        use std::sync::OnceLock;

        static CELL: OnceLock<CString> = OnceLock::new();

        let l =
            CELL.get_or_init(|| CString::new(self.version).expect("Error parsing self.version"));
        l.as_ref()
    }

    pub fn c_prov_buildinfo(&self) -> &CStr {
        use std::sync::OnceLock;

        static CELL: OnceLock<CString> = OnceLock::new();

        let l = CELL.get_or_init(|| {
            CString::new(crate::PROV_BUILDINFO).expect("Error parsing cPROV_BUILDINFO")
        });
        l.as_ref()
    }
}

impl<'a> TryFrom<*mut core::ffi::c_void> for &mut ProviderInstance<'a> {
    type Error = Error;

    #[named]
    fn try_from(vctx: *mut core::ffi::c_void) -> Result<Self, Self::Error> {
        trace!(target: log_target!(), "Called for {}",
        "impl<'a> TryFrom<*mut core::ffi::c_void> for &mut ProviderInstance<'a>"
        );
        let provp = vctx as *mut ProviderInstance;
        if provp.is_null() {
            return Err(anyhow::anyhow!("vctx was null"));
        }
        Ok(unsafe { &mut *provp })
    }
}

impl<'a> TryFrom<*mut core::ffi::c_void> for &ProviderInstance<'a> {
    type Error = Error;

    #[named]
    fn try_from(vctx: *mut core::ffi::c_void) -> Result<Self, Self::Error> {
        trace!(target: log_target!(), "Called for {}", "impl<'a> TryFrom<*mut core::ffi::c_void> for &ProviderInstance<'a>");
        let r: &mut ProviderInstance<'a> = vctx.try_into()?;
        Ok(r)
    }
}

impl CoreUpcaller for ProviderInstance<'_> {
    fn fn_from_core_dispatch(&self, id: u32) -> Option<unsafe extern "C" fn()> {
        self.core_dispatch.fn_from_core_dispatch(id)
    }
}

impl CoreUpcallerWithCoreHandle for ProviderInstance<'_> {
    fn get_core_handle(&self) -> *const OSSL_CORE_HANDLE {
        self.core_handle
    }
}

pub mod traits {
    pub use super::upcalls::traits::{CoreUpcaller, CoreUpcallerWithCoreHandle};
}