aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Galenson <jgalenson@google.com>2021-08-11 14:44:14 +0000
committerJoel Galenson <jgalenson@google.com>2021-08-11 14:44:14 +0000
commitc5051d11821e3e739e35ce22bc1c7b9a9decb9b4 (patch)
tree81ab7d86c6f58cc7c9faa2cbc6b9d188d91a71a7
parent856d48fea3c60de2ec21cddbde112a5a9025af7f (diff)
downloadx509-parser-c5051d11821e3e739e35ce22bc1c7b9a9decb9b4.tar.gz
Revert "Upgrade rust/crates/x509-parser to 0.10.0"
Revert submission 1791044-der-parser-5.1.2-xml-parser-0.10.0 Reason for revert: Build breakage due to aosp/1791850 Reverted Changes: Ib0a1d7bde:Upgrade rust/crates/x509-parser to 0.10.0 I9eead6d6d:Upgrade rust/crates/der-parser to 5.1.2 Change-Id: I89f48dfb50b4504ce7021cfc8f868a3e68baf9dd
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--Android.bp147
-rw-r--r--CHANGELOG.md48
-rw-r--r--Cargo.toml8
-rw-r--r--Cargo.toml.orig6
-rw-r--r--METADATA9
-rw-r--r--README.md25
-rw-r--r--assets/csr-empty-attributes.csrbin0 -> 670 bytes
-rw-r--r--assets/ed25519.derbin268 -> 0 bytes
-rw-r--r--assets/empty.crlbin0 -> 293 bytes
-rw-r--r--assets/example.crlbin0 -> 792 bytes
-rw-r--r--assets/gen_minimal_crl.py40
-rw-r--r--assets/minimal.crlbin0 -> 359 bytes
-rw-r--r--assets/test.csr8
-rw-r--r--cargo2android.json6
-rw-r--r--examples/print-cert.rs95
-rw-r--r--examples/print-crl.rs12
-rw-r--r--patches/data-encoding_dep.patch4
-rw-r--r--src/certificate.rs603
-rw-r--r--src/certification_request.rs148
-rw-r--r--src/cri_attributes.rs56
-rw-r--r--src/error.rs2
-rw-r--r--src/extensions.rs1128
-rw-r--r--src/lib.rs36
-rw-r--r--src/objects.rs25
-rw-r--r--src/pem.rs2
-rw-r--r--src/prelude.rs3
-rw-r--r--src/revocation_list.rs272
-rw-r--r--src/time.rs13
-rw-r--r--src/traits.rs36
-rw-r--r--src/validate.rs126
-rw-r--r--src/x509.rs272
-rw-r--r--tests/readcert.rs81
-rw-r--r--tests/readcsr.rs10
-rw-r--r--tests/verify.rs11
35 files changed, 976 insertions, 2258 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index a19dcef..50631bc 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,5 @@
{
"git": {
- "sha1": "8c8e7a47c7728b3ff6e6bf91bcc54d6acd9eeca5"
+ "sha1": "2685e2f7f2015774fb1d812f37d527b9c0f6bc36"
}
}
diff --git a/Android.bp b/Android.bp
index 8d43211..8cc7969 100644
--- a/Android.bp
+++ b/Android.bp
@@ -20,4 +20,151 @@ rust_library {
"librusticata_macros",
"libthiserror",
],
+ proc_macros: ["librustversion"],
+}
+
+rust_defaults {
+ name: "x509-parser_test_defaults",
+ crate_name: "x509_parser",
+ srcs: ["src/lib.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ edition: "2018",
+ features: ["default"],
+ rustlibs: [
+ "libbase64_rust",
+ "libchrono",
+ "libder_parser",
+ "liblazy_static",
+ "libnom",
+ "liboid_registry",
+ "librusticata_macros",
+ "libthiserror",
+ ],
+ proc_macros: ["librustversion"],
+ data: ["assets/certificate.pem"],
+}
+
+rust_test_host {
+ name: "x509-parser_host_test_src_lib",
+ defaults: ["x509-parser_test_defaults"],
+ test_options: {
+ unit_test: true,
+ },
+}
+
+rust_test {
+ name: "x509-parser_device_test_src_lib",
+ defaults: ["x509-parser_test_defaults"],
+}
+
+rust_defaults {
+ name: "x509-parser_test_defaults_x509_parser",
+ crate_name: "x509_parser",
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ edition: "2018",
+ features: ["default"],
+ rustlibs: [
+ "libbase64_rust",
+ "libchrono",
+ "libder_parser",
+ "liblazy_static",
+ "libnom",
+ "liboid_registry",
+ "librusticata_macros",
+ "libthiserror",
+ "libx509_parser",
+ ],
+ proc_macros: ["librustversion"],
+}
+
+rust_test_host {
+ name: "x509-parser_host_test_tests_pem",
+ defaults: ["x509-parser_test_defaults_x509_parser"],
+ srcs: ["tests/pem.rs"],
+ test_options: {
+ unit_test: true,
+ },
+}
+
+rust_test {
+ name: "x509-parser_device_test_tests_pem",
+ defaults: ["x509-parser_test_defaults_x509_parser"],
+ srcs: ["tests/pem.rs"],
+}
+
+rust_test_host {
+ name: "x509-parser_host_test_tests_readcert",
+ defaults: ["x509-parser_test_defaults_x509_parser"],
+ srcs: ["tests/readcert.rs"],
+ test_options: {
+ unit_test: true,
+ },
+}
+
+rust_test {
+ name: "x509-parser_device_test_tests_readcert",
+ defaults: ["x509-parser_test_defaults_x509_parser"],
+ srcs: ["tests/readcert.rs"],
+}
+
+rust_test_host {
+ name: "x509-parser_host_test_tests_readcsr",
+ defaults: ["x509-parser_test_defaults_x509_parser"],
+ srcs: ["tests/readcsr.rs"],
+ test_options: {
+ unit_test: true,
+ },
+}
+
+rust_test {
+ name: "x509-parser_device_test_tests_readcsr",
+ defaults: ["x509-parser_test_defaults_x509_parser"],
+ srcs: ["tests/readcsr.rs"],
+}
+
+rust_test_host {
+ name: "x509-parser_host_test_tests_run_all_fuzz_files",
+ defaults: ["x509-parser_test_defaults_x509_parser"],
+ srcs: ["tests/run_all_fuzz_files.rs"],
+ test_options: {
+ unit_test: true,
+ },
+}
+
+rust_test {
+ name: "x509-parser_device_test_tests_run_all_fuzz_files",
+ defaults: ["x509-parser_test_defaults_x509_parser"],
+ srcs: ["tests/run_all_fuzz_files.rs"],
+}
+
+rust_test_host {
+ name: "x509-parser_host_test_tests_test01",
+ defaults: ["x509-parser_test_defaults_x509_parser"],
+ srcs: ["tests/test01.rs"],
+ test_options: {
+ unit_test: true,
+ },
+}
+
+rust_test {
+ name: "x509-parser_device_test_tests_test01",
+ defaults: ["x509-parser_test_defaults_x509_parser"],
+ srcs: ["tests/test01.rs"],
+}
+
+rust_test_host {
+ name: "x509-parser_host_test_tests_verify",
+ defaults: ["x509-parser_test_defaults_x509_parser"],
+ srcs: ["tests/verify.rs"],
+ test_options: {
+ unit_test: true,
+ },
+}
+
+rust_test {
+ name: "x509-parser_device_test_tests_verify",
+ defaults: ["x509-parser_test_defaults_x509_parser"],
+ srcs: ["tests/verify.rs"],
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dfcd92a..2ca97ef 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,54 +6,6 @@
### Thanks
-## 0.10.0
-
-### Added
-
-- Add the `Validate` trait to run post-parsing validations of X.509 structure
-- Add the `FromDer` trait to unify parsing methods and visibility (#85)
-- Add method to format X509Name using a given registry
-- Add `X509Certificate::public_key()` method
-- Add ED25519 as a signature algorithm (#95)
-- Add support for extensions (#86):
- - CRL Distribution Points
-- Add `X509CertificateParser` builder to allow specifying parsing options
-
-### Changed/Fixed
-
-- Extensions are now stored in order of appearance in the certificate/CRL (#80)
- - `.extensions` field is not public anymore, but methods `.extensions()` and `.extensions_map()`
- have been added
-- Store CRI attributes in order
-- Fix parsing of CertificatePolicies, and use named types (closes #82)
-- Allow specifying registry in oid2sn and similar functions (closes #88)
-- Mark X509Extension::new as const fn + inline
-- Allow leading zeroes in serial number
-- Derive `Clone` for all types (when possible) (#89)
-- Fix certificate validity period check to be inclusive (#90)
-- Do not fail GeneralName parsing for x400Address and ediPartyName, read it as unparsed objects (#87)
-- Change visibility of fields in `X509Name` (replaced by accessors)
-
-### Thanks
-
-- @lilyball for numerous issues, ideas and comments
-- @SergioBenitez for lifetimes fixes (#93) and validity period check fixes (#90)
-- @rappet for Ed25519 signature verification support (#95)
-- @xonatius for the work on CRLDistributionPoints (#96, #98)
-
-## 0.9.3
-
-### Added/Changed/Fixed
-
-- Add functions oid2description() and oid_registry() (closes #79)
-- Fix typo 'ocsp_signing' (closes #84)
-- Extension: use specific variant if unsupported or failed to parse (closes #83)
-- Relax constrains on parsing to accept certificates that do not strictly respect
- DER encoding, but are widely accepted by other X.509 libraries:
- - SubjectAltName: accept non-ia5string characters
- - Extensions: accept boolean values not enoded as `00` or `ff`
- - Serial: build BigUint from raw bytes (do not check sign)
-
## 0.9.2
### Added/Changed/Fixed
diff --git a/Cargo.toml b/Cargo.toml
index 36bb744..d81f889 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,7 +13,7 @@
[package]
edition = "2018"
name = "x509-parser"
-version = "0.10.0"
+version = "0.9.2"
authors = ["Pierre Chifflier <chifflier@wzdftpd.net>"]
include = ["CHANGELOG.md", "LICENSE-*", "README.md", ".gitignore", ".travis.yml", "Cargo.toml", "src/*.rs", "tests/*.rs", "assets/*.der", "assets/*.pem", "examples/*.rs"]
description = "Parser for the X.509 v3 format (RFC 5280 certificates)"
@@ -38,7 +38,7 @@ default-features = false
version = "2.2.1"
[dependencies.der-parser]
-version = "5.1"
+version = "5.0"
features = ["bigint"]
[dependencies.lazy_static]
@@ -58,10 +58,12 @@ optional = true
[dependencies.rusticata-macros]
version = "3.0"
+[dependencies.rustversion]
+version = "1.0"
+
[dependencies.thiserror]
version = "1.0"
[features]
default = []
-validate = []
verify = ["ring"]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 0833677..30698c6 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "x509-parser"
-version = "0.10.0"
+version = "0.9.2"
description = "Parser for the X.509 v3 format (RFC 5280 certificates)"
license = "MIT/Apache-2.0"
keywords = ["X509","Certificate","parser","nom"]
@@ -32,7 +32,6 @@ rustdoc-args = ["--cfg", "docsrs"]
[features]
default = []
verify = ["ring"]
-validate = []
[dependencies]
base64 = "0.13"
@@ -43,5 +42,6 @@ nom = "6.0"
oid-registry = { version="0.1.1", features=["crypto", "x509"] }
rusticata-macros = "3.0"
ring = { version="0.16", optional=true }
-der-parser = { version = "5.1", features=["bigint"] }
+rustversion = "1.0"
+der-parser = { version = "5.0", features=["bigint"] }
thiserror = "1.0"
diff --git a/METADATA b/METADATA
index 4d6197c..dd9ebef 100644
--- a/METADATA
+++ b/METADATA
@@ -7,13 +7,14 @@ third_party {
}
url {
type: ARCHIVE
- value: "https://static.crates.io/crates/x509-parser/x509-parser-0.10.0.crate"
+ value: "https://static.crates.io/crates/x509-parser/x509-parser-0.9.2.crate"
}
- version: "0.10.0"
+ version: "0.9.2"
+ # Dual-licensed, using the least restrictive per go/thirdpartylicenses#same.
license_type: NOTICE
last_upgrade_date {
year: 2021
- month: 8
- day: 9
+ month: 7
+ day: 26
}
}
diff --git a/README.md b/README.md
index b6ffbeb..3989131 100644
--- a/README.md
+++ b/README.md
@@ -25,17 +25,12 @@ 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.
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/traits/trait.FromDer.html) trait
-), which builds a
-[`X509Certificate`](https://docs.rs/x509-parser/latest/x509_parser/certificate/struct.X509Certificate.html) object.
-
-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).
+[`parse_x509_certificate`](https://docs.rs/x509-parser/latest/x509_parser/fn.parse_x509_certificate.html), which builds a
+[`X509Certificate`](https://docs.rs/x509-parser/latest/x509_parser/x509/struct.X509Certificate.html) object.
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()](https://docs.rs/x509-parser/latest/x509_parser/x509/struct.X509Certificate.html#method.issuer) returns the
same as accessing `<object>.tbs_certificate.issuer`.
For PEM-encoded certificates, use the [`pem`](https://docs.rs/x509-parser/latest/x509_parser/pem/index.html) module.
@@ -49,12 +44,12 @@ use x509_parser::prelude::*;
static IGCA_DER: &[u8] = include_bytes!("../assets/IGC_A.der");
-let res = X509Certificate::from_der(IGCA_DER);
+let res = parse_x509_certificate(IGCA_DER);
match res {
Ok((rem, cert)) => {
assert!(rem.is_empty());
//
- assert_eq!(cert.version(), X509Version::V3);
+ assert_eq!(cert.tbs_certificate.version, X509Version::V3);
},
_ => panic!("x509 parsing failed: {:?}", res),
}
@@ -65,7 +60,7 @@ To parse a CRL and print information about revoked certificates:
```rust
#
#
-let res = CertificateRevocationList::from_der(DER);
+let res = parse_x509_crl(DER);
match res {
Ok((_rem, crl)) => {
for revoked in crl.iter_revoked_certificates() {
@@ -83,24 +78,20 @@ See also `examples/print-cert.rs`.
- The `verify` feature adds support for (cryptographic) signature verification, based on `ring`.
It adds the
- [`X509Certificate::verify_signature()`](https://docs.rs/x509-parser/latest/x509_parser/certificate/struct.X509Certificate.html#method.verify_signature)
+ [X509Certificate::verify_signature()](https://docs.rs/x509-parser/latest/x509_parser/x509/struct.X509Certificate.html#method.verify_signature)
to `X509Certificate`.
```rust
/// Cryptographic signature verification: returns true if certificate was signed by issuer
#[cfg(feature = "verify")]
pub fn check_signature(cert: &X509Certificate<'_>, issuer: &X509Certificate<'_>) -> bool {
- let issuer_public_key = issuer.public_key();
+ let issuer_public_key = &issuer.tbs_certificate.subject_pki;
cert
.verify_signature(Some(issuer_public_key))
.is_ok()
}
```
-- 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).
-
## Rust version requirements
`x509-parser` requires **Rustc version 1.45 or greater**, based on nom 6
diff --git a/assets/csr-empty-attributes.csr b/assets/csr-empty-attributes.csr
new file mode 100644
index 0000000..bfb84c8
--- /dev/null
+++ b/assets/csr-empty-attributes.csr
Binary files differ
diff --git a/assets/ed25519.der b/assets/ed25519.der
deleted file mode 100644
index cae76a1..0000000
--- a/assets/ed25519.der
+++ /dev/null
Binary files differ
diff --git a/assets/empty.crl b/assets/empty.crl
new file mode 100644
index 0000000..fc3f6b3
--- /dev/null
+++ b/assets/empty.crl
Binary files differ
diff --git a/assets/example.crl b/assets/example.crl
new file mode 100644
index 0000000..3df0b6f
--- /dev/null
+++ b/assets/example.crl
Binary files differ
diff --git a/assets/gen_minimal_crl.py b/assets/gen_minimal_crl.py
new file mode 100644
index 0000000..b3f9e65
--- /dev/null
+++ b/assets/gen_minimal_crl.py
@@ -0,0 +1,40 @@
+"""Generates a DER encoded CRL with a single revoked serial and no extensions.
+This exercises the optional-parsing functionalitites of `parse_crl_der`.
+"""
+
+import os.path as osp
+
+from OpenSSL import crypto
+
+
+def main():
+ pkey = crypto.PKey()
+ pkey.generate_key(crypto.TYPE_RSA, 2048)
+
+ ca = crypto.X509()
+ ca.set_version(2)
+ ca.set_serial_number(1)
+ ca.get_subject().CN = 'snakeoil'
+ ca.set_notBefore(b'19700101000000Z')
+ ca.set_notAfter(b'20991231235959Z')
+ ca.set_issuer(ca.get_subject())
+ ca.set_pubkey(pkey)
+ ca.sign(pkey, 'sha256')
+
+ revoked = crypto.Revoked()
+ revoked.set_serial(b'2a')
+ revoked.set_rev_date(b'19700101000000Z')
+ revoked.set_reason(None)
+
+ crl = crypto.CRL()
+ crl.set_lastUpdate(b'19700101000000Z')
+ crl.set_nextUpdate(b'20990101000000Z')
+ crl.add_revoked(revoked)
+ crl.sign(issuer_cert=ca, issuer_key=pkey, digest=b'sha256')
+
+ with open(osp.join(osp.dirname(__file__), 'minimal.crl'), 'wb') as f_crl:
+ f_crl.write(crypto.dump_crl(crypto.FILETYPE_ASN1, crl))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/assets/minimal.crl b/assets/minimal.crl
new file mode 100644
index 0000000..32cd969
--- /dev/null
+++ b/assets/minimal.crl
Binary files differ
diff --git a/assets/test.csr b/assets/test.csr
new file mode 100644
index 0000000..729df67
--- /dev/null
+++ b/assets/test.csr
@@ -0,0 +1,8 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIBBjCBrQIBADAcMRowGAYDVQQDDBF0ZXN0LnJ1c3RpY2F0YS5mcjBZMBMGByqG
+SM49AgEGCCqGSM49AwEHA0IABMP1frFxwJLXiLU6UoqOPf31ucCm2NqR2yqpcHo6
+W7iWJe31OzYs0izP2qeUvdKfz2fpAbuGiRjwvN+H10dQQEGgLzAtBgkqhkiG9w0B
+CQ4xIDAeMBwGA1UdEQQVMBOCEXRlc3QucnVzdGljYXRhLmZyMAoGCCqGSM49BAMC
+A0gAMEUCIGqQHPHgpeyZa5YMLP2X5IwfmrvpIcg5fQ2xkXotGAa0AiEAydeBwr4r
+Iu7XDe015h8uz8xZs2QUEgRdr73lJXTX+Ck=
+-----END CERTIFICATE REQUEST-----
diff --git a/cargo2android.json b/cargo2android.json
index 5f001a6..64cedd9 100644
--- a/cargo2android.json
+++ b/cargo2android.json
@@ -3,6 +3,10 @@
"data_encoding"
],
"device": true,
- "run": true
+ "run": true,
+ "test-data": [
+ "src/lib.rs=assets/certificate.pem"
+ ],
+ "tests": true
}
diff --git a/examples/print-cert.rs b/examples/print-cert.rs
index 73a9149..50a5c6d 100644
--- a/examples/print-cert.rs
+++ b/examples/print-cert.rs
@@ -4,12 +4,6 @@ use std::cmp::min;
use std::env;
use std::io;
use x509_parser::prelude::*;
-#[cfg(feature = "validate")]
-use x509_parser::validate::Validate;
-
-const PARSE_ERRORS_FATAL: bool = false;
-#[cfg(feature = "validate")]
-const VALIDATE_ERRORS_FATAL: bool = false;
fn print_hex_dump(bytes: &[u8], max_len: usize) {
let m = min(bytes.len(), max_len);
@@ -20,26 +14,12 @@ fn print_hex_dump(bytes: &[u8], max_len: usize) {
}
fn format_oid(oid: &Oid) -> String {
- match oid2sn(oid, oid_registry()) {
+ match oid2sn(oid) {
Ok(s) => s.to_owned(),
_ => format!("{}", oid),
}
}
-fn generalname_to_string(gn: &GeneralName) -> String {
- match gn {
- GeneralName::DNSName(name) => format!("DNSName:{}", name),
- GeneralName::DirectoryName(n) => format!("DirName:{}", n),
- GeneralName::EDIPartyName(obj) => format!("EDIPartyName:{:?}", obj),
- GeneralName::IPAddress(n) => format!("IPAddress:{:?}", n),
- GeneralName::OtherName(oid, n) => format!("OtherName:{}, {:?}", oid, n),
- GeneralName::RFC822Name(n) => format!("RFC822Name:{}", n),
- GeneralName::RegisteredID(oid) => format!("RegisteredID:{}", oid),
- GeneralName::URI(n) => format!("URI:{}", n),
- GeneralName::X400Address(obj) => format!("X400Address:{:?}", obj),
- }
-}
-
fn print_x509_extension(oid: &Oid, ext: &X509Extension) {
print!(" {}: ", format_oid(oid));
print!(" Critical={}", ext.critical);
@@ -49,25 +29,6 @@ fn print_x509_extension(oid: &Oid, ext: &X509Extension) {
ParsedExtension::BasicConstraints(bc) => {
println!(" X509v3 CA: {}", bc.ca);
}
- ParsedExtension::CRLDistributionPoints(points) => {
- println!(" X509v3 CRL Distribution Points:");
- for point in points {
- if let Some(name) = &point.distribution_point {
- println!(" Full Name: {:?}", name);
- }
- if let Some(reasons) = &point.reasons {
- println!(" Reasons: {}", reasons);
- }
- if let Some(crl_issuer) = &point.crl_issuer {
- print!(" CRL Issuer: ");
- for gn in crl_issuer {
- print!("{} ", generalname_to_string(gn));
- }
- println!();
- }
- println!();
- }
- }
ParsedExtension::KeyUsage(ku) => {
println!(" X509v3 Key Usage: {}", ku);
}
@@ -114,7 +75,7 @@ fn print_x509_digest_algorithm(alg: &AlgorithmIdentifier, level: usize) {
}
}
-fn print_x509_info(x509: &X509Certificate) -> io::Result<()> {
+fn print_x509_info(x509: &X509Certificate) {
println!(" Subject: {}", x509.subject());
println!(" Signature Algorithm:");
print_x509_digest_algorithm(&x509.signature_algorithm, 4);
@@ -125,66 +86,28 @@ fn print_x509_info(x509: &X509Certificate) -> io::Result<()> {
println!(" NotAfter: {}", x509.validity().not_after.to_rfc2822());
println!(" is_valid: {}", x509.validity().is_valid());
println!(" Extensions:");
- for ext in x509.extensions() {
- print_x509_extension(&ext.oid, ext);
+ for (oid, ext) in x509.extensions() {
+ print_x509_extension(oid, ext);
}
println!();
- #[cfg(feature = "validate")]
- {
- // structure validation status
- let (ok, warnings, errors) = x509.validate_to_vec();
- print!("Structure validation status: ");
- if ok {
- println!("Ok");
- } else {
- println!("FAIL");
- }
- for warning in &warnings {
- println!(" [W] {}", warning);
- }
- for error in &errors {
- println!(" [E] {}", error);
- }
- println!();
- if VALIDATE_ERRORS_FATAL && !errors.is_empty() {
- return Err(io::Error::new(io::ErrorKind::Other, "validation failed"));
- }
- }
- Ok(())
-}
-
-fn handle_certificate(file_name: &str, data: &[u8]) -> io::Result<()> {
- match parse_x509_certificate(data) {
- Ok((_, x509)) => {
- print_x509_info(&x509)?;
- Ok(())
- }
- Err(e) => {
- let s = format!("Error while parsing {}: {}", file_name, e);
- if PARSE_ERRORS_FATAL {
- Err(io::Error::new(io::ErrorKind::Other, s))
- } else {
- eprintln!("{}", s);
- Ok(())
- }
- }
- }
}
pub fn main() -> io::Result<()> {
for file_name in env::args().skip(1) {
println!("File: {}", file_name);
let data = std::fs::read(file_name.clone()).expect("Unable to read file");
- if matches!((data[0], data[1]), (0x30, 0x81..=0x83)) {
+ if (data[0], data[1]) == (0x30, 0x82) {
// probably DER
- handle_certificate(&file_name, &data)?;
+ let (_, x509) = parse_x509_certificate(&data).expect("Could not decode DER data");
+ print_x509_info(&x509);
} else {
// try as PEM
for (n, pem) in Pem::iter_from_buffer(&data).enumerate() {
let pem = pem.expect("Could not decode the PEM file");
let data = &pem.contents;
+ let (_, x509) = parse_x509_certificate(&data).expect("Could not decode DER data");
println!("Certificate [{}]", n);
- handle_certificate(&file_name, data)?;
+ print_x509_info(&x509);
}
}
}
diff --git a/examples/print-crl.rs b/examples/print-crl.rs
index a9af731..de6c0f3 100644
--- a/examples/print-crl.rs
+++ b/examples/print-crl.rs
@@ -14,7 +14,7 @@ fn print_hex_dump(bytes: &[u8], max_len: usize) {
}
fn format_oid(oid: &Oid) -> String {
- match oid2sn(oid, oid_registry()) {
+ match oid2sn(oid) {
Ok(s) => s.to_owned(),
_ => format!("{}", oid),
}
@@ -119,8 +119,8 @@ fn print_revoked_certificate(revoked: &RevokedCertificate, level: usize) {
indent = level + 2
);
println!("{:indent$}CRL Extensions:", "", indent = level + 2);
- for ext in revoked.extensions() {
- print_x509_extension(&ext.oid, ext, level + 4);
+ for (oid, ext) in revoked.extensions() {
+ print_x509_extension(oid, ext, level + 4);
}
}
@@ -138,8 +138,8 @@ fn print_crl_info(crl: &CertificateRevocationList) {
.map_or("NONE".to_owned(), |d| d.to_rfc2822())
);
println!("{:indent$}CRL Extensions:", "", indent = 2);
- for ext in crl.extensions() {
- print_x509_extension(&ext.oid, ext, 4);
+ for (oid, ext) in crl.extensions() {
+ print_x509_extension(oid, ext, 4);
}
println!(" Revoked certificates:");
for revoked in crl.iter_revoked_certificates() {
@@ -164,7 +164,7 @@ pub fn main() -> io::Result<()> {
tmpdata = data;
&tmpdata.contents
};
- let (_, crl) = parse_x509_crl(der_data).expect("Could not decode DER data");
+ let (_, crl) = parse_x509_crl(&der_data).expect("Could not decode DER data");
print_crl_info(&crl);
}
Ok(())
diff --git a/patches/data-encoding_dep.patch b/patches/data-encoding_dep.patch
index f4d04c0..f5d9396 100644
--- a/patches/data-encoding_dep.patch
+++ b/patches/data-encoding_dep.patch
@@ -3,11 +3,11 @@ index 0f8c0fd..9590fe6 100644
--- a/src/x509.rs
+++ b/src/x509.rs
@@ -6,7 +6,6 @@
+ use crate::error::{X509Error, X509Result};
use crate::objects::*;
- use crate::traits::FromDer;
-use data_encoding::HEXUPPER;
- use der_parser::ber::{parse_ber_integer, BitStringObject, MAX_OBJECT_SIZE};
+ use der_parser::ber::{BitStringObject, MAX_OBJECT_SIZE};
use der_parser::der::*;
use der_parser::error::*;
@@ -365,7 +364,7 @@ fn attribute_value_to_string(attr: &DerObject, _attr_type: &Oid) -> Result<Strin
diff --git a/src/certificate.rs b/src/certificate.rs
index 1d937b1..f29bc4e 100644
--- a/src/certificate.rs
+++ b/src/certificate.rs
@@ -3,9 +3,6 @@
use crate::error::{X509Error, X509Result};
use crate::extensions::*;
use crate::time::ASN1Time;
-use crate::traits::FromDer;
-#[cfg(feature = "validate")]
-use crate::validate::Validate;
use crate::x509::{
parse_serial, parse_signature_value, AlgorithmIdentifier, SubjectPublicKeyInfo, X509Name,
X509Version,
@@ -17,11 +14,9 @@ use der_parser::error::*;
use der_parser::num_bigint::BigUint;
use der_parser::oid::Oid;
use der_parser::*;
-use nom::{Offset, Parser};
+use nom::Offset;
use oid_registry::*;
use std::collections::HashMap;
-#[cfg(feature = "validate")]
-use std::collections::HashSet;
/// An X.509 v3 Certificate.
///
@@ -36,21 +31,21 @@ use std::collections::HashSet;
/// buffer containing the binary representation.
///
/// ```rust
+/// # use x509_parser::parse_x509_certificate;
/// # use x509_parser::certificate::X509Certificate;
-/// # use x509_parser::traits::FromDer;
/// #
/// # static DER: &'static [u8] = include_bytes!("../assets/IGC_A.der");
/// #
/// fn display_x509_info(x509: &X509Certificate<'_>) {
-/// let subject = x509.subject();
-/// let issuer = x509.issuer();
+/// let subject = &x509.tbs_certificate.subject;
+/// let issuer = &x509.tbs_certificate.issuer;
/// println!("X.509 Subject: {}", subject);
/// println!("X.509 Issuer: {}", issuer);
/// println!("X.509 serial: {}", x509.tbs_certificate.raw_serial_as_string());
/// }
/// #
/// # fn main() {
-/// # let res = X509Certificate::from_der(DER);
+/// # let res = parse_x509_certificate(DER);
/// # match res {
/// # Ok((_rem, x509)) => {
/// # display_x509_info(&x509);
@@ -59,7 +54,7 @@ use std::collections::HashSet;
/// # }
/// # }
/// ```
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct X509Certificate<'a> {
pub tbs_certificate: TbsCertificate<'a>,
pub signature_algorithm: AlgorithmIdentifier<'a>,
@@ -67,6 +62,56 @@ pub struct X509Certificate<'a> {
}
impl<'a> X509Certificate<'a> {
+ /// Parse a DER-encoded X.509 Certificate, and return the remaining of the input and the built
+ /// object.
+ ///
+ /// The returned object uses zero-copy, and so has the same lifetime as the input.
+ ///
+ /// Note that only parsing is done, not validation.
+ ///
+ /// <pre>
+ /// Certificate ::= SEQUENCE {
+ /// tbsCertificate TBSCertificate,
+ /// signatureAlgorithm AlgorithmIdentifier,
+ /// signatureValue BIT STRING }
+ /// </pre>
+ ///
+ /// # Example
+ ///
+ /// To parse a certificate and print the subject and issuer:
+ ///
+ /// ```rust
+ /// # use x509_parser::parse_x509_certificate;
+ /// #
+ /// # static DER: &'static [u8] = include_bytes!("../assets/IGC_A.der");
+ /// #
+ /// # fn main() {
+ /// let res = parse_x509_certificate(DER);
+ /// match res {
+ /// Ok((_rem, x509)) => {
+ /// let subject = &x509.tbs_certificate.subject;
+ /// let issuer = &x509.tbs_certificate.issuer;
+ /// println!("X.509 Subject: {}", subject);
+ /// println!("X.509 Issuer: {}", issuer);
+ /// },
+ /// _ => panic!("x509 parsing failed: {:?}", res),
+ /// }
+ /// # }
+ /// ```
+ pub fn from_der(i: &'a [u8]) -> X509Result<Self> {
+ parse_der_sequence_defined_g(|i, _| {
+ let (i, tbs_certificate) = TbsCertificate::from_der(i)?;
+ let (i, signature_algorithm) = AlgorithmIdentifier::from_der(i)?;
+ let (i, signature_value) = parse_signature_value(i)?;
+ let cert = X509Certificate {
+ tbs_certificate,
+ signature_algorithm,
+ signature_value,
+ };
+ Ok((i, cert))
+ })(i)
+ }
+
/// Get the version of the encoded certificate
pub fn version(&self) -> X509Version {
self.tbs_certificate.version
@@ -90,16 +135,10 @@ impl<'a> X509Certificate<'a> {
&self.tbs_certificate.validity
}
- /// Get the certificate public key information.
- #[inline]
- pub fn public_key(&self) -> &SubjectPublicKeyInfo {
- &self.tbs_certificate.subject_pki
- }
-
/// Get the certificate extensions.
#[inline]
- pub fn extensions(&self) -> &[X509Extension] {
- &self.tbs_certificate.extensions
+ pub fn extensions(&self) -> &HashMap<Oid, X509Extension> {
+ self.tbs_certificate.extensions()
}
/// Verify the cryptographic signature of this certificate
@@ -111,13 +150,12 @@ impl<'a> X509Certificate<'a> {
/// For a leaf certificate, this is the public key of the certificate that signed it.
/// It is usually an intermediate authority.
#[cfg(feature = "verify")]
- #[cfg_attr(docsrs, doc(cfg(feature = "verify")))]
pub fn verify_signature(
&self,
public_key: Option<&SubjectPublicKeyInfo>,
) -> Result<(), X509Error> {
use ring::signature;
- let spki = public_key.unwrap_or_else(|| self.public_key());
+ let spki = public_key.unwrap_or(&self.tbs_certificate.subject_pki);
let signature_alg = &self.signature_algorithm.algorithm;
// identify verification algorithm
let verification_alg: &dyn signature::VerificationAlgorithm =
@@ -133,8 +171,6 @@ impl<'a> X509Certificate<'a> {
&signature::ECDSA_P256_SHA256_ASN1
} else if *signature_alg == OID_SIG_ECDSA_WITH_SHA384 {
&signature::ECDSA_P384_SHA384_ASN1
- } else if *signature_alg == OID_SIG_ED25519 {
- &signature::ED25519
} else {
return Err(X509Error::SignatureUnsupportedAlgorithm);
};
@@ -147,141 +183,7 @@ impl<'a> X509Certificate<'a> {
}
}
-impl<'a> FromDer<'a> for X509Certificate<'a> {
- /// Parse a DER-encoded X.509 Certificate, and return the remaining of the input and the built
- /// object.
- ///
- /// The returned object uses zero-copy, and so has the same lifetime as the input.
- ///
- /// Note that only parsing is done, not validation.
- ///
- /// <pre>
- /// Certificate ::= SEQUENCE {
- /// tbsCertificate TBSCertificate,
- /// signatureAlgorithm AlgorithmIdentifier,
- /// signatureValue BIT STRING }
- /// </pre>
- ///
- /// # Example
- ///
- /// To parse a certificate and print the subject and issuer:
- ///
- /// ```rust
- /// # use x509_parser::parse_x509_certificate;
- /// #
- /// # static DER: &'static [u8] = include_bytes!("../assets/IGC_A.der");
- /// #
- /// # fn main() {
- /// let res = parse_x509_certificate(DER);
- /// match res {
- /// Ok((_rem, x509)) => {
- /// let subject = x509.subject();
- /// let issuer = x509.issuer();
- /// println!("X.509 Subject: {}", subject);
- /// println!("X.509 Issuer: {}", issuer);
- /// },
- /// _ => panic!("x509 parsing failed: {:?}", res),
- /// }
- /// # }
- /// ```
- fn from_der(i: &'a [u8]) -> X509Result<Self> {
- // run parser with default options
- X509CertificateParser::new().parse(i)
- }
-}
-
-/// X.509 Certificate parser
-///
-/// This object is a parser builder, and allows specifying parsing options.
-/// Currently, the only option is to control deep parsing of X.509v3 extensions:
-/// a parser can decide to skip deep-parsing to be faster (the structure of extensions is still
-/// parsed, and the contents can be parsed later using the [`from_der`](FromDer::from_der)
-/// method from individual extension objects).
-///
-/// This object uses the `nom::Parser` trait, which must be imported.
-///
-/// # Example
-///
-/// To parse a certificate without parsing extensions:
-///
-/// ```rust
-/// use x509_parser::certificate::X509CertificateParser;
-/// use x509_parser::nom::Parser;
-///
-/// # static DER: &'static [u8] = include_bytes!("../assets/IGC_A.der");
-/// #
-/// # fn main() {
-/// // create a parser that will not parse extensions
-/// let mut parser = X509CertificateParser::new()
-/// .with_deep_parse_extensions(false);
-/// let res = parser.parse(DER);
-/// match res {
-/// Ok((_rem, x509)) => {
-/// let subject = x509.subject();
-/// let issuer = x509.issuer();
-/// println!("X.509 Subject: {}", subject);
-/// println!("X.509 Issuer: {}", issuer);
-/// },
-/// _ => panic!("x509 parsing failed: {:?}", res),
-/// }
-/// # }
-/// ```
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub struct X509CertificateParser {
- deep_parse_extensions: bool,
- // strict: bool,
-}
-
-impl X509CertificateParser {
- #[inline]
- pub const fn new() -> Self {
- X509CertificateParser {
- deep_parse_extensions: true,
- }
- }
-
- #[inline]
- pub const fn with_deep_parse_extensions(self, deep_parse_extensions: bool) -> Self {
- X509CertificateParser {
- deep_parse_extensions,
- }
- }
-}
-
-impl<'a> Parser<&'a [u8], X509Certificate<'a>, X509Error> for X509CertificateParser {
- fn parse(&mut self, input: &'a [u8]) -> IResult<&'a [u8], X509Certificate<'a>, X509Error> {
- parse_der_sequence_defined_g(|i, _| {
- // pass options to TbsCertificate parser
- let mut tbs_parser =
- TbsCertificateParser::new().with_deep_parse_extensions(self.deep_parse_extensions);
- let (i, tbs_certificate) = tbs_parser.parse(i)?;
- let (i, signature_algorithm) = AlgorithmIdentifier::from_der(i)?;
- let (i, signature_value) = parse_signature_value(i)?;
- let cert = X509Certificate {
- tbs_certificate,
- signature_algorithm,
- signature_value,
- };
- Ok((i, cert))
- })(input)
- }
-}
-
-#[cfg(feature = "validate")]
-#[cfg_attr(docsrs, doc(cfg(feature = "validate")))]
-impl Validate for X509Certificate<'_> {
- fn validate<W, E>(&self, warn: W, err: E) -> bool
- where
- W: FnMut(&str),
- E: FnMut(&str),
- {
- let mut res = true;
- res |= self.tbs_certificate.validate(warn, err);
- res
- }
-}
-
-/// The sequence `TBSCertificate` contains information associated with the
+/// The sequence TBSCertificate contains information associated with the
/// subject of the certificate and the CA that issued it.
///
/// RFC5280 definition:
@@ -303,7 +205,7 @@ impl Validate for X509Certificate<'_> {
/// -- If present, version MUST be v3
/// }
/// </pre>
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct TbsCertificate<'a> {
pub version: X509Version,
pub serial: BigUint,
@@ -314,143 +216,12 @@ pub struct TbsCertificate<'a> {
pub subject_pki: SubjectPublicKeyInfo<'a>,
pub issuer_uid: Option<UniqueIdentifier<'a>>,
pub subject_uid: Option<UniqueIdentifier<'a>>,
- extensions: Vec<X509Extension<'a>>,
+ pub extensions: HashMap<Oid<'a>, X509Extension<'a>>,
pub(crate) raw: &'a [u8],
pub(crate) raw_serial: &'a [u8],
}
impl<'a> TbsCertificate<'a> {
- /// Returns the certificate extensions
- #[inline]
- pub fn extensions(&self) -> &[X509Extension] {
- &self.extensions
- }
-
- /// Returns an iterator over the certificate extensions
- #[inline]
- pub fn iter_extensions(&self) -> impl Iterator<Item = &X509Extension> {
- self.extensions.iter()
- }
-
- /// Searches for an extension with the given `Oid`.
- ///
- /// Note: if there are several extensions with the same `Oid`, the first one is returned.
- pub fn find_extension(&self, oid: &Oid) -> Option<&X509Extension> {
- self.extensions.iter().find(|&ext| ext.oid == *oid)
- }
-
- /// Builds and returns a map of extensions.
- ///
- /// If an extension is present twice, this will fail and return `DuplicateExtensions`.
- pub fn extensions_map(&self) -> Result<HashMap<Oid, &X509Extension>, X509Error> {
- self.extensions
- .iter()
- .try_fold(HashMap::new(), |mut m, ext| {
- if m.contains_key(&ext.oid) {
- return Err(X509Error::DuplicateExtensions);
- }
- m.insert(ext.oid.clone(), ext);
- Ok(m)
- })
- }
-
- pub fn basic_constraints(&self) -> Option<(bool, &BasicConstraints)> {
- self.find_extension(&OID_X509_EXT_BASIC_CONSTRAINTS)
- .and_then(|ext| match ext.parsed_extension {
- ParsedExtension::BasicConstraints(ref bc) => Some((ext.critical, bc)),
- _ => None,
- })
- }
-
- pub fn key_usage(&self) -> Option<(bool, &KeyUsage)> {
- self.find_extension(&OID_X509_EXT_KEY_USAGE)
- .and_then(|ext| match ext.parsed_extension {
- ParsedExtension::KeyUsage(ref ku) => Some((ext.critical, ku)),
- _ => None,
- })
- }
-
- pub fn extended_key_usage(&self) -> Option<(bool, &ExtendedKeyUsage)> {
- self.find_extension(&OID_X509_EXT_EXTENDED_KEY_USAGE)
- .and_then(|ext| match ext.parsed_extension {
- ParsedExtension::ExtendedKeyUsage(ref eku) => Some((ext.critical, eku)),
- _ => None,
- })
- }
-
- pub fn policy_constraints(&self) -> Option<(bool, &PolicyConstraints)> {
- self.find_extension(&OID_X509_EXT_POLICY_CONSTRAINTS)
- .and_then(|ext| match ext.parsed_extension {
- ParsedExtension::PolicyConstraints(ref pc) => Some((ext.critical, pc)),
- _ => None,
- })
- }
-
- pub fn inhibit_anypolicy(&self) -> Option<(bool, &InhibitAnyPolicy)> {
- self.find_extension(&OID_X509_EXT_INHIBITANT_ANY_POLICY)
- .and_then(|ext| match ext.parsed_extension {
- ParsedExtension::InhibitAnyPolicy(ref iap) => Some((ext.critical, iap)),
- _ => None,
- })
- }
-
- pub fn policy_mappings(&self) -> Option<(bool, &PolicyMappings)> {
- self.find_extension(&OID_X509_EXT_POLICY_MAPPINGS)
- .and_then(|ext| match ext.parsed_extension {
- ParsedExtension::PolicyMappings(ref pm) => Some((ext.critical, pm)),
- _ => None,
- })
- }
-
- pub fn subject_alternative_name(&self) -> Option<(bool, &SubjectAlternativeName)> {
- self.find_extension(&OID_X509_EXT_SUBJECT_ALT_NAME)
- .and_then(|ext| match ext.parsed_extension {
- ParsedExtension::SubjectAlternativeName(ref san) => Some((ext.critical, san)),
- _ => None,
- })
- }
-
- pub fn name_constraints(&self) -> Option<(bool, &NameConstraints)> {
- self.find_extension(&OID_X509_EXT_NAME_CONSTRAINTS)
- .and_then(|ext| match ext.parsed_extension {
- ParsedExtension::NameConstraints(ref nc) => Some((ext.critical, nc)),
- _ => None,
- })
- }
-
- /// Returns true if certificate has `basicConstraints CA:true`
- pub fn is_ca(&self) -> bool {
- self.basic_constraints()
- .map(|(_, bc)| bc.ca)
- .unwrap_or(false)
- }
-
- /// Get the raw bytes of the certificate serial number
- pub fn raw_serial(&self) -> &[u8] {
- self.raw_serial
- }
-
- /// Get a formatted string of the certificate serial number, separated by ':'
- pub fn raw_serial_as_string(&self) -> String {
- let mut s = self
- .raw_serial
- .iter()
- .fold(String::with_capacity(3 * self.raw_serial.len()), |a, b| {
- a + &format!("{:02x}:", b)
- });
- s.pop();
- s
- }
-}
-
-impl<'a> AsRef<[u8]> for TbsCertificate<'a> {
- #[inline]
- fn as_ref(&self) -> &[u8] {
- self.raw
- }
-}
-
-impl<'a> FromDer<'a> for TbsCertificate<'a> {
/// Parse a DER-encoded TbsCertificate object
///
/// <pre>
@@ -469,7 +240,7 @@ impl<'a> FromDer<'a> for TbsCertificate<'a> {
/// extensions [3] Extensions OPTIONAL
/// -- If present, version MUST be v3 -- }
/// </pre>
- fn from_der(i: &'a [u8]) -> X509Result<TbsCertificate<'a>> {
+ pub fn from_der(i: &'a [u8]) -> X509Result<TbsCertificate<'a>> {
let start_i = i;
parse_der_sequence_defined_g(move |i, _| {
let (i, version) = X509Version::from_der(i)?;
@@ -501,164 +272,126 @@ impl<'a> FromDer<'a> for TbsCertificate<'a> {
Ok((i, tbs))
})(i)
}
-}
-/// `TbsCertificate` parser builder
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub struct TbsCertificateParser {
- deep_parse_extensions: bool,
-}
+ /// Get a reference to the map of extensions.
+ pub fn extensions(&self) -> &HashMap<Oid, X509Extension> {
+ &self.extensions
+ }
-impl TbsCertificateParser {
- #[inline]
- pub const fn new() -> Self {
- TbsCertificateParser {
- deep_parse_extensions: true,
+ pub fn basic_constraints(&self) -> Option<(bool, &BasicConstraints)> {
+ let ext = self.extensions.get(&OID_X509_EXT_BASIC_CONSTRAINTS)?;
+ match ext.parsed_extension {
+ ParsedExtension::BasicConstraints(ref bc) => Some((ext.critical, bc)),
+ _ => None,
}
}
- #[inline]
- pub const fn with_deep_parse_extensions(self, deep_parse_extensions: bool) -> Self {
- TbsCertificateParser {
- deep_parse_extensions,
+ pub fn key_usage(&self) -> Option<(bool, &KeyUsage)> {
+ let ext = self.extensions.get(&OID_X509_EXT_KEY_USAGE)?;
+ match ext.parsed_extension {
+ ParsedExtension::KeyUsage(ref ku) => Some((ext.critical, ku)),
+ _ => None,
}
}
-}
-
-impl<'a> Parser<&'a [u8], TbsCertificate<'a>, X509Error> for TbsCertificateParser {
- fn parse(&mut self, input: &'a [u8]) -> IResult<&'a [u8], TbsCertificate<'a>, X509Error> {
- let start_i = input;
- parse_der_sequence_defined_g(move |i, _| {
- let (i, version) = X509Version::from_der(i)?;
- let (i, serial) = parse_serial(i)?;
- let (i, signature) = AlgorithmIdentifier::from_der(i)?;
- let (i, issuer) = X509Name::from_der(i)?;
- let (i, validity) = Validity::from_der(i)?;
- let (i, subject) = X509Name::from_der(i)?;
- let (i, subject_pki) = SubjectPublicKeyInfo::from_der(i)?;
- let (i, issuer_uid) = UniqueIdentifier::from_der_issuer(i)?;
- let (i, subject_uid) = UniqueIdentifier::from_der_subject(i)?;
- let (i, extensions) = if self.deep_parse_extensions {
- parse_extensions(i, BerTag(3))?
- } else {
- parse_extensions_envelope(i, BerTag(3))?
- };
- let len = start_i.offset(i);
- let tbs = TbsCertificate {
- version,
- serial: serial.1,
- signature,
- issuer,
- validity,
- subject,
- subject_pki,
- issuer_uid,
- subject_uid,
- extensions,
- raw: &start_i[..len],
- raw_serial: serial.0,
- };
- Ok((i, tbs))
- })(input)
+ pub fn extended_key_usage(&self) -> Option<(bool, &ExtendedKeyUsage)> {
+ let ext = self.extensions.get(&OID_X509_EXT_EXTENDED_KEY_USAGE)?;
+ match ext.parsed_extension {
+ ParsedExtension::ExtendedKeyUsage(ref eku) => Some((ext.critical, eku)),
+ _ => None,
+ }
}
-}
-#[cfg(feature = "validate")]
-#[cfg_attr(docsrs, doc(cfg(feature = "validate")))]
-impl Validate for TbsCertificate<'_> {
- fn validate<W, E>(&self, mut warn: W, mut err: E) -> bool
- where
- W: FnMut(&str),
- E: FnMut(&str),
- {
- let mut res = true;
- // version must be 0, 1 or 2
- if self.version.0 >= 3 {
- err("Invalid version");
- res = false;
- }
- // extensions require v3
- if !self.extensions().is_empty() && self.version != X509Version::V3 {
- err("Extensions present but version is not 3");
- res = false;
+ pub fn policy_constraints(&self) -> Option<(bool, &PolicyConstraints)> {
+ let ext = self.extensions.get(&OID_X509_EXT_POLICY_CONSTRAINTS)?;
+ match ext.parsed_extension {
+ ParsedExtension::PolicyConstraints(ref pc) => Some((ext.critical, pc)),
+ _ => None,
}
- let b = self.raw_serial();
- if b.is_empty() {
- err("Serial is empty");
- res = false;
- } else {
- // check MSB of serial
- if b[0] & 0x80 != 0 {
- warn("Serial number is negative");
- }
- // check leading zeroes in serial
- if b.len() > 1 && b[0] == 0 && b[1] & 0x80 == 0 {
- warn("Leading zeroes in serial number");
- }
+ }
+
+ pub fn inhibit_anypolicy(&self) -> Option<(bool, &InhibitAnyPolicy)> {
+ let ext = self.extensions.get(&OID_X509_EXT_INHIBITANT_ANY_POLICY)?;
+ match ext.parsed_extension {
+ ParsedExtension::InhibitAnyPolicy(ref iap) => Some((ext.critical, iap)),
+ _ => None,
}
- // subject/issuer: verify charsets
- // - wildcards in PrintableString
- // - non-IA5 in IA5String
- for attr in self.subject.iter_attributes() {
- match attr.attr_value().content {
- DerObjectContent::PrintableString(s) | DerObjectContent::IA5String(s) => {
- if !s.as_bytes().iter().all(u8::is_ascii) {
- warn(&format!(
- "Invalid charset in 'Subject', component {}",
- attr.attr_type()
- ));
- }
- }
- _ => (),
- }
+ }
+
+ pub fn policy_mappings(&self) -> Option<(bool, &PolicyMappings)> {
+ let ext = self.extensions.get(&OID_X509_EXT_POLICY_MAPPINGS)?;
+ match ext.parsed_extension {
+ ParsedExtension::PolicyMappings(ref pm) => Some((ext.critical, pm)),
+ _ => None,
}
- // check for parse errors or unsupported extensions
- for ext in self.extensions() {
- if let ParsedExtension::UnsupportedExtension { .. } = &ext.parsed_extension {
- warn(&format!("Unsupported extension {}", ext.oid));
- }
- if let ParsedExtension::ParseError { error } = &ext.parsed_extension {
- err(&format!("Parse error in extension {}: {}", ext.oid, error));
- res = false;
- }
+ }
+
+ pub fn subject_alternative_name(&self) -> Option<(bool, &SubjectAlternativeName)> {
+ let ext = self.extensions.get(&OID_X509_EXT_SUBJECT_ALT_NAME)?;
+ match ext.parsed_extension {
+ ParsedExtension::SubjectAlternativeName(ref san) => Some((ext.critical, san)),
+ _ => None,
}
- // check for duplicate extensions
- let mut m = HashSet::new();
- for ext in self.extensions() {
- if m.contains(&ext.oid) {
- err(&format!("Duplicate extension {}", ext.oid));
- res = false;
- } else {
- m.insert(ext.oid.clone());
- }
- // specific extension checks
- // SAN
- if let ParsedExtension::SubjectAlternativeName(san) = ext.parsed_extension() {
- for name in &san.general_names {
- match name {
- GeneralName::DNSName(ref s) | GeneralName::RFC822Name(ref s) => {
- // should be an ia5string
- if !s.as_bytes().iter().all(u8::is_ascii) {
- warn(&format!("Invalid charset in 'SAN' entry '{}'", s));
- }
- }
- _ => (),
- }
- }
- }
+ }
+
+ pub fn name_constraints(&self) -> Option<(bool, &NameConstraints)> {
+ let ext = self.extensions.get(&OID_X509_EXT_NAME_CONSTRAINTS)?;
+ match ext.parsed_extension {
+ ParsedExtension::NameConstraints(ref nc) => Some((ext.critical, nc)),
+ _ => None,
}
- res
+ }
+
+ /// Returns true if certificate has `basicConstraints CA:true`
+ pub fn is_ca(&self) -> bool {
+ self.basic_constraints()
+ .map(|(_, bc)| bc.ca)
+ .unwrap_or(false)
+ }
+
+ /// Get the raw bytes of the certificate serial number
+ pub fn raw_serial(&self) -> &[u8] {
+ self.raw_serial
+ }
+
+ /// Get a formatted string of the certificate serial number, separated by ':'
+ pub fn raw_serial_as_string(&self) -> String {
+ let mut s = self
+ .raw_serial
+ .iter()
+ .fold(String::with_capacity(3 * self.raw_serial.len()), |a, b| {
+ a + &format!("{:02x}:", b)
+ });
+ s.pop();
+ s
}
}
-#[derive(Clone, Debug, PartialEq)]
+impl<'a> AsRef<[u8]> for TbsCertificate<'a> {
+ fn as_ref(&self) -> &[u8] {
+ &self.raw
+ }
+}
+
+#[derive(Debug, PartialEq)]
pub struct Validity {
pub not_before: ASN1Time,
pub not_after: ASN1Time,
}
impl Validity {
+ fn from_der(i: &[u8]) -> X509Result<Self> {
+ parse_der_sequence_defined_g(|i, _| {
+ let (i, not_before) = ASN1Time::from_der(i)?;
+ let (i, not_after) = ASN1Time::from_der(i)?;
+ let v = Validity {
+ not_before,
+ not_after,
+ };
+ Ok((i, v))
+ })(i)
+ }
+
/// The time left before the certificate expires.
///
/// If the certificate is not currently valid, then `None` is
@@ -677,7 +410,7 @@ impl Validity {
/// Check the certificate time validity for the provided date/time
#[inline]
pub fn is_valid_at(&self, time: ASN1Time) -> bool {
- time >= self.not_before && time <= self.not_after
+ time >= self.not_before && time < self.not_after
}
/// Check the certificate time validity
@@ -687,21 +420,7 @@ impl Validity {
}
}
-impl<'a> FromDer<'a> for Validity {
- fn from_der(i: &[u8]) -> X509Result<Self> {
- parse_der_sequence_defined_g(|i, _| {
- let (i, not_before) = ASN1Time::from_der(i)?;
- let (i, not_after) = ASN1Time::from_der(i)?;
- let v = Validity {
- not_before,
- not_after,
- };
- Ok((i, v))
- })(i)
- }
-}
-
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct UniqueIdentifier<'a>(pub BitStringObject<'a>);
impl<'a> UniqueIdentifier<'a> {
diff --git a/src/certification_request.rs b/src/certification_request.rs
index 12d301f..ec8a9f0 100644
--- a/src/certification_request.rs
+++ b/src/certification_request.rs
@@ -1,7 +1,8 @@
use crate::cri_attributes::*;
-use crate::error::{X509Error, X509Result};
+#[cfg(feature = "verify")]
+use crate::error::X509Error;
+use crate::error::X509Result;
use crate::extensions::*;
-use crate::traits::FromDer;
use crate::x509::{
parse_signature_value, AlgorithmIdentifier, SubjectPublicKeyInfo, X509Name, X509Version,
};
@@ -15,7 +16,6 @@ use nom::Offset;
use oid_registry::*;
use std::collections::HashMap;
-/// Certification Signing Request (CSR)
#[derive(Debug, PartialEq)]
pub struct X509CertificationRequest<'a> {
pub certification_request_info: X509CertificationRequestInfo<'a>,
@@ -24,12 +24,45 @@ pub struct X509CertificationRequest<'a> {
}
impl<'a> X509CertificationRequest<'a> {
- pub fn requested_extensions(&self) -> Option<impl Iterator<Item = &ParsedExtension>> {
+ /// Parse a certification signing request (CSR)
+ ///
+ /// <pre>
+ /// CertificationRequest ::= SEQUENCE {
+ /// certificationRequestInfo CertificationRequestInfo,
+ /// signatureAlgorithm AlgorithmIdentifier{{ SignatureAlgorithms }},
+ /// signature BIT STRING
+ /// }
+ /// </pre>
+ ///
+ /// certificateRequestInfo is the "Certification request information", it is the value being
+ /// signed; signatureAlgorithm identifies the signature algorithm; and signature is the result
+ /// of signing the certification request information with the subject's private key.
+ pub fn from_der(i: &'a [u8]) -> X509Result<Self> {
+ parse_der_sequence_defined_g(|i, _| {
+ let (i, certification_request_info) = X509CertificationRequestInfo::from_der(i)?;
+ let (i, signature_algorithm) = AlgorithmIdentifier::from_der(i)?;
+ let (i, signature_value) = parse_signature_value(i)?;
+ let cert = X509CertificationRequest {
+ certification_request_info,
+ signature_algorithm,
+ signature_value,
+ };
+ Ok((i, cert))
+ })(i)
+ }
+
+ pub fn requested_extensions(&self) -> Option<impl Iterator<Item = &ParsedExtension<'a>>> {
self.certification_request_info
- .iter_attributes()
+ .attributes
+ .values()
.find_map(|attr| {
if let ParsedCriAttribute::ExtensionRequest(requested) = &attr.parsed_attribute {
- Some(requested.extensions.iter().map(|ext| &ext.parsed_extension))
+ Some(
+ requested
+ .extensions
+ .values()
+ .map(|ext| &ext.parsed_extension),
+ )
} else {
None
}
@@ -59,8 +92,6 @@ impl<'a> X509CertificationRequest<'a> {
&signature::ECDSA_P256_SHA256_ASN1
} else if *signature_alg == OID_SIG_ECDSA_WITH_SHA384 {
&signature::ECDSA_P384_SHA384_ASN1
- } else if *signature_alg == OID_SIG_ED25519 {
- &signature::ED25519
} else {
return Err(X509Error::SignatureUnsupportedAlgorithm);
};
@@ -73,101 +104,34 @@ impl<'a> X509CertificationRequest<'a> {
}
}
-/// <pre>
-/// CertificationRequest ::= SEQUENCE {
-/// certificationRequestInfo CertificationRequestInfo,
-/// signatureAlgorithm AlgorithmIdentifier{{ SignatureAlgorithms }},
-/// signature BIT STRING
-/// }
-/// </pre>
-impl<'a> FromDer<'a> for X509CertificationRequest<'a> {
- fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
- parse_der_sequence_defined_g(|i, _| {
- let (i, certification_request_info) = X509CertificationRequestInfo::from_der(i)?;
- let (i, signature_algorithm) = AlgorithmIdentifier::from_der(i)?;
- let (i, signature_value) = parse_signature_value(i)?;
- let cert = X509CertificationRequest {
- certification_request_info,
- signature_algorithm,
- signature_value,
- };
- Ok((i, cert))
- })(i)
- }
-}
-
-/// Certification Request Info structure
-///
-/// Certification request information is defined by the following ASN.1 structure:
-///
-/// <pre>
-/// CertificationRequestInfo ::= SEQUENCE {
-/// version INTEGER { v1(0) } (v1,...),
-/// subject Name,
-/// subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }},
-/// attributes [0] Attributes{{ CRIAttributes }}
-/// }
-/// </pre>
-///
-/// version is the version number; subject is the distinguished name of the certificate
-/// subject; subject_pki contains information about the public key being certified, and
-/// attributes is a collection of attributes providing additional information about the
-/// subject of the certificate.
#[derive(Debug, PartialEq)]
pub struct X509CertificationRequestInfo<'a> {
pub version: X509Version,
pub subject: X509Name<'a>,
pub subject_pki: SubjectPublicKeyInfo<'a>,
- attributes: Vec<X509CriAttribute<'a>>,
+ pub attributes: HashMap<Oid<'a>, X509CriAttribute<'a>>,
pub raw: &'a [u8],
}
impl<'a> X509CertificationRequestInfo<'a> {
- /// Get the CRL entry extensions.
- #[inline]
- pub fn attributes(&self) -> &[X509CriAttribute] {
- &self.attributes
- }
-
- /// Returns an iterator over the CRL entry extensions
- #[inline]
- pub fn iter_attributes(&self) -> impl Iterator<Item = &X509CriAttribute> {
- self.attributes.iter()
- }
-
- /// Searches for a CRL entry extension with the given `Oid`.
+ /// Parse a certification request info structure
///
- /// Note: if there are several extensions with the same `Oid`, the first one is returned.
- pub fn find_attribute(&self, oid: &Oid) -> Option<&X509CriAttribute> {
- self.attributes.iter().find(|&ext| ext.oid == *oid)
- }
-
- /// Builds and returns a map of CRL entry extensions.
+ /// Certification request information is defined by the following ASN.1 structure:
///
- /// If an extension is present twice, this will fail and return `DuplicateExtensions`.
- pub fn attributes_map(&self) -> Result<HashMap<Oid, &X509CriAttribute>, X509Error> {
- self.attributes
- .iter()
- .try_fold(HashMap::new(), |mut m, ext| {
- if m.contains_key(&ext.oid) {
- return Err(X509Error::DuplicateAttributes);
- }
- m.insert(ext.oid.clone(), ext);
- Ok(m)
- })
- }
-}
-
-/// <pre>
-/// CertificationRequestInfo ::= SEQUENCE {
-/// version INTEGER { v1(0) } (v1,...),
-/// subject Name,
-/// subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }},
-/// attributes [0] Attributes{{ CRIAttributes }}
-/// }
-/// </pre>
-impl<'a> FromDer<'a> for X509CertificationRequestInfo<'a> {
- fn from_der(i: &'a [u8]) -> X509Result<Self> {
+ /// <pre>
+ /// CertificationRequestInfo ::= SEQUENCE {
+ /// version INTEGER { v1(0) } (v1,...),
+ /// subject Name,
+ /// subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }},
+ /// attributes [0] Attributes{{ CRIAttributes }}
+ /// }
+ /// </pre>
+ ///
+ /// version is the version number; subject is the distinguished name of the certificate
+ /// subject; subject_pki contains information about the public key being certified, and
+ /// attributes is a collection of attributes providing additional information about the
+ /// subject of the certificate.
+ pub fn from_der(i: &'a [u8]) -> X509Result<Self> {
let start_i = i;
parse_der_sequence_defined_g(move |i, _| {
let (i, version) = X509Version::from_der_required(i)?;
diff --git a/src/cri_attributes.rs b/src/cri_attributes.rs
index cfb7192..410bf45 100644
--- a/src/cri_attributes.rs
+++ b/src/cri_attributes.rs
@@ -1,7 +1,6 @@
use crate::{
error::{X509Error, X509Result},
extensions::X509Extension,
- traits::FromDer,
};
use der_parser::der::{
@@ -15,15 +14,15 @@ use oid_registry::*;
use std::collections::HashMap;
/// Attributes for Certification Request
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct X509CriAttribute<'a> {
pub oid: Oid<'a>,
pub value: &'a [u8],
pub(crate) parsed_attribute: ParsedCriAttribute<'a>,
}
-impl<'a> FromDer<'a> for X509CriAttribute<'a> {
- fn from_der(i: &'a [u8]) -> X509Result<X509CriAttribute> {
+impl<'a> X509CriAttribute<'a> {
+ pub fn from_der(i: &'a [u8]) -> X509Result<X509CriAttribute> {
parse_der_sequence_defined_g(|i, _| {
let (i, oid) = map_res(parse_der_oid, |x| x.as_oid_val())(i)?;
let value_start = i;
@@ -45,19 +44,13 @@ impl<'a> FromDer<'a> for X509CriAttribute<'a> {
}
/// Section 3.1 of rfc 5272
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct ExtensionRequest<'a> {
- pub extensions: Vec<X509Extension<'a>>,
-}
-
-impl<'a> FromDer<'a> for ExtensionRequest<'a> {
- fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
- parser::parse_extension_request(i).map_err(Err::convert)
- }
+ pub extensions: HashMap<Oid<'a>, X509Extension<'a>>,
}
/// Attributes for Certification Request
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub enum ParsedCriAttribute<'a> {
ExtensionRequest(ExtensionRequest<'a>),
UnsupportedAttribute,
@@ -68,7 +61,6 @@ pub(crate) mod parser {
use der_parser::error::BerError;
use der_parser::{oid::Oid, *};
use lazy_static::lazy_static;
- use nom::combinator::map;
use nom::{Err, IResult};
type AttrParser = fn(&[u8]) -> IResult<&[u8], ParsedCriAttribute, BerError>;
@@ -82,7 +74,7 @@ pub(crate) mod parser {
}
let mut m = HashMap::new();
- add!(m, OID_PKCS9_EXTENSION_REQUEST, parse_extension_request_ext);
+ add!(m, OID_PKCS9_EXTENSION_REQUEST, parse_extension_request);
m
};
}
@@ -100,24 +92,39 @@ pub(crate) mod parser {
}
}
- pub(super) fn parse_extension_request(i: &[u8]) -> IResult<&[u8], ExtensionRequest, BerError> {
+ fn parse_extension_request(i: &[u8]) -> IResult<&[u8], ParsedCriAttribute, BerError> {
crate::extensions::parse_extension_sequence(i)
- .map(|(i, extensions)| (i, ExtensionRequest { extensions }))
+ .and_then(|(i, extensions)| {
+ crate::extensions::extensions_sequence_to_map(i, extensions)
+ })
+ .map(|(i, extensions)| {
+ (
+ i,
+ ParsedCriAttribute::ExtensionRequest(ExtensionRequest { extensions }),
+ )
+ })
.map_err(|_| Err::Error(BerError::BerTypeError))
}
+}
- fn parse_extension_request_ext(i: &[u8]) -> IResult<&[u8], ParsedCriAttribute, BerError> {
- map(
- parse_extension_request,
- ParsedCriAttribute::ExtensionRequest,
- )(i)
+fn attributes_sequence_to_map<'a>(
+ i: &'a [u8],
+ v: Vec<X509CriAttribute<'a>>,
+) -> X509Result<'a, HashMap<Oid<'a>, X509CriAttribute<'a>>> {
+ let mut attributes = HashMap::new();
+ for attr in v.into_iter() {
+ if attributes.insert(attr.oid.clone(), attr).is_some() {
+ // duplicate attributes are not allowed
+ return Err(Err::Failure(X509Error::DuplicateAttributes));
+ }
}
+ Ok((i, attributes))
}
-pub(crate) fn parse_cri_attributes(i: &[u8]) -> X509Result<Vec<X509CriAttribute>> {
+pub(crate) fn parse_cri_attributes(i: &[u8]) -> X509Result<HashMap<Oid, X509CriAttribute>> {
let (i, hdr) = der_read_element_header(i).or(Err(Err::Error(X509Error::InvalidAttributes)))?;
if i.is_empty() {
- return Ok((i, Vec::new()));
+ return Ok((i, HashMap::new()));
}
(0..hdr.structured)
.into_iter()
@@ -126,4 +133,5 @@ pub(crate) fn parse_cri_attributes(i: &[u8]) -> X509Result<Vec<X509CriAttribute>
attrs.push(attr);
Ok((rem, attrs))
})
+ .and_then(|(i, attrs)| attributes_sequence_to_map(i, attrs))
}
diff --git a/src/error.rs b/src/error.rs
index 65b0503..b2da105 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -14,7 +14,7 @@ pub struct NidError;
pub type X509Result<'a, T> = IResult<&'a [u8], T, X509Error>;
/// An error that can occur while parsing or validating a certificate.
-#[derive(Clone, Debug, PartialEq, thiserror::Error)]
+#[derive(Debug, PartialEq, thiserror::Error)]
pub enum X509Error {
#[error("generic error")]
Generic,
diff --git a/src/extensions.rs b/src/extensions.rs
index 6172d47..9932169 100644
--- a/src/extensions.rs
+++ b/src/extensions.rs
@@ -2,72 +2,20 @@
use crate::error::{X509Error, X509Result};
use crate::time::{der_to_utctime, ASN1Time};
-use crate::traits::FromDer;
-use crate::x509::{ReasonCode, RelativeDistinguishedName, X509Name};
+use crate::x509::{ReasonCode, X509Name};
-use der_parser::ber::parse_ber_bool;
use der_parser::der::*;
-use der_parser::error::{BerError, BerResult};
+use der_parser::error::BerResult;
use der_parser::num_bigint::BigUint;
use der_parser::oid::Oid;
-use nom::combinator::{all_consuming, complete, map, map_opt, map_res, opt};
+use nom::combinator::{all_consuming, complete, map_opt, map_res, opt};
use nom::multi::{many0, many1};
-use nom::{Err, IResult, Parser};
+use nom::{exact, Err};
use oid_registry::*;
use std::collections::HashMap;
use std::fmt;
-/// X.509 version 3 extension
-///
-/// X.509 extensions allow adding attributes to objects like certificates or revocation lists.
-///
-/// Each extension in a certificate is designated as either critical or non-critical. A
-/// certificate using system MUST reject the certificate if it encounters a critical extension it
-/// does not recognize; however, a non-critical extension MAY be ignored if it is not recognized.
-///
-/// Each extension includes an OID and an ASN.1 structure. When an extension appears in a
-/// certificate, the OID appears as the field extnID and the corresponding ASN.1 encoded structure
-/// is the value of the octet string extnValue. A certificate MUST NOT include more than one
-/// instance of a particular extension.
-///
-/// When parsing an extension, the global extension structure (described above) is parsed,
-/// and the object is returned if it succeeds.
-/// During this step, it also attempts to parse the content of the extension, if known.
-/// The returned object has a
-/// [`X509Extension::parsed_extension()`] method. The returned
-/// enum is either a known extension, or the special value `ParsedExtension::UnsupportedExtension`.
-///
-/// # Example
-///
-/// ```rust
-/// use x509_parser::extensions::{X509Extension, ParsedExtension};
-/// use x509_parser::traits::FromDer;
-///
-/// static DER: &[u8] = &[
-/// 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04, 0x16, 0x04, 0x14, 0xA3, 0x05, 0x2F, 0x18,
-/// 0x60, 0x50, 0xC2, 0x89, 0x0A, 0xDD, 0x2B, 0x21, 0x4F, 0xFF, 0x8E, 0x4E, 0xA8, 0x30, 0x31,
-/// 0x36 ];
-///
-/// # fn main() {
-/// let res = X509Extension::from_der(DER);
-/// match res {
-/// Ok((_rem, ext)) => {
-/// println!("Extension OID: {}", ext.oid);
-/// println!(" Critical: {}", ext.critical);
-/// let parsed_ext = ext.parsed_extension();
-/// assert!(!parsed_ext.unsupported());
-/// assert!(parsed_ext.error().is_none());
-/// if let ParsedExtension::SubjectKeyIdentifier(key_id) = parsed_ext {
-/// assert!(key_id.0.len() > 0);
-/// } else {
-/// panic!("Extension has wrong type");
-/// }
-/// },
-/// _ => panic!("x509 extension parsing failed: {:?}", res),
-/// }
-/// # }
-/// ```
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct X509Extension<'a> {
/// OID describing the extension content
pub oid: Oid<'a>,
@@ -81,9 +29,78 @@ pub struct X509Extension<'a> {
}
impl<'a> X509Extension<'a> {
- /// Creates a new extension with the provided values.
- #[inline]
- pub const fn new(
+ /// Parse a DER-encoded X.509 extension
+ ///
+ /// X.509 extensions allow adding attributes to objects like certificates or revocation lists.
+ ///
+ /// Each extension in a certificate is designated as either critical or non-critical. A
+ /// certificate using system MUST reject the certificate if it encounters a critical extension it
+ /// does not recognize; however, a non-critical extension MAY be ignored if it is not recognized.
+ ///
+ /// Each extension includes an OID and an ASN.1 structure. When an extension appears in a
+ /// certificate, the OID appears as the field extnID and the corresponding ASN.1 encoded structure
+ /// is the value of the octet string extnValue. A certificate MUST NOT include more than one
+ /// instance of a particular extension.
+ ///
+ /// This function parses the global structure (described above), and will return the object if it
+ /// succeeds. During this step, it also attempts to parse the content of the extension, if known.
+ /// The returned object has a
+ /// [parsed_extension](x509/struct.X509Extension.html#method.parsed_extension) method. The returned
+ /// enum is either a known extension, or the special value `ParsedExtension::UnsupportedExtension`.
+ ///
+ /// <pre>
+ /// Extension ::= SEQUENCE {
+ /// extnID OBJECT IDENTIFIER,
+ /// critical BOOLEAN DEFAULT FALSE,
+ /// extnValue OCTET STRING }
+ /// </pre>
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use x509_parser::extensions::{X509Extension, ParsedExtension};
+ /// #
+ /// static DER: &[u8] = &[
+ /// 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04, 0x16, 0x04, 0x14, 0xA3, 0x05, 0x2F, 0x18,
+ /// 0x60, 0x50, 0xC2, 0x89, 0x0A, 0xDD, 0x2B, 0x21, 0x4F, 0xFF, 0x8E, 0x4E, 0xA8, 0x30, 0x31,
+ /// 0x36 ];
+ ///
+ /// # fn main() {
+ /// let res = X509Extension::from_der(DER);
+ /// match res {
+ /// Ok((_rem, ext)) => {
+ /// println!("Extension OID: {}", ext.oid);
+ /// println!(" Critical: {}", ext.critical);
+ /// let parsed_ext = ext.parsed_extension();
+ /// assert!(*parsed_ext != ParsedExtension::UnsupportedExtension);
+ /// if let ParsedExtension::SubjectKeyIdentifier(key_id) = parsed_ext {
+ /// assert!(key_id.0.len() > 0);
+ /// } else {
+ /// panic!("Extension has wrong type");
+ /// }
+ /// },
+ /// _ => panic!("x509 extension parsing failed: {:?}", res),
+ /// }
+ /// # }
+ /// ```
+ pub fn from_der(i: &'a [u8]) -> X509Result<Self> {
+ parse_der_sequence_defined_g(|i, _| {
+ let (i, oid) = map_res(parse_der_oid, |x| x.as_oid_val())(i)?;
+ let (i, critical) = der_read_critical(i)?;
+ let (i, value) = map_res(parse_der_octetstring, |x| x.as_slice())(i)?;
+ let (i, parsed_extension) = crate::extensions::parser::parse_extension(i, value, &oid)?;
+ let ext = X509Extension {
+ oid,
+ critical,
+ value,
+ parsed_extension,
+ };
+ Ok((i, ext))
+ })(i)
+ .map_err(|_| X509Error::InvalidExtensions.into())
+ }
+
+ pub fn new(
oid: Oid<'a>,
critical: bool,
value: &'a [u8],
@@ -98,78 +115,16 @@ impl<'a> X509Extension<'a> {
}
/// Return the extension type or `UnsupportedExtension` if the extension is not implemented.
- #[inline]
pub fn parsed_extension(&self) -> &ParsedExtension<'a> {
&self.parsed_extension
}
}
-/// <pre>
-/// Extension ::= SEQUENCE {
-/// extnID OBJECT IDENTIFIER,
-/// critical BOOLEAN DEFAULT FALSE,
-/// extnValue OCTET STRING }
-/// </pre>
-impl<'a> FromDer<'a> for X509Extension<'a> {
- fn from_der(i: &'a [u8]) -> X509Result<Self> {
- X509ExtensionParser::new().parse(i)
- }
-}
-
-/// `X509Extension` parser builder
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub struct X509ExtensionParser {
- deep_parse_extensions: bool,
-}
-
-impl X509ExtensionParser {
- #[inline]
- pub const fn new() -> Self {
- X509ExtensionParser {
- deep_parse_extensions: true,
- }
- }
-
- #[inline]
- pub const fn with_deep_parse_extensions(self, deep_parse_extensions: bool) -> Self {
- X509ExtensionParser {
- deep_parse_extensions,
- }
- }
-}
-
-impl<'a> Parser<&'a [u8], X509Extension<'a>, X509Error> for X509ExtensionParser {
- fn parse(&mut self, input: &'a [u8]) -> IResult<&'a [u8], X509Extension<'a>, X509Error> {
- parse_der_sequence_defined_g(|i, _| {
- let (i, oid) = map_res(parse_der_oid, |x| x.as_oid_val())(i)?;
- let (i, critical) = der_read_critical(i)?;
- let (i, value) = map_res(parse_der_octetstring, |x| x.as_slice())(i)?;
- let (i, parsed_extension) = if self.deep_parse_extensions {
- parser::parse_extension(i, value, &oid)?
- } else {
- (&[] as &[_], ParsedExtension::Unparsed)
- };
- let ext = X509Extension {
- oid,
- critical,
- value,
- parsed_extension,
- };
- Ok((i, ext))
- })(input)
- .map_err(|_| X509Error::InvalidExtensions.into())
- }
-}
-
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub enum ParsedExtension<'a> {
/// Crate parser does not support this extension (yet)
- UnsupportedExtension {
- oid: Oid<'a>,
- },
- ParseError {
- error: Err<BerError>,
- },
+ UnsupportedExtension,
+ ParseError,
/// Section 4.2.1.1 of rfc 5280
AuthorityKeyIdentifier(AuthorityKeyIdentifier<'a>),
/// Section 4.2.1.2 of rfc 5280
@@ -190,8 +145,6 @@ pub enum ParsedExtension<'a> {
PolicyConstraints(PolicyConstraints),
/// Section 4.2.1.12 of rfc 5280
ExtendedKeyUsage(ExtendedKeyUsage<'a>),
- /// Section 4.2.1.13 of rfc 5280
- CRLDistributionPoints(CRLDistributionPoints<'a>),
/// Section 4.2.1.14 of rfc 5280
InhibitAnyPolicy(InhibitAnyPolicy),
/// Section 4.2.2.1 of rfc 5280
@@ -204,81 +157,31 @@ pub enum ParsedExtension<'a> {
ReasonCode(ReasonCode),
/// Section 5.3.3 of rfc 5280
InvalidityDate(ASN1Time),
- /// Unparsed extension (was not requested in parsing options)
- Unparsed,
-}
-
-impl<'a> ParsedExtension<'a> {
- /// Return `true` if the extension is unsupported
- pub fn unsupported(&self) -> bool {
- matches!(self, &ParsedExtension::UnsupportedExtension { .. })
- }
-
- /// Return a reference on the parsing error if the extension parsing failed
- pub fn error(&self) -> Option<&Err<BerError>> {
- match self {
- ParsedExtension::ParseError { error } => Some(error),
- _ => None,
- }
- }
}
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct AuthorityKeyIdentifier<'a> {
pub key_identifier: Option<KeyIdentifier<'a>>,
pub authority_cert_issuer: Option<Vec<GeneralName<'a>>>,
pub authority_cert_serial: Option<&'a [u8]>,
}
-impl<'a> FromDer<'a> for AuthorityKeyIdentifier<'a> {
- fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
- parser::parse_authoritykeyidentifier(i).map_err(Err::convert)
- }
-}
-
-pub type CertificatePolicies<'a> = Vec<PolicyInformation<'a>>;
-
-impl<'a> FromDer<'a> for CertificatePolicies<'a> {
- fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
- parser::parse_certificatepolicies(i).map_err(Err::convert)
- }
-}
-
-#[derive(Clone, Debug, PartialEq)]
-pub struct PolicyInformation<'a> {
- pub policy_id: Oid<'a>,
- pub policy_qualifiers: Option<Vec<PolicyQualifierInfo<'a>>>,
-}
-
-#[derive(Clone, Debug, PartialEq)]
-pub struct PolicyQualifierInfo<'a> {
- pub policy_qualifier_id: Oid<'a>,
- pub qualifier: &'a [u8],
+#[derive(Debug, PartialEq)]
+pub struct CertificatePolicies<'a> {
+ pub policies: HashMap<Oid<'a>, &'a [u8]>,
}
/// Identifies whether the subject of the certificate is a CA, and the max validation depth.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct BasicConstraints {
pub ca: bool,
pub path_len_constraint: Option<u32>,
}
-impl<'a> FromDer<'a> for BasicConstraints {
- fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
- parser::parse_basicconstraints(i).map_err(Err::convert)
- }
-}
-
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct KeyIdentifier<'a>(pub &'a [u8]);
-impl<'a> FromDer<'a> for KeyIdentifier<'a> {
- fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
- parser::parse_keyidentifier(i).map_err(Err::convert)
- }
-}
-
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct KeyUsage {
pub flags: u16,
}
@@ -344,13 +247,7 @@ impl fmt::Display for KeyUsage {
}
}
-impl<'a> FromDer<'a> for KeyUsage {
- fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
- parser::parse_keyusage(i).map_err(Err::convert)
- }
-}
-
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct ExtendedKeyUsage<'a> {
pub any: bool,
pub server_auth: bool,
@@ -358,17 +255,11 @@ pub struct ExtendedKeyUsage<'a> {
pub code_signing: bool,
pub email_protection: bool,
pub time_stamping: bool,
- pub ocsp_signing: bool,
+ pub ocscp_signing: bool,
pub other: Vec<Oid<'a>>,
}
-impl<'a> FromDer<'a> for ExtendedKeyUsage<'a> {
- fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
- parser::parse_extendedkeyusage(i).map_err(Err::convert)
- }
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[derive(Debug, PartialEq)]
pub struct NSCertType(u8);
// The value is a bit-string, where the individual bit positions are defined as:
@@ -432,188 +323,33 @@ impl fmt::Display for NSCertType {
}
}
-impl<'a> FromDer<'a> for NSCertType {
- fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
- parser::parse_nscerttype(i).map_err(Err::convert)
- }
-}
-
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct AuthorityInfoAccess<'a> {
- pub accessdescs: Vec<AccessDescription<'a>>,
-}
-
-impl<'a> AuthorityInfoAccess<'a> {
- /// Returns a `HashMap` mapping `Oid` to the list of references to `GeneralNames`
- ///
- /// If several names match the same `Oid`, they are merged in the same entry.
- pub fn as_hashmap(&self) -> HashMap<Oid<'a>, Vec<&GeneralName<'a>>> {
- // create the hashmap and merge entries with same OID
- let mut m: HashMap<Oid, Vec<&GeneralName>> = HashMap::new();
- for desc in &self.accessdescs {
- let AccessDescription {
- access_method: oid,
- access_location: gn,
- } = desc;
- if let Some(general_names) = m.get_mut(oid) {
- general_names.push(gn);
- } else {
- m.insert(oid.clone(), vec![gn]);
- }
- }
- m
- }
-
- /// Returns a `HashMap` mapping `Oid` to the list of `GeneralNames` (consuming the input)
- ///
- /// If several names match the same `Oid`, they are merged in the same entry.
- pub fn into_hashmap(self) -> HashMap<Oid<'a>, Vec<GeneralName<'a>>> {
- let mut aia_list = self.accessdescs;
- // create the hashmap and merge entries with same OID
- let mut m: HashMap<Oid, Vec<GeneralName>> = HashMap::new();
- for desc in aia_list.drain(..) {
- let AccessDescription {
- access_method: oid,
- access_location: gn,
- } = desc;
- if let Some(general_names) = m.get_mut(&oid) {
- general_names.push(gn);
- } else {
- m.insert(oid, vec![gn]);
- }
- }
- m
- }
-}
-
-impl<'a> FromDer<'a> for AuthorityInfoAccess<'a> {
- fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
- parser::parse_authorityinfoaccess(i).map_err(Err::convert)
- }
-}
-
-#[derive(Clone, Debug, PartialEq)]
-pub struct AccessDescription<'a> {
- pub access_method: Oid<'a>,
- pub access_location: GeneralName<'a>,
-}
-
-impl<'a> AccessDescription<'a> {
- pub const fn new(access_method: Oid<'a>, access_location: GeneralName<'a>) -> Self {
- AccessDescription {
- access_method,
- access_location,
- }
- }
+ pub accessdescs: HashMap<Oid<'a>, Vec<GeneralName<'a>>>,
}
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct InhibitAnyPolicy {
pub skip_certs: u32,
}
-impl<'a> FromDer<'a> for InhibitAnyPolicy {
- fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
- map(parse_der_u32, |skip_certs| InhibitAnyPolicy { skip_certs })(i).map_err(Err::convert)
- }
-}
-
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct PolicyMappings<'a> {
- pub mappings: Vec<PolicyMapping<'a>>,
-}
-
-impl<'a> FromDer<'a> for PolicyMappings<'a> {
- fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
- parser::parse_policymappings(i).map_err(Err::convert)
- }
-}
-
-impl<'a> PolicyMappings<'a> {
- /// Returns a `HashMap` mapping `Oid` to the list of references to `Oid`
- ///
- /// If several names match the same `Oid`, they are merged in the same entry.
- pub fn as_hashmap(&self) -> HashMap<Oid<'a>, Vec<&Oid<'a>>> {
- // create the hashmap and merge entries with same OID
- let mut m: HashMap<Oid, Vec<&_>> = HashMap::new();
- for desc in &self.mappings {
- let PolicyMapping {
- issuer_domain_policy: left,
- subject_domain_policy: right,
- } = desc;
- if let Some(l) = m.get_mut(left) {
- l.push(right);
- } else {
- m.insert(left.clone(), vec![right]);
- }
- }
- m
- }
-
- /// Returns a `HashMap` mapping `Oid` to the list of `Oid` (consuming the input)
- ///
- /// If several names match the same `Oid`, they are merged in the same entry.
- pub fn into_hashmap(self) -> HashMap<Oid<'a>, Vec<Oid<'a>>> {
- let mut l = self.mappings;
- // create the hashmap and merge entries with same OID
- let mut m: HashMap<Oid, Vec<_>> = HashMap::new();
- for mapping in l.drain(..) {
- let PolicyMapping {
- issuer_domain_policy: left,
- subject_domain_policy: right,
- } = mapping;
- if let Some(general_names) = m.get_mut(&left) {
- general_names.push(right);
- } else {
- m.insert(left, vec![right]);
- }
- }
- m
- }
-}
-
-#[derive(Clone, Debug, PartialEq)]
-pub struct PolicyMapping<'a> {
- pub issuer_domain_policy: Oid<'a>,
- pub subject_domain_policy: Oid<'a>,
-}
-
-impl<'a> PolicyMapping<'a> {
- pub const fn new(issuer_domain_policy: Oid<'a>, subject_domain_policy: Oid<'a>) -> Self {
- PolicyMapping {
- issuer_domain_policy,
- subject_domain_policy,
- }
- }
+ pub mappings: HashMap<Oid<'a>, Vec<Oid<'a>>>,
}
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct PolicyConstraints {
pub require_explicit_policy: Option<u32>,
pub inhibit_policy_mapping: Option<u32>,
}
-impl<'a> FromDer<'a> for PolicyConstraints {
- fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
- parser::parse_policyconstraints(i).map_err(Err::convert)
- }
-}
-
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct SubjectAlternativeName<'a> {
pub general_names: Vec<GeneralName<'a>>,
}
-impl<'a> FromDer<'a> for SubjectAlternativeName<'a> {
- fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
- parse_der_sequence_defined_g(|input, _| {
- let (i, general_names) = all_consuming(many0(complete(GeneralName::from_der)))(input)?;
- Ok((i, SubjectAlternativeName { general_names }))
- })(i)
- }
-}
-
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
/// Represents a GeneralName as defined in RFC5280. There
/// is no support X.400 addresses and EDIPartyName.
///
@@ -624,13 +360,11 @@ pub enum GeneralName<'a> {
RFC822Name(&'a str),
/// A hostname, format is not checked.
DNSName(&'a str),
- /// X400Address,
- X400Address(UnparsedObject<'a>),
+ // X400Address,
/// RFC5280 defines several string types, we always try to parse as utf-8
/// which is more or less a superset of the string types.
DirectoryName(X509Name<'a>),
- /// EDIPartyName
- EDIPartyName(UnparsedObject<'a>),
+ // EDIPartyName { name_assigner: Option<&'a str>, party_name: &'a str },
/// An uniform resource identifier. The format is not checked.
URI(&'a str),
/// An ip address, provided as encoded.
@@ -638,31 +372,13 @@ pub enum GeneralName<'a> {
RegisteredID(Oid<'a>),
}
-impl<'a> FromDer<'a> for GeneralName<'a> {
- fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
- parser::parse_generalname(i).map_err(Err::convert)
- }
-}
-
-#[derive(Clone, Debug, PartialEq)]
-pub struct UnparsedObject<'a> {
- pub header: DerObjectHeader<'a>,
- pub data: &'a [u8],
-}
-
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct NameConstraints<'a> {
pub permitted_subtrees: Option<Vec<GeneralSubtree<'a>>>,
pub excluded_subtrees: Option<Vec<GeneralSubtree<'a>>>,
}
-impl<'a> FromDer<'a> for NameConstraints<'a> {
- fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
- parser::parse_nameconstraints(i).map_err(Err::convert)
- }
-}
-
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
/// Represents the structure used in the name constraints extensions.
/// The fields minimum and maximum are not supported (openssl also has no support).
pub struct GeneralSubtree<'a> {
@@ -671,94 +387,11 @@ pub struct GeneralSubtree<'a> {
// maximum: Option<u32>,
}
-pub type CRLDistributionPoints<'a> = Vec<CRLDistributionPoint<'a>>;
-
-impl<'a> FromDer<'a> for CRLDistributionPoints<'a> {
- fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
- parser::parse_crldistributionpoints(i).map_err(Err::convert)
- }
-}
-
-#[derive(Clone, Debug, PartialEq)]
-pub struct CRLDistributionPoint<'a> {
- pub distribution_point: Option<DistributionPointName<'a>>,
- pub reasons: Option<ReasonFlags>,
- pub crl_issuer: Option<Vec<GeneralName<'a>>>,
-}
-
-#[derive(Clone, Debug, PartialEq)]
-pub enum DistributionPointName<'a> {
- FullName(Vec<GeneralName<'a>>),
- NameRelativeToCRLIssuer(RelativeDistinguishedName<'a>),
-}
-
-#[derive(Clone, Debug, PartialEq)]
-pub struct ReasonFlags {
- pub flags: u16,
-}
-
-impl ReasonFlags {
- pub fn key_compromise(&self) -> bool {
- (self.flags >> 1) & 1 == 1
- }
- pub fn ca_compromise(&self) -> bool {
- (self.flags >> 2) & 1 == 1
- }
- pub fn affilation_changed(&self) -> bool {
- (self.flags >> 3) & 1 == 1
- }
- pub fn superseded(&self) -> bool {
- (self.flags >> 4) & 1 == 1
- }
- pub fn cessation_of_operation(&self) -> bool {
- (self.flags >> 5) & 1 == 1
- }
- pub fn certificate_hold(&self) -> bool {
- (self.flags >> 6) & 1 == 1
- }
- pub fn privelege_withdrawn(&self) -> bool {
- (self.flags >> 7) & 1 == 1
- }
- pub fn aa_compromise(&self) -> bool {
- (self.flags >> 8) & 1 == 1
- }
-}
-
-const REASON_FLAGS: &[&str] = &[
- "Unused",
- "Key Compromise",
- "CA Compromise",
- "Affiliation Changed",
- "Superseded",
- "Cessation Of Operation",
- "Certificate Hold",
- "Privilege Withdrawn",
- "AA Compromise",
-];
-
-impl fmt::Display for ReasonFlags {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut s = String::new();
- let mut acc = self.flags;
- for flag_text in REASON_FLAGS {
- if acc & 1 != 0 {
- s = s + flag_text + ", ";
- }
- acc >>= 1;
- }
- s.pop();
- s.pop();
- f.write_str(&s)
- }
-}
-
pub(crate) mod parser {
use crate::extensions::*;
- use crate::traits::FromDer;
use der_parser::error::BerError;
use der_parser::{oid::Oid, *};
use lazy_static::lazy_static;
- use nom::bytes::streaming::take;
use nom::combinator::{map, verify};
use nom::{Err, IResult};
@@ -773,60 +406,35 @@ pub(crate) mod parser {
}
let mut m = HashMap::new();
- add!(
- m,
- OID_X509_EXT_SUBJECT_KEY_IDENTIFIER,
- parse_keyidentifier_ext
- );
- add!(m, OID_X509_EXT_KEY_USAGE, parse_keyusage_ext);
+ add!(m, OID_X509_EXT_SUBJECT_KEY_IDENTIFIER, parse_keyidentifier);
+ add!(m, OID_X509_EXT_KEY_USAGE, parse_keyusage);
add!(
m,
OID_X509_EXT_SUBJECT_ALT_NAME,
- parse_subjectalternativename_ext
- );
- add!(
- m,
- OID_X509_EXT_BASIC_CONSTRAINTS,
- parse_basicconstraints_ext
+ parse_subjectalternativename
);
- add!(m, OID_X509_EXT_NAME_CONSTRAINTS, parse_nameconstraints_ext);
+ add!(m, OID_X509_EXT_BASIC_CONSTRAINTS, parse_basicconstraints);
+ add!(m, OID_X509_EXT_NAME_CONSTRAINTS, parse_nameconstraints);
add!(
m,
OID_X509_EXT_CERTIFICATE_POLICIES,
- parse_certificatepolicies_ext
- );
- add!(m, OID_X509_EXT_POLICY_MAPPINGS, parse_policymappings_ext);
- add!(
- m,
- OID_X509_EXT_POLICY_CONSTRAINTS,
- parse_policyconstraints_ext
- );
- add!(
- m,
- OID_X509_EXT_EXTENDED_KEY_USAGE,
- parse_extendedkeyusage_ext
- );
- add!(
- m,
- OID_X509_EXT_CRL_DISTRIBUTION_POINTS,
- parse_crldistributionpoints_ext
+ parse_certificatepolicies
);
+ add!(m, OID_X509_EXT_POLICY_MAPPINGS, parse_policymappings);
+ add!(m, OID_X509_EXT_POLICY_CONSTRAINTS, parse_policyconstraints);
+ add!(m, OID_X509_EXT_EXTENDED_KEY_USAGE, parse_extendedkeyusage);
add!(
m,
OID_X509_EXT_INHIBITANT_ANY_POLICY,
- parse_inhibitanypolicy_ext
- );
- add!(
- m,
- OID_PKIX_AUTHORITY_INFO_ACCESS,
- parse_authorityinfoaccess_ext
+ parse_inhibitanypolicy
);
+ add!(m, OID_PKIX_AUTHORITY_INFO_ACCESS, parse_authorityinfoaccess);
add!(
m,
OID_X509_EXT_AUTHORITY_KEY_IDENTIFIER,
- parse_authoritykeyidentifier_ext
+ parse_authoritykeyidentifier
);
- add!(m, OID_X509_EXT_CERT_TYPE, parse_nscerttype_ext);
+ add!(m, OID_X509_EXT_CERT_TYPE, parse_nscerttype);
add!(m, OID_X509_EXT_CRL_NUMBER, parse_crl_number);
add!(m, OID_X509_EXT_REASON_CODE, parse_reason_code);
add!(m, OID_X509_EXT_INVALIDITY_DATE, parse_invalidity_date);
@@ -842,17 +450,10 @@ pub(crate) mod parser {
oid: &Oid,
) -> IResult<&'a [u8], ParsedExtension<'a>, BerError> {
if let Some(parser) = EXTENSION_PARSERS.get(oid) {
- match parser(i) {
- Ok((_, ext)) => Ok((orig_i, ext)),
- Err(error) => Ok((orig_i, ParsedExtension::ParseError { error })),
- }
+ let (_, ext) = parser(i)?;
+ Ok((orig_i, ext))
} else {
- Ok((
- orig_i,
- ParsedExtension::UnsupportedExtension {
- oid: oid.to_owned(),
- },
- ))
+ Ok((orig_i, ParsedExtension::UnsupportedExtension))
}
}
@@ -861,7 +462,11 @@ pub(crate) mod parser {
i: &'a [u8],
oid: &Oid,
) -> IResult<&'a [u8], ParsedExtension<'a>, BerError> {
- parse_extension0(orig_i, i, oid)
+ let r = parse_extension0(orig_i, i, oid);
+ if let Err(nom::Err::Incomplete(_)) = r {
+ return Ok((orig_i, ParsedExtension::UnsupportedExtension));
+ }
+ r
}
/// Parse a "Basic Constraints" extension
@@ -875,7 +480,7 @@ pub(crate) mod parser {
///
/// Note the maximum length of the `pathLenConstraint` field is limited to the size of a 32-bits
/// unsigned integer, and parsing will fail if value if larger.
- pub(super) fn parse_basicconstraints(i: &[u8]) -> IResult<&[u8], BasicConstraints, BerError> {
+ fn parse_basicconstraints(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
let (rem, obj) = parse_der_sequence(i)?;
if let Ok(seq) = obj.as_sequence() {
let (ca, path_len_constraint) = match seq.len() {
@@ -902,23 +507,17 @@ pub(crate) mod parser {
};
Ok((
rem,
- BasicConstraints {
+ ParsedExtension::BasicConstraints(BasicConstraints {
ca,
path_len_constraint,
- },
+ }),
))
} else {
Err(nom::Err::Error(BerError::InvalidLength))
}
}
- fn parse_basicconstraints_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
- map(parse_basicconstraints, ParsedExtension::BasicConstraints)(i)
- }
-
- pub(super) fn parse_nameconstraints<'a>(
- i: &'a [u8],
- ) -> IResult<&'a [u8], NameConstraints, BerError> {
+ fn parse_nameconstraints<'a>(i: &'a [u8]) -> IResult<&'a [u8], ParsedExtension, BerError> {
fn parse_subtree<'a>(i: &'a [u8]) -> IResult<&'a [u8], GeneralSubtree, BerError> {
parse_der_sequence_defined_g(|input, _| {
map(parse_generalname, |base| GeneralSubtree { base })(input)
@@ -944,23 +543,17 @@ pub(crate) mod parser {
Ok((rem, named_constraints))
})(i)?;
- Ok((ret, named_constraints))
+ Ok((ret, ParsedExtension::NameConstraints(named_constraints)))
}
- fn parse_nameconstraints_ext<'a>(i: &'a [u8]) -> IResult<&'a [u8], ParsedExtension, BerError> {
- map(parse_nameconstraints, ParsedExtension::NameConstraints)(i)
- }
-
- pub(super) fn parse_generalname<'a>(i: &'a [u8]) -> IResult<&'a [u8], GeneralName, BerError> {
+ fn parse_generalname<'a>(i: &'a [u8]) -> IResult<&'a [u8], GeneralName, BerError> {
let (rest, hdr) = verify(der_read_element_header, |hdr| hdr.is_contextspecific())(i)?;
let len = hdr.len.primitive()?;
if len > rest.len() {
return Err(nom::Err::Failure(BerError::ObjectTooShort));
}
fn ia5str<'a>(i: &'a [u8], hdr: DerObjectHeader) -> Result<&'a str, Err<BerError>> {
- // Relax constraints from RFC here: we are expecting an IA5String, but many certificates
- // are using unicode characters
- der_read_element_content_as(i, DerTag::Utf8String, hdr.len, hdr.is_constructed(), 0)?
+ der_read_element_content_as(i, DerTag::Ia5String, hdr.len, hdr.is_constructed(), 0)?
.1
.as_slice()
.and_then(|s| std::str::from_utf8(s).map_err(|_| BerError::BerValueError))
@@ -975,12 +568,7 @@ pub(crate) mod parser {
}
1 => GeneralName::RFC822Name(ia5str(rest, hdr)?),
2 => GeneralName::DNSName(ia5str(rest, hdr)?),
- 3 => {
- // XXX Not yet implemented
- let (_, data) = take(len)(rest)?;
- let obj = UnparsedObject { header: hdr, data };
- GeneralName::X400Address(obj)
- }
+ 3 => return Err(Err::Failure(BerError::Unsupported)), // x400Address
4 => {
// directoryName, name
let (_, name) = all_consuming(X509Name::from_der)(&rest[..len])
@@ -988,12 +576,7 @@ pub(crate) mod parser {
?;
GeneralName::DirectoryName(name)
}
- 5 => {
- // XXX Not yet implemented
- let (_, data) = take(len)(rest)?;
- let obj = UnparsedObject { header: hdr, data };
- GeneralName::EDIPartyName(obj)
- }
+ 5 => return Err(Err::Failure(BerError::Unsupported)), // ediPartyName
6 => GeneralName::URI(ia5str(rest, hdr)?),
7 => {
// IPAddress, OctetString
@@ -1027,7 +610,7 @@ pub(crate) mod parser {
Ok((&rest[len..], name))
}
- pub(super) fn parse_subjectalternativename_ext<'a>(
+ fn parse_subjectalternativename<'a>(
i: &'a [u8],
) -> IResult<&'a [u8], ParsedExtension, BerError> {
parse_der_sequence_defined_g(|input, _| {
@@ -1039,7 +622,7 @@ pub(crate) mod parser {
})(i)
}
- pub(super) fn parse_policyconstraints(i: &[u8]) -> IResult<&[u8], PolicyConstraints, BerError> {
+ fn parse_policyconstraints(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
parse_der_sequence_defined_g(|input, _| {
let (i, require_explicit_policy) = opt(complete(map_res(
parse_der_tagged_implicit(0, parse_der_content(DerTag::Integer)),
@@ -1053,54 +636,50 @@ pub(crate) mod parser {
require_explicit_policy,
inhibit_policy_mapping,
};
- Ok((i, policy_constraint))
+ Ok((i, ParsedExtension::PolicyConstraints(policy_constraint)))
})(i)
}
- fn parse_policyconstraints_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
- map(parse_policyconstraints, ParsedExtension::PolicyConstraints)(i)
- }
-
// PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
// issuerDomainPolicy CertPolicyId,
// subjectDomainPolicy CertPolicyId }
- pub(super) fn parse_policymappings(i: &[u8]) -> IResult<&[u8], PolicyMappings, BerError> {
+ fn parse_policymappings(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
fn parse_oid_pair(i: &[u8]) -> IResult<&[u8], Vec<DerObject<'_>>, BerError> {
// read 2 OID as a SEQUENCE OF OID - length will be checked later
parse_der_sequence_of_v(parse_der_oid)(i)
}
let (ret, pairs) = parse_der_sequence_of_v(parse_oid_pair)(i)?;
- let mut mappings = Vec::new();
- // let mut mappings: HashMap<Oid, Vec<Oid>> = HashMap::new();
+ let mut mappings: HashMap<Oid, Vec<Oid>> = HashMap::new();
for pair in pairs.iter() {
if pair.len() != 2 {
return Err(Err::Failure(BerError::BerValueError));
}
let left = pair[0].as_oid_val().map_err(nom::Err::Failure)?;
let right = pair[1].as_oid_val().map_err(nom::Err::Failure)?;
- // XXX this should go to Validate
- // if left.bytes() == oid!(raw 2.5.29.32.0) || right.bytes() == oid!(raw 2.5.29.32.0) {
- // // mapping to or from anyPolicy is not allowed
- // return Err(Err::Failure(BerError::InvalidTag));
- // }
- mappings.push(PolicyMapping::new(left, right));
+ if left.bytes() == oid!(raw 2.5.29.32.0) || right.bytes() == oid!(raw 2.5.29.32.0) {
+ // mapping to or from anyPolicy is not allowed
+ return Err(Err::Failure(BerError::InvalidTag));
+ }
+ mappings
+ .entry(left)
+ .and_modify(|v| v.push(right.clone()))
+ .or_insert_with(|| vec![right.clone()]);
}
- Ok((ret, PolicyMappings { mappings }))
- }
-
- fn parse_policymappings_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
- map(parse_policymappings, ParsedExtension::PolicyMappings)(i)
+ Ok((
+ ret,
+ ParsedExtension::PolicyMappings(PolicyMappings { mappings }),
+ ))
}
- fn parse_inhibitanypolicy_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
- let (ret, skip_certs) = parse_der_u32(i)?;
+ fn parse_inhibitanypolicy(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
+ let (ret, skip_certs) = map_res(parse_der_integer, |x: DerObject| x.as_u32())(i)?;
Ok((
ret,
ParsedExtension::InhibitAnyPolicy(InhibitAnyPolicy { skip_certs }),
))
}
- pub(super) fn parse_extendedkeyusage(i: &[u8]) -> IResult<&[u8], ExtendedKeyUsage, BerError> {
+ fn parse_extendedkeyusage(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
let (ret, seq) = parse_der_sequence_of(parse_der_oid)(i)?;
let mut seen = std::collections::HashSet::new();
let mut eku = ExtendedKeyUsage {
@@ -1110,7 +689,7 @@ pub(crate) mod parser {
code_signing: false,
email_protection: false,
time_stamping: false,
- ocsp_signing: false,
+ ocscp_signing: false,
other: Vec::new(),
};
for oid in seq.as_sequence().map_err(nom::Err::Failure)?.iter() {
@@ -1132,101 +711,12 @@ pub(crate) mod parser {
} else if asn1 == oid!(raw 1.3.6.1.5.5.7.3.8) {
eku.time_stamping = true;
} else if asn1 == oid!(raw 1.3.6.1.5.5.7.3.9) {
- eku.ocsp_signing = true;
+ eku.ocscp_signing = true;
} else {
eku.other.push(oid);
}
}
- Ok((ret, eku))
- }
-
- fn parse_extendedkeyusage_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
- map(parse_extendedkeyusage, ParsedExtension::ExtendedKeyUsage)(i)
- }
-
- // DistributionPointName ::= CHOICE {
- // fullName [0] GeneralNames,
- // nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
- fn parse_distributionpointname(i: &[u8]) -> IResult<&[u8], DistributionPointName, BerError> {
- let (rem, header) = der_read_element_header(i)?;
- match header.tag.0 {
- 0 => {
- let (rem, names) = many1(complete(parse_generalname))(rem)?;
- Ok((rem, DistributionPointName::FullName(names)))
- }
- 1 => {
- let (rem, rdn) = RelativeDistinguishedName::from_der(rem)
- .map_err(|_| BerError::BerValueError)?;
- Ok((rem, DistributionPointName::NameRelativeToCRLIssuer(rdn)))
- }
- _ => Err(Err::Error(BerError::InvalidTag)),
- }
- }
-
- // ReasonFlags ::= BIT STRING {
- // unused (0),
- // keyCompromise (1),
- // cACompromise (2),
- // affiliationChanged (3),
- // superseded (4),
- // cessationOfOperation (5),
- // certificateHold (6),
- // privilegeWithdrawn (7),
- // aACompromise (8) }
- fn parse_tagged1_reasons(i: &[u8]) -> BerResult<ReasonFlags> {
- let (rem, obj) = parse_der_tagged_implicit(1, parse_der_content(DerTag::BitString))(i)?;
- if let DerObjectContent::BitString(_, b) = obj.content {
- let flags = b
- .data
- .iter()
- .rev()
- .fold(0, |acc, x| acc << 8 | (x.reverse_bits() as u16));
- Ok((rem, ReasonFlags { flags }))
- } else {
- Err(nom::Err::Failure(BerError::InvalidTag))
- }
- }
-
- fn parse_crlissuer_content(i: &[u8]) -> BerResult<Vec<GeneralName>> {
- many1(complete(parse_generalname))(i)
- }
-
- // DistributionPoint ::= SEQUENCE {
- // distributionPoint [0] DistributionPointName OPTIONAL,
- // reasons [1] ReasonFlags OPTIONAL,
- // cRLIssuer [2] GeneralNames OPTIONAL }
- pub(super) fn parse_crldistributionpoint(
- i: &[u8],
- ) -> IResult<&[u8], CRLDistributionPoint, BerError> {
- parse_der_sequence_defined_g(|content, _| {
- let (rem, distribution_point) =
- opt(complete(parse_der_tagged_explicit_g(0, |b, _| {
- parse_distributionpointname(b)
- })))(content)?;
- let (rem, reasons) = opt(complete(parse_tagged1_reasons))(rem)?;
- let (rem, crl_issuer) = opt(complete(parse_der_tagged_implicit_g(2, |i, _, _| {
- parse_crlissuer_content(i)
- })))(rem)?;
- let crl_dp = CRLDistributionPoint {
- distribution_point,
- reasons,
- crl_issuer,
- };
- Ok((rem, crl_dp))
- })(i)
- }
-
- pub(super) fn parse_crldistributionpoints(
- i: &[u8],
- ) -> IResult<&[u8], CRLDistributionPoints, BerError> {
- parse_der_sequence_of_v(parse_crldistributionpoint)(i)
- }
-
- fn parse_crldistributionpoints_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
- map(
- parse_crldistributionpoints,
- ParsedExtension::CRLDistributionPoints,
- )(i)
+ Ok((ret, ParsedExtension::ExtendedKeyUsage(eku)))
}
// AuthorityInfoAccessSyntax ::=
@@ -1235,27 +725,30 @@ pub(crate) mod parser {
// AccessDescription ::= SEQUENCE {
// accessMethod OBJECT IDENTIFIER,
// accessLocation GeneralName }
- pub(super) fn parse_authorityinfoaccess(
- i: &[u8],
- ) -> IResult<&[u8], AuthorityInfoAccess, BerError> {
- fn parse_aia(i: &[u8]) -> IResult<&[u8], AccessDescription, BerError> {
+ fn parse_authorityinfoaccess(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
+ fn parse_aia(i: &[u8]) -> IResult<&[u8], (Oid, GeneralName), BerError> {
parse_der_sequence_defined_g(|content, _| {
// Read first element, an oid.
let (gn, oid) = map_res(parse_der_oid, |x: DerObject| x.as_oid_val())(content)?;
// Parse second element
let (rest, gn) = parse_generalname(gn)?;
- Ok((rest, AccessDescription::new(oid, gn)))
+ Ok((rest, (oid, gn)))
})(i)
}
- let (ret, accessdescs) = parse_der_sequence_of_v(parse_aia)(i)?;
- Ok((ret, AuthorityInfoAccess { accessdescs }))
- }
-
- fn parse_authorityinfoaccess_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
- map(
- parse_authorityinfoaccess,
- ParsedExtension::AuthorityInfoAccess,
- )(i)
+ let (ret, mut aia_list) = parse_der_sequence_of_v(parse_aia)(i)?;
+ // create the hashmap and merge entries with same OID
+ let mut accessdescs: HashMap<Oid, Vec<GeneralName>> = HashMap::new();
+ for (oid, gn) in aia_list.drain(..) {
+ if let Some(general_names) = accessdescs.get_mut(&oid) {
+ general_names.push(gn);
+ } else {
+ accessdescs.insert(oid, vec![gn]);
+ }
+ }
+ Ok((
+ ret,
+ ParsedExtension::AuthorityInfoAccess(AuthorityInfoAccess { accessdescs }),
+ ))
}
fn parse_aki_content<'a>(
@@ -1283,37 +776,40 @@ pub(crate) mod parser {
}
// RFC 5280 section 4.2.1.1: Authority Key Identifier
- pub(super) fn parse_authoritykeyidentifier(
- i: &[u8],
- ) -> IResult<&[u8], AuthorityKeyIdentifier, BerError> {
+ fn parse_authoritykeyidentifier(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
let (rem, aki) = parse_der_sequence_defined_g(parse_aki_content)(i)?;
- Ok((rem, aki))
+ Ok((rem, ParsedExtension::AuthorityKeyIdentifier(aki)))
}
- fn parse_authoritykeyidentifier_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
- map(
- parse_authoritykeyidentifier,
- ParsedExtension::AuthorityKeyIdentifier,
- )(i)
+ #[rustversion::not(since(1.37))]
+ fn reverse_bits(n: u8) -> u8 {
+ let mut out = 0;
+ for i in 0..=7 {
+ if n & (1 << i) != 0 {
+ out |= 1 << (7 - i);
+ }
+ }
+ out
}
- pub(super) fn parse_keyidentifier<'a>(
- i: &'a [u8],
- ) -> IResult<&'a [u8], KeyIdentifier, BerError> {
+ #[rustversion::since(1.37)]
+ #[inline]
+ fn reverse_bits(n: u8) -> u8 {
+ n.reverse_bits()
+ }
+
+ fn parse_keyidentifier<'a>(i: &'a [u8]) -> IResult<&'a [u8], ParsedExtension, BerError> {
let (rest, obj) = parse_der_octetstring(i)?;
let id = obj
.content
.as_slice()
.or(Err(Err::Error(BerError::BerTypeError)))?;
let ki = KeyIdentifier(id);
- Ok((rest, ki))
- }
-
- fn parse_keyidentifier_ext<'a>(i: &'a [u8]) -> IResult<&'a [u8], ParsedExtension, BerError> {
- map(parse_keyidentifier, ParsedExtension::SubjectKeyIdentifier)(i)
+ let ret = ParsedExtension::SubjectKeyIdentifier(ki);
+ Ok((rest, ret))
}
- pub(super) fn parse_keyusage(i: &[u8]) -> IResult<&[u8], KeyUsage, BerError> {
+ fn parse_keyusage(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
let (rest, obj) = parse_der_bitstring(i)?;
let bitstring = obj
.content
@@ -1323,15 +819,11 @@ pub(crate) mod parser {
.data
.iter()
.rev()
- .fold(0, |acc, x| acc << 8 | (x.reverse_bits() as u16));
- Ok((rest, KeyUsage { flags }))
- }
-
- fn parse_keyusage_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
- map(parse_keyusage, ParsedExtension::KeyUsage)(i)
+ .fold(0, |acc, x| acc << 8 | (reverse_bits(*x) as u16));
+ Ok((rest, ParsedExtension::KeyUsage(KeyUsage { flags })))
}
- pub(super) fn parse_nscerttype(i: &[u8]) -> IResult<&[u8], NSCertType, BerError> {
+ fn parse_nscerttype(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
let (rest, obj) = parse_der_bitstring(i)?;
let bitstring = obj
.content
@@ -1341,12 +833,8 @@ pub(crate) mod parser {
if bitstring.data.len() != 1 {
return Err(Err::Error(BerError::BerValueError));
}
- let flags = bitstring.data[0].reverse_bits();
- Ok((rest, NSCertType(flags)))
- }
-
- fn parse_nscerttype_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
- map(parse_nscerttype, ParsedExtension::NSCertType)(i)
+ let flags = reverse_bits(bitstring.data[0]);
+ Ok((rest, ParsedExtension::NSCertType(NSCertType(flags))))
}
// CertificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
@@ -1366,43 +854,27 @@ pub(crate) mod parser {
// -- augment the following definition for PolicyQualifierId
//
// PolicyQualifierId ::= OBJECT IDENTIFIER ( id-qt-cps | id-qt-unotice )
- pub(super) fn parse_certificatepolicies(
- i: &[u8],
- ) -> IResult<&[u8], Vec<PolicyInformation>, BerError> {
- fn parse_policy_qualifier_info(i: &[u8]) -> IResult<&[u8], PolicyQualifierInfo, BerError> {
+ fn parse_certificatepolicies(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
+ fn parse_policy_information(i: &[u8]) -> IResult<&[u8], (Oid, &[u8]), BerError> {
parse_der_sequence_defined_g(|content, _| {
- let (rem, policy_qualifier_id) =
+ let (qualifier_set, oid) =
map_res(parse_der_oid, |x: DerObject| x.as_oid_val())(content)?;
- let info = PolicyQualifierInfo {
- policy_qualifier_id,
- qualifier: rem,
- };
- Ok((&[], info))
+ Ok((&[], (oid, qualifier_set)))
})(i)
}
- fn parse_policy_information(i: &[u8]) -> IResult<&[u8], PolicyInformation, BerError> {
- parse_der_sequence_defined_g(|content, _| {
- let (rem, policy_id) =
- map_res(parse_der_oid, |x: DerObject| x.as_oid_val())(content)?;
- let (rem, policy_qualifiers) =
- opt(complete(parse_der_sequence_defined_g(|content, _| {
- many1(complete(parse_policy_qualifier_info))(content)
- })))(rem)?;
- let info = PolicyInformation {
- policy_id,
- policy_qualifiers,
- };
- Ok((rem, info))
- })(i)
+ let (ret, mut policy_list) = parse_der_sequence_of_v(parse_policy_information)(i)?;
+ // create the policy hashmap
+ let mut policies = HashMap::new();
+ for (oid, qualifier_set) in policy_list.drain(..) {
+ if policies.insert(oid, qualifier_set).is_some() {
+ // duplicate policies are not allowed
+ return Err(Err::Failure(BerError::InvalidTag));
+ }
}
- parse_der_sequence_of_v(parse_policy_information)(i)
- }
-
- fn parse_certificatepolicies_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
- map(
- parse_certificatepolicies,
- ParsedExtension::CertificatePolicies,
- )(i)
+ Ok((
+ ret,
+ ParsedExtension::CertificatePolicies(CertificatePolicies { policies }),
+ ))
}
// CRLReason ::= ENUMERATED { ...
@@ -1440,35 +912,26 @@ pub(crate) fn parse_extension_sequence(i: &[u8]) -> X509Result<Vec<X509Extension
)
}
-pub(crate) fn parse_extensions(i: &[u8], explicit_tag: DerTag) -> X509Result<Vec<X509Extension>> {
- if i.is_empty() {
- return Ok((i, Vec::new()));
- }
-
- match der_read_element_header(i) {
- Ok((rem, hdr)) => {
- if hdr.tag != explicit_tag {
- return Err(Err::Error(X509Error::InvalidExtensions));
- }
- all_consuming(parse_extension_sequence)(rem)
+pub(crate) fn extensions_sequence_to_map<'a>(
+ i: &'a [u8],
+ v: Vec<X509Extension<'a>>,
+) -> X509Result<'a, HashMap<Oid<'a>, X509Extension<'a>>> {
+ let mut extensions = HashMap::new();
+ for ext in v.into_iter() {
+ if extensions.insert(ext.oid.clone(), ext).is_some() {
+ // duplicate extensions are not allowed
+ return Err(Err::Failure(X509Error::DuplicateExtensions));
}
- Err(_) => Err(X509Error::InvalidExtensions.into()),
}
+ Ok((i, extensions))
}
-/// Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
-pub(crate) fn parse_extension_envelope_sequence(i: &[u8]) -> X509Result<Vec<X509Extension>> {
- let parser = X509ExtensionParser::new().with_deep_parse_extensions(false);
-
- parse_der_sequence_defined_g(move |a, _| all_consuming(many0(complete(parser)))(a))(i)
-}
-
-pub(crate) fn parse_extensions_envelope(
+pub(crate) fn parse_extensions(
i: &[u8],
explicit_tag: DerTag,
-) -> X509Result<Vec<X509Extension>> {
+) -> X509Result<HashMap<Oid, X509Extension>> {
if i.is_empty() {
- return Ok((i, Vec::new()));
+ return Ok((i, HashMap::new()));
}
match der_read_element_header(i) {
@@ -1476,16 +939,16 @@ pub(crate) fn parse_extensions_envelope(
if hdr.tag != explicit_tag {
return Err(Err::Error(X509Error::InvalidExtensions));
}
- all_consuming(parse_extension_envelope_sequence)(rem)
+ let (rem, list) = exact!(rem, parse_extension_sequence)?;
+ extensions_sequence_to_map(rem, list)
}
Err(_) => Err(X509Error::InvalidExtensions.into()),
}
}
fn der_read_critical(i: &[u8]) -> BerResult<bool> {
- // Some certificates do not respect the DER BOOLEAN constraint (true must be encoded as 0xff)
- // so we attempt to parse as BER
- let (rem, obj) = opt(parse_ber_bool)(i)?;
+ // parse_der_optional!(i, parse_der_bool)
+ let (rem, obj) = opt(parse_der_bool)(i)?;
let value = obj
.map(|o| o.as_bool().unwrap_or_default()) // unwrap cannot fail, we just read a bool
.unwrap_or(false) // default critical value
@@ -1545,7 +1008,7 @@ mod tests {
assert!(eku.code_signing);
assert!(!eku.email_protection);
assert!(eku.time_stamping);
- assert!(!eku.ocsp_signing);
+ assert!(!eku.ocscp_signing);
assert_eq!(eku.other, vec![oid!(1.2.3 .4 .0 .42)]);
}
assert_eq!(
@@ -1617,135 +1080,12 @@ mod tests {
}
);
{
- let pm = tbs.policy_mappings().unwrap().1.clone().into_hashmap();
+ let pm = tbs.policy_mappings().unwrap().1;
let mut pm_ref = HashMap::new();
pm_ref.insert(oid!(2.34.23), vec![oid!(2.2)]);
pm_ref.insert(oid!(1.1), vec![oid!(0.0.4)]);
pm_ref.insert(oid!(2.2), vec![oid!(2.2.1), oid!(2.2.3)]);
- assert_eq!(pm, pm_ref);
- }
- }
-
- #[test]
- fn test_extensions_crl_distribution_points() {
- // Extension not present
- {
- let crt =
- crate::parse_x509_certificate(include_bytes!("../assets/crl-ext/crl-no-crl.der"))
- .unwrap()
- .1;
- assert!(crt
- .tbs_certificate
- .extensions_map()
- .unwrap()
- .get(&OID_X509_EXT_CRL_DISTRIBUTION_POINTS)
- .is_none());
- }
- // CRLDistributionPoints has 1 entry with 1 URI
- {
- let crt =
- crate::parse_x509_certificate(include_bytes!("../assets/crl-ext/crl-simple.der"))
- .unwrap()
- .1;
- let crl = crt
- .tbs_certificate
- .extensions_map()
- .unwrap()
- .get(&OID_X509_EXT_CRL_DISTRIBUTION_POINTS)
- .unwrap()
- .parsed_extension();
- assert!(matches!(crl, ParsedExtension::CRLDistributionPoints(_)));
- if let ParsedExtension::CRLDistributionPoints(crl) = crl {
- assert_eq!(crl.len(), 1);
- assert!(crl[0].reasons.is_none());
- assert!(crl[0].crl_issuer.is_none());
- let distribution_point = crl[0].distribution_point.as_ref().unwrap();
- assert!(matches!(
- distribution_point,
- DistributionPointName::FullName(_)
- ));
- if let DistributionPointName::FullName(names) = distribution_point {
- assert_eq!(names.len(), 1);
- assert!(matches!(names[0], GeneralName::URI(_)));
- if let GeneralName::URI(uri) = names[0] {
- assert_eq!(uri, "http://example.com/myca.crl")
- }
- }
- }
- }
- // CRLDistributionPoints has 2 entries
- {
- let crt =
- crate::parse_x509_certificate(include_bytes!("../assets/crl-ext/crl-complex.der"))
- .unwrap()
- .1;
- let crl = crt
- .tbs_certificate
- .extensions_map()
- .unwrap()
- .get(&OID_X509_EXT_CRL_DISTRIBUTION_POINTS)
- .unwrap()
- .parsed_extension();
- assert!(matches!(crl, ParsedExtension::CRLDistributionPoints(_)));
- if let ParsedExtension::CRLDistributionPoints(crl) = crl {
- assert_eq!(crl.len(), 2);
- // First CRL Distribution point
- let reasons = crl[0].reasons.as_ref().unwrap();
- assert!(reasons.key_compromise());
- assert!(reasons.ca_compromise());
- assert!(!reasons.affilation_changed());
- assert!(!reasons.superseded());
- assert!(!reasons.cessation_of_operation());
- assert!(!reasons.certificate_hold());
- assert!(!reasons.privelege_withdrawn());
- assert!(reasons.aa_compromise());
- assert_eq!(
- format!("{}", reasons),
- "Key Compromise, CA Compromise, AA Compromise"
- );
- let issuers = crl[0].crl_issuer.as_ref().unwrap();
- assert_eq!(issuers.len(), 1);
- assert!(matches!(issuers[0], GeneralName::DirectoryName(_)));
- if let GeneralName::DirectoryName(name) = &issuers[0] {
- assert_eq!(name.to_string(), "C=US, O=Organisation, CN=Some Name");
- }
- let distribution_point = crl[0].distribution_point.as_ref().unwrap();
- assert!(matches!(
- distribution_point,
- DistributionPointName::FullName(_)
- ));
- if let DistributionPointName::FullName(names) = distribution_point {
- assert_eq!(names.len(), 1);
- assert!(matches!(names[0], GeneralName::URI(_)));
- if let GeneralName::URI(uri) = names[0] {
- assert_eq!(uri, "http://example.com/myca.crl")
- }
- }
- // Second CRL Distribution point
- let reasons = crl[1].reasons.as_ref().unwrap();
- assert!(reasons.key_compromise());
- assert!(reasons.ca_compromise());
- assert!(!reasons.affilation_changed());
- assert!(!reasons.superseded());
- assert!(!reasons.cessation_of_operation());
- assert!(!reasons.certificate_hold());
- assert!(!reasons.privelege_withdrawn());
- assert!(!reasons.aa_compromise());
- assert_eq!(format!("{}", reasons), "Key Compromise, CA Compromise");
- assert!(crl[1].crl_issuer.is_none());
- let distribution_point = crl[1].distribution_point.as_ref().unwrap();
- assert!(matches!(
- distribution_point,
- DistributionPointName::FullName(_)
- ));
- if let DistributionPointName::FullName(names) = distribution_point {
- assert_eq!(names.len(), 1);
- assert!(matches!(names[0], GeneralName::URI(_)));
- if let GeneralName::URI(uri) = names[0] {
- assert_eq!(uri, "http://example.com/myca2.crl")
- }
- }
- }
+ assert_eq!(pm.mappings, pm_ref);
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 7939c82..9cb4edb 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -23,17 +23,12 @@
//! [`pem`](pem/index.html) module for more documentation.
//!
//! To decode a DER-encoded certificate, the main parsing method is
-//! [`X509Certificate::from_der`] (
-//! part of the [`FromDer`](traits/trait.FromDer.html) trait
-//! ), which builds a
-//! [`X509Certificate`](certificate/struct.X509Certificate.html) object.
-//!
-//! An alternative method is to use [`X509CertificateParser`](certificate/struct.X509CertificateParser.html),
-//! which allows specifying parsing options (for example, not automatically parsing option contents).
+//! [`parse_x509_certificate`](fn.parse_x509_certificate.html), which builds a
+//! [`X509Certificate`](x509/struct.X509Certificate.html) object.
//!
//! 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()](x509/struct.X509Certificate.html#method.issuer) returns the
//! same as accessing `<object>.tbs_certificate.issuer`.
//!
//! For PEM-encoded certificates, use the [`pem`](pem/index.html) module.
@@ -48,12 +43,12 @@
//! static IGCA_DER: &[u8] = include_bytes!("../assets/IGC_A.der");
//!
//! # fn main() {
-//! let res = X509Certificate::from_der(IGCA_DER);
+//! let res = parse_x509_certificate(IGCA_DER);
//! match res {
//! Ok((rem, cert)) => {
//! assert!(rem.is_empty());
//! //
-//! assert_eq!(cert.version(), X509Version::V3);
+//! assert_eq!(cert.tbs_certificate.version, X509Version::V3);
//! },
//! _ => panic!("x509 parsing failed: {:?}", res),
//! }
@@ -63,12 +58,12 @@
//! To parse a CRL and print information about revoked certificates:
//!
//! ```rust
-//! # use x509_parser::prelude::*;
+//! # use x509_parser::parse_x509_crl;
//! #
//! # static DER: &[u8] = include_bytes!("../assets/example.crl");
//! #
//! # fn main() {
-//! let res = CertificateRevocationList::from_der(DER);
+//! let res = parse_x509_crl(DER);
//! match res {
//! Ok((_rem, crl)) => {
//! for revoked in crl.iter_revoked_certificates() {
@@ -87,7 +82,7 @@
//!
//! - The `verify` feature adds support for (cryptographic) signature verification, based on `ring`.
//! It adds the
-//! [`X509Certificate::verify_signature()`](certificate/struct.X509Certificate.html#method.verify_signature)
+//! [X509Certificate::verify_signature()](x509/struct.X509Certificate.html#method.verify_signature)
//! to `X509Certificate`.
//!
//! ```rust
@@ -96,17 +91,13 @@
//! /// Cryptographic signature verification: returns true if certificate was signed by issuer
//! #[cfg(feature = "verify")]
//! pub fn check_signature(cert: &X509Certificate<'_>, issuer: &X509Certificate<'_>) -> bool {
-//! let issuer_public_key = issuer.public_key();
+//! let issuer_public_key = &issuer.tbs_certificate.subject_pki;
//! cert
//! .verify_signature(Some(issuer_public_key))
//! .is_ok()
//! }
//! ```
//!
-//! - The `validate` features add methods to run more validation functions on the certificate structure
-//! and values using the [`Validate`](validate/trait.Validate.html) trait.
-//! It does not validate any cryptographic parameter (see `verify` above).
-//!
//! ## Rust version requirements
//!
//! `x509-parser` requires **Rustc version 1.45 or greater**, based on nom 6
@@ -141,10 +132,6 @@ pub mod pem;
pub mod prelude;
pub mod revocation_list;
pub mod time;
-pub mod traits;
-#[cfg(feature = "validate")]
-#[cfg_attr(docsrs, doc(cfg(feature = "validate")))]
-pub mod validate;
pub mod x509;
// reexports
@@ -156,13 +143,12 @@ pub use oid_registry;
use certificate::X509Certificate;
use error::X509Result;
use revocation_list::CertificateRevocationList;
-use traits::FromDer;
/// Parse a **DER-encoded** X.509 Certificate, and return the remaining of the input and the built
/// object.
///
///
-/// This function is an alias to [X509Certificate::from_der](certificate::X509Certificate::from_der). See this function
+/// This function is an alias to [X509Certificate::from_der](x509/struct.X509Certificate.html#method.from_der). See this function
/// for more information.
///
/// For PEM-encoded certificates, use the [`pem`](pem/index.html) module.
@@ -174,7 +160,7 @@ pub fn parse_x509_certificate<'a>(i: &'a [u8]) -> X509Result<X509Certificate<'a>
/// Parse a DER-encoded X.509 v2 CRL, and return the remaining of the input and the built
/// object.
///
-/// This function is an alias to [CertificateRevocationList::from_der](revocation_list::CertificateRevocationList::from_der). See this function
+/// This function is an alias to [CertificateRevocationList::from_der](x509/struct.CertificateRevocationList.html#method.from_der). See this function
/// for more information.
#[inline]
pub fn parse_x509_crl(i: &[u8]) -> X509Result<CertificateRevocationList> {
diff --git a/src/objects.rs b/src/objects.rs
index 9bcbe72..7729c30 100644
--- a/src/objects.rs
+++ b/src/objects.rs
@@ -10,11 +10,11 @@
//! To get the short name for a given OID:
//!
//! ```rust
-//! use x509_parser::objects::*;
+//! use x509_parser::objects::oid2sn;
//! use x509_parser::oid_registry::*;
//!
//! let oid = &OID_X509_COMMON_NAME;
-//! let sn = oid2sn(oid, oid_registry());
+//! let sn = oid2sn(oid);
//! assert_eq!(sn, Ok("commonName"));
//! ```
@@ -45,26 +45,19 @@ lazy_static! {
}
/// Return the abbreviation (for ex. CN for Common Name), or if not found, the OID short name
-pub fn oid2abbrev<'a>(oid: &'a Oid, registry: &'a OidRegistry) -> Result<&'a str, NidError> {
+pub fn oid2abbrev<'a>(oid: &'a Oid) -> Result<&'a str, NidError> {
if let Some(abbrev) = ABBREV_MAP.get(oid) {
return Ok(abbrev);
}
- registry.get(oid).map(|entry| entry.sn()).ok_or(NidError)
+ OID_REGISTRY
+ .get(oid)
+ .map(|entry| entry.sn())
+ .ok_or(NidError)
}
/// Returns the short name corresponding to the OID
-pub fn oid2sn<'a>(oid: &'a Oid, registry: &'a OidRegistry) -> Result<&'a str, NidError> {
- registry.get(oid).map(|o| o.sn()).ok_or(NidError)
-}
-
-/// Returns the description corresponding to the OID
-pub fn oid2description<'a>(oid: &'a Oid, registry: &'a OidRegistry) -> Result<&'a str, NidError> {
- registry.get(oid).map(|o| o.description()).ok_or(NidError)
-}
-
-/// Return a reference to the default registry of known OIDs
-pub fn oid_registry() -> &'static OidRegistry<'static> {
- &OID_REGISTRY
+pub fn oid2sn<'a>(oid: &'a Oid) -> Result<&'a str, NidError> {
+ OID_REGISTRY.get(oid).map(|ref o| o.sn()).ok_or(NidError)
}
#[cfg(test)]
diff --git a/src/pem.rs b/src/pem.rs
index 0bd8153..95a943f 100644
--- a/src/pem.rs
+++ b/src/pem.rs
@@ -64,7 +64,7 @@ use nom::{Err, IResult};
use std::io::{BufRead, Cursor, Seek, SeekFrom};
/// Representation of PEM data
-#[derive(Clone, PartialEq, Debug)]
+#[derive(PartialEq, Debug)]
pub struct Pem {
/// The PEM label
pub label: String,
diff --git a/src/prelude.rs b/src/prelude.rs
index 5e6d780..45be9ca 100644
--- a/src/prelude.rs
+++ b/src/prelude.rs
@@ -9,8 +9,5 @@ pub use crate::objects::*;
pub use crate::pem::*;
pub use crate::revocation_list::*;
pub use crate::time::*;
-#[cfg(feature = "validate")]
-pub use crate::validate::*;
pub use crate::x509::*;
pub use crate::*;
-pub use traits::*;
diff --git a/src/revocation_list.rs b/src/revocation_list.rs
index 6cf9d55..1c9a0c3 100644
--- a/src/revocation_list.rs
+++ b/src/revocation_list.rs
@@ -1,7 +1,6 @@
use crate::error::{X509Error, X509Result};
use crate::extensions::*;
use crate::time::ASN1Time;
-use crate::traits::FromDer;
use crate::x509::{
parse_serial, parse_signature_value, AlgorithmIdentifier, ReasonCode, X509Name, X509Version,
};
@@ -19,31 +18,7 @@ use std::collections::HashMap;
/// An X.509 v2 Certificate Revocation List (CRL).
///
/// X.509 v2 CRLs are defined in [RFC5280](https://tools.ietf.org/html/rfc5280).
-///
-/// # Example
-///
-/// To parse a CRL and print information about revoked certificates:
-///
-/// ```rust
-/// use x509_parser::revocation_list::CertificateRevocationList;
-/// use x509_parser::traits::FromDer;
-///
-/// # static DER: &'static [u8] = include_bytes!("../assets/example.crl");
-/// #
-/// # fn main() {
-/// let res = CertificateRevocationList::from_der(DER);
-/// match res {
-/// Ok((_rem, crl)) => {
-/// for revoked in crl.iter_revoked_certificates() {
-/// println!("Revoked certificate serial: {}", revoked.raw_serial_as_string());
-/// println!(" Reason: {}", revoked.reason_code().unwrap_or_default().1);
-/// }
-/// },
-/// _ => panic!("CRL parsing failed: {:?}", res),
-/// }
-/// # }
-/// ```
-#[derive(Clone, Debug)]
+#[derive(Debug)]
pub struct CertificateRevocationList<'a> {
pub tbs_cert_list: TbsCertList<'a>,
pub signature_algorithm: AlgorithmIdentifier<'a>,
@@ -51,6 +26,54 @@ pub struct CertificateRevocationList<'a> {
}
impl<'a> CertificateRevocationList<'a> {
+ /// Parse a DER-encoded X.509 v2 CRL, and return the remaining of the input and the built
+ /// object.
+ ///
+ /// The returned object uses zero-copy, and so has the same lifetime as the input.
+ ///
+ /// <pre>
+ /// CertificateList ::= SEQUENCE {
+ /// tbsCertList TBSCertList,
+ /// signatureAlgorithm AlgorithmIdentifier,
+ /// signatureValue BIT STRING }
+ /// </pre>
+ ///
+ /// # Example
+ ///
+ /// To parse a CRL and print information about revoked certificates:
+ ///
+ /// ```rust
+ /// # use x509_parser::parse_x509_crl;
+ /// #
+ /// # static DER: &'static [u8] = include_bytes!("../assets/example.crl");
+ /// #
+ /// # fn main() {
+ /// let res = parse_x509_crl(DER);
+ /// match res {
+ /// Ok((_rem, crl)) => {
+ /// for revoked in crl.iter_revoked_certificates() {
+ /// println!("Revoked certificate serial: {}", revoked.raw_serial_as_string());
+ /// println!(" Reason: {}", revoked.reason_code().unwrap_or_default().1);
+ /// }
+ /// },
+ /// _ => panic!("CRL parsing failed: {:?}", res),
+ /// }
+ /// # }
+ /// ```
+ pub fn from_der(i: &'a [u8]) -> X509Result<Self> {
+ parse_der_sequence_defined_g(|i, _| {
+ let (i, tbs_cert_list) = TbsCertList::from_der(i)?;
+ let (i, signature_algorithm) = AlgorithmIdentifier::from_der(i)?;
+ let (i, signature_value) = parse_signature_value(i)?;
+ let crl = CertificateRevocationList {
+ tbs_cert_list,
+ signature_algorithm,
+ signature_value,
+ };
+ Ok((i, crl))
+ })(i)
+ }
+
/// Get the version of the encoded certificate
pub fn version(&self) -> Option<X509Version> {
self.tbs_cert_list.version
@@ -79,9 +102,9 @@ impl<'a> CertificateRevocationList<'a> {
self.tbs_cert_list.revoked_certificates.iter()
}
- /// Get the CRL extensions.
+ /// Get the certificate extensions.
#[inline]
- pub fn extensions(&self) -> &[X509Extension] {
+ pub fn extensions(&self) -> &HashMap<Oid, X509Extension> {
&self.tbs_cert_list.extensions
}
@@ -94,35 +117,11 @@ impl<'a> CertificateRevocationList<'a> {
/// MUST NOT use CRLNumber values longer than 20 octets.
/// </pre>
pub fn crl_number(&self) -> Option<&BigUint> {
- self.extensions()
- .iter()
- .find(|&ext| ext.oid == OID_X509_EXT_BASIC_CONSTRAINTS)
- .and_then(|ext| match ext.parsed_extension {
- ParsedExtension::CRLNumber(ref num) => Some(num),
- _ => None,
- })
- }
-}
-
-/// <pre>
-/// CertificateList ::= SEQUENCE {
-/// tbsCertList TBSCertList,
-/// signatureAlgorithm AlgorithmIdentifier,
-/// signatureValue BIT STRING }
-/// </pre>
-impl<'a> FromDer<'a> for CertificateRevocationList<'a> {
- fn from_der(i: &'a [u8]) -> X509Result<Self> {
- parse_der_sequence_defined_g(|i, _| {
- let (i, tbs_cert_list) = TbsCertList::from_der(i)?;
- let (i, signature_algorithm) = AlgorithmIdentifier::from_der(i)?;
- let (i, signature_value) = parse_signature_value(i)?;
- let crl = CertificateRevocationList {
- tbs_cert_list,
- signature_algorithm,
- signature_value,
- };
- Ok((i, crl))
- })(i)
+ let ext = self.extensions().get(&OID_X509_EXT_CRL_NUMBER)?;
+ match ext.parsed_extension {
+ ParsedExtension::CRLNumber(ref num) => Some(num),
+ _ => None,
+ }
}
}
@@ -149,7 +148,7 @@ impl<'a> FromDer<'a> for CertificateRevocationList<'a> {
/// -- if present, version MUST be v2
/// }
/// </pre>
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct TbsCertList<'a> {
pub version: Option<X509Version>,
pub signature: AlgorithmIdentifier<'a>,
@@ -157,53 +156,11 @@ pub struct TbsCertList<'a> {
pub this_update: ASN1Time,
pub next_update: Option<ASN1Time>,
pub revoked_certificates: Vec<RevokedCertificate<'a>>,
- extensions: Vec<X509Extension<'a>>,
+ pub extensions: HashMap<Oid<'a>, X509Extension<'a>>,
pub(crate) raw: &'a [u8],
}
impl<'a> TbsCertList<'a> {
- /// Returns the certificate extensions
- #[inline]
- pub fn extensions(&self) -> &[X509Extension] {
- &self.extensions
- }
-
- /// Returns an iterator over the certificate extensions
- #[inline]
- pub fn iter_extensions(&self) -> impl Iterator<Item = &X509Extension> {
- self.extensions.iter()
- }
-
- /// Searches for an extension with the given `Oid`.
- ///
- /// Note: if there are several extensions with the same `Oid`, the first one is returned.
- pub fn find_extension(&self, oid: &Oid) -> Option<&X509Extension> {
- self.extensions.iter().find(|&ext| ext.oid == *oid)
- }
-
- /// Builds and returns a map of extensions.
- ///
- /// If an extension is present twice, this will fail and return `DuplicateExtensions`.
- pub fn extensions_map(&self) -> Result<HashMap<Oid, &X509Extension>, X509Error> {
- self.extensions
- .iter()
- .try_fold(HashMap::new(), |mut m, ext| {
- if m.contains_key(&ext.oid) {
- return Err(X509Error::DuplicateExtensions);
- }
- m.insert(ext.oid.clone(), ext);
- Ok(m)
- })
- }
-}
-
-impl<'a> AsRef<[u8]> for TbsCertList<'a> {
- fn as_ref(&self) -> &[u8] {
- self.raw
- }
-}
-
-impl<'a> FromDer<'a> for TbsCertList<'a> {
fn from_der(i: &'a [u8]) -> X509Result<Self> {
let start_i = i;
parse_der_sequence_defined_g(move |i, _| {
@@ -231,57 +188,53 @@ impl<'a> FromDer<'a> for TbsCertList<'a> {
}
}
-#[derive(Clone, Debug, PartialEq)]
+impl<'a> AsRef<[u8]> for TbsCertList<'a> {
+ fn as_ref(&self) -> &[u8] {
+ &self.raw
+ }
+}
+
+#[derive(Debug, PartialEq)]
pub struct RevokedCertificate<'a> {
/// The Serial number of the revoked certificate
pub user_certificate: BigUint,
/// The date on which the revocation occurred is specified.
pub revocation_date: ASN1Time,
/// Additional information about revocation
- extensions: Vec<X509Extension<'a>>,
+ pub extensions: HashMap<Oid<'a>, X509Extension<'a>>,
pub(crate) raw_serial: &'a [u8],
}
impl<'a> RevokedCertificate<'a> {
+ // revokedCertificates SEQUENCE OF SEQUENCE {
+ // userCertificate CertificateSerialNumber,
+ // revocationDate Time,
+ // crlEntryExtensions Extensions OPTIONAL
+ // -- if present, MUST be v2
+ // } OPTIONAL,
+ pub(crate) fn from_der(i: &'a [u8]) -> X509Result<Self> {
+ parse_der_sequence_defined_g(|i, _| {
+ let (i, (raw_serial, user_certificate)) = parse_serial(i)?;
+ let (i, revocation_date) = ASN1Time::from_der(i)?;
+ let (i, extensions) = opt(complete(|i| {
+ let (rem, v) = parse_extension_sequence(i)?;
+ extensions_sequence_to_map(rem, v)
+ }))(i)?;
+ let revoked = RevokedCertificate {
+ user_certificate,
+ revocation_date,
+ extensions: extensions.unwrap_or_default(),
+ raw_serial,
+ };
+ Ok((i, revoked))
+ })(i)
+ }
+
/// Return the serial number of the revoked certificate
pub fn serial(&self) -> &BigUint {
&self.user_certificate
}
- /// Get the CRL entry extensions.
- #[inline]
- pub fn extensions(&self) -> &[X509Extension] {
- &self.extensions
- }
-
- /// Returns an iterator over the CRL entry extensions
- #[inline]
- pub fn iter_extensions(&self) -> impl Iterator<Item = &X509Extension> {
- self.extensions.iter()
- }
-
- /// Searches for a CRL entry extension with the given `Oid`.
- ///
- /// Note: if there are several extensions with the same `Oid`, the first one is returned.
- pub fn find_extension(&self, oid: &Oid) -> Option<&X509Extension> {
- self.extensions.iter().find(|&ext| ext.oid == *oid)
- }
-
- /// Builds and returns a map of CRL entry extensions.
- ///
- /// If an extension is present twice, this will fail and return `DuplicateExtensions`.
- pub fn extensions_map(&self) -> Result<HashMap<Oid, &X509Extension>, X509Error> {
- self.extensions
- .iter()
- .try_fold(HashMap::new(), |mut m, ext| {
- if m.contains_key(&ext.oid) {
- return Err(X509Error::DuplicateExtensions);
- }
- m.insert(ext.oid.clone(), ext);
- Ok(m)
- })
- }
-
/// Get the raw bytes of the certificate serial number
pub fn raw_serial(&self) -> &[u8] {
self.raw_serial
@@ -301,11 +254,11 @@ impl<'a> RevokedCertificate<'a> {
/// Get the code identifying the reason for the revocation, if present
pub fn reason_code(&self) -> Option<(bool, ReasonCode)> {
- self.find_extension(&OID_X509_EXT_REASON_CODE)
- .and_then(|ext| match ext.parsed_extension {
- ParsedExtension::ReasonCode(code) => Some((ext.critical, code)),
- _ => None,
- })
+ let ext = self.extensions.get(&OID_X509_EXT_REASON_CODE)?;
+ match ext.parsed_extension {
+ ParsedExtension::ReasonCode(code) => Some((ext.critical, code)),
+ _ => None,
+ }
}
/// Get the invalidity date, if present
@@ -313,34 +266,17 @@ impl<'a> RevokedCertificate<'a> {
/// The invalidity date is the date on which it is known or suspected that the private
/// key was compromised or that the certificate otherwise became invalid.
pub fn invalidity_date(&self) -> Option<(bool, ASN1Time)> {
- self.find_extension(&OID_X509_EXT_INVALIDITY_DATE)
- .and_then(|ext| match ext.parsed_extension {
- ParsedExtension::InvalidityDate(date) => Some((ext.critical, date)),
- _ => None,
- })
+ let ext = self.extensions.get(&OID_X509_EXT_INVALIDITY_DATE)?;
+ match ext.parsed_extension {
+ ParsedExtension::InvalidityDate(date) => Some((ext.critical, date)),
+ _ => None,
+ }
}
-}
-// revokedCertificates SEQUENCE OF SEQUENCE {
-// userCertificate CertificateSerialNumber,
-// revocationDate Time,
-// crlEntryExtensions Extensions OPTIONAL
-// -- if present, MUST be v2
-// } OPTIONAL,
-impl<'a> FromDer<'a> for RevokedCertificate<'a> {
- fn from_der(i: &'a [u8]) -> X509Result<Self> {
- parse_der_sequence_defined_g(|i, _| {
- let (i, (raw_serial, user_certificate)) = parse_serial(i)?;
- let (i, revocation_date) = ASN1Time::from_der(i)?;
- let (i, extensions) = opt(complete(parse_extension_sequence))(i)?;
- let revoked = RevokedCertificate {
- user_certificate,
- revocation_date,
- extensions: extensions.unwrap_or_default(),
- raw_serial,
- };
- Ok((i, revoked))
- })(i)
+ /// Get the certificate extensions.
+ #[inline]
+ pub fn extensions(&self) -> &HashMap<Oid, X509Extension> {
+ &self.extensions
}
}
diff --git a/src/time.rs b/src/time.rs
index ca37217..1e72b87 100644
--- a/src/time.rs
+++ b/src/time.rs
@@ -12,13 +12,16 @@ use nom::bytes::complete::take;
use nom::combinator::{complete, map_res, opt};
use crate::error::{X509Error, X509Result};
-use crate::traits::FromDer;
/// An ASN.1 timestamp.
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub struct ASN1Time(DateTime<Utc>);
impl ASN1Time {
+ pub(crate) fn from_der(i: &[u8]) -> X509Result<Self> {
+ map_res(parse_choice_of_time, der_to_utctime)(i).map_err(|_| X509Error::InvalidDate.into())
+ }
+
pub(crate) fn from_der_opt(i: &[u8]) -> X509Result<Option<Self>> {
opt(map_res(parse_choice_of_time, der_to_utctime))(i)
.map_err(|_| X509Error::InvalidDate.into())
@@ -48,17 +51,11 @@ impl ASN1Time {
/// Returns an RFC 2822 date and time string such as `Tue, 1 Jul 2003 10:52:37 +0200`.
#[inline]
- pub fn to_rfc2822(self) -> String {
+ pub fn to_rfc2822(&self) -> String {
self.0.to_rfc2822()
}
}
-impl<'a> FromDer<'a> for ASN1Time {
- fn from_der(i: &[u8]) -> X509Result<Self> {
- map_res(parse_choice_of_time, der_to_utctime)(i).map_err(|_| X509Error::InvalidDate.into())
- }
-}
-
fn parse_choice_of_time(i: &[u8]) -> DerResult {
alt((
complete(parse_der_utctime),
diff --git a/src/traits.rs b/src/traits.rs
deleted file mode 100644
index bd4d070..0000000
--- a/src/traits.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-use crate::error::X509Result;
-
-/// Parse a DER-encoded object, and return the remaining of the input and the built
-/// object.
-///
-/// The returned object uses zero-copy, and so has the same lifetime as the input.
-///
-/// Note that only parsing is done, not validation (see the [`Validate`](crate::validate::Validate) trait).
-///
-/// # Example
-///
-/// To parse a certificate and print the subject and issuer:
-///
-/// ```rust
-/// # use x509_parser::prelude::*;
-/// #
-/// # static DER: &'static [u8] = include_bytes!("../assets/IGC_A.der");
-/// #
-/// # fn main() {
-/// let res = X509Certificate::from_der(DER);
-/// match res {
-/// Ok((_rem, x509)) => {
-/// let subject = x509.subject();
-/// let issuer = x509.issuer();
-/// println!("X.509 Subject: {}", subject);
-/// println!("X.509 Issuer: {}", issuer);
-/// },
-/// _ => panic!("x509 parsing failed: {:?}", res),
-/// }
-/// # }
-/// ```
-
-pub trait FromDer<'a>: Sized {
- /// Attempt to parse input bytes into a DER object
- fn from_der(bytes: &'a [u8]) -> X509Result<'a, Self>;
-}
diff --git a/src/validate.rs b/src/validate.rs
deleted file mode 100644
index 6a495af..0000000
--- a/src/validate.rs
+++ /dev/null
@@ -1,126 +0,0 @@
-/// Trait for validating item (for ex. validate X.509 structure)
-///
-/// # Examples
-///
-/// Using callbacks:
-///
-/// ```
-/// use x509_parser::certificate::X509Certificate;
-/// use x509_parser::validate::Validate;
-///
-/// #[cfg(feature = "validate")]
-/// fn validate_certificate(x509: &X509Certificate<'_>) -> Result<(), &'static str> {
-/// println!(" Subject: {}", x509.subject());
-/// // validate and print warnings and errors to stderr
-/// let ok = x509.validate(
-/// |msg| {
-/// eprintln!(" [W] {}", msg);
-/// },
-/// |msg| {
-/// eprintln!(" [E] {}", msg);
-/// },
-/// );
-/// print!("Structure validation status: ");
-/// if ok {
-/// println!("Ok");
-/// Ok(())
-/// } else {
-/// println!("FAIL");
-/// Err("validation failed")
-/// }
-/// }
-/// ```
-///
-/// Collecting warnings and errors to `Vec`:
-///
-/// ```
-/// use x509_parser::certificate::X509Certificate;
-/// use x509_parser::validate::Validate;
-///
-/// #[cfg(feature = "validate")]
-/// fn validate_certificate(x509: &X509Certificate<'_>) -> Result<(), &'static str> {
-/// println!(" Subject: {}", x509.subject());
-/// // validate and print warnings and errors to stderr
-/// let (ok, warnings, errors) = x509.validate_to_vec();
-/// print!("Structure validation status: ");
-/// if ok {
-/// println!("Ok");
-/// } else {
-/// println!("FAIL");
-/// }
-/// for warning in &warnings {
-/// eprintln!(" [W] {}", warning);
-/// }
-/// for error in &errors {
-/// eprintln!(" [E] {}", error);
-/// }
-/// println!();
-/// if !errors.is_empty() {
-/// return Err("validation failed");
-/// }
-/// Ok(())
-/// }
-/// ```
-pub trait Validate {
- /// Attempts to validate current item.
- ///
- /// Returns `true` if item was validated.
- ///
- /// Call `warn()` if a non-fatal error was encountered, and `err()`
- /// if the error is fatal. These fucntions receive a description of the error.
- fn validate<W, E>(&self, warn: W, err: E) -> bool
- where
- W: FnMut(&str),
- E: FnMut(&str);
-
- /// Attempts to validate current item, storing warning and errors in `Vec`.
- ///
- /// Returns the validation result (`true` if validated), the list of warnings,
- /// and the list of errors.
- fn validate_to_vec(&self) -> (bool, Vec<String>, Vec<String>) {
- let mut warn_list = Vec::new();
- let mut err_list = Vec::new();
- let res = self.validate(
- |s| warn_list.push(s.to_owned()),
- |s| err_list.push(s.to_owned()),
- );
- (res, warn_list, err_list)
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::Validate;
-
- struct V1 {
- a: u32,
- }
-
- impl Validate for V1 {
- fn validate<W, E>(&self, mut warn: W, _err: E) -> bool
- where
- W: FnMut(&str),
- E: FnMut(&str),
- {
- if self.a > 10 {
- warn("a is greater than 10");
- }
- true
- }
- }
-
- #[test]
- fn validate_warn() {
- let v1 = V1 { a: 1 };
- let (res, warn, err) = v1.validate_to_vec();
- assert!(res);
- assert!(warn.is_empty());
- assert!(err.is_empty());
- // same, with one warning
- let v20 = V1 { a: 20 };
- let (res, warn, err) = v20.validate_to_vec();
- assert!(res);
- assert_eq!(warn, vec!["a is greater than 10".to_string()]);
- assert!(err.is_empty());
- }
-}
diff --git a/src/x509.rs b/src/x509.rs
index ecee084..9590fe6 100644
--- a/src/x509.rs
+++ b/src/x509.rs
@@ -5,9 +5,8 @@
use crate::error::{X509Error, X509Result};
use crate::objects::*;
-use crate::traits::FromDer;
-use der_parser::ber::{parse_ber_integer, BitStringObject, MAX_OBJECT_SIZE};
+use der_parser::ber::{BitStringObject, MAX_OBJECT_SIZE};
use der_parser::der::*;
use der_parser::error::*;
use der_parser::num_bigint::BigUint;
@@ -20,44 +19,32 @@ use nom::multi::{many0, many1};
use nom::{Err, Offset};
use oid_registry::*;
use rusticata_macros::newtype_enum;
-use std::convert::TryFrom;
use std::fmt;
-use std::iter::FromIterator;
-/// The version of the encoded certificate.
-///
-/// When extensions are used, as expected in this profile, version MUST be 3
-/// (value is `2`). If no extensions are present, but a UniqueIdentifier
-/// is present, the version SHOULD be 2 (value is `1`); however, the
-/// version MAY be 3. If only basic fields are present, the version
-/// SHOULD be 1 (the value is omitted from the certificate as the default
-/// value); however, the version MAY be 2 or 3.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct X509Version(pub u32);
impl X509Version {
- pub(crate) fn from_der_required(i: &[u8]) -> X509Result<X509Version> {
+ // Parse [0] EXPLICIT Version DEFAULT v1
+ pub(crate) fn from_der(i: &[u8]) -> X509Result<X509Version> {
let (rem, hdr) =
der_read_element_header(i).or(Err(Err::Error(X509Error::InvalidVersion)))?;
match hdr.tag.0 {
0 => {
map(parse_der_u32, X509Version)(rem).or(Err(Err::Error(X509Error::InvalidVersion)))
}
- _ => Ok((&rem[1..], X509Version::V1)),
+ _ => Ok((i, X509Version::V1)),
}
}
-}
-// Parse [0] EXPLICIT Version DEFAULT v1
-impl<'a> FromDer<'a> for X509Version {
- fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
+ pub(crate) fn from_der_required(i: &[u8]) -> X509Result<X509Version> {
let (rem, hdr) =
der_read_element_header(i).or(Err(Err::Error(X509Error::InvalidVersion)))?;
match hdr.tag.0 {
0 => {
map(parse_der_u32, X509Version)(rem).or(Err(Err::Error(X509Error::InvalidVersion)))
}
- _ => Ok((i, X509Version::V1)),
+ _ => Ok((&rem[1..], X509Version::V1)),
}
}
}
@@ -70,45 +57,34 @@ newtype_enum! {
}
}
-/// A generic attribute type and value
-///
-/// These objects are used as [`RelativeDistinguishedName`] components.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct AttributeTypeAndValue<'a> {
- attr_type: Oid<'a>,
- attr_value: DerObject<'a>, // ANY -- DEFINED BY AttributeType
+ pub attr_type: Oid<'a>,
+ pub attr_value: DerObject<'a>, // ANY -- DEFINED BY AttributeType
}
impl<'a> AttributeTypeAndValue<'a> {
- /// Builds a new `AttributeTypeAndValue`
- #[inline]
- pub const fn new(attr_type: Oid<'a>, attr_value: DerObject<'a>) -> Self {
- AttributeTypeAndValue {
- attr_type,
- attr_value,
- }
- }
-
- /// Returns the attribute type
- #[inline]
- pub const fn attr_type(&self) -> &Oid {
- &self.attr_type
- }
-
- /// Returns the attribute value, as raw `DerObject`
- #[inline]
- pub const fn attr_value(&self) -> &DerObject {
- &self.attr_value
+ // AttributeTypeAndValue ::= SEQUENCE {
+ // type AttributeType,
+ // value AttributeValue }
+ fn from_der(i: &'a [u8]) -> X509Result<Self> {
+ parse_der_sequence_defined_g(|i, _| {
+ let (i, attr_type) = map_res(parse_der_oid, |x: DerObject<'a>| x.as_oid_val())(i)
+ .or(Err(X509Error::InvalidX509Name))?;
+ let (i, attr_value) = parse_attribute_value(i).or(Err(X509Error::InvalidX509Name))?;
+ let attr = AttributeTypeAndValue {
+ attr_type,
+ attr_value,
+ };
+ Ok((i, attr))
+ })(i)
}
/// Attempt to get the content as `str`.
/// This can fail if the object does not contain a string type.
///
- /// Note: the [`TryFrom`] trait is implemented for `&str`, so this is equivalent to `attr.try_into()`.
- ///
/// Only NumericString, PrintableString, UTF8String and IA5String
/// are considered here. Other string types can be read using `as_slice`.
- #[inline]
pub fn as_str(&self) -> Result<&'a str, X509Error> {
self.attr_value.as_str().map_err(|e| e.into())
}
@@ -116,45 +92,11 @@ impl<'a> AttributeTypeAndValue<'a> {
/// Attempt to get the content as a slice.
/// This can fail if the object does not contain a type directly equivalent to a slice (e.g a
/// sequence).
- ///
- /// Note: the [`TryFrom`] trait is implemented for `&[u8]`, so this is equivalent to `attr.try_into()`.
- #[inline]
pub fn as_slice(&self) -> Result<&'a [u8], X509Error> {
self.attr_value.as_slice().map_err(|e| e.into())
}
}
-impl<'a> TryFrom<AttributeTypeAndValue<'a>> for &'a str {
- type Error = X509Error;
-
- fn try_from(value: AttributeTypeAndValue<'a>) -> Result<Self, Self::Error> {
- value.attr_value.as_str().map_err(|e| e.into())
- }
-}
-
-impl<'a> TryFrom<AttributeTypeAndValue<'a>> for &'a [u8] {
- type Error = X509Error;
-
- fn try_from(value: AttributeTypeAndValue<'a>) -> Result<Self, Self::Error> {
- value.attr_value.as_slice().map_err(|e| e.into())
- }
-}
-
-// AttributeTypeAndValue ::= SEQUENCE {
-// type AttributeType,
-// value AttributeValue }
-impl<'a> FromDer<'a> for AttributeTypeAndValue<'a> {
- fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
- parse_der_sequence_defined_g(|i, _| {
- let (i, attr_type) = map_res(parse_der_oid, |x: DerObject<'a>| x.as_oid_val())(i)
- .or(Err(X509Error::InvalidX509Name))?;
- let (i, attr_value) = parse_attribute_value(i).or(Err(X509Error::InvalidX509Name))?;
- let attr = AttributeTypeAndValue::new(attr_type, attr_value);
- Ok((i, attr))
- })(i)
- }
-}
-
// AttributeValue ::= ANY -- DEFINED BY AttributeType
#[inline]
fn parse_attribute_value(i: &[u8]) -> DerResult {
@@ -182,35 +124,12 @@ fn parse_malformed_string(i: &[u8]) -> DerResult {
}
}
-/// A Relative Distinguished Name element.
-///
-/// These objects are used as [`X509Name`] components.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct RelativeDistinguishedName<'a> {
- set: Vec<AttributeTypeAndValue<'a>>,
+ pub set: Vec<AttributeTypeAndValue<'a>>,
}
impl<'a> RelativeDistinguishedName<'a> {
- /// Builds a new `RelativeDistinguishedName`
- #[inline]
- pub const fn new(set: Vec<AttributeTypeAndValue<'a>>) -> Self {
- RelativeDistinguishedName { set }
- }
-
- /// Return an iterator over the components of this object
- pub fn iter(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> {
- self.set.iter()
- }
-}
-
-impl<'a> FromIterator<AttributeTypeAndValue<'a>> for RelativeDistinguishedName<'a> {
- fn from_iter<T: IntoIterator<Item = AttributeTypeAndValue<'a>>>(iter: T) -> Self {
- let set = iter.into_iter().collect();
- RelativeDistinguishedName { set }
- }
-}
-
-impl<'a> FromDer<'a> for RelativeDistinguishedName<'a> {
fn from_der(i: &'a [u8]) -> X509Result<Self> {
parse_der_set_defined_g(|i, _| {
let (i, set) = many1(complete(AttributeTypeAndValue::from_der))(i)?;
@@ -220,15 +139,15 @@ impl<'a> FromDer<'a> for RelativeDistinguishedName<'a> {
}
}
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct SubjectPublicKeyInfo<'a> {
pub algorithm: AlgorithmIdentifier<'a>,
pub subject_public_key: BitStringObject<'a>,
}
-impl<'a> FromDer<'a> for SubjectPublicKeyInfo<'a> {
+impl<'a> SubjectPublicKeyInfo<'a> {
/// Parse the SubjectPublicKeyInfo struct portion of a DER-encoded X.509 Certificate
- fn from_der(i: &'a [u8]) -> X509Result<Self> {
+ pub fn from_der(i: &'a [u8]) -> X509Result<Self> {
parse_der_sequence_defined_g(|i, _| {
let (i, algorithm) = AlgorithmIdentifier::from_der(i)?;
let (i, subject_public_key) = map_res(parse_der_bitstring, |x: DerObject<'a>| {
@@ -247,29 +166,31 @@ impl<'a> FromDer<'a> for SubjectPublicKeyInfo<'a> {
}
}
-/// Algorithm identifier
-///
-/// An algorithm identifier is defined by the following ASN.1 structure:
-///
-/// <pre>
-/// AlgorithmIdentifier ::= SEQUENCE {
-/// algorithm OBJECT IDENTIFIER,
-/// parameters ANY DEFINED BY algorithm OPTIONAL }
-/// </pre>
-///
-/// The algorithm identifier is used to identify a cryptographic
-/// algorithm. The OBJECT IDENTIFIER component identifies the algorithm
-/// (such as DSA with SHA-1). The contents of the optional parameters
-/// field will vary according to the algorithm identified.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct AlgorithmIdentifier<'a> {
pub algorithm: Oid<'a>,
pub parameters: Option<DerObject<'a>>,
}
-impl<'a> FromDer<'a> for AlgorithmIdentifier<'a> {
+impl<'a> AlgorithmIdentifier<'a> {
+ /// Parse an algorithm identifier
+ ///
+ /// An algorithm identifier is defined by the following ASN.1 structure:
+ ///
+ /// <pre>
+ /// AlgorithmIdentifier ::= SEQUENCE {
+ /// algorithm OBJECT IDENTIFIER,
+ /// parameters ANY DEFINED BY algorithm OPTIONAL }
+ /// </pre>
+ ///
+ /// The algorithm identifier is used to identify a cryptographic
+ /// algorithm. The OBJECT IDENTIFIER component identifies the algorithm
+ /// (such as DSA with SHA-1). The contents of the optional parameters
+ /// field will vary according to the algorithm identified.
+ // lifetime is *not* useless, it is required to tell the compiler the content of the temporary
+ // DerObject has the same lifetime as the input
#[allow(clippy::needless_lifetimes)]
- fn from_der(i: &[u8]) -> X509Result<AlgorithmIdentifier> {
+ pub fn from_der(i: &[u8]) -> X509Result<AlgorithmIdentifier> {
parse_der_sequence_defined_g(|i, _| {
let (i, algorithm) = map_res(parse_der_oid, |x| x.as_oid_val())(i)
.or(Err(X509Error::InvalidAlgorithmIdentifier))?;
@@ -285,21 +206,15 @@ impl<'a> FromDer<'a> for AlgorithmIdentifier<'a> {
}
}
-/// X.509 Name (as used in `Issuer` and `Subject` fields)
-///
-/// The Name describes a hierarchical name composed of attributes, such
-/// as country name, and corresponding values, such as US. The type of
-/// the component AttributeValue is determined by the AttributeType; in
-/// general it will be a DirectoryString.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct X509Name<'a> {
- pub(crate) rdn_seq: Vec<RelativeDistinguishedName<'a>>,
+ pub rdn_seq: Vec<RelativeDistinguishedName<'a>>,
pub(crate) raw: &'a [u8],
}
impl<'a> fmt::Display for X509Name<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match x509name_to_string(&self.rdn_seq, oid_registry()) {
+ match x509name_to_string(&self.rdn_seq) {
Ok(o) => write!(f, "{}", o),
Err(_) => write!(f, "<X509Error: Invalid X.509 name>"),
}
@@ -307,18 +222,18 @@ impl<'a> fmt::Display for X509Name<'a> {
}
impl<'a> X509Name<'a> {
- /// Builds a new `X509Name` from the provided elements.
- #[inline]
- pub const fn new(rdn_seq: Vec<RelativeDistinguishedName<'a>>, raw: &'a [u8]) -> Self {
- X509Name { rdn_seq, raw }
- }
-
- /// Attempt to format the current name, using the given registry to convert OIDs to strings.
- ///
- /// Note: a default registry is provided with this crate, and is returned by the
- /// [`oid_registry()`] method.
- pub fn to_string_with_registry(&self, oid_registry: &OidRegistry) -> Result<String, X509Error> {
- x509name_to_string(&self.rdn_seq, oid_registry)
+ /// Parse the X.501 type Name, used for ex in issuer and subject of a X.509 certificate
+ pub fn from_der(i: &'a [u8]) -> X509Result<Self> {
+ let start_i = i;
+ parse_der_sequence_defined_g(move |i, _| {
+ let (i, rdn_seq) = many0(complete(RelativeDistinguishedName::from_der))(i)?;
+ let len = start_i.offset(i);
+ let name = X509Name {
+ rdn_seq,
+ raw: &start_i[..len],
+ };
+ Ok((i, name))
+ })(i)
}
// Not using the AsRef trait, as that would not give back the full 'a lifetime
@@ -327,11 +242,6 @@ impl<'a> X509Name<'a> {
}
/// Return an iterator over the `RelativeDistinguishedName` components of the name
- pub fn iter(&self) -> impl Iterator<Item = &RelativeDistinguishedName<'a>> {
- self.rdn_seq.iter()
- }
-
- /// Return an iterator over the `RelativeDistinguishedName` components of the name
pub fn iter_rdn(&self) -> impl Iterator<Item = &RelativeDistinguishedName<'a>> {
self.rdn_seq.iter()
}
@@ -383,70 +293,41 @@ impl<'a> X509Name<'a> {
/// Note that there are multiple reasons for failure or incorrect behavior, for ex. if
/// the attribute is present multiple times, or is not a UTF-8 encoded string (it can be
/// UTF-16, or even an OCTETSTRING according to the standard).
- pub fn iter_common_name(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> {
+ pub fn iter_common_name(&self) -> impl Iterator<Item = &AttributeTypeAndValue> {
self.iter_by_oid(&OID_X509_COMMON_NAME)
}
/// Return an iterator over the `Country` attributes of the X.509 Name.
- pub fn iter_country(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> {
+ pub fn iter_country(&self) -> impl Iterator<Item = &AttributeTypeAndValue> {
self.iter_by_oid(&OID_X509_COUNTRY_NAME)
}
/// Return an iterator over the `Organization` attributes of the X.509 Name.
- pub fn iter_organization(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> {
+ pub fn iter_organization(&self) -> impl Iterator<Item = &AttributeTypeAndValue> {
self.iter_by_oid(&OID_X509_ORGANIZATION_NAME)
}
/// Return an iterator over the `OrganizationalUnit` attributes of the X.509 Name.
- pub fn iter_organizational_unit(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> {
+ pub fn iter_organizational_unit(&self) -> impl Iterator<Item = &AttributeTypeAndValue> {
self.iter_by_oid(&OID_X509_ORGANIZATIONAL_UNIT)
}
/// Return an iterator over the `StateOrProvinceName` attributes of the X.509 Name.
- pub fn iter_state_or_province(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> {
+ pub fn iter_state_or_province(&self) -> impl Iterator<Item = &AttributeTypeAndValue> {
self.iter_by_oid(&OID_X509_STATE_OR_PROVINCE_NAME)
}
/// Return an iterator over the `Locality` attributes of the X.509 Name.
- pub fn iter_locality(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> {
+ pub fn iter_locality(&self) -> impl Iterator<Item = &AttributeTypeAndValue> {
self.iter_by_oid(&OID_X509_LOCALITY_NAME)
}
/// Return an iterator over the `EmailAddress` attributes of the X.509 Name.
- pub fn iter_email(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> {
+ pub fn iter_email(&self) -> impl Iterator<Item = &AttributeTypeAndValue> {
self.iter_by_oid(&OID_PKCS9_EMAIL_ADDRESS)
}
}
-impl<'a> FromIterator<RelativeDistinguishedName<'a>> for X509Name<'a> {
- fn from_iter<T: IntoIterator<Item = RelativeDistinguishedName<'a>>>(iter: T) -> Self {
- let rdn_seq = iter.into_iter().collect();
- X509Name { rdn_seq, raw: &[] }
- }
-}
-
-impl<'a> From<X509Name<'a>> for Vec<RelativeDistinguishedName<'a>> {
- fn from(name: X509Name<'a>) -> Self {
- name.rdn_seq
- }
-}
-
-impl<'a> FromDer<'a> for X509Name<'a> {
- /// Parse the X.501 type Name, used for ex in issuer and subject of a X.509 certificate
- fn from_der(i: &'a [u8]) -> X509Result<Self> {
- let start_i = i;
- parse_der_sequence_defined_g(move |i, _| {
- let (i, rdn_seq) = many0(complete(RelativeDistinguishedName::from_der))(i)?;
- let len = start_i.offset(i);
- let name = X509Name {
- rdn_seq,
- raw: &start_i[..len],
- };
- Ok((i, name))
- })(i)
- }
-}
-
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct ReasonCode(pub u8);
@@ -495,10 +376,7 @@ fn attribute_value_to_string(attr: &DerObject, _attr_type: &Oid) -> Result<Strin
/// Multiple RDNs are separated with "+"
///
/// Attributes that cannot be represented by a string are hex-encoded
-fn x509name_to_string(
- rdn_seq: &[RelativeDistinguishedName],
- oid_registry: &OidRegistry,
-) -> Result<String, X509Error> {
+fn x509name_to_string(rdn_seq: &[RelativeDistinguishedName]) -> Result<String, X509Error> {
rdn_seq.iter().fold(Ok(String::new()), |acc, rdn| {
acc.and_then(|mut _vec| {
rdn.set
@@ -507,7 +385,7 @@ fn x509name_to_string(
acc2.and_then(|mut _vec2| {
let val_str = attribute_value_to_string(&attr.attr_value, &attr.attr_type)?;
// look ABBREV, and if not found, use shortname
- let abbrev = match oid2abbrev(&attr.attr_type, oid_registry) {
+ let abbrev = match oid2abbrev(&attr.attr_type) {
Ok(s) => String::from(s),
_ => format!("{:?}", attr.attr_type),
};
@@ -537,16 +415,12 @@ pub(crate) fn parse_signature_value(i: &[u8]) -> X509Result<BitStringObject> {
}
pub(crate) fn parse_serial(i: &[u8]) -> X509Result<(&[u8], BigUint)> {
- // This should be parse_der_integer, but some certificates encode leading zeroes
- map_opt(parse_ber_integer, get_serial_info)(i).map_err(|_| X509Error::InvalidSerial.into())
+ map_opt(parse_der_integer, get_serial_info)(i).map_err(|_| X509Error::InvalidSerial.into())
}
fn get_serial_info(o: DerObject) -> Option<(&[u8], BigUint)> {
- // RFC 5280 4.1.2.2: "The serial number MUST be a positive integer"
- // however, many CAs do not respect this and send integers with MSB set,
- // so we do not use `as_biguint()`
+ let big = o.as_biguint()?;
let slice = o.as_slice().ok()?;
- let big = BigUint::from_bytes_be(slice);
Some((slice, big))
}
diff --git a/tests/readcert.rs b/tests/readcert.rs
index 6c5f4ed..064664b 100644
--- a/tests/readcert.rs
+++ b/tests/readcert.rs
@@ -1,7 +1,6 @@
use chrono::offset::{TimeZone, Utc};
use chrono::Datelike;
-use der_parser::oid;
-use nom::Parser;
+use der_parser::{oid, oid::Oid};
use oid_registry::*;
use std::collections::HashMap;
use x509_parser::prelude::*;
@@ -62,11 +61,10 @@ fn test_x509_parser() {
assert_eq!(na.year(), 2020);
assert_eq!(na.month(), 10);
assert_eq!(na.day(), 17);
- let policies = vec![PolicyInformation {
- policy_id: oid!(1.2.250 .1 .121 .1 .1 .1),
- policy_qualifiers: None,
- }];
- let expected_extensions = vec![
+ let policies = vec![(oid!(1.2.250 .1 .121 .1 .1 .1), [].as_ref())]
+ .into_iter()
+ .collect();
+ let expected_extensions_list = vec![
X509Extension::new(
oid!(2.5.29 .19),
true,
@@ -86,7 +84,7 @@ fn test_x509_parser() {
oid!(2.5.29 .32),
false,
&[48, 12, 48, 10, 6, 8, 42, 129, 122, 1, 121, 1, 1, 1],
- ParsedExtension::CertificatePolicies(policies),
+ ParsedExtension::CertificatePolicies(CertificatePolicies { policies }),
),
X509Extension::new(
oid!(2.5.29 .14),
@@ -117,7 +115,11 @@ fn test_x509_parser() {
}),
),
];
- assert_eq!(tbs_cert.extensions(), &expected_extensions as &[_]);
+ let expected_extensions: HashMap<Oid, X509Extension> = expected_extensions_list
+ .into_iter()
+ .map(|e| (e.oid.clone(), e))
+ .collect();
+ assert_eq!(tbs_cert.extensions(), &expected_extensions);
//
assert!(tbs_cert.is_ca());
//
@@ -137,7 +139,7 @@ fn test_x509_no_extensions() {
let tbs_cert = cert.tbs_certificate;
assert_eq!(tbs_cert.version, X509Version::V3);
- assert_eq!(tbs_cert.extensions().len(), 0);
+ assert_eq!(tbs_cert.extensions.len(), 0);
}
_ => panic!("x509 parsing failed: {:?}", res),
}
@@ -205,13 +207,18 @@ fn test_crl_parse() {
let revoked_cert_0 = &revoked_certs[0];
assert_eq!(*revoked_cert_0.serial(), 0x147947u32.into());
assert_eq!(revoked_cert_0.revocation_date, revocation_date);
- let expected_extensions = vec![
+ let mut extensions_map = HashMap::new();
+ extensions_map.insert(
+ oid!(2.5.29 .21),
X509Extension::new(
oid!(2.5.29 .21),
false,
&[10, 1, 3],
ParsedExtension::ReasonCode(ReasonCode::AffiliationChanged),
),
+ );
+ extensions_map.insert(
+ oid!(2.5.29 .24),
X509Extension::new(
oid!(2.5.29 .24),
false,
@@ -220,8 +227,8 @@ fn test_crl_parse() {
],
ParsedExtension::InvalidityDate(invalidity_date),
),
- ];
- assert_eq!(revoked_cert_0.extensions(), &expected_extensions as &[_]);
+ );
+ assert_eq!(revoked_cert_0.extensions, extensions_map);
assert_eq!(revoked_certs.len(), 5);
assert_eq!(revoked_certs[4].user_certificate, 1_341_771_u32.into());
@@ -250,7 +257,12 @@ fn test_crl_parse() {
ParsedExtension::CRLNumber(3u32.into()),
),
];
- assert_eq!(tbs_cert_list.extensions(), &expected_extensions as &[_]);
+
+ let expected_extensions: HashMap<Oid, X509Extension> = expected_extensions
+ .into_iter()
+ .map(|e| (e.oid.clone(), e))
+ .collect();
+ assert_eq!(tbs_cert_list.extensions, expected_extensions);
assert_eq!(tbs_cert_list.as_ref(), &CRL_DER[4..(4 + 4 + 508)]);
}
@@ -289,7 +301,15 @@ fn test_crl_parse_empty() {
}),
),
];
- assert_eq!(cert.extensions(), &expected_extensions as &[_]);
+ // do not compare iterators because of random order in hashmap
+ assert_eq!(
+ cert.tbs_cert_list.extensions.len(),
+ expected_extensions.len()
+ );
+ let parsed_extension_values: Vec<_> = cert.tbs_cert_list.extensions.values().collect();
+ for extension in &expected_extensions {
+ assert!(parsed_extension_values.contains(&extension));
+ }
assert_eq!(
cert.tbs_cert_list.as_ref(),
&EMPTY_CRL_DER[4..(4 + 3 + 200)]
@@ -311,8 +331,8 @@ fn test_crl_parse_minimal() {
let revoked_cert_0 = &revoked_certificates[0];
assert_eq!(*revoked_cert_0.serial(), 42u32.into());
assert_eq!(revoked_cert_0.revocation_date, revocation_date);
- assert!(revoked_cert_0.extensions().is_empty());
- assert!(crl.tbs_cert_list.extensions().is_empty());
+ assert_eq!(revoked_cert_0.extensions, HashMap::new());
+ assert!(crl.tbs_cert_list.extensions.is_empty());
assert_eq!(crl.tbs_cert_list.as_ref(), &MINIMAL_CRL_DER[4..(4 + 79)]);
}
err => panic!("x509 parsing failed: {:?}", err),
@@ -325,32 +345,21 @@ fn test_duplicate_authority_info_access() {
Ok((_, cert)) => {
let extension = cert
.tbs_certificate
- .find_extension(&OID_PKIX_AUTHORITY_INFO_ACCESS)
+ .extensions
+ .get(&OID_PKIX_AUTHORITY_INFO_ACCESS)
.unwrap();
let mut accessdescs = HashMap::new();
let ca_issuers = vec![
- &GeneralName::URI("http://cdp1.pca.dfn.de/dfn-ca-global-g2/pub/cacert/cacert.crt"),
- &GeneralName::URI("http://cdp2.pca.dfn.de/dfn-ca-global-g2/pub/cacert/cacert.crt"),
+ GeneralName::URI("http://cdp1.pca.dfn.de/dfn-ca-global-g2/pub/cacert/cacert.crt"),
+ GeneralName::URI("http://cdp2.pca.dfn.de/dfn-ca-global-g2/pub/cacert/cacert.crt"),
];
- let ocsp = vec![&GeneralName::URI("http://ocsp.pca.dfn.de/OCSP-Server/OCSP")];
+ let ocsp = vec![GeneralName::URI("http://ocsp.pca.dfn.de/OCSP-Server/OCSP")];
accessdescs.insert(OID_PKIX_ACCESS_DESCRIPTOR_CA_ISSUERS, ca_issuers);
accessdescs.insert(OID_PKIX_ACCESS_DESCRIPTOR_OCSP, ocsp);
- if let ParsedExtension::AuthorityInfoAccess(aia) = extension.parsed_extension() {
- let h = aia.as_hashmap();
- assert_eq!(h, accessdescs);
- } else {
- panic!("Wrong extension type parsed");
- }
+ let expected_aia =
+ ParsedExtension::AuthorityInfoAccess(AuthorityInfoAccess { accessdescs });
+ assert_eq!(*extension.parsed_extension(), expected_aia);
}
err => panic!("x509 parsing failed: {:?}", err),
}
}
-
-#[test]
-fn test_x509_parser_no_ext() {
- let mut parser = X509CertificateParser::new().with_deep_parse_extensions(false);
- let (_, x509) = parser.parse(IGCA_DER).expect("parsing failed");
- for ext in x509.extensions() {
- assert_eq!(ext.parsed_extension(), &ParsedExtension::Unparsed);
- }
-}
diff --git a/tests/readcsr.rs b/tests/readcsr.rs
index 77dafec..5b0944c 100644
--- a/tests/readcsr.rs
+++ b/tests/readcsr.rs
@@ -12,7 +12,7 @@ fn read_csr_empty_attrib() {
assert!(rem.is_empty());
let cri = &csr.certification_request_info;
assert_eq!(cri.version, X509Version(0));
- assert_eq!(cri.attributes().len(), 0);
+ assert_eq!(cri.attributes.len(), 0);
assert_eq!(csr.signature_algorithm.algorithm, OID_PKCS1_SHA256WITHRSA);
}
@@ -25,13 +25,13 @@ fn read_csr_with_san() {
assert!(rem.is_empty());
let cri = &csr.certification_request_info;
assert_eq!(cri.version, X509Version(0));
- assert_eq!(cri.attributes().len(), 1);
+ assert_eq!(cri.attributes.len(), 1);
assert_eq!(csr.signature_algorithm.algorithm, OID_SIG_ECDSA_WITH_SHA256);
- let mut rdns = cri.subject.iter();
+ let mut rdns = cri.subject.rdn_seq.iter();
let rdn = rdns.next().unwrap();
- let first = rdn.iter().next().unwrap();
- assert_eq!(first.attr_type(), &OID_X509_COMMON_NAME);
+ let first = rdn.set.first().unwrap();
+ assert_eq!(first.attr_type, OID_X509_COMMON_NAME);
assert_eq!(first.as_str().unwrap(), "test.rusticata.fr");
let expected: &[u8] = &[
diff --git a/tests/verify.rs b/tests/verify.rs
index 6cb3835..4cdd2e2 100644
--- a/tests/verify.rs
+++ b/tests/verify.rs
@@ -22,14 +22,3 @@ fn test_signature_verification() {
eprintln!("Verification: {:?}", res);
assert!(res.is_ok());
}
-
-static ED25519_DER: &[u8] = include_bytes!("../assets/ed25519.der");
-
-#[test]
-fn test_signature_verification_ed25519() {
- // this certificate is self-signed
- let (_, x509_ca) = parse_x509_certificate(ED25519_DER).expect("could not parse certificate");
- let res = x509_ca.verify_signature(None);
- eprintln!("Verification: {:?}", res);
- assert!(res.is_ok());
-}