1use 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 let md_name = unsafe { cvt_cp(EVP_MD_get0_name(self.md.as_ptr())) }?;
159 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 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 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
226struct Kdf(*mut EVP_KDF);
228
229impl Drop for Kdf {
230 fn drop(&mut self) {
231 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 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 unsafe { EVP_KDF_CTX_free(self.0) };
256 }
257}
258
259#[cfg(test)]
260mod tests {}