1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//! # PBKDF2 helpers
//!
//! This module contains helpers for the PBKDF2 algorithm.

use crate::errors;
use core::num;
use ring::pbkdf2;

/// Cryptographically create a symmetric key from a secret value.
///
/// Create a symmetric key from a secret value, based on various PBKDF2
/// parameters; an HMAC function, a salt and a number of iterations.
///
/// This method returns an error if the parameters are too weak, e.g., a SHA-1
/// digest function, zero number of iterations or no salt. Also, it returns an
/// error if the user has not provided a buffer for the key or a secret value.
///
/// ## Examples
///
/// A safe method to derive a key with PBKDF2 is the following:
///
/// ```
/// use tindercrypt::pbkdf2::derive_key;
/// use tindercrypt::rand::fill_buf;
/// use ring::pbkdf2;
///
/// let digest_algo = pbkdf2::PBKDF2_HMAC_SHA256;
/// let iterations = 100000;
/// let mut salt = [0u8; 32];
/// let secret = "My secret password".as_bytes();
/// let mut key = [0u8; 32];
///
/// fill_buf(&mut salt);
/// derive_key(digest_algo, iterations, &salt, &secret, &mut key);
/// ```
pub fn derive_key(
    digest_algo: pbkdf2::Algorithm,
    iterations: usize,
    salt: &[u8],
    secret: &[u8],
    key: &mut [u8],
) -> Result<(), errors::Error> {
    if digest_algo == pbkdf2::PBKDF2_HMAC_SHA1
        || iterations < 1
        || salt.len() == 0
    {
        return Err(errors::Error::CryptoParamsWeak);
    }

    if secret.len() == 0 || key.len() == 0 {
        return Err(errors::Error::PassphraseTooSmall);
    }

    let iterations = num::NonZeroU32::new(iterations as u32).unwrap();
    pbkdf2::derive(digest_algo, iterations, salt, secret, key);
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_pbkd2_derive_key() {
        let mut salt = [9; 10];
        let mut secret = [99; 10];
        let mut key1 = [0u8; 32];
        let mut key2 = [0u8; 32];
        let mut key3 = [0u8; 32];
        let mut key4 = [0u8; 32];
        let mut res: Result<(), errors::Error>;
        let params_err = Err(errors::Error::CryptoParamsWeak);
        let size_err = Err(errors::Error::PassphraseTooSmall);

        // Check that weak parameters and empty buffers are reported as errors.
        res =
            derive_key(pbkdf2::PBKDF2_HMAC_SHA1, 1, &salt, &secret, &mut key1);
        assert_eq!(res, params_err);
        res = derive_key(
            pbkdf2::PBKDF2_HMAC_SHA256,
            0,
            &salt,
            &secret,
            &mut key1,
        );
        assert_eq!(res, params_err);
        res =
            derive_key(pbkdf2::PBKDF2_HMAC_SHA256, 1, &[], &secret, &mut key1);
        assert_eq!(res, params_err);
        res = derive_key(pbkdf2::PBKDF2_HMAC_SHA256, 1, &salt, &[], &mut key1);
        assert_eq!(res, size_err);
        res =
            derive_key(pbkdf2::PBKDF2_HMAC_SHA256, 1, &salt, &secret, &mut []);
        assert_eq!(res, size_err);

        // Check that key derivation works, and that changes in the salt and
        // secret produce different keys.
        res = derive_key(
            pbkdf2::PBKDF2_HMAC_SHA256,
            1,
            &salt,
            &secret,
            &mut key1,
        );
        assert!(res.is_ok());
        salt[0] = 0;
        res = derive_key(
            pbkdf2::PBKDF2_HMAC_SHA256,
            1,
            &salt,
            &secret,
            &mut key2,
        );
        assert!(res.is_ok());
        salt[0] = 9;
        secret[0] = 0;
        res = derive_key(
            pbkdf2::PBKDF2_HMAC_SHA256,
            1,
            &salt,
            &secret,
            &mut key3,
        );
        assert!(res.is_ok());
        secret[0] = 99;
        res = derive_key(
            pbkdf2::PBKDF2_HMAC_SHA256,
            1,
            &salt,
            &secret,
            &mut key4,
        );
        assert!(res.is_ok());

        assert_ne!(key1, key2);
        assert_ne!(key1, key3);
        assert_ne!(key2, key3);
        assert_eq!(key1, key4);
    }
}