est-ca 0.2.0

RFC 7030 Enrollment over Secure Transport (EST) — client, server, and an internal X.509 CA in pure Rust.
//! Pluggable authentication for the EST enrollment path.
//!
//! EST bootstrap auth (RFC 7030 §3.2.3) is typically HTTP Basic with a
//! pre-shared token; renewal uses mTLS where the previous cert proves
//! identity. Consumers plug in their own [`AuthBackend`] so this crate
//! stays policy-free.

#[cfg(feature = "server")]
pub mod basic;

/// The authenticated identity returned by an [`AuthBackend`].
///
/// The `id` is placed verbatim into the Subject CommonName of the
/// issued certificate — it is the *authoritative* identifier, which
/// means anything the CSR said about its Subject is ignored. Callers
/// that need richer identity (tenant, region, role) can carry that in
/// their own `AuthBackend` implementation and encode it into the `id`
/// string, or extend this type downstream.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Principal {
    /// Stable identifier for the principal. Becomes the Subject CN.
    pub id: String,
}

impl Principal {
    /// Construct a principal with the given stable identity.
    pub fn new(id: impl Into<String>) -> Self {
        Self { id: id.into() }
    }
}

/// Policy backend that decides which principals may enroll.
///
/// Implementations typically look up a bootstrap token in a persistent
/// store (file, DB, HSM) and return the [`Principal`] it maps to. An
/// open-enrollment deployment can accept any token and derive the
/// principal from the token string itself; an allowlist deployment
/// rejects unknown tokens.
///
/// # Security — use constant-time comparison
///
/// Implementations MUST compare secret credentials (passwords, HMAC
/// keys, bearer tokens) in **constant time**. Naïve `==` / `String::eq`
/// short-circuits on the first differing byte, which leaks
/// length/prefix information over the network and enables practical
/// remote timing attacks. The
/// [`subtle`](https://docs.rs/subtle) crate's `ConstantTimeEq` trait
/// is the standard Rust primitive:
///
/// ```ignore
/// use subtle::ConstantTimeEq;
/// let ok: bool = expected.as_bytes().ct_eq(provided.as_bytes()).into();
/// ```
///
/// Better still: store Argon2 / bcrypt hashes of the tokens and use
/// the hasher's `verify` function — it handles constant-time internally.
pub trait AuthBackend: Send + Sync + 'static {
    /// Verify a bootstrap credential presented at `/simpleenroll`.
    ///
    /// `user` is the HTTP Basic username (or an opaque token id),
    /// `pass` is the password/secret. Return the verified principal
    /// on success, or an error string on rejection.
    ///
    /// **Implementers**: compare `pass` in constant time — see the
    /// trait-level security note for why.
    fn verify_bootstrap(&self, user: &str, pass: &str) -> Result<Principal, String>;
}