use super::errors::PrivateKeyError;
use super::signature::CertificateSigner;
#[cfg(not(feature = "openssl"))]
use super::errors::NoCryptoError;
#[cfg(not(any(feature = "openssl", feature = "nss")))]
use super::errors::NoSignerError;
pub trait ErasedCertificateSigner {
fn signature_algorithm_der_erased(&self) -> Result<Vec<u8>, PrivateKeyError>;
fn sign_tbs_erased(&self, tbs_der: &[u8]) -> Result<Vec<u8>, PrivateKeyError>;
}
impl CertificateSigner for dyn ErasedCertificateSigner + '_ {
type Error = PrivateKeyError;
fn signature_algorithm_der(&self) -> Result<Vec<u8>, PrivateKeyError> {
self.signature_algorithm_der_erased()
}
fn sign_tbs(&self, tbs_der: &[u8]) -> Result<Vec<u8>, PrivateKeyError> {
self.sign_tbs_erased(tbs_der)
}
}
impl CertificateSigner for Box<dyn ErasedCertificateSigner> {
type Error = PrivateKeyError;
fn signature_algorithm_der(&self) -> Result<Vec<u8>, PrivateKeyError> {
self.as_ref().signature_algorithm_der_erased()
}
fn sign_tbs(&self, tbs_der: &[u8]) -> Result<Vec<u8>, PrivateKeyError> {
self.as_ref().sign_tbs_erased(tbs_der)
}
}
#[cfg(any(feature = "openssl", feature = "nss"))]
pub(crate) struct FailedKeySigner(pub String);
#[cfg(any(feature = "openssl", feature = "nss"))]
#[derive(Debug)]
struct FailedKeySignerError(String);
#[cfg(any(feature = "openssl", feature = "nss"))]
impl std::fmt::Display for FailedKeySignerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[cfg(any(feature = "openssl", feature = "nss"))]
impl std::error::Error for FailedKeySignerError {}
#[cfg(any(feature = "openssl", feature = "nss"))]
impl ErasedCertificateSigner for FailedKeySigner {
fn signature_algorithm_der_erased(&self) -> Result<Vec<u8>, PrivateKeyError> {
Err(PrivateKeyError::new(FailedKeySignerError(self.0.clone())))
}
fn sign_tbs_erased(&self, _tbs_der: &[u8]) -> Result<Vec<u8>, PrivateKeyError> {
Err(PrivateKeyError::new(FailedKeySignerError(self.0.clone())))
}
}
#[cfg(not(any(feature = "openssl", feature = "nss")))]
pub(crate) struct NoErasedSigner;
#[cfg(not(any(feature = "openssl", feature = "nss")))]
impl ErasedCertificateSigner for NoErasedSigner {
fn signature_algorithm_der_erased(&self) -> Result<Vec<u8>, PrivateKeyError> {
Err(PrivateKeyError::new(NoSignerError))
}
fn sign_tbs_erased(&self, _tbs_der: &[u8]) -> Result<Vec<u8>, PrivateKeyError> {
Err(PrivateKeyError::new(NoSignerError))
}
}
pub trait PrivateKey {
fn public_key_spki_der(&self) -> Result<Vec<u8>, PrivateKeyError>;
fn as_signer(&self, algorithm: &str) -> Box<dyn ErasedCertificateSigner>;
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum KeySpec {
Ec(String),
Rsa(u32),
Ed25519,
Ed448,
MlDsa(String),
MlKem(String),
#[cfg(any(feature = "openssl", feature = "nss"))]
CompositeMlDsa(u32),
}
pub struct PrivateKeyBuilder {
pub spec: KeySpec,
}
impl PrivateKeyBuilder {
pub fn ec(curve: &str) -> Self {
Self {
spec: KeySpec::Ec(curve.to_string()),
}
}
pub fn rsa(bits: u32) -> Self {
Self {
spec: KeySpec::Rsa(bits),
}
}
pub fn ed25519() -> Self {
Self {
spec: KeySpec::Ed25519,
}
}
pub fn ed448() -> Self {
Self {
spec: KeySpec::Ed448,
}
}
pub fn ml_dsa(parameter_set: &str) -> Self {
Self {
spec: KeySpec::MlDsa(parameter_set.to_string()),
}
}
pub fn ml_kem(parameter_set: &str) -> Self {
Self {
spec: KeySpec::MlKem(parameter_set.to_string()),
}
}
#[cfg(any(feature = "openssl", feature = "nss"))]
pub fn composite_ml_dsa(sub_arc: u32) -> Self {
Self {
spec: KeySpec::CompositeMlDsa(sub_arc),
}
}
pub fn generate(&self) -> Result<Box<dyn PrivateKey>, PrivateKeyError> {
#[cfg(feature = "openssl")]
{
if let KeySpec::CompositeMlDsa(sub_arc) = &self.spec {
return BackendPrivateKey::generate_composite_ml_dsa(*sub_arc)
.map(|k| Box::new(k) as Box<dyn PrivateKey>);
}
crate::openssl_backend::generate_private_key(&self.spec)
.map(|k| Box::new(k) as Box<dyn PrivateKey>)
.map_err(PrivateKeyError::new)
}
#[cfg(all(not(feature = "openssl"), feature = "nss"))]
{
if let KeySpec::CompositeMlDsa(sub_arc) = &self.spec {
return BackendPrivateKey::generate_composite_ml_dsa(*sub_arc)
.map(|k| Box::new(k) as Box<dyn PrivateKey>);
}
let _ = &self.spec;
Err(PrivateKeyError::new(NoCryptoError))
}
#[cfg(not(any(feature = "openssl", feature = "nss")))]
{
let _ = &self.spec;
Err(PrivateKeyError::new(NoCryptoError))
}
}
}
#[derive(Clone)]
pub struct BackendPublicKey {
pub(crate) spki_der: Vec<u8>,
#[cfg(feature = "openssl")]
pub(crate) pkey: Option<native_ossl::pkey::Pkey<native_ossl::pkey::Public>>,
}
impl std::fmt::Debug for BackendPublicKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BackendPublicKey")
.field("spki_der_len", &self.spki_der.len())
.finish_non_exhaustive()
}
}
impl BackendPublicKey {
pub fn from_spki_der(spki_der: Vec<u8>) -> Self {
Self {
spki_der,
#[cfg(feature = "openssl")]
pkey: None,
}
}
pub fn spki_der(&self) -> &[u8] {
&self.spki_der
}
#[cfg(feature = "openssl")]
pub fn from_pem(pem: &[u8]) -> Result<Self, crate::openssl_backend::OpensslKeyError> {
let pkey = crate::openssl_backend::parse_public_key_from_pem(pem)?;
let spki_der = pkey.public_key_to_der()?;
Ok(Self {
spki_der,
pkey: Some(pkey),
})
}
#[cfg(feature = "openssl")]
pub fn from_der(der: &[u8]) -> Result<Self, crate::openssl_backend::OpensslKeyError> {
let pkey = crate::openssl_backend::parse_public_key(der)?;
Ok(Self {
spki_der: der.to_vec(),
pkey: Some(pkey),
})
}
#[cfg(feature = "openssl")]
pub fn from_rsa_components(
n: &[u8],
e: &[u8],
) -> Result<Self, crate::openssl_backend::OpensslKeyError> {
let spki_der = crate::openssl_backend::pub_rsa_from_components(n, e)?;
let pkey = crate::openssl_backend::parse_public_key(&spki_der)?;
Ok(Self {
spki_der,
pkey: Some(pkey),
})
}
#[cfg(feature = "openssl")]
pub fn from_ec_components(
x: &[u8],
y: &[u8],
curve: &str,
) -> Result<Self, crate::openssl_backend::OpensslKeyError> {
let spki_der = crate::openssl_backend::pub_ec_from_components(x, y, curve)?;
let pkey = crate::openssl_backend::parse_public_key(&spki_der)?;
Ok(Self {
spki_der,
pkey: Some(pkey),
})
}
#[cfg(feature = "openssl")]
pub fn to_pem(&self) -> Result<Vec<u8>, crate::openssl_backend::OpensslKeyError> {
Ok(crate::openssl_backend::parse_public_key(&self.spki_der)?.to_pem()?)
}
#[cfg(feature = "openssl")]
pub fn to_der(&self) -> Result<Vec<u8>, crate::openssl_backend::OpensslKeyError> {
Ok(self.spki_der.clone())
}
#[cfg(feature = "openssl")]
pub fn key_type(&self) -> &'static str {
crate::openssl_backend::pub_key_type(&self.spki_der)
}
#[cfg(feature = "openssl")]
pub fn key_bit_size(&self) -> Option<i64> {
crate::openssl_backend::pub_key_bit_size(&self.spki_der)
}
#[cfg(feature = "openssl")]
pub fn rsa_modulus(&self) -> Result<Option<Vec<u8>>, crate::openssl_backend::OpensslKeyError> {
crate::openssl_backend::pub_rsa_modulus(&self.spki_der)
}
#[cfg(feature = "openssl")]
pub fn rsa_public_exponent(
&self,
) -> Result<Option<Vec<u8>>, crate::openssl_backend::OpensslKeyError> {
crate::openssl_backend::pub_rsa_public_exponent(&self.spki_der)
}
#[cfg(feature = "openssl")]
pub fn ec_curve_name(
&self,
) -> Result<Option<&'static str>, crate::openssl_backend::OpensslKeyError> {
crate::openssl_backend::pub_ec_curve_name(&self.spki_der)
}
#[cfg(feature = "openssl")]
pub fn ec_affine_coordinates(
&self,
) -> Result<
Option<crate::openssl_backend::EcAffineCoords>,
crate::openssl_backend::OpensslKeyError,
> {
crate::openssl_backend::pub_ec_affine_coordinates(&self.spki_der)
}
#[cfg(feature = "openssl")]
pub fn rsa_oaep_encrypt(
&self,
plaintext: &[u8],
hash_alg: &str,
) -> Result<Vec<u8>, crate::openssl_backend::OpensslKeyError> {
crate::openssl_backend::pub_rsa_oaep_encrypt(&self.spki_der, plaintext, hash_alg)
}
#[cfg(all(feature = "nss", not(feature = "openssl")))]
pub fn rsa_oaep_encrypt(
&self,
plaintext: &[u8],
hash_alg: &str,
) -> Result<Vec<u8>, crate::nss_backend::rsa_transport::NssRsaError> {
crate::nss_backend::nss_rsa_oaep_encrypt(&self.spki_der, plaintext, hash_alg)
}
#[cfg(feature = "openssl")]
pub fn rsa_pkcs1v15_encrypt(
&self,
plaintext: &[u8],
) -> Result<Vec<u8>, crate::openssl_backend::OpensslKeyError> {
crate::openssl_backend::pub_rsa_pkcs1v15_encrypt(&self.spki_der, plaintext)
}
#[cfg(all(feature = "nss", not(feature = "openssl")))]
pub fn rsa_pkcs1v15_encrypt(
&self,
plaintext: &[u8],
) -> Result<Vec<u8>, crate::nss_backend::rsa_transport::NssRsaError> {
crate::nss_backend::nss_rsa_pkcs1v15_encrypt(&self.spki_der, plaintext)
}
#[cfg(feature = "openssl")]
pub fn verify_message(
&self,
data: &[u8],
signature: &[u8],
algorithm: Option<&str>,
) -> Result<(), crate::openssl_backend::OpensslKeyError> {
crate::openssl_backend::pub_verify_message(&self.spki_der, data, signature, algorithm)
}
pub fn verify_signature(
&self,
tbs_der: &[u8],
sig_alg_der: &[u8],
signature: &[u8],
) -> Result<(), PrivateKeyError> {
#[cfg(all(feature = "nss", not(feature = "openssl")))]
{
use super::signature::SignatureVerifier as _;
crate::nss_backend::NssSignatureVerifier
.verify_certificate_signature(tbs_der, sig_alg_der, signature, &self.spki_der)
.map_err(PrivateKeyError::new)
}
#[cfg(feature = "openssl")]
{
if let Some(ref pkey) = self.pkey {
crate::openssl_backend::verify_with_cached_pkey(
pkey,
tbs_der,
sig_alg_der,
signature,
)
.map_err(PrivateKeyError::new)
} else {
crate::openssl_backend::openssl_signature_verifier()
.verify_certificate_signature_erased(
tbs_der,
sig_alg_der,
signature,
&self.spki_der,
)
}
}
#[cfg(not(any(feature = "openssl", feature = "nss")))]
{
let _ = (tbs_der, sig_alg_der, signature);
Err(PrivateKeyError::new(super::errors::NoCryptoError))
}
}
pub fn verify_ml_dsa_with_context(
&self,
data: &[u8],
signature: &[u8],
context: &[u8],
) -> Result<(), PrivateKeyError> {
#[cfg(feature = "openssl")]
{
crate::openssl_backend::pub_verify_ml_dsa_with_context(
&self.spki_der,
data,
signature,
context,
)
.map_err(PrivateKeyError::new)
}
#[cfg(not(feature = "openssl"))]
{
let _ = (data, signature, context);
Err(PrivateKeyError::new(NoCryptoError))
}
}
pub fn ml_kem_encapsulate(&self) -> Result<(Vec<u8>, Vec<u8>), PrivateKeyError> {
#[cfg(feature = "openssl")]
{
crate::openssl_backend::pub_ml_kem_encapsulate(&self.spki_der)
.map_err(PrivateKeyError::new)
}
#[cfg(not(feature = "openssl"))]
{
Err(PrivateKeyError::new(NoCryptoError))
}
}
}
#[derive(Debug)]
pub struct RsaPrivateComponents<'a> {
pub n: &'a [u8],
pub e: &'a [u8],
pub d: &'a [u8],
pub p: &'a [u8],
pub q: &'a [u8],
pub dp: &'a [u8],
pub dq: &'a [u8],
pub qi: &'a [u8],
}
#[derive(Clone)]
pub struct BackendPrivateKey {
#[allow(dead_code)]
pub(crate) pkcs8_der: std::sync::OnceLock<Vec<u8>>,
#[allow(dead_code)]
pub(crate) spki_cache: Option<Vec<u8>>,
#[cfg(feature = "openssl")]
#[allow(dead_code)]
pub(crate) pkey: Option<native_ossl::pkey::Pkey<native_ossl::pkey::Private>>,
#[allow(dead_code)]
pub(crate) pkcs11: Option<crate::pkcs11_uri::Pkcs11Uri>,
}
impl std::fmt::Debug for BackendPrivateKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BackendPrivateKey")
.field("pkcs8_der_len", &self.pkcs8_der.get().map(|v| v.len()))
.field("has_spki_cache", &self.spki_cache.is_some())
.field("is_pkcs11", &self.pkcs11.is_some())
.finish_non_exhaustive()
}
}
impl BackendPrivateKey {
#[cfg(feature = "openssl")]
#[allow(dead_code)]
pub(crate) fn pkcs8_bytes(&self) -> &[u8] {
self.pkcs8_der.get_or_init(|| {
if let Some(ref pkey) = self.pkey {
return pkey
.to_pkcs8_der()
.expect("PKCS#8 serialisation of a valid key always succeeds");
}
panic!("BackendPrivateKey has neither cached PKCS#8 bytes nor a live Pkey");
})
}
#[cfg(all(feature = "nss", not(feature = "openssl")))]
#[allow(dead_code)]
pub(crate) fn pkcs8_bytes(&self) -> &[u8] {
self.pkcs8_der.get().map(|v| v.as_slice()).unwrap_or(&[])
}
pub fn from_pkcs8_der_unchecked(pkcs8_der: Vec<u8>) -> Self {
let cell = std::sync::OnceLock::new();
cell.set(pkcs8_der).expect("fresh OnceLock");
Self {
pkcs8_der: cell,
spki_cache: None,
#[cfg(feature = "openssl")]
pkey: None,
pkcs11: None,
}
}
pub fn generate_ml_dsa(parameter_set: &str) -> Result<Self, PrivateKeyError> {
#[cfg(feature = "openssl")]
{
crate::openssl_backend::priv_generate_ml_dsa(parameter_set)
.map_err(PrivateKeyError::new)
}
#[cfg(not(feature = "openssl"))]
{
let _ = parameter_set;
Err(PrivateKeyError::new(NoCryptoError))
}
}
pub fn generate_ml_kem(parameter_set: &str) -> Result<Self, PrivateKeyError> {
#[cfg(feature = "openssl")]
{
crate::openssl_backend::priv_generate_ml_kem(parameter_set)
.map_err(PrivateKeyError::new)
}
#[cfg(not(feature = "openssl"))]
{
let _ = parameter_set;
Err(PrivateKeyError::new(NoCryptoError))
}
}
pub fn ml_kem_decapsulate(&self, ciphertext: &[u8]) -> Result<Vec<u8>, PrivateKeyError> {
#[cfg(feature = "openssl")]
{
crate::openssl_backend::priv_ml_kem_decapsulate(self.pkcs8_bytes(), ciphertext)
.map_err(PrivateKeyError::new)
}
#[cfg(not(feature = "openssl"))]
{
let _ = ciphertext;
Err(PrivateKeyError::new(NoCryptoError))
}
}
pub fn from_pkcs11_uri(uri: &str) -> Result<Self, PrivateKeyError> {
#[cfg(feature = "openssl")]
{
crate::openssl_backend::priv_load_from_pkcs11_uri(uri).map_err(PrivateKeyError::new)
}
#[cfg(all(feature = "nss", not(feature = "openssl")))]
{
crate::nss_backend::priv_load_from_pkcs11_uri_nss(uri).map_err(PrivateKeyError::new)
}
#[cfg(not(any(feature = "openssl", feature = "nss")))]
{
let _ = uri;
Err(PrivateKeyError::new(NoCryptoError))
}
}
#[cfg(any(feature = "openssl", feature = "nss"))]
pub fn generate_composite_ml_dsa(sub_arc: u32) -> Result<Self, PrivateKeyError> {
#[cfg(feature = "openssl")]
{
crate::openssl_backend::priv_generate_composite_mldsa(sub_arc)
.map_err(PrivateKeyError::new)
}
#[cfg(all(not(feature = "openssl"), feature = "nss"))]
{
crate::nss_backend::priv_generate_composite_mldsa(sub_arc).map_err(PrivateKeyError::new)
}
}
pub fn sign_ml_dsa_with_context(
&self,
data: &[u8],
context: &[u8],
) -> Result<Vec<u8>, PrivateKeyError> {
#[cfg(feature = "openssl")]
{
crate::openssl_backend::priv_sign_ml_dsa_with_context(self.pkcs8_bytes(), data, context)
.map_err(PrivateKeyError::new)
}
#[cfg(not(feature = "openssl"))]
{
let _ = (data, context);
Err(PrivateKeyError::new(NoCryptoError))
}
}
}
impl PrivateKey for BackendPrivateKey {
fn public_key_spki_der(&self) -> Result<Vec<u8>, PrivateKeyError> {
if let Some(cached) = &self.spki_cache {
return Ok(cached.clone());
}
#[cfg(feature = "openssl")]
{
crate::openssl_backend::priv_public_key_spki_der(self.pkcs8_bytes())
.map_err(PrivateKeyError::new)
}
#[cfg(not(feature = "openssl"))]
{
Err(PrivateKeyError::new(NoCryptoError))
}
}
fn as_signer(&self, algorithm: &str) -> Box<dyn ErasedCertificateSigner> {
#[cfg(all(feature = "nss", not(feature = "openssl")))]
{
if let Some(oid) = super::utils::pkcs8_key_oid(self.pkcs8_bytes()) {
let comps = oid.components();
if comps.starts_with(crate::oids::COMPOSITE_MLDSA_ARC) && comps.len() == 9 {
let sub_arc = comps[8];
return if let Some(spec) =
crate::crypto::composite_mldsa::composite_spec(sub_arc)
{
crate::nss_backend::composite_mldsa_signer_from_pkcs8(
self.pkcs8_bytes(),
spec,
)
} else {
Box::new(FailedKeySigner(format!(
"unsupported composite ML-DSA sub-arc {sub_arc}"
)))
};
}
}
if let Some(pkcs11) = &self.pkcs11 {
if let Some(spki) = &self.spki_cache {
return crate::nss_backend::nss_hsm_signer(pkcs11, algorithm, spki);
}
return Box::new(crate::nss_backend::NssUnsupportedSigner);
}
if let Some(sig_alg_der) =
super::utils::sig_alg_der_from_pkcs8(self.pkcs8_bytes(), algorithm)
{
if let Some(nss_signer) =
crate::nss_backend::NssSigner::new(self.pkcs8_bytes().to_vec(), sig_alg_der)
{
return Box::new(nss_signer);
}
}
return Box::new(crate::nss_backend::NssUnsupportedSigner);
}
#[cfg(feature = "openssl")]
{
if let Some(oid) = super::utils::pkcs8_key_oid(self.pkcs8_bytes()) {
let comps = oid.components();
if comps.starts_with(crate::oids::COMPOSITE_MLDSA_ARC) && comps.len() == 9 {
let sub_arc = comps[8];
return if let Some(spec) =
crate::crypto::composite_mldsa::composite_spec(sub_arc)
{
crate::openssl_backend::composite_mldsa_signer_from_pkcs8(
self.pkcs8_bytes(),
spec,
)
} else {
Box::new(FailedKeySigner(format!(
"unsupported composite ML-DSA sub-arc {sub_arc}"
)))
};
}
}
use crate::openssl_backend::OpensslPrivateKey;
let pkey = if let Some(pkey) = &self.pkey {
pkey.clone()
} else {
match crate::openssl_backend::parse_private_key(self.pkcs8_bytes()) {
Ok(pkey) => pkey,
Err(e) => {
return Box::new(FailedKeySigner(format!("PKCS#8 parse failed: {e}")))
}
}
};
OpensslPrivateKey::from_pkey(pkey).as_signer(algorithm)
}
#[cfg(not(any(feature = "openssl", feature = "nss")))]
{
let _ = algorithm;
Box::new(NoErasedSigner)
}
}
}