use crate::x509::Error;
pub trait SignatureAlgorithm: Sync + 'static {
fn id(&self) -> &'static str;
fn x509_oids(&self) -> &'static [&'static [u64]];
fn tls_schemes(&self) -> &'static [u16];
fn verify(&self, spki: &[u8], message: &[u8], signature: &[u8]) -> Result<(), Error>;
fn rsa_modulus_bits(&self, _spki: &[u8]) -> Option<u32> {
None
}
}
pub static ALGORITHMS: &[&'static dyn SignatureAlgorithm] = &[
#[cfg(all(feature = "rsa", feature = "alloc"))]
&crate::rsa::registry::Pkcs1Sha1,
#[cfg(all(feature = "rsa", feature = "alloc"))]
&crate::rsa::registry::Pkcs1Sha256,
#[cfg(all(feature = "rsa", feature = "alloc"))]
&crate::rsa::registry::Pkcs1Sha384,
#[cfg(all(feature = "rsa", feature = "alloc"))]
&crate::rsa::registry::Pkcs1Sha512,
#[cfg(all(feature = "rsa", feature = "alloc"))]
&crate::rsa::registry::PssRsaeSha256,
#[cfg(all(feature = "rsa", feature = "alloc"))]
&crate::rsa::registry::PssRsaeSha384,
#[cfg(all(feature = "rsa", feature = "alloc"))]
&crate::rsa::registry::PssRsaeSha512,
#[cfg(all(feature = "rsa", feature = "alloc"))]
&crate::rsa::registry::PssPssSha256,
#[cfg(all(feature = "ec", feature = "alloc"))]
&crate::ec::registry::EcdsaSha256AnyCurve,
#[cfg(all(feature = "ec", feature = "alloc"))]
&crate::ec::registry::EcdsaSha384AnyCurve,
#[cfg(all(feature = "ec", feature = "alloc"))]
&crate::ec::registry::EcdsaSha512AnyCurve,
#[cfg(all(feature = "ec", feature = "alloc"))]
&crate::ec::registry::EcdsaP256Sha256,
#[cfg(all(feature = "ec", feature = "alloc"))]
&crate::ec::registry::EcdsaP384Sha384,
#[cfg(all(feature = "ec", feature = "alloc"))]
&crate::ec::registry::EcdsaP521Sha512,
#[cfg(all(feature = "ec", feature = "alloc"))]
&crate::ec::registry::EcdsaP256Sha384,
#[cfg(all(feature = "ec", feature = "alloc"))]
&crate::ec::registry::EcdsaP256Sha512,
#[cfg(all(feature = "ec", feature = "alloc"))]
&crate::ec::registry::EcdsaP384Sha256,
#[cfg(all(feature = "ec", feature = "alloc"))]
&crate::ec::registry::EcdsaP384Sha512,
#[cfg(all(feature = "ec", feature = "alloc"))]
&crate::ec::registry::EcdsaP521Sha256,
#[cfg(all(feature = "ec", feature = "alloc"))]
&crate::ec::registry::EcdsaP521Sha384,
#[cfg(all(feature = "ec", feature = "alloc"))]
&crate::ec::registry::EcdsaSecp256k1Sha256,
#[cfg(all(feature = "ec", feature = "alloc"))]
&crate::ec::registry::EcdsaSecp256k1Sha384,
#[cfg(all(feature = "ec", feature = "alloc"))]
&crate::ec::registry::EcdsaSecp256k1Sha512,
#[cfg(all(feature = "ec", feature = "alloc"))]
&crate::ec::registry::Ed25519,
#[cfg(all(feature = "mldsa", feature = "alloc"))]
&crate::mldsa::registry::MlDsa44,
#[cfg(all(feature = "mldsa", feature = "alloc"))]
&crate::mldsa::registry::MlDsa65,
#[cfg(all(feature = "mldsa", feature = "alloc"))]
&crate::mldsa::registry::MlDsa87,
#[cfg(all(feature = "slhdsa", feature = "alloc"))]
&crate::slhdsa::registry::SlhDsaSha2128s,
#[cfg(all(feature = "slhdsa", feature = "alloc"))]
&crate::slhdsa::registry::SlhDsaSha2128f,
#[cfg(all(feature = "slhdsa", feature = "alloc"))]
&crate::slhdsa::registry::SlhDsaSha2192s,
#[cfg(all(feature = "slhdsa", feature = "alloc"))]
&crate::slhdsa::registry::SlhDsaSha2192f,
#[cfg(all(feature = "slhdsa", feature = "alloc"))]
&crate::slhdsa::registry::SlhDsaSha2256s,
#[cfg(all(feature = "slhdsa", feature = "alloc"))]
&crate::slhdsa::registry::SlhDsaSha2256f,
#[cfg(all(feature = "slhdsa", feature = "alloc"))]
&crate::slhdsa::registry::SlhDsaShake128s,
#[cfg(all(feature = "slhdsa", feature = "alloc"))]
&crate::slhdsa::registry::SlhDsaShake128f,
#[cfg(all(feature = "slhdsa", feature = "alloc"))]
&crate::slhdsa::registry::SlhDsaShake192s,
#[cfg(all(feature = "slhdsa", feature = "alloc"))]
&crate::slhdsa::registry::SlhDsaShake192f,
#[cfg(all(feature = "slhdsa", feature = "alloc"))]
&crate::slhdsa::registry::SlhDsaShake256s,
#[cfg(all(feature = "slhdsa", feature = "alloc"))]
&crate::slhdsa::registry::SlhDsaShake256f,
];
pub fn find_by_oid(oid: &[u64]) -> Option<&'static dyn SignatureAlgorithm> {
for algo in ALGORITHMS {
for entry in algo.x509_oids() {
if *entry == oid {
return Some(*algo);
}
}
}
None
}
pub fn find_by_tls_scheme(scheme: u16) -> Option<&'static dyn SignatureAlgorithm> {
for algo in ALGORITHMS {
for entry in algo.tls_schemes() {
if *entry == scheme {
return Some(*algo);
}
}
}
None
}
pub fn find_by_id(id: &str) -> Option<&'static dyn SignatureAlgorithm> {
for algo in ALGORITHMS {
if algo.id() == id {
return Some(*algo);
}
}
None
}
#[cfg(feature = "alloc")]
mod policy {
use super::{SignatureAlgorithm, find_by_id};
use alloc::vec::Vec;
fn algo_eq(a: &dyn SignatureAlgorithm, b: &dyn SignatureAlgorithm) -> bool {
a.id() == b.id()
}
#[derive(Clone)]
pub struct SignaturePolicy {
permitted: Vec<&'static dyn SignatureAlgorithm>,
pub min_rsa_bits: u32,
}
impl SignaturePolicy {
pub fn modern() -> Self {
let permitted_ids = [
"rsa-pkcs1-sha256",
"rsa-pkcs1-sha384",
"rsa-pss-rsae-sha256",
"rsa-pss-rsae-sha384",
"rsa-pss-rsae-sha512",
"ecdsa-with-sha256",
"ecdsa-with-sha384",
"ecdsa-with-sha512",
"ecdsa-secp256r1-sha256",
"ecdsa-secp384r1-sha384",
"ecdsa-secp521r1-sha512",
"ed25519",
"ml-dsa-44",
"ml-dsa-65",
"ml-dsa-87",
];
let mut permitted = Vec::new();
for id in permitted_ids {
if let Some(algo) = find_by_id(id) {
permitted.push(algo);
}
}
SignaturePolicy {
permitted,
min_rsa_bits: 2048,
}
}
pub fn empty() -> Self {
SignaturePolicy {
permitted: Vec::new(),
min_rsa_bits: 2048,
}
}
pub fn permit(mut self, id: &str) -> Self {
if let Some(algo) = find_by_id(id)
&& !self.permitted.iter().any(|a| algo_eq(*a, algo))
{
self.permitted.push(algo);
}
self
}
pub fn with_min_rsa_bits(mut self, bits: u32) -> Self {
self.min_rsa_bits = bits;
self
}
pub fn permits(&self, algo: &dyn SignatureAlgorithm, spki: &[u8]) -> bool {
if !self.permitted.iter().any(|a| algo_eq(*a, algo)) {
return false;
}
if let Some(bits) = algo.rsa_modulus_bits(spki)
&& bits < self.min_rsa_bits
{
return false;
}
true
}
}
impl Default for SignaturePolicy {
fn default() -> Self {
Self::modern()
}
}
}
#[cfg(feature = "alloc")]
pub use policy::SignaturePolicy;
#[cfg(test)]
mod tests {
use super::*;
#[cfg(all(feature = "rsa", feature = "ec", feature = "alloc"))]
#[test]
fn registry_has_modern_entries() {
assert!(find_by_id("rsa-pkcs1-sha256").is_some());
assert!(find_by_id("rsa-pss-rsae-sha256").is_some());
assert!(find_by_id("ecdsa-secp256r1-sha256").is_some());
assert!(find_by_id("ecdsa-secp384r1-sha384").is_some());
assert!(find_by_id("ecdsa-secp521r1-sha512").is_some());
assert!(find_by_id("ed25519").is_some());
}
#[cfg(all(feature = "rsa", feature = "ec", feature = "alloc"))]
#[test]
fn lookup_by_oid_and_scheme() {
let algo = find_by_oid(&[1, 2, 840, 10045, 4, 3, 2]).expect("ecdsa-with-SHA256");
assert_eq!(algo.id(), "ecdsa-with-sha256");
let algo = find_by_tls_scheme(0x0403).expect("ecdsa_secp256r1_sha256");
assert_eq!(algo.id(), "ecdsa-secp256r1-sha256");
let algo = find_by_tls_scheme(0x0804).expect("rsa_pss_rsae_sha256");
assert_eq!(algo.id(), "rsa-pss-rsae-sha256");
}
#[cfg(all(feature = "rsa", feature = "ec", feature = "alloc"))]
#[test]
fn modern_policy_permits_default_set() {
let policy = SignaturePolicy::modern();
for id in [
"rsa-pkcs1-sha256",
"rsa-pkcs1-sha384",
"rsa-pss-rsae-sha256",
"rsa-pss-rsae-sha384",
"rsa-pss-rsae-sha512",
"ecdsa-secp256r1-sha256",
"ecdsa-secp384r1-sha384",
"ecdsa-secp521r1-sha512",
"ed25519",
] {
let algo = find_by_id(id).unwrap();
assert!(policy.permits(algo, &[]), "modern() should permit {id}");
}
}
#[cfg(all(feature = "ec", feature = "alloc"))]
#[test]
fn empty_policy_permits_nothing_until_opt_in() {
let algo = find_by_id("ed25519").unwrap();
let policy = SignaturePolicy::empty();
assert!(!policy.permits(algo, &[]));
let policy = policy.permit("ed25519");
assert!(policy.permits(algo, &[]));
}
#[cfg(all(feature = "rsa", feature = "alloc"))]
#[test]
fn min_rsa_bits_floor_rejects_small_keys() {
use crate::x509::AnyPublicKey;
let key = crate::test_util::rsa_test_key_a();
let pk = key.public_key();
let mut n = [0u8; 256];
pk.modulus().write_be_bytes(&mut n);
let mut e = [0u8; 256];
pk.exponent().write_be_bytes(&mut e);
let boxed = crate::rsa::BoxedRsaPublicKey::new(
crate::bignum::BoxedUint::from_be_bytes(&n),
crate::bignum::BoxedUint::from_be_bytes(&e),
);
let spki = AnyPublicKey::Rsa(boxed).to_spki_der();
let algo = find_by_id("rsa-pkcs1-sha256").unwrap();
assert!(SignaturePolicy::modern().permits(algo, &spki));
let strict = SignaturePolicy::modern().with_min_rsa_bits(4096);
assert!(!strict.permits(algo, &spki));
}
}