use synta::{Decoder, Encoding};
use crate::crypto::{CertificateSigner, SignatureVerifier};
use crate::oids;
use crate::pkcs1_types::RsassaPssParams;
use crate::crypto::utils::split_alg_id;
use native_ossl::digest::DigestAlg;
use native_ossl::params::ParamBuilder;
#[cfg(all(ossl320, feature = "pqc", ossl_mldsa))]
use native_ossl::pkey::{MessageSigner, MessageVerifier, SigAlg};
use native_ossl::pkey::{Pkey, Private, Public, SignInit, Signer, Verifier};
#[cfg(all(feature = "pqc", ossl_mldsa))]
const ML_DSA_44_SIG_LEN: usize = 2420;
#[cfg(all(feature = "pqc", ossl_mldsa))]
const ML_DSA_65_SIG_LEN: usize = 3309;
#[cfg(all(feature = "pqc", ossl_mldsa))]
const ML_DSA_87_SIG_LEN: usize = 4627;
#[derive(Debug)]
pub struct OpensslVerifierError(pub(crate) String);
impl std::fmt::Display for OpensslVerifierError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
impl std::error::Error for OpensslVerifierError {}
impl From<native_ossl::error::ErrorStack> for OpensslVerifierError {
fn from(e: native_ossl::error::ErrorStack) -> Self {
OpensslVerifierError(e.to_string())
}
}
pub struct OpensslSignatureVerifier;
fn sig_alg_to_digest(oid: &[u32]) -> Option<DigestAlg> {
if oid == oids::SHA256_WITH_RSA || oid == oids::ECDSA_WITH_SHA256 {
super::alg_cache::sha256()
} else if oid == oids::SHA384_WITH_RSA || oid == oids::ECDSA_WITH_SHA384 {
super::alg_cache::sha384()
} else if oid == oids::SHA512_WITH_RSA || oid == oids::ECDSA_WITH_SHA512 {
super::alg_cache::sha512()
} else if oid == oids::SHA1_WITH_RSA || oid == oids::ECDSA_WITH_SHA1 {
super::alg_cache::sha1()
} else {
None
}
}
fn hash_oid_to_digest_name(oid: &[u32]) -> Option<&'static std::ffi::CStr> {
if oid == oids::ID_SHA1 {
Some(c"SHA1")
} else if oid == oids::ID_SHA256 {
Some(c"SHA2-256")
} else if oid == oids::ID_SHA384 {
Some(c"SHA2-384")
} else if oid == oids::ID_SHA512 {
Some(c"SHA2-512")
} else {
None
}
}
pub(super) fn hash_alg_to_digest(oid: &[u32]) -> Option<DigestAlg> {
hash_oid_to_digest_name(oid).and_then(super::alg_cache::digest_by_name)
}
fn verify_rsa_pss_sig(
pkey: &Pkey<Public>,
params_der: &[u8],
tbs_der: &[u8],
signature: &[u8],
) -> Result<(), OpensslVerifierError> {
let (hash_digest, hash_name, salt_len) = if !params_der.is_empty() {
let mut dec = Decoder::new(params_der, Encoding::Der);
match dec.decode::<RsassaPssParams>() {
Ok(pss) => {
let hash_oid = pss
.hash_algorithm
.as_ref()
.map(|h| h.algorithm.components().to_vec())
.unwrap_or_else(|| oids::ID_SHA1.to_vec());
let salt = pss
.salt_length
.as_ref()
.and_then(|i| i.as_i64().ok())
.unwrap_or(20) as i32;
let name = hash_oid_to_digest_name(&hash_oid).ok_or_else(|| {
OpensslVerifierError(format!("unsupported RSA-PSS hash OID: {:?}", hash_oid))
})?;
let d = super::alg_cache::digest_by_name(name).ok_or_else(|| {
OpensslVerifierError(format!("unsupported RSA-PSS hash: {name:?}"))
})?;
(d, name, salt)
}
Err(e) => {
return Err(OpensslVerifierError(format!(
"failed to decode RSA-PSS parameters: {e:?}"
)))
}
}
} else {
let d = super::alg_cache::sha256()
.ok_or_else(|| OpensslVerifierError("SHA2-256 not available".to_string()))?;
(d, c"SHA2-256", 32i32)
};
let pss_params = ParamBuilder::new()?
.push_utf8_string(c"pad-mode", c"pss")?
.push_utf8_string(c"mgf1-digest", hash_name)?
.push_int(c"saltlen", salt_len)?
.build()?;
let init = SignInit {
digest: Some(&hash_digest),
params: Some(&pss_params),
};
let mut ver = Verifier::new(pkey, &init)?;
ver.update(tbs_der)?;
if ver.verify(signature)? {
Ok(())
} else {
Err(OpensslVerifierError(
"RSA-PSS signature invalid".to_string(),
))
}
}
pub(crate) fn verify_with_cached_pkey(
pkey: &Pkey<Public>,
tbs_der: &[u8],
sig_alg_der: &[u8],
signature_bits: &[u8],
) -> Result<(), OpensslVerifierError> {
let (oid, _, params_der) = split_alg_id(sig_alg_der, |e| OpensslVerifierError(e.to_string()))?;
let oid_comps = oid.components();
if oid_comps == oids::ED25519 || oid_comps == oids::ED448 {
let mut ver = Verifier::new(pkey, &SignInit::default())?;
return if ver.verify_oneshot(tbs_der, signature_bits)? {
Ok(())
} else {
Err(OpensslVerifierError("EdDSA signature invalid".to_string()))
};
}
#[cfg(feature = "pqc")]
{
#[cfg(all(ossl_mldsa, ossl320))]
{
let alg_name: Option<&std::ffi::CStr> =
if oid_comps == oids::ML_DSA_44 && pkey.is_a(c"ML-DSA-44") {
Some(c"ML-DSA-44")
} else if oid_comps == oids::ML_DSA_65 && pkey.is_a(c"ML-DSA-65") {
Some(c"ML-DSA-65")
} else if oid_comps == oids::ML_DSA_87 && pkey.is_a(c"ML-DSA-87") {
Some(c"ML-DSA-87")
} else {
None
};
if let Some(name) = alg_name {
let mut alg = SigAlg::fetch(name, None)?;
let mut ver = MessageVerifier::new(pkey, &mut alg, None)?;
return if ver.verify(tbs_der, signature_bits)? {
Ok(())
} else {
Err(OpensslVerifierError("ML-DSA signature invalid".to_string()))
};
}
}
}
if oid_comps == oids::RSASSA_PSS {
return verify_rsa_pss_sig(pkey, params_der, tbs_der, signature_bits);
}
let digest = sig_alg_to_digest(oid_comps).ok_or_else(|| {
OpensslVerifierError(format!(
"unsupported signature algorithm OID: {:?}",
oid_comps
))
})?;
let init = SignInit {
digest: Some(&digest),
params: None,
};
let mut ver = Verifier::new(pkey, &init)?;
ver.update(tbs_der)?;
if ver.verify(signature_bits)? {
Ok(())
} else {
Err(OpensslVerifierError("signature invalid".to_string()))
}
}
fn do_verify_signature(
tbs_der: &[u8],
sig_alg_der: &[u8],
signature_bits: &[u8],
issuer_spki_der: &[u8],
) -> Result<(), OpensslVerifierError> {
{
let (sig_oid, _, _) = split_alg_id(sig_alg_der, |e| OpensslVerifierError(e.to_string()))?;
let comps = sig_oid.components();
if comps.starts_with(crate::oids::COMPOSITE_MLDSA_ARC) && comps.len() == 9 {
let sub_arc = comps[8];
return super::composite::verify_composite_mldsa_signature(
tbs_der,
sub_arc,
signature_bits,
issuer_spki_der,
);
}
}
let pkey = Pkey::<Public>::from_der(issuer_spki_der)?;
verify_with_cached_pkey(&pkey, tbs_der, sig_alg_der, signature_bits)
}
impl SignatureVerifier for OpensslSignatureVerifier {
type Error = OpensslVerifierError;
fn verify_certificate_signature(
&self,
tbs_der: &[u8],
sig_alg_der: &[u8],
signature_bits: &[u8],
issuer_spki_der: &[u8],
) -> Result<(), OpensslVerifierError> {
do_verify_signature(tbs_der, sig_alg_der, signature_bits, issuer_spki_der)
}
}
#[derive(Debug)]
pub struct OpensslCertificateSignerError(pub(super) String);
impl std::fmt::Display for OpensslCertificateSignerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
impl std::error::Error for OpensslCertificateSignerError {}
impl From<native_ossl::error::ErrorStack> for OpensslCertificateSignerError {
fn from(e: native_ossl::error::ErrorStack) -> Self {
OpensslCertificateSignerError(e.to_string())
}
}
pub struct OpensslCertificateSigner<'a> {
pub(super) key: &'a Pkey<Private>,
pub(super) algorithm: &'a str,
pub(super) context_string: Option<&'a [u8]>,
}
impl<'a> OpensslCertificateSigner<'a> {
pub fn new(key: &'a Pkey<Private>, algorithm: &'a str) -> Self {
Self {
key,
algorithm,
context_string: None,
}
}
#[must_use]
pub fn with_context(mut self, ctx: &'a [u8]) -> Self {
self.context_string = Some(ctx);
self
}
}
impl<'a> CertificateSigner for OpensslCertificateSigner<'a> {
type Error = OpensslCertificateSignerError;
fn signature_algorithm_der(&self) -> Result<Vec<u8>, OpensslCertificateSignerError> {
use crate::{oids, AlgorithmIdentifier};
use synta::{Element, Null, ObjectIdentifier};
#[cfg(feature = "pqc")]
{
#[cfg(ossl_mldsa)]
{
let ml_oid: Option<&[u32]> = if self.key.is_a(c"ML-DSA-44") {
Some(oids::ML_DSA_44)
} else if self.key.is_a(c"ML-DSA-65") {
Some(oids::ML_DSA_65)
} else if self.key.is_a(c"ML-DSA-87") {
Some(oids::ML_DSA_87)
} else {
None
};
if let Some(oid_comps) = ml_oid {
let sig_oid = ObjectIdentifier::new(oid_comps)
.map_err(|e| OpensslCertificateSignerError(e.to_string()))?;
let sig_alg = AlgorithmIdentifier {
algorithm: sig_oid,
parameters: None,
};
return sig_alg
.to_der()
.map_err(|e| OpensslCertificateSignerError(e.to_string()));
}
}
}
let (sig_oid_comps, null_params): (&[u32], bool) = if self.key.is_a(c"RSA") {
let comps = match self.algorithm {
"sha1" => oids::SHA1_WITH_RSA,
"sha256" => oids::SHA256_WITH_RSA,
"sha384" => oids::SHA384_WITH_RSA,
"sha512" => oids::SHA512_WITH_RSA,
other => {
return Err(OpensslCertificateSignerError(format!(
"unsupported hash for RSA signing: {other}; \
expected sha1, sha256, sha384, or sha512"
)))
}
};
(comps, true)
} else if self.key.is_a(c"EC") {
let comps = match self.algorithm {
"sha1" => oids::ECDSA_WITH_SHA1,
"sha256" => oids::ECDSA_WITH_SHA256,
"sha384" => oids::ECDSA_WITH_SHA384,
"sha512" => oids::ECDSA_WITH_SHA512,
other => {
return Err(OpensslCertificateSignerError(format!(
"unsupported hash for EC signing: {other}; \
expected sha1, sha256, sha384, or sha512"
)))
}
};
(comps, false)
} else if self.key.is_a(c"ED25519") {
(oids::ED25519, false)
} else if self.key.is_a(c"ED448") {
(oids::ED448, false)
} else {
return Err(OpensslCertificateSignerError(
"unsupported key type; expected RSA, EC, Ed25519, Ed448, or ML-DSA".to_string(),
));
};
let sig_oid = ObjectIdentifier::new(sig_oid_comps)
.map_err(|e| OpensslCertificateSignerError(e.to_string()))?;
let sig_alg = AlgorithmIdentifier {
algorithm: sig_oid,
parameters: if null_params {
Some(Element::Null(Null))
} else {
None
},
};
sig_alg
.to_der()
.map_err(|e| OpensslCertificateSignerError(e.to_string()))
}
fn sign_tbs(&self, tbs_der: &[u8]) -> Result<Vec<u8>, OpensslCertificateSignerError> {
#[cfg(feature = "pqc")]
{
#[cfg(ossl_mldsa)]
{
let ml_dsa_sig_len: Option<usize> = if self.key.is_a(c"ML-DSA-44") {
Some(ML_DSA_44_SIG_LEN)
} else if self.key.is_a(c"ML-DSA-65") {
Some(ML_DSA_65_SIG_LEN)
} else if self.key.is_a(c"ML-DSA-87") {
Some(ML_DSA_87_SIG_LEN)
} else {
None
};
if let Some(sig_len) = ml_dsa_sig_len {
#[cfg(ossl320)]
if let Some(ctx) = self.context_string {
let alg_name = if self.key.is_a(c"ML-DSA-44") {
c"ML-DSA-44"
} else if self.key.is_a(c"ML-DSA-65") {
c"ML-DSA-65"
} else {
c"ML-DSA-87"
};
let ctx_params = ParamBuilder::new()?
.push_octet_slice(c"context-string", ctx)?
.build()?;
let mut alg = SigAlg::fetch(alg_name, None)?;
let signer = MessageSigner::new(self.key, &mut alg, Some(&ctx_params))?;
let mut sig = vec![0u8; sig_len];
let written = signer.sign_oneshot(tbs_der, &mut sig)?;
sig.truncate(written);
return Ok(sig);
}
let mut signer = Signer::new(self.key, &SignInit::default())?;
let mut sig = vec![0u8; sig_len];
let written = signer.sign_into(tbs_der, &mut sig)?;
sig.truncate(written);
return Ok(sig);
}
}
}
if self.key.is_a(c"ED25519") || self.key.is_a(c"ED448") {
let mut signer = Signer::new(self.key, &SignInit::default())?;
return Ok(signer.sign_oneshot(tbs_der)?);
}
let md = super::alg_cache::digest_by_str(self.algorithm).ok_or_else(|| {
OpensslCertificateSignerError(format!("unsupported hash algorithm: {}", self.algorithm))
})?;
let init = SignInit {
digest: Some(&md),
params: None,
};
let mut signer = Signer::new(self.key, &init)?;
signer.update(tbs_der)?;
Ok(signer.finish()?)
}
}