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