pax_global_header00006660000000000000000000000064147456747220014534gustar00rootroot0000000000000052 comment=be3a4c42e421da2745eb5589e5ce96e0eb467642 pki-types-v-1.11.0/000077500000000000000000000000001474567472200140045ustar00rootroot00000000000000pki-types-v-1.11.0/.github/000077500000000000000000000000001474567472200153445ustar00rootroot00000000000000pki-types-v-1.11.0/.github/workflows/000077500000000000000000000000001474567472200174015ustar00rootroot00000000000000pki-types-v-1.11.0/.github/workflows/ci.yml000066400000000000000000000120151474567472200205160ustar00rootroot00000000000000name: rustls permissions: contents: read on: push: pull_request: merge_group: schedule: - cron: '0 21 * * *' jobs: build: name: Build + test runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: # test a bunch of toolchains on ubuntu rust: - stable - beta - nightly os: [ubuntu-latest] # but only stable on macos/windows (slower platforms) include: - os: macos-latest rust: stable - os: windows-latest rust: stable steps: - name: Checkout sources uses: actions/checkout@v4 with: persist-credentials: false - name: Install ${{ matrix.rust }} toolchain uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - name: Install valgrind if: runner.os == 'Linux' run: sudo apt-get update && sudo apt-get install -y valgrind - name: cargo test (debug; default features) run: cargo test env: RUST_BACKTRACE: 1 - name: cargo test (debug; all features) run: cargo test --all-features env: RUST_BACKTRACE: 1 - name: cargo test (debug; no default features; no run) run: cargo test --no-default-features env: RUST_BACKTRACE: 1 wasm_build: name: Build wasm32 runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v4 with: persist-credentials: false - name: Install stable toolchain uses: dtolnay/rust-toolchain@stable - name: Add wasm target run: rustup target add wasm32-unknown-unknown - name: wasm32 build (debug; default features) run: cargo build --target wasm32-unknown-unknown --lib env: RUST_BACKTRACE: 1 - name: wasm32 build (debug; all features) run: cargo build --target wasm32-unknown-unknown --lib --all-features env: RUST_BACKTRACE: 1 - name: wasm32 build (debug; no default features) run: cargo build --target wasm32-unknown-unknown --lib --no-default-features env: RUST_BACKTRACE: 1 msrv: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] steps: - name: Checkout sources uses: actions/checkout@v4 with: persist-credentials: false - name: Install MSRV toolchain uses: dtolnay/rust-toolchain@master with: toolchain: "1.60" - run: cargo check --lib --all-features format: name: Format runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v4 with: persist-credentials: false - name: Install rust toolchain uses: dtolnay/rust-toolchain@stable with: components: rustfmt - name: Check formatting run: cargo fmt --all -- --check clippy: name: Clippy runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v4 with: persist-credentials: false - name: Install rust toolchain uses: dtolnay/rust-toolchain@stable with: components: clippy - run: cargo clippy --all-features -- --deny warnings semver: name: Check semver compatibility runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v4 with: persist-credentials: false - name: Check semver uses: obi1kenobi/cargo-semver-checks-action@v2 fuzz: name: Smoke-test fuzzing targets runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v4 with: persist-credentials: false - name: Install nightly toolchain uses: dtolnay/rust-toolchain@nightly - name: Install cargo fuzz run: cargo install cargo-fuzz - name: Smoke-test fuzz targets run: | cargo fuzz build for target in $(cargo fuzz list) ; do cargo fuzz run $target -- -max_total_time=10 done valgrind: name: Check side-channels on base64 decoder runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v4 with: persist-credentials: false - name: Install stable toolchain uses: dtolnay/rust-toolchain@stable - name: Install valgrind if: runner.os == 'Linux' run: sudo apt-get update && sudo apt-get install -y valgrind - name: Build and run test run: | cargo test --all-features --lib exe=$(cargo test --all-features --no-run --message-format json | \ jq --slurp --raw-output '.[] | select(.reason == "compiler-artifact") | select(.target.name == "rustls_pki_types") | select(.profile.test) | .executable') valgrind --error-exitcode=99 --exit-on-first-error=yes $exe pki-types-v-1.11.0/.gitignore000066400000000000000000000000331474567472200157700ustar00rootroot00000000000000/target /Cargo.lock /.idea pki-types-v-1.11.0/Cargo.toml000066400000000000000000000015251474567472200157370ustar00rootroot00000000000000[package] name = "rustls-pki-types" version = "1.11.0" edition = "2021" rust-version = "1.60" license = "MIT OR Apache-2.0" description = "Shared types for the rustls PKI ecosystem" documentation = "https://docs.rs/rustls-pki-types" homepage = "https://github.com/rustls/pki-types" repository = "https://github.com/rustls/pki-types" categories = ["network-programming", "data-structures", "cryptography"] [features] default = ["alloc"] alloc = [] std = ["alloc"] web = ["web-time"] [target.'cfg(all(target_os = "linux", target_arch = "x86_64"))'.dev-dependencies] crabgrind = "=0.1.9" # compatible with valgrind package on GHA ubuntu-latest [target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies] web-time = { version = "1", optional = true } [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] pki-types-v-1.11.0/LICENSE-APACHE000066400000000000000000000251231474567472200157330ustar00rootroot00000000000000 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 2023 Dirkjan Ochtman 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. pki-types-v-1.11.0/LICENSE-MIT000066400000000000000000000020701474567472200154370ustar00rootroot00000000000000Copyright (c) 2023 Dirkjan Ochtman 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. pki-types-v-1.11.0/README.md000066400000000000000000000054051474567472200152670ustar00rootroot00000000000000# rustls-pki-types [![Build Status](https://github.com/rustls/pki-types/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/rustls/pki-types/actions/workflows/ci.yml?query=branch%3Amain) [![Documentation](https://docs.rs/rustls-pki-types/badge.svg)](https://docs.rs/rustls-pki-types/) [![Chat](https://img.shields.io/discord/976380008299917365?logo=discord)](https://discord.gg/MCSB76RU96) This crate provides types for representing X.509 certificates, keys and other types as commonly used in the rustls ecosystem. It is intended to be used by crates that need to work with such X.509 types, such as [rustls](https://crates.io/crates/rustls), [rustls-webpki](https://crates.io/crates/rustls-webpki), and others. Some of these crates used to define their own trivial wrappers around DER-encoded bytes. However, in order to avoid inconvenient dependency edges, these were all disconnected. By using a common low-level crate of types with long-term stable API, we hope to avoid the downsides of unnecessary dependency edges while providing interoperability between crates. ## Features - Interoperability between different crates in the rustls ecosystem - Long-term stable API - No dependencies - Support for `no_std` contexts, with optional support for `alloc` ## DER and PEM Many of the types defined in this crate represent DER-encoded data. DER is a binary encoding of the ASN.1 format commonly used in web PKI specifications. It is a binary encoding, so it is relatively compact when stored in memory. However, as a binary format, it is not very easy to work with for humans and in contexts where binary data is inconvenient. For this reason, many tools and protocols use a ASCII-based encoding of DER, called PEM. In addition to the base64-encoded DER, PEM objects are delimited by header and footer lines which indicate the type of object contained in the PEM blob. This crate's types can be created from both DER and PEM encodings. ## Creating new certificates and keys This crate does not provide any functionality for creating new certificates or keys. However, the [rcgen](https://docs.rs/rcgen) crate can be used to create new certificates and keys. ## Cloning private keys This crate intentionally **does not** implement `Clone` on private key types in order to minimize the exposure of private key data in memory. If you want to extend the lifetime of a `PrivateKeyDer<'_>`, consider [`PrivateKeyDer::clone_key()`]. Alternatively since these types are immutable, consider wrapping the `PrivateKeyDer<'_>` in a [`Rc`] or an [`Arc`]. [`Rc`]: https://doc.rust-lang.org/std/rc/struct.Rc.html [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html [`PrivateKeyDer::clone_key()`]: https://docs.rs/rustls-pki-types/latest/rustls_pki_types/enum.PrivateKeyDer.html#method.clone_key pki-types-v-1.11.0/fuzz/000077500000000000000000000000001474567472200150025ustar00rootroot00000000000000pki-types-v-1.11.0/fuzz/.gitignore000066400000000000000000000000411474567472200167650ustar00rootroot00000000000000target corpus artifacts coverage pki-types-v-1.11.0/fuzz/Cargo.lock000066400000000000000000000030271474567472200167110ustar00rootroot00000000000000# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "arbitrary" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" [[package]] name = "cc" version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" dependencies = [ "jobserver", "libc", ] [[package]] name = "jobserver" version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" dependencies = [ "libc", ] [[package]] name = "libc" version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libfuzzer-sys" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" dependencies = [ "arbitrary", "cc", "once_cell", ] [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "rustls-pki-types" version = "1.8.0" [[package]] name = "rustls-pki-types-fuzz" version = "0.0.0" dependencies = [ "libfuzzer-sys", "rustls-pki-types", ] pki-types-v-1.11.0/fuzz/Cargo.toml000066400000000000000000000007721474567472200167400ustar00rootroot00000000000000[package] name = "rustls-pki-types-fuzz" version = "0.0.0" publish = false edition = "2021" [package.metadata] cargo-fuzz = true [dependencies] libfuzzer-sys = "0.4" [dependencies.rustls-pki-types] path = ".." features = ["std"] # Prevent this from interfering with workspaces [workspace] members = ["."] [profile.release] debug = 1 [[bin]] name = "private_key" path = "fuzz_targets/private_key.rs" test = false doc = false [[bin]] name = "pem" path = "fuzz_targets/pem.rs" test = false doc = false pki-types-v-1.11.0/fuzz/corpus/000077500000000000000000000000001474567472200163155ustar00rootroot00000000000000pki-types-v-1.11.0/fuzz/corpus/pem/000077500000000000000000000000001474567472200170765ustar00rootroot00000000000000pki-types-v-1.11.0/fuzz/corpus/pem/zen.pem000077700000000000000000000000001474567472200246032../../../tests/data/zen.pemustar00rootroot00000000000000pki-types-v-1.11.0/fuzz/fuzz_targets/000077500000000000000000000000001474567472200175315ustar00rootroot00000000000000pki-types-v-1.11.0/fuzz/fuzz_targets/pem.rs000066400000000000000000000011251474567472200206570ustar00rootroot00000000000000#![no_main] use std::io::Cursor; use libfuzzer_sys::fuzz_target; use rustls_pki_types::pem::PemObject; use rustls_pki_types::{CertificateDer, PrivateKeyDer}; fuzz_target!(|data: &[u8]| { // cover the code paths that use std::io for x in CertificateDer::pem_reader_iter(&mut Cursor::new(data)) { match x { Ok(_item) => (), Err(_err) => break, } } // cover the code paths that use slices for x in PrivateKeyDer::pem_slice_iter(data) { match x { Ok(_item) => (), Err(_err) => break, } } }); pki-types-v-1.11.0/fuzz/fuzz_targets/private_key.rs000066400000000000000000000002141474567472200224160ustar00rootroot00000000000000#![no_main] use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { let _ = rustls_pki_types::PrivateKeyDer::try_from(data); }); pki-types-v-1.11.0/src/000077500000000000000000000000001474567472200145735ustar00rootroot00000000000000pki-types-v-1.11.0/src/alg_id.rs000066400000000000000000000210061474567472200163570ustar00rootroot00000000000000//! The PKIX [`AlgorithmIdentifier`] type, and common values. //! //! If you need to use an [`AlgorithmIdentifier`] not defined here, //! you can define it locally. use core::fmt; use core::ops::Deref; // See src/data/README.md. /// AlgorithmIdentifier for `id-ecPublicKey` with named curve `secp256r1`. /// /// This is: /// /// ```text /// # ecPublicKey /// OBJECT_IDENTIFIER { 1.2.840.10045.2.1 } /// # secp256r1 /// OBJECT_IDENTIFIER { 1.2.840.10045.3.1.7 } /// ``` pub const ECDSA_P256: AlgorithmIdentifier = AlgorithmIdentifier::from_slice(include_bytes!("data/alg-ecdsa-p256.der")); /// AlgorithmIdentifier for `id-ecPublicKey` with named curve `secp384r1`. /// /// This is: /// /// ```text /// # ecPublicKey /// OBJECT_IDENTIFIER { 1.2.840.10045.2.1 } /// # secp384r1 /// OBJECT_IDENTIFIER { 1.3.132.0.34 } /// ``` pub const ECDSA_P384: AlgorithmIdentifier = AlgorithmIdentifier::from_slice(include_bytes!("data/alg-ecdsa-p384.der")); /// AlgorithmIdentifier for `id-ecPublicKey` with named curve `secp521r1`. /// /// This is: /// /// ```text /// # ecPublicKey /// OBJECT_IDENTIFIER { 1.2.840.10045.2.1 } /// # secp521r1 /// OBJECT_IDENTIFIER { 1.3.132.0.35 } /// ``` pub const ECDSA_P521: AlgorithmIdentifier = AlgorithmIdentifier::from_slice(include_bytes!("data/alg-ecdsa-p521.der")); /// AlgorithmIdentifier for `ecdsa-with-SHA256`. /// /// This is: /// /// ```text /// # ecdsa-with-SHA256 /// OBJECT_IDENTIFIER { 1.2.840.10045.4.3.2 } /// ``` pub const ECDSA_SHA256: AlgorithmIdentifier = AlgorithmIdentifier::from_slice(include_bytes!("data/alg-ecdsa-sha256.der")); /// AlgorithmIdentifier for `ecdsa-with-SHA384`. /// /// This is: /// /// ```text /// # ecdsa-with-SHA384 /// OBJECT_IDENTIFIER { 1.2.840.10045.4.3.3 } /// ``` pub const ECDSA_SHA384: AlgorithmIdentifier = AlgorithmIdentifier::from_slice(include_bytes!("data/alg-ecdsa-sha384.der")); /// AlgorithmIdentifier for `ecdsa-with-SHA512`. /// /// This is: /// /// ```text /// # ecdsa-with-SHA512 /// OBJECT_IDENTIFIER { 1.2.840.10045.4.3.4 } /// ``` pub const ECDSA_SHA512: AlgorithmIdentifier = AlgorithmIdentifier::from_slice(include_bytes!("data/alg-ecdsa-sha512.der")); /// AlgorithmIdentifier for `rsaEncryption`. /// /// This is: /// /// ```text /// # rsaEncryption /// OBJECT_IDENTIFIER { 1.2.840.113549.1.1.1 } /// NULL {} /// ``` pub const RSA_ENCRYPTION: AlgorithmIdentifier = AlgorithmIdentifier::from_slice(include_bytes!("data/alg-rsa-encryption.der")); /// AlgorithmIdentifier for `sha256WithRSAEncryption`. /// /// This is: /// /// ```text /// # sha256WithRSAEncryption /// OBJECT_IDENTIFIER { 1.2.840.113549.1.1.11 } /// NULL {} /// ``` pub const RSA_PKCS1_SHA256: AlgorithmIdentifier = AlgorithmIdentifier::from_slice(include_bytes!("data/alg-rsa-pkcs1-sha256.der")); /// AlgorithmIdentifier for `sha384WithRSAEncryption`. /// /// This is: /// /// ```text /// # sha384WithRSAEncryption /// OBJECT_IDENTIFIER { 1.2.840.113549.1.1.12 } /// NULL {} /// ``` pub const RSA_PKCS1_SHA384: AlgorithmIdentifier = AlgorithmIdentifier::from_slice(include_bytes!("data/alg-rsa-pkcs1-sha384.der")); /// AlgorithmIdentifier for `sha512WithRSAEncryption`. /// /// This is: /// /// ```text /// # sha512WithRSAEncryption /// OBJECT_IDENTIFIER { 1.2.840.113549.1.1.13 } /// NULL {} /// ``` pub const RSA_PKCS1_SHA512: AlgorithmIdentifier = AlgorithmIdentifier::from_slice(include_bytes!("data/alg-rsa-pkcs1-sha512.der")); /// AlgorithmIdentifier for `rsassaPss` with: /// /// - hashAlgorithm: sha256 /// - maskGenAlgorithm: mgf1 with sha256 /// - saltLength: 32 /// /// This is: /// /// ```text /// # rsassa-pss /// OBJECT_IDENTIFIER { 1.2.840.113549.1.1.10 } /// SEQUENCE { /// # hashAlgorithm: /// [0] { /// SEQUENCE { /// # sha256 /// OBJECT_IDENTIFIER { 2.16.840.1.101.3.4.2.1 } /// NULL {} /// } /// } /// # maskGenAlgorithm: /// [1] { /// SEQUENCE { /// # mgf1 /// OBJECT_IDENTIFIER { 1.2.840.113549.1.1.8 } /// SEQUENCE { /// # sha256 /// OBJECT_IDENTIFIER { 2.16.840.1.101.3.4.2.1 } /// NULL {} /// } /// } /// } /// # saltLength: /// [2] { /// INTEGER { 32 } /// } /// } /// ``` /// /// See for /// the meaning of the context-specific tags. pub const RSA_PSS_SHA256: AlgorithmIdentifier = AlgorithmIdentifier::from_slice(include_bytes!("data/alg-rsa-pss-sha256.der")); /// AlgorithmIdentifier for `rsassaPss` with: /// /// - hashAlgorithm: sha384 /// - maskGenAlgorithm: mgf1 with sha384 /// - saltLength: 48 /// /// This is: /// /// ```text /// # rsassa-pss /// OBJECT_IDENTIFIER { 1.2.840.113549.1.1.10 } /// SEQUENCE { /// # hashAlgorithm: /// [0] { /// SEQUENCE { /// # sha384 /// OBJECT_IDENTIFIER { 2.16.840.1.101.3.4.2.2 } /// NULL {} /// } /// } /// # maskGenAlgorithm: /// [1] { /// SEQUENCE { /// # mgf1 /// OBJECT_IDENTIFIER { 1.2.840.113549.1.1.8 } /// SEQUENCE { /// # sha384 /// OBJECT_IDENTIFIER { 2.16.840.1.101.3.4.2.2 } /// NULL {} /// } /// } /// } /// # saltLength: /// [2] { /// INTEGER { 48 } /// } /// } /// ``` /// /// See for /// the meaning of the context-specific tags. pub const RSA_PSS_SHA384: AlgorithmIdentifier = AlgorithmIdentifier::from_slice(include_bytes!("data/alg-rsa-pss-sha384.der")); /// AlgorithmIdentifier for `rsassaPss` with: /// /// - hashAlgorithm: sha512 /// - maskGenAlgorithm: mgf1 with sha512 /// - saltLength: 64 /// /// This is: /// /// ```text /// # rsassa-pss /// OBJECT_IDENTIFIER { 1.2.840.113549.1.1.10 } /// SEQUENCE { /// # hashAlgorithm: /// [0] { /// SEQUENCE { /// # sha512 /// OBJECT_IDENTIFIER { 2.16.840.1.101.3.4.2.3 } /// NULL {} /// } /// } /// # maskGenAlgorithm: /// [1] { /// SEQUENCE { /// # mgf1 /// OBJECT_IDENTIFIER { 1.2.840.113549.1.1.8 } /// SEQUENCE { /// # sha512 /// OBJECT_IDENTIFIER { 2.16.840.1.101.3.4.2.3 } /// NULL {} /// } /// } /// } /// # saltLength: /// [2] { /// INTEGER { 64 } /// } /// } /// ``` /// /// See for /// the meaning of the context-specific tags. pub const RSA_PSS_SHA512: AlgorithmIdentifier = AlgorithmIdentifier::from_slice(include_bytes!("data/alg-rsa-pss-sha512.der")); /// AlgorithmIdentifier for `ED25519`. /// /// This is: /// /// ```text /// # ed25519 /// OBJECT_IDENTIFIER { 1.3.101.112 } /// ``` pub const ED25519: AlgorithmIdentifier = AlgorithmIdentifier::from_slice(include_bytes!("data/alg-ed25519.der")); /// A DER encoding of the PKIX AlgorithmIdentifier type: /// /// ```ASN.1 /// AlgorithmIdentifier ::= SEQUENCE { /// algorithm OBJECT IDENTIFIER, /// parameters ANY DEFINED BY algorithm OPTIONAL } /// -- contains a value of the type /// -- registered for use with the /// -- algorithm object identifier value /// ``` /// (from ) /// /// The outer sequence encoding is *not included*, so this is the DER encoding /// of an OID for `algorithm` plus the `parameters` value. /// /// For example, this is the `rsaEncryption` algorithm (but prefer to use the constant /// [`RSA_ENCRYPTION`] instead): /// /// ``` /// let rsa_encryption = rustls_pki_types::AlgorithmIdentifier::from_slice( /// &[ /// // algorithm: 1.2.840.113549.1.1.1 /// 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, /// // parameters: NULL /// 0x05, 0x00 /// ] /// ); /// assert_eq!(rustls_pki_types::alg_id::RSA_ENCRYPTION, rsa_encryption); /// ``` /// /// Common values for this type are provided in this module. #[derive(Clone, Copy, PartialEq, Eq)] pub struct AlgorithmIdentifier(&'static [u8]); impl AlgorithmIdentifier { /// Makes a new `AlgorithmIdentifier` from a static octet slice. /// /// This does not validate the contents of the slice. pub const fn from_slice(bytes: &'static [u8]) -> Self { Self(bytes) } } impl AsRef<[u8]> for AlgorithmIdentifier { fn as_ref(&self) -> &[u8] { self.0 } } impl fmt::Debug for AlgorithmIdentifier { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { super::hex(f, self.0) } } impl Deref for AlgorithmIdentifier { type Target = [u8]; fn deref(&self) -> &Self::Target { self.as_ref() } } pki-types-v-1.11.0/src/base64.rs000066400000000000000000000546601474567472200162400ustar00rootroot00000000000000/// Decode base64 `input`, writing the result into `output`. /// /// `input` is treated as secret, so efforts are made to avoid /// leaking its value via side channels, such as timing, /// memory accesses, and execution trace. /// /// The following is deemed non-secret information: /// /// - Appearance of whitespace in `input` /// - Erroneous characters in `input` (indeed, the first illegal /// character is quoted in the error type) /// - The length of `input` /// - The length of `output` /// /// Returns the prefix of `output` that was written to. pub(crate) fn decode_secret<'a>(input: &[u8], output: &'a mut [u8]) -> Result<&'a [u8], Error> { decode(input, output, CodePoint::decode_secret) } /// Decode base64 `input`, writing the result into `output`. /// /// `input` is treated as public information, so its value may /// be leaked via side channels. /// /// Returns the prefix of `output` that was written to. pub(crate) fn decode_public<'a>(input: &[u8], output: &'a mut [u8]) -> Result<&'a [u8], Error> { decode(input, output, CodePoint::decode_public) } /// Provide an upper limit on how much space could be required /// to decode a base64 encoding of len `base64_len`. pub(crate) const fn decoded_length(base64_len: usize) -> usize { ((base64_len + 3) / 4) * 3 } fn decode<'a>( input: &[u8], output: &'a mut [u8], decode_byte: impl Fn(u8) -> CodePoint, ) -> Result<&'a [u8], Error> { let mut buffer = 0u64; let mut used = 0; let mut shift = SHIFT_INITIAL; let mut pad_mask = 0; let mut output_offset = 0; const SHIFT_INITIAL: i32 = (8 - 1) * 6; for byte in input.iter().copied() { let (item, pad) = match decode_byte(byte) { CodePoint::WHITESPACE => continue, CodePoint::INVALID => return Err(Error::InvalidCharacter(byte)), CodePoint::PAD => (0, 1), CodePoint(n) => (n, 0), }; // we collect 8 code points (therefore: 6 output bytes) into // `buffer`. this keeps this loop as tight as possible. if used == 8 { if pad_mask != 0b0000_0000 { return Err(Error::PrematurePadding); } let chunk = output .get_mut(output_offset..output_offset + 6) .ok_or(Error::InsufficientOutputSpace)?; chunk[0] = (buffer >> 40) as u8; chunk[1] = (buffer >> 32) as u8; chunk[2] = (buffer >> 24) as u8; chunk[3] = (buffer >> 16) as u8; chunk[4] = (buffer >> 8) as u8; chunk[5] = buffer as u8; output_offset += 6; buffer = 0; used = 0; pad_mask = 0; shift = SHIFT_INITIAL; } buffer |= (item as u64) << shift; shift -= 6; pad_mask |= pad << used; used += 1; } // reduce to final block if used > 4 { if pad_mask & 0b0000_1111 != 0 { return Err(Error::PrematurePadding); } let chunk = output .get_mut(output_offset..output_offset + 3) .ok_or(Error::InsufficientOutputSpace)?; chunk[0] = (buffer >> 40) as u8; chunk[1] = (buffer >> 32) as u8; chunk[2] = (buffer >> 24) as u8; buffer <<= 24; pad_mask >>= 4; used -= 4; output_offset += 3; } match (used, pad_mask) { // no trailing bytes (0, 0b0000) => {} // 4 trailing bytes, no padding (4, 0b0000) => { let chunk = output .get_mut(output_offset..output_offset + 3) .ok_or(Error::InsufficientOutputSpace)?; chunk[0] = (buffer >> 40) as u8; chunk[1] = (buffer >> 32) as u8; chunk[2] = (buffer >> 24) as u8; output_offset += 3; } // 4 trailing bytes with one padding char, or 3 trailing bytes (4, 0b1000) | (3, 0b0000) => { let chunk = output .get_mut(output_offset..output_offset + 2) .ok_or(Error::InsufficientOutputSpace)?; chunk[0] = (buffer >> 40) as u8; chunk[1] = (buffer >> 32) as u8; output_offset += 2; } // 4 trailing bytes with two padding char, or 2 trailing bytes (4, 0b1100) | (2, 0b0000) => { let chunk = output .get_mut(output_offset..output_offset + 1) .ok_or(Error::InsufficientOutputSpace)?; chunk[0] = (buffer >> 40) as u8; output_offset += 1; } // everything else is illegal _ => return Err(Error::InvalidTrailingPadding), } Ok(&output[..output_offset]) } #[derive(Debug, PartialEq)] pub(crate) enum Error { /// Given character is not valid in base64 alphabet. InvalidCharacter(u8), /// A padding character (`=`) appeared outside the final /// block of 4 characters. PrematurePadding, /// The padding characters at the end of the input were invalid. InvalidTrailingPadding, /// Not enough space in output buffer. /// /// Use `decoded_length` to get an upper bound. InsufficientOutputSpace, } #[derive(Copy, Clone, Debug, Eq, PartialEq)] struct CodePoint(u8); impl CodePoint { const WHITESPACE: Self = Self(0xf0); const PAD: Self = Self(0xf1); const INVALID: Self = Self(0xf2); } impl CodePoint { /// Side-channel rules: /// /// - code paths that produce `CodePoint(n)` must not make /// `n` observable via a side channel. /// - other code paths -- whitespace, padding or invalid -- need not, /// these are not considered secret conditions. fn decode_secret(b: u8) -> Self { let is_upper = u8_in_range(b, b'A', b'Z'); let is_lower = u8_in_range(b, b'a', b'z'); let is_digit = u8_in_range(b, b'0', b'9'); let is_plus = u8_equals(b, b'+'); let is_slash = u8_equals(b, b'/'); let is_pad = u8_equals(b, b'='); let is_space = u8_in_range(b, b'\t', b'\r') | u8_equals(b, b' '); let is_invalid = !(is_lower | is_upper | is_digit | is_plus | is_slash | is_pad | is_space); Self( (is_upper & b.wrapping_sub(b'A')) | (is_lower & (b.wrapping_sub(b'a').wrapping_add(26))) | (is_digit & (b.wrapping_sub(b'0').wrapping_add(52))) | (is_plus & 62) | (is_slash & 63) | (is_space & Self::WHITESPACE.0) | (is_pad & Self::PAD.0) | (is_invalid & Self::INVALID.0), ) } fn decode_public(a: u8) -> Self { const TABLE: [CodePoint; 256] = [ // 0x00..0x0f CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::WHITESPACE, CodePoint::WHITESPACE, CodePoint::WHITESPACE, CodePoint::WHITESPACE, CodePoint::WHITESPACE, CodePoint::INVALID, CodePoint::INVALID, // 0x10..0x1f CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, // 0x20..0x2f CodePoint::WHITESPACE, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint(62), CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint(63), // 0x30..0x3f CodePoint(52), CodePoint(53), CodePoint(54), CodePoint(55), CodePoint(56), CodePoint(57), CodePoint(58), CodePoint(59), CodePoint(60), CodePoint(61), CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::PAD, CodePoint::INVALID, CodePoint::INVALID, // 0x40..0x4f CodePoint::INVALID, CodePoint(0), CodePoint(1), CodePoint(2), CodePoint(3), CodePoint(4), CodePoint(5), CodePoint(6), CodePoint(7), CodePoint(8), CodePoint(9), CodePoint(10), CodePoint(11), CodePoint(12), CodePoint(13), CodePoint(14), // 0x50..0x5f CodePoint(15), CodePoint(16), CodePoint(17), CodePoint(18), CodePoint(19), CodePoint(20), CodePoint(21), CodePoint(22), CodePoint(23), CodePoint(24), CodePoint(25), CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, // 0x60..0x6f CodePoint::INVALID, CodePoint(26), CodePoint(27), CodePoint(28), CodePoint(29), CodePoint(30), CodePoint(31), CodePoint(32), CodePoint(33), CodePoint(34), CodePoint(35), CodePoint(36), CodePoint(37), CodePoint(38), CodePoint(39), CodePoint(40), // 0x70..0x7f CodePoint(41), CodePoint(42), CodePoint(43), CodePoint(44), CodePoint(45), CodePoint(46), CodePoint(47), CodePoint(48), CodePoint(49), CodePoint(50), CodePoint(51), CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, // 0x80..0x8f CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, // 0x90..0x9f CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, // 0xa0..0xaf CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, // 0xb0..0xbf CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, // 0xc0..0xcf CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, // 0xd0..0xdf CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, // 0xe0..0xef CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, // 0xf0..0xff CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, CodePoint::INVALID, ]; TABLE[a as usize] } } /// Returns 0xff if `a` in `lo..=hi`. /// /// lo..=hi must not be 0..=255. Callers in this file have constant /// `lo` and `hi`, and this function is private to this file. fn u8_in_range(a: u8, lo: u8, hi: u8) -> u8 { debug_assert!(lo <= hi); debug_assert!(hi - lo != 255); let a = a.wrapping_sub(lo); u8_less_than(a, (hi - lo).wrapping_add(1)) } /// Returns 0xff if a < b, 0 otherwise. fn u8_less_than(a: u8, b: u8) -> u8 { let a = u16::from(a); let b = u16::from(b); u8_broadcast16(a.wrapping_sub(b)) } /// Returns 0xff if a == b, 0 otherwise. fn u8_equals(a: u8, b: u8) -> u8 { let diff = a ^ b; u8_nonzero(diff) } /// Returns 0xff if a != 0, 0 otherwise. fn u8_nonzero(x: u8) -> u8 { u8_broadcast8(!x & x.wrapping_sub(1)) } /// Broadcasts the top bit of `x` /// /// In other words, if the top bit of `x` is set, /// returns 0xff else 0x00. fn u8_broadcast8(x: u8) -> u8 { let msb = x >> 7; 0u8.wrapping_sub(msb) } /// Broadcasts the top bit of `x` /// /// In other words, if the top bit of `x` is set, /// returns 0xff else 0x00. fn u8_broadcast16(x: u16) -> u8 { let msb = x >> 15; 0u8.wrapping_sub(msb as u8) } #[cfg(all(test, feature = "alloc"))] mod tests { use super::*; #[test] fn decode_test() { assert_eq!( decode(b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), b"\x00\x10\x83\x10\x51\x87\x20\x92\x8b\x30\xd3\x8f\x41\x14\x93\x51\x55\x97\ \x61\x96\x9b\x71\xd7\x9f\x82\x18\xa3\x92\x59\xa7\xa2\x9a\xab\xb2\xdb\xaf\ \xc3\x1c\xb3\xd3\x5d\xb7\xe3\x9e\xbb\xf3\xdf\xbf" ); assert_eq!(decode(b"aGVsbG8="), b"hello"); assert_eq!(decode(b"aGVsbG8gd29ybGQ="), b"hello world"); assert_eq!(decode(b"aGVsbG8gd29ybGQh"), b"hello world!"); assert_eq!(decode(b"////"), b"\xff\xff\xff"); assert_eq!(decode(b"++++"), b"\xfb\xef\xbe"); assert_eq!(decode(b"AAAA"), b"\x00\x00\x00"); assert_eq!(decode(b"AAA="), b"\x00\x00"); assert_eq!(decode(b"AA=="), b"\x00"); // like our previous use of rust-base64, we don't require padding // if the encoding is otherwise valid given the length assert_eq!(decode(b"AAA"), b"\x00\x00"); assert_eq!(decode(b"AA"), b"\x00"); assert_eq!(decode(b""), b""); } #[test] fn decode_errors() { let mut buf = [0u8; 6]; // illegal trailing padding assert_eq!( decode_both(b"A===", &mut buf), Err(Error::InvalidTrailingPadding) ); assert_eq!( decode_both(b"====", &mut buf), Err(Error::InvalidTrailingPadding) ); assert_eq!( decode_both(b"A==", &mut buf), Err(Error::InvalidTrailingPadding) ); assert_eq!( decode_both(b"AA=", &mut buf), Err(Error::InvalidTrailingPadding) ); assert_eq!( decode_both(b"A", &mut buf), Err(Error::InvalidTrailingPadding) ); // padding before final block assert_eq!( decode_both(b"=AAAAA==", &mut buf), Err(Error::PrematurePadding) ); assert_eq!( decode_both(b"A=AAAA==", &mut buf), Err(Error::PrematurePadding) ); assert_eq!( decode_both(b"AA=AAA==", &mut buf), Err(Error::PrematurePadding) ); assert_eq!( decode_both(b"AAA=AA==", &mut buf), Err(Error::PrematurePadding) ); // illegal inputs assert_eq!( decode_both(b"%AAA", &mut buf), Err(Error::InvalidCharacter(b'%')) ); assert_eq!( decode_both(b"A%AA", &mut buf), Err(Error::InvalidCharacter(b'%')) ); assert_eq!( decode_both(b"AA%A", &mut buf), Err(Error::InvalidCharacter(b'%')) ); assert_eq!( decode_both(b"AAA%", &mut buf), Err(Error::InvalidCharacter(b'%')) ); // output sizing assert_eq!(decode_both(b"am9lIGJw", &mut [0u8; 7]), Ok(&b"joe bp"[..])); assert_eq!(decode_both(b"am9lIGJw", &mut [0u8; 6]), Ok(&b"joe bp"[..])); assert_eq!( decode_both(b"am9lIGJw", &mut [0u8; 5]), Err(Error::InsufficientOutputSpace) ); assert_eq!( decode_both(b"am9lIGJw", &mut [0u8; 4]), Err(Error::InsufficientOutputSpace) ); assert_eq!( decode_both(b"am9lIGJw", &mut [0u8; 3]), Err(Error::InsufficientOutputSpace) ); // output sizing is not pessimistic when padding is valid assert_eq!(decode_both(b"am9=", &mut [0u8; 2]), Ok(&b"jo"[..])); assert_eq!(decode_both(b"am==", &mut [0u8; 1]), Ok(&b"j"[..])); assert_eq!(decode_both(b"am9", &mut [0u8; 2]), Ok(&b"jo"[..])); assert_eq!(decode_both(b"am", &mut [0u8; 1]), Ok(&b"j"[..])); } #[test] fn check_models() { fn u8_broadcast8_model(x: u8) -> u8 { match x & 0x80 { 0x80 => 0xff, _ => 0x00, } } fn u8_broadcast16_model(x: u16) -> u8 { match x & 0x8000 { 0x8000 => 0xff, _ => 0x00, } } fn u8_nonzero_model(x: u8) -> u8 { match x { 0 => 0xff, _ => 0x00, } } fn u8_equals_model(x: u8, y: u8) -> u8 { match x == y { true => 0xff, false => 0x00, } } fn u8_in_range_model(x: u8, y: u8, z: u8) -> u8 { match (y..=z).contains(&x) { true => 0xff, false => 0x00, } } for x in u8::MIN..=u8::MAX { assert_eq!(u8_broadcast8(x), u8_broadcast8_model(x)); assert_eq!(u8_nonzero(x), u8_nonzero_model(x)); assert_eq!(CodePoint::decode_secret(x), CodePoint::decode_public(x)); for y in u8::MIN..=u8::MAX { assert_eq!(u8_equals(x, y), u8_equals_model(x, y)); let v = (x as u16) | ((y as u16) << 8); assert_eq!(u8_broadcast16(v), u8_broadcast16_model(v)); for z in y..=u8::MAX { if z - y == 255 { continue; } assert_eq!(u8_in_range(x, y, z), u8_in_range_model(x, y, z)); } } } } #[cfg(all(feature = "std", target_os = "linux", target_arch = "x86_64"))] #[test] fn codepoint_decode_secret_does_not_branch_or_index_on_secret_input() { // this is using the same theory as use crabgrind as cg; if matches!(cg::run_mode(), cg::RunMode::Native) { std::println!("SKIPPED: must be run under valgrind"); return; } let input = [b'a']; cg::monitor_command(format!( "make_memory undefined {:p} {}", input.as_ptr(), input.len() )) .unwrap(); core::hint::black_box(CodePoint::decode_secret(input[0])); } #[track_caller] fn decode(input: &[u8]) -> alloc::vec::Vec { let length = decoded_length(input.len()); let mut v = alloc::vec![0u8; length]; let used = decode_both(input, &mut v).unwrap().len(); v.truncate(used); v } fn decode_both<'a>(input: &'_ [u8], output: &'a mut [u8]) -> Result<&'a [u8], Error> { let mut output_copy = output.to_vec(); let r_pub = decode_public(input, &mut output_copy); let r_sec = decode_secret(input, output); assert_eq!(r_pub, r_sec); r_sec } } pki-types-v-1.11.0/src/data/000077500000000000000000000000001474567472200155045ustar00rootroot00000000000000pki-types-v-1.11.0/src/data/README.md000066400000000000000000000012231474567472200167610ustar00rootroot00000000000000These files contain the binary DER encoding of the *values* of some ASN.1 [`AlgorithmIdentifier`]s, without the outer `SEQUENCE` tag or the outer length component. These files were encoded with the help of [der-ascii]. They can be decoded using: ```sh go install github.com/google/der-ascii/cmd/der2ascii@latest der2ascii -i -o .ascii ``` New or modified der-ascii files can be encoded using: ```sh go install github.com/google/der-ascii/cmd/ascii2der@latest ascii2der i .ascii -o ``` [`AlgorithmIdentifier`]: https://tools.ietf.org/html/rfc5280#section-4.1.1.2] [der-ascii]: https://github.com/google/der-ascii pki-types-v-1.11.0/src/data/alg-ecdsa-p256.der000066400000000000000000000000231474567472200205050ustar00rootroot00000000000000*H=*H=pki-types-v-1.11.0/src/data/alg-ecdsa-p384.der000066400000000000000000000000201474567472200205040ustar00rootroot00000000000000*H=+"pki-types-v-1.11.0/src/data/alg-ecdsa-p521.der000066400000000000000000000000201474567472200204750ustar00rootroot00000000000000*H=+#pki-types-v-1.11.0/src/data/alg-ecdsa-sha256.der000066400000000000000000000000121474567472200210170ustar00rootroot00000000000000*H=pki-types-v-1.11.0/src/data/alg-ecdsa-sha384.der000066400000000000000000000000121474567472200210210ustar00rootroot00000000000000*H=pki-types-v-1.11.0/src/data/alg-ecdsa-sha512.der000066400000000000000000000000121474567472200210120ustar00rootroot00000000000000*H=pki-types-v-1.11.0/src/data/alg-ed25519.der000066400000000000000000000000051474567472200177320ustar00rootroot00000000000000+eppki-types-v-1.11.0/src/data/alg-rsa-encryption.der000066400000000000000000000000151474567472200217120ustar00rootroot00000000000000 *H pki-types-v-1.11.0/src/data/alg-rsa-pkcs1-sha256.der000066400000000000000000000000151474567472200215470ustar00rootroot00000000000000 *H  pki-types-v-1.11.0/src/data/alg-rsa-pkcs1-sha384.der000066400000000000000000000000151474567472200215510ustar00rootroot00000000000000 *H  pki-types-v-1.11.0/src/data/alg-rsa-pkcs1-sha512.der000066400000000000000000000000151474567472200215420ustar00rootroot00000000000000 *H  pki-types-v-1.11.0/src/data/alg-rsa-pss-sha256.der000066400000000000000000000001011474567472200213270ustar00rootroot00000000000000 *H  040  `He0 *H 0  `He pki-types-v-1.11.0/src/data/alg-rsa-pss-sha384.der000066400000000000000000000001011474567472200213310ustar00rootroot00000000000000 *H  040  `He0 *H 0  `He0pki-types-v-1.11.0/src/data/alg-rsa-pss-sha512.der000066400000000000000000000001011474567472200213220ustar00rootroot00000000000000 *H  040  `He0 *H 0  `He@pki-types-v-1.11.0/src/lib.rs000066400000000000000000001072341474567472200157160ustar00rootroot00000000000000//! This crate provides types for representing X.509 certificates, keys and other types as //! commonly used in the rustls ecosystem. It is intended to be used by crates that need to work //! with such X.509 types, such as [rustls](https://crates.io/crates/rustls), //! [rustls-webpki](https://crates.io/crates/rustls-webpki), //! [rustls-pemfile](https://crates.io/crates/rustls-pemfile), and others. //! //! Some of these crates used to define their own trivial wrappers around DER-encoded bytes. //! However, in order to avoid inconvenient dependency edges, these were all disconnected. By //! using a common low-level crate of types with long-term stable API, we hope to avoid the //! downsides of unnecessary dependency edges while providing good interoperability between crates. //! //! ## DER and PEM //! //! Many of the types defined in this crate represent DER-encoded data. DER is a binary encoding of //! the ASN.1 format commonly used in web PKI specifications. It is a binary encoding, so it is //! relatively compact when stored in memory. However, as a binary format, it is not very easy to //! work with for humans and in contexts where binary data is inconvenient. For this reason, //! many tools and protocols use a ASCII-based encoding of DER, called PEM. In addition to the //! base64-encoded DER, PEM objects are delimited by header and footer lines which indicate the type //! of object contained in the PEM blob. //! //! Types here can be created from: //! //! - DER using (for example) [`PrivatePkcs8KeyDer::from()`]. //! - PEM using (for example) [`pem::PemObject::from_pem_slice()`]. //! //! The [`pem::PemObject`] trait contains the full selection of ways to construct //! these types from PEM encodings. That includes ways to open and read from a file, //! from a slice, or from an `std::io` stream. //! //! There is also a lower-level API that allows a given PEM file to be fully consumed //! in one pass, even if it contains different data types: see the implementation of //! the [`pem::PemObject`] trait on the `(pem::SectionKind, Vec)` tuple. //! //! ## Creating new certificates and keys //! //! This crate does not provide any functionality for creating new certificates or keys. However, //! the [rcgen](https://docs.rs/rcgen) crate can be used to create new certificates and keys. //! //! ## Cloning private keys //! //! This crate intentionally **does not** implement `Clone` on private key types in //! order to minimize the exposure of private key data in memory. //! //! If you want to extend the lifetime of a `PrivateKeyDer<'_>`, consider [`PrivateKeyDer::clone_key()`]. //! Alternatively since these types are immutable, consider wrapping the `PrivateKeyDer<'_>` in a [`Rc`] //! or an [`Arc`]. //! //! [`Rc`]: https://doc.rust-lang.org/std/rc/struct.Rc.html //! [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html //! [`PrivateKeyDer::clone_key()`]: https://docs.rs/rustls-pki-types/latest/rustls_pki_types/enum.PrivateKeyDer.html#method.clone_key //! //! ## Target `wasm32-unknown-unknown` with the `web` feature //! //! [`std::time::SystemTime`](https://doc.rust-lang.org/std/time/struct.SystemTime.html) //! is unavailable in `wasm32-unknown-unknown` targets, so calls to //! [`UnixTime::now()`](https://docs.rs/rustls-pki-types/latest/rustls_pki_types/struct.UnixTime.html#method.now), //! otherwise enabled by the [`std`](https://docs.rs/crate/rustls-pki-types/latest/features#std) feature, //! require building instead with the [`web`](https://docs.rs/crate/rustls-pki-types/latest/features#web) //! feature. It gets time by calling [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now) //! in the browser. #![cfg_attr(not(feature = "std"), no_std)] #![warn(unreachable_pub, clippy::use_self)] #![deny(missing_docs)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #[cfg(feature = "alloc")] extern crate alloc; #[cfg(feature = "alloc")] use alloc::vec::Vec; use core::fmt; use core::ops::Deref; use core::time::Duration; #[cfg(feature = "alloc")] use pem::{PemObject, PemObjectFilter, SectionKind}; #[cfg(all( feature = "std", not(all(target_family = "wasm", target_os = "unknown")) ))] use std::time::SystemTime; #[cfg(all(target_family = "wasm", target_os = "unknown", feature = "web"))] use web_time::SystemTime; pub mod alg_id; mod base64; mod server_name; /// Low-level PEM decoding APIs. /// /// These APIs allow decoding PEM format in an iterator, which means you /// can load multiple different types of PEM section from a file in a single /// pass. #[cfg(feature = "alloc")] pub mod pem; pub use alg_id::AlgorithmIdentifier; pub use server_name::{ AddrParseError, DnsName, InvalidDnsNameError, IpAddr, Ipv4Addr, Ipv6Addr, ServerName, }; /// A DER-encoded X.509 private key, in one of several formats /// /// See variant inner types for more detailed information. /// /// This can load several types of PEM-encoded private key, and then reveal /// which types were found: /// /// ```rust /// # #[cfg(all(feature = "alloc", feature = "std"))] { /// use rustls_pki_types::{PrivateKeyDer, pem::PemObject}; /// /// // load from a PEM file /// let pkcs8 = PrivateKeyDer::from_pem_file("tests/data/nistp256key.pkcs8.pem").unwrap(); /// let pkcs1 = PrivateKeyDer::from_pem_file("tests/data/rsa1024.pkcs1.pem").unwrap(); /// let sec1 = PrivateKeyDer::from_pem_file("tests/data/nistp256key.pem").unwrap(); /// assert!(matches!(pkcs8, PrivateKeyDer::Pkcs8(_))); /// assert!(matches!(pkcs1, PrivateKeyDer::Pkcs1(_))); /// assert!(matches!(sec1, PrivateKeyDer::Sec1(_))); /// # } /// ``` #[non_exhaustive] #[derive(Debug, PartialEq, Eq)] pub enum PrivateKeyDer<'a> { /// An RSA private key Pkcs1(PrivatePkcs1KeyDer<'a>), /// A Sec1 private key Sec1(PrivateSec1KeyDer<'a>), /// A PKCS#8 private key Pkcs8(PrivatePkcs8KeyDer<'a>), } impl PrivateKeyDer<'_> { /// Clone the private key to a `'static` value #[cfg(feature = "alloc")] pub fn clone_key(&self) -> PrivateKeyDer<'static> { use PrivateKeyDer::*; match self { Pkcs1(key) => Pkcs1(key.clone_key()), Sec1(key) => Sec1(key.clone_key()), Pkcs8(key) => Pkcs8(key.clone_key()), } } /// Yield the DER-encoded bytes of the private key pub fn secret_der(&self) -> &[u8] { match self { PrivateKeyDer::Pkcs1(key) => key.secret_pkcs1_der(), PrivateKeyDer::Sec1(key) => key.secret_sec1_der(), PrivateKeyDer::Pkcs8(key) => key.secret_pkcs8_der(), } } } #[cfg(feature = "alloc")] impl PemObject for PrivateKeyDer<'static> { fn from_pem(kind: SectionKind, value: Vec) -> Option { match kind { SectionKind::RsaPrivateKey => Some(Self::Pkcs1(value.into())), SectionKind::EcPrivateKey => Some(Self::Sec1(value.into())), SectionKind::PrivateKey => Some(Self::Pkcs8(value.into())), _ => None, } } } impl<'a> From> for PrivateKeyDer<'a> { fn from(key: PrivatePkcs1KeyDer<'a>) -> Self { Self::Pkcs1(key) } } impl<'a> From> for PrivateKeyDer<'a> { fn from(key: PrivateSec1KeyDer<'a>) -> Self { Self::Sec1(key) } } impl<'a> From> for PrivateKeyDer<'a> { fn from(key: PrivatePkcs8KeyDer<'a>) -> Self { Self::Pkcs8(key) } } impl<'a> TryFrom<&'a [u8]> for PrivateKeyDer<'a> { type Error = &'static str; fn try_from(key: &'a [u8]) -> Result { const SHORT_FORM_LEN_MAX: u8 = 128; const TAG_SEQUENCE: u8 = 0x30; const TAG_INTEGER: u8 = 0x02; // We expect all key formats to begin with a SEQUENCE, which requires at least 2 bytes // in the short length encoding. if key.first() != Some(&TAG_SEQUENCE) || key.len() < 2 { return Err(INVALID_KEY_DER_ERR); } // The length of the SEQUENCE is encoded in the second byte. We must skip this many bytes. let skip_len = match key[1] >= SHORT_FORM_LEN_MAX { // 1 byte for SEQUENCE tag, 1 byte for short-form len false => 2, // 1 byte for SEQUENCE tag, 1 byte for start of len, remaining bytes encoded // in key[1]. true => 2 + (key[1] - SHORT_FORM_LEN_MAX) as usize, }; let key_bytes = key.get(skip_len..).ok_or(INVALID_KEY_DER_ERR)?; // PKCS#8 (https://www.rfc-editor.org/rfc/rfc5208) describes the PrivateKeyInfo // structure as: // PrivateKeyInfo ::= SEQUENCE { // version Version, // privateKeyAlgorithm AlgorithmIdentifier {{PrivateKeyAlgorithms}}, // privateKey PrivateKey, // attributes [0] Attributes OPTIONAL // } // PKCS#5 (https://www.rfc-editor.org/rfc/rfc8018) describes the AlgorithmIdentifier // as a SEQUENCE. // // Therefore, we consider the outer SEQUENCE, a version number, and the start of // an AlgorithmIdentifier to be enough to identify a PKCS#8 key. If it were PKCS#1 or SEC1 // the version would not be followed by a SEQUENCE. if matches!(key_bytes, [TAG_INTEGER, 0x01, _, TAG_SEQUENCE, ..]) { return Ok(Self::Pkcs8(key.into())); } // PKCS#1 (https://www.rfc-editor.org/rfc/rfc8017) describes the RSAPrivateKey structure // as: // RSAPrivateKey ::= SEQUENCE { // version Version, // modulus INTEGER, -- n // publicExponent INTEGER, -- e // privateExponent INTEGER, -- d // prime1 INTEGER, -- p // prime2 INTEGER, -- q // exponent1 INTEGER, -- d mod (p-1) // exponent2 INTEGER, -- d mod (q-1) // coefficient INTEGER, -- (inverse of q) mod p // otherPrimeInfos OtherPrimeInfos OPTIONAL // } // // Therefore, we consider the outer SEQUENCE and a Version of 0 to be enough to identify // a PKCS#1 key. If it were PKCS#8, the version would be followed by a SEQUENCE. If it // were SEC1, the VERSION would have been 1. if key_bytes.starts_with(&[TAG_INTEGER, 0x01, 0x00]) { return Ok(Self::Pkcs1(key.into())); } // SEC1 (https://www.rfc-editor.org/rfc/rfc5915) describes the ECPrivateKey structure as: // ECPrivateKey ::= SEQUENCE { // version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), // privateKey OCTET STRING, // parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, // publicKey [1] BIT STRING OPTIONAL // } // // Therefore, we consider the outer SEQUENCE and an INTEGER of 1 to be enough to // identify a SEC1 key. If it were PKCS#8 or PKCS#1, the version would have been 0. if key_bytes.starts_with(&[TAG_INTEGER, 0x01, 0x01]) { return Ok(Self::Sec1(key.into())); } Err(INVALID_KEY_DER_ERR) } } static INVALID_KEY_DER_ERR: &str = "unknown or invalid key format"; #[cfg(feature = "alloc")] impl TryFrom> for PrivateKeyDer<'_> { type Error = &'static str; fn try_from(key: Vec) -> Result { Ok(match PrivateKeyDer::try_from(&key[..])? { PrivateKeyDer::Pkcs1(_) => Self::Pkcs1(key.into()), PrivateKeyDer::Sec1(_) => Self::Sec1(key.into()), PrivateKeyDer::Pkcs8(_) => Self::Pkcs8(key.into()), }) } } /// A DER-encoded plaintext RSA private key; as specified in PKCS#1/RFC 3447 /// /// RSA private keys are identified in PEM context as `RSA PRIVATE KEY` and when stored in a /// file usually use a `.pem` or `.key` extension. /// /// ```rust /// # #[cfg(all(feature = "alloc", feature = "std"))] { /// use rustls_pki_types::{PrivatePkcs1KeyDer, pem::PemObject}; /// /// // load from a PEM file /// PrivatePkcs1KeyDer::from_pem_file("tests/data/rsa1024.pkcs1.pem").unwrap(); /// /// // or from a PEM byte slice... /// # let byte_slice = include_bytes!("../tests/data/rsa1024.pkcs1.pem"); /// PrivatePkcs1KeyDer::from_pem_slice(byte_slice).unwrap(); /// # } /// ``` #[derive(PartialEq, Eq)] pub struct PrivatePkcs1KeyDer<'a>(Der<'a>); impl PrivatePkcs1KeyDer<'_> { /// Clone the private key to a `'static` value #[cfg(feature = "alloc")] pub fn clone_key(&self) -> PrivatePkcs1KeyDer<'static> { PrivatePkcs1KeyDer::from(self.0.as_ref().to_vec()) } /// Yield the DER-encoded bytes of the private key pub fn secret_pkcs1_der(&self) -> &[u8] { self.0.as_ref() } } #[cfg(feature = "alloc")] impl PemObjectFilter for PrivatePkcs1KeyDer<'static> { const KIND: SectionKind = SectionKind::RsaPrivateKey; } impl<'a> From<&'a [u8]> for PrivatePkcs1KeyDer<'a> { fn from(slice: &'a [u8]) -> Self { Self(Der(BytesInner::Borrowed(slice))) } } #[cfg(feature = "alloc")] impl From> for PrivatePkcs1KeyDer<'_> { fn from(vec: Vec) -> Self { Self(Der(BytesInner::Owned(vec))) } } impl fmt::Debug for PrivatePkcs1KeyDer<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("PrivatePkcs1KeyDer") .field(&"[secret key elided]") .finish() } } /// A Sec1-encoded plaintext private key; as specified in RFC 5915 /// /// Sec1 private keys are identified in PEM context as `EC PRIVATE KEY` and when stored in a /// file usually use a `.pem` or `.key` extension. For more on PEM files, refer to the crate /// documentation. /// /// ```rust /// # #[cfg(all(feature = "alloc", feature = "std"))] { /// use rustls_pki_types::{PrivateSec1KeyDer, pem::PemObject}; /// /// // load from a PEM file /// PrivateSec1KeyDer::from_pem_file("tests/data/nistp256key.pem").unwrap(); /// /// // or from a PEM byte slice... /// # let byte_slice = include_bytes!("../tests/data/nistp256key.pem"); /// PrivateSec1KeyDer::from_pem_slice(byte_slice).unwrap(); /// # } /// ``` #[derive(PartialEq, Eq)] pub struct PrivateSec1KeyDer<'a>(Der<'a>); impl PrivateSec1KeyDer<'_> { /// Clone the private key to a `'static` value #[cfg(feature = "alloc")] pub fn clone_key(&self) -> PrivateSec1KeyDer<'static> { PrivateSec1KeyDer::from(self.0.as_ref().to_vec()) } /// Yield the DER-encoded bytes of the private key pub fn secret_sec1_der(&self) -> &[u8] { self.0.as_ref() } } #[cfg(feature = "alloc")] impl PemObjectFilter for PrivateSec1KeyDer<'static> { const KIND: SectionKind = SectionKind::EcPrivateKey; } impl<'a> From<&'a [u8]> for PrivateSec1KeyDer<'a> { fn from(slice: &'a [u8]) -> Self { Self(Der(BytesInner::Borrowed(slice))) } } #[cfg(feature = "alloc")] impl From> for PrivateSec1KeyDer<'_> { fn from(vec: Vec) -> Self { Self(Der(BytesInner::Owned(vec))) } } impl fmt::Debug for PrivateSec1KeyDer<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("PrivateSec1KeyDer") .field(&"[secret key elided]") .finish() } } /// A DER-encoded plaintext private key; as specified in PKCS#8/RFC 5958 /// /// PKCS#8 private keys are identified in PEM context as `PRIVATE KEY` and when stored in a /// file usually use a `.pem` or `.key` extension. For more on PEM files, refer to the crate /// documentation. /// /// ```rust /// # #[cfg(all(feature = "alloc", feature = "std"))] { /// use rustls_pki_types::{PrivatePkcs8KeyDer, pem::PemObject}; /// /// // load from a PEM file /// PrivatePkcs8KeyDer::from_pem_file("tests/data/nistp256key.pkcs8.pem").unwrap(); /// PrivatePkcs8KeyDer::from_pem_file("tests/data/rsa1024.pkcs8.pem").unwrap(); /// /// // or from a PEM byte slice... /// # let byte_slice = include_bytes!("../tests/data/nistp256key.pkcs8.pem"); /// PrivatePkcs8KeyDer::from_pem_slice(byte_slice).unwrap(); /// # } /// ``` #[derive(PartialEq, Eq)] pub struct PrivatePkcs8KeyDer<'a>(Der<'a>); impl PrivatePkcs8KeyDer<'_> { /// Clone the private key to a `'static` value #[cfg(feature = "alloc")] pub fn clone_key(&self) -> PrivatePkcs8KeyDer<'static> { PrivatePkcs8KeyDer::from(self.0.as_ref().to_vec()) } /// Yield the DER-encoded bytes of the private key pub fn secret_pkcs8_der(&self) -> &[u8] { self.0.as_ref() } } #[cfg(feature = "alloc")] impl PemObjectFilter for PrivatePkcs8KeyDer<'static> { const KIND: SectionKind = SectionKind::PrivateKey; } impl<'a> From<&'a [u8]> for PrivatePkcs8KeyDer<'a> { fn from(slice: &'a [u8]) -> Self { Self(Der(BytesInner::Borrowed(slice))) } } #[cfg(feature = "alloc")] impl From> for PrivatePkcs8KeyDer<'_> { fn from(vec: Vec) -> Self { Self(Der(BytesInner::Owned(vec))) } } impl fmt::Debug for PrivatePkcs8KeyDer<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("PrivatePkcs8KeyDer") .field(&"[secret key elided]") .finish() } } /// A trust anchor (a.k.a. root CA) /// /// Traditionally, certificate verification libraries have represented trust anchors as full X.509 /// root certificates. However, those certificates contain a lot more data than is needed for /// verifying certificates. The [`TrustAnchor`] representation allows an application to store /// just the essential elements of trust anchors. /// /// The most common way to get one of these is to call [`rustls_webpki::anchor_from_trusted_cert()`]. /// /// [`rustls_webpki::anchor_from_trusted_cert()`]: https://docs.rs/rustls-webpki/latest/webpki/fn.anchor_from_trusted_cert.html #[derive(Clone, Debug, PartialEq, Eq)] pub struct TrustAnchor<'a> { /// Value of the `subject` field of the trust anchor pub subject: Der<'a>, /// Value of the `subjectPublicKeyInfo` field of the trust anchor pub subject_public_key_info: Der<'a>, /// Value of DER-encoded `NameConstraints`, containing name constraints to the trust anchor, if any pub name_constraints: Option>, } impl TrustAnchor<'_> { /// Yield a `'static` lifetime of the `TrustAnchor` by allocating owned `Der` variants #[cfg(feature = "alloc")] pub fn to_owned(&self) -> TrustAnchor<'static> { #[cfg(not(feature = "std"))] use alloc::borrow::ToOwned; TrustAnchor { subject: self.subject.as_ref().to_owned().into(), subject_public_key_info: self.subject_public_key_info.as_ref().to_owned().into(), name_constraints: self .name_constraints .as_ref() .map(|nc| nc.as_ref().to_owned().into()), } } } /// A Certificate Revocation List; as specified in RFC 5280 /// /// Certificate revocation lists are identified in PEM context as `X509 CRL` and when stored in a /// file usually use a `.crl` extension. For more on PEM files, refer to the crate documentation. /// /// ```rust /// # #[cfg(all(feature = "alloc", feature = "std"))] { /// use rustls_pki_types::{CertificateRevocationListDer, pem::PemObject}; /// /// // load several from a PEM file /// let crls: Vec<_> = CertificateRevocationListDer::pem_file_iter("tests/data/crl.pem") /// .unwrap() /// .collect(); /// assert!(crls.len() >= 1); /// /// // or one from a PEM byte slice... /// # let byte_slice = include_bytes!("../tests/data/crl.pem"); /// CertificateRevocationListDer::from_pem_slice(byte_slice).unwrap(); /// /// // or several from a PEM byte slice /// let crls: Vec<_> = CertificateRevocationListDer::pem_slice_iter(byte_slice) /// .collect(); /// assert!(crls.len() >= 1); /// # } /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub struct CertificateRevocationListDer<'a>(Der<'a>); #[cfg(feature = "alloc")] impl PemObjectFilter for CertificateRevocationListDer<'static> { const KIND: SectionKind = SectionKind::Crl; } impl AsRef<[u8]> for CertificateRevocationListDer<'_> { fn as_ref(&self) -> &[u8] { self.0.as_ref() } } impl Deref for CertificateRevocationListDer<'_> { type Target = [u8]; fn deref(&self) -> &Self::Target { self.as_ref() } } impl<'a> From<&'a [u8]> for CertificateRevocationListDer<'a> { fn from(slice: &'a [u8]) -> Self { Self(Der::from(slice)) } } #[cfg(feature = "alloc")] impl From> for CertificateRevocationListDer<'_> { fn from(vec: Vec) -> Self { Self(Der::from(vec)) } } /// A Certificate Signing Request; as specified in RFC 2986 /// /// Certificate signing requests are identified in PEM context as `CERTIFICATE REQUEST` and when stored in a /// file usually use a `.csr` extension. For more on PEM files, refer to the crate documentation. /// /// ```rust /// # #[cfg(all(feature = "alloc", feature = "std"))] { /// use rustls_pki_types::{CertificateSigningRequestDer, pem::PemObject}; /// /// // load from a PEM file /// CertificateSigningRequestDer::from_pem_file("tests/data/csr.pem").unwrap(); /// /// // or from a PEM byte slice... /// # let byte_slice = include_bytes!("../tests/data/csr.pem"); /// CertificateSigningRequestDer::from_pem_slice(byte_slice).unwrap(); /// # } /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub struct CertificateSigningRequestDer<'a>(Der<'a>); #[cfg(feature = "alloc")] impl PemObjectFilter for CertificateSigningRequestDer<'static> { const KIND: SectionKind = SectionKind::Csr; } impl AsRef<[u8]> for CertificateSigningRequestDer<'_> { fn as_ref(&self) -> &[u8] { self.0.as_ref() } } impl Deref for CertificateSigningRequestDer<'_> { type Target = [u8]; fn deref(&self) -> &Self::Target { self.as_ref() } } impl<'a> From<&'a [u8]> for CertificateSigningRequestDer<'a> { fn from(slice: &'a [u8]) -> Self { Self(Der::from(slice)) } } #[cfg(feature = "alloc")] impl From> for CertificateSigningRequestDer<'_> { fn from(vec: Vec) -> Self { Self(Der::from(vec)) } } /// A DER-encoded X.509 certificate; as specified in RFC 5280 /// /// Certificates are identified in PEM context as `CERTIFICATE` and when stored in a /// file usually use a `.pem`, `.cer` or `.crt` extension. For more on PEM files, refer to the /// crate documentation. /// /// ```rust /// # #[cfg(all(feature = "alloc", feature = "std"))] { /// use rustls_pki_types::{CertificateDer, pem::PemObject}; /// /// // load several from a PEM file /// let certs: Vec<_> = CertificateDer::pem_file_iter("tests/data/certificate.chain.pem") /// .unwrap() /// .collect(); /// assert_eq!(certs.len(), 3); /// /// // or one from a PEM byte slice... /// # let byte_slice = include_bytes!("../tests/data/certificate.chain.pem"); /// CertificateDer::from_pem_slice(byte_slice).unwrap(); /// /// // or several from a PEM byte slice /// let certs: Vec<_> = CertificateDer::pem_slice_iter(byte_slice) /// .collect(); /// assert_eq!(certs.len(), 3); /// # } /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub struct CertificateDer<'a>(Der<'a>); impl<'a> CertificateDer<'a> { /// A const constructor to create a `CertificateDer` from a slice of DER. pub const fn from_slice(bytes: &'a [u8]) -> Self { Self(Der::from_slice(bytes)) } } #[cfg(feature = "alloc")] impl PemObjectFilter for CertificateDer<'static> { const KIND: SectionKind = SectionKind::Certificate; } impl AsRef<[u8]> for CertificateDer<'_> { fn as_ref(&self) -> &[u8] { self.0.as_ref() } } impl Deref for CertificateDer<'_> { type Target = [u8]; fn deref(&self) -> &Self::Target { self.as_ref() } } impl<'a> From<&'a [u8]> for CertificateDer<'a> { fn from(slice: &'a [u8]) -> Self { Self(Der::from(slice)) } } #[cfg(feature = "alloc")] impl From> for CertificateDer<'_> { fn from(vec: Vec) -> Self { Self(Der::from(vec)) } } impl CertificateDer<'_> { /// Converts this certificate into its owned variant, unfreezing borrowed content (if any) #[cfg(feature = "alloc")] pub fn into_owned(self) -> CertificateDer<'static> { CertificateDer(Der(self.0 .0.into_owned())) } } /// A DER-encoded SubjectPublicKeyInfo (SPKI), as specified in RFC 5280. #[deprecated(since = "1.7.0", note = "Prefer `SubjectPublicKeyInfoDer` instead")] pub type SubjectPublicKeyInfo<'a> = SubjectPublicKeyInfoDer<'a>; /// A DER-encoded SubjectPublicKeyInfo (SPKI), as specified in RFC 5280. /// /// Public keys are identified in PEM context as a `PUBLIC KEY`. /// /// ```rust /// # #[cfg(all(feature = "alloc", feature = "std"))] { /// use rustls_pki_types::{SubjectPublicKeyInfoDer, pem::PemObject}; /// /// // load from a PEM file /// SubjectPublicKeyInfoDer::from_pem_file("tests/data/spki.pem").unwrap(); /// /// // or from a PEM byte slice... /// # let byte_slice = include_bytes!("../tests/data/spki.pem"); /// SubjectPublicKeyInfoDer::from_pem_slice(byte_slice).unwrap(); /// # } /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub struct SubjectPublicKeyInfoDer<'a>(Der<'a>); #[cfg(feature = "alloc")] impl PemObjectFilter for SubjectPublicKeyInfoDer<'static> { const KIND: SectionKind = SectionKind::PublicKey; } impl AsRef<[u8]> for SubjectPublicKeyInfoDer<'_> { fn as_ref(&self) -> &[u8] { self.0.as_ref() } } impl Deref for SubjectPublicKeyInfoDer<'_> { type Target = [u8]; fn deref(&self) -> &Self::Target { self.as_ref() } } impl<'a> From<&'a [u8]> for SubjectPublicKeyInfoDer<'a> { fn from(slice: &'a [u8]) -> Self { Self(Der::from(slice)) } } #[cfg(feature = "alloc")] impl From> for SubjectPublicKeyInfoDer<'_> { fn from(vec: Vec) -> Self { Self(Der::from(vec)) } } impl SubjectPublicKeyInfoDer<'_> { /// Converts this SubjectPublicKeyInfo into its owned variant, unfreezing borrowed content (if any) #[cfg(feature = "alloc")] pub fn into_owned(self) -> SubjectPublicKeyInfoDer<'static> { SubjectPublicKeyInfoDer(Der(self.0 .0.into_owned())) } } /// A TLS-encoded Encrypted Client Hello (ECH) configuration list (`ECHConfigList`); as specified in /// [draft-ietf-tls-esni-18 §4](https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-18#section-4) #[derive(Clone, Eq, PartialEq)] pub struct EchConfigListBytes<'a>(BytesInner<'a>); impl EchConfigListBytes<'_> { /// Converts this config into its owned variant, unfreezing borrowed content (if any) #[cfg(feature = "alloc")] pub fn into_owned(self) -> EchConfigListBytes<'static> { EchConfigListBytes(self.0.into_owned()) } } #[cfg(feature = "alloc")] impl EchConfigListBytes<'static> { /// Convert an iterator over PEM items into an `EchConfigListBytes` and private key. /// /// This handles the "ECHConfig file" format specified in /// /// /// Use it like: /// /// ```rust /// # #[cfg(all(feature = "alloc", feature = "std"))] { /// # use rustls_pki_types::{EchConfigListBytes, pem::PemObject}; /// let (config, key) = EchConfigListBytes::config_and_key_from_iter( /// PemObject::pem_file_iter("tests/data/ech.pem").unwrap() /// ).unwrap(); /// # } /// ``` pub fn config_and_key_from_iter( iter: impl Iterator), pem::Error>>, ) -> Result<(Self, PrivatePkcs8KeyDer<'static>), pem::Error> { let mut key = None; let mut config = None; for item in iter { let (kind, data) = item?; match kind { SectionKind::PrivateKey => { key = PrivatePkcs8KeyDer::from_pem(kind, data); } SectionKind::EchConfigList => { config = Self::from_pem(kind, data); } _ => continue, }; if let (Some(_key), Some(_config)) = (&key, &config) { return Ok((config.take().unwrap(), key.take().unwrap())); } } Err(pem::Error::NoItemsFound) } } #[cfg(feature = "alloc")] impl PemObjectFilter for EchConfigListBytes<'static> { const KIND: SectionKind = SectionKind::EchConfigList; } impl fmt::Debug for EchConfigListBytes<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { hex(f, self.as_ref()) } } impl AsRef<[u8]> for EchConfigListBytes<'_> { fn as_ref(&self) -> &[u8] { self.0.as_ref() } } impl Deref for EchConfigListBytes<'_> { type Target = [u8]; fn deref(&self) -> &Self::Target { self.as_ref() } } impl<'a> From<&'a [u8]> for EchConfigListBytes<'a> { fn from(slice: &'a [u8]) -> Self { Self(BytesInner::Borrowed(slice)) } } #[cfg(feature = "alloc")] impl From> for EchConfigListBytes<'_> { fn from(vec: Vec) -> Self { Self(BytesInner::Owned(vec)) } } /// An abstract signature verification algorithm. /// /// One of these is needed per supported pair of public key type (identified /// with `public_key_alg_id()`) and `signatureAlgorithm` (identified with /// `signature_alg_id()`). Note that both of these `AlgorithmIdentifier`s include /// the parameters encoding, so separate `SignatureVerificationAlgorithm`s are needed /// for each possible public key or signature parameters. /// /// Debug implementations should list the public key algorithm identifier and /// signature algorithm identifier in human friendly form (i.e. not encoded bytes), /// along with the name of the implementing library (to distinguish different /// implementations of the same algorithms). pub trait SignatureVerificationAlgorithm: Send + Sync + fmt::Debug { /// Verify a signature. /// /// `public_key` is the `subjectPublicKey` value from a `SubjectPublicKeyInfo` encoding /// and is untrusted. The key's `subjectPublicKeyInfo` matches the [`AlgorithmIdentifier`] /// returned by `public_key_alg_id()`. /// /// `message` is the data over which the signature was allegedly computed. /// It is not hashed; implementations of this trait function must do hashing /// if that is required by the algorithm they implement. /// /// `signature` is the signature allegedly over `message`. /// /// Return `Ok(())` only if `signature` is a valid signature on `message`. /// /// Return `Err(InvalidSignature)` if the signature is invalid, including if the `public_key` /// encoding is invalid. There is no need or opportunity to produce errors /// that are more specific than this. fn verify_signature( &self, public_key: &[u8], message: &[u8], signature: &[u8], ) -> Result<(), InvalidSignature>; /// Return the `AlgorithmIdentifier` that must equal a public key's /// `subjectPublicKeyInfo` value for this `SignatureVerificationAlgorithm` /// to be used for signature verification. fn public_key_alg_id(&self) -> AlgorithmIdentifier; /// Return the `AlgorithmIdentifier` that must equal the `signatureAlgorithm` value /// on the data to be verified for this `SignatureVerificationAlgorithm` to be used /// for signature verification. fn signature_alg_id(&self) -> AlgorithmIdentifier; /// Return `true` if this is backed by a FIPS-approved implementation. fn fips(&self) -> bool { false } } /// A detail-less error when a signature is not valid. #[derive(Debug, Copy, Clone)] pub struct InvalidSignature; /// A timestamp, tracking the number of non-leap seconds since the Unix epoch. /// /// The Unix epoch is defined January 1, 1970 00:00:00 UTC. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct UnixTime(u64); impl UnixTime { /// The current time, as a `UnixTime` #[cfg(any( all( feature = "std", not(all(target_family = "wasm", target_os = "unknown")) ), all(target_family = "wasm", target_os = "unknown", feature = "web") ))] pub fn now() -> Self { Self::since_unix_epoch( SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .unwrap(), // Safe: this code did not exist before 1970. ) } /// Convert a `Duration` since the start of 1970 to a `UnixTime` /// /// The `duration` must be relative to the Unix epoch. pub fn since_unix_epoch(duration: Duration) -> Self { Self(duration.as_secs()) } /// Number of seconds since the Unix epoch pub fn as_secs(&self) -> u64 { self.0 } } /// DER-encoded data, either owned or borrowed /// /// This wrapper type is used to represent DER-encoded data in a way that is agnostic to whether /// the data is owned (by a `Vec`) or borrowed (by a `&[u8]`). Support for the owned /// variant is only available when the `alloc` feature is enabled. #[derive(Clone, Eq, PartialEq)] pub struct Der<'a>(BytesInner<'a>); impl<'a> Der<'a> { /// A const constructor to create a `Der` from a borrowed slice pub const fn from_slice(der: &'a [u8]) -> Self { Self(BytesInner::Borrowed(der)) } } impl AsRef<[u8]> for Der<'_> { fn as_ref(&self) -> &[u8] { self.0.as_ref() } } impl Deref for Der<'_> { type Target = [u8]; fn deref(&self) -> &Self::Target { self.as_ref() } } impl<'a> From<&'a [u8]> for Der<'a> { fn from(slice: &'a [u8]) -> Self { Self(BytesInner::Borrowed(slice)) } } #[cfg(feature = "alloc")] impl From> for Der<'static> { fn from(vec: Vec) -> Self { Self(BytesInner::Owned(vec)) } } impl fmt::Debug for Der<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { hex(f, self.as_ref()) } } #[derive(Debug, Clone)] enum BytesInner<'a> { #[cfg(feature = "alloc")] Owned(Vec), Borrowed(&'a [u8]), } #[cfg(feature = "alloc")] impl BytesInner<'_> { fn into_owned(self) -> BytesInner<'static> { BytesInner::Owned(match self { Self::Owned(vec) => vec, Self::Borrowed(slice) => slice.to_vec(), }) } } impl AsRef<[u8]> for BytesInner<'_> { fn as_ref(&self) -> &[u8] { match &self { #[cfg(feature = "alloc")] BytesInner::Owned(vec) => vec.as_ref(), BytesInner::Borrowed(slice) => slice, } } } impl PartialEq for BytesInner<'_> { fn eq(&self, other: &Self) -> bool { self.as_ref() == other.as_ref() } } impl Eq for BytesInner<'_> {} // Format an iterator of u8 into a hex string fn hex<'a>(f: &mut fmt::Formatter<'_>, payload: impl IntoIterator) -> fmt::Result { for (i, b) in payload.into_iter().enumerate() { if i == 0 { write!(f, "0x")?; } write!(f, "{:02x}", b)?; } Ok(()) } #[cfg(all(test, feature = "std"))] mod tests { use super::*; #[test] fn der_debug() { let der = Der::from_slice(&[0x01, 0x02, 0x03]); assert_eq!(format!("{:?}", der), "0x010203"); } #[test] fn alg_id_debug() { let alg_id = AlgorithmIdentifier::from_slice(&[0x01, 0x02, 0x03]); assert_eq!(format!("{:?}", alg_id), "0x010203"); } #[test] fn bytes_inner_equality() { let owned_a = BytesInner::Owned(vec![1, 2, 3]); let owned_b = BytesInner::Owned(vec![4, 5]); let borrowed_a = BytesInner::Borrowed(&[1, 2, 3]); let borrowed_b = BytesInner::Borrowed(&[99]); // Self-equality. assert_eq!(owned_a, owned_a); assert_eq!(owned_b, owned_b); assert_eq!(borrowed_a, borrowed_a); assert_eq!(borrowed_b, borrowed_b); // Borrowed vs Owned equality assert_eq!(owned_a, borrowed_a); assert_eq!(borrowed_a, owned_a); // Owned inequality assert_ne!(owned_a, owned_b); assert_ne!(owned_b, owned_a); // Borrowed inequality assert_ne!(borrowed_a, borrowed_b); assert_ne!(borrowed_b, borrowed_a); // Borrowed vs Owned inequality assert_ne!(owned_a, borrowed_b); assert_ne!(borrowed_b, owned_a); } } pki-types-v-1.11.0/src/pem.rs000066400000000000000000000346761474567472200157420ustar00rootroot00000000000000use alloc::borrow::ToOwned; use alloc::format; use alloc::string::String; use alloc::vec; use alloc::vec::Vec; use core::fmt; use core::marker::PhantomData; use core::ops::ControlFlow; #[cfg(feature = "std")] use std::fs::File; #[cfg(feature = "std")] use std::io::{self, ErrorKind}; use crate::base64; /// Items that can be decoded from PEM data. pub trait PemObject: Sized { /// Decode the first section of this type from PEM contained in /// a byte slice. /// /// [`Error::NoItemsFound`] is returned if no such items are found. fn from_pem_slice(pem: &[u8]) -> Result { Self::pem_slice_iter(pem) .next() .unwrap_or(Err(Error::NoItemsFound)) } /// Iterate over all sections of this type from PEM contained in /// a byte slice. fn pem_slice_iter(pem: &[u8]) -> SliceIter<'_, Self> { SliceIter { current: pem, _ty: PhantomData, } } /// Decode the first section of this type from the PEM contents of the named file. /// /// [`Error::NoItemsFound`] is returned if no such items are found. #[cfg(feature = "std")] fn from_pem_file(file_name: impl AsRef) -> Result { Self::pem_file_iter(file_name)? .next() .unwrap_or(Err(Error::NoItemsFound)) } /// Iterate over all sections of this type from the PEM contents of the named file. /// /// This reports errors in two phases: /// /// - errors opening the file are reported from this function directly, /// - errors reading from the file are reported from the returned iterator, #[cfg(feature = "std")] fn pem_file_iter( file_name: impl AsRef, ) -> Result, Self>, Error> { Ok(ReadIter::<_, Self> { rd: io::BufReader::new(File::open(file_name).map_err(Error::Io)?), _ty: PhantomData, }) } /// Decode the first section of this type from PEM read from an [`io::Read`]. #[cfg(feature = "std")] fn from_pem_reader(rd: impl std::io::Read) -> Result { Self::pem_reader_iter(rd) .next() .unwrap_or(Err(Error::NoItemsFound)) } /// Iterate over all sections of this type from PEM present in an [`io::Read`]. #[cfg(feature = "std")] fn pem_reader_iter(rd: R) -> ReadIter, Self> { ReadIter::<_, Self> { rd: io::BufReader::new(rd), _ty: PhantomData, } } /// Conversion from a PEM [`SectionKind`] and body data. /// /// This inspects `kind`, and if it matches this type's PEM section kind, /// converts `der` into this type. fn from_pem(kind: SectionKind, der: Vec) -> Option; } pub(crate) trait PemObjectFilter: PemObject + From> { const KIND: SectionKind; } impl>> PemObject for T { fn from_pem(kind: SectionKind, der: Vec) -> Option { match Self::KIND == kind { true => Some(Self::from(der)), false => None, } } } /// Extract and return all PEM sections by reading `rd`. #[cfg(feature = "std")] pub struct ReadIter { rd: R, _ty: PhantomData, } #[cfg(feature = "std")] impl ReadIter { /// Create a new iterator. pub fn new(rd: R) -> Self { Self { rd, _ty: PhantomData, } } } #[cfg(feature = "std")] impl Iterator for ReadIter { type Item = Result; fn next(&mut self) -> Option { loop { return match from_buf(&mut self.rd) { Ok(Some((sec, item))) => match T::from_pem(sec, item) { Some(res) => Some(Ok(res)), None => continue, }, Ok(None) => return None, Err(err) => Some(Err(err)), }; } } } /// Iterator over all PEM sections in a `&[u8]` slice. pub struct SliceIter<'a, T> { current: &'a [u8], _ty: PhantomData, } impl<'a, T: PemObject> SliceIter<'a, T> { /// Create a new iterator. pub fn new(current: &'a [u8]) -> Self { Self { current, _ty: PhantomData, } } /// Returns the rest of the unparsed data. /// /// This is the slice immediately following the most /// recently returned item from `next()`. #[doc(hidden)] pub fn remainder(&self) -> &'a [u8] { self.current } } impl Iterator for SliceIter<'_, T> { type Item = Result; fn next(&mut self) -> Option { loop { return match from_slice(self.current) { Ok(Some(((sec, item), rest))) => { self.current = rest; match T::from_pem(sec, item) { Some(res) => Some(Ok(res)), None => continue, } } Ok(None) => return None, Err(err) => Some(Err(err)), }; } } } impl PemObject for (SectionKind, Vec) { fn from_pem(kind: SectionKind, der: Vec) -> Option { Some((kind, der)) } } /// Extract and decode the next supported PEM section from `input` /// /// - `Ok(None)` is returned if there is no PEM section to read from `input` /// - Syntax errors and decoding errors produce a `Err(...)` /// - Otherwise each decoded section is returned with a `Ok(Some((..., remainder)))` where /// `remainder` is the part of the `input` that follows the returned section #[allow(clippy::type_complexity)] fn from_slice(mut input: &[u8]) -> Result), &[u8])>, Error> { let mut b64buf = Vec::with_capacity(1024); let mut section = None::<(Vec<_>, Vec<_>)>; loop { let next_line = if let Some(index) = input .iter() .position(|byte| *byte == b'\n' || *byte == b'\r') { let (line, newline_plus_remainder) = input.split_at(index); input = &newline_plus_remainder[1..]; Some(line) } else if !input.is_empty() { let next_line = input; input = &[]; Some(next_line) } else { None }; match read(next_line, &mut section, &mut b64buf)? { ControlFlow::Continue(()) => continue, ControlFlow::Break(item) => return Ok(item.map(|item| (item, input))), } } } /// Extract and decode the next supported PEM section from `rd`. /// /// - Ok(None) is returned if there is no PEM section read from `rd`. /// - Underlying IO errors produce a `Err(...)` /// - Otherwise each decoded section is returned with a `Ok(Some(...))` #[cfg(feature = "std")] pub fn from_buf(rd: &mut dyn io::BufRead) -> Result)>, Error> { let mut b64buf = Vec::with_capacity(1024); let mut section = None::<(Vec<_>, Vec<_>)>; let mut line = Vec::with_capacity(80); loop { line.clear(); let len = read_until_newline(rd, &mut line).map_err(Error::Io)?; let next_line = if len == 0 { None } else { Some(line.as_slice()) }; match read(next_line, &mut section, &mut b64buf) { Ok(ControlFlow::Break(opt)) => return Ok(opt), Ok(ControlFlow::Continue(())) => continue, Err(e) => return Err(e), } } } #[allow(clippy::type_complexity)] fn read( next_line: Option<&[u8]>, section: &mut Option<(Vec, Vec)>, b64buf: &mut Vec, ) -> Result)>, ()>, Error> { let line = if let Some(line) = next_line { line } else { // EOF return match section.take() { Some((_, end_marker)) => Err(Error::MissingSectionEnd { end_marker }), None => Ok(ControlFlow::Break(None)), }; }; if line.starts_with(b"-----BEGIN ") { let (mut trailer, mut pos) = (0, line.len()); for (i, &b) in line.iter().enumerate().rev() { match b { b'-' => { trailer += 1; pos = i; } b'\n' | b'\r' | b' ' => continue, _ => break, } } if trailer != 5 { return Err(Error::IllegalSectionStart { line: line.to_vec(), }); } let ty = &line[11..pos]; let mut end = Vec::with_capacity(10 + 4 + ty.len()); end.extend_from_slice(b"-----END "); end.extend_from_slice(ty); end.extend_from_slice(b"-----"); *section = Some((ty.to_owned(), end)); return Ok(ControlFlow::Continue(())); } if let Some((section_label, end_marker)) = section.as_ref() { if line.starts_with(end_marker) { let kind = match SectionKind::try_from(§ion_label[..]) { Ok(kind) => kind, // unhandled section: have caller try again Err(()) => { *section = None; b64buf.clear(); return Ok(ControlFlow::Continue(())); } }; let mut der = vec![0u8; base64::decoded_length(b64buf.len())]; let der_len = match kind.secret() { true => base64::decode_secret(b64buf, &mut der), false => base64::decode_public(b64buf, &mut der), } .map_err(|err| Error::Base64Decode(format!("{err:?}")))? .len(); der.truncate(der_len); return Ok(ControlFlow::Break(Some((kind, der)))); } } if section.is_some() { b64buf.extend(line); } Ok(ControlFlow::Continue(())) } /// A single recognised section in a PEM file. #[non_exhaustive] #[derive(Clone, Copy, Debug, PartialEq)] pub enum SectionKind { /// A DER-encoded x509 certificate. /// /// Appears as "CERTIFICATE" in PEM files. Certificate, /// A DER-encoded Subject Public Key Info; as specified in RFC 7468. /// /// Appears as "PUBLIC KEY" in PEM files. PublicKey, /// A DER-encoded plaintext RSA private key; as specified in PKCS #1/RFC 3447 /// /// Appears as "RSA PRIVATE KEY" in PEM files. RsaPrivateKey, /// A DER-encoded plaintext private key; as specified in PKCS #8/RFC 5958 /// /// Appears as "PRIVATE KEY" in PEM files. PrivateKey, /// A Sec1-encoded plaintext private key; as specified in RFC 5915 /// /// Appears as "EC PRIVATE KEY" in PEM files. EcPrivateKey, /// A Certificate Revocation List; as specified in RFC 5280 /// /// Appears as "X509 CRL" in PEM files. Crl, /// A Certificate Signing Request; as specified in RFC 2986 /// /// Appears as "CERTIFICATE REQUEST" in PEM files. Csr, /// An EchConfigList structure, as specified in /// . /// /// Appears as "ECHCONFIG" in PEM files. EchConfigList, } impl SectionKind { fn secret(&self) -> bool { match self { Self::RsaPrivateKey | Self::PrivateKey | Self::EcPrivateKey => true, Self::Certificate | Self::PublicKey | Self::Crl | Self::Csr | Self::EchConfigList => { false } } } } impl TryFrom<&[u8]> for SectionKind { type Error = (); fn try_from(value: &[u8]) -> Result { Ok(match value { b"CERTIFICATE" => Self::Certificate, b"PUBLIC KEY" => Self::PublicKey, b"RSA PRIVATE KEY" => Self::RsaPrivateKey, b"PRIVATE KEY" => Self::PrivateKey, b"EC PRIVATE KEY" => Self::EcPrivateKey, b"X509 CRL" => Self::Crl, b"CERTIFICATE REQUEST" => Self::Csr, b"ECHCONFIG" => Self::EchConfigList, _ => return Err(()), }) } } /// Errors that may arise when parsing the contents of a PEM file #[non_exhaustive] #[derive(Debug)] pub enum Error { /// a section is missing its "END marker" line MissingSectionEnd { /// the expected "END marker" line that was not found end_marker: Vec, }, /// syntax error found in the line that starts a new section IllegalSectionStart { /// line that contains the syntax error line: Vec, }, /// base64 decode error Base64Decode(String), /// I/O errors, from APIs that accept `std::io` types. #[cfg(feature = "std")] Io(io::Error), /// No items found of desired type NoItemsFound, } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::MissingSectionEnd { end_marker } => { write!(f, "missing section end marker: {end_marker:?}") } Self::IllegalSectionStart { line } => { write!(f, "illegal section start: {line:?}") } Self::Base64Decode(e) => write!(f, "base64 decode error: {e}"), #[cfg(feature = "std")] Self::Io(e) => write!(f, "I/O error: {e}"), Self::NoItemsFound => write!(f, "no items found"), } } } #[cfg(feature = "std")] impl std::error::Error for Error {} // Ported from https://github.com/rust-lang/rust/blob/91cfcb021935853caa06698b759c293c09d1e96a/library/std/src/io/mod.rs#L1990 and // modified to look for our accepted newlines. #[cfg(feature = "std")] fn read_until_newline(r: &mut R, buf: &mut Vec) -> io::Result { let mut read = 0; loop { let (done, used) = { let available = match r.fill_buf() { Ok(n) => n, Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, Err(e) => return Err(e), }; match available .iter() .copied() .position(|b| b == b'\n' || b == b'\r') { Some(i) => { buf.extend_from_slice(&available[..=i]); (true, i + 1) } None => { buf.extend_from_slice(available); (false, available.len()) } } }; r.consume(used); read += used; if done || used == 0 { return Ok(read); } } } pki-types-v-1.11.0/src/server_name.rs000066400000000000000000001150321474567472200174510ustar00rootroot00000000000000//! DNS name validation according to RFC1035, but with underscores allowed. #[cfg(all(feature = "alloc", feature = "std"))] use alloc::borrow::Cow; #[cfg(feature = "alloc")] use alloc::string::{String, ToString}; use core::hash::{Hash, Hasher}; use core::{fmt, mem, str}; #[cfg(feature = "std")] use std::error::Error as StdError; /// Encodes ways a client can know the expected name of the server. /// /// This currently covers knowing the DNS name of the server, but /// will be extended in the future to supporting privacy-preserving names /// for the server ("ECH"). For this reason this enum is `non_exhaustive`. /// /// # Making one /// /// If you have a DNS name as a `&str`, this type implements `TryFrom<&str>`, /// so you can do: /// /// ``` /// # use rustls_pki_types::ServerName; /// ServerName::try_from("example.com").expect("invalid DNS name"); /// ``` /// /// If you have an owned `String`, you can use `TryFrom` directly: /// /// ``` /// # use rustls_pki_types::ServerName; /// let name = "example.com".to_string(); /// #[cfg(feature = "alloc")] /// ServerName::try_from(name).expect("invalid DNS name"); /// ``` /// /// which will yield a `ServerName<'static>` if successful. /// /// or, alternatively... /// /// ``` /// # use rustls_pki_types::ServerName; /// let x: ServerName = "example.com".try_into().expect("invalid DNS name"); /// ``` #[non_exhaustive] #[derive(Clone, Eq, Hash, PartialEq)] pub enum ServerName<'a> { /// The server is identified by a DNS name. The name /// is sent in the TLS Server Name Indication (SNI) /// extension. DnsName(DnsName<'a>), /// The server is identified by an IP address. SNI is not /// done. IpAddress(IpAddr), } impl ServerName<'_> { /// Produce an owned `ServerName` from this (potentially borrowed) `ServerName`. #[cfg(feature = "alloc")] pub fn to_owned(&self) -> ServerName<'static> { match self { Self::DnsName(d) => ServerName::DnsName(d.to_owned()), Self::IpAddress(i) => ServerName::IpAddress(*i), } } /// Return the string representation of this `ServerName`. /// /// In the case of a `ServerName::DnsName` instance, this function returns a borrowed `str`. /// For a `ServerName::IpAddress` instance it returns an allocated `String`. #[cfg(feature = "std")] pub fn to_str(&self) -> Cow<'_, str> { match self { Self::DnsName(d) => d.as_ref().into(), Self::IpAddress(i) => std::net::IpAddr::from(*i).to_string().into(), } } } impl fmt::Debug for ServerName<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::DnsName(d) => f.debug_tuple("DnsName").field(&d.as_ref()).finish(), Self::IpAddress(i) => f.debug_tuple("IpAddress").field(i).finish(), } } } #[cfg(feature = "alloc")] impl TryFrom for ServerName<'static> { type Error = InvalidDnsNameError; fn try_from(value: String) -> Result { match DnsName::try_from_string(value) { Ok(dns) => Ok(Self::DnsName(dns)), Err(value) => match IpAddr::try_from(value.as_str()) { Ok(ip) => Ok(Self::IpAddress(ip)), Err(_) => Err(InvalidDnsNameError), }, } } } impl<'a> TryFrom<&'a [u8]> for ServerName<'a> { type Error = InvalidDnsNameError; fn try_from(value: &'a [u8]) -> Result { match str::from_utf8(value) { Ok(s) => Self::try_from(s), Err(_) => Err(InvalidDnsNameError), } } } /// Attempt to make a ServerName from a string by parsing as a DNS name or IP address. impl<'a> TryFrom<&'a str> for ServerName<'a> { type Error = InvalidDnsNameError; fn try_from(s: &'a str) -> Result { match DnsName::try_from(s) { Ok(dns) => Ok(Self::DnsName(dns)), Err(InvalidDnsNameError) => match IpAddr::try_from(s) { Ok(ip) => Ok(Self::IpAddress(ip)), Err(_) => Err(InvalidDnsNameError), }, } } } impl From for ServerName<'_> { fn from(addr: IpAddr) -> Self { Self::IpAddress(addr) } } #[cfg(feature = "std")] impl From for ServerName<'_> { fn from(addr: std::net::IpAddr) -> Self { Self::IpAddress(addr.into()) } } impl From for ServerName<'_> { fn from(v4: Ipv4Addr) -> Self { Self::IpAddress(IpAddr::V4(v4)) } } impl From for ServerName<'_> { fn from(v6: Ipv6Addr) -> Self { Self::IpAddress(IpAddr::V6(v6)) } } #[cfg(feature = "std")] impl From for ServerName<'_> { fn from(v4: std::net::Ipv4Addr) -> Self { Self::IpAddress(IpAddr::V4(v4.into())) } } #[cfg(feature = "std")] impl From for ServerName<'_> { fn from(v6: std::net::Ipv6Addr) -> Self { Self::IpAddress(IpAddr::V6(v6.into())) } } /// A type which encapsulates a string (borrowed or owned) that is a syntactically valid DNS name. #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct DnsName<'a>(DnsNameInner<'a>); impl<'a> DnsName<'a> { /// Produce a borrowed `DnsName` from this owned `DnsName`. pub fn borrow(&'a self) -> Self { Self(match self { Self(DnsNameInner::Borrowed(s)) => DnsNameInner::Borrowed(s), #[cfg(feature = "alloc")] Self(DnsNameInner::Owned(s)) => DnsNameInner::Borrowed(s.as_str()), }) } /// Copy this object to produce an owned `DnsName`, smashing the case to lowercase /// in one operation. #[cfg(feature = "alloc")] pub fn to_lowercase_owned(&self) -> DnsName<'static> { DnsName(DnsNameInner::Owned(self.as_ref().to_ascii_lowercase())) } /// Produce an owned `DnsName` from this (potentially borrowed) `DnsName`. #[cfg(feature = "alloc")] pub fn to_owned(&self) -> DnsName<'static> { DnsName(DnsNameInner::Owned(match self { Self(DnsNameInner::Borrowed(s)) => s.to_string(), #[cfg(feature = "alloc")] Self(DnsNameInner::Owned(s)) => s.clone(), })) } #[cfg(feature = "alloc")] fn try_from_string(s: String) -> Result { match validate(s.as_bytes()) { Ok(_) => Ok(Self(DnsNameInner::Owned(s))), Err(_) => Err(s), } } /// Produces a borrowed [`DnsName`] from a borrowed [`str`]. pub const fn try_from_str(s: &str) -> Result, InvalidDnsNameError> { match validate(s.as_bytes()) { Ok(_) => Ok(DnsName(DnsNameInner::Borrowed(s))), Err(err) => Err(err), } } } #[cfg(feature = "alloc")] impl TryFrom for DnsName<'static> { type Error = InvalidDnsNameError; fn try_from(value: String) -> Result { Self::try_from_string(value).map_err(|_| InvalidDnsNameError) } } impl<'a> TryFrom<&'a str> for DnsName<'a> { type Error = InvalidDnsNameError; fn try_from(value: &'a str) -> Result { DnsName::try_from_str(value) } } impl<'a> TryFrom<&'a [u8]> for DnsName<'a> { type Error = InvalidDnsNameError; fn try_from(value: &'a [u8]) -> Result { validate(value)?; Ok(Self(DnsNameInner::Borrowed(str::from_utf8(value).unwrap()))) } } impl AsRef for DnsName<'_> { fn as_ref(&self) -> &str { match self { Self(DnsNameInner::Borrowed(s)) => s, #[cfg(feature = "alloc")] Self(DnsNameInner::Owned(s)) => s.as_str(), } } } #[derive(Clone, Eq)] enum DnsNameInner<'a> { Borrowed(&'a str), #[cfg(feature = "alloc")] Owned(String), } impl PartialEq for DnsNameInner<'_> { fn eq(&self, other: &Self) -> bool { match (self, other) { (Self::Borrowed(s), Self::Borrowed(o)) => s.eq_ignore_ascii_case(o), #[cfg(feature = "alloc")] (Self::Borrowed(s), Self::Owned(o)) => s.eq_ignore_ascii_case(o.as_str()), #[cfg(feature = "alloc")] (Self::Owned(s), Self::Borrowed(o)) => s.eq_ignore_ascii_case(o), #[cfg(feature = "alloc")] (Self::Owned(s), Self::Owned(o)) => s.eq_ignore_ascii_case(o.as_str()), } } } impl Hash for DnsNameInner<'_> { fn hash(&self, state: &mut H) { let s = match self { Self::Borrowed(s) => s, #[cfg(feature = "alloc")] Self::Owned(s) => s.as_str(), }; s.chars().for_each(|c| c.to_ascii_lowercase().hash(state)); } } impl fmt::Debug for DnsNameInner<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Borrowed(s) => f.write_fmt(format_args!("{:?}", s)), #[cfg(feature = "alloc")] Self::Owned(s) => f.write_fmt(format_args!("{:?}", s)), } } } /// The provided input could not be parsed because /// it is not a syntactically-valid DNS Name. #[derive(Debug)] pub struct InvalidDnsNameError; impl fmt::Display for InvalidDnsNameError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("invalid dns name") } } #[cfg(feature = "std")] impl StdError for InvalidDnsNameError {} const fn validate(input: &[u8]) -> Result<(), InvalidDnsNameError> { enum State { Start, Next, NumericOnly { len: usize }, NextAfterNumericOnly, Subsequent { len: usize }, Hyphen { len: usize }, } use State::*; let mut state = Start; /// "Labels must be 63 characters or less." const MAX_LABEL_LENGTH: usize = 63; /// https://devblogs.microsoft.com/oldnewthing/20120412-00/?p=7873 const MAX_NAME_LENGTH: usize = 253; if input.len() > MAX_NAME_LENGTH { return Err(InvalidDnsNameError); } let mut idx = 0; while idx < input.len() { let ch = input[idx]; state = match (state, ch) { (Start | Next | NextAfterNumericOnly | Hyphen { .. }, b'.') => { return Err(InvalidDnsNameError) } (Subsequent { .. }, b'.') => Next, (NumericOnly { .. }, b'.') => NextAfterNumericOnly, (Subsequent { len } | NumericOnly { len } | Hyphen { len }, _) if len >= MAX_LABEL_LENGTH => { return Err(InvalidDnsNameError) } (Start | Next | NextAfterNumericOnly, b'0'..=b'9') => NumericOnly { len: 1 }, (NumericOnly { len }, b'0'..=b'9') => NumericOnly { len: len + 1 }, (Start | Next | NextAfterNumericOnly, b'a'..=b'z' | b'A'..=b'Z' | b'_') => { Subsequent { len: 1 } } (Subsequent { len } | NumericOnly { len } | Hyphen { len }, b'-') => { Hyphen { len: len + 1 } } ( Subsequent { len } | NumericOnly { len } | Hyphen { len }, b'a'..=b'z' | b'A'..=b'Z' | b'_' | b'0'..=b'9', ) => Subsequent { len: len + 1 }, _ => return Err(InvalidDnsNameError), }; idx += 1; } if matches!( state, Start | Hyphen { .. } | NumericOnly { .. } | NextAfterNumericOnly ) { return Err(InvalidDnsNameError); } Ok(()) } /// `no_std` implementation of `std::net::IpAddr`. /// /// Note: because we intend to replace this type with `core::net::IpAddr` as soon as it is /// stabilized, the identity of this type should not be considered semver-stable. However, the /// attached interfaces are stable; they form a subset of those provided by `core::net::IpAddr`. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum IpAddr { /// An Ipv4 address. V4(Ipv4Addr), /// An Ipv6 address. V6(Ipv6Addr), } impl TryFrom<&str> for IpAddr { type Error = AddrParseError; fn try_from(value: &str) -> Result { match Ipv4Addr::try_from(value) { Ok(v4) => Ok(Self::V4(v4)), Err(_) => match Ipv6Addr::try_from(value) { Ok(v6) => Ok(Self::V6(v6)), Err(e) => Err(e), }, } } } #[cfg(feature = "std")] impl From for IpAddr { fn from(addr: std::net::IpAddr) -> Self { match addr { std::net::IpAddr::V4(v4) => Self::V4(v4.into()), std::net::IpAddr::V6(v6) => Self::V6(v6.into()), } } } #[cfg(feature = "std")] impl From for std::net::IpAddr { fn from(value: IpAddr) -> Self { match value { IpAddr::V4(v4) => Self::from(std::net::Ipv4Addr::from(v4)), IpAddr::V6(v6) => Self::from(std::net::Ipv6Addr::from(v6)), } } } #[cfg(feature = "std")] impl From for IpAddr { fn from(v4: std::net::Ipv4Addr) -> Self { Self::V4(v4.into()) } } #[cfg(feature = "std")] impl From for IpAddr { fn from(v6: std::net::Ipv6Addr) -> Self { Self::V6(v6.into()) } } /// `no_std` implementation of `std::net::Ipv4Addr`. /// /// Note: because we intend to replace this type with `core::net::Ipv4Addr` as soon as it is /// stabilized, the identity of this type should not be considered semver-stable. However, the /// attached interfaces are stable; they form a subset of those provided by `core::net::Ipv4Addr`. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct Ipv4Addr([u8; 4]); impl TryFrom<&str> for Ipv4Addr { type Error = AddrParseError; fn try_from(value: &str) -> Result { // don't try to parse if too long if value.len() > 15 { Err(AddrParseError(AddrKind::Ipv4)) } else { Parser::new(value.as_bytes()).parse_with(|p| p.read_ipv4_addr(), AddrKind::Ipv4) } } } #[cfg(feature = "std")] impl From for Ipv4Addr { fn from(addr: std::net::Ipv4Addr) -> Self { Self(addr.octets()) } } #[cfg(feature = "std")] impl From for std::net::Ipv4Addr { fn from(value: Ipv4Addr) -> Self { Self::from(value.0) } } impl AsRef<[u8; 4]> for Ipv4Addr { fn as_ref(&self) -> &[u8; 4] { &self.0 } } /// `no_std` implementation of `std::net::Ipv6Addr`. /// /// Note: because we intend to replace this type with `core::net::Ipv6Addr` as soon as it is /// stabilized, the identity of this type should not be considered semver-stable. However, the /// attached interfaces are stable; they form a subset of those provided by `core::net::Ipv6Addr`. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct Ipv6Addr([u8; 16]); impl TryFrom<&str> for Ipv6Addr { type Error = AddrParseError; fn try_from(value: &str) -> Result { Parser::new(value.as_bytes()).parse_with(|p| p.read_ipv6_addr(), AddrKind::Ipv6) } } impl From<[u16; 8]> for Ipv6Addr { fn from(value: [u16; 8]) -> Self { // Adapted from `std::net::Ipv6Addr::new()` let addr16 = [ value[0].to_be(), value[1].to_be(), value[2].to_be(), value[3].to_be(), value[4].to_be(), value[5].to_be(), value[6].to_be(), value[7].to_be(), ]; Self( // All elements in `addr16` are big endian. // SAFETY: `[u16; 8]` is always safe to transmute to `[u8; 16]`. unsafe { mem::transmute::<[u16; 8], [u8; 16]>(addr16) }, ) } } #[cfg(feature = "std")] impl From for Ipv6Addr { fn from(addr: std::net::Ipv6Addr) -> Self { Self(addr.octets()) } } #[cfg(feature = "std")] impl From for std::net::Ipv6Addr { fn from(value: Ipv6Addr) -> Self { Self::from(value.0) } } impl AsRef<[u8; 16]> for Ipv6Addr { fn as_ref(&self) -> &[u8; 16] { &self.0 } } // Adapted from core, 2023-11-23 // // https://github.com/rust-lang/rust/blob/fc13ca6d70f7381513c22443fc5aaee1d151ea45/library/core/src/net/parser.rs#L34 mod parser { use super::{AddrParseError, Ipv4Addr, Ipv6Addr}; pub(super) struct Parser<'a> { // Parsing as ASCII, so can use byte array. state: &'a [u8], } impl<'a> Parser<'a> { pub(super) fn new(input: &'a [u8]) -> Self { Parser { state: input } } /// Run a parser, and restore the pre-parse state if it fails. fn read_atomically(&mut self, inner: F) -> Option where F: FnOnce(&mut Parser<'_>) -> Option, { let state = self.state; let result = inner(self); if result.is_none() { self.state = state; } result } /// Run a parser, but fail if the entire input wasn't consumed. /// Doesn't run atomically. pub(super) fn parse_with( &mut self, inner: F, kind: AddrKind, ) -> Result where F: FnOnce(&mut Parser<'_>) -> Option, { let result = inner(self); if self.state.is_empty() { result } else { None }.ok_or(AddrParseError(kind)) } /// Peek the next character from the input fn peek_char(&self) -> Option { self.state.first().map(|&b| char::from(b)) } /// Read the next character from the input fn read_char(&mut self) -> Option { self.state.split_first().map(|(&b, tail)| { self.state = tail; char::from(b) }) } #[must_use] /// Read the next character from the input if it matches the target. fn read_given_char(&mut self, target: char) -> Option<()> { self.read_atomically(|p| { p.read_char() .and_then(|c| if c == target { Some(()) } else { None }) }) } /// Helper for reading separators in an indexed loop. Reads the separator /// character iff index > 0, then runs the parser. When used in a loop, /// the separator character will only be read on index > 0 (see /// read_ipv4_addr for an example) fn read_separator(&mut self, sep: char, index: usize, inner: F) -> Option where F: FnOnce(&mut Parser<'_>) -> Option, { self.read_atomically(move |p| { if index > 0 { p.read_given_char(sep)?; } inner(p) }) } // Read a number off the front of the input in the given radix, stopping // at the first non-digit character or eof. Fails if the number has more // digits than max_digits or if there is no number. fn read_number( &mut self, radix: u32, max_digits: Option, allow_zero_prefix: bool, ) -> Option { self.read_atomically(move |p| { let mut result = T::ZERO; let mut digit_count = 0; let has_leading_zero = p.peek_char() == Some('0'); while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) { result = result.checked_mul(radix)?; result = result.checked_add(digit)?; digit_count += 1; if let Some(max_digits) = max_digits { if digit_count > max_digits { return None; } } } if digit_count == 0 || (!allow_zero_prefix && has_leading_zero && digit_count > 1) { None } else { Some(result) } }) } /// Read an IPv4 address. pub(super) fn read_ipv4_addr(&mut self) -> Option { self.read_atomically(|p| { let mut groups = [0; 4]; for (i, slot) in groups.iter_mut().enumerate() { *slot = p.read_separator('.', i, |p| { // Disallow octal number in IP string. // https://tools.ietf.org/html/rfc6943#section-3.1.1 p.read_number(10, Some(3), false) })?; } Some(Ipv4Addr(groups)) }) } /// Read an IPv6 Address. pub(super) fn read_ipv6_addr(&mut self) -> Option { /// Read a chunk of an IPv6 address into `groups`. Returns the number /// of groups read, along with a bool indicating if an embedded /// trailing IPv4 address was read. Specifically, read a series of /// colon-separated IPv6 groups (0x0000 - 0xFFFF), with an optional /// trailing embedded IPv4 address. fn read_groups(p: &mut Parser<'_>, groups: &mut [u16]) -> (usize, bool) { let limit = groups.len(); for (i, slot) in groups.iter_mut().enumerate() { // Try to read a trailing embedded IPv4 address. There must be // at least two groups left. if i < limit - 1 { let ipv4 = p.read_separator(':', i, |p| p.read_ipv4_addr()); if let Some(v4_addr) = ipv4 { let [one, two, three, four] = v4_addr.0; groups[i] = u16::from_be_bytes([one, two]); groups[i + 1] = u16::from_be_bytes([three, four]); return (i + 2, true); } } let group = p.read_separator(':', i, |p| p.read_number(16, Some(4), true)); match group { Some(g) => *slot = g, None => return (i, false), } } (groups.len(), false) } self.read_atomically(|p| { // Read the front part of the address; either the whole thing, or up // to the first :: let mut head = [0; 8]; let (head_size, head_ipv4) = read_groups(p, &mut head); if head_size == 8 { return Some(head.into()); } // IPv4 part is not allowed before `::` if head_ipv4 { return None; } // Read `::` if previous code parsed less than 8 groups. // `::` indicates one or more groups of 16 bits of zeros. p.read_given_char(':')?; p.read_given_char(':')?; // Read the back part of the address. The :: must contain at least one // set of zeroes, so our max length is 7. let mut tail = [0; 7]; let limit = 8 - (head_size + 1); let (tail_size, _) = read_groups(p, &mut tail[..limit]); // Concat the head and tail of the IP address head[(8 - tail_size)..8].copy_from_slice(&tail[..tail_size]); Some(head.into()) }) } } trait ReadNumberHelper: Sized { const ZERO: Self; fn checked_mul(&self, other: u32) -> Option; fn checked_add(&self, other: u32) -> Option; } macro_rules! impl_helper { ($($t:ty)*) => ($(impl ReadNumberHelper for $t { const ZERO: Self = 0; #[inline] fn checked_mul(&self, other: u32) -> Option { Self::checked_mul(*self, other.try_into().ok()?) } #[inline] fn checked_add(&self, other: u32) -> Option { Self::checked_add(*self, other.try_into().ok()?) } })*) } impl_helper! { u8 u16 u32 } #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub(super) enum AddrKind { Ipv4, Ipv6, } } use parser::{AddrKind, Parser}; /// Failure to parse an IP address #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct AddrParseError(AddrKind); impl core::fmt::Display for AddrParseError { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { f.write_str(match self.0 { AddrKind::Ipv4 => "invalid IPv4 address syntax", AddrKind::Ipv6 => "invalid IPv6 address syntax", }) } } #[cfg(feature = "std")] impl ::std::error::Error for AddrParseError {} #[cfg(test)] mod tests { use super::*; #[cfg(feature = "alloc")] use alloc::format; #[cfg(feature = "alloc")] static TESTS: &[(&str, bool)] = &[ ("", false), ("localhost", true), ("LOCALHOST", true), (".localhost", false), ("..localhost", false), ("1.2.3.4", false), ("127.0.0.1", false), ("absolute.", true), ("absolute..", false), ("multiple.labels.absolute.", true), ("foo.bar.com", true), ("infix-hyphen-allowed.com", true), ("-prefixhypheninvalid.com", false), ("suffixhypheninvalid--", false), ("suffixhypheninvalid-.com", false), ("foo.lastlabelendswithhyphen-", false), ("infix_underscore_allowed.com", true), ("_prefixunderscorevalid.com", true), ("labelendswithnumber1.bar.com", true), ("xn--bcher-kva.example", true), ( "sixtythreesixtythreesixtythreesixtythreesixtythreesixtythreesix.com", true, ), ( "sixtyfoursixtyfoursixtyfoursixtyfoursixtyfoursixtyfoursixtyfours.com", false, ), ( "012345678901234567890123456789012345678901234567890123456789012.com", true, ), ( "0123456789012345678901234567890123456789012345678901234567890123.com", false, ), ( "01234567890123456789012345678901234567890123456789012345678901-.com", false, ), ( "012345678901234567890123456789012345678901234567890123456789012-.com", false, ), ("numeric-only-final-label.1", false), ("numeric-only-final-label.absolute.1.", false), ("1starts-with-number.com", true), ("1Starts-with-number.com", true), ("1.2.3.4.com", true), ("123.numeric-only-first-label", true), ("a123b.com", true), ("numeric-only-middle-label.4.com", true), ("1000-sans.badssl.com", true), ("twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfiftythreecharacters.twohundredandfi", true), ("twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourcharacters.twohundredandfiftyfourc", false), ]; #[cfg(feature = "alloc")] #[test] fn test_validation() { for (input, expected) in TESTS { #[cfg(feature = "std")] println!("test: {:?} expected valid? {:?}", input, expected); let name_ref = DnsName::try_from(*input); assert_eq!(*expected, name_ref.is_ok()); let name = DnsName::try_from(input.to_string()); assert_eq!(*expected, name.is_ok()); } } #[cfg(feature = "alloc")] #[test] fn error_is_debug() { assert_eq!(format!("{:?}", InvalidDnsNameError), "InvalidDnsNameError"); } #[cfg(feature = "alloc")] #[test] fn error_is_display() { assert_eq!(format!("{}", InvalidDnsNameError), "invalid dns name"); } #[cfg(feature = "alloc")] #[test] fn dns_name_is_debug() { let example = DnsName::try_from("example.com".to_string()).unwrap(); assert_eq!(format!("{:?}", example), "DnsName(\"example.com\")"); } #[cfg(feature = "alloc")] #[test] fn dns_name_traits() { let example = DnsName::try_from("example.com".to_string()).unwrap(); assert_eq!(example, example); // PartialEq #[cfg(feature = "std")] { use std::collections::HashSet; let mut h = HashSet::::new(); h.insert(example); } } #[cfg(feature = "alloc")] #[test] fn try_from_ascii_rejects_bad_utf8() { assert_eq!( format!("{:?}", DnsName::try_from(&b"\x80"[..])), "Err(InvalidDnsNameError)" ); } const fn ipv4_address( ip_address: &str, octets: [u8; 4], ) -> (&str, Result) { (ip_address, Ok(Ipv4Addr(octets))) } const IPV4_ADDRESSES: &[(&str, Result)] = &[ // Valid IPv4 addresses ipv4_address("0.0.0.0", [0, 0, 0, 0]), ipv4_address("1.1.1.1", [1, 1, 1, 1]), ipv4_address("205.0.0.0", [205, 0, 0, 0]), ipv4_address("0.205.0.0", [0, 205, 0, 0]), ipv4_address("0.0.205.0", [0, 0, 205, 0]), ipv4_address("0.0.0.205", [0, 0, 0, 205]), ipv4_address("0.0.0.20", [0, 0, 0, 20]), // Invalid IPv4 addresses ("", Err(AddrParseError(AddrKind::Ipv4))), ("...", Err(AddrParseError(AddrKind::Ipv4))), (".0.0.0.0", Err(AddrParseError(AddrKind::Ipv4))), ("0.0.0.0.", Err(AddrParseError(AddrKind::Ipv4))), ("0.0.0", Err(AddrParseError(AddrKind::Ipv4))), ("0.0.0.", Err(AddrParseError(AddrKind::Ipv4))), ("256.0.0.0", Err(AddrParseError(AddrKind::Ipv4))), ("0.256.0.0", Err(AddrParseError(AddrKind::Ipv4))), ("0.0.256.0", Err(AddrParseError(AddrKind::Ipv4))), ("0.0.0.256", Err(AddrParseError(AddrKind::Ipv4))), ("1..1.1.1", Err(AddrParseError(AddrKind::Ipv4))), ("1.1..1.1", Err(AddrParseError(AddrKind::Ipv4))), ("1.1.1..1", Err(AddrParseError(AddrKind::Ipv4))), ("025.0.0.0", Err(AddrParseError(AddrKind::Ipv4))), ("0.025.0.0", Err(AddrParseError(AddrKind::Ipv4))), ("0.0.025.0", Err(AddrParseError(AddrKind::Ipv4))), ("0.0.0.025", Err(AddrParseError(AddrKind::Ipv4))), ("1234.0.0.0", Err(AddrParseError(AddrKind::Ipv4))), ("0.1234.0.0", Err(AddrParseError(AddrKind::Ipv4))), ("0.0.1234.0", Err(AddrParseError(AddrKind::Ipv4))), ("0.0.0.1234", Err(AddrParseError(AddrKind::Ipv4))), ]; #[test] fn parse_ipv4_address_test() { for &(ip_address, expected_result) in IPV4_ADDRESSES { assert_eq!(Ipv4Addr::try_from(ip_address), expected_result); } } const fn ipv6_address( ip_address: &str, octets: [u8; 16], ) -> (&str, Result) { (ip_address, Ok(Ipv6Addr(octets))) } const IPV6_ADDRESSES: &[(&str, Result)] = &[ // Valid IPv6 addresses ipv6_address( "2a05:d018:076c:b685:e8ab:afd3:af51:3aed", [ 0x2a, 0x05, 0xd0, 0x18, 0x07, 0x6c, 0xb6, 0x85, 0xe8, 0xab, 0xaf, 0xd3, 0xaf, 0x51, 0x3a, 0xed, ], ), ipv6_address( "2A05:D018:076C:B685:E8AB:AFD3:AF51:3AED", [ 0x2a, 0x05, 0xd0, 0x18, 0x07, 0x6c, 0xb6, 0x85, 0xe8, 0xab, 0xaf, 0xd3, 0xaf, 0x51, 0x3a, 0xed, ], ), ipv6_address( "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", [ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ], ), ipv6_address( "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", [ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ], ), ipv6_address( "FFFF:ffff:ffff:ffff:ffff:ffff:ffff:ffff", [ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ], ), // Wrong hexadecimal characters on different positions ( "ffgf:ffff:ffff:ffff:ffff:ffff:ffff:ffff", Err(AddrParseError(AddrKind::Ipv6)), ), ( "ffff:gfff:ffff:ffff:ffff:ffff:ffff:ffff", Err(AddrParseError(AddrKind::Ipv6)), ), ( "ffff:ffff:fffg:ffff:ffff:ffff:ffff:ffff", Err(AddrParseError(AddrKind::Ipv6)), ), ( "ffff:ffff:ffff:ffgf:ffff:ffff:ffff:ffff", Err(AddrParseError(AddrKind::Ipv6)), ), ( "ffff:ffff:ffff:ffff:gfff:ffff:ffff:ffff", Err(AddrParseError(AddrKind::Ipv6)), ), ( "ffff:ffff:ffff:ffff:ffff:fgff:ffff:ffff", Err(AddrParseError(AddrKind::Ipv6)), ), ( "ffff:ffff:ffff:ffff:ffff:ffff:ffgf:ffff", Err(AddrParseError(AddrKind::Ipv6)), ), ( "ffff:ffff:ffff:ffff:ffff:ffff:ffgf:fffg", Err(AddrParseError(AddrKind::Ipv6)), ), // Wrong colons on uncompressed addresses ( ":ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", Err(AddrParseError(AddrKind::Ipv6)), ), ( "ffff::ffff:ffff:ffff:ffff:ffff:ffff:ffff", Err(AddrParseError(AddrKind::Ipv6)), ), ( "ffff:ffff::ffff:ffff:ffff:ffff:ffff:ffff", Err(AddrParseError(AddrKind::Ipv6)), ), ( "ffff:ffff:ffff::ffff:ffff:ffff:ffff:ffff", Err(AddrParseError(AddrKind::Ipv6)), ), ( "ffff:ffff:ffff:ffff::ffff:ffff:ffff:ffff", Err(AddrParseError(AddrKind::Ipv6)), ), ( "ffff:ffff:ffff:ffff:ffff::ffff:ffff:ffff", Err(AddrParseError(AddrKind::Ipv6)), ), ( "ffff:ffff:ffff:ffff:ffff:ffff::ffff:ffff", Err(AddrParseError(AddrKind::Ipv6)), ), ( "ffff:ffff:ffff:ffff:ffff:ffff:ffff::ffff", Err(AddrParseError(AddrKind::Ipv6)), ), // More colons than allowed ( "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:", Err(AddrParseError(AddrKind::Ipv6)), ), ( "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", Err(AddrParseError(AddrKind::Ipv6)), ), // v Invalid hexadecimal ( "ga05:d018:076c:b685:e8ab:afd3:af51:3aed", Err(AddrParseError(AddrKind::Ipv6)), ), // Cannot start with colon ( ":a05:d018:076c:b685:e8ab:afd3:af51:3aed", Err(AddrParseError(AddrKind::Ipv6)), ), // Cannot end with colon ( "2a05:d018:076c:b685:e8ab:afd3:af51:3ae:", Err(AddrParseError(AddrKind::Ipv6)), ), // Cannot have more than seven colons ( "2a05:d018:076c:b685:e8ab:afd3:af51:3a::", Err(AddrParseError(AddrKind::Ipv6)), ), // Cannot contain two colons in a row ( "2a05::018:076c:b685:e8ab:afd3:af51:3aed", Err(AddrParseError(AddrKind::Ipv6)), ), // v Textual block size is longer ( "2a056:d018:076c:b685:e8ab:afd3:af51:3ae", Err(AddrParseError(AddrKind::Ipv6)), ), // v Textual block size is shorter ( "2a0:d018:076c:b685:e8ab:afd3:af51:3aed ", Err(AddrParseError(AddrKind::Ipv6)), ), // Shorter IPv6 address ( "d018:076c:b685:e8ab:afd3:af51:3aed", Err(AddrParseError(AddrKind::Ipv6)), ), // Longer IPv6 address ( "2a05:d018:076c:b685:e8ab:afd3:af51:3aed3aed", Err(AddrParseError(AddrKind::Ipv6)), ), ]; #[test] fn parse_ipv6_address_test() { for &(ip_address, expected_result) in IPV6_ADDRESSES { assert_eq!(Ipv6Addr::try_from(ip_address), expected_result); } } #[test] fn try_from_ascii_ip_address_test() { const IP_ADDRESSES: &[(&str, Result)] = &[ // Valid IPv4 addresses ("127.0.0.1", Ok(IpAddr::V4(Ipv4Addr([127, 0, 0, 1])))), // Invalid IPv4 addresses ( // Ends with a dot; misses one octet "127.0.0.", Err(AddrParseError(AddrKind::Ipv6)), ), // Valid IPv6 addresses ( "0000:0000:0000:0000:0000:0000:0000:0001", Ok(IpAddr::V6(Ipv6Addr([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ]))), ), // Something else ( // A hostname "example.com", Err(AddrParseError(AddrKind::Ipv6)), ), ]; for &(ip_address, expected_result) in IP_ADDRESSES { assert_eq!(IpAddr::try_from(ip_address), expected_result) } } #[test] fn try_from_ascii_str_ip_address_test() { const IP_ADDRESSES: &[(&str, Result)] = &[ // Valid IPv4 addresses ("127.0.0.1", Ok(IpAddr::V4(Ipv4Addr([127, 0, 0, 1])))), // Invalid IPv4 addresses ( // Ends with a dot; misses one octet "127.0.0.", Err(AddrParseError(AddrKind::Ipv6)), ), // Valid IPv6 addresses ( "0000:0000:0000:0000:0000:0000:0000:0001", Ok(IpAddr::V6(Ipv6Addr([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ]))), ), // Something else ( // A hostname "example.com", Err(AddrParseError(AddrKind::Ipv6)), ), ]; for &(ip_address, expected_result) in IP_ADDRESSES { assert_eq!(IpAddr::try_from(ip_address), expected_result) } } #[test] #[cfg(feature = "std")] fn to_str() { let domain_str = "example.com"; let domain_servername = ServerName::try_from(domain_str).unwrap(); assert_eq!(domain_str, domain_servername.to_str()); let ipv4_str = "127.0.0.1"; let ipv4_servername = ServerName::try_from("127.0.0.1").unwrap(); assert_eq!(ipv4_str, ipv4_servername.to_str()); let ipv6_str = "::1"; let ipv6_servername = ServerName::try_from(ipv6_str).unwrap(); assert_eq!("::1", ipv6_servername.to_str()); } } pki-types-v-1.11.0/tests/000077500000000000000000000000001474567472200151465ustar00rootroot00000000000000pki-types-v-1.11.0/tests/data/000077500000000000000000000000001474567472200160575ustar00rootroot00000000000000pki-types-v-1.11.0/tests/data/certificate.chain.pem000066400000000000000000000032331474567472200221260ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIBuDCCAWqgAwIBAgICAcgwBQYDK2VwMC4xLDAqBgNVBAMMI3Bvbnl0b3duIEVk RFNBIGxldmVsIDIgaW50ZXJtZWRpYXRlMB4XDTE5MDgxNjEzMjg1MVoXDTI1MDIw NTEzMjg1MVowGTEXMBUGA1UEAwwOdGVzdHNlcnZlci5jb20wKjAFBgMrZXADIQAQ 9M4hrE+Ucw4QUmaKOeKfphklBJi1qsqtX4u+knbseqOBwDCBvTAMBgNVHRMBAf8E AjAAMAsGA1UdDwQEAwIGwDAdBgNVHQ4EFgQUa/gnV4+a22BUKTouAYX6nfLnPKYw RAYDVR0jBD0wO4AUFxIwU406tG3CsPWkHWqfuUT48auhIKQeMBwxGjAYBgNVBAMM EXBvbnl0b3duIEVkRFNBIENBggF7MDsGA1UdEQQ0MDKCDnRlc3RzZXJ2ZXIuY29t ghVzZWNvbmQudGVzdHNlcnZlci5jb22CCWxvY2FsaG9zdDAFBgMrZXADQQApDiBQ ns3fuvsWuFpIS+osj2B/gQ0b6eBAZ1UBxRyDlAo5++JZ0PtaEROyGo2t2gqi2Lyz 47mLyGCvqgVbC6cH -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIBVzCCAQmgAwIBAgIBezAFBgMrZXAwHDEaMBgGA1UEAwwRcG9ueXRvd24gRWRE U0EgQ0EwHhcNMTkwODE2MTMyODUxWhcNMjkwODEzMTMyODUxWjAuMSwwKgYDVQQD DCNwb255dG93biBFZERTQSBsZXZlbCAyIGludGVybWVkaWF0ZTAqMAUGAytlcAMh AD4h3t0UCoMDGgIq4UW4P5zDngsY4vy1pE3wzLPFI4Vdo14wXDAdBgNVHQ4EFgQU FxIwU406tG3CsPWkHWqfuUT48aswIAYDVR0lAQH/BBYwFAYIKwYBBQUHAwEGCCsG AQUFBwMCMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgH+MAUGAytlcANBAAZFvMek Z71I8CXsBmx/0E6Weoaan9mJHgKqgQdK4w4h4dRg6DjNG957IbrLFO3vZduBMnna qHP3xTFF+11Eyg8= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIBTDCB/6ADAgECAhRXcvbYynz4+usVvPtJp++sBUih3TAFBgMrZXAwHDEaMBgG A1UEAwwRcG9ueXRvd24gRWREU0EgQ0EwHhcNMTkwODE2MTMyODUwWhcNMjkwODEz MTMyODUwWjAcMRowGAYDVQQDDBFwb255dG93biBFZERTQSBDQTAqMAUGAytlcAMh AIE4tLweIfcBGfhPqyXFp5pjVxjaiKk+9fTbRy46jAFKo1MwUTAdBgNVHQ4EFgQU z5b9HjkOxffbtCZhWGg+bnxuD6wwHwYDVR0jBBgwFoAUz5b9HjkOxffbtCZhWGg+ bnxuD6wwDwYDVR0TAQH/BAUwAwEB/zAFBgMrZXADQQBNlt7z4bZ7KhzecxZEe3i5 lH9MRqbpP9Rg4HyzAJfTzFGT183HoJiISdPLbxwMn0KaqSGlVe+9GgNKswoaRAwH -----END CERTIFICATE----- pki-types-v-1.11.0/tests/data/certificate.pem000066400000000000000000000031771474567472200210540ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIEnzCCAoegAwIBAgIBezANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9wb255 dG93biBSU0EgQ0EwHhcNMTkwNjA5MTcxNTEyWhcNMjkwNjA2MTcxNTEyWjAsMSow KAYDVQQDDCFwb255dG93biBSU0EgbGV2ZWwgMiBpbnRlcm1lZGlhdGUwggGiMA0G CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCj/tOFeSW3WB+TtuLCR1L/84lZytFw zbpzOTGB1kPEKNbrMsv3lHXm5bHa8Bl3k113k7Hi7OAt/nkMm05s8LcUoovhaG5C G7tjzL+ld1nO74gNS3IQHCzxRdRwIgaDZHyICfBQBfB9/m+9z3yRtOKWJl6i/MT9 HRN6yADW/8gHFlMzRkCKBjIKXehKsu8cbtB+5MukwtXI4rKf9aYXZQOEUn1kEwQJ ZIKBXR0eyloQiZervUE7meRCTBvzXT9VoSEX49/mempp4hnfdHlRNzre4/tphBf1 fRUdpVXZ3DvmzoHdXRVzxx3X5LvDpf7Eb3ViGkXDFwkSfHEhkRnAl4lIzTH/1F25 stmT8a0PA/lCNMrzJBzkLcuem1G1uMHoQZo1f3OpslJ8gHbE9ZlIbIKmpmJS9oop Vh1BH+aOy5doCrF8uOLTQ3d5CqA/EZMGahDHy7IkeNYmG/RXUKNltv+r95gwuRP+ 9UIJ9FTa4REQbIpGWP5XibI6x4LqLTJj+VsCAwEAAaNeMFwwHQYDVR0OBBYEFEKP y8hHZVazpvIsxFcGo4YrkEkwMCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMBBggrBgEF BQcDAjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB/jANBgkqhkiG9w0BAQsFAAOC AgEAMzTRDLBExVFlw98AuX+pM+/R2Gjw5KFHvSYLKLbMRfuuZK1yNYYaYtNrtF+V a53OFgaZj56o7tXc2PB8kw4MELD0ViR8Do2bvZieFcEe4DwhdjGCjuLehVLT29qI 7T3N/JkJ5daemKZcRB6Ne0F4+6QlVVNck28HUKbQThl88RdwLUImmSAfgKSt6uJ5 wlH7wiYQR2vPXwSuEYzwot+L/91eBwuQr4Lovx9+TCKTbwQOKYjX4KfcOOQ1rx0M IMrvwWqnabc6m1F0O6//ibL0kuFkJYEgOH2uJA12FBHO+/q2tcytejkOWKWMJj6Y 2etwIHcpzXaEP7fZ75cFGqcE3s7XGsweBIPLjMP1bKxEcFKzygURm/auUuXBCFBl E16PB6JEAeCKe/8VFeyucvjPuQDWB49aq+r2SbpbI4IeZdz/QgEIOb0MpwStrvhH 9f/DtGMbjvuAEkRoOorK4m5k4GY3LsWTR2bey27AXk8N7pKarpu2N7ChBPm+EV0Y H+tAI/OfdZuNUCES00F5UAFdU8zBUZo19ao2ZqfEADimE7Epk2s0bUe4GSqEXJp6 68oVSMhZmMf/RCSNlr97f34sNiUA1YJ0JbCRZmw8KWNm9H1PARLbrgeRBZ/k31Li WLDr3fiEVk7SGxj3zo94cS6AT55DyXLiSD/bFmL1QXgZweA= -----END CERTIFICATE----- pki-types-v-1.11.0/tests/data/crl.pem000066400000000000000000000016461474567472200173510ustar00rootroot00000000000000-----BEGIN X509 CRL----- MIICiTBzAgEBMA0GCSqGSIb3DQEBCwUAMBoxGDAWBgNVBAMMD3Bvbnl0b3duIFJT QSBDQRcNMjMwNjI3MDgyODEyWhcNMjMwNzI3MDgyODEyWjAVMBMCAgHIFw0yMzA2 MjcwODI3NTlaoA4wDDAKBgNVHRQEAwIBAjANBgkqhkiG9w0BAQsFAAOCAgEAP6EX 9+hxjx/AqdBpynZXjGkEqigBcLcJ2PADOXngdQI1jC0WuYnZymUimemeULtt8X+1 ai2KxAuF1m4NEKZsrGKvO+/9s/X1xbGroyHSAMKtZafFopFpoB2aNbYlx7yIyLtD BBIZIF50g20U+3izqpHutTD10itdk9TLsSceJHpwTkNJtaWMkOfBV28nKzEzVutV f6WzRpURGzui6nQy7aIqImeanpoBoz323psMfC32U0uMBCZltyHNqsX58/2Uhucx 0IPnitNuhv4scCPf/jeRfGIWDrTf1/25LDzRxyg1S4z9aa+3GM4O3dqy4igZEhgT q3pjlJ2hUL5E0oqbZDIQD1SN8UUUv5N2AjwZcxVBNnYeGyuO7YpTBYiu62o73iL2 CjgElfaMq/9hEr9GR9kJozh7VTxtQPbnr4DiucQvhv8o/A1z+zkC0gj8iCLFtDbO 8bvDowcdle9LKkrLaBe6sO+fSH/I9Wj8vrEJKsuwaEraIdEaq2VrIMUPEWN0/MH9 vTwHyadGSMK4CWtrn9fCAgSLw6NX74D7Cx1IaS8vstMjpeUqOS0dk5ThiW47HceB DTko7rV5N+RGH2nW1ynLoZKCJQqqZcLilFMyKPui3jifJnQlMFi54jGVgg/D6UQn 7dA7wb2ux/1hSiaarp+mi7ncVOyByz6/WQP8mfc= -----END X509 CRL----- pki-types-v-1.11.0/tests/data/csr.pem000066400000000000000000000021261474567472200173520ustar00rootroot00000000000000-----BEGIN CERTIFICATE REQUEST----- MIIC+zCCAeMCAQAwfDELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRQwEgYD VQQHDAtTYW4gQW50b25pbzEdMBsGA1UECgwURXhhbXBsZSBPcmdhbml6YXRpb24x EjAQBgNVBAsMCU1hcmtldGluZzEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEiMA0G CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwJw0BcbuqZyiABlmSYTi1tcr8DB0D NcTtzsYe7tlyIKd3mEs+u6Pi3rEQGvOw5eo6CmWII2qmVOqJ2f6gjl2lZJ5DUE6B I+NNE73zfFMrttUtI8X4ChnE4rrGqqUsSvYz1YVU0KiJ/00YMjEY5XlJYYa9FgfZ sUrhj4aCFdXS6CU9jueRr+udEBElDcgTS9+pB+LFhVfUMTdxnJ3BcT4ZDDqODH3/ 5RAgq03dhRpkkaVIg2uVKTBDoM3hs8T1zIxLM7hItaZzMv4uHdfI8y+BdHrePT33 BoTlocvTEZEqqXEdw2kUd4PDgyUTjFE3b9OeLk0Ju5GRvuCW3UcS5gFvAgMBAAGg OjA4BgkqhkiG9w0BCQ4xKzApMCcGA1UdEQQgMB6CC2V4YW1wbGUuY29tgg9mb28u ZXhhbXBsZS5jb20wDQYJKoZIhvcNAQELBQADggEBACWsgxPw13QUpoCJOqvp8B1A EfsxRJITSROmukV3ZQycPT76Y3GVrM9sGjO8p13J/CVw2KcWc9xmgHF0MdvPNhnW OB6Y07hVpNnJVHb1KglOkNkTy6sVDtnZHg2klqGSyzIbwZ9R3JG8HtRdkceIrm3D gdiZyLcf1VDCCUGaskEi2CsggCQQJNyGi+8BSQ8MPKm/m0KrSchGQ157eWCCjopz f5GQe2UGOg5T7g8+S4GdECMwkMlTGUwlAM6LuOG/NZqP528PCAYQv0eOYdSwALQT GwTyU4AZ9y1uBFuaFxABew9GbDEtNY/XHTF8308edUwGBk6jfD+UuTeEwRZGs9E= -----END CERTIFICATE REQUEST----- pki-types-v-1.11.0/tests/data/ech.pem000066400000000000000000000004031474567472200173160ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MC4CAQAwBQYDK2VuBCIEICjd4yGRdsoP9gU7YT7My8DHx1Tjme8GYDXrOMCi8v1V -----END PRIVATE KEY----- -----BEGIN ECHCONFIG----- AD7+DQA65wAgACA8wVN2BtscOl3vQheUzHeIkVmKIiydUhDCliA4iyQRCwAEAAEA AQALZXhhbXBsZS5jb20AAA== -----END ECHCONFIG----- pki-types-v-1.11.0/tests/data/gunk.pem000066400000000000000000000122211474567472200175240ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1 TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/ gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2 j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6 5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS /jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D hNQ+IIX3Sj0rnP0qCglN6oH4EZw= -----END CERTIFICATE----- 000  *H  01 0 UDE1+0)U "T-Systems Enterprise Services GmbH10U T-Systems Trust Center1%0#U T-TeleSec GlobalRoot Class 20 081001104014Z 331001235959Z01 0 UDE1+0)U "T-Systems Enterprise Services GmbH10U T-Systems Trust Center1%0#U T-TeleSec GlobalRoot Class 20"0  *H 0 __s\GhU`Y4 Hi !\e_|{|-ֺfT#͹I?On7H(8žhp9MǘPD#}9GRb^1BvzlqOx`˾~3n4!r?0[Mj[CDWŘ( ŷS&QC Xv|rpo_5Ʋo4|HOZ9؝x>^DB0@0U00U0UY 6y"kaҸ,˂J0  *H  1a tr6mM"Vͻ꫑8|M󶣍D!>ba#30zk6b{#Xe+%S7bS_b)'q:"~op J4z[u|z)G~@1w@;QGz.GޝfЋTfU|)o/>xbn_xctXH 9yZ zW"-<%&=C|ȫd0.<3vͭO'n -----BEGIN CERTIFICATE----- MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAw WjELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAw MiA0ODE0NjMwODEwMDAzNjEZMBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0x MzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjdaMFoxCzAJBgNVBAYTAkZSMRIwEAYD VQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYzMDgxMDAwMzYxGTAX BgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw ggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sO ty3tRQgXstmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9M CiBtnyN6tMbaLOQdLNyzKNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPu I9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8JXrJhFwLrN1CTivngqIkicuQstDuI7pm TLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16XdG+RCYyKfHx9WzMfgIh C59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq4NYKpkDf ePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3Yz IoejwpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWT Co/1VTp2lc5ZmIoJlXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1k JWumIWmbat10TWuXekG9qxf5kBdIjzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5 hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp//TBt2dzhauH8XwIDAQABo4IB GjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE FBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of 1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczov L3d3d3cuY2VydGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilo dHRwOi8vY3JsLmNlcnRpZ25hLmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYr aHR0cDovL2NybC5kaGlteW90aXMuY29tL2NlcnRpZ25hcm9vdGNhLmNybDANBgkq hkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOItOoldaDgvUSILSo3L 6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxPTGRG HVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH6 0BGM+RFq7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncB lA2c5uk5jR+mUYyZDDl34bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdi o2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1 gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS6Cvu5zHbugRqh5jnxV/v faci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaYtlu3zM63 Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayh jWZSaX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw 3kAP+HwV96LOPNdeE4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0= -----END CERTIFICATE----- pki-types-v-1.11.0/tests/data/mixed-line-endings.crt000066400000000000000000000112201474567472200222450ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM 9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L 93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU 5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy rqXRfboQnoZsG4q5WTP468SQvvG5 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM 9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L 93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU 5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy rqXRfboQnoZsG4q5WTP468SQvvG5 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM 9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L 93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU 5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy rqXRfboQnoZsG4q5WTP468SQvvG5 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM 9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L 93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU 5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy rqXRfboQnoZsG4q5WTP468SQvvG5 -----END CERTIFICATE----- pki-types-v-1.11.0/tests/data/nistp256curve.pem000066400000000000000000000001131474567472200212140ustar00rootroot00000000000000-----BEGIN EC PARAMETERS----- BggqhkjOPQMBBw== -----END EC PARAMETERS----- pki-types-v-1.11.0/tests/data/nistp256key.pem000066400000000000000000000003431474567472200206650ustar00rootroot00000000000000-----BEGIN EC PRIVATE KEY----- MHcCAQEEIMaA7bFrjDDBSik057bIKo7UQXJZNwLK9AjYZQ7yIWFloAoGCCqGSM49 AwEHoUQDQgAExu0Z/w8nQJZAXeOXOnZun9HiZscY9H/KwYcXpeZHu+f9P9mOUEkH 5Z0av+JKtzhFspjngNLVgWcjlA1L5AJLdA== -----END EC PRIVATE KEY----- pki-types-v-1.11.0/tests/data/nistp256key.pkcs8.pem000066400000000000000000000003611474567472200217140ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgxoDtsWuMMMFKKTTn tsgqjtRBclk3Asr0CNhlDvIhYWWhRANCAATG7Rn/DydAlkBd45c6dm6f0eJmxxj0 f8rBhxel5ke75/0/2Y5QSQflnRq/4kq3OEWymOeA0tWBZyOUDUvkAkt0 -----END PRIVATE KEY----- pki-types-v-1.11.0/tests/data/rsa-key-no-trailing-newline.pem000066400000000000000000000032471474567472200240230ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCrOnJjNc/rt+EA 5V5w6Jq+d6WzukJqrFrEVogUI3ByMagTGx5YYABtbqnJXZ5Ad5gUxZzihqCvNzEP IPS4OGIfhvzKhRno1qIK4u/luoVdc/kItZ5gNE7OIW6hmgD6X1JRQjnc6GNoOINX 2wmL7zePhD6mCqNIwlqQjjDh+GHkPdlhn/XXnv/LgRYpb52lfVmihqcAej5mIr6g 5xdyV1ql53QwdOEx0IJayOVuCvVSt2l7y8vqolHQ4CwNc4XN+rTYXdia8akysFiH dMeUQi46TqWarvK+0s3hcq5NNzesg6p5miNywpUVffAYLmd6Rr9vDOkFTwQzIX8U 3Pi05TNtAgMBAAECggEALEZWoZSeiMLpKUQl4Wgj6zAg5pI47kBW0AHX+e0X/E8e uKIAfLWGJsXAnVLZwq2p42Udpe1Ny0CoLNNGtAPQS3qqDuvPwUcOa2Y2xd4u8fU0 5a2goBumbOJ0KtEZg6P2MxC+yYJFggNq4uK+WzKl2TRGxsRlaEDpDgvWU/fXyB5Q pkMVkHU8VWKtSG+rZ0nMZnGuRFKFdTmtLcTYJip4qY4EqqkmhNYdkQrBDOCGEU4o cV/Nx48iNs78SP06ZLl3rp0xxI436ZTFvPw6E1B3j0Bsh2ufYU37RtuiUTbXABkw X67Lt5evvomxWHovpD3zXg8dP8DEOBZMkc1HFJAIuQKBgQDxH3uWrAhCSEMvLB+2 1KGlpEe2V/Sq5c6I2+PqgKNU9/RdJIX0QiNZ5VQQGcBzWRv6HDWnrDWO7AxuQSA8 golZhH3bMXe8k9XWk6yn+XZsnvR6XQ702LqUH6e9cX9VRjR1gCWhSc9OcnfruwWT penS3L94qxZi8lrQSKyS0R+7OwKBgQC1yvvMKF/FDoLmbch01ep7dbWUBfXp6oM/ HSzm3Q7U8cyQdwNcwc1zR8x7TFnuvyPxqV1K1hVbUot77s5yQ9x89EQNN682zEjT 7Ox8ELYj7RxLc2cl23/Rv8Exvotv2HvbA+vr9lI/UlyUsRmZFnn+2cAcKge+o1M6 BTwTzT7RdwKBgHS45c3pV1ImwwcZ1/xccCfMH77gUxtLhbBwqaMCRI7EPTG9lW7J eW1x+0CUBrqP32AyubKCRab7E4Vn12ATXMPNxFMQXkMWWYS3FfR2aWJpjDWTyMK0 C5XawQuO3rH7+zcKIq0yGr2B4hVmAmwX+9nMbI/QWlTptxZup8OmojKzAoGAL5sK OTpRaf8U1FbnaYEjOFVoyWyOK0VYPUzcl1BINAdl9GbWIJI3xPqGV7t0yYqQVRZu 8cwCJ7oEAN5WfaG7uZUVxQhR/92bLLQccZjGub277R45YraKUFkQtIAbb5yXQpFS VKZaf26IBAAknew+4jgPkNCI2qlWgBki1GSpEJ0CgYBpksYOxy58fVySeKtxz7Vy ckfVCsCsJKIRK3GU4M67GPiDUXGPJCb8l5QUcjwD1hte6aT6/z+DhW552kfcRd/w d9UuI3uBcZosFBI5UQM4oQQ5gOLOcSmHfesRE0X5hQdcgjN025lUxUTGJqsrD6wj gh5gNFkL4M5Hoktydw93eQ== -----END PRIVATE KEY-----pki-types-v-1.11.0/tests/data/rsa1024.pkcs1.pem000066400000000000000000000015671474567472200207070ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQC1Dt8tFmGS76ciuNXvk/QRrV8wCcArWxvl7Ku0aSQXgcFBAav6 P5RD8b+dC9DihSu/r+6OOfjsAZ6oKCq3OTUfmoUhLpoBomxPczJgLyyLD+nQkp5q B1Q3WB6ACL/HJRRjJEIn7lc5u1FVBGbiCAHKMiaP4BDSym8oqimKC6uiaQIDAQAB AoGAGKmY7sxQqDIqwwkIYyT1Jv9FqwZ4/a7gYvZVATMdLnKHP3KZ2XGVoZepcRvt 7R0Us3ykcw0kgglKcj9eaizJtnSuoDPPwt53mDypPN2sU3hZgyk2tPgr49DB3MIp fjoqw4RL/p60ksgGXbDEqBuXqOtH5i61khWlMj+BWL9VDq0CQQDaELWPQGjgs+7X /QyWMJwOF4FXE4jecH/CcPVDB9K1ukllyC1HqTNe44Sp2bIDuSXXWb8yEixrEWBE ci2CSSjXAkEA1I4W9IzwEmAeLtL6VBip9ks52O0JKu373/Xv1F2GYdhnQaFw7IC6 1lSzcYMKGTmDuM8Cj26caldyv19Q0SPmvwJAdRHjZzS9GWWAJJTF3Rvbq/USix0B renXrRvXkFTy2n1YSjxdkstTuO2Mm2M0HquXlTWpX8hB8HkzpYtmwztjoQJAECKl LXVReCOhxu4vIJkqtc6qGoSL8J1WRH8X8KgU3nKeDAZkWx++jyyo3pIS/y01iZ71 U8wSxaPTyyFCMk4mYwJBALjg7g8yDy1Lg9GFfOZvAVzPjqD28jZh/VJsDz9IhYoG z89iHWHkllOisbOm+SeynVC8CoFXmJPc26U65GcjI18= -----END RSA PRIVATE KEY----- pki-types-v-1.11.0/tests/data/rsa1024.pkcs8.pem000066400000000000000000000016241474567472200207100ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALUO3y0WYZLvpyK4 1e+T9BGtXzAJwCtbG+Xsq7RpJBeBwUEBq/o/lEPxv50L0OKFK7+v7o45+OwBnqgo Krc5NR+ahSEumgGibE9zMmAvLIsP6dCSnmoHVDdYHoAIv8clFGMkQifuVzm7UVUE ZuIIAcoyJo/gENLKbyiqKYoLq6JpAgMBAAECgYAYqZjuzFCoMirDCQhjJPUm/0Wr Bnj9ruBi9lUBMx0ucoc/cpnZcZWhl6lxG+3tHRSzfKRzDSSCCUpyP15qLMm2dK6g M8/C3neYPKk83axTeFmDKTa0+Cvj0MHcwil+OirDhEv+nrSSyAZdsMSoG5eo60fm LrWSFaUyP4FYv1UOrQJBANoQtY9AaOCz7tf9DJYwnA4XgVcTiN5wf8Jw9UMH0rW6 SWXILUepM17jhKnZsgO5JddZvzISLGsRYERyLYJJKNcCQQDUjhb0jPASYB4u0vpU GKn2SznY7Qkq7fvf9e/UXYZh2GdBoXDsgLrWVLNxgwoZOYO4zwKPbpxqV3K/X1DR I+a/AkB1EeNnNL0ZZYAklMXdG9ur9RKLHQGt6detG9eQVPLafVhKPF2Sy1O47Yyb YzQeq5eVNalfyEHweTOli2bDO2OhAkAQIqUtdVF4I6HG7i8gmSq1zqoahIvwnVZE fxfwqBTecp4MBmRbH76PLKjekhL/LTWJnvVTzBLFo9PLIUIyTiZjAkEAuODuDzIP LUuD0YV85m8BXM+OoPbyNmH9UmwPP0iFigbPz2IdYeSWU6Kxs6b5J7KdULwKgVeY k9zbpTrkZyMjXw== -----END PRIVATE KEY----- pki-types-v-1.11.0/tests/data/spki.pem000066400000000000000000000014401474567472200175270ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAqIh8FTj9DIgI8DAoCBh+ 6UXOfaWkvNaGZx2GwXl4WDAa/ZSE5/8ofg/6V59bmk9yry57UR4F+blscBvE4g3U dTvWJOBRD900l21vwpDLKzZguyGOCmKwJu3vCnAQKzBRXW5sDgvO67GeU6kpaic9 LYPYnYaoxCRTYTZu0wy72rW5G0Fe8Gg/duJmUH7vqGIZupTTVzIBMbFVPBMJqprT MStDhaUL0JiAz0ZgTeNLRIBZWV9mY4PG3rZtbV0BZGR1ipAq9xfgqJcURCcKl/ZT UMtzvgk8s5hYkIJX0ZL3qsfdM4BMgIFhHq/GisQKbbu9kWldBrxQylOwa6r0m3Jv KJX2ViDSORndaCz2sppmVx5HDHnj+Bw381yawphnpumP3BJK4iof//uYKvfdc4RC y2EXL8PYPsT5DMB0jaBt92ytR5sLhn8Sl9Hk0buN4IjrYPISrdhS45xQXUqxcp9O 9hcU+rSaQyZ45cj+VlWhKq8MDvGvaAONBFSEh01mnUwoJObsAZNVFVtuOkwAli0F kGouMycQY1BGscpdC516Nya361Hk/ICyby2Y0BJrrVGaSM6poXH9yEjglzAdtSDb Cvhn/zlAI5ltm4Nv2qTgYBDns5JRGVhBym6RbbZ1C/KfCgn0hOxiw3N7AN4d0K5n LI6p7U9RnNVbWgbqsuoxBtkCAwEAAQ== -----END PUBLIC KEY----- pki-types-v-1.11.0/tests/data/whitespace-prefix.crt000066400000000000000000000023601474567472200222210ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDaTCCAlGgAwIBAgIJAOq/zL+84IswMA0GCSqGSIb3DQEBCwUAMFoxCzAJBgNV BAYTAlVTMQswCQYDVQQIDAJOQzEMMAoGA1UEBwwDUlRQMQ8wDQYDVQQKDAZOZXRB cHAxDTALBgNVBAsMBEVTSVMxEDAOBgNVBAMMB1NTRk1DQ0EwHhcNMTcxMTAxMjEw OTQyWhcNMjcxMDMwMjEwOTQyWjBaMQswCQYDVQQGEwJVUzELMAkGA1UECAwCTkMx DDAKBgNVBAcMA1JUUDEPMA0GA1UECgwGTmV0QXBwMQ0wCwYDVQQLDARFU0lTMRAw DgYDVQQDDAdTU0ZNQ0NBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA iaD9Ee0Yrdka0I+9GTJBIW/Fp5JU6kyjaxfOldW/R9lEubegXQFhDD2Xi1HZ+fTM f224glB9xLJXAHhipRK01C2MgC4kSH75WL1iAiYeOBloExqmK6OCX+sdyO7RXm/H Ra9tN2INWdvyO2pnmxsSnq56mCMsUZLtrRKp89FWgcxLg5r8QxH7xwfh5k54rxjE 144TD9yrIiQOgRSIRHUrVJ9l/F/gnwzP8wcNABeXwN71Mzl7mliPA703kONQIAyU 0E0tLpmy/U8dZdMmTBZGB7jI9f95Hl1RunfwhR371a6z38kgkvwrLzl4qflfsPjw K9n4omNk9rCH9H9tWkxxjwIDAQABozIwMDAdBgNVHQ4EFgQU/bFyCCnqdDFKlQBJ ExtV6wcMYkEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAOQMs Pz2iBD1+3RcSOsahB36WAwPCjgPiXXXpU+Zri11+m6I0Lq+OWtf+YgaQ8ylLmCQd 0p1wHlYA4qo896SycrhTQfy9GlS/aQqN192k3oBGoJcMIUnGUBGuEvyZ2aDUfkzy JUqBe+0KaT7pkvvbRL7VUz34I7ouq9fQIRZ26vUDLTY3KM1n/DXBj3e30GHGMV3K NN2twuLXPNjnryfgpliHU1rwV7r1WvrCVn4StjimP2bO5HGqD/SbiYUL2M9LOuLK 6mqY4OHumYXq3k7CHrvt0FepsN0L14LYEt1LvpPDFWP3SdN4z4KqT9AGqBaJnhhl Qiq8GWnAChspdBLxCg== -----END CERTIFICATE----- pki-types-v-1.11.0/tests/data/zen.pem000066400000000000000000000223041474567472200173570ustar00rootroot00000000000000one with everything -----BEGIN CERTIFICATE----- MIIBuDCCAWqgAwIBAgICAcgwBQYDK2VwMC4xLDAqBgNVBAMMI3Bvbnl0b3duIEVk RFNBIGxldmVsIDIgaW50ZXJtZWRpYXRlMB4XDTE5MDgxNjEzMjg1MVoXDTI1MDIw NTEzMjg1MVowGTEXMBUGA1UEAwwOdGVzdHNlcnZlci5jb20wKjAFBgMrZXADIQAQ 9M4hrE+Ucw4QUmaKOeKfphklBJi1qsqtX4u+knbseqOBwDCBvTAMBgNVHRMBAf8E AjAAMAsGA1UdDwQEAwIGwDAdBgNVHQ4EFgQUa/gnV4+a22BUKTouAYX6nfLnPKYw RAYDVR0jBD0wO4AUFxIwU406tG3CsPWkHWqfuUT48auhIKQeMBwxGjAYBgNVBAMM EXBvbnl0b3duIEVkRFNBIENBggF7MDsGA1UdEQQ0MDKCDnRlc3RzZXJ2ZXIuY29t ghVzZWNvbmQudGVzdHNlcnZlci5jb22CCWxvY2FsaG9zdDAFBgMrZXADQQApDiBQ ns3fuvsWuFpIS+osj2B/gQ0b6eBAZ1UBxRyDlAo5++JZ0PtaEROyGo2t2gqi2Lyz 47mLyGCvqgVbC6cH -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIBVzCCAQmgAwIBAgIBezAFBgMrZXAwHDEaMBgGA1UEAwwRcG9ueXRvd24gRWRE U0EgQ0EwHhcNMTkwODE2MTMyODUxWhcNMjkwODEzMTMyODUxWjAuMSwwKgYDVQQD DCNwb255dG93biBFZERTQSBsZXZlbCAyIGludGVybWVkaWF0ZTAqMAUGAytlcAMh AD4h3t0UCoMDGgIq4UW4P5zDngsY4vy1pE3wzLPFI4Vdo14wXDAdBgNVHQ4EFgQU FxIwU406tG3CsPWkHWqfuUT48aswIAYDVR0lAQH/BBYwFAYIKwYBBQUHAwEGCCsG AQUFBwMCMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgH+MAUGAytlcANBAAZFvMek Z71I8CXsBmx/0E6Weoaan9mJHgKqgQdK4w4h4dRg6DjNG957IbrLFO3vZduBMnna qHP3xTFF+11Eyg8= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIBTDCB/6ADAgECAhRXcvbYynz4+usVvPtJp++sBUih3TAFBgMrZXAwHDEaMBgG A1UEAwwRcG9ueXRvd24gRWREU0EgQ0EwHhcNMTkwODE2MTMyODUwWhcNMjkwODEz MTMyODUwWjAcMRowGAYDVQQDDBFwb255dG93biBFZERTQSBDQTAqMAUGAytlcAMh AIE4tLweIfcBGfhPqyXFp5pjVxjaiKk+9fTbRy46jAFKo1MwUTAdBgNVHQ4EFgQU z5b9HjkOxffbtCZhWGg+bnxuD6wwHwYDVR0jBBgwFoAUz5b9HjkOxffbtCZhWGg+ bnxuD6wwDwYDVR0TAQH/BAUwAwEB/zAFBgMrZXADQQBNlt7z4bZ7KhzecxZEe3i5 lH9MRqbpP9Rg4HyzAJfTzFGT183HoJiISdPLbxwMn0KaqSGlVe+9GgNKswoaRAwH -----END CERTIFICATE----- -----BEGIN ZEN MASTER JOKE----- QSBaZW4gbWFzdGVyIHZpc2l0aW5nIE5ldyBZb3JrIENpdHkgZ29lcyB1cCB0byBhIGhvdCBkb2cg dmVuZG9yIGFuZCBzYXlzLCAiTWFrZSBtZSBvbmUgd2l0aCBldmVyeXRoaW5nLiIKVGhlIGhvdCBk b2cgdmVuZG9yIGZpeGVzIGEgaG90IGRvZyBhbmQgaGFuZHMgaXQgdG8gdGhlIFplbiBtYXN0ZXIs IHdobyBwYXlzIHdpdGggYSAkMjAgYmlsbC4KVGhlIHZlbmRvciBwdXRzIHRoZSBiaWxsIGluIHRo ZSBjYXNoIGJveCBhbmQgY2xvc2VzIGl0LiAiRXhjdXNlIG1lLCBidXQgd2hlcmUncyBteSBjaGFu Z2U/IiBhc2tzIHRoZSBaZW4gbWFzdGVyLgpUaGUgdmVuZG9yIHJlc3BvbmRzLCAiQ2hhbmdlIG11 c3QgY29tZSBmcm9tIHdpdGhpbi4iCg== -----END ZEN MASTER JOKE----- -----BEGIN CERTIFICATE----- MIIEnzCCAoegAwIBAgIBezANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9wb255 dG93biBSU0EgQ0EwHhcNMTkwNjA5MTcxNTEyWhcNMjkwNjA2MTcxNTEyWjAsMSow KAYDVQQDDCFwb255dG93biBSU0EgbGV2ZWwgMiBpbnRlcm1lZGlhdGUwggGiMA0G CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCj/tOFeSW3WB+TtuLCR1L/84lZytFw zbpzOTGB1kPEKNbrMsv3lHXm5bHa8Bl3k113k7Hi7OAt/nkMm05s8LcUoovhaG5C G7tjzL+ld1nO74gNS3IQHCzxRdRwIgaDZHyICfBQBfB9/m+9z3yRtOKWJl6i/MT9 HRN6yADW/8gHFlMzRkCKBjIKXehKsu8cbtB+5MukwtXI4rKf9aYXZQOEUn1kEwQJ ZIKBXR0eyloQiZervUE7meRCTBvzXT9VoSEX49/mempp4hnfdHlRNzre4/tphBf1 fRUdpVXZ3DvmzoHdXRVzxx3X5LvDpf7Eb3ViGkXDFwkSfHEhkRnAl4lIzTH/1F25 stmT8a0PA/lCNMrzJBzkLcuem1G1uMHoQZo1f3OpslJ8gHbE9ZlIbIKmpmJS9oop Vh1BH+aOy5doCrF8uOLTQ3d5CqA/EZMGahDHy7IkeNYmG/RXUKNltv+r95gwuRP+ 9UIJ9FTa4REQbIpGWP5XibI6x4LqLTJj+VsCAwEAAaNeMFwwHQYDVR0OBBYEFEKP y8hHZVazpvIsxFcGo4YrkEkwMCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMBBggrBgEF BQcDAjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB/jANBgkqhkiG9w0BAQsFAAOC AgEAMzTRDLBExVFlw98AuX+pM+/R2Gjw5KFHvSYLKLbMRfuuZK1yNYYaYtNrtF+V a53OFgaZj56o7tXc2PB8kw4MELD0ViR8Do2bvZieFcEe4DwhdjGCjuLehVLT29qI 7T3N/JkJ5daemKZcRB6Ne0F4+6QlVVNck28HUKbQThl88RdwLUImmSAfgKSt6uJ5 wlH7wiYQR2vPXwSuEYzwot+L/91eBwuQr4Lovx9+TCKTbwQOKYjX4KfcOOQ1rx0M IMrvwWqnabc6m1F0O6//ibL0kuFkJYEgOH2uJA12FBHO+/q2tcytejkOWKWMJj6Y 2etwIHcpzXaEP7fZ75cFGqcE3s7XGsweBIPLjMP1bKxEcFKzygURm/auUuXBCFBl E16PB6JEAeCKe/8VFeyucvjPuQDWB49aq+r2SbpbI4IeZdz/QgEIOb0MpwStrvhH 9f/DtGMbjvuAEkRoOorK4m5k4GY3LsWTR2bey27AXk8N7pKarpu2N7ChBPm+EV0Y H+tAI/OfdZuNUCES00F5UAFdU8zBUZo19ao2ZqfEADimE7Epk2s0bUe4GSqEXJp6 68oVSMhZmMf/RCSNlr97f34sNiUA1YJ0JbCRZmw8KWNm9H1PARLbrgeRBZ/k31Li WLDr3fiEVk7SGxj3zo94cS6AT55DyXLiSD/bFmL1QXgZweA= -----END CERTIFICATE----- -----BEGIN NOT SUPPORTED----- This is not required to be valid base64, it should be exactly ignored. -----END NOT SUPPORTED----- -----BEGIN EC PARAMETERS----- BggqhkjOPQMBBw== -----END EC PARAMETERS----- -----BEGIN EC PRIVATE KEY----- MHcCAQEEIMaA7bFrjDDBSik057bIKo7UQXJZNwLK9AjYZQ7yIWFloAoGCCqGSM49 AwEHoUQDQgAExu0Z/w8nQJZAXeOXOnZun9HiZscY9H/KwYcXpeZHu+f9P9mOUEkH 5Z0av+JKtzhFspjngNLVgWcjlA1L5AJLdA== -----END EC PRIVATE KEY----- -----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgxoDtsWuMMMFKKTTn tsgqjtRBclk3Asr0CNhlDvIhYWWhRANCAATG7Rn/DydAlkBd45c6dm6f0eJmxxj0 f8rBhxel5ke75/0/2Y5QSQflnRq/4kq3OEWymOeA0tWBZyOUDUvkAkt0 -----END PRIVATE KEY----- -----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAqIh8FTj9DIgI8DAoCBh+ 6UXOfaWkvNaGZx2GwXl4WDAa/ZSE5/8ofg/6V59bmk9yry57UR4F+blscBvE4g3U dTvWJOBRD900l21vwpDLKzZguyGOCmKwJu3vCnAQKzBRXW5sDgvO67GeU6kpaic9 LYPYnYaoxCRTYTZu0wy72rW5G0Fe8Gg/duJmUH7vqGIZupTTVzIBMbFVPBMJqprT MStDhaUL0JiAz0ZgTeNLRIBZWV9mY4PG3rZtbV0BZGR1ipAq9xfgqJcURCcKl/ZT UMtzvgk8s5hYkIJX0ZL3qsfdM4BMgIFhHq/GisQKbbu9kWldBrxQylOwa6r0m3Jv KJX2ViDSORndaCz2sppmVx5HDHnj+Bw381yawphnpumP3BJK4iof//uYKvfdc4RC y2EXL8PYPsT5DMB0jaBt92ytR5sLhn8Sl9Hk0buN4IjrYPISrdhS45xQXUqxcp9O 9hcU+rSaQyZ45cj+VlWhKq8MDvGvaAONBFSEh01mnUwoJObsAZNVFVtuOkwAli0F kGouMycQY1BGscpdC516Nya361Hk/ICyby2Y0BJrrVGaSM6poXH9yEjglzAdtSDb Cvhn/zlAI5ltm4Nv2qTgYBDns5JRGVhBym6RbbZ1C/KfCgn0hOxiw3N7AN4d0K5n LI6p7U9RnNVbWgbqsuoxBtkCAwEAAQ== -----END PUBLIC KEY----- -----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQC1Dt8tFmGS76ciuNXvk/QRrV8wCcArWxvl7Ku0aSQXgcFBAav6 P5RD8b+dC9DihSu/r+6OOfjsAZ6oKCq3OTUfmoUhLpoBomxPczJgLyyLD+nQkp5q B1Q3WB6ACL/HJRRjJEIn7lc5u1FVBGbiCAHKMiaP4BDSym8oqimKC6uiaQIDAQAB AoGAGKmY7sxQqDIqwwkIYyT1Jv9FqwZ4/a7gYvZVATMdLnKHP3KZ2XGVoZepcRvt 7R0Us3ykcw0kgglKcj9eaizJtnSuoDPPwt53mDypPN2sU3hZgyk2tPgr49DB3MIp fjoqw4RL/p60ksgGXbDEqBuXqOtH5i61khWlMj+BWL9VDq0CQQDaELWPQGjgs+7X /QyWMJwOF4FXE4jecH/CcPVDB9K1ukllyC1HqTNe44Sp2bIDuSXXWb8yEixrEWBE ci2CSSjXAkEA1I4W9IzwEmAeLtL6VBip9ks52O0JKu373/Xv1F2GYdhnQaFw7IC6 1lSzcYMKGTmDuM8Cj26caldyv19Q0SPmvwJAdRHjZzS9GWWAJJTF3Rvbq/USix0B renXrRvXkFTy2n1YSjxdkstTuO2Mm2M0HquXlTWpX8hB8HkzpYtmwztjoQJAECKl LXVReCOhxu4vIJkqtc6qGoSL8J1WRH8X8KgU3nKeDAZkWx++jyyo3pIS/y01iZ71 U8wSxaPTyyFCMk4mYwJBALjg7g8yDy1Lg9GFfOZvAVzPjqD28jZh/VJsDz9IhYoG z89iHWHkllOisbOm+SeynVC8CoFXmJPc26U65GcjI18= -----END RSA PRIVATE KEY----- -----BEGIN PRIVATE KEY----- MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALUO3y0WYZLvpyK4 1e+T9BGtXzAJwCtbG+Xsq7RpJBeBwUEBq/o/lEPxv50L0OKFK7+v7o45+OwBnqgo Krc5NR+ahSEumgGibE9zMmAvLIsP6dCSnmoHVDdYHoAIv8clFGMkQifuVzm7UVUE ZuIIAcoyJo/gENLKbyiqKYoLq6JpAgMBAAECgYAYqZjuzFCoMirDCQhjJPUm/0Wr Bnj9ruBi9lUBMx0ucoc/cpnZcZWhl6lxG+3tHRSzfKRzDSSCCUpyP15qLMm2dK6g M8/C3neYPKk83axTeFmDKTa0+Cvj0MHcwil+OirDhEv+nrSSyAZdsMSoG5eo60fm LrWSFaUyP4FYv1UOrQJBANoQtY9AaOCz7tf9DJYwnA4XgVcTiN5wf8Jw9UMH0rW6 SWXILUepM17jhKnZsgO5JddZvzISLGsRYERyLYJJKNcCQQDUjhb0jPASYB4u0vpU GKn2SznY7Qkq7fvf9e/UXYZh2GdBoXDsgLrWVLNxgwoZOYO4zwKPbpxqV3K/X1DR I+a/AkB1EeNnNL0ZZYAklMXdG9ur9RKLHQGt6detG9eQVPLafVhKPF2Sy1O47Yyb YzQeq5eVNalfyEHweTOli2bDO2OhAkAQIqUtdVF4I6HG7i8gmSq1zqoahIvwnVZE fxfwqBTecp4MBmRbH76PLKjekhL/LTWJnvVTzBLFo9PLIUIyTiZjAkEAuODuDzIP LUuD0YV85m8BXM+OoPbyNmH9UmwPP0iFigbPz2IdYeSWU6Kxs6b5J7KdULwKgVeY k9zbpTrkZyMjXw== -----END PRIVATE KEY----- -----BEGIN X509 CRL----- MIICiTBzAgEBMA0GCSqGSIb3DQEBCwUAMBoxGDAWBgNVBAMMD3Bvbnl0b3duIFJT QSBDQRcNMjMwNjI3MDgyODEyWhcNMjMwNzI3MDgyODEyWjAVMBMCAgHIFw0yMzA2 MjcwODI3NTlaoA4wDDAKBgNVHRQEAwIBAjANBgkqhkiG9w0BAQsFAAOCAgEAP6EX 9+hxjx/AqdBpynZXjGkEqigBcLcJ2PADOXngdQI1jC0WuYnZymUimemeULtt8X+1 ai2KxAuF1m4NEKZsrGKvO+/9s/X1xbGroyHSAMKtZafFopFpoB2aNbYlx7yIyLtD BBIZIF50g20U+3izqpHutTD10itdk9TLsSceJHpwTkNJtaWMkOfBV28nKzEzVutV f6WzRpURGzui6nQy7aIqImeanpoBoz323psMfC32U0uMBCZltyHNqsX58/2Uhucx 0IPnitNuhv4scCPf/jeRfGIWDrTf1/25LDzRxyg1S4z9aa+3GM4O3dqy4igZEhgT q3pjlJ2hUL5E0oqbZDIQD1SN8UUUv5N2AjwZcxVBNnYeGyuO7YpTBYiu62o73iL2 CjgElfaMq/9hEr9GR9kJozh7VTxtQPbnr4DiucQvhv8o/A1z+zkC0gj8iCLFtDbO 8bvDowcdle9LKkrLaBe6sO+fSH/I9Wj8vrEJKsuwaEraIdEaq2VrIMUPEWN0/MH9 vTwHyadGSMK4CWtrn9fCAgSLw6NX74D7Cx1IaS8vstMjpeUqOS0dk5ThiW47HceB DTko7rV5N+RGH2nW1ynLoZKCJQqqZcLilFMyKPui3jifJnQlMFi54jGVgg/D6UQn 7dA7wb2ux/1hSiaarp+mi7ncVOyByz6/WQP8mfc= -----END X509 CRL----- -----BEGIN CERTIFICATE REQUEST----- MIIC+zCCAeMCAQAwfDELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRQwEgYD VQQHDAtTYW4gQW50b25pbzEdMBsGA1UECgwURXhhbXBsZSBPcmdhbml6YXRpb24x EjAQBgNVBAsMCU1hcmtldGluZzEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEiMA0G CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwJw0BcbuqZyiABlmSYTi1tcr8DB0D NcTtzsYe7tlyIKd3mEs+u6Pi3rEQGvOw5eo6CmWII2qmVOqJ2f6gjl2lZJ5DUE6B I+NNE73zfFMrttUtI8X4ChnE4rrGqqUsSvYz1YVU0KiJ/00YMjEY5XlJYYa9FgfZ sUrhj4aCFdXS6CU9jueRr+udEBElDcgTS9+pB+LFhVfUMTdxnJ3BcT4ZDDqODH3/ 5RAgq03dhRpkkaVIg2uVKTBDoM3hs8T1zIxLM7hItaZzMv4uHdfI8y+BdHrePT33 BoTlocvTEZEqqXEdw2kUd4PDgyUTjFE3b9OeLk0Ju5GRvuCW3UcS5gFvAgMBAAGg OjA4BgkqhkiG9w0BCQ4xKzApMCcGA1UdEQQgMB6CC2V4YW1wbGUuY29tgg9mb28u ZXhhbXBsZS5jb20wDQYJKoZIhvcNAQELBQADggEBACWsgxPw13QUpoCJOqvp8B1A EfsxRJITSROmukV3ZQycPT76Y3GVrM9sGjO8p13J/CVw2KcWc9xmgHF0MdvPNhnW OB6Y07hVpNnJVHb1KglOkNkTy6sVDtnZHg2klqGSyzIbwZ9R3JG8HtRdkceIrm3D gdiZyLcf1VDCCUGaskEi2CsggCQQJNyGi+8BSQ8MPKm/m0KrSchGQ157eWCCjopz f5GQe2UGOg5T7g8+S4GdECMwkMlTGUwlAM6LuOG/NZqP528PCAYQv0eOYdSwALQT GwTyU4AZ9y1uBFuaFxABew9GbDEtNY/XHTF8308edUwGBk6jfD+UuTeEwRZGs9E= -----END CERTIFICATE REQUEST----- -----BEGIN ECHCONFIG----- AD7+DQA65wAgACA8wVN2BtscOl3vQheUzHeIkVmKIiydUhDCliA4iyQRCwAEAAEA AQALZXhhbXBsZS5jb20AAA== -----END ECHCONFIG----- ... that's all folks! pki-types-v-1.11.0/tests/dns_name.rs000066400000000000000000000277561474567472200173210ustar00rootroot00000000000000use rustls_pki_types::DnsName; // (name, is_valid) static DNS_NAME_VALIDITY: &[(&[u8], bool)] = &[ (b"a", true), (b"a.b", true), (b"a.b.c", true), (b"a.b.c.d", true), // Hyphens, one component. (b"-", false), (b"-a", false), (b"a-", false), (b"a-b", true), // Hyphens, last component. (b"a.-", false), (b"a.-a", false), (b"a.a-", false), (b"a.a-b", true), // Hyphens, not last component. (b"-.a", false), (b"-a.a", false), (b"a-.a", false), (b"a-b.a", true), // Underscores, one component. (b"_", true), // TODO: Perhaps this should be rejected for '_' being sole character?. (b"_a", true), // TODO: Perhaps this should be rejected for '_' being 1st? (b"a_", true), (b"a_b", true), // Underscores, last component. (b"a._", true), // TODO: Perhaps this should be rejected for '_' being sole character?. (b"a._a", true), // TODO: Perhaps this should be rejected for '_' being 1st? (b"a.a_", true), (b"a.a_b", true), // Underscores, not last component. (b"_.a", true), // TODO: Perhaps this should be rejected for '_' being sole character?. (b"_a.a", true), (b"a_.a", true), (b"a_b.a", true), // empty labels (b"", false), (b".", false), (b"a", true), (b".a", false), (b".a.b", false), (b"..a", false), (b"a..b", false), (b"a...b", false), (b"a..b.c", false), (b"a.b..c", false), (b".a.b.c.", false), // absolute names (b"a.", true), (b"a.b.", true), (b"a.b.c.", true), // absolute names with empty label at end (b"a..", false), (b"a.b..", false), (b"a.b.c..", false), (b"a...", false), // Punycode (b"xn--", false), (b"xn--.", false), (b"xn--.a", false), (b"a.xn--", false), (b"a.xn--.", false), (b"a.xn--.b", false), (b"a.xn--.b", false), (b"a.xn--\0.b", false), (b"a.xn--a.b", true), (b"xn--a", true), (b"a.xn--a", true), (b"a.xn--a.a", true), (b"\xc4\x95.com", false), // UTF-8 ĕ (b"xn--jea.com", true), // punycode ĕ (b"xn--\xc4\x95.com", false), // UTF-8 ĕ, malformed punycode + UTF-8 mashup // Surprising punycode (b"xn--google.com", true), // 䕮䕵䕶䕱.com (b"xn--citibank.com", true), // 岍岊岊岅岉岎.com (b"xn--cnn.com", true), // 䁾.com (b"a.xn--cnn", true), // a.䁾 (b"a.xn--cnn.com", true), // a.䁾.com (b"1.2.3.4", false), // IPv4 address (b"1::2", false), // IPV6 address // whitespace not allowed anywhere. (b" ", false), (b" a", false), (b"a ", false), (b"a b", false), (b"a.b 1", false), (b"a\t", false), // Nulls not allowed (b"\0", false), (b"a\0", false), (b"example.org\0.example.com", false), // Hi Moxie! (b"\0a", false), (b"xn--\0", false), // Allowed character set (b"a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z", true), (b"A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z", true), (b"0.1.2.3.4.5.6.7.8.9.a", true), // "a" needed to avoid numeric last label (b"a-b", true), // hyphen (a label cannot start or end with a hyphen) // An invalid character in various positions (b"!", false), (b"!a", false), (b"a!", false), (b"a!b", false), (b"a.!", false), (b"a.a!", false), (b"a.!a", false), (b"a.a!a", false), (b"a.!a.a", false), (b"a.a!.a", false), (b"a.a!a.a", false), // Various other invalid characters (b"a!", false), (b"a@", false), (b"a#", false), (b"a$", false), (b"a%", false), (b"a^", false), (b"a&", false), (b"a*", false), (b"a(", false), (b"a)", false), // last label can't be fully numeric (b"1", false), (b"a.1", false), // other labels can be fully numeric (b"1.a", true), (b"1.2.a", true), (b"1.2.3.a", true), // last label can be *partly* numeric (b"1a", true), (b"1.1a", true), (b"1-1", true), (b"a.1-1", true), (b"a.1-a", true), // labels cannot start with a hyphen (b"-", false), (b"-1", false), // labels cannot end with a hyphen (b"1-", false), (b"1-.a", false), (b"a-", false), (b"a-.a", false), (b"a.1-.a", false), (b"a.a-.a", false), // labels can contain a hyphen in the middle (b"a-b", true), (b"1-2", true), (b"a.a-1", true), // multiple consecutive hyphens allowed (b"a--1", true), (b"1---a", true), (b"a-----------------b", true), // Wildcard specifications are not valid reference names. (b"*.a", false), (b"a*", false), (b"a*.", false), (b"a*.a", false), (b"a*.a.", false), (b"*.a.b", false), (b"*.a.b.", false), (b"a*.b.c", false), (b"*.a.b.c", false), (b"a*.b.c.d", false), // Multiple wildcards. (b"a**.b.c", false), (b"a*b*.c.d", false), (b"a*.b*.c", false), // Wildcards not in the first label. (b"a.*", false), (b"a.*.b", false), (b"a.b.*", false), (b"a.b*.c", false), (b"*.b*.c", false), (b".*.a.b", false), (b".a*.b.c", false), // Wildcards not at the end of the first label. (b"*a.b.c", false), (b"a*b.c.d", false), // Wildcards and IDNA prefix. (b"x*.a.b", false), (b"xn*.a.b", false), (b"xn-*.a.b", false), (b"xn--*.a.b", false), (b"xn--w*.a.b", false), // Redacted labels from RFC6962bis draft 4 // https://tools.ietf.org/html/draft-ietf-trans-rfc6962-bis-04#section-3.2.2 (b"(PRIVATE).foo", false), // maximum label length is 63 characters (b"123456789012345678901234567890123456789012345678901234567890abc", true), (b"123456789012345678901234567890123456789012345678901234567890abcd", false), // maximum total length is 253 characters (b"12345678901234567890123456789012345678901234567890.12345678901234567890123456789012345678901234567890.12345678901234567890123456789012345678901234567890.12345678901234567890123456789012345678901234567890.123456789012345678901234567890123456789012345678a", true), (b"12345678901234567890123456789012345678901234567890.12345678901234567890123456789012345678901234567890.12345678901234567890123456789012345678901234567890.12345678901234567890123456789012345678901234567890.1234567890123456789012345678901234567890123456789a", false), ]; // (IP address, is valid DNS name). The comments here refer to the validity of // the string as an IP address, not as a DNS name validity. static IP_ADDRESS_DNS_VALIDITY: &[(&[u8], bool)] = &[ (b"", false), (b"1", false), (b"1.2", false), (b"1.2.3", false), (b"1.2.3.4", false), (b"1.2.3.4.5", false), (b"1.2.3.4a", true), // a DNS name! (b"a.2.3.4", false), // not even a DNS name! (b"1::2", false), // IPv6 address // Whitespace not allowed (b" 1.2.3.4", false), (b"1.2.3.4 ", false), (b"1 .2.3.4", false), (b"\n1.2.3.4", false), (b"1.2.3.4\n", false), // Nulls not allowed (b"\x00", false), (b"\x001.2.3.4", false), (b"1.2.3.4\x00", false), (b"1.2.3.4\x00.5", false), // Range (b"0.0.0.0", false), (b"255.255.255.255", false), (b"256.0.0.0", false), (b"0.256.0.0", false), (b"0.0.256.0", false), (b"0.0.0.256", false), (b"999.0.0.0", false), (b"9999999999999999999.0.0.0", false), // All digits allowed (b"0.1.2.3", false), (b"4.5.6.7", false), (b"8.9.0.1", false), // Leading zeros not allowed (b"01.2.3.4", false), (b"001.2.3.4", false), (b"00000000001.2.3.4", false), (b"010.2.3.4", false), (b"1.02.3.4", false), (b"1.2.03.4", false), (b"1.2.3.04", false), // Empty components (b".2.3.4", false), (b"1..3.4", false), (b"1.2..4", false), (b"1.2.3.", false), // Too many components (b"1.2.3.4.5", false), (b"1.2.3.4.5.6", false), (b"0.1.2.3.4", false), (b"1.2.3.4.0", false), // Leading/trailing dot (b".1.2.3.4", false), (b"1.2.3.4.", false), // Other common forms of IPv4 address // http://en.wikipedia.org/wiki/IPv4#Address_representations (b"192.0.2.235", false), // dotted decimal (control value) (b"0xC0.0x00.0x02.0xEB", true), // dotted hex - actually a DNS name! (b"0301.0000.0002.0353", false), // dotted octal (b"0xC00002EB", true), // non-dotted hex, actually a DNS name! (b"3221226219", false), // non-dotted decimal (b"030000001353", false), // non-dotted octal (b"192.0.0002.0xEB", true), // mixed, actually a DNS name! (b"1234", false), (b"1234:5678", false), (b"1234:5678:9abc", false), (b"1234:5678:9abc:def0", false), (b"1234:5678:9abc:def0:1234:", false), (b"1234:5678:9abc:def0:1234:5678:", false), (b"1234:5678:9abc:def0:1234:5678:9abc:", false), (b"1234:5678:9abc:def0:1234:5678:9abc:def0", false), (b"1234:5678:9abc:def0:1234:5678:9abc:def0:", false), (b":1234:5678:9abc:def0:1234:5678:9abc:def0", false), (b"1234:5678:9abc:def0:1234:5678:9abc:def0:0000", false), // Valid contractions (b"::1", false), (b"::1234", false), (b"1234::", false), (b"1234::5678", false), (b"1234:5678::abcd", false), (b"1234:5678:9abc:def0:1234:5678:9abc::", false), // Contraction in full IPv6 addresses not allowed (b"::1234:5678:9abc:def0:1234:5678:9abc:def0", false), // start (b"1234:5678:9abc:def0:1234:5678:9abc:def0::", false), // end (b"1234:5678::9abc:def0:1234:5678:9abc:def0", false), // interior // Multiple contractions not allowed (b"::1::", false), (b"::1::2", false), (b"1::2::", false), // Colon madness! (b":", false), (b"::", false), (b":::", false), (b"::::", false), (b":::1", false), (b"::::1", false), (b"1:::2", false), (b"1::::2", false), (b"1:2:::", false), (b"1:2::::", false), (b"::1234:", false), (b":1234::", false), (b"01234::", false), // too many digits, even if zero (b"12345678::", false), // too many digits or missing colon // uppercase (b"ABCD:EFAB::", false), // miXeD CAse (b"aBcd:eFAb::", false), // IPv4-style (b"::2.3.4.5", false), (b"1234::2.3.4.5", false), (b"::abcd:2.3.4.5", false), (b"1234:5678:9abc:def0:1234:5678:252.253.254.255", false), (b"1234:5678:9abc:def0:1234::252.253.254.255", false), (b"1234::252.253.254", false), (b"::252.253.254", false), (b"::252.253.254.300", false), (b"1234::252.253.254.255:", false), (b"1234::252.253.254.255:5678", false), // Contractions that don't contract (b"::1234:5678:9abc:def0:1234:5678:9abc:def0", false), (b"1234:5678:9abc:def0:1234:5678:9abc:def0::", false), (b"1234:5678:9abc:def0::1234:5678:9abc:def0", false), (b"1234:5678:9abc:def0:1234:5678::252.253.254.255", false), // With and without leading zeros (b"::123", false), (b"::0123", false), (b"::012", false), (b"::0012", false), (b"::01", false), (b"::001", false), (b"::0001", false), (b"::0", false), (b"::00", false), (b"::000", false), (b"::0000", false), (b"::01234", false), (b"::00123", false), (b"::000123", false), // Trailing zero (b"::12340", false), // Whitespace (b" 1234:5678:9abc:def0:1234:5678:9abc:def0", false), (b"\t1234:5678:9abc:def0:1234:5678:9abc:def0", false), (b"\t1234:5678:9abc:def0:1234:5678:9abc:def0\n", false), (b"1234 :5678:9abc:def0:1234:5678:9abc:def0", false), (b"1234: 5678:9abc:def0:1234:5678:9abc:def0", false), (b":: 2.3.4.5", false), (b"1234::252.253.254.255 ", false), (b"1234::252.253.254.255\n", false), (b"1234::252.253. 254.255", false), // Nulls (b"\x00", false), (b"::1\x00:2", false), (b"::1\x00", false), (b"::1.2.3.4\x00", false), (b"::1.2\x002.3.4", false), ]; #[test] fn dns_name_ref_try_from_ascii_test() { for &(s, is_valid) in DNS_NAME_VALIDITY .iter() .chain(IP_ADDRESS_DNS_VALIDITY.iter()) { assert_eq!( DnsName::try_from(s).is_ok(), is_valid, "DnsNameRef::try_from_ascii_str failed for \"{:?}\"", s ); } } pki-types-v-1.11.0/tests/key_type.rs000066400000000000000000000032161474567472200173470ustar00rootroot00000000000000use rustls_pki_types::PrivateKeyDer; #[test] fn test_private_key_from_der() { const NIST_P256_KEY_SEC1: &[u8] = include_bytes!("../tests/keys/nistp256key.der"); const NIST_P384_KEY_SEC1: &[u8] = include_bytes!("../tests/keys/nistp384key.der"); const NIST_P521_KEY_SEC1: &[u8] = include_bytes!("../tests/keys/nistp521key.der"); for bytes in [NIST_P256_KEY_SEC1, NIST_P384_KEY_SEC1, NIST_P521_KEY_SEC1] { assert!(matches!( PrivateKeyDer::try_from(bytes).unwrap(), PrivateKeyDer::Sec1(_) )); } const RSA_2048_KEY_PKCS1: &[u8] = include_bytes!("../tests/keys/rsa2048key.pkcs1.der"); assert!(matches!( PrivateKeyDer::try_from(RSA_2048_KEY_PKCS1).unwrap(), PrivateKeyDer::Pkcs1(_) )); const NIST_P256_KEY_PKCS8: &[u8] = include_bytes!("../tests/keys/nistp256key.pkcs8.der"); const NIST_P384_KEY_PKCS8: &[u8] = include_bytes!("../tests/keys/nistp384key.pkcs8.der"); const NIST_P521_KEY_PKCS8: &[u8] = include_bytes!("../tests/keys/nistp521key.pkcs8.der"); const RSA_2048_KEY_PKCS8: &[u8] = include_bytes!("../tests/keys/rsa2048key.pkcs8.der"); const RSA_4096_KEY: &[u8] = include_bytes!("../tests/keys/rsa4096key.pkcs8.der"); const ED25519_KEY: &[u8] = include_bytes!("../tests/keys/edd25519_v2.der"); const PKCS8_KEYS: &[&[u8]] = &[ NIST_P256_KEY_PKCS8, NIST_P384_KEY_PKCS8, NIST_P521_KEY_PKCS8, RSA_2048_KEY_PKCS8, RSA_4096_KEY, ED25519_KEY, ]; for &bytes in PKCS8_KEYS { assert!(matches!( PrivateKeyDer::try_from(bytes).unwrap(), PrivateKeyDer::Pkcs8(_) )); } } pki-types-v-1.11.0/tests/keys/000077500000000000000000000000001474567472200161215ustar00rootroot00000000000000pki-types-v-1.11.0/tests/keys/edd25519_v2.der000066400000000000000000000001251474567472200203640ustar00rootroot000000000000000S0+ep" -#xJ^揠ge1.FT7Q\#!}{l΄OJpwW k@pki-types-v-1.11.0/tests/keys/eddsakey.der000066400000000000000000000000601474567472200204020ustar00rootroot000000000000000.0+ep" f)='. _lAݘQlpki-types-v-1.11.0/tests/keys/nistp256key.der000066400000000000000000000001711474567472200207170ustar00rootroot000000000000000w ƀk0J)4*ArY7e!ae *H=DB'@@]:vnfG?َPIJ8EՁg# KKtpki-types-v-1.11.0/tests/keys/nistp256key.pkcs8.der000066400000000000000000000002121474567472200217420ustar00rootroot0000000000000000*H=*H=m0k ƀk0J)4*ArY7e!aeDB'@@]:vnfG?َPIJ8EՁg# KKtpki-types-v-1.11.0/tests/keys/nistp384key.der000066400000000000000000000002471474567472200207250ustar00rootroot0000000000000000 h+Q" Rwo4 s{*6ހso!E9+"dbU9J<=a-m̼BVMVztvbԈfQa%D{I認ZS,9z8gk0ryo]U A.Cpki-types-v-1.11.0/tests/keys/nistp384key.pkcs8.der000066400000000000000000000002711474567472200217510ustar00rootroot0000000000000000*H=+"00 h+Q" Rwo4 s{*6ހso!E9dbU9J<=a-m̼BVMVztvbԈfQa%D{I認ZS,9z8gk0ryo]U A.Cpki-types-v-1.11.0/tests/keys/nistp521key.der000066400000000000000000000003371474567472200207160ustar00rootroot000000000000000BwAh6WKf+K3 T(6(?b@:ʯXKM#wT [=C F뤓 eA>^gpki-types-v-1.11.0/tests/keys/nistp521key.pkcs8.der000066400000000000000000000003611474567472200217420ustar00rootroot0000000000000000*H=+#0BwAh6WKf+K3 T(6(?b@:ʯXKM#wT [=C F뤓 eA>^gpki-types-v-1.11.0/tests/keys/rsa2048key.pkcs1.der000066400000000000000000000022461474567472200214550ustar00rootroot000000000000000ЁNZc)/)vO'hX9Pzt6w|x!5KW nB8Z*,`4z~qA-qG,<{s܍Q]E@@9BU&V!MZ\>'{l kRhE6ʈ]Rxؽj%)//^G{t"7S#?@;9l: 8o6q!K)טd?Z  # bk@SJ5rtiHPk%E.(X,T9TRV"nԑ&Q[4g45>3^M8DF%Ci8D*޹U^xUV-I:p>9eJdcS?D>FXȣL3pQFqR5M۠Fv~]OFx>^ Rf/(TvE53pˆGQzhN_4.9?os ]yCulJ̢Uev;hiXڏ%s%<+O ;o\EDFbCIIa#61qcbhAJw_~0!HFѮt"003 0>dwNbX5HDg s@HD{1L%AajsT[i'TR/Dd nu_RcbFd`H d``V‘U ;l~,՛ L{ d \[ǚ|{FZG6Q$!E̼AJb<1@(vqiBFaW25٣scl\k>&ry6Ej3eE0q#dՉ^ܓE{euG4M7)3isL'%Ha^K$o*(Gk8Yoe3yO,}䮏Xݚ!7E%H wycRT9v'{l kRhE6ʈ]Rxؽj%)//^G{t"7S#?@;9l: 8o6q!K)טd?Z  # bk@SJ5rtiHPk%E.(X,T9TRV"nԑ&Q[4g45>3^M8DF%Ci8D*޹U^xUV-I:p>9eJdcS?D>FXȣL3pQFqR5M۠Fv~]OFx>^ Rf/(TvE53pˆGQzhN_4.9?os ]yCulJ̢Uev;hiXڏ%s%<+O ;o\EDFbCIIa#61qcbhAJw_~0!HFѮt"003 0>dwNbX5HDg s@HD{1L%AajsT[i'TR/Dd nu_RcbFd`H d``V‘U ;l~,՛ L{ d \[ǚ|{FZG6Q$!E̼AJb<1@(vqiBFaW25٣scl\k>&ry6Ej3eE0q#dՉ^ܓE{euG4M7)3isL'%Ha^K$o*(Gk8Yoe3yO,}䮏Xݚ!7E%H wycRT9vl٤py:w ,1r67:MS1'u-7:.\Jwq$+֧VU v|3_^2膉@c:#+wAHC @ʐX!Gfc%z9:`AEz:Cn늷3{6fYs&gs ;S1vͼiHƞt?4sm_HqWYFu扫]13dKSzwt,TMwcI)XϪؖ[gf$_{C c]aZ K!YYX8gסc53V ] t(Gy3 9h|E QQ\pmW[8haf{CM Sn@gmElyC f2&oD+uKd8^gJ(eM󪿿qvGSБdA<Yg|9e+`ta`>KDDҢuXU~UH¦VӼBYcߛӏ>?nEÚOo  |1/&Dz fY]6I"a;Qn"ɦ :$|GNʩzDY;_ :ٴ *MKK'htɊaUfd鳫W8l Zl3! MM|?xѓWC 5KʜPPf5LT>z]0~2Z{7"M_IHk?1MvV CbpU3Ic㯗b6Kflrۋ>]S%< Ng@~V%(xpưgKI}n ś1BOsJ$Çv9UL9'#նOÞWJPjNH{C&}_y"zr}HKܺx=ޘ*bxĴYRtes >k)˶Yv/^GhM]t(l)tA>Ͳcgl}AfkSPE!I}o)I yTe/ٴ|o:Sd6Ss$20ԃ+7 ?H8n$at"tIhw#῀|I6Ryj5-e|rʸXTp17Ifpki-types-v-1.11.0/tests/pem.rs000066400000000000000000000225741474567472200163070ustar00rootroot00000000000000#![cfg(feature = "std")] use std::io::Cursor; use rustls_pki_types::pem::PemObject; use rustls_pki_types::{ pem, CertificateDer, CertificateRevocationListDer, CertificateSigningRequestDer, EchConfigListBytes, PrivateKeyDer, PrivatePkcs1KeyDer, PrivatePkcs8KeyDer, PrivateSec1KeyDer, SubjectPublicKeyInfoDer, }; #[test] fn pkcs1_private_key() { let data = include_bytes!("data/zen.pem"); PrivatePkcs1KeyDer::from_pem_slice(data).unwrap(); PrivatePkcs1KeyDer::from_pem_reader(&mut Cursor::new(&data[..])).unwrap(); PrivatePkcs1KeyDer::from_pem_file("tests/data/zen.pem").unwrap(); assert!(matches!( PrivatePkcs1KeyDer::from_pem_file("tests/data/certificate.chain.pem").unwrap_err(), pem::Error::NoItemsFound )); } #[test] fn pkcs8_private_key() { let data = include_bytes!("data/zen.pem"); PrivatePkcs8KeyDer::from_pem_slice(data).unwrap(); PrivatePkcs8KeyDer::from_pem_reader(&mut Cursor::new(&data[..])).unwrap(); PrivatePkcs8KeyDer::from_pem_file("tests/data/zen.pem").unwrap(); assert!(matches!( PrivatePkcs8KeyDer::from_pem_file("tests/data/certificate.chain.pem").unwrap_err(), pem::Error::NoItemsFound )); } #[test] fn sec1_private_key() { let data = include_bytes!("data/zen.pem"); PrivateSec1KeyDer::from_pem_slice(data).unwrap(); PrivateSec1KeyDer::from_pem_reader(&mut Cursor::new(&data[..])).unwrap(); PrivateSec1KeyDer::from_pem_file("tests/data/zen.pem").unwrap(); assert!(matches!( PrivateSec1KeyDer::from_pem_file("tests/data/certificate.chain.pem").unwrap_err(), pem::Error::NoItemsFound )); } #[test] fn any_private_key() { let data = include_bytes!("data/zen.pem"); PrivateKeyDer::from_pem_slice(data).unwrap(); PrivateKeyDer::from_pem_reader(&mut Cursor::new(&data[..])).unwrap(); PrivateKeyDer::from_pem_file("tests/data/zen.pem").unwrap(); for other_file in [ "tests/data/nistp256key.pem", "tests/data/nistp256key.pkcs8.pem", "tests/data/rsa1024.pkcs1.pem", "tests/data/rsa1024.pkcs8.pem", ] { PrivateKeyDer::from_pem_file(other_file).unwrap(); } assert!(matches!( PrivateKeyDer::from_pem_file("tests/data/certificate.chain.pem").unwrap_err(), pem::Error::NoItemsFound )); } #[test] fn no_trailing_newline() { let data = include_bytes!("data/rsa-key-no-trailing-newline.pem"); assert_eq!( PrivatePkcs8KeyDer::pem_slice_iter(data) .collect::, _>>() .unwrap() .len(), 1 ); assert_eq!( PrivatePkcs8KeyDer::pem_file_iter("tests/data/rsa-key-no-trailing-newline.pem") .unwrap() .collect::, _>>() .unwrap() .len(), 1 ); } #[test] fn certificates() { let data = include_bytes!("data/zen.pem"); assert_eq!( CertificateDer::pem_slice_iter(data) .collect::, _>>() .unwrap() .len(), 4 ); assert_eq!( CertificateDer::pem_reader_iter(&mut Cursor::new(&data[..])) .collect::, _>>() .unwrap() .len(), 4 ); assert_eq!( CertificateDer::pem_file_iter("tests/data/zen.pem") .unwrap() .count(), 4 ); assert!(matches!( CertificateDer::from_pem_file("tests/data/crl.pem").unwrap_err(), pem::Error::NoItemsFound )); assert_eq!( CertificateDer::pem_file_iter("tests/data/certificate.chain.pem") .unwrap() .count(), 3 ); } #[test] fn public_keys() { let data = include_bytes!("data/spki.pem"); SubjectPublicKeyInfoDer::from_pem_slice(data).unwrap(); SubjectPublicKeyInfoDer::from_pem_reader(&mut Cursor::new(&data[..])).unwrap(); SubjectPublicKeyInfoDer::from_pem_file("tests/data/spki.pem").unwrap(); assert!(matches!( SubjectPublicKeyInfoDer::from_pem_file("tests/data/certificate.chain.pem").unwrap_err(), pem::Error::NoItemsFound )); } #[test] fn csr() { let data = include_bytes!("data/zen.pem"); CertificateSigningRequestDer::from_pem_slice(data).unwrap(); CertificateSigningRequestDer::from_pem_reader(&mut Cursor::new(&data[..])).unwrap(); CertificateSigningRequestDer::from_pem_file("tests/data/zen.pem").unwrap(); assert!(matches!( CertificateSigningRequestDer::from_pem_file("tests/data/certificate.chain.pem") .unwrap_err(), pem::Error::NoItemsFound )); } #[test] fn crls() { let data = include_bytes!("data/zen.pem"); assert_eq!( CertificateRevocationListDer::pem_slice_iter(data) .collect::, _>>() .unwrap() .len(), 1 ); assert_eq!( CertificateRevocationListDer::pem_reader_iter(&mut Cursor::new(&data[..])) .collect::, _>>() .unwrap() .len(), 1 ); assert_eq!( CertificateRevocationListDer::pem_file_iter("tests/data/zen.pem") .unwrap() .count(), 1 ); assert!(matches!( CertificateRevocationListDer::pem_file_iter("tests/data/certificate.chain.pem") .unwrap() .count(), 0 )); assert_eq!( CertificateRevocationListDer::pem_file_iter("tests/data/crl.pem") .unwrap() .count(), 1 ); } #[test] fn ech_config() { let data = include_bytes!("data/zen.pem"); EchConfigListBytes::from_pem_slice(data).unwrap(); EchConfigListBytes::from_pem_reader(&mut Cursor::new(&data[..])).unwrap(); EchConfigListBytes::from_pem_file("tests/data/zen.pem").unwrap(); assert!(matches!( EchConfigListBytes::from_pem_file("tests/data/certificate.chain.pem").unwrap_err(), pem::Error::NoItemsFound )); let (config, key) = EchConfigListBytes::config_and_key_from_iter( PemObject::pem_file_iter("tests/data/ech.pem").unwrap(), ) .unwrap(); println!("{config:?} {key:?}"); assert!(matches!( EchConfigListBytes::config_and_key_from_iter( PemObject::pem_file_iter("tests/data/certificate.chain.pem").unwrap(), ) .unwrap_err(), pem::Error::NoItemsFound, )); } #[test] fn certificates_with_binary() { let data = include_bytes!("data/gunk.pem"); assert_eq!( CertificateDer::pem_slice_iter(data) .collect::, _>>() .unwrap() .len(), 2 ); assert_eq!( CertificateDer::pem_reader_iter(&mut Cursor::new(&data[..])) .collect::, _>>() .unwrap() .len(), 2 ); assert_eq!( CertificateDer::pem_file_iter("tests/data/gunk.pem") .unwrap() .count(), 2 ); } #[test] fn parse_in_order() { let data = include_bytes!("data/zen.pem"); let items = <(pem::SectionKind, Vec) as PemObject>::pem_slice_iter(data) .collect::, _>>() .unwrap(); assert_eq!(items.len(), 12); assert!(matches!(items[0], (pem::SectionKind::Certificate, _))); assert!(matches!(items[1], (pem::SectionKind::Certificate, _))); assert!(matches!(items[2], (pem::SectionKind::Certificate, _))); assert!(matches!(items[3], (pem::SectionKind::Certificate, _))); assert!(matches!(items[4], (pem::SectionKind::EcPrivateKey, _))); assert!(matches!(items[5], (pem::SectionKind::PrivateKey, _))); assert!(matches!(items[6], (pem::SectionKind::PublicKey, _))); assert!(matches!(items[7], (pem::SectionKind::RsaPrivateKey, _))); assert!(matches!(items[8], (pem::SectionKind::PrivateKey, _))); assert!(matches!(items[9], (pem::SectionKind::Crl, _))); assert!(matches!(items[10], (pem::SectionKind::Csr, _))); assert!(matches!(items[11], (pem::SectionKind::EchConfigList, _))); } #[test] fn whitespace_prefix() { CertificateDer::from_pem_file("tests/data/whitespace-prefix.crt").unwrap(); } #[test] fn different_line_endings() { let data = include_bytes!("data/mixed-line-endings.crt"); // Ensure non-LF line endings are not lost by mistake, causing the test // to silently regress. let mut contained_unix_ending = false; let mut contained_other_ending = false; for byte in data.iter().copied() { if contained_other_ending && contained_unix_ending { break; } if byte == b'\n' { contained_unix_ending = true; } else if byte == b'\r' { contained_other_ending = true; } } assert!(contained_unix_ending); assert!(contained_other_ending); assert_eq!( CertificateDer::pem_slice_iter(data) .collect::, _>>() .unwrap() .len(), 4 ); assert_eq!( CertificateDer::pem_file_iter("tests/data/mixed-line-endings.crt") .unwrap() .count(), 4 ); } #[test] fn slice_iterator() { let slice = b"hello\n-----BEGIN CERTIFICATE-----\naGk=\n-----END CERTIFICATE-----\ngoodbye\n"; let mut iter = CertificateDer::pem_slice_iter(slice); assert_eq!(iter.remainder(), slice); assert_eq!( iter.next().unwrap().unwrap(), CertificateDer::from(&b"hi"[..]) ); assert_eq!(iter.remainder(), b"goodbye\n"); assert!(iter.next().is_none()); } pki-types-v-1.11.0/tests/server_name.rs000066400000000000000000000027211474567472200200240ustar00rootroot00000000000000use rustls_pki_types::ServerName; fn compile_time_assert_hash() {} fn compile_time_assert_send() {} fn compile_time_assert_sync() {} #[test] fn test_server_name_traits() { compile_time_assert_hash::(); compile_time_assert_send::(); compile_time_assert_sync::(); let a = ServerName::try_from(&b"example.com"[..]).unwrap(); // `Clone` #[allow(clippy::clone_on_copy)] let _ = a.clone(); // TODO: verify the clone is the same as `a`. // TODO: Don't require `alloc` for these. #[cfg(feature = "alloc")] { // `Debug`. assert_eq!(format!("{:?}", &a), "DnsName(\"example.com\")"); } } #[cfg(feature = "alloc")] #[test] fn test_alloc_server_name_traits() { let a_ref = ServerName::try_from(&b"example.com"[..]).unwrap(); let a = a_ref.to_owned(); // `Clone`, `Debug`, `PartialEq`. assert_eq!(&a, &a.clone()); // `Debug`. assert_eq!(format!("{:?}", &a), "DnsName(\"example.com\")"); // PartialEq is case-insensitive assert_eq!( a, ServerName::try_from(&b"Example.Com"[..]) .unwrap() .to_owned() ); // PartialEq isn't completely wrong. assert_ne!( a, ServerName::try_from(&b"fxample.com"[..]) .unwrap() .to_owned() ); assert_ne!( a, ServerName::try_from(&b"example.co"[..]).unwrap().to_owned() ); }