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