openssl_kdf/
kdf.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 super::sys::EVP_KDF;
8use super::sys::EVP_KDF_CTX;
9use super::sys::EVP_KDF_CTX_free;
10use super::sys::EVP_KDF_CTX_new;
11use super::sys::EVP_KDF_derive;
12use super::sys::EVP_KDF_fetch;
13use super::sys::EVP_MD_get0_name;
14use super::sys::OSSL_KDF_PARAM_DIGEST;
15use super::sys::OSSL_KDF_PARAM_INFO;
16use super::sys::OSSL_KDF_PARAM_KBKDF_USE_L;
17use super::sys::OSSL_KDF_PARAM_KBKDF_USE_SEPARATOR;
18use super::sys::OSSL_KDF_PARAM_KEY;
19use super::sys::OSSL_KDF_PARAM_MAC;
20use super::sys::OSSL_KDF_PARAM_MODE;
21use super::sys::OSSL_KDF_PARAM_SALT;
22use super::sys::OSSL_KDF_PARAM_SEED;
23use crate::cvt;
24use crate::cvt_cp;
25use crate::cvt_p;
26use crate::params::Params;
27use crate::params::ParamsBuilder;
28use crate::sys::EVP_KDF_free;
29use openssl::error::ErrorStack;
30use openssl::hash::MessageDigest;
31use std::ffi::CStr;
32use std::ffi::CString;
33use std::ffi::NulError;
34use std::ptr;
35use thiserror::Error;
36
37#[derive(Debug, Error)]
38pub enum KdfError {
39    #[error("null byte found in string")]
40    NulError(#[from] NulError),
41    #[error("no such KDF")]
42    NoSuchKdf,
43    #[error("openSSL error")]
44    Ssl(#[from] ErrorStack),
45}
46
47pub trait KdfParams {
48    fn kdf_name(&self) -> String;
49    fn to_params(&self) -> Result<Params, KdfError>;
50}
51
52#[derive(Debug, Copy, Clone, Eq, PartialEq)]
53pub enum Mode {
54    Counter,
55    Feedback,
56}
57
58const COUNTER: &CStr = c"counter";
59const FEEDBACK: &CStr = c"feedback";
60
61impl Mode {
62    fn to_param(self) -> &'static CStr {
63        use Mode::*;
64        match self {
65            Counter => COUNTER,
66            Feedback => FEEDBACK,
67        }
68    }
69}
70
71#[derive(Debug, Copy, Clone, Eq, PartialEq)]
72pub enum Mac {
73    Hmac,
74    Cmac,
75}
76
77const HMAC: &CStr = c"HMAC";
78const CMAC: &CStr = c"CMAC";
79
80impl Mac {
81    fn to_param(self) -> &'static CStr {
82        use Mac::*;
83        match self {
84            Hmac => HMAC,
85            Cmac => CMAC,
86        }
87    }
88}
89
90#[derive(Clone, PartialEq, Eq)]
91pub struct Kbkdf {
92    md: MessageDigest,
93    mode: Mode,
94    mac: Mac,
95    salt: Vec<u8>,
96    key: Vec<u8>,
97    context: Vec<u8>,
98    seed: Vec<u8>,
99    use_l: bool,
100    use_separator: bool,
101}
102
103impl Kbkdf {
104    pub fn new(md: MessageDigest, salt: Vec<u8>, key: Vec<u8>) -> Kbkdf {
105        let mode = Mode::Counter;
106        let mac = Mac::Hmac;
107        let use_l = true;
108        let use_separator = true;
109        let context = Vec::new();
110        let seed = Vec::new();
111
112        Kbkdf {
113            md,
114            salt,
115            key,
116            mode,
117            context,
118            seed,
119            mac,
120            use_l,
121            use_separator,
122        }
123    }
124
125    pub fn set_mode(&mut self, mode: Mode) {
126        self.mode = mode;
127    }
128
129    pub fn set_mac(&mut self, mac: Mac) {
130        self.mac = mac;
131    }
132
133    pub fn set_context(&mut self, context: Vec<u8>) {
134        self.context = context;
135    }
136
137    pub fn set_seed(&mut self, seed: Vec<u8>) {
138        self.seed = seed;
139    }
140
141    pub fn set_l(&mut self, l: bool) {
142        self.use_l = l;
143    }
144
145    pub fn set_separator(&mut self, separator: bool) {
146        self.use_separator = separator;
147    }
148}
149
150impl KdfParams for Kbkdf {
151    fn kdf_name(&self) -> String {
152        String::from("KBKDF")
153    }
154
155    fn to_params(&self) -> Result<Params, KdfError> {
156        let mut params = ParamsBuilder::with_capacity(8);
157        // SAFETY: MessageDigest is guaranteed to be valid, and we immediately validate the return value.
158        let md_name = unsafe { cvt_cp(EVP_MD_get0_name(self.md.as_ptr())) }?;
159        // SAFETY: md_name has been validated to be non-null, and OpenSSL guarantees that it is valid.
160        let md_name = unsafe { CStr::from_ptr(md_name) };
161
162        params.add_string(OSSL_KDF_PARAM_DIGEST, md_name)?;
163        params.add_string(OSSL_KDF_PARAM_MAC, self.mac.to_param())?;
164        params.add_string(OSSL_KDF_PARAM_MODE, self.mode.to_param())?;
165        params.add_slice(OSSL_KDF_PARAM_KEY, &self.key)?;
166        if !self.salt.is_empty() {
167            params.add_slice(OSSL_KDF_PARAM_SALT, &self.salt)?;
168        }
169        if !self.context.is_empty() {
170            params.add_slice(OSSL_KDF_PARAM_INFO, &self.context)?;
171        }
172        if !self.seed.is_empty() {
173            params.add_slice(OSSL_KDF_PARAM_SEED, &self.seed)?;
174        }
175        if self.use_l {
176            params.add_i32(OSSL_KDF_PARAM_KBKDF_USE_L, 1)?;
177        } else {
178            params.add_i32(OSSL_KDF_PARAM_KBKDF_USE_L, 0)?;
179        }
180
181        if self.use_separator {
182            params.add_i32(OSSL_KDF_PARAM_KBKDF_USE_SEPARATOR, 1)?;
183        } else {
184            params.add_i32(OSSL_KDF_PARAM_KBKDF_USE_SEPARATOR, 0)?;
185        }
186
187        Ok(params.build())
188    }
189}
190
191pub fn derive<P: KdfParams>(kdf_params: P, output: &mut [u8]) -> Result<(), KdfError> {
192    openssl_sys::init();
193
194    let name = kdf_params.kdf_name();
195    let name = CString::new(name.as_bytes())?;
196    let name = name.as_bytes_with_nul();
197
198    // SAFETY: Name is guaranteed to be valid, null is valid for the other two parameters, and we immediately validate the return value.
199    let kdf = unsafe {
200        let ptr = EVP_KDF_fetch(ptr::null_mut(), name.as_ptr().cast(), ptr::null());
201        if ptr.is_null() {
202            Err(KdfError::NoSuchKdf)
203        } else {
204            Ok(Kdf(ptr))
205        }
206    }?;
207
208    let mut ctx = KdfContext::new(kdf)?;
209    let mut params = kdf_params.to_params()?;
210
211    // TODO: Check EVP_KDF_CTX_get_kdf_size ?
212    // SAFETY: All parameters are guaranteed to be valid, and we immediately validate the return value.
213    unsafe {
214        cvt(EVP_KDF_derive(
215            ctx.as_mut_ptr(),
216            output.as_mut_ptr(),
217            output.len(),
218            params.as_ptr(),
219        ))?
220    };
221    drop(params);
222
223    Ok(())
224}
225
226// The pointer must be valid and allocated via EVP_KDF_fetch.
227struct Kdf(*mut EVP_KDF);
228
229impl Drop for Kdf {
230    fn drop(&mut self) {
231        // SAFETY: This type guarantees that the pointer is valid and allocated via EVP_KDF_fetch.
232        unsafe { EVP_KDF_free(self.0) };
233    }
234}
235
236struct KdfContext(*mut EVP_KDF_CTX);
237
238impl KdfContext {
239    fn new(kdf: Kdf) -> Result<Self, ErrorStack> {
240        // SAFETY: kdf is guaranteed to be valid, and we immediately validate the return value.
241        let ctx = unsafe { cvt_p(EVP_KDF_CTX_new(kdf.0))? };
242        Ok(KdfContext(ctx))
243    }
244}
245
246impl KdfContext {
247    fn as_mut_ptr(&mut self) -> *mut EVP_KDF_CTX {
248        self.0
249    }
250}
251
252impl Drop for KdfContext {
253    fn drop(&mut self) {
254        // SAFETY: This type guarantees that the pointer is valid and allocated via EVP_KDF_CTX_new.
255        unsafe { EVP_KDF_CTX_free(self.0) };
256    }
257}
258
259#[cfg(test)]
260mod tests {}