use std::ptr;
use nss_sys::nspr::{PR_FALSE, PR_TRUE};
use nss_sys::{SECItemStr, SECItemType, SECStatus};
use super::ensure_nss_init;
use super::ffi::{
CKMechanismType, CKRsaPkcsMgfType, CkRsaPkcsOaepParams, PK11_FreeSlot, PK11_GetInternalSlot,
PK11_ImportDERPrivateKeyInfoAndReturnKey, PK11_PrivDecrypt, PK11_PubEncrypt,
SECKEYPrivateKeyStr, SECKEYPublicKeyStr, SECKEY_DecodeDERSubjectPublicKeyInfo,
SECKEY_DestroyPrivateKey, SECKEY_DestroyPublicKey, SECKEY_DestroySubjectPublicKeyInfo,
SECKEY_ExtractPublicKey, SECKEY_PublicKeyStrength, CKG_MGF1_SHA1, CKG_MGF1_SHA224,
CKG_MGF1_SHA256, CKG_MGF1_SHA384, CKG_MGF1_SHA512, CKM_RSA_PKCS, CKM_RSA_PKCS_OAEP, CKM_SHA224,
CKM_SHA256, CKM_SHA384, CKM_SHA512, CKM_SHA_1, CKZ_DATA_SPECIFIED, KU_KEY_ENCIPHERMENT,
};
#[derive(Debug)]
pub struct NssRsaError(pub(crate) String);
impl std::fmt::Display for NssRsaError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
impl std::error::Error for NssRsaError {}
fn hash_alg_to_ckm(alg: &str) -> Result<(CKMechanismType, CKRsaPkcsMgfType), NssRsaError> {
match alg {
"sha1" => Ok((CKM_SHA_1, CKG_MGF1_SHA1)),
"sha224" => Ok((CKM_SHA224, CKG_MGF1_SHA224)),
"sha256" => Ok((CKM_SHA256, CKG_MGF1_SHA256)),
"sha384" => Ok((CKM_SHA384, CKG_MGF1_SHA384)),
"sha512" => Ok((CKM_SHA512, CKG_MGF1_SHA512)),
other => Err(NssRsaError(format!(
"unsupported hash algorithm for RSA-OAEP: {other}; \
expected sha1, sha224, sha256, sha384, or sha512"
))),
}
}
fn import_public_key(spki_der: &[u8]) -> Result<*mut SECKEYPublicKeyStr, NssRsaError> {
let spki_item = SECItemStr {
type_: SECItemType::siBuffer,
data: spki_der.as_ptr() as *mut _,
len: spki_der.len() as u32,
};
let spki_info = unsafe { SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item) };
if spki_info.is_null() {
return Err(NssRsaError(
"SECKEY_DecodeDERSubjectPublicKeyInfo failed: cannot decode SPKI DER".to_string(),
));
}
let pub_key = unsafe { SECKEY_ExtractPublicKey(spki_info) };
unsafe { SECKEY_DestroySubjectPublicKeyInfo(spki_info) };
if pub_key.is_null() {
return Err(NssRsaError(
"SECKEY_ExtractPublicKey failed: cannot extract public key from SPKI".to_string(),
));
}
Ok(pub_key)
}
fn import_private_key(pkcs8_der: &[u8]) -> Result<*mut SECKEYPrivateKeyStr, NssRsaError> {
let slot = unsafe { PK11_GetInternalSlot() };
if slot.is_null() {
return Err(NssRsaError("PK11_GetInternalSlot failed".to_string()));
}
let pkcs8_item = SECItemStr {
type_: SECItemType::siBuffer,
data: pkcs8_der.as_ptr() as *mut _,
len: pkcs8_der.len() as u32,
};
let mut priv_key: *mut SECKEYPrivateKeyStr = ptr::null_mut();
let status = unsafe {
PK11_ImportDERPrivateKeyInfoAndReturnKey(
slot,
&pkcs8_item,
ptr::null(), ptr::null(), PR_FALSE, PR_TRUE, KU_KEY_ENCIPHERMENT,
&mut priv_key,
ptr::null_mut(),
)
};
unsafe { PK11_FreeSlot(slot) };
if status != SECStatus::SECSuccess || priv_key.is_null() {
return Err(NssRsaError(
"PK11_ImportDERPrivateKeyInfoAndReturnKey failed: \
cannot import private key for RSA decryption"
.to_string(),
));
}
Ok(priv_key)
}
pub(crate) fn nss_rsa_oaep_encrypt(
spki_der: &[u8],
plaintext: &[u8],
hash_alg: &str,
) -> Result<Vec<u8>, NssRsaError> {
if !ensure_nss_init() {
return Err(NssRsaError("NSS initialisation failed".to_string()));
}
let (hash_ckm, mgf_ckm) = hash_alg_to_ckm(hash_alg)?;
let pub_key = import_public_key(spki_der)?;
let key_len = unsafe { SECKEY_PublicKeyStrength(pub_key) } as usize;
let mut out_buf = vec![0u8; key_len];
let mut out_len: std::ffi::c_uint = key_len as std::ffi::c_uint;
let mut oaep_params = CkRsaPkcsOaepParams {
hash_alg: hash_ckm,
mgf: mgf_ckm,
source: CKZ_DATA_SPECIFIED,
p_source_data: ptr::null_mut(),
ul_source_data_len: 0,
};
let mut param_item = SECItemStr {
type_: SECItemType::siBuffer,
data: &mut oaep_params as *mut _ as *mut u8,
len: std::mem::size_of::<CkRsaPkcsOaepParams>() as u32,
};
let status = unsafe {
PK11_PubEncrypt(
pub_key,
CKM_RSA_PKCS_OAEP,
&mut param_item,
out_buf.as_mut_ptr(),
&mut out_len,
out_buf.len() as std::ffi::c_uint,
plaintext.as_ptr(),
plaintext.len() as std::ffi::c_uint,
ptr::null_mut(),
)
};
unsafe { SECKEY_DestroyPublicKey(pub_key) };
if status != SECStatus::SECSuccess {
return Err(NssRsaError("PK11_PubEncrypt (RSA-OAEP) failed".to_string()));
}
out_buf.truncate(out_len as usize);
Ok(out_buf)
}
pub(crate) fn nss_rsa_pkcs1v15_encrypt(
spki_der: &[u8],
plaintext: &[u8],
) -> Result<Vec<u8>, NssRsaError> {
if !ensure_nss_init() {
return Err(NssRsaError("NSS initialisation failed".to_string()));
}
let pub_key = import_public_key(spki_der)?;
let key_len = unsafe { SECKEY_PublicKeyStrength(pub_key) } as usize;
let mut out_buf = vec![0u8; key_len];
let mut out_len: std::ffi::c_uint = key_len as std::ffi::c_uint;
let status = unsafe {
PK11_PubEncrypt(
pub_key,
CKM_RSA_PKCS,
ptr::null_mut(), out_buf.as_mut_ptr(),
&mut out_len,
out_buf.len() as std::ffi::c_uint,
plaintext.as_ptr(),
plaintext.len() as std::ffi::c_uint,
ptr::null_mut(),
)
};
unsafe { SECKEY_DestroyPublicKey(pub_key) };
if status != SECStatus::SECSuccess {
return Err(NssRsaError(
"PK11_PubEncrypt (RSA PKCS#1 v1.5) failed".to_string(),
));
}
out_buf.truncate(out_len as usize);
Ok(out_buf)
}
pub(crate) fn nss_rsa_oaep_decrypt(
pkcs8_der: &[u8],
ciphertext: &[u8],
hash_alg: &str,
) -> Result<Vec<u8>, NssRsaError> {
if !ensure_nss_init() {
return Err(NssRsaError("NSS initialisation failed".to_string()));
}
let (hash_ckm, mgf_ckm) = hash_alg_to_ckm(hash_alg)?;
let priv_key = import_private_key(pkcs8_der)?;
let mut out_buf = vec![0u8; ciphertext.len()];
let mut out_len: std::ffi::c_uint = ciphertext.len() as std::ffi::c_uint;
let mut oaep_params = CkRsaPkcsOaepParams {
hash_alg: hash_ckm,
mgf: mgf_ckm,
source: CKZ_DATA_SPECIFIED,
p_source_data: ptr::null_mut(),
ul_source_data_len: 0,
};
let mut param_item = SECItemStr {
type_: SECItemType::siBuffer,
data: &mut oaep_params as *mut _ as *mut u8,
len: std::mem::size_of::<CkRsaPkcsOaepParams>() as u32,
};
let status = unsafe {
PK11_PrivDecrypt(
priv_key,
CKM_RSA_PKCS_OAEP,
&mut param_item,
out_buf.as_mut_ptr(),
&mut out_len,
out_buf.len() as std::ffi::c_uint,
ciphertext.as_ptr(),
ciphertext.len() as std::ffi::c_uint,
)
};
unsafe { SECKEY_DestroyPrivateKey(priv_key) };
if status != SECStatus::SECSuccess {
return Err(NssRsaError(
"PK11_PrivDecrypt (RSA-OAEP) failed".to_string(),
));
}
out_buf.truncate(out_len as usize);
Ok(out_buf)
}
pub(crate) fn nss_rsa_pkcs1v15_decrypt(
pkcs8_der: &[u8],
ciphertext: &[u8],
) -> Result<Vec<u8>, NssRsaError> {
if !ensure_nss_init() {
return Err(NssRsaError("NSS initialisation failed".to_string()));
}
let priv_key = import_private_key(pkcs8_der)?;
let mut out_buf = vec![0u8; ciphertext.len()];
let mut out_len: std::ffi::c_uint = ciphertext.len() as std::ffi::c_uint;
let status = unsafe {
PK11_PrivDecrypt(
priv_key,
CKM_RSA_PKCS,
ptr::null_mut(),
out_buf.as_mut_ptr(),
&mut out_len,
out_buf.len() as std::ffi::c_uint,
ciphertext.as_ptr(),
ciphertext.len() as std::ffi::c_uint,
)
};
unsafe { SECKEY_DestroyPrivateKey(priv_key) };
if status != SECStatus::SECSuccess {
return Err(NssRsaError(
"PK11_PrivDecrypt (RSA PKCS#1 v1.5) failed".to_string(),
));
}
out_buf.truncate(out_len as usize);
Ok(out_buf)
}