use super::OpensslKeyError;
use crate::crypto::composite_mldsa::{
build_m_prime_from_hash, extract_spki_bitstring_payload, split_composite_sig,
split_composite_spki_content, CompHash, CompositeMlDsaSpec, TradAlg,
};
#[cfg(all(feature = "pqc", ossl_mldsa))]
use crate::crypto::composite_mldsa::{
composite_oid_components, encode_composite_pkcs8, encode_composite_spki,
pkcs8_private_key_content, split_composite_privkey,
};
use crate::crypto::{ErasedCertificateSigner, PrivateKeyError};
#[cfg(all(feature = "pqc", ossl_mldsa))]
use native_ossl::pkey::{KeygenCtx, Private, Signer};
use native_ossl::pkey::{Pkey, Public, SignInit, Verifier};
const EC_CURVE_BRAINPOOL_P256R1: &[u32] = &[1, 3, 36, 3, 3, 2, 8, 1, 1, 7];
const EC_CURVE_BRAINPOOL_P384R1: &[u32] = &[1, 3, 36, 3, 3, 2, 8, 1, 1, 11];
fn encode_standalone_spki(
alg_oid_comps: &[u32],
alg_params: Option<synta::types::constructed::Element>,
raw_pk: &[u8],
) -> Result<Vec<u8>, OpensslKeyError> {
use synta::tag::{Tag, TAG_SEQUENCE};
use synta::types::string::BitStringRef;
use synta::{Encoder, Encoding, ObjectIdentifier};
let oid = ObjectIdentifier::new(alg_oid_comps)
.map_err(|e| OpensslKeyError(format!("invalid OID: {e}")))?;
let pk_bit = BitStringRef::new(raw_pk, 0)
.map_err(|e| OpensslKeyError(format!("BIT STRING encoding failed: {e}")))?;
(|| -> synta::Result<Vec<u8>> {
let mut enc = Encoder::new(Encoding::Der);
enc.start_constructed_no_guard(Tag::universal_constructed(TAG_SEQUENCE))?;
enc.start_constructed_no_guard(Tag::universal_constructed(TAG_SEQUENCE))?;
enc.encode(&oid)?;
if let Some(params) = alg_params {
enc.encode(¶ms)?;
}
enc.end_constructed()?;
enc.encode(&pk_bit)?;
enc.end_constructed()?;
enc.finish()
})()
.map_err(|e| OpensslKeyError(format!("SPKI DER encoding failed: {e}")))
}
#[cfg(all(feature = "pqc", ossl_mldsa))]
fn encode_standalone_pkcs8(
alg_oid_comps: &[u32],
alg_params: Option<synta::types::constructed::Element>,
privkey_content: &[u8],
) -> Result<Vec<u8>, OpensslKeyError> {
use synta::types::string::OctetStringRef;
use synta::ObjectIdentifier;
let oid = ObjectIdentifier::new(alg_oid_comps)
.map_err(|e| OpensslKeyError(format!("invalid OID: {e}")))?;
let alg = crate::AlgorithmIdentifier {
algorithm: oid,
parameters: alg_params,
};
let pki = crate::pkcs8_types::OneAsymmetricKey {
version: synta::Integer::from_i64(0),
private_key_algorithm: alg,
private_key: OctetStringRef::new(privkey_content),
attributes: None,
public_key: None,
};
pki.to_der()
.map_err(|e| OpensslKeyError(format!("PKCS#8 DER encoding failed: {e}")))
}
fn ec_curve_oid_comps(curve: &str) -> Result<&'static [u32], OpensslKeyError> {
match curve {
"P-256" => Ok(crate::oids::EC_CURVE_P256),
"P-384" => Ok(crate::oids::EC_CURVE_P384),
"P-521" => Ok(crate::oids::EC_CURVE_P521),
"brainpoolP256r1" => Ok(EC_CURVE_BRAINPOOL_P256R1),
"brainpoolP384r1" => Ok(EC_CURVE_BRAINPOOL_P384R1),
other => Err(OpensslKeyError(format!("unsupported EC curve: {other}"))),
}
}
fn ec_alg_params(curve: &str) -> Result<synta::types::constructed::Element<'_>, OpensslKeyError> {
use synta::{types::constructed::Element, ObjectIdentifier};
let comps = ec_curve_oid_comps(curve)?;
let oid = ObjectIdentifier::new(comps)
.map_err(|e| OpensslKeyError(format!("invalid curve OID: {e}")))?;
Ok(Element::ObjectIdentifier(oid))
}
pub(crate) fn build_m_prime(
tbs_der: &[u8],
spec: &CompositeMlDsaSpec,
) -> Result<Vec<u8>, OpensslKeyError> {
let tbs_hash = match &spec.hash {
CompHash::Sha256 => {
let md = super::alg_cache::sha256()
.ok_or_else(|| OpensslKeyError("SHA-256 not available".to_string()))?;
md.digest_to_vec(tbs_der)?
}
CompHash::Sha512 => {
let md = super::alg_cache::sha512()
.ok_or_else(|| OpensslKeyError("SHA-512 not available".to_string()))?;
md.digest_to_vec(tbs_der)?
}
CompHash::Shake256_64 => {
let md = native_ossl::digest::DigestAlg::fetch(c"SHAKE-256", None)
.map_err(|e| OpensslKeyError(format!("SHAKE-256 not available: {e}")))?;
let mut ctx = native_ossl::digest::DigestCtx::new_empty()?;
ctx.reinit(&md, None)?;
ctx.update(tbs_der)?;
let mut out = vec![0u8; 64];
ctx.finish_xof(&mut out)?;
out
}
};
Ok(build_m_prime_from_hash(&tbs_hash, spec))
}
#[cfg(all(feature = "pqc", ossl_mldsa))]
fn generate_trad_key(trad_alg: &TradAlg) -> Result<(Vec<u8>, Vec<u8>), OpensslKeyError> {
use native_ossl::params::ParamBuilder;
match trad_alg {
TradAlg::RsaPss { bits, .. } | TradAlg::RsaPkcs15 { bits, .. } => {
let params = ParamBuilder::new()?
.push_uint(c"bits", *bits)?
.push_uint(c"e", 65537u32)?
.build()?;
let mut kgen = KeygenCtx::new(c"RSA")?;
kgen.set_params(¶ms)?;
let pkey: Pkey<Private> = kgen.generate()?;
let pkcs8_der = pkey.to_pkcs8_der()?;
let spki_der = pkey.public_key_to_der()?;
let trad_sk = pkcs8_private_key_content(&pkcs8_der).map_err(OpensslKeyError)?;
let trad_pk = extract_spki_bitstring_payload(&spki_der).map_err(OpensslKeyError)?;
Ok((trad_sk, trad_pk))
}
TradAlg::Ec { curve, .. } => {
let curve_cstr: &std::ffi::CStr = match *curve {
"P-256" => c"P-256",
"P-384" => c"P-384",
"P-521" => c"P-521",
"brainpoolP256r1" => c"brainpoolP256r1",
"brainpoolP384r1" => c"brainpoolP384r1",
other => return Err(OpensslKeyError(format!("unsupported EC curve: {other}"))),
};
let params = ParamBuilder::new()?
.push_utf8_string(c"group", curve_cstr)?
.build()?;
let mut kgen = KeygenCtx::new(c"EC")?;
kgen.set_params(¶ms)?;
let pkey: Pkey<Private> = kgen.generate()?;
let pkcs8_der = pkey.to_pkcs8_der()?;
let spki_der = pkey.public_key_to_der()?;
let trad_sk = pkcs8_private_key_content(&pkcs8_der).map_err(OpensslKeyError)?;
let trad_pk = extract_spki_bitstring_payload(&spki_der).map_err(OpensslKeyError)?;
Ok((trad_sk, trad_pk))
}
TradAlg::Ed25519 => {
let mut kgen = KeygenCtx::new(c"ED25519")?;
let pkey: Pkey<Private> = kgen.generate()?;
let pkcs8_der = pkey.to_pkcs8_der()?;
let spki_der = pkey.public_key_to_der()?;
let trad_sk = pkcs8_private_key_content(&pkcs8_der).map_err(OpensslKeyError)?;
let trad_pk = extract_spki_bitstring_payload(&spki_der).map_err(OpensslKeyError)?;
Ok((trad_sk, trad_pk))
}
TradAlg::Ed448 => {
let mut kgen = KeygenCtx::new(c"ED448")?;
let pkey: Pkey<Private> = kgen.generate()?;
let pkcs8_der = pkey.to_pkcs8_der()?;
let spki_der = pkey.public_key_to_der()?;
let trad_sk = pkcs8_private_key_content(&pkcs8_der).map_err(OpensslKeyError)?;
let trad_pk = extract_spki_bitstring_payload(&spki_der).map_err(OpensslKeyError)?;
Ok((trad_sk, trad_pk))
}
}
}
#[cfg(all(feature = "pqc", ossl_mldsa))]
pub(crate) fn priv_generate_composite_mldsa(
sub_arc: u32,
) -> Result<crate::crypto::BackendPrivateKey, OpensslKeyError> {
let spec = crate::crypto::composite_mldsa::composite_spec(sub_arc)
.ok_or_else(|| OpensslKeyError(format!("unknown composite ML-DSA sub-arc: {sub_arc}")))?;
let mldsa_name: &std::ffi::CStr = match spec.mldsa_variant {
"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!("unknown ML-DSA variant: {other}"))),
};
let mut kgen = KeygenCtx::new(mldsa_name)?;
let mldsa_pkey: Pkey<Private> = kgen.generate()?;
let mldsa_seed = {
let params = mldsa_pkey.export()?;
params
.get_octet_string(c"seed")
.map_err(|e| {
OpensslKeyError(format!("failed to export {} seed: {e}", spec.mldsa_variant))
})?
.to_vec()
};
let mldsa_spki = mldsa_pkey.public_key_to_der()?;
let mldsa_pk = extract_spki_bitstring_payload(&mldsa_spki).map_err(OpensslKeyError)?;
let (trad_sk, trad_pk) = generate_trad_key(&spec.trad_alg)?;
let oid_comps = composite_oid_components(spec.sub_arc);
let spki_der =
encode_composite_spki(&oid_comps, &mldsa_pk, &trad_pk).map_err(OpensslKeyError)?;
let pkcs8_der =
encode_composite_pkcs8(&oid_comps, &mldsa_seed, &trad_sk).map_err(OpensslKeyError)?;
let pkcs8_cell = std::sync::OnceLock::new();
pkcs8_cell.set(pkcs8_der).expect("fresh OnceLock");
Ok(crate::crypto::BackendPrivateKey {
pkcs8_der: pkcs8_cell,
spki_cache: Some(spki_der),
pkey: None,
pkcs11: None,
})
}
#[cfg(not(all(feature = "pqc", ossl_mldsa)))]
pub(crate) fn priv_generate_composite_mldsa(
sub_arc: u32,
) -> Result<crate::crypto::BackendPrivateKey, OpensslKeyError> {
let _ = sub_arc;
Err(OpensslKeyError(
"composite ML-DSA key generation requires OpenSSL with ML-DSA support \
and the 'pqc' feature"
.to_string(),
))
}
#[cfg(all(feature = "pqc", ossl_mldsa))]
pub(crate) struct CompositeMLDsaOpenSSLSigner {
spec: &'static CompositeMlDsaSpec,
mldsa_pkey: Pkey<Private>,
trad_pkey: Pkey<Private>,
}
#[cfg(all(feature = "pqc", ossl_mldsa))]
impl CompositeMLDsaOpenSSLSigner {
fn from_composite_pkcs8(
pkcs8_der: &[u8],
spec: &'static CompositeMlDsaSpec,
) -> Result<Self, OpensslKeyError> {
let privkey = pkcs8_private_key_content(pkcs8_der).map_err(OpensslKeyError)?;
let (mldsa_seed, trad_sk) = split_composite_privkey(&privkey).map_err(OpensslKeyError)?;
let mldsa_name: &std::ffi::CStr = match spec.mldsa_variant {
"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!("unknown ML-DSA variant: {other}"))),
};
let seed_params = native_ossl::params::ParamBuilder::new()?
.push_octet_slice(c"seed", mldsa_seed)?
.build()?;
let mldsa_pkey = Pkey::<Private>::from_params(None, mldsa_name, &seed_params)?;
let trad_pkcs8 = encode_trad_pkcs8(&spec.trad_alg, trad_sk)?;
let trad_pkey = Pkey::<Private>::from_der(&trad_pkcs8)?;
Ok(Self {
spec,
mldsa_pkey,
trad_pkey,
})
}
fn sign_mldsa(&self, m_prime: &[u8]) -> Result<Vec<u8>, OpensslKeyError> {
use native_ossl::pkey::{MessageSigner, SigAlg};
let alg_name: &std::ffi::CStr = match self.spec.mldsa_variant {
"ML-DSA-44" => c"ML-DSA-44",
"ML-DSA-65" => c"ML-DSA-65",
"ML-DSA-87" => c"ML-DSA-87",
_ => return Err(OpensslKeyError("unknown ML-DSA variant".to_string())),
};
let ctx_params = native_ossl::params::ParamBuilder::new()?
.push_octet_slice(c"context-string", self.spec.label.as_bytes())?
.build()?;
let mut alg = SigAlg::fetch(alg_name, None)?;
let mut signer = MessageSigner::new(&self.mldsa_pkey, &mut alg, Some(&ctx_params))?;
let mut sig = vec![0u8; self.spec.mldsa_sig_size];
let written = signer.sign(m_prime, Some(&mut sig))?;
sig.truncate(written);
Ok(sig)
}
fn sign_trad(&self, m_prime: &[u8]) -> Result<Vec<u8>, OpensslKeyError> {
use native_ossl::params::ParamBuilder;
match &self.spec.trad_alg {
TradAlg::RsaPss { hash, .. } => {
let hash_cstr: &std::ffi::CStr = match *hash {
"sha256" => c"SHA2-256",
"sha512" => c"SHA2-512",
other => return Err(OpensslKeyError(format!("unsupported hash: {other}"))),
};
let md = super::alg_cache::digest_by_name(hash_cstr).ok_or_else(|| {
OpensslKeyError(format!("digest not available: {hash_cstr:?}"))
})?;
let pss_params = ParamBuilder::new()?
.push_utf8_string(c"pad-mode", c"pss")?
.push_utf8_string(c"mgf1-digest", hash_cstr)?
.push_int(c"saltlen", -1i32)? .build()?;
let init = SignInit {
digest: Some(&md),
params: Some(&pss_params),
};
let mut signer = Signer::new(&self.trad_pkey, &init)?;
signer.update(m_prime)?;
Ok(signer.finish()?)
}
TradAlg::RsaPkcs15 { hash, .. } => {
let hash_cstr: &std::ffi::CStr = match *hash {
"sha256" => c"SHA2-256",
"sha512" => c"SHA2-512",
other => return Err(OpensslKeyError(format!("unsupported hash: {other}"))),
};
let md = super::alg_cache::digest_by_name(hash_cstr).ok_or_else(|| {
OpensslKeyError(format!("digest not available: {hash_cstr:?}"))
})?;
let init = SignInit {
digest: Some(&md),
params: None,
};
let mut signer = Signer::new(&self.trad_pkey, &init)?;
signer.update(m_prime)?;
Ok(signer.finish()?)
}
TradAlg::Ec { hash, .. } => {
let hash_cstr: &std::ffi::CStr = match *hash {
"sha256" => c"SHA2-256",
"sha512" => c"SHA2-512",
other => return Err(OpensslKeyError(format!("unsupported hash: {other}"))),
};
let md = super::alg_cache::digest_by_name(hash_cstr).ok_or_else(|| {
OpensslKeyError(format!("digest not available: {hash_cstr:?}"))
})?;
let init = SignInit {
digest: Some(&md),
params: None,
};
let mut signer = Signer::new(&self.trad_pkey, &init)?;
signer.update(m_prime)?;
Ok(signer.finish()?)
}
TradAlg::Ed25519 | TradAlg::Ed448 => {
let mut signer = Signer::new(&self.trad_pkey, &SignInit::default())?;
Ok(signer.sign_oneshot(m_prime)?)
}
}
}
}
#[cfg(all(feature = "pqc", ossl_mldsa))]
fn encode_trad_pkcs8(trad_alg: &TradAlg, trad_sk: &[u8]) -> Result<Vec<u8>, OpensslKeyError> {
use synta::{Element, Null};
match trad_alg {
TradAlg::RsaPss { .. } | TradAlg::RsaPkcs15 { .. } => encode_standalone_pkcs8(
crate::oids::RSA_ENCRYPTION,
Some(Element::Null(Null)),
trad_sk,
),
TradAlg::Ec { curve, .. } => {
let params = ec_alg_params(curve)?;
encode_standalone_pkcs8(crate::oids::EC_PUBLIC_KEY, Some(params), trad_sk)
}
TradAlg::Ed25519 => encode_standalone_pkcs8(crate::oids::ED25519, None, trad_sk),
TradAlg::Ed448 => encode_standalone_pkcs8(crate::oids::ED448, None, trad_sk),
}
}
#[cfg(all(feature = "pqc", ossl_mldsa))]
impl ErasedCertificateSigner for CompositeMLDsaOpenSSLSigner {
fn signature_algorithm_der_erased(&self) -> Result<Vec<u8>, PrivateKeyError> {
let comps = composite_oid_components(self.spec.sub_arc);
let oid = synta::ObjectIdentifier::new(&comps)
.map_err(|e| PrivateKeyError::new(OpensslKeyError(e.to_string())))?;
let alg = crate::AlgorithmIdentifier {
algorithm: oid,
parameters: None,
};
alg.to_der()
.map_err(|e| PrivateKeyError::new(OpensslKeyError(e.to_string())))
}
fn sign_tbs_erased(&self, tbs_der: &[u8]) -> Result<Vec<u8>, PrivateKeyError> {
let m_prime = build_m_prime(tbs_der, self.spec).map_err(PrivateKeyError::new)?;
let mldsa_sig = self.sign_mldsa(&m_prime).map_err(PrivateKeyError::new)?;
let trad_sig = self.sign_trad(&m_prime).map_err(PrivateKeyError::new)?;
let mut sig = Vec::with_capacity(mldsa_sig.len() + trad_sig.len());
sig.extend_from_slice(&mldsa_sig);
sig.extend_from_slice(&trad_sig);
Ok(sig)
}
}
#[cfg(all(feature = "pqc", ossl_mldsa))]
pub(crate) fn composite_mldsa_signer_from_pkcs8(
pkcs8_der: &[u8],
spec: &'static CompositeMlDsaSpec,
) -> Box<dyn ErasedCertificateSigner> {
match CompositeMLDsaOpenSSLSigner::from_composite_pkcs8(pkcs8_der, spec) {
Ok(signer) => Box::new(signer),
Err(e) => Box::new(FailedCompositeSigner(format!(
"composite signer init failed: {e}"
))),
}
}
#[cfg(not(all(feature = "pqc", ossl_mldsa)))]
pub(crate) fn composite_mldsa_signer_from_pkcs8(
_pkcs8_der: &[u8],
_spec: &'static CompositeMlDsaSpec,
) -> Box<dyn ErasedCertificateSigner> {
Box::new(UnsupportedCompositeSigner)
}
#[cfg(all(feature = "pqc", ossl_mldsa))]
struct FailedCompositeSigner(String);
#[cfg(all(feature = "pqc", ossl_mldsa))]
impl ErasedCertificateSigner for FailedCompositeSigner {
fn signature_algorithm_der_erased(&self) -> Result<Vec<u8>, PrivateKeyError> {
Err(PrivateKeyError::new(OpensslKeyError(self.0.clone())))
}
fn sign_tbs_erased(&self, _tbs_der: &[u8]) -> Result<Vec<u8>, PrivateKeyError> {
Err(PrivateKeyError::new(OpensslKeyError(self.0.clone())))
}
}
#[cfg(not(all(feature = "pqc", ossl_mldsa)))]
struct UnsupportedCompositeSigner;
#[cfg(not(all(feature = "pqc", ossl_mldsa)))]
impl ErasedCertificateSigner for UnsupportedCompositeSigner {
fn signature_algorithm_der_erased(&self) -> Result<Vec<u8>, PrivateKeyError> {
Err(PrivateKeyError::new(OpensslKeyError(
"composite ML-DSA signing not supported in this build".to_string(),
)))
}
fn sign_tbs_erased(&self, _tbs_der: &[u8]) -> Result<Vec<u8>, PrivateKeyError> {
Err(PrivateKeyError::new(OpensslKeyError(
"composite ML-DSA signing not supported in this build".to_string(),
)))
}
}
pub(crate) fn verify_composite_mldsa_signature(
tbs_der: &[u8],
sub_arc: u32,
signature_bits: &[u8],
issuer_spki_der: &[u8],
) -> Result<(), super::signature::OpensslVerifierError> {
use super::signature::OpensslVerifierError;
let spec = crate::crypto::composite_mldsa::composite_spec(sub_arc).ok_or_else(|| {
OpensslVerifierError(format!("unknown composite ML-DSA sub-arc: {sub_arc}"))
})?;
let spki_payload =
extract_spki_bitstring_payload(issuer_spki_der).map_err(OpensslVerifierError)?;
let (mldsa_pk, trad_pk) =
split_composite_spki_content(&spki_payload, spec).map_err(OpensslVerifierError)?;
let (mldsa_sig, trad_sig) =
split_composite_sig(signature_bits, spec).map_err(OpensslVerifierError)?;
let m_prime = build_m_prime(tbs_der, spec).map_err(|e| OpensslVerifierError(e.to_string()))?;
let mldsa_oid: &[u32] = match spec.mldsa_variant {
"ML-DSA-44" => crate::oids::ML_DSA_44,
"ML-DSA-65" => crate::oids::ML_DSA_65,
"ML-DSA-87" => crate::oids::ML_DSA_87,
other => {
return Err(OpensslVerifierError(format!(
"unknown ML-DSA variant: {other}"
)))
}
};
let mldsa_spki = encode_standalone_spki(mldsa_oid, None, mldsa_pk)
.map_err(|e| OpensslVerifierError(e.to_string()))?;
let mldsa_result = verify_mldsa_component(&mldsa_spki, &m_prime, mldsa_sig, spec)
.map_err(|e| OpensslVerifierError(e.to_string()));
let trad_spki = encode_trad_spki(&spec.trad_alg, trad_pk)
.map_err(|e| OpensslVerifierError(e.to_string()))?;
let trad_result = verify_trad_component(&trad_spki, &m_prime, trad_sig, &spec.trad_alg)
.map_err(|e| OpensslVerifierError(e.to_string()));
mldsa_result.and(trad_result)
}
fn encode_trad_spki(trad_alg: &TradAlg, raw_pk: &[u8]) -> Result<Vec<u8>, OpensslKeyError> {
use synta::{Element, Null};
match trad_alg {
TradAlg::RsaPss { .. } | TradAlg::RsaPkcs15 { .. } => encode_standalone_spki(
crate::oids::RSA_ENCRYPTION,
Some(Element::Null(Null)),
raw_pk,
),
TradAlg::Ec { curve, .. } => {
let params = ec_alg_params(curve)?;
encode_standalone_spki(crate::oids::EC_PUBLIC_KEY, Some(params), raw_pk)
}
TradAlg::Ed25519 => encode_standalone_spki(crate::oids::ED25519, None, raw_pk),
TradAlg::Ed448 => encode_standalone_spki(crate::oids::ED448, None, raw_pk),
}
}
fn verify_mldsa_component(
mldsa_spki_der: &[u8],
m_prime: &[u8],
mldsa_sig: &[u8],
spec: &CompositeMlDsaSpec,
) -> Result<(), OpensslKeyError> {
#[cfg(all(feature = "pqc", ossl_mldsa))]
{
use native_ossl::pkey::{MessageVerifier, SigAlg};
let pkey = Pkey::<Public>::from_der(mldsa_spki_der)?;
let alg_name: &std::ffi::CStr = match spec.mldsa_variant {
"ML-DSA-44" => c"ML-DSA-44",
"ML-DSA-65" => c"ML-DSA-65",
"ML-DSA-87" => c"ML-DSA-87",
_ => return Err(OpensslKeyError("unknown ML-DSA variant".to_string())),
};
let ctx_params = native_ossl::params::ParamBuilder::new()?
.push_octet_slice(c"context-string", spec.label.as_bytes())?
.build()?;
let mut alg = SigAlg::fetch(alg_name, None)?;
let mut ver = MessageVerifier::new(&pkey, &mut alg, Some(&ctx_params))?;
if ver.verify(m_prime, mldsa_sig)? {
Ok(())
} else {
Err(OpensslKeyError(
"ML-DSA component signature invalid".to_string(),
))
}
}
#[cfg(not(all(feature = "pqc", ossl_mldsa)))]
{
let _ = (mldsa_spki_der, m_prime, mldsa_sig, spec);
Err(OpensslKeyError(
"composite ML-DSA verification requires OpenSSL with ML-DSA support \
and the 'pqc' feature"
.to_string(),
))
}
}
fn verify_trad_component(
trad_spki_der: &[u8],
m_prime: &[u8],
trad_sig: &[u8],
trad_alg: &TradAlg,
) -> Result<(), OpensslKeyError> {
use native_ossl::params::ParamBuilder;
let pkey = Pkey::<Public>::from_der(trad_spki_der)?;
match trad_alg {
TradAlg::RsaPss { hash, .. } => {
let hash_cstr: &std::ffi::CStr = match *hash {
"sha256" => c"SHA2-256",
"sha512" => c"SHA2-512",
other => return Err(OpensslKeyError(format!("unsupported hash: {other}"))),
};
let md = super::alg_cache::digest_by_name(hash_cstr)
.ok_or_else(|| OpensslKeyError(format!("digest not available: {hash_cstr:?}")))?;
let pss_params = ParamBuilder::new()?
.push_utf8_string(c"pad-mode", c"pss")?
.push_utf8_string(c"mgf1-digest", hash_cstr)?
.push_int(c"saltlen", -1i32)?
.build()?;
let init = SignInit {
digest: Some(&md),
params: Some(&pss_params),
};
let mut ver = Verifier::new(&pkey, &init)?;
ver.update(m_prime)?;
if ver.verify(trad_sig)? {
Ok(())
} else {
Err(OpensslKeyError(
"RSA-PSS component signature invalid".to_string(),
))
}
}
TradAlg::RsaPkcs15 { hash, .. } => {
let hash_cstr: &std::ffi::CStr = match *hash {
"sha256" => c"SHA2-256",
"sha512" => c"SHA2-512",
other => return Err(OpensslKeyError(format!("unsupported hash: {other}"))),
};
let md = super::alg_cache::digest_by_name(hash_cstr)
.ok_or_else(|| OpensslKeyError(format!("digest not available: {hash_cstr:?}")))?;
let init = SignInit {
digest: Some(&md),
params: None,
};
let mut ver = Verifier::new(&pkey, &init)?;
ver.update(m_prime)?;
if ver.verify(trad_sig)? {
Ok(())
} else {
Err(OpensslKeyError(
"RSA-PKCS1v15 component signature invalid".to_string(),
))
}
}
TradAlg::Ec { hash, .. } => {
let hash_cstr: &std::ffi::CStr = match *hash {
"sha256" => c"SHA2-256",
"sha512" => c"SHA2-512",
other => return Err(OpensslKeyError(format!("unsupported hash: {other}"))),
};
let md = super::alg_cache::digest_by_name(hash_cstr)
.ok_or_else(|| OpensslKeyError(format!("digest not available: {hash_cstr:?}")))?;
let init = SignInit {
digest: Some(&md),
params: None,
};
let mut ver = Verifier::new(&pkey, &init)?;
ver.update(m_prime)?;
if ver.verify(trad_sig)? {
Ok(())
} else {
Err(OpensslKeyError(
"ECDSA component signature invalid".to_string(),
))
}
}
TradAlg::Ed25519 | TradAlg::Ed448 => {
let mut ver = Verifier::new(&pkey, &SignInit::default())?;
if ver.verify_oneshot(m_prime, trad_sig)? {
Ok(())
} else {
Err(OpensslKeyError(
"EdDSA component signature invalid".to_string(),
))
}
}
}
}