Add some documentation

Signed-off-by: Erik Hollensbe <git@hollensbe.org>
This commit is contained in:
Erik Hollensbe
2022-02-17 05:07:03 -08:00
parent 5ccb3dd978
commit b488f2ac8a
5 changed files with 68 additions and 3 deletions
+21
View File
@@ -26,6 +26,9 @@ pub(crate) fn st_to_asn1(time: SystemTime) -> Result<Asn1Time, ErrorStack> {
)
}
/// CA defines a certificate authority in the standard sense of the word; it is used to sign
/// certificate signing requests and return them as fully functional certificates. To create one,
/// use the ::new constructor.
#[derive(Clone, Debug)]
pub struct CA {
certificate: X509,
@@ -33,6 +36,7 @@ pub struct CA {
}
impl CA {
/// new constructs a new certificate authority with a X.509 certificate and private key.
pub fn new(certificate: X509, private_key: PKey<Private>) -> Self {
Self {
certificate,
@@ -40,14 +44,18 @@ impl CA {
}
}
/// returns the certificate
pub fn certificate(self) -> X509 {
self.certificate
}
/// returns the private key
pub fn private_key(self) -> PKey<Private> {
self.private_key
}
/// signs a CSR with the CA's private key. The not_before and not_after parameters can be used
/// to control its lifetime.
pub fn generate_and_sign_cert(
&self,
req: X509Req,
@@ -115,6 +123,8 @@ impl CA {
Ok(builder.build())
}
/// new_test_ca is a convenience function for creating a quick and dirty CA for use in tests
/// and demo applications (such as the examples).
pub fn new_test_ca() -> Result<Self, ErrorStack> {
let mut builder = X509::builder()?;
@@ -186,15 +196,21 @@ impl CA {
}
}
/// CACollector is an async observer which waits for a CA to arrive, and fosters the creation of
/// signed CSRs as certificates. This allows for the rotation of CA certificates, or delayed
/// loading, without loss of functionality due to race conditions. Please see the `acmed` example for usage.
#[derive(Clone, Debug)]
pub struct CACollector {
poll_interval: Duration,
ca: SharedCA,
}
/// SharedCA is a simple type for managing the locking around a CA.
type SharedCA = Arc<RwLock<Option<CA>>>;
impl CACollector {
/// new is a constructor; the duration provided determines how often the loop will awake and
/// process a CA injection.
pub fn new(poll_interval: Duration) -> Self {
Self {
poll_interval,
@@ -202,10 +218,13 @@ impl CACollector {
}
}
/// returns the CA as a SharedCA.
pub fn ca(self) -> SharedCA {
self.ca.clone()
}
/// majority of callers will use this function to collect the CA. It takes a closure which
/// accepts a CA and returns it to this function so that it can overwrite the previous CA.
pub async fn spawn_collector<F>(&mut self, f: F)
where
F: Fn() -> Result<CA, ErrorStack>,
@@ -221,6 +240,8 @@ impl CACollector {
}
}
/// similar to CA::generate_and_sign_cert, this signs the CSR through the SharedCA provided by
/// the collector.
pub async fn sign(
self,
req: X509Req,
+15 -2
View File
@@ -14,9 +14,13 @@ use super::handlers::order::OrderStatus;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(into = "String")]
/// ChallengeType is an enum describing the challenge types coyote supports. Currently tls-alpn is
/// unsupported.
pub enum ChallengeType {
DNS01, // dns-01 challenge type
HTTP01, // http-01 challenge type
/// dns-01 challenge type
DNS01,
/// http-01 challenge type
HTTP01,
}
impl TryFrom<&str> for ChallengeType {
@@ -47,12 +51,16 @@ impl ChallengeType {
}
#[derive(Clone)]
/// Challenger is an async supervisor used to perform challenges on demand. This is a simple
/// monitored queue with expiration applied at every loop iteration.
pub struct Challenger {
list: Arc<Mutex<HashMap<String, Challenge>>>,
expiration: Option<chrono::Duration>,
}
impl Challenger {
/// Construct a new challenger; challenges will last as long as `expiriation` is set to, or
/// forever if Option::None.
pub fn new(expiration: Option<chrono::Duration>) -> Self {
Self {
list: Arc::new(Mutex::new(HashMap::new())),
@@ -64,6 +72,9 @@ impl Challenger {
self.list.lock().await.insert(c.reference.clone(), c);
}
/// tick should be called in a loop in its own async routine with an interval between
/// iterations. This performs each challenge in the queue and invalidates any expired
/// challenges. To commit to storage, call reconcile.
pub async fn tick<T>(&self, ticker: T)
where
T: Fn(Challenge) -> Option<()>,
@@ -118,6 +129,8 @@ impl Challenger {
}
}
/// reconcile should be called after tick. This actually commits the challenge results to the
/// backing storage.
pub async fn reconcile(&self, db: Postgres) -> Result<(), SaveError> {
let mut lock = self.list.lock().await;
let mut db_lock = db.client().await?;
+3
View File
@@ -3,6 +3,8 @@ use std::str::FromStr;
use trust_dns_client::rr::Name;
#[derive(Debug, Clone, PartialEq)]
/// DNSName is used to provide a serde interface to DNS names. It is not frequently consumed by
/// external consumers.
pub struct DNSName(pub(crate) Name);
impl DNSName {
@@ -15,6 +17,7 @@ impl DNSName {
}
}
/// serde codec implementation
pub struct DNSNameVisitor;
impl<'de> Visitor<'de> for DNSNameVisitor {
+9
View File
@@ -1,7 +1,12 @@
/// Certificate Authority functionality
pub mod ca;
/// Challenge management, including supervisory handlers.
pub mod challenge;
/// Types for managing DNS records
pub mod dns;
/// ACME HTTP handlers
pub mod handlers;
/// ACME JOSE implementation
pub mod jose;
use std::{collections::HashSet, convert::TryFrom, sync::Arc};
@@ -90,6 +95,9 @@ impl ACMEIdentifier {
}
#[async_trait]
/// NonceValidator is a storage trait that controls the generation and validation of nonces, used
/// heavily in ACME and especially in the `Replay-Nonce` HTTP header present in all calls, and the
/// `nonce` field in ACME protected headers.
pub trait NonceValidator {
/// This function must mutate the underlying storage to prune the nonce it's validating after a
/// successful fetch. One may use ACMEValidationError::NonceFetchError to specify errors with
@@ -134,6 +142,7 @@ impl NonceValidator for SetValidator {
}
#[derive(Clone)]
/// Defines a PostgreSQL-backed nonce validator
pub struct PostgresNonceValidator(crate::models::Postgres);
impl PostgresNonceValidator {
+20 -1
View File
@@ -1,6 +1,25 @@
#![allow(dead_code)]
//! Coyote lets you make ACME servers, which are not guaranteed to not explode in
//! your face. You have to code that out yourself.
//!
//! coyote aims to solve a few problems (not all of these are solved yet; see "Task List" below):
//!
//! - Provide ACME with backing storage you prefer to use, by way of Rust's traits for storage implementation.
//! - Provide ACME in non-conforming scenarios (e.g., behind corporate firewalls)
//! - Provide ACME services with hooks into the validation system, so you can implement validations however you feel like.
//! - It's a library; make it as big or as small as you like. No need for multiple implementations.
//! - A FOSS alternative to the letsencrypt canonical implementation that is _also_ tested against LE's test suite.
//!
//! `acmed` comes as an example with coyote; it is a complete canonical implementation against PostgreSQL for backing storage. It (deliberately) allows all challenges through and is not meant for production usage.
//!
//! `coyote` is intended to let you build an ACME service without using `acmed` itself, leveraging the traits and tools available in this library for scaffolding. For example, work to implement a Redis based nonce validation system would just be a trait implementation, even though it is not available in this library.
//!
/// Core ACME implementation, including HTTP handlers, JOSE implementation and plenty of crypto
pub mod acme;
/// Errors and conversions between different Error types
pub mod errors;
/// Database types and traits
pub mod models;
pub mod test;
pub(crate) mod test;
pub(crate) mod util;