synta-certificate 0.2.6

X.509 certificate structures for synta ASN.1 library
Documentation
//! Cached OpenSSL algorithm handles.
//!
//! `EVP_MD_fetch` and `EVP_CIPHER_fetch` each acquire a provider-store lock
//! and perform a string lookup on every call.  These process-global caches
//! replace per-call fetches with a single atomic refcount increment
//! (`EVP_MD_up_ref` / `EVP_CIPHER_up_ref`) on all calls after the first.
//!
//! Both `DigestAlg` and `CipherAlg` are `Sync + Send`, so storing them in
//! `OnceLock` statics is safe.  `Clone` for each type is `up_ref + ptr copy`.

use std::sync::OnceLock;

use native_ossl::cipher::CipherAlg;
use native_ossl::digest::DigestAlg;

// ── Digest cache ──────────────────────────────────────────────────────────────

macro_rules! cached_digest {
    ($fn_name:ident, $ossl_name:literal) => {
        pub(super) fn $fn_name() -> Option<DigestAlg> {
            static D: OnceLock<Option<DigestAlg>> = OnceLock::new();
            D.get_or_init(|| DigestAlg::fetch($ossl_name, None).ok())
                .as_ref()
                .map(Clone::clone)
        }
    };
}

cached_digest!(sha1, c"SHA1");
cached_digest!(sha224, c"SHA2-224");
cached_digest!(sha256, c"SHA2-256");
cached_digest!(sha384, c"SHA2-384");
cached_digest!(sha512, c"SHA2-512");
cached_digest!(sha3_256, c"SHA3-256");
cached_digest!(sha3_384, c"SHA3-384");
cached_digest!(sha3_512, c"SHA3-512");
cached_digest!(md5, c"MD5");

// ── Cipher cache ──────────────────────────────────────────────────────────────

macro_rules! cached_cipher {
    ($fn_name:ident, $ossl_name:literal) => {
        pub(super) fn $fn_name() -> Option<CipherAlg> {
            static C: OnceLock<Option<CipherAlg>> = OnceLock::new();
            C.get_or_init(|| CipherAlg::fetch($ossl_name, None).ok())
                .as_ref()
                .map(Clone::clone)
        }
    };
}

cached_cipher!(aes128_cbc, c"AES-128-CBC");
cached_cipher!(aes192_cbc, c"AES-192-CBC");
cached_cipher!(aes256_cbc, c"AES-256-CBC");
cached_cipher!(aes128_gcm, c"AES-128-GCM");
cached_cipher!(aes192_gcm, c"AES-192-GCM");
cached_cipher!(aes256_gcm, c"AES-256-GCM");
cached_cipher!(des3_cbc, c"DES-EDE3-CBC");

// ── Name-based lookups ────────────────────────────────────────────────────────

/// Return a cached `DigestAlg` by OpenSSL canonical name (`CStr`).
///
/// Covers `SHA1`, `SHA2-224`, `SHA2-256`, `SHA2-384`, `SHA2-512`,
/// `SHA3-256`, `SHA3-384`, `SHA3-512`, `MD5`.
/// Returns `None` for any other name.
pub(super) fn digest_by_name(name: &std::ffi::CStr) -> Option<DigestAlg> {
    match name.to_bytes() {
        b"SHA1" => sha1(),
        b"SHA2-224" => sha224(),
        b"SHA2-256" => sha256(),
        b"SHA2-384" => sha384(),
        b"SHA2-512" => sha512(),
        b"SHA3-256" => sha3_256(),
        b"SHA3-384" => sha3_384(),
        b"SHA3-512" => sha3_512(),
        b"MD5" => md5(),
        _ => None,
    }
}

/// Return a cached `DigestAlg` by algorithm string.
///
/// Accepts both the short synta names (`"sha256"`, `"sha3-256"`, …) and the
/// OpenSSL native names used by `DigestAlg::fetch` (`"SHA2-256"`, `"SHA3-256"`, …).
/// Returns `None` for any other string.
pub(super) fn digest_by_str(alg: &str) -> Option<DigestAlg> {
    match alg {
        "sha1" | "SHA1" => sha1(),
        "sha224" | "SHA2-224" => sha224(),
        "sha256" | "SHA2-256" => sha256(),
        "sha384" | "SHA2-384" => sha384(),
        "sha512" | "SHA2-512" => sha512(),
        "sha3-256" | "SHA3-256" => sha3_256(),
        "sha3-384" | "SHA3-384" => sha3_384(),
        "sha3-512" | "SHA3-512" => sha3_512(),
        "md5" | "MD5" => md5(),
        _ => None,
    }
}