use super::signature::{hash_alg_to_digest, OpensslCertificateSigner};
use super::OpensslKeyError;
use crate::crypto::CertificateSigner;
use native_ossl::digest::DigestAlg;
use native_ossl::pkey::{KeygenCtx, Pkey, Private};
#[derive(Debug)]
pub enum OpensslKeyIdHasherError {
UnsupportedAlgorithm(String),
Openssl(native_ossl::error::ErrorStack),
}
impl std::fmt::Display for OpensslKeyIdHasherError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
OpensslKeyIdHasherError::UnsupportedAlgorithm(s) => {
write!(f, "unsupported key-id hash algorithm: {s}")
}
OpensslKeyIdHasherError::Openssl(e) => write!(f, "OpenSSL error: {e}"),
}
}
}
impl std::error::Error for OpensslKeyIdHasherError {}
impl From<native_ossl::error::ErrorStack> for OpensslKeyIdHasherError {
fn from(e: native_ossl::error::ErrorStack) -> Self {
OpensslKeyIdHasherError::Openssl(e)
}
}
pub struct OpensslKeyIdHasher;
impl crate::crypto::KeyIdHasher for OpensslKeyIdHasher {
type Error = OpensslKeyIdHasherError;
fn hash(&self, algorithm_oid: &[u32], data: &[u8]) -> Result<Vec<u8>, OpensslKeyIdHasherError> {
let md: DigestAlg = hash_alg_to_digest(algorithm_oid).ok_or_else(|| {
OpensslKeyIdHasherError::UnsupportedAlgorithm(format!(
"OID {:?} is not a supported hash algorithm for key identifier generation",
algorithm_oid
))
})?;
md.digest_to_vec(data)
.map_err(OpensslKeyIdHasherError::from)
}
}
impl crate::crypto::ErasedSignatureVerifier for super::signature::OpensslSignatureVerifier {
fn verify_certificate_signature_erased(
&self,
tbs_der: &[u8],
sig_alg_der: &[u8],
signature_bits: &[u8],
issuer_spki_der: &[u8],
) -> Result<(), crate::crypto::PrivateKeyError> {
use crate::crypto::SignatureVerifier as _;
self.verify_certificate_signature(tbs_der, sig_alg_der, signature_bits, issuer_spki_der)
.map_err(crate::crypto::PrivateKeyError::new)
}
}
impl crate::crypto::ErasedKeyIdHasher for OpensslKeyIdHasher {
fn hash_erased(
&self,
algorithm_oid: &[u32],
data: &[u8],
) -> Result<Vec<u8>, crate::crypto::PrivateKeyError> {
use crate::crypto::KeyIdHasher as _;
self.hash(algorithm_oid, data)
.map_err(crate::crypto::PrivateKeyError::new)
}
}
pub(crate) fn openssl_signature_verifier() -> Box<dyn crate::crypto::ErasedSignatureVerifier> {
Box::new(super::signature::OpensslSignatureVerifier)
}
pub(crate) fn openssl_key_id_hasher() -> Box<dyn crate::crypto::ErasedKeyIdHasher> {
Box::new(OpensslKeyIdHasher)
}
pub struct OpensslPrivateKey {
inner: Pkey<Private>,
}
impl OpensslPrivateKey {
pub fn from_pkey(pkey: Pkey<Private>) -> Self {
Self { inner: pkey }
}
pub fn pkey(&self) -> &Pkey<Private> {
&self.inner
}
}
impl crate::crypto::PrivateKey for OpensslPrivateKey {
fn public_key_spki_der(&self) -> Result<Vec<u8>, crate::crypto::PrivateKeyError> {
self.inner
.public_key_to_der()
.map_err(crate::crypto::PrivateKeyError::new)
}
fn as_signer(&self, algorithm: &str) -> Box<dyn crate::crypto::ErasedCertificateSigner> {
Box::new(OpensslErasedSigner {
inner: self.inner.clone(),
algorithm: algorithm.to_string(),
})
}
}
struct OpensslErasedSigner {
inner: Pkey<Private>,
algorithm: String,
}
impl crate::crypto::ErasedCertificateSigner for OpensslErasedSigner {
fn signature_algorithm_der_erased(&self) -> Result<Vec<u8>, crate::crypto::PrivateKeyError> {
OpensslCertificateSigner::new(&self.inner, &self.algorithm)
.signature_algorithm_der()
.map_err(crate::crypto::PrivateKeyError::new)
}
fn sign_tbs_erased(&self, tbs_der: &[u8]) -> Result<Vec<u8>, crate::crypto::PrivateKeyError> {
OpensslCertificateSigner::new(&self.inner, &self.algorithm)
.sign_tbs(tbs_der)
.map_err(crate::crypto::PrivateKeyError::new)
}
}
fn is_composite_mldsa_pkcs8(der: &[u8]) -> bool {
let Ok(pki) = crate::pkcs8_types::PrivateKeyInfo::from_der(der) else {
return false;
};
let comps = pki.private_key_algorithm.algorithm.components();
crate::crypto::composite_mldsa::composite_spec_from_oid(comps).is_some()
}
#[cfg(feature = "openssl")]
impl crate::crypto::BackendPrivateKey {
pub(crate) fn with_pkey_cache(spki_der: Vec<u8>, pkey: Pkey<Private>) -> Self {
Self {
pkcs8_der: std::sync::OnceLock::new(),
spki_cache: Some(spki_der),
pkey: Some(pkey),
pkcs11: None,
}
}
pub fn from_pem(
pem: &[u8],
password: Option<&[u8]>,
) -> Result<Self, crate::crypto::PrivateKeyError> {
let (pkey, pkcs8_der) = crate::openssl_backend::priv_pem_to_pkey_and_pkcs8(pem, password)
.map_err(crate::crypto::PrivateKeyError::new)?;
let cell = std::sync::OnceLock::new();
cell.set(pkcs8_der).expect("fresh OnceLock");
Ok(Self {
pkcs8_der: cell,
spki_cache: None,
pkey: Some(pkey),
pkcs11: None,
})
}
pub fn from_der(der: &[u8]) -> Result<Self, crate::crypto::PrivateKeyError> {
if is_composite_mldsa_pkcs8(der) {
return Ok(Self::from_pkcs8_der_unchecked(der.to_vec()));
}
let pkey = crate::openssl_backend::parse_private_key(der)
.map_err(crate::crypto::PrivateKeyError::new)?;
let cell = std::sync::OnceLock::new();
cell.set(der.to_vec()).expect("fresh OnceLock");
Ok(Self {
pkcs8_der: cell,
spki_cache: None,
pkey: Some(pkey),
pkcs11: None,
})
}
pub fn from_pkcs8_encrypted(
data: &[u8],
password: &[u8],
) -> Result<Self, crate::crypto::PrivateKeyError> {
let (pkey, pkcs8_der) =
crate::openssl_backend::priv_from_pkcs8_encrypted_to_pkey_and_der(data, password)
.map_err(crate::crypto::PrivateKeyError::new)?;
let cell = std::sync::OnceLock::new();
cell.set(pkcs8_der).expect("fresh OnceLock");
Ok(Self {
pkcs8_der: cell,
spki_cache: None,
pkey: Some(pkey),
pkcs11: None,
})
}
pub fn to_pem(
&self,
password: Option<&[u8]>,
) -> Result<Vec<u8>, crate::crypto::PrivateKeyError> {
crate::openssl_backend::priv_pkcs8_der_to_pem(self.pkcs8_bytes(), password)
.map_err(crate::crypto::PrivateKeyError::new)
}
pub fn to_der(&self) -> Result<Vec<u8>, crate::crypto::PrivateKeyError> {
Ok(self.pkcs8_bytes().to_vec())
}
pub fn to_pkcs8_encrypted(
&self,
password: &[u8],
) -> Result<Vec<u8>, crate::crypto::PrivateKeyError> {
crate::openssl_backend::priv_to_pkcs8_encrypted(self.pkcs8_bytes(), password)
.map_err(crate::crypto::PrivateKeyError::new)
}
pub fn key_type(&self) -> &'static str {
crate::openssl_backend::priv_key_type(self.pkcs8_bytes())
}
pub fn key_bit_size(&self) -> Option<i64> {
crate::openssl_backend::priv_key_bit_size(self.pkcs8_bytes())
}
pub fn public_key(
&self,
) -> Result<crate::crypto::BackendPublicKey, crate::crypto::PrivateKeyError> {
let spki_der = if let Some(cached) = &self.spki_cache {
cached.clone()
} else {
crate::openssl_backend::priv_public_key_spki_der(self.pkcs8_bytes())
.map_err(crate::crypto::PrivateKeyError::new)?
};
let pkey = crate::openssl_backend::parse_public_key(&spki_der).ok();
Ok(crate::crypto::BackendPublicKey { spki_der, pkey })
}
pub fn rsa_oaep_decrypt(
&self,
ciphertext: &[u8],
hash_alg: &str,
) -> Result<Vec<u8>, crate::crypto::PrivateKeyError> {
crate::openssl_backend::priv_rsa_oaep_decrypt(self.pkcs8_bytes(), ciphertext, hash_alg)
.map_err(crate::crypto::PrivateKeyError::new)
}
pub fn rsa_pkcs1v15_decrypt(
&self,
ciphertext: &[u8],
) -> Result<Vec<u8>, crate::crypto::PrivateKeyError> {
crate::openssl_backend::priv_rsa_pkcs1v15_decrypt(self.pkcs8_bytes(), ciphertext)
.map_err(crate::crypto::PrivateKeyError::new)
}
pub fn generate_rsa(
key_size: u32,
public_exponent: u32,
) -> Result<Self, crate::crypto::PrivateKeyError> {
crate::openssl_backend::priv_generate_rsa(key_size, public_exponent)
.map_err(crate::crypto::PrivateKeyError::new)
}
pub fn generate_ec(curve: &str) -> Result<Self, crate::crypto::PrivateKeyError> {
crate::openssl_backend::priv_generate_ec(curve).map_err(crate::crypto::PrivateKeyError::new)
}
pub fn generate_ed25519() -> Result<Self, crate::crypto::PrivateKeyError> {
crate::openssl_backend::priv_generate_ed25519().map_err(crate::crypto::PrivateKeyError::new)
}
pub fn generate_ed448() -> Result<Self, crate::crypto::PrivateKeyError> {
crate::openssl_backend::priv_generate_ed448().map_err(crate::crypto::PrivateKeyError::new)
}
pub fn from_ec_private_scalar(
d: &[u8],
x: &[u8],
y: &[u8],
curve: &str,
) -> Result<Self, crate::crypto::PrivateKeyError> {
crate::openssl_backend::priv_ec_from_components(d, x, y, curve)
.map_err(crate::crypto::PrivateKeyError::new)
}
pub fn from_rsa_private_components(
components: &crate::crypto::RsaPrivateComponents<'_>,
) -> Result<Self, crate::crypto::PrivateKeyError> {
crate::openssl_backend::priv_rsa_from_components(components)
.map_err(crate::crypto::PrivateKeyError::new)
}
}
pub(crate) fn generate_private_key(
spec: &crate::crypto::KeySpec,
) -> Result<OpensslPrivateKey, OpensslKeyError> {
match spec {
crate::crypto::KeySpec::Ec(curve) => {
use native_ossl::params::ParamBuilder;
let curve_cstr: &std::ffi::CStr = match curve.as_str() {
"P-256" => c"P-256",
"P-384" => c"P-384",
"P-521" => c"P-521",
other => {
return Err(OpensslKeyError(format!(
"unsupported EC curve: {other}; expected one of P-256, P-384, P-521"
)))
}
};
let params = ParamBuilder::new()
.map_err(|e| OpensslKeyError(e.to_string()))?
.push_utf8_string(c"group", curve_cstr)
.map_err(|e| OpensslKeyError(e.to_string()))?
.build()
.map_err(|e| OpensslKeyError(e.to_string()))?;
let mut kgen = KeygenCtx::new(c"EC").map_err(|e| OpensslKeyError(e.to_string()))?;
kgen.set_params(¶ms)
.map_err(|e| OpensslKeyError(e.to_string()))?;
let pkey = kgen
.generate()
.map_err(|e| OpensslKeyError(e.to_string()))?;
Ok(OpensslPrivateKey::from_pkey(pkey))
}
crate::crypto::KeySpec::Rsa(bits) => {
use native_ossl::params::ParamBuilder;
let params = ParamBuilder::new()
.map_err(|e| OpensslKeyError(e.to_string()))?
.push_uint(c"bits", *bits)
.map_err(|e| OpensslKeyError(e.to_string()))?
.push_uint(c"e", 65537u32)
.map_err(|e| OpensslKeyError(e.to_string()))?
.build()
.map_err(|e| OpensslKeyError(e.to_string()))?;
let mut kgen = KeygenCtx::new(c"RSA").map_err(|e| OpensslKeyError(e.to_string()))?;
kgen.set_params(¶ms)
.map_err(|e| OpensslKeyError(e.to_string()))?;
let pkey = kgen
.generate()
.map_err(|e| OpensslKeyError(e.to_string()))?;
Ok(OpensslPrivateKey::from_pkey(pkey))
}
crate::crypto::KeySpec::Ed25519 => {
let mut kgen =
KeygenCtx::new(c"ED25519").map_err(|e| OpensslKeyError(e.to_string()))?;
let pkey = kgen
.generate()
.map_err(|e| OpensslKeyError(e.to_string()))?;
Ok(OpensslPrivateKey::from_pkey(pkey))
}
crate::crypto::KeySpec::Ed448 => {
let mut kgen = KeygenCtx::new(c"ED448").map_err(|e| OpensslKeyError(e.to_string()))?;
let pkey = kgen
.generate()
.map_err(|e| OpensslKeyError(e.to_string()))?;
Ok(OpensslPrivateKey::from_pkey(pkey))
}
#[cfg(feature = "pqc")]
crate::crypto::KeySpec::MlDsa(ps) => {
#[cfg(ossl_mldsa)]
{
let name: &std::ffi::CStr = match ps.as_str() {
"ML-DSA-44" => c"ML-DSA-44",
"ML-DSA-65" => c"ML-DSA-65",
"ML-DSA-87" => c"ML-DSA-87",
other => {
return Err(OpensslKeyError(format!(
"unsupported ML-DSA parameter set: {other}; \
expected ML-DSA-44, ML-DSA-65, or ML-DSA-87"
)))
}
};
let mut kgen = KeygenCtx::new(name).map_err(|e| OpensslKeyError(e.to_string()))?;
let pkey = kgen
.generate()
.map_err(|e| OpensslKeyError(e.to_string()))?;
Ok(OpensslPrivateKey::from_pkey(pkey))
}
#[cfg(not(ossl_mldsa))]
{
let _ = ps;
Err(OpensslKeyError(
"ML-DSA key generation requires OpenSSL with ML-DSA support".to_string(),
))
}
}
#[cfg(not(feature = "pqc"))]
crate::crypto::KeySpec::MlDsa(_) => Err(OpensslKeyError(
"ML-DSA key generation requires the 'pqc' feature".to_string(),
)),
#[cfg(feature = "pqc")]
crate::crypto::KeySpec::MlKem(ps) => {
#[cfg(all(ossl320, ossl_mlkem))]
{
let name: &std::ffi::CStr = match ps.as_str() {
"ML-KEM-512" => c"ML-KEM-512",
"ML-KEM-768" => c"ML-KEM-768",
"ML-KEM-1024" => c"ML-KEM-1024",
other => {
return Err(OpensslKeyError(format!(
"unsupported ML-KEM parameter set: {other}; \
expected ML-KEM-512, ML-KEM-768, or ML-KEM-1024"
)))
}
};
let mut kgen = KeygenCtx::new(name).map_err(|e| OpensslKeyError(e.to_string()))?;
let pkey = kgen
.generate()
.map_err(|e| OpensslKeyError(e.to_string()))?;
Ok(OpensslPrivateKey::from_pkey(pkey))
}
#[cfg(not(all(ossl320, ossl_mlkem)))]
{
let _ = ps;
Err(OpensslKeyError(
"ML-KEM key generation requires OpenSSL 3.2+ with ML-KEM support".to_string(),
))
}
}
#[cfg(not(feature = "pqc"))]
crate::crypto::KeySpec::MlKem(_) => Err(OpensslKeyError(
"ML-KEM key generation requires the 'pqc' feature".to_string(),
)),
#[cfg(any(feature = "openssl", feature = "nss"))]
crate::crypto::KeySpec::CompositeMlDsa(_) => Err(OpensslKeyError(
"composite ML-DSA keys must be generated via BackendPrivateKey::generate_composite_ml_dsa"
.to_string(),
)),
}
}
#[cfg(test)]
mod tests {
use crate::crypto::BackendPrivateKey;
#[test]
fn ec_private_scalar_roundtrip_p256() {
let original = BackendPrivateKey::generate_ec("P-256").unwrap();
let pub_original = original.public_key().unwrap();
let pem = original.to_pem(None).unwrap();
let reloaded = BackendPrivateKey::from_pem(&pem, None).unwrap();
let pub_reloaded = reloaded.public_key().unwrap();
assert_eq!(pub_original.spki_der(), pub_reloaded.spki_der());
}
#[test]
fn ec_wrong_curve_returns_error() {
let dummy = [0u8; 32];
let result = BackendPrivateKey::from_ec_private_scalar(&dummy, &dummy, &dummy, "P-999");
assert!(result.is_err());
let msg = result.unwrap_err().to_string();
assert!(msg.contains("P-999") || msg.contains("curve"), "err: {msg}");
}
#[test]
fn rsa_private_components_roundtrip() {
let original = BackendPrivateKey::generate_rsa(2048, 65537).unwrap();
let pem = original.to_pem(None).unwrap();
let reloaded = BackendPrivateKey::from_pem(&pem, None).unwrap();
let pub_original = original.public_key().unwrap();
let pub_reloaded = reloaded.public_key().unwrap();
assert_eq!(pub_original.spki_der(), pub_reloaded.spki_der());
}
#[test]
fn from_ec_private_scalar_rebuild_matches_original() {
let k1 = BackendPrivateKey::generate_ec("P-256").unwrap();
let pem = k1.to_pem(None).unwrap();
let k2 = BackendPrivateKey::from_pem(&pem, None).unwrap();
assert_eq!(
k1.public_key().unwrap().spki_der(),
k2.public_key().unwrap().spki_der()
);
}
}