kanidmd_core/
crypto.rs

1//! This module contains cryptographic setup code, a long with what policy
2//! and ciphers we accept.
3
4use openssl::ec::{EcGroup, EcKey};
5use openssl::error::ErrorStack;
6use openssl::nid::Nid;
7use openssl::pkey::{PKeyRef, Private};
8use openssl::rsa::Rsa;
9use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslSessionCacheMode, SslVerifyMode};
10use openssl::x509::{
11    extension::{
12        AuthorityKeyIdentifier, BasicConstraints, ExtendedKeyUsage, KeyUsage,
13        SubjectAlternativeName, SubjectKeyIdentifier,
14    },
15    X509NameBuilder, X509ReqBuilder, X509,
16};
17use openssl::{asn1, bn, hash, pkey};
18use sketching::*;
19
20use crate::config::TlsConfiguration;
21
22use std::fs;
23use std::io::{ErrorKind, Read, Write};
24use std::path::Path;
25
26const CA_VALID_DAYS: u32 = 30;
27const CERT_VALID_DAYS: u32 = 5;
28
29// Basing minimums off https://www.keylength.com setting "year" to 2030 - tested as at 2023-09-25
30//
31// |Method           |Date     |Symmetric| FM      |DL Key| DL Group|Elliptic Curve|Hash|
32// |   ---           |   ---   |   ---   |   ---   | ---  |   ---   |  ---         | ---|
33// |Lenstra / Verheul|2030     |  93     |2493^2016|165   | 2493    |  176         | 186|
34// |Lenstra Updated  |2030     |  88     |1698^2063|176   | 1698    |  176         | 176|
35// |ECRYPT           |2029-2068|  256    |15360    |512   | 15360   |  512         | 512|
36// |NIST             |2019-2030|  112    |2048     |224   | 2048    |  224         | 224|
37// |ANSSI            |> 2030   |  128    |3072     |200   | 3072    |  256         | 256|
38// |NSA              |-        |  256    |3072     |-     | -       |  384         | 384|
39// |RFC3766          |-        |  -      |   -     | -    |   -     |   -          |  - |
40// |BSI              |-        |  -      |   -     | -    |   -     |   -          |  - |
41// DL - Discrete Logarithm
42// FM - Factoring Modulus
43
44const RSA_MIN_KEY_SIZE_BITS: u64 = 2048;
45const EC_MIN_KEY_SIZE_BITS: u64 = 224;
46
47/// returns a signing function that meets a sensible minimum
48fn get_signing_func() -> hash::MessageDigest {
49    hash::MessageDigest::sha256()
50}
51
52/// Ensure we're enforcing safe minimums for TLS keys
53pub fn check_privkey_minimums(privkey: &PKeyRef<Private>) -> Result<(), String> {
54    if let Ok(key) = privkey.rsa() {
55        if key.size() < (RSA_MIN_KEY_SIZE_BITS / 8) as u32 {
56            Err(format!(
57                "TLS RSA key is less than {} bits!",
58                RSA_MIN_KEY_SIZE_BITS
59            ))
60        } else {
61            debug!(
62                "The RSA private key size is: {} bits, that's OK!",
63                key.size() * 8
64            );
65            Ok(())
66        }
67    } else if let Ok(key) = privkey.ec_key() {
68        // allowing this to panic because ... it's an i32 and hopefully we don't have negative bit lengths?
69        #[allow(clippy::panic)]
70        let key_bits: u64 = key.private_key().num_bits().try_into().unwrap_or_else(|_| {
71            panic!(
72                "Failed to convert EC bitlength {} to u64",
73                key.private_key().num_bits()
74            )
75        });
76
77        if key_bits < EC_MIN_KEY_SIZE_BITS {
78            Err(format!(
79                "TLS EC key is less than {} bits! Got: {}",
80                EC_MIN_KEY_SIZE_BITS, key_bits
81            ))
82        } else {
83            #[cfg(any(test, debug_assertions))]
84            println!("The EC private key size is: {} bits, that's OK!", key_bits);
85            debug!("The EC private key size is: {} bits, that's OK!", key_bits);
86            Ok(())
87        }
88    } else {
89        error!("TLS key is not RSA or EC, cannot check minimums!");
90        Ok(())
91    }
92}
93
94/// From the server configuration, generate an OpenSSL acceptor that we can use
95/// to build our sockets for HTTPS/LDAPS.
96pub fn setup_tls(
97    tls_config: &Option<TlsConfiguration>,
98) -> Result<Option<SslAcceptor>, std::io::Error> {
99    let Some(tls_param) = tls_config.as_ref() else {
100        return Ok(None);
101    };
102
103    let mut tls_builder = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls())?;
104
105    tls_builder
106        .set_certificate_chain_file(tls_param.chain.clone())
107        .map_err(|err| {
108            std::io::Error::new(
109                ErrorKind::Other,
110                format!("Failed to create TLS listener: {:?}", err),
111            )
112        })?;
113
114    tls_builder
115        .set_private_key_file(tls_param.key.clone(), SslFiletype::PEM)
116        .map_err(|err| {
117            std::io::Error::new(
118                ErrorKind::Other,
119                format!("Failed to create TLS listener: {:?}", err),
120            )
121        })?;
122
123    tls_builder.check_private_key().map_err(|err| {
124        std::io::Error::new(
125            ErrorKind::Other,
126            format!("Failed to create TLS listener: {:?}", err),
127        )
128    })?;
129
130    // If configured, setup TLS client authentication.
131    if let Some(client_ca) = tls_param.client_ca.as_ref() {
132        info!("Loading client certificates from {}", client_ca.display());
133
134        let verify = SslVerifyMode::PEER;
135        // In future we may add a "require mTLS option" which would necessitate this.
136        // verify.insert(SslVerifyMode::FAIL_IF_NO_PEER_CERT);
137        tls_builder.set_verify(verify);
138
139        // When client certs are available, we disable the TLS session cache.
140        // This is so that when the smartcard is *removed* on the client, it forces
141        // the client session to immediately expire.
142        //
143        // https://stackoverflow.com/questions/12393711/session-disconnect-the-client-after-smart-card-is-removed
144        //
145        // Alternately, on logout we need to trigger https://docs.rs/openssl/latest/openssl/ssl/struct.Ssl.html#method.set_ssl_context
146        // with https://docs.rs/openssl/latest/openssl/ssl/struct.Ssl.html#method.ssl_context +
147        // https://docs.rs/openssl/latest/openssl/ssl/struct.SslContextRef.html#method.remove_session
148        //
149        // Or we lower session time outs etc.
150        tls_builder.set_session_cache_mode(SslSessionCacheMode::OFF);
151
152        let read_dir = fs::read_dir(client_ca).map_err(|err| {
153            std::io::Error::new(
154                ErrorKind::Other,
155                format!(
156                    "Failed to create TLS listener while loading client ca from {}: {:?}",
157                    client_ca.display(),
158                    err
159                ),
160            )
161        })?;
162
163        for cert_dir_ent in read_dir.filter_map(|item| item.ok()).filter(|item| {
164            item.file_name()
165                .to_str()
166                // Hashed certs end in .0
167                // Hashed crls are .r0
168                .map(|fname| fname.ends_with(".0"))
169                .unwrap_or_default()
170        }) {
171            let mut cert_pem = String::new();
172            fs::File::open(cert_dir_ent.path())
173                .and_then(|mut file| file.read_to_string(&mut cert_pem))
174                .map_err(|err| {
175                    std::io::Error::new(
176                        ErrorKind::Other,
177                        format!("Failed to create TLS listener: {:?}", err),
178                    )
179                })?;
180
181            let cert = X509::from_pem(cert_pem.as_bytes()).map_err(|err| {
182                std::io::Error::new(
183                    ErrorKind::Other,
184                    format!("Failed to create TLS listener: {:?}", err),
185                )
186            })?;
187
188            let cert_store = tls_builder.cert_store_mut();
189            cert_store.add_cert(cert.clone()).map_err(|err| {
190                std::io::Error::new(
191                    ErrorKind::Other,
192                    format!(
193                        "Failed to load cert store while creating TLS listener: {:?}",
194                        err
195                    ),
196                )
197            })?;
198            // This tells the client what CA's they should use. It DOES NOT
199            // verify them. That's the job of the cert store above!
200            tls_builder.add_client_ca(&cert).map_err(|err| {
201                std::io::Error::new(
202                    ErrorKind::Other,
203                    format!("Failed to create TLS listener: {:?}", err),
204                )
205            })?;
206        }
207
208        // TODO: Build our own CRL map HERE!
209
210        // Allow dumping client cert chains for dev debugging
211        // In the case this is status=false, should we be dumping these anyway?
212        if enabled!(tracing::Level::TRACE) {
213            tls_builder.set_verify_callback(verify, |status, x509store| {
214                if let Some(current_cert) = x509store.current_cert() {
215                    let cert_text_bytes = current_cert.to_text().unwrap_or_default();
216                    let cert_text = String::from_utf8_lossy(cert_text_bytes.as_slice());
217                    tracing::warn!(client_cert = %cert_text);
218                };
219
220                if let Some(chain) = x509store.chain() {
221                    for cert in chain.iter() {
222                        let cert_text_bytes = cert.to_text().unwrap_or_default();
223                        let cert_text = String::from_utf8_lossy(cert_text_bytes.as_slice());
224                        tracing::warn!(chain_cert = %cert_text);
225                    }
226                }
227
228                status
229            });
230        }
231
232        // End tls_client setup
233    }
234
235    let tls_acceptor = tls_builder.build();
236
237    // let's enforce some TLS minimums!
238    let privkey = tls_acceptor.context().private_key().ok_or_else(|| {
239        std::io::Error::new(
240            ErrorKind::Other,
241            "Failed to access tls_acceptor private key".to_string(),
242        )
243    })?;
244
245    check_privkey_minimums(privkey).map_err(|err| {
246        std::io::Error::new(
247            ErrorKind::Other,
248            format!("Private key minimums were not met: {:?}", err),
249        )
250    })?;
251
252    Ok(Some(tls_acceptor))
253}
254
255fn get_ec_group() -> Result<EcGroup, ErrorStack> {
256    EcGroup::from_curve_name(Nid::X9_62_PRIME256V1)
257}
258
259#[derive(Debug)]
260pub(crate) struct CaHandle {
261    key: pkey::PKey<pkey::Private>,
262    cert: X509,
263}
264
265pub(crate) fn write_ca(
266    key_ar: impl AsRef<Path>,
267    cert_ar: impl AsRef<Path>,
268    handle: &CaHandle,
269) -> Result<(), ()> {
270    let key_path: &Path = key_ar.as_ref();
271    let cert_path: &Path = cert_ar.as_ref();
272
273    let key_pem = handle.key.private_key_to_pem_pkcs8().map_err(|e| {
274        error!(err = ?e, "Failed to convert key to PEM");
275    })?;
276
277    let cert_pem = handle.cert.to_pem().map_err(|e| {
278        error!(err = ?e, "Failed to convert cert to PEM");
279    })?;
280
281    fs::File::create(key_path)
282        .and_then(|mut file| file.write_all(&key_pem))
283        .map_err(|e| {
284            error!(err = ?e, "Failed to create {:?}", key_path);
285        })?;
286
287    fs::File::create(cert_path)
288        .and_then(|mut file| file.write_all(&cert_pem))
289        .map_err(|e| {
290            error!(err = ?e, "Failed to create {:?}", cert_path);
291        })
292}
293
294#[derive(Debug)]
295pub enum KeyType {
296    #[allow(dead_code)]
297    Rsa,
298    Ec,
299}
300impl Default for KeyType {
301    fn default() -> Self {
302        Self::Ec
303    }
304}
305
306#[derive(Debug)]
307pub struct CAConfig {
308    pub key_type: KeyType,
309    pub key_bits: u64,
310    pub skip_enforce_minimums: bool,
311}
312
313impl Default for CAConfig {
314    fn default() -> Self {
315        #[allow(clippy::expect_used)]
316        Self::new(KeyType::Ec, 256, false)
317            .expect("Somehow the defaults failed to pass validation while building a CA Config?")
318    }
319}
320
321impl CAConfig {
322    fn new(key_type: KeyType, key_bits: u64, skip_enforce_minimums: bool) -> Result<Self, String> {
323        let res = Self {
324            key_type,
325            key_bits,
326            skip_enforce_minimums,
327        };
328        if !skip_enforce_minimums {
329            res.enforce_minimums()?;
330        };
331        Ok(res)
332    }
333
334    /// Make sure we're meeting the minimum spec for key length etc
335    fn enforce_minimums(&self) -> Result<(), String> {
336        match self.key_type {
337            KeyType::Rsa => {
338                trace!(
339                    "Generating CA Config for RSA Key with {} bits",
340                    self.key_bits
341                );
342                if self.key_bits < RSA_MIN_KEY_SIZE_BITS {
343                    return Err(format!(
344                        "RSA key size must be at least {} bits",
345                        RSA_MIN_KEY_SIZE_BITS
346                    ));
347                }
348            }
349            KeyType::Ec => {
350                trace!("Generating CA Config for EcKey with {} bits", self.key_bits);
351                if self.key_bits < EC_MIN_KEY_SIZE_BITS {
352                    return Err(format!(
353                        "EC key size must be at least {} bits",
354                        EC_MIN_KEY_SIZE_BITS
355                    ));
356                }
357            }
358        };
359        Ok(())
360    }
361}
362
363pub(crate) fn gen_private_key(
364    key_type: &KeyType,
365    key_bits: Option<u64>,
366) -> Result<pkey::PKey<pkey::Private>, ErrorStack> {
367    match key_type {
368        KeyType::Rsa => {
369            let key_bits = key_bits.unwrap_or(RSA_MIN_KEY_SIZE_BITS);
370            let rsa = Rsa::generate(key_bits as u32)?;
371            pkey::PKey::from_rsa(rsa)
372        }
373        KeyType::Ec => {
374            // TODO: take key bitlength and use it for the curve group, somehow?
375            let ecgroup = get_ec_group()?;
376            let eckey = EcKey::generate(&ecgroup)?;
377            pkey::PKey::from_ec_key(eckey)
378        }
379    }
380}
381
382/// build up a CA certificate and key.
383pub(crate) fn build_ca(ca_config: Option<CAConfig>) -> Result<CaHandle, ErrorStack> {
384    let ca_config = ca_config.unwrap_or_default();
385
386    let ca_key = gen_private_key(&ca_config.key_type, Some(ca_config.key_bits))?;
387
388    if !ca_config.skip_enforce_minimums {
389        check_privkey_minimums(&ca_key).map_err(|err| {
390            admin_error!("failed to build_ca due to privkey minimums {}", err);
391            #[cfg(any(test, debug_assertions))]
392            println!("failed to build_ca due to privkey minimums: {}", err);
393            ErrorStack::get() // this probably should be a real errorstack but... how?
394        })?;
395    }
396    let mut x509_name = X509NameBuilder::new()?;
397
398    x509_name.append_entry_by_text("C", "AU")?;
399    x509_name.append_entry_by_text("ST", "QLD")?;
400    x509_name.append_entry_by_text("O", "Kanidm")?;
401    x509_name.append_entry_by_text("CN", "Kanidm Generated CA")?;
402    x509_name.append_entry_by_text("OU", "Development and Evaluation - NOT FOR PRODUCTION")?;
403    let x509_name = x509_name.build();
404
405    let mut cert_builder = X509::builder()?;
406    // Yes, 2 actually means 3 here ...
407    cert_builder.set_version(2)?;
408
409    let serial_number = bn::BigNum::from_u32(1).and_then(|serial| serial.to_asn1_integer())?;
410
411    cert_builder.set_serial_number(&serial_number)?;
412    cert_builder.set_subject_name(&x509_name)?;
413    cert_builder.set_issuer_name(&x509_name)?;
414
415    let not_before = asn1::Asn1Time::days_from_now(0)?;
416    cert_builder.set_not_before(&not_before)?;
417    let not_after = asn1::Asn1Time::days_from_now(CA_VALID_DAYS)?;
418    cert_builder.set_not_after(&not_after)?;
419
420    cert_builder.append_extension(BasicConstraints::new().critical().ca().pathlen(0).build()?)?;
421    cert_builder.append_extension(
422        KeyUsage::new()
423            .critical()
424            .key_cert_sign()
425            .crl_sign()
426            .build()?,
427    )?;
428
429    let subject_key_identifier =
430        SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(None, None))?;
431    cert_builder.append_extension(subject_key_identifier)?;
432
433    cert_builder.set_pubkey(&ca_key)?;
434
435    cert_builder.sign(&ca_key, get_signing_func())?;
436    let ca_cert = cert_builder.build();
437
438    Ok(CaHandle {
439        key: ca_key,
440        cert: ca_cert,
441    })
442}
443
444pub(crate) fn load_ca(
445    ca_key_ar: impl AsRef<Path>,
446    ca_cert_ar: impl AsRef<Path>,
447) -> Result<CaHandle, ()> {
448    let ca_key_path: &Path = ca_key_ar.as_ref();
449    let ca_cert_path: &Path = ca_cert_ar.as_ref();
450
451    let mut ca_key_pem = vec![];
452    fs::File::open(ca_key_path)
453        .and_then(|mut file| file.read_to_end(&mut ca_key_pem))
454        .map_err(|e| {
455            error!(err = ?e, "Failed to read {:?}", ca_key_path);
456        })?;
457
458    let mut ca_cert_pem = vec![];
459    fs::File::open(ca_cert_path)
460        .and_then(|mut file| file.read_to_end(&mut ca_cert_pem))
461        .map_err(|e| {
462            error!(err = ?e, "Failed to read {:?}", ca_cert_path);
463        })?;
464
465    let ca_key = pkey::PKey::private_key_from_pem(&ca_key_pem).map_err(|e| {
466        error!(err = ?e, "Failed to convert PEM to key");
467    })?;
468
469    check_privkey_minimums(&ca_key).map_err(|err| {
470        #[cfg(any(test, debug_assertions))]
471        println!("{:?}", err);
472        admin_error!("{}", err);
473    })?;
474
475    let ca_cert = X509::from_pem(&ca_cert_pem).map_err(|e| {
476        error!(err = ?e, "Failed to convert PEM to cert");
477    })?;
478
479    Ok(CaHandle {
480        key: ca_key,
481        cert: ca_cert,
482    })
483}
484
485pub(crate) struct CertHandle {
486    key: pkey::PKey<pkey::Private>,
487    cert: X509,
488    chain: Vec<X509>,
489}
490
491pub(crate) fn write_cert(
492    key_ar: impl AsRef<Path>,
493    chain_ar: impl AsRef<Path>,
494    cert_ar: impl AsRef<Path>,
495    handle: &CertHandle,
496) -> Result<(), ()> {
497    let key_path: &Path = key_ar.as_ref();
498    let chain_path: &Path = chain_ar.as_ref();
499    let cert_path: &Path = cert_ar.as_ref();
500
501    let key_pem = handle.key.private_key_to_pem_pkcs8().map_err(|e| {
502        error!(err = ?e, "Failed to convert key to PEM");
503    })?;
504
505    let cert_pem = handle.cert.to_pem().map_err(|e| {
506        error!(err = ?e, "Failed to convert cert to PEM");
507    })?;
508
509    let mut chain_pem = cert_pem.clone();
510
511    // Build the chain PEM.
512    for ca_cert in &handle.chain {
513        match ca_cert.to_pem() {
514            Ok(c) => {
515                chain_pem.extend_from_slice(&c);
516            }
517            Err(e) => {
518                error!(err = ?e, "Failed to convert cert to PEM");
519                return Err(());
520            }
521        }
522    }
523
524    fs::File::create(key_path)
525        .and_then(|mut file| file.write_all(&key_pem))
526        .map_err(|e| {
527            error!(err = ?e, "Failed to create {:?}", key_path);
528        })?;
529
530    fs::File::create(chain_path)
531        .and_then(|mut file| file.write_all(&chain_pem))
532        .map_err(|e| {
533            error!(err = ?e, "Failed to create {:?}", chain_path);
534        })?;
535
536    fs::File::create(cert_path)
537        .and_then(|mut file| file.write_all(&cert_pem))
538        .map_err(|e| {
539            error!(err = ?e, "Failed to create {:?}", cert_path);
540        })
541}
542
543pub(crate) fn build_cert(
544    domain_name: &str,
545    ca_handle: &CaHandle,
546    key_type: Option<KeyType>,
547    key_bits: Option<u64>,
548) -> Result<CertHandle, ErrorStack> {
549    let key_type = key_type.unwrap_or_default();
550    let int_key = gen_private_key(&key_type, key_bits)?;
551
552    let mut req_builder = X509ReqBuilder::new()?;
553    req_builder.set_pubkey(&int_key)?;
554
555    let mut x509_name = X509NameBuilder::new()?;
556    x509_name.append_entry_by_text("C", "AU")?;
557    x509_name.append_entry_by_text("ST", "QLD")?;
558    x509_name.append_entry_by_text("O", "Kanidm")?;
559    x509_name.append_entry_by_text("CN", domain_name)?;
560    // Requirement of packed attestation.
561    x509_name.append_entry_by_text("OU", "Development and Evaluation - NOT FOR PRODUCTION")?;
562    let x509_name = x509_name.build();
563
564    req_builder.set_subject_name(&x509_name)?;
565    req_builder.sign(&int_key, get_signing_func())?;
566    let req = req_builder.build();
567    // ==
568
569    let mut cert_builder = X509::builder()?;
570    // Yes, 2 actually means 3 here ...
571    cert_builder.set_version(2)?;
572    let serial_number = bn::BigNum::from_u32(2).and_then(|serial| serial.to_asn1_integer())?;
573
574    cert_builder.set_pubkey(&int_key)?;
575
576    cert_builder.set_serial_number(&serial_number)?;
577    cert_builder.set_subject_name(req.subject_name())?;
578    cert_builder.set_issuer_name(ca_handle.cert.subject_name())?;
579
580    let not_before = asn1::Asn1Time::days_from_now(0)?;
581    cert_builder.set_not_before(&not_before)?;
582    let not_after = asn1::Asn1Time::days_from_now(CERT_VALID_DAYS)?;
583    cert_builder.set_not_after(&not_after)?;
584
585    cert_builder.append_extension(BasicConstraints::new().build()?)?;
586
587    cert_builder.append_extension(
588        KeyUsage::new()
589            .critical()
590            .digital_signature()
591            .key_encipherment()
592            .build()?,
593    )?;
594
595    cert_builder.append_extension(
596        ExtendedKeyUsage::new()
597            // .critical()
598            .server_auth()
599            .build()?,
600    )?;
601
602    let subject_key_identifier = SubjectKeyIdentifier::new()
603        .build(&cert_builder.x509v3_context(Some(&ca_handle.cert), None))?;
604    cert_builder.append_extension(subject_key_identifier)?;
605
606    let auth_key_identifier = AuthorityKeyIdentifier::new()
607        .keyid(false)
608        .issuer(false)
609        .build(&cert_builder.x509v3_context(Some(&ca_handle.cert), None))?;
610    cert_builder.append_extension(auth_key_identifier)?;
611
612    let subject_alt_name = SubjectAlternativeName::new()
613        .dns(domain_name)
614        .build(&cert_builder.x509v3_context(Some(&ca_handle.cert), None))?;
615    cert_builder.append_extension(subject_alt_name)?;
616
617    cert_builder.sign(&ca_handle.key, get_signing_func())?;
618    let int_cert = cert_builder.build();
619
620    Ok(CertHandle {
621        key: int_key,
622        cert: int_cert,
623        chain: vec![ca_handle.cert.clone()],
624    })
625}
626
627#[test]
628// might as well test my logic
629fn test_enforced_minimums() {
630    let good_ca_configs = vec![
631        // test rsa 4096 (ok)
632        (KeyType::Rsa, 4096, false),
633        // test rsa 2048 (ok)
634        (KeyType::Rsa, 2048, false),
635        // test ec 256 (ok)
636        (KeyType::Ec, 256, false),
637    ];
638    good_ca_configs.into_iter().for_each(|config| {
639        dbg!(&config);
640        assert!(CAConfig::new(config.0, config.1, config.2).is_ok());
641    });
642    let bad_ca_configs = vec![
643        // test rsa 1024 (no)
644        (KeyType::Rsa, 1024, false),
645        // test ec 128 (no)
646        (KeyType::Ec, 128, false),
647    ];
648    bad_ca_configs.into_iter().for_each(|config| {
649        dbg!(&config);
650        assert!(CAConfig::new(config.0, config.1, config.2).is_err());
651    });
652}
653
654#[test]
655fn test_ca_loader() {
656    let ca_key_tempfile = tempfile::NamedTempFile::new().unwrap();
657    let ca_cert_tempfile = tempfile::NamedTempFile::new().unwrap();
658    // let's test the defaults first
659
660    let ca_config = CAConfig::default();
661    if let Ok(ca) = build_ca(Some(ca_config)) {
662        write_ca(ca_key_tempfile.path(), ca_cert_tempfile.path(), &ca).unwrap();
663        assert!(load_ca(ca_key_tempfile.path(), ca_cert_tempfile.path()).is_ok());
664    };
665
666    let good_ca_configs = vec![
667        // test rsa 4096 (ok)
668        (KeyType::Rsa, 4096, false),
669        // test rsa 2048 (ok)
670        (KeyType::Rsa, 2048, false),
671        // test ec 256 (ok)
672        (KeyType::Ec, 256, false),
673    ];
674    good_ca_configs.into_iter().for_each(|config| {
675        println!("testing good config {:?}", config);
676        let ca_config = CAConfig::new(config.0, config.1, config.2).unwrap();
677        let ca = build_ca(Some(ca_config)).unwrap();
678        write_ca(ca_key_tempfile.path(), ca_cert_tempfile.path(), &ca).unwrap();
679        let ca_result = load_ca(ca_key_tempfile.path(), ca_cert_tempfile.path());
680        println!("result: {:?}", ca_result);
681        assert!(ca_result.is_ok());
682    });
683    let bad_ca_configs = vec![
684        // test rsa 1024 (bad)
685        (KeyType::Rsa, 1024, true),
686    ];
687    bad_ca_configs.into_iter().for_each(|config| {
688        println!(
689            "\ntesting bad config keytype: {:?} key size: {}, skip_enforce_minimums: {}",
690            config.0, config.1, config.2
691        );
692        let ca_config = CAConfig::new(config.0, config.1, config.2).unwrap();
693        let ca = build_ca(Some(ca_config)).unwrap();
694        write_ca(ca_key_tempfile.path(), ca_cert_tempfile.path(), &ca).unwrap();
695        let ca_result = load_ca(ca_key_tempfile.path(), ca_cert_tempfile.path());
696        println!("result: {:?}", ca_result);
697        assert!(ca_result.is_err());
698    });
699}