pax_global_header00006660000000000000000000000064146010177700014515gustar00rootroot0000000000000052 comment=4956ba87efdd838c0d8d3b71e8782ee642f09d4f pki-types-v-1.4.1/000077500000000000000000000000001460101777000137105ustar00rootroot00000000000000pki-types-v-1.4.1/.github/000077500000000000000000000000001460101777000152505ustar00rootroot00000000000000pki-types-v-1.4.1/.github/workflows/000077500000000000000000000000001460101777000173055ustar00rootroot00000000000000pki-types-v-1.4.1/.github/workflows/ci.yml000066400000000000000000000100711460101777000204220ustar00rootroot00000000000000name: 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-20.04] # 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@v3 with: persist-credentials: false - name: Install ${{ matrix.rust }} toolchain uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - 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-20.04 steps: - name: Checkout sources uses: actions/checkout@v3 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-20.04, macos-latest, windows-latest] steps: - name: Checkout sources uses: actions/checkout@v3 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@v3 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@v3 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@v3 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 pki-types-v-1.4.1/.gitignore000066400000000000000000000000331460101777000156740ustar00rootroot00000000000000/target /Cargo.lock /.idea pki-types-v-1.4.1/Cargo.toml000066400000000000000000000012641460101777000156430ustar00rootroot00000000000000[package] name = "rustls-pki-types" version = "1.4.1" 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_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.4.1/LICENSE-APACHE000066400000000000000000000251231460101777000156370ustar00rootroot00000000000000 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.4.1/LICENSE-MIT000066400000000000000000000020701460101777000153430ustar00rootroot00000000000000Copyright (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.4.1/README.md000066400000000000000000000055301460101777000151720ustar00rootroot00000000000000# 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), [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 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. The [rustls-pemfile](https://docs.rs/rustls-pemfile) crate can be used to parse PEM files. ## 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.4.1/fuzz/000077500000000000000000000000001460101777000147065ustar00rootroot00000000000000pki-types-v-1.4.1/fuzz/.gitignore000066400000000000000000000000411460101777000166710ustar00rootroot00000000000000target corpus artifacts coverage pki-types-v-1.4.1/fuzz/Cargo.lock000066400000000000000000000030271460101777000166150ustar00rootroot00000000000000# 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.3.1" [[package]] name = "rustls-pki-types-fuzz" version = "0.0.0" dependencies = [ "libfuzzer-sys", "rustls-pki-types", ] pki-types-v-1.4.1/fuzz/Cargo.toml000066400000000000000000000006331460101777000166400ustar00rootroot00000000000000[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 = ".." # 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 pki-types-v-1.4.1/fuzz/fuzz_targets/000077500000000000000000000000001460101777000174355ustar00rootroot00000000000000pki-types-v-1.4.1/fuzz/fuzz_targets/private_key.rs000066400000000000000000000002141460101777000223220ustar00rootroot00000000000000#![no_main] use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { let _ = rustls_pki_types::PrivateKeyDer::try_from(data); }); pki-types-v-1.4.1/src/000077500000000000000000000000001460101777000144775ustar00rootroot00000000000000pki-types-v-1.4.1/src/lib.rs000066400000000000000000000664231460101777000156260ustar00rootroot00000000000000//! 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. //! //! The [rustls-pemfile](https://docs.rs/rustls-pemfile) crate can be used to parse PEM files. //! //! ## 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(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; mod server_name; 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. #[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<'a> PrivateKeyDer<'a> { /// 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(), } } } 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<'a> TryFrom> for PrivateKeyDer<'a> { 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. For more on PEM files, refer to the crate /// documentation. #[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() } } impl<'a> From<&'a [u8]> for PrivatePkcs1KeyDer<'a> { fn from(slice: &'a [u8]) -> Self { Self(Der(DerInner::Borrowed(slice))) } } #[cfg(feature = "alloc")] impl<'a> From> for PrivatePkcs1KeyDer<'a> { fn from(vec: Vec) -> Self { Self(Der(DerInner::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. #[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() } } impl<'a> From<&'a [u8]> for PrivateSec1KeyDer<'a> { fn from(slice: &'a [u8]) -> Self { Self(Der(DerInner::Borrowed(slice))) } } #[cfg(feature = "alloc")] impl<'a> From> for PrivateSec1KeyDer<'a> { fn from(vec: Vec) -> Self { Self(Der(DerInner::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. #[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() } } impl<'a> From<&'a [u8]> for PrivatePkcs8KeyDer<'a> { fn from(slice: &'a [u8]) -> Self { Self(Der(DerInner::Borrowed(slice))) } } #[cfg(feature = "alloc")] impl<'a> From> for PrivatePkcs8KeyDer<'a> { fn from(vec: Vec) -> Self { Self(Der(DerInner::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. #[derive(Clone, Debug, PartialEq, Eq)] pub struct CertificateRevocationListDer<'a>(Der<'a>); 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<'a> From> for CertificateRevocationListDer<'a> { 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. #[derive(Clone, Debug, PartialEq, Eq)] pub struct CertificateSigningRequestDer<'a>(Der<'a>); 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<'a> From> for CertificateSigningRequestDer<'a> { 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. #[derive(Clone, Debug, PartialEq, Eq)] pub struct CertificateDer<'a>(Der<'a>); 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<'a> From> for CertificateDer<'a> { 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())) } } /// 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 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: /// /// ``` /// 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 /// ] /// ); /// ``` #[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 { hex(f, self.0) } } impl Deref for AlgorithmIdentifier { type Target = [u8]; fn deref(&self) -> &Self::Target { self.as_ref() } } /// 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)] 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)] pub struct Der<'a>(DerInner<'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(DerInner::Borrowed(der)) } } impl AsRef<[u8]> for Der<'_> { fn as_ref(&self) -> &[u8] { match &self.0 { #[cfg(feature = "alloc")] DerInner::Owned(vec) => vec.as_ref(), DerInner::Borrowed(slice) => slice, } } } 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(DerInner::Borrowed(slice)) } } #[cfg(feature = "alloc")] impl From> for Der<'static> { fn from(vec: Vec) -> Self { Self(DerInner::Owned(vec)) } } impl fmt::Debug for Der<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { hex(f, self.as_ref()) } } impl PartialEq for Der<'_> { fn eq(&self, other: &Self) -> bool { self.as_ref().eq(other.as_ref()) } } impl Eq for Der<'_> {} #[derive(Clone)] enum DerInner<'a> { #[cfg(feature = "alloc")] Owned(Vec), Borrowed(&'a [u8]), } #[cfg(feature = "alloc")] impl DerInner<'_> { fn into_owned(self) -> DerInner<'static> { DerInner::Owned(match self { Self::Owned(vec) => vec, Self::Borrowed(slice) => slice.to_vec(), }) } } // 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"); } } #[cfg(test)] mod non_std_tests { use super::*; #[test] fn test_private_key_from_der() { fn is_pkcs8(key: &PrivateKeyDer<'_>) -> bool { matches!(key, PrivateKeyDer::Pkcs8(_)) } fn is_pkcs1(key: &PrivateKeyDer<'_>) -> bool { matches!(key, PrivateKeyDer::Pkcs1(_)) } fn is_sec1(key: &PrivateKeyDer<'_>) -> bool { matches!(key, PrivateKeyDer::Sec1(_)) } let test_cases: &[(&[u8], fn(&PrivateKeyDer<'_>) -> bool); 11] = &[ (&include_bytes!("test_keys/eddsakey.der")[..], is_pkcs8), (&include_bytes!("test_keys/nistp256key.der")[..], is_sec1), ( &include_bytes!("test_keys/nistp256key.pkcs8.der")[..], is_pkcs8, ), (&include_bytes!("test_keys/nistp384key.der")[..], is_sec1), ( &include_bytes!("test_keys/nistp384key.pkcs8.der")[..], is_pkcs8, ), (&include_bytes!("test_keys/nistp521key.der")[..], is_sec1), ( &include_bytes!("test_keys/nistp521key.pkcs8.der")[..], is_pkcs8, ), ( &include_bytes!("test_keys/rsa2048key.pkcs1.der")[..], is_pkcs1, ), ( &include_bytes!("test_keys/rsa2048key.pkcs8.der")[..], is_pkcs8, ), ( &include_bytes!("test_keys/rsa4096key.pkcs8.der")[..], is_pkcs8, ), (&include_bytes!("test_keys/edd25519_v2.der")[..], is_pkcs8), ]; for (key_bytes, expected_check_fn) in test_cases.iter() { assert!(expected_check_fn( &PrivateKeyDer::try_from(*key_bytes).unwrap() )); } } } pki-types-v-1.4.1/src/server_name.rs000066400000000000000000001121561460101777000173610ustar00rootroot00000000000000//! 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<'a> ServerName<'a> { /// 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<'a> fmt::Debug for ServerName<'a> { 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), }, } } } /// 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) -> DnsName<'_> { 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), } } } #[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, Self::Error> { validate(value.as_bytes())?; Ok(Self(DnsNameInner::Borrowed(value))) } } impl<'a> TryFrom<&'a [u8]> for DnsName<'a> { type Error = InvalidDnsNameError; fn try_from(value: &'a [u8]) -> Result, Self::Error> { validate(value)?; Ok(Self(DnsNameInner::Borrowed(str::from_utf8(value).unwrap()))) } } impl<'a> AsRef for DnsName<'a> { 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<'a> PartialEq> for DnsNameInner<'a> { fn eq(&self, other: &DnsNameInner<'a>) -> 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<'a> Hash for DnsNameInner<'a> { 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<'a> fmt::Debug for DnsNameInner<'a> { 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 {} 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); } for ch in input { 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), }; } 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)), } } } /// `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::<_, [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]) -> Parser<'a> { 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.4.1/src/test_keys/000077500000000000000000000000001460101777000165115ustar00rootroot00000000000000pki-types-v-1.4.1/src/test_keys/edd25519_v2.der000066400000000000000000000001251460101777000207540ustar00rootroot000000000000000S0+ep" -#xJ^揠ge1.FT7Q\#!}{l΄OJpwW k@pki-types-v-1.4.1/src/test_keys/eddsakey.der000066400000000000000000000000601460101777000207720ustar00rootroot000000000000000.0+ep" f)='. _lAݘQlpki-types-v-1.4.1/src/test_keys/nistp256key.der000066400000000000000000000001711460101777000213070ustar00rootroot000000000000000w ƀk0J)4*ArY7e!ae *H=DB'@@]:vnfG?َPIJ8EՁg# KKtpki-types-v-1.4.1/src/test_keys/nistp256key.pkcs8.der000066400000000000000000000002121460101777000223320ustar00rootroot0000000000000000*H=*H=m0k ƀk0J)4*ArY7e!aeDB'@@]:vnfG?َPIJ8EՁg# KKtpki-types-v-1.4.1/src/test_keys/nistp384key.der000066400000000000000000000002471460101777000213150ustar00rootroot0000000000000000 h+Q" Rwo4 s{*6ހso!E9+"dbU9J<=a-m̼BVMVztvbԈfQa%D{I認ZS,9z8gk0ryo]U A.Cpki-types-v-1.4.1/src/test_keys/nistp384key.pkcs8.der000066400000000000000000000002711460101777000223410ustar00rootroot0000000000000000*H=+"00 h+Q" Rwo4 s{*6ހso!E9dbU9J<=a-m̼BVMVztvbԈfQa%D{I認ZS,9z8gk0ryo]U A.Cpki-types-v-1.4.1/src/test_keys/nistp521key.der000066400000000000000000000003371460101777000213060ustar00rootroot000000000000000BwAh6WKf+K3 T(6(?b@:ʯXKM#wT [=C F뤓 eA>^gpki-types-v-1.4.1/src/test_keys/nistp521key.pkcs8.der000066400000000000000000000003611460101777000223320ustar00rootroot0000000000000000*H=+#0BwAh6WKf+K3 T(6(?b@:ʯXKM#wT [=C F뤓 eA>^gpki-types-v-1.4.1/src/test_keys/rsa2048key.pkcs1.der000066400000000000000000000022461460101777000220450ustar00rootroot000000000000000Ё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.4.1/tests/000077500000000000000000000000001460101777000150525ustar00rootroot00000000000000pki-types-v-1.4.1/tests/dns_name.rs000066400000000000000000000277561460101777000172250ustar00rootroot00000000000000use 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.4.1/tests/server_name.rs000066400000000000000000000027211460101777000177300ustar00rootroot00000000000000use 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() ); }