diff options
Diffstat (limited to 'third_party/rust/nss/src/aes.rs')
-rw-r--r-- | third_party/rust/nss/src/aes.rs | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/third_party/rust/nss/src/aes.rs b/third_party/rust/nss/src/aes.rs new file mode 100644 index 0000000000..280c2a66f2 --- /dev/null +++ b/third_party/rust/nss/src/aes.rs @@ -0,0 +1,120 @@ +/* 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::{ + error::*, + pk11::sym_key::import_sym_key, + util::{ensure_nss_initialized, map_nss_secstatus, ScopedPtr}, +}; +use std::{ + convert::TryFrom, + mem, + os::raw::{c_uchar, c_uint}, +}; + +const AES_GCM_TAG_LENGTH: usize = 16; + +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum Operation { + Encrypt, + Decrypt, +} + +pub fn aes_gcm_crypt( + key: &[u8], + nonce: &[u8], + aad: &[u8], + data: &[u8], + operation: Operation, +) -> Result<Vec<u8>> { + let mut gcm_params = nss_sys::CK_GCM_PARAMS { + pIv: nonce.as_ptr() as nss_sys::CK_BYTE_PTR, + ulIvLen: nss_sys::CK_ULONG::try_from(nonce.len())?, + ulIvBits: nss_sys::CK_ULONG::try_from( + nonce + .len() + .checked_mul(8) + .ok_or_else(|| ErrorKind::InternalError)?, + )?, + pAAD: aad.as_ptr() as nss_sys::CK_BYTE_PTR, + ulAADLen: nss_sys::CK_ULONG::try_from(aad.len())?, + ulTagBits: nss_sys::CK_ULONG::try_from(AES_GCM_TAG_LENGTH * 8)?, + }; + let mut params = nss_sys::SECItem { + type_: nss_sys::SECItemType::siBuffer as u32, + data: &mut gcm_params as *mut _ as *mut c_uchar, + len: c_uint::try_from(mem::size_of::<nss_sys::CK_GCM_PARAMS>())?, + }; + common_crypt( + nss_sys::CKM_AES_GCM.into(), + key, + data, + AES_GCM_TAG_LENGTH, + &mut params, + operation, + ) +} + +pub fn aes_cbc_crypt( + key: &[u8], + nonce: &[u8], + data: &[u8], + operation: Operation, +) -> Result<Vec<u8>> { + let mut params = nss_sys::SECItem { + type_: nss_sys::SECItemType::siBuffer as u32, + data: nonce.as_ptr() as *mut c_uchar, + len: c_uint::try_from(nonce.len())?, + }; + common_crypt( + nss_sys::CKM_AES_CBC_PAD.into(), + key, + data, + usize::try_from(nss_sys::AES_BLOCK_SIZE)?, // CBC mode might pad the result. + &mut params, + operation, + ) +} + +pub fn 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>> { + ensure_nss_initialized(); + // Most of the following code is inspired by the Firefox WebCrypto implementation: + // https://searchfox.org/mozilla-central/rev/f46e2bf881d522a440b30cbf5cf8d76fc212eaf4/dom/crypto/WebCryptoTask.cpp#566 + // CKA_ENCRYPT always is fine. + let sym_key = import_sym_key(mech, nss_sys::CKA_ENCRYPT.into(), &key)?; + // Initialize the output buffer (enough space for padding / a full tag). + let result_max_len = data + .len() + .checked_add(extra_data_len) + .ok_or_else(|| ErrorKind::InternalError)?; + let mut out_len: c_uint = 0; + let mut out = vec![0u8; result_max_len]; + let result_max_len_uint = c_uint::try_from(result_max_len)?; + let data_len = c_uint::try_from(data.len())?; + let f = match operation { + Operation::Decrypt => nss_sys::PK11_Decrypt, + Operation::Encrypt => nss_sys::PK11_Encrypt, + }; + map_nss_secstatus(|| unsafe { + f( + sym_key.as_mut_ptr(), + mech, + params, + out.as_mut_ptr(), + &mut out_len, + result_max_len_uint, + data.as_ptr(), + data_len, + ) + })?; + out.truncate(usize::try_from(out_len)?); + Ok(out) +} |