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::rsa::Rsa;
8use openssl::x509::{
9    extension::{
10        AuthorityKeyIdentifier, BasicConstraints, ExtendedKeyUsage, KeyUsage,
11        SubjectAlternativeName, SubjectKeyIdentifier,
12    },
13    X509NameBuilder, X509ReqBuilder, X509,
14};
15use openssl::{asn1, bn, hash, pkey};
16
17// use sketching::*;
18use crate::config::TlsConfiguration;
19use crypto_glue::{
20    ec::EcPrivateKey,
21    pkcs8::PrivateKeyInfo,
22    rsa::RS256PrivateKey,
23    traits::{DecodeDer, Pkcs1DecodeRsaPrivateKey, PublicKeyParts},
24    x509::oiddb::rfc5912,
25};
26use rustls::{
27    pki_types::{pem::PemObject, CertificateDer, CertificateRevocationListDer, PrivateKeyDer},
28    server::{ServerConfig, WebPkiClientVerifier},
29    RootCertStore,
30};
31use std::fs;
32use std::io::{Read, Write};
33use std::path::Path;
34use std::sync::Arc;
35use tokio_rustls::TlsAcceptor;
36
37const CA_VALID_DAYS: u32 = 30;
38const CERT_VALID_DAYS: u32 = 5;
39
40// Basing minimums off https://www.keylength.com setting "year" to 2030 - tested as at 2023-09-25
41//
42// |Method           |Date     |Symmetric| FM      |DL Key| DL Group|Elliptic Curve|Hash|
43// |   ---           |   ---   |   ---   |   ---   | ---  |   ---   |  ---         | ---|
44// |Lenstra / Verheul|2030     |  93     |2493^2016|165   | 2493    |  176         | 186|
45// |Lenstra Updated  |2030     |  88     |1698^2063|176   | 1698    |  176         | 176|
46// |ECRYPT           |2029-2068|  256    |15360    |512   | 15360   |  512         | 512|
47// |NIST             |2019-2030|  112    |2048     |224   | 2048    |  224         | 224|
48// |ANSSI            |> 2030   |  128    |3072     |200   | 3072    |  256         | 256|
49// |NSA              |-        |  256    |3072     |-     | -       |  384         | 384|
50// |RFC3766          |-        |  -      |   -     | -    |   -     |   -          |  - |
51// |BSI              |-        |  -      |   -     | -    |   -     |   -          |  - |
52// DL - Discrete Logarithm
53// FM - Factoring Modulus
54
55const RSA_MIN_KEY_SIZE_BITS: u64 = 2048;
56const EC_MIN_KEY_SIZE_BITS: u64 = 224;
57
58/// returns a signing function that meets a sensible minimum
59fn get_signing_func() -> hash::MessageDigest {
60    hash::MessageDigest::sha256()
61}
62
63/// Ensure we're enforcing safe minimums for TLS keys
64pub fn check_privkey_minimums(privkey: &PrivateKeyDer<'_>) -> Result<(), String> {
65    match privkey {
66        PrivateKeyDer::Pkcs8(pkcs8_der) => {
67            let private_key_info = PrivateKeyInfo::try_from(pkcs8_der.secret_pkcs8_der())
68                .map_err(|_err| "Invalid pkcs8 der".to_string())?;
69
70            let oids = private_key_info
71                .algorithm
72                .oids()
73                .map_err(|_err| "Invalid pkcs8 key oids".to_string())?;
74
75            match oids {
76                (rfc5912::ID_EC_PUBLIC_KEY, Some(rfc5912::SECP_256_R_1)) => {
77                    debug!("The EC private key size is: 256 bits, that's OK!");
78                    Ok(())
79                }
80                (rfc5912::ID_EC_PUBLIC_KEY, Some(rfc5912::SECP_384_R_1)) => {
81                    debug!("The EC private key size is: 384 bits, that's OK!");
82                    Ok(())
83                }
84                (rfc5912::RSA_ENCRYPTION, None) => {
85                    let priv_key = RS256PrivateKey::try_from(private_key_info)
86                        .map_err(|_err| "Invalid rsa key".to_string())?;
87
88                    let priv_key_bits = priv_key.size() * 8;
89
90                    if priv_key_bits < RSA_MIN_KEY_SIZE_BITS as usize {
91                        Err(format!(
92                            "TLS RSA key is less than {RSA_MIN_KEY_SIZE_BITS} bits!"
93                        ))
94                    } else {
95                        debug!(
96                            "The RSA private key size is: {} bits, that's OK!",
97                            priv_key_bits
98                        );
99                        Ok(())
100                    }
101                }
102                _ => Err("TLS Private Key Oids not understood".into()),
103            }
104        }
105        PrivateKeyDer::Sec1(sec1_der) => {
106            // ECDSA only
107            let priv_key = EcPrivateKey::from_der(sec1_der.secret_sec1_der())
108                .map_err(|_err| "Invalid sec1 der".to_string())?;
109
110            match priv_key.parameters.and_then(|params| params.named_curve()) {
111                Some(rfc5912::SECP_256_R_1) => {
112                    debug!("The EC private key size is: 256 bits, that's OK!");
113                    Ok(())
114                }
115                Some(rfc5912::SECP_384_R_1) => {
116                    debug!("The EC private key size is: 384 bits, that's OK!");
117                    Ok(())
118                }
119                Some(_) => Err("TLS Private Key Oids not understood".into()),
120                None => Err("TLS Private Key has no oids".into()),
121            }
122        }
123        PrivateKeyDer::Pkcs1(pkcs1_der) => {
124            // RSA only
125            let priv_key = RS256PrivateKey::from_pkcs1_der(pkcs1_der.secret_pkcs1_der())
126                .map_err(|_err| "Invalid pkcs1 der".to_string())?;
127
128            let priv_key_bits = priv_key.size() * 8;
129
130            if priv_key_bits < RSA_MIN_KEY_SIZE_BITS as usize {
131                Err(format!(
132                    "TLS RSA key is less than {RSA_MIN_KEY_SIZE_BITS} bits!"
133                ))
134            } else {
135                debug!(
136                    "The RSA private key size is: {} bits, that's OK!",
137                    priv_key_bits
138                );
139                Ok(())
140            }
141        }
142        _ => Err("TLS Private Key Format not understood".into()),
143    }
144}
145
146/// From the server configuration, generate an OpenSSL acceptor that we can use
147/// to build our sockets for HTTPS/LDAPS.
148pub fn setup_tls(
149    tls_config: &Option<TlsConfiguration>,
150) -> Result<Option<TlsAcceptor>, std::io::Error> {
151    let Some(tls_param) = tls_config.as_ref() else {
152        return Ok(None);
153    };
154
155    let cert_iter = CertificateDer::pem_file_iter(&tls_param.chain).map_err(|err| {
156        std::io::Error::other(format!(
157            "Failed to create TLS listener. The Certificate Chain could not be parsed: {err:?}"
158        ))
159    })?;
160
161    let cert_chain_der = cert_iter
162        .collect::<Result<Vec<_>, _>>()
163        .map_err(|err| std::io::Error::other(format!("Failed to create TLS listener. The Certificate Chain could not be transformed from PEM to DER: {err:?}")))?;
164
165    let private_key_der = PrivateKeyDer::from_pem_file(&tls_param.key).map_err(|err| {
166        std::io::Error::other(format!(
167            "Failed to create TLS listener. The Private Key could not be parsed {err:?}"
168        ))
169    })?;
170
171    check_privkey_minimums(&private_key_der).map_err(|err| {
172        std::io::Error::other(format!(
173            "Private Key does not meet cryptographic minimum standards: {err:?}"
174        ))
175    })?;
176
177    // Configure the rustls cryptoprovider. We currently use aws-lc-rc as the
178    // default. We may swap to rustcrypto in future.
179    let provider: Arc<_> = rustls::crypto::aws_lc_rs::default_provider().into();
180
181    let client_cert_verifier = if let Some(client_ca) = tls_param.client_ca.as_ref() {
182        info!(
183            "Loading client CA certificates from {}",
184            client_ca.display()
185        );
186
187        let read_dir = fs::read_dir(client_ca).map_err(|err| {
188            std::io::Error::other(format!(
189                "Failed to create TLS listener. An error occured while loading a client ca from {}: {:?}",
190                client_ca.display(),
191                err
192            ))
193        })?;
194
195        let dirents: Vec<_> = read_dir.filter_map(|item| item.ok()).collect();
196
197        let mut client_cert_roots = RootCertStore::empty();
198
199        for cert_dir_ent in dirents.iter().filter(|item| {
200            item.file_name()
201                .to_str()
202                // Hashed certs end in .0
203                // Hashed crls are .r0
204                .map(|fname| fname.ends_with(".0"))
205                .unwrap_or_default()
206        }) {
207            let cert_pem = CertificateDer::from_pem_file(cert_dir_ent.path()).map_err(|err| {
208                std::io::Error::other(format!("Failed to create TLS listener. A Client CA Certificate could not be parsed: {} {err:?}", cert_dir_ent.path().display()))
209            })?;
210
211            client_cert_roots.add(cert_pem).map_err(|err| {
212                std::io::Error::other(format!("Failed to create TLS listener. A Client CA Certificate failed to be added to the trusted store: {} {err:?}", cert_dir_ent.path().display()))
213            })?;
214        }
215
216        let mut client_cert_crls = Vec::new();
217
218        for cert_dir_ent in dirents.iter().filter(|item| {
219            item.file_name()
220                .to_str()
221                // Hashed certs end in .0
222                // Hashed crls are .r0
223                .map(|fname| fname.ends_with(".r0"))
224                .unwrap_or_default()
225        }) {
226            let cert_pem = CertificateRevocationListDer::from_pem_file(cert_dir_ent.path())
227                .map_err(|err| {
228                    std::io::Error::other(format!("Failed to create TLS listener. A Certificate Revocation List could not be parsed: {} {err:?}", cert_dir_ent.path().display()))
229                })?;
230
231            client_cert_crls.push(cert_pem);
232        }
233
234        WebPkiClientVerifier::builder_with_provider(client_cert_roots.into(), provider.clone())
235            .with_crls(client_cert_crls)
236            .allow_unauthenticated()
237            .build()
238            .map_err(|err| {
239                std::io::Error::other(format!("Failed to create TLS listener. The Client Certificate Verifier could not be built: {err:?}"))
240            })?
241    } else {
242        WebPkiClientVerifier::no_client_auth()
243    };
244
245    let tls_server_config = ServerConfig::builder_with_provider(provider)
246        .with_safe_default_protocol_versions()
247        .and_then(|builder| {
248            builder
249                .with_client_cert_verifier(client_cert_verifier)
250                .with_single_cert(cert_chain_der, private_key_der)
251        })
252        .map_err(|err| std::io::Error::other(format!("Failed to create TLS listener. The TLS Server Configuration could not be built: {err:?}")))?;
253
254    let tls_acceptor = TlsAcceptor::from(Arc::new(tls_server_config));
255
256    Ok(Some(tls_acceptor))
257}
258
259fn get_ec_group() -> Result<EcGroup, ErrorStack> {
260    EcGroup::from_curve_name(Nid::X9_62_PRIME256V1)
261}
262
263#[derive(Debug)]
264pub(crate) struct CaHandle {
265    key: pkey::PKey<pkey::Private>,
266    cert: X509,
267}
268
269pub(crate) fn write_ca(
270    key_ar: impl AsRef<Path>,
271    cert_ar: impl AsRef<Path>,
272    handle: &CaHandle,
273) -> Result<(), ()> {
274    let key_path: &Path = key_ar.as_ref();
275    let cert_path: &Path = cert_ar.as_ref();
276
277    let key_pem = handle.key.private_key_to_pem_pkcs8().map_err(|e| {
278        error!(err = ?e, "Failed to convert key to PEM");
279    })?;
280
281    let cert_pem = handle.cert.to_pem().map_err(|e| {
282        error!(err = ?e, "Failed to convert cert to PEM");
283    })?;
284
285    fs::File::create(key_path)
286        .and_then(|mut file| file.write_all(&key_pem))
287        .map_err(|e| {
288            error!(err = ?e, "Failed to create {:?}", key_path);
289        })?;
290
291    fs::File::create(cert_path)
292        .and_then(|mut file| file.write_all(&cert_pem))
293        .map_err(|e| {
294            error!(err = ?e, "Failed to create {:?}", cert_path);
295        })
296}
297
298#[derive(Debug)]
299pub enum KeyType {
300    #[allow(dead_code)]
301    Rsa,
302    Ec,
303}
304impl Default for KeyType {
305    fn default() -> Self {
306        Self::Ec
307    }
308}
309
310#[derive(Debug)]
311pub struct CAConfig {
312    pub key_type: KeyType,
313    pub key_bits: u64,
314    #[allow(dead_code)]
315    pub skip_enforce_minimums: bool,
316}
317
318impl Default for CAConfig {
319    fn default() -> Self {
320        #[allow(clippy::expect_used)]
321        Self::new(KeyType::Ec, 256, false)
322            .expect("Somehow the defaults failed to pass validation while building a CA Config?")
323    }
324}
325
326impl CAConfig {
327    fn new(key_type: KeyType, key_bits: u64, skip_enforce_minimums: bool) -> Result<Self, String> {
328        let res = Self {
329            key_type,
330            key_bits,
331            skip_enforce_minimums,
332        };
333        if !skip_enforce_minimums {
334            res.enforce_minimums()?;
335        };
336        Ok(res)
337    }
338
339    /// Make sure we're meeting the minimum spec for key length etc
340    fn enforce_minimums(&self) -> Result<(), String> {
341        match self.key_type {
342            KeyType::Rsa => {
343                trace!(
344                    "Generating CA Config for RSA Key with {} bits",
345                    self.key_bits
346                );
347                if self.key_bits < RSA_MIN_KEY_SIZE_BITS {
348                    return Err(format!(
349                        "RSA key size must be at least {RSA_MIN_KEY_SIZE_BITS} bits"
350                    ));
351                }
352            }
353            KeyType::Ec => {
354                trace!("Generating CA Config for EcKey with {} bits", self.key_bits);
355                if self.key_bits < EC_MIN_KEY_SIZE_BITS {
356                    return Err(format!(
357                        "EC key size must be at least {EC_MIN_KEY_SIZE_BITS} bits"
358                    ));
359                }
360            }
361        };
362        Ok(())
363    }
364}
365
366pub(crate) fn gen_private_key(
367    key_type: &KeyType,
368    key_bits: Option<u64>,
369) -> Result<pkey::PKey<pkey::Private>, ErrorStack> {
370    match key_type {
371        KeyType::Rsa => {
372            let key_bits = key_bits.unwrap_or(RSA_MIN_KEY_SIZE_BITS);
373            let rsa = Rsa::generate(key_bits as u32)?;
374            pkey::PKey::from_rsa(rsa)
375        }
376        KeyType::Ec => {
377            // TODO: take key bitlength and use it for the curve group, somehow?
378            let ecgroup = get_ec_group()?;
379            let eckey = EcKey::generate(&ecgroup)?;
380            pkey::PKey::from_ec_key(eckey)
381        }
382    }
383}
384
385/// build up a CA certificate and key.
386pub(crate) fn build_ca(ca_config: Option<CAConfig>) -> Result<CaHandle, ErrorStack> {
387    // We never actually call ca_config from any external input, it's fully internal
388    // and controlled by us. Should we remove ca_config instead?
389    let ca_config = ca_config.unwrap_or_default();
390
391    // We don't need to check minimums here because we are the ones generating the key.
392    let ca_key = gen_private_key(&ca_config.key_type, Some(ca_config.key_bits))?;
393
394    let mut x509_name = X509NameBuilder::new()?;
395
396    x509_name.append_entry_by_text("C", "AU")?;
397    x509_name.append_entry_by_text("ST", "QLD")?;
398    x509_name.append_entry_by_text("O", "Kanidm")?;
399    x509_name.append_entry_by_text("CN", "Kanidm Generated CA")?;
400    x509_name.append_entry_by_text("OU", "Development and Evaluation - NOT FOR PRODUCTION")?;
401    let x509_name = x509_name.build();
402
403    let mut cert_builder = X509::builder()?;
404    // Yes, 2 actually means 3 here ...
405    cert_builder.set_version(2)?;
406
407    let serial_number = bn::BigNum::from_u32(1).and_then(|serial| serial.to_asn1_integer())?;
408
409    cert_builder.set_serial_number(&serial_number)?;
410    cert_builder.set_subject_name(&x509_name)?;
411    cert_builder.set_issuer_name(&x509_name)?;
412
413    let not_before = asn1::Asn1Time::days_from_now(0)?;
414    cert_builder.set_not_before(&not_before)?;
415    let not_after = asn1::Asn1Time::days_from_now(CA_VALID_DAYS)?;
416    cert_builder.set_not_after(&not_after)?;
417
418    cert_builder.append_extension(BasicConstraints::new().critical().ca().pathlen(0).build()?)?;
419    cert_builder.append_extension(
420        KeyUsage::new()
421            .critical()
422            .key_cert_sign()
423            .crl_sign()
424            .build()?,
425    )?;
426
427    let subject_key_identifier =
428        SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(None, None))?;
429    cert_builder.append_extension(subject_key_identifier)?;
430
431    cert_builder.set_pubkey(&ca_key)?;
432
433    cert_builder.sign(&ca_key, get_signing_func())?;
434    let ca_cert = cert_builder.build();
435
436    Ok(CaHandle {
437        key: ca_key,
438        cert: ca_cert,
439    })
440}
441
442pub(crate) fn load_ca(
443    ca_key_ar: impl AsRef<Path>,
444    ca_cert_ar: impl AsRef<Path>,
445) -> Result<CaHandle, ()> {
446    let ca_key_path: &Path = ca_key_ar.as_ref();
447    let ca_cert_path: &Path = ca_cert_ar.as_ref();
448
449    let mut ca_key_pem = vec![];
450    fs::File::open(ca_key_path)
451        .and_then(|mut file| file.read_to_end(&mut ca_key_pem))
452        .map_err(|e| {
453            error!(err = ?e, "Failed to read {:?}", ca_key_path);
454        })?;
455
456    let mut ca_cert_pem = vec![];
457    fs::File::open(ca_cert_path)
458        .and_then(|mut file| file.read_to_end(&mut ca_cert_pem))
459        .map_err(|e| {
460            error!(err = ?e, "Failed to read {:?}", ca_cert_path);
461        })?;
462
463    let ca_key = pkey::PKey::private_key_from_pem(&ca_key_pem).map_err(|e| {
464        error!(err = ?e, "Failed to convert PEM to key");
465    })?;
466
467    /*
468    check_privkey_minimums(&ca_key).map_err(|err| {
469        #[cfg(any(test, debug_assertions))]
470        println!("{:?}", err);
471        admin_error!("{}", err);
472    })?;
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    /*
643    let bad_ca_configs = vec![
644        // test rsa 1024 (no)
645        (KeyType::Rsa, 1024, false),
646        // test ec 128 (no)
647        (KeyType::Ec, 128, false),
648    ];
649    bad_ca_configs.into_iter().for_each(|config| {
650        dbg!(&config);
651        assert!(CAConfig::new(config.0, config.1, config.2).is_err());
652    });
653    */
654}
655
656#[test]
657fn test_ca_loader() {
658    let ca_key_tempfile = tempfile::NamedTempFile::new().unwrap();
659    let ca_cert_tempfile = tempfile::NamedTempFile::new().unwrap();
660    // let's test the defaults first
661
662    let ca_config = CAConfig::default();
663    if let Ok(ca) = build_ca(Some(ca_config)) {
664        write_ca(ca_key_tempfile.path(), ca_cert_tempfile.path(), &ca).unwrap();
665        assert!(load_ca(ca_key_tempfile.path(), ca_cert_tempfile.path()).is_ok());
666    };
667
668    let good_ca_configs = vec![
669        // test rsa 4096 (ok)
670        (KeyType::Rsa, 4096, false),
671        // test rsa 2048 (ok)
672        (KeyType::Rsa, 2048, false),
673        // test ec 256 (ok)
674        (KeyType::Ec, 256, false),
675    ];
676    good_ca_configs.into_iter().for_each(|config| {
677        println!("testing good config {config:?}");
678        let ca_config = CAConfig::new(config.0, config.1, config.2).unwrap();
679        let ca = build_ca(Some(ca_config)).unwrap();
680        write_ca(ca_key_tempfile.path(), ca_cert_tempfile.path(), &ca).unwrap();
681        let ca_result = load_ca(ca_key_tempfile.path(), ca_cert_tempfile.path());
682        println!("result: {ca_result:?}");
683        assert!(ca_result.is_ok());
684    });
685
686    /*
687    let bad_ca_configs = vec![
688        // test rsa 1024 (bad)
689        (KeyType::Rsa, 1024, true),
690    ];
691    bad_ca_configs.into_iter().for_each(|config| {
692        println!(
693            "\ntesting bad config keytype: {:?} key size: {}, skip_enforce_minimums: {}",
694            config.0, config.1, config.2
695        );
696        let ca_config = CAConfig::new(config.0, config.1, config.2).unwrap();
697        let ca = build_ca(Some(ca_config)).unwrap();
698        write_ca(ca_key_tempfile.path(), ca_cert_tempfile.path(), &ca).unwrap();
699        let ca_result = load_ca(ca_key_tempfile.path(), ca_cert_tempfile.path());
700        println!("result: {:?}", ca_result);
701        assert!(ca_result.is_err());
702    });
703    */
704}