Merge pull request #126 from inikulin/rpk-pqc

Add RPK and PQ crypto features
This commit is contained in:
Ivan Nikulin 2023-07-10 23:08:33 +01:00 committed by GitHub
commit 63e178d880
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 10378 additions and 131 deletions

View File

@ -197,3 +197,20 @@ jobs:
run: ln -s clang clang++-12 run: ln -s clang clang++-12
- run: cargo test --features fips - run: cargo test --features fips
name: Run tests name: Run tests
test-features:
name: Test features
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
with:
submodules: 'recursive'
- name: Install Rust (rustup)
run: rustup update stable --no-self-update && rustup default stable
shell: bash
- run: cargo test --features rpk
name: Run `rpk` tests
- run: cargo test --features pq-experimental
name: Run `pq-experimental` tests
- run: cargo test --features pq-experimental,rpk
name: Run `pq-experimental,rpk` tests

View File

@ -5,3 +5,33 @@ members = [
"tokio-boring", "tokio-boring",
"hyper-boring" "hyper-boring"
] ]
resolver = "2"
[workspace.package]
version = "3.0.0"
repository = "https://github.com/cloudflare/boring"
edition = "2021"
[workspace.dependencies]
boring-sys = { version = "3.0", path = "./boring-sys" }
boring = { version = "3.0", path = "./boring" }
tokio-boring = { version = "3.0", path = "./tokio-boring" }
bindgen = { version = "0.65.1", default-features = false, features = ["runtime"] }
cmake = "0.1"
fslock = "0.2"
bitflags = "1.0"
foreign-types = "0.5"
lazy_static = "1"
libc = "0.2"
hex = "0.4"
rusty-hook = "^0.11"
futures = "0.3"
tokio = { version = "1", features = ["full"] }
anyhow = "1"
antidote = "1.0.0"
http = "0.2"
hyper = { version = "0.14", default-features = false, features = ["full"] }
linked_hash_set = "0.1"
once_cell = "1.0"
tower-layer = "0.3"

View File

@ -5,34 +5,11 @@
BoringSSL bindings for the Rust programming language and TLS adapters for [tokio](https://github.com/tokio-rs/tokio) BoringSSL bindings for the Rust programming language and TLS adapters for [tokio](https://github.com/tokio-rs/tokio)
and [hyper](https://github.com/hyperium/hyper) built on top of it. and [hyper](https://github.com/hyperium/hyper) built on top of it.
[Documentation](https://docs.rs/boring). ## Documentation
- Boring API: <https://docs.rs/boring>
## Release Support - tokio TLS adapters: <https://docs.rs/tokio-boring>
- hyper HTTPS connector: <https://docs.rs/hyper-boring>
By default, the crate statically links with the latest BoringSSL master branch. - FFI bindings: <https://docs.rs/boring-sys>
## Support for pre-built binaries
While this crate can build BoringSSL on its own, you may want to provide pre-built binaries instead.
To do so, specify the environment variable `BORING_BSSL_PATH` with the path to the binaries.
You can also provide specific headers by setting `BORING_BSSL_INCLUDE_PATH`.
_Notes_: The crate will look for headers in the `$BORING_BSSL_INCLUDE_PATH/openssl/` folder, make sure to place your headers there.
_Warning_: When providing a different version of BoringSSL make sure to use a compatible one, the crate relies on the presence of certain functions.
## Building with a FIPS-validated module
Only BoringCrypto module version 853ca1ea1168dff08011e5d42d94609cc0ca2e27, as certified with
[FIPS 140-2 certificate 4407](https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/4407)
is supported by this crate. Support is enabled by this crate's `fips` feature.
`boring-sys` comes with a test that FIPS is enabled/disabled depending on the feature flag. You can run it as follows:
```bash
$ cargo test --features fips fips::is_enabled
```
## Contribution ## Contribution

View File

@ -1,17 +1,17 @@
[package] [package]
name = "boring-sys" name = "boring-sys"
version = "2.1.0" version = { workspace = true }
authors = ["Alex Crichton <alex@alexcrichton.com>", authors = ["Alex Crichton <alex@alexcrichton.com>",
"Steven Fackler <sfackler@gmail.com>", "Steven Fackler <sfackler@gmail.com>",
"Ivan Nikulin <ifaaan@gmail.com>"] "Ivan Nikulin <ifaaan@gmail.com>"]
license = "MIT" license = "MIT"
description = "FFI bindings to BoringSSL" description = "FFI bindings to BoringSSL"
repository = "https://github.com/cloudflare/boring" repository = { workspace = true }
documentation = "https://docs.rs/boring-sys" documentation = "https://docs.rs/boring-sys"
links = "boringssl" links = "boringssl"
readme = "README.md" readme = "README.md"
categories = ["cryptography", "external-ffi-bindings"] categories = ["cryptography", "external-ffi-bindings"]
edition = "2018" edition = { workspace = true }
include = [ include = [
"/*.md", "/*.md",
"/*.toml", "/*.toml",
@ -24,12 +24,27 @@ include = [
"/deps/boringssl/LICENSE", "/deps/boringssl/LICENSE",
"/build.rs", "/build.rs",
"/src", "/src",
"/patches",
"/scripts"
] ]
[build-dependencies] [package.metadata.docs.rs]
bindgen = { version = "0.65.1", default-features = false, features = ["runtime"] } features = ["rpk", "pq-experimental"]
cmake = "0.1" rustdoc-args = ["--cfg", "docsrs"]
[features] [features]
# Use a FIPS-validated version of boringssl. # Use a FIPS-validated version of boringssl.
fips = [] fips = []
# Enables Raw public key API (https://datatracker.ietf.org/doc/html/rfc7250)
rpk = []
# Enables experimental post-quantum crypto (https://blog.cloudflare.com/post-quantum-for-all/)
pq-experimental = []
[build-dependencies]
bindgen = { workspace = true }
cmake = { workspace = true }
fslock = { workspace = true }

View File

@ -1,3 +1,7 @@
use fslock::LockFile;
use std::env;
use std::io;
use std::io::Write;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::Command; use std::process::Command;
@ -285,7 +289,6 @@ fn get_extra_clang_args_for_bindgen() -> Vec<String> {
#[allow(clippy::single_match)] #[allow(clippy::single_match)]
match os.as_ref() { match os.as_ref() {
"ios" => { "ios" => {
use std::io::Write;
// When cross-compiling for iOS, tell bindgen to use iOS sysroot, // When cross-compiling for iOS, tell bindgen to use iOS sysroot,
// and *don't* use system headers of the host macOS. // and *don't* use system headers of the host macOS.
let sdk = get_ios_sdk_name(); let sdk = get_ios_sdk_name();
@ -325,11 +328,66 @@ fn get_extra_clang_args_for_bindgen() -> Vec<String> {
params params
} }
fn main() { fn ensure_patches_applied() -> io::Result<()> {
use std::env; let mut lock_file = LockFile::open(&PathBuf::from(BORING_SSL_PATH).join(".patch_lock"))?;
println!("cargo:rerun-if-env-changed=BORING_BSSL_PATH"); lock_file.lock()?;
let bssl_dir = std::env::var("BORING_BSSL_PATH").unwrap_or_else(|_| {
let boring_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(BORING_SSL_PATH);
let mut cmd = Command::new("git");
cmd.args(["reset", "--hard"]).current_dir(&boring_dir);
run_command(&mut cmd)?;
let mut cmd = Command::new("git");
cmd.args(["clean", "-fdx"]).current_dir(&boring_dir);
run_command(&mut cmd)?;
if cfg!(feature = "pq-experimental") {
println!("cargo:warning=applying experimental post quantum crypto patch to boringssl");
run_apply_patch_script("scripts/apply_pq_patch.sh")?;
}
if cfg!(feature = "rpk") {
println!("cargo:warning=applying RPK patch to boringssl");
run_apply_patch_script("scripts/apply_rpk_patch.sh")?;
}
Ok(())
}
fn run_command(command: &mut Command) -> io::Result<()> {
let exit_status = command.spawn()?.wait()?;
if !exit_status.success() {
let err = match exit_status.code() {
Some(code) => format!("{:?} exited with status: {}", command, code),
None => format!("{:?} was terminated by signal", command),
};
return Err(io::Error::new(io::ErrorKind::Other, err));
}
Ok(())
}
fn run_apply_patch_script(script_path: impl AsRef<Path>) -> io::Result<()> {
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let src_path = manifest_dir.join(BORING_SSL_PATH).canonicalize()?;
let cmd_path = manifest_dir.join(script_path).canonicalize()?;
let mut cmd = Command::new(cmd_path);
cmd.current_dir(src_path);
run_command(&mut cmd)?;
Ok(())
}
fn build_boring_from_sources() -> String {
if !Path::new(BORING_SSL_PATH).join("CMakeLists.txt").exists() { if !Path::new(BORING_SSL_PATH).join("CMakeLists.txt").exists() {
println!("cargo:warning=fetching boringssl git submodule"); println!("cargo:warning=fetching boringssl git submodule");
// fetch the boringssl submodule // fetch the boringssl submodule
@ -342,11 +400,14 @@ fn main() {
BORING_SSL_PATH, BORING_SSL_PATH,
]) ])
.status(); .status();
if !status.map_or(false, |status| status.success()) { if !status.map_or(false, |status| status.success()) {
panic!("failed to fetch submodule - consider running `git submodule update --init --recursive deps/boringssl` yourself"); panic!("failed to fetch submodule - consider running `git submodule update --init --recursive deps/boringssl` yourself");
} }
} }
ensure_patches_applied().unwrap();
let mut cfg = get_boringssl_cmake_config(); let mut cfg = get_boringssl_cmake_config();
if cfg!(feature = "fuzzing") { if cfg!(feature = "fuzzing") {
@ -363,9 +424,24 @@ fn main() {
cfg.build_target("ssl").build(); cfg.build_target("ssl").build();
cfg.build_target("crypto").build().display().to_string() cfg.build_target("crypto").build().display().to_string()
}); }
fn main() {
println!("cargo:rerun-if-env-changed=BORING_BSSL_PATH");
#[cfg(all(feature = "fips", feature = "rpk"))]
compile_error!("`fips` and `rpk` features are mutually exclusive");
let bssl_dir = std::env::var("BORING_BSSL_PATH");
if bssl_dir.is_ok() && cfg!(any(feature = "rpk", feature = "pq-experimental")) {
panic!("precompiled BoringSSL was provided, optional patches can't be applied to it");
}
let bssl_dir = bssl_dir.unwrap_or_else(|_| build_boring_from_sources());
let build_path = get_boringssl_platform_output_path(); let build_path = get_boringssl_platform_output_path();
if cfg!(feature = "fips") { if cfg!(feature = "fips") {
println!( println!(
"cargo:rustc-link-search=native={}/build/crypto/{}", "cargo:rustc-link-search=native={}/build/crypto/{}",
@ -385,12 +461,6 @@ fn main() {
println!("cargo:rustc-link-lib=static=crypto"); println!("cargo:rustc-link-lib=static=crypto");
println!("cargo:rustc-link-lib=static=ssl"); println!("cargo:rustc-link-lib=static=ssl");
// MacOS: Allow cdylib to link with undefined symbols
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
if target_os == "macos" {
println!("cargo:rustc-cdylib-link-arg=-Wl,-undefined,dynamic_lookup");
}
println!("cargo:rerun-if-env-changed=BORING_BSSL_INCLUDE_PATH"); println!("cargo:rerun-if-env-changed=BORING_BSSL_INCLUDE_PATH");
let include_path = std::env::var("BORING_BSSL_INCLUDE_PATH").unwrap_or_else(|_| { let include_path = std::env::var("BORING_BSSL_INCLUDE_PATH").unwrap_or_else(|_| {
if cfg!(feature = "fips") { if cfg!(feature = "fips") {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,792 @@
diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h
index 53aa9b453..87309c3e1 100644
--- a/src/include/openssl/ssl.h
+++ b/src/include/openssl/ssl.h
@@ -138,6 +138,25 @@
* OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR
* OTHERWISE.
*/
+/* ====================================================================
+ * Copyright 2020 Apple Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the “Software”),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
#ifndef OPENSSL_HEADER_SSL_H
#define OPENSSL_HEADER_SSL_H
@@ -1136,6 +1155,16 @@ OPENSSL_EXPORT int SSL_CTX_set_chain_and_key(
SSL_CTX *ctx, CRYPTO_BUFFER *const *certs, size_t num_certs,
EVP_PKEY *privkey, const SSL_PRIVATE_KEY_METHOD *privkey_method);
+// SSL_CTX_set_nullchain_and_key sets the private key for a
+// TLS client or server. Reference to the given |EVP_PKEY|
+// object is added as needed. Exactly one of |privkey| or |privkey_method|
+// may be non-NULL. Returns one on success and zero on error.
+// Note the lack of a corresponding public-key certificate.
+// See SSL_CTX_set_server_raw_public_key_certificate.
+OPENSSL_EXPORT int SSL_CTX_set_nullchain_and_key(
+ SSL_CTX *ctx,
+ EVP_PKEY *privkey, const SSL_PRIVATE_KEY_METHOD *privkey_method);
+
// SSL_set_chain_and_key sets the certificate chain and private key for a TLS
// client or server. References to the given |CRYPTO_BUFFER| and |EVP_PKEY|
// objects are added as needed. Exactly one of |privkey| or |privkey_method|
@@ -1144,6 +1173,16 @@ OPENSSL_EXPORT int SSL_set_chain_and_key(
SSL *ssl, CRYPTO_BUFFER *const *certs, size_t num_certs, EVP_PKEY *privkey,
const SSL_PRIVATE_KEY_METHOD *privkey_method);
+// SSL_set_nullchain_and_key sets the private key for a TLS
+// client or server. Reference to the given |EVP_PKEY|
+// object is added as needed. Exactly one of |privkey| or |privkey_method|
+// may be non-NULL. Returns one on success and zero on error.
+// Note the lack of a corresponding public-key certificate.
+// See SSL_set_server_raw_public_key_certificate.
+OPENSSL_EXPORT int SSL_set_nullchain_and_key(
+ SSL *ssl, EVP_PKEY *privkey,
+ const SSL_PRIVATE_KEY_METHOD *privkey_method);
+
// SSL_CTX_get0_chain returns the list of |CRYPTO_BUFFER|s that were set by
// |SSL_CTX_set_chain_and_key|. Reference counts are not incremented by this
// call. The return value may be |NULL| if no chain has been set.
@@ -3023,6 +3062,21 @@ OPENSSL_EXPORT void SSL_get0_peer_application_settings(const SSL *ssl,
OPENSSL_EXPORT int SSL_has_application_settings(const SSL *ssl);
+// Server Certificate Type.
+
+#define TLSEXT_CERTIFICATETYPE_X509 0
+#define TLSEXT_CERTIFICATETYPE_RAW_PUBLIC_KEY 2
+
+OPENSSL_EXPORT int SSL_CTX_set_server_raw_public_key_certificate(
+ SSL_CTX *ctx, const uint8_t *raw_public_key, unsigned raw_public_key_len);
+
+OPENSSL_EXPORT int SSL_CTX_has_server_raw_public_key_certificate(SSL_CTX *ctx);
+
+OPENSSL_EXPORT int SSL_set_server_raw_public_key_certificate(
+ SSL *ssl, const uint8_t *raw_public_key, unsigned raw_public_key_len);
+
+OPENSSL_EXPORT int SSL_has_server_raw_public_key_certificate(SSL *ssl);
+
// Certificate compression.
//
// Certificates in TLS 1.3 can be compressed (RFC 8879). BoringSSL supports this
diff --git a/src/include/openssl/tls1.h b/src/include/openssl/tls1.h
index 772fb87a3..be605c1aa 100644
--- a/src/include/openssl/tls1.h
+++ b/src/include/openssl/tls1.h
@@ -146,6 +146,25 @@
* OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR
* OTHERWISE.
*/
+/* ====================================================================
+ * Copyright 2020 Apple Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the “Software”),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
#ifndef OPENSSL_HEADER_TLS1_H
#define OPENSSL_HEADER_TLS1_H
@@ -197,6 +216,9 @@ extern "C" {
// ExtensionType value from RFC 7301
#define TLSEXT_TYPE_application_layer_protocol_negotiation 16
+// ExtensionType value from RFC 7250
+#define TLSEXT_TYPE_server_certificate_type 20
+
// ExtensionType value from RFC 7685
#define TLSEXT_TYPE_padding 21
diff --git a/src/ssl/extensions.cc b/src/ssl/extensions.cc
index 5ee280221..2692e5478 100644
--- a/src/ssl/extensions.cc
+++ b/src/ssl/extensions.cc
@@ -105,6 +105,25 @@
* This product includes cryptographic software written by Eric Young
* (eay@cryptsoft.com). This product includes software written by Tim
* Hudson (tjh@cryptsoft.com). */
+/* ====================================================================
+ * Copyright 2020 Apple Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the “Software”),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
#include <openssl/ssl.h>
@@ -3094,6 +3113,146 @@ bool ssl_negotiate_alps(SSL_HANDSHAKE *hs, uint8_t *out_alert,
return true;
}
+// Server Certificate Type
+
+static bool ext_server_certificate_type_add_clienthello(const SSL_HANDSHAKE *hs,
+ CBB *out,
+ CBB *out_compressible,
+ ssl_client_hello_type_t type) {
+
+ if (hs->max_version <= TLS1_2_VERSION) {
+ return true;
+ }
+
+ if (hs->config->server_certificate_type_list.empty()) {
+ return true;
+ }
+
+ CBB contents, server_certificate_types;
+ if (!CBB_add_u16(out, TLSEXT_TYPE_server_certificate_type) ||
+ !CBB_add_u16_length_prefixed(out, &contents) ||
+ !CBB_add_u8_length_prefixed(&contents, &server_certificate_types) ||
+ !CBB_add_bytes(&server_certificate_types,
+ hs->config->server_certificate_type_list.data(),
+ hs->config->server_certificate_type_list.size()) ||
+ !CBB_flush(out)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool ssl_is_certificate_type_allowed(CBS *certificate_type_list,
+ uint8_t certificate_type)
+{
+ uint8_t supported_certificate_type;
+ while (CBS_len(certificate_type_list) > 0) {
+ if (!CBS_get_u8(certificate_type_list,
+ &supported_certificate_type)) {
+ break;
+ }
+
+ if (supported_certificate_type != certificate_type) {
+ continue;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+static bool ext_server_certificate_type_parse_serverhello(SSL_HANDSHAKE *hs,
+ uint8_t *out_alert,
+ CBS *content)
+{
+ if (hs->max_version <= TLS1_2_VERSION ||
+ hs->config->server_certificate_type_list.empty()) {
+ return true;
+ }
+
+ // Strict
+ if (!content) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
+ *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+ return false;
+ }
+
+ CBS certificate_type_list =
+ MakeConstSpan(hs->config->server_certificate_type_list);
+
+ uint8_t certificate_type;
+ if (CBS_get_u8(content, &certificate_type) &&
+ ssl_is_certificate_type_allowed(&certificate_type_list,
+ certificate_type)) {
+ hs->server_certificate_type = certificate_type;
+ hs->server_certificate_type_negotiated = 1;
+ return true;
+ }
+
+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
+ *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+ return false;
+}
+
+static bool ext_server_certificate_type_parse_clienthello(SSL_HANDSHAKE *hs,
+ uint8_t *out_alert,
+ CBS *content)
+{
+ if (!content) {
+ return true;
+ }
+
+ if (hs->max_version <= TLS1_2_VERSION ||
+ hs->config->server_certificate_type_list.empty()) {
+ return true;
+ }
+
+ CBS certificate_type_list =
+ MakeConstSpan(hs->config->server_certificate_type_list);
+
+ CBS type_list;
+ if (!CBS_get_u8_length_prefixed(content, &type_list)) {
+ type_list.len = 0;
+ }
+
+ uint8_t type;
+ while(CBS_len(&type_list) > 0) {
+ if (!CBS_get_u8(&type_list, &type)) {
+ break;
+ }
+
+ if (!ssl_is_certificate_type_allowed(&certificate_type_list, type)) {
+ continue;
+ }
+
+ hs->server_certificate_type = type;
+ hs->server_certificate_type_negotiated = 1;
+ return true;
+ }
+
+ *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+ return false;
+}
+
+static bool ext_server_certificate_type_add_serverhello(SSL_HANDSHAKE *hs,
+ CBB *out)
+{
+ if (!hs->server_certificate_type_negotiated) {
+ return true;
+ }
+
+ CBB contents;
+ if (!CBB_add_u16(out, TLSEXT_TYPE_server_certificate_type) ||
+ !CBB_add_u16_length_prefixed(out, &contents) ||
+ !CBB_add_u8(&contents, hs->server_certificate_type) ||
+ !CBB_flush(out)) {
+ return false;
+ }
+
+ return true;
+}
+
// kExtensions contains all the supported extensions.
static const struct tls_extension kExtensions[] = {
{
@@ -3267,6 +3426,13 @@ static const struct tls_extension kExtensions[] = {
ignore_parse_clienthello,
ext_alps_add_serverhello,
},
+ {
+ TLSEXT_TYPE_server_certificate_type,
+ ext_server_certificate_type_add_clienthello,
+ ext_server_certificate_type_parse_serverhello,
+ ext_server_certificate_type_parse_clienthello,
+ ext_server_certificate_type_add_serverhello,
+ },
};
#define kNumExtensions (sizeof(kExtensions) / sizeof(struct tls_extension))
diff --git a/src/ssl/handshake.cc b/src/ssl/handshake.cc
index 8d5a23872..b9ac70dfe 100644
--- a/src/ssl/handshake.cc
+++ b/src/ssl/handshake.cc
@@ -109,6 +109,25 @@
* Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
* ECC cipher suite support in OpenSSL originally developed by
* SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project. */
+/* ====================================================================
+ * Copyright 2020 Apple Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the “Software”),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
#include <openssl/ssl.h>
@@ -150,6 +169,7 @@ SSL_HANDSHAKE::SSL_HANDSHAKE(SSL *ssl_arg)
cert_compression_negotiated(false),
apply_jdk11_workaround(false),
can_release_private_key(false),
+ server_certificate_type_negotiated(false),
channel_id_negotiated(false) {
assert(ssl);
@@ -365,7 +385,21 @@ enum ssl_verify_result_t ssl_verify_peer_cert(SSL_HANDSHAKE *hs) {
uint8_t alert = SSL_AD_CERTIFICATE_UNKNOWN;
enum ssl_verify_result_t ret;
- if (hs->config->custom_verify_callback != nullptr) {
+ if (hs->server_certificate_type_negotiated &&
+ hs->server_certificate_type == TLSEXT_CERTIFICATETYPE_RAW_PUBLIC_KEY) {
+ ret = ssl_verify_invalid;
+ EVP_PKEY *peer_pubkey = hs->peer_pubkey.get();
+ CBS spki = MakeConstSpan(ssl->config->server_raw_public_key_certificate);
+ EVP_PKEY *pubkey = EVP_parse_public_key(&spki);
+ if (!pubkey) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+ alert = SSL_AD_INTERNAL_ERROR;
+ } else if (EVP_PKEY_cmp(peer_pubkey, pubkey) == 1 /* Equal */) {
+ ret = ssl_verify_ok;
+ } else {
+ alert = SSL_AD_BAD_CERTIFICATE;
+ }
+ } else if (hs->config->custom_verify_callback != nullptr) {
ret = hs->config->custom_verify_callback(ssl, &alert);
switch (ret) {
case ssl_verify_ok:
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index 1e6da2153..f04888384 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -138,6 +138,25 @@
* OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR
* OTHERWISE.
*/
+/* ====================================================================
+ * Copyright 2020 Apple Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the “Software”),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
#ifndef OPENSSL_HEADER_SSL_INTERNAL_H
#define OPENSSL_HEADER_SSL_INTERNAL_H
@@ -1286,6 +1305,8 @@ int ssl_write_buffer_flush(SSL *ssl);
// configured.
bool ssl_has_certificate(const SSL_HANDSHAKE *hs);
+bool ssl_has_raw_public_key_certificate(const SSL_HANDSHAKE *hs);
+
// ssl_parse_cert_chain parses a certificate list from |cbs| in the format used
// by a TLS Certificate message. On success, it advances |cbs| and returns
// true. Otherwise, it returns false and sets |*out_alert| to an alert to send
@@ -1887,6 +1908,8 @@ struct SSL_HANDSHAKE {
// |cert_compression_negotiated| is true.
uint16_t cert_compression_alg_id;
+ uint8_t server_certificate_type;
+
// ech_hpke_ctx is the HPKE context used in ECH. On the server, it is
// initialized if |ech_status| is |ssl_ech_accepted|. On the client, it is
// initialized if |selected_ech_config| is not nullptr.
@@ -2037,6 +2060,8 @@ struct SSL_HANDSHAKE {
// cert_compression_negotiated is true iff |cert_compression_alg_id| is valid.
bool cert_compression_negotiated : 1;
+ bool server_certificate_type_negotiated : 1;
+
// apply_jdk11_workaround is true if the peer is probably a JDK 11 client
// which implemented TLS 1.3 incorrectly.
bool apply_jdk11_workaround : 1;
@@ -3049,6 +3074,9 @@ struct SSL_CONFIG {
// along with their corresponding ALPS values.
GrowableArray<ALPSConfig> alps_configs;
+ Array<uint8_t> server_certificate_type_list;
+ Array<uint8_t> server_raw_public_key_certificate;
+
// Contains the QUIC transport params that this endpoint will send.
Array<uint8_t> quic_transport_params;
@@ -3648,6 +3676,9 @@ struct ssl_ctx_st {
// format.
bssl::Array<uint8_t> alpn_client_proto_list;
+ bssl::Array<uint8_t> server_certificate_type_list;
+ bssl::Array<uint8_t> server_raw_public_key_certificate;
+
// SRTP profiles we are willing to do from RFC 5764
bssl::UniquePtr<STACK_OF(SRTP_PROTECTION_PROFILE)> srtp_profiles;
diff --git a/src/ssl/ssl_cert.cc b/src/ssl/ssl_cert.cc
index aa46a8bb6..d90840fce 100644
--- a/src/ssl/ssl_cert.cc
+++ b/src/ssl/ssl_cert.cc
@@ -111,6 +111,25 @@
* Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
* ECC cipher suite support in OpenSSL originally developed by
* SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project. */
+/* ====================================================================
+ * Copyright 2020 Apple Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the “Software”),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
#include <openssl/ssl.h>
@@ -302,6 +321,25 @@ static int cert_set_chain_and_key(
return 1;
}
+static int cert_set_key(
+ CERT *cert,
+ EVP_PKEY *privkey, const SSL_PRIVATE_KEY_METHOD *privkey_method) {
+ if (privkey == NULL && privkey_method == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+
+ if (privkey != NULL && privkey_method != NULL) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_CANNOT_HAVE_BOTH_PRIVKEY_AND_METHOD);
+ return 0;
+ }
+
+ cert->privatekey = UpRef(privkey);
+ cert->key_method = privkey_method;
+
+ return 1;
+}
+
bool ssl_set_cert(CERT *cert, UniquePtr<CRYPTO_BUFFER> buffer) {
switch (check_leaf_cert_and_privkey(buffer.get(), cert->privatekey.get())) {
case leaf_cert_and_privkey_error:
@@ -343,6 +381,12 @@ bool ssl_has_certificate(const SSL_HANDSHAKE *hs) {
ssl_has_private_key(hs);
}
+bool ssl_has_raw_public_key_certificate(const SSL_HANDSHAKE *hs) {
+ return hs->server_certificate_type_negotiated &&
+ hs->server_certificate_type == TLSEXT_CERTIFICATETYPE_RAW_PUBLIC_KEY &&
+ ssl_has_private_key(hs);
+}
+
bool ssl_parse_cert_chain(uint8_t *out_alert,
UniquePtr<STACK_OF(CRYPTO_BUFFER)> *out_chain,
UniquePtr<EVP_PKEY> *out_pubkey,
@@ -721,11 +765,20 @@ bool ssl_check_leaf_certificate(SSL_HANDSHAKE *hs, EVP_PKEY *pkey,
bool ssl_on_certificate_selected(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
- if (!ssl_has_certificate(hs)) {
+ if (!ssl_has_certificate(hs) &&
+ !ssl_has_raw_public_key_certificate(hs)) {
// Nothing to do.
return true;
}
+ if (ssl_has_raw_public_key_certificate(hs)) {
+ CBS spki = MakeConstSpan(
+ ssl->config->server_raw_public_key_certificate.data(),
+ ssl->config->server_raw_public_key_certificate.size());
+ hs->local_pubkey = UniquePtr<EVP_PKEY>(EVP_parse_public_key(&spki));
+ return hs->local_pubkey != NULL;
+ }
+
if (!ssl->ctx->x509_method->ssl_auto_chain_if_needed(hs)) {
return false;
}
@@ -880,6 +933,15 @@ int SSL_set_chain_and_key(SSL *ssl, CRYPTO_BUFFER *const *certs,
privkey, privkey_method);
}
+int SSL_set_nullchain_and_key(SSL *ssl,
+ EVP_PKEY *privkey,
+ const SSL_PRIVATE_KEY_METHOD *privkey_method) {
+ if (!ssl->config) {
+ return 0;
+ }
+ return cert_set_key(ssl->config->cert.get(), privkey, privkey_method);
+}
+
int SSL_CTX_set_chain_and_key(SSL_CTX *ctx, CRYPTO_BUFFER *const *certs,
size_t num_certs, EVP_PKEY *privkey,
const SSL_PRIVATE_KEY_METHOD *privkey_method) {
@@ -887,6 +949,12 @@ int SSL_CTX_set_chain_and_key(SSL_CTX *ctx, CRYPTO_BUFFER *const *certs,
privkey_method);
}
+int SSL_CTX_set_nullchain_and_key(SSL_CTX *ctx,
+ EVP_PKEY *privkey,
+ const SSL_PRIVATE_KEY_METHOD *privkey_method) {
+ return cert_set_key(ctx->cert.get(), privkey, privkey_method);
+}
+
const STACK_OF(CRYPTO_BUFFER)* SSL_CTX_get0_chain(const SSL_CTX *ctx) {
return ctx->cert->chain.get();
}
diff --git a/src/ssl/ssl_lib.cc b/src/ssl/ssl_lib.cc
index 838761af5..e4f1a12b7 100644
--- a/src/ssl/ssl_lib.cc
+++ b/src/ssl/ssl_lib.cc
@@ -137,6 +137,25 @@
* SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY
* OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR
* OTHERWISE. */
+/* ====================================================================
+ * Copyright 2020 Apple Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the “Software”),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
#include <openssl/ssl.h>
@@ -687,6 +706,11 @@ SSL *SSL_new(SSL_CTX *ctx) {
ssl->config->handoff = ctx->handoff;
ssl->quic_method = ctx->quic_method;
+ ssl->config->server_certificate_type_list.CopyFrom(
+ ctx->server_certificate_type_list);
+ ssl->config->server_raw_public_key_certificate.CopyFrom(
+ ctx->server_raw_public_key_certificate);
+
if (!ssl->method->ssl_new(ssl.get()) ||
!ssl->ctx->x509_method->ssl_new(ssl->s3->hs.get())) {
return nullptr;
@@ -3140,6 +3164,53 @@ int SSL_CTX_set_tlsext_status_arg(SSL_CTX *ctx, void *arg) {
return 1;
}
+int SSL_CTX_set_server_raw_public_key_certificate(SSL_CTX *ctx,
+ const uint8_t *raw_public_key, unsigned raw_public_key_len) {
+ if (!ctx->server_raw_public_key_certificate.CopyFrom(
+ MakeConstSpan(raw_public_key, raw_public_key_len))) {
+ return 0; /* Failure */
+ }
+
+ if (!ctx->server_certificate_type_list.Init(1)) {
+ return 0;
+ }
+ ctx->server_certificate_type_list[0] = TLSEXT_CERTIFICATETYPE_RAW_PUBLIC_KEY;
+
+ return 1; /* Success */
+}
+
+int SSL_CTX_has_server_raw_public_key_certificate(SSL_CTX *ctx) {
+ return !ctx->server_raw_public_key_certificate.empty();
+}
+
+int SSL_set_server_raw_public_key_certificate(SSL *ssl,
+ const uint8_t *raw_public_key, unsigned raw_public_key_len) {
+ if (!ssl->config) {
+ return 0; /* Failure */
+ }
+
+ if (!ssl->config->server_raw_public_key_certificate.CopyFrom(
+ MakeConstSpan(raw_public_key, raw_public_key_len))) {
+ return 0;
+ }
+
+ if (!ssl->config->server_certificate_type_list.Init(1)) {
+ return 0;
+ }
+ ssl->config->server_certificate_type_list[0] =
+ TLSEXT_CERTIFICATETYPE_RAW_PUBLIC_KEY;
+
+ return 1; /* Success */
+}
+
+int SSL_has_server_raw_public_key_certificate(SSL *ssl) {
+ if (!ssl->config) {
+ return 0; /* Failure */
+ }
+
+ return !ssl->config->server_raw_public_key_certificate.empty();
+}
+
namespace fips202205 {
// (References are to SP 800-52r2):
diff --git a/src/ssl/tls13_both.cc b/src/ssl/tls13_both.cc
index 5ab5a1c93..79135613e 100644
--- a/src/ssl/tls13_both.cc
+++ b/src/ssl/tls13_both.cc
@@ -11,6 +11,25 @@
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+/* ====================================================================
+ * Copyright 2020 Apple Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the “Software”),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
#include <openssl/ssl.h>
@@ -197,7 +216,16 @@ bool tls13_process_certificate(SSL_HANDSHAKE *hs, const SSLMessage &msg,
return false;
}
- if (sk_CRYPTO_BUFFER_num(certs.get()) == 0) {
+ if (hs->server_certificate_type_negotiated &&
+ hs->server_certificate_type == TLSEXT_CERTIFICATETYPE_RAW_PUBLIC_KEY) {
+ pkey = UniquePtr<EVP_PKEY>(EVP_parse_public_key(&certificate));
+ if (!pkey) {
+ ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+ return false;
+ }
+ }
+ else if (sk_CRYPTO_BUFFER_num(certs.get()) == 0) {
pkey = ssl_cert_parse_pubkey(&certificate);
if (!pkey) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
@@ -299,7 +327,10 @@ bool tls13_process_certificate(SSL_HANDSHAKE *hs, const SSLMessage &msg,
}
if (sk_CRYPTO_BUFFER_num(hs->new_session->certs.get()) == 0) {
- if (!allow_anonymous) {
+ if (!allow_anonymous &&
+ !(hs->server_certificate_type_negotiated &&
+ hs->server_certificate_type ==
+ TLSEXT_CERTIFICATETYPE_RAW_PUBLIC_KEY)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_CERTIFICATE_REQUIRED);
return false;
@@ -416,6 +447,20 @@ bool tls13_add_certificate(SSL_HANDSHAKE *hs) {
return false;
}
+ if (hs->server_certificate_type_negotiated &&
+ hs->server_certificate_type == TLSEXT_CERTIFICATETYPE_RAW_PUBLIC_KEY) {
+ CBB leaf, extensions;
+ if (!CBB_add_u24_length_prefixed(&certificate_list, &leaf) ||
+ !CBB_add_bytes(&leaf,
+ ssl->config->server_raw_public_key_certificate.data(),
+ ssl->config->server_raw_public_key_certificate.size()) ||
+ !CBB_add_u16_length_prefixed(&certificate_list, &extensions)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ return false;
+ }
+ return ssl_add_message_cbb(ssl, cbb.get());
+ }
+
if (!ssl_has_certificate(hs)) {
return ssl_add_message_cbb(ssl, cbb.get());
}
diff --git a/src/ssl/tls13_server.cc b/src/ssl/tls13_server.cc
index 9d26f4e00..a92689761 100644
--- a/src/ssl/tls13_server.cc
+++ b/src/ssl/tls13_server.cc
@@ -11,6 +11,25 @@
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+/* ====================================================================
+ * Copyright 2020 Apple Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the “Software”),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
#include <openssl/ssl.h>
@@ -860,7 +879,8 @@ static enum ssl_hs_wait_t do_send_server_hello(SSL_HANDSHAKE *hs) {
// Send the server Certificate message, if necessary.
if (!ssl->s3->session_reused) {
- if (!ssl_has_certificate(hs)) {
+ if (!ssl_has_certificate(hs) &&
+ !ssl_has_raw_public_key_certificate(hs)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET);
return ssl_hs_error;
}

View File

@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
git apply -v --whitespace=fix ../../patches/boring-pq.patch

View File

@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
git apply -v --whitespace=fix ../../patches/rpk.patch

View File

@ -10,6 +10,7 @@
non_upper_case_globals, non_upper_case_globals,
unused_imports unused_imports
)] )]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
use std::convert::TryInto; use std::convert::TryInto;
use std::ffi::c_void; use std::ffi::c_void;

View File

@ -1,27 +1,37 @@
[package] [package]
name = "boring" name = "boring"
version = "2.1.0" version = { workspace = true }
authors = ["Steven Fackler <sfackler@gmail.com>", "Ivan Nikulin <ifaaan@gmail.com>"] authors = ["Steven Fackler <sfackler@gmail.com>", "Ivan Nikulin <ifaaan@gmail.com>"]
license = "Apache-2.0" license = "Apache-2.0"
description = "BoringSSL bindings" description = "BoringSSL bindings"
repository = "https://github.com/cloudflare/boring" repository = { workspace = true }
documentation = "https://docs.rs/boring" documentation = "https://docs.rs/boring"
readme = "README.md" readme = "README.md"
keywords = ["crypto", "tls", "ssl", "dtls"] keywords = ["crypto", "tls", "ssl", "dtls"]
categories = ["cryptography", "api-bindings"] categories = ["cryptography", "api-bindings"]
edition = "2018" edition = { workspace = true }
[dependencies] [package.metadata.docs.rs]
bitflags = "1.0" features = ["rpk", "pq-experimental"]
foreign-types = "0.5" rustdoc-args = ["--cfg", "docsrs"]
lazy_static = "1"
libc = "0.2"
boring-sys = { version = ">=1.1.0,<3.0.0", path = "../boring-sys" }
[dev-dependencies]
hex = "0.4"
rusty-hook = "^0.11"
[features] [features]
# Use a FIPS-validated version of boringssl. # Use a FIPS-validated version of boringssl.
fips = ["boring-sys/fips"] fips = ["boring-sys/fips"]
# Enables Raw public key API (https://datatracker.ietf.org/doc/html/rfc7250)
rpk = ["boring-sys/rpk"]
# Enables experimental post-quantum crypto (https://blog.cloudflare.com/post-quantum-for-all/)
pq-experimental = ["boring-sys/pq-experimental"]
[dependencies]
bitflags = { workspace = true }
foreign-types = { workspace = true }
lazy_static = { workspace = true }
libc = { workspace = true }
boring-sys = { workspace = true }
[dev-dependencies]
hex = { workspace = true }
rusty-hook = { workspace = true }

View File

@ -1,6 +1,73 @@
//! Bindings to BoringSSL //! Bindings to BoringSSL
//! //!
//! This crate provides a safe interface to the BoringSSL cryptography library. //! This crate provides a safe interface to the BoringSSL cryptography library.
//!
//! # Versioning
//!
//! ## Crate versioning
//!
//! The crate and all the related crates (FFI bindings, etc.) are released simultaneously and all
//! bumped to the same version disregard whether particular crate has any API changes or not.
//! However, semantic versioning guarantees still hold, as all the crate versions will be updated
//! based on the crate with most significant changes.
//!
//! ## BoringSSL version
//!
//! By default, the crate aims to statically link with the latest BoringSSL master branch.
//! *Note*: any BoringSSL revision bumps will be released as a major version update of all crates.
//!
//! # Compilation and linking options
//!
//! ## Support for pre-built binaries
//!
//! While this crate can build BoringSSL on its own, you may want to provide pre-built binaries instead.
//! To do so, specify the environment variable `BORING_BSSL_PATH` with the path to the binaries.
//!
//! You can also provide specific headers by setting `BORING_BSSL_INCLUDE_PATH`.
//!
//! _Notes_: The crate will look for headers in the `$BORING_BSSL_INCLUDE_PATH/openssl/` folder, make sure to place your headers there.
//!
//! _Warning_: When providing a different version of BoringSSL make sure to use a compatible one, the crate relies on the presence of certain functions.
//!
//! ## Building with a FIPS-validated module
//!
//! Only BoringCrypto module version `853ca1ea1168dff08011e5d42d94609cc0ca2e27`, as certified with
//! [FIPS 140-2 certificate 4407](https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/4407)
//! is supported by this crate. Support is enabled by this crate's `fips` feature.
//!
//! `boring-sys` comes with a test that FIPS is enabled/disabled depending on the feature flag. You can run it as follows:
//!
//! ```bash
//! $ cargo test --features fips fips::is_enabled
//! ```
//!
//! # Optional patches
//!
//! ## Raw Public Key
//!
//! The crate can be compiled with [RawPublicKey](https://datatracker.ietf.org/doc/html/rfc7250)
//! support by turning on `rpk` compilation feature.
//!
//! ## Experimental post-quantum cryptography
//!
//! The crate can be compiled with [post-quantum cryptography](https://blog.cloudflare.com/post-quantum-for-all/)
//! support by turning on `post-quantum` compilation feature.
//!
//! Upstream BoringSSL support the post-quantum hybrid key agreement `X25519Kyber768Draft00`. Most
//! users should stick to that one. Enabling this feature, adds a few other post-quantum key
//! agreements:
//!
//! - `X25519Kyber768Draft00Old` is the same as `X25519Kyber768Draft00`, but under its old codepoint.
//! -`X25519Kyber512Draft00`. Similar to `X25519Kyber768Draft00`, but uses level 1 parameter set for
//! Kyber. Not recommended. It's useful to test whether the shorter ClientHello upsets fewer middle
//! boxes.
//! - `P256Kyber768Draft00`. Similar again to `X25519Kyber768Draft00`, but uses P256 as classical
//! part. It uses a non-standard codepoint. Not recommended.
//!
//! Presently all these key agreements are deployed by Cloudflare, but we do not guarantee continued
//! support for them.
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#[macro_use] #[macro_use]
extern crate bitflags; extern crate bitflags;

View File

@ -20,9 +20,19 @@ ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
-----END DH PARAMETERS----- -----END DH PARAMETERS-----
"; ";
enum ContextType {
WithMethod(SslMethod),
#[cfg(feature = "rpk")]
Rpk,
}
#[allow(clippy::inconsistent_digit_grouping)] #[allow(clippy::inconsistent_digit_grouping)]
fn ctx(method: SslMethod) -> Result<SslContextBuilder, ErrorStack> { fn ctx(ty: ContextType) -> Result<SslContextBuilder, ErrorStack> {
let mut ctx = SslContextBuilder::new(method)?; let mut ctx = match ty {
ContextType::WithMethod(method) => SslContextBuilder::new(method),
#[cfg(feature = "rpk")]
ContextType::Rpk => SslContextBuilder::new_rpk(),
}?;
let mut opts = SslOptions::ALL let mut opts = SslOptions::ALL
| SslOptions::NO_COMPRESSION | SslOptions::NO_COMPRESSION
@ -64,7 +74,7 @@ impl SslConnector {
/// ///
/// The default configuration is subject to change, and is currently derived from Python. /// The default configuration is subject to change, and is currently derived from Python.
pub fn builder(method: SslMethod) -> Result<SslConnectorBuilder, ErrorStack> { pub fn builder(method: SslMethod) -> Result<SslConnectorBuilder, ErrorStack> {
let mut ctx = ctx(method)?; let mut ctx = ctx(ContextType::WithMethod(method))?;
ctx.set_default_verify_paths()?; ctx.set_default_verify_paths()?;
ctx.set_cipher_list( ctx.set_cipher_list(
"DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK", "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK",
@ -74,6 +84,17 @@ impl SslConnector {
Ok(SslConnectorBuilder(ctx)) Ok(SslConnectorBuilder(ctx))
} }
/// Creates a new builder for TLS connections with raw public key.
#[cfg(feature = "rpk")]
pub fn rpk_builder() -> Result<SslConnectorBuilder, ErrorStack> {
let mut ctx = ctx(ContextType::Rpk)?;
ctx.set_cipher_list(
"DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK",
)?;
Ok(SslConnectorBuilder(ctx))
}
/// Initiates a client-side TLS session on a stream. /// Initiates a client-side TLS session on a stream.
/// ///
/// The domain is used for SNI and hostname verification. /// The domain is used for SNI and hostname verification.
@ -179,7 +200,13 @@ impl ConnectConfiguration {
self.ssl.set_hostname(domain)?; self.ssl.set_hostname(domain)?;
} }
if self.verify_hostname { #[cfg(feature = "rpk")]
let verify_hostname = !self.ssl.ssl_context().is_rpk() && self.verify_hostname;
#[cfg(not(feature = "rpk"))]
let verify_hostname = self.verify_hostname;
if verify_hostname {
setup_verify_hostname(&mut self.ssl, domain)?; setup_verify_hostname(&mut self.ssl, domain)?;
} }
@ -209,6 +236,21 @@ impl DerefMut for ConnectConfiguration {
pub struct SslAcceptor(SslContext); pub struct SslAcceptor(SslContext);
impl SslAcceptor { impl SslAcceptor {
/// Creates a new builder configured to connect to clients that support Raw Public Keys.
#[cfg(feature = "rpk")]
pub fn rpk() -> Result<SslAcceptorBuilder, ErrorStack> {
let mut ctx = ctx(ContextType::Rpk)?;
ctx.set_options(SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1);
let dh = Dh::params_from_pem(FFDHE_2048.as_bytes())?;
ctx.set_tmp_dh(&dh)?;
ctx.set_cipher_list(
"ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:\
ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\
DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"
)?;
Ok(SslAcceptorBuilder(ctx))
}
/// Creates a new builder configured to connect to non-legacy clients. This should generally be /// Creates a new builder configured to connect to non-legacy clients. This should generally be
/// considered a reasonable default choice. /// considered a reasonable default choice.
/// ///
@ -217,7 +259,7 @@ impl SslAcceptor {
/// ///
/// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS
pub fn mozilla_intermediate_v5(method: SslMethod) -> Result<SslAcceptorBuilder, ErrorStack> { pub fn mozilla_intermediate_v5(method: SslMethod) -> Result<SslAcceptorBuilder, ErrorStack> {
let mut ctx = ctx(method)?; let mut ctx = ctx(ContextType::WithMethod(method))?;
ctx.set_options(SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1); ctx.set_options(SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1);
let dh = Dh::params_from_pem(FFDHE_2048.as_bytes())?; let dh = Dh::params_from_pem(FFDHE_2048.as_bytes())?;
ctx.set_tmp_dh(&dh)?; ctx.set_tmp_dh(&dh)?;
@ -238,7 +280,7 @@ impl SslAcceptor {
/// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS
// FIXME remove in next major version // FIXME remove in next major version
pub fn mozilla_intermediate(method: SslMethod) -> Result<SslAcceptorBuilder, ErrorStack> { pub fn mozilla_intermediate(method: SslMethod) -> Result<SslAcceptorBuilder, ErrorStack> {
let mut ctx = ctx(method)?; let mut ctx = ctx(ContextType::WithMethod(method))?;
ctx.set_options(SslOptions::CIPHER_SERVER_PREFERENCE); ctx.set_options(SslOptions::CIPHER_SERVER_PREFERENCE);
ctx.set_options(SslOptions::NO_TLSV1_3); ctx.set_options(SslOptions::NO_TLSV1_3);
let dh = Dh::params_from_pem(FFDHE_2048.as_bytes())?; let dh = Dh::params_from_pem(FFDHE_2048.as_bytes())?;
@ -264,7 +306,7 @@ impl SslAcceptor {
/// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS
// FIXME remove in next major version // FIXME remove in next major version
pub fn mozilla_modern(method: SslMethod) -> Result<SslAcceptorBuilder, ErrorStack> { pub fn mozilla_modern(method: SslMethod) -> Result<SslAcceptorBuilder, ErrorStack> {
let mut ctx = ctx(method)?; let mut ctx = ctx(ContextType::WithMethod(method))?;
ctx.set_options( ctx.set_options(
SslOptions::CIPHER_SERVER_PREFERENCE | SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1, SslOptions::CIPHER_SERVER_PREFERENCE | SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1,
); );

View File

@ -239,6 +239,12 @@ impl SslMethod {
unsafe { SslMethod(TLS_method()) } unsafe { SslMethod(TLS_method()) }
} }
/// Same as `tls`, but doesn't create X509 for certificates.
#[cfg(feature = "rpk")]
pub fn tls_with_buffer() -> SslMethod {
unsafe { SslMethod(ffi::TLS_with_buffers_method()) }
}
/// Support all versions of the DTLS protocol. /// Support all versions of the DTLS protocol.
/// ///
/// This corresponds to `DTLS_method` on OpenSSL 1.1.0 and `DTLSv1_method` /// This corresponds to `DTLS_method` on OpenSSL 1.1.0 and `DTLSv1_method`
@ -413,6 +419,11 @@ lazy_static! {
static ref SESSION_CTX_INDEX: Index<Ssl, SslContext> = Ssl::new_ex_index().unwrap(); static ref SESSION_CTX_INDEX: Index<Ssl, SslContext> = Ssl::new_ex_index().unwrap();
} }
#[cfg(feature = "rpk")]
lazy_static! {
static ref RPK_FLAG_INDEX: Index<SslContext, bool> = SslContext::new_ex_index().unwrap();
}
unsafe extern "C" fn free_data_box<T>( unsafe extern "C" fn free_data_box<T>(
_parent: *mut c_void, _parent: *mut c_void,
ptr: *mut c_void, ptr: *mut c_void,
@ -621,6 +632,18 @@ impl SslCurve {
pub const SECP521R1: SslCurve = SslCurve(ffi::NID_secp521r1); pub const SECP521R1: SslCurve = SslCurve(ffi::NID_secp521r1);
pub const X25519: SslCurve = SslCurve(ffi::NID_X25519); pub const X25519: SslCurve = SslCurve(ffi::NID_X25519);
#[cfg(not(feature = "fips"))]
pub const X25519_KYBER768_DRAFT00: SslCurve = SslCurve(ffi::NID_X25519Kyber768Draft00);
#[cfg(feature = "pq-experimental")]
pub const X25519_KYBER768_DRAFT00_OLD: SslCurve = SslCurve(ffi::NID_X25519Kyber768Draft00Old);
#[cfg(feature = "pq-experimental")]
pub const X25519_KYBER512_DRAFT00: SslCurve = SslCurve(ffi::NID_X25519Kyber512Draft00);
#[cfg(feature = "pq-experimental")]
pub const P256_KYBER768_DRAFT00: SslCurve = SslCurve(ffi::NID_P256Kyber768Draft00);
} }
/// A standard implementation of protocol selection for Application Layer Protocol Negotiation /// A standard implementation of protocol selection for Application Layer Protocol Negotiation
@ -656,8 +679,65 @@ pub fn select_next_proto<'a>(server: &[u8], client: &'a [u8]) -> Option<&'a [u8]
} }
} }
#[cfg(feature = "rpk")]
extern "C" fn rpk_verify_failure_callback(
_ssl: *mut ffi::SSL,
_out_alert: *mut u8,
) -> ffi::ssl_verify_result_t {
// Always verify the peer.
ffi::ssl_verify_result_t::ssl_verify_invalid
}
/// A builder for `SslContext`s. /// A builder for `SslContext`s.
pub struct SslContextBuilder(SslContext); pub struct SslContextBuilder {
ctx: SslContext,
#[cfg(feature = "rpk")]
is_rpk: bool,
}
#[cfg(feature = "rpk")]
impl SslContextBuilder {
/// Creates a new `SslContextBuilder` to be used with Raw Public Key.
///
/// This corresponds to [`SSL_CTX_new`].
///
/// [`SSL_CTX_new`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_new.html
pub fn new_rpk() -> Result<SslContextBuilder, ErrorStack> {
unsafe {
init();
let ctx = cvt_p(ffi::SSL_CTX_new(SslMethod::tls_with_buffer().as_ptr()))?;
Ok(SslContextBuilder::from_ptr(ctx, true))
}
}
/// Sets raw public key certificate in DER format.
pub fn set_rpk_certificate(&mut self, cert: &[u8]) -> Result<(), ErrorStack> {
unsafe {
cvt(ffi::SSL_CTX_set_server_raw_public_key_certificate(
self.as_ptr(),
cert.as_ptr(),
cert.len() as u32,
))
.map(|_| ())
}
}
/// Sets RPK null chain private key.
pub fn set_null_chain_private_key<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
where
T: HasPrivate,
{
unsafe {
cvt(ffi::SSL_CTX_set_nullchain_and_key(
self.as_ptr(),
key.as_ptr(),
ptr::null_mut(),
))
.map(|_| ())
}
}
}
impl SslContextBuilder { impl SslContextBuilder {
/// Creates a new `SslContextBuilder`. /// Creates a new `SslContextBuilder`.
@ -670,22 +750,48 @@ impl SslContextBuilder {
init(); init();
let ctx = cvt_p(ffi::SSL_CTX_new(method.as_ptr()))?; let ctx = cvt_p(ffi::SSL_CTX_new(method.as_ptr()))?;
#[cfg(feature = "rpk")]
{
Ok(SslContextBuilder::from_ptr(ctx, false))
}
#[cfg(not(feature = "rpk"))]
{
Ok(SslContextBuilder::from_ptr(ctx)) Ok(SslContextBuilder::from_ptr(ctx))
} }
} }
}
/// Creates an `SslContextBuilder` from a pointer to a raw OpenSSL value. /// Creates an `SslContextBuilder` from a pointer to a raw OpenSSL value.
/// ///
/// # Safety /// # Safety
/// ///
/// The caller must ensure that the pointer is valid and uniquely owned by the builder. /// The caller must ensure that the pointer is valid and uniquely owned by the builder.
#[cfg(feature = "rpk")]
pub unsafe fn from_ptr(ctx: *mut ffi::SSL_CTX, is_rpk: bool) -> SslContextBuilder {
let ctx = SslContext::from_ptr(ctx);
let mut builder = SslContextBuilder { ctx, is_rpk };
builder.set_ex_data(*RPK_FLAG_INDEX, is_rpk);
builder
}
/// Creates an `SslContextBuilder` from a pointer to a raw OpenSSL value.
///
/// # Safety
///
/// The caller must ensure that the pointer is valid and uniquely owned by the builder.
#[cfg(not(feature = "rpk"))]
pub unsafe fn from_ptr(ctx: *mut ffi::SSL_CTX) -> SslContextBuilder { pub unsafe fn from_ptr(ctx: *mut ffi::SSL_CTX) -> SslContextBuilder {
SslContextBuilder(SslContext::from_ptr(ctx)) SslContextBuilder {
ctx: SslContext::from_ptr(ctx),
}
} }
/// Returns a pointer to the raw OpenSSL value. /// Returns a pointer to the raw OpenSSL value.
pub fn as_ptr(&self) -> *mut ffi::SSL_CTX { pub fn as_ptr(&self) -> *mut ffi::SSL_CTX {
self.0.as_ptr() self.ctx.as_ptr()
} }
/// Configures the certificate verification method for new connections. /// Configures the certificate verification method for new connections.
@ -694,6 +800,9 @@ impl SslContextBuilder {
/// ///
/// [`SSL_CTX_set_verify`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_verify.html /// [`SSL_CTX_set_verify`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_verify.html
pub fn set_verify(&mut self, mode: SslVerifyMode) { pub fn set_verify(&mut self, mode: SslVerifyMode) {
#[cfg(feature = "rpk")]
assert!(!self.is_rpk, "This API is not supported for RPK");
unsafe { unsafe {
ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits as c_int, None); ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits as c_int, None);
} }
@ -713,6 +822,9 @@ impl SslContextBuilder {
where where
F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send, F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send,
{ {
#[cfg(feature = "rpk")]
assert!(!self.is_rpk, "This API is not supported for RPK");
unsafe { unsafe {
self.set_ex_data(SslContext::cached_ex_index::<F>(), verify); self.set_ex_data(SslContext::cached_ex_index::<F>(), verify);
ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits as c_int, Some(raw_verify::<F>)); ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits as c_int, Some(raw_verify::<F>));
@ -757,6 +869,9 @@ impl SslContextBuilder {
/// ///
/// [`SSL_CTX_set_verify_depth`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_verify_depth.html /// [`SSL_CTX_set_verify_depth`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_verify_depth.html
pub fn set_verify_depth(&mut self, depth: u32) { pub fn set_verify_depth(&mut self, depth: u32) {
#[cfg(feature = "rpk")]
assert!(!self.is_rpk, "This API is not supported for RPK");
unsafe { unsafe {
ffi::SSL_CTX_set_verify_depth(self.as_ptr(), depth as c_int); ffi::SSL_CTX_set_verify_depth(self.as_ptr(), depth as c_int);
} }
@ -768,6 +883,9 @@ impl SslContextBuilder {
/// ///
/// [`SSL_CTX_set0_verify_cert_store`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set0_verify_cert_store.html /// [`SSL_CTX_set0_verify_cert_store`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set0_verify_cert_store.html
pub fn set_verify_cert_store(&mut self, cert_store: X509Store) -> Result<(), ErrorStack> { pub fn set_verify_cert_store(&mut self, cert_store: X509Store) -> Result<(), ErrorStack> {
#[cfg(feature = "rpk")]
assert!(!self.is_rpk, "This API is not supported for RPK");
unsafe { unsafe {
let ptr = cert_store.as_ptr(); let ptr = cert_store.as_ptr();
cvt(ffi::SSL_CTX_set0_verify_cert_store(self.as_ptr(), ptr) as c_int)?; cvt(ffi::SSL_CTX_set0_verify_cert_store(self.as_ptr(), ptr) as c_int)?;
@ -783,6 +901,9 @@ impl SslContextBuilder {
/// ///
/// [`SSL_CTX_set_cert_store`]: https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_cert_store.html /// [`SSL_CTX_set_cert_store`]: https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_cert_store.html
pub fn set_cert_store(&mut self, cert_store: X509Store) { pub fn set_cert_store(&mut self, cert_store: X509Store) {
#[cfg(feature = "rpk")]
assert!(!self.is_rpk, "This API is not supported for RPK");
unsafe { unsafe {
ffi::SSL_CTX_set_cert_store(self.as_ptr(), cert_store.as_ptr()); ffi::SSL_CTX_set_cert_store(self.as_ptr(), cert_store.as_ptr());
mem::forget(cert_store); mem::forget(cert_store);
@ -842,6 +963,9 @@ impl SslContextBuilder {
/// ///
/// [`SSL_CTX_set_default_verify_paths`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_default_verify_paths.html /// [`SSL_CTX_set_default_verify_paths`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_default_verify_paths.html
pub fn set_default_verify_paths(&mut self) -> Result<(), ErrorStack> { pub fn set_default_verify_paths(&mut self) -> Result<(), ErrorStack> {
#[cfg(feature = "rpk")]
assert!(!self.is_rpk, "This API is not supported for RPK");
unsafe { cvt(ffi::SSL_CTX_set_default_verify_paths(self.as_ptr())).map(|_| ()) } unsafe { cvt(ffi::SSL_CTX_set_default_verify_paths(self.as_ptr())).map(|_| ()) }
} }
@ -853,6 +977,9 @@ impl SslContextBuilder {
/// ///
/// [`SSL_CTX_load_verify_locations`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_load_verify_locations.html /// [`SSL_CTX_load_verify_locations`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_load_verify_locations.html
pub fn set_ca_file<P: AsRef<Path>>(&mut self, file: P) -> Result<(), ErrorStack> { pub fn set_ca_file<P: AsRef<Path>>(&mut self, file: P) -> Result<(), ErrorStack> {
#[cfg(feature = "rpk")]
assert!(!self.is_rpk, "This API is not supported for RPK");
let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap(); let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
unsafe { unsafe {
cvt(ffi::SSL_CTX_load_verify_locations( cvt(ffi::SSL_CTX_load_verify_locations(
@ -873,6 +1000,9 @@ impl SslContextBuilder {
/// ///
/// [`SSL_CTX_set_client_CA_list`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_client_CA_list.html /// [`SSL_CTX_set_client_CA_list`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_client_CA_list.html
pub fn set_client_ca_list(&mut self, list: Stack<X509Name>) { pub fn set_client_ca_list(&mut self, list: Stack<X509Name>) {
#[cfg(feature = "rpk")]
assert!(!self.is_rpk, "This API is not supported for RPK");
unsafe { unsafe {
ffi::SSL_CTX_set_client_CA_list(self.as_ptr(), list.as_ptr()); ffi::SSL_CTX_set_client_CA_list(self.as_ptr(), list.as_ptr());
mem::forget(list); mem::forget(list);
@ -886,6 +1016,9 @@ impl SslContextBuilder {
/// ///
/// [`SSL_CTX_add_client_CA`]: https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_client_CA_list.html /// [`SSL_CTX_add_client_CA`]: https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_client_CA_list.html
pub fn add_client_ca(&mut self, cacert: &X509Ref) -> Result<(), ErrorStack> { pub fn add_client_ca(&mut self, cacert: &X509Ref) -> Result<(), ErrorStack> {
#[cfg(feature = "rpk")]
assert!(!self.is_rpk, "This API is not supported for RPK");
unsafe { cvt(ffi::SSL_CTX_add_client_CA(self.as_ptr(), cacert.as_ptr())).map(|_| ()) } unsafe { cvt(ffi::SSL_CTX_add_client_CA(self.as_ptr(), cacert.as_ptr())).map(|_| ()) }
} }
@ -927,6 +1060,9 @@ impl SslContextBuilder {
file: P, file: P,
file_type: SslFiletype, file_type: SslFiletype,
) -> Result<(), ErrorStack> { ) -> Result<(), ErrorStack> {
#[cfg(feature = "rpk")]
assert!(!self.is_rpk, "This API is not supported for RPK");
let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap(); let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
unsafe { unsafe {
cvt(ffi::SSL_CTX_use_certificate_file( cvt(ffi::SSL_CTX_use_certificate_file(
@ -981,6 +1117,9 @@ impl SslContextBuilder {
/// ///
/// [`SSL_CTX_add_extra_chain_cert`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_add_extra_chain_cert.html /// [`SSL_CTX_add_extra_chain_cert`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_add_extra_chain_cert.html
pub fn add_extra_chain_cert(&mut self, cert: X509) -> Result<(), ErrorStack> { pub fn add_extra_chain_cert(&mut self, cert: X509) -> Result<(), ErrorStack> {
#[cfg(feature = "rpk")]
assert!(!self.is_rpk, "This API is not supported for RPK");
unsafe { unsafe {
cvt(ffi::SSL_CTX_add_extra_chain_cert(self.as_ptr(), cert.as_ptr()) as c_int)?; cvt(ffi::SSL_CTX_add_extra_chain_cert(self.as_ptr(), cert.as_ptr()) as c_int)?;
mem::forget(cert); mem::forget(cert);
@ -1261,6 +1400,9 @@ impl SslContextBuilder {
/// ///
/// [`SSL_CTX_get_cert_store`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_cert_store.html /// [`SSL_CTX_get_cert_store`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_cert_store.html
pub fn cert_store(&self) -> &X509StoreBuilderRef { pub fn cert_store(&self) -> &X509StoreBuilderRef {
#[cfg(feature = "rpk")]
assert!(!self.is_rpk, "This API is not supported for RPK");
unsafe { X509StoreBuilderRef::from_ptr(ffi::SSL_CTX_get_cert_store(self.as_ptr())) } unsafe { X509StoreBuilderRef::from_ptr(ffi::SSL_CTX_get_cert_store(self.as_ptr())) }
} }
@ -1270,6 +1412,9 @@ impl SslContextBuilder {
/// ///
/// [`SSL_CTX_get_cert_store`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_cert_store.html /// [`SSL_CTX_get_cert_store`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_cert_store.html
pub fn cert_store_mut(&mut self) -> &mut X509StoreBuilderRef { pub fn cert_store_mut(&mut self) -> &mut X509StoreBuilderRef {
#[cfg(feature = "rpk")]
assert!(!self.is_rpk, "This API is not supported for RPK");
unsafe { X509StoreBuilderRef::from_ptr_mut(ffi::SSL_CTX_get_cert_store(self.as_ptr())) } unsafe { X509StoreBuilderRef::from_ptr_mut(ffi::SSL_CTX_get_cert_store(self.as_ptr())) }
} }
@ -1571,7 +1716,7 @@ impl SslContextBuilder {
/// Consumes the builder, returning a new `SslContext`. /// Consumes the builder, returning a new `SslContext`.
pub fn build(self) -> SslContext { pub fn build(self) -> SslContext {
self.0 self.ctx
} }
} }
@ -1650,6 +1795,9 @@ impl SslContextRef {
/// ///
/// [`SSL_CTX_get0_certificate`]: https://www.openssl.org/docs/man1.1.0/ssl/ssl.html /// [`SSL_CTX_get0_certificate`]: https://www.openssl.org/docs/man1.1.0/ssl/ssl.html
pub fn certificate(&self) -> Option<&X509Ref> { pub fn certificate(&self) -> Option<&X509Ref> {
#[cfg(feature = "rpk")]
assert!(!self.is_rpk(), "This API is not supported for RPK");
unsafe { unsafe {
let ptr = ffi::SSL_CTX_get0_certificate(self.as_ptr()); let ptr = ffi::SSL_CTX_get0_certificate(self.as_ptr());
if ptr.is_null() { if ptr.is_null() {
@ -1682,6 +1830,9 @@ impl SslContextRef {
/// ///
/// [`SSL_CTX_get_cert_store`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_cert_store.html /// [`SSL_CTX_get_cert_store`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_cert_store.html
pub fn cert_store(&self) -> &X509StoreRef { pub fn cert_store(&self) -> &X509StoreRef {
#[cfg(feature = "rpk")]
assert!(!self.is_rpk(), "This API is not supported for RPK");
unsafe { X509StoreRef::from_ptr(ffi::SSL_CTX_get_cert_store(self.as_ptr())) } unsafe { X509StoreRef::from_ptr(ffi::SSL_CTX_get_cert_store(self.as_ptr())) }
} }
@ -1764,9 +1915,18 @@ impl SslContextRef {
/// [`SslContextBuilder::set_verify`]: struct.SslContextBuilder.html#method.set_verify /// [`SslContextBuilder::set_verify`]: struct.SslContextBuilder.html#method.set_verify
/// [`SSL_CTX_get_verify_mode`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_get_verify_mode.html /// [`SSL_CTX_get_verify_mode`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_get_verify_mode.html
pub fn verify_mode(&self) -> SslVerifyMode { pub fn verify_mode(&self) -> SslVerifyMode {
#[cfg(feature = "rpk")]
assert!(!self.is_rpk(), "This API is not supported for RPK");
let mode = unsafe { ffi::SSL_CTX_get_verify_mode(self.as_ptr()) }; let mode = unsafe { ffi::SSL_CTX_get_verify_mode(self.as_ptr()) };
SslVerifyMode::from_bits(mode).expect("SSL_CTX_get_verify_mode returned invalid mode") SslVerifyMode::from_bits(mode).expect("SSL_CTX_get_verify_mode returned invalid mode")
} }
/// Returns `true` if context was created for Raw Public Key verification
#[cfg(feature = "rpk")]
pub fn is_rpk(&self) -> bool {
self.ex_data(*RPK_FLAG_INDEX).copied().unwrap_or_default()
}
} }
#[cfg(not(feature = "fips"))] #[cfg(not(feature = "fips"))]
@ -2176,16 +2336,40 @@ impl Ssl {
where where
S: Read + Write, S: Read + Write,
{ {
#[cfg(feature = "rpk")]
{
let ctx = self.ssl_context();
if ctx.is_rpk() {
unsafe {
ffi::SSL_CTX_set_custom_verify(
ctx.as_ptr(),
SslVerifyMode::PEER.bits(),
Some(rpk_verify_failure_callback),
);
}
}
}
SslStreamBuilder::new(self, stream).accept() SslStreamBuilder::new(self, stream).accept()
} }
} }
impl fmt::Debug for SslRef { impl fmt::Debug for SslRef {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Ssl") let mut builder = fmt.debug_struct("Ssl");
.field("state", &self.state_string_long())
.field("verify_result", &self.verify_result()) builder.field("state", &self.state_string_long());
.finish()
#[cfg(feature = "rpk")]
if !self.ssl_context().is_rpk() {
builder.field("verify_result", &self.verify_result());
}
#[cfg(not(feature = "rpk"))]
builder.field("verify_result", &self.verify_result());
builder.finish()
} }
} }
@ -2215,6 +2399,12 @@ impl SslRef {
/// [`SslContextBuilder::set_verify`]: struct.SslContextBuilder.html#method.set_verify /// [`SslContextBuilder::set_verify`]: struct.SslContextBuilder.html#method.set_verify
/// [`SSL_set_verify`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_verify.html /// [`SSL_set_verify`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_verify.html
pub fn set_verify(&mut self, mode: SslVerifyMode) { pub fn set_verify(&mut self, mode: SslVerifyMode) {
#[cfg(feature = "rpk")]
assert!(
!self.ssl_context().is_rpk(),
"This API is not supported for RPK"
);
unsafe { ffi::SSL_set_verify(self.as_ptr(), mode.bits as c_int, None) } unsafe { ffi::SSL_set_verify(self.as_ptr(), mode.bits as c_int, None) }
} }
@ -2224,6 +2414,12 @@ impl SslRef {
/// ///
/// [`SSL_get_verify_mode`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_get_verify_mode.html /// [`SSL_get_verify_mode`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_get_verify_mode.html
pub fn verify_mode(&self) -> SslVerifyMode { pub fn verify_mode(&self) -> SslVerifyMode {
#[cfg(feature = "rpk")]
assert!(
!self.ssl_context().is_rpk(),
"This API is not supported for RPK"
);
let mode = unsafe { ffi::SSL_get_verify_mode(self.as_ptr()) }; let mode = unsafe { ffi::SSL_get_verify_mode(self.as_ptr()) };
SslVerifyMode::from_bits(mode).expect("SSL_get_verify_mode returned invalid mode") SslVerifyMode::from_bits(mode).expect("SSL_get_verify_mode returned invalid mode")
} }
@ -2238,6 +2434,12 @@ impl SslRef {
where where
F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send, F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send,
{ {
#[cfg(feature = "rpk")]
assert!(
!self.ssl_context().is_rpk(),
"This API is not supported for RPK"
);
unsafe { unsafe {
// this needs to be in an Arc since the callback can register a new callback! // this needs to be in an Arc since the callback can register a new callback!
self.set_ex_data(Ssl::cached_ex_index(), Arc::new(verify)); self.set_ex_data(Ssl::cached_ex_index(), Arc::new(verify));
@ -2356,6 +2558,12 @@ impl SslRef {
/// ///
/// [`SSL_get_peer_certificate`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_peer_certificate.html /// [`SSL_get_peer_certificate`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_peer_certificate.html
pub fn peer_certificate(&self) -> Option<X509> { pub fn peer_certificate(&self) -> Option<X509> {
#[cfg(feature = "rpk")]
assert!(
!self.ssl_context().is_rpk(),
"This API is not supported for RPK"
);
unsafe { unsafe {
let ptr = ffi::SSL_get_peer_certificate(self.as_ptr()); let ptr = ffi::SSL_get_peer_certificate(self.as_ptr());
if ptr.is_null() { if ptr.is_null() {
@ -2375,6 +2583,12 @@ impl SslRef {
/// ///
/// [`SSL_get_peer_cert_chain`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_peer_cert_chain.html /// [`SSL_get_peer_cert_chain`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_peer_cert_chain.html
pub fn peer_cert_chain(&self) -> Option<&StackRef<X509>> { pub fn peer_cert_chain(&self) -> Option<&StackRef<X509>> {
#[cfg(feature = "rpk")]
assert!(
!self.ssl_context().is_rpk(),
"This API is not supported for RPK"
);
unsafe { unsafe {
let ptr = ffi::SSL_get_peer_cert_chain(self.as_ptr()); let ptr = ffi::SSL_get_peer_cert_chain(self.as_ptr());
if ptr.is_null() { if ptr.is_null() {
@ -2391,6 +2605,12 @@ impl SslRef {
/// ///
/// [`SslContext::certificate`]: struct.SslContext.html#method.certificate /// [`SslContext::certificate`]: struct.SslContext.html#method.certificate
pub fn certificate(&self) -> Option<&X509Ref> { pub fn certificate(&self) -> Option<&X509Ref> {
#[cfg(feature = "rpk")]
assert!(
!self.ssl_context().is_rpk(),
"This API is not supported for RPK"
);
unsafe { unsafe {
let ptr = ffi::SSL_get_certificate(self.as_ptr()); let ptr = ffi::SSL_get_certificate(self.as_ptr());
if ptr.is_null() { if ptr.is_null() {
@ -2614,6 +2834,12 @@ impl SslRef {
/// ///
/// [`SSL_get0_param`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_get0_param.html /// [`SSL_get0_param`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_get0_param.html
pub fn param_mut(&mut self) -> &mut X509VerifyParamRef { pub fn param_mut(&mut self) -> &mut X509VerifyParamRef {
#[cfg(feature = "rpk")]
assert!(
!self.ssl_context().is_rpk(),
"This API is not supported for RPK"
);
unsafe { X509VerifyParamRef::from_ptr_mut(ffi::SSL_get0_param(self.as_ptr())) } unsafe { X509VerifyParamRef::from_ptr_mut(ffi::SSL_get0_param(self.as_ptr())) }
} }
@ -2623,6 +2849,12 @@ impl SslRef {
/// ///
/// [`SSL_get_verify_result`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_get_verify_result.html /// [`SSL_get_verify_result`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_get_verify_result.html
pub fn verify_result(&self) -> X509VerifyResult { pub fn verify_result(&self) -> X509VerifyResult {
#[cfg(feature = "rpk")]
assert!(
!self.ssl_context().is_rpk(),
"This API is not supported for RPK"
);
unsafe { X509VerifyResult::from_raw(ffi::SSL_get_verify_result(self.as_ptr()) as c_int) } unsafe { X509VerifyResult::from_raw(ffi::SSL_get_verify_result(self.as_ptr()) as c_int) }
} }

View File

@ -1,33 +1,46 @@
[package] [package]
name = "hyper-boring" name = "hyper-boring"
version = "2.1.2" version = { workspace = true }
authors = ["Steven Fackler <sfackler@gmail.com>", "Ivan Nikulin <ifaaan@gmail.com>"] authors = ["Steven Fackler <sfackler@gmail.com>", "Ivan Nikulin <ifaaan@gmail.com>"]
edition = "2018" edition = { workspace = true }
description = "Hyper TLS support via BoringSSL" description = "Hyper TLS support via BoringSSL"
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"
repository = "https://github.com/cloudflare/boring" repository = { workspace = true }
documentation = "https://docs.rs/hyper-boring" documentation = "https://docs.rs/hyper-boring"
readme = "README.md" readme = "README.md"
exclude = ["test/*"] exclude = ["test/*"]
[package.metadata.docs.rs]
features = ["rpk", "pq-experimental"]
rustdoc-args = ["--cfg", "docsrs"]
[features] [features]
default = ["runtime"] default = ["runtime"]
runtime = ["hyper/runtime"] runtime = ["hyper/runtime"]
# Use a FIPS-validated version of boringssl.
fips = ["tokio-boring/fips"] fips = ["tokio-boring/fips"]
# Enables Raw public key API (https://datatracker.ietf.org/doc/html/rfc7250)
rpk = ["tokio-boring/rpk"]
# Enables experimental post-quantum crypto (https://blog.cloudflare.com/post-quantum-for-all/)
pq-experimental = ["tokio-boring/pq-experimental"]
[dependencies] [dependencies]
antidote = "1.0.0" antidote = { workspace = true }
http = "0.2" http = { workspace = true }
hyper = { version = "0.14", default-features = false, features = ["client"] } hyper = { workspace = true, features = ["client"] }
linked_hash_set = "0.1" linked_hash_set = { workspace = true }
once_cell = "1.0" once_cell = { workspace = true }
boring = { version = ">=1.1.0,<3.0.0", path = "../boring" } boring = { workspace = true }
tokio = "1" tokio = { workspace = true }
tokio-boring = { version = "2", path = "../tokio-boring" } tokio-boring = { workspace = true }
tower-layer = "0.3" tower-layer = { workspace = true }
[dev-dependencies] [dev-dependencies]
hyper = { version = "0.14", features = ["full"] } hyper = { workspace = true }
tokio = { version = "1", features = ["full"] } tokio = { workspace = true }
futures = "0.3" futures = { workspace = true }

View File

@ -1,5 +1,6 @@
//! Hyper SSL support via OpenSSL. //! Hyper SSL support via OpenSSL.
#![warn(missing_docs)] #![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
use crate::cache::{SessionCache, SessionKey}; use crate::cache::{SessionCache, SessionKey};
use antidote::Mutex; use antidote::Mutex;

View File

@ -1,25 +1,37 @@
[package] [package]
name = "tokio-boring" name = "tokio-boring"
version = "2.1.5" version = { workspace = true }
authors = ["Alex Crichton <alex@alexcrichton.com>", "Ivan Nikulin <ifaaan@gmail.com>"] authors = ["Alex Crichton <alex@alexcrichton.com>", "Ivan Nikulin <ifaaan@gmail.com>"]
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"
edition = "2018" edition = { workspace = true }
repository = "https://github.com/cloudflare/boring" repository = { workspace = true }
homepage = "https://github.com/cloudflare/boring" homepage = "https://github.com/cloudflare/boring"
documentation = "https://docs.rs/tokio-boring" documentation = "https://docs.rs/tokio-boring"
description = """ description = """
An implementation of SSL streams for Tokio backed by BoringSSL An implementation of SSL streams for Tokio backed by BoringSSL
""" """
[dependencies] [package.metadata.docs.rs]
boring = { version = ">=1.1.0,<3.0.0", path = "../boring" } features = ["rpk", "pq-experimental"]
boring-sys = { version = ">=1.1.0,<3.0.0", path = "../boring-sys" } rustdoc-args = ["--cfg", "docsrs"]
tokio = "1"
[dev-dependencies]
futures = "0.3"
tokio = { version = "1", features = ["full"] }
anyhow = "1"
[features] [features]
# Use a FIPS-validated version of boringssl.
fips = ["boring/fips"] fips = ["boring/fips"]
# Enables Raw public key API (https://datatracker.ietf.org/doc/html/rfc7250)
rpk = ["boring/rpk"]
# Enables experimental post-quantum crypto (https://blog.cloudflare.com/post-quantum-for-all/)
pq-experimental = ["boring/pq-experimental"]
[dependencies]
boring = { workspace = true }
boring-sys = { workspace = true }
tokio = { workspace = true }
[dev-dependencies]
futures = { workspace = true }
tokio = { workspace = true }
anyhow = { workspace = true }

View File

@ -11,6 +11,7 @@
//! [`boring`] crate, on which this crate is built. Configuration of TLS parameters is still //! [`boring`] crate, on which this crate is built. Configuration of TLS parameters is still
//! primarily done through the [`boring`] crate. //! primarily done through the [`boring`] crate.
#![warn(missing_docs)] #![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
use boring::ssl::{ use boring::ssl::{
self, ConnectConfiguration, ErrorCode, MidHandshakeSslStream, ShutdownResult, SslAcceptor, self, ConnectConfiguration, ErrorCode, MidHandshakeSslStream, ShutdownResult, SslAcceptor,

Binary file not shown.

Binary file not shown.

106
tokio-boring/tests/rpk.rs Normal file
View File

@ -0,0 +1,106 @@
#[cfg(feature = "rpk")]
mod test_rpk {
use boring::pkey::PKey;
use boring::ssl::{SslAcceptor, SslConnector};
use futures::future;
use std::future::Future;
use std::net::SocketAddr;
use std::pin::Pin;
use tokio::io::{AsyncReadExt, AsyncWrite, AsyncWriteExt};
use tokio::net::{TcpListener, TcpStream};
use tokio_boring::{HandshakeError, SslStream};
fn create_server() -> (
impl Future<Output = Result<SslStream<TcpStream>, HandshakeError<TcpStream>>>,
SocketAddr,
) {
let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
listener.set_nonblocking(true).unwrap();
let listener = TcpListener::from_std(listener).unwrap();
let addr = listener.local_addr().unwrap();
let server = async move {
let mut acceptor = SslAcceptor::rpk().unwrap();
let pkey = std::fs::read("tests/key.pem").unwrap();
let pkey = PKey::private_key_from_pem(&pkey).unwrap();
let cert = std::fs::read("tests/pubkey.der").unwrap();
acceptor.set_rpk_certificate(&cert).unwrap();
acceptor.set_null_chain_private_key(&pkey).unwrap();
let acceptor = acceptor.build();
let stream = listener.accept().await.unwrap().0;
tokio_boring::accept(&acceptor, stream).await
};
(server, addr)
}
#[tokio::test]
async fn server_rpk() {
let (stream, addr) = create_server();
let server = async {
let mut stream = stream.await.unwrap();
let mut buf = [0; 4];
stream.read_exact(&mut buf).await.unwrap();
assert_eq!(&buf, b"asdf");
stream.write_all(b"jkl;").await.unwrap();
future::poll_fn(|ctx| Pin::new(&mut stream).poll_shutdown(ctx))
.await
.unwrap();
};
let client = async {
let mut connector = SslConnector::rpk_builder().unwrap();
let cert = std::fs::read("tests/pubkey.der").unwrap();
connector.set_rpk_certificate(&cert).unwrap();
let config = connector.build().configure().unwrap();
let stream = TcpStream::connect(&addr).await.unwrap();
let mut stream = tokio_boring::connect(config, "localhost", stream)
.await
.unwrap();
stream.write_all(b"asdf").await.unwrap();
let mut buf = vec![];
stream.read_to_end(&mut buf).await.unwrap();
assert_eq!(buf, b"jkl;");
};
future::join(server, client).await;
}
#[tokio::test]
async fn client_rpk_unknown_cert() {
let (stream, addr) = create_server();
let server = async {
assert!(stream.await.is_err());
};
let client = async {
let mut connector = SslConnector::rpk_builder().unwrap();
let cert = std::fs::read("tests/pubkey2.der").unwrap();
connector.set_rpk_certificate(&cert).unwrap();
let config = connector.build().configure().unwrap();
let stream = TcpStream::connect(&addr).await.unwrap();
assert!(tokio_boring::connect(config, "localhost", stream)
.await
.is_err());
};
future::join(server, client).await;
}
}