nuts_container/
kdf.rs

1// MIT License
2//
3// Copyright (c) 2022-2024 Robin Doer
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to
7// deal in the Software without restriction, including without limitation the
8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9// sell copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in
13// all copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21// IN THE SOFTWARE.
22
23#[cfg(test)]
24mod tests;
25
26use log::{debug, trace};
27use openssl::error::ErrorStack;
28use openssl::pkcs5::pbkdf2_hmac;
29use std::fmt;
30use std::num::ParseIntError;
31use std::str::FromStr;
32use thiserror::Error;
33
34use crate::buffer::{Buffer, BufferError, BufferMut};
35use crate::digest::Digest;
36use crate::ossl;
37use crate::svec::SecureVec;
38
39/// [`Kdf`] related error codes.
40#[derive(Debug, Error)]
41pub enum KdfError {
42    /// An error in the OpenSSL library occured.
43    #[error(transparent)]
44    OpenSSL(#[from] ErrorStack),
45}
46
47/// Supported key derivation functions.
48///
49/// Defines data used to calculate a wrapping key.
50///
51/// The wrapping key is created used by an algorithm defined as a variant of
52/// this enum. The variants holds fields to customize the algorithm.
53///
54/// Based on a password provided by the user one of the algorithms are used to
55/// calculate a wrapping key. The wrapping key then is used for encryption of
56/// the secret in the header of the container.
57#[derive(Clone, PartialEq)]
58pub enum Kdf {
59    /// No key derivation
60    None,
61
62    /// PBKDF2
63    Pbkdf2 {
64        /// Digest used by PBKDF2.
65        digest: Digest,
66
67        /// Number of iterations used by PBKDF2.
68        iterations: u32,
69
70        /// A salt value used by PBKDF2.
71        salt: Vec<u8>,
72    },
73}
74
75impl Kdf {
76    /// Tests whether this is a [`None`](Self::None) kdf.
77    pub fn is_none(&self) -> bool {
78        match self {
79            Kdf::None => true,
80            Kdf::Pbkdf2 {
81                digest: _,
82                iterations: _,
83                salt: _,
84            } => false,
85        }
86    }
87
88    /// Tests whether this is a [`Pbkdf2`](Self::Pbkdf2) kdf.
89    pub fn is_pbkdf2(&self) -> bool {
90        match self {
91            Kdf::None => false,
92            Kdf::Pbkdf2 {
93                digest: _,
94                iterations: _,
95                salt: _,
96            } => true,
97        }
98    }
99
100    /// Creates a `Kdf` instance for the PBKDF2 algorithm.
101    ///
102    /// The `digest`, `iterations` and the `salt` values are used to customize
103    /// the PBKDF2 algorithm.
104    ///
105    /// # Examples
106    ///
107    /// ```rust
108    /// use nuts_container::*;
109    ///
110    /// let pbkdf2 = Kdf::pbkdf2(Digest::Sha1, 5, &[1, 2, 3]);
111    ///
112    /// match pbkdf2 {
113    ///     Kdf::Pbkdf2 {
114    ///         digest,
115    ///         iterations,
116    ///         salt,
117    ///     } => {
118    ///         assert_eq!(digest, Digest::Sha1);
119    ///         assert_eq!(iterations, 5);
120    ///         assert_eq!(salt, [1, 2, 3]);
121    ///     }
122    ///     _ => panic!("invalid kdf"),
123    /// }
124    /// ```
125    pub fn pbkdf2(digest: Digest, iterations: u32, salt: &[u8]) -> Kdf {
126        Kdf::Pbkdf2 {
127            digest,
128            iterations,
129            salt: salt.to_vec(),
130        }
131    }
132
133    /// Generates a `Kdf` instance for the PBKDF2 algorithm.
134    ///
135    /// The `digest`and `iterations` value is used to customize the PBKDF2
136    /// algorithm. For the [`salt`] `salt_len` bytes of random data are
137    /// generated.
138    ///
139    /// # Errors
140    ///
141    /// This method will return an [`Error::OpenSSL`] error if there was an
142    /// error generating the random data.
143    ///
144    /// # Examples
145    ///
146    /// ```rust
147    /// use nuts_container::*;
148    ///
149    /// let kdf = Kdf::generate_pbkdf2(Digest::Sha1, 5, 3).unwrap();
150    ///
151    /// match kdf {
152    ///     Kdf::Pbkdf2 {
153    ///         digest,
154    ///         iterations,
155    ///         salt,
156    ///     } => {
157    ///         assert_eq!(digest, Digest::Sha1);
158    ///         assert_eq!(iterations, 5);
159    ///         assert_eq!(salt.len(), 3); // salt filled with random data
160    ///     }
161    ///     _ => panic!("invalid kdf"),
162    /// }
163    /// ```
164    ///
165    /// [`salt`]: #variant.Pbkdf2.field.salt
166    /// [`Error::OpenSSL`]: ../error/enum.Error.html#variant.OpenSSL
167    pub fn generate_pbkdf2(
168        digest: Digest,
169        iterations: u32,
170        salt_len: u32,
171    ) -> Result<Kdf, KdfError> {
172        let mut salt = vec![0; salt_len as usize];
173        ossl::rand_bytes(&mut salt)?;
174
175        Ok(Kdf::Pbkdf2 {
176            digest,
177            iterations,
178            salt,
179        })
180    }
181
182    fn create_key_internal(&self, password: &[u8]) -> Result<SecureVec, KdfError> {
183        match self {
184            Kdf::None => Ok(vec![].into()),
185            Kdf::Pbkdf2 {
186                digest,
187                iterations,
188                salt,
189            } => {
190                if password.is_empty() {
191                    panic!("invalid password, cannot be empty");
192                }
193
194                if salt.is_empty() {
195                    panic!("invalid salt, cannot be empty");
196                }
197
198                let md = digest.as_openssl();
199                let mut key = vec![0; digest.size()];
200
201                pbkdf2_hmac(password, salt, *iterations as usize, md, &mut key)?;
202
203                Ok(key.into())
204            }
205        }
206    }
207
208    pub(crate) fn create_key(
209        &self,
210        password: &[u8],
211        min_len: usize,
212    ) -> Result<SecureVec, KdfError> {
213        let mut key = self.create_key_internal(password)?;
214
215        // ignore min_len for None
216        while !self.is_none() && key.len() < min_len {
217            let xxx = self.create_key_internal(&key)?;
218            key.extend(xxx.as_ref());
219
220            trace!("create_key (step): len = {}", key.len());
221        }
222
223        debug!("create_key: min_len = {}, len = {}", min_len, key.len());
224
225        Ok(key)
226    }
227
228    pub(crate) fn get_from_buffer<T: Buffer>(buf: &mut T) -> Result<Kdf, BufferError> {
229        let b = buf.get_u32()?;
230
231        match b {
232            0 => Ok(Kdf::None),
233            1 => {
234                let digest = Digest::get_from_buffer(buf)?;
235                let iterations = buf.get_u32()?;
236                let salt = buf.get_vec::<8>()?;
237
238                Ok(Kdf::pbkdf2(digest, iterations, &salt))
239            }
240            _ => Err(BufferError::InvalidIndex("Kdf".to_string(), b)),
241        }
242    }
243
244    pub(crate) fn put_into_buffer<T: BufferMut>(&self, buf: &mut T) -> Result<(), BufferError> {
245        match self {
246            Kdf::None => buf.put_u32(0),
247            Kdf::Pbkdf2 {
248                digest,
249                iterations,
250                salt,
251            } => {
252                buf.put_u32(1)?;
253                digest.put_into_buffer(buf)?;
254                buf.put_u32(*iterations)?;
255                buf.put_vec::<8>(salt.as_slice())?;
256
257                Ok(())
258            }
259        }
260    }
261}
262
263impl fmt::Display for Kdf {
264    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
265        match self {
266            Kdf::None => fmt.write_str("none"),
267            Kdf::Pbkdf2 {
268                digest,
269                iterations,
270                salt,
271            } => {
272                write!(fmt, "pbkdf2:{}:{}:{}", digest, iterations, salt.len())
273            }
274        }
275    }
276}
277
278impl fmt::Debug for Kdf {
279    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
280        match self {
281            Kdf::None => fmt.debug_struct("None").finish(),
282            Kdf::Pbkdf2 {
283                digest,
284                iterations,
285                salt,
286            } => {
287                let salt = format!("<{} bytes>", salt.len());
288                fmt.debug_struct("Pbkdf2")
289                    .field("digest", &digest)
290                    .field("iterations", &iterations)
291                    .field("salt", &salt)
292                    .finish()
293            }
294        }
295    }
296}
297
298fn parse_none(v: &[&str]) -> Result<Kdf, ParseKdfNoneError> {
299    if v.is_empty() {
300        Ok(Kdf::None)
301    } else {
302        Err(ParseKdfNoneError::InvalidNumberOfArguments(v.len()))
303    }
304}
305
306fn parse_pbkdf2(v: &[&str]) -> Result<Kdf, ParseKdfPbkdf2Error> {
307    const DEFAULT_DIGEST: Digest = Digest::Sha256;
308    const DEFAULT_ITERATIONS: u32 = 65536;
309    const DEFAULT_SALT_LEN: u32 = 16;
310
311    if !v.is_empty() && v.len() != 3 {
312        return Err(ParseKdfPbkdf2Error::InvalidNumberOfArguments(v.len()));
313    }
314
315    let digest = if v.is_empty() || v[0].is_empty() {
316        DEFAULT_DIGEST
317    } else {
318        v[0].parse::<Digest>()
319            .map_err(|()| ParseKdfPbkdf2Error::InvalidDigest(v[0].to_string()))?
320    };
321
322    let iterations = if v.is_empty() || v[1].is_empty() {
323        DEFAULT_ITERATIONS
324    } else {
325        v[1].parse::<u32>()
326            .map_err(ParseKdfPbkdf2Error::InvalidIterations)?
327    };
328
329    let salt_len = if v.is_empty() || v[2].is_empty() {
330        DEFAULT_SALT_LEN
331    } else {
332        v[2].parse::<u32>()
333            .map_err(ParseKdfPbkdf2Error::InvalidSaltLen)?
334    };
335
336    Ok(Kdf::generate_pbkdf2(digest, iterations, salt_len)?)
337}
338
339#[derive(Debug, Error)]
340pub enum ParseKdfNoneError {
341    #[error("invalid number of arguments for the none-kdf, expected none but got {0}")]
342    InvalidNumberOfArguments(usize),
343}
344
345#[derive(Debug, Error)]
346pub enum ParseKdfPbkdf2Error {
347    #[error("invalid number of arguments for PBKDF2, got {0} but none or three are expected")]
348    InvalidNumberOfArguments(usize),
349
350    #[error("invalid digest: {0}")]
351    InvalidDigest(String),
352
353    #[error("invalid iterations: {0}")]
354    InvalidIterations(#[source] ParseIntError),
355
356    #[error("invalid salt length: {0}")]
357    InvalidSaltLen(#[source] ParseIntError),
358
359    #[error(transparent)]
360    Kdf(#[from] KdfError),
361}
362
363#[derive(Debug, Error)]
364pub enum ParseKdfError {
365    #[error(transparent)]
366    None(ParseKdfNoneError),
367
368    #[error(transparent)]
369    Pbkdf2(ParseKdfPbkdf2Error),
370
371    #[error("unknown kdf: {0}")]
372    Unknown(String),
373}
374
375impl FromStr for Kdf {
376    type Err = ParseKdfError;
377
378    fn from_str(s: &str) -> Result<Self, ParseKdfError> {
379        let v: Vec<&str> = s
380            .split(':')
381            .map(|s| s.trim_matches(char::is_whitespace))
382            .collect();
383
384        if v.is_empty() {
385            todo!()
386        }
387
388        match v[0] {
389            "none" => parse_none(&v[1..]).map_err(ParseKdfError::None),
390            "pbkdf2" => parse_pbkdf2(&v[1..]).map_err(ParseKdfError::Pbkdf2),
391            _ => Err(ParseKdfError::Unknown(v[0].to_string())),
392        }
393    }
394}