openssl_kdf/
params.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4// See also the LICENSE file in the root of the crate for additional copyright
5// information.
6
7use crate::cvt_p;
8use crate::sys::OSSL_PARAM;
9use crate::sys::OSSL_PARAM_END;
10use crate::sys::OSSL_PARAM_construct_int;
11use crate::sys::OSSL_PARAM_construct_octet_string;
12use crate::sys::OSSL_PARAM_construct_utf8_string;
13use libc::c_char;
14use libc::c_int;
15use libc::c_void;
16use openssl::error::ErrorStack;
17use openssl_sys as ffi;
18use std::ffi::CStr;
19use std::fmt;
20use std::ptr;
21
22enum Param {
23    I32(*mut c_int),
24    String(*mut c_char, usize),
25    Vec(*mut c_void, usize),
26}
27
28impl Param {
29    fn alloc_i32(val: i32) -> Result<Param, ErrorStack> {
30        // SAFETY: Passing a non-zero size and immediately validating the return value.
31        let p = (unsafe {
32            cvt_p(ffi::CRYPTO_malloc(
33                size_of::<c_int>(),
34                concat!(file!(), "\0").as_ptr().cast(),
35                line!() as c_int,
36            ))
37        }?)
38        .cast::<c_int>();
39        // SAFETY: We have validated that p is non-null.
40        unsafe { *p = val };
41
42        Ok(Param::I32(p))
43    }
44
45    fn alloc_string(val: &CStr) -> Result<Param, ErrorStack> {
46        Ok(Param::String(
47            (alloc_slice_inner(val.to_bytes_with_nul())?).cast(),
48            val.to_bytes_with_nul().len(),
49        ))
50    }
51
52    fn alloc_vec(val: &[u8]) -> Result<Param, ErrorStack> {
53        Ok(Param::Vec(alloc_slice_inner(val)?, val.len()))
54    }
55}
56
57fn alloc_slice_inner(val: &[u8]) -> Result<*mut c_void, ErrorStack> {
58    Ok(if val.is_empty() {
59        ptr::null_mut()
60    } else {
61        // SAFETY: Passing a non-zero size and immediately validating the return value.
62        let p = unsafe {
63            cvt_p(ffi::CRYPTO_malloc(
64                val.len(),
65                concat!(file!(), "\0").as_ptr().cast(),
66                line!() as c_int,
67            ))
68        }?;
69        // SAFETY: We have validated that p is non-null, and it will be the same length as val.
70        unsafe { ptr::copy_nonoverlapping(val.as_ptr(), p.cast::<u8>(), val.len()) };
71        p
72    })
73}
74
75macro_rules! drop_param {
76    ($p:ident) => {{
77        ffi::CRYPTO_free(
78            $p.cast::<c_void>(),
79            concat!(file!(), "\0").as_ptr().cast(),
80            line!() as c_int,
81        );
82    }};
83}
84
85impl Drop for Param {
86    fn drop(&mut self) {
87        // SAFETY: Params are guaranteed to be allocated via CRYPTO_malloc and valid.
88        unsafe {
89            match *self {
90                Param::I32(p) => drop_param!(p),
91                Param::String(p, _) => drop_param!(p),
92                Param::Vec(p, _) => drop_param!(p),
93            }
94        }
95    }
96}
97
98pub struct ParamsBuilder(Vec<(&'static CStr, Param)>);
99
100impl ParamsBuilder {
101    pub fn with_capacity(capacity: usize) -> Self {
102        let params = Vec::with_capacity(capacity);
103        Self(params)
104    }
105
106    pub fn build(self) -> Params {
107        let len = self.0.len();
108
109        let mut params = Params {
110            fixed: self.0,
111            output: Vec::with_capacity(len + 1),
112        };
113
114        // Mapping each argument held in the builder, and mapping them to a new output Vec.
115        // This new output vec is to be consumed by a EVP_KDF_CTX_set_params or similar function
116        // the output vec references data held in the first vec.
117        // Data is allocated by the openssl allocator, so assumed in a memory stable realm.
118        // It's important the data does not move from the time we create the "output" slice and the
119        // moment it's read by the EVP_KDF_CTX_set_params functions.
120        for (name, p) in &mut params.fixed {
121            use Param::*;
122            // SAFETY: Name is guaranteed to be a valid C string, and the bufs are only constructed by alloc_slice_inner,
123            // which makes sure they are valid and have correct lengths.
124            let v = unsafe {
125                match p {
126                    I32(v) => OSSL_PARAM_construct_int(name.as_ptr(), *v),
127                    Vec(buf, len) => OSSL_PARAM_construct_octet_string(name.as_ptr(), *buf, *len),
128                    String(buf, len) => OSSL_PARAM_construct_utf8_string(name.as_ptr(), *buf, *len),
129                }
130            };
131            params.output.push(v);
132        }
133        params.output.push(OSSL_PARAM_END);
134        params
135    }
136}
137
138macro_rules! add_construct {
139    ($func:ident, $name:ident, $ty:ty) => {
140        impl ParamsBuilder {
141            pub fn $func(&mut self, key: &'static CStr, val: $ty) -> Result<(), ErrorStack> {
142                self.0.push((key, Param::$name(val)?));
143                Ok(())
144            }
145        }
146    };
147}
148
149add_construct!(add_i32, alloc_i32, i32);
150add_construct!(add_string, alloc_string, &CStr);
151add_construct!(add_slice, alloc_vec, &[u8]);
152// TODO(baloo): add u32, etc
153
154pub struct Params {
155    fixed: Vec<(&'static CStr, Param)>,
156    output: Vec<OSSL_PARAM>,
157}
158
159impl Params {
160    pub fn len(&self) -> usize {
161        self.output.len()
162    }
163
164    pub fn as_mut_ptr(&mut self) -> *mut OSSL_PARAM {
165        self.output.as_mut_ptr()
166    }
167
168    pub fn as_ptr(&mut self) -> *const OSSL_PARAM {
169        self.output.as_ptr()
170    }
171}
172
173impl fmt::Debug for Params {
174    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175        write!(f, "Params([")?;
176        for o in &self.output {
177            write!(f, "OSSL_PARAM {{")?;
178            if o.data_type != 0 {
179                // SAFETY: key is guaranteed by construction to be a valid c string.
180                write!(f, "name = {:?}, ", unsafe { CStr::from_ptr(o.key) })?;
181                write!(f, "buf = {:?}, ", o.data)?;
182                write!(f, "len = {:?}", o.data_size)?;
183            } else {
184                write!(f, "END")?;
185            }
186
187            write!(f, "}}, ")?;
188        }
189        write!(f, "])")
190    }
191}