nss/
aes.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use crate::{
6    error::*,
7    pk11::sym_key::import_sym_key,
8    util::{assert_nss_initialized, map_nss_secstatus, ScopedPtr},
9};
10use std::{
11    mem,
12    os::raw::{c_uchar, c_uint},
13};
14
15const AES_GCM_TAG_LENGTH: usize = 16;
16
17#[derive(Debug, Copy, Clone)]
18pub enum Operation {
19    Encrypt,
20    Decrypt,
21}
22
23pub fn aes_gcm_crypt(
24    key: &[u8],
25    nonce: &[u8],
26    aad: &[u8],
27    data: &[u8],
28    operation: Operation,
29) -> Result<Vec<u8>> {
30    let mut gcm_params = nss_sys::CK_GCM_PARAMS {
31        pIv: nonce.as_ptr() as nss_sys::CK_BYTE_PTR,
32        ulIvLen: nss_sys::CK_ULONG::try_from(nonce.len())?,
33        ulIvBits: nss_sys::CK_ULONG::try_from(
34            nonce.len().checked_mul(8).ok_or(ErrorKind::InternalError)?,
35        )?,
36        pAAD: aad.as_ptr() as nss_sys::CK_BYTE_PTR,
37        ulAADLen: nss_sys::CK_ULONG::try_from(aad.len())?,
38        ulTagBits: nss_sys::CK_ULONG::try_from(AES_GCM_TAG_LENGTH * 8)?,
39    };
40    let mut params = nss_sys::SECItem {
41        type_: nss_sys::SECItemType::siBuffer as u32,
42        data: &mut gcm_params as *mut _ as *mut c_uchar,
43        len: c_uint::try_from(mem::size_of::<nss_sys::CK_GCM_PARAMS>())?,
44    };
45    common_crypt(
46        nss_sys::CKM_AES_GCM.into(),
47        key,
48        data,
49        AES_GCM_TAG_LENGTH,
50        &mut params,
51        operation,
52    )
53}
54
55pub fn aes_cbc_crypt(
56    key: &[u8],
57    nonce: &[u8],
58    data: &[u8],
59    operation: Operation,
60) -> Result<Vec<u8>> {
61    let mut params = nss_sys::SECItem {
62        type_: nss_sys::SECItemType::siBuffer as u32,
63        data: nonce.as_ptr() as *mut c_uchar,
64        len: c_uint::try_from(nonce.len())?,
65    };
66    common_crypt(
67        nss_sys::CKM_AES_CBC_PAD.into(),
68        key,
69        data,
70        usize::try_from(nss_sys::AES_BLOCK_SIZE)?, // CBC mode might pad the result.
71        &mut params,
72        operation,
73    )
74}
75
76pub fn common_crypt(
77    mech: nss_sys::CK_MECHANISM_TYPE,
78    key: &[u8],
79    data: &[u8],
80    extra_data_len: usize,
81    params: &mut nss_sys::SECItem,
82    operation: Operation,
83) -> Result<Vec<u8>> {
84    assert_nss_initialized();
85    // Most of the following code is inspired by the Firefox WebCrypto implementation:
86    // https://searchfox.org/mozilla-central/rev/f46e2bf881d522a440b30cbf5cf8d76fc212eaf4/dom/crypto/WebCryptoTask.cpp#566
87    // CKA_ENCRYPT always is fine.
88    let sym_key = import_sym_key(mech, nss_sys::CKA_ENCRYPT.into(), key)?;
89    // Initialize the output buffer (enough space for padding / a full tag).
90    let result_max_len = data
91        .len()
92        .checked_add(extra_data_len)
93        .ok_or(ErrorKind::InternalError)?;
94    let mut out_len: c_uint = 0;
95    let mut out = vec![0u8; result_max_len];
96    let result_max_len_uint = c_uint::try_from(result_max_len)?;
97    let data_len = c_uint::try_from(data.len())?;
98    let f = match operation {
99        Operation::Decrypt => nss_sys::PK11_Decrypt,
100        Operation::Encrypt => nss_sys::PK11_Encrypt,
101    };
102    map_nss_secstatus(|| unsafe {
103        f(
104            sym_key.as_mut_ptr(),
105            mech,
106            params,
107            out.as_mut_ptr(),
108            &mut out_len,
109            result_max_len_uint,
110            data.as_ptr(),
111            data_len,
112        )
113    })?;
114    out.truncate(usize::try_from(out_len)?);
115    Ok(out)
116}