summaryrefslogtreecommitdiffstats
path: root/rust/vendor/x509-parser
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--rust/vendor/x509-parser/.cargo-checksum.json1
-rw-r--r--rust/vendor/x509-parser/CHANGELOG.md355
-rw-r--r--rust/vendor/x509-parser/Cargo.lock462
-rw-r--r--rust/vendor/x509-parser/Cargo.toml100
-rw-r--r--rust/vendor/x509-parser/LICENSE-APACHE201
-rw-r--r--rust/vendor/x509-parser/LICENSE-MIT25
-rw-r--r--rust/vendor/x509-parser/README.md135
-rw-r--r--rust/vendor/x509-parser/assets/IGC_A.derbin0 -> 1030 bytes
-rw-r--r--rust/vendor/x509-parser/assets/IGC_A.pem24
-rw-r--r--rust/vendor/x509-parser/assets/ca_minimalcrl.derbin0 -> 679 bytes
-rw-r--r--rust/vendor/x509-parser/assets/certificate.derbin0 -> 1375 bytes
-rw-r--r--rust/vendor/x509-parser/assets/certificate.pem31
-rw-r--r--rust/vendor/x509-parser/assets/crl-ext/crl-complex.derbin0 -> 965 bytes
-rw-r--r--rust/vendor/x509-parser/assets/crl-ext/crl-no-crl.derbin0 -> 807 bytes
-rw-r--r--rust/vendor/x509-parser/assets/crl-ext/crl-simple.derbin0 -> 853 bytes
-rw-r--r--rust/vendor/x509-parser/assets/csr-challenge-password.pem33
-rw-r--r--rust/vendor/x509-parser/assets/csr-empty-attributes.csrbin0 -> 670 bytes
-rw-r--r--rust/vendor/x509-parser/assets/duplicate_value_in_authority_info_access.derbin0 -> 1571 bytes
-rw-r--r--rust/vendor/x509-parser/assets/ed25519.derbin0 -> 268 bytes
-rw-r--r--rust/vendor/x509-parser/assets/empty.crlbin0 -> 293 bytes
-rw-r--r--rust/vendor/x509-parser/assets/example.crlbin0 -> 792 bytes
-rw-r--r--rust/vendor/x509-parser/assets/extension1.derbin0 -> 1549 bytes
-rw-r--r--rust/vendor/x509-parser/assets/extension2.derbin0 -> 1727 bytes
-rw-r--r--rust/vendor/x509-parser/assets/lets-encrypt-x3-cross-signed.derbin0 -> 1174 bytes
-rw-r--r--rust/vendor/x509-parser/assets/minimal.crlbin0 -> 359 bytes
-rw-r--r--rust/vendor/x509-parser/assets/no_end.pem9
-rw-r--r--rust/vendor/x509-parser/assets/no_extensions.derbin0 -> 341 bytes
-rw-r--r--rust/vendor/x509-parser/assets/no_extensions.pem10
-rw-r--r--rust/vendor/x509-parser/assets/test.csr8
-rw-r--r--rust/vendor/x509-parser/assets/v1.derbin0 -> 673 bytes
-rw-r--r--rust/vendor/x509-parser/examples/print-cert.rs417
-rw-r--r--rust/vendor/x509-parser/examples/print-crl.rs154
-rw-r--r--rust/vendor/x509-parser/src/certificate.rs782
-rw-r--r--rust/vendor/x509-parser/src/certification_request.rs166
-rw-r--r--rust/vendor/x509-parser/src/cri_attributes.rs179
-rw-r--r--rust/vendor/x509-parser/src/error.rs123
-rw-r--r--rust/vendor/x509-parser/src/extensions/generalname.rs115
-rw-r--r--rust/vendor/x509-parser/src/extensions/keyusage.rs150
-rw-r--r--rust/vendor/x509-parser/src/extensions/mod.rs1484
-rw-r--r--rust/vendor/x509-parser/src/extensions/nameconstraints.rs59
-rw-r--r--rust/vendor/x509-parser/src/extensions/policymappings.rs92
-rw-r--r--rust/vendor/x509-parser/src/extensions/sct.rs124
-rw-r--r--rust/vendor/x509-parser/src/lib.rs213
-rw-r--r--rust/vendor/x509-parser/src/objects.rs92
-rw-r--r--rust/vendor/x509-parser/src/pem.rs255
-rw-r--r--rust/vendor/x509-parser/src/prelude.rs18
-rw-r--r--rust/vendor/x509-parser/src/public_key.rs132
-rw-r--r--rust/vendor/x509-parser/src/revocation_list.rs365
-rw-r--r--rust/vendor/x509-parser/src/signature_algorithm.rs326
-rw-r--r--rust/vendor/x509-parser/src/signature_value.rs11
-rw-r--r--rust/vendor/x509-parser/src/time.rs179
-rw-r--r--rust/vendor/x509-parser/src/utils.rs19
-rw-r--r--rust/vendor/x509-parser/src/validate/certificate.rs17
-rw-r--r--rust/vendor/x509-parser/src/validate/extensions.rs115
-rw-r--r--rust/vendor/x509-parser/src/validate/loggers.rs83
-rw-r--r--rust/vendor/x509-parser/src/validate/mod.rs262
-rw-r--r--rust/vendor/x509-parser/src/validate/name.rs32
-rw-r--r--rust/vendor/x509-parser/src/validate/structure.rs160
-rw-r--r--rust/vendor/x509-parser/src/verify.rs78
-rw-r--r--rust/vendor/x509-parser/src/x509.rs667
-rw-r--r--rust/vendor/x509-parser/tests/pem.rs49
-rw-r--r--rust/vendor/x509-parser/tests/readcert.rs360
-rw-r--r--rust/vendor/x509-parser/tests/readcrl.rs18
-rw-r--r--rust/vendor/x509-parser/tests/readcsr.rs125
-rw-r--r--rust/vendor/x509-parser/tests/run_all_fuzz_files.rs39
-rw-r--r--rust/vendor/x509-parser/tests/test01.rs19
-rw-r--r--rust/vendor/x509-parser/tests/verify.rs35
67 files changed, 8908 insertions, 0 deletions
diff --git a/rust/vendor/x509-parser/.cargo-checksum.json b/rust/vendor/x509-parser/.cargo-checksum.json
new file mode 100644
index 0000000..23e09c4
--- /dev/null
+++ b/rust/vendor/x509-parser/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"CHANGELOG.md":"26cff1f987bc08f232bc82e844639cdf0321ce27620d94e93799b61ffefcb5fd","Cargo.lock":"50420468b592973ebb987b43fb88fb6212764b5f00f9cf6d1aa35a7bd1504500","Cargo.toml":"ae21317b0dc1957f8879fece77c4b20bf84ad34649e53c32c2a6ccef091ee57f","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"a5c61b93b6ee1d104af9920cf020ff3c7efe818e31fe562c72261847a728f513","README.md":"067be566b938ab29678debf9b618ff23efee1af25b3807be7d7f9cf74184b3e2","assets/IGC_A.der":"b9bea7860a962ea3611dab97ab6da3e21c1068b97d55575ed0e11279c11c8932","assets/IGC_A.pem":"0a2172389aefb412d58187f4707d1947e9ca1e77e30669c4ce03401ad90e6bcc","assets/ca_minimalcrl.der":"e62b689fd79c7e1208fa910df96839ca458671e0aae92573abb37f43d90dc5c7","assets/certificate.der":"8fa4cb4e938b5a855fb1497fa288ac55723e0109d0518b505481348dec0de471","assets/certificate.pem":"ed18153fca7a0ba64c52a2df4d9199e91aa2536621ce7fa7dbcb2365f27199e7","assets/crl-ext/crl-complex.der":"64acfbe4da92d28b6f1f498b21d2f9364529b73743c916c75fbf5eb1b14b6217","assets/crl-ext/crl-no-crl.der":"54f9d02e1d4b148cdbc0c09166d9d9648fd8a7078a734a52b490829775f6afb0","assets/crl-ext/crl-simple.der":"27250bbac66b8120931e1eca3db17a041f5e57a2fa9d4505fb7e14cb36b8dc21","assets/csr-challenge-password.pem":"647eec9fe35374b15633ae47501b1aed5c397bf79e31b3eb6187abb11f6347b6","assets/csr-empty-attributes.csr":"c9590910a25b9de5dabe2cee92c6a776829a3f4517e04a82940f8d63ed7aa28b","assets/duplicate_value_in_authority_info_access.der":"91efafdd6648ddf460a0df21c5606eb497e8499d76486c4ee1d68d7f83f25033","assets/ed25519.der":"d38693b676f3b80b183e7ab61d14182a9d207c131a35ed67696cf8955972551d","assets/empty.crl":"546c5c5659b1712b5a75d56283c1c8b430c27a92380a4d1336cd10304c9f7576","assets/example.crl":"0117fb79eddef3e4fb98ab1fdddbd234227add49f1f68eabdd1a49065b8cff59","assets/extension1.der":"3b9ee8d9070277fdddf85e92d99651ccc4a1736c2079ba492797e34c3cf3b47f","assets/extension2.der":"b62badf95878a9961e26bbcd306711c87f8edbd9c3913070ce328dd90c94fec6","assets/lets-encrypt-x3-cross-signed.der":"25847d668eb4f04fdd40b12b6b0740c567da7d024308eb6c2c96fe41d9de218d","assets/minimal.crl":"8400125989ec8f08e3e42411f36d05887b1a75a28f2ba22a3c0224fde3c6d871","assets/no_end.pem":"e5dcdff988479c74facc6718a11eded6e1f9d496482771ee76358153abb0729f","assets/no_extensions.der":"39f41ff9385a90df31a90fc6c0c686cf2343dea2f18cd058339a0d00a07a51cb","assets/no_extensions.pem":"77f53d745e6391c60360f3e55700ab32a9b0f6f44896412791f0a8ca8a0209e1","assets/test.csr":"591f651ba48a18237103057972ca250cc50640cb760466bfe3a511d9ed19332d","assets/v1.der":"3d6da682d33093d85aa632846ba9fabf31fe90edc091a27fd7f62e514f858c57","examples/print-cert.rs":"036605671f29b612ab08ff1ad42a7a73888e8623fd57b6bd214c4ee0ee828ec1","examples/print-crl.rs":"07ad03a276e11e71a2770215998033268c3e0e7b2457e041359a5d47125feec1","src/certificate.rs":"f1516d8d9a29c0f7c9c6e72d8878acbc8d76b52adae2028fe10d30004d05897e","src/certification_request.rs":"e72df52b00957ae4c94e49697a9c6fc20116f965edd08ca97670e6f1615df69e","src/cri_attributes.rs":"1f9a4c2f8563e1f6e0d39435c74708a43272fa01e10bcb933328afd1592cf3fc","src/error.rs":"071ec0426c9b066d7cc3f9ab41023dda40ffd16fbf93b5d88b710b462505d324","src/extensions/generalname.rs":"38da2f80d50a6222b8ac70ebec7cd84c413a2a56fe7509a0842e354e9ba64ca7","src/extensions/keyusage.rs":"e03fa7fbb5054c321c78c42fd5260f5f5f535fc02155aa51d1af4c8937662de6","src/extensions/mod.rs":"ba6473c8afba47f996b49bad18a61089f5aa3255093d92a05b121e25d72ea431","src/extensions/nameconstraints.rs":"00532ef23fa172cbe80b6ac7ec34725cbb11a01983ccbc9bfe94256e024d4fb2","src/extensions/policymappings.rs":"c1b1d36ffad3bfd2354d80c59d7977ac5d17c0b4fd1a224894c4ebdb1c5d7a73","src/extensions/sct.rs":"04e44fde5c2532fc9276c00e3eb2a7fe1e42335f2dad77b8da137817bb3f6a96","src/lib.rs":"b49ce3a01c6ac209f8106808abfc13b5ff90f015c651cfdab232a0693302f7d3","src/objects.rs":"102ece2f6ce7c484f4daf73665d36f417432db75ab90c5718bda1f4093d4a985","src/pem.rs":"cab9937201c909c9b92946dbabd98dbd06bc5684638d3cdeec2dd829bc0dffa9","src/prelude.rs":"82471321b16588e25df2a6d277acb53ce342de962c61ab2fa4bd3e4c202562aa","src/public_key.rs":"f9c593759ffccb1db9c766ff79b4041c85dcd0e42d10d6ef22d012b3134692df","src/revocation_list.rs":"c7fe79225d53b6a96103d2849451a5a84f37f4da4c4cb7fb5a2df05c685a5739","src/signature_algorithm.rs":"0ee7f84b1ad2004f4cb59ad2ca68d3828ffd8cbc42228bf05f15171d5a590c93","src/signature_value.rs":"b1c1f51429f13ada0a1e9b75b075f53f70e3aa44c5edb5e93ed8d0dff116467b","src/time.rs":"570aef0b70cdebce8adcffbadc3d67c4e4085106b2153c92d4b3ddd976d8acde","src/utils.rs":"59f0f22026da24998225ddb443960e8fda22ae94bb79cb3fe2c523deadfb0395","src/validate/certificate.rs":"9d3c74a1f886392cc10fe7d0a6ba4e0ded2fd195354c649689e7f58bee0ff971","src/validate/extensions.rs":"e7043d5a24ba63f3eb0515a477a757846af9a1f68eb33ef87c6ffd717799cbae","src/validate/loggers.rs":"24b00af5e4c43d5c7a029d8ea7674ec070c985ac092a928471a1d3141c9e3a2d","src/validate/mod.rs":"d748ab37ce2057e23a4b09b3f4c053c6c9d4047a9f53875f5d54c73724dfb146","src/validate/name.rs":"26543fe1a2401e8eb7890c8ae77933d53a3b8dc0b932f6d6b6370589af702048","src/validate/structure.rs":"0b6046c021bd780abcebc47abebc29023f6a2b6f0e3123ea0622dab5610c3512","src/verify.rs":"d6e798ac095395835d460f63a60f64dbbd18081cb2be979b4edb385ef5ccda2f","src/x509.rs":"e3d5bcee663faddad4a93a89d32217ea222ae8ad08daeec342aa757bdbd28a5b","tests/pem.rs":"eb568a214fcfaa0822dca524f75f1ffd6381e92897728eeba237ae584e870a21","tests/readcert.rs":"a97110e9d0572a307155495dedbe14ad401750c7d27a26f96dc89923e8c42bdf","tests/readcrl.rs":"4671c737cb93acbe1ced61f14985d3a173f779929f13048725734623a505ce47","tests/readcsr.rs":"82b6e6352672a960eca44d7f88b2c39eb3583b2d22e0e1467de5ba178b92df95","tests/run_all_fuzz_files.rs":"bc3a4c1b9792f7d6bf5767775be07c3720c1ae8567b2c865b4e204c3a70d7359","tests/test01.rs":"fcb258a7204ac7a4c62ab46e12126c11edf23b62a7f24ef40ae7d843482e1341","tests/verify.rs":"a6c454e84809cdd8b6faeb2bab94719d0ffbfe9fbe01fc68a69e622cda3517f1"},"package":"7069fba5b66b9193bd2c5d3d4ff12b839118f6bcbef5328efafafb5395cf63da"} \ No newline at end of file
diff --git a/rust/vendor/x509-parser/CHANGELOG.md b/rust/vendor/x509-parser/CHANGELOG.md
new file mode 100644
index 0000000..36c4f63
--- /dev/null
+++ b/rust/vendor/x509-parser/CHANGELOG.md
@@ -0,0 +1,355 @@
+# ChangeLog
+
+## [Unreleased][unreleased]
+
+### Added/Changed/Fixed
+
+### Thanks
+
+## 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
+
+
diff --git a/rust/vendor/x509-parser/Cargo.lock b/rust/vendor/x509-parser/Cargo.lock
new file mode 100644
index 0000000..d8e6a17
--- /dev/null
+++ b/rust/vendor/x509-parser/Cargo.lock
@@ -0,0 +1,462 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "asn1-rs"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0"
+dependencies = [
+ "asn1-rs-derive",
+ "asn1-rs-impl",
+ "displaydoc",
+ "nom",
+ "num-traits",
+ "rusticata-macros",
+ "thiserror",
+ "time",
+]
+
+[[package]]
+name = "asn1-rs-derive"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "synstructure",
+]
+
+[[package]]
+name = "asn1-rs-impl"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bumpalo"
+version = "3.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
+
+[[package]]
+name = "cc"
+version = "1.0.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c6b2562119bf28c3439f7f02db99faf0aa1a8cdfe5772a2ee155d32227239f0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "data-encoding"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
+
+[[package]]
+name = "der-parser"
+version = "8.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e"
+dependencies = [
+ "asn1-rs",
+ "displaydoc",
+ "nom",
+ "num-bigint",
+ "num-traits",
+ "rusticata-macros",
+]
+
+[[package]]
+name = "deranged"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7684a49fb1af197853ef7b2ee694bc1f5b4179556f1e5710e1760c5db6f5e929"
+
+[[package]]
+name = "displaydoc"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.28",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+
+[[package]]
+name = "js-sys"
+version = "0.3.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
+
+[[package]]
+name = "log"
+version = "0.4.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[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.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "oid-registry"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff"
+dependencies = [
+ "asn1-rs",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "ring"
+version = "0.16.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
+dependencies = [
+ "cc",
+ "libc",
+ "once_cell",
+ "spin",
+ "untrusted",
+ "web-sys",
+ "winapi",
+]
+
+[[package]]
+name = "rusticata-macros"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632"
+dependencies = [
+ "nom",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.180"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ea67f183f058fe88a4e3ec6e2788e003840893b91bac4559cabedd00863b3ed"
+
+[[package]]
+name = "spin"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "synstructure"
+version = "0.12.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "unicode-xid",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.28",
+]
+
+[[package]]
+name = "time"
+version = "0.3.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fdd63d58b18d663fbdf70e049f00a22c8e42be082203be7f26589213cd75ea"
+dependencies = [
+ "deranged",
+ "itoa",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
+
+[[package]]
+name = "time-macros"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb71511c991639bb078fd5bf97757e03914361c48100d52878b8e52b46fb92cd"
+dependencies = [
+ "time-core",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
+
+[[package]]
+name = "untrusted"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.28",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.28",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
+
+[[package]]
+name = "web-sys"
+version = "0.3.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "x509-parser"
+version = "0.15.1"
+dependencies = [
+ "asn1-rs",
+ "data-encoding",
+ "der-parser",
+ "lazy_static",
+ "nom",
+ "oid-registry",
+ "ring",
+ "rusticata-macros",
+ "thiserror",
+ "time",
+]
diff --git a/rust/vendor/x509-parser/Cargo.toml b/rust/vendor/x509-parser/Cargo.toml
new file mode 100644
index 0000000..dbc6a42
--- /dev/null
+++ b/rust/vendor/x509-parser/Cargo.toml
@@ -0,0 +1,100 @@
+# 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"
+name = "x509-parser"
+version = "0.15.1"
+authors = ["Pierre Chifflier <chifflier@wzdftpd.net>"]
+include = [
+ "CHANGELOG.md",
+ "LICENSE-*",
+ "README.md",
+ ".gitignore",
+ ".travis.yml",
+ "Cargo.toml",
+ "src/*.rs",
+ "src/extensions/*.rs",
+ "src/validate/*.rs",
+ "tests/*.rs",
+ "assets/*.crl",
+ "assets/*.csr",
+ "assets/*.der",
+ "assets/*.pem",
+ "assets/crl-ext/*.der",
+ "examples/*.rs",
+]
+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",
+]
+
+[dependencies.asn1-rs]
+version = "0.5"
+features = ["datetime"]
+
+[dependencies.data-encoding]
+version = "2.2.1"
+
+[dependencies.der-parser]
+version = "8.1.0"
+features = ["bigint"]
+
+[dependencies.lazy_static]
+version = "1.4"
+
+[dependencies.nom]
+version = "7.0"
+
+[dependencies.oid-registry]
+version = "0.6"
+features = [
+ "crypto",
+ "x509",
+ "x962",
+]
+
+[dependencies.ring]
+version = "0.16.11"
+optional = true
+
+[dependencies.rusticata-macros]
+version = "4.0"
+
+[dependencies.thiserror]
+version = "1.0.2"
+
+[dependencies.time]
+version = "0.3.7"
+features = ["formatting"]
+
+[features]
+default = []
+validate = []
+verify = ["ring"]
diff --git a/rust/vendor/x509-parser/LICENSE-APACHE b/rust/vendor/x509-parser/LICENSE-APACHE
new file mode 100644
index 0000000..16fe87b
--- /dev/null
+++ b/rust/vendor/x509-parser/LICENSE-APACHE
@@ -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.
diff --git a/rust/vendor/x509-parser/LICENSE-MIT b/rust/vendor/x509-parser/LICENSE-MIT
new file mode 100644
index 0000000..290e7b9
--- /dev/null
+++ b/rust/vendor/x509-parser/LICENSE-MIT
@@ -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.
diff --git a/rust/vendor/x509-parser/README.md b/rust/vendor/x509-parser/README.md
new file mode 100644
index 0000000..cf055f2
--- /dev/null
+++ b/rust/vendor/x509-parser/README.md
@@ -0,0 +1,135 @@
+<!-- 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.57.0+-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.
+
+# 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` feature adds support for (cryptographic) signature verification, based on `ring`.
+ It adds the
+ [`X509Certificate::verify_signature()`](https://docs.rs/x509-parser/latest/x509_parser/certificate/struct.X509Certificate.html#method.verify_signature)
+ to `X509Certificate`.
+
+```rust
+/// Cryptographic signature verification: returns true if certificate was signed by issuer
+#[cfg(feature = "verify")]
+pub fn check_signature(cert: &X509Certificate<'_>, issuer: &X509Certificate<'_>) -> bool {
+ let issuer_public_key = issuer.public_key();
+ cert
+ .verify_signature(Some(issuer_public_key))
+ .is_ok()
+}
+```
+
+- The `validate` features add methods to run more validation functions on the certificate structure
+ and values using the [`Validate`](https://docs.rs/x509-parser/latest/x509_parser/validate/trait.Validate.html) trait.
+ It does not validate any cryptographic parameter (see `verify` above).
+
+## Rust version requirements
+
+`x509-parser` requires **Rustc version 1.57 or greater**, based on der-parser
+dependencies and for proc-macro attributes support.
+
+Note that due to breaking changes in the `time` crate, a specific version of this
+crate must be specified for compiler versions <= 1.57:
+`cargo update -p time --precise 0.3.9`
+
+[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.
diff --git a/rust/vendor/x509-parser/assets/IGC_A.der b/rust/vendor/x509-parser/assets/IGC_A.der
new file mode 100644
index 0000000..2c55538
--- /dev/null
+++ b/rust/vendor/x509-parser/assets/IGC_A.der
Binary files differ
diff --git a/rust/vendor/x509-parser/assets/IGC_A.pem b/rust/vendor/x509-parser/assets/IGC_A.pem
new file mode 100644
index 0000000..8940ef5
--- /dev/null
+++ b/rust/vendor/x509-parser/assets/IGC_A.pem
@@ -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-----
diff --git a/rust/vendor/x509-parser/assets/ca_minimalcrl.der b/rust/vendor/x509-parser/assets/ca_minimalcrl.der
new file mode 100644
index 0000000..7635da4
--- /dev/null
+++ b/rust/vendor/x509-parser/assets/ca_minimalcrl.der
Binary files differ
diff --git a/rust/vendor/x509-parser/assets/certificate.der b/rust/vendor/x509-parser/assets/certificate.der
new file mode 100644
index 0000000..e5f48f5
--- /dev/null
+++ b/rust/vendor/x509-parser/assets/certificate.der
Binary files differ
diff --git a/rust/vendor/x509-parser/assets/certificate.pem b/rust/vendor/x509-parser/assets/certificate.pem
new file mode 100644
index 0000000..3e08af7
--- /dev/null
+++ b/rust/vendor/x509-parser/assets/certificate.pem
@@ -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-----
diff --git a/rust/vendor/x509-parser/assets/crl-ext/crl-complex.der b/rust/vendor/x509-parser/assets/crl-ext/crl-complex.der
new file mode 100644
index 0000000..f5140f1
--- /dev/null
+++ b/rust/vendor/x509-parser/assets/crl-ext/crl-complex.der
Binary files differ
diff --git a/rust/vendor/x509-parser/assets/crl-ext/crl-no-crl.der b/rust/vendor/x509-parser/assets/crl-ext/crl-no-crl.der
new file mode 100644
index 0000000..03f7357
--- /dev/null
+++ b/rust/vendor/x509-parser/assets/crl-ext/crl-no-crl.der
Binary files differ
diff --git a/rust/vendor/x509-parser/assets/crl-ext/crl-simple.der b/rust/vendor/x509-parser/assets/crl-ext/crl-simple.der
new file mode 100644
index 0000000..224a743
--- /dev/null
+++ b/rust/vendor/x509-parser/assets/crl-ext/crl-simple.der
Binary files differ
diff --git a/rust/vendor/x509-parser/assets/csr-challenge-password.pem b/rust/vendor/x509-parser/assets/csr-challenge-password.pem
new file mode 100644
index 0000000..a48e99d
--- /dev/null
+++ b/rust/vendor/x509-parser/assets/csr-challenge-password.pem
@@ -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-----
diff --git a/rust/vendor/x509-parser/assets/csr-empty-attributes.csr b/rust/vendor/x509-parser/assets/csr-empty-attributes.csr
new file mode 100644
index 0000000..bfb84c8
--- /dev/null
+++ b/rust/vendor/x509-parser/assets/csr-empty-attributes.csr
Binary files differ
diff --git a/rust/vendor/x509-parser/assets/duplicate_value_in_authority_info_access.der b/rust/vendor/x509-parser/assets/duplicate_value_in_authority_info_access.der
new file mode 100644
index 0000000..1379255
--- /dev/null
+++ b/rust/vendor/x509-parser/assets/duplicate_value_in_authority_info_access.der
Binary files differ
diff --git a/rust/vendor/x509-parser/assets/ed25519.der b/rust/vendor/x509-parser/assets/ed25519.der
new file mode 100644
index 0000000..cae76a1
--- /dev/null
+++ b/rust/vendor/x509-parser/assets/ed25519.der
Binary files differ
diff --git a/rust/vendor/x509-parser/assets/empty.crl b/rust/vendor/x509-parser/assets/empty.crl
new file mode 100644
index 0000000..fc3f6b3
--- /dev/null
+++ b/rust/vendor/x509-parser/assets/empty.crl
Binary files differ
diff --git a/rust/vendor/x509-parser/assets/example.crl b/rust/vendor/x509-parser/assets/example.crl
new file mode 100644
index 0000000..3df0b6f
--- /dev/null
+++ b/rust/vendor/x509-parser/assets/example.crl
Binary files differ
diff --git a/rust/vendor/x509-parser/assets/extension1.der b/rust/vendor/x509-parser/assets/extension1.der
new file mode 100644
index 0000000..d3f14be
--- /dev/null
+++ b/rust/vendor/x509-parser/assets/extension1.der
Binary files differ
diff --git a/rust/vendor/x509-parser/assets/extension2.der b/rust/vendor/x509-parser/assets/extension2.der
new file mode 100644
index 0000000..b0f7006
--- /dev/null
+++ b/rust/vendor/x509-parser/assets/extension2.der
Binary files differ
diff --git a/rust/vendor/x509-parser/assets/lets-encrypt-x3-cross-signed.der b/rust/vendor/x509-parser/assets/lets-encrypt-x3-cross-signed.der
new file mode 100644
index 0000000..e08466c
--- /dev/null
+++ b/rust/vendor/x509-parser/assets/lets-encrypt-x3-cross-signed.der
Binary files differ
diff --git a/rust/vendor/x509-parser/assets/minimal.crl b/rust/vendor/x509-parser/assets/minimal.crl
new file mode 100644
index 0000000..e77d446
--- /dev/null
+++ b/rust/vendor/x509-parser/assets/minimal.crl
Binary files differ
diff --git a/rust/vendor/x509-parser/assets/no_end.pem b/rust/vendor/x509-parser/assets/no_end.pem
new file mode 100644
index 0000000..9bd844e
--- /dev/null
+++ b/rust/vendor/x509-parser/assets/no_end.pem
@@ -0,0 +1,9 @@
+-----BEGIN CERTIFICATE-----
+MIIBUTCB2KADAgECAgkAtXGSKEzrTR4wCgYIKoZIzj0EAwIwEDEOMAwGA1UEAwwF
+YmVubm8wHhcNMTgxMTEzMDI1NDQwWhcNMTkxMTEzMDI1NDQwWjAQMQ4wDAYDVQQD
+DAViZW5ubzB2MBAGByqGSM49AgEGBSuBBAAiA2IABDhrHLVTMHC7GTyB/MNztToW
+ss2zlmvR62X1pQaBN6fYhBJE1XYa0V2C1fGGXj92MencOtXyfYVxn+DY07gyT/71
+HQ12TJOe90wwjy2/6N1W1jOv5HjphVT8JQlVNqAC+DAKBggqhkjOPQQDAgNoADBl
+AjAfzS1tmZ+GSAaXrsPcfAd1A9yfWVtB8tWxFNNo2j7/cL3puf2vQnlwV/0BoZZ1
+K4ICMQDaE0HemgYp6RPQzeb96a2gjlaEbwNy1B8O74fE24WRXiOUm4eln9wGQ3I1
+iV6NtZU=
diff --git a/rust/vendor/x509-parser/assets/no_extensions.der b/rust/vendor/x509-parser/assets/no_extensions.der
new file mode 100644
index 0000000..39278c6
--- /dev/null
+++ b/rust/vendor/x509-parser/assets/no_extensions.der
Binary files differ
diff --git a/rust/vendor/x509-parser/assets/no_extensions.pem b/rust/vendor/x509-parser/assets/no_extensions.pem
new file mode 100644
index 0000000..de3731f
--- /dev/null
+++ b/rust/vendor/x509-parser/assets/no_extensions.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBUTCB2KADAgECAgkAtXGSKEzrTR4wCgYIKoZIzj0EAwIwEDEOMAwGA1UEAwwF
+YmVubm8wHhcNMTgxMTEzMDI1NDQwWhcNMTkxMTEzMDI1NDQwWjAQMQ4wDAYDVQQD
+DAViZW5ubzB2MBAGByqGSM49AgEGBSuBBAAiA2IABDhrHLVTMHC7GTyB/MNztToW
+ss2zlmvR62X1pQaBN6fYhBJE1XYa0V2C1fGGXj92MencOtXyfYVxn+DY07gyT/71
+HQ12TJOe90wwjy2/6N1W1jOv5HjphVT8JQlVNqAC+DAKBggqhkjOPQQDAgNoADBl
+AjAfzS1tmZ+GSAaXrsPcfAd1A9yfWVtB8tWxFNNo2j7/cL3puf2vQnlwV/0BoZZ1
+K4ICMQDaE0HemgYp6RPQzeb96a2gjlaEbwNy1B8O74fE24WRXiOUm4eln9wGQ3I1
+iV6NtZU=
+-----END CERTIFICATE-----
diff --git a/rust/vendor/x509-parser/assets/test.csr b/rust/vendor/x509-parser/assets/test.csr
new file mode 100644
index 0000000..729df67
--- /dev/null
+++ b/rust/vendor/x509-parser/assets/test.csr
@@ -0,0 +1,8 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIBBjCBrQIBADAcMRowGAYDVQQDDBF0ZXN0LnJ1c3RpY2F0YS5mcjBZMBMGByqG
+SM49AgEGCCqGSM49AwEHA0IABMP1frFxwJLXiLU6UoqOPf31ucCm2NqR2yqpcHo6
+W7iWJe31OzYs0izP2qeUvdKfz2fpAbuGiRjwvN+H10dQQEGgLzAtBgkqhkiG9w0B
+CQ4xIDAeMBwGA1UdEQQVMBOCEXRlc3QucnVzdGljYXRhLmZyMAoGCCqGSM49BAMC
+A0gAMEUCIGqQHPHgpeyZa5YMLP2X5IwfmrvpIcg5fQ2xkXotGAa0AiEAydeBwr4r
+Iu7XDe015h8uz8xZs2QUEgRdr73lJXTX+Ck=
+-----END CERTIFICATE REQUEST-----
diff --git a/rust/vendor/x509-parser/assets/v1.der b/rust/vendor/x509-parser/assets/v1.der
new file mode 100644
index 0000000..f749852
--- /dev/null
+++ b/rust/vendor/x509-parser/assets/v1.der
Binary files differ
diff --git a/rust/vendor/x509-parser/examples/print-cert.rs b/rust/vendor/x509-parser/examples/print-cert.rs
new file mode 100644
index 0000000..9792085
--- /dev/null
+++ b/rust/vendor/x509-parser/examples/print-cert.rs
@@ -0,0 +1,417 @@
+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),
+ }
+}
+
+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: {:x}", key_id);
+ }
+ 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: {:x}", id);
+ }
+ 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(feature = "verify")]
+ {
+ 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!("{}Mask Generation Function: ", indent_s);
+ 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!("{}Mask Generation Function: ", indent_s);
+ 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{:x} ({})", e, 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!("{:02x}:", b)
+ });
+ 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(())
+}
diff --git a/rust/vendor/x509-parser/examples/print-crl.rs b/rust/vendor/x509-parser/examples/print-crl.rs
new file mode 100644
index 0000000..73337cb
--- /dev/null
+++ b/rust/vendor/x509-parser/examples/print-crl.rs
@@ -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(())
+}
diff --git a/rust/vendor/x509-parser/src/certificate.rs b/rust/vendor/x509-parser/src/certificate.rs
new file mode 100644
index 0000000..3bd6b8e
--- /dev/null
+++ b/rust/vendor/x509-parser/src/certificate.rs
@@ -0,0 +1,782 @@
+//! 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(feature = "verify")]
+use crate::verify::verify_signature;
+use asn1_rs::{BitString, FromDer, OptTaggedExplicit};
+use core::ops::Deref;
+use der_parser::ber::Tag;
+use der_parser::der::*;
+use der_parser::error::*;
+use der_parser::num_bigint::BigUint;
+use der_parser::*;
+use nom::{Offset, Parser};
+use oid_registry::Oid;
+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>,
+}
+
+impl<'a> X509Certificate<'a> {
+ /// 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(feature = "verify")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "verify")))]
+ 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> 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<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<'a> Parser<&'a [u8], X509Certificate<'a>, X509Error> for X509CertificateParser {
+ fn parse(&mut self, input: &'a [u8]) -> IResult<&'a [u8], X509Certificate<'a>, X509Error> {
+ parse_der_sequence_defined_g(|i, _| {
+ // pass options to TbsCertificate parser
+ let mut tbs_parser =
+ TbsCertificateParser::new().with_deep_parse_extensions(self.deep_parse_extensions);
+ let (i, tbs_certificate) = tbs_parser.parse(i)?;
+ let (i, signature_algorithm) = AlgorithmIdentifier::from_der(i)?;
+ let (i, signature_value) = parse_signature_value(i)?;
+ let cert = X509Certificate {
+ tbs_certificate,
+ signature_algorithm,
+ signature_value,
+ };
+ Ok((i, cert))
+ })(input)
+ }
+}
+
+#[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_INHIBITANT_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>>, 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<'a> AsRef<[u8]> for TbsCertificate<'a> {
+ #[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<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<'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<'a> FromDer<'a, 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<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) = OptTaggedExplicit::<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());
+ }
+}
diff --git a/rust/vendor/x509-parser/src/certification_request.rs b/rust/vendor/x509-parser/src/certification_request.rs
new file mode 100644
index 0000000..e56d05a
--- /dev/null
+++ b/rust/vendor/x509-parser/src/certification_request.rs
@@ -0,0 +1,166 @@
+use crate::cri_attributes::*;
+use crate::error::{X509Error, X509Result};
+use crate::extensions::*;
+use crate::x509::{
+ parse_signature_value, AlgorithmIdentifier, SubjectPublicKeyInfo, X509Name, X509Version,
+};
+
+#[cfg(feature = "verify")]
+use crate::verify::verify_signature;
+use asn1_rs::{BitString, FromDer};
+use der_parser::der::*;
+use der_parser::oid::Oid;
+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>,
+}
+
+impl<'a> X509CertificationRequest<'a> {
+ 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(feature = "verify")]
+ 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,
+ )
+ }
+}
+
+/// <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> {
+ parse_der_sequence_defined_g(|i, _| {
+ let (i, certification_request_info) = X509CertificationRequestInfo::from_der(i)?;
+ let (i, signature_algorithm) = AlgorithmIdentifier::from_der(i)?;
+ let (i, signature_value) = parse_signature_value(i)?;
+ let cert = X509CertificationRequest {
+ certification_request_info,
+ signature_algorithm,
+ signature_value,
+ };
+ Ok((i, cert))
+ })(i)
+ }
+}
+
+/// Certification Request Info structure
+///
+/// Certification request information is defined by the following ASN.1 structure:
+///
+/// <pre>
+/// CertificationRequestInfo ::= SEQUENCE {
+/// version INTEGER { v1(0) } (v1,...),
+/// subject Name,
+/// subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }},
+/// attributes [0] Attributes{{ CRIAttributes }}
+/// }
+/// </pre>
+///
+/// version is the version number; subject is the distinguished name of the certificate
+/// subject; subject_pki contains information about the public key being certified, and
+/// attributes is a collection of attributes providing additional information about the
+/// subject of the certificate.
+#[derive(Debug, PartialEq)]
+pub struct X509CertificationRequestInfo<'a> {
+ pub version: X509Version,
+ pub subject: X509Name<'a>,
+ pub subject_pki: SubjectPublicKeyInfo<'a>,
+ attributes: Vec<X509CriAttribute<'a>>,
+ pub raw: &'a [u8],
+}
+
+impl<'a> X509CertificationRequestInfo<'a> {
+ /// Get the CRL entry extensions.
+ #[inline]
+ pub fn attributes(&self) -> &[X509CriAttribute] {
+ &self.attributes
+ }
+
+ /// Returns an iterator over the CRL entry extensions
+ #[inline]
+ pub fn iter_attributes(&self) -> impl Iterator<Item = &X509CriAttribute> {
+ self.attributes.iter()
+ }
+
+ /// Searches for a CRL entry extension with the given `Oid`.
+ ///
+ /// 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<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)
+ }
+}
diff --git a/rust/vendor/x509-parser/src/cri_attributes.rs b/rust/vendor/x509-parser/src/cri_attributes.rs
new file mode 100644
index 0000000..1092200
--- /dev/null
+++ b/rust/vendor/x509-parser/src/cri_attributes.rs
@@ -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<X509CriAttribute> {
+ 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) = crate::cri_attributes::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))
+ }
+}
diff --git a/rust/vendor/x509-parser/src/error.rs b/rust/vendor/x509-parser/src/error.rs
new file mode 100644
index 0000000..85def3f
--- /dev/null
+++ b/rust/vendor/x509-parser/src/error.rs
@@ -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),
+}
diff --git a/rust/vendor/x509-parser/src/extensions/generalname.rs b/rust/vendor/x509-parser/src/extensions/generalname.rs
new file mode 100644
index 0000000..37e6f18
--- /dev/null
+++ b/rust/vendor/x509-parser/src/extensions/generalname.rs
@@ -0,0 +1,115 @@
+use crate::error::{X509Error, X509Result};
+use crate::prelude::format_serial;
+use crate::x509::X509Name;
+use asn1_rs::{Any, CheckDerConstraints, Class, Error, FromDer, Oid, Sequence};
+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.
+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>),
+}
+
+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)?;
+ 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(|_| nom::Err::Failure(Error::BerValueError))
+ }
+ let name = 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())),
+ };
+ Ok(name)
+ }
+}
+
+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<'a> fmt::Display for GeneralName<'a> {
+ 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),
+ }
+ }
+}
+
+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))
+}
diff --git a/rust/vendor/x509-parser/src/extensions/keyusage.rs b/rust/vendor/x509-parser/src/extensions/keyusage.rs
new file mode 100644
index 0000000..81a07f1
--- /dev/null
+++ b/rust/vendor/x509-parser/src/extensions/keyusage.rs
@@ -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))
+}
diff --git a/rust/vendor/x509-parser/src/extensions/mod.rs b/rust/vendor/x509-parser/src/extensions/mod.rs
new file mode 100644
index 0000000..868dc77
--- /dev/null
+++ b/rust/vendor/x509-parser/src/extensions/mod.rs
@@ -0,0 +1,1484 @@
+//! X.509 Extensions objects and types
+
+use crate::error::{X509Error, X509Result};
+use crate::time::ASN1Time;
+use crate::utils::format_serial;
+use crate::x509::{ReasonCode, RelativeDistinguishedName};
+
+use asn1_rs::FromDer;
+use der_parser::ber::parse_ber_bool;
+use der_parser::der::*;
+use der_parser::error::{BerError, BerResult};
+use der_parser::num_bigint::BigUint;
+use der_parser::oid::Oid;
+use nom::combinator::{all_consuming, complete, cut, map, map_res, opt};
+use nom::multi::{many0, many1};
+use nom::{Err, IResult, Parser};
+use oid_registry::*;
+use std::collections::HashMap;
+use std::fmt::{self, LowerHex};
+
+mod generalname;
+mod keyusage;
+mod nameconstraints;
+mod policymappings;
+mod sct;
+
+pub use generalname::*;
+pub use keyusage::*;
+pub use nameconstraints::*;
+pub use policymappings::*;
+pub use sct::*;
+
+/// X.509 version 3 extension
+///
+/// X.509 extensions allow adding attributes to objects like certificates or revocation lists.
+///
+/// Each extension in a certificate is designated as either critical or non-critical. A
+/// certificate using system MUST reject the certificate if it encounters a critical extension it
+/// does not recognize; however, a non-critical extension MAY be ignored if it is not recognized.
+///
+/// Each extension includes an OID and an ASN.1 structure. When an extension appears in a
+/// certificate, the OID appears as the field extnID and the corresponding ASN.1 encoded structure
+/// is the value of the octet string extnValue. A certificate MUST NOT include more than one
+/// instance of a particular extension.
+///
+/// When parsing an extension, the global extension structure (described above) is parsed,
+/// and the object is returned if it succeeds.
+/// During this step, it also attempts to parse the content of the extension, if known.
+/// The returned object has a
+/// [`X509Extension::parsed_extension()`] method. The returned
+/// enum is either a known extension, or the special value `ParsedExtension::UnsupportedExtension`.
+///
+/// # Example
+///
+/// ```rust
+/// use x509_parser::prelude::FromDer;
+/// use x509_parser::extensions::{X509Extension, ParsedExtension};
+///
+/// static DER: &[u8] = &[
+/// 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04, 0x16, 0x04, 0x14, 0xA3, 0x05, 0x2F, 0x18,
+/// 0x60, 0x50, 0xC2, 0x89, 0x0A, 0xDD, 0x2B, 0x21, 0x4F, 0xFF, 0x8E, 0x4E, 0xA8, 0x30, 0x31,
+/// 0x36 ];
+///
+/// # fn main() {
+/// let res = X509Extension::from_der(DER);
+/// match res {
+/// Ok((_rem, ext)) => {
+/// println!("Extension OID: {}", ext.oid);
+/// println!(" Critical: {}", ext.critical);
+/// let parsed_ext = ext.parsed_extension();
+/// assert!(!parsed_ext.unsupported());
+/// assert!(parsed_ext.error().is_none());
+/// if let ParsedExtension::SubjectKeyIdentifier(key_id) = parsed_ext {
+/// assert!(key_id.0.len() > 0);
+/// } else {
+/// panic!("Extension has wrong type");
+/// }
+/// },
+/// _ => panic!("x509 extension parsing failed: {:?}", res),
+/// }
+/// # }
+/// ```
+#[derive(Clone, Debug, PartialEq)]
+pub struct X509Extension<'a> {
+ /// OID describing the extension content
+ pub oid: Oid<'a>,
+ /// Boolean value describing the 'critical' attribute of the extension
+ ///
+ /// An extension includes the boolean critical, with a default value of FALSE.
+ pub critical: bool,
+ /// Raw content of the extension
+ pub value: &'a [u8],
+ pub(crate) parsed_extension: ParsedExtension<'a>,
+}
+
+impl<'a> X509Extension<'a> {
+ /// Creates a new extension with the provided values.
+ #[inline]
+ pub const fn new(
+ oid: Oid<'a>,
+ critical: bool,
+ value: &'a [u8],
+ parsed_extension: ParsedExtension<'a>,
+ ) -> X509Extension<'a> {
+ X509Extension {
+ oid,
+ critical,
+ value,
+ parsed_extension,
+ }
+ }
+
+ /// Return the extension type or `UnsupportedExtension` if the extension is not implemented.
+ #[inline]
+ pub fn parsed_extension(&self) -> &ParsedExtension<'a> {
+ &self.parsed_extension
+ }
+}
+
+/// <pre>
+/// Extension ::= SEQUENCE {
+/// extnID OBJECT IDENTIFIER,
+/// critical BOOLEAN DEFAULT FALSE,
+/// extnValue OCTET STRING }
+/// </pre>
+impl<'a> FromDer<'a, X509Error> for X509Extension<'a> {
+ fn from_der(i: &'a [u8]) -> X509Result<Self> {
+ X509ExtensionParser::new().parse(i)
+ }
+}
+
+/// `X509Extension` parser builder
+#[derive(Clone, Copy, Debug)]
+pub struct X509ExtensionParser {
+ deep_parse_extensions: bool,
+}
+
+impl X509ExtensionParser {
+ #[inline]
+ pub const fn new() -> Self {
+ X509ExtensionParser {
+ deep_parse_extensions: true,
+ }
+ }
+
+ #[inline]
+ pub const fn with_deep_parse_extensions(self, deep_parse_extensions: bool) -> Self {
+ X509ExtensionParser {
+ deep_parse_extensions,
+ }
+ }
+}
+
+impl<'a> Parser<&'a [u8], X509Extension<'a>, X509Error> for X509ExtensionParser {
+ fn parse(&mut self, input: &'a [u8]) -> IResult<&'a [u8], X509Extension<'a>, X509Error> {
+ parse_der_sequence_defined_g(|i, _| {
+ let (i, oid) = Oid::from_der(i)?;
+ let (i, critical) = der_read_critical(i)?;
+ let (i, value) = <&[u8]>::from_der(i)?;
+ let (i, parsed_extension) = if self.deep_parse_extensions {
+ parser::parse_extension(i, value, &oid)?
+ } else {
+ (&[] as &[_], ParsedExtension::Unparsed)
+ };
+ let ext = X509Extension {
+ oid,
+ critical,
+ value,
+ parsed_extension,
+ };
+ Ok((i, ext))
+ })(input)
+ .map_err(|_| X509Error::InvalidExtensions.into())
+ }
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub enum ParsedExtension<'a> {
+ /// Crate parser does not support this extension (yet)
+ UnsupportedExtension {
+ oid: Oid<'a>,
+ },
+ ParseError {
+ error: Err<BerError>,
+ },
+ /// Section 4.2.1.1 of rfc 5280
+ AuthorityKeyIdentifier(AuthorityKeyIdentifier<'a>),
+ /// Section 4.2.1.2 of rfc 5280
+ SubjectKeyIdentifier(KeyIdentifier<'a>),
+ /// Section 4.2.1.3 of rfc 5280
+ KeyUsage(KeyUsage),
+ /// Section 4.2.1.4 of rfc 5280
+ CertificatePolicies(CertificatePolicies<'a>),
+ /// Section 4.2.1.5 of rfc 5280
+ PolicyMappings(PolicyMappings<'a>),
+ /// Section 4.2.1.6 of rfc 5280
+ SubjectAlternativeName(SubjectAlternativeName<'a>),
+ /// Section 4.2.1.7 of rfc 5280
+ IssuerAlternativeName(IssuerAlternativeName<'a>),
+ /// Section 4.2.1.9 of rfc 5280
+ BasicConstraints(BasicConstraints),
+ /// Section 4.2.1.10 of rfc 5280
+ NameConstraints(NameConstraints<'a>),
+ /// Section 4.2.1.11 of rfc 5280
+ PolicyConstraints(PolicyConstraints),
+ /// Section 4.2.1.12 of rfc 5280
+ ExtendedKeyUsage(ExtendedKeyUsage<'a>),
+ /// Section 4.2.1.13 of rfc 5280
+ CRLDistributionPoints(CRLDistributionPoints<'a>),
+ /// Section 4.2.1.14 of rfc 5280
+ InhibitAnyPolicy(InhibitAnyPolicy),
+ /// Section 4.2.2.1 of rfc 5280
+ AuthorityInfoAccess(AuthorityInfoAccess<'a>),
+ /// Netscape certificate type (subject is SSL client, an SSL server, or a CA)
+ NSCertType(NSCertType),
+ /// Netscape certificate comment
+ NsCertComment(&'a str),
+ /// Section 5.3.1 of rfc 5280
+ CRLNumber(BigUint),
+ /// Section 5.3.1 of rfc 5280
+ ReasonCode(ReasonCode),
+ /// Section 5.3.3 of rfc 5280
+ InvalidityDate(ASN1Time),
+ /// rfc 6962
+ SCT(Vec<SignedCertificateTimestamp<'a>>),
+ /// Unparsed extension (was not requested in parsing options)
+ Unparsed,
+}
+
+impl<'a> ParsedExtension<'a> {
+ /// Return `true` if the extension is unsupported
+ pub fn unsupported(&self) -> bool {
+ matches!(self, &ParsedExtension::UnsupportedExtension { .. })
+ }
+
+ /// Return a reference on the parsing error if the extension parsing failed
+ pub fn error(&self) -> Option<&Err<BerError>> {
+ match self {
+ ParsedExtension::ParseError { error } => Some(error),
+ _ => None,
+ }
+ }
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct AuthorityKeyIdentifier<'a> {
+ pub key_identifier: Option<KeyIdentifier<'a>>,
+ pub authority_cert_issuer: Option<Vec<GeneralName<'a>>>,
+ pub authority_cert_serial: Option<&'a [u8]>,
+}
+
+impl<'a> FromDer<'a, X509Error> for AuthorityKeyIdentifier<'a> {
+ fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
+ parser::parse_authoritykeyidentifier(i).map_err(Err::convert)
+ }
+}
+
+pub type CertificatePolicies<'a> = Vec<PolicyInformation<'a>>;
+
+// impl<'a> FromDer<'a> for CertificatePolicies<'a> {
+// fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
+// parser::parse_certificatepolicies(i).map_err(Err::convert)
+// }
+// }
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct PolicyInformation<'a> {
+ pub policy_id: Oid<'a>,
+ pub policy_qualifiers: Option<Vec<PolicyQualifierInfo<'a>>>,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct PolicyQualifierInfo<'a> {
+ pub policy_qualifier_id: Oid<'a>,
+ pub qualifier: &'a [u8],
+}
+
+/// Identifies whether the subject of the certificate is a CA, and the max validation depth.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct BasicConstraints {
+ pub ca: bool,
+ pub path_len_constraint: Option<u32>,
+}
+
+impl<'a> FromDer<'a, X509Error> for BasicConstraints {
+ fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
+ parser::parse_basicconstraints(i).map_err(Err::convert)
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct KeyIdentifier<'a>(pub &'a [u8]);
+
+impl<'a> FromDer<'a, X509Error> for KeyIdentifier<'a> {
+ fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
+ parser::parse_keyidentifier(i).map_err(Err::convert)
+ }
+}
+
+impl<'a> LowerHex for KeyIdentifier<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let s = format_serial(self.0);
+ f.write_str(&s)
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct NSCertType(u8);
+
+// The value is a bit-string, where the individual bit positions are defined as:
+//
+// bit-0 SSL client - this cert is certified for SSL client authentication use
+// bit-1 SSL server - this cert is certified for SSL server authentication use
+// bit-2 S/MIME - this cert is certified for use by clients (New in PR3)
+// bit-3 Object Signing - this cert is certified for signing objects such as Java applets and plugins(New in PR3)
+// bit-4 Reserved - this bit is reserved for future use
+// bit-5 SSL CA - this cert is certified for issuing certs for SSL use
+// bit-6 S/MIME CA - this cert is certified for issuing certs for S/MIME use (New in PR3)
+// bit-7 Object Signing CA - this cert is certified for issuing certs for Object Signing (New in PR3)
+impl NSCertType {
+ pub fn ssl_client(&self) -> bool {
+ self.0 & 0x1 == 1
+ }
+ pub fn ssl_server(&self) -> bool {
+ (self.0 >> 1) & 1 == 1
+ }
+ pub fn smime(&self) -> bool {
+ (self.0 >> 2) & 1 == 1
+ }
+ pub fn object_signing(&self) -> bool {
+ (self.0 >> 3) & 1 == 1
+ }
+ pub fn ssl_ca(&self) -> bool {
+ (self.0 >> 5) & 1 == 1
+ }
+ pub fn smime_ca(&self) -> bool {
+ (self.0 >> 6) & 1 == 1
+ }
+ pub fn object_signing_ca(&self) -> bool {
+ (self.0 >> 7) & 1 == 1
+ }
+}
+
+const NS_CERT_TYPE_FLAGS: &[&str] = &[
+ "SSL CLient",
+ "SSL Server",
+ "S/MIME",
+ "Object Signing",
+ "Reserved",
+ "SSL CA",
+ "S/MIME CA",
+ "Object Signing CA",
+];
+
+impl fmt::Display for NSCertType {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut s = String::new();
+ let mut acc = self.0;
+ for flag_text in NS_CERT_TYPE_FLAGS {
+ if acc & 1 != 0 {
+ s = s + flag_text + ", ";
+ }
+ acc >>= 1;
+ }
+ s.pop();
+ s.pop();
+ f.write_str(&s)
+ }
+}
+
+impl<'a> FromDer<'a, X509Error> for NSCertType {
+ fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
+ parser::parse_nscerttype(i).map_err(Err::convert)
+ }
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct AuthorityInfoAccess<'a> {
+ pub accessdescs: Vec<AccessDescription<'a>>,
+}
+
+impl<'a> AuthorityInfoAccess<'a> {
+ /// Returns an iterator over the Access Descriptors
+ pub fn iter(&self) -> impl Iterator<Item = &AccessDescription<'a>> {
+ self.accessdescs.iter()
+ }
+
+ /// Returns a `HashMap` mapping `Oid` to the list of references to `GeneralNames`
+ ///
+ /// If several names match the same `Oid`, they are merged in the same entry.
+ pub fn as_hashmap(&self) -> HashMap<Oid<'a>, Vec<&GeneralName<'a>>> {
+ // create the hashmap and merge entries with same OID
+ let mut m: HashMap<Oid, Vec<&GeneralName>> = HashMap::new();
+ for desc in &self.accessdescs {
+ let AccessDescription {
+ access_method: oid,
+ access_location: gn,
+ } = desc;
+ if let Some(general_names) = m.get_mut(oid) {
+ general_names.push(gn);
+ } else {
+ m.insert(oid.clone(), vec![gn]);
+ }
+ }
+ m
+ }
+
+ /// Returns a `HashMap` mapping `Oid` to the list of `GeneralNames` (consuming the input)
+ ///
+ /// If several names match the same `Oid`, they are merged in the same entry.
+ pub fn into_hashmap(self) -> HashMap<Oid<'a>, Vec<GeneralName<'a>>> {
+ let mut aia_list = self.accessdescs;
+ // create the hashmap and merge entries with same OID
+ let mut m: HashMap<Oid, Vec<GeneralName>> = HashMap::new();
+ for desc in aia_list.drain(..) {
+ let AccessDescription {
+ access_method: oid,
+ access_location: gn,
+ } = desc;
+ if let Some(general_names) = m.get_mut(&oid) {
+ general_names.push(gn);
+ } else {
+ m.insert(oid, vec![gn]);
+ }
+ }
+ m
+ }
+}
+
+impl<'a> FromDer<'a, X509Error> for AuthorityInfoAccess<'a> {
+ fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
+ parser::parse_authorityinfoaccess(i).map_err(Err::convert)
+ }
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct AccessDescription<'a> {
+ pub access_method: Oid<'a>,
+ pub access_location: GeneralName<'a>,
+}
+
+impl<'a> AccessDescription<'a> {
+ pub const fn new(access_method: Oid<'a>, access_location: GeneralName<'a>) -> Self {
+ AccessDescription {
+ access_method,
+ access_location,
+ }
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct InhibitAnyPolicy {
+ pub skip_certs: u32,
+}
+
+impl<'a> FromDer<'a, X509Error> for InhibitAnyPolicy {
+ fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
+ map(parse_der_u32, |skip_certs| InhibitAnyPolicy { skip_certs })(i).map_err(Err::convert)
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct PolicyConstraints {
+ pub require_explicit_policy: Option<u32>,
+ pub inhibit_policy_mapping: Option<u32>,
+}
+
+impl<'a> FromDer<'a, X509Error> for PolicyConstraints {
+ fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
+ parser::parse_policyconstraints(i).map_err(Err::convert)
+ }
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct SubjectAlternativeName<'a> {
+ pub general_names: Vec<GeneralName<'a>>,
+}
+
+impl<'a> FromDer<'a, X509Error> for SubjectAlternativeName<'a> {
+ fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
+ parse_der_sequence_defined_g(|input, _| {
+ let (i, general_names) =
+ all_consuming(many0(complete(cut(GeneralName::from_der))))(input)?;
+ Ok((i, SubjectAlternativeName { general_names }))
+ })(i)
+ }
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct IssuerAlternativeName<'a> {
+ pub general_names: Vec<GeneralName<'a>>,
+}
+
+impl<'a> FromDer<'a, X509Error> for IssuerAlternativeName<'a> {
+ fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
+ parse_der_sequence_defined_g(|input, _| {
+ let (i, general_names) =
+ all_consuming(many0(complete(cut(GeneralName::from_der))))(input)?;
+ Ok((i, IssuerAlternativeName { general_names }))
+ })(i)
+ }
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct CRLDistributionPoints<'a> {
+ pub points: Vec<CRLDistributionPoint<'a>>,
+}
+
+impl<'a> std::ops::Deref for CRLDistributionPoints<'a> {
+ type Target = Vec<CRLDistributionPoint<'a>>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.points
+ }
+}
+
+impl<'a> FromDer<'a, X509Error> for CRLDistributionPoints<'a> {
+ fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
+ parser::parse_crldistributionpoints(i).map_err(Err::convert)
+ }
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct CRLDistributionPoint<'a> {
+ pub distribution_point: Option<DistributionPointName<'a>>,
+ pub reasons: Option<ReasonFlags>,
+ pub crl_issuer: Option<Vec<GeneralName<'a>>>,
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub enum DistributionPointName<'a> {
+ FullName(Vec<GeneralName<'a>>),
+ NameRelativeToCRLIssuer(RelativeDistinguishedName<'a>),
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct ReasonFlags {
+ pub flags: u16,
+}
+
+impl ReasonFlags {
+ pub fn key_compromise(&self) -> bool {
+ (self.flags >> 1) & 1 == 1
+ }
+ pub fn ca_compromise(&self) -> bool {
+ (self.flags >> 2) & 1 == 1
+ }
+ pub fn affilation_changed(&self) -> bool {
+ (self.flags >> 3) & 1 == 1
+ }
+ pub fn superseded(&self) -> bool {
+ (self.flags >> 4) & 1 == 1
+ }
+ pub fn cessation_of_operation(&self) -> bool {
+ (self.flags >> 5) & 1 == 1
+ }
+ pub fn certificate_hold(&self) -> bool {
+ (self.flags >> 6) & 1 == 1
+ }
+ pub fn privelege_withdrawn(&self) -> bool {
+ (self.flags >> 7) & 1 == 1
+ }
+ pub fn aa_compromise(&self) -> bool {
+ (self.flags >> 8) & 1 == 1
+ }
+}
+
+const REASON_FLAGS: &[&str] = &[
+ "Unused",
+ "Key Compromise",
+ "CA Compromise",
+ "Affiliation Changed",
+ "Superseded",
+ "Cessation Of Operation",
+ "Certificate Hold",
+ "Privilege Withdrawn",
+ "AA Compromise",
+];
+
+impl fmt::Display for ReasonFlags {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut s = String::new();
+ let mut acc = self.flags;
+ for flag_text in REASON_FLAGS {
+ if acc & 1 != 0 {
+ s = s + flag_text + ", ";
+ }
+ acc >>= 1;
+ }
+ s.pop();
+ s.pop();
+ f.write_str(&s)
+ }
+}
+
+pub(crate) mod parser {
+ use crate::extensions::*;
+ use crate::time::ASN1Time;
+ use asn1_rs::{GeneralizedTime, ParseResult};
+ use der_parser::error::BerError;
+ use der_parser::{oid::Oid, *};
+ use lazy_static::lazy_static;
+ use nom::combinator::{cut, map};
+ use nom::{Err, IResult};
+
+ type ExtParser = fn(&[u8]) -> IResult<&[u8], ParsedExtension, BerError>;
+
+ lazy_static! {
+ static ref EXTENSION_PARSERS: HashMap<Oid<'static>, ExtParser> = {
+ macro_rules! add {
+ ($m:ident, $oid:ident, $p:ident) => {
+ $m.insert($oid, $p as ExtParser);
+ };
+ }
+
+ let mut m = HashMap::new();
+ add!(
+ m,
+ OID_X509_EXT_SUBJECT_KEY_IDENTIFIER,
+ parse_keyidentifier_ext
+ );
+ add!(m, OID_X509_EXT_KEY_USAGE, parse_keyusage_ext);
+ add!(
+ m,
+ OID_X509_EXT_SUBJECT_ALT_NAME,
+ parse_subjectalternativename_ext
+ );
+ add!(
+ m,
+ OID_X509_EXT_ISSUER_ALT_NAME,
+ parse_issueralternativename_ext
+ );
+ add!(
+ m,
+ OID_X509_EXT_BASIC_CONSTRAINTS,
+ parse_basicconstraints_ext
+ );
+ add!(m, OID_X509_EXT_NAME_CONSTRAINTS, parse_nameconstraints_ext);
+ add!(
+ m,
+ OID_X509_EXT_CERTIFICATE_POLICIES,
+ parse_certificatepolicies_ext
+ );
+ add!(m, OID_X509_EXT_POLICY_MAPPINGS, parse_policymappings_ext);
+ add!(
+ m,
+ OID_X509_EXT_POLICY_CONSTRAINTS,
+ parse_policyconstraints_ext
+ );
+ add!(
+ m,
+ OID_X509_EXT_EXTENDED_KEY_USAGE,
+ parse_extendedkeyusage_ext
+ );
+ add!(
+ m,
+ OID_X509_EXT_CRL_DISTRIBUTION_POINTS,
+ parse_crldistributionpoints_ext
+ );
+ add!(
+ m,
+ OID_X509_EXT_INHIBITANT_ANY_POLICY,
+ parse_inhibitanypolicy_ext
+ );
+ add!(
+ m,
+ OID_PKIX_AUTHORITY_INFO_ACCESS,
+ parse_authorityinfoaccess_ext
+ );
+ add!(
+ m,
+ OID_X509_EXT_AUTHORITY_KEY_IDENTIFIER,
+ parse_authoritykeyidentifier_ext
+ );
+ add!(m, OID_CT_LIST_SCT, parse_sct_ext);
+ add!(m, OID_X509_EXT_CERT_TYPE, parse_nscerttype_ext);
+ add!(m, OID_X509_EXT_CERT_COMMENT, parse_nscomment_ext);
+ add!(m, OID_X509_EXT_CRL_NUMBER, parse_crl_number);
+ add!(m, OID_X509_EXT_REASON_CODE, parse_reason_code);
+ add!(m, OID_X509_EXT_INVALIDITY_DATE, parse_invalidity_date);
+ m
+ };
+ }
+
+ // look into the parser map if the extension is known, and parse it
+ // otherwise, leave it as UnsupportedExtension
+ fn parse_extension0<'a>(
+ orig_i: &'a [u8],
+ i: &'a [u8],
+ oid: &Oid,
+ ) -> IResult<&'a [u8], ParsedExtension<'a>, BerError> {
+ if let Some(parser) = EXTENSION_PARSERS.get(oid) {
+ match parser(i) {
+ Ok((_, ext)) => Ok((orig_i, ext)),
+ Err(error) => Ok((orig_i, ParsedExtension::ParseError { error })),
+ }
+ } else {
+ Ok((
+ orig_i,
+ ParsedExtension::UnsupportedExtension {
+ oid: oid.to_owned(),
+ },
+ ))
+ }
+ }
+
+ pub(crate) fn parse_extension<'a>(
+ orig_i: &'a [u8],
+ i: &'a [u8],
+ oid: &Oid,
+ ) -> IResult<&'a [u8], ParsedExtension<'a>, BerError> {
+ parse_extension0(orig_i, i, oid)
+ }
+
+ /// Parse a "Basic Constraints" extension
+ ///
+ /// <pre>
+ /// id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 }
+ /// BasicConstraints ::= SEQUENCE {
+ /// cA BOOLEAN DEFAULT FALSE,
+ /// pathLenConstraint INTEGER (0..MAX) OPTIONAL }
+ /// </pre>
+ ///
+ /// Note the maximum length of the `pathLenConstraint` field is limited to the size of a 32-bits
+ /// unsigned integer, and parsing will fail if value if larger.
+ pub(super) fn parse_basicconstraints(i: &[u8]) -> IResult<&[u8], BasicConstraints, BerError> {
+ let (rem, obj) = parse_der_sequence(i)?;
+ if let Ok(seq) = obj.as_sequence() {
+ let (ca, path_len_constraint) = match seq.len() {
+ 0 => (false, None),
+ 1 => {
+ if let Ok(b) = seq[0].as_bool() {
+ (b, None)
+ } else if let Ok(u) = seq[0].as_u32() {
+ (false, Some(u))
+ } else {
+ return Err(nom::Err::Error(BerError::InvalidTag));
+ }
+ }
+ 2 => {
+ let ca = seq[0]
+ .as_bool()
+ .or(Err(nom::Err::Error(BerError::InvalidLength)))?;
+ let pl = seq[1]
+ .as_u32()
+ .or(Err(nom::Err::Error(BerError::InvalidLength)))?;
+ (ca, Some(pl))
+ }
+ _ => return Err(nom::Err::Error(BerError::InvalidLength)),
+ };
+ Ok((
+ rem,
+ BasicConstraints {
+ ca,
+ path_len_constraint,
+ },
+ ))
+ } else {
+ Err(nom::Err::Error(BerError::InvalidLength))
+ }
+ }
+
+ fn parse_basicconstraints_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
+ map(parse_basicconstraints, ParsedExtension::BasicConstraints)(i)
+ }
+
+ fn parse_nameconstraints_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
+ map(parse_nameconstraints, ParsedExtension::NameConstraints)(i)
+ }
+
+ pub(super) fn parse_subjectalternativename_ext(
+ i: &[u8],
+ ) -> IResult<&[u8], ParsedExtension, BerError> {
+ parse_der_sequence_defined_g(|input, _| {
+ let (i, general_names) = all_consuming(many0(complete(cut(parse_generalname))))(input)?;
+ Ok((
+ i,
+ ParsedExtension::SubjectAlternativeName(SubjectAlternativeName { general_names }),
+ ))
+ })(i)
+ }
+
+ pub(super) fn parse_issueralternativename_ext(
+ i: &[u8],
+ ) -> IResult<&[u8], ParsedExtension, BerError> {
+ parse_der_sequence_defined_g(|input, _| {
+ let (i, general_names) = all_consuming(many0(complete(cut(parse_generalname))))(input)?;
+ Ok((
+ i,
+ ParsedExtension::IssuerAlternativeName(IssuerAlternativeName { general_names }),
+ ))
+ })(i)
+ }
+
+ pub(super) fn parse_policyconstraints(i: &[u8]) -> IResult<&[u8], PolicyConstraints, BerError> {
+ parse_der_sequence_defined_g(|input, _| {
+ let (i, require_explicit_policy) = opt(complete(map_res(
+ parse_der_tagged_implicit(0, parse_der_content(Tag::Integer)),
+ |x| x.as_u32(),
+ )))(input)?;
+ let (i, inhibit_policy_mapping) = all_consuming(opt(complete(map_res(
+ parse_der_tagged_implicit(1, parse_der_content(Tag::Integer)),
+ |x| x.as_u32(),
+ ))))(i)?;
+ let policy_constraint = PolicyConstraints {
+ require_explicit_policy,
+ inhibit_policy_mapping,
+ };
+ Ok((i, policy_constraint))
+ })(i)
+ }
+
+ fn parse_policyconstraints_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
+ map(parse_policyconstraints, ParsedExtension::PolicyConstraints)(i)
+ }
+
+ fn parse_policymappings_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
+ map(parse_policymappings, ParsedExtension::PolicyMappings)(i)
+ }
+
+ fn parse_inhibitanypolicy_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
+ let (ret, skip_certs) = parse_der_u32(i)?;
+ Ok((
+ ret,
+ ParsedExtension::InhibitAnyPolicy(InhibitAnyPolicy { skip_certs }),
+ ))
+ }
+
+ fn parse_extendedkeyusage_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
+ map(parse_extendedkeyusage, ParsedExtension::ExtendedKeyUsage)(i)
+ }
+
+ // DistributionPointName ::= CHOICE {
+ // fullName [0] GeneralNames,
+ // nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
+ fn parse_distributionpointname(i: &[u8]) -> IResult<&[u8], DistributionPointName, BerError> {
+ let (rem, header) = der_read_element_header(i)?;
+ match header.tag().0 {
+ 0 => {
+ let (rem, names) = many1(complete(parse_generalname))(rem)?;
+ Ok((rem, DistributionPointName::FullName(names)))
+ }
+ 1 => {
+ let (rem, rdn) = RelativeDistinguishedName::from_der(rem)
+ .map_err(|_| BerError::BerValueError)?;
+ Ok((rem, DistributionPointName::NameRelativeToCRLIssuer(rdn)))
+ }
+ _ => Err(Err::Error(BerError::InvalidTag)),
+ }
+ }
+
+ // ReasonFlags ::= BIT STRING {
+ // unused (0),
+ // keyCompromise (1),
+ // cACompromise (2),
+ // affiliationChanged (3),
+ // superseded (4),
+ // cessationOfOperation (5),
+ // certificateHold (6),
+ // privilegeWithdrawn (7),
+ // aACompromise (8) }
+ fn parse_tagged1_reasons(i: &[u8]) -> BerResult<ReasonFlags> {
+ let (rem, obj) = parse_der_tagged_implicit(1, parse_der_content(Tag::BitString))(i)?;
+ if let DerObjectContent::BitString(_, b) = obj.content {
+ let flags = b
+ .data
+ .iter()
+ .rev()
+ .fold(0, |acc, x| acc << 8 | (x.reverse_bits() as u16));
+ Ok((rem, ReasonFlags { flags }))
+ } else {
+ Err(nom::Err::Failure(BerError::InvalidTag))
+ }
+ }
+
+ fn parse_crlissuer_content(i: &[u8]) -> BerResult<Vec<GeneralName>> {
+ many1(complete(parse_generalname))(i)
+ }
+
+ // DistributionPoint ::= SEQUENCE {
+ // distributionPoint [0] DistributionPointName OPTIONAL,
+ // reasons [1] ReasonFlags OPTIONAL,
+ // cRLIssuer [2] GeneralNames OPTIONAL }
+ pub(super) fn parse_crldistributionpoint(
+ i: &[u8],
+ ) -> IResult<&[u8], CRLDistributionPoint, BerError> {
+ parse_der_sequence_defined_g(|content, _| {
+ let (rem, distribution_point) =
+ opt(complete(parse_der_tagged_explicit_g(0, |b, _| {
+ parse_distributionpointname(b)
+ })))(content)?;
+ let (rem, reasons) = opt(complete(parse_tagged1_reasons))(rem)?;
+ let (rem, crl_issuer) = opt(complete(parse_der_tagged_implicit_g(2, |i, _, _| {
+ parse_crlissuer_content(i)
+ })))(rem)?;
+ let crl_dp = CRLDistributionPoint {
+ distribution_point,
+ reasons,
+ crl_issuer,
+ };
+ Ok((rem, crl_dp))
+ })(i)
+ }
+
+ pub(super) fn parse_crldistributionpoints(
+ i: &[u8],
+ ) -> IResult<&[u8], CRLDistributionPoints, BerError> {
+ let (ret, crldps) = parse_der_sequence_of_v(parse_crldistributionpoint)(i)?;
+ Ok((ret, CRLDistributionPoints { points: crldps }))
+ }
+
+ fn parse_crldistributionpoints_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
+ map(
+ parse_crldistributionpoints,
+ ParsedExtension::CRLDistributionPoints,
+ )(i)
+ }
+
+ // AuthorityInfoAccessSyntax ::=
+ // SEQUENCE SIZE (1..MAX) OF AccessDescription
+ //
+ // AccessDescription ::= SEQUENCE {
+ // accessMethod OBJECT IDENTIFIER,
+ // accessLocation GeneralName }
+ pub(super) fn parse_authorityinfoaccess(
+ i: &[u8],
+ ) -> IResult<&[u8], AuthorityInfoAccess, BerError> {
+ fn parse_aia(i: &[u8]) -> IResult<&[u8], AccessDescription, BerError> {
+ parse_der_sequence_defined_g(|content, _| {
+ // Read first element, an oid.
+ let (gn, oid) = Oid::from_der(content)?;
+ // Parse second element
+ let (rest, gn) = parse_generalname(gn)?;
+ Ok((rest, AccessDescription::new(oid, gn)))
+ })(i)
+ }
+ let (ret, accessdescs) = parse_der_sequence_of_v(parse_aia)(i)?;
+ Ok((ret, AuthorityInfoAccess { accessdescs }))
+ }
+
+ fn parse_authorityinfoaccess_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
+ map(
+ parse_authorityinfoaccess,
+ ParsedExtension::AuthorityInfoAccess,
+ )(i)
+ }
+
+ fn parse_aki_content<'a>(
+ i: &'a [u8],
+ _hdr: Header<'_>,
+ ) -> IResult<&'a [u8], AuthorityKeyIdentifier<'a>, BerError> {
+ let (i, key_identifier) = opt(complete(parse_der_tagged_implicit_g(0, |d, _, _| {
+ Ok((&[], KeyIdentifier(d)))
+ })))(i)?;
+ let (i, authority_cert_issuer) =
+ opt(complete(parse_der_tagged_implicit_g(1, |d, _, _| {
+ many0(complete(parse_generalname))(d)
+ })))(i)?;
+ let (i, authority_cert_serial) = opt(complete(parse_der_tagged_implicit(
+ 2,
+ parse_der_content(Tag::Integer),
+ )))(i)?;
+ let authority_cert_serial = authority_cert_serial.and_then(|o| o.as_slice().ok());
+ let aki = AuthorityKeyIdentifier {
+ key_identifier,
+ authority_cert_issuer,
+ authority_cert_serial,
+ };
+ Ok((i, aki))
+ }
+
+ // RFC 5280 section 4.2.1.1: Authority Key Identifier
+ pub(super) fn parse_authoritykeyidentifier(
+ i: &[u8],
+ ) -> IResult<&[u8], AuthorityKeyIdentifier, BerError> {
+ let (rem, aki) = parse_der_sequence_defined_g(parse_aki_content)(i)?;
+ Ok((rem, aki))
+ }
+
+ fn parse_authoritykeyidentifier_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
+ map(
+ parse_authoritykeyidentifier,
+ ParsedExtension::AuthorityKeyIdentifier,
+ )(i)
+ }
+
+ pub(super) fn parse_keyidentifier(i: &[u8]) -> IResult<&[u8], KeyIdentifier, BerError> {
+ let (rest, id) = <&[u8]>::from_der(i)?;
+ let ki = KeyIdentifier(id);
+ Ok((rest, ki))
+ }
+
+ fn parse_keyidentifier_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
+ map(parse_keyidentifier, ParsedExtension::SubjectKeyIdentifier)(i)
+ }
+
+ fn parse_keyusage_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
+ map(parse_keyusage, ParsedExtension::KeyUsage)(i)
+ }
+
+ pub(super) fn parse_nscerttype(i: &[u8]) -> IResult<&[u8], NSCertType, BerError> {
+ let (rest, obj) = parse_der_bitstring(i)?;
+ let bitstring = obj
+ .content
+ .as_bitstring()
+ .or(Err(Err::Error(BerError::BerTypeError)))?;
+ // bitstring should be 1 byte long
+ if bitstring.data.len() != 1 {
+ return Err(Err::Error(BerError::BerValueError));
+ }
+ let flags = bitstring.data[0].reverse_bits();
+ Ok((rest, NSCertType(flags)))
+ }
+
+ fn parse_nscerttype_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
+ map(parse_nscerttype, ParsedExtension::NSCertType)(i)
+ }
+
+ fn parse_nscomment_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
+ match parse_der_ia5string(i) {
+ Ok((i, obj)) => {
+ let s = obj.as_str()?;
+ Ok((i, ParsedExtension::NsCertComment(s)))
+ }
+ Err(e) => {
+ // Some implementations encode the comment directly, without
+ // wrapping it in an IA5String
+ if let Ok(s) = std::str::from_utf8(i) {
+ Ok((&[], ParsedExtension::NsCertComment(s)))
+ } else {
+ Err(e)
+ }
+ }
+ }
+ }
+
+ // CertificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
+ //
+ // PolicyInformation ::= SEQUENCE {
+ // policyIdentifier CertPolicyId,
+ // policyQualifiers SEQUENCE SIZE (1..MAX) OF
+ // PolicyQualifierInfo OPTIONAL }
+ //
+ // CertPolicyId ::= OBJECT IDENTIFIER
+ //
+ // PolicyQualifierInfo ::= SEQUENCE {
+ // policyQualifierId PolicyQualifierId,
+ // qualifier ANY DEFINED BY policyQualifierId }
+ //
+ // -- Implementations that recognize additional policy qualifiers MUST
+ // -- augment the following definition for PolicyQualifierId
+ //
+ // PolicyQualifierId ::= OBJECT IDENTIFIER ( id-qt-cps | id-qt-unotice )
+ pub(super) fn parse_certificatepolicies(
+ i: &[u8],
+ ) -> IResult<&[u8], Vec<PolicyInformation>, BerError> {
+ fn parse_policy_qualifier_info(i: &[u8]) -> IResult<&[u8], PolicyQualifierInfo, BerError> {
+ parse_der_sequence_defined_g(|content, _| {
+ let (rem, policy_qualifier_id) = Oid::from_der(content)?;
+ let info = PolicyQualifierInfo {
+ policy_qualifier_id,
+ qualifier: rem,
+ };
+ Ok((&[], info))
+ })(i)
+ }
+ fn parse_policy_information(i: &[u8]) -> IResult<&[u8], PolicyInformation, BerError> {
+ parse_der_sequence_defined_g(|content, _| {
+ let (rem, policy_id) = Oid::from_der(content)?;
+ let (rem, policy_qualifiers) =
+ opt(complete(parse_der_sequence_defined_g(|content, _| {
+ many1(complete(parse_policy_qualifier_info))(content)
+ })))(rem)?;
+ let info = PolicyInformation {
+ policy_id,
+ policy_qualifiers,
+ };
+ Ok((rem, info))
+ })(i)
+ }
+ parse_der_sequence_of_v(parse_policy_information)(i)
+ }
+
+ fn parse_certificatepolicies_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
+ map(
+ parse_certificatepolicies,
+ ParsedExtension::CertificatePolicies,
+ )(i)
+ }
+
+ // CRLReason ::= ENUMERATED { ...
+ fn parse_reason_code(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
+ let (rest, obj) = parse_der_enum(i)?;
+ let code = obj
+ .content
+ .as_u32()
+ .or(Err(Err::Error(BerError::BerValueError)))?;
+ if code > 10 {
+ return Err(Err::Error(BerError::BerValueError));
+ }
+ let ret = ParsedExtension::ReasonCode(ReasonCode(code as u8));
+ Ok((rest, ret))
+ }
+
+ // invalidityDate ::= GeneralizedTime
+ fn parse_invalidity_date(i: &[u8]) -> ParseResult<ParsedExtension> {
+ let (rest, t) = GeneralizedTime::from_der(i)?;
+ let dt = t.utc_datetime()?;
+ Ok((rest, ParsedExtension::InvalidityDate(ASN1Time::new(dt))))
+ }
+
+ // CRLNumber ::= INTEGER (0..MAX)
+ // Note from RFC 3280: "CRL verifiers MUST be able to handle CRLNumber values up to 20 octets."
+ fn parse_crl_number(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
+ let (rest, num) = map_res(parse_der_integer, |obj| obj.as_biguint())(i)?;
+ Ok((rest, ParsedExtension::CRLNumber(num)))
+ }
+
+ fn parse_sct_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
+ map(
+ parse_ct_signed_certificate_timestamp_list,
+ ParsedExtension::SCT,
+ )(i)
+ }
+}
+
+/// Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
+pub(crate) fn parse_extension_sequence(i: &[u8]) -> X509Result<Vec<X509Extension>> {
+ parse_der_sequence_defined_g(|a, _| all_consuming(many0(complete(X509Extension::from_der)))(a))(
+ i,
+ )
+}
+
+pub(crate) fn parse_extensions(i: &[u8], explicit_tag: Tag) -> X509Result<Vec<X509Extension>> {
+ if i.is_empty() {
+ return Ok((i, Vec::new()));
+ }
+
+ match der_read_element_header(i) {
+ Ok((rem, hdr)) => {
+ if hdr.tag() != explicit_tag {
+ return Err(Err::Error(X509Error::InvalidExtensions));
+ }
+ all_consuming(parse_extension_sequence)(rem)
+ }
+ Err(_) => Err(X509Error::InvalidExtensions.into()),
+ }
+}
+
+/// Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
+pub(crate) fn parse_extension_envelope_sequence(i: &[u8]) -> X509Result<Vec<X509Extension>> {
+ let parser = X509ExtensionParser::new().with_deep_parse_extensions(false);
+
+ parse_der_sequence_defined_g(move |a, _| all_consuming(many0(complete(parser)))(a))(i)
+}
+
+pub(crate) fn parse_extensions_envelope(
+ i: &[u8],
+ explicit_tag: Tag,
+) -> X509Result<Vec<X509Extension>> {
+ if i.is_empty() {
+ return Ok((i, Vec::new()));
+ }
+
+ match der_read_element_header(i) {
+ Ok((rem, hdr)) => {
+ if hdr.tag() != explicit_tag {
+ return Err(Err::Error(X509Error::InvalidExtensions));
+ }
+ all_consuming(parse_extension_envelope_sequence)(rem)
+ }
+ Err(_) => Err(X509Error::InvalidExtensions.into()),
+ }
+}
+
+fn der_read_critical(i: &[u8]) -> BerResult<bool> {
+ // Some certificates do not respect the DER BOOLEAN constraint (true must be encoded as 0xff)
+ // so we attempt to parse as BER
+ let (rem, obj) = opt(parse_ber_bool)(i)?;
+ let value = obj
+ .map(|o| o.as_bool().unwrap_or_default()) // unwrap cannot fail, we just read a bool
+ .unwrap_or(false) // default critical value
+ ;
+ Ok((rem, value))
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_keyusage_flags() {
+ let ku = KeyUsage { flags: 98 };
+ assert!(!ku.digital_signature());
+ assert!(ku.non_repudiation());
+ assert!(!ku.key_encipherment());
+ assert!(!ku.data_encipherment());
+ assert!(!ku.key_agreement());
+ assert!(ku.key_cert_sign());
+ assert!(ku.crl_sign());
+ assert!(!ku.encipher_only());
+ assert!(!ku.decipher_only());
+ }
+
+ #[test]
+ fn test_extensions1() {
+ use der_parser::oid;
+ let crt = crate::parse_x509_certificate(include_bytes!("../../assets/extension1.der"))
+ .unwrap()
+ .1;
+ let tbs = &crt.tbs_certificate;
+ let bc = crt
+ .basic_constraints()
+ .expect("could not get basic constraints")
+ .expect("no basic constraints found");
+ assert_eq!(
+ bc.value,
+ &BasicConstraints {
+ ca: true,
+ path_len_constraint: Some(1)
+ }
+ );
+ {
+ let ku = tbs
+ .key_usage()
+ .expect("could not get key usage")
+ .expect("no key usage found")
+ .value;
+ assert!(ku.digital_signature());
+ assert!(!ku.non_repudiation());
+ assert!(ku.key_encipherment());
+ assert!(ku.data_encipherment());
+ assert!(ku.key_agreement());
+ assert!(!ku.key_cert_sign());
+ assert!(!ku.crl_sign());
+ assert!(ku.encipher_only());
+ assert!(ku.decipher_only());
+ }
+ {
+ let eku = tbs
+ .extended_key_usage()
+ .expect("could not get extended key usage")
+ .expect("no extended key usage found")
+ .value;
+ assert!(!eku.any);
+ assert!(eku.server_auth);
+ assert!(!eku.client_auth);
+ assert!(eku.code_signing);
+ assert!(!eku.email_protection);
+ assert!(eku.time_stamping);
+ assert!(!eku.ocsp_signing);
+ assert_eq!(eku.other, vec![oid!(1.2.3 .4 .0 .42)]);
+ }
+ assert_eq!(
+ tbs.policy_constraints()
+ .expect("could not get policy constraints")
+ .expect("no policy constraints found")
+ .value,
+ &PolicyConstraints {
+ require_explicit_policy: None,
+ inhibit_policy_mapping: Some(10)
+ }
+ );
+ let val = tbs
+ .inhibit_anypolicy()
+ .expect("could not get inhibit_anypolicy")
+ .expect("no inhibit_anypolicy found")
+ .value;
+ assert_eq!(val, &InhibitAnyPolicy { skip_certs: 2 });
+ {
+ let alt_names = &tbs
+ .subject_alternative_name()
+ .expect("could not get subject alt names")
+ .expect("no subject alt names found")
+ .value
+ .general_names;
+ assert_eq!(alt_names[0], GeneralName::RFC822Name("foo@example.com"));
+ assert_eq!(alt_names[1], GeneralName::URI("http://my.url.here/"));
+ assert_eq!(
+ alt_names[2],
+ GeneralName::IPAddress([192, 168, 7, 1].as_ref())
+ );
+ assert_eq!(
+ format!(
+ "{}",
+ match alt_names[3] {
+ GeneralName::DirectoryName(ref dn) => dn,
+ _ => unreachable!(),
+ }
+ ),
+ "C=UK, O=My Organization, OU=My Unit, CN=My Name"
+ );
+ assert_eq!(alt_names[4], GeneralName::DNSName("localhost"));
+ assert_eq!(alt_names[5], GeneralName::RegisteredID(oid!(1.2.90 .0)));
+ assert_eq!(
+ alt_names[6],
+ GeneralName::OtherName(oid!(1.2.3 .4), b"\xA0\x17\x0C\x15some other identifier")
+ );
+ }
+
+ {
+ let name_constraints = &tbs
+ .name_constraints()
+ .expect("could not get name constraints")
+ .expect("no name constraints found")
+ .value;
+ assert_eq!(name_constraints.permitted_subtrees, None);
+ assert_eq!(
+ name_constraints.excluded_subtrees,
+ Some(vec![
+ GeneralSubtree {
+ base: GeneralName::IPAddress([192, 168, 0, 0, 255, 255, 0, 0].as_ref())
+ },
+ GeneralSubtree {
+ base: GeneralName::RFC822Name("foo.com")
+ },
+ ])
+ );
+ }
+ }
+
+ #[test]
+ fn test_extensions2() {
+ use der_parser::oid;
+ let crt = crate::parse_x509_certificate(include_bytes!("../../assets/extension2.der"))
+ .unwrap()
+ .1;
+ let tbs = crt.tbs_certificate;
+ assert_eq!(
+ tbs.policy_constraints()
+ .expect("could not get policy constraints")
+ .expect("no policy constraints found")
+ .value,
+ &PolicyConstraints {
+ require_explicit_policy: Some(5000),
+ inhibit_policy_mapping: None
+ }
+ );
+ {
+ let pm = tbs
+ .policy_mappings()
+ .expect("could not get policy_mappings")
+ .expect("no policy_mappings found")
+ .value
+ .clone()
+ .into_hashmap();
+ let mut pm_ref = HashMap::new();
+ pm_ref.insert(oid!(2.34.23), vec![oid!(2.2)]);
+ pm_ref.insert(oid!(1.1), vec![oid!(0.0.4)]);
+ pm_ref.insert(oid!(2.2), vec![oid!(2.2.1), oid!(2.2.3)]);
+ assert_eq!(pm, pm_ref);
+ }
+ }
+
+ #[test]
+ fn test_extensions_crl_distribution_points() {
+ // Extension not present
+ {
+ let crt = crate::parse_x509_certificate(include_bytes!(
+ "../../assets/crl-ext/crl-no-crl.der"
+ ))
+ .unwrap()
+ .1;
+ assert!(crt
+ .tbs_certificate
+ .extensions_map()
+ .unwrap()
+ .get(&OID_X509_EXT_CRL_DISTRIBUTION_POINTS)
+ .is_none());
+ }
+ // CRLDistributionPoints has 1 entry with 1 URI
+ {
+ let crt = crate::parse_x509_certificate(include_bytes!(
+ "../../assets/crl-ext/crl-simple.der"
+ ))
+ .unwrap()
+ .1;
+ let crl = crt
+ .tbs_certificate
+ .extensions_map()
+ .unwrap()
+ .get(&OID_X509_EXT_CRL_DISTRIBUTION_POINTS)
+ .unwrap()
+ .parsed_extension();
+ assert!(matches!(crl, ParsedExtension::CRLDistributionPoints(_)));
+ if let ParsedExtension::CRLDistributionPoints(crl) = crl {
+ assert_eq!(crl.len(), 1);
+ assert!(crl[0].reasons.is_none());
+ assert!(crl[0].crl_issuer.is_none());
+ let distribution_point = crl[0].distribution_point.as_ref().unwrap();
+ assert!(matches!(
+ distribution_point,
+ DistributionPointName::FullName(_)
+ ));
+ if let DistributionPointName::FullName(names) = distribution_point {
+ assert_eq!(names.len(), 1);
+ assert!(matches!(names[0], GeneralName::URI(_)));
+ if let GeneralName::URI(uri) = names[0] {
+ assert_eq!(uri, "http://example.com/myca.crl")
+ }
+ }
+ }
+ }
+ // CRLDistributionPoints has 2 entries
+ {
+ let crt = crate::parse_x509_certificate(include_bytes!(
+ "../../assets/crl-ext/crl-complex.der"
+ ))
+ .unwrap()
+ .1;
+ let crl = crt
+ .tbs_certificate
+ .extensions_map()
+ .unwrap()
+ .get(&OID_X509_EXT_CRL_DISTRIBUTION_POINTS)
+ .unwrap()
+ .parsed_extension();
+ assert!(matches!(crl, ParsedExtension::CRLDistributionPoints(_)));
+ if let ParsedExtension::CRLDistributionPoints(crl) = crl {
+ assert_eq!(crl.len(), 2);
+ // First CRL Distribution point
+ let reasons = crl[0].reasons.as_ref().unwrap();
+ assert!(reasons.key_compromise());
+ assert!(reasons.ca_compromise());
+ assert!(!reasons.affilation_changed());
+ assert!(!reasons.superseded());
+ assert!(!reasons.cessation_of_operation());
+ assert!(!reasons.certificate_hold());
+ assert!(!reasons.privelege_withdrawn());
+ assert!(reasons.aa_compromise());
+ assert_eq!(
+ format!("{}", reasons),
+ "Key Compromise, CA Compromise, AA Compromise"
+ );
+ let issuers = crl[0].crl_issuer.as_ref().unwrap();
+ assert_eq!(issuers.len(), 1);
+ assert!(matches!(issuers[0], GeneralName::DirectoryName(_)));
+ if let GeneralName::DirectoryName(name) = &issuers[0] {
+ assert_eq!(name.to_string(), "C=US, O=Organisation, CN=Some Name");
+ }
+ let distribution_point = crl[0].distribution_point.as_ref().unwrap();
+ assert!(matches!(
+ distribution_point,
+ DistributionPointName::FullName(_)
+ ));
+ if let DistributionPointName::FullName(names) = distribution_point {
+ assert_eq!(names.len(), 1);
+ assert!(matches!(names[0], GeneralName::URI(_)));
+ if let GeneralName::URI(uri) = names[0] {
+ assert_eq!(uri, "http://example.com/myca.crl")
+ }
+ }
+ // Second CRL Distribution point
+ let reasons = crl[1].reasons.as_ref().unwrap();
+ assert!(reasons.key_compromise());
+ assert!(reasons.ca_compromise());
+ assert!(!reasons.affilation_changed());
+ assert!(!reasons.superseded());
+ assert!(!reasons.cessation_of_operation());
+ assert!(!reasons.certificate_hold());
+ assert!(!reasons.privelege_withdrawn());
+ assert!(!reasons.aa_compromise());
+ assert_eq!(format!("{}", reasons), "Key Compromise, CA Compromise");
+ assert!(crl[1].crl_issuer.is_none());
+ let distribution_point = crl[1].distribution_point.as_ref().unwrap();
+ assert!(matches!(
+ distribution_point,
+ DistributionPointName::FullName(_)
+ ));
+ if let DistributionPointName::FullName(names) = distribution_point {
+ assert_eq!(names.len(), 1);
+ assert!(matches!(names[0], GeneralName::URI(_)));
+ if let GeneralName::URI(uri) = names[0] {
+ assert_eq!(uri, "http://example.com/myca2.crl")
+ }
+ }
+ }
+ }
+ }
+
+ // Test cases for:
+ // - parsing SubjectAlternativeName
+ // - parsing NameConstraints
+}
diff --git a/rust/vendor/x509-parser/src/extensions/nameconstraints.rs b/rust/vendor/x509-parser/src/extensions/nameconstraints.rs
new file mode 100644
index 0000000..da9a999
--- /dev/null
+++ b/rust/vendor/x509-parser/src/extensions/nameconstraints.rs
@@ -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))
+}
diff --git a/rust/vendor/x509-parser/src/extensions/policymappings.rs b/rust/vendor/x509-parser/src/extensions/policymappings.rs
new file mode 100644
index 0000000..2bd367d
--- /dev/null
+++ b/rust/vendor/x509-parser/src/extensions/policymappings.rs
@@ -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 }))
+}
diff --git a/rust/vendor/x509-parser/src/extensions/sct.rs b/rust/vendor/x509-parser/src/extensions/sct.rs
new file mode 100644
index 0000000..646f774
--- /dev/null
+++ b/rust/vendor/x509-parser/src/extensions/sct.rs
@@ -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))
+}
diff --git a/rust/vendor/x509-parser/src/lib.rs b/rust/vendor/x509-parser/src/lib.rs
new file mode 100644
index 0000000..1ddd357
--- /dev/null
+++ b/rust/vendor/x509-parser/src/lib.rs
@@ -0,0 +1,213 @@
+//! [![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.57.0+-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.
+//!
+//! # 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` feature adds support for (cryptographic) signature verification, based on `ring`.
+//! It adds the
+//! [`X509Certificate::verify_signature()`](certificate/struct.X509Certificate.html#method.verify_signature)
+//! to `X509Certificate`.
+//!
+//! ```rust
+//! # #[cfg(feature = "verify")]
+//! # use x509_parser::certificate::X509Certificate;
+//! /// Cryptographic signature verification: returns true if certificate was signed by issuer
+//! #[cfg(feature = "verify")]
+//! pub fn check_signature(cert: &X509Certificate<'_>, issuer: &X509Certificate<'_>) -> bool {
+//! let issuer_public_key = issuer.public_key();
+//! cert
+//! .verify_signature(Some(issuer_public_key))
+//! .is_ok()
+//! }
+//! ```
+//!
+//! - The `validate` features add methods to run more validation functions on the certificate structure
+//! and values using the [`Validate`](validate/trait.Validate.html) trait.
+//! It does not validate any cryptographic parameter (see `verify` above).
+//!
+//! ## Rust version requirements
+//!
+//! `x509-parser` requires **Rustc version 1.57 or greater**, based on der-parser
+//! dependencies and for proc-macro attributes support.
+//!
+//! Note that due to breaking changes in the `time` crate, a specific version of this
+//! crate must be specified for compiler versions <= 1.57:
+//! `cargo update -p time --precise 0.3.9`
+//!
+//! [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(feature = "verify")]
+#[cfg_attr(docsrs, doc(cfg(feature = "verify")))]
+pub mod verify;
+pub mod x509;
+
+// reexports
+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)
+}
diff --git a/rust/vendor/x509-parser/src/objects.rs b/rust/vendor/x509-parser/src/objects.rs
new file mode 100644
index 0000000..9e3dc63
--- /dev/null
+++ b/rust/vendor/x509-parser/src/objects.rs
@@ -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, 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 => (),
+ // _ => (),
+ // }
+ }
+}
diff --git a/rust/vendor/x509-parser/src/pem.rs b/rust/vendor/x509-parser/src/pem.rs
new file mode 100644
index 0000000..f8bfc7a
--- /dev/null
+++ b/rust/vendor/x509-parser/src/pem.rs
@@ -0,0 +1,255 @@
+//! 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, 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 = r.read_line(&mut line)?;
+ 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, ::nom::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");
+ }
+}
diff --git a/rust/vendor/x509-parser/src/prelude.rs b/rust/vendor/x509-parser/src/prelude.rs
new file mode 100644
index 0000000..a1749ce
--- /dev/null
+++ b/rust/vendor/x509-parser/src/prelude.rs
@@ -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;
diff --git a/rust/vendor/x509-parser/src/public_key.rs b/rust/vendor/x509-parser/src/public_key.rs
new file mode 100644
index 0000000..4016a9f
--- /dev/null
+++ b/rust/vendor/x509-parser/src/public_key.rs
@@ -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<'a> PublicKey<'a> {
+ /// 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<'a> RSAPublicKey<'a> {
+ /// 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 }
+ }
+}
diff --git a/rust/vendor/x509-parser/src/revocation_list.rs b/rust/vendor/x509-parser/src/revocation_list.rs
new file mode 100644
index 0000000..55cf8f3
--- /dev/null
+++ b/rust/vendor/x509-parser/src/revocation_list.rs
@@ -0,0 +1,365 @@
+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(feature = "verify")]
+use crate::verify::verify_signature;
+#[cfg(feature = "verify")]
+use crate::x509::SubjectPublicKeyInfo;
+use asn1_rs::{BitString, FromDer};
+use der_parser::ber::Tag;
+use der_parser::der::*;
+use der_parser::num_bigint::BigUint;
+use der_parser::oid::Oid;
+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>,
+}
+
+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(feature = "verify")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "verify")))]
+ 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,
+ )
+ }
+}
+
+/// <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<Self> {
+ parse_der_sequence_defined_g(|i, _| {
+ let (i, tbs_cert_list) = TbsCertList::from_der(i)?;
+ let (i, signature_algorithm) = AlgorithmIdentifier::from_der(i)?;
+ let (i, signature_value) = parse_signature_value(i)?;
+ let crl = CertificateRevocationList {
+ tbs_cert_list,
+ signature_algorithm,
+ signature_value,
+ };
+ Ok((i, crl))
+ })(i)
+ }
+}
+
+/// 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<'a> TbsCertList<'a> {
+ /// Returns the certificate extensions
+ #[inline]
+ pub fn extensions(&self) -> &[X509Extension] {
+ &self.extensions
+ }
+
+ /// Returns an iterator over the certificate extensions
+ #[inline]
+ pub fn iter_extensions(&self) -> impl Iterator<Item = &X509Extension> {
+ self.extensions.iter()
+ }
+
+ /// Searches for an extension with the given `Oid`.
+ ///
+ /// Note: if there are several extensions with the same `Oid`, the first one is returned.
+ pub fn find_extension(&self, oid: &Oid) -> Option<&X509Extension> {
+ self.extensions.iter().find(|&ext| ext.oid == *oid)
+ }
+
+ /// Builds and returns a map of extensions.
+ ///
+ /// If an extension is present twice, this will fail and return `DuplicateExtensions`.
+ pub fn extensions_map(&self) -> Result<HashMap<Oid, &X509Extension>, X509Error> {
+ self.extensions
+ .iter()
+ .try_fold(HashMap::new(), |mut m, ext| {
+ if m.contains_key(&ext.oid) {
+ return Err(X509Error::DuplicateExtensions);
+ }
+ m.insert(ext.oid.clone(), ext);
+ Ok(m)
+ })
+ }
+}
+
+impl<'a> AsRef<[u8]> for TbsCertList<'a> {
+ fn as_ref(&self) -> &[u8] {
+ self.raw
+ }
+}
+
+impl<'a> FromDer<'a, X509Error> for TbsCertList<'a> {
+ fn from_der(i: &'a [u8]) -> X509Result<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<'a> RevokedCertificate<'a> {
+ /// 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<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)
+}
diff --git a/rust/vendor/x509-parser/src/signature_algorithm.rs b/rust/vendor/x509-parser/src/signature_algorithm.rs
new file mode 100644
index 0000000..24a37a4
--- /dev/null
+++ b/rust/vendor/x509-parser/src/signature_algorithm.rs
@@ -0,0 +1,326 @@
+use crate::error::X509Error;
+use crate::x509::AlgorithmIdentifier;
+use asn1_rs::{
+ oid, Any, CheckDerConstraints, Class, DerAutoDerive, Error, FromDer, Oid, 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 {
+ 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.
diff --git a/rust/vendor/x509-parser/src/signature_value.rs b/rust/vendor/x509-parser/src/signature_value.rs
new file mode 100644
index 0000000..df13bd0
--- /dev/null
+++ b/rust/vendor/x509-parser/src/signature_value.rs
@@ -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>,
+}
diff --git a/rust/vendor/x509-parser/src/time.rs b/rust/vendor/x509-parser/src/time.rs
new file mode 100644
index 0000000..7d2b008
--- /dev/null
+++ b/rust/vendor/x509-parser/src/time.rs
@@ -0,0 +1,179 @@
+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(OffsetDateTime);
+
+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, dt)) => Ok((rem, Some(ASN1Time(dt)))),
+ 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 {
+ Self(dt)
+ }
+
+ #[inline]
+ pub const fn to_datetime(&self) -> OffsetDateTime {
+ self.0
+ }
+
+ /// 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(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.0.unix_timestamp()
+ }
+
+ /// Returns a `ASN1Time` which corresponds to the current date.
+ #[inline]
+ pub fn now() -> Self {
+ ASN1Time(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.0
+ .format(&time::format_description::well_known::Rfc2822)
+ .map_err(|e| e.to_string())
+ }
+}
+
+impl<'a> FromDer<'a, X509Error> for ASN1Time {
+ fn from_der(i: &[u8]) -> X509Result<Self> {
+ let (rem, dt) = parse_choice_of_time(i).map_err(|_| X509Error::InvalidDate)?;
+ Ok((rem, ASN1Time(dt)))
+ }
+}
+
+pub(crate) fn parse_choice_of_time(i: &[u8]) -> ParseResult<OffsetDateTime> {
+ if let Ok((rem, t)) = UtcTime::from_der(i) {
+ let dt = t.utc_adjusted_datetime()?;
+ return Ok((rem, dt));
+ }
+ if let Ok((rem, t)) = GeneralizedTime::from_der(i) {
+ let dt = t.utc_datetime()?;
+ return Ok((rem, dt));
+ }
+ parse_malformed_date(i)
+}
+
+// allow relaxed parsing of UTCTime (ex: 370116130016+0000)
+fn parse_malformed_date(i: &[u8]) -> ParseResult<OffsetDateTime> {
+ #[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(nom::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(nom::Err::Error(Error::BerValueError))
+ }
+ _ => Err(nom::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
+ .0
+ .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(self.0 + rhs))
+ }
+}
+
+impl Sub<ASN1Time> for ASN1Time {
+ type Output = Option<Duration>;
+
+ #[inline]
+ fn sub(self, rhs: ASN1Time) -> Option<Duration> {
+ if self.0 > rhs.0 {
+ Some(self.0 - rhs.0)
+ } else {
+ None
+ }
+ }
+}
+
+impl From<OffsetDateTime> for ASN1Time {
+ fn from(dt: OffsetDateTime) -> Self {
+ ASN1Time(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());
+ }
+}
diff --git a/rust/vendor/x509-parser/src/utils.rs b/rust/vendor/x509-parser/src/utils.rs
new file mode 100644
index 0000000..72b4d99
--- /dev/null
+++ b/rust/vendor/x509-parser/src/utils.rs
@@ -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!("{:02x}:", b)
+ });
+ 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));
+ }
+}
diff --git a/rust/vendor/x509-parser/src/validate/certificate.rs b/rust/vendor/x509-parser/src/validate/certificate.rs
new file mode 100644
index 0000000..d375a80
--- /dev/null
+++ b/rust/vendor/x509-parser/src/validate/certificate.rs
@@ -0,0 +1,17 @@
+use crate::certificate::*;
+use crate::validate::*;
+
+use extensions::X509ExtensionsValidator;
+
+#[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
+ }
+}
diff --git a/rust/vendor/x509-parser/src/validate/extensions.rs b/rust/vendor/x509-parser/src/validate/extensions.rs
new file mode 100644
index 0000000..a838f6e
--- /dev/null
+++ b/rust/vendor/x509-parser/src/validate/extensions.rs
@@ -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
+ }
+}
diff --git a/rust/vendor/x509-parser/src/validate/loggers.rs b/rust/vendor/x509-parser/src/validate/loggers.rs
new file mode 100644
index 0000000..0052f8d
--- /dev/null
+++ b/rust/vendor/x509-parser/src/validate/loggers.rs
@@ -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);
+ }
+}
diff --git a/rust/vendor/x509-parser/src/validate/mod.rs b/rust/vendor/x509-parser/src/validate/mod.rs
new file mode 100644
index 0000000..a2cba26
--- /dev/null
+++ b/rust/vendor/x509-parser/src/validate/mod.rs
@@ -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());
+ }
+}
diff --git a/rust/vendor/x509-parser/src/validate/name.rs b/rust/vendor/x509-parser/src/validate/name.rs
new file mode 100644
index 0000000..569f157
--- /dev/null
+++ b/rust/vendor/x509-parser/src/validate/name.rs
@@ -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
+ }
+}
diff --git a/rust/vendor/x509-parser/src/validate/structure.rs b/rust/vendor/x509-parser/src/validate/structure.rs
new file mode 100644
index 0000000..191a3d7
--- /dev/null
+++ b/rust/vendor/x509-parser/src/validate/structure.rs
@@ -0,0 +1,160 @@
+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;
+ }
+ // extensions require v3
+ if !item.extensions().is_empty() && item.version != X509Version::V3 {
+ l.err("Extensions present but version is not 3");
+ 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);
+ // 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
+ }
+}
diff --git a/rust/vendor/x509-parser/src/verify.rs b/rust/vendor/x509-parser/src/verify.rs
new file mode 100644
index 0000000..c056d77
--- /dev/null
+++ b/rust/vendor/x509-parser/src/verify.rs
@@ -0,0 +1,78 @@
+use crate::prelude::*;
+use asn1_rs::BitString;
+use oid_registry::{
+ OID_EC_P256, OID_NIST_EC_P384, 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,
+};
+
+/// 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 `ring` supports.
+pub fn verify_signature(
+ public_key: &SubjectPublicKeyInfo,
+ signature_algorithm: &AlgorithmIdentifier,
+ signature_value: &BitString,
+ raw_data: &[u8],
+) -> Result<(), X509Error> {
+ use ring::signature;
+ let signature_algorithm = &signature_algorithm.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_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 `ring` supports.
+fn get_ec_curve_sha(
+ pubkey_alg: &AlgorithmIdentifier,
+ sha_len: usize,
+) -> Option<&'static dyn ring::signature::VerificationAlgorithm> {
+ use ring::signature;
+ 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
+ }
+}
diff --git a/rust/vendor/x509-parser/src/x509.rs b/rust/vendor/x509-parser/src/x509.rs
new file mode 100644
index 0000000..5052ebd
--- /dev/null
+++ b/rust/vendor/x509-parser/src/x509.rs
@@ -0,0 +1,667 @@
+//! 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, Oid, 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`](core::convert::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(nom::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(nom::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<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<'a> SubjectPublicKeyInfo<'a> {
+ /// 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<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 {
+ &self.algorithm
+ }
+
+ /// Get a reference to the algorithm parameters, if present
+ pub const fn parameters(&'a self) -> Option<&'a Any> {
+ 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<'a> fmt::Display for X509Name<'a> {
+ 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<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().fold(Ok(String::new()), |acc, rdn| {
+ acc.and_then(|mut _vec| {
+ rdn.set
+ .iter()
+ .fold(Ok(String::new()), |acc2, attr| {
+ acc2.and_then(|mut _vec2| {
+ let val_str = attribute_value_to_string(&attr.attr_value, &attr.attr_type)?;
+ // look ABBREV, and if not found, use shortname
+ let abbrev = match oid2abbrev(&attr.attr_type, oid_registry) {
+ Ok(s) => String::from(s),
+ _ => format!("{:?}", attr.attr_type),
+ };
+ let rdn = format!("{}={}", abbrev, val_str);
+ match _vec2.len() {
+ 0 => Ok(rdn),
+ _ => Ok(_vec2 + " + " + &rdn),
+ }
+ })
+ })
+ .map(|v| match _vec.len() {
+ 0 => v,
+ _ => _vec + ", " + &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"
+ );
+ }
+}
diff --git a/rust/vendor/x509-parser/tests/pem.rs b/rust/vendor/x509-parser/tests/pem.rs
new file mode 100644
index 0000000..eb97ff2
--- /dev/null
+++ b/rust/vendor/x509-parser/tests/pem.rs
@@ -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());
+}
diff --git a/rust/vendor/x509-parser/tests/readcert.rs b/rust/vendor/x509-parser/tests/readcert.rs
new file mode 100644
index 0000000..3fa3cd4
--- /dev/null
+++ b/rust/vendor/x509-parser/tests/readcert.rs
@@ -0,0 +1,360 @@
+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");
+
+#[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)]);
+ }
+ _ => 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);
+ }
+}
diff --git a/rust/vendor/x509-parser/tests/readcrl.rs b/rust/vendor/x509-parser/tests/readcrl.rs
new file mode 100644
index 0000000..45b63bf
--- /dev/null
+++ b/rust/vendor/x509-parser/tests/readcrl.rs
@@ -0,0 +1,18 @@
+// Currently, this file is only used to test 'verify' features, so we guard it to this feature
+// To be removed if other test functions with different features are added
+#![cfg(feature = "verify")]
+
+use x509_parser::prelude::*;
+
+const CA_DATA: &[u8] = include_bytes!("../assets/ca_minimalcrl.der");
+const CRL_DATA: &[u8] = include_bytes!("../assets/minimal.crl");
+
+#[cfg(feature = "verify")]
+#[test]
+fn read_crl_verify() {
+ 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());
+}
diff --git a/rust/vendor/x509-parser/tests/readcsr.rs b/rust/vendor/x509-parser/tests/readcsr.rs
new file mode 100644
index 0000000..1124c48
--- /dev/null
+++ b/rust/vendor/x509-parser/tests/readcsr.rs
@@ -0,0 +1,125 @@
+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(feature = "verify")]
+#[test]
+fn read_csr_verify() {
+ let der = pem::parse_x509_pem(CSR_DATA).unwrap().1;
+ let (_, csr) = X509CertificationRequest::from_der(&der.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();
+}
diff --git a/rust/vendor/x509-parser/tests/run_all_fuzz_files.rs b/rust/vendor/x509-parser/tests/run_all_fuzz_files.rs
new file mode 100644
index 0000000..96041b4
--- /dev/null
+++ b/rust/vendor/x509-parser/tests/run_all_fuzz_files.rs
@@ -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);
+}
diff --git a/rust/vendor/x509-parser/tests/test01.rs b/rust/vendor/x509-parser/tests/test01.rs
new file mode 100644
index 0000000..45c0dca
--- /dev/null
+++ b/rust/vendor/x509-parser/tests/test01.rs
@@ -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);
+}
diff --git a/rust/vendor/x509-parser/tests/verify.rs b/rust/vendor/x509-parser/tests/verify.rs
new file mode 100644
index 0000000..6cb3835
--- /dev/null
+++ b/rust/vendor/x509-parser/tests/verify.rs
@@ -0,0 +1,35 @@
+#![cfg(feature = "verify")]
+
+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());
+}