synta-certificate 0.2.6

X.509 certificate structures for synta ASN.1 library
Documentation
//! Certificate signing and signature verification traits.

use super::errors::{NoSignatureVerifierError, NoSignerError, PrivateKeyError};

// ── SignatureVerifier ─────────────────────────────────────────────────────────

/// Verifies that a certificate's signature is valid under an issuer's public key.
///
/// Implementors receive the raw bytes needed for verification so they can be
/// passed directly to a crypto library without any additional ASN.1 parsing.
///
/// # Contract
///
/// - `tbs_der`: the DER-encoded `TBSCertificate` (the bytes that were signed).
/// - `sig_alg_der`: the DER-encoded outer `signatureAlgorithm`
///   `AlgorithmIdentifier` (identifies the signature scheme and parameters).
/// - `signature_bits`: the raw signature bytes extracted from the outer
///   `signature BIT STRING` (the bit-string value bytes, unused bits stripped).
/// - `issuer_spki_der`: the DER-encoded `SubjectPublicKeyInfo` of the issuer
///   certificate (contains the public key used to verify).
///
/// Returns `Ok(())` if the signature is valid, or an error otherwise.
pub trait SignatureVerifier {
    type Error: std::error::Error + Send + Sync + 'static;

    fn verify_certificate_signature(
        &self,
        tbs_der: &[u8],
        sig_alg_der: &[u8],
        signature_bits: &[u8],
        issuer_spki_der: &[u8],
    ) -> Result<(), Self::Error>;
}

/// Sentinel [`SignatureVerifier`] that always returns an error.
///
/// Use as a placeholder when no crypto backend is available, to surface a
/// clear error rather than panicking or silently succeeding.
pub struct NoSignatureVerifier;

impl SignatureVerifier for NoSignatureVerifier {
    type Error = NoSignatureVerifierError;

    fn verify_certificate_signature(
        &self,
        _tbs_der: &[u8],
        _sig_alg_der: &[u8],
        _signature_bits: &[u8],
        _issuer_spki_der: &[u8],
    ) -> Result<(), NoSignatureVerifierError> {
        Err(NoSignatureVerifierError)
    }
}

// ── CertificateSigner ─────────────────────────────────────────────────────────

/// Signs a `TBSCertificate` and reports the `AlgorithmIdentifier` to embed.
///
/// Implementors supply both the `AlgorithmIdentifier` DER that RFC 5280
/// requires to appear verbatim in `TBSCertificate.signature` *and* in the
/// outer `Certificate.signatureAlgorithm`, and the signing operation itself.
///
/// # Contract
///
/// - [`signature_algorithm_der`] is called first to obtain the DER-encoded
///   `AlgorithmIdentifier` SEQUENCE.  The returned bytes are embedded verbatim
///   in both locations.
/// - [`sign_tbs`] is then called with the fully encoded `TBSCertificate` DER
///   and must return the raw signature bytes **without** a BIT STRING wrapper;
///   [`CertificateBuilder::sign`] adds the `BIT STRING` header
///   (`unused_bits = 0`) before assembling the final `Certificate`.
///
/// # Example
///
/// ```rust,ignore
/// use synta_certificate::crypto::CertificateSigner;
///
/// struct MySigner { /* ... */ }
///
/// impl CertificateSigner for MySigner {
///     type Error = MyError;
///
///     fn signature_algorithm_der(&self) -> Result<Vec<u8>, MyError> {
///         // Pre-encoded AlgorithmIdentifier for, e.g., id-Ed25519:
///         Ok(vec![0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70])
///     }
///
///     fn sign_tbs(&self, tbs_der: &[u8]) -> Result<Vec<u8>, MyError> {
///         self.key.sign(tbs_der)
///     }
/// }
///
/// let cert_der = CertificateBuilder::new()
///     /* … set fields … */
///     .sign(&MySigner { /* … */ })?;
/// ```
///
/// [`CertificateBuilder::sign`]: crate::builder::CertificateBuilder::sign
/// [`signature_algorithm_der`]: CertificateSigner::signature_algorithm_der
/// [`sign_tbs`]: CertificateSigner::sign_tbs
pub trait CertificateSigner {
    /// Error type returned by this signer.
    type Error: std::error::Error + Send + Sync + 'static;

    /// Return the DER-encoded `AlgorithmIdentifier` SEQUENCE for this signer.
    ///
    /// The bytes must be a complete, valid DER `AlgorithmIdentifier` SEQUENCE —
    /// exactly what appears in `TBSCertificate.signature` and
    /// `Certificate.signatureAlgorithm`.
    fn signature_algorithm_der(&self) -> Result<Vec<u8>, Self::Error>;

    /// Sign the DER-encoded `TBSCertificate` and return the raw signature bytes.
    ///
    /// The returned bytes must be the raw signing output — **not** wrapped in
    /// a BIT STRING.  [`CertificateBuilder::sign`] adds that wrapper.
    ///
    /// [`CertificateBuilder::sign`]: crate::builder::CertificateBuilder::sign
    fn sign_tbs(&self, tbs_der: &[u8]) -> Result<Vec<u8>, Self::Error>;
}

/// Sentinel [`CertificateSigner`] that always returns an error.
///
/// Use as a placeholder when no signing backend is available, to surface a
/// clear error rather than panicking or silently producing an invalid output.
pub struct NoSigner;

impl CertificateSigner for NoSigner {
    type Error = NoSignerError;

    fn signature_algorithm_der(&self) -> Result<Vec<u8>, NoSignerError> {
        Err(NoSignerError)
    }

    fn sign_tbs(&self, _tbs_der: &[u8]) -> Result<Vec<u8>, NoSignerError> {
        Err(NoSignerError)
    }
}

// ── UnsignedCertificateSigner (RFC 9925) ──────────────────────────────────────

/// [`CertificateSigner`] that produces RFC 9925 unsigned certificates.
///
/// An unsigned certificate carries `id-alg-unsigned` (1.3.6.1.5.5.7.6.36) in
/// both `TBSCertificate.signature` and the outer `Certificate.signatureAlgorithm`,
/// with algorithm parameters absent.  The `signatureValue` field is a
/// zero-length BIT STRING (DER: `03 01 00`).
///
/// [`CertificateBuilder::sign`] wraps the empty byte vector returned by
/// [`sign_tbs`] in a BIT STRING with `unused_bits = 0`, producing the correct
/// zero-length BIT STRING automatically.
///
/// No private key or cryptographic operation is required.  The signer is
/// always infallible.
///
/// # Example
///
/// ```rust,ignore
/// use synta_certificate::{CertificateBuilder, UnsignedCertificateSigner};
///
/// let cert_der = CertificateBuilder::new()
///     .issuer_name(&issuer_name_der)
///     .subject_name(&subject_name_der)
///     .public_key_der(&spki_der)
///     .serial_number(synta::Integer::from_i64(1))
///     .not_valid_before(not_before)
///     .not_valid_after(not_after)
///     .sign(&UnsignedCertificateSigner)?;
/// ```
///
/// [`sign_tbs`]: UnsignedCertificateSigner::sign_tbs
/// [`CertificateBuilder::sign`]: crate::builder::CertificateBuilder::sign
pub struct UnsignedCertificateSigner;

impl CertificateSigner for UnsignedCertificateSigner {
    type Error = std::convert::Infallible;

    /// Return the DER-encoded `AlgorithmIdentifier` for `id-alg-unsigned`.
    ///
    /// Encodes `SEQUENCE { OID 1.3.6.1.5.5.7.6.36 }` with absent parameters
    /// as required by RFC 9925 §3.
    fn signature_algorithm_der(&self) -> Result<Vec<u8>, std::convert::Infallible> {
        use crate::{oids, AlgorithmIdentifier};
        use synta::traits::Encode;
        use synta::ObjectIdentifier;

        let oid = ObjectIdentifier::new(oids::ALG_UNSIGNED)
            .expect("ALG_UNSIGNED is a valid static OID constant");
        let alg = AlgorithmIdentifier {
            algorithm: oid,
            parameters: None,
        };
        let mut enc = synta::Encoder::new(synta::Encoding::Der);
        alg.encode(&mut enc)
            .expect("AlgorithmIdentifier DER encoding is infallible for a plain OID");
        Ok(enc
            .finish()
            .expect("DER encoder finalisation is infallible"))
    }

    /// Return an empty byte vector (zero-length signature payload).
    ///
    /// `CertificateBuilder::sign` wraps this in a BIT STRING with
    /// `unused_bits = 0`, producing `03 01 00` — the zero-length BIT STRING
    /// required by RFC 9925 §3.
    fn sign_tbs(&self, _tbs_der: &[u8]) -> Result<Vec<u8>, std::convert::Infallible> {
        Ok(Vec::new())
    }
}

// ── ErasedSignatureVerifier ───────────────────────────────────────────────────

/// Object-safe [`SignatureVerifier`] variant with a type-erased error.
///
/// Used as the return type of [`default_signature_verifier`] so that callers
/// receive a `Box<dyn ErasedSignatureVerifier>` without naming the backend type.
/// The `synta-x509-verification` crate and other crates accept this via the
/// blanket [`SignatureVerifier`] impl below.
pub trait ErasedSignatureVerifier {
    /// Verify a certificate signature; returns [`PrivateKeyError`] on failure.
    fn verify_certificate_signature_erased(
        &self,
        tbs_der: &[u8],
        sig_alg_der: &[u8],
        signature_bits: &[u8],
        issuer_spki_der: &[u8],
    ) -> Result<(), PrivateKeyError>;
}

/// Blanket impl: `&dyn ErasedSignatureVerifier` implements [`SignatureVerifier`].
impl SignatureVerifier for dyn ErasedSignatureVerifier + '_ {
    type Error = PrivateKeyError;

    fn verify_certificate_signature(
        &self,
        tbs_der: &[u8],
        sig_alg_der: &[u8],
        signature_bits: &[u8],
        issuer_spki_der: &[u8],
    ) -> Result<(), PrivateKeyError> {
        self.verify_certificate_signature_erased(
            tbs_der,
            sig_alg_der,
            signature_bits,
            issuer_spki_der,
        )
    }
}

/// Blanket impl: `Box<dyn ErasedSignatureVerifier>` implements [`SignatureVerifier`].
///
/// This lets a `Box<dyn ErasedSignatureVerifier>` (as returned by
/// [`default_signature_verifier`]) be stored directly in
/// `synta_x509_verification::PolicyDefinition::ops` without unwrapping the box.
impl SignatureVerifier for Box<dyn ErasedSignatureVerifier> {
    type Error = PrivateKeyError;

    fn verify_certificate_signature(
        &self,
        tbs_der: &[u8],
        sig_alg_der: &[u8],
        signature_bits: &[u8],
        issuer_spki_der: &[u8],
    ) -> Result<(), PrivateKeyError> {
        self.as_ref().verify_certificate_signature_erased(
            tbs_der,
            sig_alg_der,
            signature_bits,
            issuer_spki_der,
        )
    }
}

/// Return a signature verifier backed by the default crypto backend.
///
/// When the `openssl` feature is enabled, returns an OpenSSL-backed verifier.
/// Returns a [`PrivateKeyError`]-yielding stub when no backend is available.
///
/// The returned object implements [`SignatureVerifier`] via a blanket impl on
/// `dyn ErasedSignatureVerifier` — no backend type appears in the caller's
/// imports.
///
/// # Example
///
/// ```rust,ignore
/// use synta_certificate::default_signature_verifier;
///
/// let verifier = default_signature_verifier();
/// // Pass verifier.as_ref() wherever a &dyn SignatureVerifier is needed.
/// ```
pub fn default_signature_verifier() -> Box<dyn ErasedSignatureVerifier> {
    #[cfg(all(feature = "nss", not(feature = "openssl")))]
    {
        crate::nss_backend::nss_signature_verifier()
    }
    #[cfg(feature = "openssl")]
    {
        crate::openssl_backend::openssl_signature_verifier()
    }
    #[cfg(not(any(feature = "openssl", feature = "nss")))]
    {
        Box::new(NoSignatureVerifierErased)
    }
}

/// No-op erased verifier used when no backend is compiled in.
#[cfg(not(any(feature = "openssl", feature = "nss")))]
struct NoSignatureVerifierErased;

#[cfg(not(any(feature = "openssl", feature = "nss")))]
impl ErasedSignatureVerifier for NoSignatureVerifierErased {
    fn verify_certificate_signature_erased(
        &self,
        _tbs_der: &[u8],
        _sig_alg_der: &[u8],
        _signature_bits: &[u8],
        _issuer_spki_der: &[u8],
    ) -> Result<(), PrivateKeyError> {
        Err(PrivateKeyError::new(NoSignatureVerifierError))
    }
}