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
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use crate::util::{ensure_nss_initialized, map_nss_secstatus, sec_item_as_slice, ScopedPtr};
use crate::{
    error::*,
    pk11::{
        slot::get_internal_slot,
        types::{AlgorithmID, SymKey},
    },
};

// Expose for consumers to choose the hashing algorithm
// Currently only SHA256 supported
pub use crate::pk11::context::HashAlgorithm;
use nss_sys::SECOidTag;
use std::convert::TryFrom;

// ***** BASED ON THE FOLLOWING IMPLEMENTATION *****
// https://searchfox.org/mozilla-central/rev/8ccea36c4fb09412609fb738c722830d7098602b/dom/crypto/WebCryptoTask.cpp#2567

pub fn pbkdf2_key_derive(
    password: &[u8],
    salt: &[u8],
    iterations: u32,
    hash_algorithm: HashAlgorithm,
    out: &mut [u8],
) -> Result<()> {
    ensure_nss_initialized();
    let oid_tag = match hash_algorithm {
        HashAlgorithm::SHA256 => SECOidTag::SEC_OID_HMAC_SHA256 as u32,
        HashAlgorithm::SHA384 => SECOidTag::SEC_OID_HMAC_SHA384 as u32,
    };
    let mut sec_salt = nss_sys::SECItem {
        len: u32::try_from(salt.len())?,
        data: salt.as_ptr() as *mut u8,
        type_: 0,
    };
    let alg_id = unsafe {
        AlgorithmID::from_ptr(nss_sys::PK11_CreatePBEV2AlgorithmID(
            SECOidTag::SEC_OID_PKCS5_PBKDF2 as u32,
            SECOidTag::SEC_OID_HMAC_SHA1 as u32,
            oid_tag,
            i32::try_from(out.len())?,
            i32::try_from(iterations)?,
            &mut sec_salt as *mut nss_sys::SECItem,
        ))?
    };

    let slot = get_internal_slot()?;
    let mut sec_pw = nss_sys::SECItem {
        len: u32::try_from(password.len())?,
        data: password.as_ptr() as *mut u8,
        type_: 0,
    };
    let sym_key = unsafe {
        SymKey::from_ptr(nss_sys::PK11_PBEKeyGen(
            slot.as_mut_ptr(),
            alg_id.as_mut_ptr(),
            &mut sec_pw as *mut nss_sys::SECItem,
            nss_sys::PR_FALSE,
            std::ptr::null_mut(),
        ))?
    };
    map_nss_secstatus(|| unsafe { nss_sys::PK11_ExtractKeyValue(sym_key.as_mut_ptr()) })?;

    // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
    // just refers to a buffer managed by `sym_key` which we copy into `buf`
    let mut key_data = unsafe { *nss_sys::PK11_GetKeyData(sym_key.as_mut_ptr()) };
    let buf = unsafe { sec_item_as_slice(&mut key_data)? };
    // Stop panic in swap_with_slice by returning an error if the sizes mismatch
    if buf.len() != out.len() {
        return Err(ErrorKind::InternalError.into());
    }
    out.swap_with_slice(buf);
    Ok(())
}