pub mod challenge;
pub mod renewal;
pub mod store;
use crate::config::AcmeDirectory;
#[must_use]
pub fn directory_url(directory: &AcmeDirectory) -> String {
match directory {
AcmeDirectory::Staging => instant_acme::LetsEncrypt::Staging.url().to_owned(),
AcmeDirectory::Production => instant_acme::LetsEncrypt::Production.url().to_owned(),
AcmeDirectory::Custom { url } => url.clone(),
}
}
#[must_use]
pub fn directory_label(directory: &AcmeDirectory) -> String {
match directory {
AcmeDirectory::Staging => "staging".to_owned(),
AcmeDirectory::Production => "production".to_owned(),
AcmeDirectory::Custom { url } => format!("custom-{}", short_hash(url)),
}
}
#[must_use]
pub fn short_hash(input: &str) -> String {
use sha2::{Digest as _, Sha256};
let digest = Sha256::digest(input.as_bytes());
let mut out = String::with_capacity(16);
for byte in &digest[..8] {
use std::fmt::Write as _;
let _ = write!(out, "{byte:02x}");
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn directory_url_maps_known_directories() {
assert!(directory_url(&AcmeDirectory::Staging).contains("staging"));
assert!(!directory_url(&AcmeDirectory::Production).contains("staging"));
assert_eq!(
directory_url(&AcmeDirectory::Custom {
url: "https://pebble.test/dir".to_owned()
}),
"https://pebble.test/dir"
);
}
#[test]
fn directory_label_is_stable_and_distinct() {
assert_eq!(directory_label(&AcmeDirectory::Staging), "staging");
assert_eq!(directory_label(&AcmeDirectory::Production), "production");
let a = directory_label(&AcmeDirectory::Custom {
url: "https://a.test/dir".to_owned(),
});
let b = directory_label(&AcmeDirectory::Custom {
url: "https://b.test/dir".to_owned(),
});
assert_ne!(a, b);
assert!(a.starts_with("custom-"));
}
#[test]
fn short_hash_is_deterministic() {
assert_eq!(short_hash("app.example.com"), short_hash("app.example.com"));
assert_ne!(short_hash("a.example.com"), short_hash("b.example.com"));
assert_eq!(short_hash("x").len(), 16);
}
}