diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d9abb155..167f03b7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,20 +13,37 @@ env: RUSTFLAGS: -D warnings jobs: + format-unstable: + name: Format (unstable) + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v6 + with: + persist-credentials: false + - name: Install rust nightly toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly + components: rustfmt + - name: Check formatting (unstable) + run: cargo fmt --all -- --check --config-path .rustfmt.unstable.toml + - if: ${{ failure() }} + run: echo "Nightly formatting check failed. Please run \`cargo +nightly fmt --config-path .rustfmt.unstable.toml\`" + lint: - name: Format & clippy + name: Clippy runs-on: ubuntu-latest continue-on-error: true steps: - name: Checkout sources - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: persist-credentials: false - name: Install rust toolchain uses: dtolnay/rust-toolchain@stable with: - components: clippy, rustfmt - - run: cargo fmt -- --check + components: clippy # `fips` and `aws_lc_rs_unstable` cannot be used together, so avoid `--all-features` - run: cargo clippy --features ring,pem,x509-parser --all-targets # rustls-cert-gen require either aws_lc_rs or ring feature @@ -45,7 +62,7 @@ jobs: toolchain: [stable, nightly] steps: - name: Checkout sources - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: persist-credentials: false - name: Install rust toolchain @@ -55,28 +72,28 @@ jobs: - name: cargo doc (ring) run: cargo doc --features ring,pem,x509-parser --document-private-items env: - RUSTDOCFLAGS: ${{ matrix.toolchain == 'nightly' && '-Dwarnings --cfg=docsrs' || '-Dwarnings' }} + RUSTDOCFLAGS: ${{ matrix.toolchain == 'nightly' && '-Dwarnings --cfg=rcgen_docsrs' || '-Dwarnings' }} - name: cargo doc (aws_lc_rs_unstable) run: cargo doc --features aws_lc_rs_unstable,pem,x509-parser --document-private-items env: - RUSTDOCFLAGS: ${{ matrix.toolchain == 'nightly' && '-Dwarnings --cfg=docsrs' || '-Dwarnings' }} + RUSTDOCFLAGS: ${{ matrix.toolchain == 'nightly' && '-Dwarnings --cfg=rcgen_docsrs' || '-Dwarnings' }} - name: cargo doc (fips) run: cargo doc --no-default-features --features fips --document-private-items env: - RUSTDOCFLAGS: ${{ matrix.toolchain == 'nightly' && '-Dwarnings --cfg=docsrs' || '-Dwarnings' }} + RUSTDOCFLAGS: ${{ matrix.toolchain == 'nightly' && '-Dwarnings --cfg=rcgen_docsrs' || '-Dwarnings' }} check-external-types: name: Validate external types appearing in public API runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: persist-credentials: false - name: Install rust toolchain uses: dtolnay/rust-toolchain@master with: - toolchain: nightly-2025-08-06 + toolchain: nightly-2025-10-18 # ^ sync with https://github.com/awslabs/cargo-check-external-types/blob/main/rust-toolchain.toml - run: cargo install --locked cargo-check-external-types - name: run cargo-check-external-types for rcgen/ @@ -91,7 +108,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: persist-credentials: false @@ -104,7 +121,7 @@ jobs: name: Check MSRV runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@master with: toolchain: 1.71.0 @@ -120,7 +137,7 @@ jobs: # https://github.com/randombit/botan-rs/issues/82 BOTAN_CONFIGURE_LINK_METHOD: copy steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: persist-credentials: false - uses: actions/cache@v4 @@ -166,7 +183,7 @@ jobs: toolchain: stable 7 months ago runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: persist-credentials: false - uses: actions/cache@v4 @@ -198,7 +215,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: persist-credentials: false - name: Install rust toolchain @@ -211,7 +228,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: persist-credentials: false - uses: actions/cache@v4 diff --git a/.rustfmt.toml b/.rustfmt.toml index 56786628..902647d7 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -1,2 +1,3 @@ +# keep in sync with .rustfmt.unstable.toml hard_tabs = true match_block_trailing_comma = true diff --git a/.rustfmt.unstable.toml b/.rustfmt.unstable.toml new file mode 100644 index 00000000..43be1c35 --- /dev/null +++ b/.rustfmt.unstable.toml @@ -0,0 +1,7 @@ +# keep in sync with .rustfmt.toml +hard_tabs = true +match_block_trailing_comma = true + +# format imports +group_imports = "StdExternalCrate" +imports_granularity = "Module" \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 846db8ef..1ea24f0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -105,6 +105,7 @@ checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba" dependencies = [ "aws-lc-fips-sys", "aws-lc-sys", + "untrusted 0.7.1", "zeroize", ] @@ -726,7 +727,7 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rcgen" -version = "0.14.4" +version = "0.14.6" dependencies = [ "aws-lc-rs", "openssl", @@ -778,7 +779,7 @@ dependencies = [ "cfg-if", "getrandom 0.2.16", "libc", - "untrusted", + "untrusted 0.9.0", "windows-sys 0.52.0", ] @@ -856,7 +857,7 @@ dependencies = [ "aws-lc-rs", "ring", "rustls-pki-types", - "untrusted", + "untrusted 0.9.0", ] [[package]] @@ -992,6 +993,12 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "untrusted" version = "0.9.0" @@ -1238,6 +1245,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb3e137310115a65136898d2079f003ce33331a6c4b0d51f1531d1be082b6425" dependencies = [ "asn1-rs", + "aws-lc-rs", "data-encoding", "der-parser", "lazy_static", diff --git a/rcgen/Cargo.toml b/rcgen/Cargo.toml index 4095d4ed..331abacc 100644 --- a/rcgen/Cargo.toml +++ b/rcgen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rcgen" -version = "0.14.4" +version = "0.14.6" documentation = "https://docs.rs/rcgen" description.workspace = true repository.workspace = true @@ -12,11 +12,11 @@ keywords.workspace = true [features] default = ["crypto", "pem", "ring"] -aws_lc_rs = ["crypto", "dep:aws-lc-rs", "aws-lc-rs/aws-lc-sys"] -aws_lc_rs_unstable = ["aws_lc_rs", "aws-lc-rs/unstable"] +aws_lc_rs = ["crypto", "dep:aws-lc-rs", "aws-lc-rs/aws-lc-sys", "x509-parser?/verify-aws"] +aws_lc_rs_unstable = ["aws_lc_rs", "aws-lc-rs/unstable", "x509-parser?/verify-aws"] fips = ["crypto", "dep:aws-lc-rs", "aws-lc-rs/fips"] crypto = [] -ring = ["crypto", "dep:ring"] +ring = ["crypto", "dep:ring", "x509-parser?/verify"] [dependencies] aws-lc-rs = { workspace = true, optional = true } @@ -24,7 +24,7 @@ pem = { workspace = true, optional = true } pki-types = { workspace = true } ring = { workspace = true, optional = true } time = { workspace = true } -x509-parser = { workspace = true, features = ["verify"], optional = true } +x509-parser = { workspace = true, optional = true } yasna = { workspace = true } zeroize = { workspace = true, optional = true } @@ -39,12 +39,17 @@ required-features = ["pem"] name = "sign-leaf-with-ca" required-features = ["pem", "x509-parser"] +[[example]] +name = "sign-leaf-with-pem-files" +required-features = ["pem", "x509-parser"] + [[example]] name = "simple" required-features = ["crypto", "pem"] [package.metadata.docs.rs] -features = ["x509-parser"] +features = ["aws_lc_rs", "aws_lc_rs_unstable", "crypto", "ring", "x509-parser"] +rustdoc-args = ["--cfg", "rcgen_docsrs"] [package.metadata.cargo_check_external_types] allowed_external_types = [ @@ -52,3 +57,6 @@ allowed_external_types = [ "zeroize::Zeroize", "rustls_pki_types::*", ] + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ["cfg(rcgen_docsrs)"] } diff --git a/rcgen/examples/rsa-irc-openssl.rs b/rcgen/examples/rsa-irc-openssl.rs index 12fceae7..aacb1791 100644 --- a/rcgen/examples/rsa-irc-openssl.rs +++ b/rcgen/examples/rsa-irc-openssl.rs @@ -1,9 +1,10 @@ #[cfg(unix)] fn main() -> Result<(), Box> { - use rcgen::{date_time_ymd, CertificateParams, DistinguishedName}; use std::fmt::Write; use std::fs; + use rcgen::{date_time_ymd, CertificateParams, DistinguishedName}; + let mut params: CertificateParams = Default::default(); params.not_before = date_time_ymd(2021, 5, 19); params.not_after = date_time_ymd(4096, 1, 1); diff --git a/rcgen/examples/sign-leaf-with-ca.rs b/rcgen/examples/sign-leaf-with-ca.rs index 23be6771..bfa08eeb 100644 --- a/rcgen/examples/sign-leaf-with-ca.rs +++ b/rcgen/examples/sign-leaf-with-ca.rs @@ -1,6 +1,7 @@ +use rcgen::DnValue::PrintableString; use rcgen::{ - BasicConstraints, Certificate, CertificateParams, DnType, DnValue::PrintableString, - ExtendedKeyUsagePurpose, IsCa, Issuer, KeyPair, KeyUsagePurpose, + BasicConstraints, Certificate, CertificateParams, DnType, ExtendedKeyUsagePurpose, IsCa, + Issuer, KeyPair, KeyUsagePurpose, }; use time::{Duration, OffsetDateTime}; diff --git a/rcgen/examples/sign-leaf-with-pem-files.rs b/rcgen/examples/sign-leaf-with-pem-files.rs new file mode 100644 index 00000000..33ee4436 --- /dev/null +++ b/rcgen/examples/sign-leaf-with-pem-files.rs @@ -0,0 +1,82 @@ +//! Generate a new certificate, and sign it with an existing root or +//! intermediate certificate. +//! +//! Requires four positional command line arguments: +//! * File path to PEM containing signer's key pair +//! * File path to PEM containing signer's certificate +//! * File path for generated PEM containing output key pair +//! * File path for generated PEM containing output certificate + +use std::error::Error; +use std::fs; +use std::path::PathBuf; + +use rcgen::{CertificateParams, DnType, ExtendedKeyUsagePurpose, Issuer, KeyPair, KeyUsagePurpose}; +use time::{Duration, OffsetDateTime}; + +fn main() -> Result<(), Box> { + let mut args = std::env::args().skip(1); + + let signer_keys_file = PathBuf::from( + args.next() + .ok_or("provide signer's pem keys file as 1st argument")?, + ); + + let signer_cert_file = PathBuf::from( + args.next() + .ok_or("provide signer's pem certificate file as 2nd argument")?, + ); + + let output_keys_file = + PathBuf::from(args.next().ok_or("output pem keys file as 3rd argument")?); + + let output_cert_file = PathBuf::from(args.next().ok_or("output pem cert file as 4th fourth")?); + + // Read existing certificate authority + let keys_pem = fs::read_to_string(&signer_keys_file)?; + let cert_pem = fs::read_to_string(&signer_cert_file)?; + + let key_pair = KeyPair::from_pem(&keys_pem)?; + let signer = Issuer::from_ca_cert_pem(&cert_pem, key_pair)?; + + // Create a new signed server certificate + const DOMAIN: &str = "example.domain"; + + let sans = vec![DOMAIN.into()]; + + let mut params = CertificateParams::new(sans)?; + + params.distinguished_name.push(DnType::CommonName, DOMAIN); + params.use_authority_key_identifier_extension = true; + params.key_usages.push(KeyUsagePurpose::DigitalSignature); + params + .extended_key_usages + .push(ExtendedKeyUsagePurpose::ServerAuth); + + const DAY: Duration = Duration::days(1); + + let yesterday = OffsetDateTime::now_utc() + .checked_sub(DAY) + .ok_or("invalid yesterday")?; + + let tomorrow = OffsetDateTime::now_utc() + .checked_add(DAY) + .ok_or("invalid tomorrow")?; + + params.not_before = yesterday; + params.not_after = tomorrow; + + let output_keys = KeyPair::generate()?; + let output_cert = params.signed_by(&output_keys, &signer)?; + + // Write new certificate + fs::write(&output_keys_file, output_keys.serialize_pem())?; + fs::write(&output_cert_file, output_cert.pem())?; + + println!("Wrote signed leaf certificate:"); + println!(" keys: {}", output_keys_file.display()); + println!(" cert: {}", output_cert_file.display()); + println!(); + + Ok(()) +} diff --git a/rcgen/examples/simple.rs b/rcgen/examples/simple.rs index 927583a5..08558382 100644 --- a/rcgen/examples/simple.rs +++ b/rcgen/examples/simple.rs @@ -1,6 +1,7 @@ -use rcgen::{date_time_ymd, CertificateParams, DistinguishedName, DnType, KeyPair, SanType}; use std::fs; +use rcgen::{date_time_ymd, CertificateParams, DistinguishedName, DnType, KeyPair, SanType}; + fn main() -> Result<(), Box> { let mut params: CertificateParams = Default::default(); params.not_before = date_time_ymd(1975, 1, 1); diff --git a/rcgen/src/certificate.rs b/rcgen/src/certificate.rs index e634268f..6a238862 100644 --- a/rcgen/src/certificate.rs +++ b/rcgen/src/certificate.rs @@ -403,7 +403,7 @@ impl CertificateParams { pub_key: &K, issuer: &Issuer<'_, impl SigningKey>, ) -> Result, Error> { - let der = sign_der(&*issuer.signing_key, |writer| { + let der = sign_der(&issuer.signing_key, |writer| { let pub_key_spki = pub_key.subject_public_key_info(); // Write version writer.next().write_tagged(Tag::context(0), |writer| { diff --git a/rcgen/src/crl.rs b/rcgen/src/crl.rs index b0f2d81f..379970a5 100644 --- a/rcgen/src/crl.rs +++ b/rcgen/src/crl.rs @@ -2,8 +2,7 @@ use pem::Pem; use pki_types::CertificateRevocationListDer; use time::OffsetDateTime; -use yasna::DERWriter; -use yasna::Tag; +use yasna::{DERWriter, Tag}; use crate::key_pair::sign_der; #[cfg(feature = "pem")] @@ -203,7 +202,7 @@ impl CertificateRevocationListParams { } fn serialize_der(&self, issuer: &Issuer<'_, impl SigningKey>) -> Result, Error> { - sign_der(&*issuer.signing_key, |writer| { + sign_der(&issuer.signing_key, |writer| { // Write CRL version. // RFC 5280 §5.1.2.1: // This optional field describes the version of the encoded CRL. When diff --git a/rcgen/src/csr.rs b/rcgen/src/csr.rs index c6cb4147..c18ac9e9 100644 --- a/rcgen/src/csr.rs +++ b/rcgen/src/csr.rs @@ -10,7 +10,7 @@ use crate::{ Certificate, CertificateParams, Error, Issuer, PublicKeyData, SignatureAlgorithm, SigningKey, }; #[cfg(feature = "x509-parser")] -use crate::{DistinguishedName, SanType}; +use crate::{DistinguishedName, ExtendedKeyUsagePurpose, KeyUsagePurpose, SanType}; /// A public key, extracted from a CSR #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -75,7 +75,7 @@ pub struct CertificateSigningRequestParams { } impl CertificateSigningRequestParams { - /// Parse a certificate signing request from the ASCII PEM format + /// Parse and verify a certificate signing request from the ASCII PEM format /// /// See [`from_der`](Self::from_der) for more details. #[cfg(all(feature = "pem", feature = "x509-parser"))] @@ -84,25 +84,31 @@ impl CertificateSigningRequestParams { Self::from_der(&csr.contents().into()) } - /// Parse a certificate signing request from DER-encoded bytes + /// Parse and verify a certificate signing request from DER-encoded bytes /// - /// Currently, this only supports the `Subject Alternative Name` extension. - /// On encountering other extensions, this function will return an error. + /// Currently, this supports the following extensions: + /// - `Subject Alternative Name` (see [`SanType`]) + /// - `Key Usage` (see [`KeyUsagePurpose`]) + /// - `Extended Key Usage` (see [`ExtendedKeyUsagePurpose`]) /// - /// [`rustls_pemfile::csr()`] is often used to obtain a [`CertificateSigningRequestDer`] from + /// On encountering other extensions, this function will return [`Error::UnsupportedExtension`]. + /// If the request's signature is invalid, it will return + /// [`Error::InvalidCertificationRequestSignature`]. + /// + /// The [`PemObject`] trait is often used to obtain a [`CertificateSigningRequestDer`] from /// PEM input. If you already have a byte slice containing DER, it can trivially be converted /// into [`CertificateSigningRequestDer`] using the [`Into`] trait. /// - /// [`rustls_pemfile::csr()`]: https://docs.rs/rustls-pemfile/latest/rustls_pemfile/fn.csr.html + /// [`PemObject`]: pki_types::pem::PemObject #[cfg(feature = "x509-parser")] pub fn from_der(csr: &CertificateSigningRequestDer<'_>) -> Result { - use crate::KeyUsagePurpose; use x509_parser::prelude::FromDer; let csr = x509_parser::certification_request::X509CertificationRequest::from_der(csr) .map_err(|_| Error::CouldNotParseCertificationRequest)? .1; - csr.verify_signature().map_err(|_| Error::RingUnspecified)?; + csr.verify_signature() + .map_err(|_| Error::InvalidCertificationRequestSignature)?; let alg_oid = csr .signature_algorithm .algorithm @@ -135,37 +141,27 @@ impl CertificateSigningRequestParams { }, x509_parser::extensions::ParsedExtension::ExtendedKeyUsage(eku) => { if eku.any { - params.insert_extended_key_usage(crate::ExtendedKeyUsagePurpose::Any); + params.insert_extended_key_usage(ExtendedKeyUsagePurpose::Any); } if eku.server_auth { - params.insert_extended_key_usage( - crate::ExtendedKeyUsagePurpose::ServerAuth, - ); + params.insert_extended_key_usage(ExtendedKeyUsagePurpose::ServerAuth); } if eku.client_auth { - params.insert_extended_key_usage( - crate::ExtendedKeyUsagePurpose::ClientAuth, - ); + params.insert_extended_key_usage(ExtendedKeyUsagePurpose::ClientAuth); } if eku.code_signing { - params.insert_extended_key_usage( - crate::ExtendedKeyUsagePurpose::CodeSigning, - ); + params.insert_extended_key_usage(ExtendedKeyUsagePurpose::CodeSigning); } if eku.email_protection { params.insert_extended_key_usage( - crate::ExtendedKeyUsagePurpose::EmailProtection, + ExtendedKeyUsagePurpose::EmailProtection, ); } if eku.time_stamping { - params.insert_extended_key_usage( - crate::ExtendedKeyUsagePurpose::TimeStamping, - ); + params.insert_extended_key_usage(ExtendedKeyUsagePurpose::TimeStamping); } if eku.ocsp_signing { - params.insert_extended_key_usage( - crate::ExtendedKeyUsagePurpose::OcspSigning, - ); + params.insert_extended_key_usage(ExtendedKeyUsagePurpose::OcspSigning); } if !eku.other.is_empty() { return Err(Error::UnsupportedExtension); @@ -178,7 +174,6 @@ impl CertificateSigningRequestParams { // Not yet handled: // * is_ca - // * extended_key_usages // * name_constraints // and any other extensions. @@ -211,10 +206,11 @@ impl CertificateSigningRequestParams { #[cfg(all(test, feature = "x509-parser"))] mod tests { - use crate::{CertificateParams, ExtendedKeyUsagePurpose, KeyPair, KeyUsagePurpose}; use x509_parser::certification_request::X509CertificationRequest; use x509_parser::prelude::{FromDer, ParsedExtension}; + use crate::{CertificateParams, ExtendedKeyUsagePurpose, KeyPair, KeyUsagePurpose}; + #[test] fn dont_write_sans_extension_if_no_sans_are_present() { let mut params = CertificateParams::default(); diff --git a/rcgen/src/error.rs b/rcgen/src/error.rs index f53af19e..e6ae3961 100644 --- a/rcgen/src/error.rs +++ b/rcgen/src/error.rs @@ -10,6 +10,9 @@ pub enum Error { CouldNotParseCertificationRequest, /// The given key pair couldn't be parsed CouldNotParseKeyPair, + /// The CSR signature is invalid + #[cfg(feature = "x509-parser")] + InvalidCertificationRequestSignature, #[cfg(feature = "x509-parser")] /// Invalid subject alternative name type InvalidNameType, @@ -62,6 +65,8 @@ impl fmt::Display for Error { )?, CouldNotParseKeyPair => write!(f, "Could not parse key pair")?, #[cfg(feature = "x509-parser")] + InvalidCertificationRequestSignature => write!(f, "Invalid CSR signature")?, + #[cfg(feature = "x509-parser")] InvalidNameType => write!(f, "Invalid subject alternative name type")?, InvalidAsn1String(e) => write!(f, "{e}")?, InvalidIpAddressOctetLength(actual) => { diff --git a/rcgen/src/key_pair.rs b/rcgen/src/key_pair.rs index 1df39f46..839b049a 100644 --- a/rcgen/src/key_pair.rs +++ b/rcgen/src/key_pair.rs @@ -1,6 +1,8 @@ #[cfg(feature = "crypto")] use std::fmt; +#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] +use aws_lc_rs::unstable::signature::PqdsaKeyPair; #[cfg(feature = "pem")] use pem::Pem; #[cfg(feature = "crypto")] @@ -22,13 +24,12 @@ use crate::ring_like::{ }, {ecdsa_from_pkcs8, rsa_key_pair_public_modulus_len}, }; +use crate::sign_algo::SignatureAlgorithm; #[cfg(feature = "crypto")] use crate::sign_algo::{algo::*, SignAlgo}; +use crate::Error; #[cfg(feature = "pem")] use crate::ENCODE_CONFIG; -use crate::{sign_algo::SignatureAlgorithm, Error}; -#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] -use aws_lc_rs::unstable::signature::PqdsaKeyPair; /// A key pair variant #[allow(clippy::large_enum_variant)] @@ -629,6 +630,12 @@ pub(crate) fn sign_der( }) } +impl SigningKey for &S { + fn sign(&self, msg: &[u8]) -> Result, Error> { + (*self).sign(msg) + } +} + /// A key that can be used to sign messages pub trait SigningKey: PublicKeyData { /// Signs `msg` using the selected algorithm @@ -673,10 +680,8 @@ impl SubjectPublicKeyInfo { /// Create a `SubjectPublicKey` value from DER-encoded SubjectPublicKeyInfo bytes #[cfg(feature = "x509-parser")] pub fn from_der(spki_der: &[u8]) -> Result { - use x509_parser::{ - prelude::FromDer, - x509::{AlgorithmIdentifier, SubjectPublicKeyInfo}, - }; + use x509_parser::prelude::FromDer; + use x509_parser::x509::{AlgorithmIdentifier, SubjectPublicKeyInfo}; let (rem, spki) = SubjectPublicKeyInfo::from_der(spki_der).map_err(|e| Error::X509(e.to_string()))?; @@ -718,6 +723,16 @@ impl PublicKeyData for SubjectPublicKeyInfo { } } +impl PublicKeyData for &K { + fn der_bytes(&self) -> &[u8] { + (*self).der_bytes() + } + + fn algorithm(&self) -> &'static SignatureAlgorithm { + (*self).algorithm() + } +} + /// The public key data of a key pair pub trait PublicKeyData { /// The public key data in DER format @@ -746,11 +761,8 @@ pub(crate) fn serialize_public_key_der(key: &(impl PublicKeyData + ?Sized), writ #[cfg(all(test, feature = "crypto"))] mod test { use super::*; - - use crate::ring_like::{ - rand::SystemRandom, - signature::{EcdsaKeyPair, ECDSA_P256_SHA256_FIXED_SIGNING}, - }; + use crate::ring_like::rand::SystemRandom; + use crate::ring_like::signature::{EcdsaKeyPair, ECDSA_P256_SHA256_FIXED_SIGNING}; #[cfg(all(feature = "x509-parser", feature = "pem"))] #[test] diff --git a/rcgen/src/lib.rs b/rcgen/src/lib.rs index 5ec7c727..83816182 100644 --- a/rcgen/src/lib.rs +++ b/rcgen/src/lib.rs @@ -29,7 +29,7 @@ println!("{}", signing_key.serialize_pem()); #![forbid(unsafe_code)] #![forbid(non_ascii_idents)] #![deny(missing_docs)] -#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![cfg_attr(rcgen_docsrs, feature(doc_cfg))] #![warn(unreachable_pub)] use std::borrow::Cow; @@ -41,18 +41,6 @@ use std::net::IpAddr; use std::net::{Ipv4Addr, Ipv6Addr}; use std::ops::Deref; -#[cfg(feature = "pem")] -use pem::Pem; -use pki_types::CertificateDer; -use time::{OffsetDateTime, 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; - -use crate::string::{BmpString, Ia5String, PrintableString, TeletexString, UniversalString}; - pub use certificate::{ date_time_ymd, Attribute, BasicConstraints, Certificate, CertificateParams, CidrSubnet, CustomExtension, DnType, ExtendedKeyUsagePurpose, GeneralSubtree, IsCa, NameConstraints, @@ -65,14 +53,22 @@ pub use csr::{CertificateSigningRequest, CertificateSigningRequestParams, Public pub use error::{Error, InvalidAsn1String}; #[cfg(feature = "crypto")] pub use key_pair::KeyPair; -pub use key_pair::PublicKeyData; #[cfg(all(feature = "crypto", feature = "aws_lc_rs"))] pub use key_pair::RsaKeySize; -pub use key_pair::{SigningKey, SubjectPublicKeyInfo}; +pub use key_pair::{PublicKeyData, SigningKey, SubjectPublicKeyInfo}; +#[cfg(feature = "pem")] +use pem::Pem; +use pki_types::CertificateDer; #[cfg(feature = "crypto")] use ring_like::digest; pub use sign_algo::algo::*; pub use sign_algo::SignatureAlgorithm; +use time::{OffsetDateTime, Time}; +use yasna::models::{GeneralizedTime, ObjectIdentifier, UTCTime}; +use yasna::tags::{TAG_BMPSTRING, TAG_TELETEXSTRING, TAG_UNIVERSALSTRING}; +use yasna::{DERWriter, Tag}; + +use crate::string::{BmpString, Ia5String, PrintableString, TeletexString, UniversalString}; mod certificate; mod crl; @@ -201,7 +197,7 @@ pub struct Issuer<'a, S> { distinguished_name: Cow<'a, DistinguishedName>, key_identifier_method: Cow<'a, KeyIdMethod>, key_usages: Cow<'a, [KeyUsagePurpose]>, - signing_key: MaybeOwned<'a, S>, + signing_key: S, } impl<'a, S: SigningKey> Issuer<'a, S> { @@ -211,7 +207,7 @@ impl<'a, S: SigningKey> Issuer<'a, S> { distinguished_name: Cow::Owned(params.distinguished_name), key_identifier_method: Cow::Owned(params.key_identifier_method), key_usages: Cow::Owned(params.key_usages), - signing_key: MaybeOwned::Owned(signing_key), + signing_key, } } @@ -219,12 +215,12 @@ impl<'a, S: SigningKey> Issuer<'a, S> { /// /// Use [`Issuer::new`] instead if you want to obtain an [`Issuer`] that owns /// its parameters. - pub fn from_params(params: &'a CertificateParams, signing_key: &'a S) -> Self { + pub fn from_params(params: &'a CertificateParams, signing_key: S) -> Self { Self { distinguished_name: Cow::Borrowed(¶ms.distinguished_name), key_identifier_method: Cow::Borrowed(¶ms.key_identifier_method), key_usages: Cow::Borrowed(¶ms.key_usages), - signing_key: MaybeOwned::Borrowed(signing_key), + signing_key, } } @@ -256,7 +252,7 @@ impl<'a, S: SigningKey> Issuer<'a, S> { distinguished_name: Cow::Owned(DistinguishedName::from_name( &x509.tbs_certificate.subject, )?), - signing_key: MaybeOwned::Owned(signing_key), + signing_key, }) } @@ -291,22 +287,6 @@ impl<'a, S> fmt::Debug for Issuer<'a, S> { } } -enum MaybeOwned<'a, T> { - Owned(T), - Borrowed(&'a T), -} - -impl Deref for MaybeOwned<'_, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - match self { - MaybeOwned::Owned(t) => t, - MaybeOwned::Borrowed(t) => t, - } - } -} - // https://tools.ietf.org/html/rfc5280#section-4.1.1 // Example certs usable as reference: @@ -1003,10 +983,10 @@ mod tests { #[cfg(feature = "x509-parser")] mod test_ip_address_from_octets { - use super::super::ip_addr_from_octets; - use super::super::Error; use std::net::IpAddr; + use super::super::{ip_addr_from_octets, Error}; + #[test] fn ipv4() { let octets = [10, 20, 30, 40]; @@ -1051,10 +1031,12 @@ 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]; diff --git a/rcgen/src/sign_algo.rs b/rcgen/src/sign_algo.rs index 2894cd5e..bc9d18b3 100644 --- a/rcgen/src/sign_algo.rs +++ b/rcgen/src/sign_algo.rs @@ -1,17 +1,16 @@ use std::fmt; use std::hash::{Hash, Hasher}; +#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] +use aws_lc_rs::unstable::signature::{ + PqdsaSigningAlgorithm, ML_DSA_44_SIGNING, ML_DSA_65_SIGNING, ML_DSA_87_SIGNING, +}; use yasna::models::ObjectIdentifier; -use yasna::DERWriter; -use yasna::Tag; +use yasna::{DERWriter, Tag}; #[cfg(feature = "crypto")] use crate::ring_like::signature::{self, EcdsaSigningAlgorithm, EdDSAParameters, RsaEncoding}; use crate::Error; -#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] -use aws_lc_rs::unstable::signature::{ - PqdsaSigningAlgorithm, ML_DSA_44_SIGNING, ML_DSA_65_SIGNING, ML_DSA_87_SIGNING, -}; #[cfg(feature = "crypto")] #[derive(Clone, Copy, Debug)] @@ -119,9 +118,8 @@ impl SignatureAlgorithm { /// The list of supported signature algorithms pub(crate) mod algo { - use crate::oid::*; - use super::*; + use crate::oid::*; /// 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 { diff --git a/rcgen/src/string.rs b/rcgen/src/string.rs index c133463d..9390f9e3 100644 --- a/rcgen/src/string.rs +++ b/rcgen/src/string.rs @@ -1,6 +1,7 @@ //! ASN.1 string types -use std::{fmt, str::FromStr}; +use std::fmt; +use std::str::FromStr; use crate::{Error, InvalidAsn1String}; @@ -578,7 +579,7 @@ impl TryFrom<&str> for UniversalString { // A `char` is any ‘Unicode code point’ other than a surrogate code point. // The code units for UTF-32 correspond exactly to Unicode code points. // (https://www.unicode.org/reports/tr19/tr19-9.html#Introduction) - // So any `char` is a valid UTF-32, we just cast it to perform the convertion. + // So any `char` is a valid UTF-32, we just cast it to perform the conversion. for char in value.chars().map(|char| char as u32) { bytes.extend(char.to_be_bytes()) } diff --git a/rustls-cert-gen/src/cert.rs b/rustls-cert-gen/src/cert.rs index 624c71d5..ddee0042 100644 --- a/rustls-cert-gen/src/cert.rs +++ b/rustls-cert-gen/src/cert.rs @@ -1,10 +1,13 @@ -use std::{fmt, fs::File, io, path::Path, str::FromStr}; +use std::fs::File; +use std::path::Path; +use std::str::FromStr; +use std::{fmt, io}; use bpaf::Bpaf; +use rcgen::DnValue::PrintableString; use rcgen::{ BasicConstraints, Certificate, CertificateParams, CertifiedIssuer, DistinguishedName, DnType, - DnValue::PrintableString, ExtendedKeyUsagePurpose, IsCa, KeyPair, KeyUsagePurpose, SanType, - SignatureAlgorithm, + ExtendedKeyUsagePurpose, IsCa, KeyPair, KeyUsagePurpose, SanType, SignatureAlgorithm, }; /// Builder to configure TLS [CertificateParams] to be finalized diff --git a/rustls-cert-gen/src/main.rs b/rustls-cert-gen/src/main.rs index 459c1093..e54c93e2 100644 --- a/rustls-cert-gen/src/main.rs +++ b/rustls-cert-gen/src/main.rs @@ -1,4 +1,6 @@ -use std::{net::IpAddr, path::PathBuf, str::FromStr}; +use std::net::IpAddr; +use std::path::PathBuf; +use std::str::FromStr; use bpaf::Bpaf; use rcgen::{Error, SanType}; diff --git a/verify-tests/src/lib.rs b/verify-tests/src/lib.rs index 4c2cc5db..aa7e6a39 100644 --- a/verify-tests/src/lib.rs +++ b/verify-tests/src/lib.rs @@ -1,14 +1,13 @@ -use time::{Duration, OffsetDateTime}; - -use rcgen::{BasicConstraints, Certificate, CertificateParams, Issuer, KeyPair}; use rcgen::{ - CertificateRevocationList, CrlDistributionPoint, CrlIssuingDistributionPoint, CrlScope, + BasicConstraints, Certificate, CertificateParams, CertificateRevocationList, + CertificateRevocationListParams, CrlDistributionPoint, CrlIssuingDistributionPoint, CrlScope, + DnType, IsCa, Issuer, KeyIdMethod, KeyPair, KeyUsagePurpose, RevocationReason, + RevokedCertParams, SerialNumber, }; -use rcgen::{CertificateRevocationListParams, DnType, IsCa, KeyIdMethod}; -use rcgen::{KeyUsagePurpose, RevocationReason, RevokedCertParams, SerialNumber}; +use time::{Duration, OffsetDateTime}; // Generated by adding `println!("{}", cert.serialize_private_key_pem());` -// to the test_webpki_25519 test and panicing explicitly. +// to the test_webpki_25519 test and panicking explicitly. // This is a "v2" key containing the public key as well as the // private one. pub const ED25519_TEST_KEY_PAIR_PEM_V2: &str = r#" diff --git a/verify-tests/tests/botan.rs b/verify-tests/tests/botan.rs index 29715732..76c48a60 100644 --- a/verify-tests/tests/botan.rs +++ b/verify-tests/tests/botan.rs @@ -1,12 +1,11 @@ #![cfg(feature = "x509-parser")] +use rcgen::{ + BasicConstraints, Certificate, CertificateParams, CertificateRevocationListParams, DnType, + DnValue, IsCa, Issuer, KeyPair, KeyUsagePurpose, RevocationReason, RevokedCertParams, + SerialNumber, +}; use time::{Duration, OffsetDateTime}; - -use rcgen::{BasicConstraints, Certificate, CertificateParams, DnType, IsCa, Issuer}; -use rcgen::{CertificateRevocationListParams, RevocationReason, RevokedCertParams}; -use rcgen::{DnValue, KeyPair}; -use rcgen::{KeyUsagePurpose, SerialNumber}; - use verify_tests as util; fn default_params() -> (CertificateParams, KeyPair) { diff --git a/verify-tests/tests/generic.rs b/verify-tests/tests/generic.rs index 108d5eaa..3b6f668e 100644 --- a/verify-tests/tests/generic.rs +++ b/verify-tests/tests/generic.rs @@ -105,10 +105,8 @@ mod test_x509_custom_ext { #[cfg(feature = "x509-parser")] mod test_csr_custom_attributes { use rcgen::{Attribute, CertificateParams, KeyPair}; - use x509_parser::{ - der_parser::Oid, - prelude::{FromDer, X509CertificationRequest}, - }; + use x509_parser::der_parser::Oid; + use x509_parser::prelude::{FromDer, X509CertificationRequest}; /// Test serializing a CSR with custom attributes. /// This test case uses `challengePassword` from [RFC 2985], a simple diff --git a/verify-tests/tests/openssl.rs b/verify-tests/tests/openssl.rs index 4286d200..a99cad49 100644 --- a/verify-tests/tests/openssl.rs +++ b/verify-tests/tests/openssl.rs @@ -11,7 +11,6 @@ use openssl::ssl::{HandshakeError, SslAcceptor, SslConnector, SslMethod}; use openssl::stack::Stack; use openssl::x509::store::{X509Store, X509StoreBuilder}; use openssl::x509::{CrlStatus, X509Crl, X509Req, X509StoreContext, X509}; - use rcgen::{ BasicConstraints, Certificate, CertificateParams, DnType, DnValue, GeneralSubtree, IsCa, Issuer, KeyPair, NameConstraints, diff --git a/verify-tests/tests/webpki.rs b/verify-tests/tests/webpki.rs index 89bc7ff5..75ad79c8 100644 --- a/verify-tests/tests/webpki.rs +++ b/verify-tests/tests/webpki.rs @@ -5,27 +5,24 @@ use aws_lc_rs::unstable::signature::{ PqdsaKeyPair, PqdsaSigningAlgorithm, ML_DSA_44_SIGNING, ML_DSA_65_SIGNING, ML_DSA_87_SIGNING, }; use pki_types::{CertificateDer, ServerName, SignatureVerificationAlgorithm, UnixTime}; +use rcgen::{ + BasicConstraints, Certificate, CertificateParams, CertificateRevocationListParams, DnType, + Error, ExtendedKeyUsagePurpose, IsCa, Issuer, KeyPair, KeyUsagePurpose, PublicKeyData, + RevocationReason, RevokedCertParams, SerialNumber, SigningKey, +}; +#[cfg(feature = "x509-parser")] +use rcgen::{CertificateSigningRequestParams, DnValue}; use ring::rand::SystemRandom; use ring::signature::{self, EcdsaKeyPair, EcdsaSigningAlgorithm, Ed25519KeyPair, KeyPair as _}; #[cfg(feature = "pem")] use ring::signature::{RsaEncoding, RsaKeyPair}; use time::{Duration, OffsetDateTime}; +use verify_tests as util; use webpki::{ anchor_from_trusted_cert, BorrowedCertRevocationList, CertRevocationList, EndEntityCert, KeyUsage, RevocationOptionsBuilder, }; -use rcgen::{ - BasicConstraints, Certificate, CertificateParams, DnType, Error, IsCa, Issuer, KeyPair, - PublicKeyData, SigningKey, -}; -use rcgen::{CertificateRevocationListParams, RevocationReason, RevokedCertParams}; -#[cfg(feature = "x509-parser")] -use rcgen::{CertificateSigningRequestParams, DnValue}; -use rcgen::{ExtendedKeyUsagePurpose, KeyUsagePurpose, SerialNumber}; - -use verify_tests as util; - fn sign_msg_ecdsa(key_pair: &KeyPair, msg: &[u8], alg: &'static EcdsaSigningAlgorithm) -> Vec { let pk_der = key_pair.serialize_der(); let key_pair =