/* 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> { 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::())?, }; 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> { 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> { 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) }