├── .github └── workflows │ ├── ci.yml │ └── semgrep.yml ├── .gitignore ├── .gitmodules ├── .rusty-hook.toml ├── Cargo.toml ├── README.md ├── RELEASE_NOTES ├── THIRD_PARTY ├── boring-sys ├── Cargo.toml ├── LICENSE-MIT ├── README.md ├── build │ ├── config.rs │ └── main.rs ├── cmake │ ├── aarch64-linux.cmake │ └── armv7-linux.cmake ├── patches │ ├── boring-pq.patch │ ├── rpk.patch │ └── underscore-wildcards.patch └── src │ └── lib.rs ├── boring ├── Cargo.toml ├── LICENSE ├── README.md ├── examples │ ├── fips_enabled.rs │ └── mk_certs.rs ├── src │ ├── aes.rs │ ├── asn1.rs │ ├── base64.rs │ ├── bio.rs │ ├── bn.rs │ ├── conf.rs │ ├── derive.rs │ ├── dh.rs │ ├── dsa.rs │ ├── ec.rs │ ├── ecdsa.rs │ ├── error.rs │ ├── ex_data.rs │ ├── fips.rs │ ├── hash.rs │ ├── hpke.rs │ ├── lib.rs │ ├── macros.rs │ ├── memcmp.rs │ ├── nid.rs │ ├── pkcs12.rs │ ├── pkcs5.rs │ ├── pkey.rs │ ├── rand.rs │ ├── rsa.rs │ ├── sha.rs │ ├── sign.rs │ ├── srtp.rs │ ├── ssl │ │ ├── async_callbacks.rs │ │ ├── bio.rs │ │ ├── callbacks.rs │ │ ├── connector.rs │ │ ├── ech.rs │ │ ├── error.rs │ │ ├── mod.rs │ │ ├── mut_only.rs │ │ └── test │ │ │ ├── cert_compressor.rs │ │ │ ├── cert_verify.rs │ │ │ ├── custom_verify.rs │ │ │ ├── ech.rs │ │ │ ├── mod.rs │ │ │ ├── private_key_method.rs │ │ │ ├── server.rs │ │ │ ├── session.rs │ │ │ └── verify.rs │ ├── stack.rs │ ├── string.rs │ ├── symm.rs │ ├── util.rs │ ├── version.rs │ └── x509 │ │ ├── extension.rs │ │ ├── mod.rs │ │ ├── store.rs │ │ ├── tests │ │ ├── mod.rs │ │ └── trusted_first.rs │ │ └── verify.rs └── test │ ├── alt_name_cert.pem │ ├── cert-wildcard.pem │ ├── cert-with-intermediate.pem │ ├── cert.pem │ ├── certs.pem │ ├── cms.p12 │ ├── cms_pubkey.der │ ├── dhparams.pem │ ├── dsa.pem │ ├── dsa.pem.pub │ ├── dsaparam.pem │ ├── echconfig │ ├── echconfig-2 │ ├── echconfiglist │ ├── echconfiglist-2 │ ├── echkey │ ├── echkey-2 │ ├── identity.p12 │ ├── intermediate-ca.key │ ├── intermediate-ca.pem │ ├── key.der │ ├── key.der.pub │ ├── key.pem │ ├── key.pem.pub │ ├── keystore-empty-chain.p12 │ ├── nid_test_cert.pem │ ├── nid_uid_test_cert.pem │ ├── pkcs1.pem.pub │ ├── pkcs8-nocrypt.der │ ├── pkcs8.der │ ├── root-ca-2.key │ ├── root-ca-2.pem │ ├── root-ca-cross.pem │ ├── root-ca.key │ ├── root-ca.pem │ ├── rsa-encrypted.pem │ ├── rsa.pem │ └── rsa.pem.pub ├── cliff.toml ├── hyper-boring ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── src │ ├── cache.rs │ ├── lib.rs │ ├── v0.rs │ └── v1.rs └── tests │ ├── test │ ├── cert.pem │ ├── key.pem │ └── root-ca.pem │ ├── v0.rs │ └── v1.rs ├── scripts └── publish.sh └── tokio-boring ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── examples └── simple-async.rs ├── src ├── async_callbacks.rs ├── bridge.rs └── lib.rs └── tests ├── async_custom_verify.rs ├── async_get_session.rs ├── async_private_key_method.rs ├── async_select_certificate.rs ├── cert.pem ├── client_server.rs ├── common └── mod.rs ├── key.pem ├── pubkey.der ├── pubkey2.der └── rpk.rs /.github/workflows/semgrep.yml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request: {} 3 | workflow_dispatch: {} 4 | push: 5 | branches: 6 | - master 7 | schedule: 8 | - cron: "0 0 * * *" 9 | name: Semgrep config 10 | jobs: 11 | semgrep: 12 | name: semgrep/ci 13 | runs-on: ubuntu-latest 14 | env: 15 | SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} 16 | SEMGREP_URL: https://cloudflare.semgrep.dev 17 | SEMGREP_APP_URL: https://cloudflare.semgrep.dev 18 | SEMGREP_VERSION_CHECK_URL: https://cloudflare.semgrep.dev/api/check-version 19 | container: 20 | image: semgrep/semgrep 21 | steps: 22 | - uses: actions/checkout@v4 23 | - run: semgrep ci 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | .idea/ 4 | *.iml 5 | .vscode/ 6 | .DS_Store -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "boring-sys/deps/boringssl"] 2 | path = boring-sys/deps/boringssl 3 | url = https://github.com/google/boringssl.git 4 | ignore = dirty 5 | [submodule "boring-sys/deps/boringssl-fips"] 6 | path = boring-sys/deps/boringssl-fips 7 | url = https://github.com/google/boringssl.git 8 | -------------------------------------------------------------------------------- /.rusty-hook.toml: -------------------------------------------------------------------------------- 1 | [hooks] 2 | pre-commit = "cargo fmt --all -- --check" -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "boring", 4 | "boring-sys", 5 | "tokio-boring", 6 | "hyper-boring" 7 | ] 8 | resolver = "2" 9 | 10 | [workspace.package] 11 | version = "4.17.0" 12 | repository = "https://github.com/cloudflare/boring" 13 | edition = "2021" 14 | 15 | [workspace.metadata.release] 16 | pre-release-commit-message = "Release {{version}}" 17 | shared-version = true 18 | tag-prefix = "" 19 | publish = false 20 | 21 | [workspace.dependencies] 22 | boring-sys = { version = "4.17.0", path = "./boring-sys" } 23 | boring = { version = "4.17.0", path = "./boring" } 24 | tokio-boring = { version = "4.17.0", path = "./tokio-boring" } 25 | 26 | bindgen = { version = "0.71.1", default-features = false, features = ["runtime"] } 27 | bytes = "1" 28 | cmake = "0.1.18" 29 | fs_extra = "1.3.0" 30 | fslock = "0.2" 31 | bitflags = "2.4" 32 | foreign-types = "0.5" 33 | libc = "0.2" 34 | hex = "0.4" 35 | rusty-hook = "^0.11" 36 | futures = "0.3" 37 | tokio = "1" 38 | anyhow = "1" 39 | antidote = "1.0.0" 40 | http = "1" 41 | http-body-util = "0.1.2" 42 | http_old = { package = "http", version = "0.2" } 43 | hyper = "1" 44 | hyper-util = "0.1.6" 45 | hyper_old = { package = "hyper", version = "0.14", default-features = false } 46 | linked_hash_set = "0.1" 47 | openssl-macros = "0.1.1" 48 | tower = "0.4" 49 | tower-layer = "0.3" 50 | tower-service = "0.3" 51 | autocfg = "1.3.0" 52 | brotli = "6.0" 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # boring 2 | 3 | [![crates.io](https://img.shields.io/crates/v/boring.svg)](https://crates.io/crates/boring) 4 | 5 | BoringSSL bindings for the Rust programming language and TLS adapters for [tokio](https://github.com/tokio-rs/tokio) 6 | and [hyper](https://github.com/hyperium/hyper) built on top of it. 7 | 8 | ## Documentation 9 | - Boring API: 10 | - tokio TLS adapters: 11 | - hyper HTTPS connector: 12 | - FFI bindings: 13 | 14 | ## Contribution 15 | 16 | Unless you explicitly state otherwise, any contribution intentionally 17 | submitted for inclusion in the work by you, as defined in the Apache-2.0 18 | license, shall be dual licensed under the terms of both the Apache License, 19 | Version 2.0 and the MIT license without any additional terms or conditions. 20 | 21 | ## Accolades 22 | 23 | The project is based on a fork of [rust-openssl](https://github.com/sfackler/rust-openssl). 24 | -------------------------------------------------------------------------------- /boring-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "boring-sys" 3 | version = { workspace = true } 4 | authors = ["Alex Crichton ", 5 | "Steven Fackler ", 6 | "Ivan Nikulin "] 7 | license = "MIT" 8 | description = "FFI bindings to BoringSSL" 9 | repository = { workspace = true } 10 | documentation = "https://docs.rs/boring-sys" 11 | links = "boringssl" 12 | build = "build/main.rs" 13 | readme = "README.md" 14 | categories = ["cryptography", "external-ffi-bindings"] 15 | edition = { workspace = true } 16 | include = [ 17 | "/*.md", 18 | "/*.toml", 19 | "/LICENSE-MIT", 20 | "/cmake/*.cmake", 21 | # boringssl (non-FIPS) 22 | "/deps/boringssl/src/util/32-bit-toolchain.cmake", 23 | "/deps/boringssl/**/*.[chS]", 24 | "/deps/boringssl/**/*.asm", 25 | "/deps/boringssl/sources.json", 26 | "/deps/boringssl/src/crypto/obj/obj_mac.num", 27 | "/deps/boringssl/src/crypto/obj/objects.txt", 28 | "/deps/boringssl/**/*.bzl", 29 | "/deps/boringssl/src/**/*.cc", 30 | "/deps/boringssl/**/CMakeLists.txt", 31 | "/deps/boringssl/**/sources.cmake", 32 | "/deps/boringssl/LICENSE", 33 | # boringssl (FIPS) 34 | "/deps/boringssl-fips/src/util/32-bit-toolchain.cmake", 35 | "/deps/boringssl-fips/**/*.[chS]", 36 | "/deps/boringssl-fips/**/*.asm", 37 | "/deps/boringssl-fips/**/*.pl", 38 | "/deps/boringssl-fips/**/*.go", 39 | "/deps/boringssl-fips/**/go.mod", 40 | "/deps/boringssl-fips/**/go.sum", 41 | "/deps/boringssl-fips/sources.json", 42 | "/deps/boringssl-fips/crypto/obj/obj_mac.num", 43 | "/deps/boringssl-fips/crypto/obj/objects.txt", 44 | "/deps/boringssl-fips/crypto/err/*.errordata", 45 | "/deps/boringssl-fips/**/*.bzl", 46 | "/deps/boringssl-fips/**/*.cc", 47 | "/deps/boringssl-fips/**/CMakeLists.txt", 48 | "/deps/boringssl-fips/**/sources.cmake", 49 | "/deps/boringssl-fips/LICENSE", 50 | "/build/*", 51 | "/src", 52 | "/patches", 53 | ] 54 | 55 | [package.metadata.docs.rs] 56 | features = ["rpk", "pq-experimental", "underscore-wildcards"] 57 | rustdoc-args = ["--cfg", "docsrs"] 58 | 59 | [features] 60 | # Compile boringssl using the FIPS build flag if building boringssl from 61 | # scratch. 62 | # 63 | # See 64 | # https://boringssl.googlesource.com/boringssl/+/master/crypto/fipsmodule/FIPS.md 65 | # for instructions and more details on the boringssl FIPS flag. 66 | fips = [] 67 | 68 | # Use a precompiled FIPS-validated version of BoringSSL. Meant to be used with 69 | # FIPS-20230428 or newer. Users must set `BORING_BSSL_FIPS_PATH` to use this 70 | # feature, or else the build will fail. 71 | fips-precompiled = [] 72 | 73 | # Link with precompiled FIPS-validated `bcm.o` module. 74 | fips-link-precompiled = [] 75 | 76 | # Enables Raw public key API (https://datatracker.ietf.org/doc/html/rfc7250) 77 | rpk = [] 78 | 79 | # Applies a patch (`patches/boring-pq.patch`) to the boringSSL source code that 80 | # enables support for PQ key exchange. This feature is necessary in order to 81 | # compile the bindings for the default branch of boringSSL (`deps/boringssl`). 82 | # Alternatively, a version of boringSSL that implements the same feature set 83 | # can be provided by setting `BORING_BSSL{,_FIPS}_SOURCE_PATH`. 84 | pq-experimental = [] 85 | 86 | # Applies a patch (`patches/underscore-wildcards.patch`) to enable 87 | # `ffi::X509_CHECK_FLAG_UNDERSCORE_WILDCARDS`. Same caveats as 88 | # those for `pq-experimental` feature apply. 89 | underscore-wildcards = [] 90 | 91 | [build-dependencies] 92 | autocfg = { workspace = true } 93 | bindgen = { workspace = true } 94 | cmake = { workspace = true } 95 | fs_extra = { workspace = true } 96 | fslock = { workspace = true } 97 | 98 | [lints.rust] 99 | unexpected_cfgs = { level = "allow", check-cfg = ['cfg(const_fn)'] } 100 | -------------------------------------------------------------------------------- /boring-sys/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Alex Crichton 2 | Copyright (c) 2020 Ivan Nikulin 3 | 4 | Permission is hereby granted, free of charge, to any 5 | person obtaining a copy of this software and associated 6 | documentation files (the "Software"), to deal in the 7 | Software without restriction, including without 8 | limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software 11 | is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice 15 | shall be included in all copies or substantial portions 16 | of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 19 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 20 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 21 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 22 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 25 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 | DEALINGS IN THE SOFTWARE. 27 | -------------------------------------------------------------------------------- /boring-sys/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /boring-sys/cmake/aarch64-linux.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Linux) 2 | set(CMAKE_SYSTEM_PROCESSOR aarch64) 3 | # Rely on environment variables to set the compiler and include paths. 4 | -------------------------------------------------------------------------------- /boring-sys/cmake/armv7-linux.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Linux) 2 | set(CMAKE_SYSTEM_PROCESSOR armv7) 3 | # Rely on environment variables to set the compiler and include paths. 4 | -------------------------------------------------------------------------------- /boring-sys/patches/underscore-wildcards.patch: -------------------------------------------------------------------------------- 1 | https://github.com/google/boringssl/compare/master...cloudflare:boringssl:underscore-wildcards 2 | 3 | --- a/src/crypto/x509v3/v3_utl.c 4 | +++ b/src/crypto/x509v3/v3_utl.c 5 | @@ -790,7 +790,9 @@ static int wildcard_match(const unsigned char *prefix, size_t prefix_len, 6 | // Check that the part matched by the wildcard contains only 7 | // permitted characters and only matches a single label. 8 | for (p = wildcard_start; p != wildcard_end; ++p) { 9 | - if (!OPENSSL_isalnum(*p) && *p != '-') { 10 | + if (!OPENSSL_isalnum(*p) && *p != '-' && 11 | + !(*p == '_' && 12 | + (flags & X509_CHECK_FLAG_UNDERSCORE_WILDCARDS))) { 13 | return 0; 14 | } 15 | } 16 | --- a/src/crypto/x509/x509_test.cc 17 | +++ b/src/crypto/x509/x509_test.cc 18 | @@ -4500,6 +4500,31 @@ TEST(X509Test, Names) { 19 | /*invalid_emails=*/{}, 20 | /*flags=*/0, 21 | }, 22 | + 23 | + // Underscores in DNS names are forbidden by default. 24 | + { 25 | + /*cert_subject=*/{}, 26 | + /*cert_dns_names=*/{"*.example.com"}, 27 | + /*cert_emails=*/{}, 28 | + /*valid_dns_names=*/{}, 29 | + /*invalid_dns_names=*/{"not_allowed.example.com"}, 30 | + /*valid_emails=*/{}, 31 | + /*invalid_emails=*/{}, 32 | + /*flags=*/0, 33 | + }, 34 | + 35 | + // Underscores in DNS names can be allowed with the right flag. 36 | + { 37 | + /*cert_subject=*/{}, 38 | + /*cert_dns_names=*/{"*.example.com"}, 39 | + /*cert_emails=*/{}, 40 | + /*valid_dns_names=*/{"now_allowed.example.com"}, 41 | + /*invalid_dns_names=*/{}, 42 | + /*valid_emails=*/{}, 43 | + /*invalid_emails=*/{}, 44 | + /*flags=*/X509_CHECK_FLAG_UNDERSCORE_WILDCARDS, 45 | + }, 46 | + 47 | }; 48 | 49 | size_t i = 0; 50 | --- a/src/include/openssl/x509c3.h 51 | +++ b/src/include/openssl/x509v3.h 52 | @@ -4497,6 +4497,8 @@ OPENSSL_EXPORT int X509_PURPOSE_get_id(const X509_PURPOSE *); 53 | #define X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS 0 54 | // Skip the subject common name fallback if subjectAltNames is missing. 55 | #define X509_CHECK_FLAG_NEVER_CHECK_SUBJECT 0x20 56 | +// Allow underscores in DNS wildcard matches. 57 | +#define X509_CHECK_FLAG_UNDERSCORE_WILDCARDS 0x40 58 | 59 | OPENSSL_EXPORT int X509_check_host(X509 *x, const char *chk, size_t chklen, 60 | unsigned int flags, char **peername); 61 | -- 62 | -------------------------------------------------------------------------------- /boring-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow( 2 | clippy::missing_safety_doc, 3 | clippy::redundant_static_lifetimes, 4 | clippy::too_many_arguments, 5 | clippy::unreadable_literal, 6 | clippy::upper_case_acronyms, 7 | improper_ctypes, 8 | non_camel_case_types, 9 | non_snake_case, 10 | non_upper_case_globals, 11 | unused_imports 12 | )] 13 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 14 | 15 | use std::convert::TryInto; 16 | use std::ffi::c_void; 17 | use std::os::raw::{c_char, c_int, c_uint, c_ulong}; 18 | 19 | #[allow( 20 | clippy::useless_transmute, 21 | clippy::derive_partial_eq_without_eq, 22 | clippy::ptr_offset_with_cast, 23 | dead_code 24 | )] 25 | mod generated { 26 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 27 | } 28 | pub use generated::*; 29 | 30 | #[cfg(target_pointer_width = "64")] 31 | pub type BN_ULONG = u64; 32 | #[cfg(target_pointer_width = "32")] 33 | pub type BN_ULONG = u32; 34 | 35 | pub const fn ERR_PACK(l: c_int, f: c_int, r: c_int) -> c_ulong { 36 | ((l as c_ulong & 0x0FF) << 24) | ((f as c_ulong & 0xFFF) << 12) | (r as c_ulong & 0xFFF) 37 | } 38 | 39 | pub const fn ERR_GET_LIB(l: c_uint) -> c_int { 40 | ((l >> 24) & 0x0FF) as c_int 41 | } 42 | 43 | pub const fn ERR_GET_FUNC(l: c_uint) -> c_int { 44 | ((l >> 12) & 0xFFF) as c_int 45 | } 46 | 47 | pub const fn ERR_GET_REASON(l: c_uint) -> c_int { 48 | (l & 0xFFF) as c_int 49 | } 50 | 51 | pub fn init() { 52 | unsafe { 53 | CRYPTO_library_init(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /boring/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "boring" 3 | version = { workspace = true } 4 | authors = ["Steven Fackler ", "Ivan Nikulin "] 5 | license = "Apache-2.0" 6 | description = "BoringSSL bindings" 7 | repository = { workspace = true } 8 | documentation = "https://docs.rs/boring" 9 | readme = "README.md" 10 | keywords = ["crypto", "tls", "ssl", "dtls"] 11 | categories = ["cryptography", "api-bindings"] 12 | edition = { workspace = true } 13 | rust-version = "1.80" 14 | 15 | [package.metadata.docs.rs] 16 | features = ["rpk", "pq-experimental", "underscore-wildcards"] 17 | rustdoc-args = ["--cfg", "docsrs"] 18 | 19 | [features] 20 | # Controlling the build 21 | 22 | # NOTE: This feature is deprecated. It is needed for the submoduled 23 | # boringssl-fips, which is extremely old and requires modifications to the 24 | # bindings, as some newer APIs don't exist and some function signatures have 25 | # changed. It is highly recommended to use `fips-precompiled` instead. 26 | # 27 | # This feature sets `fips-compat` on behalf of the user to guarantee bindings 28 | # compatibility with the submoduled boringssl-fips. 29 | # 30 | # Use a FIPS-validated version of BoringSSL. 31 | fips = ["fips-compat", "boring-sys/fips"] 32 | 33 | # Build with compatibility for the submoduled boringssl-fips, without enabling 34 | # the `fips` feature itself (useful e.g. if `fips-link-precompiled` is used 35 | # with an older BoringSSL version). 36 | fips-compat = [] 37 | 38 | # Use a precompiled FIPS-validated version of BoringSSL. Meant to be used with 39 | # FIPS-20230428 or newer. Users must set `BORING_BSSL_FIPS_PATH` to use this 40 | # feature, or else the build will fail. 41 | fips-precompiled = ["boring-sys/fips-precompiled"] 42 | 43 | # Link with precompiled FIPS-validated `bcm.o` module. 44 | fips-link-precompiled = ["boring-sys/fips-link-precompiled"] 45 | 46 | # Enables Raw public key API (https://datatracker.ietf.org/doc/html/rfc7250) 47 | # This feature is necessary in order to compile the bindings for the 48 | # default branch of boringSSL. Alternatively, a version of boringSSL that 49 | # implements the same feature set can be provided by setting 50 | # `BORING_BSSL{,_FIPS}_SOURCE_PATH` and `BORING_BSSL{,_FIPS}_ASSUME_PATCHED`. 51 | rpk = ["boring-sys/rpk"] 52 | 53 | # Applies a patch to the boringSSL source code that enables support for PQ key 54 | # exchange. This feature is necessary in order to compile the bindings for the 55 | # default branch of boringSSL. Alternatively, a version of boringSSL that 56 | # implements the same feature set can be provided by setting 57 | # `BORING_BSSL{,_FIPS}_SOURCE_PATH` and `BORING_BSSL{,_FIPS}_ASSUME_PATCHED`. 58 | pq-experimental = ["boring-sys/pq-experimental"] 59 | 60 | # Applies a patch to enable 61 | # `ffi::X509_CHECK_FLAG_UNDERSCORE_WILDCARDS`. Same caveats as 62 | # those for `pq-experimental` feature apply. 63 | underscore-wildcards = ["boring-sys/underscore-wildcards"] 64 | 65 | # Controlling key exchange preferences at compile time 66 | 67 | # Choose key exchange preferences at compile time. This prevents the user from 68 | # choosing their own preferences. 69 | kx-safe-default = [] 70 | 71 | # Support PQ key exchange. The client will prefer classical key exchange, but 72 | # will upgrade to PQ key exchange if requested by the server. This is the 73 | # safest option if you don't know if the peer supports PQ key exchange. This 74 | # feature implies "kx-safe-default". 75 | kx-client-pq-supported = ["kx-safe-default"] 76 | 77 | # Prefer PQ key exchange. The client will prefer PQ exchange, but fallback to 78 | # classical key exchange if requested by the server. This is the best option if 79 | # you know the peer supports PQ key exchange. This feature implies 80 | # "kx-safe-default" and "kx-client-pq-supported". 81 | kx-client-pq-preferred = ["kx-safe-default", "kx-client-pq-supported"] 82 | 83 | # Disable key exchange involving non-NIST key exchange on the client side. 84 | # Implies "kx-safe-default". 85 | kx-client-nist-required = ["kx-safe-default"] 86 | 87 | [dependencies] 88 | bitflags = { workspace = true } 89 | foreign-types = { workspace = true } 90 | openssl-macros = { workspace = true } 91 | libc = { workspace = true } 92 | boring-sys = { workspace = true } 93 | 94 | [dev-dependencies] 95 | hex = { workspace = true } 96 | rusty-hook = { workspace = true } 97 | brotli = { workspace = true } 98 | -------------------------------------------------------------------------------- /boring/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2011-2017 Google Inc. 2 | 2013 Jack Lloyd 3 | 2013-2014 Steven Fackler 4 | 2020 Ivan Nikulin 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | -------------------------------------------------------------------------------- /boring/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /boring/examples/fips_enabled.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("boring::fips::enabled(): {}", boring::fips::enabled()); 3 | } 4 | -------------------------------------------------------------------------------- /boring/examples/mk_certs.rs: -------------------------------------------------------------------------------- 1 | //! A program that generates ca certs, certs verified by the ca, and public 2 | //! and private keys. 3 | 4 | extern crate boring; 5 | 6 | use boring::asn1::Asn1Time; 7 | use boring::bn::{BigNum, MsbOption}; 8 | use boring::error::ErrorStack; 9 | use boring::hash::MessageDigest; 10 | use boring::pkey::{PKey, PKeyRef, Private}; 11 | use boring::rsa::Rsa; 12 | use boring::x509::extension::{ 13 | AuthorityKeyIdentifier, BasicConstraints, KeyUsage, SubjectAlternativeName, 14 | SubjectKeyIdentifier, 15 | }; 16 | use boring::x509::{X509NameBuilder, X509Ref, X509Req, X509ReqBuilder, X509}; 17 | 18 | /// Make a CA certificate and private key 19 | fn mk_ca_cert() -> Result<(X509, PKey), ErrorStack> { 20 | let rsa = Rsa::generate(2048)?; 21 | let privkey = PKey::from_rsa(rsa)?; 22 | 23 | let mut x509_name = X509NameBuilder::new()?; 24 | x509_name.append_entry_by_text("C", "US")?; 25 | x509_name.append_entry_by_text("ST", "TX")?; 26 | x509_name.append_entry_by_text("O", "Some CA organization")?; 27 | x509_name.append_entry_by_text("CN", "ca test")?; 28 | let x509_name = x509_name.build(); 29 | 30 | let mut cert_builder = X509::builder()?; 31 | cert_builder.set_version(2)?; 32 | let serial_number = { 33 | let mut serial = BigNum::new()?; 34 | serial.rand(159, MsbOption::MAYBE_ZERO, false)?; 35 | serial.to_asn1_integer()? 36 | }; 37 | cert_builder.set_serial_number(&serial_number)?; 38 | cert_builder.set_subject_name(&x509_name)?; 39 | cert_builder.set_issuer_name(&x509_name)?; 40 | cert_builder.set_pubkey(&privkey)?; 41 | let not_before = Asn1Time::days_from_now(0)?; 42 | cert_builder.set_not_before(¬_before)?; 43 | let not_after = Asn1Time::days_from_now(365)?; 44 | cert_builder.set_not_after(¬_after)?; 45 | 46 | cert_builder.append_extension(BasicConstraints::new().critical().ca().build()?)?; 47 | cert_builder.append_extension( 48 | KeyUsage::new() 49 | .critical() 50 | .key_cert_sign() 51 | .crl_sign() 52 | .build()?, 53 | )?; 54 | 55 | let subject_key_identifier = 56 | SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(None, None))?; 57 | cert_builder.append_extension(subject_key_identifier)?; 58 | 59 | cert_builder.sign(&privkey, MessageDigest::sha256())?; 60 | let cert = cert_builder.build(); 61 | 62 | Ok((cert, privkey)) 63 | } 64 | 65 | /// Make a X509 request with the given private key 66 | fn mk_request(privkey: &PKey) -> Result { 67 | let mut req_builder = X509ReqBuilder::new()?; 68 | req_builder.set_pubkey(privkey)?; 69 | 70 | let mut x509_name = X509NameBuilder::new()?; 71 | x509_name.append_entry_by_text("C", "US")?; 72 | x509_name.append_entry_by_text("ST", "TX")?; 73 | x509_name.append_entry_by_text("O", "Some organization")?; 74 | x509_name.append_entry_by_text("CN", "www.example.com")?; 75 | let x509_name = x509_name.build(); 76 | req_builder.set_subject_name(&x509_name)?; 77 | 78 | req_builder.sign(privkey, MessageDigest::sha256())?; 79 | let req = req_builder.build(); 80 | Ok(req) 81 | } 82 | 83 | /// Make a certificate and private key signed by the given CA cert and private key 84 | fn mk_ca_signed_cert( 85 | ca_cert: &X509Ref, 86 | ca_privkey: &PKeyRef, 87 | ) -> Result<(X509, PKey), ErrorStack> { 88 | let rsa = Rsa::generate(2048)?; 89 | let privkey = PKey::from_rsa(rsa)?; 90 | 91 | let req = mk_request(&privkey)?; 92 | 93 | let mut cert_builder = X509::builder()?; 94 | cert_builder.set_version(2)?; 95 | let serial_number = { 96 | let mut serial = BigNum::new()?; 97 | serial.rand(159, MsbOption::MAYBE_ZERO, false)?; 98 | serial.to_asn1_integer()? 99 | }; 100 | cert_builder.set_serial_number(&serial_number)?; 101 | cert_builder.set_subject_name(req.subject_name())?; 102 | cert_builder.set_issuer_name(ca_cert.subject_name())?; 103 | cert_builder.set_pubkey(&privkey)?; 104 | let not_before = Asn1Time::days_from_now(0)?; 105 | cert_builder.set_not_before(¬_before)?; 106 | let not_after = Asn1Time::days_from_now(365)?; 107 | cert_builder.set_not_after(¬_after)?; 108 | 109 | cert_builder.append_extension(BasicConstraints::new().build()?)?; 110 | 111 | cert_builder.append_extension( 112 | KeyUsage::new() 113 | .critical() 114 | .non_repudiation() 115 | .digital_signature() 116 | .key_encipherment() 117 | .build()?, 118 | )?; 119 | 120 | let subject_key_identifier = 121 | SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(Some(ca_cert), None))?; 122 | cert_builder.append_extension(subject_key_identifier)?; 123 | 124 | let auth_key_identifier = AuthorityKeyIdentifier::new() 125 | .keyid(false) 126 | .issuer(false) 127 | .build(&cert_builder.x509v3_context(Some(ca_cert), None))?; 128 | cert_builder.append_extension(auth_key_identifier)?; 129 | 130 | let subject_alt_name = SubjectAlternativeName::new() 131 | .dns("*.example.com") 132 | .dns("hello.com") 133 | .build(&cert_builder.x509v3_context(Some(ca_cert), None))?; 134 | cert_builder.append_extension(subject_alt_name)?; 135 | 136 | cert_builder.sign(ca_privkey, MessageDigest::sha256())?; 137 | let cert = cert_builder.build(); 138 | 139 | Ok((cert, privkey)) 140 | } 141 | 142 | fn real_main() -> Result<(), ErrorStack> { 143 | let (ca_cert, ca_privkey) = mk_ca_cert()?; 144 | let (cert, _privkey) = mk_ca_signed_cert(&ca_cert, &ca_privkey)?; 145 | 146 | // Verify that this cert was issued by this ca 147 | match ca_cert.issued(&cert) { 148 | Ok(()) => println!("Certificate verified!"), 149 | Err(ver_err) => println!("Failed to verify certificate: {}", ver_err), 150 | }; 151 | 152 | Ok(()) 153 | } 154 | 155 | fn main() { 156 | match real_main() { 157 | Ok(()) => println!("Finished."), 158 | Err(e) => println!("Error: {}", e), 159 | }; 160 | } 161 | -------------------------------------------------------------------------------- /boring/src/base64.rs: -------------------------------------------------------------------------------- 1 | //! Base64 encoding support. 2 | use crate::cvt_n; 3 | use crate::error::ErrorStack; 4 | use crate::ffi; 5 | use libc::c_int; 6 | use openssl_macros::corresponds; 7 | 8 | /// Encodes a slice of bytes to a base64 string. 9 | /// 10 | /// # Panics 11 | /// 12 | /// Panics if the input length or computed output length overflow a signed C integer. 13 | #[corresponds(EVP_EncodeBlock)] 14 | pub fn encode_block(src: &[u8]) -> String { 15 | assert!(src.len() <= c_int::MAX as usize); 16 | let src_len = src.len(); 17 | 18 | let len = encoded_len(src_len).unwrap(); 19 | let mut out = Vec::with_capacity(len); 20 | 21 | // SAFETY: `encoded_len` ensures space for 4 output characters 22 | // for every 3 input bytes including padding and nul terminator. 23 | // `EVP_EncodeBlock` will write only single byte ASCII characters. 24 | // `EVP_EncodeBlock` will only write to not read from `out`. 25 | unsafe { 26 | let out_len = ffi::EVP_EncodeBlock(out.as_mut_ptr(), src.as_ptr(), src_len); 27 | out.set_len(out_len); 28 | String::from_utf8_unchecked(out) 29 | } 30 | } 31 | 32 | /// Decodes a base64-encoded string to bytes. 33 | /// 34 | /// # Panics 35 | /// 36 | /// Panics if the input length or computed output length overflow a signed C integer. 37 | #[corresponds(EVP_DecodeBlock)] 38 | pub fn decode_block(src: &str) -> Result, ErrorStack> { 39 | let src = src.trim(); 40 | 41 | // https://github.com/openssl/openssl/issues/12143 42 | if src.is_empty() { 43 | return Ok(vec![]); 44 | } 45 | 46 | assert!(src.len() <= c_int::MAX as usize); 47 | let src_len = src.len(); 48 | 49 | let len = decoded_len(src_len).unwrap(); 50 | let mut out = Vec::with_capacity(len); 51 | 52 | // SAFETY: `decoded_len` ensures space for 3 output bytes 53 | // for every 4 input characters including padding. 54 | // `EVP_DecodeBlock` can write fewer bytes after stripping 55 | // leading and trailing whitespace, but never more. 56 | // `EVP_DecodeBlock` will only write to not read from `out`. 57 | unsafe { 58 | let out_len = cvt_n(ffi::EVP_DecodeBlock( 59 | out.as_mut_ptr(), 60 | src.as_ptr(), 61 | src_len, 62 | ))?; 63 | out.set_len(out_len as usize); 64 | } 65 | 66 | if src.ends_with('=') { 67 | out.pop(); 68 | if src.ends_with("==") { 69 | out.pop(); 70 | } 71 | } 72 | 73 | Ok(out) 74 | } 75 | 76 | fn encoded_len(src_len: usize) -> Option { 77 | let mut len = (src_len / 3).checked_mul(4)?; 78 | 79 | if src_len % 3 != 0 { 80 | len = len.checked_add(4)?; 81 | } 82 | 83 | len = len.checked_add(1)?; 84 | 85 | Some(len) 86 | } 87 | 88 | fn decoded_len(src_len: usize) -> Option { 89 | let mut len = (src_len / 4).checked_mul(3)?; 90 | 91 | if src_len % 4 != 0 { 92 | len = len.checked_add(3)?; 93 | } 94 | 95 | Some(len) 96 | } 97 | 98 | #[cfg(test)] 99 | mod tests { 100 | use super::*; 101 | 102 | #[test] 103 | fn test_encode_block() { 104 | assert_eq!("".to_string(), encode_block(b"")); 105 | assert_eq!("Zg==".to_string(), encode_block(b"f")); 106 | assert_eq!("Zm8=".to_string(), encode_block(b"fo")); 107 | assert_eq!("Zm9v".to_string(), encode_block(b"foo")); 108 | assert_eq!("Zm9vYg==".to_string(), encode_block(b"foob")); 109 | assert_eq!("Zm9vYmE=".to_string(), encode_block(b"fooba")); 110 | assert_eq!("Zm9vYmFy".to_string(), encode_block(b"foobar")); 111 | } 112 | 113 | #[test] 114 | fn test_decode_block() { 115 | assert_eq!(b"".to_vec(), decode_block("").unwrap()); 116 | assert_eq!(b"f".to_vec(), decode_block("Zg==").unwrap()); 117 | assert_eq!(b"fo".to_vec(), decode_block("Zm8=").unwrap()); 118 | assert_eq!(b"foo".to_vec(), decode_block("Zm9v").unwrap()); 119 | assert_eq!(b"foob".to_vec(), decode_block("Zm9vYg==").unwrap()); 120 | assert_eq!(b"fooba".to_vec(), decode_block("Zm9vYmE=").unwrap()); 121 | assert_eq!(b"foobar".to_vec(), decode_block("Zm9vYmFy").unwrap()); 122 | } 123 | 124 | #[test] 125 | fn test_strip_whitespace() { 126 | assert_eq!(b"foobar".to_vec(), decode_block(" Zm9vYmFy\n").unwrap()); 127 | assert_eq!(b"foob".to_vec(), decode_block(" Zm9vYg==\n").unwrap()); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /boring/src/bio.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi; 2 | use crate::ffi::BIO_new_mem_buf; 3 | use std::marker::PhantomData; 4 | use std::ptr; 5 | use std::slice; 6 | 7 | use crate::cvt_p; 8 | use crate::error::ErrorStack; 9 | 10 | pub struct MemBioSlice<'a>(*mut ffi::BIO, PhantomData<&'a [u8]>); 11 | 12 | impl Drop for MemBioSlice<'_> { 13 | fn drop(&mut self) { 14 | unsafe { 15 | ffi::BIO_free_all(self.0); 16 | } 17 | } 18 | } 19 | 20 | impl<'a> MemBioSlice<'a> { 21 | pub fn new(buf: &'a [u8]) -> Result, ErrorStack> { 22 | #[cfg(not(feature = "fips-compat"))] 23 | type BufLen = isize; 24 | #[cfg(feature = "fips-compat")] 25 | type BufLen = libc::c_int; 26 | 27 | ffi::init(); 28 | 29 | assert!(buf.len() <= BufLen::MAX as usize); 30 | let bio = unsafe { 31 | cvt_p(BIO_new_mem_buf( 32 | buf.as_ptr() as *const _, 33 | buf.len() as BufLen, 34 | ))? 35 | }; 36 | 37 | Ok(MemBioSlice(bio, PhantomData)) 38 | } 39 | 40 | pub fn as_ptr(&self) -> *mut ffi::BIO { 41 | self.0 42 | } 43 | } 44 | 45 | pub struct MemBio(*mut ffi::BIO); 46 | 47 | impl Drop for MemBio { 48 | fn drop(&mut self) { 49 | unsafe { 50 | ffi::BIO_free_all(self.0); 51 | } 52 | } 53 | } 54 | 55 | impl MemBio { 56 | pub fn new() -> Result { 57 | ffi::init(); 58 | 59 | let bio = unsafe { cvt_p(ffi::BIO_new(ffi::BIO_s_mem()))? }; 60 | Ok(MemBio(bio)) 61 | } 62 | 63 | pub fn as_ptr(&self) -> *mut ffi::BIO { 64 | self.0 65 | } 66 | 67 | pub fn get_buf(&self) -> &[u8] { 68 | unsafe { 69 | let mut ptr = ptr::null_mut(); 70 | let len = ffi::BIO_get_mem_data(self.0, &mut ptr); 71 | slice::from_raw_parts(ptr as *const _ as *const _, len as usize) 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /boring/src/conf.rs: -------------------------------------------------------------------------------- 1 | //! Interface for processing OpenSSL configuration files. 2 | use crate::ffi; 3 | use foreign_types::ForeignType; 4 | use libc::c_void; 5 | 6 | use crate::cvt_p; 7 | use crate::error::ErrorStack; 8 | 9 | pub struct ConfMethod(*mut c_void); 10 | 11 | impl ConfMethod { 12 | /// Construct from raw pointer. 13 | /// 14 | /// # Safety 15 | /// 16 | /// The caller must ensure that the pointer is valid. 17 | pub unsafe fn from_ptr(ptr: *mut c_void) -> ConfMethod { 18 | ConfMethod(ptr) 19 | } 20 | 21 | /// Convert to raw pointer. 22 | pub fn as_ptr(&self) -> *mut c_void { 23 | self.0 24 | } 25 | } 26 | 27 | foreign_type_and_impl_send_sync! { 28 | type CType = ffi::CONF; 29 | fn drop = ffi::NCONF_free; 30 | 31 | pub struct Conf; 32 | } 33 | 34 | impl Conf { 35 | /// Create a configuration parser. 36 | pub fn new(method: ConfMethod) -> Result { 37 | unsafe { cvt_p(ffi::NCONF_new(method.as_ptr())).map(|p| Conf::from_ptr(p)) } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /boring/src/derive.rs: -------------------------------------------------------------------------------- 1 | //! Shared secret derivation. 2 | use crate::ffi; 3 | use foreign_types::ForeignTypeRef; 4 | use openssl_macros::corresponds; 5 | use std::marker::PhantomData; 6 | use std::ptr; 7 | 8 | use crate::error::ErrorStack; 9 | use crate::pkey::{HasPrivate, HasPublic, PKeyRef}; 10 | use crate::{cvt, cvt_p}; 11 | 12 | /// A type used to derive a shared secret between two keys. 13 | pub struct Deriver<'a>(*mut ffi::EVP_PKEY_CTX, PhantomData<&'a ()>); 14 | 15 | unsafe impl Sync for Deriver<'_> {} 16 | unsafe impl Send for Deriver<'_> {} 17 | 18 | impl Drop for Deriver<'_> { 19 | fn drop(&mut self) { 20 | unsafe { 21 | ffi::EVP_PKEY_CTX_free(self.0); 22 | } 23 | } 24 | } 25 | 26 | #[allow(clippy::len_without_is_empty)] 27 | impl<'a> Deriver<'a> { 28 | /// Creates a new `Deriver` using the provided private key. 29 | #[corresponds(EVP_PKEY_derive_init)] 30 | pub fn new(key: &'a PKeyRef) -> Result, ErrorStack> 31 | where 32 | T: HasPrivate, 33 | { 34 | unsafe { 35 | cvt_p(ffi::EVP_PKEY_CTX_new(key.as_ptr(), ptr::null_mut())) 36 | .map(|p| Deriver(p, PhantomData)) 37 | .and_then(|ctx| cvt(ffi::EVP_PKEY_derive_init(ctx.0)).map(|_| ctx)) 38 | } 39 | } 40 | 41 | /// Sets the peer key used for secret derivation. 42 | #[corresponds(EVP_PKEY_derive_set_peer)] 43 | pub fn set_peer(&mut self, key: &'a PKeyRef) -> Result<(), ErrorStack> 44 | where 45 | T: HasPublic, 46 | { 47 | unsafe { cvt(ffi::EVP_PKEY_derive_set_peer(self.0, key.as_ptr())).map(|_| ()) } 48 | } 49 | 50 | /// Returns the size of the shared secret. 51 | /// 52 | /// It can be used to size the buffer passed to [`Deriver::derive`]. 53 | #[corresponds(EVP_PKEY_derive)] 54 | /// [`EVP_PKEY_derive`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_derive_init.html 55 | pub fn len(&mut self) -> Result { 56 | unsafe { 57 | let mut len = 0; 58 | cvt(ffi::EVP_PKEY_derive(self.0, ptr::null_mut(), &mut len)).map(|_| len) 59 | } 60 | } 61 | 62 | /// Derives a shared secret between the two keys, writing it into the buffer. 63 | /// 64 | /// Returns the number of bytes written. 65 | #[corresponds(EVP_PKEY_derive)] 66 | pub fn derive(&mut self, buf: &mut [u8]) -> Result { 67 | let mut len = buf.len(); 68 | unsafe { 69 | cvt(ffi::EVP_PKEY_derive( 70 | self.0, 71 | buf.as_mut_ptr() as *mut _, 72 | &mut len, 73 | )) 74 | .map(|_| len) 75 | } 76 | } 77 | 78 | /// A convenience function which derives a shared secret and returns it in a new buffer. 79 | /// 80 | /// This simply wraps [`Deriver::len`] and [`Deriver::derive`]. 81 | /// 82 | /// [`Deriver::len`]: #method.len 83 | /// [`Deriver::derive`]: #method.derive 84 | pub fn derive_to_vec(&mut self) -> Result, ErrorStack> { 85 | let len = self.len()?; 86 | let mut buf = vec![0; len]; 87 | let len = self.derive(&mut buf)?; 88 | buf.truncate(len); 89 | Ok(buf) 90 | } 91 | } 92 | 93 | #[cfg(test)] 94 | mod test { 95 | use super::*; 96 | 97 | use crate::ec::{EcGroup, EcKey}; 98 | use crate::nid::Nid; 99 | use crate::pkey::PKey; 100 | 101 | #[test] 102 | fn derive_without_peer() { 103 | let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); 104 | let ec_key = EcKey::generate(&group).unwrap(); 105 | let pkey = PKey::from_ec_key(ec_key).unwrap(); 106 | let mut deriver = Deriver::new(&pkey).unwrap(); 107 | deriver.derive_to_vec().unwrap_err(); 108 | } 109 | 110 | #[test] 111 | fn test_ec_key_derive() { 112 | let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); 113 | let ec_key = EcKey::generate(&group).unwrap(); 114 | let ec_key2 = EcKey::generate(&group).unwrap(); 115 | let pkey = PKey::from_ec_key(ec_key).unwrap(); 116 | let pkey2 = PKey::from_ec_key(ec_key2).unwrap(); 117 | let mut deriver = Deriver::new(&pkey).unwrap(); 118 | deriver.set_peer(&pkey2).unwrap(); 119 | let shared = deriver.derive_to_vec().unwrap(); 120 | assert!(!shared.is_empty()); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /boring/src/dh.rs: -------------------------------------------------------------------------------- 1 | use crate::error::ErrorStack; 2 | use crate::ffi; 3 | use foreign_types::{ForeignType, ForeignTypeRef}; 4 | use openssl_macros::corresponds; 5 | use std::mem; 6 | use std::ptr; 7 | 8 | use crate::bn::BigNum; 9 | use crate::pkey::{HasParams, Params}; 10 | use crate::{cvt, cvt_p}; 11 | 12 | generic_foreign_type_and_impl_send_sync! { 13 | type CType = ffi::DH; 14 | fn drop = ffi::DH_free; 15 | 16 | pub struct Dh; 17 | 18 | pub struct DhRef; 19 | } 20 | 21 | impl DhRef 22 | where 23 | T: HasParams, 24 | { 25 | to_pem! { 26 | /// Serializes the parameters into a PEM-encoded PKCS#3 DHparameter structure. 27 | /// 28 | /// The output will have a header of `-----BEGIN DH PARAMETERS-----`. 29 | #[corresponds(PEM_write_bio_DHparams)] 30 | params_to_pem, 31 | ffi::PEM_write_bio_DHparams 32 | } 33 | 34 | to_der! { 35 | /// Serializes the parameters into a DER-encoded PKCS#3 DHparameter structure. 36 | #[corresponds(i2d_DHparams)] 37 | params_to_der, 38 | ffi::i2d_DHparams 39 | } 40 | } 41 | 42 | impl Dh { 43 | pub fn from_params(p: BigNum, g: BigNum, q: BigNum) -> Result, ErrorStack> { 44 | unsafe { 45 | let dh = Dh::from_ptr(cvt_p(ffi::DH_new())?); 46 | cvt(DH_set0_pqg(dh.0, p.as_ptr(), q.as_ptr(), g.as_ptr()))?; 47 | mem::forget((p, g, q)); 48 | Ok(dh) 49 | } 50 | } 51 | 52 | from_pem! { 53 | /// Deserializes a PEM-encoded PKCS#3 DHpararameters structure. 54 | /// 55 | /// The input should have a header of `-----BEGIN DH PARAMETERS-----`. 56 | #[corresponds(PEM_read_bio_DHparams)] 57 | params_from_pem, 58 | Dh, 59 | ffi::PEM_read_bio_DHparams 60 | } 61 | 62 | from_der! { 63 | /// Deserializes a DER-encoded PKCS#3 DHparameters structure. 64 | #[corresponds(d2i_DHparams)] 65 | params_from_der, 66 | Dh, 67 | ffi::d2i_DHparams, 68 | ::libc::c_long 69 | } 70 | } 71 | 72 | use crate::ffi::DH_set0_pqg; 73 | 74 | #[cfg(test)] 75 | mod tests { 76 | use crate::bn::BigNum; 77 | use crate::dh::Dh; 78 | use crate::ssl::{SslContext, SslMethod}; 79 | 80 | #[test] 81 | fn test_dh() { 82 | let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); 83 | let p = BigNum::from_hex_str( 84 | "87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F25D2CEED4435E3B00E00DF8F1D61957D4FAF7DF\ 85 | 4561B2AA3016C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD5A8A9D306BCF67ED91F9E6725B47\ 86 | 58C022E0B1EF4275BF7B6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C4FDB70C581B23F76B6\ 87 | 3ACAE1CAA6B7902D52526735488A0EF13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D967E144E5\ 88 | 140564251CCACB83E6B486F6B3CA3F7971506026C0B857F689962856DED4010ABD0BE621C3A3960A54E710\ 89 | C375F26375D7014103A4B54330C198AF126116D2276E11715F693877FAD7EF09CADB094AE91E1A1597", 90 | ).unwrap(); 91 | let g = BigNum::from_hex_str( 92 | "3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF205407F4793A1A0BA12510DBC15077BE463FFF4FED\ 93 | 4AAC0BB555BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18A55AE31341000A650196F931C77A\ 94 | 57F2DDF463E5E9EC144B777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC831D14348F6F2F9193B5\ 95 | 045AF2767164E1DFC967C1FB3F2E55A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14C8484B1E\ 96 | 052588B9B7D2BBD2DF016199ECD06E1557CD0915B3353BBB64E0EC377FD028370DF92B52C7891428CDC67E\ 97 | B6184B523D1DB246C32F63078490F00EF8D647D148D47954515E2327CFEF98C582664B4C0F6CC41659", 98 | ).unwrap(); 99 | let q = BigNum::from_hex_str( 100 | "8CF83642A709A097B447997640129DA299B1A47D1EB3750BA308B0FE64F5FBD3", 101 | ) 102 | .unwrap(); 103 | let dh = Dh::from_params(p, g, q).unwrap(); 104 | ctx.set_tmp_dh(&dh).unwrap(); 105 | } 106 | 107 | #[test] 108 | fn test_dh_from_pem() { 109 | let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); 110 | let params = include_bytes!("../test/dhparams.pem"); 111 | let dh = Dh::params_from_pem(params).unwrap(); 112 | ctx.set_tmp_dh(&dh).unwrap(); 113 | } 114 | 115 | #[test] 116 | fn test_dh_from_der() { 117 | let params = include_bytes!("../test/dhparams.pem"); 118 | let dh = Dh::params_from_pem(params).unwrap(); 119 | let der = dh.params_to_der().unwrap(); 120 | Dh::params_from_der(&der).unwrap(); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /boring/src/ecdsa.rs: -------------------------------------------------------------------------------- 1 | //! Low level Elliptic Curve Digital Signature Algorithm (ECDSA) functions. 2 | 3 | use crate::ffi; 4 | use foreign_types::{ForeignType, ForeignTypeRef}; 5 | use libc::{c_int, size_t}; 6 | use openssl_macros::corresponds; 7 | use std::mem; 8 | use std::ptr; 9 | 10 | use crate::bn::{BigNum, BigNumRef}; 11 | use crate::ec::EcKeyRef; 12 | use crate::error::ErrorStack; 13 | use crate::pkey::{HasPrivate, HasPublic}; 14 | use crate::{cvt_n, cvt_p}; 15 | 16 | foreign_type_and_impl_send_sync! { 17 | type CType = ffi::ECDSA_SIG; 18 | fn drop = ffi::ECDSA_SIG_free; 19 | 20 | /// A low level interface to ECDSA 21 | /// 22 | /// OpenSSL documentation at [`ECDSA_sign`] 23 | /// 24 | /// [`ECDSA_sign`]: https://www.openssl.org/docs/man1.1.0/crypto/ECDSA_sign.html 25 | pub struct EcdsaSig; 26 | } 27 | 28 | impl EcdsaSig { 29 | /// Computes a digital signature of the hash value `data` using the private EC key eckey. 30 | #[corresponds(ECDSA_do_sign)] 31 | pub fn sign(data: &[u8], eckey: &EcKeyRef) -> Result 32 | where 33 | T: HasPrivate, 34 | { 35 | unsafe { 36 | assert!(data.len() <= c_int::MAX as usize); 37 | let sig = cvt_p(ffi::ECDSA_do_sign( 38 | data.as_ptr(), 39 | data.len() as size_t, 40 | eckey.as_ptr(), 41 | ))?; 42 | Ok(EcdsaSig::from_ptr(sig as *mut _)) 43 | } 44 | } 45 | 46 | /// Returns a new `EcdsaSig` by setting the `r` and `s` values associated with a 47 | /// ECDSA signature. 48 | #[corresponds(ECDSA_SIG_set0)] 49 | pub fn from_private_components(r: BigNum, s: BigNum) -> Result { 50 | unsafe { 51 | let sig = cvt_p(ffi::ECDSA_SIG_new())?; 52 | ECDSA_SIG_set0(sig, r.as_ptr(), s.as_ptr()); 53 | mem::forget((r, s)); 54 | Ok(EcdsaSig::from_ptr(sig as *mut _)) 55 | } 56 | } 57 | 58 | from_der! { 59 | /// Decodes a DER-encoded ECDSA signature. 60 | #[corresponds(d2i_ECDSA_SIG)] 61 | from_der, 62 | EcdsaSig, 63 | ffi::d2i_ECDSA_SIG, 64 | ::libc::c_long 65 | } 66 | } 67 | 68 | impl EcdsaSigRef { 69 | to_der! { 70 | /// Serializes the ECDSA signature into a DER-encoded ECDSASignature structure. 71 | #[corresponds(i2d_ECDSA_SIG)] 72 | to_der, 73 | ffi::i2d_ECDSA_SIG 74 | } 75 | 76 | /// Verifies if the signature is a valid ECDSA signature using the given public key. 77 | #[corresponds(ECDSA_do_verify)] 78 | pub fn verify(&self, data: &[u8], eckey: &EcKeyRef) -> Result 79 | where 80 | T: HasPublic, 81 | { 82 | unsafe { 83 | assert!(data.len() <= c_int::MAX as usize); 84 | cvt_n(ffi::ECDSA_do_verify( 85 | data.as_ptr(), 86 | data.len() as size_t, 87 | self.as_ptr(), 88 | eckey.as_ptr(), 89 | )) 90 | .map(|x| x == 1) 91 | } 92 | } 93 | 94 | /// Returns internal component: `r` of an `EcdsaSig`. (See X9.62 or FIPS 186-2) 95 | #[corresponds(ECDSA_SIG_get0)] 96 | pub fn r(&self) -> &BigNumRef { 97 | unsafe { 98 | let mut r = ptr::null(); 99 | ECDSA_SIG_get0(self.as_ptr(), &mut r, ptr::null_mut()); 100 | BigNumRef::from_ptr(r as *mut _) 101 | } 102 | } 103 | 104 | /// Returns internal components: `s` of an `EcdsaSig`. (See X9.62 or FIPS 186-2) 105 | #[corresponds(ECDSA_SIG_get0)] 106 | pub fn s(&self) -> &BigNumRef { 107 | unsafe { 108 | let mut s = ptr::null(); 109 | ECDSA_SIG_get0(self.as_ptr(), ptr::null_mut(), &mut s); 110 | BigNumRef::from_ptr(s as *mut _) 111 | } 112 | } 113 | } 114 | 115 | use crate::ffi::{ECDSA_SIG_get0, ECDSA_SIG_set0}; 116 | -------------------------------------------------------------------------------- /boring/src/ex_data.rs: -------------------------------------------------------------------------------- 1 | use libc::c_int; 2 | use std::marker::PhantomData; 3 | 4 | /// A slot in a type's "extra data" structure. 5 | /// 6 | /// It is parameterized over the type containing the extra data as well as the 7 | /// type of the data in the slot. 8 | pub struct Index(c_int, PhantomData<(T, U)>); 9 | 10 | impl Copy for Index {} 11 | 12 | impl Clone for Index { 13 | fn clone(&self) -> Index { 14 | *self 15 | } 16 | } 17 | 18 | impl Index { 19 | /// Creates an `Index` from a raw integer index. 20 | /// 21 | /// # Safety 22 | /// 23 | /// The caller must ensure that the index correctly maps to a `U` value stored in a `T`. 24 | pub unsafe fn from_raw(idx: c_int) -> Index { 25 | Index(idx, PhantomData) 26 | } 27 | 28 | #[allow(clippy::trivially_copy_pass_by_ref)] 29 | pub fn as_raw(&self) -> c_int { 30 | self.0 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /boring/src/fips.rs: -------------------------------------------------------------------------------- 1 | //! FIPS 140-2 support. 2 | //! 3 | //! See [OpenSSL's documentation] for details. 4 | //! 5 | //! [OpenSSL's documentation]: https://www.openssl.org/docs/fips/UserGuide-2.0.pdf 6 | use crate::ffi; 7 | use openssl_macros::corresponds; 8 | 9 | /// Determines if the library is running in the FIPS 140-2 mode of operation. 10 | #[corresponds(FIPS_mode)] 11 | pub fn enabled() -> bool { 12 | unsafe { ffi::FIPS_mode() != 0 } 13 | } 14 | 15 | #[test] 16 | fn is_enabled() { 17 | #[cfg(any( 18 | feature = "fips", 19 | feature = "fips-precompiled", 20 | feature = "fips-link-precompiled" 21 | ))] 22 | assert!(enabled()); 23 | #[cfg(not(any( 24 | feature = "fips", 25 | feature = "fips-precompiled", 26 | feature = "fips-link-precompiled" 27 | )))] 28 | assert!(!enabled()); 29 | } 30 | -------------------------------------------------------------------------------- /boring/src/hpke.rs: -------------------------------------------------------------------------------- 1 | use crate::error::ErrorStack; 2 | use crate::{cvt_0i, cvt_p, ffi}; 3 | 4 | use foreign_types::ForeignType; 5 | 6 | foreign_type_and_impl_send_sync! { 7 | type CType = ffi::EVP_HPKE_KEY; 8 | fn drop = ffi::EVP_HPKE_KEY_free; 9 | 10 | pub struct HpkeKey; 11 | } 12 | 13 | impl HpkeKey { 14 | /// Allocates and initializes a key with the `EVP_HPKE_KEY` type using the 15 | /// `EVP_hpke_x25519_hkdf_sha256` KEM algorithm. 16 | pub fn dhkem_p256_sha256(pkey: &[u8]) -> Result { 17 | unsafe { 18 | ffi::init(); 19 | let hpke = cvt_p(ffi::EVP_HPKE_KEY_new()).map(|p| HpkeKey::from_ptr(p))?; 20 | 21 | cvt_0i(ffi::EVP_HPKE_KEY_init( 22 | hpke.as_ptr(), 23 | ffi::EVP_hpke_x25519_hkdf_sha256(), 24 | pkey.as_ptr(), 25 | pkey.len(), 26 | ))?; 27 | 28 | Ok(hpke) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /boring/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Bindings to BoringSSL 2 | //! 3 | //! This crate provides a safe interface to the BoringSSL cryptography library. 4 | //! 5 | //! # Versioning 6 | //! 7 | //! ## Crate versioning 8 | //! 9 | //! The crate and all the related crates (FFI bindings, etc.) are released simultaneously and all 10 | //! bumped to the same version disregard whether particular crate has any API changes or not. 11 | //! However, semantic versioning guarantees still hold, as all the crate versions will be updated 12 | //! based on the crate with most significant changes. 13 | //! 14 | //! ## BoringSSL version 15 | //! 16 | //! By default, the crate aims to statically link with the latest BoringSSL master branch. 17 | //! *Note*: any BoringSSL revision bumps will be released as a major version update of all crates. 18 | //! 19 | //! # Compilation and linking options 20 | //! 21 | //! ## Environment variables 22 | //! 23 | //! This crate uses various environment variables to tweak how boring is built. The variables 24 | //! are all prefixed by `BORING_BSSL_` for non-FIPS builds, and by `BORING_BSSL_FIPS_` for FIPS builds. 25 | //! 26 | //! ## Support for pre-built binaries or custom source 27 | //! 28 | //! While this crate can build BoringSSL on its own, you may want to provide pre-built binaries instead. 29 | //! To do so, specify the environment variable `BORING_BSSL{,_FIPS}_PATH` with the path to the binaries. 30 | //! 31 | //! You can also provide specific headers by setting `BORING_BSSL{,_FIPS}_INCLUDE_PATH`. 32 | //! 33 | //! _Notes_: The crate will look for headers in the`$BORING_BSSL{,_FIPS}_INCLUDE_PATH/openssl/` 34 | //! folder, make sure to place your headers there. 35 | //! 36 | //! In alternative a different path for the BoringSSL source code directory can be specified by setting 37 | //! `BORING_BSSL{,_FIPS}_SOURCE_PATH` which will automatically be compiled during the build process. 38 | //! 39 | //! _Warning_: When providing a different version of BoringSSL make sure to use a compatible one, the 40 | //! crate relies on the presence of certain functions. 41 | //! 42 | //! ## Building with a FIPS-validated module 43 | //! 44 | //! Only BoringCrypto module version `853ca1ea1168dff08011e5d42d94609cc0ca2e27`, as certified with 45 | //! [FIPS 140-2 certificate 4407](https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/4407) 46 | //! is supported by this crate. Support is enabled by this crate's `fips` feature. 47 | //! 48 | //! `boring-sys` comes with a test that FIPS is enabled/disabled depending on the feature flag. You can run it as follows: 49 | //! 50 | //! ```bash 51 | //! $ cargo test --features fips fips::is_enabled 52 | //! ``` 53 | //! 54 | //! ## Linking current BoringSSL version with precompiled FIPS-validated module (`bcm.o`) 55 | //! 56 | //! It's possible to link latest supported version of BoringSSL with FIPS-validated crypto module 57 | //! (`bcm.o`). To enable this compilation option one should enable `fips-link-precompiled` 58 | //! compilation feature and provide a `BORING_BSSL_FIPS_PRECOMPILED_BCM_O` env variable with a path to the 59 | //! precompiled FIPS-validated `bcm.o` module. 60 | //! 61 | //! Note that `BORING_BSSL_PRECOMPILED_BCM_O` is never used, as linking BoringSSL with precompiled non-FIPS 62 | //! module is not supported. 63 | //! 64 | //! ## Linking with a C++ standard library 65 | //! 66 | //! Recent versions of boringssl require some C++ standard library features, so boring needs to link 67 | //! with a STL implementation. This can be controlled using the BORING_BSSL_RUST_CPPLIB variable. If 68 | //! no library is specified, libc++ is used on macOS and iOS whereas libstdc++ is used on other Unix 69 | //! systems. 70 | //! 71 | //! # Optional patches 72 | //! 73 | //! ## Raw Public Key 74 | //! 75 | //! The crate can be compiled with [RawPublicKey](https://datatracker.ietf.org/doc/html/rfc7250) 76 | //! support by turning on `rpk` compilation feature. 77 | //! 78 | //! ## Experimental post-quantum cryptography 79 | //! 80 | //! The crate can be compiled with [post-quantum cryptography](https://blog.cloudflare.com/post-quantum-for-all/) 81 | //! support by turning on `post-quantum` compilation feature. 82 | //! 83 | //! Upstream BoringSSL support the post-quantum hybrid key agreement `X25519Kyber768Draft00`. Most 84 | //! users should stick to that one for now. Enabling this feature, adds a few other post-quantum key 85 | //! agreements: 86 | //! 87 | //! - `X25519MLKEM768` is the successor of `X25519Kyber768Draft00`. We expect servers to switch 88 | //! before the end of 2024. 89 | //! - `X25519Kyber768Draft00Old` is the same as `X25519Kyber768Draft00`, but under its old codepoint. 90 | //! - `X25519Kyber512Draft00`. Similar to `X25519Kyber768Draft00`, but uses level 1 parameter set for 91 | //! Kyber. Not recommended. It's useful to test whether the shorter ClientHello upsets fewer middle 92 | //! boxes. 93 | //! - `P256Kyber768Draft00`. Similar again to `X25519Kyber768Draft00`, but uses P256 as classical 94 | //! part. It uses a non-standard codepoint. Not recommended. 95 | //! 96 | //! Presently all these key agreements are deployed by Cloudflare, but we do not guarantee continued 97 | //! support for them. 98 | 99 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 100 | 101 | #[macro_use] 102 | extern crate bitflags; 103 | #[macro_use] 104 | extern crate foreign_types; 105 | extern crate boring_sys as ffi; 106 | extern crate libc; 107 | 108 | #[cfg(test)] 109 | extern crate hex; 110 | 111 | use std::ffi::{c_long, c_void}; 112 | 113 | #[doc(inline)] 114 | pub use crate::ffi::init; 115 | 116 | use libc::{c_int, size_t}; 117 | 118 | use crate::error::ErrorStack; 119 | 120 | #[macro_use] 121 | mod macros; 122 | 123 | mod bio; 124 | #[macro_use] 125 | mod util; 126 | pub mod aes; 127 | pub mod asn1; 128 | pub mod base64; 129 | pub mod bn; 130 | pub mod conf; 131 | pub mod derive; 132 | pub mod dh; 133 | pub mod dsa; 134 | pub mod ec; 135 | pub mod ecdsa; 136 | pub mod error; 137 | pub mod ex_data; 138 | pub mod fips; 139 | pub mod hash; 140 | #[cfg(not(feature = "fips"))] 141 | pub mod hpke; 142 | pub mod memcmp; 143 | pub mod nid; 144 | pub mod pkcs12; 145 | pub mod pkcs5; 146 | pub mod pkey; 147 | pub mod rand; 148 | pub mod rsa; 149 | pub mod sha; 150 | pub mod sign; 151 | pub mod srtp; 152 | pub mod ssl; 153 | pub mod stack; 154 | pub mod string; 155 | pub mod symm; 156 | pub mod version; 157 | pub mod x509; 158 | 159 | fn cvt_p(r: *mut T) -> Result<*mut T, ErrorStack> { 160 | if r.is_null() { 161 | Err(ErrorStack::get()) 162 | } else { 163 | Ok(r) 164 | } 165 | } 166 | 167 | fn cvt_0(r: size_t) -> Result { 168 | if r == 0 { 169 | Err(ErrorStack::get()) 170 | } else { 171 | Ok(r) 172 | } 173 | } 174 | 175 | fn cvt_0i(r: c_int) -> Result { 176 | if r == 0 { 177 | Err(ErrorStack::get()) 178 | } else { 179 | Ok(r) 180 | } 181 | } 182 | 183 | fn cvt(r: c_int) -> Result { 184 | if r <= 0 { 185 | Err(ErrorStack::get()) 186 | } else { 187 | Ok(r) 188 | } 189 | } 190 | 191 | fn cvt_n(r: c_int) -> Result { 192 | if r < 0 { 193 | Err(ErrorStack::get()) 194 | } else { 195 | Ok(r) 196 | } 197 | } 198 | 199 | unsafe extern "C" fn free_data_box( 200 | _parent: *mut c_void, 201 | ptr: *mut c_void, 202 | _ad: *mut ffi::CRYPTO_EX_DATA, 203 | _idx: c_int, 204 | _argl: c_long, 205 | _argp: *mut c_void, 206 | ) { 207 | if !ptr.is_null() { 208 | drop(Box::::from_raw(ptr as *mut T)); 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /boring/src/memcmp.rs: -------------------------------------------------------------------------------- 1 | //! Utilities to safely compare cryptographic values. 2 | //! 3 | //! Extra care must be taken when comparing values in 4 | //! cryptographic code. If done incorrectly, it can lead 5 | //! to a [timing attack](https://en.wikipedia.org/wiki/Timing_attack). 6 | //! By analyzing the time taken to execute parts of a cryptographic 7 | //! algorithm, and attacker can attempt to compromise the 8 | //! cryptosystem. 9 | //! 10 | //! The utilities in this module are designed to be resistant 11 | //! to this type of attack. 12 | //! 13 | //! # Examples 14 | //! 15 | //! To perform a constant-time comparison of two arrays of the same length but different 16 | //! values: 17 | //! 18 | //! ``` 19 | //! use boring::memcmp::eq; 20 | //! 21 | //! // We want to compare `a` to `b` and `c`, without giving 22 | //! // away through timing analysis that `c` is more similar to `a` 23 | //! // than `b`. 24 | //! let a = [0, 0, 0]; 25 | //! let b = [1, 1, 1]; 26 | //! let c = [0, 0, 1]; 27 | //! 28 | //! // These statements will execute in the same amount of time. 29 | //! assert!(!eq(&a, &b)); 30 | //! assert!(!eq(&a, &c)); 31 | //! ``` 32 | use crate::ffi; 33 | use libc::size_t; 34 | 35 | /// Returns `true` iff `a` and `b` contain the same bytes. 36 | /// 37 | /// This operation takes an amount of time dependent on the length of the two 38 | /// arrays given, but is independent of the contents of a and b. 39 | /// 40 | /// # Panics 41 | /// 42 | /// This function will panic the current task if `a` and `b` do not have the same 43 | /// length. 44 | /// 45 | /// # Examples 46 | /// 47 | /// To perform a constant-time comparison of two arrays of the same length but different 48 | /// values: 49 | /// 50 | /// ``` 51 | /// use boring::memcmp::eq; 52 | /// 53 | /// // We want to compare `a` to `b` and `c`, without giving 54 | /// // away through timing analysis that `c` is more similar to `a` 55 | /// // than `b`. 56 | /// let a = [0, 0, 0]; 57 | /// let b = [1, 1, 1]; 58 | /// let c = [0, 0, 1]; 59 | /// 60 | /// // These statements will execute in the same amount of time. 61 | /// assert!(!eq(&a, &b)); 62 | /// assert!(!eq(&a, &c)); 63 | /// ``` 64 | pub fn eq(a: &[u8], b: &[u8]) -> bool { 65 | assert!(a.len() == b.len()); 66 | let ret = unsafe { 67 | ffi::CRYPTO_memcmp( 68 | a.as_ptr() as *const _, 69 | b.as_ptr() as *const _, 70 | a.len() as size_t, 71 | ) 72 | }; 73 | ret == 0 74 | } 75 | 76 | #[cfg(test)] 77 | mod tests { 78 | use super::eq; 79 | 80 | #[test] 81 | fn test_eq() { 82 | assert!(eq(&[], &[])); 83 | assert!(eq(&[1], &[1])); 84 | assert!(!eq(&[1, 2, 3], &[1, 2, 4])); 85 | } 86 | 87 | #[test] 88 | #[should_panic] 89 | fn test_diff_lens() { 90 | eq(&[], &[1]); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /boring/src/rand.rs: -------------------------------------------------------------------------------- 1 | //! Utilities for secure random number generation. 2 | //! 3 | //! # Examples 4 | //! 5 | //! To generate a buffer with cryptographically strong bytes: 6 | //! 7 | //! ``` 8 | //! use boring::rand::rand_bytes; 9 | //! 10 | //! let mut buf = [0; 256]; 11 | //! rand_bytes(&mut buf).unwrap(); 12 | //! ``` 13 | use crate::ffi; 14 | use libc::c_int; 15 | 16 | use crate::cvt; 17 | use crate::error::ErrorStack; 18 | 19 | /// Fill buffer with cryptographically strong pseudo-random bytes. 20 | /// 21 | /// This corresponds to [`RAND_bytes`]. 22 | /// 23 | /// # Examples 24 | /// 25 | /// To generate a buffer with cryptographically strong bytes: 26 | /// 27 | /// ``` 28 | /// use boring::rand::rand_bytes; 29 | /// 30 | /// let mut buf = [0; 256]; 31 | /// rand_bytes(&mut buf).unwrap(); 32 | /// ``` 33 | /// 34 | /// [`RAND_bytes`]: https://www.openssl.org/docs/man1.1.0/crypto/RAND_bytes.html 35 | pub fn rand_bytes(buf: &mut [u8]) -> Result<(), ErrorStack> { 36 | unsafe { 37 | ffi::init(); 38 | assert!(buf.len() <= c_int::MAX as usize); 39 | cvt(ffi::RAND_bytes(buf.as_mut_ptr(), buf.len())).map(|_| ()) 40 | } 41 | } 42 | 43 | #[cfg(test)] 44 | mod tests { 45 | use super::rand_bytes; 46 | 47 | #[test] 48 | fn test_rand_bytes() { 49 | let mut buf = [0; 32]; 50 | rand_bytes(&mut buf).unwrap(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /boring/src/srtp.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi; 2 | use crate::stack::Stackable; 3 | use foreign_types::ForeignTypeRef; 4 | use libc::c_ulong; 5 | use std::ffi::CStr; 6 | use std::str; 7 | 8 | /// fake free method, since SRTP_PROTECTION_PROFILE is static 9 | unsafe fn free(_profile: *mut ffi::SRTP_PROTECTION_PROFILE) {} 10 | 11 | foreign_type_and_impl_send_sync! { 12 | type CType = ffi::SRTP_PROTECTION_PROFILE; 13 | fn drop = free; 14 | 15 | pub struct SrtpProtectionProfile; 16 | } 17 | 18 | impl Stackable for SrtpProtectionProfile { 19 | type StackType = ffi::stack_st_SRTP_PROTECTION_PROFILE; 20 | } 21 | 22 | impl SrtpProtectionProfileRef { 23 | pub fn id(&self) -> SrtpProfileId { 24 | SrtpProfileId::from_raw(unsafe { (*self.as_ptr()).id }) 25 | } 26 | pub fn name(&self) -> &'static str { 27 | unsafe { CStr::from_ptr((*self.as_ptr()).name as *const _) } 28 | .to_str() 29 | .expect("should be UTF-8") 30 | } 31 | } 32 | 33 | /// An identifier of an SRTP protection profile. 34 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 35 | pub struct SrtpProfileId(c_ulong); 36 | 37 | impl SrtpProfileId { 38 | pub const SRTP_AES128_CM_SHA1_80: SrtpProfileId = 39 | SrtpProfileId(ffi::SRTP_AES128_CM_SHA1_80 as _); 40 | pub const SRTP_AES128_CM_SHA1_32: SrtpProfileId = 41 | SrtpProfileId(ffi::SRTP_AES128_CM_SHA1_32 as _); 42 | pub const SRTP_AES128_F8_SHA1_80: SrtpProfileId = 43 | SrtpProfileId(ffi::SRTP_AES128_F8_SHA1_80 as _); 44 | pub const SRTP_AES128_F8_SHA1_32: SrtpProfileId = 45 | SrtpProfileId(ffi::SRTP_AES128_F8_SHA1_32 as _); 46 | pub const SRTP_NULL_SHA1_80: SrtpProfileId = SrtpProfileId(ffi::SRTP_NULL_SHA1_80 as _); 47 | pub const SRTP_NULL_SHA1_32: SrtpProfileId = SrtpProfileId(ffi::SRTP_NULL_SHA1_32 as _); 48 | 49 | /// Creates a `SrtpProfileId` from an integer representation. 50 | pub fn from_raw(value: c_ulong) -> SrtpProfileId { 51 | SrtpProfileId(value) 52 | } 53 | 54 | /// Returns the integer representation of `SrtpProfileId`. 55 | #[allow(clippy::trivially_copy_pass_by_ref)] 56 | pub fn as_raw(&self) -> c_ulong { 57 | self.0 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /boring/src/ssl/bio.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::{ 2 | self, BIO_clear_retry_flags, BIO_new, BIO_set_retry_read, BIO_set_retry_write, BIO, 3 | BIO_CTRL_DGRAM_QUERY_MTU, BIO_CTRL_FLUSH, 4 | }; 5 | use libc::{c_char, c_int, c_long, c_void, strlen}; 6 | use std::any::Any; 7 | use std::io; 8 | use std::io::prelude::*; 9 | use std::panic::{catch_unwind, AssertUnwindSafe}; 10 | use std::ptr; 11 | use std::slice; 12 | 13 | use crate::cvt_p; 14 | use crate::error::ErrorStack; 15 | 16 | pub struct StreamState { 17 | pub stream: S, 18 | pub error: Option, 19 | pub panic: Option>, 20 | pub dtls_mtu_size: c_long, 21 | } 22 | 23 | /// Safe wrapper for BIO_METHOD 24 | pub struct BioMethod(BIO_METHOD); 25 | 26 | impl BioMethod { 27 | fn new() -> BioMethod { 28 | BioMethod(BIO_METHOD::new::()) 29 | } 30 | } 31 | 32 | unsafe impl Sync for BioMethod {} 33 | unsafe impl Send for BioMethod {} 34 | 35 | pub fn new(stream: S) -> Result<(*mut BIO, BioMethod), ErrorStack> { 36 | let method = BioMethod::new::(); 37 | 38 | let state = Box::new(StreamState { 39 | stream, 40 | error: None, 41 | panic: None, 42 | dtls_mtu_size: 0, 43 | }); 44 | 45 | unsafe { 46 | let bio = cvt_p(BIO_new(method.0.get()))?; 47 | BIO_set_data(bio, Box::into_raw(state) as *mut _); 48 | BIO_set_init(bio, 1); 49 | 50 | Ok((bio, method)) 51 | } 52 | } 53 | 54 | pub unsafe fn take_error(bio: *mut BIO) -> Option { 55 | let state = state::(bio); 56 | state.error.take() 57 | } 58 | 59 | pub unsafe fn take_panic(bio: *mut BIO) -> Option> { 60 | let state = state::(bio); 61 | state.panic.take() 62 | } 63 | 64 | pub unsafe fn get_ref<'a, S: 'a>(bio: *mut BIO) -> &'a S { 65 | &state(bio).stream 66 | } 67 | 68 | pub unsafe fn get_mut<'a, S: 'a>(bio: *mut BIO) -> &'a mut S { 69 | &mut state(bio).stream 70 | } 71 | 72 | pub unsafe extern "C" fn take_stream(bio: *mut BIO) -> S { 73 | assert!(!bio.is_null()); 74 | 75 | let data = BIO_get_data(bio); 76 | 77 | assert!(!data.is_null()); 78 | 79 | let state = Box::>::from_raw(data as *mut _); 80 | 81 | BIO_set_data(bio, ptr::null_mut()); 82 | 83 | state.stream 84 | } 85 | 86 | pub unsafe fn set_dtls_mtu_size(bio: *mut BIO, mtu_size: usize) { 87 | if mtu_size as u64 > c_long::MAX as u64 { 88 | panic!( 89 | "Given MTU size {} can't be represented in a positive `c_long` range", 90 | mtu_size 91 | ) 92 | } 93 | state::(bio).dtls_mtu_size = mtu_size as c_long; 94 | } 95 | 96 | unsafe fn state<'a, S: 'a>(bio: *mut BIO) -> &'a mut StreamState { 97 | let data = BIO_get_data(bio) as *mut StreamState; 98 | 99 | assert!(!data.is_null()); 100 | 101 | &mut *data 102 | } 103 | 104 | unsafe extern "C" fn bwrite(bio: *mut BIO, buf: *const c_char, len: c_int) -> c_int { 105 | BIO_clear_retry_flags(bio); 106 | 107 | let state = state::(bio); 108 | let buf = slice::from_raw_parts(buf as *const _, len as usize); 109 | 110 | match catch_unwind(AssertUnwindSafe(|| state.stream.write(buf))) { 111 | Ok(Ok(len)) => len as c_int, 112 | Ok(Err(err)) => { 113 | if retriable_error(&err) { 114 | BIO_set_retry_write(bio); 115 | } 116 | state.error = Some(err); 117 | -1 118 | } 119 | Err(err) => { 120 | state.panic = Some(err); 121 | -1 122 | } 123 | } 124 | } 125 | 126 | unsafe extern "C" fn bread(bio: *mut BIO, buf: *mut c_char, len: c_int) -> c_int { 127 | BIO_clear_retry_flags(bio); 128 | 129 | let state = state::(bio); 130 | let buf = slice::from_raw_parts_mut(buf as *mut _, len as usize); 131 | 132 | match catch_unwind(AssertUnwindSafe(|| state.stream.read(buf))) { 133 | Ok(Ok(len)) => len as c_int, 134 | Ok(Err(err)) => { 135 | if retriable_error(&err) { 136 | BIO_set_retry_read(bio); 137 | } 138 | state.error = Some(err); 139 | -1 140 | } 141 | Err(err) => { 142 | state.panic = Some(err); 143 | -1 144 | } 145 | } 146 | } 147 | 148 | #[allow(clippy::match_like_matches_macro)] // matches macro requires rust 1.42.0 149 | fn retriable_error(err: &io::Error) -> bool { 150 | match err.kind() { 151 | io::ErrorKind::WouldBlock | io::ErrorKind::NotConnected => true, 152 | _ => false, 153 | } 154 | } 155 | 156 | unsafe extern "C" fn bputs(bio: *mut BIO, s: *const c_char) -> c_int { 157 | bwrite::(bio, s, strlen(s) as c_int) 158 | } 159 | 160 | unsafe extern "C" fn ctrl( 161 | bio: *mut BIO, 162 | cmd: c_int, 163 | _num: c_long, 164 | _ptr: *mut c_void, 165 | ) -> c_long { 166 | let state = state::(bio); 167 | 168 | if cmd == BIO_CTRL_FLUSH { 169 | match catch_unwind(AssertUnwindSafe(|| state.stream.flush())) { 170 | Ok(Ok(())) => 1, 171 | Ok(Err(err)) => { 172 | state.error = Some(err); 173 | 0 174 | } 175 | Err(err) => { 176 | state.panic = Some(err); 177 | 0 178 | } 179 | } 180 | } else if cmd == BIO_CTRL_DGRAM_QUERY_MTU { 181 | state.dtls_mtu_size 182 | } else { 183 | 0 184 | } 185 | } 186 | 187 | unsafe extern "C" fn create(bio: *mut BIO) -> c_int { 188 | BIO_set_init(bio, 0); 189 | BIO_set_num(bio, 0); 190 | BIO_set_data(bio, ptr::null_mut()); 191 | BIO_set_flags(bio, 0); 192 | 1 193 | } 194 | 195 | unsafe extern "C" fn destroy(bio: *mut BIO) -> c_int { 196 | if bio.is_null() { 197 | return 0; 198 | } 199 | 200 | let data = BIO_get_data(bio); 201 | 202 | if !data.is_null() { 203 | drop(Box::>::from_raw(data as *mut _)); 204 | BIO_set_data(bio, ptr::null_mut()); 205 | } 206 | 207 | BIO_set_init(bio, 0); 208 | 1 209 | } 210 | 211 | use crate::ffi::{BIO_get_data, BIO_set_data, BIO_set_flags, BIO_set_init}; 212 | 213 | #[allow(bad_style)] 214 | unsafe fn BIO_set_num(_bio: *mut ffi::BIO, _num: c_int) {} 215 | 216 | #[allow(bad_style, clippy::upper_case_acronyms)] 217 | struct BIO_METHOD(*mut ffi::BIO_METHOD); 218 | 219 | impl BIO_METHOD { 220 | fn new() -> BIO_METHOD { 221 | unsafe { 222 | let ptr = ffi::BIO_meth_new(ffi::BIO_TYPE_NONE, c"rust".as_ptr().cast()); 223 | assert!(!ptr.is_null()); 224 | let ret = BIO_METHOD(ptr); 225 | assert!(ffi::BIO_meth_set_write(ptr, Some(bwrite::)) != 0); 226 | assert!(ffi::BIO_meth_set_read(ptr, Some(bread::)) != 0); 227 | assert!(ffi::BIO_meth_set_puts(ptr, Some(bputs::)) != 0); 228 | assert!(ffi::BIO_meth_set_ctrl(ptr, Some(ctrl::)) != 0); 229 | assert!(ffi::BIO_meth_set_create(ptr, Some(create)) != 0); 230 | assert!(ffi::BIO_meth_set_destroy(ptr, Some(destroy::)) != 0); 231 | ret 232 | } 233 | } 234 | 235 | fn get(&self) -> *mut ffi::BIO_METHOD { 236 | self.0 237 | } 238 | } 239 | 240 | impl Drop for BIO_METHOD { 241 | fn drop(&mut self) { 242 | unsafe { 243 | ffi::BIO_meth_free(self.0); 244 | } 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /boring/src/ssl/ech.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi; 2 | use foreign_types::ForeignType; 3 | use libc::c_int; 4 | 5 | use crate::error::ErrorStack; 6 | use crate::hpke::HpkeKey; 7 | use crate::{cvt_0i, cvt_p}; 8 | 9 | pub struct SslEchKeysBuilder { 10 | keys: SslEchKeys, 11 | } 12 | 13 | impl SslEchKeysBuilder { 14 | pub fn new() -> Result { 15 | unsafe { 16 | ffi::init(); 17 | let keys = cvt_p(ffi::SSL_ECH_KEYS_new())?; 18 | 19 | Ok(SslEchKeysBuilder::from_ptr(keys)) 20 | } 21 | } 22 | 23 | pub unsafe fn from_ptr(keys: *mut ffi::SSL_ECH_KEYS) -> Self { 24 | Self { 25 | keys: SslEchKeys::from_ptr(keys), 26 | } 27 | } 28 | 29 | pub fn add_key( 30 | &mut self, 31 | is_retry_config: bool, 32 | ech_config: &[u8], 33 | key: HpkeKey, 34 | ) -> Result<(), ErrorStack> { 35 | unsafe { 36 | cvt_0i(ffi::SSL_ECH_KEYS_add( 37 | self.keys.as_ptr(), 38 | is_retry_config as c_int, 39 | ech_config.as_ptr(), 40 | ech_config.len(), 41 | key.as_ptr(), 42 | )) 43 | .map(|_| ()) 44 | } 45 | } 46 | 47 | pub fn build(self) -> SslEchKeys { 48 | self.keys 49 | } 50 | } 51 | 52 | foreign_type_and_impl_send_sync! { 53 | type CType = ffi::SSL_ECH_KEYS; 54 | fn drop = ffi::SSL_ECH_KEYS_free; 55 | 56 | pub struct SslEchKeys; 57 | } 58 | 59 | impl SslEchKeys { 60 | pub fn builder() -> Result { 61 | SslEchKeysBuilder::new() 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /boring/src/ssl/mut_only.rs: -------------------------------------------------------------------------------- 1 | pub(crate) struct MutOnly(T); 2 | 3 | impl MutOnly { 4 | pub(crate) fn new(value: T) -> Self { 5 | Self(value) 6 | } 7 | 8 | pub(crate) fn get_mut(&mut self) -> &mut T { 9 | &mut self.0 10 | } 11 | } 12 | 13 | /// SAFETY: The type does not let anyone get a &T so Sync is irrelevant. 14 | unsafe impl Sync for MutOnly {} 15 | -------------------------------------------------------------------------------- /boring/src/ssl/test/cert_compressor.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write as _; 2 | 3 | use super::server::Server; 4 | use crate::ssl::CertificateCompressor; 5 | use crate::x509::store::X509StoreBuilder; 6 | use crate::x509::X509; 7 | 8 | struct BrotliCompressor { 9 | q: u32, 10 | lgwin: u32, 11 | } 12 | 13 | impl Default for BrotliCompressor { 14 | fn default() -> Self { 15 | Self { q: 11, lgwin: 32 } 16 | } 17 | } 18 | 19 | impl CertificateCompressor for BrotliCompressor { 20 | const ALGORITHM: crate::ssl::CertificateCompressionAlgorithm = 21 | crate::ssl::CertificateCompressionAlgorithm(1234); 22 | 23 | const CAN_COMPRESS: bool = true; 24 | 25 | const CAN_DECOMPRESS: bool = true; 26 | 27 | fn compress(&self, input: &[u8], output: &mut W) -> std::io::Result<()> 28 | where 29 | W: std::io::Write, 30 | { 31 | let mut writer = brotli::CompressorWriter::new(output, 1024, self.q, self.lgwin); 32 | writer.write_all(input)?; 33 | Ok(()) 34 | } 35 | 36 | fn decompress(&self, input: &[u8], output: &mut W) -> std::io::Result<()> 37 | where 38 | W: std::io::Write, 39 | { 40 | brotli::BrotliDecompress(&mut std::io::Cursor::new(input), output)?; 41 | Ok(()) 42 | } 43 | } 44 | 45 | #[test] 46 | fn server_only_cert_compression() { 47 | let mut server_builder = Server::builder(); 48 | server_builder 49 | .ctx() 50 | .add_certificate_compression_algorithm(BrotliCompressor::default()) 51 | .unwrap(); 52 | 53 | let server = server_builder.build(); 54 | 55 | let mut store = X509StoreBuilder::new().unwrap(); 56 | let x509 = X509::from_pem(super::ROOT_CERT).unwrap(); 57 | store.add_cert(x509).unwrap(); 58 | 59 | let client = server.client(); 60 | 61 | client.connect(); 62 | } 63 | 64 | #[test] 65 | fn client_only_cert_compression() { 66 | let server_builder = Server::builder().build(); 67 | 68 | let mut store = X509StoreBuilder::new().unwrap(); 69 | let x509 = X509::from_pem(super::ROOT_CERT).unwrap(); 70 | store.add_cert(x509).unwrap(); 71 | 72 | let mut client = server_builder.client(); 73 | client 74 | .ctx() 75 | .add_certificate_compression_algorithm(BrotliCompressor::default()) 76 | .unwrap(); 77 | 78 | client.connect(); 79 | } 80 | 81 | #[test] 82 | fn client_and_server_cert_compression() { 83 | let mut server = Server::builder(); 84 | server 85 | .ctx() 86 | .add_certificate_compression_algorithm(BrotliCompressor::default()) 87 | .unwrap(); 88 | 89 | let server = server.build(); 90 | 91 | let mut store = X509StoreBuilder::new().unwrap(); 92 | let x509 = X509::from_pem(super::ROOT_CERT).unwrap(); 93 | store.add_cert(x509).unwrap(); 94 | 95 | let mut client = server.client(); 96 | client 97 | .ctx() 98 | .add_certificate_compression_algorithm(BrotliCompressor::default()) 99 | .unwrap(); 100 | 101 | client.connect(); 102 | } 103 | -------------------------------------------------------------------------------- /boring/src/ssl/test/cert_verify.rs: -------------------------------------------------------------------------------- 1 | use crate::hash::MessageDigest; 2 | use crate::ssl::test::Server; 3 | use crate::ssl::SslVerifyMode; 4 | 5 | #[test] 6 | fn error_when_trusted_but_callback_returns_false() { 7 | let mut server = Server::builder(); 8 | server.should_error(); 9 | let server = server.build(); 10 | let mut client = server.client_with_root_ca(); 11 | client.ctx().set_verify(SslVerifyMode::PEER); 12 | client.ctx().set_cert_verify_callback(|x509| { 13 | // The cert is OK 14 | assert!(x509.verify_cert().unwrap()); 15 | assert!(x509.current_cert().is_some()); 16 | assert!(x509.verify_result().is_ok()); 17 | // But we return false 18 | false 19 | }); 20 | 21 | client.connect_err(); 22 | } 23 | 24 | #[test] 25 | fn no_error_when_untrusted_but_callback_returns_true() { 26 | let server = Server::builder().build(); 27 | let mut client = server.client(); 28 | client.ctx().set_verify(SslVerifyMode::PEER); 29 | client.ctx().set_cert_verify_callback(|x509| { 30 | // The cert is not OK 31 | assert!(!x509.verify_cert().unwrap()); 32 | assert!(x509.current_cert().is_some()); 33 | assert!(x509.verify_result().is_err()); 34 | // But we return true 35 | true 36 | }); 37 | 38 | client.connect(); 39 | } 40 | 41 | #[test] 42 | fn no_error_when_trusted_and_callback_returns_true() { 43 | let server = Server::builder().build(); 44 | let mut client = server.client_with_root_ca(); 45 | client.ctx().set_verify(SslVerifyMode::PEER); 46 | client.ctx().set_cert_verify_callback(|x509| { 47 | // The cert is OK 48 | assert!(x509.verify_cert().unwrap()); 49 | assert!(x509.current_cert().is_some()); 50 | assert!(x509.verify_result().is_ok()); 51 | // And we return true 52 | true 53 | }); 54 | client.connect(); 55 | } 56 | 57 | #[test] 58 | fn callback_receives_correct_certificate() { 59 | // Server sends the full chain (leaf + root)... 60 | let server = Server::builder_full_chain().build(); 61 | // but client doesn't load the root as trusted. 62 | // So we expect an error. 63 | let mut client = server.client(); 64 | let leaf_sha1 = "59172d9313e84459bcff27f967e79e6e9217e584"; 65 | let root_sha1 = "c0cbdf7cdd03c9773e5468e1f6d2da7d5cbb1875"; 66 | client.ctx().set_verify(SslVerifyMode::PEER); 67 | client.ctx().set_cert_verify_callback(move |x509| { 68 | assert!(!x509.verify_cert().unwrap()); 69 | // This is set to the root, since that's the problematic cert. 70 | assert!(x509.current_cert().is_some()); 71 | // This is set to the leaf, since that's the cert we're verifying. 72 | assert!(x509.cert().is_some()); 73 | assert!(x509.verify_result().is_err()); 74 | 75 | let root = x509 76 | .current_cert() 77 | .unwrap() 78 | .digest(MessageDigest::sha1()) 79 | .unwrap(); 80 | assert_eq!(hex::encode(root), root_sha1); 81 | 82 | let leaf = x509.cert().unwrap().digest(MessageDigest::sha1()).unwrap(); 83 | assert_eq!(hex::encode(leaf), leaf_sha1); 84 | 85 | // Test that `untrusted` is set to the original chain. 86 | assert_eq!(x509.untrusted().unwrap().len(), 2); 87 | let leaf = x509 88 | .untrusted() 89 | .unwrap() 90 | .get(0) 91 | .unwrap() 92 | .digest(MessageDigest::sha1()) 93 | .unwrap(); 94 | assert_eq!(hex::encode(leaf), leaf_sha1); 95 | let root = x509 96 | .untrusted() 97 | .unwrap() 98 | .get(1) 99 | .unwrap() 100 | .digest(MessageDigest::sha1()) 101 | .unwrap(); 102 | assert_eq!(hex::encode(root), root_sha1); 103 | true 104 | }); 105 | 106 | client.connect(); 107 | } 108 | 109 | #[test] 110 | fn callback_receives_correct_chain() { 111 | let server = Server::builder().build(); 112 | let mut client = server.client_with_root_ca(); 113 | let leaf_sha1 = "59172d9313e84459bcff27f967e79e6e9217e584"; 114 | let root_sha1 = "c0cbdf7cdd03c9773e5468e1f6d2da7d5cbb1875"; 115 | client.ctx().set_verify(SslVerifyMode::PEER); 116 | client.ctx().set_cert_verify_callback(move |x509| { 117 | assert!(x509.verify_cert().unwrap()); 118 | assert!(x509.current_cert().is_some()); 119 | assert!(x509.verify_result().is_ok()); 120 | let chain = x509.chain().unwrap(); 121 | assert!(chain.len() == 2); 122 | let leaf_cert = chain.get(0).unwrap(); 123 | let leaf_digest = leaf_cert.digest(MessageDigest::sha1()).unwrap(); 124 | assert_eq!(hex::encode(leaf_digest), leaf_sha1); 125 | let root_cert = chain.get(1).unwrap(); 126 | let root_digest = root_cert.digest(MessageDigest::sha1()).unwrap(); 127 | assert_eq!(hex::encode(root_digest), root_sha1); 128 | true 129 | }); 130 | 131 | client.connect(); 132 | } 133 | -------------------------------------------------------------------------------- /boring/src/ssl/test/ech.rs: -------------------------------------------------------------------------------- 1 | use crate::hpke::HpkeKey; 2 | use crate::ssl::ech::SslEchKeys; 3 | use crate::ssl::test::server::{ClientSslBuilder, Server}; 4 | use crate::ssl::HandshakeError; 5 | 6 | // For future reference, these configs are generated by building the bssl tool (the binary is built 7 | // alongside boringssl) and running the following command: 8 | // 9 | // ./bssl generate-ech -out-ech-config-list ./list -out-ech-config ./config -out-private-key ./key 10 | // -public-name ech.com -config-id 1 11 | static ECH_CONFIG_LIST: &[u8] = include_bytes!("../../../test/echconfiglist"); 12 | static ECH_CONFIG: &[u8] = include_bytes!("../../../test/echconfig"); 13 | static ECH_KEY: &[u8] = include_bytes!("../../../test/echkey"); 14 | 15 | static ECH_CONFIG_2: &[u8] = include_bytes!("../../../test/echconfig-2"); 16 | static ECH_KEY_2: &[u8] = include_bytes!("../../../test/echkey-2"); 17 | 18 | fn bootstrap_ech(config: &[u8], key: &[u8], list: &[u8]) -> (Server, ClientSslBuilder) { 19 | let server = { 20 | let key = HpkeKey::dhkem_p256_sha256(key).unwrap(); 21 | let mut ech_keys_builder = SslEchKeys::builder().unwrap(); 22 | ech_keys_builder.add_key(true, config, key).unwrap(); 23 | let ech_keys = ech_keys_builder.build(); 24 | 25 | let mut builder = Server::builder(); 26 | builder.ctx().set_ech_keys(&ech_keys).unwrap(); 27 | 28 | builder.build() 29 | }; 30 | 31 | let mut client = server.client_with_root_ca().build().builder(); 32 | client.ssl().set_ech_config_list(list).unwrap(); 33 | client.ssl().set_hostname("foobar.com").unwrap(); 34 | 35 | (server, client) 36 | } 37 | 38 | #[test] 39 | fn ech() { 40 | let (_server, client) = bootstrap_ech(ECH_CONFIG, ECH_KEY, ECH_CONFIG_LIST); 41 | 42 | let ssl_stream = client.connect(); 43 | assert!(ssl_stream.ssl().ech_accepted()) 44 | } 45 | 46 | #[test] 47 | fn ech_rejection() { 48 | // Server is initialized using `ECH_CONFIG_2`, so using `ECH_CONFIG_LIST` instead of 49 | // `ECH_CONFIG_LIST_2` should trigger rejection. 50 | let (_server, client) = bootstrap_ech(ECH_CONFIG_2, ECH_KEY_2, ECH_CONFIG_LIST); 51 | 52 | let HandshakeError::Failure(failed_ssl_stream) = client.connect_err() else { 53 | panic!("wrong HandshakeError failure variant!"); 54 | }; 55 | assert_eq!( 56 | failed_ssl_stream.ssl().get_ech_name_override(), 57 | Some(b"ech.com".to_vec().as_ref()) 58 | ); 59 | assert!(failed_ssl_stream.ssl().get_ech_retry_configs().is_some()); 60 | assert!(!failed_ssl_stream.ssl().ech_accepted()) 61 | } 62 | 63 | #[test] 64 | fn ech_grease() { 65 | let server = Server::builder().build(); 66 | 67 | let mut client = server.client_with_root_ca().build().builder(); 68 | // Verified with a pcap locally that the ECH extension gets sent due to GREASE 69 | client.ssl().set_enable_ech_grease(true); 70 | 71 | let ssl_stream = client.connect(); 72 | assert!(!ssl_stream.ssl().ech_accepted()) 73 | } 74 | -------------------------------------------------------------------------------- /boring/src/ssl/test/server.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Read, Write}; 2 | use std::net::{SocketAddr, TcpListener, TcpStream}; 3 | use std::thread::{self, JoinHandle}; 4 | 5 | use crate::ssl::{ 6 | HandshakeError, Ssl, SslContext, SslContextBuilder, SslFiletype, SslMethod, SslRef, SslStream, 7 | }; 8 | 9 | pub struct Server { 10 | handle: Option>, 11 | addr: SocketAddr, 12 | } 13 | 14 | impl Drop for Server { 15 | fn drop(&mut self) { 16 | if !thread::panicking() { 17 | self.handle.take().unwrap().join().unwrap(); 18 | } 19 | } 20 | } 21 | 22 | impl Server { 23 | pub fn builder() -> Builder { 24 | let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); 25 | ctx.set_certificate_chain_file("test/cert.pem").unwrap(); 26 | ctx.set_private_key_file("test/key.pem", SslFiletype::PEM) 27 | .unwrap(); 28 | 29 | Builder { 30 | ctx, 31 | ssl_cb: Box::new(|_| {}), 32 | io_cb: Box::new(|_| {}), 33 | err_cb: Box::new(|_| {}), 34 | should_error: false, 35 | expected_connections_count: 1, 36 | } 37 | } 38 | 39 | /// Serves the leaf and the root together. 40 | pub fn builder_full_chain() -> Builder { 41 | let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); 42 | // Uses certs.pem instead of cert.pem. 43 | ctx.set_certificate_chain_file("test/certs.pem").unwrap(); 44 | ctx.set_private_key_file("test/key.pem", SslFiletype::PEM) 45 | .unwrap(); 46 | 47 | Builder { 48 | ctx, 49 | ssl_cb: Box::new(|_| {}), 50 | io_cb: Box::new(|_| {}), 51 | err_cb: Box::new(|_| {}), 52 | should_error: false, 53 | expected_connections_count: 1, 54 | } 55 | } 56 | 57 | pub fn client(&self) -> ClientBuilder { 58 | ClientBuilder { 59 | ctx: SslContext::builder(SslMethod::tls()).unwrap(), 60 | addr: self.addr, 61 | } 62 | } 63 | 64 | pub fn client_with_root_ca(&self) -> ClientBuilder { 65 | let mut client = self.client(); 66 | 67 | client.ctx().set_ca_file("test/root-ca.pem").unwrap(); 68 | 69 | client 70 | } 71 | 72 | pub fn connect_tcp(&self) -> TcpStream { 73 | TcpStream::connect(self.addr).unwrap() 74 | } 75 | } 76 | 77 | pub struct Builder { 78 | ctx: SslContextBuilder, 79 | ssl_cb: Box, 80 | io_cb: Box) + Send>, 81 | err_cb: Box) + Send>, 82 | should_error: bool, 83 | expected_connections_count: usize, 84 | } 85 | 86 | impl Builder { 87 | pub fn ctx(&mut self) -> &mut SslContextBuilder { 88 | &mut self.ctx 89 | } 90 | 91 | pub fn ssl_cb(&mut self, cb: F) 92 | where 93 | F: 'static + FnMut(&mut SslRef) + Send, 94 | { 95 | self.ssl_cb = Box::new(cb); 96 | } 97 | 98 | pub fn io_cb(&mut self, cb: F) 99 | where 100 | F: 'static + FnMut(SslStream) + Send, 101 | { 102 | self.io_cb = Box::new(cb); 103 | } 104 | 105 | pub fn err_cb(&mut self, cb: impl FnMut(HandshakeError) + Send + 'static) { 106 | self.should_error(); 107 | 108 | self.err_cb = Box::new(cb); 109 | } 110 | 111 | pub fn should_error(&mut self) { 112 | self.should_error = true; 113 | } 114 | 115 | pub fn expected_connections_count(&mut self, count: usize) { 116 | self.expected_connections_count = count; 117 | } 118 | 119 | pub fn build(self) -> Server { 120 | let ctx = self.ctx.build(); 121 | let socket = TcpListener::bind("127.0.0.1:0").unwrap(); 122 | let addr = socket.local_addr().unwrap(); 123 | let mut ssl_cb = self.ssl_cb; 124 | let mut io_cb = self.io_cb; 125 | let mut err_cb = self.err_cb; 126 | let should_error = self.should_error; 127 | let mut count = self.expected_connections_count; 128 | 129 | let handle = thread::spawn(move || { 130 | while count > 0 { 131 | let socket = socket.accept().unwrap().0; 132 | let mut ssl = Ssl::new(&ctx).unwrap(); 133 | 134 | ssl_cb(&mut ssl); 135 | 136 | let r = ssl.accept(socket); 137 | 138 | if should_error { 139 | err_cb(r.unwrap_err()); 140 | } else { 141 | let mut socket = r.unwrap(); 142 | 143 | socket.write_all(&[0]).unwrap(); 144 | io_cb(socket); 145 | } 146 | 147 | count -= 1; 148 | } 149 | }); 150 | 151 | Server { 152 | handle: Some(handle), 153 | addr, 154 | } 155 | } 156 | } 157 | 158 | pub struct ClientBuilder { 159 | ctx: SslContextBuilder, 160 | addr: SocketAddr, 161 | } 162 | 163 | impl ClientBuilder { 164 | pub fn ctx(&mut self) -> &mut SslContextBuilder { 165 | &mut self.ctx 166 | } 167 | 168 | pub fn build(self) -> Client { 169 | Client { 170 | ctx: self.ctx.build(), 171 | addr: self.addr, 172 | } 173 | } 174 | 175 | pub fn connect(self) -> SslStream { 176 | self.build().builder().connect() 177 | } 178 | 179 | pub fn connect_err(self) -> HandshakeError { 180 | self.build().builder().connect_err() 181 | } 182 | } 183 | 184 | pub struct Client { 185 | ctx: SslContext, 186 | addr: SocketAddr, 187 | } 188 | 189 | impl Client { 190 | pub fn builder(&self) -> ClientSslBuilder { 191 | ClientSslBuilder { 192 | ssl: Ssl::new(&self.ctx).unwrap(), 193 | addr: self.addr, 194 | } 195 | } 196 | } 197 | 198 | pub struct ClientSslBuilder { 199 | ssl: Ssl, 200 | addr: SocketAddr, 201 | } 202 | 203 | impl ClientSslBuilder { 204 | pub fn ssl(&mut self) -> &mut SslRef { 205 | &mut self.ssl 206 | } 207 | 208 | pub fn connect(self) -> SslStream { 209 | let socket = TcpStream::connect(self.addr).unwrap(); 210 | let mut s = self.ssl.connect(socket).unwrap(); 211 | s.read_exact(&mut [0]).unwrap(); 212 | s 213 | } 214 | 215 | pub fn connect_err(self) -> HandshakeError { 216 | let socket = TcpStream::connect(self.addr).unwrap(); 217 | 218 | self.ssl.setup_connect(socket).handshake().unwrap_err() 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /boring/src/ssl/test/session.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | use std::sync::atomic::{AtomicBool, Ordering}; 3 | use std::sync::OnceLock; 4 | 5 | use crate::ssl::test::server::Server; 6 | use crate::ssl::{ 7 | ErrorCode, GetSessionPendingError, HandshakeError, Ssl, SslContext, SslContextBuilder, 8 | SslMethod, SslOptions, SslSession, SslSessionCacheMode, SslVersion, 9 | }; 10 | 11 | #[test] 12 | fn idle_session() { 13 | let ctx = SslContext::builder(SslMethod::tls()).unwrap().build(); 14 | let ssl = Ssl::new(&ctx).unwrap(); 15 | assert!(ssl.session().is_none()); 16 | } 17 | 18 | #[test] 19 | fn active_session() { 20 | let server = Server::builder().build(); 21 | 22 | let s = server.client().connect(); 23 | 24 | let session = s.ssl().session().unwrap(); 25 | let len = session.master_key_len(); 26 | let mut buf = vec![0; len - 1]; 27 | let copied = session.master_key(&mut buf); 28 | assert_eq!(copied, buf.len()); 29 | let mut buf = vec![0; len + 1]; 30 | let copied = session.master_key(&mut buf); 31 | assert_eq!(copied, len); 32 | } 33 | 34 | #[test] 35 | fn new_get_session_callback() { 36 | static FOUND_SESSION: AtomicBool = AtomicBool::new(false); 37 | static SERVER_SESSION_DER: OnceLock> = OnceLock::new(); 38 | static CLIENT_SESSION_DER: OnceLock> = OnceLock::new(); 39 | 40 | let mut server = Server::builder(); 41 | 42 | server.expected_connections_count(2); 43 | server 44 | .ctx() 45 | .set_max_proto_version(Some(SslVersion::TLS1_2)) 46 | .unwrap(); 47 | server.ctx().set_options(SslOptions::NO_TICKET); 48 | server 49 | .ctx() 50 | .set_session_cache_mode(SslSessionCacheMode::SERVER | SslSessionCacheMode::NO_INTERNAL); 51 | server.ctx().set_new_session_callback(|_, session| { 52 | SERVER_SESSION_DER.set(session.to_der().unwrap()).unwrap() 53 | }); 54 | unsafe { 55 | server.ctx().set_get_session_callback(|_, id| { 56 | let Some(der) = SERVER_SESSION_DER.get() else { 57 | return Ok(None); 58 | }; 59 | 60 | let session = SslSession::from_der(der).unwrap(); 61 | 62 | FOUND_SESSION.store(true, Ordering::SeqCst); 63 | 64 | assert_eq!(id, session.id()); 65 | 66 | Ok(Some(session)) 67 | }); 68 | } 69 | server.ctx().set_session_id_context(b"foo").unwrap(); 70 | 71 | let server = server.build(); 72 | 73 | let mut client = server.client(); 74 | 75 | client 76 | .ctx() 77 | .set_session_cache_mode(SslSessionCacheMode::CLIENT); 78 | client.ctx().set_new_session_callback(|_, session| { 79 | CLIENT_SESSION_DER.set(session.to_der().unwrap()).unwrap() 80 | }); 81 | 82 | let client = client.build(); 83 | 84 | client.builder().connect(); 85 | 86 | assert!(CLIENT_SESSION_DER.get().is_some()); 87 | assert!(SERVER_SESSION_DER.get().is_some()); 88 | assert!(!FOUND_SESSION.load(Ordering::SeqCst)); 89 | 90 | let mut ssl_builder = client.builder(); 91 | 92 | unsafe { 93 | ssl_builder 94 | .ssl() 95 | .set_session(&SslSession::from_der(CLIENT_SESSION_DER.get().unwrap()).unwrap()) 96 | .unwrap(); 97 | } 98 | 99 | ssl_builder.connect(); 100 | 101 | assert!(FOUND_SESSION.load(Ordering::SeqCst)); 102 | } 103 | 104 | #[test] 105 | fn new_get_session_callback_pending() { 106 | static CALLED_SERVER_CALLBACK: AtomicBool = AtomicBool::new(false); 107 | 108 | let mut server = Server::builder(); 109 | 110 | server 111 | .ctx() 112 | .set_max_proto_version(Some(SslVersion::TLS1_2)) 113 | .unwrap(); 114 | server.ctx().set_options(SslOptions::NO_TICKET); 115 | server 116 | .ctx() 117 | .set_session_cache_mode(SslSessionCacheMode::SERVER | SslSessionCacheMode::NO_INTERNAL); 118 | unsafe { 119 | server.ctx().set_get_session_callback(|_, _| { 120 | if !CALLED_SERVER_CALLBACK.swap(true, Ordering::SeqCst) { 121 | return Err(GetSessionPendingError); 122 | } 123 | 124 | Ok(None) 125 | }); 126 | } 127 | server.ctx().set_session_id_context(b"foo").unwrap(); 128 | server.err_cb(|error| { 129 | let HandshakeError::WouldBlock(mid_handshake) = error else { 130 | panic!("should be WouldBlock"); 131 | }; 132 | 133 | assert!(mid_handshake.error().would_block()); 134 | assert_eq!(mid_handshake.error().code(), ErrorCode::PENDING_SESSION); 135 | 136 | let mut socket = mid_handshake.handshake().unwrap(); 137 | 138 | socket.write_all(&[0]).unwrap(); 139 | }); 140 | 141 | let server = server.build(); 142 | 143 | let mut client = server.client(); 144 | 145 | client 146 | .ctx() 147 | .set_session_cache_mode(SslSessionCacheMode::CLIENT); 148 | 149 | client.connect(); 150 | } 151 | 152 | #[test] 153 | fn new_session_callback_swapped_ctx() { 154 | static CALLED_BACK: AtomicBool = AtomicBool::new(false); 155 | 156 | let mut server = Server::builder(); 157 | server.ctx().set_session_id_context(b"foo").unwrap(); 158 | 159 | let server = server.build(); 160 | 161 | let mut client = server.client(); 162 | 163 | client 164 | .ctx() 165 | .set_session_cache_mode(SslSessionCacheMode::CLIENT | SslSessionCacheMode::NO_INTERNAL); 166 | client 167 | .ctx() 168 | .set_new_session_callback(|_, _| CALLED_BACK.store(true, Ordering::SeqCst)); 169 | 170 | let mut client = client.build().builder(); 171 | 172 | let ctx = SslContextBuilder::new(SslMethod::tls()).unwrap().build(); 173 | client.ssl().set_ssl_context(&ctx).unwrap(); 174 | 175 | client.connect(); 176 | 177 | assert!(CALLED_BACK.load(Ordering::SeqCst)); 178 | } 179 | 180 | #[test] 181 | fn session_cache_size() { 182 | let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); 183 | ctx.set_session_cache_size(1234); 184 | let ctx = ctx.build(); 185 | assert_eq!(ctx.session_cache_size(), 1234); 186 | } 187 | -------------------------------------------------------------------------------- /boring/src/ssl/test/verify.rs: -------------------------------------------------------------------------------- 1 | use super::server::Server; 2 | use crate::hash::MessageDigest; 3 | use crate::ssl::SslVerifyMode; 4 | use crate::x509::store::X509StoreBuilder; 5 | use crate::x509::X509; 6 | use hex; 7 | use std::sync::atomic::{AtomicBool, Ordering}; 8 | 9 | #[test] 10 | fn untrusted() { 11 | let mut server = Server::builder(); 12 | server.should_error(); 13 | let server = server.build(); 14 | 15 | let mut client = server.client(); 16 | client.ctx().set_verify(SslVerifyMode::PEER); 17 | 18 | client.connect_err(); 19 | } 20 | 21 | #[test] 22 | fn trusted() { 23 | let server = Server::builder().build(); 24 | let client = server.client_with_root_ca(); 25 | 26 | client.connect(); 27 | } 28 | 29 | #[test] 30 | fn trusted_with_set_cert() { 31 | let server = Server::builder().build(); 32 | 33 | let mut store = X509StoreBuilder::new().unwrap(); 34 | let x509 = X509::from_pem(super::ROOT_CERT).unwrap(); 35 | store.add_cert(x509).unwrap(); 36 | 37 | let mut client = server.client(); 38 | client.ctx().set_verify(SslVerifyMode::PEER); 39 | client.ctx().set_verify_cert_store(store.build()).unwrap(); 40 | 41 | client.connect(); 42 | } 43 | 44 | #[test] 45 | fn untrusted_callback_override_ok() { 46 | let server = Server::builder().build(); 47 | 48 | let mut client = server.client(); 49 | client 50 | .ctx() 51 | .set_verify_callback(SslVerifyMode::PEER, |_, x509| { 52 | assert!(x509.current_cert().is_some()); 53 | assert!(x509.verify_result().is_err()); 54 | 55 | true 56 | }); 57 | 58 | client.connect(); 59 | } 60 | 61 | #[test] 62 | fn untrusted_callback_override_bad() { 63 | let mut server = Server::builder(); 64 | server.should_error(); 65 | let server = server.build(); 66 | 67 | let mut client = server.client(); 68 | client 69 | .ctx() 70 | .set_verify_callback(SslVerifyMode::PEER, |_, _| false); 71 | 72 | client.connect_err(); 73 | } 74 | 75 | #[test] 76 | fn trusted_callback_override_ok() { 77 | let server = Server::builder().build(); 78 | let mut client = server.client_with_root_ca(); 79 | 80 | client 81 | .ctx() 82 | .set_verify_callback(SslVerifyMode::PEER, |_, x509| { 83 | assert!(x509.current_cert().is_some()); 84 | assert_eq!(x509.verify_result(), Ok(())); 85 | 86 | true 87 | }); 88 | 89 | client.connect(); 90 | } 91 | 92 | #[test] 93 | fn trusted_callback_override_bad() { 94 | let mut server = Server::builder(); 95 | 96 | server.should_error(); 97 | 98 | let server = server.build(); 99 | let mut client = server.client_with_root_ca(); 100 | 101 | client 102 | .ctx() 103 | .set_verify_callback(SslVerifyMode::PEER, |_, _| false); 104 | 105 | client.connect_err(); 106 | } 107 | 108 | #[test] 109 | fn callback() { 110 | static CALLED_BACK: AtomicBool = AtomicBool::new(false); 111 | 112 | let server = Server::builder().build(); 113 | 114 | let mut client = server.client(); 115 | let expected = "59172d9313e84459bcff27f967e79e6e9217e584"; 116 | client 117 | .ctx() 118 | .set_verify_callback(SslVerifyMode::PEER, move |_, x509| { 119 | CALLED_BACK.store(true, Ordering::SeqCst); 120 | let cert = x509.current_cert().unwrap(); 121 | let digest = cert.digest(MessageDigest::sha1()).unwrap(); 122 | assert_eq!(hex::encode(digest), expected); 123 | true 124 | }); 125 | 126 | client.connect(); 127 | assert!(CALLED_BACK.load(Ordering::SeqCst)); 128 | } 129 | 130 | #[test] 131 | fn ssl_callback() { 132 | static CALLED_BACK: AtomicBool = AtomicBool::new(false); 133 | 134 | let server = Server::builder().build(); 135 | 136 | let mut client = server.client().build().builder(); 137 | let expected = "59172d9313e84459bcff27f967e79e6e9217e584"; 138 | client 139 | .ssl() 140 | .set_verify_callback(SslVerifyMode::PEER, move |_, x509| { 141 | CALLED_BACK.store(true, Ordering::SeqCst); 142 | let cert = x509.current_cert().unwrap(); 143 | let digest = cert.digest(MessageDigest::sha1()).unwrap(); 144 | assert_eq!(hex::encode(digest), expected); 145 | true 146 | }); 147 | 148 | client.connect(); 149 | assert!(CALLED_BACK.load(Ordering::SeqCst)); 150 | } 151 | -------------------------------------------------------------------------------- /boring/src/string.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi; 2 | use foreign_types::ForeignTypeRef; 3 | use libc::{c_char, c_void}; 4 | use std::convert::AsRef; 5 | use std::ffi::CStr; 6 | use std::fmt; 7 | use std::ops::Deref; 8 | use std::str; 9 | 10 | use crate::stack::Stackable; 11 | 12 | foreign_type_and_impl_send_sync! { 13 | type CType = c_char; 14 | fn drop = free; 15 | 16 | pub struct OpensslString; 17 | } 18 | 19 | impl fmt::Display for OpensslString { 20 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 21 | fmt::Display::fmt(&**self, f) 22 | } 23 | } 24 | 25 | impl fmt::Debug for OpensslString { 26 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 27 | fmt::Debug::fmt(&**self, f) 28 | } 29 | } 30 | 31 | impl Stackable for OpensslString { 32 | type StackType = ffi::stack_st_OPENSSL_STRING; 33 | } 34 | 35 | impl AsRef for OpensslString { 36 | fn as_ref(&self) -> &str { 37 | self 38 | } 39 | } 40 | 41 | impl AsRef<[u8]> for OpensslString { 42 | fn as_ref(&self) -> &[u8] { 43 | self.as_bytes() 44 | } 45 | } 46 | 47 | impl Deref for OpensslStringRef { 48 | type Target = str; 49 | 50 | fn deref(&self) -> &str { 51 | unsafe { 52 | let slice = CStr::from_ptr(self.as_ptr()).to_bytes(); 53 | str::from_utf8_unchecked(slice) 54 | } 55 | } 56 | } 57 | 58 | impl AsRef for OpensslStringRef { 59 | fn as_ref(&self) -> &str { 60 | self 61 | } 62 | } 63 | 64 | impl AsRef<[u8]> for OpensslStringRef { 65 | fn as_ref(&self) -> &[u8] { 66 | self.as_bytes() 67 | } 68 | } 69 | 70 | impl fmt::Display for OpensslStringRef { 71 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 72 | fmt::Display::fmt(&**self, f) 73 | } 74 | } 75 | 76 | impl fmt::Debug for OpensslStringRef { 77 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 78 | fmt::Debug::fmt(&**self, f) 79 | } 80 | } 81 | 82 | unsafe fn free(buf: *mut c_char) { 83 | crate::ffi::OPENSSL_free(buf as *mut c_void); 84 | } 85 | -------------------------------------------------------------------------------- /boring/src/util.rs: -------------------------------------------------------------------------------- 1 | use crate::error::ErrorStack; 2 | use foreign_types::{ForeignType, ForeignTypeRef}; 3 | use libc::{c_char, c_int, c_void}; 4 | use std::any::Any; 5 | use std::panic::{self, AssertUnwindSafe}; 6 | use std::slice; 7 | 8 | /// Wraps a user-supplied callback and a slot for panics thrown inside the callback (while FFI 9 | /// frames are on the stack). 10 | /// 11 | /// When dropped, checks if the callback has panicked, and resumes unwinding if so. 12 | pub struct CallbackState { 13 | /// The user callback. Taken out of the `Option` when called. 14 | cb: Option, 15 | /// If the callback panics, we place the panic object here, to be re-thrown once OpenSSL 16 | /// returns. 17 | panic: Option>, 18 | } 19 | 20 | impl CallbackState { 21 | pub fn new(callback: F) -> Self { 22 | CallbackState { 23 | cb: Some(callback), 24 | panic: None, 25 | } 26 | } 27 | } 28 | 29 | impl Drop for CallbackState { 30 | fn drop(&mut self) { 31 | if let Some(panic) = self.panic.take() { 32 | panic::resume_unwind(panic); 33 | } 34 | } 35 | } 36 | 37 | /// Password callback function, passed to private key loading functions. 38 | /// 39 | /// `cb_state` is expected to be a pointer to a `CallbackState`. 40 | pub unsafe extern "C" fn invoke_passwd_cb( 41 | buf: *mut c_char, 42 | size: c_int, 43 | _rwflag: c_int, 44 | cb_state: *mut c_void, 45 | ) -> c_int 46 | where 47 | F: FnOnce(&mut [u8]) -> Result, 48 | { 49 | let callback = &mut *(cb_state as *mut CallbackState); 50 | 51 | let result = panic::catch_unwind(AssertUnwindSafe(|| { 52 | let pass_slice = slice::from_raw_parts_mut(buf as *mut u8, size as usize); 53 | callback.cb.take().unwrap()(pass_slice) 54 | })); 55 | 56 | match result { 57 | Ok(Ok(len)) => len as c_int, 58 | Ok(Err(_)) => { 59 | // FIXME restore error stack 60 | 0 61 | } 62 | Err(err) => { 63 | callback.panic = Some(err); 64 | 0 65 | } 66 | } 67 | } 68 | 69 | #[allow(dead_code)] 70 | pub trait ForeignTypeExt: ForeignType { 71 | unsafe fn from_ptr_opt(ptr: *mut Self::CType) -> Option { 72 | if ptr.is_null() { 73 | None 74 | } else { 75 | Some(Self::from_ptr(ptr)) 76 | } 77 | } 78 | } 79 | impl ForeignTypeExt for FT {} 80 | 81 | pub trait ForeignTypeRefExt: ForeignTypeRef { 82 | unsafe fn from_const_ptr<'a>(ptr: *const Self::CType) -> &'a Self { 83 | Self::from_ptr(ptr as *mut Self::CType) 84 | } 85 | 86 | unsafe fn from_const_ptr_opt<'a>(ptr: *const Self::CType) -> Option<&'a Self> { 87 | if ptr.is_null() { 88 | None 89 | } else { 90 | Some(Self::from_const_ptr(ptr as *mut Self::CType)) 91 | } 92 | } 93 | } 94 | impl ForeignTypeRefExt for FT {} 95 | -------------------------------------------------------------------------------- /boring/src/version.rs: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 (the "License"); 2 | // you may not use this file except in compliance with the License. 3 | // You may obtain a copy of the License at 4 | // 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | // Unless required by applicable law or agreed to in writing, software 8 | // distributed under the License is distributed on an "AS IS" BASIS, 9 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | // See the License for the specific language governing permissions and 11 | // limitations under the License. 12 | // 13 | 14 | use std::ffi::CStr; 15 | 16 | use crate::ffi::{ 17 | OpenSSL_version, OpenSSL_version_num, OPENSSL_BUILT_ON, OPENSSL_CFLAGS, OPENSSL_DIR, 18 | OPENSSL_PLATFORM, OPENSSL_VERSION, 19 | }; 20 | 21 | /// OPENSSL_VERSION_NUMBER is a numeric release version identifier: 22 | /// 23 | /// `MNNFFPPS: major minor fix patch status` 24 | /// 25 | /// The status nibble has one of the values 0 for development, 1 to e for betas 1 to 14, and f for release. 26 | /// 27 | /// for example 28 | /// 29 | /// `0x000906000 == 0.9.6 dev` 30 | /// `0x000906023 == 0.9.6b beta 3` 31 | /// `0x00090605f == 0.9.6e release` 32 | /// 33 | /// Versions prior to 0.9.3 have identifiers < 0x0930. Versions between 0.9.3 and 0.9.5 had a version identifier with this interpretation: 34 | /// 35 | /// `MMNNFFRBB major minor fix final beta/patch` 36 | /// 37 | /// for example 38 | /// 39 | /// `0x000904100 == 0.9.4 release` 40 | /// `0x000905000 == 0.9.5 dev` 41 | /// 42 | /// Version 0.9.5a had an interim interpretation that is like the current one, except the patch level got the highest bit set, to keep continuity. The number was therefore 0x0090581f 43 | /// 44 | /// The return value of this function can be compared to the macro to make sure that the correct version of the library has been loaded, especially when using DLLs on Windows systems. 45 | pub fn number() -> i64 { 46 | unsafe { OpenSSL_version_num() as i64 } 47 | } 48 | 49 | /// The text variant of the version number and the release date. For example, "OpenSSL 0.9.5a 1 Apr 2000". 50 | pub fn version() -> &'static str { 51 | unsafe { 52 | CStr::from_ptr(OpenSSL_version(OPENSSL_VERSION)) 53 | .to_str() 54 | .unwrap() 55 | } 56 | } 57 | 58 | /// The compiler flags set for the compilation process in the form "compiler: ..." if available or 59 | /// "compiler: information not available" otherwise. 60 | pub fn c_flags() -> &'static str { 61 | unsafe { 62 | CStr::from_ptr(OpenSSL_version(OPENSSL_CFLAGS)) 63 | .to_str() 64 | .unwrap() 65 | } 66 | } 67 | 68 | /// The date of the build process in the form "built on: ..." if available or "built on: date not available" otherwise. 69 | pub fn built_on() -> &'static str { 70 | unsafe { 71 | CStr::from_ptr(OpenSSL_version(OPENSSL_BUILT_ON)) 72 | .to_str() 73 | .unwrap() 74 | } 75 | } 76 | 77 | /// The "Configure" target of the library build in the form "platform: ..." if available or "platform: information not available" otherwise. 78 | pub fn platform() -> &'static str { 79 | unsafe { 80 | CStr::from_ptr(OpenSSL_version(OPENSSL_PLATFORM)) 81 | .to_str() 82 | .unwrap() 83 | } 84 | } 85 | 86 | /// The "OPENSSLDIR" setting of the library build in the form "OPENSSLDIR: "..."" if available or "OPENSSLDIR: N/A" otherwise. 87 | pub fn dir() -> &'static str { 88 | unsafe { 89 | CStr::from_ptr(OpenSSL_version(OPENSSL_DIR)) 90 | .to_str() 91 | .unwrap() 92 | } 93 | } 94 | 95 | /// This test ensures that we do not segfault when calling the functions of this module 96 | /// and that the strings respect a reasonable format. 97 | #[test] 98 | fn test_versions() { 99 | println!("Number: '{}'", number()); 100 | println!("Version: '{}'", version()); 101 | println!("C flags: '{}'", c_flags()); 102 | println!("Built on: '{}'", built_on()); 103 | println!("Platform: '{}'", platform()); 104 | println!("Dir: '{}'", dir()); 105 | 106 | assert!(number() > 0); 107 | assert!(version().starts_with("BoringSSL")); 108 | assert!(c_flags().starts_with("compiler:")); 109 | assert!(built_on().starts_with("built on:")); 110 | assert!(dir().starts_with("OPENSSLDIR:")); 111 | } 112 | -------------------------------------------------------------------------------- /boring/src/x509/store.rs: -------------------------------------------------------------------------------- 1 | //! Describe a context in which to verify an `X509` certificate. 2 | //! 3 | //! The `X509` certificate store holds trusted CA certificates used to verify 4 | //! peer certificates. 5 | //! 6 | //! # Example 7 | //! 8 | //! ```rust 9 | //! use boring::x509::store::{X509StoreBuilder, X509Store}; 10 | //! use boring::x509::{X509, X509Name}; 11 | //! use boring::asn1::Asn1Time; 12 | //! use boring::pkey::PKey; 13 | //! use boring::hash::MessageDigest; 14 | //! use boring::rsa::Rsa; 15 | //! use boring::nid::Nid; 16 | //! 17 | //! let rsa = Rsa::generate(2048).unwrap(); 18 | //! let pkey = PKey::from_rsa(rsa).unwrap(); 19 | //! let mut name = X509Name::builder().unwrap(); 20 | //! 21 | //! name.append_entry_by_nid(Nid::COMMONNAME, "foobar.com").unwrap(); 22 | //! 23 | //! let name = name.build(); 24 | //! let mut builder = X509::builder().unwrap(); 25 | //! 26 | //! // Sep 27th, 2016 27 | //! let sample_time = Asn1Time::from_unix(1474934400).unwrap(); 28 | //! 29 | //! builder.set_version(2).unwrap(); 30 | //! builder.set_subject_name(&name).unwrap(); 31 | //! builder.set_issuer_name(&name).unwrap(); 32 | //! builder.set_pubkey(&pkey).unwrap(); 33 | //! builder.set_not_before(&sample_time); 34 | //! builder.set_not_after(&sample_time); 35 | //! builder.sign(&pkey, MessageDigest::sha256()).unwrap(); 36 | //! 37 | //! let certificate: X509 = builder.build(); 38 | //! let mut builder = X509StoreBuilder::new().unwrap(); 39 | //! let _ = builder.add_cert(certificate); 40 | //! let store: X509Store = builder.build(); 41 | //! ``` 42 | 43 | use crate::error::ErrorStack; 44 | use crate::ffi; 45 | use crate::stack::StackRef; 46 | use crate::x509::verify::{X509VerifyFlags, X509VerifyParamRef}; 47 | use crate::x509::{X509Object, X509}; 48 | use crate::{cvt, cvt_p}; 49 | use foreign_types::{ForeignType, ForeignTypeRef}; 50 | use openssl_macros::corresponds; 51 | use std::mem; 52 | 53 | foreign_type_and_impl_send_sync! { 54 | type CType = ffi::X509_STORE; 55 | fn drop = ffi::X509_STORE_free; 56 | 57 | /// A builder type used to construct an `X509Store`. 58 | pub struct X509StoreBuilder; 59 | } 60 | 61 | impl X509StoreBuilder { 62 | /// Returns a builder for a certificate store. 63 | /// 64 | /// The store is initially empty. 65 | pub fn new() -> Result { 66 | unsafe { 67 | ffi::init(); 68 | 69 | cvt_p(ffi::X509_STORE_new()).map(|p| X509StoreBuilder::from_ptr(p)) 70 | } 71 | } 72 | 73 | /// Constructs the `X509Store`. 74 | pub fn build(self) -> X509Store { 75 | let store = X509Store(self.0); 76 | mem::forget(self); 77 | store 78 | } 79 | } 80 | 81 | impl X509StoreBuilderRef { 82 | /// Adds a certificate to the certificate store. 83 | // FIXME should take an &X509Ref 84 | #[corresponds(X509_STORE_add_cert)] 85 | pub fn add_cert(&mut self, cert: X509) -> Result<(), ErrorStack> { 86 | unsafe { cvt(ffi::X509_STORE_add_cert(self.as_ptr(), cert.as_ptr())).map(|_| ()) } 87 | } 88 | 89 | /// Load certificates from their default locations. 90 | /// 91 | /// These locations are read from the `SSL_CERT_FILE` and `SSL_CERT_DIR` 92 | /// environment variables if present, or defaults specified at OpenSSL 93 | /// build time otherwise. 94 | #[corresponds(X509_STORE_set_default_paths)] 95 | pub fn set_default_paths(&mut self) -> Result<(), ErrorStack> { 96 | unsafe { cvt(ffi::X509_STORE_set_default_paths(self.as_ptr())).map(|_| ()) } 97 | } 98 | 99 | /// Sets certificate chain validation related flags. 100 | #[corresponds(X509_STORE_set_flags)] 101 | pub fn set_flags(&mut self, flags: X509VerifyFlags) { 102 | unsafe { 103 | cvt(ffi::X509_STORE_set_flags(self.as_ptr(), flags.bits())).unwrap(); 104 | } 105 | } 106 | 107 | /// Returns a mutable reference to the X509 verification configuration. 108 | #[corresponds(X509_STORE_get0_param)] 109 | pub fn verify_param_mut(&mut self) -> &mut X509VerifyParamRef { 110 | unsafe { X509VerifyParamRef::from_ptr_mut(ffi::X509_STORE_get0_param(self.as_ptr())) } 111 | } 112 | 113 | /// Sets certificate chain validation related parameters. 114 | #[corresponds(X509_STORE_set1_param)] 115 | pub fn set_param(&mut self, param: &X509VerifyParamRef) -> Result<(), ErrorStack> { 116 | unsafe { cvt(ffi::X509_STORE_set1_param(self.as_ptr(), param.as_ptr())).map(|_| ()) } 117 | } 118 | } 119 | 120 | foreign_type_and_impl_send_sync! { 121 | type CType = ffi::X509_STORE; 122 | fn drop = ffi::X509_STORE_free; 123 | 124 | /// A certificate store to hold trusted `X509` certificates. 125 | pub struct X509Store; 126 | } 127 | 128 | impl X509StoreRef { 129 | /// **Warning: this method is unsound** 130 | /// 131 | /// Get a reference to the cache of certificates in this store. 132 | /// 133 | /// # Safety 134 | /// References may be invalidated by any access to the shared cache. 135 | #[deprecated( 136 | note = "This method is unsound https://github.com/sfackler/rust-openssl/issues/2096" 137 | )] 138 | #[corresponds(X509_STORE_get0_objects)] 139 | pub fn objects(&self) -> &StackRef { 140 | unsafe { StackRef::from_ptr(ffi::X509_STORE_get0_objects(self.as_ptr())) } 141 | } 142 | 143 | /// For testing only, where it doesn't have to expose an unsafe pointer 144 | #[cfg(test)] 145 | #[allow(deprecated)] 146 | pub fn objects_len(&self) -> usize { 147 | self.objects().len() 148 | } 149 | } 150 | 151 | #[test] 152 | #[allow(dead_code)] 153 | // X509Store must not implement Clone because `SslContextBuilder::cert_store_mut` lets 154 | // you get a mutable reference to a store that could have been cloned before being 155 | // passed to `SslContextBuilder::set_cert_store`. 156 | fn no_clone_for_x509store() { 157 | trait MustNotImplementClone {} 158 | impl MustNotImplementClone for T {} 159 | impl MustNotImplementClone for X509Store {} 160 | } 161 | -------------------------------------------------------------------------------- /boring/src/x509/tests/trusted_first.rs: -------------------------------------------------------------------------------- 1 | //! See https://github.com/google/boringssl/blob/cc696073cffe7978d489297fbdeac4c0030384aa/crypto/x509/x509_test.cc#L3977-L3980 2 | 3 | use crate::stack::Stack; 4 | use crate::x509::store::X509StoreBuilder; 5 | use crate::x509::verify::{X509Flags, X509VerifyParamRef}; 6 | use crate::x509::{X509Ref, X509StoreContext, X509VerifyError, X509VerifyResult, X509}; 7 | 8 | #[test] 9 | fn test_verify_cert() { 10 | let root2 = X509::from_pem(include_bytes!("../../../test/root-ca-2.pem")).unwrap(); 11 | let root1 = X509::from_pem(include_bytes!("../../../test/root-ca.pem")).unwrap(); 12 | let root1_cross = X509::from_pem(include_bytes!("../../../test/root-ca-cross.pem")).unwrap(); 13 | let intermediate = X509::from_pem(include_bytes!("../../../test/intermediate-ca.pem")).unwrap(); 14 | let leaf = X509::from_pem(include_bytes!("../../../test/cert-with-intermediate.pem")).unwrap(); 15 | 16 | assert_eq!(Ok(()), verify(&leaf, &[&root1], &[&intermediate], |_| {})); 17 | 18 | #[cfg(not(feature = "fips-compat"))] 19 | assert_eq!( 20 | Ok(()), 21 | verify( 22 | &leaf, 23 | &[&root1, &root2], 24 | &[&intermediate, &root1_cross], 25 | |_| {} 26 | ) 27 | ); 28 | 29 | #[cfg(feature = "fips-compat")] 30 | assert_eq!( 31 | Err(X509VerifyError::CERT_HAS_EXPIRED), 32 | verify( 33 | &leaf, 34 | &[&root1, &root2], 35 | &[&intermediate, &root1_cross], 36 | |_| {} 37 | ) 38 | ); 39 | 40 | assert_eq!( 41 | Ok(()), 42 | verify( 43 | &leaf, 44 | &[&root1, &root2], 45 | &[&intermediate, &root1_cross], 46 | |param| param.set_flags(X509Flags::TRUSTED_FIRST), 47 | ) 48 | ); 49 | 50 | assert_eq!( 51 | Err(X509VerifyError::CERT_HAS_EXPIRED), 52 | verify( 53 | &leaf, 54 | &[&root1, &root2], 55 | &[&intermediate, &root1_cross], 56 | |param| param.clear_flags(X509Flags::TRUSTED_FIRST), 57 | ) 58 | ); 59 | 60 | assert_eq!( 61 | Ok(()), 62 | verify(&leaf, &[&root1], &[&intermediate, &root1_cross], |param| { 63 | param.clear_flags(X509Flags::TRUSTED_FIRST) 64 | },) 65 | ); 66 | } 67 | 68 | fn verify( 69 | cert: &X509Ref, 70 | trusted: &[&X509Ref], 71 | untrusted: &[&X509Ref], 72 | configure: impl FnOnce(&mut X509VerifyParamRef), 73 | ) -> X509VerifyResult { 74 | let trusted = { 75 | let mut builder = X509StoreBuilder::new().unwrap(); 76 | 77 | for cert in trusted { 78 | builder.add_cert((**cert).to_owned()).unwrap(); 79 | } 80 | 81 | builder.build() 82 | }; 83 | 84 | let untrusted = { 85 | let mut stack = Stack::new().unwrap(); 86 | 87 | for cert in untrusted { 88 | stack.push((**cert).to_owned()).unwrap(); 89 | } 90 | 91 | stack 92 | }; 93 | 94 | let mut store_ctx = X509StoreContext::new().unwrap(); 95 | 96 | store_ctx 97 | .init(&trusted, cert, &untrusted, |ctx| { 98 | configure(ctx.verify_param_mut()); 99 | ctx.verify_cert().unwrap(); 100 | 101 | Ok(ctx.verify_result()) 102 | }) 103 | .expect("failed to obtain X509VerifyResult") 104 | } 105 | -------------------------------------------------------------------------------- /boring/test/alt_name_cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDsDCCApigAwIBAgIBATANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJBVTET 3 | MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ 4 | dHkgTHRkMB4XDTE4MDExNTExMDcwM1oXDTI4MDExMzExMDcwM1owfDELMAkGA1UE 5 | BhMCVVMxCzAJBgNVBAgMAk5ZMREwDwYDVQQHDAhOZXcgWW9yazEVMBMGA1UECgwM 6 | RXhhbXBsZSwgTExDMTYwNAYDVQQDDC1FeGFtcGxlIENvbXBhbnkvZW1haWxBZGRy 7 | ZXNzPXRlc3RAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK 8 | AoIBAQCo9CWMRLMXo1CF/iORh9B4NhtJF/8tR9PlG95sNvyWuQQ/8jfev+8zErpl 9 | xfLkt0pJqcoiZG8g9NU0kU6o5T+/1QgZclCAoZaS0Jqxmoo2Yk/1Qsj16pnMBc10 10 | uSDk6V9aJSX1vKwONVNSwiHA1MhX+i7Wf7/K0niq+k7hOkhleFkWgZtUq41gXh1V 11 | fOugka7UktYnk9mrBbAMjmaloZNn2pMMAQxVg4ThiLm3zvuWqvXASWzUZc7IAd1G 12 | bN4AtDuhs252eqE9E4iTHk7F14wAS1JWqv666hReGHrmZJGx0xQTM9vPD1HN5t2U 13 | 3KTfhO/mTlAUWVyg9tCtOzboKgs1AgMBAAGjdDByMAkGA1UdEwQCMAAwCwYDVR0P 14 | BAQDAgWgMFgGA1UdEQRRME+CC2V4YW1wbGUuY29thwR/AAABhxAAAAAAAAAAAAAA 15 | AAAAAAABgRB0ZXN0QGV4YW1wbGUuY29thhZodHRwOi8vd3d3LmV4YW1wbGUuY29t 16 | MA0GCSqGSIb3DQEBCwUAA4IBAQAx14G99z/MnSbs8h5jSos+dgLvhc2IQB/3CChE 17 | hPyELc7iyw1iteRs7bS1m2NZx6gv6TZ6VydDrK1dnWSatQ7sskXTO+zfC6qjMwXl 18 | IV+u7T8EREwciniIA82d8GWs60BGyBL3zp2iUOr5ULG4+c/S6OLdlyJv+fDKv+Xo 19 | fKv1UGDi5rcvUBikeNkpEPTN9UsE9/A8XJfDyq+4RKuDW19EtzOOeVx4xpHOMnAy 20 | VVAQVMKJzhoXtLF4k2j409na+f6FIcZSBet+plmzfB+WZNIgUUi/7MQIXOFQRkj4 21 | zH3SnsPm/IYpJzlH2vHhlqIBdaSoTWpGVWPq7D+H8OS3mmXF 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /boring/test/cert-wildcard.pem: -------------------------------------------------------------------------------- 1 | notAfter=Aug 12 11:30:03 2026 GMT 2 | -----BEGIN CERTIFICATE----- 3 | MIIDKDCCAhACFGwwuilXOHjBjQ584FD9drp9Uh/LMA0GCSqGSIb3DQEBCwUAMEUx 4 | CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl 5 | cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjMxMjE4MTEzMDAzWhcNMjYwODEyMTEz 6 | MDAzWjBcMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE 7 | BwwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRUwEwYDVQQDDAwqLmZvb2Jhci5j 8 | b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCo9CWMRLMXo1CF/iOR 9 | h9B4NhtJF/8tR9PlG95sNvyWuQQ/8jfev+8zErplxfLkt0pJqcoiZG8g9NU0kU6o 10 | 5T+/1QgZclCAoZaS0Jqxmoo2Yk/1Qsj16pnMBc10uSDk6V9aJSX1vKwONVNSwiHA 11 | 1MhX+i7Wf7/K0niq+k7hOkhleFkWgZtUq41gXh1VfOugka7UktYnk9mrBbAMjmal 12 | oZNn2pMMAQxVg4ThiLm3zvuWqvXASWzUZc7IAd1GbN4AtDuhs252eqE9E4iTHk7F 13 | 14wAS1JWqv666hReGHrmZJGx0xQTM9vPD1HN5t2U3KTfhO/mTlAUWVyg9tCtOzbo 14 | Kgs1AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAHG83qKMl5bPoL2s7TaJZ909NaQO 15 | 4C69ueXlD4HJEFe7L9mkeQoDaF7RwWSBwN2RZT5hzQhghRotqLA06XwKbQHji/R7 16 | sYYVUHunobFUHsr51tFN1BIDoAWJa0N2rm/OxbcK471eWNKjMiS2vvvPdaMxxHAx 17 | IsjAJBJec4IxNIUNNKqCS/xNYcdiyrmmU3oFWGqb0As/eDOBw0Amd0aayasFJrRV 18 | 3KZI5OcFg/J3XvdaxMJD+RPyUysKRXg6K8jzYc/PB8LhWVXpLxjEzeO2IHCaZprh 19 | dUTP8+Ob+ioxujvlslxc4nrrUD5EWwnpEIr7e4af27JHQVaNyHbRw6wI2uk= 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /boring/test/cert-with-intermediate.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDMzCCAhsCFBEiNxpuknaO7Pw1Yi88UW4aiGo0MA0GCSqGSIb3DQEBCwUAMFIx 3 | CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMS4wLAYDVQQKDCVJbnRl 4 | cm5ldCBXaWRnaXRzIFB0eSBMdGQgSW50ZXJtZWRpYXRlMB4XDTI0MDEwMzE0MjIz 5 | MFoXDTI2MDgxMjE0MjIzMFowWjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUt 6 | U3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDETMBEGA1UE 7 | AwwKZm9vYmFyLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKj0 8 | JYxEsxejUIX+I5GH0Hg2G0kX/y1H0+Ub3mw2/Ja5BD/yN96/7zMSumXF8uS3Skmp 9 | yiJkbyD01TSRTqjlP7/VCBlyUIChlpLQmrGaijZiT/VCyPXqmcwFzXS5IOTpX1ol 10 | JfW8rA41U1LCIcDUyFf6LtZ/v8rSeKr6TuE6SGV4WRaBm1SrjWBeHVV866CRrtSS 11 | 1ieT2asFsAyOZqWhk2fakwwBDFWDhOGIubfO+5aq9cBJbNRlzsgB3UZs3gC0O6Gz 12 | bnZ6oT0TiJMeTsXXjABLUlaq/rrqFF4YeuZkkbHTFBMz288PUc3m3ZTcpN+E7+ZO 13 | UBRZXKD20K07NugqCzUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEANWlOvyLEHdPV 14 | 8rMdfqLTZZyA79L1N3bP1FWS97fF36Y9EnTKChenwkBob1abY4jQ2/LICKND+ux8 15 | xDlmMlYRH4aM5bXAjOcdpmq9R9SuzsK/2m79xONF//AX4zb0s5b+QEwdYkfJ5jiO 16 | xMrnatwHQhFvQIQvuTo2o0WZEnkubNYDxVh7UOv9cOQjwm0+58CIEG5SHR9grG5u 17 | TTswu7DswgpfSCKKPaFCF4pWxLfryYwadO0/4Ot/ZbElbAdJYC8CI1QC14knk2cD 18 | 0ZG9jaVPP9wCAt/ZIu8NbsZN7DNbISaXVfMju+xSdey8B3FLRkLq9TKmnLum5LR2 19 | TyM6hDIh8A== 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /boring/test/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDGzCCAgMCCQCHcfe97pgvpTANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB 3 | VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 4 | cyBQdHkgTHRkMB4XDTE2MDgxNDE3MDAwM1oXDTI2MDgxMjE3MDAwM1owWjELMAkG 5 | A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0 6 | IFdpZGdpdHMgUHR5IEx0ZDETMBEGA1UEAwwKZm9vYmFyLmNvbTCCASIwDQYJKoZI 7 | hvcNAQEBBQADggEPADCCAQoCggEBAKj0JYxEsxejUIX+I5GH0Hg2G0kX/y1H0+Ub 8 | 3mw2/Ja5BD/yN96/7zMSumXF8uS3SkmpyiJkbyD01TSRTqjlP7/VCBlyUIChlpLQ 9 | mrGaijZiT/VCyPXqmcwFzXS5IOTpX1olJfW8rA41U1LCIcDUyFf6LtZ/v8rSeKr6 10 | TuE6SGV4WRaBm1SrjWBeHVV866CRrtSS1ieT2asFsAyOZqWhk2fakwwBDFWDhOGI 11 | ubfO+5aq9cBJbNRlzsgB3UZs3gC0O6GzbnZ6oT0TiJMeTsXXjABLUlaq/rrqFF4Y 12 | euZkkbHTFBMz288PUc3m3ZTcpN+E7+ZOUBRZXKD20K07NugqCzUCAwEAATANBgkq 13 | hkiG9w0BAQsFAAOCAQEASvYHuIl5C0NHBELPpVHNuLbQsDQNKVj3a54+9q1JkiMM 14 | 6taEJYfw7K1Xjm4RoiFSHpQBh+PWZS3hToToL2Zx8JfMR5MuAirdPAy1Sia/J/qE 15 | wQdJccqmvuLkLTSlsGbEJ/LUUgOAgrgHOZM5lUgIhCneA0/dWJ3PsN0zvn69/faY 16 | oo1iiolWiIHWWBUSdr3jM2AJaVAsTmLh00cKaDNk37JB940xConBGSl98JPrNrf9 17 | dUAiT0iIBngDBdHnn/yTj+InVEFyZSKrNtiDSObFHxPcxGteHNrCPJdP1e+GqkHp 18 | HJMRZVCQpSMzvHlofHSNgzWV1MX5h1CP4SGZdBDTfA== 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /boring/test/certs.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDGzCCAgMCCQCHcfe97pgvpTANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB 3 | VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 4 | cyBQdHkgTHRkMB4XDTE2MDgxNDE3MDAwM1oXDTI2MDgxMjE3MDAwM1owWjELMAkG 5 | A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0 6 | IFdpZGdpdHMgUHR5IEx0ZDETMBEGA1UEAwwKZm9vYmFyLmNvbTCCASIwDQYJKoZI 7 | hvcNAQEBBQADggEPADCCAQoCggEBAKj0JYxEsxejUIX+I5GH0Hg2G0kX/y1H0+Ub 8 | 3mw2/Ja5BD/yN96/7zMSumXF8uS3SkmpyiJkbyD01TSRTqjlP7/VCBlyUIChlpLQ 9 | mrGaijZiT/VCyPXqmcwFzXS5IOTpX1olJfW8rA41U1LCIcDUyFf6LtZ/v8rSeKr6 10 | TuE6SGV4WRaBm1SrjWBeHVV866CRrtSS1ieT2asFsAyOZqWhk2fakwwBDFWDhOGI 11 | ubfO+5aq9cBJbNRlzsgB3UZs3gC0O6GzbnZ6oT0TiJMeTsXXjABLUlaq/rrqFF4Y 12 | euZkkbHTFBMz288PUc3m3ZTcpN+E7+ZOUBRZXKD20K07NugqCzUCAwEAATANBgkq 13 | hkiG9w0BAQsFAAOCAQEASvYHuIl5C0NHBELPpVHNuLbQsDQNKVj3a54+9q1JkiMM 14 | 6taEJYfw7K1Xjm4RoiFSHpQBh+PWZS3hToToL2Zx8JfMR5MuAirdPAy1Sia/J/qE 15 | wQdJccqmvuLkLTSlsGbEJ/LUUgOAgrgHOZM5lUgIhCneA0/dWJ3PsN0zvn69/faY 16 | oo1iiolWiIHWWBUSdr3jM2AJaVAsTmLh00cKaDNk37JB940xConBGSl98JPrNrf9 17 | dUAiT0iIBngDBdHnn/yTj+InVEFyZSKrNtiDSObFHxPcxGteHNrCPJdP1e+GqkHp 18 | HJMRZVCQpSMzvHlofHSNgzWV1MX5h1CP4SGZdBDTfA== 19 | -----END CERTIFICATE----- 20 | -----BEGIN CERTIFICATE----- 21 | MIIDXTCCAkWgAwIBAgIJAOIvDiVb18eVMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV 22 | BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX 23 | aWRnaXRzIFB0eSBMdGQwHhcNMTYwODE0MTY1NjExWhcNMjYwODEyMTY1NjExWjBF 24 | MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 25 | ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB 26 | CgKCAQEArVHWFn52Lbl1l59exduZntVSZyDYpzDND+S2LUcO6fRBWhV/1Kzox+2G 27 | ZptbuMGmfI3iAnb0CFT4uC3kBkQQlXonGATSVyaFTFR+jq/lc0SP+9Bd7SBXieIV 28 | eIXlY1TvlwIvj3Ntw9zX+scTA4SXxH6M0rKv9gTOub2vCMSHeF16X8DQr4XsZuQr 29 | 7Cp7j1I4aqOJyap5JTl5ijmG8cnu0n+8UcRlBzy99dLWJG0AfI3VRJdWpGTNVZ92 30 | aFff3RpK3F/WI2gp3qV1ynRAKuvmncGC3LDvYfcc2dgsc1N6Ffq8GIrkgRob6eBc 31 | klDHp1d023Lwre+VaVDSo1//Y72UFwIDAQABo1AwTjAdBgNVHQ4EFgQUbNOlA6sN 32 | XyzJjYqciKeId7g3/ZowHwYDVR0jBBgwFoAUbNOlA6sNXyzJjYqciKeId7g3/Zow 33 | DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAVVaR5QWLZIRR4Dw6TSBn 34 | BQiLpBSXN6oAxdDw6n4PtwW6CzydaA+creiK6LfwEsiifUfQe9f+T+TBSpdIYtMv 35 | Z2H2tjlFX8VrjUFvPrvn5c28CuLI0foBgY8XGSkR2YMYzWw2jPEq3Th/KM5Catn3 36 | AFm3bGKWMtGPR4v+90chEN0jzaAmJYRrVUh9vea27bOCn31Nse6XXQPmSI6Gyncy 37 | OAPUsvPClF3IjeL1tmBotWqSGn1cYxLo+Lwjk22A9h6vjcNQRyZF2VLVvtwYrNU3 38 | mwJ6GCLsLHpwW/yjyvn8iEltnJvByM/eeRnfXV6WDObyiZsE/n6DxIRJodQzFqy9 39 | GA== 40 | -----END CERTIFICATE----- 41 | -------------------------------------------------------------------------------- /boring/test/cms.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/boring/05f798adc485b7a37028f49a5828fa5bf07aadf3/boring/test/cms.p12 -------------------------------------------------------------------------------- /boring/test/cms_pubkey.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/boring/05f798adc485b7a37028f49a5828fa5bf07aadf3/boring/test/cms_pubkey.der -------------------------------------------------------------------------------- /boring/test/dhparams.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN DH PARAMETERS----- 2 | MIIBCAKCAQEAh3Betv+hf5jNsOmGXU8oxuABD2B8r0yU8FVgjnCZBSVo61qJ0A2d 3 | J6r8rYKbjtolnrZN/V4IPSzYvxurHbu8nbiFVyhOySPchI2Fu+YT/HsSe/0MH9bW 4 | gJTNzmutWoy9VxtWLCmXnOSZHep3MZ1ZNimno6Kh2qQ7VJr0+KF8GbxUKOPv4SqK 5 | NBwouIQXFc0pE9kGhcGKbr7TnHhyJFCRLNP1OVDQZbcoKjk1Vh+5sy7vM2VUTQmM 6 | yOToT2LEZVAUJXNumcYMki9MIwfYCwYZbNt0ZEolyHzUEesuyHfU1eJd6+sKEjUz 7 | 5GteQIR7AehxZIS+cytu7BXO7B0owLJ2awIBAg== 8 | -----END DH PARAMETERS----- 9 | -------------------------------------------------------------------------------- /boring/test/dsa.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN DSA PRIVATE KEY----- 2 | MIIBuwIBAAKBgQCkKe/jtYKJNQafaE7kg2aaJOEPUV0Doi451jkXHp5UfLh6+t42 3 | eabSGkE9WBAlILgaB8yHckLe9+zozN39+SUDp94kb2r38/8w/9Ffhbsep9uiyOj2 4 | ZRQur6SkpKQDKcnAd6IMZXZcvdSgPC90A6qraYUZKq7Csjn63gbC+IvXHwIVAIgS 5 | PE43lXD8/rGYxos4cxCgGGAxAoGASMV56WhLvVQtWMVI36WSIxbZnC2EsnNIKeVW 6 | yXnP/OmPJ2mdezG7i1alcwsO2TnSLbvjvGPlyzIqZzHvWC8EmDqsfbU+n8we/Eal 7 | sm5nloC8m9ECWpbTzbNdvrAAj9UPVWjcDwg7grAGGysh6lGbBv5P+4zL/niq1UiE 8 | LnKcifgCgYEAo6mAasO0+MVcu8shxxUXXNeTLsZ8NB/BIx9EZ/dzE23ivNW8dq1A 9 | eecAAYhssI2m/CspQvyKw+seCvg4FccxJgB3+mGOe+blFHwO3eAwoyRn/t3DZDHh 10 | FjxKKRsQdy4BkZv+vhTyIYYCw0iPZ5Wfln+pyGGTveIDED1MPG+J6c8CFCJAUlEl 11 | 4nHvbC15xLXXpd46zycY 12 | -----END DSA PRIVATE KEY----- 13 | -------------------------------------------------------------------------------- /boring/test/dsa.pem.pub: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBtzCCASsGByqGSM44BAEwggEeAoGBAKQp7+O1gok1Bp9oTuSDZpok4Q9RXQOi 3 | LjnWORcenlR8uHr63jZ5ptIaQT1YECUguBoHzIdyQt737OjM3f35JQOn3iRvavfz 4 | /zD/0V+Fux6n26LI6PZlFC6vpKSkpAMpycB3ogxldly91KA8L3QDqqtphRkqrsKy 5 | OfreBsL4i9cfAhUAiBI8TjeVcPz+sZjGizhzEKAYYDECgYBIxXnpaEu9VC1YxUjf 6 | pZIjFtmcLYSyc0gp5VbJec/86Y8naZ17MbuLVqVzCw7ZOdItu+O8Y+XLMipnMe9Y 7 | LwSYOqx9tT6fzB78RqWybmeWgLyb0QJaltPNs12+sACP1Q9VaNwPCDuCsAYbKyHq 8 | UZsG/k/7jMv+eKrVSIQucpyJ+AOBhQACgYEAo6mAasO0+MVcu8shxxUXXNeTLsZ8 9 | NB/BIx9EZ/dzE23ivNW8dq1AeecAAYhssI2m/CspQvyKw+seCvg4FccxJgB3+mGO 10 | e+blFHwO3eAwoyRn/t3DZDHhFjxKKRsQdy4BkZv+vhTyIYYCw0iPZ5Wfln+pyGGT 11 | veIDED1MPG+J6c8= 12 | -----END PUBLIC KEY----- 13 | -------------------------------------------------------------------------------- /boring/test/dsaparam.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN DSA PARAMETERS----- 2 | MIIBHgKBgQCkKe/jtYKJNQafaE7kg2aaJOEPUV0Doi451jkXHp5UfLh6+t42eabS 3 | GkE9WBAlILgaB8yHckLe9+zozN39+SUDp94kb2r38/8w/9Ffhbsep9uiyOj2ZRQu 4 | r6SkpKQDKcnAd6IMZXZcvdSgPC90A6qraYUZKq7Csjn63gbC+IvXHwIVAIgSPE43 5 | lXD8/rGYxos4cxCgGGAxAoGASMV56WhLvVQtWMVI36WSIxbZnC2EsnNIKeVWyXnP 6 | /OmPJ2mdezG7i1alcwsO2TnSLbvjvGPlyzIqZzHvWC8EmDqsfbU+n8we/Ealsm5n 7 | loC8m9ECWpbTzbNdvrAAj9UPVWjcDwg7grAGGysh6lGbBv5P+4zL/niq1UiELnKc 8 | ifg= 9 | -----END DSA PARAMETERS----- 10 | -------------------------------------------------------------------------------- /boring/test/echconfig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/boring/05f798adc485b7a37028f49a5828fa5bf07aadf3/boring/test/echconfig -------------------------------------------------------------------------------- /boring/test/echconfig-2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/boring/05f798adc485b7a37028f49a5828fa5bf07aadf3/boring/test/echconfig-2 -------------------------------------------------------------------------------- /boring/test/echconfiglist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/boring/05f798adc485b7a37028f49a5828fa5bf07aadf3/boring/test/echconfiglist -------------------------------------------------------------------------------- /boring/test/echconfiglist-2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/boring/05f798adc485b7a37028f49a5828fa5bf07aadf3/boring/test/echconfiglist-2 -------------------------------------------------------------------------------- /boring/test/echkey: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/boring/05f798adc485b7a37028f49a5828fa5bf07aadf3/boring/test/echkey -------------------------------------------------------------------------------- /boring/test/echkey-2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/boring/05f798adc485b7a37028f49a5828fa5bf07aadf3/boring/test/echkey-2 -------------------------------------------------------------------------------- /boring/test/identity.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/boring/05f798adc485b7a37028f49a5828fa5bf07aadf3/boring/test/identity.p12 -------------------------------------------------------------------------------- /boring/test/intermediate-ca.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEApyZi0joAIH8M7GaitH27lQs1XZaXwv5PI9bIeSkIX4BafIcU 3 | I7hdPLreJoP6EsuoswXyhgZU8FQvNRjMAGPzvVviG+vY/l6jFI+JqW7Pphr35QJO 4 | jqxwW/K/wYei0FS6C3SF+CJ2yIgG4YlJM9Hoq/EpfIxmyuZ2yHD/JiQ5kxNP7vKT 5 | 77f7k5zpCVG1kiQZKQT4iysQDz2e1ozi3W7NCDqnvM9+EX1DREiCqRUwuzIuTlkA 6 | Z+8HpET5wS2Pm5uJQXDCid0slYMcuOL6JoHekbs69wkt4powcPPhXWoTCyUjpqsx 7 | w0KPiBzViJUsd8JUq8ge+jVqP2pzvkQk4BWTMwIDAQABAoIBAFBiEHIjPH5kOzXQ 8 | 4fxE3xn9KuvYCSHYJP0KRJyn1AQBeQKb/15yQjx7bWw+WdwCHx4BBTHZB64P/ifd 9 | xfWGG+h7sJBW6qLhpjG0GbLmvGuYWpDCfD72xI4jfn42mWDw7gumPOsov9EOQaji 10 | 2dZW4zsVHitsZd672HHqjXmtQBbvEcm/cuPoL2/s9crKwHbF90kb9BOYcz+/U138 11 | zPfXFM5AbivpiQHA7CmMrQ833op42fQCfrxGs1XkZPO30EPpCtmHE6+Xhv+GGWp/ 12 | EF7D6pwkT4OElgzAP6cthPqZOtQLE2LCM/mTy/cFRtrBjKPlZ/UBrxjaLI1FHoAk 13 | TYX81GECgYEA451EWFCrX5vWEMJE7zCALiQbxxOhQCIEufvkxUvDnsqjTPj/ylpZ 14 | usu+YC5BcgwVxjso8dEL8pcd8pAFVLeX8wBDeGfTdhUmPPZmx8ViFbZTVq/Tz+Jw 15 | tHo0foq8gfTLJrvdzuyFEHR6z5O16EtKOa+G1gIF/atV8hO/oRXLXvECgYEAu/7B 16 | SpzbBJoxame27uaakYYVKuAD2wVAmfP9XuTV6IvE845sgmzTYURxSnO0RZCuNj5x 17 | 7u//HdGoFuA3o4Un4XLx1qot2op9ql1xuD/V3aqrvhpzvoCklks6t+PW3/Sef6TH 18 | 21TOCahpoQqD/UrSrHEJXC7lmkfcPCir3QflnGMCgYBV2AFnwXzwwShaB7rR7xvY 19 | yxuC2H9vXaUks8DTPEDaCZjPNfXaznqa/a6ePbPHHJG1wqgtk2cLJj1QN0sbaWaw 20 | akAIEDhrh4x1X4TiASp9/9askgGznLZfCtvzgcWYycc4o5ADM6b3zsZmtVHc+1BS 21 | M0YKPpcd1dnDQ/l4+mxKMQKBgQCenCB+j/plVqaMjLaVty//yW2AgAIgvryzZ1yE 22 | vHMRQSNJDgfUvnZVIUaoNxiIfLnPAD5mBkxq3yF/M2sd5lEwcCdEIs6PDLtbin1Q 23 | o2MQI1fFC1JODwFN4GjJD0ySJTO4o9EO5uzyzwlXmqSjhoZagQARq2uCEFDq3LGr 24 | yWba2wKBgDT+5qjFweI5RmE1pDvUH9gOPNflyOpyjnbueunmTn1rzcRhx9xgM3QB 25 | ehWCRR1Y/vAnu5uupf0rG/Y/gtvIVyC3F+0csNox7T9e0t4sdgORYOVWbvsIF2t9 26 | 2HYjY782ws3EBF5yNKJDgV2sNjA0Wpb6lahkxRut314jnwGplR2w 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /boring/test/intermediate-ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDdTCCAl2gAwIBAgIUE+pcEHU5e4wkMpp2qoyHy+wH8aMwDQYJKoZIhvcNAQEL 3 | BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDAxMDMxMDM0MDdaFw0yNjA4 5 | MTIxMDM0MDdaMFIxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMS4w 6 | LAYDVQQKDCVJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQgSW50ZXJtZWRpYXRlMIIB 7 | IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApyZi0joAIH8M7GaitH27lQs1 8 | XZaXwv5PI9bIeSkIX4BafIcUI7hdPLreJoP6EsuoswXyhgZU8FQvNRjMAGPzvVvi 9 | G+vY/l6jFI+JqW7Pphr35QJOjqxwW/K/wYei0FS6C3SF+CJ2yIgG4YlJM9Hoq/Ep 10 | fIxmyuZ2yHD/JiQ5kxNP7vKT77f7k5zpCVG1kiQZKQT4iysQDz2e1ozi3W7NCDqn 11 | vM9+EX1DREiCqRUwuzIuTlkAZ+8HpET5wS2Pm5uJQXDCid0slYMcuOL6JoHekbs6 12 | 9wkt4powcPPhXWoTCyUjpqsxw0KPiBzViJUsd8JUq8ge+jVqP2pzvkQk4BWTMwID 13 | AQABo1AwTjAdBgNVHQ4EFgQUZ3ELefSmoenETTa39CQwVzdoLZcwHwYDVR0jBBgw 14 | FoAUbNOlA6sNXyzJjYqciKeId7g3/ZowDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B 15 | AQsFAAOCAQEAHCXvCU1XQM5a7hhgrMcKQRto9GEVIljnPv5H7x+wPvCfR1By/kzI 16 | fsl+hA1q02ymLtOW16aq4si4exsQl4SktC+5hyhu0yOCevRYXCcrh5NrNbwTFnK/ 17 | LIP4dRz4XBxC9pYg0rqvo+v64at6EBTXxYfBHo9Cj0QuZIJoYmGEOojdE0PudZdc 18 | b1iuXk9FZlUueFq8uSkHD7EpxonPS9iWQA5dbw1Q+0hWqvA/npAvFXBHkF/Jyaht 19 | fpqoUrr4LYUr/ShC1IVHG7TAEElnOGz0dY6uxkr1B6YxBvCJ6gesk2cJBC1i3g9I 20 | 9xvt4zxQNQynX0IHcar8xfgZqD4ZdWQY0w== 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /boring/test/key.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/boring/05f798adc485b7a37028f49a5828fa5bf07aadf3/boring/test/key.der -------------------------------------------------------------------------------- /boring/test/key.der.pub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/boring/05f798adc485b7a37028f49a5828fa5bf07aadf3/boring/test/key.der.pub -------------------------------------------------------------------------------- /boring/test/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCo9CWMRLMXo1CF 3 | /iORh9B4NhtJF/8tR9PlG95sNvyWuQQ/8jfev+8zErplxfLkt0pJqcoiZG8g9NU0 4 | kU6o5T+/1QgZclCAoZaS0Jqxmoo2Yk/1Qsj16pnMBc10uSDk6V9aJSX1vKwONVNS 5 | wiHA1MhX+i7Wf7/K0niq+k7hOkhleFkWgZtUq41gXh1VfOugka7UktYnk9mrBbAM 6 | jmaloZNn2pMMAQxVg4ThiLm3zvuWqvXASWzUZc7IAd1GbN4AtDuhs252eqE9E4iT 7 | Hk7F14wAS1JWqv666hReGHrmZJGx0xQTM9vPD1HN5t2U3KTfhO/mTlAUWVyg9tCt 8 | OzboKgs1AgMBAAECggEBAKLj6IOJBKXolczpzb8UkyAjAkGBektcseV07gelJ/fk 9 | 3z0LuWPv5p12E/HlXB24vU2x/ikUbbP3eMsawRzDEahQqmNmPEkYAYUAy/Qpi9GN 10 | DYvn3LqDec4jVgeQKS+p9H2DzUpTogp8zR2//yzbuWBg2+F//xh7vU0S0RQCziPM 11 | x7RSBgbhxSfChfEJbS2sDnzfh0jRQmoY95iFv7puet1FJtzdZ4fgCd1RqmC2lFM5 12 | H0eZtN/Cz19lieVs0b996DErdEBqClVZO00eYbRozCDaBzRU3ybB/dMrGJxhkkXm 13 | wb3kWMtziH9qOYsostuHIFu8eKFLloKxFnq2R4DGxOECgYEA2KUIZISOeGJSBcLJ 14 | JAUK2gvgXPNo4HHWIwOA9xeN3ZJlsnPlffXQNnm6t1st1V2gfMm9I2n0m/F0y2B/ 15 | n/XGSa8bghfPA9l0c2h58lkL3JQJR/paa8ycTz+YZPrznEyN7Qa0RrJXUvZv9lQL 16 | Hc3+FHcSHgMqDV2f2bHAEu9YGi0CgYEAx6VEIPNvrHFgjo/jk1RTuk+m0xEWQsZL 17 | Cs+izQMr2TaeJn8LG+93AvFuYn0J0nT3WuStLPrUg8i4IhSS6lf1tId5ivIZPm4r 18 | YwMyblBJXhnHbk7Uqodjfw/3s6V2HAu++B7hTdyVr9DFuST9uv4m8bkPV8rfX1jE 19 | I2rAPVWvgikCgYB+wNAQP547wQrMZBLbCDg5KwmyWJfb+b6X7czexOEz6humNTjo 20 | YZHYzY/5B1fhpk3ntQD8X1nGg5caBvOk21+QbOtjShrM3cXMYCw5JvBRtitX+Zo9 21 | yBEMLOE0877ki8XeEDYZxu5gk98d+D4oygUGZEQtWxyXhVepPt5qNa8OYQKBgQDH 22 | RVgZI6KFlqzv3wMh3PutbS9wYQ+9GrtwUQuIYe/0YSW9+vSVr5E0qNKrD28sV39F 23 | hBauXLady0yvB6YUrjMbPFW+sCMuQzyfGWPO4+g3OrfqjFiM1ZIkE0YEU9Tt7XNx 24 | qTDtTI1D7bhNMnTnniI1B6ge0und+3XafAThs5L48QKBgQCTTpfqMt8kU3tcI9sf 25 | 0MK03y7kA76d5uw0pZbWFy7KI4qnzWutCzb+FMPWWsoFtLJLPZy//u/ZCUVFVa4d 26 | 0Y/ASNQIESVPXFLAltlLo4MSmsg1vCBsbviEEaPeEjvMrgki93pYtd/aOSgkYC1T 27 | mEq154s5rmqh+h+XRIf7Au0SLw== 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /boring/test/key.pem.pub: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr1bXMptaIgOL9PVL8a7W 3 | KG/C8+IbxP018eMBQZT0SnPQmXp0Q8Aai/F+AEDE7b5sO5U7WdxU4GRYw0wqkQNF 4 | si78KNfoj2ZMlx6NRfl4UKuzrpGTPgQxuKDYedngPpWcbmW4P3zEL2Y7b18n9NJr 5 | atRUzH1Zh/ReRO525Xadu58aviPw1Mzgse7cKyzb03Gll9noLnYNIIpO8jL+QyrD 6 | 8qNmfacmR20U0a6XDTtmsmk7AitGETICbTT0KRf+oAP0yIHoonllPpNLUEPZQjrp 7 | ClS/S/wKdj7gaq9TaMbHULhFMjbCV8cuPu//rUAuWp3riaznZGOVQyn3Dp2CB3ad 8 | yQIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /boring/test/keystore-empty-chain.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/boring/05f798adc485b7a37028f49a5828fa5bf07aadf3/boring/test/keystore-empty-chain.p12 -------------------------------------------------------------------------------- /boring/test/nid_test_cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIB1DCCAX6gAwIBAgIJAMzXWZGWHleWMA0GCSqGSIb3DQEBCwUAMFYxHzAdBgkq 3 | hkiG9w0BCQEWEHRlc3RAZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29t 4 | MR0wGwYJKoZIhvcNAQkUHg4ARQB4AGEAbQBwAGwAZTAeFw0xNTA3MDEwNjQ3NDRa 5 | Fw0xNTA3MzEwNjQ3NDRaMFYxHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhhbXBsZS5j 6 | b20xFDASBgNVBAMMC2V4YW1wbGUuY29tMR0wGwYJKoZIhvcNAQkUHg4ARQB4AGEA 7 | bQBwAGwAZTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCmejzp4+o35FD0hAnx2trL 8 | 08h07X5jZca9DgZH35hWXPh7fMucLt/IPXIRnz2zKEa/Mo6D2V/fx03Mqo0epid7 9 | AgMBAAGjLzAtMB0GA1UdDgQWBBRQa57tXz3rZNRz+fTbo3w3jQJMBTAMBgNVHRME 10 | BTADAQH/MA0GCSqGSIb3DQEBCwUAA0EAm0iY9cr+gvC+vcQIebdofpQ4GcDW8U6W 11 | Bxs8ZXinLl69P0jYLum3+XITNFRiyQqcivaxdxthxDNOX7P+aKwkJA== 12 | -----END CERTIFICATE----- 13 | -------------------------------------------------------------------------------- /boring/test/nid_uid_test_cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEGTCCAwGgAwIBAgIJAItKTzcGfL1lMA0GCSqGSIb3DQEBCwUAMIGiMSIwIAYK 3 | CZImiZPyLGQBAQwSdGhpcyBpcyB0aGUgdXNlcklkMQswCQYDVQQGEwJVUzETMBEG 4 | A1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJU3Vubnl2YWxlMRUwEwYDVQQKDAxS 5 | dXN0IE9wZW5TU0wxDDAKBgNVBAsMA09TUzEhMB8GA1UEAwwYcnVzdC1vcGVuc3Ns 6 | LmV4YW1wbGUuY29tMB4XDTE2MDIwMjE3MjIwMVoXDTE2MDMwMzE3MjIwMVowgaIx 7 | IjAgBgoJkiaJk/IsZAEBDBJ0aGlzIGlzIHRoZSB1c2VySWQxCzAJBgNVBAYTAlVT 8 | MRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlTdW5ueXZhbGUxFTATBgNV 9 | BAoMDFJ1c3QgT3BlblNTTDEMMAoGA1UECwwDT1NTMSEwHwYDVQQDDBhydXN0LW9w 10 | ZW5zc2wuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB 11 | AQDa3Gc+IE5DOhTv1m5DZW8qKiyNLd7v4DaAYLXSsDuLs+9wJ+Bs+wlBfrg+PT0t 12 | EJlPaLL9IfD5eR3WpFu62TUexYhnJh+3vhCGsFHOXcTjtM+wy/dzZtOVh2wTzvqE 13 | /FHBGw1eG3Ww+RkSFbwYmtm8JhIN8ffYxGn2O0yQpxypf5hNPYrC81zX+52X2w1h 14 | jDYLpYt55w+e6q+iRRFk0tKiWHEqqh/r6UQQRpj2EeS+xTloZlO6h0nl2NPkVF3r 15 | CXBoT8Ittxr7sqcYqf8TAA0I4qZRYXKYehFmv/VkSt85CcURJ/zXeoJ1TpxSvQie 16 | 2R9cRDkYROrIOAFbB/0mmHLBAgMBAAGjUDBOMB0GA1UdDgQWBBRKfPqtgrbdbTmH 17 | XR6RC/p8t/65GjAfBgNVHSMEGDAWgBRKfPqtgrbdbTmHXR6RC/p8t/65GjAMBgNV 18 | HRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCKfeGRduGsIwKNiGcDUNkNrc7Z 19 | f8SWAmb/R6xiDfgjbhrtfBDowIZ5natEkTgf6kQPMJKyjg2NEM2uJWBc55rLOHIv 20 | es1wQOlYjfEUmFD3lTIt2TM/IUgXn2j+zV1CRkJthQLVFChXsidd0Bqq2fBjd3ad 21 | Yjzrxf3uOTBAs27koh2INNHfcUZCRsx8hP739zz2kw/r5NB/9iyENEyJKQvxo0jb 22 | oN0JK2joGZrWetDukQrqf032TsdkboW5JresYybbAD3326Ljp+hlT/3WINc+3nZJ 23 | Dn+pPMdpuZ5BUZ+u+XyNEPum3k3P3K19AF+zWYGooX0J1cmuCBrrqce20Lwy 24 | -----END CERTIFICATE----- 25 | -------------------------------------------------------------------------------- /boring/test/pkcs1.pem.pub: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PUBLIC KEY----- 2 | MIIBCgKCAQEAyrcf7lv42BCoiDd3LYmF8eaGO4rhmGzGgi+NSZowkEuLhibHGQle 3 | FkZC7h1VKsxKFgy7Fx+GYHkv9OLm9H5fdp3HhYlo19bZVGvSJ66OJe/Bc4S02bBb 4 | Y8vwpc/N5O77m5J/nHLuL7XJtpfSKkX+3NPiX1X2L99iipt7F0a7hNws3G3Lxg6t 5 | P3Yc55TPjXzXvDIgjt/fag6iF8L/bR3augJJdDhLzNucR8A5HcvPtIVo51R631Zq 6 | MCh+dZvgz9zGCXwsvSky/iOJTHN3wnpsWuCAzS1iJMfjR783Tfv6sWFs19FH7pHP 7 | xBA3b2enPM9KBzINGOly0eM4h0fh+VBltQIDAQAB 8 | -----END RSA PUBLIC KEY----- 9 | -------------------------------------------------------------------------------- /boring/test/pkcs8-nocrypt.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/boring/05f798adc485b7a37028f49a5828fa5bf07aadf3/boring/test/pkcs8-nocrypt.der -------------------------------------------------------------------------------- /boring/test/pkcs8.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/boring/05f798adc485b7a37028f49a5828fa5bf07aadf3/boring/test/pkcs8.der -------------------------------------------------------------------------------- /boring/test/root-ca-2.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAwSgPW00nGOTje0FHm1HXT3ANHjW3HGPbxiMSRvSoBEdv8R9e 3 | 0PoKjse0xQsa5nKolbUhJBS6dkH+13VPOs6e+vZyXD6OYQfi2e42Mw0SPFl5g2qU 4 | fy/17q3ipqPJnT6YHhGpx/P91F7yjDAF2MvNRGQ/6AYWD4MUr6+5vPHEYhQ/JlAK 5 | cN/uWUM+bg2mwMNpmuGRV1tg0hKl9nT1fX1i6OSszPLIY1dC0eHv2gRh1unrXNoY 6 | A76xohiwcJYAAPjrx167S8td8gmWnOwYnCnGIw2ceNpEQv4/bEvtKiLGOu4HUO/x 7 | a7emy1QZgJth5TqQf0pwPEJC0mV5LFoXtlOpjwIDAQABAoIBAB3xBshhYlEikfy2 8 | NtJl0ll3BiGLtBHLjPLe1uN242CebkTTVxBP4jkVzfjJaucUGPvz8uoz6F+ShV2C 9 | ysBT7SL79uhDrjBuV4TuvyoUuaHvQL3VVKWOmrHf9IVeWE9ut4fZtxbOxKcZ/MEs 10 | ZIuhs/UJETr3To4jBJ7jP4iBda66LXeGDwnhia04zpFWwRRyRFvdVwTvMm9TN2GY 11 | 21J6rnc44bGQ/l0qsxSrv/OuW3ZmFNz85mRbuLswgawJgjMSlt1orLz5mdofDxlz 12 | 898nSrpJq/mAj8kgV+sH2jGv5FVZrv30W/0GlXObiJLhJiywYkpXbSn/H//w9+ij 13 | ItQXjDECgYEA9RNootH8F0vuWVezKrt9ApE+8AzwltDgMlFc/HEjINnKFg7SWsSN 14 | oinPVcQ07PS+2E7Fgv/6IFv/RTATGIPYrNWGE17dR8xmwh+Xrexz45c49cHTwVCF 15 | VM2J0PvlcEEAscMe1bd5HqIOJm+hvvqfwWdacUPZgtsaS9F/e33vj0cCgYEAycQx 16 | c5EQ9T82z+qMhQ/mf63kYMYDsHbv6F92Pt38V24yh8NTW5lEcV+NonDFrPVH5Vd3 17 | gU1lvXnG8e3Aj6EXOeEzfu9dpfdyqyZXIU2hbRPYuha+goBd03pM8+YEIgJX3ktu 18 | 1Q+G6uMrSLVbe+l06OEcYmvj8xGNWrk/3+ZiB3kCgYEA15+c92xjLSgcbDTyKU3O 19 | Pj0Gr/Pilf7u0ratZlowew3DdMbToxK+PogkqKQ5oKXxZ6Vet9R6AJCQtxIGKxKN 20 | x/sRvOdBL5OScYeUT2zzxbFeZzODGNm8hZFViS6nfq1ibARtk8Gaai5Q3tZm6/3c 21 | IzDI7VCyBiS6LS0EyeVSqa8CgYBRmM6G9jvtcssv+qMpjOyi5iheGraTPwZ262Re 22 | uFe85Av7a7riaHGNiB83enP3JpsU3PKvkCV9IyqZ3JTrgTJrbe/tfdBZtmDhZngG 23 | N+b4vfYADAKvtEo9pFBKstMpDdmLROZltAnUJFr05KNC0X8+Twuzof5l5stLzW9P 24 | lVQ/wQKBgQDT4ixRRx4DlMMzBXNRTkUuZloEhZtLC5xj71KhE7OeOdJ0e6DHJMg3 25 | VDVQk+y3Qc+8Hh9yxMK/zrYLdHSVyvHTk+7AbppLGX7ZtyLm/gVq3l3VjWKmXKbm 26 | ZT+3+2gqVyjr/p69T7/aLexvfzU5LdjwO7SQFNB4qZaG74WpGAlkMg== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /boring/test/root-ca-2.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDSzCCAjOgAwIBAgIUOQyKTQXHMKXZgwc5iktVZkL7hfwwDQYJKoZIhvcNAQEL 3 | BQAwRzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxIzAhBgNVBAoM 4 | GkludGVybmV0IFdpZGdpdHMgUHR5IEx0ZCAyMB4XDTIzMTIwNDEwMzA0N1oXDTIz 5 | MTIxNDEwMzA0N1owRzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx 6 | IzAhBgNVBAoMGkludGVybmV0IFdpZGdpdHMgUHR5IEx0ZCAyMIIBIjANBgkqhkiG 7 | 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwSgPW00nGOTje0FHm1HXT3ANHjW3HGPbxiMS 8 | RvSoBEdv8R9e0PoKjse0xQsa5nKolbUhJBS6dkH+13VPOs6e+vZyXD6OYQfi2e42 9 | Mw0SPFl5g2qUfy/17q3ipqPJnT6YHhGpx/P91F7yjDAF2MvNRGQ/6AYWD4MUr6+5 10 | vPHEYhQ/JlAKcN/uWUM+bg2mwMNpmuGRV1tg0hKl9nT1fX1i6OSszPLIY1dC0eHv 11 | 2gRh1unrXNoYA76xohiwcJYAAPjrx167S8td8gmWnOwYnCnGIw2ceNpEQv4/bEvt 12 | KiLGOu4HUO/xa7emy1QZgJth5TqQf0pwPEJC0mV5LFoXtlOpjwIDAQABoy8wLTAd 13 | BgNVHQ4EFgQU1rQttC2Y2T0HZAjzRkacyFLVBr8wDAYDVR0TBAUwAwEB/zANBgkq 14 | hkiG9w0BAQsFAAOCAQEAsVucQLIzAKHwN/4ZuVOPpfy/B3+i/Stu2tvNhBxWpbh9 15 | RQTa0ylpDfaAOLr+TfxCyT0/NmblK4QWxN6AJ5AZS9fVnstLhInafv7So0n3LCg5 16 | eQkVcQtMdwHucfMw/iz7r229mOHBbK6cnZhu72rcnn7N/RlU+iEucfi6jO+r9iD1 17 | y20glRta+wEqIBg7nGhulOwwdHVkX7ulpnXIqNCgNvU7/Mp7J+CxuWmeZKLvUQAh 18 | D/gHs9kOPK4izN9QBrRwbiyTaD8G7kFlVWD1tPXrOhBdE1L4OJWvUDSfO0DKueIW 19 | aQa2fFsR1iPuFX/jeTuPk5X2+u5eH4pXj13NEqKvOA== 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /boring/test/root-ca-cross.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDajCCAlKgAwIBAgIUSYINSQdbr8yzV186s/zQj+2zol8wDQYJKoZIhvcNAQEL 3 | BQAwRzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxIzAhBgNVBAoM 4 | GkludGVybmV0IFdpZGdpdHMgUHR5IEx0ZCAyMB4XDTI0MDEwMzEwMzUyM1oXDTI2 5 | MDgxMjEwMzUyM1owRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx 6 | ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN 7 | AQEBBQADggEPADCCAQoCggEBAK1R1hZ+di25dZefXsXbmZ7VUmcg2KcwzQ/kti1H 8 | Dun0QVoVf9Ss6MfthmabW7jBpnyN4gJ29AhU+Lgt5AZEEJV6JxgE0lcmhUxUfo6v 9 | 5XNEj/vQXe0gV4niFXiF5WNU75cCL49zbcPc1/rHEwOEl8R+jNKyr/YEzrm9rwjE 10 | h3hdel/A0K+F7GbkK+wqe49SOGqjicmqeSU5eYo5hvHJ7tJ/vFHEZQc8vfXS1iRt 11 | AHyN1USXVqRkzVWfdmhX390aStxf1iNoKd6ldcp0QCrr5p3Bgtyw72H3HNnYLHNT 12 | ehX6vBiK5IEaG+ngXJJQx6dXdNty8K3vlWlQ0qNf/2O9lBcCAwEAAaNQME4wHQYD 13 | VR0OBBYEFGzTpQOrDV8syY2KnIiniHe4N/2aMB8GA1UdIwQYMBaAFNa0LbQtmNk9 14 | B2QI80ZGnMhS1Qa/MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBALw+ 15 | mUsLEoqk6eI4jGv5TPP56RPMRdI+wwmQ8+sQ4DIOzDErkIIQMtoP3aqU6kstHrfY 16 | RZ2tJSWfKb9GcE2SL5VtHQCjSJLsE7f+fTpCFn41q0QMsXF22IOxT2eDvK4Kb496 17 | NVulV6DhsHmbSjo6kla9U3Zqv4WiqLTNj757j+YgmplZQNx8vT5HkPIUi20IxEKV 18 | m6CtPa0M2c2Hl/Y9v006AHmaXnabGvwnLsK92NV0oQb6KnB0mxOrL8od765SF9T0 19 | OXiNK/2ilN2UB1ft16GI/tU+2N+sTmW9/+S5lExfG/S3qXJwc1l4OC9tH9CxOtYt 20 | 6Q+cAmgl6qxF3ltltCM= 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /boring/test/root-ca.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpQIBAAKCAQEArVHWFn52Lbl1l59exduZntVSZyDYpzDND+S2LUcO6fRBWhV/ 3 | 1Kzox+2GZptbuMGmfI3iAnb0CFT4uC3kBkQQlXonGATSVyaFTFR+jq/lc0SP+9Bd 4 | 7SBXieIVeIXlY1TvlwIvj3Ntw9zX+scTA4SXxH6M0rKv9gTOub2vCMSHeF16X8DQ 5 | r4XsZuQr7Cp7j1I4aqOJyap5JTl5ijmG8cnu0n+8UcRlBzy99dLWJG0AfI3VRJdW 6 | pGTNVZ92aFff3RpK3F/WI2gp3qV1ynRAKuvmncGC3LDvYfcc2dgsc1N6Ffq8GIrk 7 | gRob6eBcklDHp1d023Lwre+VaVDSo1//Y72UFwIDAQABAoIBAGZrnd/dC2kp11uq 8 | Sg8SHk3GMdPPjTf/lq51sVJAU4fdV2Eso0XCiCzdKDcqR6F+jiu8jHp4YO0riW8N 9 | b1pkjohGjyOaddIaaVsZ80/OkgDz20Ird9XQ7uoEODvopA12+755BDH5PDwqHVeM 10 | nKfPiwAK6Jz6CxGO9bq9ZNoBiSyO1uofaB4Cpp8t74XVeAuPiI/Bb6WJ8TW5K5dt 11 | x0Jihdo46QgZR+z4PnyWIoACkhSoQmtTb9NUrpKceBcxdCrZ/kEmYpnPq/PuSw6g 12 | 6HthjYP/H9Xulz69UR5Ez6z+1pU1rKFmQ46qK7X3zVHg233MlGekMzxdmShEjzCP 13 | BMGYpQECgYEA5tqTZsUJwx3HDhkaZ/XOtaQqwOnZm9wPwTjGbV1t4+NUJzsl5gjP 14 | ho+I8ZSGZ6MnNSh+ClpYhUHYBq0rTuAAYL2arcMOuOs1GrMmiZJbXm8zq8M7gYr5 15 | V99H/7akSx66WV/agPkLIvh/BWxlWgQcoVAIzZibbLUxr7Ye50pCLfECgYEAwDLn 16 | mFz0mFMvGtaSp8RnTDTFCz9czCeDt0GujCxG1epdvtuxlg/S1QH+mGzA/AHkiu7z 17 | uzCwGKWozNTdRkqVwYoJTB+AYHseSkuGP+a1zr39w+xBW/vESb2oP95GIwprXcG2 18 | b/qdeQVzuLQhYoqWI2u8CBwlHFfpQO4Bp2ea+ocCgYEAurIgLSfCqlpFpiAlG9hN 19 | 8NYwgU1d4E+LKj+JMd8yRO+PGh8amjub4X3pST5NqDjpN3Nk42iHWFWUqGmZsbM0 20 | ewg7tLUgDeqiStKBoxaK8AdMqWc9k5lZ53e6mZISsnHKUQdVBaLjH8gJqdAs8yyK 21 | HudEB0mYwMSUxz6pJXIHrXECgYEAhJkaCpXm8chB8UQj/baUhZDKeI4IWZjRWHbq 22 | Ey7g1+hPMMOk6yCTlf1ARqyRH8u2ftuIL5bRhs+Te21IE5yVYOb4rxn0mZuXNC6S 23 | ujdTKwUMtESkeu9hZnaAQz/4J2ii1hY05WCDj+DhC4bKmY9/MYS8PuQb/kfwVqld 24 | Xr8tvrUCgYEAmslHocXBUFXyRDkEOx/aKo+t9fPBr95PBZzFUt9ejrTP4PXsLa46 25 | 3/PNOCGdrQxh5qHHcvLwR4bPL++Dj+qMUTJXANrArKPDpE2WqH6pqWIC6yaZvzUk 26 | 17QbpXR6bHcdJV045pWpw40UCStTocVynY1lBfOw8VqxBIBlpVBBzew= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /boring/test/root-ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDXTCCAkWgAwIBAgIJAOIvDiVb18eVMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV 3 | BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX 4 | aWRnaXRzIFB0eSBMdGQwHhcNMTYwODE0MTY1NjExWhcNMjYwODEyMTY1NjExWjBF 5 | MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 6 | ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB 7 | CgKCAQEArVHWFn52Lbl1l59exduZntVSZyDYpzDND+S2LUcO6fRBWhV/1Kzox+2G 8 | ZptbuMGmfI3iAnb0CFT4uC3kBkQQlXonGATSVyaFTFR+jq/lc0SP+9Bd7SBXieIV 9 | eIXlY1TvlwIvj3Ntw9zX+scTA4SXxH6M0rKv9gTOub2vCMSHeF16X8DQr4XsZuQr 10 | 7Cp7j1I4aqOJyap5JTl5ijmG8cnu0n+8UcRlBzy99dLWJG0AfI3VRJdWpGTNVZ92 11 | aFff3RpK3F/WI2gp3qV1ynRAKuvmncGC3LDvYfcc2dgsc1N6Ffq8GIrkgRob6eBc 12 | klDHp1d023Lwre+VaVDSo1//Y72UFwIDAQABo1AwTjAdBgNVHQ4EFgQUbNOlA6sN 13 | XyzJjYqciKeId7g3/ZowHwYDVR0jBBgwFoAUbNOlA6sNXyzJjYqciKeId7g3/Zow 14 | DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAVVaR5QWLZIRR4Dw6TSBn 15 | BQiLpBSXN6oAxdDw6n4PtwW6CzydaA+creiK6LfwEsiifUfQe9f+T+TBSpdIYtMv 16 | Z2H2tjlFX8VrjUFvPrvn5c28CuLI0foBgY8XGSkR2YMYzWw2jPEq3Th/KM5Catn3 17 | AFm3bGKWMtGPR4v+90chEN0jzaAmJYRrVUh9vea27bOCn31Nse6XXQPmSI6Gyncy 18 | OAPUsvPClF3IjeL1tmBotWqSGn1cYxLo+Lwjk22A9h6vjcNQRyZF2VLVvtwYrNU3 19 | mwJ6GCLsLHpwW/yjyvn8iEltnJvByM/eeRnfXV6WDObyiZsE/n6DxIRJodQzFqy9 20 | GA== 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /boring/test/rsa-encrypted.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: AES-128-CBC,E2F16153E2BA3D617285A68C896BA6AF 4 | 5 | vO9SnhtGjGe8pG1pN//vsONnvJr+DjU+lFCiSqGMPT7tezDnbehLfS+9kus2HV7r 6 | HmI14JvVG9O7NpF7zMyBRlHYdWcCCWED9Yar0NsWN9419e5pMe/bqIXAzAiJbtT4 7 | OB9U5XF3m+349zjN1dVXPPLGRmMC1pcHAlofeb5nIUFTvUi5xcsbe1itGjgkkvHb 8 | Bt8NioHTBun8kKrlsFQOuB55ylBU/eWG8DQBtvFOmQ7iWp0RnGQfh8k5e5rcZNpQ 9 | fD9ygc7UVISl0xTrIG4IH15g34H+nrBauKtIPOpNPuXQPOMHCZv3XH8wnhrWHHwT 10 | ZFnQBdXbSpQtMsRh0phG2G+VIlyCgSn4+CxjCJ+TgFtsoK/tU0unmRYc59QnTxxb 11 | qkHYsPs3E0NApQAgH1ENEGl1M+FGLYQH7gftjc3ophBTeRA17sRmD7Y4QBInggsq 12 | Gv6tImPVBdekAjz/Ls/EyMwjAvvrL5eAokqrIsAarGo+zmbJKHzknw2KUz2En0+k 13 | YYaxB4oy9u7bzuQlvio6xYHJEb4K197bby4Dldmqv7YCCJBJwhOBAInMD687viKv 14 | vcUwL8YuS6cW5E8MbvEENlY4+lvKKj3M8Bnyb79cYIPQe92EuCwXU9DZXPRMLwwM 15 | oFEJpF5E/PmNJzu+B52ahHtDrh83WSx71fWqjdTqwkPZhAYo3ztsfFkb/UqUcq8u 16 | rBSebeUjZh0XZ9B04eshZQ5vJUcXGtYIe/77beV3Pv89/fw+zTZjpiP9Q3sZALzf 17 | Qt0YGp0/6qBuqR1tcqdu65AS2hun7yFw7uRavqYKvww4axRiz2do+xWmZFuoCAwD 18 | EWktaUujltpvAc1lo7lg4C6nByefJB9Xqk22N/vpqOsWr1NbAntT42Qj/HF9BVWR 19 | osvN3yMnKYWYe6oSTVnNBDM5obWAIHd3I9gcxTOTb1KsEwt2RrDs5EpB5ptS3Fjo 20 | JfBRhNZQ3cXttrIIhsHgDn9BDNg865/xpIgktKj0gEd60Abx0PqkAIm6IZTh4Efg 21 | 7uZwfzxB+saOcddbrW2gNdzVZMC0s2Ye3sqHhtLbAJ3BlXYTxE4CAvTg54Ny+5hF 22 | IjvjlOKgXceSG1cSfk21/wyp9RY3Ft0AEYvvp0kZScWZaoA2aSFDUrchXVhgrEbn 23 | lJ7UptjefwRFIreAlwbKSbIDDNWnyzvIWyHfQ2aYqgnb7W7XqNPSgH9cALCfzirI 24 | dlRHjha0bMUtrjPCC/YfMXzJBVniy0gG6Pd5uC7vz/Awn6/6HRQVNaTQASphPBQ7 25 | bJuz+JTfzI9OUVCMRMdnb6b35U4P9tibFmnPvzTIPe+3WUmf8aRsLS3NN3G1Webd 26 | PMYVZpMycPaAI0Ht87axhsOzlxCWHYWjdHa+WoNNc1J90TxLCmAHquh5BDaWvjMK 27 | 0DySftJZjV7Tf1p2KosmU83LRl39B5NHMbZb1xOEZl9IWwhT/PVKTVZ25xdxWLfb 28 | hF4l8rfvKehIp5r4t8zW1bvI2Hl6vrUvmcUVWt3BfKjxlgwRVD0vvwonMt1INesF 29 | 204vUBeXbDsUUicLwOyUgaFvJ3XU3dOyvL9MhOgM5OgoFRRhG+4AS8a5JCD8iLtq 30 | -----END RSA PRIVATE KEY----- 31 | -------------------------------------------------------------------------------- /boring/test/rsa.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd/wWJcyQoTbji9k0 3 | l8W26mPddxHmfHQp+Vaw+4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL+yRT+SFd2lZS+pC 4 | gNMsD1W/YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb/7OMg0LOL+bSf63kpaSHSX 5 | ndS5z5rexMdbBYUsLA9e+KXBdQOS+UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uD 6 | Zlxvb3qCo5ZwKh9kG4LT6/I5IhlJH7aGhyxXFvUK+DWNmoudF8NAco9/h9iaGNj8 7 | q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQIDAQABAoIBABKucaRpzQorw35S 8 | bEUAVx8dYXUdZOlJcHtiWQ+dC6V8ljxAHj/PLyzTveyI5QO/xkObCyjIL303l2cf 9 | UhPu2MFaJdjVzqACXuOrLot/eSFvxjvqVidTtAZExqFRJ9mylUVAoLvhowVWmC1O 10 | n95fZCXxTUtxNEG1Xcc7m0rtzJKs45J+N/V9DP1edYH6USyPSWGp6wuA+KgHRnKK 11 | Vf9GRx80JQY7nVNkL17eHoTWEwga+lwi0FEoW9Y7lDtWXYmKBWhUE+U8PGxlJf8f 12 | 40493HDw1WRQ/aSLoS4QTp3rn7gYgeHEvfJdkkf0UMhlknlo53M09EFPdadQ4TlU 13 | bjqKc50CgYEA4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH/5IB3jw3bcxGn6QLvnE 14 | tfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw/Py5PJdTJNPY8cQn7ouZ2KKDcmnPG 15 | BY5t7yLc1QlQ5xHdwW1VhvKn+nXqhJTBgIPgtldC+KDV5z+y2XDwGUcCgYEAuQPE 16 | fgmVtjL0Uyyx88GZFF1fOunH3+7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYs 17 | p1ZSe7zFYHj7C6ul7TjeLQeZD/YwD66t62wDmpe/HlB+TnBA+njbglfIsRLtXlnD 18 | zQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdcCgYAHAp9XcCSrn8wVkMVkKdb7 19 | DOX4IKjzdahm+ctDAJN4O/y7OW5FKebvUjdAIt2GuoTZ71iTG+7F0F+lP88jtjP4 20 | U4qe7VHoewl4MKOfXZKTe+YCS1XbNvfgwJ3Ltyl1OH9hWvu2yza7q+d5PCsDzqtm 21 | 27kxuvULVeya+TEdAB1ijQKBgQCH/3r6YrVH/uCWGy6bzV1nGNOdjKc9tmkfOJmN 22 | 54dxdixdpozCQ6U4OxZrsj3FcOhHBsqAHvX2uuYjagqvo3cOj1TRqNocX40omfCC 23 | Mx3bD1yPPf/6TI2XECva/ggqEY2mYzmIiA5LVVmc5nrybr+lssFKneeyxN2Wq93S 24 | 0iJMdQKBgCGHewxzoa1r8ZMD0LETNrToK423K377UCYqXfg5XMclbrjPbEC3YI1Z 25 | NqMtuhdBJqUnBi6tjKMF+34Xf0CUN8ncuXGO2CAYvO8PdyCixHX52ybaDjy1FtCE 26 | 6yUXjoKNXKvUm7MWGsAYH6f4IegOetN5NvmUMFStCSkh7ixZLkN1 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /boring/test/rsa.pem.pub: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAofgWCuLjybRlzo0tZWJj 3 | NiuSfb4p4fAkd/wWJcyQoTbji9k0l8W26mPddxHmfHQp+Vaw+4qPCJrcS2mJPMEz 4 | P1Pt0Bm4d4QlL+yRT+SFd2lZS+pCgNMsD1W/YpRPEwOWvG6b32690r2jZ47soMZo 5 | 9wGzjb/7OMg0LOL+bSf63kpaSHSXndS5z5rexMdbBYUsLA9e+KXBdQOS+UTo7WTB 6 | EMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6/I5IhlJH7aGhyxX 7 | FvUK+DWNmoudF8NAco9/h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXp 8 | oQIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /cliff.toml: -------------------------------------------------------------------------------- 1 | [changelog] 2 | header = "" 3 | body = """ 4 | {% if version %}\ 5 | {{ version | trim_start_matches(pat="v") }}\ 6 | {% else %}\ 7 | Unreleased\ 8 | {% endif %}\ 9 | {% for commit in commits %} 10 | - {{ commit.author.timestamp | date }} {{ commit.message | split(pat="\n") | first }}\ 11 | {% endfor %} 12 | 13 | 14 | """ 15 | footer = "" 16 | 17 | [git] 18 | conventional_commits = false 19 | commit_parsers = [ 20 | { message = "^Merge", skip = true }, 21 | { message = "^Pull request", skip = true }, 22 | { message = "^Release", skip = true }, 23 | ] 24 | filter_commits = false 25 | tag_pattern = "[v0-9]*" 26 | sort_commits = "newest" 27 | -------------------------------------------------------------------------------- /hyper-boring/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | .vscode/ 4 | .idea/ 5 | *.iml 6 | -------------------------------------------------------------------------------- /hyper-boring/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hyper-boring" 3 | version = { workspace = true } 4 | authors = ["Steven Fackler ", "Ivan Nikulin "] 5 | edition = { workspace = true } 6 | description = "Hyper TLS support via BoringSSL" 7 | license = "MIT OR Apache-2.0" 8 | repository = { workspace = true } 9 | documentation = "https://docs.rs/hyper-boring" 10 | readme = "README.md" 11 | exclude = ["test/*"] 12 | rust-version = "1.80" 13 | 14 | [package.metadata.docs.rs] 15 | features = ["pq-experimental"] 16 | rustdoc-args = ["--cfg", "docsrs"] 17 | 18 | [features] 19 | default = ["runtime"] 20 | 21 | runtime = ["hyper_old/runtime"] 22 | 23 | # Use a FIPS-validated version of boringssl. 24 | fips = ["tokio-boring/fips"] 25 | 26 | # Use a FIPS build of BoringSSL, but don't set "fips-compat". 27 | # 28 | # As of boringSSL commit a430310d6563c0734ddafca7731570dfb683dc19, we no longer 29 | # need to make exceptions for the types of BufLen, ProtosLen, and ValueLen, 30 | # which means the "fips-compat" feature is no longer needed. 31 | # 32 | # TODO(cjpatton) Delete this feature and modify "fips" so that it doesn't imply 33 | # "fips-compat". 34 | fips-precompiled = ["tokio-boring/fips-precompiled"] 35 | 36 | # Link with precompiled FIPS-validated `bcm.o` module. 37 | fips-link-precompiled = ["tokio-boring/fips-link-precompiled"] 38 | 39 | # Enables experimental post-quantum crypto (https://blog.cloudflare.com/post-quantum-for-all/) 40 | pq-experimental = ["tokio-boring/pq-experimental"] 41 | 42 | # Enable Hyper 1 support 43 | hyper1 = ["dep:http", "dep:hyper", "dep:hyper-util", "dep:tower-service"] 44 | 45 | [dependencies] 46 | antidote = { workspace = true } 47 | http = { workspace = true, optional = true } 48 | http_old = { workspace = true } 49 | hyper = { workspace = true, optional = true } 50 | hyper-util = { workspace = true, optional = true, features = ["client", "client-legacy"] } 51 | hyper_old = { workspace = true, features = ["client"] } 52 | linked_hash_set = { workspace = true } 53 | boring = { workspace = true } 54 | tokio = { workspace = true } 55 | tokio-boring = { workspace = true } 56 | tower-layer = { workspace = true } 57 | tower-service = { workspace = true, optional = true } 58 | 59 | [dev-dependencies] 60 | bytes = { workspace = true } 61 | http-body-util = { workspace = true } 62 | hyper-util = { workspace = true, features = ["http1", "http2", "service", "tokio"] } 63 | hyper = { workspace = true, features = ["server"] } 64 | hyper_old = { workspace = true, features = [ "full" ] } 65 | tokio = { workspace = true, features = [ "full" ] } 66 | tower = { workspace = true, features = ["util"] } 67 | futures = { workspace = true } 68 | 69 | [package.metadata.release] 70 | pre-release-hook = [ 71 | "git-cliff", 72 | "--workdir", "..", 73 | "-o", "../RELEASE_NOTES", 74 | "--tag", "{{version}}" 75 | ] 76 | -------------------------------------------------------------------------------- /hyper-boring/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2016 Steven Fackler 4 | Copyright (c) 2020 Ivan Nikulin 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | -------------------------------------------------------------------------------- /hyper-boring/README.md: -------------------------------------------------------------------------------- 1 | # hyper-boring 2 | 3 | [Documentation](https://docs.rs/hyper-boring) 4 | 5 | Hyper SSL support via BoringSSL. 6 | 7 | ## License 8 | 9 | Licensed under either of 10 | 11 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 12 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 13 | 14 | at your option. 15 | 16 | ### Contribution 17 | 18 | Unless you explicitly state otherwise, any contribution intentionally 19 | submitted for inclusion in the work by you, as defined in the Apache-2.0 20 | license, shall be dual licensed as above, without any additional terms or 21 | conditions. 22 | 23 | ## Accolades 24 | 25 | The project is based on a fork of [hyper-openssl](https://github.com/sfackler/hyper-openssl). -------------------------------------------------------------------------------- /hyper-boring/src/cache.rs: -------------------------------------------------------------------------------- 1 | use boring::ssl::SslVersion; 2 | use boring::ssl::{SslSession, SslSessionRef}; 3 | use linked_hash_set::LinkedHashSet; 4 | use std::borrow::Borrow; 5 | use std::collections::hash_map::{Entry, HashMap}; 6 | use std::hash::{Hash, Hasher}; 7 | 8 | #[derive(Hash, PartialEq, Eq, Clone)] 9 | pub struct SessionKey { 10 | pub host: String, 11 | pub port: u16, 12 | } 13 | 14 | #[derive(Clone)] 15 | struct HashSession(SslSession); 16 | 17 | impl PartialEq for HashSession { 18 | fn eq(&self, other: &HashSession) -> bool { 19 | self.0.id() == other.0.id() 20 | } 21 | } 22 | 23 | impl Eq for HashSession {} 24 | 25 | impl Hash for HashSession { 26 | fn hash(&self, state: &mut H) 27 | where 28 | H: Hasher, 29 | { 30 | self.0.id().hash(state); 31 | } 32 | } 33 | 34 | impl Borrow<[u8]> for HashSession { 35 | fn borrow(&self) -> &[u8] { 36 | self.0.id() 37 | } 38 | } 39 | 40 | pub struct SessionCache { 41 | sessions: HashMap>, 42 | reverse: HashMap, 43 | /// Maximum capacity of LinkedHashSet per SessionKey 44 | per_key_session_capacity: usize, 45 | } 46 | 47 | impl SessionCache { 48 | pub fn with_capacity(per_key_session_capacity: usize) -> SessionCache { 49 | SessionCache { 50 | sessions: HashMap::new(), 51 | reverse: HashMap::new(), 52 | per_key_session_capacity, 53 | } 54 | } 55 | 56 | pub fn insert(&mut self, key: SessionKey, session: SslSession) { 57 | let session = HashSession(session); 58 | 59 | let sessions = self.sessions.entry(key.clone()).or_default(); 60 | 61 | // if sessions exceed capacity, discard oldest 62 | if sessions.len() >= self.per_key_session_capacity { 63 | if let Some(hash) = sessions.pop_front() { 64 | self.reverse.remove(&hash); 65 | } 66 | } 67 | 68 | sessions.insert(session.clone()); 69 | self.reverse.insert(session, key); 70 | } 71 | 72 | pub fn get(&mut self, key: &SessionKey) -> Option { 73 | let session = { 74 | let sessions = self.sessions.get_mut(key)?; 75 | sessions.front().cloned()?.0 76 | }; 77 | 78 | // https://tools.ietf.org/html/rfc8446#appendix-C.4 79 | // OpenSSL will remove the session from its cache after the handshake completes anyway, but this ensures 80 | // that concurrent handshakes don't end up with the same session. 81 | if session.protocol_version() == SslVersion::TLS1_3 { 82 | self.remove(&session); 83 | } 84 | 85 | Some(session) 86 | } 87 | 88 | pub fn remove(&mut self, session: &SslSessionRef) { 89 | let key = match self.reverse.remove(session.id()) { 90 | Some(key) => key, 91 | None => return, 92 | }; 93 | 94 | if let Entry::Occupied(mut sessions) = self.sessions.entry(key) { 95 | sessions.get_mut().remove(session.id()); 96 | if sessions.get().is_empty() { 97 | sessions.remove(); 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /hyper-boring/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Hyper SSL support via OpenSSL. 2 | #![warn(missing_docs)] 3 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 4 | 5 | use crate::cache::SessionKey; 6 | use boring::error::ErrorStack; 7 | use boring::ex_data::Index; 8 | use boring::ssl::Ssl; 9 | use std::fmt; 10 | use std::sync::LazyLock; 11 | use tokio_boring::SslStream; 12 | 13 | mod cache; 14 | mod v0; 15 | /// Hyper 1 support. 16 | #[cfg(feature = "hyper1")] 17 | pub mod v1; 18 | 19 | pub use self::v0::*; 20 | 21 | fn key_index() -> Result, ErrorStack> { 22 | static IDX: LazyLock> = LazyLock::new(|| Ssl::new_ex_index().unwrap()); 23 | Ok(*IDX) 24 | } 25 | 26 | /// Settings for [`HttpsLayer`] 27 | pub struct HttpsLayerSettings { 28 | session_cache_capacity: usize, 29 | } 30 | 31 | impl HttpsLayerSettings { 32 | /// Constructs an [`HttpsLayerSettingsBuilder`] for configuring settings 33 | pub fn builder() -> HttpsLayerSettingsBuilder { 34 | HttpsLayerSettingsBuilder(HttpsLayerSettings::default()) 35 | } 36 | } 37 | 38 | impl Default for HttpsLayerSettings { 39 | fn default() -> Self { 40 | Self { 41 | session_cache_capacity: 8, 42 | } 43 | } 44 | } 45 | 46 | /// Builder for [`HttpsLayerSettings`] 47 | pub struct HttpsLayerSettingsBuilder(HttpsLayerSettings); 48 | 49 | impl HttpsLayerSettingsBuilder { 50 | /// Sets maximum number of sessions to cache. Session capacity is per session key (domain). 51 | /// Defaults to 8. 52 | pub fn set_session_cache_capacity(&mut self, capacity: usize) { 53 | self.0.session_cache_capacity = capacity; 54 | } 55 | 56 | /// Consumes the builder, returning a new [`HttpsLayerSettings`] 57 | pub fn build(self) -> HttpsLayerSettings { 58 | self.0 59 | } 60 | } 61 | 62 | /// A stream which may be wrapped with TLS. 63 | pub enum MaybeHttpsStream { 64 | /// A raw HTTP stream. 65 | Http(T), 66 | /// An SSL-wrapped HTTP stream. 67 | Https(SslStream), 68 | } 69 | 70 | impl fmt::Debug for MaybeHttpsStream { 71 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 72 | match *self { 73 | MaybeHttpsStream::Http(..) => f.pad("Http(..)"), 74 | MaybeHttpsStream::Https(..) => f.pad("Https(..)"), 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /hyper-boring/tests/test/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDGzCCAgMCCQCHcfe97pgvpTANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB 3 | VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 4 | cyBQdHkgTHRkMB4XDTE2MDgxNDE3MDAwM1oXDTI2MDgxMjE3MDAwM1owWjELMAkG 5 | A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0 6 | IFdpZGdpdHMgUHR5IEx0ZDETMBEGA1UEAwwKZm9vYmFyLmNvbTCCASIwDQYJKoZI 7 | hvcNAQEBBQADggEPADCCAQoCggEBAKj0JYxEsxejUIX+I5GH0Hg2G0kX/y1H0+Ub 8 | 3mw2/Ja5BD/yN96/7zMSumXF8uS3SkmpyiJkbyD01TSRTqjlP7/VCBlyUIChlpLQ 9 | mrGaijZiT/VCyPXqmcwFzXS5IOTpX1olJfW8rA41U1LCIcDUyFf6LtZ/v8rSeKr6 10 | TuE6SGV4WRaBm1SrjWBeHVV866CRrtSS1ieT2asFsAyOZqWhk2fakwwBDFWDhOGI 11 | ubfO+5aq9cBJbNRlzsgB3UZs3gC0O6GzbnZ6oT0TiJMeTsXXjABLUlaq/rrqFF4Y 12 | euZkkbHTFBMz288PUc3m3ZTcpN+E7+ZOUBRZXKD20K07NugqCzUCAwEAATANBgkq 13 | hkiG9w0BAQsFAAOCAQEASvYHuIl5C0NHBELPpVHNuLbQsDQNKVj3a54+9q1JkiMM 14 | 6taEJYfw7K1Xjm4RoiFSHpQBh+PWZS3hToToL2Zx8JfMR5MuAirdPAy1Sia/J/qE 15 | wQdJccqmvuLkLTSlsGbEJ/LUUgOAgrgHOZM5lUgIhCneA0/dWJ3PsN0zvn69/faY 16 | oo1iiolWiIHWWBUSdr3jM2AJaVAsTmLh00cKaDNk37JB940xConBGSl98JPrNrf9 17 | dUAiT0iIBngDBdHnn/yTj+InVEFyZSKrNtiDSObFHxPcxGteHNrCPJdP1e+GqkHp 18 | HJMRZVCQpSMzvHlofHSNgzWV1MX5h1CP4SGZdBDTfA== 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /hyper-boring/tests/test/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCo9CWMRLMXo1CF 3 | /iORh9B4NhtJF/8tR9PlG95sNvyWuQQ/8jfev+8zErplxfLkt0pJqcoiZG8g9NU0 4 | kU6o5T+/1QgZclCAoZaS0Jqxmoo2Yk/1Qsj16pnMBc10uSDk6V9aJSX1vKwONVNS 5 | wiHA1MhX+i7Wf7/K0niq+k7hOkhleFkWgZtUq41gXh1VfOugka7UktYnk9mrBbAM 6 | jmaloZNn2pMMAQxVg4ThiLm3zvuWqvXASWzUZc7IAd1GbN4AtDuhs252eqE9E4iT 7 | Hk7F14wAS1JWqv666hReGHrmZJGx0xQTM9vPD1HN5t2U3KTfhO/mTlAUWVyg9tCt 8 | OzboKgs1AgMBAAECggEBAKLj6IOJBKXolczpzb8UkyAjAkGBektcseV07gelJ/fk 9 | 3z0LuWPv5p12E/HlXB24vU2x/ikUbbP3eMsawRzDEahQqmNmPEkYAYUAy/Qpi9GN 10 | DYvn3LqDec4jVgeQKS+p9H2DzUpTogp8zR2//yzbuWBg2+F//xh7vU0S0RQCziPM 11 | x7RSBgbhxSfChfEJbS2sDnzfh0jRQmoY95iFv7puet1FJtzdZ4fgCd1RqmC2lFM5 12 | H0eZtN/Cz19lieVs0b996DErdEBqClVZO00eYbRozCDaBzRU3ybB/dMrGJxhkkXm 13 | wb3kWMtziH9qOYsostuHIFu8eKFLloKxFnq2R4DGxOECgYEA2KUIZISOeGJSBcLJ 14 | JAUK2gvgXPNo4HHWIwOA9xeN3ZJlsnPlffXQNnm6t1st1V2gfMm9I2n0m/F0y2B/ 15 | n/XGSa8bghfPA9l0c2h58lkL3JQJR/paa8ycTz+YZPrznEyN7Qa0RrJXUvZv9lQL 16 | Hc3+FHcSHgMqDV2f2bHAEu9YGi0CgYEAx6VEIPNvrHFgjo/jk1RTuk+m0xEWQsZL 17 | Cs+izQMr2TaeJn8LG+93AvFuYn0J0nT3WuStLPrUg8i4IhSS6lf1tId5ivIZPm4r 18 | YwMyblBJXhnHbk7Uqodjfw/3s6V2HAu++B7hTdyVr9DFuST9uv4m8bkPV8rfX1jE 19 | I2rAPVWvgikCgYB+wNAQP547wQrMZBLbCDg5KwmyWJfb+b6X7czexOEz6humNTjo 20 | YZHYzY/5B1fhpk3ntQD8X1nGg5caBvOk21+QbOtjShrM3cXMYCw5JvBRtitX+Zo9 21 | yBEMLOE0877ki8XeEDYZxu5gk98d+D4oygUGZEQtWxyXhVepPt5qNa8OYQKBgQDH 22 | RVgZI6KFlqzv3wMh3PutbS9wYQ+9GrtwUQuIYe/0YSW9+vSVr5E0qNKrD28sV39F 23 | hBauXLady0yvB6YUrjMbPFW+sCMuQzyfGWPO4+g3OrfqjFiM1ZIkE0YEU9Tt7XNx 24 | qTDtTI1D7bhNMnTnniI1B6ge0und+3XafAThs5L48QKBgQCTTpfqMt8kU3tcI9sf 25 | 0MK03y7kA76d5uw0pZbWFy7KI4qnzWutCzb+FMPWWsoFtLJLPZy//u/ZCUVFVa4d 26 | 0Y/ASNQIESVPXFLAltlLo4MSmsg1vCBsbviEEaPeEjvMrgki93pYtd/aOSgkYC1T 27 | mEq154s5rmqh+h+XRIf7Au0SLw== 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /hyper-boring/tests/test/root-ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDXTCCAkWgAwIBAgIJAOIvDiVb18eVMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV 3 | BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX 4 | aWRnaXRzIFB0eSBMdGQwHhcNMTYwODE0MTY1NjExWhcNMjYwODEyMTY1NjExWjBF 5 | MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 6 | ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB 7 | CgKCAQEArVHWFn52Lbl1l59exduZntVSZyDYpzDND+S2LUcO6fRBWhV/1Kzox+2G 8 | ZptbuMGmfI3iAnb0CFT4uC3kBkQQlXonGATSVyaFTFR+jq/lc0SP+9Bd7SBXieIV 9 | eIXlY1TvlwIvj3Ntw9zX+scTA4SXxH6M0rKv9gTOub2vCMSHeF16X8DQr4XsZuQr 10 | 7Cp7j1I4aqOJyap5JTl5ijmG8cnu0n+8UcRlBzy99dLWJG0AfI3VRJdWpGTNVZ92 11 | aFff3RpK3F/WI2gp3qV1ynRAKuvmncGC3LDvYfcc2dgsc1N6Ffq8GIrkgRob6eBc 12 | klDHp1d023Lwre+VaVDSo1//Y72UFwIDAQABo1AwTjAdBgNVHQ4EFgQUbNOlA6sN 13 | XyzJjYqciKeId7g3/ZowHwYDVR0jBBgwFoAUbNOlA6sNXyzJjYqciKeId7g3/Zow 14 | DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAVVaR5QWLZIRR4Dw6TSBn 15 | BQiLpBSXN6oAxdDw6n4PtwW6CzydaA+creiK6LfwEsiifUfQe9f+T+TBSpdIYtMv 16 | Z2H2tjlFX8VrjUFvPrvn5c28CuLI0foBgY8XGSkR2YMYzWw2jPEq3Th/KM5Catn3 17 | AFm3bGKWMtGPR4v+90chEN0jzaAmJYRrVUh9vea27bOCn31Nse6XXQPmSI6Gyncy 18 | OAPUsvPClF3IjeL1tmBotWqSGn1cYxLo+Lwjk22A9h6vjcNQRyZF2VLVvtwYrNU3 19 | mwJ6GCLsLHpwW/yjyvn8iEltnJvByM/eeRnfXV6WDObyiZsE/n6DxIRJodQzFqy9 20 | GA== 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /hyper-boring/tests/v0.rs: -------------------------------------------------------------------------------- 1 | use boring::ssl::{SslAcceptor, SslConnector, SslFiletype, SslMethod}; 2 | use futures::StreamExt; 3 | use hyper_boring::HttpsConnector; 4 | use hyper_old::client::HttpConnector; 5 | use hyper_old::server::conn::Http; 6 | use hyper_old::{service, Response}; 7 | use hyper_old::{Body, Client}; 8 | use std::convert::Infallible; 9 | use std::{io, iter}; 10 | use tokio::net::TcpListener; 11 | 12 | #[tokio::test] 13 | #[cfg(feature = "runtime")] 14 | async fn google() { 15 | let ssl = HttpsConnector::new().unwrap(); 16 | let client = Client::builder() 17 | .pool_max_idle_per_host(0) 18 | .build::<_, Body>(ssl); 19 | 20 | for _ in 0..3 { 21 | let resp = client 22 | .get("https://www.google.com".parse().unwrap()) 23 | .await 24 | .expect("connection should succeed"); 25 | let mut body = resp.into_body(); 26 | while body.next().await.transpose().unwrap().is_some() {} 27 | } 28 | } 29 | 30 | #[tokio::test] 31 | async fn localhost() { 32 | let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); 33 | let addr = listener.local_addr().unwrap(); 34 | let port = addr.port(); 35 | 36 | let server = async move { 37 | let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); 38 | acceptor.set_session_id_context(b"test").unwrap(); 39 | acceptor 40 | .set_private_key_file("tests/test/key.pem", SslFiletype::PEM) 41 | .unwrap(); 42 | acceptor 43 | .set_certificate_chain_file("tests/test/cert.pem") 44 | .unwrap(); 45 | let acceptor = acceptor.build(); 46 | 47 | for _ in 0..3 { 48 | let stream = listener.accept().await.unwrap().0; 49 | let stream = tokio_boring::accept(&acceptor, stream).await.unwrap(); 50 | 51 | let service = 52 | service::service_fn(|_| async { Ok::<_, io::Error>(Response::new(Body::empty())) }); 53 | 54 | Http::new() 55 | .http1_keep_alive(false) 56 | .serve_connection(stream, service) 57 | .await 58 | .unwrap(); 59 | } 60 | }; 61 | tokio::spawn(server); 62 | 63 | let resolver = 64 | tower::service_fn(move |_name| async move { Ok::<_, Infallible>(iter::once(addr)) }); 65 | 66 | let mut connector = HttpConnector::new_with_resolver(resolver); 67 | 68 | connector.enforce_http(false); 69 | 70 | let mut ssl = SslConnector::builder(SslMethod::tls()).unwrap(); 71 | 72 | ssl.set_ca_file("tests/test/root-ca.pem").unwrap(); 73 | 74 | use std::fs::File; 75 | use std::io::Write; 76 | 77 | let file = File::create("../target/keyfile.log").unwrap(); 78 | ssl.set_keylog_callback(move |_, line| { 79 | let _ = writeln!(&file, "{}", line); 80 | }); 81 | 82 | let ssl = HttpsConnector::with_connector(connector, ssl).unwrap(); 83 | let client = Client::builder().build::<_, Body>(ssl); 84 | 85 | for _ in 0..3 { 86 | let resp = client 87 | .get(format!("https://foobar.com:{}", port).parse().unwrap()) 88 | .await 89 | .unwrap(); 90 | assert!(resp.status().is_success(), "{}", resp.status()); 91 | let mut body = resp.into_body(); 92 | while body.next().await.transpose().unwrap().is_some() {} 93 | } 94 | } 95 | 96 | #[tokio::test] 97 | async fn alpn_h2() { 98 | use boring::ssl::{self, AlpnError}; 99 | 100 | let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); 101 | let addr = listener.local_addr().unwrap(); 102 | let port = addr.port(); 103 | 104 | let server = async move { 105 | let mut acceptor = SslAcceptor::mozilla_modern(SslMethod::tls()).unwrap(); 106 | acceptor 107 | .set_certificate_chain_file("tests/test/cert.pem") 108 | .unwrap(); 109 | acceptor 110 | .set_private_key_file("tests/test/key.pem", SslFiletype::PEM) 111 | .unwrap(); 112 | acceptor.set_alpn_select_callback(|_, client| { 113 | ssl::select_next_proto(b"\x02h2", client).ok_or(AlpnError::NOACK) 114 | }); 115 | let acceptor = acceptor.build(); 116 | 117 | let stream = listener.accept().await.unwrap().0; 118 | let stream = tokio_boring::accept(&acceptor, stream).await.unwrap(); 119 | assert_eq!(stream.ssl().selected_alpn_protocol().unwrap(), b"h2"); 120 | 121 | let service = 122 | service::service_fn(|_| async { Ok::<_, io::Error>(Response::new(Body::empty())) }); 123 | 124 | Http::new() 125 | .http2_only(true) 126 | .serve_connection(stream, service) 127 | .await 128 | .unwrap(); 129 | }; 130 | tokio::spawn(server); 131 | 132 | let resolver = 133 | tower::service_fn(move |_name| async move { Ok::<_, Infallible>(iter::once(addr)) }); 134 | 135 | let mut connector = HttpConnector::new_with_resolver(resolver); 136 | 137 | connector.enforce_http(false); 138 | 139 | let mut ssl = SslConnector::builder(SslMethod::tls()).unwrap(); 140 | 141 | ssl.set_ca_file("tests/test/root-ca.pem").unwrap(); 142 | 143 | let mut ssl = HttpsConnector::with_connector(connector, ssl).unwrap(); 144 | 145 | ssl.set_ssl_callback(|ssl, _| ssl.set_alpn_protos(b"\x02h2\x08http/1.1")); 146 | 147 | let client = Client::builder().build::<_, Body>(ssl); 148 | 149 | let resp = client 150 | .get(format!("https://foobar.com:{}", port).parse().unwrap()) 151 | .await 152 | .unwrap(); 153 | assert!(resp.status().is_success(), "{}", resp.status()); 154 | let mut body = resp.into_body(); 155 | while body.next().await.transpose().unwrap().is_some() {} 156 | } 157 | -------------------------------------------------------------------------------- /hyper-boring/tests/v1.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "hyper1")] 2 | 3 | use boring::ssl::{SslAcceptor, SslConnector, SslFiletype, SslMethod}; 4 | use bytes::Bytes; 5 | use futures::StreamExt; 6 | use http_body_util::{BodyStream, Empty}; 7 | use hyper::{service, Response}; 8 | use hyper_boring::v1::HttpsConnector; 9 | use hyper_util::client::legacy::connect::HttpConnector; 10 | use hyper_util::client::legacy::Client; 11 | use hyper_util::rt::{TokioExecutor, TokioIo}; 12 | use std::convert::Infallible; 13 | use std::{io, iter}; 14 | use tokio::net::TcpListener; 15 | 16 | #[tokio::test] 17 | async fn google() { 18 | let ssl = HttpsConnector::new().unwrap(); 19 | let client = Client::builder(TokioExecutor::new()) 20 | .pool_max_idle_per_host(0) 21 | .build::<_, Empty>(ssl); 22 | 23 | for _ in 0..3 { 24 | let resp = client 25 | .get("https://www.google.com".parse().unwrap()) 26 | .await 27 | .expect("connection should succeed"); 28 | let mut body = BodyStream::new(resp.into_body()); 29 | while body.next().await.transpose().unwrap().is_some() {} 30 | } 31 | } 32 | 33 | #[tokio::test] 34 | async fn localhost() { 35 | let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); 36 | let addr = listener.local_addr().unwrap(); 37 | let port = addr.port(); 38 | 39 | let server = async move { 40 | let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); 41 | acceptor.set_session_id_context(b"test").unwrap(); 42 | acceptor 43 | .set_private_key_file("tests/test/key.pem", SslFiletype::PEM) 44 | .unwrap(); 45 | acceptor 46 | .set_certificate_chain_file("tests/test/cert.pem") 47 | .unwrap(); 48 | let acceptor = acceptor.build(); 49 | 50 | for _ in 0..3 { 51 | let stream = listener.accept().await.unwrap().0; 52 | let stream = tokio_boring::accept(&acceptor, stream).await.unwrap(); 53 | 54 | let service = service::service_fn(|_| async { 55 | Ok::<_, io::Error>(Response::new(>::new())) 56 | }); 57 | 58 | hyper::server::conn::http1::Builder::new() 59 | .keep_alive(false) 60 | .serve_connection(TokioIo::new(stream), service) 61 | .await 62 | .unwrap(); 63 | } 64 | }; 65 | tokio::spawn(server); 66 | 67 | let resolver = 68 | tower::service_fn(move |_name| async move { Ok::<_, Infallible>(iter::once(addr)) }); 69 | 70 | let mut connector = HttpConnector::new_with_resolver(resolver); 71 | 72 | connector.enforce_http(false); 73 | 74 | let mut ssl = SslConnector::builder(SslMethod::tls()).unwrap(); 75 | 76 | ssl.set_ca_file("tests/test/root-ca.pem").unwrap(); 77 | 78 | use std::fs::File; 79 | use std::io::Write; 80 | 81 | let file = File::create("../target/keyfile.log").unwrap(); 82 | ssl.set_keylog_callback(move |_, line| { 83 | let _ = writeln!(&file, "{}", line); 84 | }); 85 | 86 | let ssl = HttpsConnector::with_connector(connector, ssl).unwrap(); 87 | let client = Client::builder(TokioExecutor::new()).build::<_, Empty>(ssl); 88 | 89 | for _ in 0..3 { 90 | let resp = client 91 | .get(format!("https://foobar.com:{}", port).parse().unwrap()) 92 | .await 93 | .unwrap(); 94 | assert!(resp.status().is_success(), "{}", resp.status()); 95 | let mut body = BodyStream::new(resp.into_body()); 96 | while body.next().await.transpose().unwrap().is_some() {} 97 | } 98 | } 99 | 100 | #[tokio::test] 101 | async fn alpn_h2() { 102 | use boring::ssl::{self, AlpnError}; 103 | 104 | let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); 105 | let addr = listener.local_addr().unwrap(); 106 | let port = addr.port(); 107 | 108 | let server = async move { 109 | let mut acceptor = SslAcceptor::mozilla_modern(SslMethod::tls()).unwrap(); 110 | acceptor 111 | .set_certificate_chain_file("tests/test/cert.pem") 112 | .unwrap(); 113 | acceptor 114 | .set_private_key_file("tests/test/key.pem", SslFiletype::PEM) 115 | .unwrap(); 116 | acceptor.set_alpn_select_callback(|_, client| { 117 | ssl::select_next_proto(b"\x02h2", client).ok_or(AlpnError::NOACK) 118 | }); 119 | let acceptor = acceptor.build(); 120 | 121 | let stream = listener.accept().await.unwrap().0; 122 | let stream = tokio_boring::accept(&acceptor, stream).await.unwrap(); 123 | assert_eq!(stream.ssl().selected_alpn_protocol().unwrap(), b"h2"); 124 | 125 | let service = service::service_fn(|_| async { 126 | Ok::<_, io::Error>(Response::new(>::new())) 127 | }); 128 | 129 | hyper::server::conn::http2::Builder::new(TokioExecutor::new()) 130 | .serve_connection(TokioIo::new(stream), service) 131 | .await 132 | .unwrap(); 133 | }; 134 | tokio::spawn(server); 135 | 136 | let resolver = 137 | tower::service_fn(move |_name| async move { Ok::<_, Infallible>(iter::once(addr)) }); 138 | 139 | let mut connector = HttpConnector::new_with_resolver(resolver); 140 | 141 | connector.enforce_http(false); 142 | 143 | let mut ssl = SslConnector::builder(SslMethod::tls()).unwrap(); 144 | 145 | ssl.set_ca_file("tests/test/root-ca.pem").unwrap(); 146 | 147 | let mut ssl = HttpsConnector::with_connector(connector, ssl).unwrap(); 148 | 149 | ssl.set_ssl_callback(|ssl, _| ssl.set_alpn_protos(b"\x02h2\x08http/1.1")); 150 | 151 | let client = Client::builder(TokioExecutor::new()).build::<_, Empty>(ssl); 152 | 153 | let resp = client 154 | .get(format!("https://foobar.com:{}", port).parse().unwrap()) 155 | .await 156 | .unwrap(); 157 | assert!(resp.status().is_success(), "{}", resp.status()); 158 | let mut body = BodyStream::new(resp.into_body()); 159 | while body.next().await.transpose().unwrap().is_some() {} 160 | } 161 | -------------------------------------------------------------------------------- /scripts/publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -euo pipefail 4 | 5 | SCRIPT_NAME=$(basename "$0") 6 | REQUIRE_BRANCH='master' 7 | CURRENT_BRANCH="$(git symbolic-ref --short HEAD)" 8 | 9 | if [[ "$CURRENT_BRANCH" != "$REQUIRE_BRANCH" ]]; then 10 | echo "Please \`git checkout $REQUIRE_BRANCH\` to run $SCRIPT_NAME (cannot run from current branch $CURRENT_BRANCH)." 11 | exit 1 12 | fi 13 | 14 | if [[ "$(git diff --stat)" != '' ]]; then 15 | echo 'Please commit or discard your changes before creating a new release.' 16 | exit 1 17 | fi 18 | 19 | echo "=== Publishing boring-sys... ===" 20 | (cd boring-sys && cargo publish) 21 | sleep 20 22 | 23 | echo "=== Publishing boring... ===" 24 | (cd boring && cargo publish) 25 | sleep 20 26 | 27 | echo "=== Publishing tokio-boring... ===" 28 | (cd tokio-boring && cargo publish) 29 | sleep 20 30 | 31 | echo "=== Publishing hyper-boring... ===" 32 | (cd hyper-boring && cargo publish) 33 | sleep 20 -------------------------------------------------------------------------------- /tokio-boring/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [Unreleased] 4 | 5 | ## [v2.1.4] - 2021-12-16 6 | 7 | ### Changed 8 | 9 | * Removed unnecessary `S: Debug` constraint for `HandshakeError` -------------------------------------------------------------------------------- /tokio-boring/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tokio-boring" 3 | version = { workspace = true } 4 | authors = ["Alex Crichton ", "Ivan Nikulin "] 5 | license = "MIT OR Apache-2.0" 6 | edition = { workspace = true } 7 | repository = { workspace = true } 8 | homepage = "https://github.com/cloudflare/boring" 9 | documentation = "https://docs.rs/tokio-boring" 10 | description = """ 11 | An implementation of SSL streams for Tokio backed by BoringSSL 12 | """ 13 | 14 | [package.metadata.docs.rs] 15 | features = ["rpk", "pq-experimental"] 16 | rustdoc-args = ["--cfg", "docsrs"] 17 | 18 | [features] 19 | # Use a FIPS-validated version of boringssl. 20 | fips = ["boring/fips", "boring-sys/fips"] 21 | 22 | # Use a FIPS build of BoringSSL, but don't set "fips-compat". 23 | # 24 | # As of boringSSL commit a430310d6563c0734ddafca7731570dfb683dc19, we no longer 25 | # need to make exceptions for the types of BufLen, ProtosLen, and ValueLen, 26 | # which means the "fips-compat" feature is no longer needed. 27 | # 28 | # TODO(cjpatton) Delete this feature and modify "fips" so that it doesn't imply 29 | # "fips-compat". 30 | fips-precompiled = ["boring/fips-precompiled"] 31 | 32 | # Link with precompiled FIPS-validated `bcm.o` module. 33 | fips-link-precompiled = ["boring/fips-link-precompiled", "boring-sys/fips-link-precompiled"] 34 | 35 | # Enables experimental post-quantum crypto (https://blog.cloudflare.com/post-quantum-for-all/) 36 | pq-experimental = ["boring/pq-experimental"] 37 | 38 | # Enables Raw public key API (https://datatracker.ietf.org/doc/html/rfc7250) 39 | rpk = ["boring/rpk"] 40 | 41 | [dependencies] 42 | boring = { workspace = true } 43 | boring-sys = { workspace = true } 44 | tokio = { workspace = true } 45 | 46 | [dev-dependencies] 47 | futures = { workspace = true } 48 | tokio = { workspace = true, features = [ "full" ] } 49 | anyhow = { workspace = true } 50 | -------------------------------------------------------------------------------- /tokio-boring/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Tokio contributors 2 | Copyright (c) 2020 Ivan Nikulin 3 | 4 | Permission is hereby granted, free of charge, to any 5 | person obtaining a copy of this software and associated 6 | documentation files (the "Software"), to deal in the 7 | Software without restriction, including without 8 | limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software 11 | is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice 15 | shall be included in all copies or substantial portions 16 | of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 19 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 20 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 21 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 22 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 25 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 | DEALINGS IN THE SOFTWARE. 27 | -------------------------------------------------------------------------------- /tokio-boring/README.md: -------------------------------------------------------------------------------- 1 | # tokio-boring 2 | 3 | An implementation of SSL streams for Tokio built on top of the BoringSSL. 4 | 5 | [Documentation](https://docs.rs/tokio-boring) 6 | 7 | ## Usage 8 | 9 | First, add this to your `Cargo.toml`: 10 | 11 | ```toml 12 | [dependencies] 13 | tokio-boring = "1.0.0" 14 | ``` 15 | 16 | Then, use either `accept` or `connect` as appropriate. 17 | 18 | ```rust 19 | use boring::ssl; 20 | use tokio::net::TcpListener; 21 | 22 | #[tokio::main] 23 | async fn main() -> anyhow::Result<()> { 24 | let listener = TcpListener::bind("127.0.0.1:8080").await?; 25 | let (tcp_stream, _addr) = listener.accept().await?; 26 | 27 | let server = ssl::SslMethod::tls_server(); 28 | let mut ssl_builder = boring::ssl::SslAcceptor::mozilla_modern(server)?; 29 | ssl_builder.set_default_verify_paths()?; 30 | ssl_builder.set_verify(ssl::SslVerifyMode::PEER); 31 | let acceptor = ssl_builder.build(); 32 | let _ssl_stream = tokio_boring::accept(&acceptor, tcp_stream).await?; 33 | Ok(()) 34 | } 35 | ``` 36 | 37 | This library is an implementation of TLS streams using BoringSSL for 38 | negotiating the connection. Each TLS stream implements the `Read` and 39 | `Write` traits to interact and interoperate with the rest of the futures I/O 40 | ecosystem. Client connections initiated from this crate verify hostnames 41 | automatically and by default. 42 | 43 | `tokio-boring` exports this ability through [`accept`] and [`connect`]. `accept` should 44 | be used by servers, and `connect` by clients. These augment the functionality provided by the 45 | [`boring`] crate, on which this crate is built. Configuration of TLS parameters is still 46 | primarily done through the [`boring`] crate. 47 | 48 | # License 49 | 50 | This project is licensed under either of 51 | 52 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or 53 | http://www.apache.org/licenses/LICENSE-2.0) 54 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or 55 | http://opensource.org/licenses/MIT) 56 | 57 | at your option. 58 | 59 | ### Contribution 60 | 61 | Unless you explicitly state otherwise, any contribution intentionally submitted 62 | for inclusion in Serde by you, as defined in the Apache-2.0 license, shall be 63 | dual licensed as above, without any additional terms or conditions. 64 | 65 | ## Accolades 66 | 67 | The project is based on a fork of [tokio-openssl](https://github.com/sfackler/tokio-openssl). 68 | -------------------------------------------------------------------------------- /tokio-boring/examples/simple-async.rs: -------------------------------------------------------------------------------- 1 | use boring::ssl; 2 | use tokio::net::TcpListener; 3 | 4 | #[tokio::main] 5 | async fn main() -> anyhow::Result<()> { 6 | let listener = TcpListener::bind("127.0.0.1:8080").await?; 7 | let (tcp_stream, _addr) = listener.accept().await?; 8 | 9 | let server = ssl::SslMethod::tls_server(); 10 | let mut ssl_builder = boring::ssl::SslAcceptor::mozilla_modern(server)?; 11 | ssl_builder.set_default_verify_paths()?; 12 | ssl_builder.set_verify(ssl::SslVerifyMode::PEER); 13 | let acceptor = ssl_builder.build(); 14 | let _ssl_stream = tokio_boring::accept(&acceptor, tcp_stream).await?; 15 | Ok(()) 16 | } 17 | -------------------------------------------------------------------------------- /tokio-boring/src/async_callbacks.rs: -------------------------------------------------------------------------------- 1 | use boring::ssl::{ 2 | AsyncPrivateKeyMethod, AsyncSelectCertError, BoxGetSessionFuture, BoxSelectCertFuture, 3 | ClientHello, SslContextBuilder, SslRef, 4 | }; 5 | 6 | /// Extensions to [`SslContextBuilder`]. 7 | /// 8 | /// This trait provides additional methods to use async callbacks with boring. 9 | pub trait SslContextBuilderExt: private::Sealed { 10 | /// Sets a callback that is called before most [`ClientHello`] processing 11 | /// and before the decision whether to resume a session is made. The 12 | /// callback may inspect the [`ClientHello`] and configure the connection. 13 | /// 14 | /// This method uses a function that returns a future whose output is 15 | /// itself a closure that will be passed [`ClientHello`] to configure 16 | /// the connection based on the computations done in the future. 17 | /// 18 | /// See [`SslContextBuilder::set_select_certificate_callback`] for the sync 19 | /// setter of this callback. 20 | fn set_async_select_certificate_callback(&mut self, callback: F) 21 | where 22 | F: Fn(&mut ClientHello<'_>) -> Result 23 | + Send 24 | + Sync 25 | + 'static; 26 | 27 | /// Configures a custom private key method on the context. 28 | /// 29 | /// See [`AsyncPrivateKeyMethod`] for more details. 30 | fn set_async_private_key_method(&mut self, method: impl AsyncPrivateKeyMethod); 31 | 32 | /// Sets a callback that is called when a client proposed to resume a session 33 | /// but it was not found in the internal cache. 34 | /// 35 | /// The callback is passed a reference to the session ID provided by the client. 36 | /// It should return the session corresponding to that ID if available. This is 37 | /// only used for servers, not clients. 38 | /// 39 | /// See [`SslContextBuilder::set_get_session_callback`] for the sync setter 40 | /// of this callback. 41 | /// 42 | /// # Safety 43 | /// 44 | /// The returned [`SslSession`] must not be associated with a different [`SslContext`]. 45 | unsafe fn set_async_get_session_callback(&mut self, callback: F) 46 | where 47 | F: Fn(&mut SslRef, &[u8]) -> Option + Send + Sync + 'static; 48 | } 49 | 50 | impl SslContextBuilderExt for SslContextBuilder { 51 | fn set_async_select_certificate_callback(&mut self, callback: F) 52 | where 53 | F: Fn(&mut ClientHello<'_>) -> Result 54 | + Send 55 | + Sync 56 | + 'static, 57 | { 58 | self.set_async_select_certificate_callback(callback); 59 | } 60 | 61 | fn set_async_private_key_method(&mut self, method: impl AsyncPrivateKeyMethod) { 62 | self.set_async_private_key_method(method); 63 | } 64 | 65 | unsafe fn set_async_get_session_callback(&mut self, callback: F) 66 | where 67 | F: Fn(&mut SslRef, &[u8]) -> Option + Send + Sync + 'static, 68 | { 69 | self.set_async_get_session_callback(callback); 70 | } 71 | } 72 | 73 | mod private { 74 | pub trait Sealed {} 75 | } 76 | 77 | impl private::Sealed for SslContextBuilder {} 78 | -------------------------------------------------------------------------------- /tokio-boring/src/bridge.rs: -------------------------------------------------------------------------------- 1 | //! Bridge between sync IO traits and async tokio IO traits. 2 | use std::fmt; 3 | use std::io; 4 | use std::pin::Pin; 5 | use std::task::{Context, Poll, Waker}; 6 | use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; 7 | 8 | pub(crate) struct AsyncStreamBridge { 9 | pub(crate) stream: S, 10 | waker: Option, 11 | } 12 | 13 | impl AsyncStreamBridge { 14 | pub(crate) fn new(stream: S) -> Self 15 | where 16 | S: AsyncRead + AsyncWrite + Unpin, 17 | { 18 | Self { 19 | stream, 20 | waker: None, 21 | } 22 | } 23 | 24 | pub(crate) fn set_waker(&mut self, ctx: Option<&mut Context<'_>>) { 25 | self.waker = ctx.map(|ctx| ctx.waker().clone()) 26 | } 27 | 28 | /// # Panics 29 | /// 30 | /// Panics if the bridge has no waker. 31 | pub(crate) fn with_context(&mut self, f: F) -> R 32 | where 33 | S: Unpin, 34 | F: FnOnce(&mut Context<'_>, Pin<&mut S>) -> R, 35 | { 36 | let mut ctx = 37 | Context::from_waker(self.waker.as_ref().expect("BUG: missing waker in bridge")); 38 | 39 | f(&mut ctx, Pin::new(&mut self.stream)) 40 | } 41 | } 42 | 43 | impl io::Read for AsyncStreamBridge 44 | where 45 | S: AsyncRead + Unpin, 46 | { 47 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 48 | self.with_context(|ctx, stream| { 49 | let mut buf = ReadBuf::new(buf); 50 | 51 | match stream.poll_read(ctx, &mut buf)? { 52 | Poll::Ready(()) => Ok(buf.filled().len()), 53 | Poll::Pending => Err(io::Error::from(io::ErrorKind::WouldBlock)), 54 | } 55 | }) 56 | } 57 | } 58 | 59 | impl io::Write for AsyncStreamBridge 60 | where 61 | S: AsyncWrite + Unpin, 62 | { 63 | fn write(&mut self, buf: &[u8]) -> io::Result { 64 | match self.with_context(|ctx, stream| stream.poll_write(ctx, buf)) { 65 | Poll::Ready(r) => r, 66 | Poll::Pending => Err(io::Error::from(io::ErrorKind::WouldBlock)), 67 | } 68 | } 69 | 70 | fn flush(&mut self) -> io::Result<()> { 71 | match self.with_context(|ctx, stream| stream.poll_flush(ctx)) { 72 | Poll::Ready(r) => r, 73 | Poll::Pending => Err(io::Error::from(io::ErrorKind::WouldBlock)), 74 | } 75 | } 76 | } 77 | 78 | impl fmt::Debug for AsyncStreamBridge 79 | where 80 | S: fmt::Debug, 81 | { 82 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 83 | fmt::Debug::fmt(&self.stream, fmt) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /tokio-boring/tests/async_custom_verify.rs: -------------------------------------------------------------------------------- 1 | use boring::ssl::{BoxCustomVerifyFinish, BoxCustomVerifyFuture, SslAlert, SslRef, SslVerifyMode}; 2 | use futures::future; 3 | use tokio::task::yield_now; 4 | 5 | mod common; 6 | 7 | use self::common::{connect, create_server, with_trivial_client_server_exchange}; 8 | 9 | #[tokio::test] 10 | async fn test_async_custom_verify_callback_trivial() { 11 | with_trivial_client_server_exchange(|builder| { 12 | builder.set_async_custom_verify_callback(SslVerifyMode::PEER, |_| { 13 | Ok(Box::pin(async { 14 | Ok(Box::new(|_: &mut _| Ok(())) as BoxCustomVerifyFinish) 15 | })) 16 | }); 17 | }) 18 | .await; 19 | } 20 | 21 | #[tokio::test] 22 | async fn test_async_custom_verify_callback_yield() { 23 | with_trivial_client_server_exchange(|builder| { 24 | builder.set_async_custom_verify_callback(SslVerifyMode::PEER, |_| { 25 | Ok(Box::pin(async { 26 | yield_now().await; 27 | 28 | Ok(Box::new(|_: &mut _| Ok(())) as BoxCustomVerifyFinish) 29 | })) 30 | }); 31 | }) 32 | .await; 33 | } 34 | 35 | #[tokio::test] 36 | async fn test_async_custom_verify_callback_return_error() { 37 | with_async_custom_verify_callback_error(|_| Err(SslAlert::CERTIFICATE_REVOKED)).await; 38 | } 39 | 40 | #[tokio::test] 41 | async fn test_async_custom_verify_callback_future_error() { 42 | with_async_custom_verify_callback_error(|_| { 43 | Ok(Box::pin(async move { Err(SslAlert::CERTIFICATE_REVOKED) })) 44 | }) 45 | .await; 46 | } 47 | 48 | #[tokio::test] 49 | async fn test_async_custom_verify_callback_future_yield_error() { 50 | with_async_custom_verify_callback_error(|_| { 51 | Ok(Box::pin(async move { 52 | yield_now().await; 53 | 54 | Err(SslAlert::CERTIFICATE_REVOKED) 55 | })) 56 | }) 57 | .await; 58 | } 59 | 60 | #[tokio::test] 61 | async fn test_async_custom_verify_callback_finish_error() { 62 | with_async_custom_verify_callback_error(|_| { 63 | Ok(Box::pin(async move { 64 | yield_now().await; 65 | 66 | Ok(Box::new(|_: &mut _| Err(SslAlert::CERTIFICATE_REVOKED)) as BoxCustomVerifyFinish) 67 | })) 68 | }) 69 | .await; 70 | } 71 | 72 | async fn with_async_custom_verify_callback_error( 73 | callback: impl Fn(&mut SslRef) -> Result + Send + Sync + 'static, 74 | ) { 75 | let (stream, addr) = create_server(|_| {}); 76 | 77 | let server = async { 78 | let err = stream.await.unwrap_err(); 79 | 80 | assert_eq!( 81 | err.to_string(), 82 | "TLS handshake failed [SSLV3_ALERT_CERTIFICATE_REVOKED]" 83 | ); 84 | }; 85 | 86 | let client = async { 87 | let _err = connect(addr, |builder| { 88 | builder.set_async_custom_verify_callback(SslVerifyMode::PEER, callback); 89 | builder.set_ca_file("tests/cert.pem") 90 | }) 91 | .await 92 | .unwrap_err(); 93 | }; 94 | 95 | future::join(server, client).await; 96 | } 97 | -------------------------------------------------------------------------------- /tokio-boring/tests/async_get_session.rs: -------------------------------------------------------------------------------- 1 | use boring::ssl::{SslOptions, SslRef, SslSession, SslSessionCacheMode, SslVersion}; 2 | use futures::future; 3 | use std::sync::atomic::{AtomicBool, Ordering}; 4 | use std::sync::OnceLock; 5 | use tokio::net::TcpStream; 6 | use tokio::task::yield_now; 7 | use tokio_boring::BoxGetSessionFinish; 8 | 9 | mod common; 10 | 11 | use self::common::{create_acceptor, create_connector, create_listener}; 12 | 13 | #[tokio::test] 14 | async fn test() { 15 | static FOUND_SESSION: AtomicBool = AtomicBool::new(false); 16 | static SERVER_SESSION_DER: OnceLock> = OnceLock::new(); 17 | static CLIENT_SESSION_DER: OnceLock> = OnceLock::new(); 18 | 19 | let (listener, addr) = create_listener(); 20 | 21 | let acceptor = create_acceptor(move |builder| { 22 | builder 23 | .set_max_proto_version(Some(SslVersion::TLS1_2)) 24 | .unwrap(); 25 | builder.set_options(SslOptions::NO_TICKET); 26 | builder 27 | .set_session_cache_mode(SslSessionCacheMode::SERVER | SslSessionCacheMode::NO_INTERNAL); 28 | builder.set_new_session_callback(|_, session| { 29 | SERVER_SESSION_DER.set(session.to_der().unwrap()).unwrap() 30 | }); 31 | 32 | unsafe { 33 | builder.set_async_get_session_callback(|_, _| { 34 | let der = SERVER_SESSION_DER.get()?; 35 | 36 | Some(Box::pin(async move { 37 | yield_now().await; 38 | 39 | FOUND_SESSION.store(true, Ordering::SeqCst); 40 | 41 | Some(Box::new(|_: &mut SslRef, _: &[u8]| { 42 | Some(SslSession::from_der(der).unwrap()) 43 | }) as BoxGetSessionFinish) 44 | })) 45 | }); 46 | } 47 | }); 48 | 49 | let connector = create_connector(|builder| { 50 | builder.set_session_cache_mode(SslSessionCacheMode::CLIENT); 51 | builder.set_new_session_callback(|_, session| { 52 | CLIENT_SESSION_DER.set(session.to_der().unwrap()).unwrap() 53 | }); 54 | 55 | builder.set_ca_file("tests/cert.pem") 56 | }); 57 | 58 | let server = async move { 59 | tokio_boring::accept(&acceptor, listener.accept().await.unwrap().0) 60 | .await 61 | .unwrap(); 62 | 63 | assert!(SERVER_SESSION_DER.get().is_some()); 64 | assert!(!FOUND_SESSION.load(Ordering::SeqCst)); 65 | 66 | tokio_boring::accept(&acceptor, listener.accept().await.unwrap().0) 67 | .await 68 | .unwrap(); 69 | 70 | assert!(FOUND_SESSION.load(Ordering::SeqCst)); 71 | }; 72 | 73 | let client = async move { 74 | tokio_boring::connect( 75 | connector.configure().unwrap(), 76 | "localhost", 77 | TcpStream::connect(&addr).await.unwrap(), 78 | ) 79 | .await 80 | .unwrap(); 81 | 82 | let der = CLIENT_SESSION_DER.get().unwrap(); 83 | 84 | let mut config = connector.configure().unwrap(); 85 | 86 | unsafe { 87 | config 88 | .set_session(&SslSession::from_der(der).unwrap()) 89 | .unwrap(); 90 | } 91 | 92 | tokio_boring::connect( 93 | config, 94 | "localhost", 95 | TcpStream::connect(&addr).await.unwrap(), 96 | ) 97 | .await 98 | .unwrap(); 99 | }; 100 | 101 | future::join(server, client).await; 102 | } 103 | -------------------------------------------------------------------------------- /tokio-boring/tests/async_private_key_method.rs: -------------------------------------------------------------------------------- 1 | use boring::hash::MessageDigest; 2 | use boring::pkey::PKey; 3 | use boring::rsa::Padding; 4 | use boring::sign::{RsaPssSaltlen, Signer}; 5 | use boring::ssl::{SslRef, SslSignatureAlgorithm}; 6 | use futures::future; 7 | use tokio::task::yield_now; 8 | use tokio_boring::{AsyncPrivateKeyMethod, AsyncPrivateKeyMethodError, BoxPrivateKeyMethodFuture}; 9 | 10 | mod common; 11 | 12 | use self::common::{connect, create_server, with_trivial_client_server_exchange}; 13 | 14 | #[allow(clippy::type_complexity)] 15 | struct Method { 16 | sign: Box< 17 | dyn Fn( 18 | &mut SslRef, 19 | &[u8], 20 | SslSignatureAlgorithm, 21 | &mut [u8], 22 | ) -> Result 23 | + Send 24 | + Sync 25 | + 'static, 26 | >, 27 | decrypt: Box< 28 | dyn Fn( 29 | &mut SslRef, 30 | &[u8], 31 | &mut [u8], 32 | ) -> Result 33 | + Send 34 | + Sync 35 | + 'static, 36 | >, 37 | } 38 | 39 | impl Method { 40 | fn new() -> Self { 41 | Self { 42 | sign: Box::new(|_, _, _, _| unreachable!("called sign")), 43 | decrypt: Box::new(|_, _, _| unreachable!("called decrypt")), 44 | } 45 | } 46 | 47 | fn sign( 48 | mut self, 49 | sign: impl Fn( 50 | &mut SslRef, 51 | &[u8], 52 | SslSignatureAlgorithm, 53 | &mut [u8], 54 | ) -> Result 55 | + Send 56 | + Sync 57 | + 'static, 58 | ) -> Self { 59 | self.sign = Box::new(sign); 60 | 61 | self 62 | } 63 | 64 | #[allow(dead_code)] 65 | fn decrypt( 66 | mut self, 67 | decrypt: impl Fn( 68 | &mut SslRef, 69 | &[u8], 70 | &mut [u8], 71 | ) -> Result 72 | + Send 73 | + Sync 74 | + 'static, 75 | ) -> Self { 76 | self.decrypt = Box::new(decrypt); 77 | 78 | self 79 | } 80 | } 81 | 82 | impl AsyncPrivateKeyMethod for Method { 83 | fn sign( 84 | &self, 85 | ssl: &mut SslRef, 86 | input: &[u8], 87 | signature_algorithm: SslSignatureAlgorithm, 88 | output: &mut [u8], 89 | ) -> Result { 90 | (self.sign)(ssl, input, signature_algorithm, output) 91 | } 92 | 93 | fn decrypt( 94 | &self, 95 | ssl: &mut SslRef, 96 | input: &[u8], 97 | output: &mut [u8], 98 | ) -> Result { 99 | (self.decrypt)(ssl, input, output) 100 | } 101 | } 102 | 103 | #[tokio::test] 104 | async fn test_sign_failure() { 105 | with_async_private_key_method_error( 106 | Method::new().sign(|_, _, _, _| Err(AsyncPrivateKeyMethodError)), 107 | ) 108 | .await; 109 | } 110 | 111 | #[tokio::test] 112 | async fn test_sign_future_failure() { 113 | with_async_private_key_method_error( 114 | Method::new().sign(|_, _, _, _| Ok(Box::pin(async { Err(AsyncPrivateKeyMethodError) }))), 115 | ) 116 | .await; 117 | } 118 | 119 | #[tokio::test] 120 | async fn test_sign_future_yield_failure() { 121 | with_async_private_key_method_error(Method::new().sign(|_, _, _, _| { 122 | Ok(Box::pin(async { 123 | yield_now().await; 124 | 125 | Err(AsyncPrivateKeyMethodError) 126 | })) 127 | })) 128 | .await; 129 | } 130 | 131 | #[tokio::test] 132 | async fn test_sign_ok() { 133 | with_trivial_client_server_exchange(|builder| { 134 | builder.set_async_private_key_method(Method::new().sign( 135 | |_, input, signature_algorithm, _| { 136 | assert_eq!( 137 | signature_algorithm, 138 | SslSignatureAlgorithm::RSA_PSS_RSAE_SHA256, 139 | ); 140 | 141 | let input = input.to_owned(); 142 | 143 | Ok(Box::pin(async move { 144 | Ok(Box::new(move |_: &mut SslRef, output: &mut [u8]| { 145 | Ok(sign_with_default_config(&input, output)) 146 | }) as Box<_>) 147 | })) 148 | }, 149 | )); 150 | }) 151 | .await; 152 | } 153 | 154 | fn sign_with_default_config(input: &[u8], output: &mut [u8]) -> usize { 155 | let pkey = PKey::private_key_from_pem(include_bytes!("key.pem")).unwrap(); 156 | let mut signer = Signer::new(MessageDigest::sha256(), &pkey).unwrap(); 157 | 158 | signer.set_rsa_padding(Padding::PKCS1_PSS).unwrap(); 159 | signer 160 | .set_rsa_pss_saltlen(RsaPssSaltlen::DIGEST_LENGTH) 161 | .unwrap(); 162 | 163 | signer.update(input).unwrap(); 164 | 165 | signer.sign(output).unwrap() 166 | } 167 | 168 | async fn with_async_private_key_method_error(method: Method) { 169 | let (stream, addr) = create_server(move |builder| { 170 | builder.set_async_private_key_method(method); 171 | }); 172 | 173 | let server = async { 174 | let _err = stream.await.unwrap_err(); 175 | }; 176 | 177 | let client = async { 178 | let _err = connect(addr, |builder| builder.set_ca_file("tests/cert.pem")) 179 | .await 180 | .unwrap_err(); 181 | }; 182 | 183 | future::join(server, client).await; 184 | } 185 | -------------------------------------------------------------------------------- /tokio-boring/tests/async_select_certificate.rs: -------------------------------------------------------------------------------- 1 | use boring::ssl::ClientHello; 2 | use futures::future; 3 | use tokio::task::yield_now; 4 | use tokio_boring::{AsyncSelectCertError, BoxSelectCertFinish, BoxSelectCertFuture}; 5 | 6 | mod common; 7 | 8 | use self::common::{connect, create_server, with_trivial_client_server_exchange}; 9 | 10 | #[tokio::test] 11 | async fn test_async_select_certificate_callback_trivial() { 12 | with_trivial_client_server_exchange(|builder| { 13 | builder.set_async_select_certificate_callback(|_| { 14 | Ok(Box::pin(async { 15 | Ok(Box::new(|_: ClientHello<'_>| Ok(())) as BoxSelectCertFinish) 16 | })) 17 | }); 18 | }) 19 | .await; 20 | } 21 | 22 | #[tokio::test] 23 | async fn test_async_select_certificate_callback_yield() { 24 | with_trivial_client_server_exchange(|builder| { 25 | builder.set_async_select_certificate_callback(|_| { 26 | Ok(Box::pin(async { 27 | yield_now().await; 28 | 29 | Ok(Box::new(|_: ClientHello<'_>| Ok(())) as BoxSelectCertFinish) 30 | })) 31 | }); 32 | }) 33 | .await; 34 | } 35 | 36 | #[tokio::test] 37 | async fn test_async_select_certificate_callback_return_error() { 38 | with_async_select_certificate_callback_error(|_| Err(AsyncSelectCertError)).await; 39 | } 40 | 41 | #[tokio::test] 42 | async fn test_async_select_certificate_callback_future_error() { 43 | with_async_select_certificate_callback_error(|_| { 44 | Ok(Box::pin(async move { Err(AsyncSelectCertError) })) 45 | }) 46 | .await; 47 | } 48 | 49 | #[tokio::test] 50 | async fn test_async_select_certificate_callback_future_yield_error() { 51 | with_async_select_certificate_callback_error(|_| { 52 | Ok(Box::pin(async move { 53 | yield_now().await; 54 | 55 | Err(AsyncSelectCertError) 56 | })) 57 | }) 58 | .await; 59 | } 60 | 61 | #[tokio::test] 62 | async fn test_async_select_certificate_callback_finish_error() { 63 | with_async_select_certificate_callback_error(|_| { 64 | Ok(Box::pin(async move { 65 | yield_now().await; 66 | 67 | Ok(Box::new(|_: ClientHello<'_>| Err(AsyncSelectCertError)) as BoxSelectCertFinish) 68 | })) 69 | }) 70 | .await; 71 | } 72 | 73 | async fn with_async_select_certificate_callback_error( 74 | callback: impl Fn(&mut ClientHello<'_>) -> Result 75 | + Send 76 | + Sync 77 | + 'static, 78 | ) { 79 | let (stream, addr) = create_server(|builder| { 80 | builder.set_async_select_certificate_callback(callback); 81 | }); 82 | 83 | let server = async { 84 | let _err = stream.await.unwrap_err(); 85 | }; 86 | 87 | let client = async { 88 | let _err = connect(addr, |builder| builder.set_ca_file("tests/cert.pem")) 89 | .await 90 | .unwrap_err(); 91 | }; 92 | 93 | future::join(server, client).await; 94 | } 95 | -------------------------------------------------------------------------------- /tokio-boring/tests/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDhTCCAm2gAwIBAgIJALClJS+cq+ykMA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV 3 | BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX 4 | aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNjExMjgwNTU5 5 | MjNaFw0yNjExMjYwNTU5MjNaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l 6 | LVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV 7 | BAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL4k 8 | A0mghV17MPi033tKh1IK4pM6zpzHFrgi2smU97O/kxvSbNnDoZHEdqq3AtRcjELg 9 | HeKTd02jyAHxGoRTAVORIp0p4LCCvlR7EnHH78e3vIq3lkLe5uqyujnx2NJJIrIX 10 | r5Y+Z3QuMUSAix6GreAb19KcTZG82igvh4dOQP/pmlqQsyrPpioLy50O2NuBqU5Q 11 | xyevFRHsWfe3M7ayzJBVwMpDJxg3saOETXgzMzfKtrj2Pw0mfcHQMtsPv7z85ug0 12 | yyd9iXwwLYx2RqZ6epChsWuY2zj7Zfcis3DzbsrW8/J758KNkjZVWS9aJmDGsT3R 13 | xRlVDnIeow/SWi5qtqECAwEAAaNQME4wHQYDVR0OBBYEFNU1F6I+C06y6rN1yjn0 14 | i/ARufw1MB8GA1UdIwQYMBaAFNU1F6I+C06y6rN1yjn0i/ARufw1MAwGA1UdEwQF 15 | MAMBAf8wDQYJKoZIhvcNAQELBQADggEBACvFmTY+QSrc9EIAtuGk20L4OHrkOoRv 16 | veMIu3PAGbrzjE0rRC1qeLqkqudlWCk+xE6nNe90tB0qyY8AOgj68K2OplrJIhqt 17 | rxJ/Ohtbepwi53Q5npRoib6f9aL+FuT0hnVtVon2ngWRizSdH/CY7vCWuJjTtlon 18 | 3J8TGPA1cnj8FtEEfF3ISd0/XCE2oar875FOscf7S0eLnORbuunCVU/RaNn25h/r 19 | 9EhvoaPZ6cSZpt7UliMkSt6b07/A2SwU5C19BS1XoqGH02P9OV0pmuJn7N/fOGer 20 | aVbDiPpb+UAUHFUSyu32iK6T2/6OuJS7MQ1cI2biB2SWgWNBTmhRF1s= 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /tokio-boring/tests/client_server.rs: -------------------------------------------------------------------------------- 1 | use boring::ssl::{SslConnector, SslMethod}; 2 | use futures::future; 3 | use std::net::ToSocketAddrs; 4 | use tokio::io::{AsyncReadExt, AsyncWriteExt}; 5 | use tokio::net::TcpStream; 6 | 7 | mod common; 8 | 9 | use self::common::{connect, create_server, with_trivial_client_server_exchange}; 10 | 11 | #[tokio::test] 12 | async fn google() { 13 | let addr = "google.com:443".to_socket_addrs().unwrap().next().unwrap(); 14 | let stream = TcpStream::connect(&addr).await.unwrap(); 15 | 16 | let config = SslConnector::builder(SslMethod::tls()) 17 | .unwrap() 18 | .build() 19 | .configure() 20 | .unwrap(); 21 | let mut stream = tokio_boring::connect(config, "google.com", stream) 22 | .await 23 | .unwrap(); 24 | 25 | stream.write_all(b"GET / HTTP/1.0\r\n\r\n").await.unwrap(); 26 | 27 | let mut buf = vec![]; 28 | stream.read_to_end(&mut buf).await.unwrap(); 29 | let response = String::from_utf8_lossy(&buf); 30 | let response = response.trim_end(); 31 | 32 | // any response code is fine 33 | assert!(response.starts_with("HTTP/1.0 ")); 34 | assert!(response.ends_with("") || response.ends_with("")); 35 | } 36 | 37 | #[tokio::test] 38 | async fn server() { 39 | with_trivial_client_server_exchange(|_| ()).await; 40 | } 41 | 42 | #[tokio::test] 43 | async fn handshake_error() { 44 | let (stream, addr) = create_server(|_| ()); 45 | 46 | let server = async { 47 | let err = stream.await.unwrap_err(); 48 | 49 | assert!(err.into_source_stream().is_some()); 50 | }; 51 | 52 | let client = async { 53 | let err = connect(addr, |_| Ok(())).await.unwrap_err(); 54 | 55 | assert!(err.into_source_stream().is_some()); 56 | }; 57 | 58 | future::join(server, client).await; 59 | } 60 | -------------------------------------------------------------------------------- /tokio-boring/tests/common/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use boring::error::ErrorStack; 4 | use boring::ssl::{ 5 | SslAcceptor, SslAcceptorBuilder, SslConnector, SslConnectorBuilder, SslFiletype, SslMethod, 6 | }; 7 | use futures::future::{self, Future}; 8 | use std::net::SocketAddr; 9 | use std::pin::Pin; 10 | use tokio::io::{AsyncReadExt, AsyncWrite, AsyncWriteExt}; 11 | use tokio::net::{TcpListener, TcpStream}; 12 | use tokio_boring::{HandshakeError, SslStream}; 13 | 14 | pub(crate) fn create_server( 15 | setup: impl FnOnce(&mut SslAcceptorBuilder), 16 | ) -> ( 17 | impl Future, HandshakeError>>, 18 | SocketAddr, 19 | ) { 20 | let (listener, addr) = create_listener(); 21 | 22 | let server = async move { 23 | let acceptor = create_acceptor(setup); 24 | 25 | let stream = listener.accept().await.unwrap().0; 26 | 27 | tokio_boring::accept(&acceptor, stream).await 28 | }; 29 | 30 | (server, addr) 31 | } 32 | 33 | pub(crate) fn create_listener() -> (TcpListener, SocketAddr) { 34 | let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); 35 | 36 | listener.set_nonblocking(true).unwrap(); 37 | 38 | let listener = TcpListener::from_std(listener).unwrap(); 39 | let addr = listener.local_addr().unwrap(); 40 | 41 | (listener, addr) 42 | } 43 | 44 | pub(crate) fn create_acceptor(setup: impl FnOnce(&mut SslAcceptorBuilder)) -> SslAcceptor { 45 | let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); 46 | 47 | acceptor 48 | .set_private_key_file("tests/key.pem", SslFiletype::PEM) 49 | .unwrap(); 50 | 51 | acceptor 52 | .set_certificate_chain_file("tests/cert.pem") 53 | .unwrap(); 54 | 55 | setup(&mut acceptor); 56 | 57 | acceptor.build() 58 | } 59 | 60 | pub(crate) async fn connect( 61 | addr: SocketAddr, 62 | setup: impl FnOnce(&mut SslConnectorBuilder) -> Result<(), ErrorStack>, 63 | ) -> Result, HandshakeError> { 64 | let config = create_connector(setup).configure().unwrap(); 65 | 66 | let stream = TcpStream::connect(&addr).await.unwrap(); 67 | 68 | tokio_boring::connect(config, "localhost", stream).await 69 | } 70 | 71 | pub(crate) fn create_connector( 72 | setup: impl FnOnce(&mut SslConnectorBuilder) -> Result<(), ErrorStack>, 73 | ) -> SslConnector { 74 | let mut connector = SslConnector::builder(SslMethod::tls()).unwrap(); 75 | 76 | setup(&mut connector).unwrap(); 77 | 78 | connector.build() 79 | } 80 | 81 | pub(crate) async fn with_trivial_client_server_exchange( 82 | server_setup: impl FnOnce(&mut SslAcceptorBuilder), 83 | ) { 84 | let (stream, addr) = create_server(server_setup); 85 | 86 | let server = async { 87 | let mut stream = stream.await.unwrap(); 88 | let mut buf = [0; 4]; 89 | stream.read_exact(&mut buf).await.unwrap(); 90 | assert_eq!(&buf, b"asdf"); 91 | 92 | stream.write_all(b"jkl;").await.unwrap(); 93 | 94 | future::poll_fn(|ctx| Pin::new(&mut stream).poll_shutdown(ctx)) 95 | .await 96 | .unwrap(); 97 | }; 98 | 99 | let client = async { 100 | let mut stream = connect(addr, |builder| builder.set_ca_file("tests/cert.pem")) 101 | .await 102 | .unwrap(); 103 | 104 | stream.write_all(b"asdf").await.unwrap(); 105 | 106 | let mut buf = vec![]; 107 | stream.read_to_end(&mut buf).await.unwrap(); 108 | assert_eq!(buf, b"jkl;"); 109 | }; 110 | 111 | future::join(server, client).await; 112 | } 113 | -------------------------------------------------------------------------------- /tokio-boring/tests/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC+JANJoIVdezD4 3 | tN97SodSCuKTOs6cxxa4ItrJlPezv5Mb0mzZw6GRxHaqtwLUXIxC4B3ik3dNo8gB 4 | 8RqEUwFTkSKdKeCwgr5UexJxx+/Ht7yKt5ZC3ubqsro58djSSSKyF6+WPmd0LjFE 5 | gIsehq3gG9fSnE2RvNooL4eHTkD/6ZpakLMqz6YqC8udDtjbgalOUMcnrxUR7Fn3 6 | tzO2ssyQVcDKQycYN7GjhE14MzM3yra49j8NJn3B0DLbD7+8/OboNMsnfYl8MC2M 7 | dkamenqQobFrmNs4+2X3IrNw827K1vPye+fCjZI2VVkvWiZgxrE90cUZVQ5yHqMP 8 | 0louarahAgMBAAECggEAd08brQCHjs/1O6orLS7n2IgyAhZ9fQzD6ckdJi5Oe8Cz 9 | K1sPqFlEMbZoi9iIcv6bmH8O4ZSM4O/rWaSTcgKvq2M/qASWE8wGZ/ZN7Y16nQRi 10 | z1xBcjZyCUUa668g0VrI5Z1NNWZ0/gbaLVTHduEli6GM/H/NgKxS67JfRXzJ9onl 11 | d6vrK+xmeHyA7QSOieEDettaNCvm+HjU8mmOb4F1pCNZktDrch5rI8EzQlmFQuq4 12 | y50YLRZGSlK1QLjzMnT//oaP7mHjN/inzZTHBvTzhU2OjcjzEW7l4ry224Sdu/eH 13 | lhEnNk2eq+mH/yESkn3sJcmH4uYIXh8Dyvcy/uVkPQKBgQDzSC89qxT4sls9n0sL 14 | 0DfVhq1D7kEXggD/4wNA714N24N/NWi5BYUDZVh9Kxqy9SuWlFYg1L1ZNZHB02aV 15 | GJdEiFMFgRea2E5NHnhWop+qYPq5N9jD72MHmz/6swX9VGi1p5DqjzK2hWMgoih9 16 | 4ky1zxMw+P+aDaQ6xwZF1nr+mwKBgQDIFKTvaJYjqQ/lzRMIPLA3sg6RQ+Mqwt/C 17 | BZ9Oc3DGtuglV8F73i7ML2Ptg0GtVZo3NJgGzMerpNvEoc1pDCuZkzSYitcYysQQ 18 | wsailMQFCv9jJ9g28lSGKlEPYhcLejH8ZRi8jH0fObHIvgr7komNvvPIDFnw/uR8 19 | WsgrloD1cwKBgAdlAkqVkKWehjdxSA6r3YaX+Vw/OatFQFKGy+qFXA5/xZdwQCaf 20 | jFN2GSJ01PLrkM+a4qNM1BSKFEwX6N5PSQnEOwHH0rfaK0cczfuUJdY/7F8E24nZ 21 | FOF+TouINX5lumkLFtSKVbhGhaTQSPrKjhpYmPS8HMjJ8Vv4ALDOvB5RAoGBAJAS 22 | RX3bCpmdCESKOdUplh5UyaaSgsZs0qCsWb0s5R1B4cHaAgnGwF3pFgSWCjndNRHh 23 | fkMPPAv9xv49IGMvD0ojtLDO8Pn6L9p91niFtOyIscNdkpRmRLTjTcFM+ZkbIVlE 24 | Ft7WLtbIPZt2NQRXzVLTGEmJk040zKQ63n58flm/AoGBAKt97WLeHB9S/q0dpEGX 25 | Qk+1BXRAH0/4wK9lNrSeaw+npFr8rNN9K3sIBC/XnOwhT+wbKBpOoBT3PNHbNxVr 26 | EPPQ/pPmZ1TcHc7bszJnZon2S2PFJRDN4601X1/eFoTvakBnLlt1096paaolSmCG 27 | nYED9qXuh2VzUU1GgcqPXgf/ 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /tokio-boring/tests/pubkey.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/boring/05f798adc485b7a37028f49a5828fa5bf07aadf3/tokio-boring/tests/pubkey.der -------------------------------------------------------------------------------- /tokio-boring/tests/pubkey2.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/boring/05f798adc485b7a37028f49a5828fa5bf07aadf3/tokio-boring/tests/pubkey2.der -------------------------------------------------------------------------------- /tokio-boring/tests/rpk.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "rpk")] 2 | mod test_rpk { 3 | use boring::pkey::PKey; 4 | use boring::ssl::{SslAcceptor, SslConnector}; 5 | use futures::future; 6 | use std::future::Future; 7 | use std::net::SocketAddr; 8 | use std::pin::Pin; 9 | use tokio::io::{AsyncReadExt, AsyncWrite, AsyncWriteExt}; 10 | use tokio::net::{TcpListener, TcpStream}; 11 | use tokio_boring::{HandshakeError, SslStream}; 12 | 13 | fn create_server() -> ( 14 | impl Future, HandshakeError>>, 15 | SocketAddr, 16 | ) { 17 | let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); 18 | 19 | listener.set_nonblocking(true).unwrap(); 20 | 21 | let listener = TcpListener::from_std(listener).unwrap(); 22 | let addr = listener.local_addr().unwrap(); 23 | 24 | let server = async move { 25 | let mut acceptor = SslAcceptor::rpk().unwrap(); 26 | let pkey = std::fs::read("tests/key.pem").unwrap(); 27 | let pkey = PKey::private_key_from_pem(&pkey).unwrap(); 28 | let cert = std::fs::read("tests/pubkey.der").unwrap(); 29 | 30 | acceptor.set_rpk_certificate(&cert).unwrap(); 31 | acceptor.set_null_chain_private_key(&pkey).unwrap(); 32 | 33 | let acceptor = acceptor.build(); 34 | 35 | let stream = listener.accept().await.unwrap().0; 36 | 37 | tokio_boring::accept(&acceptor, stream).await 38 | }; 39 | 40 | (server, addr) 41 | } 42 | 43 | #[tokio::test] 44 | async fn server_rpk() { 45 | let (stream, addr) = create_server(); 46 | 47 | let server = async { 48 | let mut stream = stream.await.unwrap(); 49 | let mut buf = [0; 4]; 50 | stream.read_exact(&mut buf).await.unwrap(); 51 | assert_eq!(&buf, b"asdf"); 52 | 53 | stream.write_all(b"jkl;").await.unwrap(); 54 | 55 | future::poll_fn(|ctx| Pin::new(&mut stream).poll_shutdown(ctx)) 56 | .await 57 | .unwrap(); 58 | }; 59 | 60 | let client = async { 61 | let mut connector = SslConnector::rpk_builder().unwrap(); 62 | let cert = std::fs::read("tests/pubkey.der").unwrap(); 63 | 64 | connector.set_rpk_certificate(&cert).unwrap(); 65 | let config = connector.build().configure().unwrap(); 66 | 67 | let stream = TcpStream::connect(&addr).await.unwrap(); 68 | let mut stream = tokio_boring::connect(config, "localhost", stream) 69 | .await 70 | .unwrap(); 71 | 72 | stream.write_all(b"asdf").await.unwrap(); 73 | 74 | let mut buf = vec![]; 75 | stream.read_to_end(&mut buf).await.unwrap(); 76 | assert_eq!(buf, b"jkl;"); 77 | }; 78 | 79 | future::join(server, client).await; 80 | } 81 | 82 | #[tokio::test] 83 | async fn client_rpk_unknown_cert() { 84 | let (stream, addr) = create_server(); 85 | 86 | let server = async { 87 | assert!(stream.await.is_err()); 88 | }; 89 | 90 | let client = async { 91 | let mut connector = SslConnector::rpk_builder().unwrap(); 92 | let cert = std::fs::read("tests/pubkey2.der").unwrap(); 93 | 94 | connector.set_rpk_certificate(&cert).unwrap(); 95 | let config = connector.build().configure().unwrap(); 96 | 97 | let stream = TcpStream::connect(&addr).await.unwrap(); 98 | 99 | let err = tokio_boring::connect(config, "localhost", stream) 100 | .await 101 | .unwrap_err(); 102 | 103 | // NOTE: smoke test for https://github.com/cloudflare/boring/issues/140 104 | let _ = err.to_string(); 105 | }; 106 | 107 | future::join(server, client).await; 108 | } 109 | } 110 | --------------------------------------------------------------------------------