use crate::error::ErrorStack;
use crate::util;
use crate::{cvt, cvt_p};
use foreign_types::ForeignType;
use libc::{c_char, c_int, c_uint, c_void};
use openssl_macros::corresponds;
use std::ffi::CStr;
use std::marker::PhantomData;
use std::ptr;
foreign_type_and_impl_send_sync! {
type CType = ffi::OSSL_PARAM;
fn drop = ffi::OSSL_PARAM_free;
pub struct OsslParamArray;
pub struct OsslParamArrayRef;
}
impl OsslParamArray {
#[corresponds(OSSL_PARAM_get_octet_string)]
#[allow(dead_code)] pub(crate) fn locate_octet_string<'a>(&'a self, key: &CStr) -> Result<&'a [u8], ErrorStack> {
unsafe {
let param = cvt_p(ffi::OSSL_PARAM_locate(self.as_ptr(), key.as_ptr()))?;
let mut val: *const c_void = ptr::null_mut();
let mut val_len: usize = 0;
cvt(ffi::OSSL_PARAM_get_octet_string_ptr(
param,
&mut val,
&mut val_len,
))?;
Ok(util::from_raw_parts(val as *const u8, val_len))
}
}
}
foreign_type_and_impl_send_sync! {
type CType = ffi::OSSL_PARAM_BLD;
fn drop = ffi::OSSL_PARAM_BLD_free;
pub struct OsslParamBuilderInternal;
pub struct OsslParamBuilderRefInternal;
}
pub struct OsslParamBuilder<'a> {
builder: OsslParamBuilderInternal,
_marker: PhantomData<&'a ()>,
}
impl<'a> OsslParamBuilder<'a> {
#[corresponds(OSSL_PARAM_BLD_new)]
#[cfg_attr(any(not(ossl320), osslconf = "OPENSSL_NO_ARGON2"), allow(dead_code))]
pub(crate) fn new() -> Result<OsslParamBuilder<'a>, ErrorStack> {
unsafe {
ffi::init();
cvt_p(ffi::OSSL_PARAM_BLD_new()).map(|builder| OsslParamBuilder {
builder: OsslParamBuilderInternal(builder),
_marker: PhantomData,
})
}
}
#[corresponds(OSSL_PARAM_BLD_to_param)]
#[cfg_attr(any(not(ossl320), osslconf = "OPENSSL_NO_ARGON2"), allow(dead_code))]
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_param(&'a mut self) -> Result<OsslParamArray, ErrorStack> {
unsafe {
let params = cvt_p(ffi::OSSL_PARAM_BLD_to_param(self.as_ptr()))?;
Ok(OsslParamArray::from_ptr(params))
}
}
#[corresponds(OSSL_PARAM_BLD_push_utf8_string)]
pub(crate) fn add_utf8_string(
&mut self,
key: &'a CStr,
buf: &'a [u8],
) -> Result<(), ErrorStack> {
unsafe {
cvt(ffi::OSSL_PARAM_BLD_push_utf8_string(
self.as_ptr(),
key.as_ptr(),
buf.as_ptr() as *const c_char,
buf.len(),
))
.map(|_| ())
}
}
#[corresponds(OSSL_PARAM_BLD_push_octet_string)]
#[cfg_attr(any(not(ossl320), osslconf = "OPENSSL_NO_ARGON2"), allow(dead_code))]
pub(crate) fn add_octet_string(
&mut self,
key: &'a CStr,
buf: &'a [u8],
) -> Result<(), ErrorStack> {
unsafe {
cvt(ffi::OSSL_PARAM_BLD_push_octet_string(
self.as_ptr(),
key.as_ptr(),
buf.as_ptr() as *const c_void,
buf.len(),
))
.map(|_| ())
}
}
#[corresponds(OSSL_PARAM_BLD_push_int)]
pub(crate) fn add_int(&mut self, key: &'a CStr, val: i32) -> Result<(), ErrorStack> {
unsafe {
cvt(ffi::OSSL_PARAM_BLD_push_int(
self.as_ptr(),
key.as_ptr(),
val as c_int,
))
.map(|_| ())
}
}
#[corresponds(OSSL_PARAM_BLD_push_uint)]
#[cfg_attr(any(not(ossl320), osslconf = "OPENSSL_NO_ARGON2"), allow(dead_code))]
pub(crate) fn add_uint(&mut self, key: &'a CStr, val: u32) -> Result<(), ErrorStack> {
unsafe {
cvt(ffi::OSSL_PARAM_BLD_push_uint(
self.as_ptr(),
key.as_ptr(),
val as c_uint,
))
.map(|_| ())
}
}
pub(crate) unsafe fn as_ptr(&mut self) -> *mut ffi::OSSL_PARAM_BLD {
self.builder.as_ptr()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_builder_locate_octet_string() {
let mut builder = OsslParamBuilder::new().unwrap();
builder
.add_octet_string(CStr::from_bytes_with_nul(b"key1\0").unwrap(), b"value1")
.unwrap();
let params = builder.to_param().unwrap();
assert!(params
.locate_octet_string(CStr::from_bytes_with_nul(b"invalid\0").unwrap())
.is_err());
assert_eq!(
params
.locate_octet_string(CStr::from_bytes_with_nul(b"key1\0").unwrap())
.unwrap(),
b"value1"
);
}
}