| { | ||
| "git": { | ||
| "sha1": "620e2195d8574bb4d4fd753a3c82fd5eca739b39" | ||
| "sha1": "9f7fbb653e505cf0f4a84d8eb25fc3827216a30f" | ||
| }, | ||
| "path_in_vcs": "rcgen" | ||
| } |
+7
-44
@@ -75,5 +75,5 @@ # This file is automatically @generated by Cargo. | ||
| name = "aws-lc-rs" | ||
| version = "1.13.1" | ||
| version = "1.13.3" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "93fcc8f365936c834db5514fc45aee5b1202d677e6b40e48468aaaa8183ca8c7" | ||
| checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba" | ||
| dependencies = [ | ||
@@ -87,5 +87,5 @@ "aws-lc-fips-sys", | ||
| name = "aws-lc-sys" | ||
| version = "0.29.0" | ||
| version = "0.30.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "61b1d86e7705efe1be1b569bab41d4fa1e14e220b60a160f78de2db687add079" | ||
| checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff" | ||
| dependencies = [ | ||
@@ -135,26 +135,2 @@ "bindgen", | ||
| [[package]] | ||
| name = "botan" | ||
| version = "0.11.1" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "24d4c7647d67c53194fa0740404c6c508880aef2bfe99a9868dbb4b86f090377" | ||
| dependencies = [ | ||
| "botan-sys", | ||
| ] | ||
| [[package]] | ||
| name = "botan-src" | ||
| version = "0.30701.2" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "8c4e1c7910f3b4712aed10e4259eca77e79ed84c1b023098c8eac596b993fc44" | ||
| [[package]] | ||
| name = "botan-sys" | ||
| version = "0.11.1" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "04285fa0c094cc9961fe435b1b279183db9394844ad82ce483aa6196c0e6da38" | ||
| dependencies = [ | ||
| "botan-src", | ||
| ] | ||
| [[package]] | ||
| name = "cc" | ||
@@ -558,6 +534,5 @@ version = "1.2.27" | ||
| name = "rcgen" | ||
| version = "0.14.3" | ||
| version = "0.14.4" | ||
| dependencies = [ | ||
| "aws-lc-rs", | ||
| "botan", | ||
| "openssl", | ||
@@ -567,3 +542,2 @@ "pem", | ||
| "rustls-pki-types", | ||
| "rustls-webpki", | ||
| "time", | ||
@@ -656,13 +630,2 @@ "x509-parser", | ||
| [[package]] | ||
| name = "rustls-webpki" | ||
| version = "0.103.3" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" | ||
| dependencies = [ | ||
| "ring", | ||
| "rustls-pki-types", | ||
| "untrusted", | ||
| ] | ||
| [[package]] | ||
| name = "serde" | ||
@@ -977,5 +940,5 @@ version = "1.0.219" | ||
| name = "x509-parser" | ||
| version = "0.17.0" | ||
| version = "0.18.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "4569f339c0c402346d4a75a9e39cf8dad310e287eef1ff56d4c68e5067f53460" | ||
| checksum = "eb3e137310115a65136898d2079f003ce33331a6c4b0d51f1531d1be082b6425" | ||
| dependencies = [ | ||
@@ -982,0 +945,0 @@ "asn1-rs", |
+7
-45
@@ -16,3 +16,3 @@ # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO | ||
| name = "rcgen" | ||
| version = "0.14.3" | ||
| version = "0.14.4" | ||
| build = false | ||
@@ -51,2 +51,6 @@ autolib = false | ||
| ] | ||
| aws_lc_rs_unstable = [ | ||
| "aws_lc_rs", | ||
| "aws-lc-rs/unstable", | ||
| ] | ||
| crypto = [] | ||
@@ -93,24 +97,4 @@ default = [ | ||
| [[test]] | ||
| name = "botan" | ||
| path = "tests/botan.rs" | ||
| [[test]] | ||
| name = "generic" | ||
| path = "tests/generic.rs" | ||
| [[test]] | ||
| name = "openssl" | ||
| path = "tests/openssl.rs" | ||
| [[test]] | ||
| name = "util" | ||
| path = "tests/util.rs" | ||
| [[test]] | ||
| name = "webpki" | ||
| path = "tests/webpki.rs" | ||
| [dependencies.aws-lc-rs] | ||
| version = "1.6.0" | ||
| version = "1.13.3" | ||
| optional = true | ||
@@ -136,3 +120,3 @@ default-features = false | ||
| [dependencies.x509-parser] | ||
| version = "0.17" | ||
| version = "0.18" | ||
| features = ["verify"] | ||
@@ -152,25 +136,3 @@ optional = true | ||
| [dev-dependencies.botan] | ||
| version = "0.11" | ||
| features = ["vendored"] | ||
| [dev-dependencies.pki-types] | ||
| version = "1" | ||
| package = "rustls-pki-types" | ||
| [dev-dependencies.ring] | ||
| version = "0.17" | ||
| [dev-dependencies.rustls-webpki] | ||
| version = "0.103" | ||
| features = [ | ||
| "ring", | ||
| "std", | ||
| ] | ||
| [dev-dependencies.x509-parser] | ||
| version = "0.17" | ||
| features = ["verify"] | ||
| [target."cfg(unix)".dev-dependencies.openssl] | ||
| version = "0.10" |
+31
-8
@@ -30,2 +30,4 @@ #[cfg(feature = "crypto")] | ||
| use crate::{sign_algo::SignatureAlgorithm, Error}; | ||
| #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] | ||
| use aws_lc_rs::unstable::signature::PqdsaKeyPair; | ||
@@ -40,2 +42,5 @@ /// A key pair variant | ||
| Ed(Ed25519KeyPair), | ||
| /// A Pqdsa key pair | ||
| #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] | ||
| Pq(PqdsaKeyPair), | ||
| /// A RSA key pair | ||
@@ -51,2 +56,4 @@ Rsa(RsaKeyPair, &'static dyn RsaEncoding), | ||
| Self::Ed(key_pair) => write!(f, "{key_pair:?}"), | ||
| #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] | ||
| Self::Pq(key_pair) => write!(f, "{key_pair:?}"), | ||
| Self::Rsa(key_pair, _) => write!(f, "{key_pair:?}"), | ||
@@ -58,8 +65,2 @@ } | ||
| /// A key pair used to sign certificates and CSRs | ||
| /// | ||
| /// Note that ring, the underlying library to handle RSA keys | ||
| /// requires them to be in a special format, meaning that | ||
| /// `openssl genrsa` doesn't work. See ring's [documentation](ring::signature::RsaKeyPair::from_pkcs8) | ||
| /// for how to generate RSA keys in the wanted format | ||
| /// and conversion between the formats. | ||
| #[cfg(feature = "crypto")] | ||
@@ -124,2 +125,13 @@ pub struct KeyPair { | ||
| }, | ||
| #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] | ||
| SignAlgo::PqDsa(sign_alg) => { | ||
| let key_pair = PqdsaKeyPair::generate(sign_alg)._err()?; | ||
| let key_pair_serialized = key_pair.to_pkcs8()._err()?.as_ref().to_vec(); | ||
| Ok(KeyPair { | ||
| kind: KeyPairKind::Pq(key_pair), | ||
| alg, | ||
| serialized_der: key_pair_serialized, | ||
| }) | ||
| }, | ||
| #[cfg(feature = "aws_lc_rs")] | ||
@@ -386,5 +398,8 @@ SignAlgo::Rsa(sign_alg) => Self::generate_rsa_inner(alg, sign_alg, KeySize::Rsa2048), | ||
| /// | ||
| /// The key is in raw format, as how [`ring::signature::KeyPair::public_key`] | ||
| /// would output, and how [`ring::signature::UnparsedPublicKey::verify`] | ||
| /// The key is in raw format, as how [`KeyPair::public_key()`][public_key] | ||
| /// would output, and how [`UnparsedPublicKey::verify()`][verify] | ||
| /// would accept. | ||
| /// | ||
| /// [public_key]: crate::ring_like::signature::KeyPair::public_key() | ||
| /// [verify]: crate::ring_like::signature::UnparsedPublicKey::verify() | ||
| pub fn public_key_raw(&self) -> &[u8] { | ||
@@ -445,2 +460,8 @@ self.der_bytes() | ||
| KeyPairKind::Ed(kp) => kp.sign(msg).as_ref().to_owned(), | ||
| #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] | ||
| KeyPairKind::Pq(kp) => { | ||
| let mut signature = vec![0; kp.algorithm().signature_len()]; | ||
| kp.sign(msg, &mut signature)._err()?; | ||
| signature | ||
| }, | ||
| KeyPairKind::Rsa(kp, padding_alg) => { | ||
@@ -463,2 +484,4 @@ let system_random = SystemRandom::new(); | ||
| KeyPairKind::Ed(kp) => kp.public_key().as_ref(), | ||
| #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] | ||
| KeyPairKind::Pq(kp) => kp.public_key().as_ref(), | ||
| KeyPairKind::Rsa(kp, _) => kp.public_key().as_ref(), | ||
@@ -465,0 +488,0 @@ } |
+7
-0
@@ -28,2 +28,9 @@ /// pkcs-9-at-extensionRequest in [RFC 2985](https://www.rfc-editor.org/rfc/rfc2985#appendix-A) | ||
| #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] | ||
| pub(crate) const ML_DSA_44: &[u64] = &[2, 16, 840, 1, 101, 3, 4, 3, 17]; | ||
| #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] | ||
| pub(crate) const ML_DSA_65: &[u64] = &[2, 16, 840, 1, 101, 3, 4, 3, 18]; | ||
| #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] | ||
| pub(crate) const ML_DSA_87: &[u64] = &[2, 16, 840, 1, 101, 3, 4, 3, 19]; | ||
| /// rsaEncryption in [RFC 4055](https://www.rfc-editor.org/rfc/rfc4055#section-6) | ||
@@ -30,0 +37,0 @@ pub(crate) const RSA_ENCRYPTION: &[u64] = &[1, 2, 840, 113549, 1, 1, 1]; |
+36
-0
@@ -11,2 +11,6 @@ use std::fmt; | ||
| use crate::Error; | ||
| #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] | ||
| use aws_lc_rs::unstable::signature::{ | ||
| PqdsaSigningAlgorithm, ML_DSA_44_SIGNING, ML_DSA_65_SIGNING, ML_DSA_87_SIGNING, | ||
| }; | ||
@@ -18,2 +22,4 @@ #[cfg(feature = "crypto")] | ||
| EdDsa(&'static EdDSAParameters), | ||
| #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] | ||
| PqDsa(&'static PqdsaSigningAlgorithm), | ||
| Rsa(&'static dyn RsaEncoding), | ||
@@ -214,2 +220,32 @@ } | ||
| }; | ||
| /// ML-DSA-44 signing as per <https://www.ietf.org/archive/id/draft-ietf-lamps-dilithium-certificates-12.html#name-identifiers>. | ||
| #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] | ||
| pub static PKCS_ML_DSA_44: SignatureAlgorithm = SignatureAlgorithm { | ||
| oids_sign_alg: &[ML_DSA_44], | ||
| #[cfg(all(feature = "crypto", feature = "aws_lc_rs_unstable"))] | ||
| sign_alg: SignAlgo::PqDsa(&ML_DSA_44_SIGNING), | ||
| oid_components: ML_DSA_44, | ||
| params: SignatureAlgorithmParams::None, | ||
| }; | ||
| /// ML-DSA-44 signing as per <https://www.ietf.org/archive/id/draft-ietf-lamps-dilithium-certificates-12.html#name-identifiers>. | ||
| #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] | ||
| pub static PKCS_ML_DSA_65: SignatureAlgorithm = SignatureAlgorithm { | ||
| oids_sign_alg: &[ML_DSA_65], | ||
| #[cfg(all(feature = "crypto", feature = "aws_lc_rs_unstable"))] | ||
| sign_alg: SignAlgo::PqDsa(&ML_DSA_65_SIGNING), | ||
| oid_components: ML_DSA_65, | ||
| params: SignatureAlgorithmParams::None, | ||
| }; | ||
| /// ML-DSA-44 signing as per <https://www.ietf.org/archive/id/draft-ietf-lamps-dilithium-certificates-12.html#name-identifiers>. | ||
| #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] | ||
| pub static PKCS_ML_DSA_87: SignatureAlgorithm = SignatureAlgorithm { | ||
| oids_sign_alg: &[ML_DSA_87], | ||
| #[cfg(all(feature = "crypto", feature = "aws_lc_rs_unstable"))] | ||
| sign_alg: SignAlgo::PqDsa(&ML_DSA_87_SIGNING), | ||
| oid_components: ML_DSA_87, | ||
| params: SignatureAlgorithmParams::None, | ||
| }; | ||
| } | ||
@@ -216,0 +252,0 @@ // Signature algorithm IDs as per https://tools.ietf.org/html/rfc4055 |
-250
| #![cfg(all(feature = "crypto", feature = "x509-parser"))] | ||
| use time::{Duration, OffsetDateTime}; | ||
| use rcgen::{BasicConstraints, Certificate, CertificateParams, DnType, IsCa, Issuer}; | ||
| use rcgen::{CertificateRevocationListParams, RevocationReason, RevokedCertParams}; | ||
| use rcgen::{DnValue, KeyPair}; | ||
| use rcgen::{KeyUsagePurpose, SerialNumber}; | ||
| mod util; | ||
| fn default_params() -> (CertificateParams, KeyPair) { | ||
| let (mut params, key_pair) = util::default_params(); | ||
| // Botan has a sanity check that enforces a maximum expiration date | ||
| params.not_after = rcgen::date_time_ymd(3016, 1, 1); | ||
| (params, key_pair) | ||
| } | ||
| fn check_cert(cert_der: &[u8], cert: &Certificate) { | ||
| println!("{}", cert.pem()); | ||
| check_cert_ca(cert_der, cert, cert_der); | ||
| } | ||
| fn check_cert_ca(cert_der: &[u8], _cert: &Certificate, ca_der: &[u8]) { | ||
| println!( | ||
| "botan version: {}", | ||
| botan::Version::current().unwrap().string | ||
| ); | ||
| let trust_anchor = botan::Certificate::load(ca_der).unwrap(); | ||
| let end_entity_cert = botan::Certificate::load(cert_der).unwrap(); | ||
| // Set time to Jan 10, 2004 | ||
| const REFERENCE_TIME: Option<u64> = Some(0x40_00_00_00); | ||
| // Verify the certificate | ||
| end_entity_cert | ||
| .verify( | ||
| &[], | ||
| &[&trust_anchor], | ||
| None, | ||
| Some("crabs.crabs"), | ||
| REFERENCE_TIME, | ||
| ) | ||
| .unwrap(); | ||
| // TODO perform a full handshake | ||
| } | ||
| #[test] | ||
| fn test_botan() { | ||
| let (params, key_pair) = default_params(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate. | ||
| check_cert(cert.der(), &cert); | ||
| } | ||
| #[test] | ||
| fn test_botan_256() { | ||
| let (params, _) = default_params(); | ||
| let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate. | ||
| check_cert(cert.der(), &cert); | ||
| } | ||
| #[test] | ||
| fn test_botan_384() { | ||
| let (params, _) = default_params(); | ||
| let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P384_SHA384).unwrap(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate. | ||
| check_cert(cert.der(), &cert); | ||
| } | ||
| #[test] | ||
| #[cfg(feature = "aws_lc_rs")] | ||
| fn test_botan_521() { | ||
| let (params, _) = default_params(); | ||
| let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P521_SHA512).unwrap(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate. | ||
| check_cert(cert.der(), &cert); | ||
| } | ||
| #[test] | ||
| fn test_botan_25519() { | ||
| let (params, _) = default_params(); | ||
| let key_pair = KeyPair::generate_for(&rcgen::PKCS_ED25519).unwrap(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate. | ||
| check_cert(cert.der(), &cert); | ||
| } | ||
| #[test] | ||
| fn test_botan_25519_v1_given() { | ||
| let (params, _) = default_params(); | ||
| let key_pair = KeyPair::from_pem(util::ED25519_TEST_KEY_PAIR_PEM_V1).unwrap(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate. | ||
| check_cert(cert.der(), &cert); | ||
| } | ||
| #[test] | ||
| fn test_botan_25519_v2_given() { | ||
| let (params, _) = default_params(); | ||
| let key_pair = KeyPair::from_pem(util::ED25519_TEST_KEY_PAIR_PEM_V2).unwrap(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate. | ||
| check_cert(cert.der(), &cert); | ||
| } | ||
| #[test] | ||
| fn test_botan_rsa_given() { | ||
| let (params, _) = default_params(); | ||
| let key_pair = KeyPair::from_pem(util::RSA_TEST_KEY_PAIR_PEM).unwrap(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate. | ||
| check_cert(cert.der(), &cert); | ||
| } | ||
| #[test] | ||
| fn test_botan_separate_ca() { | ||
| let (mut ca_params, ca_key) = default_params(); | ||
| ca_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); | ||
| let ca_cert = ca_params.self_signed(&ca_key).unwrap(); | ||
| let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]).unwrap(); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::OrganizationName, "Crab widgits SE"); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::CommonName, "Dev domain"); | ||
| // Botan has a sanity check that enforces a maximum expiration date | ||
| params.not_after = rcgen::date_time_ymd(3016, 1, 1); | ||
| let key_pair = KeyPair::generate().unwrap(); | ||
| let ca = Issuer::new(ca_params, ca_key); | ||
| let cert = params.signed_by(&key_pair, &ca).unwrap(); | ||
| check_cert_ca(cert.der(), &cert, ca_cert.der()); | ||
| } | ||
| #[cfg(feature = "x509-parser")] | ||
| #[test] | ||
| fn test_botan_imported_ca() { | ||
| let (mut params, ca_key) = default_params(); | ||
| params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); | ||
| let ca_cert = params.self_signed(&ca_key).unwrap(); | ||
| let ca_cert_der = ca_cert.der(); | ||
| let ca = Issuer::from_ca_cert_der(ca_cert.der(), ca_key).unwrap(); | ||
| let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]).unwrap(); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::OrganizationName, "Crab widgits SE"); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::CommonName, "Dev domain"); | ||
| // Botan has a sanity check that enforces a maximum expiration date | ||
| params.not_after = rcgen::date_time_ymd(3016, 1, 1); | ||
| let key_pair = KeyPair::generate().unwrap(); | ||
| let cert = params.signed_by(&key_pair, &ca).unwrap(); | ||
| check_cert_ca(cert.der(), &cert, ca_cert_der); | ||
| } | ||
| #[cfg(feature = "x509-parser")] | ||
| #[test] | ||
| fn test_botan_imported_ca_with_printable_string() { | ||
| let (mut params, imported_ca_key) = default_params(); | ||
| params.distinguished_name.push( | ||
| DnType::CountryName, | ||
| DnValue::PrintableString("US".try_into().unwrap()), | ||
| ); | ||
| params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); | ||
| let ca_cert = params.self_signed(&imported_ca_key).unwrap(); | ||
| let ca = Issuer::from_ca_cert_der(ca_cert.der(), imported_ca_key).unwrap(); | ||
| let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]).unwrap(); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::OrganizationName, "Crab widgits SE"); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::CommonName, "Dev domain"); | ||
| // Botan has a sanity check that enforces a maximum expiration date | ||
| params.not_after = rcgen::date_time_ymd(3016, 1, 1); | ||
| let key_pair = KeyPair::generate().unwrap(); | ||
| let cert = params.signed_by(&key_pair, &ca).unwrap(); | ||
| check_cert_ca(cert.der(), &cert, ca_cert.der()); | ||
| } | ||
| #[test] | ||
| fn test_botan_crl_parse() { | ||
| // Create an issuer CA. | ||
| let alg = &rcgen::PKCS_ECDSA_P256_SHA256; | ||
| let (mut issuer, _) = util::default_params(); | ||
| issuer.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); | ||
| issuer.key_usages = vec![ | ||
| KeyUsagePurpose::KeyCertSign, | ||
| KeyUsagePurpose::DigitalSignature, | ||
| KeyUsagePurpose::CrlSign, | ||
| ]; | ||
| let issuer_key = KeyPair::generate_for(alg).unwrap(); | ||
| let ca = Issuer::new(issuer, issuer_key); | ||
| // Create an end entity cert issued by the issuer. | ||
| let (mut ee, _) = util::default_params(); | ||
| ee.is_ca = IsCa::NoCa; | ||
| ee.serial_number = Some(SerialNumber::from(99999)); | ||
| // Botan has a sanity check that enforces a maximum expiration date | ||
| ee.not_after = rcgen::date_time_ymd(3016, 1, 1); | ||
| let ee_key = KeyPair::generate_for(alg).unwrap(); | ||
| let ee_cert = ee.signed_by(&ee_key, &ca).unwrap(); | ||
| let botan_ee = botan::Certificate::load(ee_cert.der()).unwrap(); | ||
| // Generate a CRL with the issuer that revokes the EE cert. | ||
| let now = OffsetDateTime::now_utc(); | ||
| let crl = CertificateRevocationListParams { | ||
| this_update: now, | ||
| next_update: now + Duration::weeks(1), | ||
| crl_number: rcgen::SerialNumber::from(1234), | ||
| issuing_distribution_point: None, | ||
| revoked_certs: vec![RevokedCertParams { | ||
| serial_number: ee.serial_number.clone().unwrap(), | ||
| revocation_time: now, | ||
| reason_code: Some(RevocationReason::KeyCompromise), | ||
| invalidity_date: None, | ||
| }], | ||
| key_identifier_method: rcgen::KeyIdMethod::Sha256, | ||
| }; | ||
| let crl = crl.signed_by(&ca).unwrap(); | ||
| // We should be able to load the CRL in both serializations. | ||
| botan::CRL::load(crl.pem().unwrap().as_ref()).unwrap(); | ||
| let crl = botan::CRL::load(crl.der()).unwrap(); | ||
| // We should find the EE cert revoked. | ||
| assert!(crl.is_revoked(&botan_ee).unwrap()); | ||
| } |
-475
| #![cfg(feature = "crypto")] | ||
| mod util; | ||
| #[cfg(feature = "pem")] | ||
| mod test_key_params_mismatch { | ||
| use std::collections::hash_map::DefaultHasher; | ||
| use std::hash::{Hash, Hasher}; | ||
| fn generate_hash<T: Hash>(subject: &T) -> u64 { | ||
| let mut hasher = DefaultHasher::new(); | ||
| subject.hash(&mut hasher); | ||
| hasher.finish() | ||
| } | ||
| #[test] | ||
| fn test_key_params_mismatch() { | ||
| let available_key_params = [ | ||
| &rcgen::PKCS_RSA_SHA256, | ||
| &rcgen::PKCS_ECDSA_P256_SHA256, | ||
| &rcgen::PKCS_ECDSA_P384_SHA384, | ||
| #[cfg(feature = "aws_lc_rs")] | ||
| &rcgen::PKCS_ECDSA_P521_SHA512, | ||
| &rcgen::PKCS_ED25519, | ||
| ]; | ||
| for (i, kalg_1) in available_key_params.iter().enumerate() { | ||
| for (j, kalg_2) in available_key_params.iter().enumerate() { | ||
| if i == j { | ||
| assert_eq!(*kalg_1, *kalg_2); | ||
| assert_eq!(generate_hash(*kalg_1), generate_hash(*kalg_2)); | ||
| continue; | ||
| } | ||
| assert_ne!(*kalg_1, *kalg_2); | ||
| assert_ne!(generate_hash(*kalg_1), generate_hash(*kalg_2)); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| #[cfg(feature = "x509-parser")] | ||
| mod test_x509_custom_ext { | ||
| use crate::util; | ||
| use rcgen::CustomExtension; | ||
| use x509_parser::oid_registry::asn1_rs; | ||
| use x509_parser::prelude::{ | ||
| FromDer, ParsedCriAttribute, X509Certificate, X509CertificationRequest, | ||
| }; | ||
| #[test] | ||
| fn custom_ext() { | ||
| // Create an imaginary critical custom extension for testing. | ||
| let test_oid = asn1_rs::Oid::from(&[2, 5, 29, 999999]).unwrap(); | ||
| let test_ext = yasna::construct_der(|writer| { | ||
| writer.write_utf8_string("🦀 greetz to ferris 🦀"); | ||
| }); | ||
| let mut custom_ext = CustomExtension::from_oid_content( | ||
| test_oid.iter().unwrap().collect::<Vec<u64>>().as_slice(), | ||
| test_ext.clone(), | ||
| ); | ||
| custom_ext.set_criticality(true); | ||
| // Generate a certificate with the custom extension, parse it with x509-parser. | ||
| let (mut params, test_key) = util::default_params(); | ||
| params.custom_extensions = vec![custom_ext]; | ||
| // Ensure the custom exts. being omitted into a CSR doesn't require SAN ext being present. | ||
| // See https://github.com/rustls/rcgen/issues/122 | ||
| params.subject_alt_names = Vec::default(); | ||
| let test_cert = params.self_signed(&test_key).unwrap(); | ||
| let (_, x509_test_cert) = X509Certificate::from_der(test_cert.der()).unwrap(); | ||
| // We should be able to find the extension by OID, with expected criticality and value. | ||
| let favorite_drink_ext = x509_test_cert | ||
| .get_extension_unique(&test_oid) | ||
| .expect("invalid extensions") | ||
| .expect("missing custom extension"); | ||
| assert!(favorite_drink_ext.critical); | ||
| assert_eq!(favorite_drink_ext.value, test_ext); | ||
| // Generate a CSR with the custom extension, parse it with x509-parser. | ||
| let test_cert_csr = params.serialize_request(&test_key).unwrap(); | ||
| let (_, x509_csr) = X509CertificationRequest::from_der(test_cert_csr.der()).unwrap(); | ||
| // We should find that the CSR contains requested extensions. | ||
| // Note: we can't use `x509_csr.requested_extensions()` here because it maps the raw extension | ||
| // request extensions to their parsed form, and of course x509-parser doesn't parse our custom extension. | ||
| let exts = x509_csr | ||
| .certification_request_info | ||
| .iter_attributes() | ||
| .find_map(|attr| { | ||
| if let ParsedCriAttribute::ExtensionRequest(requested) = &attr.parsed_attribute() { | ||
| Some(requested.extensions.iter().collect::<Vec<_>>()) | ||
| } else { | ||
| None | ||
| } | ||
| }) | ||
| .expect("missing requested extensions"); | ||
| // We should find the custom extension with expected criticality and value. | ||
| let custom_ext = exts | ||
| .iter() | ||
| .find(|ext| ext.oid == test_oid) | ||
| .expect("missing requested custom extension"); | ||
| assert!(custom_ext.critical); | ||
| assert_eq!(custom_ext.value, test_ext); | ||
| } | ||
| } | ||
| #[cfg(feature = "x509-parser")] | ||
| mod test_csr_custom_attributes { | ||
| use rcgen::{Attribute, CertificateParams, KeyPair}; | ||
| use x509_parser::{ | ||
| der_parser::Oid, | ||
| prelude::{FromDer, X509CertificationRequest}, | ||
| }; | ||
| /// Test serializing a CSR with custom attributes. | ||
| /// This test case uses `challengePassword` from [RFC 2985], a simple | ||
| /// ATTRIBUTE that contains a single UTF8String. | ||
| /// | ||
| /// [RFC 2985]: <https://datatracker.ietf.org/doc/html/rfc2985> | ||
| #[test] | ||
| fn test_csr_custom_attributes() { | ||
| // OID for challengePassword | ||
| const CHALLENGE_PWD_OID: &[u64] = &[1, 2, 840, 113549, 1, 9, 7]; | ||
| // Attribute values for challengePassword | ||
| let challenge_pwd_values = yasna::try_construct_der::<_, ()>(|writer| { | ||
| // Reminder: CSR attribute values are contained in a SET | ||
| writer.write_set(|writer| { | ||
| // Challenge passwords only have one value, a UTF8String | ||
| writer | ||
| .next() | ||
| .write_utf8_string("nobody uses challenge passwords anymore"); | ||
| Ok(()) | ||
| }) | ||
| }) | ||
| .unwrap(); | ||
| // Challenge password attribute | ||
| let challenge_password_attribute = Attribute { | ||
| oid: CHALLENGE_PWD_OID, | ||
| values: challenge_pwd_values.clone(), | ||
| }; | ||
| // Serialize a DER-encoded CSR | ||
| let params = CertificateParams::default(); | ||
| let key_pair = KeyPair::generate().unwrap(); | ||
| let csr = params | ||
| .serialize_request_with_attributes(&key_pair, vec![challenge_password_attribute]) | ||
| .unwrap(); | ||
| // Parse the CSR | ||
| let (_, x509_csr) = X509CertificationRequest::from_der(csr.der()).unwrap(); | ||
| let parsed_attribute_value = x509_csr | ||
| .certification_request_info | ||
| .attributes_map() | ||
| .unwrap() | ||
| .get(&Oid::from(CHALLENGE_PWD_OID).unwrap()) | ||
| .unwrap() | ||
| .value; | ||
| assert_eq!(parsed_attribute_value, challenge_pwd_values); | ||
| } | ||
| } | ||
| #[cfg(feature = "x509-parser")] | ||
| mod test_x509_parser_crl { | ||
| use crate::util; | ||
| use x509_parser::extensions::{DistributionPointName, ParsedExtension}; | ||
| use x509_parser::num_bigint::BigUint; | ||
| use x509_parser::prelude::{FromDer, GeneralName, IssuingDistributionPoint, X509Certificate}; | ||
| use x509_parser::revocation_list::CertificateRevocationList; | ||
| use x509_parser::x509::X509Version; | ||
| #[test] | ||
| fn parse_crl() { | ||
| // Create a CRL with one revoked cert, and an issuer to sign the CRL. | ||
| let (crl_params, crl, issuer) = util::test_crl(); | ||
| let revoked_cert = crl_params.revoked_certs.first().unwrap(); | ||
| let revoked_cert_serial = BigUint::from_bytes_be(revoked_cert.serial_number.as_ref()); | ||
| let (_, x509_issuer) = X509Certificate::from_der(issuer.der()).unwrap(); | ||
| // We should be able to parse the CRL with x509-parser without error. | ||
| let (_, x509_crl) = | ||
| CertificateRevocationList::from_der(crl.der()).expect("failed to parse CRL DER"); | ||
| // The properties of the CRL should match expected. | ||
| assert_eq!(x509_crl.version().unwrap(), X509Version(1)); | ||
| assert_eq!(x509_crl.issuer(), x509_issuer.subject()); | ||
| assert_eq!( | ||
| x509_crl.last_update().to_datetime().unix_timestamp(), | ||
| crl_params.this_update.unix_timestamp() | ||
| ); | ||
| assert_eq!( | ||
| x509_crl | ||
| .next_update() | ||
| .unwrap() | ||
| .to_datetime() | ||
| .unix_timestamp(), | ||
| crl_params.next_update.unix_timestamp() | ||
| ); | ||
| let crl_number = BigUint::from_bytes_be(crl_params.crl_number.as_ref()); | ||
| assert_eq!(x509_crl.crl_number().unwrap(), &crl_number); | ||
| // We should find the expected revoked certificate serial with the correct reason code. | ||
| let x509_revoked_cert = x509_crl | ||
| .iter_revoked_certificates() | ||
| .next() | ||
| .expect("failed to find revoked cert in CRL"); | ||
| assert_eq!(x509_revoked_cert.user_certificate, revoked_cert_serial); | ||
| let (_, reason_code) = x509_revoked_cert.reason_code().unwrap(); | ||
| assert_eq!(reason_code.0, revoked_cert.reason_code.unwrap() as u8); | ||
| // The issuing distribution point extension should be present and marked critical. | ||
| let issuing_dp_ext = x509_crl | ||
| .extensions() | ||
| .iter() | ||
| .find(|ext| { | ||
| ext.oid == x509_parser::oid_registry::OID_X509_EXT_ISSUER_DISTRIBUTION_POINT | ||
| }) | ||
| .expect("failed to find issuing distribution point extension"); | ||
| assert!(issuing_dp_ext.critical); | ||
| // The parsed issuing distribution point extension should match expected. | ||
| let ParsedExtension::IssuingDistributionPoint(idp) = issuing_dp_ext.parsed_extension() | ||
| else { | ||
| panic!("missing parsed CRL IDP ext"); | ||
| }; | ||
| assert_eq!( | ||
| idp, | ||
| &IssuingDistributionPoint { | ||
| only_contains_user_certs: true, | ||
| only_contains_ca_certs: false, | ||
| only_contains_attribute_certs: false, | ||
| indirect_crl: false, | ||
| only_some_reasons: None, | ||
| distribution_point: Some(DistributionPointName::FullName(vec![GeneralName::URI( | ||
| "http://example.com/crl", | ||
| )])), | ||
| } | ||
| ); | ||
| // We should be able to verify the CRL signature with the issuer. | ||
| assert!(x509_crl.verify_signature(x509_issuer.public_key()).is_ok()); | ||
| } | ||
| } | ||
| #[cfg(feature = "x509-parser")] | ||
| mod test_parse_crl_dps { | ||
| use crate::util; | ||
| use x509_parser::extensions::{DistributionPointName, ParsedExtension}; | ||
| #[test] | ||
| fn parse_crl_dps() { | ||
| // Generate and parse a certificate that includes two CRL distribution points. | ||
| let der = util::cert_with_crl_dps(); | ||
| let (_, parsed_cert) = x509_parser::parse_x509_certificate(&der).unwrap(); | ||
| // We should find a CRL DP extension was parsed. | ||
| let crl_dps = parsed_cert | ||
| .get_extension_unique(&x509_parser::oid_registry::OID_X509_EXT_CRL_DISTRIBUTION_POINTS) | ||
| .expect("malformed CRL distribution points extension") | ||
| .expect("missing CRL distribution points extension"); | ||
| // The extension should not be critical. | ||
| assert!(!crl_dps.critical); | ||
| // We should be able to parse the definition. | ||
| let crl_dps = match crl_dps.parsed_extension() { | ||
| ParsedExtension::CRLDistributionPoints(crl_dps) => crl_dps, | ||
| _ => panic!("unexpected parsed extension type"), | ||
| }; | ||
| // There should be two DPs. | ||
| assert_eq!(crl_dps.points.len(), 2); | ||
| // Each distribution point should only include a distribution point name holding a sequence | ||
| // of general names. | ||
| let general_names = crl_dps | ||
| .points | ||
| .iter() | ||
| .flat_map(|dp| { | ||
| // We shouldn't find a cRLIssuer or onlySomeReasons field. | ||
| assert!(dp.crl_issuer.is_none()); | ||
| assert!(dp.reasons.is_none()); | ||
| match dp | ||
| .distribution_point | ||
| .as_ref() | ||
| .expect("missing distribution point name") | ||
| { | ||
| DistributionPointName::FullName(general_names) => general_names.iter(), | ||
| DistributionPointName::NameRelativeToCRLIssuer(_) => { | ||
| panic!("unexpected name relative to cRL issuer") | ||
| }, | ||
| } | ||
| }) | ||
| .collect::<Vec<_>>(); | ||
| // All of the general names should be URIs. | ||
| let uris = general_names | ||
| .iter() | ||
| .map(|general_name| match general_name { | ||
| x509_parser::extensions::GeneralName::URI(uri) => *uri, | ||
| _ => panic!("unexpected general name type"), | ||
| }) | ||
| .collect::<Vec<_>>(); | ||
| // We should find the expected URIs. | ||
| assert_eq!( | ||
| uris, | ||
| &[ | ||
| "http://example.com/crl.der", | ||
| "http://crls.example.com/1234", | ||
| "ldap://example.com/crl.der" | ||
| ] | ||
| ); | ||
| } | ||
| } | ||
| #[cfg(feature = "x509-parser")] | ||
| mod test_csr_extension_request { | ||
| use rcgen::{CertificateParams, ExtendedKeyUsagePurpose, KeyPair, KeyUsagePurpose}; | ||
| use x509_parser::prelude::{FromDer, ParsedExtension, X509CertificationRequest}; | ||
| #[test] | ||
| fn dont_write_sans_extension_if_no_sans_are_present() { | ||
| let mut params = CertificateParams::default(); | ||
| params.key_usages.push(KeyUsagePurpose::DigitalSignature); | ||
| let key_pair = KeyPair::generate().unwrap(); | ||
| let csr = params.serialize_request(&key_pair).unwrap(); | ||
| let (_, parsed_csr) = X509CertificationRequest::from_der(csr.der()).unwrap(); | ||
| assert!(!parsed_csr | ||
| .requested_extensions() | ||
| .unwrap() | ||
| .any(|ext| matches!(ext, ParsedExtension::SubjectAlternativeName(_)))); | ||
| } | ||
| #[test] | ||
| fn write_extension_request_if_ekus_are_present() { | ||
| let mut params = CertificateParams::default(); | ||
| params | ||
| .extended_key_usages | ||
| .push(ExtendedKeyUsagePurpose::ClientAuth); | ||
| let key_pair = KeyPair::generate().unwrap(); | ||
| let csr = params.serialize_request(&key_pair).unwrap(); | ||
| let (_, parsed_csr) = X509CertificationRequest::from_der(csr.der()).unwrap(); | ||
| let requested_extensions = parsed_csr | ||
| .requested_extensions() | ||
| .unwrap() | ||
| .collect::<Vec<_>>(); | ||
| assert!(matches!( | ||
| requested_extensions.first().unwrap(), | ||
| ParsedExtension::ExtendedKeyUsage(_) | ||
| )); | ||
| } | ||
| } | ||
| #[cfg(feature = "x509-parser")] | ||
| mod test_csr { | ||
| use rcgen::{ | ||
| CertificateParams, CertificateSigningRequestParams, ExtendedKeyUsagePurpose, KeyPair, | ||
| KeyUsagePurpose, | ||
| }; | ||
| #[test] | ||
| fn test_csr_roundtrip() { | ||
| // We should be able to serialize a CSR, and then parse the CSR. | ||
| let params = CertificateParams::default(); | ||
| generate_and_test_parsed_csr(¶ms); | ||
| } | ||
| #[test] | ||
| fn test_csr_with_key_usages_roundtrip() { | ||
| let mut params = CertificateParams::default(); | ||
| params.key_usages = vec![ | ||
| KeyUsagePurpose::DigitalSignature, | ||
| KeyUsagePurpose::ContentCommitment, | ||
| KeyUsagePurpose::KeyEncipherment, | ||
| KeyUsagePurpose::DataEncipherment, | ||
| KeyUsagePurpose::KeyAgreement, | ||
| KeyUsagePurpose::KeyCertSign, | ||
| KeyUsagePurpose::CrlSign, | ||
| // It doesn't make sense to have both encipher and decipher only | ||
| // So we'll take this opportunity to test omitting a key usage | ||
| // KeyUsagePurpose::EncipherOnly, | ||
| KeyUsagePurpose::DecipherOnly, | ||
| ]; | ||
| generate_and_test_parsed_csr(¶ms); | ||
| } | ||
| #[test] | ||
| fn test_csr_with_extended_key_usages_roundtrip() { | ||
| let mut params = CertificateParams::default(); | ||
| params.extended_key_usages = vec![ | ||
| ExtendedKeyUsagePurpose::ServerAuth, | ||
| ExtendedKeyUsagePurpose::ClientAuth, | ||
| ]; | ||
| generate_and_test_parsed_csr(¶ms); | ||
| } | ||
| #[test] | ||
| fn test_csr_with_key_usgaes_and_extended_key_usages_roundtrip() { | ||
| let mut params = CertificateParams::default(); | ||
| params.key_usages = vec![KeyUsagePurpose::DigitalSignature]; | ||
| params.extended_key_usages = vec![ExtendedKeyUsagePurpose::ClientAuth]; | ||
| generate_and_test_parsed_csr(¶ms); | ||
| } | ||
| fn generate_and_test_parsed_csr(params: &CertificateParams) { | ||
| // Generate a key pair for the CSR | ||
| let key_pair = KeyPair::generate().unwrap(); | ||
| // Serialize the CSR into DER from the given parameters | ||
| let csr = params.serialize_request(&key_pair).unwrap(); | ||
| // Parse the CSR we just serialized | ||
| let csrp = CertificateSigningRequestParams::from_der(csr.der()).unwrap(); | ||
| // Ensure algorithms match. | ||
| assert_eq!(key_pair.algorithm(), csrp.public_key.algorithm()); | ||
| // Assert that our parsed parameters match our initial parameters | ||
| assert_eq!(*params, csrp.params); | ||
| } | ||
| } | ||
| #[cfg(feature = "x509-parser")] | ||
| mod test_subject_alternative_name_criticality { | ||
| use x509_parser::certificate::X509Certificate; | ||
| use x509_parser::extensions::X509Extension; | ||
| use x509_parser::{oid_registry, parse_x509_certificate}; | ||
| use crate::util::default_params; | ||
| #[test] | ||
| fn with_subject_sans_not_critical() { | ||
| let (params, keypair) = default_params(); | ||
| assert!( | ||
| !params | ||
| .distinguished_name | ||
| .iter() | ||
| .collect::<Vec<_>>() | ||
| .is_empty(), | ||
| "non-empty subject required for test" | ||
| ); | ||
| let cert = params.self_signed(&keypair).unwrap(); | ||
| let cert = cert.der(); | ||
| let (_, parsed) = parse_x509_certificate(cert).unwrap(); | ||
| assert!( | ||
| !san_ext(&parsed).critical, | ||
| "with subject, SAN ext should not be critical" | ||
| ); | ||
| } | ||
| #[test] | ||
| fn without_subject_sans_critical() { | ||
| let (mut params, keypair) = default_params(); | ||
| params.distinguished_name = Default::default(); | ||
| let cert = params.self_signed(&keypair).unwrap(); | ||
| let cert = cert.der(); | ||
| let (_, parsed) = parse_x509_certificate(cert).unwrap(); | ||
| assert!( | ||
| san_ext(&parsed).critical, | ||
| "without subject, SAN ext should be critical" | ||
| ); | ||
| } | ||
| fn san_ext<'cert>(cert: &'cert X509Certificate) -> &'cert X509Extension<'cert> { | ||
| cert.extensions() | ||
| .iter() | ||
| .find(|ext| ext.oid == oid_registry::OID_X509_EXT_SUBJECT_ALT_NAME) | ||
| .expect("missing SAN extension") | ||
| } | ||
| } |
-532
| #![cfg(all(unix, feature = "pem"))] | ||
| use std::cell::RefCell; | ||
| use std::io::{Error, ErrorKind, Read, Result as ioResult, Write}; | ||
| use std::rc::Rc; | ||
| use openssl::asn1::{Asn1Integer, Asn1Time}; | ||
| use openssl::bn::BigNum; | ||
| use openssl::pkey::PKey; | ||
| use openssl::ssl::{HandshakeError, SslAcceptor, SslConnector, SslMethod}; | ||
| use openssl::stack::Stack; | ||
| use openssl::x509::store::{X509Store, X509StoreBuilder}; | ||
| use openssl::x509::{CrlStatus, X509Crl, X509Req, X509StoreContext, X509}; | ||
| use rcgen::{ | ||
| BasicConstraints, Certificate, CertificateParams, DnType, DnValue, GeneralSubtree, IsCa, | ||
| Issuer, KeyPair, NameConstraints, | ||
| }; | ||
| mod util; | ||
| fn verify_cert_basic(cert: &Certificate) { | ||
| let cert_pem = cert.pem(); | ||
| println!("{cert_pem}"); | ||
| let x509 = X509::from_pem(cert_pem.as_bytes()).unwrap(); | ||
| let mut builder = X509StoreBuilder::new().unwrap(); | ||
| builder.add_cert(x509.clone()).unwrap(); | ||
| let store: X509Store = builder.build(); | ||
| let mut ctx = X509StoreContext::new().unwrap(); | ||
| let mut stack = Stack::new().unwrap(); | ||
| stack.push(x509.clone()).unwrap(); | ||
| ctx.init(&store, &x509, stack.as_ref(), |ctx| { | ||
| ctx.verify_cert().unwrap(); | ||
| Ok(()) | ||
| }) | ||
| .unwrap(); | ||
| } | ||
| // TODO implement Debug manually instead of | ||
| // deriving it | ||
| #[derive(Clone, Debug)] | ||
| struct PipeInner([Vec<u8>; 2]); | ||
| #[derive(Clone, Debug)] | ||
| struct PipeEnd { | ||
| read_pos: usize, | ||
| /// Which end of the pipe | ||
| end_idx: usize, | ||
| inner: Rc<RefCell<PipeInner>>, | ||
| } | ||
| fn create_pipe() -> (PipeEnd, PipeEnd) { | ||
| let pipe_inner = PipeInner([Vec::new(), Vec::new()]); | ||
| let inner = Rc::new(RefCell::new(pipe_inner)); | ||
| ( | ||
| PipeEnd { | ||
| read_pos: 0, | ||
| end_idx: 0, | ||
| inner: inner.clone(), | ||
| }, | ||
| PipeEnd { | ||
| read_pos: 0, | ||
| end_idx: 1, | ||
| inner, | ||
| }, | ||
| ) | ||
| } | ||
| impl Write for PipeEnd { | ||
| fn write(&mut self, buf: &[u8]) -> ioResult<usize> { | ||
| self.inner.borrow_mut().0[self.end_idx].extend_from_slice(buf); | ||
| Ok(buf.len()) | ||
| } | ||
| fn flush(&mut self) -> ioResult<()> { | ||
| Ok(()) | ||
| } | ||
| } | ||
| impl Read for PipeEnd { | ||
| fn read(&mut self, mut buf: &mut [u8]) -> ioResult<usize> { | ||
| let inner = self.inner.borrow_mut(); | ||
| let r_sl = &inner.0[1 - self.end_idx][self.read_pos..]; | ||
| if r_sl.is_empty() { | ||
| return Err(Error::new(ErrorKind::WouldBlock, "oh no!")); | ||
| } | ||
| let r = buf.len().min(r_sl.len()); | ||
| std::io::copy(&mut &r_sl[..r], &mut buf)?; | ||
| self.read_pos += r; | ||
| Ok(r) | ||
| } | ||
| } | ||
| fn verify_cert(cert: &Certificate, key_pair: &KeyPair) { | ||
| verify_cert_basic(cert); | ||
| let key = key_pair.serialize_der(); | ||
| verify_cert_ca(&cert.pem(), &key, &cert.pem()); | ||
| } | ||
| fn verify_cert_ca(cert_pem: &str, key: &[u8], ca_cert_pem: &str) { | ||
| println!("{cert_pem}"); | ||
| println!("{ca_cert_pem}"); | ||
| let x509 = X509::from_pem(cert_pem.as_bytes()).unwrap(); | ||
| let ca_x509 = X509::from_pem(ca_cert_pem.as_bytes()).unwrap(); | ||
| let mut builder = X509StoreBuilder::new().unwrap(); | ||
| builder.add_cert(ca_x509).unwrap(); | ||
| let store: X509Store = builder.build(); | ||
| let srv = SslMethod::tls_server(); | ||
| let mut ssl_srv_ctx = SslAcceptor::mozilla_modern(srv).unwrap(); | ||
| //let key = cert.serialize_private_key_der(); | ||
| let pkey = PKey::private_key_from_der(key).unwrap(); | ||
| ssl_srv_ctx.set_private_key(&pkey).unwrap(); | ||
| ssl_srv_ctx.set_certificate(&x509).unwrap(); | ||
| let cln = SslMethod::tls_client(); | ||
| let mut ssl_cln_ctx = SslConnector::builder(cln).unwrap(); | ||
| ssl_cln_ctx.set_cert_store(store); | ||
| let ssl_srv_ctx = ssl_srv_ctx.build(); | ||
| let ssl_cln_ctx = ssl_cln_ctx.build(); | ||
| let (pipe_end_1, pipe_end_2) = create_pipe(); | ||
| let (mut ssl_srv_stream, mut ssl_cln_stream) = { | ||
| let mut srv_res = ssl_srv_ctx.accept(pipe_end_1); | ||
| let mut cln_res = ssl_cln_ctx.connect("crabs.crabs", pipe_end_2); | ||
| let mut ready = 0u8; | ||
| let mut iter_budget = 100; | ||
| loop { | ||
| match cln_res { | ||
| Ok(_) => ready |= 2, | ||
| Err(HandshakeError::WouldBlock(mh)) => cln_res = mh.handshake(), | ||
| Err(e) => panic!("Error: {e:?}"), | ||
| } | ||
| match srv_res { | ||
| Ok(_) => ready |= 1, | ||
| Err(HandshakeError::WouldBlock(mh)) => srv_res = mh.handshake(), | ||
| Err(e) => panic!("Error: {e:?}"), | ||
| } | ||
| if ready == 3 { | ||
| break (cln_res.unwrap(), srv_res.unwrap()); | ||
| } | ||
| if iter_budget == 0 { | ||
| panic!("iter budget exhausted"); | ||
| } | ||
| iter_budget -= 1; | ||
| } | ||
| }; | ||
| const HELLO_FROM_SRV: &[u8] = b"hello from server"; | ||
| const HELLO_FROM_CLN: &[u8] = b"hello from client"; | ||
| ssl_srv_stream.ssl_write(HELLO_FROM_SRV).unwrap(); | ||
| ssl_cln_stream.ssl_write(HELLO_FROM_CLN).unwrap(); | ||
| // TODO read the data we just wrote from the streams | ||
| } | ||
| fn verify_csr(params: &CertificateParams, key_pair: &KeyPair) { | ||
| let csr = params | ||
| .serialize_request(key_pair) | ||
| .and_then(|csr| csr.pem()) | ||
| .unwrap(); | ||
| println!("{csr}"); | ||
| let key = key_pair.serialize_der(); | ||
| let pkey = PKey::private_key_from_der(&key).unwrap(); | ||
| let req = X509Req::from_pem(csr.as_bytes()).unwrap(); | ||
| req.verify(&pkey).unwrap(); | ||
| } | ||
| #[test] | ||
| fn test_openssl() { | ||
| let (params, key_pair) = util::default_params(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| verify_cert(&cert, &key_pair); | ||
| } | ||
| #[test] | ||
| fn test_request() { | ||
| let (params, key_pair) = util::default_params(); | ||
| verify_csr(¶ms, &key_pair); | ||
| } | ||
| #[test] | ||
| fn test_openssl_256() { | ||
| let (params, _) = util::default_params(); | ||
| let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate. | ||
| verify_cert(&cert, &key_pair); | ||
| verify_csr(¶ms, &key_pair); | ||
| } | ||
| #[test] | ||
| fn test_openssl_384() { | ||
| let (params, _) = util::default_params(); | ||
| let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P384_SHA384).unwrap(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate. | ||
| verify_cert(&cert, &key_pair); | ||
| verify_csr(¶ms, &key_pair); | ||
| } | ||
| #[test] | ||
| #[cfg(feature = "aws_lc_rs")] | ||
| fn test_openssl_521() { | ||
| let (params, _) = util::default_params(); | ||
| let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P521_SHA512).unwrap(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate. | ||
| verify_cert(&cert, &key_pair); | ||
| verify_csr(¶ms, &key_pair); | ||
| } | ||
| #[test] | ||
| fn test_openssl_25519() { | ||
| let (params, _) = util::default_params(); | ||
| let key_pair = KeyPair::generate_for(&rcgen::PKCS_ED25519).unwrap(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate. | ||
| // TODO openssl doesn't support v2 keys (yet) | ||
| // https://github.com/est31/rcgen/issues/11 | ||
| // https://github.com/openssl/openssl/issues/10468 | ||
| verify_cert_basic(&cert); | ||
| //verify_csr(&cert); | ||
| } | ||
| #[test] | ||
| fn test_openssl_25519_v1_given() { | ||
| let (params, _) = util::default_params(); | ||
| let key_pair = rcgen::KeyPair::from_pem(util::ED25519_TEST_KEY_PAIR_PEM_V1).unwrap(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate as well as CSR, | ||
| // but only on OpenSSL >= 1.1.1 | ||
| // On prior versions, only do basic verification | ||
| #[allow(clippy::unusual_byte_groupings)] | ||
| if openssl::version::number() >= 0x1_01_01_00_f { | ||
| verify_cert(&cert, &key_pair); | ||
| verify_csr(¶ms, &key_pair); | ||
| } else { | ||
| verify_cert_basic(&cert); | ||
| } | ||
| } | ||
| #[test] | ||
| fn test_openssl_25519_v2_given() { | ||
| let (params, _) = util::default_params(); | ||
| let key_pair = rcgen::KeyPair::from_pem(util::ED25519_TEST_KEY_PAIR_PEM_V2).unwrap(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate. | ||
| // TODO openssl doesn't support v2 keys (yet) | ||
| // https://github.com/est31/rcgen/issues/11 | ||
| // https://github.com/openssl/openssl/issues/10468 | ||
| verify_cert_basic(&cert); | ||
| //verify_csr(&cert); | ||
| } | ||
| #[test] | ||
| fn test_openssl_rsa_given() { | ||
| let (params, _) = util::default_params(); | ||
| let key_pair = KeyPair::from_pem(util::RSA_TEST_KEY_PAIR_PEM).unwrap(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate. | ||
| verify_cert(&cert, &key_pair); | ||
| verify_csr(¶ms, &key_pair); | ||
| } | ||
| #[test] | ||
| fn test_openssl_rsa_combinations_given() { | ||
| let alg_list = [ | ||
| &rcgen::PKCS_RSA_SHA256, | ||
| &rcgen::PKCS_RSA_SHA384, | ||
| &rcgen::PKCS_RSA_SHA512, | ||
| //&rcgen::PKCS_RSA_PSS_SHA256, | ||
| ]; | ||
| for (i, alg) in alg_list.iter().enumerate() { | ||
| let (params, _) = util::default_params(); | ||
| let key_pair = | ||
| KeyPair::from_pkcs8_pem_and_sign_algo(util::RSA_TEST_KEY_PAIR_PEM, alg).unwrap(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate. | ||
| if i >= 4 { | ||
| verify_cert(&cert, &key_pair); | ||
| verify_csr(¶ms, &key_pair); | ||
| } else { | ||
| // The PSS key types are not fully supported. | ||
| // An attempt to use them gives a handshake error. | ||
| verify_cert_basic(&cert); | ||
| } | ||
| } | ||
| } | ||
| #[test] | ||
| fn test_openssl_separate_ca() { | ||
| let (mut ca_params, ca_key) = util::default_params(); | ||
| ca_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); | ||
| let ca_cert = ca_params.self_signed(&ca_key).unwrap(); | ||
| let ca_cert_pem = ca_cert.pem(); | ||
| let ca = Issuer::new(ca_params, ca_key); | ||
| let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]).unwrap(); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::OrganizationName, "Crab widgits SE"); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::CommonName, "Dev domain"); | ||
| let cert_key = KeyPair::generate().unwrap(); | ||
| let cert = params.signed_by(&cert_key, &ca).unwrap(); | ||
| let key = cert_key.serialize_der(); | ||
| verify_cert_ca(&cert.pem(), &key, &ca_cert_pem); | ||
| } | ||
| #[test] | ||
| fn test_openssl_separate_ca_with_printable_string() { | ||
| let (mut ca_params, ca_key) = util::default_params(); | ||
| ca_params.distinguished_name.push( | ||
| DnType::CountryName, | ||
| DnValue::PrintableString("US".try_into().unwrap()), | ||
| ); | ||
| ca_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); | ||
| let ca_cert = ca_params.self_signed(&ca_key).unwrap(); | ||
| let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]).unwrap(); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::OrganizationName, "Crab widgits SE"); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::CommonName, "Dev domain"); | ||
| let cert_key = KeyPair::generate().unwrap(); | ||
| let ca = Issuer::new(ca_params, ca_key); | ||
| let cert = params.signed_by(&cert_key, &ca).unwrap(); | ||
| let key = cert_key.serialize_der(); | ||
| verify_cert_ca(&cert.pem(), &key, &ca_cert.pem()); | ||
| } | ||
| #[test] | ||
| fn test_openssl_separate_ca_with_other_signing_alg() { | ||
| let (mut ca_params, _) = util::default_params(); | ||
| ca_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); | ||
| let ca_key = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap(); | ||
| let ca_cert = ca_params.self_signed(&ca_key).unwrap(); | ||
| let ca = Issuer::new(ca_params, ca_key); | ||
| let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]).unwrap(); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::OrganizationName, "Crab widgits SE"); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::CommonName, "Dev domain"); | ||
| let cert_key = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P384_SHA384).unwrap(); | ||
| let cert = params.signed_by(&cert_key, &ca).unwrap(); | ||
| let key = cert_key.serialize_der(); | ||
| verify_cert_ca(&cert.pem(), &key, &ca_cert.pem()); | ||
| } | ||
| #[test] | ||
| fn test_openssl_separate_ca_name_constraints() { | ||
| let (mut ca_params, ca_key) = util::default_params(); | ||
| ca_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); | ||
| println!("openssl version: {:x}", openssl::version::number()); | ||
| ca_params.name_constraints = Some(NameConstraints { | ||
| permitted_subtrees: vec![GeneralSubtree::DnsName("crabs.crabs".to_string())], | ||
| //permitted_subtrees : vec![GeneralSubtree::DnsName("".to_string())], | ||
| //permitted_subtrees : Vec::new(), | ||
| //excluded_subtrees : vec![GeneralSubtree::DnsName(".v".to_string())], | ||
| excluded_subtrees: Vec::new(), | ||
| }); | ||
| let ca_cert = ca_params.self_signed(&ca_key).unwrap(); | ||
| let ca = Issuer::new(ca_params, ca_key); | ||
| let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]).unwrap(); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::OrganizationName, "Crab widgits SE"); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::CommonName, "Dev domain"); | ||
| let cert_key = KeyPair::generate().unwrap(); | ||
| let cert = params.signed_by(&cert_key, &ca).unwrap(); | ||
| let key = cert_key.serialize_der(); | ||
| verify_cert_ca(&cert.pem(), &key, &ca_cert.pem()); | ||
| } | ||
| #[test] | ||
| fn test_openssl_crl_parse() { | ||
| // Create a CRL with one revoked cert, and an issuer to sign the CRL. | ||
| let (crl_params, crl, issuer) = util::test_crl(); | ||
| let revoked_cert = crl_params.revoked_certs.first().unwrap(); | ||
| let revoked_cert_serial = &revoked_cert.serial_number; | ||
| // Serialize the CRL signed by the issuer in both PEM and DER. | ||
| let crl_pem = crl.pem().unwrap(); | ||
| // We should be able to parse the PEM form without error. | ||
| assert!(X509Crl::from_pem(crl_pem.as_bytes()).is_ok()); | ||
| // We should also be able to parse the DER form without error. | ||
| let openssl_crl = X509Crl::from_der(crl.der()).expect("failed to parse CRL DER"); | ||
| // The properties of the CRL should match expected. | ||
| let openssl_issuer = X509::from_der(issuer.der()).unwrap(); | ||
| // Asn1Time::from_unix takes i64 or i32 (depending on CPU architecture) | ||
| #[allow(clippy::useless_conversion)] | ||
| let expected_last_update = | ||
| Asn1Time::from_unix(crl_params.this_update.unix_timestamp().try_into().unwrap()).unwrap(); | ||
| assert!(openssl_crl.last_update().eq(&expected_last_update)); | ||
| // Asn1Time::from_unix takes i64 or i32 (depending on CPU architecture) | ||
| #[allow(clippy::useless_conversion)] | ||
| let expected_next_update = | ||
| Asn1Time::from_unix(crl_params.next_update.unix_timestamp().try_into().unwrap()).unwrap(); | ||
| assert!(openssl_crl.next_update().unwrap().eq(&expected_next_update)); | ||
| assert!(matches!( | ||
| openssl_crl | ||
| .issuer_name() | ||
| .try_cmp(openssl_issuer.issuer_name()) | ||
| .unwrap(), | ||
| core::cmp::Ordering::Equal | ||
| )); | ||
| // We should find the revoked certificate is revoked. | ||
| let openssl_serial = BigNum::from_slice(revoked_cert_serial.as_ref()).unwrap(); | ||
| let openssl_serial = Asn1Integer::from_bn(&openssl_serial).unwrap(); | ||
| let openssl_crl_status = openssl_crl.get_by_serial(&openssl_serial); | ||
| assert!(matches!(openssl_crl_status, CrlStatus::Revoked(_))); | ||
| // We should be able to verify the CRL signature with the issuer's public key. | ||
| let issuer_pkey = openssl_issuer.public_key().unwrap(); | ||
| assert!(openssl_crl | ||
| .verify(&issuer_pkey) | ||
| .expect("failed to verify CRL signature")); | ||
| } | ||
| #[test] | ||
| fn test_openssl_crl_dps_parse() { | ||
| // Generate and parse a certificate that includes two CRL distribution points. | ||
| let der = util::cert_with_crl_dps(); | ||
| let cert = X509::from_der(&der).expect("failed to parse cert DER"); | ||
| // We should find the CRL DPs extension. | ||
| let dps = cert | ||
| .crl_distribution_points() | ||
| .expect("missing crl distribution points extension"); | ||
| assert!(!dps.is_empty()); | ||
| // We should find two distribution points, each with a distribution point name containing | ||
| // a full name sequence of general names. | ||
| let general_names = dps | ||
| .iter() | ||
| .flat_map(|dp| { | ||
| dp.distpoint() | ||
| .expect("distribution point missing distribution point name") | ||
| .fullname() | ||
| .expect("distribution point name missing general names") | ||
| .iter() | ||
| }) | ||
| .collect::<Vec<_>>(); | ||
| // Each general name should be a URI name. | ||
| let uris = general_names | ||
| .iter() | ||
| .map(|general_name| { | ||
| general_name | ||
| .uri() | ||
| .expect("general name is not a directory name") | ||
| }) | ||
| .collect::<Vec<_>>(); | ||
| // We should find the expected URIs. | ||
| assert_eq!( | ||
| uris, | ||
| &[ | ||
| "http://example.com/crl.der", | ||
| "http://crls.example.com/1234", | ||
| "ldap://example.com/crl.der" | ||
| ] | ||
| ); | ||
| } | ||
| #[test] | ||
| #[cfg(all(feature = "crypto", feature = "aws_lc_rs"))] | ||
| fn test_openssl_pkcs1_and_sec1_keys() { | ||
| use openssl::ec::{EcGroup, EcKey}; | ||
| use openssl::nid::Nid; | ||
| use openssl::pkey::PKey; | ||
| use openssl::rsa::Rsa; | ||
| use pki_types::PrivateKeyDer; | ||
| let rsa = Rsa::generate(2048).unwrap(); | ||
| let rsa = PKey::from_rsa(rsa).unwrap(); | ||
| let pkcs1_rsa_key_der = PrivateKeyDer::try_from(rsa.private_key_to_der().unwrap()).unwrap(); | ||
| KeyPair::try_from(&pkcs1_rsa_key_der).unwrap(); | ||
| let pkcs8_rsa_key_der = PrivateKeyDer::try_from(rsa.private_key_to_pkcs8().unwrap()).unwrap(); | ||
| KeyPair::try_from(&pkcs8_rsa_key_der).unwrap(); | ||
| let group = EcGroup::from_curve_name(Nid::SECP521R1).unwrap(); | ||
| let ec_key = EcKey::generate(&group).unwrap(); | ||
| let ec_key = PKey::from_ec_key(ec_key).unwrap(); | ||
| let sec1_ec_key_der = PrivateKeyDer::try_from(ec_key.private_key_to_der().unwrap()).unwrap(); | ||
| KeyPair::try_from(&sec1_ec_key_der).unwrap(); | ||
| let pkcs8_ec_key_der = PrivateKeyDer::try_from(ec_key.private_key_to_pkcs8().unwrap()).unwrap(); | ||
| KeyPair::try_from(&pkcs8_ec_key_der).unwrap(); | ||
| } |
-145
| #![cfg(feature = "crypto")] | ||
| use time::{Duration, OffsetDateTime}; | ||
| use rcgen::{BasicConstraints, Certificate, CertificateParams, Issuer, KeyPair}; | ||
| use rcgen::{ | ||
| CertificateRevocationList, CrlDistributionPoint, CrlIssuingDistributionPoint, CrlScope, | ||
| }; | ||
| use rcgen::{CertificateRevocationListParams, DnType, IsCa, KeyIdMethod}; | ||
| use rcgen::{KeyUsagePurpose, RevocationReason, RevokedCertParams, SerialNumber}; | ||
| // Generated by adding `println!("{}", cert.serialize_private_key_pem());` | ||
| // to the test_webpki_25519 test and panicing explicitly. | ||
| // This is a "v2" key containing the public key as well as the | ||
| // private one. | ||
| #[allow(unused)] | ||
| pub const ED25519_TEST_KEY_PAIR_PEM_V2: &str = r#" | ||
| -----BEGIN PRIVATE KEY----- | ||
| MFMCAQEwBQYDK2VwBCIEIC2pHJYjFHhK8V7mj6BnHWUVMS4CRolUlDdRXKCtguDu | ||
| oSMDIQDrvH/x8Nx9untsuc6ET+ce3w7PSuLY8BLWcHdXDGvkQA== | ||
| -----END PRIVATE KEY----- | ||
| "#; | ||
| // Generated with `openssl genpkey -algorithm ED25519` | ||
| // A "v1" key as it doesn't contain the public key (which can be | ||
| // derived from the private one) | ||
| #[allow(unused)] | ||
| pub const ED25519_TEST_KEY_PAIR_PEM_V1: &str = r#" | ||
| -----BEGIN PRIVATE KEY----- | ||
| MC4CAQAwBQYDK2VwBCIEIDSat0MacDt2fokpnzuBaXvAQR6RJGS9rgIYOU2mZKld | ||
| -----END PRIVATE KEY----- | ||
| "#; | ||
| /* | ||
| Generated by: openssl genpkey -algorithm RSA \ | ||
| -pkeyopt rsa_keygen_bits:2048 \ | ||
| -pkeyopt rsa_keygen_pubexp:65537 | \ | ||
| openssl pkcs8 -topk8 -nocrypt -outform pem | ||
| */ | ||
| #[allow(dead_code)] // Used in some but not all test compilation units. | ||
| #[cfg(feature = "pem")] | ||
| pub const RSA_TEST_KEY_PAIR_PEM: &str = r#" | ||
| -----BEGIN PRIVATE KEY----- | ||
| MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDYjmgyV3/LSizJ | ||
| XrYrATZrrPr2Edo8yiOgBLFmi4sgeGdQ5n6nhjTGfBEIP2Ia6z+hbiGOMncabEBc | ||
| zkdME+JFYVCSkS7r4ivMOzp2egxLgcPKcerBoXI8DUbHhIR9z89lHiPHDJv3+d0A | ||
| c1b9bz9b8OAeZWiQmFvmjpbc2DfhQ2OFx2MwFZCYF196rrXOc6/SR2esZVRrkW22 | ||
| RBKFTgz6GIA5A/5VWKIISSqEB1gOcMz2iq5987I28+Ez4rcLZ2lB7cZ7TbNxkAwt | ||
| 0fPL+EuyP7XOzbIj4/kSAlU5xfwNERa3BEuOFro4i5EmSDj+lR5xdRpFnx0j5zOo | ||
| zUL2lHG9AgMBAAECggEARpV8DtSIOcmOeYAeXjwB8eyqy+Obv26fV/vPmr3m9glo | ||
| m2zVYWMT9pHft1F5d46v6b0MwN1gBsO74sP1Zy2f9b83VN5vbcEFR4cSkiVLtpyw | ||
| JV8mBkDKDBrDtCpUSPGgBrRhMvLAL35Ic2oks2w8OYp0clPZVi/i3G4jbA4pgIkt | ||
| yB6k79Uhzz2nfZ0VpPORGNsBOl5UK1LkmIhTJ6S0LsLj7XSet9YHR0k0F0/NOSzz | ||
| +jMUzfjOPm8M+b3wk9yAQP7qT9Iy3MHbGAad4gNXGu1LqeDRkfmM5pnoG0ASP3+B | ||
| IvX2l0ZLeCtg+GRLlGvUVI1HSQHCsuiC6/g2bq7JAQKBgQD3/Eb58VjpdwJYPrg/ | ||
| srfnC9sKSf5C0Q8YSmkfvOmeD6Vqe0EXRuMyhwTkkVdz04yPiB2j0fXdeB9h16Ic | ||
| 9HWb/UNGWNpV7Ul1MSHbeu32Xor+5IkqCGgSoMznlt9QPR4PxfIOgO8cVL1HgNAZ | ||
| JnBDzhTG0FfY75hqpCDmFGAZwQKBgQDfjhk5aM0yGLYgZfw/K9BrwjctQBWdrps2 | ||
| 4TtkG7Kuj0hsimCdrqJQ5JN8aUM41zDUr3Px1uN5gUAZ3dE9DoGsgj15ZwgVkAMM | ||
| E54bfzOqkbh+mRpptIxL4HmHB45vgvz0YljeRoOEQvPF/OSGLti7VIkD4898PFKl | ||
| cU+P9m5+/QKBgDi8XTi+AQuZEM5Duz/Hkc+opLqb5zI+RmfWTmrWe9SP29aa0G+U | ||
| 5lIfFf19SzbSxavpBm7+kHPVEcj+3rYlL+s6bHPhzEIwgcfwL8DZRSxCwSZD/yXA | ||
| up7Yb0jk+b6P3RravOCYmxwuPwfm7rVyV+kLczFxZUfauVJcrrI1Iy+BAoGBAJjG | ||
| MEDGeSxaLOS5LYgyNg3ePPzkhaEruRDpHUBNmW+npZPfgSVhObXUb2IfQXwvu0Qt | ||
| 3yuPcgcQKDFFIH/8UOwGWWKE4cZyk1KGeY9K/5D6Yr3JfX5tj08vSX3Y0SMtvhZ4 | ||
| u0izoZ8abiOIrtdwXlau76/D2ICLbON5Kykz/NE1AoGAId2+pO9p8jBt9l+5jZo7 | ||
| Rw/mb5icMaG2hqAzs37gUPbpSwQFOmGhQmNM+WvYEvUUuiTxI3AOeEK8Mj+BVB4+ | ||
| uE3X/fWK/JR9iOzH9OM31Nua8/EJzr7BmUpXeRr4dAtVimeQ+5HY6IgRsFGPKKwv | ||
| YPTHy8SWRA2sMII3ArhHJ8A= | ||
| -----END PRIVATE KEY----- | ||
| "#; | ||
| pub fn default_params() -> (CertificateParams, KeyPair) { | ||
| let mut params = | ||
| CertificateParams::new(vec!["crabs.crabs".to_string(), "localhost".to_string()]).unwrap(); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::OrganizationName, "Crab widgits SE"); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::CommonName, "Master CA"); | ||
| let key_pair = KeyPair::generate().unwrap(); | ||
| (params, key_pair) | ||
| } | ||
| #[allow(unused)] // Used by openssl + x509-parser features. | ||
| pub fn test_crl() -> ( | ||
| CertificateRevocationListParams, | ||
| CertificateRevocationList, | ||
| Certificate, | ||
| ) { | ||
| let (mut issuer, key_pair) = default_params(); | ||
| issuer.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); | ||
| issuer.key_usages = vec![ | ||
| KeyUsagePurpose::KeyCertSign, | ||
| KeyUsagePurpose::DigitalSignature, | ||
| KeyUsagePurpose::CrlSign, | ||
| ]; | ||
| let issuer_cert = issuer.self_signed(&key_pair).unwrap(); | ||
| let ca = Issuer::new(issuer, key_pair); | ||
| let now = OffsetDateTime::now_utc(); | ||
| let next_week = now + Duration::weeks(1); | ||
| let revoked_cert = RevokedCertParams { | ||
| serial_number: SerialNumber::from_slice(&[0x00, 0xC0, 0xFF, 0xEE]), | ||
| revocation_time: now, | ||
| reason_code: Some(RevocationReason::KeyCompromise), | ||
| invalidity_date: None, | ||
| }; | ||
| let params = CertificateRevocationListParams { | ||
| this_update: now, | ||
| next_update: next_week, | ||
| crl_number: SerialNumber::from(1234), | ||
| issuing_distribution_point: Some(CrlIssuingDistributionPoint { | ||
| distribution_point: CrlDistributionPoint { | ||
| uris: vec!["http://example.com/crl".to_string()], | ||
| }, | ||
| scope: Some(CrlScope::UserCertsOnly), | ||
| }), | ||
| revoked_certs: vec![revoked_cert], | ||
| key_identifier_method: KeyIdMethod::Sha256, | ||
| }; | ||
| let crl = params.signed_by(&ca).unwrap(); | ||
| (params, crl, issuer_cert) | ||
| } | ||
| #[allow(unused)] // Used by openssl + x509-parser features. | ||
| pub fn cert_with_crl_dps() -> Vec<u8> { | ||
| let (mut params, key_pair) = default_params(); | ||
| params.crl_distribution_points = vec![ | ||
| CrlDistributionPoint { | ||
| uris: vec![ | ||
| "http://example.com/crl.der".to_string(), | ||
| "http://crls.example.com/1234".to_string(), | ||
| ], | ||
| }, | ||
| CrlDistributionPoint { | ||
| uris: vec!["ldap://example.com/crl.der".to_string()], | ||
| }, | ||
| ]; | ||
| params.self_signed(&key_pair).unwrap().der().to_vec() | ||
| } |
-687
| #![cfg(feature = "crypto")] | ||
| use std::time::Duration as StdDuration; | ||
| use pki_types::{CertificateDer, ServerName, SignatureVerificationAlgorithm, UnixTime}; | ||
| use ring::rand::SystemRandom; | ||
| use ring::signature::{self, EcdsaKeyPair, EcdsaSigningAlgorithm, Ed25519KeyPair, KeyPair as _}; | ||
| #[cfg(feature = "pem")] | ||
| use ring::signature::{RsaEncoding, RsaKeyPair}; | ||
| use time::{Duration, OffsetDateTime}; | ||
| use webpki::{ | ||
| anchor_from_trusted_cert, BorrowedCertRevocationList, CertRevocationList, EndEntityCert, | ||
| KeyUsage, RevocationOptionsBuilder, | ||
| }; | ||
| use rcgen::{ | ||
| BasicConstraints, Certificate, CertificateParams, DnType, Error, IsCa, Issuer, KeyPair, | ||
| PublicKeyData, SigningKey, | ||
| }; | ||
| use rcgen::{CertificateRevocationListParams, RevocationReason, RevokedCertParams}; | ||
| #[cfg(feature = "x509-parser")] | ||
| use rcgen::{CertificateSigningRequestParams, DnValue}; | ||
| use rcgen::{ExtendedKeyUsagePurpose, KeyUsagePurpose, SerialNumber}; | ||
| mod util; | ||
| fn sign_msg_ecdsa(key_pair: &KeyPair, msg: &[u8], alg: &'static EcdsaSigningAlgorithm) -> Vec<u8> { | ||
| let pk_der = key_pair.serialize_der(); | ||
| let key_pair = | ||
| EcdsaKeyPair::from_pkcs8(alg, &pk_der, &ring::rand::SystemRandom::new()).unwrap(); | ||
| let system_random = SystemRandom::new(); | ||
| let signature = key_pair.sign(&system_random, msg).unwrap(); | ||
| signature.as_ref().to_vec() | ||
| } | ||
| fn sign_msg_ed25519(key_pair: &KeyPair, msg: &[u8]) -> Vec<u8> { | ||
| let pk_der = key_pair.serialize_der(); | ||
| let key_pair = Ed25519KeyPair::from_pkcs8_maybe_unchecked(&pk_der).unwrap(); | ||
| let signature = key_pair.sign(msg); | ||
| signature.as_ref().to_vec() | ||
| } | ||
| #[cfg(feature = "pem")] | ||
| fn sign_msg_rsa(key_pair: &KeyPair, msg: &[u8], encoding: &'static dyn RsaEncoding) -> Vec<u8> { | ||
| let pk_der = key_pair.serialize_der(); | ||
| let key_pair = RsaKeyPair::from_pkcs8(&pk_der).unwrap(); | ||
| let system_random = SystemRandom::new(); | ||
| let mut signature = vec![0; key_pair.public().modulus_len()]; | ||
| key_pair | ||
| .sign(encoding, &system_random, msg, &mut signature) | ||
| .unwrap(); | ||
| signature | ||
| } | ||
| fn check_cert<'a, 'b, S: SigningKey + 'a>( | ||
| cert_der: &CertificateDer<'_>, | ||
| cert: &'a Certificate, | ||
| cert_key: &'a S, | ||
| alg: &dyn SignatureVerificationAlgorithm, | ||
| sign_fn: impl FnOnce(&'a S, &'b [u8]) -> Vec<u8>, | ||
| ) { | ||
| #[cfg(feature = "pem")] | ||
| { | ||
| println!("{}", cert.pem()); | ||
| } | ||
| check_cert_ca(cert_der, cert_key, cert_der, alg, alg, sign_fn); | ||
| } | ||
| fn check_cert_ca<'a, 'b, S: SigningKey + 'a>( | ||
| cert_der: &CertificateDer<'_>, | ||
| cert_key: &'a S, | ||
| ca_der: &CertificateDer<'_>, | ||
| cert_alg: &dyn SignatureVerificationAlgorithm, | ||
| ca_alg: &dyn SignatureVerificationAlgorithm, | ||
| sign_fn: impl FnOnce(&'a S, &'b [u8]) -> Vec<u8>, | ||
| ) { | ||
| let trust_anchor = anchor_from_trusted_cert(ca_der).unwrap(); | ||
| let trust_anchor_list = &[trust_anchor]; | ||
| let end_entity_cert = EndEntityCert::try_from(cert_der).unwrap(); | ||
| // Set time to Jan 10, 2004 | ||
| let time = UnixTime::since_unix_epoch(StdDuration::from_secs(0x40_00_00_00)); | ||
| // (1/3) Check whether the cert is valid | ||
| end_entity_cert | ||
| .verify_for_usage( | ||
| &[cert_alg, ca_alg], | ||
| &trust_anchor_list[..], | ||
| &[], | ||
| time, | ||
| KeyUsage::server_auth(), | ||
| None, | ||
| None, | ||
| ) | ||
| .expect("valid TLS server cert"); | ||
| // (2/3) Check that the cert is valid for the given DNS name | ||
| let dns_name = ServerName::try_from("crabs.crabs").unwrap(); | ||
| end_entity_cert | ||
| .verify_is_valid_for_subject_name(&dns_name) | ||
| .expect("valid for DNS name"); | ||
| // (3/3) Check that a message signed by the cert is valid. | ||
| let msg = b"Hello, World! This message is signed."; | ||
| let signature = sign_fn(cert_key, msg); | ||
| end_entity_cert | ||
| .verify_signature(cert_alg, msg, &signature) | ||
| .expect("signature is valid"); | ||
| } | ||
| #[test] | ||
| fn test_webpki() { | ||
| let (params, key_pair) = util::default_params(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate. | ||
| let sign_fn = | ||
| |key_pair, msg| sign_msg_ecdsa(key_pair, msg, &signature::ECDSA_P256_SHA256_ASN1_SIGNING); | ||
| check_cert( | ||
| cert.der(), | ||
| &cert, | ||
| &key_pair, | ||
| webpki::ring::ECDSA_P256_SHA256, | ||
| sign_fn, | ||
| ); | ||
| } | ||
| #[test] | ||
| fn test_webpki_256() { | ||
| let (params, _) = util::default_params(); | ||
| let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate. | ||
| let sign_fn = |cert, msg| sign_msg_ecdsa(cert, msg, &signature::ECDSA_P256_SHA256_ASN1_SIGNING); | ||
| check_cert( | ||
| cert.der(), | ||
| &cert, | ||
| &key_pair, | ||
| webpki::ring::ECDSA_P256_SHA256, | ||
| sign_fn, | ||
| ); | ||
| } | ||
| #[test] | ||
| fn test_webpki_384() { | ||
| let (params, _) = util::default_params(); | ||
| let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P384_SHA384).unwrap(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate. | ||
| let sign_fn = |cert, msg| sign_msg_ecdsa(cert, msg, &signature::ECDSA_P384_SHA384_ASN1_SIGNING); | ||
| check_cert( | ||
| cert.der(), | ||
| &cert, | ||
| &key_pair, | ||
| webpki::ring::ECDSA_P384_SHA384, | ||
| sign_fn, | ||
| ); | ||
| } | ||
| #[test] | ||
| fn test_webpki_25519() { | ||
| let (params, _) = util::default_params(); | ||
| let key_pair = KeyPair::generate_for(&rcgen::PKCS_ED25519).unwrap(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate. | ||
| check_cert( | ||
| cert.der(), | ||
| &cert, | ||
| &key_pair, | ||
| webpki::ring::ED25519, | ||
| sign_msg_ed25519, | ||
| ); | ||
| } | ||
| #[cfg(feature = "pem")] | ||
| #[test] | ||
| fn test_webpki_25519_v1_given() { | ||
| let (params, _) = util::default_params(); | ||
| let key_pair = rcgen::KeyPair::from_pem(util::ED25519_TEST_KEY_PAIR_PEM_V1).unwrap(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate. | ||
| check_cert( | ||
| cert.der(), | ||
| &cert, | ||
| &key_pair, | ||
| webpki::ring::ED25519, | ||
| sign_msg_ed25519, | ||
| ); | ||
| } | ||
| #[cfg(feature = "pem")] | ||
| #[test] | ||
| fn test_webpki_25519_v2_given() { | ||
| let (params, _) = util::default_params(); | ||
| let key_pair = rcgen::KeyPair::from_pem(util::ED25519_TEST_KEY_PAIR_PEM_V2).unwrap(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate. | ||
| check_cert( | ||
| cert.der(), | ||
| &cert, | ||
| &key_pair, | ||
| webpki::ring::ED25519, | ||
| sign_msg_ed25519, | ||
| ); | ||
| } | ||
| #[cfg(feature = "pem")] | ||
| #[test] | ||
| fn test_webpki_rsa_given() { | ||
| let (params, _) = util::default_params(); | ||
| let key_pair = rcgen::KeyPair::from_pem(util::RSA_TEST_KEY_PAIR_PEM).unwrap(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate. | ||
| check_cert( | ||
| cert.der(), | ||
| &cert, | ||
| &key_pair, | ||
| webpki::ring::RSA_PKCS1_2048_8192_SHA256, | ||
| |msg, cert| sign_msg_rsa(msg, cert, &signature::RSA_PKCS1_SHA256), | ||
| ); | ||
| } | ||
| #[cfg(feature = "pem")] | ||
| #[test] | ||
| fn test_webpki_rsa_combinations_given() { | ||
| let configs: &[(_, _, &'static dyn signature::RsaEncoding)] = &[ | ||
| ( | ||
| &rcgen::PKCS_RSA_SHA256, | ||
| webpki::ring::RSA_PKCS1_2048_8192_SHA256, | ||
| &signature::RSA_PKCS1_SHA256, | ||
| ), | ||
| ( | ||
| &rcgen::PKCS_RSA_SHA384, | ||
| webpki::ring::RSA_PKCS1_2048_8192_SHA384, | ||
| &signature::RSA_PKCS1_SHA384, | ||
| ), | ||
| ( | ||
| &rcgen::PKCS_RSA_SHA512, | ||
| webpki::ring::RSA_PKCS1_2048_8192_SHA512, | ||
| &signature::RSA_PKCS1_SHA512, | ||
| ), | ||
| //(&rcgen::PKCS_RSA_PSS_SHA256, &webpki::RSA_PSS_2048_8192_SHA256_LEGACY_KEY, &signature::RSA_PSS_SHA256), | ||
| ]; | ||
| for c in configs { | ||
| let (params, _) = util::default_params(); | ||
| let key_pair = | ||
| rcgen::KeyPair::from_pkcs8_pem_and_sign_algo(util::RSA_TEST_KEY_PAIR_PEM, c.0).unwrap(); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate. | ||
| check_cert(cert.der(), &cert, &key_pair, c.1, |msg, cert| { | ||
| sign_msg_rsa(msg, cert, c.2) | ||
| }); | ||
| } | ||
| } | ||
| #[test] | ||
| fn test_webpki_separate_ca() { | ||
| let (mut ca_params, ca_key) = util::default_params(); | ||
| ca_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); | ||
| let ca_cert = ca_params.self_signed(&ca_key).unwrap(); | ||
| let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]).unwrap(); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::OrganizationName, "Crab widgits SE"); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::CommonName, "Dev domain"); | ||
| let key_pair = KeyPair::generate().unwrap(); | ||
| let ca = Issuer::new(ca_params, ca_key); | ||
| let cert = params.signed_by(&key_pair, &ca).unwrap(); | ||
| let sign_fn = |cert, msg| sign_msg_ecdsa(cert, msg, &signature::ECDSA_P256_SHA256_ASN1_SIGNING); | ||
| check_cert_ca( | ||
| cert.der(), | ||
| &key_pair, | ||
| ca_cert.der(), | ||
| webpki::ring::ECDSA_P256_SHA256, | ||
| webpki::ring::ECDSA_P256_SHA256, | ||
| sign_fn, | ||
| ); | ||
| } | ||
| #[test] | ||
| fn test_webpki_separate_ca_with_other_signing_alg() { | ||
| let (mut ca_params, _) = util::default_params(); | ||
| ca_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); | ||
| let ca_key = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap(); | ||
| let ca_cert = ca_params.self_signed(&ca_key).unwrap(); | ||
| let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]).unwrap(); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::OrganizationName, "Crab widgits SE"); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::CommonName, "Dev domain"); | ||
| let key_pair = KeyPair::generate_for(&rcgen::PKCS_ED25519).unwrap(); | ||
| let ca = Issuer::new(ca_params, ca_key); | ||
| let cert = params.signed_by(&key_pair, &ca).unwrap(); | ||
| check_cert_ca( | ||
| cert.der(), | ||
| &key_pair, | ||
| ca_cert.der(), | ||
| webpki::ring::ED25519, | ||
| webpki::ring::ECDSA_P256_SHA256, | ||
| sign_msg_ed25519, | ||
| ); | ||
| } | ||
| #[test] | ||
| fn from_remote() { | ||
| struct Remote(EcdsaKeyPair); | ||
| impl SigningKey for Remote { | ||
| fn sign(&self, msg: &[u8]) -> Result<Vec<u8>, rcgen::Error> { | ||
| let system_random = SystemRandom::new(); | ||
| self.0 | ||
| .sign(&system_random, msg) | ||
| .map(|s| s.as_ref().to_owned()) | ||
| .map_err(|_| Error::RingUnspecified) | ||
| } | ||
| } | ||
| impl PublicKeyData for Remote { | ||
| fn der_bytes(&self) -> &[u8] { | ||
| self.0.public_key().as_ref() | ||
| } | ||
| fn algorithm(&self) -> &'static rcgen::SignatureAlgorithm { | ||
| &rcgen::PKCS_ECDSA_P256_SHA256 | ||
| } | ||
| } | ||
| let rng = ring::rand::SystemRandom::new(); | ||
| let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap(); | ||
| let remote = EcdsaKeyPair::from_pkcs8( | ||
| &signature::ECDSA_P256_SHA256_ASN1_SIGNING, | ||
| &key_pair.serialize_der(), | ||
| &rng, | ||
| ) | ||
| .unwrap(); | ||
| let key_pair = EcdsaKeyPair::from_pkcs8( | ||
| &signature::ECDSA_P256_SHA256_ASN1_SIGNING, | ||
| &key_pair.serialize_der(), | ||
| &rng, | ||
| ) | ||
| .unwrap(); | ||
| let remote = Remote(remote); | ||
| let (params, _) = util::default_params(); | ||
| let cert = params.self_signed(&remote).unwrap(); | ||
| // Now verify the certificate. | ||
| let sign_fn = move |_, msg| { | ||
| let system_random = SystemRandom::new(); | ||
| let signature = key_pair.sign(&system_random, msg).unwrap(); | ||
| signature.as_ref().to_vec() | ||
| }; | ||
| check_cert( | ||
| cert.der(), | ||
| &cert, | ||
| &remote, | ||
| webpki::ring::ECDSA_P256_SHA256, | ||
| sign_fn, | ||
| ); | ||
| } | ||
| /* | ||
| // TODO https://github.com/briansmith/webpki/issues/134 | ||
| // TODO https://github.com/briansmith/webpki/issues/135 | ||
| #[test] | ||
| fn test_webpki_separate_ca_name_constraints() { | ||
| let mut params = util::default_params(); | ||
| params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); | ||
| params.name_constraints = Some(NameConstraints { | ||
| // TODO also add a test with non-empty permitted_subtrees that | ||
| // doesn't contain a DirectoryName entry. This isn't possible | ||
| // currently due to a limitation of webpki. | ||
| permitted_subtrees : vec![GeneralSubtree::DnsName("dev".to_string()), GeneralSubtree::DirectoryName(rcgen::DistinguishedName::new())], | ||
| //permitted_subtrees : vec![GeneralSubtree::DnsName("dev".to_string())], | ||
| //permitted_subtrees : Vec::new(), | ||
| //excluded_subtrees : vec![GeneralSubtree::DnsName("v".to_string())], | ||
| excluded_subtrees : Vec::new(), | ||
| }); | ||
| let ca_cert = Certificate::from_params(params).unwrap(); | ||
| println!("{}", ca_cert.serialize_pem().unwrap()); | ||
| let ca_der = ca_cert.serialize_der().unwrap(); | ||
| let mut params = CertificateParams::new(vec!["crabs.dev".to_string()]); | ||
| params.distinguished_name = rcgen::DistinguishedName::new(); | ||
| //params.distinguished_name.push(DnType::OrganizationName, "Crab widgits SE"); | ||
| //params.distinguished_name.push(DnType::CommonName, "Dev domain"); | ||
| let cert = Certificate::from_params(params).unwrap(); | ||
| let cert_der = cert.serialize_der_with_signer(&ca_cert).unwrap(); | ||
| println!("{}", cert.serialize_pem_with_signer(&ca_cert).unwrap()); | ||
| let sign_fn = |cert, msg| sign_msg_ecdsa(cert, msg, | ||
| &signature::ECDSA_P256_SHA256_ASN1_SIGNING); | ||
| check_cert_ca(&cert_der, &cert, &ca_der, | ||
| &webpki::ECDSA_P256_SHA256, &webpki::ECDSA_P256_SHA256, sign_fn); | ||
| } | ||
| */ | ||
| #[cfg(feature = "x509-parser")] | ||
| #[test] | ||
| fn test_webpki_imported_ca() { | ||
| let (mut params, ca_key) = util::default_params(); | ||
| params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); | ||
| params.key_usages.push(KeyUsagePurpose::KeyCertSign); | ||
| let ca_cert = params.self_signed(&ca_key).unwrap(); | ||
| let ca = Issuer::from_ca_cert_der(ca_cert.der(), ca_key).unwrap(); | ||
| assert_eq!(ca.key_usages(), &[KeyUsagePurpose::KeyCertSign]); | ||
| let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]).unwrap(); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::OrganizationName, "Crab widgits SE"); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::CommonName, "Dev domain"); | ||
| let cert_key = KeyPair::generate().unwrap(); | ||
| let cert = params.signed_by(&cert_key, &ca).unwrap(); | ||
| let sign_fn = |cert, msg| sign_msg_ecdsa(cert, msg, &signature::ECDSA_P256_SHA256_ASN1_SIGNING); | ||
| check_cert_ca( | ||
| cert.der(), | ||
| &cert_key, | ||
| ca_cert.der(), | ||
| webpki::ring::ECDSA_P256_SHA256, | ||
| webpki::ring::ECDSA_P256_SHA256, | ||
| sign_fn, | ||
| ); | ||
| } | ||
| #[cfg(feature = "x509-parser")] | ||
| #[test] | ||
| fn test_webpki_imported_ca_with_printable_string() { | ||
| let (mut params, ca_key) = util::default_params(); | ||
| params.distinguished_name.push( | ||
| DnType::CountryName, | ||
| DnValue::PrintableString("US".try_into().unwrap()), | ||
| ); | ||
| params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); | ||
| let ca_cert = params.self_signed(&ca_key).unwrap(); | ||
| let ca = Issuer::from_ca_cert_der(ca_cert.der(), ca_key).unwrap(); | ||
| let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]).unwrap(); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::OrganizationName, "Crab widgits SE"); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::CommonName, "Dev domain"); | ||
| let cert_key = KeyPair::generate().unwrap(); | ||
| let cert = params.signed_by(&cert_key, &ca).unwrap(); | ||
| let sign_fn = |cert, msg| sign_msg_ecdsa(cert, msg, &signature::ECDSA_P256_SHA256_ASN1_SIGNING); | ||
| check_cert_ca( | ||
| cert.der(), | ||
| &cert_key, | ||
| ca_cert.der(), | ||
| webpki::ring::ECDSA_P256_SHA256, | ||
| webpki::ring::ECDSA_P256_SHA256, | ||
| sign_fn, | ||
| ); | ||
| } | ||
| #[cfg(feature = "x509-parser")] | ||
| #[test] | ||
| fn test_certificate_from_csr() { | ||
| let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]).unwrap(); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::OrganizationName, "Crab widgits SE"); | ||
| params | ||
| .distinguished_name | ||
| .push(DnType::CommonName, "Dev domain"); | ||
| let eku_test = vec![ | ||
| ExtendedKeyUsagePurpose::Any, | ||
| ExtendedKeyUsagePurpose::ClientAuth, | ||
| ExtendedKeyUsagePurpose::CodeSigning, | ||
| ExtendedKeyUsagePurpose::EmailProtection, | ||
| ExtendedKeyUsagePurpose::OcspSigning, | ||
| ExtendedKeyUsagePurpose::ServerAuth, | ||
| ExtendedKeyUsagePurpose::TimeStamping, | ||
| ]; | ||
| for eku in &eku_test { | ||
| params.insert_extended_key_usage(eku.clone()); | ||
| } | ||
| let cert_key = KeyPair::generate().unwrap(); | ||
| let csr = params.serialize_request(&cert_key).unwrap(); | ||
| let csr = CertificateSigningRequestParams::from_der(csr.der()).unwrap(); | ||
| let ekus_contained = &csr.params.extended_key_usages; | ||
| for eku in &eku_test { | ||
| assert!(ekus_contained.contains(eku)); | ||
| } | ||
| let (mut ca_params, ca_key) = util::default_params(); | ||
| ca_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); | ||
| for eku in &eku_test { | ||
| ca_params.insert_extended_key_usage(eku.clone()); | ||
| } | ||
| let ekus_contained = &ca_params.extended_key_usages; | ||
| for eku in &eku_test { | ||
| assert!(ekus_contained.contains(eku)); | ||
| } | ||
| let ca_cert = ca_params.self_signed(&ca_key).unwrap(); | ||
| let ekus_contained = &ca_params.extended_key_usages; | ||
| for eku in &eku_test { | ||
| assert!(ekus_contained.contains(eku)); | ||
| } | ||
| let ekus = ca_params.extended_key_usages.clone(); | ||
| let ca = Issuer::new(ca_params, ca_key); | ||
| let cert = csr.signed_by(&ca).unwrap(); | ||
| let ekus_contained = &csr.params.extended_key_usages; | ||
| for eku in &eku_test { | ||
| assert!(ekus_contained.contains(eku)); | ||
| } | ||
| for eku in &eku_test { | ||
| assert!(ekus.contains(eku)); | ||
| } | ||
| let sign_fn = | ||
| |key_pair, msg| sign_msg_ecdsa(key_pair, msg, &signature::ECDSA_P256_SHA256_ASN1_SIGNING); | ||
| check_cert_ca( | ||
| cert.der(), | ||
| &cert_key, | ||
| ca_cert.der(), | ||
| webpki::ring::ECDSA_P256_SHA256, | ||
| webpki::ring::ECDSA_P256_SHA256, | ||
| sign_fn, | ||
| ); | ||
| } | ||
| #[test] | ||
| fn test_webpki_serial_number() { | ||
| let (mut params, key_pair) = util::default_params(); | ||
| params.serial_number = Some(vec![0, 1, 2].into()); | ||
| let cert = params.self_signed(&key_pair).unwrap(); | ||
| // Now verify the certificate. | ||
| let sign_fn = |cert, msg| sign_msg_ecdsa(cert, msg, &signature::ECDSA_P256_SHA256_ASN1_SIGNING); | ||
| check_cert( | ||
| cert.der(), | ||
| &cert, | ||
| &key_pair, | ||
| webpki::ring::ECDSA_P256_SHA256, | ||
| sign_fn, | ||
| ); | ||
| } | ||
| #[test] | ||
| fn test_webpki_crl_parse() { | ||
| // Create a CRL with one revoked cert, and an issuer to sign the CRL. | ||
| let (crl_params, crl, _) = util::test_crl(); | ||
| let revoked_cert = crl_params.revoked_certs.first().unwrap(); | ||
| // We should be able to parse the CRL DER without error. | ||
| let webpki_crl = CertRevocationList::from( | ||
| BorrowedCertRevocationList::from_der(crl.der()).expect("failed to parse CRL DER"), | ||
| ); | ||
| // We should be able to find the revoked cert with the expected properties. | ||
| let webpki_revoked_cert = webpki_crl | ||
| .find_serial(revoked_cert.serial_number.as_ref()) | ||
| .expect("failed to parse revoked certs in CRL") | ||
| .expect("failed to find expected revoked cert in CRL"); | ||
| assert_eq!( | ||
| webpki_revoked_cert.serial_number, | ||
| revoked_cert.serial_number.as_ref() | ||
| ); | ||
| assert_eq!( | ||
| webpki_revoked_cert.reason_code.unwrap() as u64, | ||
| revoked_cert.reason_code.unwrap() as u64 | ||
| ); | ||
| assert_eq!( | ||
| webpki_revoked_cert.revocation_date, | ||
| UnixTime::since_unix_epoch(StdDuration::from_secs( | ||
| revoked_cert.revocation_time.unix_timestamp() as u64 | ||
| )) | ||
| ); | ||
| } | ||
| #[test] | ||
| fn test_webpki_crl_revoke() { | ||
| // Create an issuer CA. | ||
| let alg = &rcgen::PKCS_ECDSA_P256_SHA256; | ||
| let (mut issuer, _) = util::default_params(); | ||
| issuer.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); | ||
| issuer.key_usages = vec![ | ||
| KeyUsagePurpose::KeyCertSign, | ||
| KeyUsagePurpose::DigitalSignature, | ||
| KeyUsagePurpose::CrlSign, | ||
| ]; | ||
| let issuer_key = KeyPair::generate_for(alg).unwrap(); | ||
| let issuer_cert = issuer.self_signed(&issuer_key).unwrap(); | ||
| // Create an end entity cert issued by the issuer. | ||
| let (mut ee, _) = util::default_params(); | ||
| ee.is_ca = IsCa::NoCa; | ||
| ee.extended_key_usages = vec![ExtendedKeyUsagePurpose::ClientAuth]; | ||
| ee.serial_number = Some(SerialNumber::from(99999)); | ||
| let ee_key = KeyPair::generate_for(alg).unwrap(); | ||
| let issuer = Issuer::new(issuer, issuer_key); | ||
| let ee_cert = ee.signed_by(&ee_key, &issuer).unwrap(); | ||
| // Set up webpki's verification requirements. | ||
| let trust_anchor = anchor_from_trusted_cert(issuer_cert.der()).unwrap(); | ||
| let trust_anchor_list = &[trust_anchor]; | ||
| let end_entity_cert = EndEntityCert::try_from(ee_cert.der()).unwrap(); | ||
| let unix_time = 0x40_00_00_00; | ||
| let time = UnixTime::since_unix_epoch(StdDuration::from_secs(unix_time)); | ||
| // The end entity cert should validate with the issuer without error. | ||
| end_entity_cert | ||
| .verify_for_usage( | ||
| &[webpki::ring::ECDSA_P256_SHA256], | ||
| &trust_anchor_list[..], | ||
| &[], | ||
| time, | ||
| KeyUsage::client_auth(), | ||
| None, | ||
| None, | ||
| ) | ||
| .expect("failed to validate ee cert with issuer"); | ||
| // Generate a CRL with the issuer that revokes the EE cert. | ||
| let now = OffsetDateTime::from_unix_timestamp(unix_time as i64).unwrap(); | ||
| let crl = CertificateRevocationListParams { | ||
| this_update: now, | ||
| next_update: now + Duration::weeks(1), | ||
| crl_number: rcgen::SerialNumber::from(1234), | ||
| issuing_distribution_point: None, | ||
| revoked_certs: vec![RevokedCertParams { | ||
| serial_number: ee.serial_number.clone().unwrap(), | ||
| revocation_time: now, | ||
| reason_code: Some(RevocationReason::KeyCompromise), | ||
| invalidity_date: None, | ||
| }], | ||
| key_identifier_method: rcgen::KeyIdMethod::Sha256, | ||
| } | ||
| .signed_by(&issuer) | ||
| .unwrap(); | ||
| let crl = CertRevocationList::from(BorrowedCertRevocationList::from_der(crl.der()).unwrap()); | ||
| // The end entity cert should **not** validate when we provide a CRL that revokes the EE cert. | ||
| let result = end_entity_cert.verify_for_usage( | ||
| &[webpki::ring::ECDSA_P256_SHA256], | ||
| &trust_anchor_list[..], | ||
| &[], | ||
| time, | ||
| KeyUsage::client_auth(), | ||
| Some(RevocationOptionsBuilder::new(&[&crl]).unwrap().build()), | ||
| None, | ||
| ); | ||
| assert!(matches!(result, Err(webpki::Error::CertRevoked))); | ||
| } | ||
| #[test] | ||
| fn test_webpki_cert_crl_dps() { | ||
| let der = util::cert_with_crl_dps(); | ||
| let cert = CertificateDer::from(der); | ||
| webpki::EndEntityCert::try_from(&cert).expect("failed to parse cert with CRL DPs ext"); | ||
| // Webpki doesn't expose the parsed CRL distribution extension, so we can't interrogate that | ||
| // it matches the expected form. See `openssl.rs` for more extensive coverage. | ||
| } |
Sorry, the diff of this file is not supported yet