tokio_rustls_acme/caches/
test.rs

1use crate::{AccountCache, CertCache};
2use async_trait::async_trait;
3use rcgen::{
4    date_time_ymd, BasicConstraints, CertificateParams, DistinguishedName, DnType, IsCa,
5    KeyUsagePurpose, PKCS_ECDSA_P256_SHA256,
6};
7use std::fmt::Debug;
8use std::marker::PhantomData;
9use std::sync::atomic::AtomicPtr;
10use std::sync::Arc;
11
12/// Test cache, which generates certificates for ACME incompatible test environments.
13/// ```rust
14/// # use tokio_rustls_acme::{AcmeConfig};
15/// # use tokio_rustls_acme::caches::{DirCache, TestCache};
16/// # let test_environment = true;
17/// let mut config = AcmeConfig::new(["example.com"])
18///     .cache(DirCache::new("./cache"));
19/// if test_environment {
20///     config = config.cache(TestCache::new());
21/// }
22/// ```
23#[derive(Clone)]
24pub struct TestCache<EC: Debug = std::io::Error, EA: Debug = std::io::Error> {
25    ca_cert: Arc<rcgen::Certificate>,
26    ca_pem: Arc<String>,
27    ca_key_pair: Arc<rcgen::KeyPair>,
28    _cert_error: PhantomData<AtomicPtr<Box<EC>>>,
29    _account_error: PhantomData<AtomicPtr<Box<EA>>>,
30}
31
32impl<EC: Debug, EA: Debug> Default for TestCache<EC, EA> {
33    fn default() -> Self {
34        let mut params = CertificateParams::default();
35        let mut distinguished_name = DistinguishedName::new();
36        distinguished_name.push(DnType::CountryName, "US");
37        distinguished_name.push(DnType::OrganizationName, "Test CA");
38        distinguished_name.push(DnType::CommonName, "Test CA");
39        params.distinguished_name = distinguished_name;
40
41        params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
42        params.key_usages = vec![KeyUsagePurpose::KeyCertSign, KeyUsagePurpose::CrlSign];
43        params.not_before = date_time_ymd(2000, 1, 1);
44        params.not_after = date_time_ymd(3000, 1, 1);
45
46        let key_pair = rcgen::KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256).unwrap();
47        let ca_cert = params.self_signed(&key_pair).unwrap();
48        let ca_pem = ca_cert.pem();
49        Self {
50            ca_cert: ca_cert.into(),
51            ca_key_pair: key_pair.into(),
52            ca_pem: ca_pem.into(),
53            _cert_error: Default::default(),
54            _account_error: Default::default(),
55        }
56    }
57}
58
59impl<EC: Debug, EA: Debug> TestCache<EC, EA> {
60    pub fn new() -> Self {
61        Self::default()
62    }
63
64    pub fn ca_pem(&self) -> &str {
65        &self.ca_pem
66    }
67}
68
69#[async_trait]
70impl<EC: Debug, EA: Debug> CertCache for TestCache<EC, EA> {
71    type EC = EC;
72    async fn load_cert(
73        &self,
74        domains: &[String],
75        _directory_url: &str,
76    ) -> Result<Option<Vec<u8>>, Self::EC> {
77        let key_pair = rcgen::KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256).unwrap();
78        let mut params = CertificateParams::new(domains).unwrap();
79        let mut distinguished_name = DistinguishedName::new();
80        distinguished_name.push(DnType::CommonName, "Test Cert");
81        params.distinguished_name = distinguished_name;
82        params.not_before = date_time_ymd(2000, 1, 1);
83        params.not_after = date_time_ymd(3000, 1, 1);
84
85        let cert = match params.signed_by(&key_pair, &self.ca_cert, &self.ca_key_pair) {
86            Ok(cert) => cert,
87            Err(err) => {
88                log::error!("test cache: generation error: {:?}", err);
89                return Ok(None);
90            }
91        };
92
93        let cert_pem = cert.pem();
94
95        let pem = [
96            &key_pair.serialize_pem(),
97            "\n",
98            &cert_pem,
99            "\n",
100            &self.ca_pem,
101        ]
102        .concat();
103        Ok(Some(pem.into_bytes()))
104    }
105    async fn store_cert(
106        &self,
107        _domains: &[String],
108        _directory_url: &str,
109        _cert: &[u8],
110    ) -> Result<(), Self::EC> {
111        log::info!("test cache configured, could not store certificate");
112        Ok(())
113    }
114}
115
116#[async_trait]
117impl<EC: Debug, EA: Debug> AccountCache for TestCache<EC, EA> {
118    type EA = EA;
119    async fn load_account(
120        &self,
121        _contact: &[String],
122        _directory_url: &str,
123    ) -> Result<Option<Vec<u8>>, Self::EA> {
124        log::info!("test cache configured, could not load account");
125        Ok(None)
126    }
127    async fn store_account(
128        &self,
129        _contact: &[String],
130        _directory_url: &str,
131        _account: &[u8],
132    ) -> Result<(), Self::EA> {
133        log::info!("test cache configured, could not store account");
134        Ok(())
135    }
136}