diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 661d01f6..140a2397 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,21 @@ env: RUSTDOCFLAGS: -D warnings jobs: + rustfmt: + name: Format + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Install rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + - name: Check formatting + run: cargo fmt -- --check + clippy: name: Clippy runs-on: ubuntu-latest diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 00000000..56786628 --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,2 @@ +hard_tabs = true +match_block_trailing_comma = true diff --git a/examples/rsa-irc-openssl.rs b/examples/rsa-irc-openssl.rs index e4688d40..62d3518d 100644 --- a/examples/rsa-irc-openssl.rs +++ b/examples/rsa-irc-openssl.rs @@ -1,19 +1,18 @@ #![allow(clippy::complexity, clippy::style, clippy::pedantic)] -use rcgen::{Certificate, CertificateParams, - DistinguishedName, date_time_ymd}; -use std::fs; +use rcgen::{date_time_ymd, Certificate, CertificateParams, DistinguishedName}; use std::convert::TryInto; +use std::fs; fn main() -> Result<(), Box> { - let mut params :CertificateParams = Default::default(); + let mut params: CertificateParams = Default::default(); params.not_before = date_time_ymd(2021, 05, 19); params.not_after = date_time_ymd(4096, 01, 01); params.distinguished_name = DistinguishedName::new(); params.alg = &rcgen::PKCS_RSA_SHA256; - let pkey :openssl::pkey::PKey<_> = openssl::rsa::Rsa::generate(2048)?.try_into()?; + let pkey: openssl::pkey::PKey<_> = openssl::rsa::Rsa::generate(2048)?.try_into()?; let key_pair_pem = String::from_utf8(pkey.private_key_to_pem_pkcs8()?)?; let key_pair = rcgen::KeyPair::from_pem(&key_pair_pem)?; params.key_pair = Some(key_pair); @@ -23,16 +22,17 @@ fn main() -> Result<(), Box> { let pem = pem::parse(&pem_serialized)?; let der_serialized = pem.contents(); let hash = ring::digest::digest(&ring::digest::SHA512, &der_serialized); - let hash_hex :String = hash.as_ref().iter() - .map(|b| format!("{b:02x}")) - .collect(); + let hash_hex: String = hash.as_ref().iter().map(|b| format!("{b:02x}")).collect(); println!("sha-512 fingerprint: {hash_hex}"); println!("{pem_serialized}"); println!("{}", cert.serialize_private_key_pem()); std::fs::create_dir_all("certs/")?; fs::write("certs/cert.pem", &pem_serialized.as_bytes())?; fs::write("certs/cert.der", &der_serialized)?; - fs::write("certs/key.pem", &cert.serialize_private_key_pem().as_bytes())?; + fs::write( + "certs/key.pem", + &cert.serialize_private_key_pem().as_bytes(), + )?; fs::write("certs/key.der", &cert.serialize_private_key_der())?; Ok(()) } diff --git a/examples/rsa-irc.rs b/examples/rsa-irc.rs index 02f1fb37..d8525c90 100644 --- a/examples/rsa-irc.rs +++ b/examples/rsa-irc.rs @@ -1,16 +1,15 @@ #![allow(clippy::complexity, clippy::style, clippy::pedantic)] -use rsa::RsaPrivateKey; -use rsa::pkcs8::EncodePrivateKey; use rand::rngs::OsRng; +use rsa::pkcs8::EncodePrivateKey; +use rsa::RsaPrivateKey; -use rcgen::{Certificate, CertificateParams, - DistinguishedName, date_time_ymd}; -use std::fs; +use rcgen::{date_time_ymd, Certificate, CertificateParams, DistinguishedName}; use std::convert::TryFrom; +use std::fs; fn main() -> Result<(), Box> { - let mut params :CertificateParams = Default::default(); + let mut params: CertificateParams = Default::default(); params.not_before = date_time_ymd(2021, 05, 19); params.not_after = date_time_ymd(4096, 01, 01); params.distinguished_name = DistinguishedName::new(); @@ -18,7 +17,7 @@ fn main() -> Result<(), Box> { params.alg = &rcgen::PKCS_RSA_SHA256; let mut rng = OsRng; - let bits = 2048; + let bits = 2048; let private_key = RsaPrivateKey::new(&mut rng, bits)?; let private_key_der = private_key.to_pkcs8_der()?; let key_pair = rcgen::KeyPair::try_from(private_key_der.as_bytes()).unwrap(); @@ -29,16 +28,17 @@ fn main() -> Result<(), Box> { let pem = pem::parse(&pem_serialized)?; let der_serialized = pem.contents(); let hash = ring::digest::digest(&ring::digest::SHA512, &der_serialized); - let hash_hex :String = hash.as_ref().iter() - .map(|b| format!("{:02x}", b)) - .collect(); + let hash_hex: String = hash.as_ref().iter().map(|b| format!("{:02x}", b)).collect(); println!("sha-512 fingerprint: {hash_hex}"); println!("{pem_serialized}"); println!("{}", cert.serialize_private_key_pem()); std::fs::create_dir_all("certs/")?; fs::write("certs/cert.pem", &pem_serialized.as_bytes())?; fs::write("certs/cert.der", &der_serialized)?; - fs::write("certs/key.pem", &cert.serialize_private_key_pem().as_bytes())?; + fs::write( + "certs/key.pem", + &cert.serialize_private_key_pem().as_bytes(), + )?; fs::write("certs/key.der", &cert.serialize_private_key_der())?; Ok(()) } diff --git a/src/crl.rs b/src/crl.rs index 615468a4..d62bf084 100644 --- a/src/crl.rs +++ b/src/crl.rs @@ -1,13 +1,18 @@ -use yasna::Tag; #[cfg(feature = "pem")] use pem::Pem; -use yasna::DERWriter; use time::OffsetDateTime; +use yasna::DERWriter; +use yasna::Tag; -use crate::{write_dt_utc_or_generalized, write_x509_extension, write_x509_authority_key_identifier, write_distinguished_name}; use crate::oid::*; -use crate::{Certificate, RcgenError, SerialNumber, SignatureAlgorithm, KeyIdMethod, KeyUsagePurpose}; use crate::ENCODE_CONFIG; +use crate::{ + write_distinguished_name, write_dt_utc_or_generalized, write_x509_authority_key_identifier, + write_x509_extension, +}; +use crate::{ + Certificate, KeyIdMethod, KeyUsagePurpose, RcgenError, SerialNumber, SignatureAlgorithm, +}; /// A certificate revocation list (CRL) /// @@ -44,12 +49,12 @@ use crate::ENCODE_CONFIG; /// println!("{}", crl.serialize_pem_with_signer(&issuer).unwrap()); ///# } pub struct CertificateRevocationList { - params :CertificateRevocationListParams, + params: CertificateRevocationListParams, } impl CertificateRevocationList { /// Generates a new certificate revocation list (CRL) from the given parameters. - pub fn from_params(params :CertificateRevocationListParams) -> Result { + pub fn from_params(params: CertificateRevocationListParams) -> Result { if params.next_update.le(¶ms.this_update) { return Err(RcgenError::InvalidCrlNextUpdate); } @@ -61,8 +66,10 @@ impl CertificateRevocationList { } /// Serializes the certificate revocation list (CRL) in binary DER format, signed with /// the issuing certificate authority's key. - pub fn serialize_der_with_signer(&self, ca :&Certificate) -> Result, RcgenError> { - if !ca.params.key_usages.is_empty() && !ca.params.key_usages.contains(&KeyUsagePurpose::CrlSign) { + pub fn serialize_der_with_signer(&self, ca: &Certificate) -> Result, RcgenError> { + if !ca.params.key_usages.is_empty() + && !ca.params.key_usages.contains(&KeyUsagePurpose::CrlSign) + { return Err(RcgenError::IssuerNotCrlSigner); } self.params.serialize_der_with_signer(ca) @@ -72,7 +79,7 @@ impl CertificateRevocationList { /// /// *This function is only available if rcgen is built with the "pem" feature* #[cfg(feature = "pem")] - pub fn serialize_pem_with_signer(&self, ca :&Certificate) -> Result { + pub fn serialize_pem_with_signer(&self, ca: &Certificate) -> Result { let contents = self.serialize_der_with_signer(ca)?; let p = Pem::new("X509 CRL", contents); Ok(pem::encode_config(&p, ENCODE_CONFIG)) @@ -86,11 +93,11 @@ impl CertificateRevocationList { pub struct CrlDistributionPoint { /// One or more URI distribution point names, indicating a place the current CRL can /// be retrieved. When present, SHOULD include at least one LDAP or HTTP URI. - pub uris :Vec, + pub uris: Vec, } impl CrlDistributionPoint { - pub(crate) fn write_der(&self, writer :DERWriter) { + pub(crate) fn write_der(&self, writer: DERWriter) { // DistributionPoint SEQUENCE writer.write_sequence(|writer| { write_distribution_point_name_uris(writer.next(), &self.uris); @@ -98,22 +105,29 @@ impl CrlDistributionPoint { } } -fn write_distribution_point_name_uris<'a>(writer :DERWriter, uris: impl IntoIterator) { +fn write_distribution_point_name_uris<'a>( + writer: DERWriter, + uris: impl IntoIterator, +) { // distributionPoint DistributionPointName writer.write_tagged_implicit(Tag::context(0), |writer| { writer.write_sequence(|writer| { // fullName GeneralNames - writer.next().write_tagged_implicit(Tag::context(0), | writer| { - // GeneralNames - writer.write_sequence(|writer| { - for uri in uris.into_iter() { - // uniformResourceIdentifier [6] IA5String, - writer.next().write_tagged_implicit(Tag::context(6), |writer| { - writer.write_ia5_string(uri) - }); - } - }) - }); + writer + .next() + .write_tagged_implicit(Tag::context(0), |writer| { + // GeneralNames + writer.write_sequence(|writer| { + for uri in uris.into_iter() { + // uniformResourceIdentifier [6] IA5String, + writer + .next() + .write_tagged_implicit(Tag::context(6), |writer| { + writer.write_ia5_string(uri) + }); + } + }) + }); }); }); } @@ -141,28 +155,28 @@ pub enum RevocationReason { /// Parameters used for certificate revocation list (CRL) generation pub struct CertificateRevocationListParams { /// Issue date of the CRL. - pub this_update :OffsetDateTime, + pub this_update: OffsetDateTime, /// The date by which the next CRL will be issued. - pub next_update :OffsetDateTime, + pub next_update: OffsetDateTime, /// A monotonically increasing sequence number for a given CRL scope and issuer. - pub crl_number :SerialNumber, + pub crl_number: SerialNumber, /// An optional CRL extension identifying the CRL distribution point and scope for a /// particular CRL as described in RFC 5280 Section 5.2.5[^1]. /// /// [^1]: - pub issuing_distribution_point :Option, + pub issuing_distribution_point: Option, /// A list of zero or more parameters describing revoked certificates included in the CRL. - pub revoked_certs :Vec, + pub revoked_certs: Vec, /// Signature algorithm to use when signing the serialized CRL. - pub alg :&'static SignatureAlgorithm, + pub alg: &'static SignatureAlgorithm, /// Method to generate key identifiers from public keys /// /// Defaults to SHA-256. - pub key_identifier_method :KeyIdMethod, + pub key_identifier_method: KeyIdMethod, } impl CertificateRevocationListParams { - fn serialize_der_with_signer(&self, ca :&Certificate) -> Result, RcgenError> { + fn serialize_der_with_signer(&self, ca: &Certificate) -> Result, RcgenError> { yasna::try_construct_der(|writer| { // https://www.rfc-editor.org/rfc/rfc5280#section-5.1 writer.write_sequence(|writer| { @@ -184,7 +198,7 @@ impl CertificateRevocationListParams { }) }) } - fn write_crl(&self, writer :DERWriter, ca :&Certificate) -> Result<(), RcgenError> { + fn write_crl(&self, writer: DERWriter, ca: &Certificate) -> Result<(), RcgenError> { writer.write_sequence(|writer| { // Write CRL version. // RFC 5280 §5.1.2.1: @@ -251,9 +265,14 @@ impl CertificateRevocationListParams { // Write issuing distribution point (if present). if let Some(issuing_distribution_point) = &self.issuing_distribution_point { - write_x509_extension(writer.next(), OID_CRL_ISSUING_DISTRIBUTION_POINT, true, |writer| { - issuing_distribution_point.write_der(writer); - }); + write_x509_extension( + writer.next(), + OID_CRL_ISSUING_DISTRIBUTION_POINT, + true, + |writer| { + issuing_distribution_point.write_der(writer); + }, + ); } }); }); @@ -267,14 +286,14 @@ impl CertificateRevocationListParams { /// [issuing distribution point extension](https://datatracker.ietf.org/doc/html/rfc5280#section-5.2.5). pub struct CrlIssuingDistributionPoint { /// The CRL's distribution point, containing a sequence of URIs the CRL can be retrieved from. - pub distribution_point :CrlDistributionPoint, + pub distribution_point: CrlDistributionPoint, /// An optional description of the CRL's scope. If omitted, the CRL may contain /// both user certs and CA certs. - pub scope :Option, + pub scope: Option, } impl CrlIssuingDistributionPoint { - fn write_der(&self, writer :DERWriter) { + fn write_der(&self, writer: DERWriter) { // IssuingDistributionPoint SEQUENCE writer.write_sequence(|writer| { // distributionPoint [0] DistributionPointName OPTIONAL @@ -309,19 +328,19 @@ pub enum CrlScope { /// Parameters used for describing a revoked certificate included in a [`CertificateRevocationList`]. pub struct RevokedCertParams { /// Serial number identifying the revoked certificate. - pub serial_number :SerialNumber, + pub serial_number: SerialNumber, /// The date at which the CA processed the revocation. - pub revocation_time :OffsetDateTime, + pub revocation_time: OffsetDateTime, /// An optional reason code identifying why the certificate was revoked. - pub reason_code :Option, + pub reason_code: Option, /// An optional field describing the date on which it was known or suspected that the /// private key was compromised or the certificate otherwise became invalid. This date /// may be earlier than the [`RevokedCertParams::revocation_time`]. - pub invalidity_date :Option, + pub invalidity_date: Option, } impl RevokedCertParams { - fn write_der(&self, writer :DERWriter) { + fn write_der(&self, writer: DERWriter) { writer.write_sequence(|writer| { // Write serial number. // RFC 5280 §4.1.2.2: @@ -331,7 +350,9 @@ impl RevokedCertParams { // Note: Non-conforming CAs may issue certificates with serial numbers // that are negative or zero. Certificate users SHOULD be prepared to // gracefully handle such certificates. - writer.next().write_bigint_bytes(self.serial_number.as_ref(), true); + writer + .next() + .write_bigint_bytes(self.serial_number.as_ref(), true); // Write revocation date. write_dt_utc_or_generalized(writer.next(), self.revocation_time); @@ -342,7 +363,8 @@ impl RevokedCertParams { // optional for conforming CRL issuers and applications. However, CRL // issuers SHOULD include reason codes (Section 5.3.1) and invalidity // dates (Section 5.3.2) whenever this information is available. - let has_reason_code = matches!(self.reason_code, Some(reason) if reason != RevocationReason::Unspecified); + let has_reason_code = + matches!(self.reason_code, Some(reason) if reason != RevocationReason::Unspecified); let has_invalidity_date = self.invalidity_date.is_some(); if has_reason_code || has_invalidity_date { writer.next().write_sequence(|writer| { @@ -355,9 +377,14 @@ impl RevokedCertParams { // Write invalidity date if present. self.invalidity_date.map(|invalidity_date| { - write_x509_extension(writer.next(), OID_CRL_INVALIDITY_DATE, false, |writer| { - write_dt_utc_or_generalized(writer, invalidity_date); - }) + write_x509_extension( + writer.next(), + OID_CRL_INVALIDITY_DATE, + false, + |writer| { + write_dt_utc_or_generalized(writer, invalidity_date); + }, + ) }); }); } diff --git a/src/csr.rs b/src/csr.rs index 0dc368d3..243b174f 100644 --- a/src/csr.rs +++ b/src/csr.rs @@ -1,10 +1,10 @@ -#[cfg(feature = "pem")] -use pem::Pem; #[cfg(feature = "x509-parser")] use crate::{DistinguishedName, SanType}; +#[cfg(feature = "pem")] +use pem::Pem; use std::hash::Hash; -use crate::{Certificate, CertificateParams, SignatureAlgorithm, PublicKeyData, RcgenError}; +use crate::{Certificate, CertificateParams, PublicKeyData, RcgenError, SignatureAlgorithm}; /// A public key, extracted from a CSR #[derive(Debug, PartialEq, Eq, Hash)] @@ -26,8 +26,8 @@ impl PublicKeyData for PublicKey { /// Data for a certificate signing request #[allow(missing_docs)] pub struct CertificateSigningRequest { - pub params :CertificateParams, - pub public_key :PublicKey, + pub params: CertificateParams, + pub public_key: PublicKey, } impl CertificateSigningRequest { @@ -35,9 +35,8 @@ impl CertificateSigningRequest { /// /// See [`from_der`](Self::from_der) for more details. #[cfg(all(feature = "pem", feature = "x509-parser"))] - pub fn from_pem(pem_str :&str) -> Result { - let csr = pem::parse(pem_str) - .or(Err(RcgenError::CouldNotParseCertificationRequest))?; + pub fn from_pem(pem_str: &str) -> Result { + let csr = pem::parse(pem_str).or(Err(RcgenError::CouldNotParseCertificationRequest))?; Self::from_der(csr.contents()) } @@ -46,12 +45,17 @@ impl CertificateSigningRequest { /// Currently, this only supports the `Subject Alternative Name` extension. /// On encountering other extensions, this function will return an error. #[cfg(feature = "x509-parser")] - pub fn from_der(csr :&[u8]) -> Result { + pub fn from_der(csr: &[u8]) -> Result { use x509_parser::prelude::FromDer; let csr = x509_parser::certification_request::X509CertificationRequest::from_der(csr) - .map_err(|_| RcgenError::CouldNotParseCertificationRequest)?.1; - csr.verify_signature().map_err(|_| RcgenError::RingUnspecified)?; - let alg_oid = csr.signature_algorithm.algorithm.iter() + .map_err(|_| RcgenError::CouldNotParseCertificationRequest)? + .1; + csr.verify_signature() + .map_err(|_| RcgenError::RingUnspecified)?; + let alg_oid = csr + .signature_algorithm + .algorithm + .iter() .ok_or(RcgenError::CouldNotParseCertificationRequest)? .collect::>(); let alg = SignatureAlgorithm::from_oid(&alg_oid)?; @@ -67,9 +71,11 @@ impl CertificateSigningRequest { match ext { x509_parser::extensions::ParsedExtension::SubjectAlternativeName(san) => { for name in &san.general_names { - params.subject_alt_names.push(SanType::try_from_general(name)?); + params + .subject_alt_names + .push(SanType::try_from_general(name)?); } - } + }, _ => return Err(RcgenError::UnsupportedExtension), } } @@ -87,15 +93,17 @@ impl CertificateSigningRequest { }) } /// Serializes the requested certificate, signed with another certificate's key, in binary DER format - pub fn serialize_der_with_signer(&self, ca :&Certificate) -> Result, RcgenError> { + pub fn serialize_der_with_signer(&self, ca: &Certificate) -> Result, RcgenError> { self.params.serialize_der_with_signer(&self.public_key, ca) } /// Serializes the requested certificate, signed with another certificate's key, to the ASCII PEM format /// /// *This function is only available if rcgen is built with the "pem" feature* #[cfg(feature = "pem")] - pub fn serialize_pem_with_signer(&self, ca :&Certificate) -> Result { - let contents = self.params.serialize_der_with_signer(&self.public_key, ca)?; + pub fn serialize_pem_with_signer(&self, ca: &Certificate) -> Result { + let contents = self + .params + .serialize_der_with_signer(&self.public_key, ca)?; let p = Pem::new("CERTIFICATE", contents); Ok(pem::encode_config(&p, crate::ENCODE_CONFIG)) } diff --git a/src/error.rs b/src/error.rs index 3fd50b4f..8623c919 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,5 @@ -use std::fmt; use std::error::Error; +use std::fmt; #[derive(Debug, PartialEq, Eq)] #[non_exhaustive] @@ -49,26 +49,40 @@ pub enum RcgenError { } impl fmt::Display for RcgenError { - fn fmt(&self, f :&mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::RcgenError::*; match self { CouldNotParseCertificate => write!(f, "Could not parse certificate")?, - CouldNotParseCertificationRequest => write!(f, "Could not parse certificate signing \ - request")?, + CouldNotParseCertificationRequest => write!( + f, + "Could not parse certificate signing \ + request" + )?, CouldNotParseKeyPair => write!(f, "Could not parse key pair")?, #[cfg(feature = "x509-parser")] InvalidNameType => write!(f, "Invalid subject alternative name type")?, - InvalidIpAddressOctetLength(actual) => write!(f, "Invalid IP address octet length of {actual} bytes")?, - KeyGenerationUnavailable => write!(f, "There is no support for generating \ - keys for the given algorithm")?, - UnsupportedSignatureAlgorithm => write!(f, "The requested signature algorithm \ - is not supported")?, + InvalidIpAddressOctetLength(actual) => { + write!(f, "Invalid IP address octet length of {actual} bytes")? + }, + KeyGenerationUnavailable => write!( + f, + "There is no support for generating \ + keys for the given algorithm" + )?, + UnsupportedSignatureAlgorithm => write!( + f, + "The requested signature algorithm \ + is not supported" + )?, #[cfg(feature = "x509-parser")] UnsupportedExtension => write!(f, "Unsupported extension requested in CSR")?, RingUnspecified => write!(f, "Unspecified ring error")?, RingKeyRejected(e) => write!(f, "Key rejected by ring: {}", e)?, - CertificateKeyPairMismatch => write!(f, "The provided certificate's signature \ - algorithm is incompatible with the given key pair")?, + CertificateKeyPairMismatch => write!( + f, + "The provided certificate's signature \ + algorithm is incompatible with the given key pair" + )?, Time => write!(f, "Time error")?, RemoteKeyError => write!(f, "Remote key error")?, @@ -76,7 +90,10 @@ impl fmt::Display for RcgenError { PemError(e) => write!(f, "PEM error: {}", e)?, UnsupportedInCsr => write!(f, "Certificate parameter unsupported in CSR")?, InvalidCrlNextUpdate => write!(f, "Invalid CRL next update parameter")?, - IssuerNotCrlSigner => write!(f, "CRL issuer must specify no key usage, or key usage including cRLSign")?, + IssuerNotCrlSigner => write!( + f, + "CRL issuer must specify no key usage, or key usage including cRLSign" + )?, }; Ok(()) } @@ -85,20 +102,20 @@ impl fmt::Display for RcgenError { impl Error for RcgenError {} impl From for RcgenError { - fn from(_unspecified :ring::error::Unspecified) -> Self { + fn from(_unspecified: ring::error::Unspecified) -> Self { RcgenError::RingUnspecified } } impl From for RcgenError { - fn from(err :ring::error::KeyRejected) -> Self { + fn from(err: ring::error::KeyRejected) -> Self { RcgenError::RingKeyRejected(err.description_()) } } #[cfg(feature = "pem")] impl From for RcgenError { - fn from(e :pem::PemError) -> Self { + fn from(e: pem::PemError) -> Self { RcgenError::PemError(e) } } diff --git a/src/key_pair.rs b/src/key_pair.rs index 1a3bc670..86443c3a 100644 --- a/src/key_pair.rs +++ b/src/key_pair.rs @@ -1,15 +1,15 @@ #[cfg(feature = "pem")] use pem::Pem; -use ring::signature::{self, EcdsaKeyPair, Ed25519KeyPair, RsaKeyPair, RsaEncoding}; use ring::rand::SystemRandom; use ring::signature::KeyPair as RingKeyPair; -use yasna::DERWriter; -use std::fmt; +use ring::signature::{self, EcdsaKeyPair, Ed25519KeyPair, RsaEncoding, RsaKeyPair}; use std::convert::TryFrom; +use std::fmt; +use yasna::DERWriter; -use crate::{ENCODE_CONFIG, RcgenError, SignatureAlgorithm}; -use crate::sign_algo::SignAlgo; use crate::sign_algo::algo::*; +use crate::sign_algo::SignAlgo; +use crate::{RcgenError, SignatureAlgorithm, ENCODE_CONFIG}; /// A key pair vairant #[allow(clippy::large_enum_variant)] @@ -25,7 +25,7 @@ pub(crate) enum KeyPairKind { } impl fmt::Debug for KeyPairKind { - fn fmt(&self, f :&mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::Ec(key_pair) => write!(f, "{:?}", key_pair), Self::Ed(key_pair) => write!(f, "{:?}", key_pair), @@ -44,38 +44,37 @@ impl fmt::Debug for KeyPairKind { /// and conversion between the formats. #[derive(Debug)] pub struct KeyPair { - pub(crate) kind :KeyPairKind, - pub(crate) alg :&'static SignatureAlgorithm, - pub(crate) serialized_der :Vec, + pub(crate) kind: KeyPairKind, + pub(crate) alg: &'static SignatureAlgorithm, + pub(crate) serialized_der: Vec, } impl KeyPair { /// Parses the key pair from the DER format /// /// Equivalent to using the [`TryFrom`] implementation. - pub fn from_der(der :&[u8]) -> Result { + pub fn from_der(der: &[u8]) -> Result { Ok(der.try_into()?) } /// Parses the key pair from the ASCII PEM format /// /// *This constructor is only available if rcgen is built with the "pem" feature* #[cfg(feature = "pem")] - pub fn from_pem(pem_str :&str) -> Result { + pub fn from_pem(pem_str: &str) -> Result { let private_key = pem::parse(pem_str)?; - let private_key_der :&[_] = private_key.contents(); + let private_key_der: &[_] = private_key.contents(); Ok(private_key_der.try_into()?) } /// Obtains the key pair from a raw public key and a remote private key - pub fn from_remote(key_pair :Box) -> Result { + pub fn from_remote(key_pair: Box) -> Result { Ok(Self { - alg : key_pair.algorithm(), - kind : KeyPairKind::Remote(key_pair), - serialized_der : Vec::new(), + alg: key_pair.algorithm(), + kind: KeyPairKind::Remote(key_pair), + serialized_der: Vec::new(), }) } - /// Obtains the key pair from a DER formatted key /// using the specified [`SignatureAlgorithm`](SignatureAlgorithm) /// @@ -83,9 +82,12 @@ impl KeyPair { /// /// *This constructor is only available if rcgen is built with the "pem" feature* #[cfg(feature = "pem")] - pub fn from_pem_and_sign_algo(pem_str :&str, alg :&'static SignatureAlgorithm) -> Result { + pub fn from_pem_and_sign_algo( + pem_str: &str, + alg: &'static SignatureAlgorithm, + ) -> Result { let private_key = pem::parse(pem_str)?; - let private_key_der :&[_] = private_key.contents(); + let private_key_der: &[_] = private_key.contents(); Ok(Self::from_der_and_sign_algo(private_key_der, alg)?) } @@ -98,15 +100,24 @@ impl KeyPair { /// key pair. However sometimes multiple signature algorithms fit for the /// same der key. In that instance, you can use this function to precisely /// specify the `SignatureAlgorithm`. - pub fn from_der_and_sign_algo(pkcs8 :&[u8], alg :&'static SignatureAlgorithm) -> Result { + pub fn from_der_and_sign_algo( + pkcs8: &[u8], + alg: &'static SignatureAlgorithm, + ) -> Result { let pkcs8_vec = pkcs8.to_vec(); let kind = if alg == &PKCS_ED25519 { KeyPairKind::Ed(Ed25519KeyPair::from_pkcs8_maybe_unchecked(pkcs8)?) } else if alg == &PKCS_ECDSA_P256_SHA256 { - KeyPairKind::Ec(EcdsaKeyPair::from_pkcs8(&signature::ECDSA_P256_SHA256_ASN1_SIGNING, pkcs8)?) + KeyPairKind::Ec(EcdsaKeyPair::from_pkcs8( + &signature::ECDSA_P256_SHA256_ASN1_SIGNING, + pkcs8, + )?) } else if alg == &PKCS_ECDSA_P384_SHA384 { - KeyPairKind::Ec(EcdsaKeyPair::from_pkcs8(&signature::ECDSA_P384_SHA384_ASN1_SIGNING, pkcs8)?) + KeyPairKind::Ec(EcdsaKeyPair::from_pkcs8( + &signature::ECDSA_P384_SHA384_ASN1_SIGNING, + pkcs8, + )?) } else if alg == &PKCS_RSA_SHA256 { let rsakp = RsaKeyPair::from_pkcs8(pkcs8)?; KeyPairKind::Rsa(rsakp, &signature::RSA_PKCS1_SHA256) @@ -126,19 +137,28 @@ impl KeyPair { Ok(KeyPair { kind, alg, - serialized_der : pkcs8_vec, + serialized_der: pkcs8_vec, }) } - pub(crate) fn from_raw(pkcs8: &[u8]) -> Result<(KeyPairKind, &'static SignatureAlgorithm), RcgenError> { + pub(crate) fn from_raw( + pkcs8: &[u8], + ) -> Result<(KeyPairKind, &'static SignatureAlgorithm), RcgenError> { let (kind, alg) = if let Ok(edkp) = Ed25519KeyPair::from_pkcs8_maybe_unchecked(pkcs8) { (KeyPairKind::Ed(edkp), &PKCS_ED25519) - } else if let Ok(eckp) = EcdsaKeyPair::from_pkcs8(&signature::ECDSA_P256_SHA256_ASN1_SIGNING, pkcs8) { + } else if let Ok(eckp) = + EcdsaKeyPair::from_pkcs8(&signature::ECDSA_P256_SHA256_ASN1_SIGNING, pkcs8) + { (KeyPairKind::Ec(eckp), &PKCS_ECDSA_P256_SHA256) - } else if let Ok(eckp) = EcdsaKeyPair::from_pkcs8(&signature::ECDSA_P384_SHA384_ASN1_SIGNING, pkcs8) { + } else if let Ok(eckp) = + EcdsaKeyPair::from_pkcs8(&signature::ECDSA_P384_SHA384_ASN1_SIGNING, pkcs8) + { (KeyPairKind::Ec(eckp), &PKCS_ECDSA_P384_SHA384) } else if let Ok(rsakp) = RsaKeyPair::from_pkcs8(pkcs8) { - (KeyPairKind::Rsa(rsakp, &signature::RSA_PKCS1_SHA256), &PKCS_RSA_SHA256) + ( + KeyPairKind::Rsa(rsakp, &signature::RSA_PKCS1_SHA256), + &PKCS_RSA_SHA256, + ) } else { return Err(RcgenError::CouldNotParseKeyPair); }; @@ -155,7 +175,7 @@ pub trait RemoteKeyPair { fn public_key(&self) -> &[u8]; /// Signs `msg` using the selected algorithm - fn sign(&self, msg :&[u8]) -> Result, RcgenError>; + fn sign(&self, msg: &[u8]) -> Result, RcgenError>; /// Reveals which algorithm will be used when you call `sign()` fn algorithm(&self) -> &'static SignatureAlgorithm; @@ -189,18 +209,19 @@ impl TryFrom> for KeyPair { impl KeyPair { /// Generate a new random key pair for the specified signature algorithm - pub fn generate(alg :&'static SignatureAlgorithm) -> Result { + pub fn generate(alg: &'static SignatureAlgorithm) -> Result { let system_random = SystemRandom::new(); match alg.sign_alg { SignAlgo::EcDsa(sign_alg) => { let key_pair_doc = EcdsaKeyPair::generate_pkcs8(sign_alg, &system_random)?; let key_pair_serialized = key_pair_doc.as_ref().to_vec(); - let key_pair = EcdsaKeyPair::from_pkcs8(&sign_alg, &&key_pair_doc.as_ref()).unwrap(); + let key_pair = + EcdsaKeyPair::from_pkcs8(&sign_alg, &&key_pair_doc.as_ref()).unwrap(); Ok(KeyPair { - kind : KeyPairKind::Ec(key_pair), + kind: KeyPairKind::Ec(key_pair), alg, - serialized_der : key_pair_serialized, + serialized_der: key_pair_serialized, }) }, SignAlgo::EdDsa(_sign_alg) => { @@ -209,9 +230,9 @@ impl KeyPair { let key_pair = Ed25519KeyPair::from_pkcs8(&&key_pair_doc.as_ref()).unwrap(); Ok(KeyPair { - kind : KeyPairKind::Ed(key_pair), + kind: KeyPairKind::Ed(key_pair), alg, - serialized_der : key_pair_serialized, + serialized_der: key_pair_serialized, }) }, // Ring doesn't have RSA key generation yet: @@ -229,16 +250,15 @@ impl KeyPair { self.raw_bytes() } /// Check if this key pair can be used with the given signature algorithm - pub fn is_compatible(&self, signature_algorithm :&SignatureAlgorithm) -> bool { + pub fn is_compatible(&self, signature_algorithm: &SignatureAlgorithm) -> bool { self.alg == signature_algorithm } /// Returns (possibly multiple) compatible [`SignatureAlgorithm`]'s /// that the key can be used with - pub fn compatible_algs(&self) - -> impl Iterator { + pub fn compatible_algs(&self) -> impl Iterator { std::iter::once(self.alg) } - pub(crate) fn sign(&self, msg :&[u8], writer :DERWriter) -> Result<(), RcgenError> { + pub(crate) fn sign(&self, msg: &[u8], writer: DERWriter) -> Result<(), RcgenError> { match &self.kind { KeyPairKind::Ec(kp) => { let system_random = SystemRandom::new(); @@ -254,8 +274,7 @@ impl KeyPair { KeyPairKind::Rsa(kp, padding_alg) => { let system_random = SystemRandom::new(); let mut signature = vec![0; kp.public_modulus_len()]; - kp.sign(*padding_alg, &system_random, - msg, &mut signature)?; + kp.sign(*padding_alg, &system_random, msg, &mut signature)?; let sig = &signature.as_ref(); writer.write_bitvec_bytes(&sig, &sig.len() * 8); }, @@ -345,7 +364,7 @@ impl PublicKeyData for KeyPair { pub(crate) trait PublicKeyData { fn alg(&self) -> &SignatureAlgorithm; fn raw_bytes(&self) -> &[u8]; - fn serialize_public_key_der(&self, writer :DERWriter) { + fn serialize_public_key_der(&self, writer: DERWriter) { writer.write_sequence(|writer| { self.alg().write_oids_sign_alg(writer.next()); let pk = self.raw_bytes(); diff --git a/src/lib.rs b/src/lib.rs index df94d90a..7742e1c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,40 +30,40 @@ println!("{}", cert.serialize_private_key_pem()); #![deny(missing_docs)] #![allow(clippy::complexity, clippy::style, clippy::pedantic)] -use yasna::Tag; -use yasna::models::ObjectIdentifier; #[cfg(feature = "pem")] use pem::Pem; use ring::digest; -use yasna::DERWriter; -use yasna::models::{GeneralizedTime, UTCTime}; -use yasna::tags::{TAG_BMPSTRING, TAG_TELETEXSTRING, TAG_UNIVERSALSTRING}; -use time::{Date, Month, OffsetDateTime, PrimitiveDateTime, Time}; use std::collections::HashMap; -use std::fmt; use std::convert::TryFrom; +use std::fmt; +use std::hash::Hash; use std::net::IpAddr; #[cfg(feature = "x509-parser")] use std::net::{Ipv4Addr, Ipv6Addr}; use std::str::FromStr; -use std::hash::Hash; +use time::{Date, Month, OffsetDateTime, PrimitiveDateTime, Time}; +use yasna::models::ObjectIdentifier; +use yasna::models::{GeneralizedTime, UTCTime}; +use yasna::tags::{TAG_BMPSTRING, TAG_TELETEXSTRING, TAG_UNIVERSALSTRING}; +use yasna::DERWriter; +use yasna::Tag; +pub use crate::crl::{ + CertificateRevocationList, CertificateRevocationListParams, CrlDistributionPoint, + CrlIssuingDistributionPoint, CrlScope, RevocationReason, RevokedCertParams, +}; +pub use crate::csr::{CertificateSigningRequest, PublicKey}; pub use crate::error::RcgenError; -use crate::oid::*; use crate::key_pair::PublicKeyData; pub use crate::key_pair::{KeyPair, RemoteKeyPair}; -pub use crate::csr::{PublicKey, CertificateSigningRequest}; -pub use crate::crl::{ - CertificateRevocationList, CrlDistributionPoint, CertificateRevocationListParams, - CrlIssuingDistributionPoint, CrlScope, RevocationReason, RevokedCertParams -}; -pub use crate::sign_algo::SignatureAlgorithm; +use crate::oid::*; pub use crate::sign_algo::algo::*; +pub use crate::sign_algo::SignatureAlgorithm; /// A self signed certificate together with signing keys pub struct Certificate { - params :CertificateParams, - key_pair :KeyPair, + params: CertificateParams, + key_pair: KeyPair, } /** @@ -90,7 +90,9 @@ println!("{}", cert.serialize_private_key_pem()); # } ``` */ -pub fn generate_simple_self_signed(subject_alt_names :impl Into>) -> Result { +pub fn generate_simple_self_signed( + subject_alt_names: impl Into>, +) -> Result { Certificate::from_params(CertificateParams::new(subject_alt_names)) } @@ -140,20 +142,18 @@ fn ip_addr_from_octets(octets: &[u8]) -> Result { impl SanType { #[cfg(feature = "x509-parser")] - fn try_from_general(name :&x509_parser::extensions::GeneralName<'_>) -> Result { + fn try_from_general( + name: &x509_parser::extensions::GeneralName<'_>, + ) -> Result { Ok(match name { x509_parser::extensions::GeneralName::RFC822Name(name) => { SanType::Rfc822Name((*name).into()) - } - x509_parser::extensions::GeneralName::DNSName(name) => { - SanType::DnsName((*name).into()) - } - x509_parser::extensions::GeneralName::URI(name) => { - SanType::URI((*name).into()) - } + }, + x509_parser::extensions::GeneralName::DNSName(name) => SanType::DnsName((*name).into()), + x509_parser::extensions::GeneralName::URI(name) => SanType::URI((*name).into()), x509_parser::extensions::GeneralName::IPAddress(octets) => { SanType::IpAddress(ip_addr_from_octets(octets)?) - } + }, _ => return Err(RcgenError::InvalidNameType), }) } @@ -161,10 +161,10 @@ impl SanType { fn tag(&self) -> u64 { // Defined in the GeneralName list in // https://tools.ietf.org/html/rfc5280#page-38 - const TAG_RFC822_NAME :u64 = 1; - const TAG_DNS_NAME :u64 = 2; - const TAG_URI :u64 = 6; - const TAG_IP_ADDRESS :u64 = 7; + const TAG_RFC822_NAME: u64 = 1; + const TAG_DNS_NAME: u64 = 2; + const TAG_URI: u64 = 6; + const TAG_IP_ADDRESS: u64 = 7; match self { SanType::Rfc822Name(_name) => TAG_RFC822_NAME, @@ -195,10 +195,10 @@ impl GeneralSubtree { fn tag(&self) -> u64 { // Defined in the GeneralName list in // https://tools.ietf.org/html/rfc5280#page-38 - const TAG_RFC822_NAME :u64 = 1; - const TAG_DNS_NAME :u64 = 2; - const TAG_DIRECTORY_NAME :u64 = 4; - const TAG_IP_ADDRESS :u64 = 7; + const TAG_RFC822_NAME: u64 = 1; + const TAG_DNS_NAME: u64 = 2; + const TAG_DIRECTORY_NAME: u64 = 4; + const TAG_IP_ADDRESS: u64 = 7; match self { GeneralSubtree::Rfc822Name(_name) => TAG_RFC822_NAME, @@ -243,7 +243,7 @@ impl CidrSubnet { /// let subnet = CidrSubnet::from_str("192.0.2.0/24").unwrap(); /// assert_eq!(subnet, CidrSubnet::V4([0xC0, 0x00, 0x02, 0x00], [0xFF, 0xFF, 0xFF, 0x00])); /// ``` - pub fn from_str(s :&str) -> Result { + pub fn from_str(s: &str) -> Result { let mut iter = s.split('/'); if let (Some(addr_s), Some(prefix_s)) = (iter.next(), iter.next()) { let addr = IpAddr::from_str(addr_s).map_err(|_| ())?; @@ -266,24 +266,20 @@ impl CidrSubnet { /// let subnet = CidrSubnet::from_addr_prefix(addr, 24); /// assert_eq!(subnet, CidrSubnet::V4([0xC0, 0x00, 0x02, 0x00], [0xFF, 0xFF, 0xFF, 0x00])); /// ``` - pub fn from_addr_prefix(addr :IpAddr, prefix :u8) -> Self { + pub fn from_addr_prefix(addr: IpAddr, prefix: u8) -> Self { match addr { - IpAddr::V4(addr) => { - Self::from_v4_prefix(addr.octets(), prefix) - }, - IpAddr::V6(addr) => { - Self::from_v6_prefix(addr.octets(), prefix) - }, + IpAddr::V4(addr) => Self::from_v4_prefix(addr.octets(), prefix), + IpAddr::V6(addr) => Self::from_v6_prefix(addr.octets(), prefix), } } /// Obtains the CidrSubnet from an IPv4 address in network byte order /// as well as the specified prefix. - pub fn from_v4_prefix(addr :[u8; 4], prefix :u8) -> Self { + pub fn from_v4_prefix(addr: [u8; 4], prefix: u8) -> Self { CidrSubnet::V4(addr, mask!(u32, prefix)) } /// Obtains the CidrSubnet from an IPv6 address in network byte order /// as well as the specified prefix. - pub fn from_v6_prefix(addr :[u8; 16], prefix :u8) -> Self { + pub fn from_v6_prefix(addr: [u8; 16], prefix: u8) -> Self { CidrSubnet::V6(addr, mask!(u128, prefix)) } fn to_bytes(&self) -> Vec { @@ -337,7 +333,7 @@ impl DnType { } /// Generate a DnType for the provided OID - pub fn from_oid(slice :&[u64]) -> Self { + pub fn from_oid(slice: &[u64]) -> Self { match slice { OID_COUNTRY_NAME => DnType::CountryName, OID_LOCALITY_NAME => DnType::LocalityName, @@ -345,7 +341,7 @@ impl DnType { OID_ORG_NAME => DnType::OrganizationName, OID_ORG_UNIT_NAME => DnType::OrganizationalUnitName, OID_COMMON_NAME => DnType::CommonName, - oid => DnType::CustomDnType(oid.into()) + oid => DnType::CustomDnType(oid.into()), } } } @@ -368,9 +364,9 @@ pub enum DnValue { impl From for DnValue where - T :Into + T: Into, { - fn from(t :T) -> Self { + fn from(t: T) -> Self { DnValue::Utf8String(t.into()) } } @@ -387,20 +383,20 @@ See also the RFC 5280 sections on the [issuer](https://tools.ietf.org/html/rfc52 and [subject](https://tools.ietf.org/html/rfc5280#section-4.1.2.6) fields. */ pub struct DistinguishedName { - entries :HashMap, - order :Vec, + entries: HashMap, + order: Vec, } impl DistinguishedName { /// Creates a new, empty distinguished name pub fn new() -> Self { Self { - entries : HashMap::new(), - order : Vec::new(), + entries: HashMap::new(), + order: Vec::new(), } } /// Obtains the attribute value for the given attribute type - pub fn get(&self, ty :&DnType) -> Option<&DnValue> { + pub fn get(&self, ty: &DnType) -> Option<&DnValue> { self.entries.get(ty) } /// Removes the attribute with the specified DnType @@ -408,7 +404,7 @@ impl DistinguishedName { /// Returns true when an actual removal happened, false /// when no attribute with the specified DnType was /// found. - pub fn remove(&mut self, ty :DnType) -> bool { + pub fn remove(&mut self, ty: DnType) -> bool { let removed = self.entries.remove(&ty).is_some(); if removed { self.order.retain(|ty_o| &ty != ty_o); @@ -425,7 +421,7 @@ impl DistinguishedName { /// assert_eq!(dn.get(&DnType::OrganizationName), Some(&DnValue::Utf8String("Crab widgits SE".to_string()))); /// assert_eq!(dn.get(&DnType::CommonName), Some(&DnValue::PrintableString("Master Cert".to_string()))); /// ``` - pub fn push(&mut self, ty :DnType, s :impl Into) { + pub fn push(&mut self, ty: DnType, s: impl Into) { if !self.entries.contains_key(&ty) { self.order.push(ty.clone()); } @@ -434,13 +430,13 @@ impl DistinguishedName { /// Iterate over the entries pub fn iter(&self) -> DistinguishedNameIterator<'_> { DistinguishedNameIterator { - distinguished_name :self, - iter :self.order.iter() + distinguished_name: self, + iter: self.order.iter(), } } #[cfg(feature = "x509-parser")] - fn from_name(name :&x509_parser::x509::X509Name) -> Result { + fn from_name(name: &x509_parser::x509::X509Name) -> Result { use x509_parser::der_parser::asn1_rs::Tag; let mut dn = DistinguishedName::new(); @@ -458,7 +454,9 @@ impl DistinguishedName { panic!("x509-parser distinguished name set is empty"); }; - let attr_type_oid = attr.attr_type().iter() + let attr_type_oid = attr + .attr_type() + .iter() .ok_or(RcgenError::CouldNotParseCertificate)?; let dn_type = DnType::from_oid(&attr_type_oid.collect::>()); let data = attr.attr_value().data; @@ -489,18 +487,17 @@ impl DistinguishedName { Iterator over [`DistinguishedName`] entries */ pub struct DistinguishedNameIterator<'a> { - distinguished_name :&'a DistinguishedName, - iter :std::slice::Iter<'a, DnType>, + distinguished_name: &'a DistinguishedName, + iter: std::slice::Iter<'a, DnType>, } -impl <'a> Iterator for DistinguishedNameIterator<'a> { +impl<'a> Iterator for DistinguishedNameIterator<'a> { type Item = (&'a DnType, &'a DnValue); fn next(&mut self) -> Option { - self.iter.next() - .and_then(|ty| { - self.distinguished_name.entries.get(ty).map(|v| (ty, v)) - }) + self.iter + .next() + .and_then(|ty| self.distinguished_name.entries.get(ty).map(|v| (ty, v))) } } @@ -508,31 +505,31 @@ impl <'a> Iterator for DistinguishedNameIterator<'a> { #[allow(missing_docs)] #[non_exhaustive] pub struct CertificateParams { - pub alg :&'static SignatureAlgorithm, - pub not_before :OffsetDateTime, - pub not_after :OffsetDateTime, - pub serial_number :Option, - pub subject_alt_names :Vec, - pub distinguished_name :DistinguishedName, - pub is_ca :IsCa, - pub key_usages :Vec, - pub extended_key_usages :Vec, - pub name_constraints :Option, + pub alg: &'static SignatureAlgorithm, + pub not_before: OffsetDateTime, + pub not_after: OffsetDateTime, + pub serial_number: Option, + pub subject_alt_names: Vec, + pub distinguished_name: DistinguishedName, + pub is_ca: IsCa, + pub key_usages: Vec, + pub extended_key_usages: Vec, + pub name_constraints: Option, /// An optional list of certificate revocation list (CRL) distribution points as described /// in RFC 5280 Section 4.2.1.13[^1]. Each distribution point contains one or more URIs where /// an up-to-date CRL with scope including this certificate can be retrieved. /// /// [^1]: - pub crl_distribution_points :Vec, - pub custom_extensions :Vec, + pub crl_distribution_points: Vec, + pub custom_extensions: Vec, /// The certificate's key pair, a new random key pair will be generated if this is `None` - pub key_pair :Option, + pub key_pair: Option, /// If `true`, the 'Authority Key Identifier' extension will be added to the generated cert - pub use_authority_key_identifier_extension :bool, + pub use_authority_key_identifier_extension: bool, /// Method to generate key identifiers from public keys /// /// Defaults to SHA-256. - pub key_identifier_method :KeyIdMethod, + pub key_identifier_method: KeyIdMethod, } impl Default for CertificateParams { @@ -543,21 +540,21 @@ impl Default for CertificateParams { let mut distinguished_name = DistinguishedName::new(); distinguished_name.push(DnType::CommonName, "rcgen self signed cert"); CertificateParams { - alg : &PKCS_ECDSA_P256_SHA256, + alg: &PKCS_ECDSA_P256_SHA256, not_before, not_after, - serial_number : None, - subject_alt_names : Vec::new(), + serial_number: None, + subject_alt_names: Vec::new(), distinguished_name, - is_ca : IsCa::NoCa, - key_usages : Vec::new(), - extended_key_usages : Vec::new(), - name_constraints : None, - crl_distribution_points : Vec::new(), - custom_extensions : Vec::new(), - key_pair : None, - use_authority_key_identifier_extension : false, - key_identifier_method : KeyIdMethod::Sha256, + is_ca: IsCa::NoCa, + key_usages: Vec::new(), + extended_key_usages: Vec::new(), + name_constraints: None, + crl_distribution_points: Vec::new(), + custom_extensions: Vec::new(), + key_pair: None, + use_authority_key_identifier_extension: false, + key_identifier_method: KeyIdMethod::Sha256, } } } @@ -569,9 +566,8 @@ impl CertificateParams { /// /// *This constructor is only available if rcgen is built with the "pem" and "x509-parser" features* #[cfg(all(feature = "pem", feature = "x509-parser"))] - pub fn from_ca_cert_pem(pem_str :&str, key_pair :KeyPair) -> Result { - let certificate = pem::parse(pem_str) - .or(Err(RcgenError::CouldNotParseCertificate))?; + pub fn from_ca_cert_pem(pem_str: &str, key_pair: KeyPair) -> Result { + let certificate = pem::parse(pem_str).or(Err(RcgenError::CouldNotParseCertificate))?; Self::from_ca_cert_der(certificate.contents(), key_pair) } @@ -591,11 +587,14 @@ impl CertificateParams { /// /// *This constructor is only available if rcgen is built with the "x509-parser" feature* #[cfg(feature = "x509-parser")] - pub fn from_ca_cert_der(ca_cert :&[u8], key_pair :KeyPair) -> Result { + pub fn from_ca_cert_der(ca_cert: &[u8], key_pair: KeyPair) -> Result { let (_remainder, x509) = x509_parser::parse_x509_certificate(ca_cert) .or(Err(RcgenError::CouldNotParseCertificate))?; - let alg_oid = x509.signature_algorithm.algorithm.iter() + let alg_oid = x509 + .signature_algorithm + .algorithm + .iter() .ok_or(RcgenError::CouldNotParseCertificate)?; let alg = SignatureAlgorithm::from_oid(&alg_oid.collect::>())?; @@ -608,34 +607,45 @@ impl CertificateParams { let name_constraints = Self::convert_x509_name_constraints(&x509)?; let serial_number = Some(x509.serial.to_bytes_be().into()); - Ok( - CertificateParams { - alg, - is_ca, - subject_alt_names, - key_usages, - extended_key_usages, - name_constraints, - serial_number, - distinguished_name : dn, - key_pair : Some(key_pair), - not_before : validity.not_before.to_datetime(), - not_after : validity.not_after.to_datetime(), - .. Default::default() - } - ) + Ok(CertificateParams { + alg, + is_ca, + subject_alt_names, + key_usages, + extended_key_usages, + name_constraints, + serial_number, + distinguished_name: dn, + key_pair: Some(key_pair), + not_before: validity.not_before.to_datetime(), + not_after: validity.not_after.to_datetime(), + ..Default::default() + }) } #[cfg(feature = "x509-parser")] - fn convert_x509_is_ca(x509 :&x509_parser::certificate::X509Certificate<'_>) -> Result { + fn convert_x509_is_ca( + x509: &x509_parser::certificate::X509Certificate<'_>, + ) -> Result { use x509_parser::extensions::BasicConstraints as B; - let basic_constraints = x509.basic_constraints() - .or(Err(RcgenError::CouldNotParseCertificate))?.map(|ext| ext.value); + let basic_constraints = x509 + .basic_constraints() + .or(Err(RcgenError::CouldNotParseCertificate))? + .map(|ext| ext.value); let is_ca = match basic_constraints { - Some(B { ca: true, path_len_constraint: Some(n) }) if *n <= u8::MAX as u32 => IsCa::Ca(BasicConstraints::Constrained(*n as u8)), - Some(B { ca: true, path_len_constraint: Some(_) }) => return Err(RcgenError::CouldNotParseCertificate), - Some(B { ca: true, path_len_constraint: None }) => IsCa::Ca(BasicConstraints::Unconstrained), + Some(B { + ca: true, + path_len_constraint: Some(n), + }) if *n <= u8::MAX as u32 => IsCa::Ca(BasicConstraints::Constrained(*n as u8)), + Some(B { + ca: true, + path_len_constraint: Some(_), + }) => return Err(RcgenError::CouldNotParseCertificate), + Some(B { + ca: true, + path_len_constraint: None, + }) => IsCa::Ca(BasicConstraints::Unconstrained), Some(B { ca: false, .. }) => IsCa::ExplicitNoCa, None => IsCa::NoCa, }; @@ -643,8 +653,11 @@ impl CertificateParams { Ok(is_ca) } #[cfg(feature = "x509-parser")] - fn convert_x509_subject_alternative_name(x509 :&x509_parser::certificate::X509Certificate<'_>) -> Result, RcgenError> { - let sans = x509.subject_alternative_name() + fn convert_x509_subject_alternative_name( + x509: &x509_parser::certificate::X509Certificate<'_>, + ) -> Result, RcgenError> { + let sans = x509 + .subject_alternative_name() .or(Err(RcgenError::CouldNotParseCertificate))? .map(|ext| &ext.value.general_names); @@ -659,8 +672,11 @@ impl CertificateParams { } } #[cfg(feature = "x509-parser")] - fn convert_x509_key_usages(x509 :&x509_parser::certificate::X509Certificate<'_>) -> Result, RcgenError> { - let key_usage = x509.key_usage() + fn convert_x509_key_usages( + x509: &x509_parser::certificate::X509Certificate<'_>, + ) -> Result, RcgenError> { + let key_usage = x509 + .key_usage() .or(Err(RcgenError::CouldNotParseCertificate))? .map(|ext| ext.value); @@ -697,8 +713,11 @@ impl CertificateParams { Ok(key_usages) } #[cfg(feature = "x509-parser")] - fn convert_x509_extended_key_usages(x509 :&x509_parser::certificate::X509Certificate<'_>) -> Result, RcgenError> { - let extended_key_usage = x509.extended_key_usage() + fn convert_x509_extended_key_usages( + x509: &x509_parser::certificate::X509Certificate<'_>, + ) -> Result, RcgenError> { + let extended_key_usage = x509 + .extended_key_usage() .or(Err(RcgenError::CouldNotParseCertificate))? .map(|ext| ext.value); @@ -729,8 +748,11 @@ impl CertificateParams { Ok(extended_key_usages) } #[cfg(feature = "x509-parser")] - fn convert_x509_name_constraints(x509 :&x509_parser::certificate::X509Certificate<'_>) -> Result, RcgenError> { - let constraints = x509.name_constraints() + fn convert_x509_name_constraints( + x509: &x509_parser::certificate::X509Certificate<'_>, + ) -> Result, RcgenError> { + let constraints = x509 + .name_constraints() .or(Err(RcgenError::CouldNotParseCertificate))? .map(|ext| ext.value); @@ -747,7 +769,10 @@ impl CertificateParams { Vec::new() }; - let name_constraints = NameConstraints { permitted_subtrees, excluded_subtrees }; + let name_constraints = NameConstraints { + permitted_subtrees, + excluded_subtrees, + }; Ok(Some(name_constraints)) } else { @@ -755,7 +780,9 @@ impl CertificateParams { } } #[cfg(feature = "x509-parser")] - fn convert_x509_general_subtrees(subtrees :&[x509_parser::extensions::GeneralSubtree<'_>]) -> Result, RcgenError> { + fn convert_x509_general_subtrees( + subtrees: &[x509_parser::extensions::GeneralSubtree<'_>], + ) -> Result, RcgenError> { use x509_parser::extensions::GeneralName; let mut result = Vec::new(); @@ -763,42 +790,52 @@ impl CertificateParams { let subtree = match &subtree.base { GeneralName::RFC822Name(s) => GeneralSubtree::Rfc822Name(s.to_string()), GeneralName::DNSName(s) => GeneralSubtree::DnsName(s.to_string()), - GeneralName::DirectoryName(n) => GeneralSubtree::DirectoryName(DistinguishedName::from_name(&n)?), + GeneralName::DirectoryName(n) => { + GeneralSubtree::DirectoryName(DistinguishedName::from_name(&n)?) + }, GeneralName::IPAddress(bytes) if bytes.len() == 8 => { let addr: [u8; 4] = bytes[..4].try_into().unwrap(); let mask: [u8; 4] = bytes[4..].try_into().unwrap(); GeneralSubtree::IpAddress(CidrSubnet::V4(addr, mask)) - } + }, GeneralName::IPAddress(bytes) if bytes.len() == 32 => { let addr: [u8; 16] = bytes[..16].try_into().unwrap(); let mask: [u8; 16] = bytes[16..].try_into().unwrap(); GeneralSubtree::IpAddress(CidrSubnet::V6(addr, mask)) - } + }, _ => continue, }; result.push(subtree); } Ok(result) } - fn write_subject_alt_names(&self, writer :DERWriter) { + fn write_subject_alt_names(&self, writer: DERWriter) { write_x509_extension(writer, OID_SUBJECT_ALT_NAME, false, |writer| { writer.write_sequence(|writer| { for san in self.subject_alt_names.iter() { - writer.next().write_tagged_implicit(Tag::context(san.tag()), |writer| { - match san { - SanType::Rfc822Name(name) | - SanType::DnsName(name) | - SanType::URI(name) => writer.write_ia5_string(name), - SanType::IpAddress(IpAddr::V4(addr)) => writer.write_bytes(&addr.octets()), - SanType::IpAddress(IpAddr::V6(addr)) => writer.write_bytes(&addr.octets()), - } - }); + writer.next().write_tagged_implicit( + Tag::context(san.tag()), + |writer| match san { + SanType::Rfc822Name(name) + | SanType::DnsName(name) + | SanType::URI(name) => writer.write_ia5_string(name), + SanType::IpAddress(IpAddr::V4(addr)) => { + writer.write_bytes(&addr.octets()) + }, + SanType::IpAddress(IpAddr::V6(addr)) => { + writer.write_bytes(&addr.octets()) + }, + }, + ); } }); }); } - fn write_request(&self, pub_key: &K, writer :DERWriter) - -> Result<(), RcgenError> { + fn write_request( + &self, + pub_key: &K, + writer: DERWriter, + ) -> Result<(), RcgenError> { // No .. pattern, we use this to ensure every field is used #[deny(unused)] let Self { @@ -845,17 +882,25 @@ impl CertificateParams { writer.next().write_sequence(|writer| { writer.next().write_oid(&ty.to_oid()); match content { - DnValue::TeletexString(s) => writer.next().write_tagged_implicit(TAG_TELETEXSTRING, |writer| { - writer.write_bytes(s) - }), - DnValue::PrintableString(s) => writer.next().write_printable_string(s), - DnValue::UniversalString(s) => writer.next().write_tagged_implicit(TAG_UNIVERSALSTRING, |writer| { - writer.write_bytes(s) - }), + DnValue::TeletexString(s) => writer + .next() + .write_tagged_implicit(TAG_TELETEXSTRING, |writer| { + writer.write_bytes(s) + }), + DnValue::PrintableString(s) => { + writer.next().write_printable_string(s) + }, + DnValue::UniversalString(s) => writer + .next() + .write_tagged_implicit(TAG_UNIVERSALSTRING, |writer| { + writer.write_bytes(s) + }), DnValue::Utf8String(s) => writer.next().write_utf8_string(s), - DnValue::BmpString(s) => writer.next().write_tagged_implicit(TAG_BMPSTRING, |writer| { - writer.write_bytes(s) - }), + DnValue::BmpString(s) => writer + .next() + .write_tagged_implicit(TAG_BMPSTRING, |writer| { + writer.write_bytes(s) + }), } }); }); @@ -894,11 +939,15 @@ impl CertificateParams { }); } }); - }); Ok(()) } - fn write_cert(&self, writer :DERWriter, pub_key: &K, ca :&Certificate) -> Result<(), RcgenError> { + fn write_cert( + &self, + writer: DERWriter, + pub_key: &K, + ca: &Certificate, + ) -> Result<(), RcgenError> { writer.write_sequence(|writer| { // Write version writer.next().write_tagged(Tag::context(0), |writer| { @@ -930,13 +979,13 @@ impl CertificateParams { // Write subjectPublicKeyInfo pub_key.serialize_public_key_der(writer.next()); // write extensions - let should_write_exts = self.use_authority_key_identifier_extension || - !self.subject_alt_names.is_empty() || - !self.extended_key_usages.is_empty() || - self.name_constraints.iter().any(|c| !c.is_empty()) || - matches!(self.is_ca, IsCa::ExplicitNoCa) || - matches!(self.is_ca, IsCa::Ca(_)) || - !self.custom_extensions.is_empty(); + let should_write_exts = self.use_authority_key_identifier_extension + || !self.subject_alt_names.is_empty() + || !self.extended_key_usages.is_empty() + || self.name_constraints.iter().any(|c| !c.is_empty()) + || matches!(self.is_ca, IsCa::ExplicitNoCa) + || matches!(self.is_ca, IsCa::Ca(_)) + || !self.custom_extensions.is_empty(); if should_write_exts { writer.next().write_tagged(Tag::context(3), |writer| { writer.write_sequence(|writer| { @@ -951,12 +1000,11 @@ impl CertificateParams { // Write standard key usage if !self.key_usages.is_empty() { writer.next().write_sequence(|writer| { - let oid = ObjectIdentifier::from_slice(OID_KEY_USAGE); writer.next().write_oid(&oid); writer.next().write_bool(true); - let mut bits :u16 = 0; + let mut bits: u16 = 0; for entry in self.key_usages.iter() { // Map the index to a value @@ -977,11 +1025,7 @@ impl CertificateParams { // Compute the 1-based most significant bit let msb = 16 - bits.leading_zeros(); - let nb = if msb <= 8 { - 1 - } else { - 2 - }; + let nb = if msb <= 8 { 1 } else { 2 }; let bits = bits.reverse_bits().to_be_bytes(); @@ -994,76 +1038,121 @@ impl CertificateParams { // Write them writer.next().write_bytes(&der); - }); } // Write extended key usage if !self.extended_key_usages.is_empty() { - write_x509_extension(writer.next(), OID_EXT_KEY_USAGE, false, |writer| { - writer.write_sequence(|writer| { - for usage in self.extended_key_usages.iter() { - let oid = ObjectIdentifier::from_slice(usage.oid()); - writer.next().write_oid(&oid); - } - }); - }); + write_x509_extension( + writer.next(), + OID_EXT_KEY_USAGE, + false, + |writer| { + writer.write_sequence(|writer| { + for usage in self.extended_key_usages.iter() { + let oid = ObjectIdentifier::from_slice(usage.oid()); + writer.next().write_oid(&oid); + } + }); + }, + ); } if let Some(name_constraints) = &self.name_constraints { // If both trees are empty, the extension must be omitted. if !name_constraints.is_empty() { - write_x509_extension(writer.next(), OID_NAME_CONSTRAINTS, true, |writer| { - writer.write_sequence(|writer| { - if !name_constraints.permitted_subtrees.is_empty() { - write_general_subtrees(writer.next(), 0, &name_constraints.permitted_subtrees); - } - if !name_constraints.excluded_subtrees.is_empty() { - write_general_subtrees(writer.next(), 1, &name_constraints.excluded_subtrees); - } - }); - }); + write_x509_extension( + writer.next(), + OID_NAME_CONSTRAINTS, + true, + |writer| { + writer.write_sequence(|writer| { + if !name_constraints.permitted_subtrees.is_empty() { + write_general_subtrees( + writer.next(), + 0, + &name_constraints.permitted_subtrees, + ); + } + if !name_constraints.excluded_subtrees.is_empty() { + write_general_subtrees( + writer.next(), + 1, + &name_constraints.excluded_subtrees, + ); + } + }); + }, + ); } } if !self.crl_distribution_points.is_empty() { - write_x509_extension(writer.next(), OID_CRL_DISTRIBUTION_POINTS, false, |writer| { - writer.write_sequence(|writer| { - for distribution_point in &self.crl_distribution_points { - distribution_point.write_der(writer.next()); - } - }) - }); + write_x509_extension( + writer.next(), + OID_CRL_DISTRIBUTION_POINTS, + false, + |writer| { + writer.write_sequence(|writer| { + for distribution_point in &self.crl_distribution_points { + distribution_point.write_der(writer.next()); + } + }) + }, + ); } match self.is_ca { IsCa::Ca(ref constraint) => { // Write subject_key_identifier - write_x509_extension(writer.next(), OID_SUBJECT_KEY_IDENTIFIER, false, |writer| { - let key_identifier = self.key_identifier(pub_key); - writer.write_bytes(key_identifier.as_ref()); - }); + write_x509_extension( + writer.next(), + OID_SUBJECT_KEY_IDENTIFIER, + false, + |writer| { + let key_identifier = self.key_identifier(pub_key); + writer.write_bytes(key_identifier.as_ref()); + }, + ); // Write basic_constraints - write_x509_extension(writer.next(), OID_BASIC_CONSTRAINTS, true, |writer| { - writer.write_sequence(|writer| { - writer.next().write_bool(true); // cA flag - if let BasicConstraints::Constrained(path_len_constraint) = constraint { - writer.next().write_u8(*path_len_constraint); - } - }); - }); - } + write_x509_extension( + writer.next(), + OID_BASIC_CONSTRAINTS, + true, + |writer| { + writer.write_sequence(|writer| { + writer.next().write_bool(true); // cA flag + if let BasicConstraints::Constrained( + path_len_constraint, + ) = constraint + { + writer.next().write_u8(*path_len_constraint); + } + }); + }, + ); + }, IsCa::ExplicitNoCa => { // Write subject_key_identifier - write_x509_extension(writer.next(), OID_SUBJECT_KEY_IDENTIFIER, false, |writer| { - let key_identifier = self.key_identifier(pub_key); - writer.write_bytes(key_identifier.as_ref()); - }); + write_x509_extension( + writer.next(), + OID_SUBJECT_KEY_IDENTIFIER, + false, + |writer| { + let key_identifier = self.key_identifier(pub_key); + writer.write_bytes(key_identifier.as_ref()); + }, + ); // Write basic_constraints - write_x509_extension(writer.next(), OID_BASIC_CONSTRAINTS, true, |writer| { - writer.write_sequence(|writer| { - writer.next().write_bool(false); // cA flag - }); - }); - } - IsCa::NoCa => {} + write_x509_extension( + writer.next(), + OID_BASIC_CONSTRAINTS, + true, + |writer| { + writer.write_sequence(|writer| { + writer.next().write_bool(false); // cA flag + }); + }, + ); + }, + IsCa::NoCa => {}, } // Write the custom extensions @@ -1099,10 +1188,13 @@ impl CertificateParams { let truncated_digest = &digest.as_ref()[0..20]; truncated_digest.to_vec() } - fn serialize_der_with_signer(&self, pub_key: &K, ca :&Certificate) -> Result, RcgenError> { + fn serialize_der_with_signer( + &self, + pub_key: &K, + ca: &Certificate, + ) -> Result, RcgenError> { yasna::try_construct_der(|writer| { writer.write_sequence(|writer| { - let tbs_cert_list_serialized = yasna::try_construct_der(|writer| { self.write_cert(writer, pub_key, ca)?; Ok::<(), RcgenError>(()) @@ -1147,19 +1239,18 @@ pub enum BasicConstraints { impl CertificateParams { /// Generate certificate parameters with reasonable defaults - pub fn new(subject_alt_names :impl Into>) -> Self { - let subject_alt_names = subject_alt_names.into() + pub fn new(subject_alt_names: impl Into>) -> Self { + let subject_alt_names = subject_alt_names + .into() .into_iter() - .map(|s| { - match s.parse() { - Ok(ip) => SanType::IpAddress(ip), - Err(_) => SanType::DnsName(s) - } + .map(|s| match s.parse() { + Ok(ip) => SanType::IpAddress(ip), + Err(_) => SanType::DnsName(s), }) .collect::>(); CertificateParams { subject_alt_names, - .. Default::default() + ..Default::default() } } } @@ -1170,12 +1261,12 @@ impl CertificateParams { pub struct NameConstraints { /// If non-empty, a whitelist of subtrees that the /// domain has to match. - pub permitted_subtrees :Vec, + pub permitted_subtrees: Vec, /// A list of excluded subtrees. /// /// Any name matching an excluded subtree is invalid /// even if it also matches a permitted subtree. - pub excluded_subtrees :Vec, + pub excluded_subtrees: Vec, } impl NameConstraints { @@ -1247,11 +1338,11 @@ impl ExtendedKeyUsagePurpose { /// [RFC 5280](https://tools.ietf.org/html/rfc5280#section-4.2) #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub struct CustomExtension { - oid :Vec, - critical :bool, + oid: Vec, + critical: bool, /// The content must be DER-encoded - content :Vec, + content: Vec, } impl CustomExtension { @@ -1259,27 +1350,27 @@ impl CustomExtension { /// as specified in [RFC 8737](https://tools.ietf.org/html/rfc8737#section-3) /// /// Panics if the passed `sha_digest` parameter doesn't hold 32 bytes (256 bits). - pub fn new_acme_identifier(sha_digest :&[u8]) -> Self { + pub fn new_acme_identifier(sha_digest: &[u8]) -> Self { assert_eq!(sha_digest.len(), 32, "wrong size of sha_digest"); let content = yasna::construct_der(|writer| { writer.write_bytes(sha_digest); }); Self { - oid : OID_PE_ACME.to_owned(), - critical : true, + oid: OID_PE_ACME.to_owned(), + critical: true, content, } } /// Create a new custom extension with the specified content - pub fn from_oid_content(oid :&[u64], content :Vec) -> Self { + pub fn from_oid_content(oid: &[u64], content: Vec) -> Self { Self { - oid : oid.to_owned(), - critical : false, + oid: oid.to_owned(), + critical: false, content, } } /// Sets the criticality flag of the extension. - pub fn set_criticality(&mut self, criticality :bool) { + pub fn set_criticality(&mut self, criticality: bool) { self.critical = criticality; } /// Obtains the criticality flag of the extension. @@ -1319,16 +1410,16 @@ pub enum KeyIdMethod { /// have to import the time crate yourself in order to specify date /// information, second so that users don't have to type unproportionately /// long code just to generate an instance of [`OffsetDateTime`]. -pub fn date_time_ymd(year :i32, month :u8, day :u8) -> OffsetDateTime { +pub fn date_time_ymd(year: i32, month: u8, day: u8) -> OffsetDateTime { let month = Month::try_from(month).expect("out-of-range month"); let primitive_dt = PrimitiveDateTime::new( Date::from_calendar_date(year, month, day).expect("invalid or out-of-range date"), - Time::MIDNIGHT + Time::MIDNIGHT, ); primitive_dt.assume_utc() } -fn dt_strip_nanos(dt :OffsetDateTime) -> OffsetDateTime { +fn dt_strip_nanos(dt: OffsetDateTime) -> OffsetDateTime { // Set nanoseconds to zero // This is needed because the GeneralizedTime serializer would otherwise // output fractional values which RFC 5280 explicitly forbode [1]. @@ -1336,17 +1427,17 @@ fn dt_strip_nanos(dt :OffsetDateTime) -> OffsetDateTime { // therefore, it needs to be stripped of nanoseconds fully. // [1]: https://tools.ietf.org/html/rfc5280#section-4.1.2.5.2 // TODO: handle leap seconds if dt becomes leap second aware - let time = Time::from_hms(dt.hour(), dt.minute(), dt.second()) - .expect("invalid or out-of-range time"); + let time = + Time::from_hms(dt.hour(), dt.minute(), dt.second()).expect("invalid or out-of-range time"); dt.replace_time(time) } -fn dt_to_generalized(dt :OffsetDateTime) -> GeneralizedTime { +fn dt_to_generalized(dt: OffsetDateTime) -> GeneralizedTime { let date_time = dt_strip_nanos(dt); GeneralizedTime::from_datetime(date_time) } -fn write_dt_utc_or_generalized(writer :DERWriter, dt :OffsetDateTime) { +fn write_dt_utc_or_generalized(writer: DERWriter, dt: OffsetDateTime) { // RFC 5280 requires CAs to write certificate validity dates // below 2050 as UTCTime, and anything starting from 2050 // as GeneralizedTime [1]. The RFC doesn't say anything @@ -1363,44 +1454,55 @@ fn write_dt_utc_or_generalized(writer :DERWriter, dt :OffsetDateTime) { } } -fn write_distinguished_name(writer :DERWriter, dn :&DistinguishedName) { - writer.write_sequence(|writer| { - for (ty, content) in dn.iter() { - writer.next().write_set(|writer| { - writer.next().write_sequence(|writer| { - writer.next().write_oid(&ty.to_oid()); - match content { - DnValue::TeletexString(s) => writer.next().write_tagged_implicit(TAG_TELETEXSTRING, |writer| { - writer.write_bytes(s) - }), - DnValue::PrintableString(s) => writer.next().write_printable_string(s), - DnValue::UniversalString(s) => writer.next().write_tagged_implicit(TAG_UNIVERSALSTRING, |writer| { +fn write_distinguished_name(writer: DERWriter, dn: &DistinguishedName) { + writer.write_sequence(|writer| { + for (ty, content) in dn.iter() { + writer.next().write_set(|writer| { + writer.next().write_sequence(|writer| { + writer.next().write_oid(&ty.to_oid()); + match content { + DnValue::TeletexString(s) => writer + .next() + .write_tagged_implicit(TAG_TELETEXSTRING, |writer| { writer.write_bytes(s) }), - DnValue::Utf8String(s) => writer.next().write_utf8_string(s), - DnValue::BmpString(s) => writer.next().write_tagged_implicit(TAG_BMPSTRING, |writer| { + DnValue::PrintableString(s) => writer.next().write_printable_string(s), + DnValue::UniversalString(s) => writer + .next() + .write_tagged_implicit(TAG_UNIVERSALSTRING, |writer| { writer.write_bytes(s) }), - } - }); + DnValue::Utf8String(s) => writer.next().write_utf8_string(s), + DnValue::BmpString(s) => writer + .next() + .write_tagged_implicit(TAG_BMPSTRING, |writer| writer.write_bytes(s)), + } }); - } - }); + }); + } + }); } -fn write_general_subtrees(writer :DERWriter, tag :u64, general_subtrees :&[GeneralSubtree]) { +fn write_general_subtrees(writer: DERWriter, tag: u64, general_subtrees: &[GeneralSubtree]) { writer.write_tagged_implicit(Tag::context(tag), |writer| { writer.write_sequence(|writer| { for subtree in general_subtrees.iter() { writer.next().write_sequence(|writer| { - writer.next().write_tagged_implicit(Tag::context(subtree.tag()), |writer| { - match subtree { - GeneralSubtree::Rfc822Name(name) | - GeneralSubtree::DnsName(name) => writer.write_ia5_string(name), - GeneralSubtree::DirectoryName(name) => write_distinguished_name(writer, name), - GeneralSubtree::IpAddress(subnet) => writer.write_bytes(&subnet.to_bytes()), - } - }); + writer + .next() + .write_tagged_implicit( + Tag::context(subtree.tag()), + |writer| match subtree { + GeneralSubtree::Rfc822Name(name) + | GeneralSubtree::DnsName(name) => writer.write_ia5_string(name), + GeneralSubtree::DirectoryName(name) => { + write_distinguished_name(writer, name) + }, + GeneralSubtree::IpAddress(subnet) => { + writer.write_bytes(&subnet.to_bytes()) + }, + }, + ); // minimum must be 0 (the default) and maximum must be absent }); } @@ -1412,7 +1514,7 @@ impl Certificate { /// Generates a new certificate from the given parameters. /// /// If there is no key pair included, then a new key pair will be generated and used. - pub fn from_params(mut params :CertificateParams) -> Result { + pub fn from_params(mut params: CertificateParams) -> Result { let key_pair = if let Some(key_pair) = params.key_pair.take() { if !key_pair.is_compatible(¶ms.alg) { return Err(RcgenError::CertificateKeyPairMismatch); @@ -1422,14 +1524,11 @@ impl Certificate { KeyPair::generate(¶ms.alg)? }; - Ok(Certificate { - params, - key_pair, - }) + Ok(Certificate { params, key_pair }) } /// Returns the certificate parameters pub fn get_params(&self) -> &CertificateParams { - &self.params + &self.params } /// Calculates a subject key identifier for the certificate subject's public key. /// This key identifier is used in the SubjectKeyIdentifier X.509v3 extension. @@ -1441,7 +1540,7 @@ impl Certificate { self.serialize_der_with_signer(&self) } /// Serializes the certificate, signed with another certificate's key, in binary DER format - pub fn serialize_der_with_signer(&self, ca :&Certificate) -> Result, RcgenError> { + pub fn serialize_der_with_signer(&self, ca: &Certificate) -> Result, RcgenError> { self.params.serialize_der_with_signer(&self.key_pair, ca) } /// Serializes a certificate signing request in binary DER format @@ -1472,7 +1571,7 @@ impl Certificate { /// *This function is only available if rcgen is built with the "pem" feature* #[cfg(feature = "pem")] pub fn serialize_pem(&self) -> Result { - let contents = self.serialize_der()?; + let contents = self.serialize_der()?; let p = Pem::new("CERTIFICATE", contents); Ok(pem::encode_config(&p, ENCODE_CONFIG)) } @@ -1480,7 +1579,7 @@ impl Certificate { /// /// *This function is only available if rcgen is built with the "pem" feature* #[cfg(feature = "pem")] - pub fn serialize_pem_with_signer(&self, ca :&Certificate) -> Result { + pub fn serialize_pem_with_signer(&self, ca: &Certificate) -> Result { let contents = self.serialize_der_with_signer(ca)?; let p = Pem::new("CERTIFICATE", contents); Ok(pem::encode_config(&p, ENCODE_CONFIG)) @@ -1512,7 +1611,12 @@ impl Certificate { } /// Serializes an X.509v3 extension according to RFC 5280 -fn write_x509_extension(writer :DERWriter, extension_oid :&[u64], is_critical :bool, value_serializer :impl FnOnce(DERWriter)) { +fn write_x509_extension( + writer: DERWriter, + extension_oid: &[u64], + is_critical: bool, + value_serializer: impl FnOnce(DERWriter), +) { // Extension specification: // Extension ::= SEQUENCE { // extnID OBJECT IDENTIFIER, @@ -1535,7 +1639,7 @@ fn write_x509_extension(writer :DERWriter, extension_oid :&[u64], is_critical :b } /// Serializes an X.509v3 authority key identifier extension according to RFC 5280. -fn write_x509_authority_key_identifier(writer :DERWriter, ca :&Certificate) { +fn write_x509_authority_key_identifier(writer: DERWriter, ca: &Certificate) { // Write Authority Key Identifier // RFC 5280 states: // 'The keyIdentifier field of the authorityKeyIdentifier extension MUST @@ -1548,9 +1652,11 @@ fn write_x509_authority_key_identifier(writer :DERWriter, ca :&Certificate) { // include this extension in all CRLs issued.' write_x509_extension(writer, OID_AUTHORITY_KEY_IDENTIFIER, false, |writer| { writer.write_sequence(|writer| { - writer.next().write_tagged_implicit(Tag::context(0), |writer| { - writer.write_bytes(ca.get_key_identifier().as_ref()) - }) + writer + .next() + .write_tagged_implicit(Tag::context(0), |writer| { + writer.write_bytes(ca.get_key_identifier().as_ref()) + }) }); }); } @@ -1587,51 +1693,51 @@ impl zeroize::Zeroize for CertificateParams { /// A certificate serial number. #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub struct SerialNumber { - inner :Vec, + inner: Vec, } impl SerialNumber { - /// Create a serial number from the given byte slice. - pub fn from_slice(bytes :&[u8]) -> SerialNumber { - let inner = bytes.to_vec(); - SerialNumber { inner } - } + /// Create a serial number from the given byte slice. + pub fn from_slice(bytes: &[u8]) -> SerialNumber { + let inner = bytes.to_vec(); + SerialNumber { inner } + } - /// Return the byte representation of the serial number. - pub fn to_bytes(&self) -> Vec { - self.inner.clone() - } + /// Return the byte representation of the serial number. + pub fn to_bytes(&self) -> Vec { + self.inner.clone() + } - /// Return the length of the serial number in bytes. - pub fn len(&self) -> usize { - self.inner.len() - } + /// Return the length of the serial number in bytes. + pub fn len(&self) -> usize { + self.inner.len() + } } impl fmt::Display for SerialNumber { - fn fmt(&self, f :&mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - let hex :Vec<_> = self.inner.iter().map(|b| format!("{:02x}", b)).collect(); - write!(f, "{}", hex.join(":")) - } + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + let hex: Vec<_> = self.inner.iter().map(|b| format!("{:02x}", b)).collect(); + write!(f, "{}", hex.join(":")) + } } impl From> for SerialNumber { - fn from(inner :Vec) -> SerialNumber { - SerialNumber { inner } - } + fn from(inner: Vec) -> SerialNumber { + SerialNumber { inner } + } } impl From for SerialNumber { - fn from(u :u64) -> SerialNumber { - let inner = u.to_be_bytes().into(); - SerialNumber { inner } - } + fn from(u: u64) -> SerialNumber { + let inner = u.to_be_bytes().into(); + SerialNumber { inner } + } } impl AsRef<[u8]> for SerialNumber { - fn as_ref(&self) -> &[u8] { - &self.inner - } + fn as_ref(&self) -> &[u8] { + &self.inner + } } #[cfg(test)] @@ -1704,7 +1810,7 @@ mod tests { let (_rem, cert) = x509_parser::parse_x509_certificate(&der).unwrap(); // Check oid - let key_usage_oid_str= "2.5.29.15"; + let key_usage_oid_str = "2.5.29.15"; // Found flag let mut found = false; @@ -1712,11 +1818,11 @@ mod tests { for ext in cert.extensions() { if key_usage_oid_str == ext.oid.to_id_string() { match ext.parsed_extension() { - x509_parser::extensions::ParsedExtension::KeyUsage(usage) =>{ + x509_parser::extensions::ParsedExtension::KeyUsage(usage) => { assert!(usage.flags == 7); found = true; - } - _ => {} + }, + _ => {}, } } } @@ -1744,7 +1850,7 @@ mod tests { let (_rem, cert) = x509_parser::parse_x509_certificate(&der).unwrap(); // Check oid - let key_usage_oid_str= "2.5.29.15"; + let key_usage_oid_str = "2.5.29.15"; // Found flag let mut found = false; @@ -1752,11 +1858,11 @@ mod tests { for ext in cert.extensions() { if key_usage_oid_str == ext.oid.to_id_string() { match ext.parsed_extension() { - x509_parser::extensions::ParsedExtension::KeyUsage(usage) =>{ + x509_parser::extensions::ParsedExtension::KeyUsage(usage) => { assert!(usage.flags == 256); found = true; - } - _ => {} + }, + _ => {}, } } } @@ -1793,16 +1899,21 @@ mod tests { // algorithms, as it has no access to the iter function. for (i, alg_i) in SignatureAlgorithm::iter().enumerate() { for (j, alg_j) in SignatureAlgorithm::iter().enumerate() { - assert_eq!(alg_i == alg_j, i == j, - "Algorighm relationship mismatch for algorithm index pair {} and {}", i, j); + assert_eq!( + alg_i == alg_j, + i == j, + "Algorighm relationship mismatch for algorithm index pair {} and {}", + i, + j + ); } } } #[cfg(feature = "pem")] mod test_pem_serialization { - use crate::CertificateParams; - use crate::Certificate; + use crate::Certificate; + use crate::CertificateParams; #[test] #[cfg(windows)] @@ -1823,16 +1934,15 @@ mod tests { #[cfg(feature = "x509-parser")] mod test_ip_address_from_octets { - use std::net::IpAddr; use super::super::ip_addr_from_octets; use super::super::RcgenError; + use std::net::IpAddr; #[test] fn ipv4() { let octets = [10, 20, 30, 40]; - let actual = ip_addr_from_octets(&octets) - .unwrap(); + let actual = ip_addr_from_octets(&octets).unwrap(); assert_eq!(IpAddr::from(octets), actual) } @@ -1841,8 +1951,7 @@ mod tests { fn ipv6() { let octets = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; - let actual = ip_addr_from_octets(&octets) - .unwrap(); + let actual = ip_addr_from_octets(&octets).unwrap(); assert_eq!(IpAddr::from(octets), actual) } @@ -1850,16 +1959,14 @@ mod tests { #[test] fn mismatch() { let incorrect: Vec = (0..10).into_iter().collect(); - let actual = ip_addr_from_octets(&incorrect) - .unwrap_err(); + let actual = ip_addr_from_octets(&incorrect).unwrap_err(); assert_eq!(RcgenError::InvalidIpAddressOctetLength(10), actual); } #[test] fn none() { - let actual = ip_addr_from_octets(&[]) - .unwrap_err(); + let actual = ip_addr_from_octets(&[]).unwrap_err(); assert_eq!(RcgenError::InvalidIpAddressOctetLength(0), actual); } @@ -1867,8 +1974,7 @@ mod tests { #[test] fn too_many() { let incorrect: Vec = (0..20).into_iter().collect(); - let actual = ip_addr_from_octets(&incorrect) - .unwrap_err(); + let actual = ip_addr_from_octets(&incorrect).unwrap_err(); assert_eq!(RcgenError::InvalidIpAddressOctetLength(20), actual); } @@ -1876,16 +1982,15 @@ mod tests { #[cfg(feature = "x509-parser")] mod test_san_type_from_general_name { + use crate::SanType; use std::net::IpAddr; use x509_parser::extensions::GeneralName; - use crate::SanType; #[test] fn with_ipv4() { let octets = [1, 2, 3, 4]; let value = GeneralName::IPAddress(&octets); - let actual = SanType::try_from_general(&value) - .unwrap(); + let actual = SanType::try_from_general(&value).unwrap(); assert_eq!(SanType::IpAddress(IpAddr::from(octets)), actual); } diff --git a/src/main.rs b/src/main.rs index 4aab67eb..9a6ad87f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,19 +1,23 @@ #![allow(clippy::complexity, clippy::style, clippy::pedantic)] -use rcgen::{Certificate, CertificateParams, - DistinguishedName, DnType, SanType, - date_time_ymd}; +use rcgen::{date_time_ymd, Certificate, CertificateParams, DistinguishedName, DnType, SanType}; use std::fs; fn main() -> Result<(), Box> { - let mut params :CertificateParams = Default::default(); + let mut params: CertificateParams = Default::default(); params.not_before = date_time_ymd(1975, 01, 01); params.not_after = date_time_ymd(4096, 01, 01); params.distinguished_name = DistinguishedName::new(); - params.distinguished_name.push(DnType::OrganizationName, "Crab widgits SE"); - params.distinguished_name.push(DnType::CommonName, "Master Cert"); - params.subject_alt_names = vec![SanType::DnsName("crabs.crabs".to_string()), - SanType::DnsName("localhost".to_string())]; + params + .distinguished_name + .push(DnType::OrganizationName, "Crab widgits SE"); + params + .distinguished_name + .push(DnType::CommonName, "Master Cert"); + params.subject_alt_names = vec![ + SanType::DnsName("crabs.crabs".to_string()), + SanType::DnsName("localhost".to_string()), + ]; let cert = Certificate::from_params(params)?; @@ -25,7 +29,10 @@ fn main() -> Result<(), Box> { std::fs::create_dir_all("certs/")?; fs::write("certs/cert.pem", &pem_serialized.as_bytes())?; fs::write("certs/cert.der", &der_serialized)?; - fs::write("certs/key.pem", &cert.serialize_private_key_pem().as_bytes())?; + fs::write( + "certs/key.pem", + &cert.serialize_private_key_pem().as_bytes(), + )?; fs::write("certs/key.der", &cert.serialize_private_key_der())?; Ok(()) } diff --git a/src/oid.rs b/src/oid.rs index 738ce951..aee56efd 100644 --- a/src/oid.rs +++ b/src/oid.rs @@ -1,74 +1,74 @@ /// pkcs-9-at-extensionRequest in RFC 2985 -pub const OID_PKCS_9_AT_EXTENSION_REQUEST :&[u64] = &[1, 2, 840, 113549, 1, 9, 14]; +pub const OID_PKCS_9_AT_EXTENSION_REQUEST: &[u64] = &[1, 2, 840, 113549, 1, 9, 14]; /// id-at-countryName in RFC 5280 -pub const OID_COUNTRY_NAME :&[u64] = &[2, 5, 4, 6]; +pub const OID_COUNTRY_NAME: &[u64] = &[2, 5, 4, 6]; /// id-at-localityName in RFC 5280 -pub const OID_LOCALITY_NAME :&[u64] = &[2, 5, 4, 7]; +pub const OID_LOCALITY_NAME: &[u64] = &[2, 5, 4, 7]; /// id-at-stateOrProvinceName in RFC 5280 -pub const OID_STATE_OR_PROVINCE_NAME :&[u64] = &[2, 5, 4, 8]; +pub const OID_STATE_OR_PROVINCE_NAME: &[u64] = &[2, 5, 4, 8]; /// id-at-organizationName in RFC 5280 -pub const OID_ORG_NAME :&[u64] = &[2, 5, 4, 10]; +pub const OID_ORG_NAME: &[u64] = &[2, 5, 4, 10]; /// id-at-organizationalUnitName in RFC 5280 -pub const OID_ORG_UNIT_NAME :&[u64] = &[2, 5, 4, 11]; +pub const OID_ORG_UNIT_NAME: &[u64] = &[2, 5, 4, 11]; /// id-at-commonName in RFC 5280 -pub const OID_COMMON_NAME :&[u64] = &[2, 5, 4, 3]; +pub const OID_COMMON_NAME: &[u64] = &[2, 5, 4, 3]; // https://tools.ietf.org/html/rfc5480#section-2.1.1 -pub const OID_EC_PUBLIC_KEY :&[u64] = &[1, 2, 840, 10045, 2, 1]; -pub const OID_EC_SECP_256_R1 :&[u64] = &[1, 2, 840, 10045, 3, 1, 7]; -pub const OID_EC_SECP_384_R1 :&[u64] = &[1, 3, 132, 0, 34]; +pub const OID_EC_PUBLIC_KEY: &[u64] = &[1, 2, 840, 10045, 2, 1]; +pub const OID_EC_SECP_256_R1: &[u64] = &[1, 2, 840, 10045, 3, 1, 7]; +pub const OID_EC_SECP_384_R1: &[u64] = &[1, 3, 132, 0, 34]; // rsaEncryption in RFC 4055 -pub const OID_RSA_ENCRYPTION :&[u64] = &[1, 2, 840, 113549, 1, 1, 1]; +pub const OID_RSA_ENCRYPTION: &[u64] = &[1, 2, 840, 113549, 1, 1, 1]; // id-RSASSA-PSS in RFC 4055 -pub const OID_RSASSA_PSS :&[u64] = &[1, 2, 840, 113549, 1, 1, 10]; +pub const OID_RSASSA_PSS: &[u64] = &[1, 2, 840, 113549, 1, 1, 10]; // https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.3 -pub const OID_KEY_USAGE :&[u64] = &[2, 5, 29, 15]; +pub const OID_KEY_USAGE: &[u64] = &[2, 5, 29, 15]; // https://tools.ietf.org/html/rfc5280#appendix-A.2 // https://tools.ietf.org/html/rfc5280#section-4.2.1.6 -pub const OID_SUBJECT_ALT_NAME :&[u64] = &[2, 5, 29, 17]; +pub const OID_SUBJECT_ALT_NAME: &[u64] = &[2, 5, 29, 17]; // https://tools.ietf.org/html/rfc5280#section-4.2.1.9 -pub const OID_BASIC_CONSTRAINTS :&[u64] = &[2, 5, 29, 19]; +pub const OID_BASIC_CONSTRAINTS: &[u64] = &[2, 5, 29, 19]; // https://tools.ietf.org/html/rfc5280#section-4.2.1.2 -pub const OID_SUBJECT_KEY_IDENTIFIER :&[u64] = &[2, 5, 29, 14]; +pub const OID_SUBJECT_KEY_IDENTIFIER: &[u64] = &[2, 5, 29, 14]; // https://tools.ietf.org/html/rfc5280#section-4.2.1.1 -pub const OID_AUTHORITY_KEY_IDENTIFIER :&[u64] = &[2, 5, 29, 35]; +pub const OID_AUTHORITY_KEY_IDENTIFIER: &[u64] = &[2, 5, 29, 35]; // id-ce-extKeyUsage in // https://tools.ietf.org/html/rfc5280#section-4.2.1.12 -pub const OID_EXT_KEY_USAGE :&[u64] = &[2, 5, 29, 37]; +pub const OID_EXT_KEY_USAGE: &[u64] = &[2, 5, 29, 37]; // id-ce-nameConstraints in // https://tools.ietf.org/html/rfc5280#section-4.2.1.10 -pub const OID_NAME_CONSTRAINTS :&[u64] = &[2, 5, 29, 30]; +pub const OID_NAME_CONSTRAINTS: &[u64] = &[2, 5, 29, 30]; // id-ce-cRLDistributionPoints in // https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.13 -pub const OID_CRL_DISTRIBUTION_POINTS :&[u64] = &[2, 5, 29, 31]; +pub const OID_CRL_DISTRIBUTION_POINTS: &[u64] = &[2, 5, 29, 31]; // id-pe-acmeIdentifier in // https://www.iana.org/assignments/smi-numbers/smi-numbers.xhtml#smi-numbers-1.3.6.1.5.5.7.1 -pub const OID_PE_ACME :&[u64] = &[1, 3, 6, 1, 5, 5, 7, 1, 31]; +pub const OID_PE_ACME: &[u64] = &[1, 3, 6, 1, 5, 5, 7, 1, 31]; // id-ce-cRLNumber in // https://www.rfc-editor.org/rfc/rfc5280#section-5.2.3 -pub const OID_CRL_NUMBER :&[u64] = &[2, 5, 29, 20]; +pub const OID_CRL_NUMBER: &[u64] = &[2, 5, 29, 20]; // id-ce-cRLReasons // https://www.rfc-editor.org/rfc/rfc5280#section-5.3.1 -pub const OID_CRL_REASONS :&[u64] = &[2, 5, 29, 21]; +pub const OID_CRL_REASONS: &[u64] = &[2, 5, 29, 21]; // id-ce-invalidityDate // https://www.rfc-editor.org/rfc/rfc5280#section-5.3.2 -pub const OID_CRL_INVALIDITY_DATE :&[u64] = &[2, 5, 29, 24]; +pub const OID_CRL_INVALIDITY_DATE: &[u64] = &[2, 5, 29, 24]; // id-ce-issuingDistributionPoint // https://datatracker.ietf.org/doc/html/rfc5280#section-5.2.5 -pub const OID_CRL_ISSUING_DISTRIBUTION_POINT :&[u64] = &[2, 5, 29, 28]; +pub const OID_CRL_ISSUING_DISTRIBUTION_POINT: &[u64] = &[2, 5, 29, 28]; diff --git a/src/sign_algo.rs b/src/sign_algo.rs index f21add80..9a0aca6f 100644 --- a/src/sign_algo.rs +++ b/src/sign_algo.rs @@ -1,9 +1,9 @@ -use yasna::Tag; -use yasna::models::ObjectIdentifier; use ring::signature::{self, EcdsaSigningAlgorithm, EdDSAParameters}; -use yasna::DERWriter; use std::fmt; use std::hash::{Hash, Hasher}; +use yasna::models::ObjectIdentifier; +use yasna::DERWriter; +use yasna::Tag; use crate::oid::*; use crate::RcgenError; @@ -22,22 +22,22 @@ pub(crate) enum SignatureAlgorithmParams { Null, /// RSASSA-PSS-params as per RFC 4055 RsaPss { - hash_algorithm :&'static [u64], - salt_length :u64, + hash_algorithm: &'static [u64], + salt_length: u64, }, } /// Signature algorithm type pub struct SignatureAlgorithm { - oids_sign_alg :&'static [&'static [u64]], - pub(crate) sign_alg :SignAlgo, - oid_components :&'static [u64], - params :SignatureAlgorithmParams, + oids_sign_alg: &'static [&'static [u64]], + pub(crate) sign_alg: SignAlgo, + oid_components: &'static [u64], + params: SignatureAlgorithmParams, } impl fmt::Debug for SignatureAlgorithm { - fn fmt(&self, f :&mut fmt::Formatter) -> fmt::Result { - use algo::*; + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use algo::*; if self == &PKCS_RSA_SHA256 { write!(f, "PKCS_RSA_SHA256") } else if self == &PKCS_RSA_SHA384 { @@ -59,7 +59,7 @@ impl fmt::Debug for SignatureAlgorithm { } impl PartialEq for SignatureAlgorithm { - fn eq(&self, other :&Self) -> bool { + fn eq(&self, other: &Self) -> bool { (self.oids_sign_alg, self.oid_components) == (other.oids_sign_alg, other.oid_components) } } @@ -76,21 +76,21 @@ impl Hash for SignatureAlgorithm { impl SignatureAlgorithm { pub(crate) fn iter() -> std::slice::Iter<'static, &'static SignatureAlgorithm> { - use algo::*; - static ALGORITHMS :&[&SignatureAlgorithm] = &[ + use algo::*; + static ALGORITHMS: &[&SignatureAlgorithm] = &[ &PKCS_RSA_SHA256, &PKCS_RSA_SHA384, &PKCS_RSA_SHA512, //&PKCS_RSA_PSS_SHA256, &PKCS_ECDSA_P256_SHA256, &PKCS_ECDSA_P384_SHA384, - &PKCS_ED25519 + &PKCS_ED25519, ]; ALGORITHMS.iter() } /// Retrieve the SignatureAlgorithm for the provided OID - pub fn from_oid(oid :&[u64]) -> Result<&'static SignatureAlgorithm, RcgenError> { + pub fn from_oid(oid: &[u64]) -> Result<&'static SignatureAlgorithm, RcgenError> { for algo in Self::iter() { if algo.oid_components == oid { return Ok(algo); @@ -102,98 +102,99 @@ impl SignatureAlgorithm { /// The list of supported signature algorithms pub mod algo { - use super::*; - - /// RSA signing with PKCS#1 1.5 padding and SHA-256 hashing as per [RFC 4055](https://tools.ietf.org/html/rfc4055) - pub static PKCS_RSA_SHA256 :SignatureAlgorithm = SignatureAlgorithm { - oids_sign_alg :&[&OID_RSA_ENCRYPTION], - sign_alg :SignAlgo::Rsa(), - // sha256WithRSAEncryption in RFC 4055 - oid_components : &[1, 2, 840, 113549, 1, 1, 11], - params : SignatureAlgorithmParams::Null, - }; - - /// RSA signing with PKCS#1 1.5 padding and SHA-256 hashing as per [RFC 4055](https://tools.ietf.org/html/rfc4055) - pub static PKCS_RSA_SHA384 :SignatureAlgorithm = SignatureAlgorithm { - oids_sign_alg :&[&OID_RSA_ENCRYPTION], - sign_alg :SignAlgo::Rsa(), - // sha384WithRSAEncryption in RFC 4055 - oid_components : &[1, 2, 840, 113549, 1, 1, 12], - params : SignatureAlgorithmParams::Null, - }; - - /// RSA signing with PKCS#1 1.5 padding and SHA-512 hashing as per [RFC 4055](https://tools.ietf.org/html/rfc4055) - pub static PKCS_RSA_SHA512 :SignatureAlgorithm = SignatureAlgorithm { - oids_sign_alg :&[&OID_RSA_ENCRYPTION], - sign_alg :SignAlgo::Rsa(), - // sha512WithRSAEncryption in RFC 4055 - oid_components : &[1, 2, 840, 113549, 1, 1, 13], - params : SignatureAlgorithmParams::Null, - }; - - // TODO: not really sure whether the certs we generate actually work. - // Both openssl and webpki reject them. It *might* be possible that openssl - // accepts the certificate if the key is a proper RSA-PSS key, but ring doesn't - // support those: https://github.com/briansmith/ring/issues/1353 - // - /// RSA signing with PKCS#1 2.1 RSASSA-PSS padding and SHA-256 hashing as per [RFC 4055](https://tools.ietf.org/html/rfc4055) - pub(crate) static PKCS_RSA_PSS_SHA256 :SignatureAlgorithm = SignatureAlgorithm { - // We could also use OID_RSA_ENCRYPTION here, but it's recommended - // to use ID-RSASSA-PSS if possible. - oids_sign_alg :&[&OID_RSASSA_PSS], - sign_alg :SignAlgo::Rsa(), - oid_components : &OID_RSASSA_PSS,//&[1, 2, 840, 113549, 1, 1, 13], - // rSASSA-PSS-SHA256-Params in RFC 4055 - params : SignatureAlgorithmParams::RsaPss { - // id-sha256 in https://datatracker.ietf.org/doc/html/rfc4055#section-2.1 - hash_algorithm : &[2, 16, 840, 1, 101, 3, 4, 2, 1], - salt_length : 20, - }, - }; - - /// ECDSA signing using the P-256 curves and SHA-256 hashing as per [RFC 5758](https://tools.ietf.org/html/rfc5758#section-3.2) - pub static PKCS_ECDSA_P256_SHA256 :SignatureAlgorithm = SignatureAlgorithm { - oids_sign_alg :&[&OID_EC_PUBLIC_KEY, &OID_EC_SECP_256_R1], - sign_alg :SignAlgo::EcDsa(&signature::ECDSA_P256_SHA256_ASN1_SIGNING), - // ecdsa-with-SHA256 in RFC 5758 - oid_components : &[1, 2, 840, 10045, 4, 3, 2], - params : SignatureAlgorithmParams::None, - }; - - /// ECDSA signing using the P-384 curves and SHA-384 hashing as per [RFC 5758](https://tools.ietf.org/html/rfc5758#section-3.2) - pub static PKCS_ECDSA_P384_SHA384 :SignatureAlgorithm = SignatureAlgorithm { - oids_sign_alg :&[&OID_EC_PUBLIC_KEY, &OID_EC_SECP_384_R1], - sign_alg :SignAlgo::EcDsa(&signature::ECDSA_P384_SHA384_ASN1_SIGNING), - // ecdsa-with-SHA384 in RFC 5758 - oid_components : &[1, 2, 840, 10045, 4, 3, 3], - params : SignatureAlgorithmParams::None, - }; - - // TODO PKCS_ECDSA_P521_SHA512 https://github.com/briansmith/ring/issues/824 - - /// ED25519 curve signing as per [RFC 8410](https://tools.ietf.org/html/rfc8410) - pub static PKCS_ED25519 :SignatureAlgorithm = SignatureAlgorithm { - // id-Ed25519 in RFC 8410 - oids_sign_alg :&[&[1, 3, 101, 112]], - sign_alg :SignAlgo::EdDsa(&signature::ED25519), - // id-Ed25519 in RFC 8410 - oid_components : &[1, 3, 101, 112], - params : SignatureAlgorithmParams::None, - }; + use super::*; + + /// RSA signing with PKCS#1 1.5 padding and SHA-256 hashing as per [RFC 4055](https://tools.ietf.org/html/rfc4055) + pub static PKCS_RSA_SHA256: SignatureAlgorithm = SignatureAlgorithm { + oids_sign_alg: &[&OID_RSA_ENCRYPTION], + sign_alg: SignAlgo::Rsa(), + // sha256WithRSAEncryption in RFC 4055 + oid_components: &[1, 2, 840, 113549, 1, 1, 11], + params: SignatureAlgorithmParams::Null, + }; + + /// RSA signing with PKCS#1 1.5 padding and SHA-256 hashing as per [RFC 4055](https://tools.ietf.org/html/rfc4055) + pub static PKCS_RSA_SHA384: SignatureAlgorithm = SignatureAlgorithm { + oids_sign_alg: &[&OID_RSA_ENCRYPTION], + sign_alg: SignAlgo::Rsa(), + // sha384WithRSAEncryption in RFC 4055 + oid_components: &[1, 2, 840, 113549, 1, 1, 12], + params: SignatureAlgorithmParams::Null, + }; + + /// RSA signing with PKCS#1 1.5 padding and SHA-512 hashing as per [RFC 4055](https://tools.ietf.org/html/rfc4055) + pub static PKCS_RSA_SHA512: SignatureAlgorithm = SignatureAlgorithm { + oids_sign_alg: &[&OID_RSA_ENCRYPTION], + sign_alg: SignAlgo::Rsa(), + // sha512WithRSAEncryption in RFC 4055 + oid_components: &[1, 2, 840, 113549, 1, 1, 13], + params: SignatureAlgorithmParams::Null, + }; + + // TODO: not really sure whether the certs we generate actually work. + // Both openssl and webpki reject them. It *might* be possible that openssl + // accepts the certificate if the key is a proper RSA-PSS key, but ring doesn't + // support those: https://github.com/briansmith/ring/issues/1353 + // + /// RSA signing with PKCS#1 2.1 RSASSA-PSS padding and SHA-256 hashing as per [RFC 4055](https://tools.ietf.org/html/rfc4055) + pub(crate) static PKCS_RSA_PSS_SHA256: SignatureAlgorithm = SignatureAlgorithm { + // We could also use OID_RSA_ENCRYPTION here, but it's recommended + // to use ID-RSASSA-PSS if possible. + oids_sign_alg: &[&OID_RSASSA_PSS], + sign_alg: SignAlgo::Rsa(), + oid_components: &OID_RSASSA_PSS, //&[1, 2, 840, 113549, 1, 1, 13], + // rSASSA-PSS-SHA256-Params in RFC 4055 + params: SignatureAlgorithmParams::RsaPss { + // id-sha256 in https://datatracker.ietf.org/doc/html/rfc4055#section-2.1 + hash_algorithm: &[2, 16, 840, 1, 101, 3, 4, 2, 1], + salt_length: 20, + }, + }; + + /// ECDSA signing using the P-256 curves and SHA-256 hashing as per [RFC 5758](https://tools.ietf.org/html/rfc5758#section-3.2) + pub static PKCS_ECDSA_P256_SHA256: SignatureAlgorithm = SignatureAlgorithm { + oids_sign_alg: &[&OID_EC_PUBLIC_KEY, &OID_EC_SECP_256_R1], + sign_alg: SignAlgo::EcDsa(&signature::ECDSA_P256_SHA256_ASN1_SIGNING), + // ecdsa-with-SHA256 in RFC 5758 + oid_components: &[1, 2, 840, 10045, 4, 3, 2], + params: SignatureAlgorithmParams::None, + }; + + /// ECDSA signing using the P-384 curves and SHA-384 hashing as per [RFC 5758](https://tools.ietf.org/html/rfc5758#section-3.2) + pub static PKCS_ECDSA_P384_SHA384: SignatureAlgorithm = SignatureAlgorithm { + oids_sign_alg: &[&OID_EC_PUBLIC_KEY, &OID_EC_SECP_384_R1], + sign_alg: SignAlgo::EcDsa(&signature::ECDSA_P384_SHA384_ASN1_SIGNING), + // ecdsa-with-SHA384 in RFC 5758 + oid_components: &[1, 2, 840, 10045, 4, 3, 3], + params: SignatureAlgorithmParams::None, + }; + + // TODO PKCS_ECDSA_P521_SHA512 https://github.com/briansmith/ring/issues/824 + + /// ED25519 curve signing as per [RFC 8410](https://tools.ietf.org/html/rfc8410) + pub static PKCS_ED25519: SignatureAlgorithm = SignatureAlgorithm { + // id-Ed25519 in RFC 8410 + oids_sign_alg: &[&[1, 3, 101, 112]], + sign_alg: SignAlgo::EdDsa(&signature::ED25519), + // id-Ed25519 in RFC 8410 + oid_components: &[1, 3, 101, 112], + params: SignatureAlgorithmParams::None, + }; } // Signature algorithm IDs as per https://tools.ietf.org/html/rfc4055 impl SignatureAlgorithm { fn alg_ident_oid(&self) -> ObjectIdentifier { ObjectIdentifier::from_slice(self.oid_components) } - fn write_params(&self, writer :&mut yasna::DERWriterSeq) { + fn write_params(&self, writer: &mut yasna::DERWriterSeq) { match self.params { SignatureAlgorithmParams::None => (), SignatureAlgorithmParams::Null => { writer.next().write_null(); }, SignatureAlgorithmParams::RsaPss { - hash_algorithm, salt_length, + hash_algorithm, + salt_length, } => { writer.next().write_sequence(|writer| { // https://datatracker.ietf.org/doc/html/rfc4055#section-3.1 @@ -209,7 +210,7 @@ impl SignatureAlgorithm { writer.next().write_tagged(Tag::context(1), |writer| { writer.write_sequence(|writer| { // id-mgf1 in RFC 4055 - const ID_MGF1 :&[u64] = &[1, 2, 840, 113549, 1, 1, 8]; + const ID_MGF1: &[u64] = &[1, 2, 840, 113549, 1, 1, 8]; let oid = ObjectIdentifier::from_slice(ID_MGF1); writer.next().write_oid(&oid); writer.next().write_sequence(|writer| { @@ -229,14 +230,14 @@ impl SignatureAlgorithm { } } /// Writes the algorithm identifier as it appears inside a signature - pub(crate) fn write_alg_ident(&self, writer :DERWriter) { + pub(crate) fn write_alg_ident(&self, writer: DERWriter) { writer.write_sequence(|writer| { writer.next().write_oid(&self.alg_ident_oid()); self.write_params(writer); }); } /// Writes the algorithm identifier as it appears inside subjectPublicKeyInfo - pub(crate) fn write_oids_sign_alg(&self, writer :DERWriter) { + pub(crate) fn write_oids_sign_alg(&self, writer: DERWriter) { writer.write_sequence(|writer| { for oid in self.oids_sign_alg { let oid = ObjectIdentifier::from_slice(oid); diff --git a/tests/botan.rs b/tests/botan.rs index 875a0c7f..1ea09e14 100644 --- a/tests/botan.rs +++ b/tests/botan.rs @@ -1,10 +1,12 @@ #![cfg(all(feature = "x509-parser", not(windows)))] -use time::{Duration, OffsetDateTime}; use rcgen::DnValue; use rcgen::{BasicConstraints, Certificate, CertificateParams, DnType, IsCa}; +use rcgen::{ + CertificateRevocationList, CertificateRevocationListParams, RevocationReason, RevokedCertParams, +}; use rcgen::{KeyUsagePurpose, SerialNumber}; -use rcgen::{CertificateRevocationList, CertificateRevocationListParams, RevokedCertParams, RevocationReason}; +use time::{Duration, OffsetDateTime}; mod util; @@ -15,13 +17,16 @@ fn default_params() -> CertificateParams { params } -fn check_cert<'a, 'b>(cert_der :&[u8], cert :&'a Certificate) { +fn check_cert<'a, 'b>(cert_der: &[u8], cert: &'a Certificate) { println!("{}", cert.serialize_pem().unwrap()); check_cert_ca(cert_der, cert, cert_der); } -fn check_cert_ca<'a, 'b>(cert_der :&[u8], _cert :&'a Certificate, ca_der :&[u8]) { - println!("botan version: {}", botan::Version::current().unwrap().string); +fn check_cert_ca<'a, 'b>(cert_der: &[u8], _cert: &'a 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(); @@ -29,7 +34,15 @@ fn check_cert_ca<'a, 'b>(cert_der :&[u8], _cert :&'a Certificate, ca_der :&[u8]) const REFERENCE_TIME: Option = Some(0x40_00_00_00); // Verify the certificate - end_entity_cert.verify(&[], &[&trust_anchor], None, Some("crabs.crabs"), REFERENCE_TIME).unwrap(); + end_entity_cert + .verify( + &[], + &[&trust_anchor], + None, + Some("crabs.crabs"), + REFERENCE_TIME, + ) + .unwrap(); // TODO perform a full handshake } @@ -141,8 +154,12 @@ fn test_botan_separate_ca() { let ca_der = ca_cert.serialize_der().unwrap(); let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]); - params.distinguished_name.push(DnType::OrganizationName, "Crab widgits SE"); - params.distinguished_name.push(DnType::CommonName, "Dev domain"); + 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, 01, 01); @@ -160,16 +177,23 @@ fn test_botan_imported_ca() { params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); let ca_cert = Certificate::from_params(params).unwrap(); - let (ca_cert_der, ca_key_der) = (ca_cert.serialize_der().unwrap(), ca_cert.serialize_private_key_der()); + let (ca_cert_der, ca_key_der) = ( + ca_cert.serialize_der().unwrap(), + ca_cert.serialize_private_key_der(), + ); let ca_key_pair = ca_key_der.as_slice().try_into().unwrap(); - let imported_ca_cert_params = CertificateParams::from_ca_cert_der(ca_cert_der.as_slice(), ca_key_pair) - .unwrap(); + let imported_ca_cert_params = + CertificateParams::from_ca_cert_der(ca_cert_der.as_slice(), ca_key_pair).unwrap(); let imported_ca_cert = Certificate::from_params(imported_ca_cert_params).unwrap(); let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]); - params.distinguished_name.push(DnType::OrganizationName, "Crab widgits SE"); - params.distinguished_name.push(DnType::CommonName, "Dev domain"); + 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, 01, 01); let cert = Certificate::from_params(params).unwrap(); @@ -183,20 +207,30 @@ fn test_botan_imported_ca() { fn test_botan_imported_ca_with_printable_string() { use std::convert::TryInto; let mut params = default_params(); - params.distinguished_name.push(DnType::CountryName, DnValue::PrintableString("US".to_string())); + params.distinguished_name.push( + DnType::CountryName, + DnValue::PrintableString("US".to_string()), + ); params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); let ca_cert = Certificate::from_params(params).unwrap(); - let (ca_cert_der, ca_key_der) = (ca_cert.serialize_der().unwrap(), ca_cert.serialize_private_key_der()); + let (ca_cert_der, ca_key_der) = ( + ca_cert.serialize_der().unwrap(), + ca_cert.serialize_private_key_der(), + ); let ca_key_pair = ca_key_der.as_slice().try_into().unwrap(); - let imported_ca_cert_params = CertificateParams::from_ca_cert_der(ca_cert_der.as_slice(), ca_key_pair) - .unwrap(); + let imported_ca_cert_params = + CertificateParams::from_ca_cert_der(ca_cert_der.as_slice(), ca_key_pair).unwrap(); let imported_ca_cert = Certificate::from_params(imported_ca_cert_params).unwrap(); let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]); - params.distinguished_name.push(DnType::OrganizationName, "Crab widgits SE"); - params.distinguished_name.push(DnType::CommonName, "Dev domain"); + 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, 01, 01); let cert = Certificate::from_params(params).unwrap(); @@ -211,7 +245,11 @@ fn test_botan_crl_parse() { 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]; + issuer.key_usages = vec![ + KeyUsagePurpose::KeyCertSign, + KeyUsagePurpose::DigitalSignature, + KeyUsagePurpose::CrlSign, + ]; issuer.alg = alg; let issuer = Certificate::from_params(issuer).unwrap(); @@ -228,12 +266,12 @@ fn test_botan_crl_parse() { // Generate a CRL with the issuer that revokes the EE cert. let now = OffsetDateTime::now_utc(); - let crl = CertificateRevocationListParams{ + 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{ + revoked_certs: vec![RevokedCertParams { serial_number: ee.get_params().serial_number.clone().unwrap(), revocation_time: now, reason_code: Some(RevocationReason::KeyCompromise), diff --git a/tests/generic.rs b/tests/generic.rs index 33cc57e2..647eb7df 100644 --- a/tests/generic.rs +++ b/tests/generic.rs @@ -1,8 +1,8 @@ mod util; -use rcgen::{RcgenError, KeyPair, Certificate}; -use std::hash::{Hash, Hasher}; +use rcgen::{Certificate, KeyPair, RcgenError}; use std::collections::hash_map::DefaultHasher; +use std::hash::{Hash, Hasher}; fn generate_hash(subject: &T) -> u64 { let mut hasher = DefaultHasher::new(); @@ -41,15 +41,21 @@ fn test_key_params_mismatch() { assert_eq!( Certificate::from_params(wrong_params).err(), Some(RcgenError::CertificateKeyPairMismatch), - "i: {} j: {}", i, j); + "i: {} j: {}", + i, + j + ); } } } #[cfg(feature = "x509-parser")] mod test_convert_x509_subject_alternative_name { + use rcgen::{ + BasicConstraints, Certificate, CertificateParams, IsCa, KeyPair, SanType, + PKCS_ECDSA_P256_SHA256, + }; use std::net::{IpAddr, Ipv4Addr}; - use rcgen::{BasicConstraints, Certificate, CertificateParams, IsCa, KeyPair, PKCS_ECDSA_P256_SHA256, SanType}; #[test] fn converts_from_ip() { @@ -80,11 +86,11 @@ mod test_convert_x509_subject_alternative_name { #[cfg(feature = "x509-parser")] mod test_x509_parser_crl { + use crate::util; use x509_parser::num_bigint::BigUint; use x509_parser::prelude::{FromDer, X509Certificate}; use x509_parser::revocation_list::CertificateRevocationList; use x509_parser::x509::X509Version; - use crate::util; #[test] fn parse_crl() { @@ -99,30 +105,44 @@ mod test_x509_parser_crl { let crl_der = crl.serialize_der_with_signer(&issuer).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"); + 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.get_params().this_update.unix_timestamp()); - assert_eq!(x509_crl.next_update().unwrap().to_datetime().unix_timestamp(), - crl.get_params().next_update.unix_timestamp()); + assert_eq!( + x509_crl.last_update().to_datetime().unix_timestamp(), + crl.get_params().this_update.unix_timestamp() + ); + assert_eq!( + x509_crl + .next_update() + .unwrap() + .to_datetime() + .unix_timestamp(), + crl.get_params().next_update.unix_timestamp() + ); // TODO: Waiting on x509-parser 0.15.1 to be released. // let crl_number = BigUint::from_bytes_be(crl.get_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() + 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); + 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) + 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); // TODO: x509-parser does not yet parse the CRL issuing DP extension for further examination. @@ -134,8 +154,8 @@ mod test_x509_parser_crl { #[cfg(feature = "x509-parser")] mod test_parse_crl_dps { - use x509_parser::extensions::{DistributionPointName, ParsedExtension}; use crate::util; + use x509_parser::extensions::{DistributionPointName, ParsedExtension}; #[test] fn parse_crl_dps() { @@ -144,7 +164,8 @@ mod test_parse_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) + 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"); @@ -154,7 +175,7 @@ mod test_parse_crl_dps { // 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") + _ => panic!("unexpected parsed extension type"), }; // There should be two DPs. @@ -162,26 +183,44 @@ mod test_parse_crl_dps { // 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::>(); + 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::>(); // All of the general names should be URIs. - let uris = general_names.iter().map(|general_name| { - match general_name { + let uris = general_names + .iter() + .map(|general_name| match general_name { x509_parser::extensions::GeneralName::URI(uri) => *uri, - _ => panic!("unexpected general name type") - } - }).collect::>(); + _ => panic!("unexpected general name type"), + }) + .collect::>(); // We should find the expected URIs. - assert_eq!(uris, &["http://example.com/crl.der", "http://crls.example.com/1234", "ldap://example.com/crl.der"]); + assert_eq!( + uris, + &[ + "http://example.com/crl.der", + "http://crls.example.com/1234", + "ldap://example.com/crl.der" + ] + ); } } diff --git a/tests/openssl.rs b/tests/openssl.rs index 203e70a6..97b196d2 100644 --- a/tests/openssl.rs +++ b/tests/openssl.rs @@ -1,20 +1,21 @@ -use rcgen::{Certificate, NameConstraints, GeneralSubtree, IsCa, BasicConstraints, CertificateParams, DnType, DnValue}; -use openssl::pkey::PKey; -use openssl::x509::{CrlStatus, X509, X509Crl, X509Req, X509StoreContext}; -use openssl::x509::store::{X509StoreBuilder, X509Store}; -use openssl::ssl::{SslMethod, SslConnector, - SslAcceptor, HandshakeError}; -use openssl::stack::Stack; use openssl::asn1::{Asn1Integer, Asn1Time}; use openssl::bn::BigNum; -use std::io::{Write, Read, Result as ioResult, ErrorKind, - Error}; +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, + NameConstraints, +}; use std::cell::RefCell; +use std::io::{Error, ErrorKind, Read, Result as ioResult, Write}; use std::rc::Rc; mod util; -fn verify_cert_basic(cert :&Certificate) { +fn verify_cert_basic(cert: &Certificate) { let cert_pem = cert.serialize_pem().unwrap(); println!("{cert_pem}"); @@ -22,14 +23,15 @@ fn verify_cert_basic(cert :&Certificate) { let mut builder = X509StoreBuilder::new().unwrap(); builder.add_cert(x509.clone()).unwrap(); - let store :X509Store = builder.build(); + 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(); + }) + .unwrap(); } // TODO implement Debug manually instead of @@ -39,24 +41,27 @@ struct PipeInner([Vec; 2]); #[derive(Debug)] struct PipeEnd { - read_pos :usize, + read_pos: usize, /// Which end of the pipe - end_idx :usize, - inner :Rc>, + end_idx: usize, + inner: Rc>, } 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, - }) + ( + PipeEnd { + read_pos: 0, + end_idx: 0, + inner: inner.clone(), + }, + PipeEnd { + read_pos: 0, + end_idx: 1, + inner, + }, + ) } impl Write for PipeEnd { @@ -72,7 +77,7 @@ impl Write for PipeEnd { impl Read for PipeEnd { fn read(&mut self, mut buf: &mut [u8]) -> ioResult { let inner = self.inner.borrow_mut(); - let r_sl = &inner.0[1-self.end_idx][self.read_pos..]; + let r_sl = &inner.0[1 - self.end_idx][self.read_pos..]; if r_sl.len() == 0 { return Err(Error::new(ErrorKind::WouldBlock, "oh no!")); } @@ -83,7 +88,7 @@ impl Read for PipeEnd { } } -fn verify_cert(cert :&Certificate) { +fn verify_cert(cert: &Certificate) { verify_cert_basic(cert); let cert_pem = cert.serialize_pem().unwrap(); let key = cert.serialize_private_key_der(); @@ -91,7 +96,7 @@ fn verify_cert(cert :&Certificate) { verify_cert_ca(&cert_pem, &key, &cert_pem); } -fn verify_cert_ca(cert_pem :&str, key :&[u8], ca_cert_pem :&str) { +fn verify_cert_ca(cert_pem: &str, key: &[u8], ca_cert_pem: &str) { println!("{cert_pem}"); println!("{ca_cert_pem}"); @@ -99,11 +104,10 @@ fn verify_cert_ca(cert_pem :&str, key :&[u8], ca_cert_pem :&str) { 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 store: X509Store = builder.build(); let srv = SslMethod::tls_server(); let mut ssl_srv_ctx = SslAcceptor::mozilla_modern(srv).unwrap(); @@ -148,8 +152,8 @@ fn verify_cert_ca(cert_pem :&str, key :&[u8], ca_cert_pem :&str) { } }; - const HELLO_FROM_SRV :&[u8] = b"hello from server"; - const HELLO_FROM_CLN :&[u8] = b"hello from client"; + 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(); @@ -157,7 +161,7 @@ fn verify_cert_ca(cert_pem :&str, key :&[u8], ca_cert_pem :&str) { // TODO read the data we just wrote from the streams } -fn verify_csr(cert :&Certificate) { +fn verify_csr(cert: &Certificate) { let csr = cert.serialize_request_pem().unwrap(); println!("{csr}"); let key = cert.serialize_private_key_der(); @@ -314,8 +318,12 @@ fn test_openssl_separate_ca() { let ca_cert_pem = ca_cert.serialize_pem().unwrap(); let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]); - params.distinguished_name.push(DnType::OrganizationName, "Crab widgits SE"); - params.distinguished_name.push(DnType::CommonName, "Dev domain"); + 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_pem = cert.serialize_pem_with_signer(&ca_cert).unwrap(); let key = cert.serialize_private_key_der(); @@ -326,14 +334,21 @@ fn test_openssl_separate_ca() { #[test] fn test_openssl_separate_ca_with_printable_string() { let mut params = util::default_params(); - params.distinguished_name.push(DnType::CountryName, DnValue::PrintableString("US".to_string())); + params.distinguished_name.push( + DnType::CountryName, + DnValue::PrintableString("US".to_string()), + ); params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); let ca_cert = Certificate::from_params(params).unwrap(); let ca_cert_pem = ca_cert.serialize_pem().unwrap(); let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]); - params.distinguished_name.push(DnType::OrganizationName, "Crab widgits SE"); - params.distinguished_name.push(DnType::CommonName, "Dev domain"); + 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_pem = cert.serialize_pem_with_signer(&ca_cert).unwrap(); let key = cert.serialize_private_key_der(); @@ -351,8 +366,12 @@ fn test_openssl_separate_ca_with_other_signing_alg() { let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]); params.alg = &rcgen::PKCS_ECDSA_P384_SHA384; - params.distinguished_name.push(DnType::OrganizationName, "Crab widgits SE"); - params.distinguished_name.push(DnType::CommonName, "Dev domain"); + 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_pem = cert.serialize_pem_with_signer(&ca_cert).unwrap(); let key = cert.serialize_private_key_der(); @@ -368,18 +387,22 @@ fn test_openssl_separate_ca_name_constraints() { println!("openssl version: {:x}", openssl::version::number()); params.name_constraints = Some(NameConstraints { - permitted_subtrees : vec![GeneralSubtree::DnsName("crabs.crabs".to_string())], + 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(), + excluded_subtrees: Vec::new(), }); let ca_cert = Certificate::from_params(params).unwrap(); let ca_cert_pem = ca_cert.serialize_pem().unwrap(); let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]); - params.distinguished_name.push(DnType::OrganizationName, "Crab widgits SE"); - params.distinguished_name.push(DnType::CommonName, "Dev domain"); + 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_pem = cert.serialize_pem_with_signer(&ca_cert).unwrap(); let key = cert.serialize_private_key_der(); @@ -406,15 +429,19 @@ fn test_openssl_crl_parse() { // The properties of the CRL should match expected. let openssl_issuer = X509::from_der(&issuer.serialize_der().unwrap()).unwrap(); - let expected_last_update = Asn1Time::from_unix(crl.get_params().this_update.unix_timestamp()) - .unwrap(); + let expected_last_update = + Asn1Time::from_unix(crl.get_params().this_update.unix_timestamp()).unwrap(); assert!(openssl_crl.last_update().eq(&expected_last_update)); - let expected_next_update = Asn1Time::from_unix(crl.get_params().next_update.unix_timestamp()) - .unwrap(); + let expected_next_update = + Asn1Time::from_unix(crl.get_params().next_update.unix_timestamp()).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)); + 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(); @@ -424,7 +451,9 @@ fn test_openssl_crl_parse() { // 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")); + assert!(openssl_crl + .verify(&issuer_pkey) + .expect("failed to verify CRL signature")); } #[test] @@ -434,26 +463,41 @@ fn test_openssl_crl_dps_parse() { 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"); + 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::>(); + 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::>(); // 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::>(); + let uris = general_names + .iter() + .map(|general_name| { + general_name + .uri() + .expect("general name is not a directory name") + }) + .collect::>(); // We should find the expected URIs. - assert_eq!(uris, &["http://example.com/crl.der", "http://crls.example.com/1234", "ldap://example.com/crl.der"]); + assert_eq!( + uris, + &[ + "http://example.com/crl.der", + "http://crls.example.com/1234", + "ldap://example.com/crl.der" + ] + ); } diff --git a/tests/util.rs b/tests/util.rs index 6c4ca298..0ceba095 100644 --- a/tests/util.rs +++ b/tests/util.rs @@ -1,15 +1,19 @@ -use time::{Duration, OffsetDateTime}; use rcgen::{BasicConstraints, Certificate, CertificateParams}; -use rcgen::{CertificateRevocationList, CrlDistributionPoint, CrlIssuingDistributionPoint, CrlScope}; +use rcgen::{ + CertificateRevocationList, CrlDistributionPoint, CrlIssuingDistributionPoint, CrlScope, +}; use rcgen::{CertificateRevocationListParams, DnType, IsCa, KeyIdMethod}; -use rcgen::{KeyUsagePurpose, PKCS_ECDSA_P256_SHA256, RevocationReason, RevokedCertParams, SerialNumber}; +use rcgen::{ + KeyUsagePurpose, RevocationReason, RevokedCertParams, SerialNumber, PKCS_ECDSA_P256_SHA256, +}; +use time::{Duration, OffsetDateTime}; // 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#" +pub const ED25519_TEST_KEY_PAIR_PEM_V2: &str = r#" -----BEGIN PRIVATE KEY----- MFMCAQEwBQYDK2VwBCIEIC2pHJYjFHhK8V7mj6BnHWUVMS4CRolUlDdRXKCtguDu oSMDIQDrvH/x8Nx9untsuc6ET+ce3w7PSuLY8BLWcHdXDGvkQA== @@ -19,7 +23,7 @@ oSMDIQDrvH/x8Nx9untsuc6ET+ce3w7PSuLY8BLWcHdXDGvkQA== // 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#" +pub const ED25519_TEST_KEY_PAIR_PEM_V1: &str = r#" -----BEGIN PRIVATE KEY----- MC4CAQAwBQYDK2VwBCIEIDSat0MacDt2fokpnzuBaXvAQR6RJGS9rgIYOU2mZKld -----END PRIVATE KEY----- @@ -32,7 +36,7 @@ Generated by: openssl genpkey -algorithm RSA \ openssl pkcs8 -topk8 -nocrypt -outform pem */ #[cfg(feature = "pem")] -pub const RSA_TEST_KEY_PAIR_PEM :&str = r#" +pub const RSA_TEST_KEY_PAIR_PEM: &str = r#" -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDYjmgyV3/LSizJ XrYrATZrrPr2Edo8yiOgBLFmi4sgeGdQ5n6nhjTGfBEIP2Ia6z+hbiGOMncabEBc @@ -64,11 +68,14 @@ YPTHy8SWRA2sMII3ArhHJ8A= "#; pub fn default_params() -> CertificateParams { - let mut params = CertificateParams::new(vec![ - "crabs.crabs".to_string(), "localhost".to_string(), - ]); - params.distinguished_name.push(DnType::OrganizationName, "Crab widgits SE"); - params.distinguished_name.push(DnType::CommonName, "Master CA"); + let mut params = + CertificateParams::new(vec!["crabs.crabs".to_string(), "localhost".to_string()]); + params + .distinguished_name + .push(DnType::OrganizationName, "Crab widgits SE"); + params + .distinguished_name + .push(DnType::CommonName, "Master CA"); params } @@ -76,24 +83,30 @@ pub fn default_params() -> CertificateParams { pub fn test_crl() -> (CertificateRevocationList, Certificate) { let mut issuer = default_params(); issuer.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); - issuer.key_usages = vec![KeyUsagePurpose::KeyCertSign, KeyUsagePurpose::DigitalSignature, KeyUsagePurpose::CrlSign]; + issuer.key_usages = vec![ + KeyUsagePurpose::KeyCertSign, + KeyUsagePurpose::DigitalSignature, + KeyUsagePurpose::CrlSign, + ]; let issuer = Certificate::from_params(issuer).unwrap(); let now = OffsetDateTime::now_utc(); let next_week = now + Duration::weeks(1); - let revoked_cert = RevokedCertParams{ + 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 crl = CertificateRevocationListParams{ + let crl = 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()] }, + 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], @@ -109,12 +122,15 @@ pub fn test_crl() -> (CertificateRevocationList, Certificate) { pub fn cert_with_crl_dps() -> Vec { let mut params = 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![ + "http://example.com/crl.der".to_string(), + "http://crls.example.com/1234".to_string(), + ], }, - CrlDistributionPoint{ + CrlDistributionPoint { uris: vec!["ldap://example.com/crl.der".to_string()], - } + }, ]; let cert = Certificate::from_params(params).unwrap(); diff --git a/tests/webpki.rs b/tests/webpki.rs index 7b809622..47f760af 100644 --- a/tests/webpki.rs +++ b/tests/webpki.rs @@ -1,22 +1,30 @@ +use rcgen::{ + BasicConstraints, Certificate, CertificateParams, DnType, IsCa, KeyPair, RemoteKeyPair, +}; +use rcgen::{ + CertificateRevocationList, CertificateRevocationListParams, RevocationReason, RevokedCertParams, +}; #[cfg(feature = "x509-parser")] use rcgen::{CertificateSigningRequest, DnValue}; -use rcgen::{BasicConstraints, Certificate, CertificateParams, DnType, IsCa, KeyPair, RemoteKeyPair}; -use rcgen::{KeyUsagePurpose, ExtendedKeyUsagePurpose, SerialNumber}; -use rcgen::{CertificateRevocationList, CertificateRevocationListParams, RevocationReason, RevokedCertParams}; -use webpki::{EndEntityCert, TrustAnchor, BorrowedCertRevocationList, CertRevocationList, KeyUsage}; +use rcgen::{ExtendedKeyUsagePurpose, KeyUsagePurpose, SerialNumber}; use webpki::SignatureAlgorithm; -use webpki::{Time, DnsNameRef}; +use webpki::{ + BorrowedCertRevocationList, CertRevocationList, EndEntityCert, KeyUsage, TrustAnchor, +}; +use webpki::{DnsNameRef, Time}; -use ring::{rand::SystemRandom}; -use ring::signature::{self, EcdsaKeyPair, EcdsaSigningAlgorithm, - Ed25519KeyPair, KeyPair as _, RsaEncoding, RsaKeyPair}; +use ring::rand::SystemRandom; +use ring::signature::{ + self, EcdsaKeyPair, EcdsaSigningAlgorithm, Ed25519KeyPair, KeyPair as _, RsaEncoding, + RsaKeyPair, +}; use std::convert::TryFrom; -use time::{OffsetDateTime, Duration}; +use time::{Duration, OffsetDateTime}; mod util; -fn sign_msg_ecdsa(cert :&Certificate, msg :&[u8], alg :&'static EcdsaSigningAlgorithm) -> Vec { +fn sign_msg_ecdsa(cert: &Certificate, msg: &[u8], alg: &'static EcdsaSigningAlgorithm) -> Vec { let pk_der = cert.serialize_private_key_der(); let key_pair = EcdsaKeyPair::from_pkcs8(&alg, &pk_der).unwrap(); let system_random = SystemRandom::new(); @@ -24,32 +32,42 @@ fn sign_msg_ecdsa(cert :&Certificate, msg :&[u8], alg :&'static EcdsaSigningAlgo signature.as_ref().to_vec() } -fn sign_msg_ed25519(cert :&Certificate, msg :&[u8]) -> Vec { +fn sign_msg_ed25519(cert: &Certificate, msg: &[u8]) -> Vec { let pk_der = cert.serialize_private_key_der(); let key_pair = Ed25519KeyPair::from_pkcs8_maybe_unchecked(&pk_der).unwrap(); let signature = key_pair.sign(&msg); signature.as_ref().to_vec() } -fn sign_msg_rsa(cert :&Certificate, msg :&[u8], encoding :&'static dyn RsaEncoding) -> Vec { +fn sign_msg_rsa(cert: &Certificate, msg: &[u8], encoding: &'static dyn RsaEncoding) -> Vec { let pk_der = cert.serialize_private_key_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(); + key_pair + .sign(encoding, &system_random, &msg, &mut signature) + .unwrap(); signature } -fn check_cert<'a, 'b>(cert_der :&[u8], cert :&'a Certificate, alg :&SignatureAlgorithm, - sign_fn :impl FnOnce(&'a Certificate, &'b [u8]) -> Vec) { +fn check_cert<'a, 'b>( + cert_der: &[u8], + cert: &'a Certificate, + alg: &SignatureAlgorithm, + sign_fn: impl FnOnce(&'a Certificate, &'b [u8]) -> Vec, +) { println!("{}", cert.serialize_pem().unwrap()); check_cert_ca(cert_der, cert, cert_der, alg, alg, sign_fn); } -fn check_cert_ca<'a, 'b>(cert_der :&[u8], cert :&'a Certificate, ca_der :&[u8], - cert_alg :&SignatureAlgorithm, ca_alg :&SignatureAlgorithm, - sign_fn :impl FnOnce(&'a Certificate, &'b [u8]) -> Vec) { +fn check_cert_ca<'a, 'b>( + cert_der: &[u8], + cert: &'a Certificate, + ca_der: &[u8], + cert_alg: &SignatureAlgorithm, + ca_alg: &SignatureAlgorithm, + sign_fn: impl FnOnce(&'a Certificate, &'b [u8]) -> Vec, +) { let trust_anchor = TrustAnchor::try_from_cert_der(&ca_der).unwrap(); let trust_anchor_list = &[trust_anchor]; let end_entity_cert = EndEntityCert::try_from(cert_der).unwrap(); @@ -58,29 +76,29 @@ fn check_cert_ca<'a, 'b>(cert_der :&[u8], cert :&'a Certificate, ca_der :&[u8], let time = Time::from_seconds_since_unix_epoch(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(), - &[], - ).expect("valid TLS server cert"); + end_entity_cert + .verify_for_usage( + &[&cert_alg, &ca_alg], + &trust_anchor_list[..], + &[], + time, + KeyUsage::server_auth(), + &[], + ) + .expect("valid TLS server cert"); // (2/3) Check that the cert is valid for the given DNS name let dns_name = DnsNameRef::try_from_ascii_str("crabs.crabs").unwrap(); - end_entity_cert.verify_is_valid_for_subject_name( - webpki::SubjectNameRef::from(dns_name) - ).expect("valid for DNS name"); + end_entity_cert + .verify_is_valid_for_subject_name(webpki::SubjectNameRef::from(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, msg); - end_entity_cert.verify_signature( - &cert_alg, - msg, - &signature, - ).expect("signature is valid"); + end_entity_cert + .verify_signature(&cert_alg, msg, &signature) + .expect("signature is valid"); } #[test] @@ -91,8 +109,7 @@ fn test_webpki() { // Now verify the certificate. let cert_der = cert.serialize_der().unwrap(); - let sign_fn = |cert, msg| sign_msg_ecdsa(cert, msg, - &signature::ECDSA_P256_SHA256_ASN1_SIGNING); + let sign_fn = |cert, msg| sign_msg_ecdsa(cert, msg, &signature::ECDSA_P256_SHA256_ASN1_SIGNING); check_cert(&cert_der, &cert, &webpki::ECDSA_P256_SHA256, sign_fn); } @@ -106,8 +123,7 @@ fn test_webpki_256() { // Now verify the certificate. let cert_der = cert.serialize_der().unwrap(); - let sign_fn = |cert, msg| sign_msg_ecdsa(cert, msg, - &signature::ECDSA_P256_SHA256_ASN1_SIGNING); + let sign_fn = |cert, msg| sign_msg_ecdsa(cert, msg, &signature::ECDSA_P256_SHA256_ASN1_SIGNING); check_cert(&cert_der, &cert, &webpki::ECDSA_P256_SHA256, sign_fn); } @@ -121,8 +137,7 @@ fn test_webpki_384() { // Now verify the certificate. let cert_der = cert.serialize_der().unwrap(); - let sign_fn = |cert, msg| sign_msg_ecdsa(cert, msg, - &signature::ECDSA_P384_SHA384_ASN1_SIGNING); + let sign_fn = |cert, msg| sign_msg_ecdsa(cert, msg, &signature::ECDSA_P384_SHA384_ASN1_SIGNING); check_cert(&cert_der, &cert, &webpki::ECDSA_P384_SHA384, sign_fn); } @@ -184,16 +199,32 @@ fn test_webpki_rsa_given() { // Now verify the certificate. let cert_der = cert.serialize_der().unwrap(); - check_cert(&cert_der, &cert, &webpki::RSA_PKCS1_2048_8192_SHA256, - |msg, cert| sign_msg_rsa(msg, cert, &signature::RSA_PKCS1_SHA256)); + check_cert( + &cert_der, + &cert, + &webpki::RSA_PKCS1_2048_8192_SHA256, + |msg, cert| sign_msg_rsa(msg, cert, &signature::RSA_PKCS1_SHA256), + ); } #[test] fn test_webpki_rsa_combinations_given() { - let configs :&[(_, _, &'static dyn signature::RsaEncoding)] = &[ - (&rcgen::PKCS_RSA_SHA256, &webpki::RSA_PKCS1_2048_8192_SHA256, &signature::RSA_PKCS1_SHA256), - (&rcgen::PKCS_RSA_SHA384, &webpki::RSA_PKCS1_2048_8192_SHA384, &signature::RSA_PKCS1_SHA384), - (&rcgen::PKCS_RSA_SHA512, &webpki::RSA_PKCS1_2048_8192_SHA512, &signature::RSA_PKCS1_SHA512), + let configs: &[(_, _, &'static dyn signature::RsaEncoding)] = &[ + ( + &rcgen::PKCS_RSA_SHA256, + &webpki::RSA_PKCS1_2048_8192_SHA256, + &signature::RSA_PKCS1_SHA256, + ), + ( + &rcgen::PKCS_RSA_SHA384, + &webpki::RSA_PKCS1_2048_8192_SHA384, + &signature::RSA_PKCS1_SHA384, + ), + ( + &rcgen::PKCS_RSA_SHA512, + &webpki::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 { @@ -207,8 +238,9 @@ fn test_webpki_rsa_combinations_given() { // Now verify the certificate. let cert_der = cert.serialize_der().unwrap(); - check_cert(&cert_der, &cert, c.1, - |msg, cert| sign_msg_rsa(msg, cert, c.2)); + check_cert(&cert_der, &cert, c.1, |msg, cert| { + sign_msg_rsa(msg, cert, c.2) + }); } } @@ -221,16 +253,25 @@ fn test_webpki_separate_ca() { let ca_der = ca_cert.serialize_der().unwrap(); let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]); - params.distinguished_name.push(DnType::OrganizationName, "Crab widgits SE"); - params.distinguished_name.push(DnType::CommonName, "Dev domain"); + 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(); - 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); + 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, + ); } #[test] @@ -244,14 +285,24 @@ fn test_webpki_separate_ca_with_other_signing_alg() { let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]); params.alg = &rcgen::PKCS_ED25519; - params.distinguished_name.push(DnType::OrganizationName, "Crab widgits SE"); - params.distinguished_name.push(DnType::CommonName, "Dev domain"); + 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(); - check_cert_ca(&cert_der, &cert, &ca_der, - &webpki::ED25519, &webpki::ECDSA_P256_SHA256, sign_msg_ed25519); + check_cert_ca( + &cert_der, + &cert, + &ca_der, + &webpki::ED25519, + &webpki::ECDSA_P256_SHA256, + sign_msg_ed25519, + ); } #[test] @@ -263,9 +314,10 @@ fn from_remote() { self.0.public_key().as_ref() } - fn sign(&self, msg :&[u8]) -> Result, rcgen::RcgenError> { + fn sign(&self, msg: &[u8]) -> Result, rcgen::RcgenError> { let system_random = SystemRandom::new(); - self.0.sign(&system_random, msg) + self.0 + .sign(&system_random, msg) .map(|s| s.as_ref().to_owned()) .map_err(rcgen::RcgenError::from) } @@ -276,8 +328,16 @@ fn from_remote() { } let key_pair = KeyPair::generate(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap(); - let remote = EcdsaKeyPair::from_pkcs8(&signature::ECDSA_P256_SHA256_ASN1_SIGNING, &key_pair.serialize_der()).unwrap(); - let key_pair = EcdsaKeyPair::from_pkcs8(&signature::ECDSA_P256_SHA256_ASN1_SIGNING, &key_pair.serialize_der()).unwrap(); + let remote = EcdsaKeyPair::from_pkcs8( + &signature::ECDSA_P256_SHA256_ASN1_SIGNING, + &key_pair.serialize_der(), + ) + .unwrap(); + let key_pair = EcdsaKeyPair::from_pkcs8( + &signature::ECDSA_P256_SHA256_ASN1_SIGNING, + &key_pair.serialize_der(), + ) + .unwrap(); let remote = KeyPair::from_remote(Box::new(Remote(remote))).unwrap(); let mut params = util::default_params(); @@ -342,23 +402,35 @@ fn test_webpki_imported_ca() { params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); let ca_cert = Certificate::from_params(params).unwrap(); - let (ca_cert_der, ca_key_der) = (ca_cert.serialize_der().unwrap(), ca_cert.serialize_private_key_der()); + let (ca_cert_der, ca_key_der) = ( + ca_cert.serialize_der().unwrap(), + ca_cert.serialize_private_key_der(), + ); let ca_key_pair = ca_key_der.as_slice().try_into().unwrap(); - let imported_ca_cert_params = CertificateParams::from_ca_cert_der(ca_cert_der.as_slice(), ca_key_pair) - .unwrap(); + let imported_ca_cert_params = + CertificateParams::from_ca_cert_der(ca_cert_der.as_slice(), ca_key_pair).unwrap(); let imported_ca_cert = Certificate::from_params(imported_ca_cert_params).unwrap(); let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]); - params.distinguished_name.push(DnType::OrganizationName, "Crab widgits SE"); - params.distinguished_name.push(DnType::CommonName, "Dev domain"); + 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(&imported_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_cert_der, - &webpki::ECDSA_P256_SHA256, &webpki::ECDSA_P256_SHA256, sign_fn); + let sign_fn = |cert, msg| sign_msg_ecdsa(cert, msg, &signature::ECDSA_P256_SHA256_ASN1_SIGNING); + check_cert_ca( + &cert_der, + &cert, + &ca_cert_der, + &webpki::ECDSA_P256_SHA256, + &webpki::ECDSA_P256_SHA256, + sign_fn, + ); } #[cfg(feature = "x509-parser")] @@ -366,35 +438,54 @@ fn test_webpki_imported_ca() { fn test_webpki_imported_ca_with_printable_string() { use std::convert::TryInto; let mut params = util::default_params(); - params.distinguished_name.push(DnType::CountryName, DnValue::PrintableString("US".to_string())); + params.distinguished_name.push( + DnType::CountryName, + DnValue::PrintableString("US".to_string()), + ); params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); let ca_cert = Certificate::from_params(params).unwrap(); - let (ca_cert_der, ca_key_der) = (ca_cert.serialize_der().unwrap(), ca_cert.serialize_private_key_der()); + let (ca_cert_der, ca_key_der) = ( + ca_cert.serialize_der().unwrap(), + ca_cert.serialize_private_key_der(), + ); let ca_key_pair = ca_key_der.as_slice().try_into().unwrap(); - let imported_ca_cert_params = CertificateParams::from_ca_cert_der(ca_cert_der.as_slice(), ca_key_pair) - .unwrap(); + let imported_ca_cert_params = + CertificateParams::from_ca_cert_der(ca_cert_der.as_slice(), ca_key_pair).unwrap(); let imported_ca_cert = Certificate::from_params(imported_ca_cert_params).unwrap(); let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]); - params.distinguished_name.push(DnType::OrganizationName, "Crab widgits SE"); - params.distinguished_name.push(DnType::CommonName, "Dev domain"); + 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(&imported_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_cert_der, - &webpki::ECDSA_P256_SHA256, &webpki::ECDSA_P256_SHA256, sign_fn); + let sign_fn = |cert, msg| sign_msg_ecdsa(cert, msg, &signature::ECDSA_P256_SHA256_ASN1_SIGNING); + check_cert_ca( + &cert_der, + &cert, + &ca_cert_der, + &webpki::ECDSA_P256_SHA256, + &webpki::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()]); - params.distinguished_name.push(DnType::OrganizationName, "Crab widgits SE"); - params.distinguished_name.push(DnType::CommonName, "Dev domain"); + 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 csr_der = cert.serialize_request_der().unwrap(); let csr = CertificateSigningRequest::from_der(&csr_der).unwrap(); @@ -405,23 +496,27 @@ fn test_certificate_from_csr() { let ca_cert_der = ca_cert.serialize_der().unwrap(); let cert_der = csr.serialize_der_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_cert_der, - &webpki::ECDSA_P256_SHA256, &webpki::ECDSA_P256_SHA256, sign_fn); + let sign_fn = |cert, msg| sign_msg_ecdsa(cert, msg, &signature::ECDSA_P256_SHA256_ASN1_SIGNING); + check_cert_ca( + &cert_der, + &cert, + &ca_cert_der, + &webpki::ECDSA_P256_SHA256, + &webpki::ECDSA_P256_SHA256, + sign_fn, + ); } #[test] fn test_webpki_serial_number() { let mut params = util::default_params(); - params.serial_number = Some(vec![0,1,2].into()); + params.serial_number = Some(vec![0, 1, 2].into()); let cert = Certificate::from_params(params).unwrap(); // Now verify the certificate. let cert_der = cert.serialize_der().unwrap(); - let sign_fn = |cert, msg| sign_msg_ecdsa(cert, msg, - &signature::ECDSA_P256_SHA256_ASN1_SIGNING); + let sign_fn = |cert, msg| sign_msg_ecdsa(cert, msg, &signature::ECDSA_P256_SHA256_ASN1_SIGNING); check_cert(&cert_der, &cert, &webpki::ECDSA_P256_SHA256, sign_fn); } @@ -435,26 +530,31 @@ fn test_webpki_crl_parse() { let der = crl.serialize_der_with_signer(&issuer).unwrap(); // We should be able to parse the CRL DER without error. - let webpki_crl = BorrowedCertRevocationList::from_der(&der) - .expect("failed to parse CRL DER"); + let webpki_crl = BorrowedCertRevocationList::from_der(&der).expect("failed to parse CRL DER"); // Webpki represents certificate SPKIs internally without the outer SEQUENCE. // We remove that here before calling verify_signature. let issuer_spki = issuer.get_key_pair().public_key_der(); - let raw_spki = yasna::parse_der(&issuer_spki, |reader|reader.read_tagged_der()).unwrap(); + let raw_spki = yasna::parse_der(&issuer_spki, |reader| reader.read_tagged_der()).unwrap(); // We should be able to verify the CRL signature with the issuer's raw SPKI. - webpki_crl.verify_signature( - &[&webpki::ECDSA_P256_SHA256], - &raw_spki.value(), - ).expect("failed to validate CRL signature"); + webpki_crl + .verify_signature(&[&webpki::ECDSA_P256_SHA256], &raw_spki.value()) + .expect("failed to validate CRL signature"); // 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()) + 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.as_ref(), 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.serial_number.as_ref(), + 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, Time::from_seconds_since_unix_epoch(revoked_cert.revocation_time.unix_timestamp() as u64) @@ -464,10 +564,14 @@ fn test_webpki_crl_parse() { #[test] fn test_webpki_crl_revoke() { // Create an issuer CA. - let alg= &rcgen::PKCS_ECDSA_P256_SHA256; + 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]; + issuer.key_usages = vec![ + KeyUsagePurpose::KeyCertSign, + KeyUsagePurpose::DigitalSignature, + KeyUsagePurpose::CrlSign, + ]; issuer.alg = alg; let issuer = Certificate::from_params(issuer).unwrap(); let issuer_der = issuer.serialize_der().unwrap(); @@ -489,23 +593,25 @@ fn test_webpki_crl_revoke() { let time = Time::from_seconds_since_unix_epoch(unix_time); // The end entity cert should validate with the issuer without error. - end_entity_cert.verify_for_usage( - &[&webpki::ECDSA_P256_SHA256], - &trust_anchor_list[..], - &[], - time, - KeyUsage::client_auth(), - &[], - ).expect("failed to validate ee cert with issuer"); + end_entity_cert + .verify_for_usage( + &[&webpki::ECDSA_P256_SHA256], + &trust_anchor_list[..], + &[], + time, + KeyUsage::client_auth(), + &[], + ) + .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{ + 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{ + revoked_certs: vec![RevokedCertParams { serial_number: ee.get_params().serial_number.clone().unwrap(), revocation_time: now, reason_code: Some(RevocationReason::KeyCompromise),