openssl/
ossl_param.rs

1//! OSSL_PARAM management for OpenSSL 3.*
2//!
3//! The OSSL_PARAM structure represents an array of generic
4//! attributes that can represent various
5//! properties in OpenSSL, including keys and operations.
6//!
7//! This is always represented as an array of OSSL_PARAM
8//! structures, terminated by an entry with a NULL key.
9//!
10//! For convenience, the OSSL_PARAM_BLD builder can be used to
11//! dynamically construct these structures.
12//!
13//! Note, that this module is available only in OpenSSL 3.* and
14//! only internally for this crate.
15
16use crate::error::ErrorStack;
17use crate::util;
18use crate::{cvt, cvt_p};
19use foreign_types::ForeignType;
20use libc::{c_uint, c_void};
21use openssl_macros::corresponds;
22use std::ffi::CStr;
23use std::marker::PhantomData;
24use std::ptr;
25
26foreign_type_and_impl_send_sync! {
27    // This is the singular type, but it is always allocated
28    // and used as an array of such types.
29    type CType = ffi::OSSL_PARAM;
30    // OSSL_PARMA_free correctly frees the entire array.
31    fn drop = ffi::OSSL_PARAM_free;
32
33    /// `OsslParamArray` constructed using `OsslParamBuilder`.
34    /// Internally this is a pointer to an array of the OSSL_PARAM
35    /// structures.
36    pub struct OsslParamArray;
37    /// Reference to `OsslParamArray`.
38    pub struct OsslParamArrayRef;
39}
40
41impl OsslParamArray {
42    /// Locates the individual `OSSL_PARAM` element representing an
43    /// octet string identified by the key in the `OsslParamArray`
44    /// array and returns a reference to it.
45    ///
46    /// Combines OSSL_PARAM_locate and OSSL_PARAM_get_octet_string.
47    #[corresponds(OSSL_PARAM_get_octet_string)]
48    #[allow(dead_code)] // TODO: remove when when used by ML-DSA / ML-KEM
49    pub(crate) fn locate_octet_string<'a>(&'a self, key: &CStr) -> Result<&'a [u8], ErrorStack> {
50        unsafe {
51            let param = cvt_p(ffi::OSSL_PARAM_locate(self.as_ptr(), key.as_ptr()))?;
52            let mut val: *const c_void = ptr::null_mut();
53            let mut val_len: usize = 0;
54            cvt(ffi::OSSL_PARAM_get_octet_string_ptr(
55                param,
56                &mut val,
57                &mut val_len,
58            ))?;
59            Ok(util::from_raw_parts(val as *const u8, val_len))
60        }
61    }
62}
63
64foreign_type_and_impl_send_sync! {
65    type CType = ffi::OSSL_PARAM_BLD;
66    fn drop = ffi::OSSL_PARAM_BLD_free;
67
68    /// Builder used to construct `OsslParamArray`.
69    pub struct OsslParamBuilderInternal;
70    /// Reference to `OsslParamBuilderInternal`.
71    pub struct OsslParamBuilderRefInternal;
72}
73
74/// Wrapper around the internal OsslParamBuilderInternal that adds lifetime management
75/// since the builder does not own the key and value data that is added to it.
76pub struct OsslParamBuilder<'a> {
77    builder: OsslParamBuilderInternal,
78    _marker: PhantomData<&'a ()>,
79}
80
81impl<'a> OsslParamBuilder<'a> {
82    /// Returns a builder for an OsslParamArray.
83    ///
84    /// The array is initially empty.
85    #[corresponds(OSSL_PARAM_BLD_new)]
86    #[cfg_attr(any(not(ossl320), osslconf = "OPENSSL_NO_ARGON2"), allow(dead_code))]
87    pub(crate) fn new() -> Result<OsslParamBuilder<'a>, ErrorStack> {
88        unsafe {
89            ffi::init();
90
91            cvt_p(ffi::OSSL_PARAM_BLD_new()).map(|builder| OsslParamBuilder {
92                builder: OsslParamBuilderInternal(builder),
93                _marker: PhantomData,
94            })
95        }
96    }
97
98    /// Constructs the `OsslParamArray` and clears this builder.
99    #[corresponds(OSSL_PARAM_BLD_to_param)]
100    #[cfg_attr(any(not(ossl320), osslconf = "OPENSSL_NO_ARGON2"), allow(dead_code))]
101    #[allow(clippy::wrong_self_convention)]
102    pub(crate) fn to_param(&'a mut self) -> Result<OsslParamArray, ErrorStack> {
103        unsafe {
104            let params = cvt_p(ffi::OSSL_PARAM_BLD_to_param(self.as_ptr()))?;
105            Ok(OsslParamArray::from_ptr(params))
106        }
107    }
108
109    /// Adds a octet string to `OsslParamBuilder`.
110    #[corresponds(OSSL_PARAM_BLD_push_octet_string)]
111    #[cfg_attr(any(not(ossl320), osslconf = "OPENSSL_NO_ARGON2"), allow(dead_code))]
112    pub(crate) fn add_octet_string(
113        &mut self,
114        key: &'a CStr,
115        buf: &'a [u8],
116    ) -> Result<(), ErrorStack> {
117        unsafe {
118            cvt(ffi::OSSL_PARAM_BLD_push_octet_string(
119                self.as_ptr(),
120                key.as_ptr(),
121                buf.as_ptr() as *const c_void,
122                buf.len(),
123            ))
124            .map(|_| ())
125        }
126    }
127
128    /// Adds a unsigned int to `OsslParamBuilder`.
129    #[corresponds(OSSL_PARAM_BLD_push_uint)]
130    #[cfg_attr(any(not(ossl320), osslconf = "OPENSSL_NO_ARGON2"), allow(dead_code))]
131    pub(crate) fn add_uint(&mut self, key: &'a CStr, val: u32) -> Result<(), ErrorStack> {
132        unsafe {
133            cvt(ffi::OSSL_PARAM_BLD_push_uint(
134                self.as_ptr(),
135                key.as_ptr(),
136                val as c_uint,
137            ))
138            .map(|_| ())
139        }
140    }
141
142    /// Returns a raw pointer to the underlying `OSSL_PARAM_BLD` structure.
143    pub(crate) unsafe fn as_ptr(&mut self) -> *mut ffi::OSSL_PARAM_BLD {
144        self.builder.as_ptr()
145    }
146}
147
148#[cfg(test)]
149mod tests {
150    use super::*;
151    #[test]
152    fn test_builder_locate_octet_string() {
153        let mut builder = OsslParamBuilder::new().unwrap();
154        builder
155            .add_octet_string(CStr::from_bytes_with_nul(b"key1\0").unwrap(), b"value1")
156            .unwrap();
157        let params = builder.to_param().unwrap();
158
159        assert!(params
160            .locate_octet_string(CStr::from_bytes_with_nul(b"invalid\0").unwrap())
161            .is_err());
162        assert_eq!(
163            params
164                .locate_octet_string(CStr::from_bytes_with_nul(b"key1\0").unwrap())
165                .unwrap(),
166            b"value1"
167        );
168    }
169}