use std::{
borrow::ToOwned,
collections::{HashMap, HashSet},
convert::From,
io::BufReader,
sync::{Arc, Mutex},
};
use once_cell::sync::Lazy;
use rustls::{
server::{ClientHello, ResolvesServerCert},
sign::{CertifiedKey, RsaSigningKey},
Certificate, PrivateKey,
};
use sha2::{Digest, Sha256};
use x509_parser::pem::{parse_x509_pem, Pem};
use sozu_command::{
certificate::{get_cn_and_san_attributes, CertificateError, Fingerprint},
proto::command::{AddCertificate, CertificateAndKey, ReplaceCertificate, TlsVersion},
};
use crate::router::trie::{Key, KeyValue, TrieNode};
static DEFAULT_CERTIFICATE: Lazy<ParsedCertificateAndKey> = Lazy::new(|| {
let certificate_and_key = CertificateAndKey {
certificate: include_str!("../assets/certificate.pem").to_string(),
certificate_chain: vec![include_str!("../assets/certificate_chain.pem").to_string()],
key: include_str!("../assets/key.pem").to_string(),
versions: vec![],
names: vec![],
};
GenericCertificateResolver::parse(&certificate_and_key).unwrap()
});
pub trait CertificateResolver {
type Error;
fn get_certificate(&self, fingerprint: &Fingerprint) -> Option<ParsedCertificateAndKey>;
fn add_certificate(&mut self, opts: &AddCertificate) -> Result<Fingerprint, Self::Error>;
fn remove_certificate(&mut self, opts: &Fingerprint) -> Result<(), Self::Error>;
fn replace_certificate(
&mut self,
opts: &ReplaceCertificate,
) -> Result<Fingerprint, Self::Error> {
let fingerprint = self.add_certificate(&AddCertificate {
address: opts.address.to_owned(),
certificate: opts.new_certificate.to_owned(),
expired_at: opts.new_expired_at.to_owned(),
})?;
match hex::decode(&opts.old_fingerprint) {
Ok(old_fingerprint) => self.remove_certificate(&Fingerprint(old_fingerprint))?,
Err(err) => {
error!("failed to parse fingerprint, {}", err);
}
}
Ok(fingerprint)
}
}
pub trait CertificateResolverHelper {
type Error;
fn find_certificates_by_names(
&self,
names: &HashSet<String>,
) -> Result<HashSet<Fingerprint>, Self::Error>;
fn certificate_names(&self, pem: &Pem) -> Result<HashSet<String>, Self::Error>;
fn fingerprint(certificate: &Pem) -> Fingerprint;
fn parse(
certificate_and_key: &CertificateAndKey,
) -> Result<ParsedCertificateAndKey, Self::Error>;
}
#[derive(Clone, Debug)]
pub struct CertificateOverride {
pub names: Option<HashSet<String>>,
pub expiration: Option<i64>,
}
impl From<&AddCertificate> for CertificateOverride {
fn from(opts: &AddCertificate) -> Self {
let mut names = None;
if !opts.certificate.names.is_empty() {
names = Some(opts.certificate.names.iter().cloned().collect())
}
Self {
names,
expiration: opts.expired_at.to_owned(),
}
}
}
#[derive(Debug)]
pub struct ParsedCertificateAndKey {
pub certificate: Pem,
pub chain: Vec<Pem>,
pub key: String,
pub versions: Vec<TlsVersion>,
}
impl Clone for ParsedCertificateAndKey {
fn clone(&self) -> Self {
let certificate = Pem {
label: self.certificate.label.to_owned(),
contents: self.certificate.contents.to_owned(),
};
Self {
certificate,
chain: self
.chain
.iter()
.map(|chain| Pem {
label: chain.label.to_owned(),
contents: chain.contents.to_owned(),
})
.collect(),
key: self.key.to_owned(),
versions: self.versions.to_owned(),
}
}
}
#[derive(thiserror::Error, Clone, Debug)]
pub enum GenericCertificateResolverError {
#[error("failed to get common name and subject alternate names from pem, {0}")]
InvalidCommonNameAndSubjectAlternateNames(CertificateError),
#[error("failed to parse pem certificate, {0}")]
InvalidPem(String),
#[error("failed to parse der certificate, {0}")]
InvalidDer(String),
#[error("certificate, chain or private key is invalid")]
InvalidPrivateKey,
#[error("certificate is still in use")]
IsStillInUse,
}
#[derive(Debug)]
pub struct GenericCertificateResolver {
pub domains: TrieNode<Fingerprint>,
certificates: HashMap<Fingerprint, ParsedCertificateAndKey>,
name_fingerprint_idx: HashMap<String, HashSet<Fingerprint>>,
overrides: HashMap<Fingerprint, CertificateOverride>,
}
impl CertificateResolver for GenericCertificateResolver {
type Error = GenericCertificateResolverError;
fn get_certificate(&self, fingerprint: &Fingerprint) -> Option<ParsedCertificateAndKey> {
self.certificates.get(fingerprint).map(ToOwned::to_owned)
}
fn add_certificate(&mut self, opts: &AddCertificate) -> Result<Fingerprint, Self::Error> {
let parsed_certificate_and_key = Self::parse(&opts.certificate)?;
let fingerprint = Self::fingerprint(&parsed_certificate_and_key.certificate);
if !opts.certificate.names.is_empty() || opts.expired_at.is_some() {
self.overrides
.insert(fingerprint.to_owned(), CertificateOverride::from(opts));
} else {
self.overrides.remove(&fingerprint);
}
let (ok, certificates_to_remove) =
self.should_insert(&fingerprint, &parsed_certificate_and_key)?;
if !ok {
return Ok(fingerprint);
}
let new_names = match self.get_names_override(&fingerprint) {
Some(names) => names,
None => self.certificate_names(&parsed_certificate_and_key.certificate)?,
};
self.certificates
.insert(fingerprint.to_owned(), parsed_certificate_and_key);
for name in new_names {
self.domains
.insert(name.to_owned().into_bytes(), fingerprint.to_owned());
self.name_fingerprint_idx
.entry(name)
.or_insert_with(HashSet::new)
.insert(fingerprint.to_owned());
}
for (fingerprint, names) in &certificates_to_remove {
for name in names {
if let Some(fingerprints) = self.name_fingerprint_idx.get_mut(name) {
fingerprints.remove(fingerprint);
}
}
self.certificates.remove(fingerprint);
}
Ok(fingerprint.to_owned())
}
fn remove_certificate(&mut self, fingerprint: &Fingerprint) -> Result<(), Self::Error> {
if let Some(certificate_and_key) = self.get_certificate(fingerprint) {
let names = match self.get_names_override(fingerprint) {
Some(names) => names,
None => self.certificate_names(&certificate_and_key.certificate)?,
};
if self.is_required_for_domain(&names, fingerprint) {
return Err(GenericCertificateResolverError::IsStillInUse);
}
for name in &names {
if let Some(fingerprints) = self.name_fingerprint_idx.get_mut(name) {
fingerprints.remove(fingerprint);
if fingerprints.is_empty() {
self.domains.domain_remove(&name.to_owned().into_bytes());
}
}
}
self.certificates.remove(fingerprint);
}
Ok(())
}
}
impl CertificateResolverHelper for GenericCertificateResolver {
type Error = GenericCertificateResolverError;
fn find_certificates_by_names(
&self,
names: &HashSet<String>,
) -> Result<HashSet<Fingerprint>, Self::Error> {
let mut fingerprints = HashSet::new();
for name in names {
if let Some(fprints) = self.name_fingerprint_idx.get(name) {
fprints.iter().for_each(|fingerprint| {
fingerprints.insert(fingerprint.to_owned());
});
}
}
Ok(fingerprints)
}
fn certificate_names(&self, pem: &Pem) -> Result<HashSet<String>, Self::Error> {
let fingerprint = Self::fingerprint(pem);
if let Some(certificate_override) = self.overrides.get(&fingerprint) {
if let Some(names) = &certificate_override.names {
return Ok(names.to_owned());
}
}
get_cn_and_san_attributes(pem)
.map_err(GenericCertificateResolverError::InvalidCommonNameAndSubjectAlternateNames)
}
fn fingerprint(pem: &Pem) -> Fingerprint {
Fingerprint(Sha256::digest(&pem.contents).iter().cloned().collect())
}
fn parse(
certificate_and_key: &CertificateAndKey,
) -> Result<ParsedCertificateAndKey, Self::Error> {
let certificate =
sozu_command::certificate::parse(certificate_and_key.certificate.as_bytes())
.map_err(|err| GenericCertificateResolverError::InvalidPem(err.to_string()))?;
let mut chains = vec![];
for chain in &certificate_and_key.certificate_chain {
let (_, chain) = parse_x509_pem(chain.as_bytes())
.map_err(|err| GenericCertificateResolverError::InvalidPem(err.to_string()))?;
chains.push(chain);
}
let mut key_reader = BufReader::new(certificate_and_key.key.as_bytes());
let parsed_keys = rustls_pemfile::rsa_private_keys(&mut key_reader);
if let Ok(mut keys) = parsed_keys {
if !keys.is_empty() {
let key = PrivateKey(keys.swap_remove(0));
if RsaSigningKey::new(&key).is_ok() {
let versions = certificate_and_key
.versions
.iter()
.filter_map(|v| TlsVersion::try_from(*v).ok())
.collect();
return Ok(ParsedCertificateAndKey {
certificate,
chain: chains,
key: certificate_and_key.key.to_owned(),
versions,
});
}
}
}
let mut key_reader = BufReader::new(certificate_and_key.key.as_bytes());
let parsed_keys = rustls_pemfile::pkcs8_private_keys(&mut key_reader);
if let Ok(mut keys) = parsed_keys {
if !keys.is_empty() {
let key = PrivateKey(keys.swap_remove(0));
if RsaSigningKey::new(&key).is_ok() {
let versions = certificate_and_key
.versions
.iter()
.filter_map(|v| TlsVersion::try_from(*v).ok())
.collect();
return Ok(ParsedCertificateAndKey {
certificate,
chain: chains,
key: certificate_and_key.key.to_owned(),
versions,
});
}
if rustls::sign::any_ecdsa_type(&key).is_ok() {
let versions = certificate_and_key
.versions
.iter()
.filter_map(|v| TlsVersion::try_from(*v).ok())
.collect();
return Ok(ParsedCertificateAndKey {
certificate,
chain: chains,
key: certificate_and_key.key.to_owned(),
versions,
});
}
}
}
Err(GenericCertificateResolverError::InvalidPrivateKey)
}
}
impl Default for GenericCertificateResolver {
fn default() -> Self {
Self {
domains: TrieNode::root(),
certificates: Default::default(),
name_fingerprint_idx: Default::default(),
overrides: Default::default(),
}
}
}
impl GenericCertificateResolver {
pub fn new() -> Self {
Self::default()
}
fn is_required_for_domain(&self, names: &HashSet<String>, fingerprint: &Fingerprint) -> bool {
for name in names {
if let Some(fingerprints) = self.name_fingerprint_idx.get(name) {
if 1 == fingerprints.len() && fingerprints.get(fingerprint).is_some() {
return true;
}
}
}
false
}
fn should_insert(
&self,
fingerprint: &Fingerprint,
parsed_certificate_and_key: &ParsedCertificateAndKey,
) -> Result<(bool, HashMap<Fingerprint, HashSet<String>>), GenericCertificateResolverError>
{
let x509 = parsed_certificate_and_key
.certificate
.parse_x509()
.map_err(|err| GenericCertificateResolverError::InvalidDer(err.to_string()))?;
let new_names = match self.get_names_override(fingerprint) {
Some(names) => names,
None => self.certificate_names(&parsed_certificate_and_key.certificate)?,
};
let expiration = self
.get_expiration_override(fingerprint)
.unwrap_or_else(|| x509.validity().not_after.timestamp());
let fingerprints = self.find_certificates_by_names(&new_names)?;
let mut certificates = HashMap::new();
for fingerprint in &fingerprints {
if let Some(certificate_and_key) = self.get_certificate(fingerprint) {
certificates.insert(fingerprint, certificate_and_key);
}
}
let mut should_insert = false;
let mut certificates_to_remove = HashMap::new();
let mut certificates_names = HashSet::new();
for (fingerprint, certificate_and_key) in certificates {
let certificate = certificate_and_key
.certificate
.parse_x509()
.map_err(|err| GenericCertificateResolverError::InvalidDer(err.to_string()))?;
let certificate_names = match self.get_names_override(fingerprint) {
Some(names) => names,
None => self.certificate_names(&parsed_certificate_and_key.certificate)?,
};
let certificate_expiration = self
.get_expiration_override(fingerprint)
.unwrap_or_else(|| certificate.validity().not_after.timestamp());
let extra_names = certificate_names
.difference(&new_names)
.collect::<HashSet<_>>();
if extra_names.is_empty() && certificate_expiration < expiration {
certificates_to_remove.insert(fingerprint.to_owned(), certificate_names.to_owned());
should_insert = true;
}
for name in certificate_names {
certificates_names.insert(name);
}
}
let diff: HashSet<&String> = new_names.difference(&certificates_names).collect();
if !should_insert && diff.is_empty() {
return Ok((false, certificates_to_remove));
}
Ok((true, certificates_to_remove))
}
fn get_expiration_override(&self, fingerprint: &Fingerprint) -> Option<i64> {
self.overrides.get(fingerprint).and_then(|co| co.expiration)
}
fn get_names_override(&self, fingerprint: &Fingerprint) -> Option<HashSet<String>> {
self.overrides
.get(fingerprint)
.and_then(|co| co.names.to_owned())
}
pub fn domain_lookup(
&self,
domain: &[u8],
accept_wildcard: bool,
) -> Option<&KeyValue<Key, Fingerprint>> {
self.domains.domain_lookup(domain, accept_wildcard)
}
}
#[derive(Debug)]
pub struct MutexWrappedCertificateResolver(pub Mutex<GenericCertificateResolver>);
impl ResolvesServerCert for MutexWrappedCertificateResolver {
fn resolve(&self, client_hello: ClientHello) -> Option<Arc<CertifiedKey>> {
let server_name = client_hello.server_name();
let sigschemes = client_hello.signature_schemes();
if server_name.is_none() {
error!("cannot look up certificate: no SNI from session");
return None;
}
let name: &str = server_name.unwrap();
trace!(
"trying to resolve name: {:?} for signature scheme: {:?}",
name,
sigschemes
);
if let Ok(ref mut resolver) = self.0.try_lock() {
if let Some((_, fingerprint)) = resolver.domains.domain_lookup(name.as_bytes(), true) {
trace!(
"looking for certificate for {:?} with fingerprint {:?}",
name,
fingerprint
);
return resolver
.certificates
.get(fingerprint)
.and_then(Self::generate_certified_key)
.map(Arc::new);
}
}
debug!("Default certificate is used for {}", name);
incr!("tls.default_cert_used");
Self::generate_certified_key(&DEFAULT_CERTIFICATE).map(Arc::new)
}
}
impl Default for MutexWrappedCertificateResolver {
fn default() -> Self {
Self(Mutex::new(GenericCertificateResolver::default()))
}
}
impl MutexWrappedCertificateResolver {
pub fn new() -> Self {
Self::default()
}
fn generate_certified_key(
certificate_and_key: &ParsedCertificateAndKey,
) -> Option<CertifiedKey> {
let mut chains = vec![Certificate(
certificate_and_key.certificate.contents.to_owned(),
)];
for certificate in &certificate_and_key.chain {
chains.push(Certificate(certificate.contents.to_owned()));
}
let mut key_reader = BufReader::new(certificate_and_key.key.as_bytes());
let parsed_key = rustls_pemfile::rsa_private_keys(&mut key_reader);
if let Ok(mut keys) = parsed_key {
if !keys.is_empty() {
let key = PrivateKey(keys.swap_remove(0));
if let Ok(signing_key) = RsaSigningKey::new(&key) {
let certified = CertifiedKey::new(chains, Arc::new(signing_key));
return Some(certified);
}
} else {
let mut key_reader = BufReader::new(certificate_and_key.key.as_bytes());
let parsed_key = rustls_pemfile::pkcs8_private_keys(&mut key_reader);
if let Ok(mut keys) = parsed_key {
if !keys.is_empty() {
let key = PrivateKey(keys.swap_remove(0));
if let Ok(signing_key) = RsaSigningKey::new(&key) {
let certified = CertifiedKey::new(chains, Arc::new(signing_key));
return Some(certified);
} else if let Ok(k) = rustls::sign::any_ecdsa_type(&key) {
let certified = CertifiedKey::new(chains, k);
return Some(certified);
} else {
error!("could not decode signing key (tried RSA and ECDSA)");
}
}
}
}
} else {
error!("could not parse private key: {:?}", parsed_key);
}
None
}
}
#[cfg(test)]
mod tests {
use std::{
collections::HashSet,
error::Error,
time::{Duration, SystemTime},
};
use super::{
CertificateResolver, CertificateResolverHelper, GenericCertificateResolver,
GenericCertificateResolverError,
};
use rand::{seq::SliceRandom, thread_rng};
use sozu_command::proto::command::{AddCertificate, CertificateAndKey};
use x509_parser::pem::parse_x509_pem;
#[test]
fn lifecycle() -> Result<(), Box<dyn Error + Send + Sync>> {
let address = "127.0.0.1:8080".to_string();
let mut resolver = GenericCertificateResolver::new();
let certificate_and_key = CertificateAndKey {
certificate: String::from(include_str!("../assets/certificate.pem")),
key: String::from(include_str!("../assets/key.pem")),
..Default::default()
};
let (_, pem) = parse_x509_pem(certificate_and_key.certificate.as_bytes())
.map_err(|err| GenericCertificateResolverError::InvalidPem(err.to_string()))?;
let fingerprint = resolver.add_certificate(&AddCertificate {
address: address,
certificate: certificate_and_key,
expired_at: None,
})?;
if resolver.get_certificate(&fingerprint).is_none() {
return Err("failed to retrieve certificate".into());
}
if let Err(err) = resolver.remove_certificate(&fingerprint) {
match err {
GenericCertificateResolverError::IsStillInUse => {}
_ => {
return Err(format!("the certificate must not been removed, {err}").into());
}
}
}
let names = resolver.certificate_names(&pem)?;
if resolver.find_certificates_by_names(&names)?.is_empty()
|| resolver.get_certificate(&fingerprint).is_none()
{
return Err(
"failed to retrieve certificate that we had the command to delete, but mandatory"
.into(),
);
}
Ok(())
}
#[test]
fn name_override() -> Result<(), Box<dyn Error + Send + Sync>> {
let address = "127.0.0.1:8080".to_string();
let mut resolver = GenericCertificateResolver::new();
let certificate_and_key = CertificateAndKey {
certificate: String::from(include_str!("../assets/certificate.pem")),
key: String::from(include_str!("../assets/key.pem")),
names: vec!["localhost".into(), "lolcatho.st".into()],
..Default::default()
};
let (_, pem) = parse_x509_pem(certificate_and_key.certificate.as_bytes())
.map_err(|err| GenericCertificateResolverError::InvalidPem(err.to_string()))?;
let fingerprint = resolver.add_certificate(&AddCertificate {
address: address,
certificate: certificate_and_key,
expired_at: None,
})?;
if resolver.get_certificate(&fingerprint).is_none() {
return Err("failed to retrieve certificate".into());
}
if let Err(err) = resolver.remove_certificate(&fingerprint) {
match err {
GenericCertificateResolverError::IsStillInUse => {}
_ => {
return Err(format!("the certificate must not been removed, {err}").into());
}
}
}
let names = resolver.certificate_names(&pem)?;
if resolver.find_certificates_by_names(&names)?.is_empty()
|| resolver.get_certificate(&fingerprint).is_none()
{
return Err(
"failed to retrieve certificate that we had the command to delete, but mandatory"
.into(),
);
}
let mut lolcat = HashSet::new();
lolcat.insert(String::from("lolcatho.st"));
if resolver.find_certificates_by_names(&lolcat)?.is_empty()
|| resolver.get_certificate(&fingerprint).is_none()
{
return Err(
"failed to retrieve certificate that we had the command to delete, but mandatory"
.into(),
);
}
Ok(())
}
#[test]
fn replacement() -> Result<(), Box<dyn Error + Send + Sync>> {
let address = "127.0.0.1:8080".to_string();
let mut resolver = GenericCertificateResolver::new();
let certificate_and_key_1y = CertificateAndKey {
certificate: String::from(include_str!("../assets/tests/certificate-1y.pem")),
key: String::from(include_str!("../assets/tests/key-1y.pem")),
..Default::default()
};
let (_, pem) = parse_x509_pem(certificate_and_key_1y.certificate.as_bytes())
.map_err(|err| GenericCertificateResolverError::InvalidPem(err.to_string()))?;
let names_1y = resolver.certificate_names(&pem)?;
let fingerprint_1y = resolver.add_certificate(&AddCertificate {
address: address.clone(),
certificate: certificate_and_key_1y,
expired_at: None,
})?;
if resolver.get_certificate(&fingerprint_1y).is_none() {
return Err("failed to retrieve certificate".into());
}
let certificate_and_key_2y = CertificateAndKey {
certificate: String::from(include_str!("../assets/tests/certificate-2y.pem")),
key: String::from(include_str!("../assets/tests/key-2y.pem")),
..Default::default()
};
let fingerprint_2y = resolver.add_certificate(&AddCertificate {
address,
certificate: certificate_and_key_2y,
expired_at: None,
})?;
if resolver.get_certificate(&fingerprint_2y).is_none() {
return Err("failed to retrieve certificate".into());
}
if resolver.get_certificate(&fingerprint_1y).is_some() {
return Err("certificate must be replaced by the 2y expiration one".into());
}
if resolver.get_certificate(&fingerprint_2y).is_none() {
return Err("certificate must be added instead of the 1y expiration one".into());
}
let fingerprints = resolver.find_certificates_by_names(&names_1y)?;
if fingerprints.get(&fingerprint_1y).is_some() {
return Err("index must not reference the 1y expiration certificate".into());
}
if fingerprints.get(&fingerprint_2y).is_none() {
return Err("index have to reference the 2y expiration certificate".into());
}
Ok(())
}
#[test]
fn expiration_override() -> Result<(), Box<dyn Error + Send + Sync>> {
let address = "127.0.0.1:8080".to_string();
let mut resolver = GenericCertificateResolver::new();
let certificate_and_key_1y = CertificateAndKey {
certificate: String::from(include_str!("../assets/tests/certificate-1y.pem")),
key: String::from(include_str!("../assets/tests/key-1y.pem")),
..Default::default()
};
let (_, pem) = parse_x509_pem(certificate_and_key_1y.certificate.as_bytes())
.map_err(|err| GenericCertificateResolverError::InvalidPem(err.to_string()))?;
let names_1y = resolver.certificate_names(&pem)?;
let fingerprint_1y = resolver.add_certificate(&AddCertificate {
address: address.clone(),
certificate: certificate_and_key_1y,
expired_at: Some(
(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?
+ Duration::from_secs(3 * 365 * 24 * 3600))
.as_secs() as i64,
),
})?;
if resolver.get_certificate(&fingerprint_1y).is_none() {
return Err("failed to retrieve certificate".into());
}
let certificate_and_key_2y = CertificateAndKey {
certificate: String::from(include_str!("../assets/tests/certificate-2y.pem")),
key: String::from(include_str!("../assets/tests/key-2y.pem")),
..Default::default()
};
let fingerprint_2y = resolver.add_certificate(&AddCertificate {
address,
certificate: certificate_and_key_2y,
expired_at: None,
})?;
if resolver.get_certificate(&fingerprint_2y).is_some() {
return Err("certificate should not be loaded".into());
}
if resolver.get_certificate(&fingerprint_1y).is_none() {
return Err("certificate must not be replaced by the 2y expiration one".into());
}
if resolver.get_certificate(&fingerprint_2y).is_some() {
return Err("certificate must not be added instead of the 1y expiration one".into());
}
let fingerprints = resolver.find_certificates_by_names(&names_1y)?;
if fingerprints.get(&fingerprint_1y).is_none() {
return Err("index must reference the 1y expiration certificate".into());
}
if fingerprints.get(&fingerprint_2y).is_some() {
return Err("index must not reference the 2y expiration certificate".into());
}
Ok(())
}
#[test]
fn random() -> Result<(), Box<dyn Error + Send + Sync>> {
let mut certificates = vec![
CertificateAndKey {
certificate: include_str!("../assets/tests/certificate-1.pem").to_string(),
key: include_str!("../assets/tests/key.pem").to_string(),
..Default::default()
},
CertificateAndKey {
certificate: include_str!("../assets/tests/certificate-2.pem").to_string(),
key: include_str!("../assets/tests/key.pem").to_string(),
..Default::default()
},
CertificateAndKey {
certificate: include_str!("../assets/tests/certificate-3.pem").to_string(),
key: include_str!("../assets/tests/key.pem").to_string(),
..Default::default()
},
CertificateAndKey {
certificate: include_str!("../assets/tests/certificate-4.pem").to_string(),
key: include_str!("../assets/tests/key.pem").to_string(),
..Default::default()
},
CertificateAndKey {
certificate: include_str!("../assets/tests/certificate-5.pem").to_string(),
key: include_str!("../assets/tests/key.pem").to_string(),
..Default::default()
},
CertificateAndKey {
certificate: include_str!("../assets/tests/certificate-6.pem").to_string(),
key: include_str!("../assets/tests/key.pem").to_string(),
..Default::default()
},
];
let mut fingerprints = vec![];
for certificate in &certificates {
let (_, pem) = parse_x509_pem(certificate.certificate.as_bytes())
.map_err(|err| GenericCertificateResolverError::InvalidPem(err.to_string()))?;
fingerprints.push(GenericCertificateResolver::fingerprint(&pem));
}
certificates.shuffle(&mut thread_rng());
let address = "127.0.0.1:8080".to_string();
let mut resolver = GenericCertificateResolver::default();
for certificate in &certificates {
resolver.add_certificate(&AddCertificate {
address: address.clone(),
certificate: certificate.to_owned(),
expired_at: None,
})?;
}
let mut names = HashSet::new();
names.insert("example.org".to_string());
let fprints = resolver.find_certificates_by_names(&names)?;
if 1 != fprints.len() && !fprints.contains(&fingerprints[1]) {
return Err("domain 'example.org' resolve to the wrong certificate".into());
}
let mut names = HashSet::new();
names.insert("*.example.org".to_string());
let fprints = resolver.find_certificates_by_names(&names)?;
if 1 != fprints.len() && !fprints.contains(&fingerprints[2]) {
return Err("domain '*.example.org' resolve to the wrong certificate".into());
}
let mut names = HashSet::new();
names.insert("clever-cloud.com".to_string());
let fprints = resolver.find_certificates_by_names(&names)?;
if 1 != fprints.len() && !fprints.contains(&fingerprints[4]) {
return Err("domain 'clever-cloud.com' resolve to the wrong certificate".into());
}
let mut names = HashSet::new();
names.insert("*.clever-cloud.com".to_string());
let fprints = resolver.find_certificates_by_names(&names)?;
if 1 != fprints.len() && !fprints.contains(&fingerprints[5]) {
return Err("domain '*.clever-cloud.com' resolve to the wrong certificate".into());
}
Ok(())
}
}