use std::ptr;
use nss_sys::nspr::{PR_FALSE, PR_TRUE};
use nss_sys::{SECItemStr, SECItemType, SECStatus};
use super::ensure_nss_init;
use crate::crypto::utils::split_alg_id;
use crate::crypto::{ErasedCertificateSigner, PrivateKeyError};
use crate::oids;
use super::ffi::{
PK11_FreeSlot, PK11_GetInternalSlot, PK11_ImportDERPrivateKeyInfoAndReturnKey, PK11_Sign,
PK11_SignatureLen, SECITEM_FreeItem, SECKEYPrivateKeyStr, SECKEY_DestroyPrivateKey, SECOidTag,
SEC_SignData, KU_DIGITAL_SIGNATURE, SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE,
SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE, SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE,
SEC_OID_ED25519_SIGNATURE, SEC_OID_ML_DSA_44, SEC_OID_ML_DSA_65, SEC_OID_ML_DSA_87,
SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION, SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION,
SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION,
};
#[derive(Debug)]
pub struct NssSignerError(pub(crate) String);
impl std::fmt::Display for NssSignerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
impl std::error::Error for NssSignerError {}
fn oid_components_to_nss_tag(components: &[u32]) -> Option<SECOidTag> {
match components {
c if c == oids::ECDSA_WITH_SHA256 => Some(SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE),
c if c == oids::ECDSA_WITH_SHA384 => Some(SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE),
c if c == oids::ECDSA_WITH_SHA512 => Some(SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE),
c if c == oids::SHA256_WITH_RSA => Some(SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION),
c if c == oids::SHA384_WITH_RSA => Some(SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION),
c if c == oids::SHA512_WITH_RSA => Some(SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION),
c if c == oids::ED25519 => Some(SEC_OID_ED25519_SIGNATURE),
c if c == oids::ML_DSA_44 => Some(SEC_OID_ML_DSA_44),
c if c == oids::ML_DSA_65 => Some(SEC_OID_ML_DSA_65),
c if c == oids::ML_DSA_87 => Some(SEC_OID_ML_DSA_87),
_ => None, }
}
pub(super) fn do_nss_sign(
pkcs8_der: &[u8],
tbs_der: &[u8],
nss_alg_tag: SECOidTag,
) -> Result<Vec<u8>, NssSignerError> {
if !ensure_nss_init() {
return Err(NssSignerError("NSS initialisation failed".to_string()));
}
let slot = unsafe { PK11_GetInternalSlot() };
if slot.is_null() {
return Err(NssSignerError("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 import_status = unsafe {
PK11_ImportDERPrivateKeyInfoAndReturnKey(
slot,
&pkcs8_item,
ptr::null(), ptr::null(), PR_FALSE, PR_TRUE, KU_DIGITAL_SIGNATURE,
&mut priv_key,
ptr::null_mut(), )
};
unsafe { PK11_FreeSlot(slot) };
if import_status != SECStatus::SECSuccess || priv_key.is_null() {
return Err(NssSignerError(
"PK11_ImportDERPrivateKeyInfoAndReturnKey failed: \
cannot import private key into NSS internal slot"
.to_string(),
));
}
let sig = if nss_alg_tag == SEC_OID_ED25519_SIGNATURE {
let sig_len = unsafe { PK11_SignatureLen(priv_key) };
if sig_len <= 0 {
unsafe { SECKEY_DestroyPrivateKey(priv_key) };
return Err(NssSignerError(
"PK11_SignatureLen returned non-positive value".to_string(),
));
}
let mut sig_buf = vec![0u8; sig_len as usize];
let msg_item = SECItemStr {
type_: SECItemType::siBuffer,
data: tbs_der.as_ptr() as *mut _,
len: tbs_der.len() as u32,
};
let mut sig_item = SECItemStr {
type_: SECItemType::siBuffer,
data: sig_buf.as_mut_ptr(),
len: sig_len as u32,
};
let sign_status = unsafe { PK11_Sign(priv_key, &mut sig_item, &msg_item) };
unsafe { SECKEY_DestroyPrivateKey(priv_key) };
if sign_status != SECStatus::SECSuccess {
return Err(NssSignerError("PK11_Sign (Ed25519) failed".to_string()));
}
sig_buf.truncate(sig_item.len as usize);
sig_buf
} else {
let mut sig_item = SECItemStr {
type_: SECItemType::siBuffer,
data: ptr::null_mut(),
len: 0,
};
let sign_status = unsafe {
SEC_SignData(
&mut sig_item,
tbs_der.as_ptr(),
tbs_der.len() as std::ffi::c_int,
priv_key,
nss_alg_tag,
)
};
unsafe { SECKEY_DestroyPrivateKey(priv_key) };
if sign_status != SECStatus::SECSuccess {
return Err(NssSignerError("SEC_SignData failed".to_string()));
}
let sig =
unsafe { std::slice::from_raw_parts(sig_item.data, sig_item.len as usize).to_vec() };
unsafe { SECITEM_FreeItem(&mut sig_item, PR_FALSE) };
sig
};
Ok(sig)
}
pub struct NssSigner {
pkcs8_der: Vec<u8>,
sig_alg_der: Vec<u8>,
nss_alg_tag: SECOidTag,
}
impl NssSigner {
pub fn new(pkcs8_der: Vec<u8>, sig_alg_der: Vec<u8>) -> Option<Self> {
let (oid, _, _) = split_alg_id(&sig_alg_der, |_| ()).ok()?;
let nss_alg_tag = oid_components_to_nss_tag(oid.components())?;
Some(Self {
pkcs8_der,
sig_alg_der,
nss_alg_tag,
})
}
}
impl ErasedCertificateSigner for NssSigner {
fn signature_algorithm_der_erased(&self) -> Result<Vec<u8>, PrivateKeyError> {
Ok(self.sig_alg_der.clone())
}
fn sign_tbs_erased(&self, tbs_der: &[u8]) -> Result<Vec<u8>, PrivateKeyError> {
do_nss_sign(&self.pkcs8_der, tbs_der, self.nss_alg_tag).map_err(PrivateKeyError::new)
}
}
pub(crate) struct NssUnsupportedSigner;
impl ErasedCertificateSigner for NssUnsupportedSigner {
fn signature_algorithm_der_erased(&self) -> Result<Vec<u8>, PrivateKeyError> {
Err(PrivateKeyError::new(NssSignerError(
"signing algorithm not supported by the NSS backend".to_string(),
)))
}
fn sign_tbs_erased(&self, _tbs_der: &[u8]) -> Result<Vec<u8>, PrivateKeyError> {
Err(PrivateKeyError::new(NssSignerError(
"signing algorithm not supported by the NSS backend".to_string(),
)))
}
}