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, PK11RsaGenParams, PK11_ExportDERPrivateKeyInfo, PK11_FreeSlot,
PK11_GenerateKeyPair, PK11_GetInternalSlot, PK11_ImportDERPrivateKeyInfoAndReturnKey,
SECITEM_FreeItem, SECKEYPrivateKeyStr, SECKEYPublicKeyStr, SECKEY_ConvertToPublicKey,
SECKEY_DestroyPrivateKey, SECKEY_DestroyPublicKey, SECKEY_EncodeDERSubjectPublicKeyInfo,
SECKEY_PublicKeyStrength, CKM_EC_EDWARDS_KEY_PAIR_GEN, CKM_EC_KEY_PAIR_GEN,
CKM_ML_DSA_KEY_PAIR_GEN, CKM_RSA_PKCS_KEY_PAIR_GEN, CKP_ML_DSA_44, CKP_ML_DSA_65,
CKP_ML_DSA_87, KU_DIGITAL_SIGNATURE,
};
use crate::crypto::PrivateKeyError;
#[derive(Debug)]
struct NssKeyError(String);
impl std::fmt::Display for NssKeyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
impl std::error::Error for NssKeyError {}
fn key_err(msg: impl Into<String>) -> PrivateKeyError {
PrivateKeyError::new(NssKeyError(msg.into()))
}
pub(super) fn nss_generate_key(
mechanism: CKMechanismType,
param: *mut std::ffi::c_void,
) -> Result<crate::crypto::BackendPrivateKey, PrivateKeyError> {
if !ensure_nss_init() {
return Err(key_err("NSS initialisation failed"));
}
let slot = unsafe { PK11_GetInternalSlot() };
if slot.is_null() {
return Err(key_err("PK11_GetInternalSlot failed"));
}
let mut pub_key: *mut SECKEYPublicKeyStr = ptr::null_mut();
let priv_key = unsafe {
PK11_GenerateKeyPair(
slot,
mechanism,
param,
&mut pub_key,
PR_FALSE, PR_TRUE, ptr::null_mut(), )
};
unsafe { PK11_FreeSlot(slot) };
if priv_key.is_null() {
return Err(key_err("PK11_GenerateKeyPair failed"));
}
if !pub_key.is_null() {
unsafe { SECKEY_DestroyPublicKey(pub_key) };
}
let pkcs8_item = unsafe { PK11_ExportDERPrivateKeyInfo(priv_key, ptr::null_mut()) };
unsafe { SECKEY_DestroyPrivateKey(priv_key) };
if pkcs8_item.is_null() {
return Err(key_err("PK11_ExportDERPrivateKeyInfo failed"));
}
let pkcs8_der =
unsafe { std::slice::from_raw_parts((*pkcs8_item).data, (*pkcs8_item).len as usize) }
.to_vec();
unsafe { SECITEM_FreeItem(pkcs8_item, PR_TRUE) };
let cell = std::sync::OnceLock::new();
cell.set(pkcs8_der).expect("fresh OnceLock");
Ok(crate::crypto::BackendPrivateKey {
pkcs8_der: cell,
spki_cache: None,
pkcs11: None,
})
}
fn ensure_positive(bytes: &[u8]) -> Vec<u8> {
if bytes.first().is_some_and(|&b| b & 0x80 != 0) {
let mut v = Vec::with_capacity(bytes.len() + 1);
v.push(0u8);
v.extend_from_slice(bytes);
v
} else {
bytes.to_vec()
}
}
fn build_pkcs8_der(
alg: crate::AlgorithmIdentifier<'_>,
inner_key_der: &[u8],
) -> synta::Result<Vec<u8>> {
use synta::types::string::OctetStringRef;
let pki = crate::pkcs8_types::OneAsymmetricKey {
version: synta::Integer::from_i64(0),
private_key_algorithm: alg,
private_key: OctetStringRef::new(inner_key_der),
attributes: None,
public_key: None,
};
pki.to_der()
}
fn nss_ec_pkcs8_from_components(
d: &[u8],
x: &[u8],
y: &[u8],
curve_oid_components: &[u32],
) -> Result<Vec<u8>, String> {
use synta::tag::{Tag, TAG_SEQUENCE};
use synta::types::string::BitStringRef;
use synta::{Encoder, Encoding, Integer, ObjectIdentifier};
let point = {
let mut v = Vec::with_capacity(1 + x.len() + y.len());
v.push(0x04); v.extend_from_slice(x);
v.extend_from_slice(y);
v
};
let inner_der = (|| -> synta::Result<Vec<u8>> {
let mut enc = Encoder::new(Encoding::Der);
enc.start_constructed_no_guard(Tag::universal_constructed(TAG_SEQUENCE))?;
enc.encode(&Integer::from_i64(1))?;
let d_octet = synta::types::string::OctetStringRef::new(d);
enc.encode(&d_octet)?;
enc.start_constructed_no_guard(Tag::context_specific_constructed(1))?;
let pub_bit = BitStringRef::new(&point, 0)?;
enc.encode(&pub_bit)?;
enc.end_constructed()?;
enc.end_constructed()?;
enc.finish()
})()
.map_err(|e| format!("ECPrivateKey DER encoding failed: {e}"))?;
let curve_oid = ObjectIdentifier::new(curve_oid_components)
.map_err(|e| format!("invalid curve OID components: {e}"))?;
let ec_pk_oid = ObjectIdentifier::new(crate::oids::EC_PUBLIC_KEY)
.map_err(|e| format!("invalid EC_PUBLIC_KEY OID: {e}"))?;
let alg = crate::AlgorithmIdentifier {
algorithm: ec_pk_oid,
parameters: Some(synta::Element::ObjectIdentifier(curve_oid)),
};
build_pkcs8_der(alg, &inner_der).map_err(|e| format!("PKCS#8 DER encoding failed: {e}"))
}
fn nss_rsa_pkcs8_from_components(
c: &crate::crypto::RsaPrivateComponents<'_>,
) -> Result<Vec<u8>, String> {
use synta::tag::{Tag, TAG_SEQUENCE};
use synta::{Encoder, Encoding, Integer, ObjectIdentifier};
let n = ensure_positive(c.n);
let e = ensure_positive(c.e);
let d = ensure_positive(c.d);
let p = ensure_positive(c.p);
let q = ensure_positive(c.q);
let dp = ensure_positive(c.dp);
let dq = ensure_positive(c.dq);
let qi = ensure_positive(c.qi);
let inner_der = (|| -> synta::Result<Vec<u8>> {
let mut enc = Encoder::new(Encoding::Der);
enc.start_constructed_no_guard(Tag::universal_constructed(TAG_SEQUENCE))?;
enc.encode(&Integer::from_i64(0))?;
enc.encode(&Integer::from_bytes(&n))?;
enc.encode(&Integer::from_bytes(&e))?;
enc.encode(&Integer::from_bytes(&d))?;
enc.encode(&Integer::from_bytes(&p))?;
enc.encode(&Integer::from_bytes(&q))?;
enc.encode(&Integer::from_bytes(&dp))?;
enc.encode(&Integer::from_bytes(&dq))?;
enc.encode(&Integer::from_bytes(&qi))?;
enc.end_constructed()?;
enc.finish()
})()
.map_err(|e| format!("RSAPrivateKey DER encoding failed: {e}"))?;
let rsa_oid = ObjectIdentifier::new(crate::oids::RSA_ENCRYPTION)
.map_err(|e| format!("invalid RSA_ENCRYPTION OID: {e}"))?;
let alg = crate::AlgorithmIdentifier {
algorithm: rsa_oid,
parameters: Some(synta::Element::Null(synta::Null)),
};
build_pkcs8_der(alg, &inner_der).map_err(|e| format!("PKCS#8 DER encoding failed: {e}"))
}
fn nss_rsa_key_bit_size(pkcs8_der: &[u8]) -> Option<i64> {
if !ensure_nss_init() {
return None;
}
let slot = unsafe { PK11_GetInternalSlot() };
if slot.is_null() {
return None;
}
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_DIGITAL_SIGNATURE,
&mut priv_key,
ptr::null_mut(),
)
};
unsafe { PK11_FreeSlot(slot) };
if status != SECStatus::SECSuccess || priv_key.is_null() {
return None;
}
let pub_key = unsafe { SECKEY_ConvertToPublicKey(priv_key) };
unsafe { SECKEY_DestroyPrivateKey(priv_key) };
if pub_key.is_null() {
return None;
}
let byte_len = unsafe { SECKEY_PublicKeyStrength(pub_key) };
unsafe { SECKEY_DestroyPublicKey(pub_key) };
Some((byte_len as i64) * 8)
}
fn nss_public_key_spki_der(pkcs8_der: &[u8]) -> Result<Vec<u8>, PrivateKeyError> {
if !ensure_nss_init() {
return Err(key_err("NSS initialisation failed"));
}
let slot = unsafe { PK11_GetInternalSlot() };
if slot.is_null() {
return Err(key_err("PK11_GetInternalSlot failed"));
}
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_DIGITAL_SIGNATURE,
&mut priv_key,
ptr::null_mut(),
)
};
unsafe { PK11_FreeSlot(slot) };
if status != SECStatus::SECSuccess || priv_key.is_null() {
return Err(key_err(
"PK11_ImportDERPrivateKeyInfoAndReturnKey failed: cannot import private key",
));
}
let pub_key = unsafe { SECKEY_ConvertToPublicKey(priv_key) };
unsafe { SECKEY_DestroyPrivateKey(priv_key) };
if pub_key.is_null() {
return Err(key_err("SECKEY_ConvertToPublicKey failed"));
}
let spki_item = unsafe { SECKEY_EncodeDERSubjectPublicKeyInfo(pub_key) };
unsafe { SECKEY_DestroyPublicKey(pub_key) };
if spki_item.is_null() {
return Err(key_err("SECKEY_EncodeDERSubjectPublicKeyInfo failed"));
}
let spki_der =
unsafe { std::slice::from_raw_parts((*spki_item).data, (*spki_item).len as usize) }
.to_vec();
unsafe { SECITEM_FreeItem(spki_item, PR_TRUE) };
Ok(spki_der)
}
pub(super) fn generate_ml_dsa(
mldsa_variant: &str,
) -> Result<crate::crypto::BackendPrivateKey, PrivateKeyError> {
let param_set: std::ffi::c_ulong = match mldsa_variant {
"ML-DSA-44" => CKP_ML_DSA_44,
"ML-DSA-65" => CKP_ML_DSA_65,
"ML-DSA-87" => CKP_ML_DSA_87,
other => return Err(key_err(format!("unknown ML-DSA variant: {other}"))),
};
nss_generate_key(
CKM_ML_DSA_KEY_PAIR_GEN,
¶m_set as *const std::ffi::c_ulong as *mut std::ffi::c_void,
)
}
#[cfg(all(feature = "nss", not(feature = "openssl")))]
impl crate::crypto::BackendPrivateKey {
pub fn from_pem(pem: &[u8], _password: Option<&[u8]>) -> Result<Self, PrivateKeyError> {
let der = crate::pem::pem_blocks(pem)
.into_iter()
.find(|(label, _)| label == "PRIVATE KEY")
.map(|(_, der)| der)
.ok_or_else(|| key_err("no 'PRIVATE KEY' PEM block found"))?;
Self::from_der(&der)
}
pub fn from_der(der: &[u8]) -> Result<Self, PrivateKeyError> {
crate::pkcs8_types::PrivateKeyInfo::from_der(der)
.map_err(|e| key_err(format!("invalid PKCS#8 DER: {e}")))?;
let cell = std::sync::OnceLock::new();
cell.set(der.to_vec()).expect("fresh OnceLock");
Ok(Self {
pkcs8_der: cell,
spki_cache: None,
pkcs11: None,
})
}
pub fn from_pkcs8_encrypted(_data: &[u8], _password: &[u8]) -> Result<Self, PrivateKeyError> {
Err(key_err(
"encrypted PKCS#8 import is not yet implemented for the NSS backend",
))
}
pub fn to_pem(&self, password: Option<&[u8]>) -> Result<Vec<u8>, PrivateKeyError> {
if password.is_some() {
return Err(key_err(
"encrypted PKCS#8 PEM export is not yet implemented for the NSS backend",
));
}
Ok(crate::pem::der_to_pem("PRIVATE KEY", self.pkcs8_bytes()))
}
pub fn to_der(&self) -> Result<Vec<u8>, PrivateKeyError> {
Ok(self.pkcs8_bytes().to_vec())
}
pub fn to_pkcs8_encrypted(&self, _password: &[u8]) -> Result<Vec<u8>, PrivateKeyError> {
Err(key_err(
"encrypted PKCS#8 export is not yet implemented for the NSS backend",
))
}
pub fn key_type(&self) -> &'static str {
crate::crypto::utils::key_type_from_pkcs8(self.pkcs8_bytes())
}
pub fn key_bit_size(&self) -> Option<i64> {
if self.key_type() == "rsa" {
nss_rsa_key_bit_size(self.pkcs8_bytes())
} else {
None
}
}
pub fn public_key(&self) -> Result<crate::crypto::BackendPublicKey, PrivateKeyError> {
let spki_der = nss_public_key_spki_der(self.pkcs8_bytes())?;
Ok(crate::crypto::BackendPublicKey { spki_der })
}
pub fn rsa_oaep_decrypt(
&self,
ciphertext: &[u8],
hash_alg: &str,
) -> Result<Vec<u8>, PrivateKeyError> {
crate::nss_backend::nss_rsa_oaep_decrypt(self.pkcs8_bytes(), ciphertext, hash_alg)
.map_err(PrivateKeyError::new)
}
pub fn rsa_pkcs1v15_decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>, PrivateKeyError> {
crate::nss_backend::nss_rsa_pkcs1v15_decrypt(self.pkcs8_bytes(), ciphertext)
.map_err(PrivateKeyError::new)
}
pub fn generate_rsa(key_size: u32, public_exponent: u32) -> Result<Self, PrivateKeyError> {
let mut params = PK11RsaGenParams {
key_size_in_bits: key_size as std::ffi::c_int,
pe: public_exponent as std::ffi::c_ulong,
};
nss_generate_key(
CKM_RSA_PKCS_KEY_PAIR_GEN,
&mut params as *mut PK11RsaGenParams as *mut std::ffi::c_void,
)
}
pub fn generate_ec(curve: &str) -> Result<Self, PrivateKeyError> {
let ec_oid: &[u8] = match curve {
"P-256" => &[0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07],
"P-384" => &[0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22],
"P-521" => &[0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23],
other => {
return Err(key_err(format!(
"unsupported EC curve: '{other}'; expected P-256, P-384, or P-521"
)))
}
};
let mut params_item = SECItemStr {
type_: SECItemType::siBuffer,
data: ec_oid.as_ptr() as *mut _,
len: ec_oid.len() as u32,
};
nss_generate_key(
CKM_EC_KEY_PAIR_GEN,
&mut params_item as *mut SECItemStr as *mut std::ffi::c_void,
)
}
pub fn generate_ed25519() -> Result<Self, PrivateKeyError> {
let oid: &[u8] = &[0x06, 0x03, 0x2b, 0x65, 0x70];
let mut params_item = SECItemStr {
type_: SECItemType::siBuffer,
data: oid.as_ptr() as *mut _,
len: oid.len() as u32,
};
nss_generate_key(
CKM_EC_EDWARDS_KEY_PAIR_GEN,
&mut params_item as *mut SECItemStr as *mut std::ffi::c_void,
)
}
pub fn generate_ed448() -> Result<Self, PrivateKeyError> {
let oid: &[u8] = &[0x06, 0x03, 0x2b, 0x65, 0x71];
let mut params_item = SECItemStr {
type_: SECItemType::siBuffer,
data: oid.as_ptr() as *mut _,
len: oid.len() as u32,
};
nss_generate_key(
CKM_EC_EDWARDS_KEY_PAIR_GEN,
&mut params_item as *mut SECItemStr as *mut std::ffi::c_void,
)
}
pub fn from_ec_private_scalar(
d: &[u8],
x: &[u8],
y: &[u8],
curve: &str,
) -> Result<Self, PrivateKeyError> {
let curve_oid_components: &[u32] = match curve {
"P-256" => crate::oids::EC_CURVE_P256,
"P-384" => crate::oids::EC_CURVE_P384,
"P-521" => crate::oids::EC_CURVE_P521,
other => {
return Err(key_err(format!(
"unsupported EC curve: '{other}'; expected P-256, P-384, or P-521"
)))
}
};
let pkcs8_der = nss_ec_pkcs8_from_components(d, x, y, curve_oid_components)
.map_err(|e| key_err(e.to_string()))?;
Self::from_der(&pkcs8_der)
}
pub fn from_rsa_private_components(
components: &crate::crypto::RsaPrivateComponents<'_>,
) -> Result<Self, PrivateKeyError> {
let pkcs8_der =
nss_rsa_pkcs8_from_components(components).map_err(|e| key_err(e.to_string()))?;
Self::from_der(&pkcs8_der)
}
}