use nss_sys::nspr::PR_TRUE;
use nss_sys::{SECItemStr, SECItemType};
use super::ffi::{
CKMechanismType, CkAesGcmParams, PK11ContextStr, PK11_CipherOp, PK11_CreateContextBySymKey,
PK11_CreateDigestContext, PK11_DestroyContext, PK11_DigestBegin, PK11_DigestFinal,
PK11_DigestOp, PK11_FreeSlot, PK11_FreeSymKey, PK11_GenerateRandom, PK11_GetInternalSlot,
PK11_ImportSymKey, CKA_DECRYPT, CKA_ENCRYPT, CKM_AES_CBC, CKM_AES_CBC_PAD, CKM_AES_GCM,
CKM_DES3_CBC, CKM_DES3_CBC_PAD, CKM_MD5_HMAC, CKM_SHA224_HMAC, CKM_SHA256_HMAC,
CKM_SHA384_HMAC, CKM_SHA512_HMAC, CKM_SHA_1_HMAC, PK11_ORIGIN_UNWRAP,
};
use super::{alg_name_to_hash_tag, ensure_nss_init};
use crate::crypto::{
constant_time_eq, BlockCipherProvider, DataHasher, ErasedDataHasher, ErasedHmacProvider,
ErasedStreamingHmacProvider, HmacProvider, HmacState, Pbkdf2Provider, PrivateKeyError,
SecureRandom, StreamingHmacProvider,
};
struct NssHashState {
ctx: Option<std::ptr::NonNull<PK11ContextStr>>,
output_len: usize,
}
unsafe impl Send for NssHashState {}
impl crate::crypto::HashState for NssHashState {
fn update(&mut self, data: &[u8]) {
let ctx = self
.ctx
.expect("NssHashState::update called after finalize")
.as_ptr();
let status = unsafe { PK11_DigestOp(ctx, data.as_ptr(), data.len() as std::ffi::c_uint) };
assert_eq!(
status,
nss_sys::SECStatus::SECSuccess,
"PK11_DigestOp failed"
);
}
fn finalize_boxed(mut self: Box<Self>) -> Vec<u8> {
let ctx = self
.ctx
.take()
.expect("NssHashState::finalize_boxed called after finalize")
.as_ptr();
let mut out = vec![0u8; self.output_len];
let mut out_len: std::ffi::c_uint = 0;
let status = unsafe {
PK11_DigestFinal(
ctx,
out.as_mut_ptr(),
&mut out_len,
self.output_len as std::ffi::c_uint,
)
};
unsafe { PK11_DestroyContext(ctx, PR_TRUE) };
assert_eq!(
status,
nss_sys::SECStatus::SECSuccess,
"PK11_DigestFinal failed"
);
out.truncate(out_len as usize);
out
}
}
impl Drop for NssHashState {
fn drop(&mut self) {
if let Some(ctx) = self.ctx.take() {
unsafe { PK11_DestroyContext(ctx.as_ptr(), PR_TRUE) };
}
}
}
impl crate::crypto::StreamingHasher for NssDataHasher {
type Error = NssDataHasherError;
fn new_hash(
&self,
algorithm: &str,
) -> Result<Box<dyn crate::crypto::HashState>, NssDataHasherError> {
if !ensure_nss_init() {
return Err(NssDataHasherError("NSS initialisation failed".into()));
}
let (nss_tag, output_len) = super::alg_name_to_hash_tag(algorithm).ok_or_else(|| {
NssDataHasherError(format!(
"unsupported hash algorithm: {algorithm} \
(accepted: md5, sha1, sha224, sha256, sha384, sha512)"
))
})?;
let raw_ctx = unsafe { PK11_CreateDigestContext(nss_tag) };
if raw_ctx.is_null() {
return Err(NssDataHasherError("PK11_CreateDigestContext failed".into()));
}
let status = unsafe { PK11_DigestBegin(raw_ctx) };
if status != nss_sys::SECStatus::SECSuccess {
unsafe { PK11_DestroyContext(raw_ctx, PR_TRUE) };
return Err(NssDataHasherError("PK11_DigestBegin failed".into()));
}
Ok(Box::new(NssHashState {
ctx: Some(std::ptr::NonNull::new(raw_ctx).unwrap()),
output_len,
}))
}
}
impl crate::crypto::ErasedStreamingHasher for NssDataHasher {
fn new_hash_erased(
&self,
algorithm: &str,
) -> Result<Box<dyn crate::crypto::HashState>, crate::crypto::PrivateKeyError> {
use crate::crypto::StreamingHasher as _;
self.new_hash(algorithm)
.map_err(crate::crypto::PrivateKeyError::new)
}
}
pub(crate) fn nss_streaming_hasher() -> Box<dyn crate::crypto::ErasedStreamingHasher> {
Box::new(NssDataHasher)
}
fn alg_name_to_hmac_mech(algorithm: &str) -> Option<(CKMechanismType, usize)> {
match algorithm {
"md5" => Some((CKM_MD5_HMAC, 16)),
"sha1" => Some((CKM_SHA_1_HMAC, 20)),
"sha224" => Some((CKM_SHA224_HMAC, 28)),
"sha256" => Some((CKM_SHA256_HMAC, 32)),
"sha384" => Some((CKM_SHA384_HMAC, 48)),
"sha512" => Some((CKM_SHA512_HMAC, 64)),
_ => None,
}
}
#[derive(Debug)]
pub struct NssDataHasherError(String);
impl std::fmt::Display for NssDataHasherError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
impl std::error::Error for NssDataHasherError {}
pub struct NssDataHasher;
impl DataHasher for NssDataHasher {
type Error = NssDataHasherError;
fn hash_data(&self, algorithm: &str, data: &[u8]) -> Result<Vec<u8>, NssDataHasherError> {
if !ensure_nss_init() {
return Err(NssDataHasherError("NSS initialisation failed".into()));
}
let (nss_tag, output_len) = alg_name_to_hash_tag(algorithm).ok_or_else(|| {
NssDataHasherError(format!(
"unsupported hash algorithm: {algorithm} \
(accepted: md5, sha1, sha224, sha256, sha384, sha512)"
))
})?;
let raw_ctx = unsafe { PK11_CreateDigestContext(nss_tag) };
if raw_ctx.is_null() {
return Err(NssDataHasherError(format!(
"PK11_CreateDigestContext failed for {algorithm}"
)));
}
let status = unsafe { PK11_DigestBegin(raw_ctx) };
if status != nss_sys::SECStatus::SECSuccess {
unsafe { PK11_DestroyContext(raw_ctx, PR_TRUE) };
return Err(NssDataHasherError("PK11_DigestBegin failed".into()));
}
if !data.is_empty() {
let status =
unsafe { PK11_DigestOp(raw_ctx, data.as_ptr(), data.len() as std::ffi::c_uint) };
if status != nss_sys::SECStatus::SECSuccess {
unsafe { PK11_DestroyContext(raw_ctx, PR_TRUE) };
return Err(NssDataHasherError("PK11_DigestOp failed".into()));
}
}
let mut out = vec![0u8; output_len];
let mut out_len: std::ffi::c_uint = 0;
let status = unsafe {
PK11_DigestFinal(
raw_ctx,
out.as_mut_ptr(),
&mut out_len,
output_len as std::ffi::c_uint,
)
};
unsafe { PK11_DestroyContext(raw_ctx, PR_TRUE) };
if status != nss_sys::SECStatus::SECSuccess {
return Err(NssDataHasherError("PK11_DigestFinal failed".into()));
}
out.truncate(out_len as usize);
Ok(out)
}
}
impl ErasedDataHasher for NssDataHasher {
fn hash_data_erased(&self, algorithm: &str, data: &[u8]) -> Result<Vec<u8>, PrivateKeyError> {
self.hash_data(algorithm, data)
.map_err(PrivateKeyError::new)
}
}
#[derive(Debug)]
pub struct NssHmacError(String);
impl std::fmt::Display for NssHmacError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
impl std::error::Error for NssHmacError {}
pub struct NssHmacProvider;
fn hmac_block_size_for_alg(algorithm: &str) -> Option<usize> {
match algorithm {
"md5" | "sha1" | "sha224" | "sha256" => Some(64),
"sha384" | "sha512" => Some(128),
_ => None,
}
}
fn nss_hmac_compute_rust(
algorithm: &str,
block_size: usize,
key: &[u8],
data: &[u8],
) -> Result<Vec<u8>, NssHmacError> {
let hasher = NssDataHasher;
let key_hashed;
let key = if key.len() > block_size {
key_hashed = hasher
.hash_data(algorithm, key)
.map_err(|e| NssHmacError(e.to_string()))?;
&key_hashed[..]
} else {
key
};
let mut k = vec![0u8; block_size];
k[..key.len()].copy_from_slice(key);
let mut inner: Vec<u8> = Vec::with_capacity(block_size + data.len());
for &b in &k {
inner.push(b ^ 0x36);
}
inner.extend_from_slice(data);
let inner_hash = hasher
.hash_data(algorithm, &inner)
.map_err(|e| NssHmacError(e.to_string()))?;
let mut outer: Vec<u8> = Vec::with_capacity(block_size + inner_hash.len());
for &b in &k {
outer.push(b ^ 0x5c);
}
outer.extend_from_slice(&inner_hash);
hasher
.hash_data(algorithm, &outer)
.map_err(|e| NssHmacError(e.to_string()))
}
impl HmacProvider for NssHmacProvider {
type Error = NssHmacError;
fn hmac_compute(
&self,
algorithm: &str,
key: &[u8],
data: &[u8],
) -> Result<Vec<u8>, NssHmacError> {
let block_size = hmac_block_size_for_alg(algorithm).ok_or_else(|| {
NssHmacError(format!(
"unsupported HMAC algorithm: {algorithm} \
(accepted: md5, sha1, sha256, sha384, sha512)"
))
})?;
nss_hmac_compute_rust(algorithm, block_size, key, data)
}
fn hmac_verify(
&self,
algorithm: &str,
key: &[u8],
data: &[u8],
expected: &[u8],
) -> Result<(), NssHmacError> {
let mac = self.hmac_compute(algorithm, key, data)?;
if constant_time_eq(&mac, expected) {
Ok(())
} else {
Err(NssHmacError("HMAC verification failed".into()))
}
}
}
impl ErasedHmacProvider for NssHmacProvider {
fn hmac_compute_erased(
&self,
algorithm: &str,
key: &[u8],
data: &[u8],
) -> Result<Vec<u8>, PrivateKeyError> {
self.hmac_compute(algorithm, key, data)
.map_err(PrivateKeyError::new)
}
fn hmac_verify_erased(
&self,
algorithm: &str,
key: &[u8],
data: &[u8],
expected: &[u8],
) -> Result<(), PrivateKeyError> {
self.hmac_verify(algorithm, key, data, expected)
.map_err(PrivateKeyError::new)
}
}
pub(crate) fn nss_data_hasher() -> Box<dyn ErasedDataHasher> {
Box::new(NssDataHasher)
}
pub(crate) fn nss_hmac_provider() -> Box<dyn ErasedHmacProvider> {
Box::new(NssHmacProvider)
}
impl SecureRandom for NssDataHasher {
type Error = NssDataHasherError;
fn rand_bytes(&self, out: &mut [u8]) -> Result<(), NssDataHasherError> {
if !ensure_nss_init() {
return Err(NssDataHasherError("NSS initialisation failed".into()));
}
if out.is_empty() {
return Ok(());
}
let status = unsafe { PK11_GenerateRandom(out.as_mut_ptr(), out.len() as std::ffi::c_int) };
if status == nss_sys::SECStatus::SECSuccess {
Ok(())
} else {
Err(NssDataHasherError("PK11_GenerateRandom failed".into()))
}
}
}
pub(crate) fn nss_secure_random() -> NssDataHasher {
NssDataHasher
}
impl Pbkdf2Provider for NssHmacProvider {
type Error = NssHmacError;
fn pbkdf2_hmac(
&self,
algorithm: &str,
password: &[u8],
salt: &[u8],
iterations: usize,
length: usize,
) -> Result<Vec<u8>, NssHmacError> {
if length == 0 {
return Ok(Vec::new());
}
let (_, hash_len) = alg_name_to_hmac_mech(algorithm).ok_or_else(|| {
NssHmacError(format!(
"unsupported PBKDF2 algorithm: {algorithm} \
(accepted: md5, sha1, sha224, sha256, sha384, sha512)"
))
})?;
if iterations == 0 {
return Err(NssHmacError("PBKDF2 iterations must be >= 1".into()));
}
let blocks = length.div_ceil(hash_len);
let mut dk = Vec::with_capacity(length);
for block_idx in 1u32..=(blocks as u32) {
let mut input = Vec::with_capacity(salt.len() + 4);
input.extend_from_slice(salt);
input.extend_from_slice(&block_idx.to_be_bytes());
let mut u = self.hmac_compute(algorithm, password, &input)?;
let mut f = u.clone();
for _ in 1..iterations {
u = self.hmac_compute(algorithm, password, &u)?;
for (a, b) in f.iter_mut().zip(u.iter()) {
*a ^= b;
}
}
dk.extend_from_slice(&f);
}
dk.truncate(length);
Ok(dk)
}
}
pub(crate) fn nss_pbkdf2_provider() -> NssHmacProvider {
NssHmacProvider
}
struct NssHmacStateRust {
opad_key: Vec<u8>,
inner_state: Box<dyn crate::crypto::HashState>,
algorithm: String,
}
unsafe impl Send for NssHmacStateRust {}
impl HmacState for NssHmacStateRust {
fn update(&mut self, data: &[u8]) {
self.inner_state.update(data);
}
fn finalize_boxed(self: Box<Self>) -> Vec<u8> {
let inner_hash = self.inner_state.finalize_boxed();
let mut outer = self.opad_key.clone();
outer.extend_from_slice(&inner_hash);
NssDataHasher
.hash_data(&self.algorithm, &outer)
.expect("NSS outer HMAC hash failed")
}
}
impl StreamingHmacProvider for NssHmacProvider {
type Error = NssHmacError;
fn new_hmac(&self, algorithm: &str, key: &[u8]) -> Result<Box<dyn HmacState>, NssHmacError> {
let block_size = hmac_block_size_for_alg(algorithm).ok_or_else(|| {
NssHmacError(format!(
"unsupported streaming HMAC algorithm: {algorithm} \
(accepted: md5, sha1, sha256, sha384, sha512)"
))
})?;
let hasher = NssDataHasher;
let key_hashed;
let key = if key.len() > block_size {
key_hashed = hasher
.hash_data(algorithm, key)
.map_err(|e| NssHmacError(e.to_string()))?;
&key_hashed[..]
} else {
key
};
let mut k = vec![0u8; block_size];
k[..key.len()].copy_from_slice(key);
let ipad_key: Vec<u8> = k.iter().map(|b| b ^ 0x36).collect();
let opad_key: Vec<u8> = k.iter().map(|b| b ^ 0x5c).collect();
use crate::crypto::StreamingHasher as _;
let mut inner_state = hasher
.new_hash(algorithm)
.map_err(|e| NssHmacError(e.to_string()))?;
inner_state.update(&ipad_key);
Ok(Box::new(NssHmacStateRust {
opad_key,
inner_state,
algorithm: algorithm.to_string(),
}))
}
}
impl ErasedStreamingHmacProvider for NssHmacProvider {
fn new_hmac_erased(
&self,
algorithm: &str,
key: &[u8],
) -> Result<Box<dyn HmacState>, PrivateKeyError> {
use crate::crypto::StreamingHmacProvider as _;
self.new_hmac(algorithm, key).map_err(PrivateKeyError::new)
}
}
pub(crate) fn nss_streaming_hmac_provider() -> Box<dyn ErasedStreamingHmacProvider> {
Box::new(NssHmacProvider)
}
fn nss_block_cipher_op(
mechanism: CKMechanismType,
encrypt: bool,
key: &[u8],
iv: &[u8],
input: &[u8],
block_size: usize,
) -> Result<Vec<u8>, NssDataHasherError> {
if !ensure_nss_init() {
return Err(NssDataHasherError("NSS initialisation failed".into()));
}
let slot = unsafe { PK11_GetInternalSlot() };
if slot.is_null() {
return Err(NssDataHasherError("PK11_GetInternalSlot failed".into()));
}
let key_item = SECItemStr {
type_: SECItemType::siBuffer,
data: key.as_ptr() as *mut _,
len: key.len() as u32,
};
let operation = if encrypt { CKA_ENCRYPT } else { CKA_DECRYPT };
let sym_key = unsafe {
PK11_ImportSymKey(
slot,
mechanism,
PK11_ORIGIN_UNWRAP,
operation,
&key_item,
std::ptr::null_mut(),
)
};
unsafe { PK11_FreeSlot(slot) };
if sym_key.is_null() {
return Err(NssDataHasherError("PK11_ImportSymKey failed".into()));
}
let iv_item = SECItemStr {
type_: SECItemType::siBuffer,
data: iv.as_ptr() as *mut _,
len: iv.len() as u32,
};
let ctx = unsafe { PK11_CreateContextBySymKey(mechanism, operation, sym_key, &iv_item) };
if ctx.is_null() {
unsafe { PK11_FreeSymKey(sym_key) };
return Err(NssDataHasherError(
"PK11_CreateContextBySymKey failed".into(),
));
}
let init_status = unsafe { PK11_DigestBegin(ctx) };
if init_status != nss_sys::SECStatus::SECSuccess {
unsafe {
PK11_DestroyContext(ctx, PR_TRUE);
PK11_FreeSymKey(sym_key);
}
return Err(NssDataHasherError("PK11_DigestBegin failed".into()));
}
let max_out = input.len() + block_size;
let mut out = vec![0u8; max_out];
let mut partial_len: std::ffi::c_int = 0;
let cipher_status = unsafe {
PK11_CipherOp(
ctx,
out.as_mut_ptr(),
&mut partial_len,
max_out as std::ffi::c_int,
input.as_ptr(),
input.len() as std::ffi::c_int,
)
};
if cipher_status != nss_sys::SECStatus::SECSuccess {
unsafe {
PK11_DestroyContext(ctx, PR_TRUE);
PK11_FreeSymKey(sym_key);
}
return Err(NssDataHasherError(format!(
"NSS PK11_CipherOp ({}) failed",
if encrypt { "encrypt" } else { "decrypt" }
)));
}
let partial_ulen = partial_len as usize;
let remaining = max_out - partial_ulen;
let mut final_len: std::ffi::c_uint = 0;
let final_status = unsafe {
PK11_DigestFinal(
ctx,
out[partial_ulen..].as_mut_ptr(),
&mut final_len,
remaining as std::ffi::c_uint,
)
};
unsafe {
PK11_DestroyContext(ctx, PR_TRUE);
PK11_FreeSymKey(sym_key);
}
if final_status != nss_sys::SECStatus::SECSuccess {
return Err(NssDataHasherError(format!(
"NSS PK11_DigestFinal ({}) failed",
if encrypt { "encrypt" } else { "decrypt" }
)));
}
out.truncate(partial_ulen + final_len as usize);
Ok(out)
}
fn nss_aes_gcm_encrypt(
key: &[u8],
nonce: &[u8],
plaintext: &[u8],
aad: &[u8],
) -> Result<Vec<u8>, NssDataHasherError> {
nss_aes_gcm_op(true, key, nonce, plaintext, aad)
}
fn nss_aes_gcm_decrypt(
key: &[u8],
nonce: &[u8],
ciphertext_with_tag: &[u8],
aad: &[u8],
) -> Result<Vec<u8>, NssDataHasherError> {
if ciphertext_with_tag.len() < 16 {
return Err(NssDataHasherError(
"AES-GCM input too short (must be at least 16 bytes for the tag)".into(),
));
}
nss_aes_gcm_op(false, key, nonce, ciphertext_with_tag, aad)
}
fn nss_aes_gcm_op(
encrypt: bool,
key: &[u8],
nonce: &[u8],
input: &[u8],
aad: &[u8],
) -> Result<Vec<u8>, NssDataHasherError> {
if !matches!(key.len(), 16 | 24 | 32) {
return Err(NssDataHasherError(
"AES-GCM key must be 16, 24, or 32 bytes".into(),
));
}
if nonce.len() != 12 {
return Err(NssDataHasherError("AES-GCM nonce must be 12 bytes".into()));
}
if !ensure_nss_init() {
return Err(NssDataHasherError("NSS initialisation failed".into()));
}
let gcm_params = CkAesGcmParams {
iv_ptr: nonce.as_ptr(),
iv_len: nonce.len() as std::ffi::c_ulong,
iv_bits: (nonce.len() * 8) as std::ffi::c_ulong,
aad_ptr: if aad.is_empty() {
std::ptr::null()
} else {
aad.as_ptr()
},
aad_len: aad.len() as std::ffi::c_ulong,
tag_bits: 128,
};
let param_item = SECItemStr {
type_: SECItemType::siBuffer,
data: &gcm_params as *const CkAesGcmParams as *mut u8,
len: std::mem::size_of::<CkAesGcmParams>() as u32,
};
let slot = unsafe { PK11_GetInternalSlot() };
if slot.is_null() {
return Err(NssDataHasherError("PK11_GetInternalSlot failed".into()));
}
let key_item = SECItemStr {
type_: SECItemType::siBuffer,
data: key.as_ptr() as *mut _,
len: key.len() as u32,
};
let operation = if encrypt { CKA_ENCRYPT } else { CKA_DECRYPT };
let sym_key = unsafe {
PK11_ImportSymKey(
slot,
CKM_AES_GCM,
PK11_ORIGIN_UNWRAP,
operation,
&key_item,
std::ptr::null_mut(),
)
};
unsafe { PK11_FreeSlot(slot) };
if sym_key.is_null() {
return Err(NssDataHasherError("PK11_ImportSymKey failed".into()));
}
let ctx = unsafe { PK11_CreateContextBySymKey(CKM_AES_GCM, operation, sym_key, ¶m_item) };
if ctx.is_null() {
unsafe { PK11_FreeSymKey(sym_key) };
return Err(NssDataHasherError(
"PK11_CreateContextBySymKey failed".into(),
));
}
let init_status = unsafe { PK11_DigestBegin(ctx) };
if init_status != nss_sys::SECStatus::SECSuccess {
unsafe {
PK11_DestroyContext(ctx, nss_sys::nspr::PR_TRUE);
PK11_FreeSymKey(sym_key);
}
return Err(NssDataHasherError("PK11_DigestBegin failed".into()));
}
let max_out = input.len() + 16; let mut out = vec![0u8; max_out];
let mut partial_len: std::ffi::c_int = 0;
let cipher_status = unsafe {
PK11_CipherOp(
ctx,
out.as_mut_ptr(),
&mut partial_len,
max_out as std::ffi::c_int,
input.as_ptr(),
input.len() as std::ffi::c_int,
)
};
if cipher_status != nss_sys::SECStatus::SECSuccess {
unsafe {
PK11_DestroyContext(ctx, nss_sys::nspr::PR_TRUE);
PK11_FreeSymKey(sym_key);
}
return Err(NssDataHasherError(format!(
"NSS PK11_CipherOp (AES-GCM {}) failed",
if encrypt { "encrypt" } else { "decrypt" }
)));
}
let partial_ulen = partial_len as usize;
let remaining = max_out - partial_ulen;
let mut final_len: std::ffi::c_uint = 0;
let final_status = unsafe {
PK11_DigestFinal(
ctx,
out[partial_ulen..].as_mut_ptr(),
&mut final_len,
remaining as std::ffi::c_uint,
)
};
unsafe {
PK11_DestroyContext(ctx, nss_sys::nspr::PR_TRUE);
PK11_FreeSymKey(sym_key);
}
if final_status != nss_sys::SECStatus::SECSuccess {
return Err(NssDataHasherError(if encrypt {
"NSS PK11_DigestFinal (AES-GCM encrypt) failed".into()
} else {
"AES-GCM authentication tag mismatch".into()
}));
}
out.truncate(partial_ulen + final_len as usize);
Ok(out)
}
impl BlockCipherProvider for NssDataHasher {
type Error = NssDataHasherError;
fn aes_cbc_encrypt(
&self,
key: &[u8],
iv: &[u8],
plaintext: &[u8],
pad: bool,
) -> Result<Vec<u8>, NssDataHasherError> {
if key.len() != 16 && key.len() != 24 && key.len() != 32 {
return Err(NssDataHasherError(format!(
"invalid AES key length: {} (must be 16, 24, or 32)",
key.len()
)));
}
if iv.len() != 16 {
return Err(NssDataHasherError(format!(
"invalid AES-CBC IV length: {} (must be 16)",
iv.len()
)));
}
let mech = if pad { CKM_AES_CBC_PAD } else { CKM_AES_CBC };
nss_block_cipher_op(mech, true, key, iv, plaintext, 16)
}
fn aes_cbc_decrypt(
&self,
key: &[u8],
iv: &[u8],
ciphertext: &[u8],
unpad: bool,
) -> Result<Vec<u8>, NssDataHasherError> {
if key.len() != 16 && key.len() != 24 && key.len() != 32 {
return Err(NssDataHasherError(format!(
"invalid AES key length: {} (must be 16, 24, or 32)",
key.len()
)));
}
if iv.len() != 16 {
return Err(NssDataHasherError(format!(
"invalid AES-CBC IV length: {} (must be 16)",
iv.len()
)));
}
let mech = if unpad { CKM_AES_CBC_PAD } else { CKM_AES_CBC };
nss_block_cipher_op(mech, false, key, iv, ciphertext, 16)
}
fn des3_cbc_encrypt(
&self,
key: &[u8],
iv: &[u8],
plaintext: &[u8],
pad: bool,
) -> Result<Vec<u8>, NssDataHasherError> {
if key.len() != 16 && key.len() != 24 {
return Err(NssDataHasherError(format!(
"invalid 3DES key length: {} (must be 16 or 24)",
key.len()
)));
}
if iv.len() != 8 {
return Err(NssDataHasherError(format!(
"invalid 3DES-CBC IV length: {} (must be 8)",
iv.len()
)));
}
let mech = if pad { CKM_DES3_CBC_PAD } else { CKM_DES3_CBC };
nss_block_cipher_op(mech, true, key, iv, plaintext, 8)
}
fn des3_cbc_decrypt(
&self,
key: &[u8],
iv: &[u8],
ciphertext: &[u8],
unpad: bool,
) -> Result<Vec<u8>, NssDataHasherError> {
if key.len() != 16 && key.len() != 24 {
return Err(NssDataHasherError(format!(
"invalid 3DES key length: {} (must be 16 or 24)",
key.len()
)));
}
if iv.len() != 8 {
return Err(NssDataHasherError(format!(
"invalid 3DES-CBC IV length: {} (must be 8)",
iv.len()
)));
}
let mech = if unpad {
CKM_DES3_CBC_PAD
} else {
CKM_DES3_CBC
};
nss_block_cipher_op(mech, false, key, iv, ciphertext, 8)
}
fn aes_gcm_encrypt(
&self,
key: &[u8],
nonce: &[u8],
plaintext: &[u8],
aad: &[u8],
) -> Result<Vec<u8>, NssDataHasherError> {
nss_aes_gcm_encrypt(key, nonce, plaintext, aad)
}
fn aes_gcm_decrypt(
&self,
key: &[u8],
nonce: &[u8],
ciphertext_with_tag: &[u8],
aad: &[u8],
) -> Result<Vec<u8>, NssDataHasherError> {
nss_aes_gcm_decrypt(key, nonce, ciphertext_with_tag, aad)
}
}
pub(crate) fn nss_block_cipher_provider() -> NssDataHasher {
NssDataHasher
}