diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 6330417..518f3ca 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -48,6 +48,9 @@ jobs: - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} + - name: Cargo update (fix for MSRV) + run: cargo update -p time --precise 0.3.41 + if: matrix.rust == '1.67.1' - run: RUSTFLAGS="-D warnings" cargo check --locked --all-targets --all-features test: @@ -99,6 +102,17 @@ jobs: components: clippy - run: cargo clippy --locked --all-features -- -D warnings + readme: + name: Check if README is up to date + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Install stable toolchain + uses: dtolnay/rust-toolchain@stable + - run: | + cargo install cargo-rdme + cargo rdme --check --no-fail-on-warnings + doc: name: Build documentation needs: check diff --git a/CHANGELOG.md b/CHANGELOG.md index e813581..8f4b501 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,32 @@ ### Thanks +## 0.18.0 + +### Added/Changed + +- Update lock file and dependencies +- Fix clippy warnings +- Visitor: add method to visit unknown extension and those with parse errors +- Add new feature `verify-aws` to used `aws-lc-rs` as crypto provider instead of `ring` + - The features are exclusive, so only one should be used + - If both are specified, `aws-lc-rs` is used (but both dependencies are included) +- Add `as_raw` methods to `X509Certificate`, `CertificateRevocationList` and `X509CertificationRequest` + - This method exposes the raw ASN.1 DER bytes used to build the object (#217) + +Extensions: +- Add support for SubjectInfoAccess extension +- GeneralName: add a new variant `Invalid` so an invalid entry does not stop + parsing for the entire list of names (for ex in SAN) + +### Fixed + +- PEM: ignore lines in comments which contain invalid UTF-8 characters (#180) + +### Thanks + +- Daniel McCarney + ## 0.17.0 ### Added/Changed/Fixed diff --git a/Cargo.lock b/Cargo.lock index b3d3b29..324afc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -592,18 +592,18 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "thiserror" -version = "2.0.14" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.14" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote", @@ -868,7 +868,7 @@ dependencies = [ [[package]] name = "x509-parser" -version = "0.18.0-beta.1" +version = "0.19.0-beta.1" dependencies = [ "asn1-rs", "aws-lc-rs", diff --git a/Cargo.toml b/Cargo.toml index 1eb3e11..9f07381 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "x509-parser" -version = "0.18.0-beta.1" +version = "0.19.0-beta.1" description = "Parser for the X.509 v3 format (RFC 5280 certificates)" license = "MIT OR Apache-2.0" keywords = ["X509","Certificate","parser","nom"] diff --git a/README.md b/README.md index 93492b6..b049fd9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ - - [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE-MIT) [![Apache License 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](./LICENSE-APACHE) [![docs.rs](https://docs.rs/x509-parser/badge.svg)](https://docs.rs/x509-parser) @@ -8,6 +6,8 @@ [![Github CI](https://github.com/rusticata/x509-parser/workflows/Continuous%20integration/badge.svg)](https://github.com/rusticata/x509-parser/actions) [![Minimum rustc version](https://img.shields.io/badge/rustc-1.67.1+-lightgray.svg)](#rust-version-requirements) + + # X.509 Parser A X.509 v3 ([RFC5280]) parser, implemented with the [nom](https://github.com/Geal/nom) @@ -22,25 +22,36 @@ and is part of the [Rusticata](https://github.com/rusticata) project. Certificates are usually encoded in two main formats: PEM (usually the most common format) or DER. A PEM-encoded certificate is a container, storing a DER object. See the -[`pem`](https://docs.rs/x509-parser/latest/x509_parser/pem/index.html) module for more documentation. +[`pem`] module for more documentation. To decode a DER-encoded certificate, the main parsing method is -`X509Certificate::from_der` ( -part of the [`FromDer`](https://docs.rs/x509-parser/latest/x509_parser/prelude/trait.FromDer.html) trait -), which builds a -[`X509Certificate`](https://docs.rs/x509-parser/latest/x509_parser/certificate/struct.X509Certificate.html) object. +`X509Certificate::parse_der` (from the [`DerParser`](asn1_rs::DerParser) trait) +which builds a [`X509Certificate`] object. + +The [`parse_der`](asn1_rs::DerParser) trait takes an [`Input`](asn1_rs::Input) +object, which can be built from the input bytes. This helps tracking offsets (in case of +error). +For convenience, +the [`X509Certificate::from_der`] method (part of the [`FromDer`] trait) +does the same directly on the input bytes, but it can loose the precise error location. An alternative method is to use [`X509CertificateParser`](https://docs.rs/x509-parser/latest/x509_parser/certificate/struct.X509CertificateParser.html), which allows specifying parsing options (for example, not automatically parsing option contents). +Similar methods are provided for other X.509 objects: +- [`X509Certificate`] for X.509 Certificates +- [`CertificateRevocationList`] for X.509 v2 Certificate Revocation List (CRL) +- [`X509CertificationRequest`](https://docs.rs/x509-parser/latest/x509_parser/certification_request/struct.X509CertificationRequest.html) for Certification Signing Request (CSR) + The returned objects for parsers follow the definitions of the RFC. This means that accessing fields is done by accessing struct members recursively. Some helper functions are provided, for -example [`X509Certificate::issuer()`](https://docs.rs/x509-parser/latest/x509_parser/certificate/struct.X509Certificate.html#method.issuer) returns the +example `X509Certificate::issuer()` returns the same as accessing `.tbs_certificate.issuer`. -For PEM-encoded certificates, use the [`pem`](https://docs.rs/x509-parser/latest/x509_parser/pem/index.html) module. +For PEM-encoded certificates, use the [`pem`] module. -This crate also provides visitor traits: [`X509CertificateVisitor`](crate::visitor::X509CertificateVisitor). +This crate also provides visitor traits: `X509CertificateVisitor`, `CertificateRevocationListVisitor`. +See the [`visitor`] module. # Examples @@ -51,7 +62,8 @@ use x509_parser::prelude::*; static IGCA_DER: &[u8] = include_bytes!("../assets/IGC_A.der"); -let res = X509Certificate::from_der(IGCA_DER); +let input = Input::from(IGCA_DER); +let res = X509Certificate::parse_der(input); match res { Ok((rem, cert)) => { assert!(rem.is_empty()); @@ -65,9 +77,8 @@ match res { To parse a CRL and print information about revoked certificates: ```rust -# -# -let res = CertificateRevocationList::from_der(DER); +let input = Input::from(DER); +let res = CertificateRevocationList::parse_der(input); match res { Ok((_rem, crl)) => { for revoked in crl.iter_revoked_certificates() { @@ -85,7 +96,7 @@ See also `examples/print-cert.rs`. - The `verify` and `verify-aws` features adds support for (cryptographic) signature verification, based on `ring` or `aws-lc` respectively. It adds the - [`X509Certificate::verify_signature()`](https://docs.rs/x509-parser/latest/x509_parser/certificate/struct.X509Certificate.html#method.verify_signature) + [`X509Certificate::verify_signature()`] method to `X509Certificate`. ```rust @@ -99,6 +110,13 @@ pub fn check_signature(cert: &X509Certificate<'_>, issuer: &X509Certificate<'_>) } ``` +- The `verify-aws` feature offers the same support for signature verification, but based on + `aws-lc-rs` instead of `ring`. + +- _Note_: if both `verify` and `verify-aws` features are enabled (which happens when using + `--all-features`), the verification will use `aws-lc-rs`. It also has the side-effect of + having a dependency on `ring`, even if it is not used. + - The `validate` features add methods to run more validation functions on the certificate structure and values using the [`Validate`](https://docs.rs/x509-parser/latest/x509_parser/validate/trait.Validate.html) trait. It does not validate any cryptographic parameter (see `verify` above). @@ -109,7 +127,18 @@ pub fn check_signature(cert: &X509Certificate<'_>, issuer: &X509Certificate<'_>) dependencies and for proc-macro attributes support. [RFC5280]: https://tools.ietf.org/html/rfc5280 - + + + +## MSRV policy + +This projects tries to maintain compatibility with older version of the rust compiler for the following +durations: +- `master` branch: _12 months_ minimum +- older releases: about 24 months + +However, due to dependencies and the fact that some crate writers tend to require very recent +versions of the compiler, this can prove to be difficult. These numbers are given as _best-effort_. ## Changes diff --git a/UPGRADING.md b/UPGRADING.md index e60a49c..69a8a23 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -1,6 +1,6 @@ -## Upgrading from 0.17 to 0.18 +## Upgrading from 0.18 to 0.19 -The major changes in version 0.18 are described here. +The major changes in version 0.19 are described here. ### Cargo and dependencies @@ -53,4 +53,4 @@ The following changes are not part of this crate, but are exposed in `Any` objec - Many parsers have been replaced by derive attributes (like `Sequence` or `Choice`) when possible. This reduces risks of errors and makes code more easier to maintain + Encoders are not derived for now -- File `extensions/mod.rs` has been split in multiple files \ No newline at end of file +- File `extensions/mod.rs` has been split in multiple files diff --git a/src/lib.rs b/src/lib.rs index d7e1908..d52deb6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,3 @@ -//! [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE-MIT) -//! [![Apache License 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](./LICENSE-APACHE) -//! [![docs.rs](https://docs.rs/x509-parser/badge.svg)](https://docs.rs/x509-parser) -//! [![crates.io](https://img.shields.io/crates/v/x509-parser.svg)](https://crates.io/crates/x509-parser) -//! [![Download numbers](https://img.shields.io/crates/d/x509-parser.svg)](https://crates.io/crates/x509-parser) -//! [![Github CI](https://github.com/rusticata/x509-parser/workflows/Continuous%20integration/badge.svg)](https://github.com/rusticata/x509-parser/actions) -//! [![Minimum rustc version](https://img.shields.io/badge/rustc-1.67.1+-lightgray.svg)](#rust-version-requirements) -//! //! # X.509 Parser //! //! A X.509 v3 ([RFC5280]) parser, implemented with the [nom](https://github.com/Geal/nom) @@ -20,25 +12,37 @@ //! //! Certificates are usually encoded in two main formats: PEM (usually the most common format) or //! DER. A PEM-encoded certificate is a container, storing a DER object. See the -//! [`pem`](pem/index.html) module for more documentation. +//! [`pem`] module for more documentation. //! //! To decode a DER-encoded certificate, the main parsing method is -//! `X509Certificate::from_der` ( -//! part of the [`FromDer`](prelude/trait.FromDer.html) trait -//! ), which builds a -//! [`X509Certificate`](certificate/struct.X509Certificate.html) object. +//! `X509Certificate::parse_der` (from the [`DerParser`](asn1_rs::DerParser) trait) +//! which builds a [`X509Certificate`] object. +//! +//! The [`parse_der`](asn1_rs::DerParser) trait takes an [`Input`](asn1_rs::Input) +//! object, which can be built from the input bytes. This helps tracking offsets (in case of +//! error). +//! For convenience, +//! the [`X509Certificate::from_der`] method (part of the [`FromDer`] trait) +//! does the same directly on the input bytes, but it can loose the precise error location. //! -//! An alternative method is to use [`X509CertificateParser`](certificate/struct.X509CertificateParser.html), +//! An alternative method is to use [`X509CertificateParser`](crate::certificate::X509CertificateParser), //! which allows specifying parsing options (for example, not automatically parsing option contents). +//! +//! Similar methods are provided for other X.509 objects: +//! - [`X509Certificate`] for X.509 Certificates +//! - [`CertificateRevocationList`] for X.509 v2 Certificate Revocation List (CRL) +//! - [`X509CertificationRequest`](crate::certification_request::X509CertificationRequest) for Certification Signing Request (CSR) + //! //! The returned objects for parsers follow the definitions of the RFC. This means that accessing //! fields is done by accessing struct members recursively. Some helper functions are provided, for -//! example [`X509Certificate::issuer()`](certificate/struct.X509Certificate.html#method.issuer) returns the +//! example [`X509Certificate::issuer()`](crate::certificate::TbsCertificate::issuer()) returns the //! same as accessing `.tbs_certificate.issuer`. //! -//! For PEM-encoded certificates, use the [`pem`](pem/index.html) module. +//! For PEM-encoded certificates, use the [`pem`] module. //! -//! This crate also provides visitor traits: [`X509CertificateVisitor`](crate::visitor::X509CertificateVisitor). +//! This crate also provides visitor traits: [`X509CertificateVisitor`](crate::visitor::X509CertificateVisitor), [`CertificateRevocationListVisitor`](crate::visitor::CertificateRevocationListVisitor). +//! See the [`visitor`] module. //! //! # Examples //! @@ -50,7 +54,8 @@ //! static IGCA_DER: &[u8] = include_bytes!("../assets/IGC_A.der"); //! //! # fn main() { -//! let res = X509Certificate::from_der(IGCA_DER); +//! let input = Input::from(IGCA_DER); +//! let res = X509Certificate::parse_der(input); //! match res { //! Ok((rem, cert)) => { //! assert!(rem.is_empty()); @@ -70,7 +75,8 @@ //! # static DER: &[u8] = include_bytes!("../assets/example.crl"); //! # //! # fn main() { -//! let res = CertificateRevocationList::from_der(DER); +//! let input = Input::from(DER); +//! let res = CertificateRevocationList::parse_der(input); //! match res { //! Ok((_rem, crl)) => { //! for revoked in crl.iter_revoked_certificates() { @@ -89,7 +95,7 @@ //! //! - The `verify` and `verify-aws` features adds support for (cryptographic) signature verification, based on `ring` or `aws-lc` respectively. //! It adds the -//! [`X509Certificate::verify_signature()`](certificate/struct.X509Certificate.html#method.verify_signature) +//! [`X509Certificate::verify_signature()`] method //! to `X509Certificate`. //! //! ```rust @@ -113,7 +119,7 @@ //! having a dependency on `ring`, even if it is not used. //! //! - The `validate` features add methods to run more validation functions on the certificate structure -//! and values using the [`Validate`](validate/trait.Validate.html) trait. +//! and values using the [`Validate`](crate::validate::Validate) trait. //! It does not validate any cryptographic parameter (see `verify` above). //! //! ## Rust version requirements diff --git a/src/prelude.rs b/src/prelude.rs index 7d0f6f6..1d453f5 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -14,4 +14,4 @@ pub use crate::validate::*; pub use crate::x509::*; pub use crate::*; -pub use asn1_rs::{DerParser, FromDer}; +pub use asn1_rs::{DerParser, FromDer, Input};