chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,6 @@
{
"git": {
"sha1": "33b15d2db5a19b15c17bb15fa57b08691316ee95"
},
"path_in_vcs": ""
}

430
vendor/x509-parser/CHANGELOG.md vendored Normal file
View File

@@ -0,0 +1,430 @@
# ChangeLog
## [Unreleased][unreleased]
### Added/Changed/Fixed
### Thanks
## 0.18.1
### Fixed
- fix(verify): also enable all functions when using verify-aws, otherwise
par of the API is missing
## 0.18.0
### Added/Changed
- Update lock file and dependencies
- Fix clippy warnings
- Visitor: add method to visit unknown extension and those with parse errors
- Add new feature `verify-aws` to used `aws-lc-rs` as crypto provider instead of `ring`
- The features are exclusive, so only one should be used
- If both are specified, `aws-lc-rs` is used (but both dependencies are included)
- Add `as_raw` methods to `X509Certificate`, `CertificateRevocationList` and `X509CertificationRequest`
- This method exposes the raw ASN.1 DER bytes used to build the object (#217)
Extensions:
- Add support for SubjectInfoAccess extension
- GeneralName: add a new variant `Invalid` so an invalid entry does not stop
parsing for the entire list of names (for ex in SAN)
### Fixed
- PEM: ignore lines in comments which contain invalid UTF-8 characters (#180)
### Thanks
- Daniel McCarney
## 0.17.0
### Added/Changed/Fixed
Global:
- Upgrade `asn1-rs` to version 0.6.2. (#161)
- Update asn1-rs to 0.7, der-parser to 10.0 and oid-registry to 0.8
- Upgrade time to 0.3.35 to make the crate compatible with rust >1.79.0 (#168, #175)
- Update MSRV to 1.67 (due to time 0.3.35, see #168)
- Add Visitor traits for X.509 Certificates and Certificate Revocation Lists (#179)
Code:
- Add support for RSA-PSS signature verification (#156)
- ASN1Time: store the kind of time (UTC or Generalized) in ASN1Time (#163)
- X509StructureValidator: add validation for dates encoding (#163)
- X509StructureValidator: enforce version > 1 for issuerUniqueID or subjectUniqueID (Closes #162)
### Thanks
- Daniel McCarney, DefiCake, Victor M. Alvarez, Nikolaus Thuemmel
## 0.16.0
### Added/Changed/Fixed
Global:
- Updated `*ring*` to 0.17.7 (#148)
- Updated `time` to 0.3.20 (#148)
- Updated asn1-rs to 0.6, der-parser and oid-registry
- Set MSRV to 1.63 (due to `time`/`ring`) (#148)
Code:
- Added support for parsing CRL `IssuingDistributionPoint` extensions (#146)
- Fixed lifetime signature on `TbsCertificate::subject_alt_names` function (#151)
- Fixed parsing of certificate `UniqueIdentifier` fields to use implicit tagging
instead of explicit (#145)
- Fixed `clippy::manual_try_fold` findings (#147)
### Thanks
- aggstam, Biagio Festa, Daniel McCarney
## 0.15.1
### Added/Changed/Fixed
- Attribute: fix parsing of BmpString string type to use UTF-16 (Closes #143)
- `revocation_list`: use correct OID for CRL number.
- Fix receiver lifetimes in `AttributeTypeAndValue`
### Thanks
- Sergio Benitez, Daniel McCarney, Lily Ballard
## 0.15.0
### Added/Changed/Fixed
Global:
- Use SPDX license format (#137)
- Set MSRV to 1.57 (due to `ring`/`once_cell`)
- Switch base64 decoding to `data-encoding` crate (#136)
Code:
- Add `verify` feature to verify a certificate revocation list by a public key
- Fixed CriAttributes parser (#131)
- Refactor code for parsing X509Version
- Add verify signature method to revocation list (#130)
- Add support for parsing challenge password attribute in CSR's (#129)
- Add support for multi-word PEM labels (C#135)
Docs:
- Fix broken FromDer trait link in README
### Thanks
- Bernd Krietenstein, Florian Zipperle, Jean-Baptiste Trystram, Daniel McCarney,
Jeff Hiner, Campbell He, Sebastian Dröge
## 0.14.0
### Added/Changed
- Add support for parsing signature parameters and value (closes #94)
- Change `ASN1Time::to_rfc2822()` to return a Result
- ASN1Time: modify `from_timestamp` to return a Result
- ASN1Time: implement Display
- Upgrade versions of asn1-rs, oid-registry and der-parser
- AlgorithmIdentifier: add const methods to create object/access fields
- Globally: start using `asn1-rs` types, simplify parsers:
- AlgorithmIdentifier: automatically derive struct, use type ANY
- Merge old FromDer trait into `asn1_rs::FromDer` (using X509Error)
- Replace BitStringObject with BitString
- AttributeTypeAndValue: use Any instead of DerObject
- Extensions: replace UnparsedObject with Any
- X509Error: add methods to simplify conversions
- CRI Attributes: rewrite and simplify parsers
- Simplify parsers for multiple types and extensions
### Fixed
- Fix ECDSA signature verification when CA and certificate use different curves
### Thanks
## 0.13.2
### Fixed
- Fix panic in ASN1Time::to_rfc2822() when year is less than 1900
## 0.13.1
### Fixed
- Fix regression with certificate verification for ECDSA signatures using the P-256 curve and SHA-384 (#118)
- Set minimum version of `time` to 0.3.7 (#119)
- Allow empty SEQUENCE when OPTIONAL, for ex in CRL extensions (#120)
### Thanks
- @SergioBenitez, @flavio, @acarlson0000
## 0.13.0
### Added/Changed/Fixed
Crate:
- Update to der-parser 7.0 and asn1-rs
- Remove chrono (#111)
- Set MSRV to 1.53
Validators:
- Add `Deref<Target=TbsCertificate>` trait to `X509Certificate`
- Add `Validator` trait and deprecate `Validate`
* The previous validation is implemented in `X509StructureValidator`
* Split some checks (not on structure) to `X509CertificateValidator`
Extensions:
- add support for nsComment
- add support for IssuerAltName
- start adding support for CT Signed Certificate Timestamp (rfc6962)
- raise error if a SAN entry cannot be parsed
- deprecate `TbsCertificate::find_extension()` and add preferred method `TbsCertificate::get_extension_unique()`:
the latter checks for duplicate extensions (#113)
Signatures:
- Fix signature verification for EC curves (#116)
Public Keys:
- Add base functions for parsing public keys (RSA, DSA, GOST)
### Thanks
- @lilyball, @g2p
## 0.12.0
### Added/Changed/Fixed
- Upgrade to nom 7
## 0.11.0
### Added
- Add SubjectPublicKeyInfo::raw field
### Changed/Fixed
- Fix der-parser dependency (#102)
- Update oid-registry dependency (#77)
- Set MSRV to 1.46 (indirect dependency on lexical-core and bitvec)
- Extend the lifetimes exposed on TbsCertificate (#104)
- Add missing test assets (#103)
### Thanks
- @jgalenson, @g2p, @kpp
## 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
- Remove der-oid-macro from dependencies, not used directly
- Use der_parser::num_bigint, remove it from direct dependencies
- Add methods to iterate all blocks from a PEM file (#75)
- Update MSRV to 1.45.0
## 0.9.1
### Added/Changed/Fixed
- Fix: X509Name::iter_state_or_province OID value
- Re-export oid-registry, and add doc to show how to access OID
### Thanks
- @0xazure for fixing X509Name::iter_state_or_province
## 0.9.0
### Added/Changed/Fixed
- Upgrade to `nom` 6.0
- Upgrade to `der-parser` 5.0
- Upgrade MSRV to 1.44.0
- Re-export crates so crate users do not have to import them
- Add function parse_x509_pem and deprecate pem_to_der (#53)
- Add helper methods to X509Name and simplify accessing values
- Add support for ReasonCode extension
- Add support for InvalidityDate extension
- Add support for CRL Number extension
- Add support for Certificate Signing Request (#58)
- Change type of X509Version (now directly using the u32 value)
- X509Name: relax check, allow some non-rfc compliant strings (#50)
- Relax some constraints for invalid dates
- CRL: extract raw serial, and add methods to access it
- CRL: add method to iterate revoked certificates
- RevokedCertificate: convert extensions list to hashmap
- Refactor crate modules and visibility
- Rename top-level functions to `parse_x509_certificate` and parse_x509_crl`
- Refactor error handling, return meaningful errors when possible
- Make many more functions public (parse_tbs_certificate, etc.)
### Thanks
- Dirkjan Ochtman (@djc): support for Certificate Signing Request (CSR), code refactoring, etc.
## 0.8.0
### Added/Changed
- Upgrade to `der-parser` 4.0
- Move from `time` to `chrono`
- `time 0.1 is very old, and time 0.2 broke compatibility and cannot parse timezones
- Add public type `ASN1Time` object to abstract implementation
- *this breaks API for direct access to `not_before`, `not_after` etc.*
- Fix clippy warnings
- `nid2obj` argument is now passed by copy, not reference
- Add method to get a formatted string of the certificate serial number
- Add method to get decoded version
- Add convenience methods to access the most common fields (subject, issuer, etc.)
- Expose the raw DER of an X509Name
- Make `parse_x509_name` public, for parsing distinguished names
- Make OID objects public
- Implement parsing for some extensions
- Support for extensions is not complete, support for more types will be added later
- Add example to decode and print certificates
- Add `verify` feature to verify cryptographic signature by a public key
### Fixed
- Fix parsing of types not representable by string in X509Name (#36)
- Fix parsing of certificates with empty subject (#37)
### Thanks
- @jannschu, @g2p for the extensions parsing
- @wayofthepie for the tests and contributions
- @nicholasbishop for contributions
## 0.7.0
- Expose raw bytes of the certificate serial number
- Set edition to 2018
## 0.6.4
- Fix infinite loop when certificate has no END mark
## 0.6.3
- Fix infinite loop when reading non-pem data (#28)
## 0.6.2
- Remove debug code left in `Pem::read`
## 0.6.1
- Add CRL parser
- Expose CRL tbs bytes
- PEM: ignore lines before BEGIN label (#21)
- Fix parsing default values for TbsCertificate version field (#24)
- Use BerResult from der-parser for simpler function signatures
- Expose tbsCertificate bytes
- Upgrade dependencies (base64)
## 0.6.0
- Update to der-parser 3.0 and nom 5
- Breaks API, cleaner error types
## 0.5.1
- Add `time_to_expiration` to `Validity` object
- Add method to read a `Pem` object from `BufRead + Seek`
- Add method to `Pem` to decode and extract certificate
## 0.5.0
- Update to der-parser 2.0
## 0.4.3
- Make `parse_subject_public_key_info` public
- Add function `sn2oid` (get an OID by short name)
## 0.4.2
- Support GeneralizedTime conversion
## 0.4.1
- Fix case where certificate has no extensions
## 0.4.0
- Upgrade to der-parser 1.1, and Use num-bigint over num
- Rename x509_parser to parse_x509_der
- Do not export subparsers
- Improve documentation
## 0.3.0
- Upgrade to nom 4
## 0.2.0
- Rewrite X.509 structures and parsing code to work in one pass
**Warning: this is a breaking change**
- Add support for PEM-encoded certificates
- Add some documentation

847
vendor/x509-parser/Cargo.lock generated vendored Normal file
View File

@@ -0,0 +1,847 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "asn1-rs"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60"
dependencies = [
"asn1-rs-derive",
"asn1-rs-impl",
"displaydoc",
"nom",
"num-traits",
"rusticata-macros",
"thiserror",
"time",
]
[[package]]
name = "asn1-rs-derive"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]]
name = "asn1-rs-impl"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "aws-lc-rs"
version = "1.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba"
dependencies = [
"aws-lc-sys",
"untrusted 0.7.1",
"zeroize",
]
[[package]]
name = "aws-lc-sys"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff"
dependencies = [
"bindgen",
"cc",
"cmake",
"dunce",
"fs_extra",
]
[[package]]
name = "bindgen"
version = "0.69.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088"
dependencies = [
"bitflags",
"cexpr",
"clang-sys",
"itertools",
"lazy_static",
"lazycell",
"log",
"prettyplease",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn",
"which",
]
[[package]]
name = "bitflags"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "cc"
version = "1.2.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e"
dependencies = [
"jobserver",
"libc",
"shlex",
]
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
[[package]]
name = "cfg-if"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
[[package]]
name = "clang-sys"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "cmake"
version = "0.1.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0"
dependencies = [
"cc",
]
[[package]]
name = "data-encoding"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
[[package]]
name = "der-parser"
version = "10.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6"
dependencies = [
"asn1-rs",
"displaydoc",
"nom",
"num-bigint",
"num-traits",
"rusticata-macros",
]
[[package]]
name = "deranged"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
dependencies = [
"powerfmt",
]
[[package]]
name = "displaydoc"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "dunce"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "errno"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
dependencies = [
"libc",
"windows-sys 0.60.2",
]
[[package]]
name = "fs_extra"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
[[package]]
name = "getrandom"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
"libc",
"wasi 0.11.1+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi 0.14.2+wasi-0.2.4",
]
[[package]]
name = "glob"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "home"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "itertools"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "jobserver"
version = "0.1.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
dependencies = [
"getrandom 0.3.3",
"libc",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.175"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
[[package]]
name = "libloading"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
dependencies = [
"cfg-if",
"windows-targets 0.53.3",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]]
name = "log"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "memchr"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "num-bigint"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "oid-registry"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7"
dependencies = [
"asn1-rs",
]
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "prettyplease"
version = "0.2.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d61789d7719defeb74ea5fe81f2fdfdbd28a803847077cecce2ff14e1472f6f1"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "regex"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "ring"
version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [
"cc",
"cfg-if",
"getrandom 0.2.16",
"libc",
"untrusted 0.9.0",
"windows-sys 0.52.0",
]
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rusticata-macros"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632"
dependencies = [
"nom",
]
[[package]]
name = "rustix"
version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.59.0",
]
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "syn"
version = "2.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bc3fcb250e53458e712715cf74285c1f889686520d79294a9ef3bd7aa1fc619"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "synstructure"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "thiserror"
version = "2.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "time"
version = "0.3.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
dependencies = [
"deranged",
"itoa",
"num-conv",
"powerfmt",
"serde",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
[[package]]
name = "time-macros"
version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
dependencies = [
"num-conv",
"time-core",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasi"
version = "0.14.2+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "which"
version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
dependencies = [
"either",
"home",
"once_cell",
"rustix",
]
[[package]]
name = "windows-link"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets 0.53.3",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm 0.52.6",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.53.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
dependencies = [
"windows-link",
"windows_aarch64_gnullvm 0.53.0",
"windows_aarch64_msvc 0.53.0",
"windows_i686_gnu 0.53.0",
"windows_i686_gnullvm 0.53.0",
"windows_i686_msvc 0.53.0",
"windows_x86_64_gnu 0.53.0",
"windows_x86_64_gnullvm 0.53.0",
"windows_x86_64_msvc 0.53.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags",
]
[[package]]
name = "x509-parser"
version = "0.18.1"
dependencies = [
"asn1-rs",
"aws-lc-rs",
"data-encoding",
"der-parser",
"lazy_static",
"nom",
"oid-registry",
"ring",
"rusticata-macros",
"thiserror",
"time",
]
[[package]]
name = "zeroize"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"

153
vendor/x509-parser/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,153 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
rust-version = "1.67.1"
name = "x509-parser"
version = "0.18.1"
authors = ["Pierre Chifflier <chifflier@wzdftpd.net>"]
build = false
include = [
"CHANGELOG.md",
"LICENSE-*",
"README.md",
".gitignore",
".travis.yml",
"Cargo.toml",
"src/*.rs",
"src/extensions/*.rs",
"src/validate/*.rs",
"src/visitor/*.rs",
"tests/*.rs",
"assets/*.crl",
"assets/*.csr",
"assets/*.der",
"assets/*.pem",
"assets/crl-ext/*.der",
"examples/*.rs",
]
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Parser for the X.509 v3 format (RFC 5280 certificates)"
homepage = "https://github.com/rusticata/x509-parser"
readme = "README.md"
keywords = [
"X509",
"Certificate",
"parser",
"nom",
]
categories = [
"parser-implementations",
"cryptography",
]
license = "MIT OR Apache-2.0"
repository = "https://github.com/rusticata/x509-parser.git"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = [
"--cfg",
"docsrs",
]
[features]
default = []
validate = []
verify = ["ring"]
verify-aws = ["aws-lc-rs"]
[lib]
name = "x509_parser"
path = "src/lib.rs"
[[example]]
name = "print-cert"
path = "examples/print-cert.rs"
[[example]]
name = "print-crl"
path = "examples/print-crl.rs"
[[test]]
name = "pem"
path = "tests/pem.rs"
[[test]]
name = "readcert"
path = "tests/readcert.rs"
[[test]]
name = "readcrl"
path = "tests/readcrl.rs"
[[test]]
name = "readcsr"
path = "tests/readcsr.rs"
[[test]]
name = "run_all_fuzz_files"
path = "tests/run_all_fuzz_files.rs"
[[test]]
name = "test01"
path = "tests/test01.rs"
[[test]]
name = "verify"
path = "tests/verify.rs"
[dependencies.asn1-rs]
version = "0.7.0"
features = ["datetime"]
[dependencies.aws-lc-rs]
version = "1.0"
optional = true
[dependencies.data-encoding]
version = "2.2.1"
[dependencies.der-parser]
version = "10.0"
features = ["bigint"]
[dependencies.lazy_static]
version = "1.4"
[dependencies.nom]
version = "7.0"
[dependencies.oid-registry]
version = "0.8.1"
features = [
"crypto",
"x509",
"x962",
]
[dependencies.ring]
version = "0.17.12"
optional = true
[dependencies.rusticata-macros]
version = "4.0"
[dependencies.thiserror]
version = "2.0"
[dependencies.time]
version = "0.3.35"
features = ["formatting"]

201
vendor/x509-parser/LICENSE-APACHE vendored Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

25
vendor/x509-parser/LICENSE-MIT vendored Normal file
View File

@@ -0,0 +1,25 @@
Copyright (c) 2017 Pierre Chifflier
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

133
vendor/x509-parser/README.md vendored Normal file
View File

@@ -0,0 +1,133 @@
<!-- cargo-sync-readme start -->
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE-MIT)
[![Apache License 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](./LICENSE-APACHE)
[![docs.rs](https://docs.rs/x509-parser/badge.svg)](https://docs.rs/x509-parser)
[![crates.io](https://img.shields.io/crates/v/x509-parser.svg)](https://crates.io/crates/x509-parser)
[![Download numbers](https://img.shields.io/crates/d/x509-parser.svg)](https://crates.io/crates/x509-parser)
[![Github CI](https://github.com/rusticata/x509-parser/workflows/Continuous%20integration/badge.svg)](https://github.com/rusticata/x509-parser/actions)
[![Minimum rustc version](https://img.shields.io/badge/rustc-1.67.1+-lightgray.svg)](#rust-version-requirements)
# X.509 Parser
A X.509 v3 ([RFC5280]) parser, implemented with the [nom](https://github.com/Geal/nom)
parser combinator framework.
It is written in pure Rust, fast, and makes extensive use of zero-copy. A lot of care is taken
to ensure security and safety of this crate, including design (recursion limit, defensive
programming), tests, and fuzzing. It also aims to be panic-free.
The code is available on [Github](https://github.com/rusticata/x509-parser)
and is part of the [Rusticata](https://github.com/rusticata) project.
Certificates are usually encoded in two main formats: PEM (usually the most common format) or
DER. A PEM-encoded certificate is a container, storing a DER object. See the
[`pem`](https://docs.rs/x509-parser/latest/x509_parser/pem/index.html) module for more documentation.
To decode a DER-encoded certificate, the main parsing method is
`X509Certificate::from_der` (
part of the [`FromDer`](https://docs.rs/x509-parser/latest/x509_parser/prelude/trait.FromDer.html) trait
), which builds a
[`X509Certificate`](https://docs.rs/x509-parser/latest/x509_parser/certificate/struct.X509Certificate.html) object.
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).
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
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.
This crate also provides visitor traits: [`X509CertificateVisitor`](crate::visitor::X509CertificateVisitor).
# Examples
Parsing a certificate in DER format:
```rust
use x509_parser::prelude::*;
static IGCA_DER: &[u8] = include_bytes!("../assets/IGC_A.der");
let res = X509Certificate::from_der(IGCA_DER);
match res {
Ok((rem, cert)) => {
assert!(rem.is_empty());
//
assert_eq!(cert.version(), X509Version::V3);
},
_ => panic!("x509 parsing failed: {:?}", res),
}
```
To parse a CRL and print information about revoked certificates:
```rust
#
#
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),
}
```
See also `examples/print-cert.rs`.
# Features
- The `verify` and `verify-aws` features add support for (cryptographic) signature verification, based on `ring` or `aws-lc` respectively.
It adds the
[`X509Certificate::verify_signature()`](https://docs.rs/x509-parser/latest/x509_parser/certificate/struct.X509Certificate.html#method.verify_signature)
to `X509Certificate`.
```rust
/// Cryptographic signature verification: returns true if certificate was signed by issuer
#[cfg(any(feature = "verify", feature = "verify-aws"))]
pub fn check_signature(cert: &X509Certificate<'_>, issuer: &X509Certificate<'_>) -> bool {
let issuer_public_key = issuer.public_key();
cert
.verify_signature(Some(issuer_public_key))
.is_ok()
}
```
- The `validate` feature 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.67.1 or greater**, based on der-parser
dependencies and for proc-macro attributes support.
[RFC5280]: https://tools.ietf.org/html/rfc5280
<!-- cargo-sync-readme end -->
## Changes
See [CHANGELOG.md](CHANGELOG.md)
# License
Licensed under either of
* Apache License, Version 2.0
([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license
([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
## Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.

BIN
vendor/x509-parser/assets/IGC_A.der vendored Normal file

Binary file not shown.

24
vendor/x509-parser/assets/IGC_A.pem vendored Normal file
View File

@@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYT
AkZSMQ8wDQYDVQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQ
TS9TR0ROMQ4wDAYDVQQLEwVEQ1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG
9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMB4XDTAyMTIxMzE0MjkyM1oXDTIw
MTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIEwZGcmFuY2UxDjAM
BgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NTSTEO
MAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2
LmZyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaI
s9z4iPf930Pfeo2aSVz2TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2
xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCWSo7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4
u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYyHF2fYPepraX/z9E0+X1b
F8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNdfrGoRpAx
Vs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGd
PDPQtQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNV
HSAEDjAMMAoGCCqBegF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAx
NjAfBgNVHSMEGDAWgBSjBS8YYFDCiQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUF
AAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RKq89toB9RlPhJy3Q2FLwV3duJ
L92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3QMZsyK10XZZOY
YLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg
Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2a
NjSaTFR+FwNIlQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R
0982gaEbeC9xs/FZTEYYKKuF0mBWWg==
-----END CERTIFICATE-----

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFWzCCBEOgAwIBAgISAyBIAwu7NBD5CTxX8suDCMgFMA0GCSqGSIb3DQEBCwUA
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTA3MTIxMTEyMzBaFw0x
OTEwMTAxMTEyMzBaMB0xGzAZBgNVBAMTEmxpc3RzLmZvci1vdXIuaW5mbzCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVoti34X46DaI2nX24C+aZ2Ofkm
hKbidiXiRTon1MLSMGl1oNW9MyRyYYCzP4j6DNKChJnr8ZnVShh2oZD+yHWP9lpn
XMGkbsUxejRMU9hnaAB50pXRIDAzavkVFCguFlJ8nKkv/Y1Avlw7tc2aZOd3lOZB
Er8gJ8mRDGqqsNU+Z12I6slEstzGMpsq6AewCVw4lMjdWWgugzUrxQTRAsG87on6
gOiQH2cMODN3L7Fq4KOLQIjb3/luQhAQhpdKmEGFLin3c+f5or3thCDuwwDtOU1l
Zf+8t9S8pZPLrZrIs6H2xjXqCRuUY7iRNbO18Ukc6rlDYhBj9LT+cpmBbHECAwEA
AaOCAmYwggJiMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYI
KwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUJj2pvRtl3GloH3He6FX1
ds3X0VEwHwYDVR0jBBgwFoAUqEpqYwR93brm0Tm3pkVl7/Oo7KEwbwYIKwYBBQUH
AQEEYzBhMC4GCCsGAQUFBzABhiJodHRwOi8vb2NzcC5pbnQteDMubGV0c2VuY3J5
cHQub3JnMC8GCCsGAQUFBzAChiNodHRwOi8vY2VydC5pbnQteDMubGV0c2VuY3J5
cHQub3JnLzAdBgNVHREEFjAUghJsaXN0cy5mb3Itb3VyLmluZm8wTAYDVR0gBEUw
QzAIBgZngQwBAgEwNwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0cDov
L2Nwcy5sZXRzZW5jcnlwdC5vcmcwggEDBgorBgEEAdZ5AgQCBIH0BIHxAO8AdgAp
PFGWVMg5ZbqqUPxYB9S3b79Yeily3KTDDPTlRUf0eAAAAWvmGV7yAAAEAwBHMEUC
ICQL2Sm14aCMLxX9a9RbySgyBfichMRdbu6QA2Mbrl4eAiEA1vgJ7snqUWCgoqEE
3SEfK3ioMopzWBsPvG6LdCuCMRAAdQBvU3asMfAxGdiZAKRRFf93FRwR2QLBACkG
jbIImjfZEwAAAWvmGV9oAAAEAwBGMEQCIExGqw3Lo0nSCyUuTRf92FgGASwWYji5
UGnXuYnpJrAvAiBw8AWVag8fzZ4ogAhY9EFRNdLrUcBjStipL888vyuxKzANBgkq
hkiG9w0BAQsFAAOCAQEAF8BBLDvSWZg57B6aDtzfUTSGetCYs3k0vJqCJlL+Pz7/
UruCSsojQzp5R6jvvgYQ83MaIdwe2mgt+OCQB5v7ylctyBzBmYIw9nPnxEC7HlcJ
L2K/k5ZjJFRnv4kV1Si8+TIpEAV0ksf39KGKemG8kGi4GXV1v03zSv0p8aCarpuo
SKBJ4qlB0CvmS2MqV4KnzO0O2h0c/ZQ4jg7l53eiN7VPdRMMO1DRw+MaW6I/hEZp
+oZQ7hhKXgKUBvF4IGwyrfyIZ8AeWKG4IP98COgyRbz7qtrAVevRKCM0ZC2t04A2
Fcix40FKEeiE093Aj3cweMYxNLPgwgQP8Xu3kA5QEw==
-----END CERTIFICATE-----

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,33 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIFuDCCA6ACAQAwgb4xCzAJBgNVBAYTAkdCMR8wHQYDVQQIDBZUZXN0IFN0YXRl
IG9yIFByb3ZpbmNlMRYwFAYDVQQHDA1UZXN0IExvY2FsaXR5MRowGAYDVQQKDBFP
cmdhbml6YXRpb24gTmFtZTEhMB8GA1UECwwYT3JnYW5pemF0aW9uYWwgVW5pdCBO
YW1lMRQwEgYDVQQDDAtDb21tb24gTmFtZTEhMB8GCSqGSIb3DQEJARYSdGVzdEBl
bWFpbC5hZGRyZXNzMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2pTx
B0wQkImcsMaHCO1jYsFRii4JhWJQT326yzI/1FM7pWA/s/kecMOkWVn/KtGDroqE
9Jw6UQOEsmFpLJEOTzAaGbNMM2BkJBNdhO2Vmmhyjks1sJljTftA+aKu4zpxs0i6
r83oMc6iJ08kMMyDYrKCt4oJjln7dpJXc8OoV9INoRpyU2KhTiqJdw0+4V8lB3BI
QGP6o4mN1Jx/ElJogWyS4CB3wt9I7n1RH08+f/StCs0iAkASKFxAoCLrdIKSk4CM
yUFilaZz6SvHXiLLi+NoLkc+nDaDu4Nu7Pj/e2fYtq8dLRB8A25v5wqQplF7s/ZJ
wiFHVfFJDY8wtdBJ12bgLOPQbqmpBiWVrnBZHGMQTsc5YSMHy6EPv33w16cUWneu
Ho/ryqBwTWww+fCnjOapkSajckmVpm8e4fijhNmq6N6VTpfgOZ3loCjVOp4/EW3M
L+qsYQyM5Y4trY4h0zZh9hZZmQig6vLMIl9n1r+580rLeGsdeyHwHAc6jfwsy4Zz
/jOfbWF3rB1xNgSBHL0po02k8PWFr+uBfKPmfTK8Yvlz3fsTJfzLr6u6y3XAejw/
ZZjjPydwLn3hL/q+J9SAhA3wxnCu3puwiyjhQQHgUHLkQsYpivdF1a7Pbz+l1D89
cTOwwkYonkReosLI5QiKbQAX2NbSfVqv8ALn09sCAwEAAaCBszAjBgkqhkiG9w0B
CQcxFgwUQSBjaGFsbGVuZ2UgcGFzc3dvcmQwgYsGCSqGSIb3DQEJDjF+MHwwDgYD
VR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNV
HQ4EFgQUkDknz2bwybBXuXEG1RmUpP84GxMwLAYDVR0RBCUwI4cEfwAAAYcQAAAA
AAAAAAAAAAAAAAAAAYIJbG9jYWxob3N0MA0GCSqGSIb3DQEBCwUAA4ICAQAll1fo
6YIKQHINhdj6aKRHKZ/CluFSTAvctga5+JQG8clbWWxjI+RDUZhnw/r5Jr+8OaBW
rm7c/aDePP9AOQ93tBfIEv4OMwP4jQVOrZJuAOuyrUoSTSehMPZoR7IZv2QcyRk+
RUi2aqZbYLPFptsJ6+8FlX/ieSM+GEBmj2W9U5LsvI2y6B34N0uy/if9IbQO8OHj
afrjZc2jZ1QYj69OUJekEjPZzrb3kSIVELyWfZEocq+e0hLgQEdI0ZCIdzKFeH8i
yQfCtJ7g7k/hVkrQcyd3JwFvhIJRlyhw8ORxOHqFsyZ2XWvg0lGVWk9ksRDGFcf3
g38Erl1sU3jPzEfb/B2aio2DO+42ZY65P7nuLNB5Tatdyy+yYmd3OlxL0XtUb/VW
DS9zKjNrjBLvgN9w5QUE0hc4HOHfcceEcPljALoeZWF/XmPjEUjbiKSIP7vIKDi2
bkx1LiLnuDzjvapG/C6YmuFrTxoqjc9qZs/e2NjrxJ5tI3s/d9R9UET+jh/1swLH
wz35SohLLDLc2ShaNlNuMOwWOySFV93RwjxcP6OsEFyCZrVsQ6UXz6odXk509A5e
w3xhpMdQFlsNRpH0f7CjcxsTUx3qFEXMwq0iYt9w0wrTezOTL25oGouHH11JQsZh
g7iXkOHZIfzZVIF3sagZhdp7stnJTWnUIhJvXw==
-----END CERTIFICATE REQUEST-----

Binary file not shown.

BIN
vendor/x509-parser/assets/ed25519.der vendored Normal file

Binary file not shown.

BIN
vendor/x509-parser/assets/empty.crl vendored Normal file

Binary file not shown.

BIN
vendor/x509-parser/assets/example.crl vendored Normal file

Binary file not shown.

BIN
vendor/x509-parser/assets/extension1.der vendored Normal file

Binary file not shown.

BIN
vendor/x509-parser/assets/extension2.der vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
vendor/x509-parser/assets/minimal.crl vendored Normal file

Binary file not shown.

9
vendor/x509-parser/assets/no_end.pem vendored Normal file
View File

@@ -0,0 +1,9 @@
-----BEGIN CERTIFICATE-----
MIIBUTCB2KADAgECAgkAtXGSKEzrTR4wCgYIKoZIzj0EAwIwEDEOMAwGA1UEAwwF
YmVubm8wHhcNMTgxMTEzMDI1NDQwWhcNMTkxMTEzMDI1NDQwWjAQMQ4wDAYDVQQD
DAViZW5ubzB2MBAGByqGSM49AgEGBSuBBAAiA2IABDhrHLVTMHC7GTyB/MNztToW
ss2zlmvR62X1pQaBN6fYhBJE1XYa0V2C1fGGXj92MencOtXyfYVxn+DY07gyT/71
HQ12TJOe90wwjy2/6N1W1jOv5HjphVT8JQlVNqAC+DAKBggqhkjOPQQDAgNoADBl
AjAfzS1tmZ+GSAaXrsPcfAd1A9yfWVtB8tWxFNNo2j7/cL3puf2vQnlwV/0BoZZ1
K4ICMQDaE0HemgYp6RPQzeb96a2gjlaEbwNy1B8O74fE24WRXiOUm4eln9wGQ3I1
iV6NtZU=

Binary file not shown.

View File

@@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIBUTCB2KADAgECAgkAtXGSKEzrTR4wCgYIKoZIzj0EAwIwEDEOMAwGA1UEAwwF
YmVubm8wHhcNMTgxMTEzMDI1NDQwWhcNMTkxMTEzMDI1NDQwWjAQMQ4wDAYDVQQD
DAViZW5ubzB2MBAGByqGSM49AgEGBSuBBAAiA2IABDhrHLVTMHC7GTyB/MNztToW
ss2zlmvR62X1pQaBN6fYhBJE1XYa0V2C1fGGXj92MencOtXyfYVxn+DY07gyT/71
HQ12TJOe90wwjy2/6N1W1jOv5HjphVT8JQlVNqAC+DAKBggqhkjOPQQDAgNoADBl
AjAfzS1tmZ+GSAaXrsPcfAd1A9yfWVtB8tWxFNNo2j7/cL3puf2vQnlwV/0BoZZ1
K4ICMQDaE0HemgYp6RPQzeb96a2gjlaEbwNy1B8O74fE24WRXiOUm4eln9wGQ3I1
iV6NtZU=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,7 @@
# Generating Test Certificates
```shell
openssl req -new -x509 -newkey rsa:2048 -keyout /dev/null -nodes -sigopt rsa_padding_mode:pss -sha256 -sigopt rsa_pss_saltlen:-1 -outform der -out self_signed_sha256.der -batch
openssl req -new -x509 -newkey rsa:2048 -keyout /dev/null -nodes -sigopt rsa_padding_mode:pss -sha384 -sigopt rsa_pss_saltlen:-1 -outform der -out self_signed_sha384.der -batch
openssl req -new -x509 -newkey rsa:2048 -keyout /dev/null -nodes -sigopt rsa_padding_mode:pss -sha512 -sigopt rsa_pss_saltlen:-1 -outform der -out self_signed_sha512.der -batch
```

8
vendor/x509-parser/assets/test.csr vendored Normal file
View File

@@ -0,0 +1,8 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIBBjCBrQIBADAcMRowGAYDVQQDDBF0ZXN0LnJ1c3RpY2F0YS5mcjBZMBMGByqG
SM49AgEGCCqGSM49AwEHA0IABMP1frFxwJLXiLU6UoqOPf31ucCm2NqR2yqpcHo6
W7iWJe31OzYs0izP2qeUvdKfz2fpAbuGiRjwvN+H10dQQEGgLzAtBgkqhkiG9w0B
CQ4xIDAeMBwGA1UdEQQVMBOCEXRlc3QucnVzdGljYXRhLmZyMAoGCCqGSM49BAMC
A0gAMEUCIGqQHPHgpeyZa5YMLP2X5IwfmrvpIcg5fQ2xkXotGAa0AiEAydeBwr4r
Iu7XDe015h8uz8xZs2QUEgRdr73lJXTX+Ck=
-----END CERTIFICATE REQUEST-----

BIN
vendor/x509-parser/assets/unique_ids.der vendored Normal file

Binary file not shown.

BIN
vendor/x509-parser/assets/v1.der vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,418 @@
use der_parser::der::Tag;
use der_parser::oid::Oid;
use nom::HexDisplay;
use std::cmp::min;
use std::convert::TryFrom;
use std::env;
use std::io;
use std::net::{Ipv4Addr, Ipv6Addr};
use x509_parser::prelude::*;
use x509_parser::public_key::PublicKey;
use x509_parser::signature_algorithm::SignatureAlgorithm;
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);
print!("{}", &bytes[..m].to_hex(16));
if bytes.len() > max_len {
println!("... <continued>");
}
}
fn format_oid(oid: &Oid) -> String {
match oid2sn(oid, oid_registry()) {
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:?}"),
GeneralName::Invalid(tag, b) => format!("Invalid:tag={tag},data={b:?}"),
}
}
fn print_x509_extension(oid: &Oid, ext: &X509Extension) {
println!(
" [crit:{} l:{}] {}: ",
ext.critical,
ext.value.len(),
format_oid(oid)
);
match ext.parsed_extension() {
ParsedExtension::AuthorityKeyIdentifier(aki) => {
println!(" X509v3 Authority Key Identifier");
if let Some(key_id) = &aki.key_identifier {
println!(" Key Identifier: {key_id:x}");
}
if let Some(issuer) = &aki.authority_cert_issuer {
for name in issuer {
println!(" Cert Issuer: {name}");
}
}
if let Some(serial) = aki.authority_cert_serial {
println!(" Cert Serial: {}", format_serial(serial));
}
}
ParsedExtension::BasicConstraints(bc) => {
println!(" X509v3 CA: {}", bc.ca);
}
ParsedExtension::CRLDistributionPoints(points) => {
println!(" X509v3 CRL Distribution Points:");
for point in points.iter() {
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}");
}
ParsedExtension::NSCertType(ty) => {
println!(" Netscape Cert Type: {ty}");
}
ParsedExtension::SubjectAlternativeName(san) => {
for name in &san.general_names {
let s = match name {
GeneralName::DNSName(s) => {
format!("DNS:{s}")
}
GeneralName::IPAddress(b) => {
let ip = match b.len() {
4 => {
let b = <[u8; 4]>::try_from(*b).unwrap();
let ip = Ipv4Addr::from(b);
format!("{ip}")
}
16 => {
let b = <[u8; 16]>::try_from(*b).unwrap();
let ip = Ipv6Addr::from(b);
format!("{ip}")
}
l => format!("invalid (len={l})"),
};
format!("IP Address:{ip}")
}
_ => {
format!("{name:?}")
}
};
println!(" X509v3 SAN: {s}");
}
}
ParsedExtension::SubjectKeyIdentifier(id) => {
println!(" X509v3 Subject Key Identifier: {id:x}");
}
x => println!(" {x:?}"),
}
}
fn print_x509_digest_algorithm(alg: &AlgorithmIdentifier, level: usize) {
println!(
"{:indent$}Oid: {}",
"",
format_oid(&alg.algorithm),
indent = level
);
if let Some(parameter) = &alg.parameters {
let s = match parameter.tag() {
Tag::Oid => {
let oid = parameter.as_oid().unwrap();
format_oid(&oid)
}
_ => format!("{}", parameter.tag()),
};
println!("{:indent$}Parameter: <PRESENT> {}", "", s, indent = level);
let bytes = parameter.as_bytes();
print_hex_dump(bytes, 32);
} else {
println!("{:indent$}Parameter: <ABSENT>", "", indent = level);
}
}
fn print_x509_info(x509: &X509Certificate) -> io::Result<()> {
let version = x509.version();
if version.0 < 3 {
println!(" Version: {version}");
} else {
println!(" Version: INVALID({})", version.0);
}
println!(" Serial: {}", x509.tbs_certificate.raw_serial_as_string());
println!(" Subject: {}", x509.subject());
println!(" Issuer: {}", x509.issuer());
println!(" Validity:");
println!(" NotBefore: {}", x509.validity().not_before);
println!(" NotAfter: {}", x509.validity().not_after);
println!(" is_valid: {}", x509.validity().is_valid());
println!(" Subject Public Key Info:");
print_x509_ski(x509.public_key());
print_x509_signature_algorithm(&x509.signature_algorithm, 4);
println!(" Signature Value:");
for l in format_number_to_hex_with_colon(&x509.signature_value.data, 16) {
println!(" {l}");
}
println!(" Extensions:");
for ext in x509.extensions() {
print_x509_extension(&ext.oid, ext);
}
println!();
print!("Structure validation status: ");
#[cfg(feature = "validate")]
{
let mut logger = VecLogger::default();
// structure validation status
let ok = X509StructureValidator
.chain(X509CertificateValidator)
.validate(x509, &mut logger);
if ok {
println!("Ok");
} else {
println!("FAIL");
}
for warning in logger.warnings() {
println!(" [W] {warning}");
}
for error in logger.errors() {
println!(" [E] {error}");
}
println!();
if VALIDATE_ERRORS_FATAL && !logger.errors().is_empty() {
return Err(io::Error::new(io::ErrorKind::Other, "validation failed"));
}
}
#[cfg(not(feature = "validate"))]
{
println!("Unknown (feature 'validate' not enabled)");
}
#[cfg(any(feature = "verify", feature = "verify-aws"))]
{
print!("Signature verification: ");
if x509.subject() == x509.issuer() {
if x509.verify_signature(None).is_ok() {
println!("OK");
println!(" [I] certificate is self-signed");
} else if x509.subject() == x509.issuer() {
println!("FAIL");
println!(" [W] certificate looks self-signed, but signature verification failed");
}
} else {
// if subject is different from issuer, we cannot verify certificate without the public key of the issuer
println!("N/A");
}
}
Ok(())
}
fn print_x509_signature_algorithm(signature_algorithm: &AlgorithmIdentifier, indent: usize) {
match SignatureAlgorithm::try_from(signature_algorithm) {
Ok(sig_alg) => {
print!(" Signature Algorithm: ");
match sig_alg {
SignatureAlgorithm::DSA => println!("DSA"),
SignatureAlgorithm::ECDSA => println!("ECDSA"),
SignatureAlgorithm::ED25519 => println!("ED25519"),
SignatureAlgorithm::RSA => println!("RSA"),
SignatureAlgorithm::RSASSA_PSS(params) => {
println!("RSASSA-PSS");
let indent_s = format!("{:indent$}", "", indent = indent + 2);
println!(
"{}Hash Algorithm: {}",
indent_s,
format_oid(params.hash_algorithm_oid()),
);
print!("{indent_s}Mask Generation Function: ");
if let Ok(mask_gen) = params.mask_gen_algorithm() {
println!(
"{}/{}",
format_oid(&mask_gen.mgf),
format_oid(&mask_gen.hash),
);
} else {
println!("INVALID");
}
println!("{}Salt Length: {}", indent_s, params.salt_length());
}
SignatureAlgorithm::RSAAES_OAEP(params) => {
println!("RSAAES-OAEP");
let indent_s = format!("{:indent$}", "", indent = indent + 2);
println!(
"{}Hash Algorithm: {}",
indent_s,
format_oid(params.hash_algorithm_oid()),
);
print!("{indent_s}Mask Generation Function: ");
if let Ok(mask_gen) = params.mask_gen_algorithm() {
println!(
"{}/{}",
format_oid(&mask_gen.mgf),
format_oid(&mask_gen.hash),
);
} else {
println!("INVALID");
}
println!(
"{}pSourceFunc: {}",
indent_s,
format_oid(&params.p_source_alg().algorithm),
);
}
}
}
Err(e) => {
eprintln!("Could not parse signature algorithm: {e}");
println!(" Signature Algorithm:");
print_x509_digest_algorithm(signature_algorithm, indent);
}
}
}
fn print_x509_ski(public_key: &SubjectPublicKeyInfo) {
println!(" Public Key Algorithm:");
print_x509_digest_algorithm(&public_key.algorithm, 6);
match public_key.parsed() {
Ok(PublicKey::RSA(rsa)) => {
println!(" RSA Public Key: ({} bit)", rsa.key_size());
// print_hex_dump(rsa.modulus, 1024);
for l in format_number_to_hex_with_colon(rsa.modulus, 16) {
println!(" {l}");
}
if let Ok(e) = rsa.try_exponent() {
println!(" exponent: 0x{e:x} ({e})");
} else {
println!(" exponent: <INVALID>:");
print_hex_dump(rsa.exponent, 32);
}
}
Ok(PublicKey::EC(ec)) => {
println!(" EC Public Key: ({} bit)", ec.key_size());
for l in format_number_to_hex_with_colon(ec.data(), 16) {
println!(" {l}");
}
// // identify curve
// if let Some(params) = &public_key.algorithm.parameters {
// let curve_oid = params.as_oid();
// let curve = curve_oid
// .map(|oid| {
// oid_registry()
// .get(oid)
// .map(|entry| entry.sn())
// .unwrap_or("<UNKNOWN>")
// })
// .unwrap_or("<ERROR: NOT AN OID>");
// println!(" Curve: {}", curve);
// }
}
Ok(PublicKey::DSA(y)) => {
println!(" DSA Public Key: ({} bit)", 8 * y.len());
for l in format_number_to_hex_with_colon(y, 16) {
println!(" {l}");
}
}
Ok(PublicKey::GostR3410(y)) => {
println!(" GOST R 34.10-94 Public Key: ({} bit)", 8 * y.len());
for l in format_number_to_hex_with_colon(y, 16) {
println!(" {l}");
}
}
Ok(PublicKey::GostR3410_2012(y)) => {
println!(" GOST R 34.10-2012 Public Key: ({} bit)", 8 * y.len());
for l in format_number_to_hex_with_colon(y, 16) {
println!(" {l}");
}
}
Ok(PublicKey::Unknown(b)) => {
println!(" Unknown key type");
print_hex_dump(b, 256);
if let Ok((rem, res)) = der_parser::parse_der(b) {
eprintln!("rem: {} bytes", rem.len());
eprintln!("{res:?}");
} else {
eprintln!(" <Could not parse key as DER>");
}
}
Err(_) => {
println!(" INVALID PUBLIC KEY");
}
}
// dbg!(&public_key);
// todo!();
}
fn format_number_to_hex_with_colon(b: &[u8], row_size: usize) -> Vec<String> {
let mut v = Vec::with_capacity(1 + b.len() / row_size);
for r in b.chunks(row_size) {
let s = r.iter().fold(String::with_capacity(3 * r.len()), |a, b| {
a + &format!("{b:02x}:")
});
v.push(s)
}
v
}
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)) {
// probably DER
handle_certificate(&file_name, &data)?;
} else {
// try as PEM
for (n, pem) in Pem::iter_from_buffer(&data).enumerate() {
match pem {
Ok(pem) => {
let data = &pem.contents;
println!("Certificate [{n}]");
handle_certificate(&file_name, data)?;
}
Err(e) => {
eprintln!("Error while decoding PEM entry {n}: {e}");
}
}
}
}
}
Ok(())
}

154
vendor/x509-parser/examples/print-crl.rs vendored Normal file
View File

@@ -0,0 +1,154 @@
use der_parser::oid::Oid;
use nom::HexDisplay;
use std::cmp::min;
use std::env;
use std::io;
use x509_parser::prelude::*;
fn print_hex_dump(bytes: &[u8], max_len: usize) {
let m = min(bytes.len(), max_len);
print!("{}", &bytes[..m].to_hex(16));
if bytes.len() > max_len {
println!("... <continued>");
}
}
fn format_oid(oid: &Oid) -> String {
match oid2sn(oid, oid_registry()) {
Ok(s) => s.to_owned(),
_ => format!("{oid}"),
}
}
fn print_authority_key_identifier(aki: &AuthorityKeyIdentifier, level: usize) {
if let Some(id) = &aki.key_identifier {
println!("{:indent$}keyid: {:x}", "", id, indent = level);
}
if aki.authority_cert_issuer.is_some() {
unimplemented!();
}
if let Some(serial) = aki.authority_cert_serial {
let s = format_serial(serial);
println!("{:indent$}serial: {}", "", &s, indent = level);
}
}
fn print_x509_extension(oid: &Oid, ext: &X509Extension, level: usize) {
match ext.parsed_extension() {
ParsedExtension::CRLNumber(num) => {
println!("{:indent$}X509v3 CRL Number: {}", "", num, indent = level);
}
ParsedExtension::ReasonCode(code) => {
println!(
"{:indent$}X509v3 CRL Reason Code: {}",
"",
code,
indent = level
);
}
ParsedExtension::InvalidityDate(date) => {
println!("{:indent$}Invalidity Date: {}", "", date, indent = level);
}
ParsedExtension::AuthorityKeyIdentifier(aki) => {
println!(
"{:indent$}X509v3 Authority Key Identifier:",
"",
indent = level
);
print_authority_key_identifier(aki, level + 2);
}
x => {
print!("{:indent$}{}:", "", format_oid(oid), indent = level);
print!(" Critical={}", ext.critical);
print!(" len={}", ext.value.len());
println!();
println!(" {:indent$}{:?}", "", x, indent = level);
}
}
}
fn print_x509_digest_algorithm(alg: &AlgorithmIdentifier, level: usize) {
println!(
"{:indent$}Oid: {}",
"",
format_oid(&alg.algorithm),
indent = level
);
if let Some(parameter) = &alg.parameters {
println!(
"{:indent$}Parameter: <PRESENT> {:?}",
"",
parameter.tag(),
indent = level
);
let bytes = parameter.as_bytes();
print_hex_dump(bytes, 32);
} else {
println!("{:indent$}Parameter: <ABSENT>", "", indent = level);
}
}
fn print_revoked_certificate(revoked: &RevokedCertificate, level: usize) {
println!(
"{:indent$}Serial number: {}",
"",
revoked.raw_serial_as_string(),
indent = level
);
println!(
"{:indent$}Revocation Date: {}",
"",
revoked.revocation_date,
indent = level + 2
);
println!("{:indent$}CRL Extensions:", "", indent = level + 2);
for ext in revoked.extensions() {
print_x509_extension(&ext.oid, ext, level + 4);
}
}
fn print_crl_info(crl: &CertificateRevocationList) {
println!(" Version: {}", crl.version().unwrap_or(X509Version(0)));
// println!(" Subject: {}", crl.subject());
println!(" Signature Algorithm:");
print_x509_digest_algorithm(&crl.signature_algorithm, 4);
println!(" Issuer: {}", crl.issuer());
// println!(" Serial: {}", crl.tbs_certificate.raw_serial_as_string());
println!(" Last Update: {}", crl.last_update());
println!(
" Next Update: {}",
crl.next_update()
.map_or_else(|| "NONE".to_string(), |d| d.to_string())
);
println!("{:indent$}CRL Extensions:", "", indent = 2);
for ext in crl.extensions() {
print_x509_extension(&ext.oid, ext, 4);
}
println!(" Revoked certificates:");
for revoked in crl.iter_revoked_certificates() {
print_revoked_certificate(revoked, 4);
}
println!();
}
pub fn main() -> io::Result<()> {
for file_name in env::args().skip(1) {
// placeholder to store decoded PEM data, if needed
let tmpdata;
println!("File: {file_name}");
let data = std::fs::read(file_name.clone()).expect("Unable to read file");
let der_data: &[u8] = if (data[0], data[1]) == (0x30, 0x82) {
// probably DER
&data
} else {
// try as PEM
let (_, data) = parse_x509_pem(&data).expect("Could not decode the PEM file");
tmpdata = data;
&tmpdata.contents
};
let (_, crl) = parse_x509_crl(der_data).expect("Could not decode DER data");
print_crl_info(&crl);
}
Ok(())
}

820
vendor/x509-parser/src/certificate.rs vendored Normal file
View File

@@ -0,0 +1,820 @@
//! X.509 Certificate object definitions and operations
use crate::error::{X509Error, X509Result};
use crate::extensions::*;
use crate::time::ASN1Time;
use crate::utils::format_serial;
#[cfg(feature = "validate")]
use crate::validate::*;
use crate::x509::{
parse_serial, parse_signature_value, AlgorithmIdentifier, SubjectPublicKeyInfo, X509Name,
X509Version,
};
#[cfg(any(feature = "verify", feature = "verify-aws"))]
use crate::verify::verify_signature;
use asn1_rs::{BitString, FromDer, OptTaggedImplicit};
use core::ops::Deref;
use der_parser::der::*;
use der_parser::error::*;
use der_parser::num_bigint::BigUint;
use der_parser::*;
use nom::{Offset, Parser};
use oid_registry::*;
use std::collections::HashMap;
use time::Duration;
/// An X.509 v3 Certificate.
///
/// X.509 v3 certificates are defined in [RFC5280](https://tools.ietf.org/html/rfc5280), section
/// 4.1. This object uses the same structure for content, so for ex the subject can be accessed
/// using the path `x509.tbs_certificate.subject`.
///
/// `X509Certificate` also contains convenience methods to access the most common fields (subject,
/// issuer, etc.). These are provided using `Deref<Target = TbsCertificate>`, so documentation for
/// these methods can be found in the [`TbsCertificate`] object.
///
/// A `X509Certificate` is a zero-copy view over a buffer, so the lifetime is the same as the
/// buffer containing the binary representation.
///
/// ```rust
/// # use x509_parser::prelude::FromDer;
/// # use x509_parser::certificate::X509Certificate;
/// #
/// # static DER: &'static [u8] = include_bytes!("../assets/IGC_A.der");
/// #
/// fn display_x509_info(x509: &X509Certificate<'_>) {
/// let subject = x509.subject();
/// let issuer = x509.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);
/// # match res {
/// # Ok((_rem, x509)) => {
/// # display_x509_info(&x509);
/// # },
/// # _ => panic!("x509 parsing failed: {:?}", res),
/// # }
/// # }
/// ```
#[derive(Clone, Debug, PartialEq)]
pub struct X509Certificate<'a> {
pub tbs_certificate: TbsCertificate<'a>,
pub signature_algorithm: AlgorithmIdentifier<'a>,
pub signature_value: BitString<'a>,
/// Complete raw ASN.1 DER content (TBS certificate, signature algorithm and signature).
pub(crate) raw: &'a [u8],
}
impl<'a> X509Certificate<'a> {
/// Return the raw ASN.1 DER content of the complete signed certificate that was parsed.
///
/// This includes the to-be-signed (TBS) certificate, the signature algorithm, and the signature.
/// If you want just the ASN.1 DER of the TBS certificate, prefer [`TbsCertificate::as_ref()`].
///
/// We avoid the `AsRef` trait in this instance to ensure the full lifetime of the `X509Certificate` is used.
pub fn as_raw(&self) -> &'a [u8] {
self.raw
}
/// Verify the cryptographic signature of this certificate
///
/// `public_key` is the public key of the **signer**. For a self-signed certificate,
/// (for ex. a public root certificate authority), this is the key from the certificate,
/// so you can use `None`.
///
/// For a leaf certificate, this is the public key of the certificate that signed it.
/// It is usually an intermediate authority.
///
/// Not all algorithms are supported, this function is limited to what `ring` supports.
#[cfg(any(feature = "verify", feature = "verify-aws"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "verify", feature = "verify-aws"))))]
pub fn verify_signature(
&self,
public_key: Option<&SubjectPublicKeyInfo>,
) -> Result<(), X509Error> {
let spki = public_key.unwrap_or_else(|| self.public_key());
verify_signature(
spki,
&self.signature_algorithm,
&self.signature_value,
self.tbs_certificate.raw,
)
}
}
impl<'a> AsRef<[u8]> for X509Certificate<'a> {
fn as_ref(&self) -> &[u8] {
self.as_raw()
}
}
impl<'a> Deref for X509Certificate<'a> {
type Target = TbsCertificate<'a>;
fn deref(&self) -> &Self::Target {
&self.tbs_certificate
}
}
impl<'a> FromDer<'a, X509Error> 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<'a, 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)]
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 Default for X509CertificateParser {
fn default() -> Self {
X509CertificateParser::new()
}
}
impl<'a> Parser<&'a [u8], X509Certificate<'a>, X509Error> for X509CertificateParser {
fn parse(&mut self, input: &'a [u8]) -> IResult<&'a [u8], X509Certificate<'a>, X509Error> {
let start_i = input;
let (rem, mut cert) = 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,
raw: &[],
};
Ok((i, cert))
})(input)?;
let len = start_i.offset(rem);
cert.raw = &start_i[..len];
Ok((rem, cert))
}
}
#[allow(deprecated)]
#[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),
{
X509StructureValidator.validate(self, &mut CallbackLogger::new(warn, err))
}
}
/// The sequence `TBSCertificate` contains information associated with the
/// subject of the certificate and the CA that issued it.
///
/// RFC5280 definition:
///
/// <pre>
/// TBSCertificate ::= SEQUENCE {
/// version [0] EXPLICIT Version DEFAULT v1,
/// serialNumber CertificateSerialNumber,
/// signature AlgorithmIdentifier,
/// issuer Name,
/// validity Validity,
/// subject Name,
/// subjectPublicKeyInfo SubjectPublicKeyInfo,
/// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
/// -- If present, version MUST be v2 or v3
/// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
/// -- If present, version MUST be v2 or v3
/// extensions [3] EXPLICIT Extensions OPTIONAL
/// -- If present, version MUST be v3
/// }
/// </pre>
#[derive(Clone, Debug, PartialEq)]
pub struct TbsCertificate<'a> {
pub version: X509Version,
pub serial: BigUint,
pub signature: AlgorithmIdentifier<'a>,
pub issuer: X509Name<'a>,
pub validity: Validity,
pub subject: X509Name<'a>,
pub subject_pki: SubjectPublicKeyInfo<'a>,
pub issuer_uid: Option<UniqueIdentifier<'a>>,
pub subject_uid: Option<UniqueIdentifier<'a>>,
extensions: Vec<X509Extension<'a>>,
pub(crate) raw: &'a [u8],
pub(crate) raw_serial: &'a [u8],
}
impl<'a> TbsCertificate<'a> {
/// Get the version of the encoded certificate
pub fn version(&self) -> X509Version {
self.version
}
/// Get the certificate subject.
#[inline]
pub fn subject(&self) -> &X509Name<'_> {
&self.subject
}
/// Get the certificate issuer.
#[inline]
pub fn issuer(&self) -> &X509Name<'_> {
&self.issuer
}
/// Get the certificate validity.
#[inline]
pub fn validity(&self) -> &Validity {
&self.validity
}
/// Get the certificate public key information.
#[inline]
pub fn public_key(&self) -> &SubjectPublicKeyInfo<'_> {
&self.subject_pki
}
/// Returns the certificate extensions
#[inline]
pub fn extensions(&self) -> &[X509Extension<'a>] {
&self.extensions
}
/// Returns an iterator over the certificate extensions
#[inline]
pub fn iter_extensions(&self) -> impl Iterator<Item = &X509Extension<'a>> {
self.extensions.iter()
}
/// Searches for an extension with the given `Oid`.
///
/// Return `Ok(Some(extension))` if exactly one was found, `Ok(None)` if none was found,
/// or an error `DuplicateExtensions` if the extension is present twice or more.
#[inline]
pub fn get_extension_unique(&self, oid: &Oid) -> Result<Option<&X509Extension<'a>>, X509Error> {
get_extension_unique(&self.extensions, oid)
}
/// Searches for an extension with the given `Oid`.
///
/// ## Duplicate extensions
///
/// Note: if there are several extensions with the same `Oid`, the first one is returned, masking other values.
///
/// RFC5280 forbids having duplicate extensions, but does not specify how errors should be handled.
///
/// **Because of this, the `find_extension` method is not safe and should not be used!**
/// The [`get_extension_unique`](Self::get_extension_unique) method checks for duplicate extensions and should be
/// preferred.
#[deprecated(
since = "0.13.0",
note = "Do not use this function (duplicate extensions are not checked), use `get_extension_unique`"
)]
pub fn find_extension(&self, oid: &Oid) -> Option<&X509Extension<'a>> {
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<'a>>, 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)
})
}
/// Attempt to get the certificate Basic Constraints extension
///
/// Return `Ok(Some(extension))` if exactly one was found, `Ok(None)` if none was found,
/// or an error if the extension is present twice or more.
pub fn basic_constraints(
&self,
) -> Result<Option<BasicExtension<&BasicConstraints>>, X509Error> {
let r = self
.get_extension_unique(&OID_X509_EXT_BASIC_CONSTRAINTS)?
.and_then(|ext| match ext.parsed_extension {
ParsedExtension::BasicConstraints(ref bc) => {
Some(BasicExtension::new(ext.critical, bc))
}
_ => None,
});
Ok(r)
}
/// Attempt to get the certificate Key Usage extension
///
/// Return `Ok(Some(extension))` if exactly one was found, `Ok(None)` if none was found,
/// or an error if the extension is invalid, or is present twice or more.
pub fn key_usage(&self) -> Result<Option<BasicExtension<&KeyUsage>>, X509Error> {
self.get_extension_unique(&OID_X509_EXT_KEY_USAGE)?
.map_or(Ok(None), |ext| match ext.parsed_extension {
ParsedExtension::KeyUsage(ref value) => {
Ok(Some(BasicExtension::new(ext.critical, value)))
}
_ => Err(X509Error::InvalidExtensions),
})
}
/// Attempt to get the certificate Extended Key Usage extension
///
/// Return `Ok(Some(extension))` if exactly one was found, `Ok(None)` if none was found,
/// or an error if the extension is invalid, or is present twice or more.
pub fn extended_key_usage(
&self,
) -> Result<Option<BasicExtension<&ExtendedKeyUsage<'_>>>, X509Error> {
self.get_extension_unique(&OID_X509_EXT_EXTENDED_KEY_USAGE)?
.map_or(Ok(None), |ext| match ext.parsed_extension {
ParsedExtension::ExtendedKeyUsage(ref value) => {
Ok(Some(BasicExtension::new(ext.critical, value)))
}
_ => Err(X509Error::InvalidExtensions),
})
}
/// Attempt to get the certificate Policy Constraints extension
///
/// Return `Ok(Some(extension))` if exactly one was found, `Ok(None)` if none was found,
/// or an error if the extension is invalid, or is present twice or more.
pub fn policy_constraints(
&self,
) -> Result<Option<BasicExtension<&PolicyConstraints>>, X509Error> {
self.get_extension_unique(&OID_X509_EXT_POLICY_CONSTRAINTS)?
.map_or(Ok(None), |ext| match ext.parsed_extension {
ParsedExtension::PolicyConstraints(ref value) => {
Ok(Some(BasicExtension::new(ext.critical, value)))
}
_ => Err(X509Error::InvalidExtensions),
})
}
/// Attempt to get the certificate Policy Constraints extension
///
/// Return `Ok(Some(extension))` if exactly one was found, `Ok(None)` if none was found,
/// or an error if the extension is invalid, or is present twice or more.
pub fn inhibit_anypolicy(
&self,
) -> Result<Option<BasicExtension<&InhibitAnyPolicy>>, X509Error> {
self.get_extension_unique(&OID_X509_EXT_INHIBIT_ANY_POLICY)?
.map_or(Ok(None), |ext| match ext.parsed_extension {
ParsedExtension::InhibitAnyPolicy(ref value) => {
Ok(Some(BasicExtension::new(ext.critical, value)))
}
_ => Err(X509Error::InvalidExtensions),
})
}
/// Attempt to get the certificate Policy Mappings extension
///
/// Return `Ok(Some(extension))` if exactly one was found, `Ok(None)` if none was found,
/// or an error if the extension is invalid, or is present twice or more.
pub fn policy_mappings(
&self,
) -> Result<Option<BasicExtension<&PolicyMappings<'_>>>, X509Error> {
self.get_extension_unique(&OID_X509_EXT_POLICY_MAPPINGS)?
.map_or(Ok(None), |ext| match ext.parsed_extension {
ParsedExtension::PolicyMappings(ref value) => {
Ok(Some(BasicExtension::new(ext.critical, value)))
}
_ => Err(X509Error::InvalidExtensions),
})
}
/// Attempt to get the certificate Subject Alternative Name extension
///
/// Return `Ok(Some(extension))` if exactly one was found, `Ok(None)` if none was found,
/// or an error if the extension is invalid, or is present twice or more.
pub fn subject_alternative_name(
&self,
) -> Result<Option<BasicExtension<&SubjectAlternativeName<'a>>>, X509Error> {
self.get_extension_unique(&OID_X509_EXT_SUBJECT_ALT_NAME)?
.map_or(Ok(None), |ext| match ext.parsed_extension {
ParsedExtension::SubjectAlternativeName(ref value) => {
Ok(Some(BasicExtension::new(ext.critical, value)))
}
_ => Err(X509Error::InvalidExtensions),
})
}
/// Attempt to get the certificate Name Constraints extension
///
/// Return `Ok(Some(extension))` if exactly one was found, `Ok(None)` if none was found,
/// or an error if the extension is invalid, or is present twice or more.
pub fn name_constraints(
&self,
) -> Result<Option<BasicExtension<&NameConstraints<'_>>>, X509Error> {
self.get_extension_unique(&OID_X509_EXT_NAME_CONSTRAINTS)?
.map_or(Ok(None), |ext| match ext.parsed_extension {
ParsedExtension::NameConstraints(ref value) => {
Ok(Some(BasicExtension::new(ext.critical, value)))
}
_ => Err(X509Error::InvalidExtensions),
})
}
/// Returns true if certificate has `basicConstraints CA:true`
pub fn is_ca(&self) -> bool {
self.basic_constraints()
.unwrap_or(None)
.map(|ext| ext.value.ca)
.unwrap_or(false)
}
/// Get the raw bytes of the certificate serial number
pub fn raw_serial(&self) -> &'a [u8] {
self.raw_serial
}
/// Get a formatted string of the certificate serial number, separated by ':'
pub fn raw_serial_as_string(&self) -> String {
format_serial(self.raw_serial)
}
}
/// Searches for an extension with the given `Oid`.
///
/// Note: if there are several extensions with the same `Oid`, an error `DuplicateExtensions` is returned.
fn get_extension_unique<'a, 'b>(
extensions: &'a [X509Extension<'b>],
oid: &Oid,
) -> Result<Option<&'a X509Extension<'b>>, X509Error> {
let mut res = None;
for ext in extensions {
if ext.oid == *oid {
if res.is_some() {
return Err(X509Error::DuplicateExtensions);
}
res = Some(ext);
}
}
Ok(res)
}
impl AsRef<[u8]> for TbsCertificate<'_> {
#[inline]
fn as_ref(&self) -> &[u8] {
self.raw
}
}
impl<'a> FromDer<'a, X509Error> for TbsCertificate<'a> {
/// Parse a DER-encoded TbsCertificate object
///
/// <pre>
/// TBSCertificate ::= SEQUENCE {
/// version [0] Version DEFAULT v1,
/// serialNumber CertificateSerialNumber,
/// signature AlgorithmIdentifier,
/// issuer Name,
/// validity Validity,
/// subject Name,
/// subjectPublicKeyInfo SubjectPublicKeyInfo,
/// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
/// -- If present, version MUST be v2 or v3
/// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
/// -- If present, version MUST be v2 or v3
/// extensions [3] Extensions OPTIONAL
/// -- If present, version MUST be v3 -- }
/// </pre>
fn from_der(i: &'a [u8]) -> X509Result<'a, TbsCertificate<'a>> {
let start_i = i;
parse_der_sequence_defined_g(move |i, _| {
let (i, version) = X509Version::from_der_tagged_0(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) = parse_extensions(i, Tag(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))
})(i)
}
}
/// `TbsCertificate` parser builder
#[derive(Clone, Copy, Debug)]
pub struct TbsCertificateParser {
deep_parse_extensions: bool,
}
impl TbsCertificateParser {
#[inline]
pub const fn new() -> Self {
TbsCertificateParser {
deep_parse_extensions: true,
}
}
#[inline]
pub const fn with_deep_parse_extensions(self, deep_parse_extensions: bool) -> Self {
TbsCertificateParser {
deep_parse_extensions,
}
}
}
impl Default for TbsCertificateParser {
fn default() -> Self {
TbsCertificateParser::new()
}
}
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_tagged_0(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, Tag(3))?
} else {
parse_extensions_envelope(i, Tag(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)
}
}
#[allow(deprecated)]
#[cfg(feature = "validate")]
#[cfg_attr(docsrs, doc(cfg(feature = "validate")))]
impl Validate for TbsCertificate<'_> {
fn validate<W, E>(&self, warn: W, err: E) -> bool
where
W: FnMut(&str),
E: FnMut(&str),
{
TbsCertificateStructureValidator.validate(self, &mut CallbackLogger::new(warn, err))
}
}
/// Basic extension structure, used in search results
#[derive(Debug, PartialEq, Eq)]
pub struct BasicExtension<T> {
pub critical: bool,
pub value: T,
}
impl<T> BasicExtension<T> {
pub const fn new(critical: bool, value: T) -> Self {
Self { critical, value }
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Validity {
pub not_before: ASN1Time,
pub not_after: ASN1Time,
}
impl Validity {
/// The time left before the certificate expires.
///
/// If the certificate is not currently valid, then `None` is
/// returned. Otherwise, the `Duration` until the certificate
/// expires is returned.
pub fn time_to_expiration(&self) -> Option<Duration> {
let now = ASN1Time::now();
if !self.is_valid_at(now) {
return None;
}
// Note that the duration below is guaranteed to be positive,
// since we just checked that now < na
self.not_after - now
}
/// 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
}
/// Check the certificate time validity
#[inline]
pub fn is_valid(&self) -> bool {
self.is_valid_at(ASN1Time::now())
}
}
impl FromDer<'_, X509Error> 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, Eq)]
pub struct UniqueIdentifier<'a>(pub BitString<'a>);
impl<'a> UniqueIdentifier<'a> {
// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL
fn from_der_issuer(i: &'a [u8]) -> X509Result<'a, Option<Self>> {
Self::parse::<1>(i).map_err(|_| X509Error::InvalidIssuerUID.into())
}
// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL
fn from_der_subject(i: &[u8]) -> X509Result<'_, Option<UniqueIdentifier<'_>>> {
Self::parse::<2>(i).map_err(|_| X509Error::InvalidSubjectUID.into())
}
// Parse a [tag] UniqueIdentifier OPTIONAL
//
// UniqueIdentifier ::= BIT STRING
fn parse<const TAG: u32>(i: &[u8]) -> BerResult<'_, Option<UniqueIdentifier<'_>>> {
let (rem, unique_id) = OptTaggedImplicit::<BitString, Error, TAG>::from_der(i)?;
let unique_id = unique_id.map(|u| UniqueIdentifier(u.into_inner()));
Ok((rem, unique_id))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn check_validity_expiration() {
let mut v = Validity {
not_before: ASN1Time::now(),
not_after: ASN1Time::now(),
};
assert_eq!(v.time_to_expiration(), None);
v.not_after = (v.not_after + Duration::new(60, 0)).unwrap();
assert!(v.time_to_expiration().is_some());
assert!(v.time_to_expiration().unwrap() <= Duration::new(60, 0));
// The following assumes this timing won't take 10 seconds... I
// think that is safe.
assert!(v.time_to_expiration().unwrap() > Duration::new(50, 0));
}
#[test]
fn extension_duplication() {
let extensions = vec![
X509Extension::new(oid! {1.2}, true, &[], ParsedExtension::Unparsed),
X509Extension::new(oid! {1.3}, true, &[], ParsedExtension::Unparsed),
X509Extension::new(oid! {1.2}, true, &[], ParsedExtension::Unparsed),
X509Extension::new(oid! {1.4}, true, &[], ParsedExtension::Unparsed),
X509Extension::new(oid! {1.4}, true, &[], ParsedExtension::Unparsed),
];
let r2 = get_extension_unique(&extensions, &oid! {1.2});
assert!(r2.is_err());
let r3 = get_extension_unique(&extensions, &oid! {1.3});
assert!(r3.is_ok());
let r4 = get_extension_unique(&extensions, &oid! {1.4});
assert!(r4.is_err());
}
}

View File

@@ -0,0 +1,189 @@
use crate::cri_attributes::*;
use crate::error::{X509Error, X509Result};
use crate::extensions::*;
use crate::x509::{
parse_signature_value, AlgorithmIdentifier, SubjectPublicKeyInfo, X509Name, X509Version,
};
#[cfg(any(feature = "verify", feature = "verify-aws"))]
use crate::verify::verify_signature;
use asn1_rs::{BitString, FromDer};
use der_parser::der::*;
use der_parser::*;
use nom::Offset;
use std::collections::HashMap;
/// Certification Signing Request (CSR)
#[derive(Debug, PartialEq)]
pub struct X509CertificationRequest<'a> {
pub certification_request_info: X509CertificationRequestInfo<'a>,
pub signature_algorithm: AlgorithmIdentifier<'a>,
pub signature_value: BitString<'a>,
/// Complete raw ASN.1 DER content (request info, signature algorithm and signature).
pub(crate) raw: &'a [u8],
}
impl<'a> X509CertificationRequest<'a> {
/// Return the raw ASN.1 DER content of the complete signed certification request that was parsed.
///
/// This includes the certification request info, the signature algorithm, and the signature.
///
/// We avoid the `AsRef` trait in this instance to ensure the full lifetime of the `X509CertificationRequest` is used.
pub fn as_raw(&self) -> &'a [u8] {
self.raw
}
pub fn requested_extensions(&self) -> Option<impl Iterator<Item = &ParsedExtension<'_>>> {
self.certification_request_info
.iter_attributes()
.find_map(|attr| {
if let ParsedCriAttribute::ExtensionRequest(requested) = &attr.parsed_attribute {
Some(requested.extensions.iter().map(|ext| &ext.parsed_extension))
} else {
None
}
})
}
/// Verify the cryptographic signature of this certification request
///
/// Uses the public key contained in the CSR, which must be the one of the entity
/// requesting the certification for this verification to succeed.
#[cfg(any(feature = "verify", feature = "verify-aws"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "verify", feature = "verify-aws"))))]
pub fn verify_signature(&self) -> Result<(), X509Error> {
let spki = &self.certification_request_info.subject_pki;
verify_signature(
spki,
&self.signature_algorithm,
&self.signature_value,
self.certification_request_info.raw,
)
}
}
impl<'a> AsRef<[u8]> for X509CertificationRequest<'a> {
fn as_ref(&self) -> &[u8] {
self.as_raw()
}
}
/// <pre>
/// CertificationRequest ::= SEQUENCE {
/// certificationRequestInfo CertificationRequestInfo,
/// signatureAlgorithm AlgorithmIdentifier{{ SignatureAlgorithms }},
/// signature BIT STRING
/// }
/// </pre>
impl<'a> FromDer<'a, X509Error> for X509CertificationRequest<'a> {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
let start_i = i;
let (rem, mut req) = 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,
raw: &[],
};
Ok((i, cert))
})(i)?;
let len = start_i.offset(rem);
req.raw = &start_i[..len];
Ok((rem, req))
}
}
/// 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 raw: &'a [u8],
}
impl X509CertificationRequestInfo<'_> {
/// 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`.
///
/// 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.
///
/// 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, X509Error> for X509CertificationRequestInfo<'a> {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
let start_i = i;
parse_der_sequence_defined_g(move |i, _| {
let (i, version) = X509Version::from_der(i)?;
let (i, subject) = X509Name::from_der(i)?;
let (i, subject_pki) = SubjectPublicKeyInfo::from_der(i)?;
let (i, attributes) = parse_cri_attributes(i)?;
let len = start_i.offset(i);
let tbs = X509CertificationRequestInfo {
version,
subject,
subject_pki,
attributes,
raw: &start_i[..len],
};
Ok((i, tbs))
})(i)
}
}

179
vendor/x509-parser/src/cri_attributes.rs vendored Normal file
View File

@@ -0,0 +1,179 @@
use crate::{
error::{X509Error, X509Result},
extensions::X509Extension,
};
use asn1_rs::{Error, FromDer, Header, Oid, Sequence, Tag};
use nom::combinator::{all_consuming, complete};
use nom::multi::many0;
use nom::Err;
use oid_registry::*;
use std::collections::HashMap;
/// Attributes for Certification Request
#[derive(Clone, Debug, PartialEq)]
pub struct X509CriAttribute<'a> {
pub oid: Oid<'a>,
pub value: &'a [u8],
pub(crate) parsed_attribute: ParsedCriAttribute<'a>,
}
impl<'a> FromDer<'a, X509Error> for X509CriAttribute<'a> {
fn from_der(i: &'a [u8]) -> X509Result<'a, X509CriAttribute<'a>> {
Sequence::from_ber_and_then(i, |i| {
let (i, oid) = Oid::from_der(i)?;
let value_start = i;
let (i, hdr) = Header::from_der(i)?;
if hdr.tag() != Tag::Set {
return Err(Err::Error(Error::BerTypeError));
};
let (i, parsed_attribute) =
parser::parse_attribute(i, &oid).map_err(|_| Err::Error(Error::BerValueError))?;
let attribute = X509CriAttribute {
oid,
value: &value_start[..value_start.len() - i.len()],
parsed_attribute,
};
Ok((i, attribute))
})
.map_err(|_| X509Error::InvalidAttributes.into())
}
}
impl<'a> X509CriAttribute<'a> {
/// Return the attribute type or `UnsupportedAttribute` if the attribute is unknown.
#[inline]
pub fn parsed_attribute(&self) -> &ParsedCriAttribute<'a> {
&self.parsed_attribute
}
}
/// Section 3.1 of rfc 5272
#[derive(Clone, Debug, PartialEq)]
pub struct ExtensionRequest<'a> {
pub extensions: Vec<X509Extension<'a>>,
}
impl<'a> FromDer<'a, X509Error> for ExtensionRequest<'a> {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
parser::parse_extension_request(i).map_err(Err::convert)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ChallengePassword(pub String);
/// Attributes for Certification Request
#[derive(Clone, Debug, PartialEq)]
pub enum ParsedCriAttribute<'a> {
ChallengePassword(ChallengePassword),
ExtensionRequest(ExtensionRequest<'a>),
UnsupportedAttribute,
}
pub(crate) mod parser {
use crate::cri_attributes::*;
use der_parser::der::{
parse_der_bmpstring, parse_der_printablestring, parse_der_t61string,
parse_der_universalstring, parse_der_utf8string,
};
use lazy_static::lazy_static;
use nom::branch::alt;
use nom::combinator::map;
type AttrParser = fn(&[u8]) -> X509Result<ParsedCriAttribute>;
lazy_static! {
static ref ATTRIBUTE_PARSERS: HashMap<Oid<'static>, AttrParser> = {
macro_rules! add {
($m:ident, $oid:ident, $p:ident) => {
$m.insert($oid, $p as AttrParser);
};
}
let mut m = HashMap::new();
add!(m, OID_PKCS9_EXTENSION_REQUEST, parse_extension_request_attr);
add!(
m,
OID_PKCS9_CHALLENGE_PASSWORD,
parse_challenge_password_attr
);
m
};
}
// look into the parser map if the extension is known, and parse it
// otherwise, leave it as UnsupportedExtension
pub(crate) fn parse_attribute<'a>(
i: &'a [u8],
oid: &Oid,
) -> X509Result<'a, ParsedCriAttribute<'a>> {
if let Some(parser) = ATTRIBUTE_PARSERS.get(oid) {
parser(i)
} else {
Ok((i, ParsedCriAttribute::UnsupportedAttribute))
}
}
pub(super) fn parse_extension_request(i: &[u8]) -> X509Result<'_, ExtensionRequest<'_>> {
crate::extensions::parse_extension_sequence(i)
.map(|(i, extensions)| (i, ExtensionRequest { extensions }))
}
fn parse_extension_request_attr(i: &[u8]) -> X509Result<'_, ParsedCriAttribute<'_>> {
map(
parse_extension_request,
ParsedCriAttribute::ExtensionRequest,
)(i)
}
// RFC 2985, 5.4.1 Challenge password
// challengePassword ATTRIBUTE ::= {
// WITH SYNTAX DirectoryString {pkcs-9-ub-challengePassword}
// EQUALITY MATCHING RULE caseExactMatch
// SINGLE VALUE TRUE
// ID pkcs-9-at-challengePassword
// }
// RFC 5280, 4.1.2.4. Issuer
// DirectoryString ::= CHOICE {
// teletexString TeletexString (SIZE (1..MAX)),
// printableString PrintableString (SIZE (1..MAX)),
// universalString UniversalString (SIZE (1..MAX)),
// utf8String UTF8String (SIZE (1..MAX)),
// bmpString BMPString (SIZE (1..MAX))
// }
pub(super) fn parse_challenge_password(i: &[u8]) -> X509Result<'_, ChallengePassword> {
let (rem, obj) = match alt((
parse_der_utf8string,
parse_der_printablestring,
parse_der_universalstring,
parse_der_bmpstring,
parse_der_t61string, // == teletexString
))(i)
{
Ok((rem, obj)) => (rem, obj),
Err(_) => return Err(Err::Error(X509Error::InvalidAttributes)),
};
match obj.content.as_str() {
Ok(s) => Ok((rem, ChallengePassword(s.to_string()))),
Err(_) => Err(Err::Error(X509Error::InvalidAttributes)),
}
}
fn parse_challenge_password_attr(i: &[u8]) -> X509Result<'_, ParsedCriAttribute<'_>> {
map(
parse_challenge_password,
ParsedCriAttribute::ChallengePassword,
)(i)
}
}
pub(crate) fn parse_cri_attributes(i: &[u8]) -> X509Result<'_, Vec<X509CriAttribute<'_>>> {
let (i, hdr) = Header::from_der(i).map_err(|_| Err::Error(X509Error::InvalidAttributes))?;
if hdr.is_contextspecific() && hdr.tag().0 == 0 {
all_consuming(many0(complete(X509CriAttribute::from_der)))(i)
} else {
Err(Err::Error(X509Error::InvalidAttributes))
}
}

123
vendor/x509-parser/src/error.rs vendored Normal file
View File

@@ -0,0 +1,123 @@
//! X.509 errors
use der_parser::error::BerError;
use nom::error::{ErrorKind, ParseError};
use nom::IResult;
/// An error that can occur while converting an OID to a Nid.
#[derive(Debug, PartialEq, Eq)]
pub struct NidError;
/// Holds the result of parsing functions (X.509)
///
/// Note that this type is also a `Result`, so usual functions (`map`, `unwrap` etc.) are available.
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)]
pub enum X509Error {
#[error("generic error")]
Generic,
#[error("invalid version")]
InvalidVersion,
#[error("invalid serial")]
InvalidSerial,
#[error("invalid algorithm identifier")]
InvalidAlgorithmIdentifier,
#[error("invalid X.509 name")]
InvalidX509Name,
#[error("invalid date")]
InvalidDate,
#[error("invalid X.509 Subject Public Key Info")]
InvalidSPKI,
#[error("invalid X.509 Subject Unique ID")]
InvalidSubjectUID,
#[error("invalid X.509 Issuer Unique ID")]
InvalidIssuerUID,
#[error("invalid extensions")]
InvalidExtensions,
#[error("invalid attributes")]
InvalidAttributes,
#[error("duplicate extensions")]
DuplicateExtensions,
#[error("duplicate attributes")]
DuplicateAttributes,
#[error("invalid Signature DER Value")]
InvalidSignatureValue,
#[error("invalid TBS certificate")]
InvalidTbsCertificate,
// error types from CRL
#[error("invalid User certificate")]
InvalidUserCertificate,
/// Top-level certificate structure is invalid
#[error("invalid certificate")]
InvalidCertificate,
#[error("signature verification error")]
SignatureVerificationError,
#[error("signature unsupported algorithm")]
SignatureUnsupportedAlgorithm,
#[error("invalid number")]
InvalidNumber,
#[error("BER error: {0}")]
Der(#[from] BerError),
#[error("nom error: {0:?}")]
NomError(ErrorKind),
}
impl From<nom::Err<BerError>> for X509Error {
fn from(e: nom::Err<BerError>) -> Self {
Self::Der(BerError::from(e))
}
}
impl From<nom::Err<X509Error>> for X509Error {
fn from(e: nom::Err<X509Error>) -> Self {
match e {
nom::Err::Error(e) | nom::Err::Failure(e) => e,
nom::Err::Incomplete(i) => Self::Der(BerError::Incomplete(i)),
}
}
}
impl From<X509Error> for nom::Err<X509Error> {
fn from(e: X509Error) -> nom::Err<X509Error> {
nom::Err::Error(e)
}
}
impl From<ErrorKind> for X509Error {
fn from(e: ErrorKind) -> X509Error {
X509Error::NomError(e)
}
}
impl<I> ParseError<I> for X509Error {
fn from_error_kind(_input: I, kind: ErrorKind) -> Self {
X509Error::NomError(kind)
}
fn append(_input: I, kind: ErrorKind, _other: Self) -> Self {
X509Error::NomError(kind)
}
}
/// An error that can occur while parsing or validating a certificate.
#[derive(Debug, thiserror::Error)]
pub enum PEMError {
#[error("base64 decode error")]
Base64DecodeError,
#[error("incomplete PEM")]
IncompletePEM,
#[error("invalid header")]
InvalidHeader,
#[error("missing header")]
MissingHeader,
#[error("IO error: {0}")]
IOError(#[from] std::io::Error),
}

View File

@@ -0,0 +1,131 @@
use crate::error::{X509Error, X509Result};
use crate::prelude::format_serial;
use crate::x509::X509Name;
use asn1_rs::{Any, CheckDerConstraints, Class, Error, FromDer, Oid, Sequence, Tag};
use core::convert::TryFrom;
use nom::combinator::all_consuming;
use nom::{Err, IResult};
use std::fmt;
#[derive(Clone, Debug, PartialEq)]
/// Represents a GeneralName as defined in RFC5280. There
/// is no support X.400 addresses and EDIPartyName.
///
/// String formats are not validated (except for valid UTF-8).
pub enum GeneralName<'a> {
OtherName(Oid<'a>, &'a [u8]),
/// More or less an e-mail, the format is not checked.
RFC822Name(&'a str),
/// A hostname, format is not checked.
DNSName(&'a str),
/// X400Address,
X400Address(Any<'a>),
/// 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(Any<'a>),
/// An uniform resource identifier. The format is not checked.
URI(&'a str),
/// An ip address, provided as encoded.
IPAddress(&'a [u8]),
RegisteredID(Oid<'a>),
/// Invalid data (for ex. invalid UTF-8 data in DNSName entry)
Invalid(Tag, &'a [u8]),
}
impl<'a> TryFrom<Any<'a>> for GeneralName<'a> {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self, Self::Error> {
any.class().assert_eq(Class::ContextSpecific)?;
let name = match parse_generalname_entry(any.clone()) {
Ok(name) => name,
Err(_) => GeneralName::Invalid(any.tag(), any.data),
};
Ok(name)
}
}
fn parse_generalname_entry(
any: Any<'_>,
) -> Result<GeneralName<'_>, <GeneralName<'_> as TryFrom<Any<'_>>>::Error> {
Ok(match any.tag().0 {
0 => {
// otherName SEQUENCE { OID, [0] explicit any defined by oid }
let (rest, oid) = Oid::from_der(any.data)?;
GeneralName::OtherName(oid, rest)
}
1 => GeneralName::RFC822Name(ia5str(any)?),
2 => GeneralName::DNSName(ia5str(any)?),
3 => {
// XXX Not yet implemented
GeneralName::X400Address(any)
}
4 => {
// directoryName, name
let (_, name) = all_consuming(X509Name::from_der)(any.data)
.or(Err(Error::Unsupported)) // XXX remove me
?;
GeneralName::DirectoryName(name)
}
5 => {
// XXX Not yet implemented
GeneralName::EDIPartyName(any)
}
6 => GeneralName::URI(ia5str(any)?),
7 => {
// IPAddress, OctetString
GeneralName::IPAddress(any.data)
}
8 => {
let oid = Oid::new(any.data.into());
GeneralName::RegisteredID(oid)
}
_ => return Err(Error::unexpected_tag(None, any.tag())),
})
}
impl CheckDerConstraints for GeneralName<'_> {
fn check_constraints(any: &Any) -> asn1_rs::Result<()> {
Sequence::check_constraints(any)
}
}
impl<'a> FromDer<'a, X509Error> for GeneralName<'a> {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
parse_generalname(i).map_err(Err::convert)
}
}
impl fmt::Display for GeneralName<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
GeneralName::OtherName(oid, _) => write!(f, "OtherName({oid}, [...])"),
GeneralName::RFC822Name(s) => write!(f, "RFC822Name({s})"),
GeneralName::DNSName(s) => write!(f, "DNSName({s})"),
GeneralName::X400Address(_) => write!(f, "X400Address(<unparsed>)"),
GeneralName::DirectoryName(dn) => write!(f, "DirectoryName({dn})"),
GeneralName::EDIPartyName(_) => write!(f, "EDIPartyName(<unparsed>)"),
GeneralName::URI(s) => write!(f, "URI({s})"),
GeneralName::IPAddress(b) => write!(f, "IPAddress({})", format_serial(b)),
GeneralName::RegisteredID(oid) => write!(f, "RegisteredID({oid})"),
GeneralName::Invalid(tag, b) => {
write!(f, "Invalid(tag={}, data={})", tag, format_serial(b))
}
}
}
}
fn ia5str(any: Any<'_>) -> Result<&str, Err<Error>> {
// Relax constraints from RFC here: we are expecting an IA5String, but many certificates
// are using unicode characters
std::str::from_utf8(any.data).map_err(|_| Err::Failure(Error::BerValueError))
}
pub(crate) fn parse_generalname(i: &[u8]) -> IResult<&[u8], GeneralName<'_>, Error> {
let (rest, any) = Any::from_der(i)?;
let gn = GeneralName::try_from(any)?;
Ok((rest, gn))
}

View File

@@ -0,0 +1,150 @@
use crate::error::{X509Error, X509Result};
use asn1_rs::FromDer;
use der_parser::der::*;
use der_parser::error::BerError;
use der_parser::{oid, oid::Oid};
use nom::{Err, IResult};
use std::fmt;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct KeyUsage {
pub flags: u16,
}
impl KeyUsage {
pub fn digital_signature(&self) -> bool {
self.flags & 1 == 1
}
pub fn non_repudiation(&self) -> bool {
(self.flags >> 1) & 1u16 == 1
}
pub fn key_encipherment(&self) -> bool {
(self.flags >> 2) & 1u16 == 1
}
pub fn data_encipherment(&self) -> bool {
(self.flags >> 3) & 1u16 == 1
}
pub fn key_agreement(&self) -> bool {
(self.flags >> 4) & 1u16 == 1
}
pub fn key_cert_sign(&self) -> bool {
(self.flags >> 5) & 1u16 == 1
}
pub fn crl_sign(&self) -> bool {
(self.flags >> 6) & 1u16 == 1
}
pub fn encipher_only(&self) -> bool {
(self.flags >> 7) & 1u16 == 1
}
pub fn decipher_only(&self) -> bool {
(self.flags >> 8) & 1u16 == 1
}
}
// This list must have the same order as KeyUsage flags declaration (4.2.1.3)
const KEY_USAGE_FLAGS: &[&str] = &[
"Digital Signature",
"Non Repudiation",
"Key Encipherment",
"Data Encipherment",
"Key Agreement",
"Key Cert Sign",
"CRL Sign",
"Encipher Only",
"Decipher Only",
];
impl fmt::Display for KeyUsage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = KEY_USAGE_FLAGS
.iter()
.enumerate()
.fold(String::new(), |acc, (idx, s)| {
if (self.flags >> idx) & 1 != 0 {
acc + s + ", "
} else {
acc
}
});
s.pop();
s.pop();
f.write_str(&s)
}
}
impl<'a> FromDer<'a, X509Error> for KeyUsage {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
parse_keyusage(i).map_err(Err::convert)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ExtendedKeyUsage<'a> {
pub any: bool,
pub server_auth: bool,
pub client_auth: bool,
pub code_signing: bool,
pub email_protection: bool,
pub time_stamping: bool,
pub ocsp_signing: bool,
pub other: Vec<Oid<'a>>,
}
impl<'a> FromDer<'a, X509Error> for ExtendedKeyUsage<'a> {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
parse_extendedkeyusage(i).map_err(Err::convert)
}
}
pub(crate) fn parse_keyusage(i: &[u8]) -> IResult<&[u8], KeyUsage, BerError> {
let (rest, obj) = parse_der_bitstring(i)?;
let bitstring = obj
.content
.as_bitstring()
.or(Err(Err::Error(BerError::BerTypeError)))?;
let flags = bitstring
.data
.iter()
.rev()
.fold(0, |acc, x| (acc << 8) | (x.reverse_bits() as u16));
Ok((rest, KeyUsage { flags }))
}
pub(crate) fn parse_extendedkeyusage(i: &[u8]) -> IResult<&[u8], ExtendedKeyUsage<'_>, BerError> {
let (ret, seq) = <Vec<Oid>>::from_der(i)?;
let mut seen = std::collections::HashSet::new();
let mut eku = ExtendedKeyUsage {
any: false,
server_auth: false,
client_auth: false,
code_signing: false,
email_protection: false,
time_stamping: false,
ocsp_signing: false,
other: Vec::new(),
};
for oid in &seq {
if !seen.insert(oid.clone()) {
continue;
}
let asn1 = oid.as_bytes();
if asn1 == oid!(raw 2.5.29.37.0) {
eku.any = true;
} else if asn1 == oid!(raw 1.3.6.1.5.5.7.3.1) {
eku.server_auth = true;
} else if asn1 == oid!(raw 1.3.6.1.5.5.7.3.2) {
eku.client_auth = true;
} else if asn1 == oid!(raw 1.3.6.1.5.5.7.3.3) {
eku.code_signing = true;
} else if asn1 == oid!(raw 1.3.6.1.5.5.7.3.4) {
eku.email_protection = true;
} 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;
} else {
eku.other.push(oid.clone());
}
}
Ok((ret, eku))
}

1658
vendor/x509-parser/src/extensions/mod.rs vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,59 @@
use super::GeneralName;
use crate::error::{X509Error, X509Result};
use crate::extensions::parse_generalname;
use asn1_rs::FromDer;
use der_parser::der::*;
use der_parser::error::BerError;
use nom::combinator::{all_consuming, complete, map, opt};
use nom::multi::many1;
use nom::{Err, IResult};
#[derive(Clone, Debug, PartialEq)]
pub struct NameConstraints<'a> {
pub permitted_subtrees: Option<Vec<GeneralSubtree<'a>>>,
pub excluded_subtrees: Option<Vec<GeneralSubtree<'a>>>,
}
impl<'a> FromDer<'a, X509Error> for NameConstraints<'a> {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
parse_nameconstraints(i).map_err(Err::convert)
}
}
#[derive(Clone, 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> {
pub base: GeneralName<'a>,
// minimum: u32,
// maximum: Option<u32>,
}
pub(crate) fn parse_nameconstraints(i: &[u8]) -> IResult<&[u8], NameConstraints<'_>, BerError> {
fn parse_subtree(i: &[u8]) -> IResult<&[u8], GeneralSubtree<'_>, BerError> {
parse_der_sequence_defined_g(|input, _| {
map(parse_generalname, |base| GeneralSubtree { base })(input)
})(i)
}
fn parse_subtrees(i: &[u8]) -> IResult<&[u8], Vec<GeneralSubtree<'_>>, BerError> {
all_consuming(many1(complete(parse_subtree)))(i)
}
let (ret, named_constraints) = parse_der_sequence_defined_g(|input, _| {
let (rem, permitted_subtrees) =
opt(complete(parse_der_tagged_explicit_g(0, |input, _| {
parse_subtrees(input)
})))(input)?;
let (rem, excluded_subtrees) =
opt(complete(parse_der_tagged_explicit_g(1, |input, _| {
parse_subtrees(input)
})))(rem)?;
let named_constraints = NameConstraints {
permitted_subtrees,
excluded_subtrees,
};
Ok((rem, named_constraints))
})(i)?;
Ok((ret, named_constraints))
}

View File

@@ -0,0 +1,92 @@
use crate::error::{X509Error, X509Result};
use asn1_rs::{DerSequence, Error, FromDer, Oid};
use nom::{Err, IResult};
use std::collections::HashMap;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PolicyMappings<'a> {
pub mappings: Vec<PolicyMapping<'a>>,
}
impl<'a> FromDer<'a, X509Error> for PolicyMappings<'a> {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
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, Eq, DerSequence)]
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,
}
}
}
// PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
// issuerDomainPolicy CertPolicyId,
// subjectDomainPolicy CertPolicyId }
pub(crate) fn parse_policymappings(i: &[u8]) -> IResult<&[u8], PolicyMappings<'_>, Error> {
let (ret, pairs) = <Vec<PolicyMapping>>::from_der(i)?;
// let mut mappings: HashMap<Oid, Vec<Oid>> = HashMap::new();
let mappings = pairs;
// let mut mappings = Vec::new();
// for pair in pairs.iter() {
// // 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));
// }
Ok((ret, PolicyMappings { mappings }))
}

124
vendor/x509-parser/src/extensions/sct.rs vendored Normal file
View File

@@ -0,0 +1,124 @@
//! Certificate transparency [RFC6962](https://datatracker.ietf.org/doc/html/rfc6962)
//!
//! Code borrowed from tls-parser crate (file <https://github.com/rusticata/tls-parser/blob/tls-parser-0.11.0/src/certificate_transparency.rs>)
use std::convert::TryInto;
use asn1_rs::FromDer;
use der_parser::error::BerError;
use nom::bytes::streaming::take;
use nom::combinator::{complete, map_parser};
use nom::multi::{length_data, many1};
use nom::number::streaming::{be_u16, be_u64, be_u8};
use nom::IResult;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SignedCertificateTimestamp<'a> {
pub version: CtVersion,
pub id: CtLogID<'a>,
pub timestamp: u64,
pub extensions: CtExtensions<'a>,
pub signature: DigitallySigned<'a>,
}
/// Certificate Transparency Version as defined in
/// [RFC6962 Section 3.2](https://datatracker.ietf.org/doc/html/rfc6962#section-3.2)
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CtVersion(pub u8);
impl CtVersion {
pub const V1: CtVersion = CtVersion(0);
}
/// LogID as defined in
/// [RFC6962 Section 3.2](https://datatracker.ietf.org/doc/html/rfc6962#section-3.2)
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CtLogID<'a> {
pub key_id: &'a [u8; 32],
}
/// CtExtensions as defined in
/// [RFC6962 Section 3.2](https://datatracker.ietf.org/doc/html/rfc6962#section-3.2)
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CtExtensions<'a>(pub &'a [u8]);
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DigitallySigned<'a> {
pub hash_alg_id: u8,
pub sign_alg_id: u8,
pub data: &'a [u8],
}
/// Parses a list of Signed Certificate Timestamp entries
pub fn parse_ct_signed_certificate_timestamp_list(
i: &[u8],
) -> IResult<&[u8], Vec<SignedCertificateTimestamp<'_>>, BerError> {
// use nom::HexDisplay;
// eprintln!("{}", i.to_hex(16));
let (rem, b) = <&[u8]>::from_der(i)?;
let (b, sct_len) = be_u16(b)?;
let (_, sct_list) = map_parser(
take(sct_len as usize),
many1(complete(parse_ct_signed_certificate_timestamp)),
)(b)?;
Ok((rem, sct_list))
}
/// Parses as single Signed Certificate Timestamp entry
pub fn parse_ct_signed_certificate_timestamp(
i: &[u8],
) -> IResult<&[u8], SignedCertificateTimestamp<'_>, BerError> {
map_parser(
length_data(be_u16),
parse_ct_signed_certificate_timestamp_content,
)(i)
}
pub(crate) fn parse_ct_signed_certificate_timestamp_content(
i: &[u8],
) -> IResult<&[u8], SignedCertificateTimestamp<'_>, BerError> {
let (i, version) = be_u8(i)?;
let (i, id) = parse_log_id(i)?;
let (i, timestamp) = be_u64(i)?;
let (i, extensions) = parse_ct_extensions(i)?;
let (i, signature) = parse_digitally_signed(i)?;
let sct = SignedCertificateTimestamp {
version: CtVersion(version),
id,
timestamp,
extensions,
signature,
};
Ok((i, sct))
}
// Safety: cannot fail, take() returns exactly 32 bytes
fn parse_log_id(i: &[u8]) -> IResult<&[u8], CtLogID<'_>, BerError> {
let (i, key_id) = take(32usize)(i)?;
Ok((
i,
CtLogID {
key_id: key_id
.try_into()
.expect("take(32) is in sync with key_id size"),
},
))
}
fn parse_ct_extensions(i: &[u8]) -> IResult<&[u8], CtExtensions<'_>, BerError> {
let (i, ext_len) = be_u16(i)?;
let (i, ext_data) = take(ext_len as usize)(i)?;
Ok((i, CtExtensions(ext_data)))
}
fn parse_digitally_signed(i: &[u8]) -> IResult<&[u8], DigitallySigned<'_>, BerError> {
let (i, hash_alg_id) = be_u8(i)?;
let (i, sign_alg_id) = be_u8(i)?;
let (i, data) = length_data(be_u16)(i)?;
let signed = DigitallySigned {
hash_alg_id,
sign_alg_id,
data,
};
Ok((i, signed))
}

220
vendor/x509-parser/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,220 @@
//! [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE-MIT)
//! [![Apache License 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](./LICENSE-APACHE)
//! [![docs.rs](https://docs.rs/x509-parser/badge.svg)](https://docs.rs/x509-parser)
//! [![crates.io](https://img.shields.io/crates/v/x509-parser.svg)](https://crates.io/crates/x509-parser)
//! [![Download numbers](https://img.shields.io/crates/d/x509-parser.svg)](https://crates.io/crates/x509-parser)
//! [![Github CI](https://github.com/rusticata/x509-parser/workflows/Continuous%20integration/badge.svg)](https://github.com/rusticata/x509-parser/actions)
//! [![Minimum rustc version](https://img.shields.io/badge/rustc-1.67.1+-lightgray.svg)](#rust-version-requirements)
//!
//! # X.509 Parser
//!
//! A X.509 v3 ([RFC5280]) parser, implemented with the [nom](https://github.com/Geal/nom)
//! parser combinator framework.
//!
//! It is written in pure Rust, fast, and makes extensive use of zero-copy. A lot of care is taken
//! to ensure security and safety of this crate, including design (recursion limit, defensive
//! programming), tests, and fuzzing. It also aims to be panic-free.
//!
//! The code is available on [Github](https://github.com/rusticata/x509-parser)
//! and is part of the [Rusticata](https://github.com/rusticata) project.
//!
//! Certificates are usually encoded in two main formats: PEM (usually the most common format) or
//! DER. A PEM-encoded certificate is a container, storing a DER object. See the
//! [`pem`](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`](prelude/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).
//!
//! 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
//! same as accessing `<object>.tbs_certificate.issuer`.
//!
//! For PEM-encoded certificates, use the [`pem`](pem/index.html) module.
//!
//! This crate also provides visitor traits: [`X509CertificateVisitor`](crate::visitor::X509CertificateVisitor).
//!
//! # Examples
//!
//! Parsing a certificate in DER format:
//!
//! ```rust
//! use x509_parser::prelude::*;
//!
//! static IGCA_DER: &[u8] = include_bytes!("../assets/IGC_A.der");
//!
//! # fn main() {
//! let res = X509Certificate::from_der(IGCA_DER);
//! match res {
//! Ok((rem, cert)) => {
//! assert!(rem.is_empty());
//! //
//! assert_eq!(cert.version(), X509Version::V3);
//! },
//! _ => panic!("x509 parsing failed: {:?}", res),
//! }
//! # }
//! ```
//!
//! To parse a CRL and print information about revoked certificates:
//!
//! ```rust
//! # use x509_parser::prelude::*;
//! #
//! # static DER: &[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),
//! }
//! # }
//! ```
//!
//! See also `examples/print-cert.rs`.
//!
//! # Features
//!
//! - The `verify` and `verify-aws` features adds support for (cryptographic) signature verification, based on `ring` or `aws-lc` respectively.
//! It adds the
//! [`X509Certificate::verify_signature()`](certificate/struct.X509Certificate.html#method.verify_signature)
//! to `X509Certificate`.
//!
//! ```rust
//! # #[cfg(any(feature = "verify", feature = "verify-aws"))]
//! # use x509_parser::certificate::X509Certificate;
//! /// Cryptographic signature verification: returns true if certificate was signed by issuer
//! #[cfg(any(feature = "verify", feature = "verify-aws"))]
//! pub fn check_signature(cert: &X509Certificate<'_>, issuer: &X509Certificate<'_>) -> bool {
//! let issuer_public_key = issuer.public_key();
//! cert
//! .verify_signature(Some(issuer_public_key))
//! .is_ok()
//! }
//! ```
//!
//! - The `verify-aws` feature offers the same support for signature verification, but based on
//! `aws-lc-rs` instead of `ring`.
//!
//! - _Note_: if both `verify` and `verify-aws` features are enabled (which happens when using
//! `--all-features`), the verification will use `aws-lc-rs`. It also has the side-effect of
//! having a dependency on `ring`, even if it is not used.
//!
//! - The `validate` features add methods to run more validation functions on the certificate structure
//! and values using the [`Validate`](validate/trait.Validate.html) trait.
//! It does not validate any cryptographic parameter (see `verify` above).
//!
//! ## Rust version requirements
//!
//! `x509-parser` requires **Rustc version 1.67.1 or greater**, based on der-parser
//! dependencies and for proc-macro attributes support.
//!
//! [RFC5280]: https://tools.ietf.org/html/rfc5280
#![deny(/*missing_docs,*/
unstable_features,
unused_import_braces, unused_qualifications)]
#![warn(
missing_debug_implementations,
/* missing_docs,
rust_2018_idioms,*/
unreachable_pub
)]
#![forbid(unsafe_code)]
#![deny(rustdoc::broken_intra_doc_links)]
#![doc(test(
no_crate_inject,
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
))]
#![cfg_attr(docsrs, feature(doc_cfg))]
pub mod certificate;
pub mod certification_request;
pub mod cri_attributes;
pub mod error;
pub mod extensions;
pub mod objects;
pub mod pem;
pub mod prelude;
pub mod public_key;
pub mod revocation_list;
pub mod signature_algorithm;
pub mod signature_value;
pub mod time;
pub mod utils;
#[cfg(feature = "validate")]
#[cfg_attr(docsrs, doc(cfg(feature = "validate")))]
pub mod validate;
#[cfg(any(feature = "verify", feature = "verify-aws"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "verify", feature = "verify-aws"))))]
pub mod verify;
pub mod visitor;
pub mod x509;
// reexports
pub use asn1_rs;
pub use der_parser;
pub use der_parser::num_bigint;
pub use nom;
pub use oid_registry;
use asn1_rs::FromDer;
use certificate::X509Certificate;
use error::X509Result;
use revocation_list::CertificateRevocationList;
/// 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
/// for more information.
///
/// For PEM-encoded certificates, use the [`pem`](pem/index.html) module.
#[inline]
pub fn parse_x509_certificate(i: &[u8]) -> X509Result<'_, X509Certificate<'_>> {
X509Certificate::from_der(i)
}
/// 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
/// for more information.
#[inline]
pub fn parse_x509_crl(i: &[u8]) -> X509Result<'_, CertificateRevocationList<'_>> {
CertificateRevocationList::from_der(i)
}
/// Parse a DER-encoded X.509 Certificate, and return the remaining of the input and the built
#[deprecated(
since = "0.9.0",
note = "please use `parse_x509_certificate` or `X509Certificate::from_der` instead"
)]
#[inline]
pub fn parse_x509_der(i: &[u8]) -> X509Result<'_, X509Certificate<'_>> {
X509Certificate::from_der(i)
}
/// Parse a DER-encoded X.509 v2 CRL, and return the remaining of the input and the built
/// object.
#[deprecated(
since = "0.9.0",
note = "please use `parse_x509_crl` or `CertificateRevocationList::from_der` instead"
)]
#[inline]
pub fn parse_crl_der(i: &[u8]) -> X509Result<'_, CertificateRevocationList<'_>> {
CertificateRevocationList::from_der(i)
}

92
vendor/x509-parser/src/objects.rs vendored Normal file
View File

@@ -0,0 +1,92 @@
//! X.509 helper objects definitions and registry
//!
//! All OID objects and definitions are now stored in the [oid-registry](https://crates.io/crates/oid-registry) crate.
//!
//! This crate is re-exporting `oid-registry`, so to access the OID constants the
//! `x509_parser::oid_oid_registry` namespace can be used (see example below).
//!
//! ## Example
//!
//! To get the short name for a given OID:
//!
//! ```rust
//! use x509_parser::objects::*;
//! use x509_parser::oid_registry::*;
//!
//! let oid = &OID_X509_COMMON_NAME;
//! let sn = oid2sn(oid, oid_registry());
//! assert_eq!(sn, Ok("commonName"));
//! ```
use crate::error::NidError;
use asn1_rs::oid;
use lazy_static::lazy_static;
use oid_registry::*;
use std::collections::HashMap;
lazy_static! {
static ref OID_REGISTRY: OidRegistry<'static> = {
let mut reg = OidRegistry::default().with_all_crypto().with_x509();
// OIDs not in the default registry can be added here
let entry = OidEntry::new("id-mgf1", "Mask Generator Function 1 (MGF1)");
reg.insert(oid! {1.2.840.113549.1.1.8}, entry);
reg
};
static ref ABBREV_MAP: HashMap<Oid<'static>, &'static str> = {
let mut m = HashMap::new();
m.insert(OID_X509_COMMON_NAME, "CN");
m.insert(OID_X509_COUNTRY_NAME, "C");
m.insert(OID_X509_LOCALITY_NAME, "L");
m.insert(OID_X509_STATE_OR_PROVINCE_NAME, "ST");
m.insert(OID_X509_ORGANIZATION_NAME, "O");
m.insert(OID_X509_ORGANIZATIONAL_UNIT, "OU");
m.insert(OID_DOMAIN_COMPONENT, "DC");
m.insert(OID_PKCS9_EMAIL_ADDRESS, "Email");
m
};
}
/// 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> {
if let Some(abbrev) = ABBREV_MAP.get(oid) {
return Ok(abbrev);
}
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
}
#[cfg(test)]
mod tests {
use super::*;
use der_parser::oid;
// This test is meant to check syntax of pattern matching with OID objects
#[test]
fn test_oid_match() {
let oid = oid!(1.2.840 .113549 .1 .1 .5);
if oid == OID_PKCS1_SHA1WITHRSA {
// ok
}
// matching is not possible with Cow constants in pattern,
// see https://rust-lang.github.io/rfcs/1445-restrict-constants-in-patterns.html
//
// match oid {
// OID_PKCS1_SHA1WITHRSA => (),
// _ => (),
// }
}
}

264
vendor/x509-parser/src/pem.rs vendored Normal file
View File

@@ -0,0 +1,264 @@
//! Decoding functions for PEM-encoded data
//!
//! A PEM object is a container, which can store (amongst other formats) a public X.509
//! Certificate, or a CRL, etc. It contains only printable characters.
//! PEM-encoded binary data is essentially a beginning and matching end tag that encloses
//! base64-encoded binary data (see:
//! <https://en.wikipedia.org/wiki/Privacy-enhanced_Electronic_Mail>).
//!
//! # Examples
//!
//! To parse a certificate in PEM format, first create the `Pem` object, then decode
//! contents:
//!
//! ```rust,no_run
//! use x509_parser::pem::Pem;
//! use x509_parser::x509::X509Version;
//!
//! static IGCA_PEM: &str = "../assets/IGC_A.pem";
//!
//! # fn main() {
//! let data = std::fs::read(IGCA_PEM).expect("Could not read file");
//! for pem in Pem::iter_from_buffer(&data) {
//! let pem = pem.expect("Reading next PEM block failed");
//! let x509 = pem.parse_x509().expect("X.509: decoding DER failed");
//! assert_eq!(x509.tbs_certificate.version, X509Version::V3);
//! }
//! # }
//! ```
//!
//! This is the most direct method to parse PEM data.
//!
//! Another method to parse the certificate is to use `parse_x509_pem`:
//!
//! ```rust,no_run
//! use x509_parser::pem::parse_x509_pem;
//! use x509_parser::parse_x509_certificate;
//!
//! static IGCA_PEM: &[u8] = include_bytes!("../assets/IGC_A.pem");
//!
//! # fn main() {
//! let res = parse_x509_pem(IGCA_PEM);
//! match res {
//! Ok((rem, pem)) => {
//! assert!(rem.is_empty());
//! //
//! assert_eq!(pem.label, String::from("CERTIFICATE"));
//! //
//! let res_x509 = parse_x509_certificate(&pem.contents);
//! assert!(res_x509.is_ok());
//! },
//! _ => panic!("PEM parsing failed: {:?}", res),
//! }
//! # }
//! ```
//!
//! Note that all methods require to store the `Pem` object in a variable, mainly because decoding
//! the PEM object requires allocation of buffers, and that the lifetime of X.509 certificates will
//! be bound to these buffers.
use crate::certificate::X509Certificate;
use crate::error::{PEMError, X509Error};
use crate::parse_x509_certificate;
use nom::{Err, IResult};
use std::io::{BufRead, Cursor, ErrorKind, Seek};
/// Representation of PEM data
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Pem {
/// The PEM label
pub label: String,
/// The PEM decoded data
pub contents: Vec<u8>,
}
#[deprecated(since = "0.8.3", note = "please use `parse_x509_pem` instead")]
pub fn pem_to_der(i: &[u8]) -> IResult<&[u8], Pem, PEMError> {
parse_x509_pem(i)
}
/// Read a PEM-encoded structure, and decode the base64 data
///
/// Return a structure describing the PEM object: the enclosing tag, and the data.
/// Allocates a new buffer for the decoded data.
///
/// Note that only the *first* PEM block is decoded. To iterate all blocks from PEM data,
/// use [`Pem::iter_from_buffer`].
///
/// For X.509 (`CERTIFICATE` tag), the data is a certificate, encoded in DER. To parse the
/// certificate content, use `Pem::parse_x509` or `parse_x509_certificate`.
pub fn parse_x509_pem(i: &[u8]) -> IResult<&'_ [u8], Pem, PEMError> {
let reader = Cursor::new(i);
let res = Pem::read(reader);
match res {
Ok((pem, bytes_read)) => Ok((&i[bytes_read..], pem)),
Err(e) => Err(Err::Error(e)),
}
}
impl Pem {
/// Read the next PEM-encoded structure, and decode the base64 data
///
/// Returns the certificate (encoded in DER) and the number of bytes read.
/// Allocates a new buffer for the decoded data.
///
/// Note that a PEM file can contain multiple PEM blocks. This function returns the
/// *first* decoded object, starting from the current reader position.
/// To get all objects, call this function repeatedly until `PEMError::MissingHeader`
/// is returned.
///
/// # Examples
/// ```
/// let file = std::fs::File::open("assets/certificate.pem").unwrap();
/// let subject = x509_parser::pem::Pem::read(std::io::BufReader::new(file))
/// .unwrap().0
/// .parse_x509().unwrap()
/// .tbs_certificate.subject.to_string();
/// assert_eq!(subject, "CN=lists.for-our.info");
/// ```
pub fn read(mut r: impl BufRead + Seek) -> Result<(Pem, usize), PEMError> {
let mut line = String::new();
let label = loop {
let num_bytes = match r.read_line(&mut line) {
Ok(line) => line,
Err(e) if e.kind() == ErrorKind::InvalidData => {
// some tools put invalid UTF-8 data in PEM comment section. Ignore line
continue;
}
Err(e) => {
return Err(e.into());
}
};
if num_bytes == 0 {
// EOF
return Err(PEMError::MissingHeader);
}
if !line.starts_with("-----BEGIN ") {
line.clear();
continue;
}
let v: Vec<&str> = line.split("-----").collect();
if v.len() < 3 || !v[0].is_empty() {
return Err(PEMError::InvalidHeader);
}
let label = v[1].strip_prefix("BEGIN ").ok_or(PEMError::InvalidHeader)?;
break label;
};
let label = label.split('-').next().ok_or(PEMError::InvalidHeader)?;
let mut s = String::new();
loop {
let mut l = String::new();
let num_bytes = r.read_line(&mut l)?;
if num_bytes == 0 {
return Err(PEMError::IncompletePEM);
}
if l.starts_with("-----END ") {
// finished reading
break;
}
s.push_str(l.trim_end());
}
let contents = data_encoding::BASE64
.decode(s.as_bytes())
.or(Err(PEMError::Base64DecodeError))?;
let pem = Pem {
label: label.to_string(),
contents,
};
Ok((pem, r.stream_position()? as usize))
}
/// Decode the PEM contents into a X.509 object
pub fn parse_x509(&self) -> Result<X509Certificate<'_>, Err<X509Error>> {
parse_x509_certificate(&self.contents).map(|(_, x509)| x509)
}
/// Returns an iterator over the PEM-encapsulated parts of a buffer
///
/// Only the sections enclosed in blocks starting with `-----BEGIN xxx-----`
/// and ending with `-----END xxx-----` will be considered.
/// Lines before, between or after such blocks will be ignored.
///
/// The iterator is fallible: `next()` returns a `Result<Pem, PEMError>` object.
/// An error indicates a block is present but invalid.
///
/// If the buffer does not contain any block, iterator will be empty.
pub fn iter_from_buffer(i: &[u8]) -> PemIterator<Cursor<&[u8]>> {
let reader = Cursor::new(i);
PemIterator { reader }
}
/// Returns an iterator over the PEM-encapsulated parts of a reader
///
/// Only the sections enclosed in blocks starting with `-----BEGIN xxx-----`
/// and ending with `-----END xxx-----` will be considered.
/// Lines before, between or after such blocks will be ignored.
///
/// The iterator is fallible: `next()` returns a `Result<Pem, PEMError>` object.
/// An error indicates a block is present but invalid.
///
/// If the reader does not contain any block, iterator will be empty.
pub fn iter_from_reader<R: BufRead + Seek>(reader: R) -> PemIterator<R> {
PemIterator { reader }
}
}
/// Iterator over PEM-encapsulated blocks
///
/// Only the sections enclosed in blocks starting with `-----BEGIN xxx-----`
/// and ending with `-----END xxx-----` will be considered.
/// Lines before, between or after such blocks will be ignored.
///
/// The iterator is fallible: `next()` returns a `Result<Pem, PEMError>` object.
/// An error indicates a block is present but invalid.
///
/// If the buffer does not contain any block, iterator will be empty.
#[allow(missing_debug_implementations)]
pub struct PemIterator<Reader: BufRead + Seek> {
reader: Reader,
}
impl<R: BufRead + Seek> Iterator for PemIterator<R> {
type Item = Result<Pem, PEMError>;
fn next(&mut self) -> Option<Self::Item> {
if let Ok(&[]) = self.reader.fill_buf() {
return None;
}
let reader = self.reader.by_ref();
let r = Pem::read(reader).map(|(pem, _)| pem);
if let Err(PEMError::MissingHeader) = r {
None
} else {
Some(r)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn read_pem_from_file() {
let file = std::io::BufReader::new(std::fs::File::open("assets/certificate.pem").unwrap());
let subject = Pem::read(file)
.unwrap()
.0
.parse_x509()
.unwrap()
.tbs_certificate
.subject
.to_string();
assert_eq!(subject, "CN=lists.for-our.info");
}
#[test]
fn pem_multi_word_label() {
const PEM_BYTES: &[u8] =
b"-----BEGIN MULTI WORD LABEL-----\n-----END MULTI WORD LABEL-----";
let (_, pem) = parse_x509_pem(PEM_BYTES).expect("should parse pem");
assert_eq!(pem.label, "MULTI WORD LABEL");
}
}

18
vendor/x509-parser/src/prelude.rs vendored Normal file
View File

@@ -0,0 +1,18 @@
//! A "prelude" for users of the x509-parser crate.
pub use crate::certificate::*;
pub use crate::certification_request::*;
pub use crate::cri_attributes::*;
pub use crate::error::*;
pub use crate::extensions::*;
pub use crate::objects::*;
pub use crate::pem::*;
pub use crate::revocation_list::*;
pub use crate::time::*;
pub use crate::utils::*;
#[cfg(feature = "validate")]
pub use crate::validate::*;
pub use crate::x509::*;
pub use crate::*;
pub use asn1_rs::FromDer;

132
vendor/x509-parser/src/public_key.rs vendored Normal file
View File

@@ -0,0 +1,132 @@
use crate::error::*;
use asn1_rs::FromDer;
use der_parser::{
der::{parse_der_integer, parse_der_sequence_defined_g},
error::BerResult,
};
/// Public Key value
#[derive(Debug, PartialEq, Eq)]
pub enum PublicKey<'a> {
RSA(RSAPublicKey<'a>),
EC(ECPoint<'a>),
/// DSAPublicKey ::= INTEGER -- public key, Y (RFC 3279)
DSA(&'a [u8]),
/// GostR3410-94-PublicKey ::= OCTET STRING -- public key, Y (RFC 4491)
GostR3410(&'a [u8]),
/// GostR3410-2012-256-PublicKey ::= OCTET STRING (64),
/// GostR3410-2012-512-PublicKey ::= OCTET STRING (128). (RFC 4491-bis)
GostR3410_2012(&'a [u8]),
Unknown(&'a [u8]),
}
impl PublicKey<'_> {
/// Return the key size (in bits) or 0
pub fn key_size(&self) -> usize {
match self {
Self::EC(ec) => ec.key_size(),
Self::RSA(rsa) => rsa.key_size(),
Self::DSA(y) | Self::GostR3410(y) => y.len() * 8,
_ => 0,
}
}
}
/// RSA public Key, defined in rfc3279
#[derive(Debug, PartialEq, Eq)]
pub struct RSAPublicKey<'a> {
/// Raw bytes of the modulus
///
/// This possibly includes a leading 0 if the MSB is 1
pub modulus: &'a [u8],
/// Raw bytes of the exponent
///
/// This possibly includes a leading 0 if the MSB is 1
pub exponent: &'a [u8],
}
impl RSAPublicKey<'_> {
/// Attempt to convert exponent to u64
///
/// Returns an error if integer is too large, empty, or negative
pub fn try_exponent(&self) -> Result<u64, X509Error> {
let mut buf = [0u8; 8];
if self.exponent.is_empty() || self.exponent[0] & 0x80 != 0 || self.exponent.len() > 8 {
return Err(X509Error::InvalidNumber);
}
buf[8_usize.saturating_sub(self.exponent.len())..].copy_from_slice(self.exponent);
let int = <u64>::from_be_bytes(buf);
Ok(int)
}
/// Return the key size (in bits) or 0
pub fn key_size(&self) -> usize {
if !self.modulus.is_empty() && self.modulus[0] & 0x80 == 0 {
// XXX len must substract leading zeroes
let modulus = &self.modulus[1..];
8 * modulus.len()
} else {
0
}
}
}
// helper function to parse with error type BerError
fn parse_rsa_key(bytes: &[u8]) -> BerResult<'_, RSAPublicKey<'_>> {
parse_der_sequence_defined_g(move |i, _| {
let (i, obj_modulus) = parse_der_integer(i)?;
let (i, obj_exponent) = parse_der_integer(i)?;
let modulus = obj_modulus.as_slice()?;
let exponent = obj_exponent.as_slice()?;
let key = RSAPublicKey { modulus, exponent };
Ok((i, key))
})(bytes)
}
impl<'a> FromDer<'a, X509Error> for RSAPublicKey<'a> {
fn from_der(bytes: &'a [u8]) -> X509Result<'a, Self> {
parse_rsa_key(bytes).map_err(|_| nom::Err::Error(X509Error::InvalidSPKI))
}
}
/// Elliptic Curve point, as defined in [RFC5480](https://datatracker.ietf.org/doc/html/rfc5480)
#[derive(Debug, PartialEq, Eq)]
pub struct ECPoint<'a> {
data: &'a [u8],
}
impl<'a> ECPoint<'a> {
/// EC Point content (See Standards for Efficient Cryptography Group (SECG), "SEC1: Elliptic Curve Cryptography")
pub fn data(&'a self) -> &'a [u8] {
self.data
}
/// Return the key size (in bits) or 0
pub fn key_size(&self) -> usize {
match self.data {
[] => {
// empty
0
}
[4, rem @ ..] => {
// uncompressed
rem.len() * 8 / 2
}
[2..=3, rem @ ..] => {
// compressed
rem.len() * 8
}
_ => {
// invalid
0
}
}
}
}
impl<'a> From<&'a [u8]> for ECPoint<'a> {
fn from(data: &'a [u8]) -> Self {
ECPoint { data }
}
}

View File

@@ -0,0 +1,388 @@
use crate::error::{X509Error, X509Result};
use crate::extensions::*;
use crate::time::ASN1Time;
use crate::utils::format_serial;
use crate::x509::{
parse_serial, parse_signature_value, AlgorithmIdentifier, ReasonCode, X509Name, X509Version,
};
#[cfg(any(feature = "verify", feature = "verify-aws"))]
use crate::verify::verify_signature;
#[cfg(any(feature = "verify", feature = "verify-aws"))]
use crate::x509::SubjectPublicKeyInfo;
use asn1_rs::{BitString, FromDer};
use der_parser::der::*;
use der_parser::num_bigint::BigUint;
use nom::combinator::{all_consuming, complete, map, opt};
use nom::multi::many0;
use nom::Offset;
use oid_registry::*;
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::prelude::FromDer;
/// use x509_parser::revocation_list::CertificateRevocationList;
///
/// # 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)]
pub struct CertificateRevocationList<'a> {
pub tbs_cert_list: TbsCertList<'a>,
pub signature_algorithm: AlgorithmIdentifier<'a>,
pub signature_value: BitString<'a>,
/// Complete raw ASN.1 DER content (TBS certificate list, signature algorithm and signature).
pub(crate) raw: &'a [u8],
}
impl<'a> CertificateRevocationList<'a> {
/// Get the version of the encoded certificate
pub fn version(&self) -> Option<X509Version> {
self.tbs_cert_list.version
}
/// Get the certificate issuer.
#[inline]
pub fn issuer(&self) -> &X509Name<'_> {
&self.tbs_cert_list.issuer
}
/// Get the date and time of the last (this) update.
#[inline]
pub fn last_update(&self) -> ASN1Time {
self.tbs_cert_list.this_update
}
/// Get the date and time of the next update, if present.
#[inline]
pub fn next_update(&self) -> Option<ASN1Time> {
self.tbs_cert_list.next_update
}
/// Return an iterator over the `RevokedCertificate` objects
pub fn iter_revoked_certificates(&self) -> impl Iterator<Item = &RevokedCertificate<'a>> {
self.tbs_cert_list.revoked_certificates.iter()
}
/// Get the CRL extensions.
#[inline]
pub fn extensions(&self) -> &[X509Extension<'_>] {
&self.tbs_cert_list.extensions
}
/// Get the CRL number, if present
///
/// Note that the returned value is a `BigUint`, because of the following RFC specification:
/// <pre>
/// Given the requirements above, CRL numbers can be expected to contain long integers. CRL
/// verifiers MUST be able to handle CRLNumber values up to 20 octets. Conformant CRL issuers
/// 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_CRL_NUMBER)
.and_then(|ext| match ext.parsed_extension {
ParsedExtension::CRLNumber(ref num) => Some(num),
_ => None,
})
}
/// Verify the cryptographic signature of this certificate revocation list
///
/// `public_key` is the public key of the **signer**.
///
/// Not all algorithms are supported, this function is limited to what `ring` supports.
#[cfg(any(feature = "verify", feature = "verify-aws"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "verify", feature = "verify-aws"))))]
pub fn verify_signature(&self, public_key: &SubjectPublicKeyInfo) -> Result<(), X509Error> {
verify_signature(
public_key,
&self.signature_algorithm,
&self.signature_value,
self.tbs_cert_list.raw,
)
}
///
/// Return the raw ASN.1 DER content of the complete signed certificate revocation list that was parsed.
///
/// This includes the to-be-signed (TBS) certificate list, the signature algorithm, and the signature.
/// If you want just the ASN.1 DER of the TBS certificate list, prefer [`TbsCertList::as_ref()`].
///
/// We avoid the `AsRef` trait in this instance to ensure the full lifetime of the `CertificateRevocationList` is used.
pub fn as_raw(&self) -> &'a [u8] {
self.raw
}
}
impl<'a> AsRef<[u8]> for CertificateRevocationList<'a> {
fn as_ref(&self) -> &[u8] {
self.as_raw()
}
}
/// <pre>
/// CertificateList ::= SEQUENCE {
/// tbsCertList TBSCertList,
/// signatureAlgorithm AlgorithmIdentifier,
/// signatureValue BIT STRING }
/// </pre>
impl<'a> FromDer<'a, X509Error> for CertificateRevocationList<'a> {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
let start_i = i;
let (rem, mut crl) = 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,
raw: &[],
};
Ok((i, crl))
})(i)?;
let len = start_i.offset(rem);
crl.raw = &start_i[..len];
Ok((rem, crl))
}
}
/// The sequence TBSCertList contains information about the certificates that have
/// been revoked by the CA that issued the CRL.
///
/// RFC5280 definition:
///
/// <pre>
/// TBSCertList ::= SEQUENCE {
/// version Version OPTIONAL,
/// -- if present, MUST be v2
/// signature AlgorithmIdentifier,
/// issuer Name,
/// thisUpdate Time,
/// nextUpdate Time OPTIONAL,
/// revokedCertificates SEQUENCE OF SEQUENCE {
/// userCertificate CertificateSerialNumber,
/// revocationDate Time,
/// crlEntryExtensions Extensions OPTIONAL
/// -- if present, version MUST be v2
/// } OPTIONAL,
/// crlExtensions [0] EXPLICIT Extensions OPTIONAL
/// -- if present, version MUST be v2
/// }
/// </pre>
#[derive(Clone, Debug, PartialEq)]
pub struct TbsCertList<'a> {
pub version: Option<X509Version>,
pub signature: AlgorithmIdentifier<'a>,
pub issuer: X509Name<'a>,
pub this_update: ASN1Time,
pub next_update: Option<ASN1Time>,
pub revoked_certificates: Vec<RevokedCertificate<'a>>,
extensions: Vec<X509Extension<'a>>,
pub(crate) raw: &'a [u8],
}
impl TbsCertList<'_> {
/// 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 AsRef<[u8]> for TbsCertList<'_> {
fn as_ref(&self) -> &[u8] {
self.raw
}
}
impl<'a> FromDer<'a, X509Error> for TbsCertList<'a> {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
let start_i = i;
parse_der_sequence_defined_g(move |i, _| {
let (i, version) =
opt(map(parse_der_u32, X509Version))(i).or(Err(X509Error::InvalidVersion))?;
let (i, signature) = AlgorithmIdentifier::from_der(i)?;
let (i, issuer) = X509Name::from_der(i)?;
let (i, this_update) = ASN1Time::from_der(i)?;
let (i, next_update) = ASN1Time::from_der_opt(i)?;
let (i, revoked_certificates) = opt(complete(parse_revoked_certificates))(i)?;
let (i, extensions) = parse_extensions(i, Tag(0))?;
let len = start_i.offset(i);
let tbs = TbsCertList {
version,
signature,
issuer,
this_update,
next_update,
revoked_certificates: revoked_certificates.unwrap_or_default(),
extensions,
raw: &start_i[..len],
};
Ok((i, tbs))
})(i)
}
}
#[derive(Clone, 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(crate) raw_serial: &'a [u8],
}
impl RevokedCertificate<'_> {
/// 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
}
/// Get a formatted string of the certificate serial number, separated by ':'
pub fn raw_serial_as_string(&self) -> String {
format_serial(self.raw_serial)
}
/// 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,
})
}
/// Get the invalidity date, if present
///
/// 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,
})
}
}
// revokedCertificates SEQUENCE OF SEQUENCE {
// userCertificate CertificateSerialNumber,
// revocationDate Time,
// crlEntryExtensions Extensions OPTIONAL
// -- if present, MUST be v2
// } OPTIONAL,
impl<'a> FromDer<'a, X509Error> for RevokedCertificate<'a> {
fn from_der(i: &'a [u8]) -> X509Result<'a, 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)
}
}
fn parse_revoked_certificates(i: &[u8]) -> X509Result<'_, Vec<RevokedCertificate<'_>>> {
parse_der_sequence_defined_g(|a, _| {
all_consuming(many0(complete(RevokedCertificate::from_der)))(a)
})(i)
}

View File

@@ -0,0 +1,326 @@
use crate::error::X509Error;
use crate::x509::AlgorithmIdentifier;
use asn1_rs::{
oid, Any, CheckDerConstraints, Class, DerAutoDerive, Error, FromDer, OptTaggedExplicit,
OptTaggedParser, Tag,
};
use core::convert::TryFrom;
use oid_registry::*;
#[allow(non_camel_case_types)]
#[derive(Debug, PartialEq)]
pub enum SignatureAlgorithm<'a> {
RSA,
RSASSA_PSS(Box<RsaSsaPssParams<'a>>),
RSAAES_OAEP(Box<RsaAesOaepParams<'a>>),
DSA,
ECDSA,
ED25519,
}
impl<'a, 'b> TryFrom<&'b AlgorithmIdentifier<'a>> for SignatureAlgorithm<'a> {
type Error = X509Error;
fn try_from(value: &'b AlgorithmIdentifier<'a>) -> Result<Self, Self::Error> {
if value.algorithm.starts_with(&oid! {1.2.840.113549.1.1}) {
// children of PKCS1 are all RSA
// test if RSASSA-PSS
if value.algorithm == OID_PKCS1_RSASSAPSS {
let params = match value.parameters.as_ref() {
Some(any) => any,
None => return Err(X509Error::InvalidSignatureValue),
};
let params = RsaSsaPssParams::try_from(params)
.map_err(|_| X509Error::InvalidSignatureValue)?;
Ok(SignatureAlgorithm::RSASSA_PSS(Box::new(params)))
} else {
// rfc3279#section-2.2.1: the parameters component of that type SHALL be
// the ASN.1 type NULL
// We could enforce presence of NULL, but that would make a strict parser
// so it would best go to a verifier.
Ok(SignatureAlgorithm::RSA)
}
} else if test_ecdsa_oid(&value.algorithm) {
// parameter should be NULL - see above
Ok(SignatureAlgorithm::ECDSA)
} else if value.algorithm.starts_with(&oid! {1.2.840.10040.4}) {
// parameter should be NULL - see above
Ok(SignatureAlgorithm::DSA)
} else if value.algorithm == OID_SIG_ED25519 {
Ok(SignatureAlgorithm::ED25519)
} else if value.algorithm == oid! {1.2.840.113549.1.1.7} {
let params = match value.parameters.as_ref() {
Some(any) => any,
None => return Err(X509Error::InvalidSignatureValue),
};
let params =
RsaAesOaepParams::try_from(params).map_err(|_| X509Error::InvalidSignatureValue)?;
Ok(SignatureAlgorithm::RSAAES_OAEP(Box::new(params)))
} else {
if cfg!(debug_assertions) {
// TODO: remove debug
eprintln!("bad Signature AlgorithmIdentifier: {}", value.algorithm);
}
Err(X509Error::InvalidSignatureValue)
}
}
}
#[inline]
fn test_ecdsa_oid(oid: &Oid) -> bool {
// test if oid is a child from {ansi-x962 signatures}
oid.starts_with(&oid! {1.2.840.10045.4})
}
// RSASSA-PSS public keys [RFC4055](https://www.rfc-editor.org/rfc/rfc4055.html)
// RSASSA-PSS-params ::= SEQUENCE {
// hashAlgorithm [0] HashAlgorithm DEFAULT
// sha1Identifier,
// maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT
// mgf1SHA1Identifier,
// saltLength [2] INTEGER DEFAULT 20,
// trailerField [3] INTEGER DEFAULT 1 }
#[derive(Debug, PartialEq)]
pub struct RsaSsaPssParams<'a> {
hash_alg: Option<AlgorithmIdentifier<'a>>,
mask_gen_algorithm: Option<AlgorithmIdentifier<'a>>,
salt_length: Option<u32>,
trailer_field: Option<u32>,
}
impl<'a> RsaSsaPssParams<'a> {
/// Get a reference to the rsa ssa pss params's hash algorithm.
pub fn hash_algorithm(&self) -> Option<&AlgorithmIdentifier<'_>> {
self.hash_alg.as_ref()
}
/// Return the hash algorithm OID, or SHA1 if absent (RFC4055)
pub fn hash_algorithm_oid(&self) -> &'a Oid<'_> {
const SHA1: &Oid = &OID_HASH_SHA1;
self.hash_alg
.as_ref()
.map(|alg| &alg.algorithm)
.unwrap_or(SHA1)
}
/// Get a reference to the rsa ssa pss params's mask generation algorithm.
pub fn mask_gen_algorithm_raw(&self) -> Option<&AlgorithmIdentifier<'_>> {
self.mask_gen_algorithm.as_ref()
}
/// Get the rsa ssa pss params's mask generation algorithm.
///
/// If the algorithm encoding is invalid, raise an error `InvalidAlgorithmIdentifier`
pub fn mask_gen_algorithm(&self) -> Result<MaskGenAlgorithm<'_, '_>, X509Error> {
match self.mask_gen_algorithm.as_ref() {
Some(alg) => {
let (_, hash) = alg
.parameters()
.and_then(|any| Oid::from_der(any.data).ok())
.ok_or(X509Error::InvalidAlgorithmIdentifier)?;
Ok(MaskGenAlgorithm::new(alg.algorithm.clone(), hash))
}
_ => {
Ok(MaskGenAlgorithm::new(
oid! {1.2.840.113549.1.1.8}, // id-mgf1
OID_HASH_SHA1,
))
}
}
}
/// Return the salt length
pub fn salt_length(&self) -> u32 {
self.salt_length.unwrap_or(20)
}
/// Return the trailer field (value must be `1` according to RFC4055)
pub fn trailer_field(&self) -> u32 {
self.trailer_field.unwrap_or(1)
}
}
impl<'a> TryFrom<Any<'a>> for RsaSsaPssParams<'a> {
type Error = X509Error;
fn try_from(value: Any<'a>) -> Result<Self, Self::Error> {
Self::try_from(&value)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for RsaSsaPssParams<'a> {
type Error = X509Error;
fn try_from(value: &'b Any<'a>) -> Result<Self, Self::Error> {
value.tag().assert_eq(Tag::Sequence)?;
let i = &value.data;
// let (i, hash_alg) = OptTaggedExplicit::<_, X509Error, 0>::from_der(i)?;
let (i, hash_alg) = OptTaggedParser::new(Class::ContextSpecific, Tag(0))
.parse_der(i, |_, inner| AlgorithmIdentifier::from_der(inner))?;
// let (i, mask_gen_algorithm) = OptTaggedExplicit::<_, Error, 1>::from_der(i)?;
let (i, mask_gen_algorithm) = OptTaggedParser::new(Class::ContextSpecific, Tag(1))
.parse_der(i, |_, inner| AlgorithmIdentifier::from_der(inner))?;
let (i, salt_length) = OptTaggedExplicit::<_, Error, 2>::from_der(i)?;
let (_, trailer_field) = OptTaggedExplicit::<_, Error, 3>::from_der(i)?;
let params = RsaSsaPssParams {
hash_alg,
mask_gen_algorithm,
salt_length: salt_length.map(|t| t.into_inner()),
trailer_field: trailer_field.map(|t| t.into_inner()),
};
Ok(params)
}
}
impl CheckDerConstraints for RsaSsaPssParams<'_> {
fn check_constraints(any: &Any) -> asn1_rs::Result<()> {
any.header.assert_constructed()?;
Ok(())
}
}
impl DerAutoDerive for RsaSsaPssParams<'_> {}
#[derive(Debug, PartialEq, Eq)]
pub struct MaskGenAlgorithm<'a, 'b> {
pub mgf: Oid<'a>,
pub hash: Oid<'b>,
}
impl<'a, 'b> MaskGenAlgorithm<'a, 'b> {
pub const fn new(mgf: Oid<'a>, hash: Oid<'b>) -> Self {
Self { mgf, hash }
}
}
// RSAAES-OAEP public keys [RFC8017](https://www.rfc-editor.org/rfc/rfc8017.html)
// RSAES-OAEP-params ::= SEQUENCE {
// hashFunc [0] AlgorithmIdentifier DEFAULT
// sha1Identifier,
// maskGenFunc [1] AlgorithmIdentifier DEFAULT
// mgf1SHA1Identifier,
// pSourceFunc [2] AlgorithmIdentifier DEFAULT
// pSpecifiedEmptyIdentifier }
//
// pSpecifiedEmptyIdentifier AlgorithmIdentifier ::=
// { id-pSpecified, nullOctetString }
//
// nullOctetString OCTET STRING (SIZE (0)) ::= { ''H }
#[derive(Debug, PartialEq)]
pub struct RsaAesOaepParams<'a> {
hash_alg: Option<AlgorithmIdentifier<'a>>,
mask_gen_alg: Option<AlgorithmIdentifier<'a>>,
p_source_alg: Option<AlgorithmIdentifier<'a>>,
}
impl<'a> RsaAesOaepParams<'a> {
pub const EMPTY: &'static AlgorithmIdentifier<'static> = &AlgorithmIdentifier::new(
oid! {1.2.840.113549.1.1.9}, // id-pSpecified
None,
);
/// Get a reference to the rsa aes oaep params's hash algorithm.
pub fn hash_algorithm(&self) -> Option<&AlgorithmIdentifier<'_>> {
self.hash_alg.as_ref()
}
/// Return the hash algorithm OID, or SHA1 if absent (RFC4055)
pub fn hash_algorithm_oid(&self) -> &'a Oid<'_> {
const SHA1: &Oid = &OID_HASH_SHA1;
self.hash_alg
.as_ref()
.map(|alg| &alg.algorithm)
.unwrap_or(SHA1)
}
/// Get a reference to the rsa ssa pss params's mask generation algorithm.
pub fn mask_gen_algorithm_raw(&self) -> Option<&AlgorithmIdentifier<'_>> {
self.mask_gen_alg.as_ref()
}
/// Get the rsa ssa pss params's mask generation algorithm.
///
/// If the algorithm encoding is invalid, raise an error `InvalidAlgorithmIdentifier`
pub fn mask_gen_algorithm(&self) -> Result<MaskGenAlgorithm<'_, '_>, X509Error> {
match self.mask_gen_alg.as_ref() {
Some(alg) => {
let (_, hash) = alg
.parameters()
.and_then(|any| Oid::from_der(any.data).ok())
.ok_or(X509Error::InvalidAlgorithmIdentifier)?;
Ok(MaskGenAlgorithm::new(alg.algorithm.clone(), hash))
}
_ => {
Ok(MaskGenAlgorithm::new(
oid! {1.2.840.113549.1.1.8}, // id-mgf1
OID_HASH_SHA1,
))
}
}
}
/// Return the pSourceFunc algorithm
pub fn p_source_alg(&'a self) -> &'a AlgorithmIdentifier<'a> {
self.p_source_alg.as_ref().unwrap_or(Self::EMPTY)
}
}
impl<'a> TryFrom<Any<'a>> for RsaAesOaepParams<'a> {
type Error = X509Error;
fn try_from(value: Any<'a>) -> Result<Self, Self::Error> {
Self::try_from(&value)
}
}
// hashFunc [0] AlgorithmIdentifier DEFAULT
// sha1Identifier,
// maskGenFunc [1] AlgorithmIdentifier DEFAULT
// mgf1SHA1Identifier,
// pSourceFunc [2] AlgorithmIdentifier DEFAULT
// pSpecifiedEmptyIdentifier }
impl<'a, 'b> TryFrom<&'b Any<'a>> for RsaAesOaepParams<'a> {
type Error = X509Error;
fn try_from(value: &'b Any<'a>) -> Result<Self, Self::Error> {
value.tag().assert_eq(Tag::Sequence)?;
let i = &value.data;
// let (i, hash_alg) = OptTaggedExplicit::<_, X509Error, 0>::from_der(i)?;
let (i, hash_alg) = OptTaggedParser::new(Class::ContextSpecific, Tag(0))
.parse_der(i, |_, inner| AlgorithmIdentifier::from_der(inner))?;
// let (i, mask_gen_algorithm) = OptTaggedExplicit::<_, Error, 1>::from_der(i)?;
let (i, mask_gen_alg) = OptTaggedParser::new(Class::ContextSpecific, Tag(1))
.parse_der(i, |_, inner| AlgorithmIdentifier::from_der(inner))?;
let (_, p_source_alg) = OptTaggedParser::new(Class::ContextSpecific, Tag(2))
.parse_der(i, |_, inner| AlgorithmIdentifier::from_der(inner))?;
let params = RsaAesOaepParams {
hash_alg,
mask_gen_alg,
p_source_alg,
};
Ok(params)
}
}
impl CheckDerConstraints for RsaAesOaepParams<'_> {
fn check_constraints(any: &Any) -> asn1_rs::Result<()> {
any.header.assert_constructed()?;
Ok(())
}
}
impl DerAutoDerive for RsaAesOaepParams<'_> {}
// ECC subject public key information [RFC5480](https://datatracker.ietf.org/doc/rfc5480/)
// ECParameters ::= CHOICE {
// namedCurve OBJECT IDENTIFIER
// -- implicitCurve NULL
// -- specifiedCurve SpecifiedECDomain
// }
// -- implicitCurve and specifiedCurve MUST NOT be used in PKIX.
// -- Details for SpecifiedECDomain can be found in [X9.62].
// -- Any future additions to this CHOICE should be coordinated
// -- with ANSI X9.

View File

@@ -0,0 +1,11 @@
use asn1_rs::{DerSequence, Integer};
/// ECDSA Signature Value (RFC3279)
// Ecdsa-Sig-Value ::= SEQUENCE {
// r INTEGER,
// s INTEGER }
#[derive(Debug, PartialEq, Eq, DerSequence)]
pub struct EcdsaSigValue<'a> {
pub r: Integer<'a>,
pub s: Integer<'a>,
}

220
vendor/x509-parser/src/time.rs vendored Normal file
View File

@@ -0,0 +1,220 @@
use asn1_rs::nom::Err;
use asn1_rs::{Error, FromDer, GeneralizedTime, Header, ParseResult, UtcTime};
use der_parser::ber::{Tag, MAX_OBJECT_SIZE};
use std::fmt;
use std::ops::{Add, Sub};
use time::macros::format_description;
use time::{Duration, OffsetDateTime};
use crate::error::{X509Error, X509Result};
/// An ASN.1 timestamp.
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub struct ASN1Time {
time: OffsetDateTime,
generalized: bool,
}
impl ASN1Time {
pub(crate) fn from_der_opt(i: &[u8]) -> X509Result<'_, Option<Self>> {
if i.is_empty() {
return Ok((i, None));
}
match parse_choice_of_time(i) {
Ok((rem, time)) => Ok((rem, Some(time))),
Err(Err::Error(Error::InvalidTag)) | Err(Err::Error(Error::UnexpectedTag { .. })) => {
Ok((i, None))
}
Err(_) => Err(Err::Error(X509Error::InvalidDate)),
}
}
#[inline]
pub const fn new(dt: OffsetDateTime) -> Self {
let generalized = dt.year() > 2049;
Self {
time: dt,
generalized,
}
}
#[inline]
pub const fn new_generalized(dt: OffsetDateTime) -> Self {
Self {
time: dt,
generalized: true,
}
}
#[inline]
pub const fn new_utc(dt: OffsetDateTime) -> Self {
Self {
time: dt,
generalized: false,
}
}
#[inline]
pub const fn to_datetime(&self) -> OffsetDateTime {
self.time
}
/// Makes a new `ASN1Time` from the number of non-leap seconds since Epoch
pub fn from_timestamp(secs: i64) -> Result<Self, X509Error> {
let dt = OffsetDateTime::from_unix_timestamp(secs).map_err(|_| X509Error::InvalidDate)?;
Ok(ASN1Time::new(dt))
}
/// Returns the number of non-leap seconds since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp").
#[inline]
pub fn timestamp(&self) -> i64 {
self.time.unix_timestamp()
}
/// Returns a `ASN1Time` which corresponds to the current date.
#[inline]
pub fn now() -> Self {
ASN1Time::new(OffsetDateTime::now_utc())
}
/// Returns an RFC 2822 date and time string such as `Tue, 1 Jul 2003 10:52:37 +0200`.
///
/// Conversion to RFC2822 date can fail if date cannot be represented in this format,
/// for example if year < 1900.
///
/// For an infallible conversion to string, use `.to_string()`.
#[inline]
pub fn to_rfc2822(self) -> Result<String, String> {
self.time
.format(&time::format_description::well_known::Rfc2822)
.map_err(|e| e.to_string())
}
/// Return `true` if date is encoded as UTCTime
///
/// According to RFC 5280, dates though year 2049 should be encoded as UTCTime, and
/// GeneralizedTime after 2029.
#[inline]
pub const fn is_utctime(&self) -> bool {
!self.generalized
}
/// Return `true` if date is encoded as GeneralizedTime
///
/// According to RFC 5280, dates though year 2049 should be encoded as UTCTime, and
/// GeneralizedTime after 2029.
#[inline]
pub const fn is_generalizedtime(&self) -> bool {
self.generalized
}
}
impl FromDer<'_, X509Error> for ASN1Time {
fn from_der(i: &[u8]) -> X509Result<'_, Self> {
let (rem, time) = parse_choice_of_time(i).map_err(|_| X509Error::InvalidDate)?;
Ok((rem, time))
}
}
pub(crate) fn parse_choice_of_time(i: &[u8]) -> ParseResult<'_, ASN1Time> {
if let Ok((rem, t)) = UtcTime::from_der(i) {
let dt = t.utc_adjusted_datetime()?;
return Ok((rem, ASN1Time::new_utc(dt)));
}
if let Ok((rem, t)) = GeneralizedTime::from_der(i) {
let dt = t.utc_datetime()?;
return Ok((rem, ASN1Time::new_generalized(dt)));
}
parse_malformed_date(i)
}
// allow relaxed parsing of UTCTime (ex: 370116130016+0000)
fn parse_malformed_date(i: &[u8]) -> ParseResult<'_, ASN1Time> {
#[allow(clippy::trivially_copy_pass_by_ref)]
// fn check_char(b: &u8) -> bool {
// (0x20 <= *b && *b <= 0x7f) || (*b == b'+')
// }
let (_rem, hdr) = Header::from_der(i)?;
let len = hdr.length().definite()?;
if len > MAX_OBJECT_SIZE {
return Err(Err::Error(Error::InvalidLength));
}
match hdr.tag() {
Tag::UtcTime => {
// // if we are in this function, the PrintableString could not be validated.
// // Accept it without validating charset, because some tools do not respect the charset
// // restrictions (for ex. they use '*' while explicingly disallowed)
// let (rem, data) = take(len as usize)(rem)?;
// if !data.iter().all(check_char) {
// return Err(nom::Err::Error(BerError::BerValueError));
// }
// let s = std::str::from_utf8(data).map_err(|_| BerError::BerValueError)?;
// let content = BerObjectContent::UTCTime(s);
// let obj = DerObject::from_header_and_content(hdr, content);
// Ok((rem, obj))
Err(Err::Error(Error::BerValueError))
}
_ => Err(Err::Error(Error::unexpected_tag(None, hdr.tag()))),
}
}
impl fmt::Display for ASN1Time {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let format = format_description!("[month repr:short] [day padding:space] [hour]:[minute]:[second] [year padding:none] [offset_hour sign:mandatory]:[offset_minute]");
let s = self
.time
.format(format)
.unwrap_or_else(|e| format!("Invalid date: {e}"));
f.write_str(&s)
}
}
impl Add<Duration> for ASN1Time {
type Output = Option<ASN1Time>;
#[inline]
fn add(self, rhs: Duration) -> Option<ASN1Time> {
Some(ASN1Time::new(self.time + rhs))
}
}
impl Sub<ASN1Time> for ASN1Time {
type Output = Option<Duration>;
#[inline]
fn sub(self, rhs: ASN1Time) -> Option<Duration> {
if self.time > rhs.time {
Some(self.time - rhs.time)
} else {
None
}
}
}
impl From<OffsetDateTime> for ASN1Time {
fn from(dt: OffsetDateTime) -> Self {
ASN1Time::new(dt)
}
}
#[cfg(test)]
mod tests {
use time::macros::datetime;
use super::ASN1Time;
#[test]
fn test_time_to_string() {
let d = datetime!(1 - 1 - 1 12:34:56 UTC);
let t = ASN1Time::from(d);
assert_eq!(t.to_string(), "Jan 1 12:34:56 1 +00:00".to_string());
}
#[test]
fn test_nonrfc2822_date() {
// test year < 1900
let d = datetime!(1 - 1 - 1 00:00:00 UTC);
let t = ASN1Time::from(d);
assert!(t.to_rfc2822().is_err());
}
}

19
vendor/x509-parser/src/utils.rs vendored Normal file
View File

@@ -0,0 +1,19 @@
/// Formats a slice to a colon-separated hex string (for ex `01:02:ff:ff`)
pub fn format_serial(i: &[u8]) -> String {
let mut s = i.iter().fold(String::with_capacity(3 * i.len()), |a, b| {
a + &format!("{b:02x}:")
});
s.pop();
s
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_serial() {
let b: &[u8] = &[1, 2, 3, 4, 0xff];
assert_eq!("01:02:03:04:ff", format_serial(b));
}
}

View File

@@ -0,0 +1,15 @@
use crate::certificate::*;
use crate::validate::*;
#[derive(Debug)]
pub struct X509CertificateValidator;
impl<'a> Validator<'a> for X509CertificateValidator {
type Item = X509Certificate<'a>;
fn validate<L: Logger>(&self, item: &'a Self::Item, l: &'_ mut L) -> bool {
let mut res = true;
res &= X509ExtensionsValidator.validate(&item.extensions(), l);
res
}
}

View File

@@ -0,0 +1,115 @@
use crate::extensions::*;
use crate::validate::*;
use std::collections::HashSet;
// extra-pedantic checks
const WARN_SHOULD_BE_CRITICAL: bool = false;
macro_rules! test_critical {
(MUST $ext:ident, $l:ident, $name:expr) => {
if !$ext.critical {
$l.err(&format!("Extension {} MUST be critical, but is not", $name));
}
};
(MUST NOT $ext:ident, $l:ident, $name:expr) => {
if $ext.critical {
$l.err(&format!("Extension {} MUST NOT be critical, but is", $name));
}
};
(SHOULD $ext:ident, $l:ident, $name:expr) => {
if WARN_SHOULD_BE_CRITICAL && !$ext.critical {
$l.warn(&format!(
"Extension {} SHOULD be critical, but is not",
$name
));
}
};
(SHOULD NOT $ext:ident, $l:ident, $name:expr) => {
if WARN_SHOULD_BE_CRITICAL && $ext.critical {
$l.warn(&format!(
"Extension {} SHOULD NOT be critical, but is",
$name
));
}
};
}
#[derive(Debug)]
pub struct X509ExtensionsValidator;
impl<'a> Validator<'a> for X509ExtensionsValidator {
type Item = &'a [X509Extension<'a>];
fn validate<L: Logger>(&self, item: &'a Self::Item, l: &'_ mut L) -> bool {
let mut res = true;
// check for duplicate extensions
{
let mut m = HashSet::new();
for ext in item.iter() {
if m.contains(&ext.oid) {
l.err(&format!("Duplicate extension {}", ext.oid));
res = false;
} else {
m.insert(ext.oid.clone());
}
}
}
for ext in item.iter() {
// specific extension checks
match ext.parsed_extension() {
ParsedExtension::AuthorityKeyIdentifier(aki) => {
// Conforming CAs MUST mark this extension as non-critical
test_critical!(MUST NOT ext, l, "AKI");
// issuer or serial is present must be either both present or both absent
if aki.authority_cert_issuer.is_some() ^ aki.authority_cert_serial.is_some() {
l.warn("AKI: only one of Issuer and Serial is present");
}
}
ParsedExtension::CertificatePolicies(policies) => {
// A certificate policy OID MUST NOT appear more than once in a
// certificate policies extension.
let mut policy_oids = HashSet::new();
for policy_info in policies {
if policy_oids.contains(&policy_info.policy_id) {
l.err(&format!(
"Certificate Policies: duplicate policy {}",
policy_info.policy_id
));
res = false;
} else {
policy_oids.insert(policy_info.policy_id.clone());
}
}
}
ParsedExtension::KeyUsage(ku) => {
// SHOULD be critical
test_critical!(SHOULD ext, l, "KeyUsage");
// When the keyUsage extension appears in a certificate, at least one of the bits
// MUST be set to 1.
if ku.flags == 0 {
l.err("KeyUsage: all flags are set to 0");
}
}
ParsedExtension::SubjectAlternativeName(san) => {
// SHOULD be non-critical
test_critical!(SHOULD NOT ext, l, "SubjectAltName");
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) {
l.warn(&format!("Invalid charset in 'SAN' entry '{s}'"));
}
}
_ => (),
}
}
}
_ => (),
}
}
res
}
}

View File

@@ -0,0 +1,83 @@
pub trait Logger {
fn warn(&mut self, message: &str);
fn err(&mut self, message: &str);
}
/// Simple Logger for [`Validator`](crate::validate::Validator) trait, storing messages in `Vec`
#[derive(Debug, Default)]
pub struct VecLogger {
warnings: Vec<String>,
errors: Vec<String>,
}
impl VecLogger {
/// Get stored warnings
pub fn warnings(&self) -> &[String] {
&self.warnings
}
/// Get stored errors
pub fn errors(&self) -> &[String] {
&self.errors
}
}
impl Logger for VecLogger {
fn warn(&mut self, message: &str) {
self.warnings.push(message.to_owned())
}
fn err(&mut self, message: &str) {
self.errors.push(message.to_owned())
}
}
/// Simple Logger for [`Validator`](crate::validate::Validator) trait, printing messages to `stderr`
#[derive(Debug, Default)]
pub struct StderrLogger;
impl Logger for StderrLogger {
fn warn(&mut self, message: &str) {
eprintln!("[W] {message}");
}
fn err(&mut self, message: &str) {
eprintln!("[E] {message}");
}
}
/// Simple Logger for [`Validator`](crate::validate::Validator) trait, using closures for `warn`/`err`.
#[derive(Debug, Default)]
pub struct CallbackLogger<W, E>
where
W: FnMut(&str),
E: FnMut(&str),
{
warn: W,
err: E,
}
impl<W, E> CallbackLogger<W, E>
where
W: FnMut(&str),
E: FnMut(&str),
{
pub fn new(warn: W, err: E) -> Self {
CallbackLogger { warn, err }
}
}
impl<W, E> Logger for CallbackLogger<W, E>
where
W: FnMut(&str),
E: FnMut(&str),
{
fn warn(&mut self, message: &str) {
(self.warn)(message);
}
fn err(&mut self, message: &str) {
(self.err)(message);
}
}

262
vendor/x509-parser/src/validate/mod.rs vendored Normal file
View File

@@ -0,0 +1,262 @@
mod certificate;
mod extensions;
mod loggers;
mod name;
mod structure;
use std::marker::PhantomData;
pub use certificate::*;
pub use extensions::*;
pub use loggers::*;
pub use name::*;
pub use structure::*;
/// Trait for validating item (for ex. validate X.509 structure)
///
/// # Examples
///
/// Using callbacks:
///
/// ```
/// use x509_parser::certificate::X509Certificate;
/// # #[allow(deprecated)]
/// use x509_parser::validate::Validate;
/// # #[allow(deprecated)]
/// #[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;
/// # #[allow(deprecated)]
/// use x509_parser::validate::Validate;
///
/// # #[allow(deprecated)]
/// #[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(())
/// }
/// ```
#[deprecated(since = "0.13.0", note = "please use `X509StructureValidator` instead")]
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)
}
}
/// Trait for build item validators (for ex. validate X.509 structure)
///
/// See [`X509StructureValidator`] for a default implementation, validating the
/// DER structure of a X.509 Certificate.
///
/// See implementors of the [`Logger`] trait for methods to collect or handle warnings and errors.
///
/// # Examples
///
/// Collecting warnings and errors to `Vec`:
///
/// ```
/// use x509_parser::certificate::X509Certificate;
/// use x509_parser::validate::*;
///
/// # #[allow(deprecated)]
/// #[cfg(feature = "validate")]
/// fn validate_certificate(x509: &X509Certificate<'_>) -> Result<(), &'static str> {
/// let mut logger = VecLogger::default();
/// println!(" Subject: {}", x509.subject());
/// // validate and print warnings and errors to stderr
/// let ok = X509StructureValidator.validate(&x509, &mut logger);
/// print!("Structure validation status: ");
/// if ok {
/// println!("Ok");
/// } else {
/// println!("FAIL");
/// }
/// for warning in logger.warnings() {
/// eprintln!(" [W] {}", warning);
/// }
/// for error in logger.errors() {
/// eprintln!(" [E] {}", error);
/// }
/// println!();
/// if !logger.errors().is_empty() {
/// return Err("validation failed");
/// }
/// Ok(())
/// }
/// ```
pub trait Validator<'a> {
/// The item to validate
type Item;
/// Attempts to validate current item.
///
/// Returns `true` if item was validated.
///
/// Call `l.warn()` if a non-fatal error was encountered, and `l.err()`
/// if the error is fatal. These functions receive a description of the error.
fn validate<L: Logger>(&self, item: &'a Self::Item, l: &'_ mut L) -> bool;
fn chain<V2>(self, v2: V2) -> ChainValidator<'a, Self, V2, Self::Item>
where
Self: Sized,
V2: Validator<'a, Item = Self::Item>,
{
ChainValidator {
v1: self,
v2,
_p: PhantomData,
}
}
}
#[derive(Debug)]
pub struct ChainValidator<'a, A, B, I>
where
A: Validator<'a, Item = I>,
B: Validator<'a, Item = I>,
{
v1: A,
v2: B,
_p: PhantomData<&'a ()>,
}
impl<'a, A, B, I> Validator<'a> for ChainValidator<'a, A, B, I>
where
A: Validator<'a, Item = I>,
B: Validator<'a, Item = I>,
{
type Item = I;
fn validate<L: Logger>(&'_ self, item: &'a Self::Item, l: &'_ mut L) -> bool {
self.v1.validate(item, l) & self.v2.validate(item, l)
}
}
#[allow(deprecated)]
#[cfg(test)]
mod tests {
use crate::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
}
}
struct V1Validator;
impl<'a> Validator<'a> for V1Validator {
type Item = V1;
fn validate<L: Logger>(&self, item: &'a Self::Item, l: &'_ mut L) -> bool {
if item.a > 10 {
l.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());
}
#[test]
fn validator_warn() {
let mut logger = VecLogger::default();
let v1 = V1 { a: 1 };
let res = V1Validator.validate(&v1, &mut logger);
assert!(res);
assert!(logger.warnings().is_empty());
assert!(logger.errors().is_empty());
// same, with one warning
let v20 = V1 { a: 20 };
let res = V1Validator.validate(&v20, &mut logger);
assert!(res);
assert_eq!(logger.warnings(), &["a is greater than 10".to_string()]);
assert!(logger.errors().is_empty());
}
}

32
vendor/x509-parser/src/validate/name.rs vendored Normal file
View File

@@ -0,0 +1,32 @@
use crate::validate::*;
use crate::x509::*;
use asn1_rs::Tag;
#[derive(Debug)]
pub struct X509NameStructureValidator;
impl<'a> Validator<'a> for X509NameStructureValidator {
type Item = X509Name<'a>;
fn validate<L: Logger>(&self, item: &'a Self::Item, l: &'_ mut L) -> bool {
let res = true;
// subject/issuer: verify charsets
// - wildcards in PrintableString
// - non-IA5 in IA5String
for attr in item.iter_attributes() {
match attr.attr_value().tag() {
Tag::PrintableString | Tag::Ia5String => {
let b = attr.attr_value().as_bytes();
if !b.iter().all(u8::is_ascii) {
l.warn(&format!(
"Invalid charset in X.509 Name, component {}",
attr.attr_type()
));
}
}
_ => (),
}
}
res
}
}

View File

@@ -0,0 +1,187 @@
use super::{Logger, Validator, X509NameStructureValidator};
use crate::certificate::*;
use crate::extensions::{GeneralName, ParsedExtension};
use crate::public_key::PublicKey;
use crate::x509::{SubjectPublicKeyInfo, X509Version};
/// Default X.509 structure validator for `X509Certificate`
///
/// This [`Validator`] iterates the X.509 Certificate fields, and verifies the
/// DER encoding and structure:
/// - numbers with wrong encoding/sign (for ex. serial number)
/// - strings with characters not allowed in DER type (for ex. '*' in `PrintableString`)
///
/// # Examples
///
/// Validate structure, collect warnings and errors to a `Vec`:
///
/// ```
/// use x509_parser::certificate::X509Certificate;
/// use x509_parser::validate::*;
///
/// # #[allow(deprecated)]
/// #[cfg(feature = "validate")]
/// fn validate_certificate(x509: &X509Certificate<'_>) -> Result<(), &'static str> {
/// let mut logger = VecLogger::default();
/// println!(" Subject: {}", x509.subject());
/// // validate and print warnings and errors to stderr
/// let ok = X509StructureValidator.validate(&x509, &mut logger);
/// print!("Structure validation status: ");
/// if ok {
/// println!("Ok");
/// } else {
/// println!("FAIL");
/// }
/// for warning in logger.warnings() {
/// eprintln!(" [W] {}", warning);
/// }
/// for error in logger.errors() {
/// eprintln!(" [E] {}", error);
/// }
/// println!();
/// if !logger.errors().is_empty() {
/// return Err("validation failed");
/// }
/// Ok(())
/// }
/// ```
#[derive(Debug, Default)]
pub struct X509StructureValidator;
impl<'a> Validator<'a> for X509StructureValidator {
type Item = X509Certificate<'a>;
fn validate<L: Logger>(&self, item: &'a Self::Item, l: &'_ mut L) -> bool {
let mut res = true;
res &= TbsCertificateStructureValidator.validate(&item.tbs_certificate, l);
res
}
}
/// Default X.509 structure validator for `TbsCertificate`
#[derive(Debug, Default)]
pub struct TbsCertificateStructureValidator;
impl<'a> Validator<'a> for TbsCertificateStructureValidator {
type Item = TbsCertificate<'a>;
fn validate<L: Logger>(&self, item: &'a Self::Item, l: &'_ mut L) -> bool {
let mut res = true;
// version must be 0, 1 or 2
if item.version.0 >= 3 {
l.err("Invalid version");
res = false;
}
let b = item.raw_serial();
if b.is_empty() {
l.err("Serial is empty");
res = false;
} else {
// check MSB of serial
if b[0] & 0x80 != 0 {
l.warn("Serial number is negative");
}
// check leading zeroes in serial
if b.len() > 1 && b[0] == 0 && b[1] & 0x80 == 0 {
l.warn("Leading zeroes in serial number");
}
}
// subject/issuer: verify charsets
res &= X509NameStructureValidator.validate(&item.subject, l);
res &= X509NameStructureValidator.validate(&item.issuer, l);
// subject public key
res &= X509PublicKeyValidator.validate(&item.subject_pki, l);
// validity: dates <= 2049 must use UTCTime, >= 2050 must use GeneralizedTime
let validity = item.validity();
let year_notbefore = validity.not_before.to_datetime().year();
if year_notbefore <= 2049 {
if !validity.not_before.is_utctime() {
l.warn("year <= 2049 should use UTCTime (notBefore)");
}
} else if !validity.not_before.is_generalizedtime() {
l.warn("year >= 2050 should use GeneralizedTime (notBefore)");
}
let year_notafter = validity.not_after.to_datetime().year();
if year_notafter <= 2049 {
if !validity.not_after.is_utctime() {
l.warn("year <= 2049 should use UTCTime (notAfter)");
}
} else if !validity.not_after.is_generalizedtime() {
l.warn("year >= 2050 should use GeneralizedTime (notAfter)");
}
if item.version == X509Version::V1 {
// unique identifiers: version must 2 or 3
if item.issuer_uid.is_some() {
l.warn("issuerUniqueID present but version 1");
}
if item.subject_uid.is_some() {
l.warn("subjectUniqueID present but version 1");
}
}
// extensions require v3
if !item.extensions().is_empty() && item.version != X509Version::V3 {
l.err("Extensions present but version is not 3");
res = false;
}
// check for parse errors or unsupported extensions
for ext in item.extensions() {
if let ParsedExtension::UnsupportedExtension { .. } = &ext.parsed_extension {
l.warn(&format!("Unsupported extension {}", ext.oid));
}
if let ParsedExtension::ParseError { error } = &ext.parsed_extension {
l.err(&format!("Parse error in extension {}: {}", ext.oid, error));
res = false;
}
}
// check extensions
for ext in item.extensions() {
// 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) {
l.warn(&format!("Invalid charset in 'SAN' entry '{s}'"));
}
}
_ => (),
}
}
}
}
res
}
}
#[derive(Debug, Default)]
pub struct X509PublicKeyValidator;
impl<'a> Validator<'a> for X509PublicKeyValidator {
type Item = SubjectPublicKeyInfo<'a>;
fn validate<L: Logger>(&self, item: &'a Self::Item, l: &'_ mut L) -> bool {
let mut res = true;
// res &= TbsCertificateStructureValidator.validate(&item.tbs_certificate, l);
match item.parsed() {
Ok(PublicKey::RSA(rsa)) => {
if rsa.modulus[0] & 0x80 != 0 {
l.warn("Public key: (RSA) modulus is negative");
}
if rsa.exponent[0] & 0x80 != 0 {
l.warn("Public key: (RSA) exponent is negative");
}
}
Ok(PublicKey::Unknown(_b)) => {
l.warn("Unknown public key type");
}
Ok(_) => {}
Err(_) => {
l.err("Invalid public key");
res = false;
}
}
res
}
}

116
vendor/x509-parser/src/verify.rs vendored Normal file
View File

@@ -0,0 +1,116 @@
use crate::prelude::*;
use crate::signature_algorithm::RsaSsaPssParams;
use asn1_rs::{Any, BitString};
use oid_registry::{
OID_EC_P256, OID_NIST_EC_P384, OID_NIST_HASH_SHA256, OID_NIST_HASH_SHA384,
OID_NIST_HASH_SHA512, OID_PKCS1_RSASSAPSS, OID_PKCS1_SHA1WITHRSA, OID_PKCS1_SHA256WITHRSA,
OID_PKCS1_SHA384WITHRSA, OID_PKCS1_SHA512WITHRSA, OID_SHA1_WITH_RSA, OID_SIG_ECDSA_WITH_SHA256,
OID_SIG_ECDSA_WITH_SHA384, OID_SIG_ED25519,
};
use std::convert::TryFrom;
// Since the `signature` object is similar in ring and in aws-lc-rs, we just use simple logic
// to determine which one to use.
// If both verify and verify-aws features are enabled, aws will be used.
#[cfg(feature = "verify-aws")]
use aws_lc_rs::signature;
#[cfg(all(feature = "verify", not(feature = "verify-aws")))]
use ring::signature;
/// Verify the cryptographic signature of the raw data (can be a certificate, a CRL or a CSR).
///
/// `public_key` is the public key of the **signer**.
///
/// Not all algorithms are supported, this function is limited to what `aws_lc_rs` or `ring` supports.
pub fn verify_signature(
public_key: &SubjectPublicKeyInfo,
signature_algorithm: &AlgorithmIdentifier,
signature_value: &BitString,
raw_data: &[u8],
) -> Result<(), X509Error> {
let AlgorithmIdentifier {
algorithm: signature_algorithm,
parameters: signature_algorithm_parameters,
} = &signature_algorithm;
// identify verification algorithm
let verification_alg: &dyn signature::VerificationAlgorithm = if *signature_algorithm
== OID_PKCS1_SHA1WITHRSA
|| *signature_algorithm == OID_SHA1_WITH_RSA
{
&signature::RSA_PKCS1_1024_8192_SHA1_FOR_LEGACY_USE_ONLY
} else if *signature_algorithm == OID_PKCS1_SHA256WITHRSA {
&signature::RSA_PKCS1_2048_8192_SHA256
} else if *signature_algorithm == OID_PKCS1_SHA384WITHRSA {
&signature::RSA_PKCS1_2048_8192_SHA384
} else if *signature_algorithm == OID_PKCS1_SHA512WITHRSA {
&signature::RSA_PKCS1_2048_8192_SHA512
} else if *signature_algorithm == OID_PKCS1_RSASSAPSS {
get_rsa_pss_verification_algo(signature_algorithm_parameters)
.ok_or(X509Error::SignatureUnsupportedAlgorithm)?
} else if *signature_algorithm == OID_SIG_ECDSA_WITH_SHA256 {
get_ec_curve_sha(&public_key.algorithm, 256)
.ok_or(X509Error::SignatureUnsupportedAlgorithm)?
} else if *signature_algorithm == OID_SIG_ECDSA_WITH_SHA384 {
get_ec_curve_sha(&public_key.algorithm, 384)
.ok_or(X509Error::SignatureUnsupportedAlgorithm)?
} else if *signature_algorithm == OID_SIG_ED25519 {
&signature::ED25519
} else {
return Err(X509Error::SignatureUnsupportedAlgorithm);
};
// get public key
let key =
signature::UnparsedPublicKey::new(verification_alg, &public_key.subject_public_key.data);
// verify signature
key.verify(raw_data, &signature_value.data)
.or(Err(X509Error::SignatureVerificationError))
}
/// Find the verification algorithm for the given EC curve and SHA digest size
///
/// Not all algorithms are supported, we are limited to what `aws_lc_rs` or `ring`supports.
fn get_ec_curve_sha(
pubkey_alg: &AlgorithmIdentifier,
sha_len: usize,
) -> Option<&'static dyn signature::VerificationAlgorithm> {
let curve_oid = pubkey_alg.parameters.as_ref()?.as_oid().ok()?;
// let curve_oid = pubkey_alg.parameters.as_ref()?.as_oid().ok()?;
if curve_oid == OID_EC_P256 {
match sha_len {
256 => Some(&signature::ECDSA_P256_SHA256_ASN1),
384 => Some(&signature::ECDSA_P256_SHA384_ASN1),
_ => None,
}
} else if curve_oid == OID_NIST_EC_P384 {
match sha_len {
256 => Some(&signature::ECDSA_P384_SHA256_ASN1),
384 => Some(&signature::ECDSA_P384_SHA384_ASN1),
_ => None,
}
} else {
None
}
}
/// Find the verification algorithm for the given RSA-PSS parameters
///
/// Not all algorithms are supported, we are limited to what `aws_lc_rs` or `ring` supports.
/// Notably, the SHA-1 hash algorithm is not supported.
fn get_rsa_pss_verification_algo(
params: &Option<Any>,
) -> Option<&'static dyn signature::VerificationAlgorithm> {
let params = params.as_ref()?;
let params = RsaSsaPssParams::try_from(params).ok()?;
let hash_algo = params.hash_algorithm_oid();
if *hash_algo == OID_NIST_HASH_SHA256 {
Some(&signature::RSA_PSS_2048_8192_SHA256)
} else if *hash_algo == OID_NIST_HASH_SHA384 {
Some(&signature::RSA_PSS_2048_8192_SHA384)
} else if *hash_algo == OID_NIST_HASH_SHA512 {
Some(&signature::RSA_PSS_2048_8192_SHA512)
} else {
None
}
}

View File

@@ -0,0 +1,302 @@
use asn1_rs::BitString;
use oid_registry::*;
use crate::certificate::*;
use crate::extensions::*;
use crate::x509::*;
/// Visitor pattern for [`X509Certificate`]
///
/// # Extensions
///
/// Visitor methods are provided for extensions, both in a generic way (receiving a [`X509Extension`]
/// object) and in a specific way for standard extensions (for ex, `visit_extension_aki` receives a
/// [`AuthorityKeyIdentifier`]).
///
/// For a specific method to be called, the extension OID must be correct and the extension must be
/// successfully parsed as the specific type.
///
/// A specific method can be called multiple times, if the extension is present multiple times.
///
/// Extension parsing methods are redundant. This is not a problem because default methods do nothing,
/// but if a trait implementation provides several `visit_extension...` methods it must be aware
/// that it will visit the same extension multiple times.
///
/// # Example
///
/// ```rust
/// use x509_parser::prelude::*;
/// use x509_parser::visitor::X509CertificateVisitor;
/// #[derive(Debug, Default)]
/// struct SubjectIssuerVisitor {
/// issuer: String,
/// subject: String,
/// is_ca: bool,
/// }
///
/// impl X509CertificateVisitor for SubjectIssuerVisitor {
/// fn visit_issuer(&mut self, name: &X509Name<'_>) {
/// self.issuer = name.to_string();
/// }
///
/// fn visit_subject(&mut self, name: &X509Name<'_>) {
/// self.subject = name.to_string();
/// }
///
/// fn visit_extension_basic_constraints(&mut self, bc: &BasicConstraints) {
/// self.is_ca = bc.ca;
/// }
/// }
/// ```
pub trait X509CertificateVisitor {
/// Run the provided visitor (`self`) over the [`X509Certificate`] object
fn walk(&mut self, x509: &X509Certificate)
where
Self: Sized,
{
x509.walk(self);
}
/// Invoked for the "TBSCertificate" field of the X.509 Certificate, before visiting children
fn visit_tbs_certificate(&mut self, _tbs: &TbsCertificate) {}
/// Invoked for the "signatureAlgorithm" field of the X.509 Certificate
///
/// Note: this is the "signatureAlgorithm" in the "Certificate" sequence. According to the
/// specifications, it should be equal to "signature" field from the "TBSCertificate" sequence.
fn visit_signature_algorithm(&mut self, _algorithm: &AlgorithmIdentifier) {}
/// Invoked for the "signatureValue" field of the TBSCertificate
fn visit_signature_value(&mut self, _signature: &BitString) {}
/// Invoked for the "version" field of the TBSCertificate
fn visit_version(&mut self, _version: &X509Version) {}
/// Invoked for the "serialNumber" field of the TBSCertificate
fn visit_serial_number(&mut self, _serial: &[u8]) {}
/// Invoked for the "signature" field of the TBSCertificate
///
/// Note: this is the "signature" field from the "TBSCertificate" sequence. According to the
/// specifications, it should be equal to "signatureAlgorithm" in the "Certificate" sequence.
fn visit_tbs_signature_algorithm(&mut self, _algorithm: &AlgorithmIdentifier) {}
/// Invoked for the "issuer" field of the TBSCertificate
fn visit_issuer(&mut self, _name: &X509Name) {}
/// Invoked for the "validity" field of the TBSCertificate
fn visit_validity(&mut self, _validity: &Validity) {}
/// Invoked for the "subject" field of the TBSCertificate
fn visit_subject(&mut self, _name: &X509Name) {}
/// Invoked for the "subjectPublicKeyInfo" field of the TBSCertificate
fn visit_subject_public_key_info(&mut self, _subject_pki: &SubjectPublicKeyInfo) {}
/// Invoked for the "issuerUniqueID" field of the TBSCertificate
fn visit_issuer_unique_id(&mut self, _id: Option<&UniqueIdentifier>) {}
/// Invoked for the "subjectUniqueID" field of the TBSCertificate
fn visit_subject_unique_id(&mut self, _id: Option<&UniqueIdentifier>) {}
/// Invoked for extensions, before visiting children
fn pre_visit_extensions(&mut self, _extensions: &[X509Extension]) {}
/// Invoked for any extension that appear in the X.509 Certificate
///
/// Note: this method may be redundant with any other extension visitor method
fn visit_extension(&mut self, _extension: &X509Extension) {}
/// Invoked for extensions, after visiting children
fn post_visit_extensions(&mut self, _extensions: &[X509Extension]) {}
/// Invoked for the "Authority Key Identifier" extension (if present)
fn visit_extension_aki(&mut self, _aki: &AuthorityKeyIdentifier) {}
/// Invoked for the "Subject Key Identifier" extension (if present)
fn visit_extension_ski(&mut self, _id: &KeyIdentifier) {}
/// Invoked for the "Key Usage" extension (if present)
fn visit_extension_key_usage(&mut self, _usage: &KeyUsage) {}
/// Invoked for the "Certificate Policies" extension (if present)
fn visit_extension_certificate_policies(&mut self, _policies: &CertificatePolicies) {}
/// Invoked for the "Subject Alternative Name" extension (if present)
fn visit_extension_subject_alternative_name(&mut self, _san: &SubjectAlternativeName) {}
/// Invoked for the "Issuer Alternative Name" extension (if present)
fn visit_extension_issuer_alternative_name(&mut self, _ian: &IssuerAlternativeName) {}
/// Invoked for the "Basic Constraints" extension (if present)
fn visit_extension_basic_constraints(&mut self, _bc: &BasicConstraints) {}
/// Invoked for the "Name Constraints" extension (if present)
fn visit_extension_name_constraints(&mut self, _constraints: &NameConstraints) {}
/// Invoked for the "Name Constraints" extension (if present)
fn visit_extension_nscert_comment(&mut self, _nscert_comment: &str) {}
/// Invoked for the "Name Constraints" extension (if present)
fn visit_extension_nscert_type(&mut self, _nscert_type: &NSCertType) {}
/// Invoked for the "Policy Constraints" extension (if present)
fn visit_extension_policy_constraints(&mut self, _constraints: &PolicyConstraints) {}
/// Invoked for the "Policy Mappings" extension (if present)
fn visit_extension_policy_mappings(&mut self, _mappings: &PolicyMappings) {}
/// Invoked for the "Extended Key Usage" extension (if present)
fn visit_extension_extended_key_usage(&mut self, _usage: &ExtendedKeyUsage) {}
/// Invoked for the "CRL Distribution Points" extension (if present)
fn visit_extension_crl_distribution_points(&mut self, _crl: &CRLDistributionPoints) {}
/// Invoked for the "Inhibit anyPolicy" extension (if present)
fn visit_extension_inhibit_anypolicy(&mut self, _policy: &InhibitAnyPolicy) {}
/// Invoked for the "Authority Information Access" extension (if present)
fn visit_extension_authority_information_access(&mut self, _info: &AuthorityInfoAccess) {}
/// Invoked for the "Signed Certificate Timestamp" (SCT) extension (if present)
fn visit_extension_sct(&mut self, _sct: &[SignedCertificateTimestamp]) {}
/// Invoked for any other extension than the specific (recognized) types
///
/// This can happen for several reasons:
/// - the parser did not recognize the extension content
/// - the parser was explicitly asked to not parse extension content
/// - the extension could be correct (for ex in a CRL), but is not supposed to be part of a Certificate
fn visit_extension_unknown(&mut self, _ext: &X509Extension) {}
/// Invoked for any extension than caused a parse error
///
/// Normally, this should not match anything except for invalid data.
/// This could match any known extension malformed or wrongly encoded.
fn visit_extension_parse_error(
&mut self,
_extension: &X509Extension,
_error: &asn1_rs::Err<asn1_rs::Error>,
) {
}
}
impl X509Certificate<'_> {
/// Run the provided [`X509CertificateVisitor`] over the X.509 Certificate (`self`)
pub fn walk<V: X509CertificateVisitor>(&self, visitor: &mut V) {
visitor.visit_tbs_certificate(&self.tbs_certificate);
self.tbs_certificate.walk(visitor);
visitor.visit_signature_algorithm(&self.signature_algorithm);
visitor.visit_signature_value(&self.signature_value);
}
}
impl TbsCertificate<'_> {
/// Run the provided `visitor` over the [`TbsCertificate`] object
pub fn walk<V: X509CertificateVisitor>(&self, visitor: &mut V) {
// shorten name to reduce line length
let v = visitor;
v.visit_version(&self.version);
v.visit_serial_number(self.raw_serial());
v.visit_tbs_signature_algorithm(&self.signature);
v.visit_issuer(&self.issuer);
v.visit_validity(&self.validity);
v.visit_subject(&self.subject);
v.visit_subject_public_key_info(&self.subject_pki);
v.visit_issuer_unique_id(self.issuer_uid.as_ref());
v.visit_subject_unique_id(self.subject_uid.as_ref());
v.pre_visit_extensions(self.extensions());
for extension in self.extensions() {
v.visit_extension(extension);
match extension.parsed_extension() {
ParsedExtension::AuthorityInfoAccess(info) => {
v.visit_extension_authority_information_access(info)
}
ParsedExtension::AuthorityKeyIdentifier(aki) => v.visit_extension_aki(aki),
ParsedExtension::BasicConstraints(bc) => v.visit_extension_basic_constraints(bc),
ParsedExtension::CertificatePolicies(policies) => {
v.visit_extension_certificate_policies(policies)
}
ParsedExtension::CRLDistributionPoints(crl) => {
v.visit_extension_crl_distribution_points(crl)
}
ParsedExtension::ExtendedKeyUsage(usage) => {
v.visit_extension_extended_key_usage(usage)
}
ParsedExtension::InhibitAnyPolicy(policy) => {
v.visit_extension_inhibit_anypolicy(policy)
}
ParsedExtension::IssuerAlternativeName(ian) => {
v.visit_extension_issuer_alternative_name(ian)
}
ParsedExtension::KeyUsage(usage) => v.visit_extension_key_usage(usage),
ParsedExtension::NSCertType(nscert_type) => {
v.visit_extension_nscert_type(nscert_type)
}
ParsedExtension::NameConstraints(constraints) => {
v.visit_extension_name_constraints(constraints)
}
ParsedExtension::NsCertComment(comment) => {
v.visit_extension_nscert_comment(comment)
}
ParsedExtension::PolicyConstraints(constraints) => {
v.visit_extension_policy_constraints(constraints)
}
ParsedExtension::PolicyMappings(mappings) => {
v.visit_extension_policy_mappings(mappings)
}
ParsedExtension::SCT(sct) => v.visit_extension_sct(sct),
ParsedExtension::SubjectAlternativeName(san) => {
v.visit_extension_subject_alternative_name(san)
}
ParsedExtension::SubjectKeyIdentifier(id) => v.visit_extension_ski(id),
ParsedExtension::ParseError { error } => {
v.visit_extension_parse_error(extension, error)
}
_ => v.visit_extension_unknown(extension),
}
}
v.post_visit_extensions(self.extensions());
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::FromDer;
static IGCA_DER: &[u8] = include_bytes!("../../assets/IGC_A.der");
#[test]
fn visitor_certificate() {
#[derive(Debug, Default)]
struct SubjectIssuerVisitor {
issuer: String,
subject: String,
is_ca: bool,
}
impl X509CertificateVisitor for SubjectIssuerVisitor {
fn visit_issuer(&mut self, name: &X509Name) {
self.issuer = name.to_string();
}
fn visit_subject(&mut self, name: &X509Name) {
self.subject = name.to_string();
}
fn visit_extension_basic_constraints(&mut self, bc: &BasicConstraints) {
self.is_ca = bc.ca;
}
}
let mut visitor = SubjectIssuerVisitor::default();
let (_, x509) = X509Certificate::from_der(IGCA_DER).unwrap();
x509.walk(&mut visitor);
assert!(!visitor.issuer.is_empty());
assert!(visitor.is_ca);
assert_eq!(&visitor.issuer, &visitor.subject);
}
}

View File

@@ -0,0 +1,225 @@
use asn1_rs::BitString;
use der_parser::num_bigint::BigUint;
use oid_registry::*;
use crate::extensions::*;
use crate::revocation_list::*;
use crate::time::ASN1Time;
use crate::x509::*;
/// Visitor pattern for [`CertificateRevocationList`]
///
/// # Extensions
///
/// Visitor methods are provided for extensions, both in a generic way (receiving a [`X509Extension`]
/// object) and in a specific way for standard extensions (for ex, `visit_extension_aki` receives a
/// [`AuthorityKeyIdentifier`]).
///
/// For a specific method to be called, the extension OID must be correct and the extension must be
/// successfully parsed as the specific type.
///
/// A specific method can be called multiple times, if the extension is present multiple times.
///
/// Extension parsing methods are redundant. This is not a problem because default methods do nothing,
/// but if a trait implementation provides several `visit_extension...` methods it must be aware
/// that it will visit the same extension multiple times.
///
/// # Example
///
/// ```rust
/// use der_parser::num_bigint::BigUint;
/// use x509_parser::prelude::*;
/// use x509_parser::visitor::CertificateRevocationListVisitor;
/// #[derive(Debug, Default)]
/// struct RevokedCertsVisitor {
/// certificates: Vec<BigUint>,
/// }
///
/// impl CertificateRevocationListVisitor for RevokedCertsVisitor {
/// fn visit_revoked_certificate(&mut self, certificate: &RevokedCertificate<'_>) {
/// self.certificates.push(certificate.user_certificate.clone());
/// }
/// }
/// ```
pub trait CertificateRevocationListVisitor {
/// Run the provided visitor (`self`) over the Certificate Revocation List
fn walk(&mut self, crl: &CertificateRevocationList)
where
Self: Sized,
{
crl.walk(self);
}
/// Invoked for the "tbsCertList" field of the Certificate Revocation List, before visiting children
fn visit_tbs_cert_list(&mut self, _tbs: &TbsCertList) {}
/// Invoked for the "signatureAlgorithm" field of the Certificate Revocation List
///
/// Note: this is the "signatureAlgorithm" in the "CertificateList" sequence. According to the
/// specifications, it should be equal to "signature" field from the "TBSCertificate" sequence.
fn visit_signature_algorithm(&mut self, _algorithm: &AlgorithmIdentifier) {}
/// Invoked for the "signatureValue" field of the TBSCertList
fn visit_signature_value(&mut self, _signature: &BitString) {}
/// Invoked for the "version" field of the TBSCertList
fn visit_version(&mut self, _version: Option<&X509Version>) {}
/// Invoked for the "signature" field of the TBSCertList
///
/// Note: this is the "signature" field from the "TBSCertList" sequence. According to the
/// specifications, it should be equal to "signatureAlgorithm" in the "CertificateList" sequence.
fn visit_tbs_signature_algorithm(&mut self, _algorithm: &AlgorithmIdentifier) {}
/// Invoked for the "issuer" field of the TBSCertList
fn visit_issuer(&mut self, _name: &X509Name) {}
/// Invoked for the "thisUpdate" field of the TBSCertList
fn visit_this_update(&mut self, _time: &ASN1Time) {}
/// Invoked for the "nextUpdate" field of the TBSCertList
fn visit_next_update(&mut self, _time: Option<&ASN1Time>) {}
/// Invoked for revoked certificate that appear in the TBSCertList
fn visit_revoked_certificates(&mut self, _certificate: &[RevokedCertificate]) {}
/// Invoked for any revoked certificates that appear in the TBSCertList
///
/// Note: this function is redundant with `visit_revoked_certificates`
fn visit_revoked_certificate(&mut self, _certificate: &RevokedCertificate) {}
/// Invoked for extensions, before visiting children
fn pre_visit_extensions(&mut self, _extensions: &[X509Extension]) {}
/// Invoked for any extension that appear in the TBSCertList
///
/// Note: this method may be redundant with any other extension visitor method
fn visit_extension(&mut self, _extension: &X509Extension) {}
/// Invoked for extensions, after visiting children
fn post_visit_extensions(&mut self, _extensions: &[X509Extension]) {}
/// Invoked for the "Authority Key Identifier" (if present)
fn visit_extension_aki(&mut self, _aki: &AuthorityKeyIdentifier) {}
/// Invoked for the "Issuer Alternative Name" (if present)
fn visit_extension_issuer_alternative_name(&mut self, _ian: &IssuerAlternativeName) {}
/// Invoked for the "CRL Number" (if present)
fn visit_extension_crl_number(&mut self, _number: &BigUint) {}
/// Invoked for the "Issuing Distribution Point" (if present)
fn visit_extension_issuing_distribution_point(&mut self, _dp: &IssuingDistributionPoint) {}
/// Invoked for the "Authority Information Access" (if present)
fn visit_extension_authority_information_access(&mut self, _info: &AuthorityInfoAccess) {}
/// Invoked for the "Reason Code" (if present)
fn visit_extension_reason_code(&mut self, _code: &ReasonCode) {}
/// Invoked for the "Invalidity Date" (if present)
fn visit_extension_invalidity_date(&mut self, _time: &ASN1Time) {}
/// Invoked for the "Signed Certificate Timestamp" (SCT) (if present)
fn visit_extension_sct(&mut self, _sct: &[SignedCertificateTimestamp]) {}
/// Invoked for any other extension than the specific (recognized) types
///
/// This can happen for several reasons:
/// - the parser did not recognize the extension content
/// - the parser was explicitly asked to not parse extension content
/// - the extension could be correct (for ex in a CRL), but is not supposed to be part of a Certificate
fn visit_extension_unknown(&mut self, _ext: &X509Extension) {}
/// Invoked for any extension than caused a parse error
///
/// Normally, this should not match anything except for invalid data.
/// This could match any known extension malformed or wrongly encoded.
fn visit_extension_parse_error(
&mut self,
_extension: &X509Extension,
_error: &asn1_rs::Err<asn1_rs::Error>,
) {
}
}
impl CertificateRevocationList<'_> {
/// Run the provided [`CertificateRevocationListVisitor`] over the Certificate Revocation List (`self`)
pub fn walk<V: CertificateRevocationListVisitor>(&self, visitor: &mut V) {
visitor.visit_tbs_cert_list(&self.tbs_cert_list);
self.tbs_cert_list.walk(visitor);
visitor.visit_signature_algorithm(&self.signature_algorithm);
visitor.visit_signature_value(&self.signature_value);
}
}
impl TbsCertList<'_> {
/// Run the provided `visitor` over the [`TbsCertList`] object
pub fn walk<V: CertificateRevocationListVisitor>(&self, visitor: &mut V) {
// shorten name to reduce line length
let v = visitor;
v.visit_version(self.version.as_ref());
v.visit_tbs_signature_algorithm(&self.signature);
v.visit_issuer(&self.issuer);
v.visit_this_update(&self.this_update);
v.visit_next_update(self.next_update.as_ref());
v.visit_revoked_certificates(&self.revoked_certificates);
for certificate in &self.revoked_certificates {
v.visit_revoked_certificate(certificate);
}
v.pre_visit_extensions(self.extensions());
for extension in self.extensions() {
v.visit_extension(extension);
match extension.parsed_extension() {
ParsedExtension::AuthorityInfoAccess(info) => {
v.visit_extension_authority_information_access(info)
}
ParsedExtension::AuthorityKeyIdentifier(aki) => v.visit_extension_aki(aki),
ParsedExtension::CRLNumber(number) => v.visit_extension_crl_number(number),
ParsedExtension::InvalidityDate(time) => v.visit_extension_invalidity_date(time),
ParsedExtension::IssuerAlternativeName(ian) => {
v.visit_extension_issuer_alternative_name(ian)
}
ParsedExtension::IssuingDistributionPoint(dp) => {
v.visit_extension_issuing_distribution_point(dp)
}
ParsedExtension::ReasonCode(code) => v.visit_extension_reason_code(code),
ParsedExtension::SCT(sct) => v.visit_extension_sct(sct),
ParsedExtension::ParseError { error } => {
v.visit_extension_parse_error(extension, error)
}
_ => v.visit_extension_unknown(extension),
}
}
v.post_visit_extensions(self.extensions());
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::FromDer;
static CRL: &[u8] = include_bytes!("../../assets/example.crl");
#[test]
fn visitor_crl() {
#[derive(Debug, Default)]
struct RevokedCertsVisitor {
certificates: Vec<BigUint>,
}
impl CertificateRevocationListVisitor for RevokedCertsVisitor {
fn visit_revoked_certificate(&mut self, certificate: &RevokedCertificate) {
self.certificates.push(certificate.user_certificate.clone());
}
}
let mut visitor = RevokedCertsVisitor::default();
let (_, crl) = CertificateRevocationList::from_der(CRL).unwrap();
crl.walk(&mut visitor);
assert_eq!(visitor.certificates.len(), 5);
}
}

5
vendor/x509-parser/src/visitor/mod.rs vendored Normal file
View File

@@ -0,0 +1,5 @@
mod certificate_visitor;
mod crl_visitor;
pub use certificate_visitor::*;
pub use crl_visitor::*;

663
vendor/x509-parser/src/x509.rs vendored Normal file
View File

@@ -0,0 +1,663 @@
//! X.509 objects and types
//!
//! Based on RFC5280
//!
use crate::error::{X509Error, X509Result};
use crate::objects::*;
use crate::public_key::*;
use asn1_rs::{
Any, BitString, BmpString, DerSequence, FromBer, FromDer, OptTaggedParser, ParseResult,
};
use core::convert::TryFrom;
use data_encoding::HEXUPPER;
use der_parser::ber::MAX_OBJECT_SIZE;
use der_parser::der::*;
use der_parser::error::*;
use der_parser::num_bigint::BigUint;
use der_parser::*;
use nom::branch::alt;
use nom::bytes::complete::take;
use nom::combinator::{complete, map};
use nom::multi::{many0, many1};
use nom::{Err, Offset};
use oid_registry::*;
use rusticata_macros::newtype_enum;
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 {
/// Parse `[0]` EXPLICIT Version DEFAULT v1
pub(crate) fn from_der_tagged_0(i: &[u8]) -> X509Result<'_, X509Version> {
let (rem, opt_version) = OptTaggedParser::from(0)
.parse_der(i, |_, data| Self::from_der(data))
.map_err(Err::convert)?;
let version = opt_version.unwrap_or(X509Version::V1);
Ok((rem, version))
}
}
// Version ::= INTEGER { v1(0), v2(1), v3(2) }
impl<'a> FromDer<'a, X509Error> for X509Version {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
map(<u32>::from_der, X509Version)(i).map_err(|_| Err::Error(X509Error::InvalidVersion))
}
}
newtype_enum! {
impl display X509Version {
V1 = 0,
V2 = 1,
V3 = 2,
}
}
/// A generic attribute type and value
///
/// These objects are used as [`RelativeDistinguishedName`] components.
#[derive(Clone, Debug, PartialEq)]
pub struct AttributeTypeAndValue<'a> {
attr_type: Oid<'a>,
attr_value: Any<'a>, // ANY -- DEFINED BY AttributeType
}
impl<'a> AttributeTypeAndValue<'a> {
/// Builds a new `AttributeTypeAndValue`
#[inline]
pub const fn new(attr_type: Oid<'a>, attr_value: Any<'a>) -> Self {
AttributeTypeAndValue {
attr_type,
attr_value,
}
}
/// Returns the attribute type
#[inline]
pub const fn attr_type(&self) -> &Oid<'a> {
&self.attr_type
}
/// Returns the attribute value, as `ANY`
#[inline]
pub const fn attr_value(&self) -> &Any<'a> {
&self.attr_value
}
/// 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> {
// TODO: replace this with helper function, when it is added to asn1-rs
match self.attr_value.tag() {
Tag::NumericString | Tag::PrintableString | Tag::Utf8String | Tag::Ia5String => {
let s = core::str::from_utf8(self.attr_value.data)
.map_err(|_| X509Error::InvalidAttributes)?;
Ok(s)
}
t => Err(X509Error::Der(Error::unexpected_tag(None, t))),
}
}
/// Get the content as a slice.
#[inline]
pub fn as_slice(&self) -> &[u8] {
self.attr_value.as_bytes()
}
}
impl<'a, 'b> TryFrom<&'a AttributeTypeAndValue<'b>> for &'a str {
type Error = X509Error;
fn try_from(value: &'a AttributeTypeAndValue<'b>) -> Result<Self, Self::Error> {
value.attr_value.as_str().map_err(|e| e.into())
}
}
impl<'a, 'b> From<&'a AttributeTypeAndValue<'b>> for &'a [u8] {
fn from(value: &'a AttributeTypeAndValue<'b>) -> Self {
value.as_slice()
}
}
// AttributeTypeAndValue ::= SEQUENCE {
// type AttributeType,
// value AttributeValue }
impl<'a> FromDer<'a, X509Error> for AttributeTypeAndValue<'a> {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
parse_der_sequence_defined_g(|i, _| {
let (i, attr_type) = Oid::from_der(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]) -> ParseResult<'_, Any<'_>, Error> {
alt((Any::from_der, parse_malformed_string))(i)
}
fn parse_malformed_string(i: &[u8]) -> ParseResult<'_, Any<'_>, Error> {
let (rem, hdr) = Header::from_der(i)?;
let len = hdr.length().definite()?;
if len > MAX_OBJECT_SIZE {
return Err(Err::Error(Error::InvalidLength));
}
match hdr.tag() {
Tag::PrintableString => {
// if we are in this function, the PrintableString could not be validated.
// Accept it without validating charset, because some tools do not respect the charset
// restrictions (for ex. they use '*' while explicingly disallowed)
let (rem, data) = take(len)(rem)?;
// check valid encoding
let _ = std::str::from_utf8(data).map_err(|_| Error::BerValueError)?;
let obj = Any::new(hdr, data);
Ok((rem, obj))
}
t => Err(Err::Error(Error::unexpected_tag(
Some(Tag::PrintableString),
t,
))),
}
}
/// A Relative Distinguished Name element.
///
/// These objects are used as [`X509Name`] components.
#[derive(Clone, Debug, PartialEq)]
pub struct RelativeDistinguishedName<'a> {
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, X509Error> for RelativeDistinguishedName<'a> {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
parse_der_set_defined_g(|i, _| {
let (i, set) = many1(complete(AttributeTypeAndValue::from_der))(i)?;
let rdn = RelativeDistinguishedName { set };
Ok((i, rdn))
})(i)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct SubjectPublicKeyInfo<'a> {
pub algorithm: AlgorithmIdentifier<'a>,
pub subject_public_key: BitString<'a>,
/// A raw unparsed PKIX, ASN.1 DER form (see RFC 5280, Section 4.1).
///
/// Note: use the [`Self::parsed()`] function to parse this object.
pub raw: &'a [u8],
}
impl SubjectPublicKeyInfo<'_> {
/// Attempt to parse the public key, and return the parsed version or an error
pub fn parsed(&self) -> Result<PublicKey<'_>, X509Error> {
let b = &self.subject_public_key.data;
if self.algorithm.algorithm == OID_PKCS1_RSAENCRYPTION {
let (_, key) = RSAPublicKey::from_der(b).map_err(|_| X509Error::InvalidSPKI)?;
Ok(PublicKey::RSA(key))
} else if self.algorithm.algorithm == OID_KEY_TYPE_EC_PUBLIC_KEY {
let key = ECPoint::from(b.as_ref());
Ok(PublicKey::EC(key))
} else if self.algorithm.algorithm == OID_KEY_TYPE_DSA {
let s = parse_der_integer(b)
.and_then(|(_, obj)| obj.as_slice().map_err(Err::Error))
.or(Err(X509Error::InvalidSPKI))?;
Ok(PublicKey::DSA(s))
} else if self.algorithm.algorithm == OID_GOST_R3410_2001 {
let (_, s) = <&[u8]>::from_der(b).or(Err(X509Error::InvalidSPKI))?;
Ok(PublicKey::GostR3410(s))
} else if self.algorithm.algorithm == OID_KEY_TYPE_GOST_R3410_2012_256
|| self.algorithm.algorithm == OID_KEY_TYPE_GOST_R3410_2012_512
{
let (_, s) = <&[u8]>::from_der(b).or(Err(X509Error::InvalidSPKI))?;
Ok(PublicKey::GostR3410_2012(s))
} else {
Ok(PublicKey::Unknown(b))
}
}
}
impl<'a> FromDer<'a, X509Error> for SubjectPublicKeyInfo<'a> {
/// Parse the SubjectPublicKeyInfo struct portion of a DER-encoded X.509 Certificate
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
let start_i = i;
parse_der_sequence_defined_g(move |i, _| {
let (i, algorithm) = AlgorithmIdentifier::from_der(i)?;
let (i, subject_public_key) = BitString::from_der(i).or(Err(X509Error::InvalidSPKI))?;
let len = start_i.offset(i);
let raw = &start_i[..len];
let spki = SubjectPublicKeyInfo {
algorithm,
subject_public_key,
raw,
};
Ok((i, spki))
})(i)
}
}
/// 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, DerSequence)]
#[error(X509Error)]
pub struct AlgorithmIdentifier<'a> {
#[map_err(|_| X509Error::InvalidAlgorithmIdentifier)]
pub algorithm: Oid<'a>,
#[optional]
pub parameters: Option<Any<'a>>,
}
impl<'a> AlgorithmIdentifier<'a> {
/// Create a new `AlgorithmIdentifier`
pub const fn new(algorithm: Oid<'a>, parameters: Option<Any<'a>>) -> Self {
Self {
algorithm,
parameters,
}
}
/// Get the algorithm OID
pub const fn oid(&'a self) -> &'a Oid<'a> {
&self.algorithm
}
/// Get a reference to the algorithm parameters, if present
pub const fn parameters(&'a self) -> Option<&'a Any<'a>> {
self.parameters.as_ref()
}
}
/// 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)]
pub struct X509Name<'a> {
pub(crate) rdn_seq: Vec<RelativeDistinguishedName<'a>>,
pub(crate) raw: &'a [u8],
}
impl fmt::Display for X509Name<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match x509name_to_string(&self.rdn_seq, oid_registry()) {
Ok(o) => write!(f, "{o}"),
Err(_) => write!(f, "<X509Error: Invalid X.509 name>"),
}
}
}
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)
}
// Not using the AsRef trait, as that would not give back the full 'a lifetime
pub fn as_raw(&self) -> &'a [u8] {
self.raw
}
/// 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()
}
/// Return an iterator over the attribute types and values of the name
pub fn iter_attributes(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> {
self.rdn_seq.iter().flat_map(|rdn| rdn.set.iter())
}
/// Return an iterator over the components identified by the given OID
///
/// The type of the component AttributeValue is determined by the AttributeType; in
/// general it will be a DirectoryString.
///
/// Attributes with same OID may be present multiple times, so the returned object is
/// an iterator.
/// Expected number of objects in this iterator are
/// - 0: not found
/// - 1: present once (common case)
/// - 2 or more: attribute is present multiple times
pub fn iter_by_oid(&self, oid: &Oid<'a>) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> {
// this is necessary, otherwise rustc complains
// that caller creates a temporary value for reference (for ex.
// `self.iter_by_oid(&OID_X509_LOCALITY_NAME)`
// )
let oid = oid.clone();
self.iter_attributes()
.filter(move |obj| obj.attr_type == oid)
}
/// Return an iterator over the `CommonName` attributes of the X.509 Name.
///
/// Returned iterator can be empty if there are no `CommonName` attributes.
/// If you expect only one `CommonName` to be present, then using `next()` will
/// get an `Option<&AttributeTypeAndValue>`.
///
/// A common operation is to extract the `CommonName` as a string.
///
/// ```
/// use x509_parser::x509::X509Name;
///
/// fn get_first_cn_as_str<'a>(name: &'a X509Name<'_>) -> Option<&'a str> {
/// name.iter_common_name()
/// .next()
/// .and_then(|cn| cn.as_str().ok())
/// }
/// ```
///
/// 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>> {
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>> {
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>> {
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>> {
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>> {
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>> {
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>> {
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, X509Error> 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<'a, 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);
newtype_enum! {
impl display ReasonCode {
Unspecified = 0,
KeyCompromise = 1,
CACompromise = 2,
AffiliationChanged = 3,
Superseded = 4,
CessationOfOperation = 5,
CertificateHold = 6,
// value 7 is not used
RemoveFromCRL = 8,
PrivilegeWithdrawn = 9,
AACompromise = 10,
}
}
impl Default for ReasonCode {
fn default() -> Self {
ReasonCode::Unspecified
}
}
// Attempt to convert attribute to string. If type is not a string, return value is the hex
// encoding of the attribute value
fn attribute_value_to_string(attr: &Any, _attr_type: &Oid) -> Result<String, X509Error> {
// TODO: replace this with helper function, when it is added to asn1-rs
match attr.tag() {
Tag::NumericString
| Tag::VisibleString
| Tag::PrintableString
| Tag::GeneralString
| Tag::ObjectDescriptor
| Tag::GraphicString
| Tag::T61String
| Tag::VideotexString
| Tag::Utf8String
| Tag::Ia5String => {
let s = core::str::from_utf8(attr.data).map_err(|_| X509Error::InvalidAttributes)?;
Ok(s.to_owned())
}
Tag::BmpString => {
// TODO: remove this when a new release of asn1-rs removes the need to consume attr in try_from
let any = attr.clone();
let s = BmpString::try_from(any).map_err(|_| X509Error::InvalidAttributes)?;
Ok(s.string())
}
_ => {
// type is not a string, get slice and convert it to base64
Ok(HEXUPPER.encode(attr.as_bytes()))
}
}
}
/// Convert a DER representation of a X.509 name to a human-readable string
///
/// RDNs are separated with ","
/// 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> {
rdn_seq.iter().try_fold(String::new(), |acc, rdn| {
rdn.set
.iter()
.try_fold(String::new(), |acc2, attr| {
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) {
Ok(s) => String::from(s),
_ => format!("{:?}", attr.attr_type),
};
let rdn = format!("{abbrev}={val_str}");
match acc2.len() {
0 => Ok(rdn),
_ => Ok(acc2 + " + " + &rdn),
}
})
.map(|v| match acc.len() {
0 => v,
_ => acc + ", " + &v,
})
})
}
pub(crate) fn parse_signature_value(i: &[u8]) -> X509Result<'_, BitString<'_>> {
BitString::from_der(i).or(Err(Err::Error(X509Error::InvalidSignatureValue)))
}
pub(crate) fn parse_serial(i: &[u8]) -> X509Result<'_, (&[u8], BigUint)> {
let (rem, any) = Any::from_ber(i).map_err(|_| X509Error::InvalidSerial)?;
// 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()`
any.tag()
.assert_eq(Tag::Integer)
.map_err(|_| X509Error::InvalidSerial)?;
let slice = any.data;
let big = BigUint::from_bytes_be(slice);
Ok((rem, (slice, big)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_x509_version() {
// correct version
let data: &[u8] = &[0xa0, 0x03, 0x02, 0x01, 0x00];
let r = X509Version::from_der_tagged_0(data);
assert!(r.is_ok());
// wrong tag
let data: &[u8] = &[0xa1, 0x03, 0x02, 0x01, 0x00];
let r = X509Version::from_der_tagged_0(data);
assert!(r.is_ok());
// short read
let data: &[u8] = &[0xa0, 0x01];
let r = X509Version::from_der_tagged_0(data);
assert!(r.is_err());
// short read with wrong tag
let data: &[u8] = &[0xa1, 0x01];
let r = X509Version::from_der_tagged_0(data);
assert!(r.is_err());
}
#[test]
fn test_x509_name() {
let name = X509Name {
rdn_seq: vec![
RelativeDistinguishedName {
set: vec![AttributeTypeAndValue {
attr_type: oid! {2.5.4.6}, // countryName
attr_value: Any::from_tag_and_data(Tag::PrintableString, b"FR"),
}],
},
RelativeDistinguishedName {
set: vec![AttributeTypeAndValue {
attr_type: oid! {2.5.4.8}, // stateOrProvinceName
attr_value: Any::from_tag_and_data(Tag::PrintableString, b"Some-State"),
}],
},
RelativeDistinguishedName {
set: vec![AttributeTypeAndValue {
attr_type: oid! {2.5.4.10}, // organizationName
attr_value: Any::from_tag_and_data(
Tag::PrintableString,
b"Internet Widgits Pty Ltd",
),
}],
},
RelativeDistinguishedName {
set: vec![
AttributeTypeAndValue {
attr_type: oid! {2.5.4.3}, // CN
attr_value: Any::from_tag_and_data(Tag::PrintableString, b"Test1"),
},
AttributeTypeAndValue {
attr_type: oid! {2.5.4.3}, // CN
attr_value: Any::from_tag_and_data(Tag::PrintableString, b"Test2"),
},
],
},
],
raw: &[], // incorrect, but enough for testing
};
assert_eq!(
name.to_string(),
"C=FR, ST=Some-State, O=Internet Widgits Pty Ltd, CN=Test1 + CN=Test2"
);
}
}

49
vendor/x509-parser/tests/pem.rs vendored Normal file
View File

@@ -0,0 +1,49 @@
use std::io::Cursor;
use x509_parser::pem::{parse_x509_pem, Pem};
use x509_parser::{parse_x509_certificate, x509::X509Version};
static IGCA_PEM: &[u8] = include_bytes!("../assets/IGC_A.pem");
#[test]
fn test_x509_parse_pem() {
let (rem, pem) = parse_x509_pem(IGCA_PEM).expect("PEM parsing failed");
// println!("{:?}", pem);
assert!(rem.is_empty());
assert_eq!(pem.label, String::from("CERTIFICATE"));
//
// now check that the content is indeed a certificate
let (rem, crt) = parse_x509_certificate(&pem.contents).expect("X.509 parsing failed");
// println!("res: {:?}", res);
assert!(rem.is_empty());
assert_eq!(crt.tbs_certificate.version, X509Version::V3);
}
#[test]
fn test_pem_read() {
let reader = Cursor::new(IGCA_PEM);
let (pem, bytes_read) = Pem::read(reader).expect("Reading PEM failed");
// println!("{:?}", pem);
assert_eq!(bytes_read, IGCA_PEM.len());
assert_eq!(pem.label, String::from("CERTIFICATE"));
//
// now check that the content is indeed a certificate
let x509 = pem.parse_x509().expect("X.509: decoding DER failed");
assert_eq!(x509.tbs_certificate.version, X509Version::V3);
}
#[test]
fn test_pem_not_pem() {
let bytes = vec![0x1, 0x2, 0x3, 0x4, 0x5];
let reader = Cursor::new(bytes);
let res = Pem::read(reader);
assert!(res.is_err());
}
static NO_END: &[u8] = include_bytes!("../assets/no_end.pem");
#[test]
fn test_pem_no_end() {
let reader = Cursor::new(NO_END);
let res = Pem::read(reader);
assert!(res.is_err());
}

393
vendor/x509-parser/tests/readcert.rs vendored Normal file
View File

@@ -0,0 +1,393 @@
use ::time::macros::datetime;
use ::time::OffsetDateTime;
use der_parser::oid;
use nom::Parser;
use oid_registry::*;
use std::collections::HashMap;
use x509_parser::prelude::*;
static IGCA_DER: &[u8] = include_bytes!("../assets/IGC_A.der");
static NO_EXTENSIONS_DER: &[u8] = include_bytes!("../assets/no_extensions.der");
static V1: &[u8] = include_bytes!("../assets/v1.der");
static CRL_DER: &[u8] = include_bytes!("../assets/example.crl");
static EMPTY_CRL_DER: &[u8] = include_bytes!("../assets/empty.crl");
static MINIMAL_CRL_DER: &[u8] = include_bytes!("../assets/minimal.crl");
static DUPLICATE_VALUE_IN_AIA: &[u8] =
include_bytes!("../assets/duplicate_value_in_authority_info_access.der");
static UNIQUE_IDS_DER: &[u8] = include_bytes!("../assets/unique_ids.der");
#[test]
fn test_x509_parser() {
let empty = &b""[..];
//assert_eq!(x509_parser(IGCA_DER), IResult::Done(empty, (1)));
let res = parse_x509_certificate(IGCA_DER);
// println!("res: {:?}", res);
match res {
Ok((e, cert)) => {
assert_eq!(e, empty);
//
let tbs_cert = &cert.tbs_certificate;
assert_eq!(tbs_cert.version, X509Version::V3);
//
let s = tbs_cert.raw_serial_as_string();
assert_eq!(&s, "39:11:45:10:94");
//
let expected_subject = "C=FR, ST=France, L=Paris, O=PM/SGDN, OU=DCSSI, CN=IGC/A, Email=igca@sgdn.pm.gouv.fr";
assert_eq!(format!("{}", tbs_cert.subject), expected_subject);
//
let cn_list: Result<Vec<_>, X509Error> = cert
.subject()
.iter_common_name()
.map(|attr| attr.as_str())
.collect();
assert_eq!(cn_list, Ok(vec!["IGC/A"]));
//
let sig = &tbs_cert.signature;
assert_eq!(sig.algorithm, oid! {1.2.840.113549.1.1.5});
//
let expected_issuer = "C=FR, ST=France, L=Paris, O=PM/SGDN, OU=DCSSI, CN=IGC/A, Email=igca@sgdn.pm.gouv.fr";
assert_eq!(format!("{}", tbs_cert.issuer), expected_issuer);
let expected_issuer_der = &IGCA_DER[35..171];
assert_eq!(tbs_cert.issuer.as_raw(), expected_issuer_der);
//
let sig_alg = &cert.signature_algorithm;
assert_eq!(sig_alg.algorithm, OID_PKCS1_SHA1WITHRSA);
//
let not_before = &tbs_cert.validity.not_before;
let not_after = &tbs_cert.validity.not_after;
let nb = not_before.to_datetime();
let na = not_after.to_datetime();
assert_eq!(nb.year(), 2002);
assert_eq!(nb.month() as u8, 12);
assert_eq!(nb.day(), 13);
assert_eq!(na.year(), 2020);
assert_eq!(na.month() as u8, 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![
X509Extension::new(
oid!(2.5.29 .19),
true,
&[48, 3, 1, 1, 255],
ParsedExtension::BasicConstraints(BasicConstraints {
ca: true,
path_len_constraint: None,
}),
),
X509Extension::new(
oid!(2.5.29 .15),
false,
&[3, 2, 1, 70],
ParsedExtension::KeyUsage(KeyUsage { flags: 98 }),
),
X509Extension::new(
oid!(2.5.29 .32),
false,
&[48, 12, 48, 10, 6, 8, 42, 129, 122, 1, 121, 1, 1, 1],
ParsedExtension::CertificatePolicies(policies),
),
X509Extension::new(
oid!(2.5.29 .14),
false,
&[
4, 20, 163, 5, 47, 24, 96, 80, 194, 137, 10, 221, 43, 33, 79, 255, 142, 78,
168, 48, 49, 54,
],
ParsedExtension::SubjectKeyIdentifier(KeyIdentifier(&[
163, 5, 47, 24, 96, 80, 194, 137, 10, 221, 43, 33, 79, 255, 142, 78, 168,
48, 49, 54,
])),
),
X509Extension::new(
oid!(2.5.29 .35),
false,
&[
48, 22, 128, 20, 163, 5, 47, 24, 96, 80, 194, 137, 10, 221, 43, 33, 79,
255, 142, 78, 168, 48, 49, 54,
],
ParsedExtension::AuthorityKeyIdentifier(AuthorityKeyIdentifier {
key_identifier: Some(KeyIdentifier(&[
163, 5, 47, 24, 96, 80, 194, 137, 10, 221, 43, 33, 79, 255, 142, 78,
168, 48, 49, 54,
])),
authority_cert_issuer: None,
authority_cert_serial: None,
}),
),
];
assert_eq!(tbs_cert.extensions(), &expected_extensions as &[_]);
//
assert!(tbs_cert.is_ca());
//
assert_eq!(tbs_cert.as_ref(), &IGCA_DER[4..(8 + 746)]);
// check that cert.as_raw() returns the certificate bytes
assert_eq!(cert.as_raw(), IGCA_DER);
}
_ => panic!("x509 parsing failed: {:?}", res),
}
}
#[test]
fn test_x509_no_extensions() {
let empty = &b""[..];
let res = parse_x509_certificate(NO_EXTENSIONS_DER);
match res {
Ok((e, cert)) => {
assert_eq!(e, empty);
let tbs_cert = cert.tbs_certificate;
assert_eq!(tbs_cert.version, X509Version::V3);
assert_eq!(tbs_cert.extensions().len(), 0);
}
_ => panic!("x509 parsing failed: {:?}", res),
}
}
#[test]
fn test_parse_subject_public_key_info() {
let res = SubjectPublicKeyInfo::from_der(&IGCA_DER[339..])
.expect("Parse public key info")
.1;
assert_eq!(res.algorithm.algorithm, OID_PKCS1_RSAENCRYPTION);
let params = res.algorithm.parameters.expect("algorithm parameters");
assert_eq!(params.tag().0, 5);
let spk = res.subject_public_key;
println!("spk.data.len {}", spk.data.len());
assert_eq!(spk.data.len(), 270);
}
#[test]
fn test_version_v1() {
let (rem, cert) = parse_x509_certificate(V1).expect("Could not parse v1 certificate");
assert!(rem.is_empty());
assert_eq!(cert.version(), X509Version::V1);
let tbs_cert = cert.tbs_certificate;
assert_eq!(format!("{}", tbs_cert.subject), "CN=marquee");
assert_eq!(format!("{}", tbs_cert.issuer), "CN=marquee");
}
#[test]
fn test_crl_parse() {
match parse_x509_crl(CRL_DER) {
Ok((e, cert)) => {
assert!(e.is_empty());
let tbs_cert_list = cert.tbs_cert_list;
assert_eq!(tbs_cert_list.version, Some(X509Version::V2));
let sig = &tbs_cert_list.signature;
assert_eq!(sig.algorithm, OID_PKCS1_SHA1WITHRSA);
let expected_issuer =
"O=Sample Signer Organization, OU=Sample Signer Unit, CN=Sample Signer Cert";
assert_eq!(format!("{}", tbs_cert_list.issuer), expected_issuer);
let sig_alg = &cert.signature_algorithm;
assert_eq!(sig_alg.algorithm, OID_PKCS1_SHA1WITHRSA);
let this_update = tbs_cert_list.this_update;
let next_update = tbs_cert_list.next_update.unwrap();
let tu = OffsetDateTime::from_unix_timestamp(this_update.timestamp()).unwrap();
let nu = OffsetDateTime::from_unix_timestamp(next_update.timestamp()).unwrap();
assert_eq!(tu.year(), 2013);
assert_eq!(tu.month() as u8, 2);
assert_eq!(tu.day(), 18);
assert_eq!(nu.year(), 2013);
assert_eq!(nu.month() as u8, 2);
assert_eq!(nu.day(), 18);
let revocation_date = datetime! {2013-02-18 10:22:12 UTC};
let invalidity_date = datetime! {2013-02-18 10:22:00 UTC};
let revoked_certs = &tbs_cert_list.revoked_certificates;
let revoked_cert_0 = &revoked_certs[0];
assert_eq!(*revoked_cert_0.serial(), 0x147947u32.into());
assert_eq!(
revoked_cert_0.revocation_date.to_datetime(),
revocation_date
);
let expected_extensions = vec![
X509Extension::new(
oid!(2.5.29 .21),
false,
&[10, 1, 3],
ParsedExtension::ReasonCode(ReasonCode::AffiliationChanged),
),
X509Extension::new(
oid!(2.5.29 .24),
false,
&[
24, 15, 50, 48, 49, 51, 48, 50, 49, 56, 49, 48, 50, 50, 48, 48, 90,
],
ParsedExtension::InvalidityDate(invalidity_date.into()),
),
];
assert_eq!(revoked_cert_0.extensions(), &expected_extensions as &[_]);
assert_eq!(revoked_certs.len(), 5);
assert_eq!(revoked_certs[4].user_certificate, 1_341_771_u32.into());
let expected_extensions = vec![
X509Extension::new(
oid!(2.5.29 .35),
false,
&[
48, 22, 128, 20, 190, 18, 1, 204, 170, 234, 17, 128, 218, 46, 173, 178,
234, 199, 181, 251, 159, 249, 173, 52,
],
ParsedExtension::AuthorityKeyIdentifier(AuthorityKeyIdentifier {
key_identifier: Some(KeyIdentifier(&[
190, 18, 1, 204, 170, 234, 17, 128, 218, 46, 173, 178, 234, 199, 181,
251, 159, 249, 173, 52,
])),
authority_cert_issuer: None,
authority_cert_serial: None,
}),
),
X509Extension::new(
oid!(2.5.29 .20),
false,
&[2, 1, 3],
ParsedExtension::CRLNumber(3u32.into()),
),
];
assert_eq!(tbs_cert_list.extensions(), &expected_extensions as &[_]);
assert_eq!(tbs_cert_list.as_ref(), &CRL_DER[4..(4 + 4 + 508)]);
}
err => panic!("x509 parsing failed: {:?}", err),
}
}
#[test]
fn test_crl_parse_empty() {
match parse_x509_crl(EMPTY_CRL_DER) {
Ok((e, cert)) => {
assert!(e.is_empty());
assert!(cert.tbs_cert_list.revoked_certificates.is_empty());
let expected_extensions = vec![
X509Extension::new(
oid!(2.5.29 .20),
false,
&[2, 1, 2],
ParsedExtension::CRLNumber(2u32.into()),
),
X509Extension::new(
OID_X509_EXT_AUTHORITY_KEY_IDENTIFIER,
false,
&[
48, 22, 128, 20, 34, 101, 12, 214, 90, 157, 52, 137, 243, 131, 180, 149,
82, 191, 80, 27, 57, 39, 6, 172,
],
ParsedExtension::AuthorityKeyIdentifier(AuthorityKeyIdentifier {
key_identifier: Some(KeyIdentifier(&[
34, 101, 12, 214, 90, 157, 52, 137, 243, 131, 180, 149, 82, 191, 80,
27, 57, 39, 6, 172,
])),
authority_cert_issuer: None,
authority_cert_serial: None,
}),
),
];
assert_eq!(cert.extensions(), &expected_extensions as &[_]);
assert_eq!(
cert.tbs_cert_list.as_ref(),
&EMPTY_CRL_DER[4..(4 + 3 + 200)]
);
}
err => panic!("x509 parsing failed: {:?}", err),
}
}
#[test]
fn test_crl_parse_minimal() {
match parse_x509_crl(MINIMAL_CRL_DER) {
Ok((e, crl)) => {
assert!(e.is_empty());
let revocation_date = datetime! {1970-01-01 00:00:00 UTC};
let revoked_certificates = &crl.tbs_cert_list.revoked_certificates;
assert_eq!(revoked_certificates.len(), 1);
let revoked_cert_0 = &revoked_certificates[0];
assert_eq!(*revoked_cert_0.serial(), 42u32.into());
assert_eq!(
revoked_cert_0.revocation_date.to_datetime(),
revocation_date
);
assert!(revoked_cert_0.extensions().is_empty());
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),
}
}
#[test]
fn test_duplicate_authority_info_access() {
match parse_x509_certificate(DUPLICATE_VALUE_IN_AIA) {
Ok((_, cert)) => {
let extension = cert
.tbs_certificate
.get_extension_unique(&OID_PKIX_AUTHORITY_INFO_ACCESS)
.expect("could not get AIA")
.expect("no AIA found");
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"),
];
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");
}
}
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);
}
}
#[test]
fn test_tbscert_unique_identifiers() {
let mut parser = X509CertificateParser::new().with_deep_parse_extensions(false);
let (_, x509) = parser.parse(UNIQUE_IDS_DER).expect("parsing failed");
assert_eq!(
&x509
.tbs_certificate
.issuer_uid
.expect("missing issuer uid")
.0
.as_ref(),
&[
0x30, 0x16, 0x80, 0x14, 0xc5, 0x78, 0x84, 0xb8, 0xc, 0x6e, 0x8c, 0x4c, 0xce, 0xb9,
0x94, 0x6f, 0x98, 0xfc, 0xf3, 0x8a, 0x54, 0xb1, 0x80, 0xe0
]
);
assert_eq!(
&x509
.tbs_certificate
.subject_uid
.expect("missing subject uid")
.0
.as_ref(),
&[
0x4, 0x14, 0xdf, 0x13, 0xac, 0x69, 0x14, 0x90, 0x62, 0xdb, 0x3d, 0xe9, 0xb4, 0x56,
0xe6, 0xa6, 0x90, 0x26, 0xbf, 0x2c, 0xef, 0x81
]
);
}

111
vendor/x509-parser/tests/readcrl.rs vendored Normal file
View File

@@ -0,0 +1,111 @@
use x509_parser::prelude::*;
#[cfg(any(feature = "verify", feature = "verify-aws"))]
#[test]
fn read_crl_verify() {
const CA_DATA: &[u8] = include_bytes!("../assets/ca_minimalcrl.der");
const CRL_DATA: &[u8] = include_bytes!("../assets/minimal.crl");
let (_, x509_ca) = X509Certificate::from_der(CA_DATA).expect("could not parse certificate");
let (_, crl) = parse_x509_crl(CRL_DATA).expect("could not parse revocation list");
let res = crl.verify_signature(&x509_ca.tbs_certificate.subject_pki);
eprintln!("Verification: {res:?}");
assert!(res.is_ok());
// check that `.as_raw()` returns the input bytes
assert_eq!(crl.as_raw(), CRL_DATA);
}
fn crl_idp<'a>(crl: &'a CertificateRevocationList) -> &'a IssuingDistributionPoint<'a> {
let crl_idp = crl
.tbs_cert_list
.find_extension(&oid_registry::OID_X509_EXT_ISSUER_DISTRIBUTION_POINT)
.expect("missing IDP extension");
match crl_idp.parsed_extension() {
ParsedExtension::IssuingDistributionPoint(crl_idp) => crl_idp,
_ => panic!("wrong extension type"),
}
}
#[test]
fn read_minimal_crl_idp() {
const CRL_DATA: &[u8] = include_bytes!("../assets/crl-idp/minimal.der");
let (_, crl) = parse_x509_crl(CRL_DATA).expect("could not parse revocation list");
let crl_idp = crl_idp(&crl);
let dp = crl_idp
.distribution_point
.as_ref()
.expect("missing distribution point");
let full_name = match dp {
DistributionPointName::FullName(full_name) => full_name,
DistributionPointName::NameRelativeToCRLIssuer(_) => {
panic!("wrong distribution point name type")
}
};
assert_eq!(full_name.len(), 1);
let uri = match full_name.first().unwrap() {
GeneralName::URI(uri) => *uri,
_ => panic!("wrong general name type"),
};
assert_eq!(uri, "http://crl.trustcor.ca/sub/dv-ssl-rsa-s-0.crl");
assert!(!crl_idp.only_contains_user_certs);
assert!(!crl_idp.only_contains_ca_certs);
assert!(crl_idp.only_some_reasons.is_none());
assert!(!crl_idp.only_contains_attribute_certs);
}
#[test]
fn test_only_user_crl_idp() {
const CRL_DATA: &[u8] = include_bytes!("../assets/crl-idp/only_user_certs.der");
let (_, crl) = parse_x509_crl(CRL_DATA).expect("could not parse revocation list");
let crl_idp = crl_idp(&crl);
assert!(crl_idp.only_contains_user_certs);
assert!(!crl_idp.only_contains_ca_certs);
assert!(crl_idp.only_some_reasons.is_none());
assert!(!crl_idp.only_contains_attribute_certs);
}
#[test]
fn test_only_ca_crl_idp() {
const CRL_DATA: &[u8] = include_bytes!("../assets/crl-idp/only_ca_certs.der");
let (_, crl) = parse_x509_crl(CRL_DATA).expect("could not parse revocation list");
let crl_idp = crl_idp(&crl);
assert!(!crl_idp.only_contains_user_certs);
assert!(crl_idp.only_contains_ca_certs);
assert!(crl_idp.only_some_reasons.is_none());
assert!(!crl_idp.only_contains_attribute_certs);
}
#[test]
fn test_only_some_reasons_crl_idp() {
const CRL_DATA: &[u8] = include_bytes!("../assets/crl-idp/only_some_reasons.der");
let (_, crl) = parse_x509_crl(CRL_DATA).expect("could not parse revocation list");
let crl_idp = crl_idp(&crl);
assert!(!crl_idp.only_contains_user_certs);
assert!(!crl_idp.only_contains_ca_certs);
assert!(!crl_idp.only_contains_attribute_certs);
let reasons = crl_idp
.only_some_reasons
.as_ref()
.expect("missing only_some_reasons");
assert!(reasons.key_compromise());
assert!(reasons.affilation_changed());
}
#[test]
fn test_only_attribute_cers_crl_idp() {
const CRL_DATA: &[u8] = include_bytes!("../assets/crl-idp/only_attribute_certs.der");
let (_, crl) = parse_x509_crl(CRL_DATA).expect("could not parse revocation list");
let crl_idp = crl_idp(&crl);
assert!(!crl_idp.only_contains_user_certs);
assert!(!crl_idp.only_contains_ca_certs);
assert!(crl_idp.only_some_reasons.is_none());
assert!(crl_idp.only_contains_attribute_certs);
}

128
vendor/x509-parser/tests/readcsr.rs vendored Normal file
View File

@@ -0,0 +1,128 @@
use asn1_rs::Set;
use oid_registry::{
OID_PKCS1_SHA256WITHRSA, OID_PKCS9_CHALLENGE_PASSWORD, OID_SIG_ECDSA_WITH_SHA256,
OID_X509_COMMON_NAME,
};
use x509_parser::prelude::*;
const CSR_DATA_EMPTY_ATTRIB: &[u8] = include_bytes!("../assets/csr-empty-attributes.csr");
const CSR_DATA: &[u8] = include_bytes!("../assets/test.csr");
const CSR_CHALLENGE_PASSWORD: &[u8] = include_bytes!("../assets/csr-challenge-password.pem");
#[test]
fn read_csr_empty_attrib() {
let (rem, csr) =
X509CertificationRequest::from_der(CSR_DATA_EMPTY_ATTRIB).expect("could not parse CSR");
assert!(rem.is_empty());
let cri = &csr.certification_request_info;
assert_eq!(cri.version, X509Version(0));
assert_eq!(cri.attributes().len(), 0);
assert_eq!(csr.signature_algorithm.algorithm, OID_PKCS1_SHA256WITHRSA);
}
#[test]
fn read_csr_with_san() {
let der = pem::parse_x509_pem(CSR_DATA).unwrap().1;
let (rem, csr) =
X509CertificationRequest::from_der(&der.contents).expect("could not parse CSR");
assert!(rem.is_empty());
let cri = &csr.certification_request_info;
assert_eq!(cri.version, X509Version(0));
assert_eq!(cri.attributes().len(), 1);
assert_eq!(csr.signature_algorithm.algorithm, OID_SIG_ECDSA_WITH_SHA256);
let mut rdns = cri.subject.iter();
let rdn = rdns.next().unwrap();
let first = rdn.iter().next().unwrap();
assert_eq!(first.attr_type(), &OID_X509_COMMON_NAME);
assert_eq!(first.as_str().unwrap(), "test.rusticata.fr");
let expected: &[u8] = &[
4, 195, 245, 126, 177, 113, 192, 146, 215, 136, 181, 58, 82, 138, 142, 61, 253, 245, 185,
192, 166, 216, 218, 145, 219, 42, 169, 112, 122, 58, 91, 184, 150, 37, 237, 245, 59, 54,
44, 210, 44, 207, 218, 167, 148, 189, 210, 159, 207, 103, 233, 1, 187, 134, 137, 24, 240,
188, 223, 135, 215, 71, 80, 64, 65,
];
assert_eq!(cri.subject_pki.subject_public_key.data, expected);
let mut extensions = csr.requested_extensions().unwrap();
match extensions.next().unwrap() {
ParsedExtension::SubjectAlternativeName(san) => {
let name = san.general_names.first().unwrap();
assert!(matches!(name, GeneralName::DNSName("test.rusticata.fr")));
}
_ => unreachable!(),
}
}
#[test]
fn read_csr_with_challenge_password() {
let der = pem::parse_x509_pem(CSR_CHALLENGE_PASSWORD).unwrap().1;
let (rem, csr) = X509CertificationRequest::from_der(&der.contents)
.expect("Could not parse CSR with challenge password");
assert!(rem.is_empty());
let cri = &csr.certification_request_info;
assert_eq!(cri.version, X509Version(0));
assert_eq!(cri.attributes().len(), 2);
let challenge_password_attr = csr
.certification_request_info
.find_attribute(&OID_PKCS9_CHALLENGE_PASSWORD)
.expect("Challenge password not found in CSR");
// 1. Check: Parse value
let (rem, challenge_password_from_value) =
Set::from_der_and_then(challenge_password_attr.value, String::from_der)
.expect("Error parsing challenge password attribute");
assert_eq!(challenge_password_from_value, "A challenge password");
assert!(rem.is_empty());
// 2. Check: Get value directly from parsed attribute
if let ParsedCriAttribute::ChallengePassword(challenge_password_from_parsed_attribute) =
challenge_password_attr.parsed_attribute()
{
assert_eq!(
challenge_password_from_parsed_attribute.0,
"A challenge password"
);
} else {
panic!("Parsed attribute is not a challenge password");
}
// Make sure we can read requested extensions
let extensions = csr
.requested_extensions()
.expect("Didn't find requested extensions in CSR");
let mut found_san = false;
for extension in extensions {
if let ParsedExtension::SubjectAlternativeName(san) = extension {
let name = san.general_names.get(2).unwrap();
assert!(matches!(name, GeneralName::DNSName("localhost")));
found_san = true;
}
}
assert!(found_san);
}
#[cfg(any(feature = "verify", feature = "verify-aws"))]
#[test]
fn read_csr_verify() {
let pem = pem::parse_x509_pem(CSR_DATA).unwrap().1;
let (_, csr) = X509CertificationRequest::from_der(&pem.contents).expect("could not parse CSR");
csr.verify_signature().unwrap();
let mut der = pem::parse_x509_pem(CSR_DATA).unwrap().1;
assert_eq!(&der.contents[28..37], b"rusticata");
for (i, b) in b"foobarbaz".iter().enumerate() {
der.contents[28 + i] = *b;
}
assert_eq!(&der.contents[28..37], b"foobarbaz");
let (_, csr) = X509CertificationRequest::from_der(&der.contents).expect("could not parse CSR");
csr.verify_signature().unwrap_err();
// check that `.as_raw()` returns the input bytes
assert_eq!(csr.as_raw(), &der.contents);
}

View File

@@ -0,0 +1,39 @@
use std::fs::{self, DirEntry};
use x509_parser::parse_x509_certificate;
const ARTIFACTS_DIR: &str = "fuzz/artifacts/fuzzer_script_1";
const CORPUS_DIR: &str = "fuzz/corpus/fuzzer_script_1";
#[test]
fn run_all_fuzz_files() {
parse_dir(ARTIFACTS_DIR);
parse_dir(CORPUS_DIR);
}
fn parse_dir(name: &str) {
match fs::read_dir(name) {
Ok(dir_entries) => {
dir_entries.for_each(|entry| {
let _ = entry.as_ref().map(parse_file);
});
}
Err(_) => eprintln!("fuzzer corpus/artifacts not found - ignoring test"),
}
}
fn parse_file(entry: &DirEntry) -> std::io::Result<()> {
let path = entry.path();
// println!("{:?}", entry.path());
let data = fs::read(path).unwrap();
let _ = parse_x509_certificate(&data);
Ok(())
}
#[test]
#[ignore = "placeholder for specific tests"]
fn run_fuzz_candidate() {
const CANDIDATE: &str = "fuzz/corpus/fuzzer_script_1/bd0096a63b9979d64763915a342a59af9dc281fb";
let data = fs::read(CANDIDATE).unwrap();
let _ = parse_x509_certificate(&data);
}

19
vendor/x509-parser/tests/test01.rs vendored Normal file
View File

@@ -0,0 +1,19 @@
use nom::bytes::complete::take;
#[test]
fn test01() {
let data = b"0\x88\xff\xff\xff\xff\xff\xff\xff\xff00\x0f\x02\x000\x00\x00\x00\x00\x00\x0000\x0f\x00\xff\x0a\xbb\xff";
let _ = x509_parser::parse_x509_certificate(data);
}
fn parser02(input: &[u8]) -> nom::IResult<&[u8], ()> {
let (_hdr, input) = take(1_usize)(input)?;
let (_data, input) = take(18_446_744_073_709_551_615_usize)(input)?;
Ok((input, ()))
}
#[test]
fn test02() {
let data = b"0\x88\xff\xff\xff\xff\xff\xff\xff\xff00\x0f\x02\x000\x00\x00\x00\x00\x00\x0000\x0f\x00\xff\x0a\xbb\xff";
let _ = parser02(data);
}

69
vendor/x509-parser/tests/verify.rs vendored Normal file
View File

@@ -0,0 +1,69 @@
#![cfg(any(feature = "verify", feature = "verify-aws"))]
use x509_parser::parse_x509_certificate;
static CA_DER: &[u8] = include_bytes!("../assets/IGC_A.der");
static CA_LETSENCRYPT_X3: &[u8] = include_bytes!("../assets/lets-encrypt-x3-cross-signed.der");
static CERT_DER: &[u8] = include_bytes!("../assets/certificate.der");
#[test]
fn test_signature_verification() {
// for a root CA, verify self-signature
let (_, x509_ca) = parse_x509_certificate(CA_DER).expect("could not parse certificate");
let res = x509_ca.verify_signature(None);
eprintln!("Verification: {res:?}");
assert!(res.is_ok());
// for a standard certificate, first load the authority, then the certificate, and verify it
let (_, x509_ca) =
parse_x509_certificate(CA_LETSENCRYPT_X3).expect("could not parse certificate");
let (_, x509_cert) = parse_x509_certificate(CERT_DER).expect("could not parse certificate");
let res = x509_cert.verify_signature(Some(&x509_ca.tbs_certificate.subject_pki));
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());
}
static RSA_PSS_SELF_SIGNED_SHA256: &[u8] =
include_bytes!("../assets/rsa-pss/self_signed_sha256.der");
static RSA_PSS_SELF_SIGNED_SHA384: &[u8] =
include_bytes!("../assets/rsa-pss/self_signed_sha384.der");
static RSA_PSS_SELF_SIGNED_SHA512: &[u8] =
include_bytes!("../assets/rsa-pss/self_signed_sha512.der");
#[test]
fn test_signature_verification_rsa_pss_sha256() {
let (_, x509) =
parse_x509_certificate(RSA_PSS_SELF_SIGNED_SHA256).expect("could not parse certificate");
let res = x509.verify_signature(None);
eprintln!("Verification: {res:?}");
assert!(res.is_ok());
}
#[test]
fn test_signature_verification_rsa_pss_sha384() {
let (_, x509) =
parse_x509_certificate(RSA_PSS_SELF_SIGNED_SHA384).expect("could not parse certificate");
let res = x509.verify_signature(None);
eprintln!("Verification: {res:?}");
assert!(res.is_ok());
}
#[test]
fn test_signature_verification_rsa_pss_sha512() {
let (_, x509) =
parse_x509_certificate(RSA_PSS_SELF_SIGNED_SHA512).expect("could not parse certificate");
let res = x509.verify_signature(None);
eprintln!("Verification: {res:?}");
assert!(res.is_ok());
}