diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cb68529c..083ee77c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -322,7 +322,8 @@ jobs: working-directory: ${{ runner.temp }}/llvm/bin run: ln -s clang clang++-12 - name: Install ${{ matrix.target }} toolchain - run: brew tap messense/macos-cross-toolchains && brew install --overwrite python@3.11 && brew install ${{ matrix.target }} + # TODO(rmehra): find a better way to overwrite the python3 version without specifying version + run: brew tap messense/macos-cross-toolchains && brew install --overwrite python@3.12 && brew install ${{ matrix.target }} - name: Set BORING_BSSL_FIPS_COMPILER_EXTERNAL_TOOLCHAIN run: echo "BORING_BSSL_FIPS_COMPILER_EXTERNAL_TOOLCHAIN=$(brew --prefix ${{ matrix.target }})/toolchain" >> $GITHUB_ENV shell: bash @@ -360,3 +361,5 @@ jobs: name: Run `rpk,underscore-wildcards` tests - run: cargo test --features pq-experimental,rpk,underscore-wildcards name: Run `pq-experimental,rpk,underscore-wildcards` tests + - run: cargo test -p hyper-boring --features hyper1 + name: Run hyper 1.0 tests for hyper-boring diff --git a/Cargo.toml b/Cargo.toml index 9723a25e..09dbc836 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ resolver = "2" [workspace.package] -version = "4.9.1" +version = "4.10.2" repository = "https://github.com/cloudflare/boring" edition = "2021" @@ -19,11 +19,12 @@ tag-prefix = "" publish = false [workspace.dependencies] -boring-sys = { version = "4.9.1", path = "./boring-sys", package = "rboring-sys" } -boring = { version = "4.9.1", path = "./boring", package = "rboring"} -tokio-boring = { version = "4.9.1", path = "./tokio-boring", package = "tokio-rboring"} +boring-sys = { version = "4.10.2", path = "./rboring-sys" } +boring = { version = "4.10.2", path = "./rboring" } +tokio-boring = { version = "4.10.2", path = "./rtokio-boring" } -bindgen = { version = "0.68.1", default-features = false, features = ["runtime"] } +bindgen = { version = "0.70.1", default-features = false, features = ["runtime"] } +bytes = "1" cmake = "0.1.18" fs_extra = "1.3.0" fslock = "0.2" @@ -36,9 +37,15 @@ futures = "0.3" tokio = "1" anyhow = "1" antidote = "1.0.0" -http = "0.2" -hyper = { package = "rhyper", version = "0.14", default-features = false } +http = "1" +http-body-util = "0.1.2" +http_old = { package = "http", version = "0.2" } +hyper = "1" +hyper-util = "0.1.6" +hyper_old = { package = "rhyper", version = "0.14", default-features = false } linked_hash_set = "0.1" once_cell = "1.0" +openssl-macros = "0.1.1" tower = "0.4" tower-layer = "0.3" +tower-service = "0.3" diff --git a/RELEASE_NOTES b/RELEASE_NOTES index ab943c5f..f1531f74 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,25 @@ +4.10.2 +- 2024-09-18 boring-pq.patch Fix by not updating crypto_test_data.cc + +4.10.1 +- 2024-09-18 Don't support X25519MLKEM768 by default (yet) + +4.10.0 +- 2024-09-18 Implement optional Hyper 1 support in hyper-boring (#246) +- 2024-09-17 Add post-quantum key agreement X25519MLKEM768 +- 2024-09-10 Revert "PQ: fix timing sidechannels and add IPDWing" +- 2024-09-17 Update bindgen to 0.70.1 +- 2024-09-17 Expose SSL(_CTX)_set1_curves_list (#270) +- 2024-09-11 Expose SSL_CTX_set_info_callback (#266) +- 2024-09-03 Use ForeignType::into_ptr wherever applicable +- 2024-08-19 Expose RSAPSS public key Id type +- 2024-08-15 Fix macos FIPS crossbuild +- 2024-08-15 Add tests for X509Ref::subject_key_id, X509Ref::authority_key_id, and X509NameRef::print_ex +- 2024-08-14 Expose X509NameRef::print_ex +- 2024-08-13 Introduce `corresponds` macro from openssl-macros +- 2024-08-14 Introduce ForeignTypeExt and ForeignTypeRefExt +- 2024-08-09 Expose mTLS related APIs +- 2024-08-14 chore(boring-sys): Fix git apply patch on Windows (#261) 4.9.1 - 2024-08-04 Properly handle `Option` in `SslRef::set_curves` diff --git a/boring-sys/Cargo.toml b/boring-sys/Cargo.toml index a1684f3d..fc62e6d7 100644 --- a/boring-sys/Cargo.toml +++ b/boring-sys/Cargo.toml @@ -81,3 +81,6 @@ bindgen = { workspace = true } cmake = { workspace = true } fs_extra = { workspace = true } fslock = { workspace = true } + +[lints.rust] +unexpected_cfgs = { level = "allow", check-cfg = ['cfg(const_fn)'] } diff --git a/boring-sys/build/main.rs b/boring-sys/build/main.rs index 46d7d70d..c0b6f36c 100644 --- a/boring-sys/build/main.rs +++ b/boring-sys/build/main.rs @@ -504,6 +504,14 @@ fn ensure_patches_applied(config: &Config) -> io::Result<()> { fn apply_patch(config: &Config, patch_name: &str) -> io::Result<()> { let src_path = get_boringssl_source_path(config); + #[cfg(not(windows))] + let cmd_path = config + .manifest_dir + .join("patches") + .join(patch_name) + .canonicalize()?; + + #[cfg(windows)] let cmd_path = config.manifest_dir.join("patches").join(patch_name); let mut args = vec!["apply", "-v", "--whitespace=fix"]; @@ -683,6 +691,7 @@ fn main() { }); let mut builder = bindgen::Builder::default() + .rust_target(bindgen::RustTarget::Stable_1_68) // bindgen MSRV is 1.70, so this is enough .derive_copy(true) .derive_debug(true) .derive_default(true) diff --git a/boring-sys/patches/boring-pq.patch b/boring-sys/patches/boring-pq.patch index d4294dc5..38488004 100644 --- a/boring-sys/patches/boring-pq.patch +++ b/boring-sys/patches/boring-pq.patch @@ -1,74 +1,73 @@ -From 836d390deaf8b50fed0cafd55b17a63e80454d7f Mon Sep 17 00:00:00 2001 +From b98d803dbecc9d6848d8cbffa62b5c943fb75f70 Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Fri, 22 Jul 2022 16:43:48 +0200 -Subject: [PATCH] Add temporary post-quantum key agreements +Subject: [PATCH] Add additional post-quantum key agreements -BoringSSL upstream support X25519Kyber768Draft00 already under -codepoint 0x6399, which is the recommended post-quantum key -agreement to use +BoringSSL upstream has supported the temporary post-quantum +key agreement X25519Kyber768Draft00 (0x6399) for a while. +At the time of writing X25519Kyber768Draft00 is widely deployed by browsers. + +Recent BoringSSL adds support for X25519MLKEM768 (0x11ec), +which will be the long term post-quantum key agreement of choice, +and many browsers are expected to switch to it before the end of 2024. This patch adds: -1. Supports for P256Kyber768Draft00 under 0xfe32, which we temporarily +1. Support for MLKEM768X25519 under the codepoint 0x11ec. The version + of BoringSSL we patch against did not support it yet. + +2. Supports for P256Kyber768Draft00 under 0xfe32, which we temporarily need for compliance reasons. (Note that this is not the codepoint allocated for that exchange in the IANA table.) It also enables it in FIPS mode. -2. Support for X25519Kyber768Draft00 under the old codepoint 0xfe31. +3. Support for X25519Kyber768Draft00 under the old codepoint 0xfe31. -3. Support for X25519Kyber512Draft00 under the codepoint 0xfe30. This +4. Support for X25519Kyber512Draft00 under the codepoint 0xfe30. This key agreement should only be used for testing: to see if the smaller keyshare makes a difference. -4. Supportfor IPDWing under codepoint 0xfe41. This key agreement - is a preliminary version of X-Wing using the initial public draft - of ML-KEM. It should not be used. - The patch also replaces Google's implementation of Kyber, by the portable reference implementation, so as to support Kyber512. -Cf RTG-2076 RTG-2051 RTG-2508 RTG-2707 RTG-2607 +Cf RTG-2076 RTG-2051 RTG-2508 RTG-2707 RTG-2607 RTG-3239 --- - BUILD.generated.bzl | 7 +- + BUILD.generated.bzl | 5 +- BUILD.generated_tests.bzl | 4 - - CMakeLists.txt | 6 +- - sources.json | 11 +- - src/crypto/CMakeLists.txt | 7 +- - src/crypto/kyber/fips202.c | 699 +++++++ - src/crypto/kyber/fips202.h | 29 + + CMakeLists.txt | 4 +- + sources.json | 9 +- + src/crypto/CMakeLists.txt | 5 +- src/crypto/kyber/internal.h | 91 - - src/crypto/kyber/ipdwing.c | 110 ++ src/crypto/kyber/keccak.c | 204 -- - src/crypto/kyber/kyber.c | 2319 +++++++++++++++------- - src/crypto/kyber/kyber.h | 29 + + src/crypto/kyber/keccak_tests.txt | 3071 ----------------------------- + src/crypto/kyber/kyber.c | 3011 +++++++++++++++++++++------- src/crypto/kyber/kyber512.c | 5 + src/crypto/kyber/kyber768.c | 4 + src/crypto/kyber/kyber_test.cc | 229 --- + src/crypto/kyber/kyber_tests.txt | 905 --------- src/crypto/obj/obj_dat.h | 17 +- src/crypto/obj/obj_mac.num | 4 + src/crypto/obj/objects.txt | 6 +- - src/include/openssl/kyber.h | 252 ++- + src/include/openssl/kyber.h | 203 +- src/include/openssl/nid.h | 12 + src/include/openssl/ssl.h | 4 + src/sources.cmake | 2 - src/ssl/extensions.cc | 4 + - src/ssl/ssl_key_share.cc | 493 ++++- + src/ssl/ssl_key_share.cc | 525 ++++- src/ssl/ssl_lib.cc | 2 +- src/ssl/ssl_test.cc | 29 +- src/tool/speed.cc | 162 +- - 30 files changed, 3276 insertions(+), 5445 deletions(-) - create mode 100644 src/crypto/kyber/fips202.c - create mode 100644 src/crypto/kyber/fips202.h + 26 files changed, 3088 insertions(+), 5433 deletions(-) delete mode 100644 src/crypto/kyber/internal.h - create mode 100644 src/crypto/kyber/ipdwing.c delete mode 100644 src/crypto/kyber/keccak.c - create mode 100644 src/crypto/kyber/kyber.h + delete mode 100644 src/crypto/kyber/keccak_tests.txt create mode 100644 src/crypto/kyber/kyber512.c create mode 100644 src/crypto/kyber/kyber768.c delete mode 100644 src/crypto/kyber/kyber_test.cc + delete mode 100644 src/crypto/kyber/kyber_tests.txt diff --git a/BUILD.generated.bzl b/BUILD.generated.bzl -index 738e1055f..d1d232399 100644 +index 738e1055f..9466757a2 100644 --- a/BUILD.generated.bzl +++ b/BUILD.generated.bzl @@ -253,7 +253,6 @@ crypto_internal_headers = [ @@ -79,16 +78,14 @@ index 738e1055f..d1d232399 100644 "src/crypto/lhash/internal.h", "src/crypto/obj/obj_dat.h", "src/crypto/pkcs7/internal.h", -@@ -382,8 +381,10 @@ crypto_sources = [ +@@ -382,8 +381,8 @@ crypto_sources = [ "src/crypto/fipsmodule/fips_shared_support.c", "src/crypto/hpke/hpke.c", "src/crypto/hrss/hrss.c", - "src/crypto/kyber/keccak.c", - "src/crypto/kyber/kyber.c", -+ "src/crypto/kyber/fips202.c", + "src/crypto/kyber/kyber512.c", + "src/crypto/kyber/kyber768.c", -+ "src/crypto/kyber/ipdwing.c", "src/crypto/lhash/lhash.c", "src/crypto/mem.c", "src/crypto/obj/obj.c", @@ -122,40 +119,36 @@ index 92dec1e01..8f70dedc0 100644 "src/crypto/pkcs8/test/no_encryption.p12", "src/crypto/pkcs8/test/nss.p12", diff --git a/CMakeLists.txt b/CMakeLists.txt -index faed2befa..678a0167a 100644 +index faed2befa..931c0e3a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -375,8 +375,10 @@ add_library( +@@ -375,8 +375,8 @@ add_library( src/crypto/fipsmodule/fips_shared_support.c src/crypto/hpke/hpke.c src/crypto/hrss/hrss.c - src/crypto/kyber/keccak.c - src/crypto/kyber/kyber.c -+ src/crypto/kyber/fips202.c + src/crypto/kyber/kyber512.c + src/crypto/kyber/kyber768.c -+ src/crypto/kyber/ipdwing.c src/crypto/lhash/lhash.c src/crypto/mem.c src/crypto/obj/obj.c diff --git a/sources.json b/sources.json -index 4c0048e1d..d021a14b1 100644 +index 4c0048e1d..f6ea5c40f 100644 --- a/sources.json +++ b/sources.json -@@ -111,8 +111,10 @@ +@@ -111,8 +111,8 @@ "src/crypto/fipsmodule/fips_shared_support.c", "src/crypto/hpke/hpke.c", "src/crypto/hrss/hrss.c", - "src/crypto/kyber/keccak.c", - "src/crypto/kyber/kyber.c", -+ "src/crypto/kyber/fips202.c", + "src/crypto/kyber/kyber512.c", + "src/crypto/kyber/kyber768.c", -+ "src/crypto/kyber/ipdwing.c", "src/crypto/lhash/lhash.c", "src/crypto/mem.c", "src/crypto/obj/obj.c", -@@ -549,7 +551,6 @@ +@@ -549,7 +549,6 @@ "src/crypto/hpke/hpke_test.cc", "src/crypto/hrss/hrss_test.cc", "src/crypto/impl_dispatch_test.cc", @@ -163,7 +156,7 @@ index 4c0048e1d..d021a14b1 100644 "src/crypto/lhash/lhash_test.cc", "src/crypto/obj/obj_test.cc", "src/crypto/pem/pem_test.cc", -@@ -634,8 +635,6 @@ +@@ -634,8 +633,6 @@ "src/crypto/fipsmodule/rand/ctrdrbg_vectors.txt", "src/crypto/hmac_extra/hmac_tests.txt", "src/crypto/hpke/hpke_test_vectors.txt", @@ -172,7 +165,7 @@ index 4c0048e1d..d021a14b1 100644 "src/crypto/pkcs8/test/empty_password.p12", "src/crypto/pkcs8/test/no_encryption.p12", "src/crypto/pkcs8/test/nss.p12", -@@ -1060,4 +1059,4 @@ +@@ -1060,4 +1057,4 @@ "urandom_test": [ "src/crypto/fipsmodule/rand/urandom_test.cc" ] @@ -180,23 +173,21 @@ index 4c0048e1d..d021a14b1 100644 \ No newline at end of file +} diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt -index cdb5ddca1..9dcb7a566 100644 +index cdb5ddca1..2052fa791 100644 --- a/src/crypto/CMakeLists.txt +++ b/src/crypto/CMakeLists.txt -@@ -170,8 +170,10 @@ add_library( +@@ -170,8 +170,8 @@ add_library( ex_data.c hpke/hpke.c hrss/hrss.c - kyber/keccak.c - kyber/kyber.c -+ kyber/fips202.c + kyber/kyber512.c + kyber/kyber768.c -+ kyber/ipdwing.c lhash/lhash.c mem.c obj/obj.c -@@ -400,7 +402,6 @@ add_executable( +@@ -400,7 +400,6 @@ add_executable( hmac_extra/hmac_test.cc hrss/hrss_test.cc impl_dispatch_test.cc @@ -204,746 +195,6 @@ index cdb5ddca1..9dcb7a566 100644 lhash/lhash_test.cc obj/obj_test.cc pem/pem_test.cc -diff --git a/src/crypto/kyber/fips202.c b/src/crypto/kyber/fips202.c -new file mode 100644 -index 000000000..9713a4f7e ---- /dev/null -+++ b/src/crypto/kyber/fips202.c -@@ -0,0 +1,699 @@ -+/* Based on the public domain implementation in crypto_hash/keccakc512/simple/ from -+ * http://bench.cr.yp.to/supercop.html by Ronny Van Keer and the public domain "TweetFips202" -+ * implementation from https://twitter.com/tweetfips202 by Gilles Van Assche, Daniel J. Bernstein, -+ * and Peter Schwabe */ -+ -+#include "fips202.h" -+ -+#define NROUNDS 24 -+#define ROL(a, offset) ((a << offset) ^ (a >> (64-offset))) -+ -+/************************************************* -+* Name: load64 -+* -+* Description: Load 8 bytes into uint64_t in little-endian order -+* -+* Arguments: - const uint8_t *x: pointer to input byte array -+* -+* Returns the loaded 64-bit unsigned integer -+**************************************************/ -+static uint64_t load64(const uint8_t x[8]) { -+ unsigned int i; -+ uint64_t r = 0; -+ -+ for(i=0;i<8;i++) -+ r |= (uint64_t)x[i] << 8*i; -+ -+ return r; -+} -+ -+/************************************************* -+* Name: store64 -+* -+* Description: Store a 64-bit integer to array of 8 bytes in little-endian order -+* -+* Arguments: - uint8_t *x: pointer to the output byte array (allocated) -+* - uint64_t u: input 64-bit unsigned integer -+**************************************************/ -+static void store64(uint8_t x[8], uint64_t u) { -+ unsigned int i; -+ -+ for(i=0;i<8;i++) -+ x[i] = u >> 8*i; -+} -+ -+/* Keccak round constants */ -+static const uint64_t KeccakF_RoundConstants[NROUNDS] = { -+ (uint64_t)0x0000000000000001ULL, -+ (uint64_t)0x0000000000008082ULL, -+ (uint64_t)0x800000000000808aULL, -+ (uint64_t)0x8000000080008000ULL, -+ (uint64_t)0x000000000000808bULL, -+ (uint64_t)0x0000000080000001ULL, -+ (uint64_t)0x8000000080008081ULL, -+ (uint64_t)0x8000000000008009ULL, -+ (uint64_t)0x000000000000008aULL, -+ (uint64_t)0x0000000000000088ULL, -+ (uint64_t)0x0000000080008009ULL, -+ (uint64_t)0x000000008000000aULL, -+ (uint64_t)0x000000008000808bULL, -+ (uint64_t)0x800000000000008bULL, -+ (uint64_t)0x8000000000008089ULL, -+ (uint64_t)0x8000000000008003ULL, -+ (uint64_t)0x8000000000008002ULL, -+ (uint64_t)0x8000000000000080ULL, -+ (uint64_t)0x000000000000800aULL, -+ (uint64_t)0x800000008000000aULL, -+ (uint64_t)0x8000000080008081ULL, -+ (uint64_t)0x8000000000008080ULL, -+ (uint64_t)0x0000000080000001ULL, -+ (uint64_t)0x8000000080008008ULL -+}; -+ -+/************************************************* -+* Name: KeccakF1600_StatePermute -+* -+* Description: The Keccak F1600 Permutation -+* -+* Arguments: - uint64_t *state: pointer to input/output Keccak state -+**************************************************/ -+static void KeccakF1600_StatePermute(uint64_t state[25]) -+{ -+ int round; -+ -+ uint64_t Aba, Abe, Abi, Abo, Abu; -+ uint64_t Aga, Age, Agi, Ago, Agu; -+ uint64_t Aka, Ake, Aki, Ako, Aku; -+ uint64_t Ama, Ame, Ami, Amo, Amu; -+ uint64_t Asa, Ase, Asi, Aso, Asu; -+ uint64_t BCa, BCe, BCi, BCo, BCu; -+ uint64_t Da, De, Di, Do, Du; -+ uint64_t Eba, Ebe, Ebi, Ebo, Ebu; -+ uint64_t Ega, Ege, Egi, Ego, Egu; -+ uint64_t Eka, Eke, Eki, Eko, Eku; -+ uint64_t Ema, Eme, Emi, Emo, Emu; -+ uint64_t Esa, Ese, Esi, Eso, Esu; -+ -+ //copyFromState(A, state) -+ Aba = state[ 0]; -+ Abe = state[ 1]; -+ Abi = state[ 2]; -+ Abo = state[ 3]; -+ Abu = state[ 4]; -+ Aga = state[ 5]; -+ Age = state[ 6]; -+ Agi = state[ 7]; -+ Ago = state[ 8]; -+ Agu = state[ 9]; -+ Aka = state[10]; -+ Ake = state[11]; -+ Aki = state[12]; -+ Ako = state[13]; -+ Aku = state[14]; -+ Ama = state[15]; -+ Ame = state[16]; -+ Ami = state[17]; -+ Amo = state[18]; -+ Amu = state[19]; -+ Asa = state[20]; -+ Ase = state[21]; -+ Asi = state[22]; -+ Aso = state[23]; -+ Asu = state[24]; -+ -+ for(round = 0; round < NROUNDS; round += 2) { -+ // prepareTheta -+ BCa = Aba^Aga^Aka^Ama^Asa; -+ BCe = Abe^Age^Ake^Ame^Ase; -+ BCi = Abi^Agi^Aki^Ami^Asi; -+ BCo = Abo^Ago^Ako^Amo^Aso; -+ BCu = Abu^Agu^Aku^Amu^Asu; -+ -+ //thetaRhoPiChiIotaPrepareTheta(round, A, E) -+ Da = BCu^ROL(BCe, 1); -+ De = BCa^ROL(BCi, 1); -+ Di = BCe^ROL(BCo, 1); -+ Do = BCi^ROL(BCu, 1); -+ Du = BCo^ROL(BCa, 1); -+ -+ Aba ^= Da; -+ BCa = Aba; -+ Age ^= De; -+ BCe = ROL(Age, 44); -+ Aki ^= Di; -+ BCi = ROL(Aki, 43); -+ Amo ^= Do; -+ BCo = ROL(Amo, 21); -+ Asu ^= Du; -+ BCu = ROL(Asu, 14); -+ Eba = BCa ^((~BCe)& BCi ); -+ Eba ^= (uint64_t)KeccakF_RoundConstants[round]; -+ Ebe = BCe ^((~BCi)& BCo ); -+ Ebi = BCi ^((~BCo)& BCu ); -+ Ebo = BCo ^((~BCu)& BCa ); -+ Ebu = BCu ^((~BCa)& BCe ); -+ -+ Abo ^= Do; -+ BCa = ROL(Abo, 28); -+ Agu ^= Du; -+ BCe = ROL(Agu, 20); -+ Aka ^= Da; -+ BCi = ROL(Aka, 3); -+ Ame ^= De; -+ BCo = ROL(Ame, 45); -+ Asi ^= Di; -+ BCu = ROL(Asi, 61); -+ Ega = BCa ^((~BCe)& BCi ); -+ Ege = BCe ^((~BCi)& BCo ); -+ Egi = BCi ^((~BCo)& BCu ); -+ Ego = BCo ^((~BCu)& BCa ); -+ Egu = BCu ^((~BCa)& BCe ); -+ -+ Abe ^= De; -+ BCa = ROL(Abe, 1); -+ Agi ^= Di; -+ BCe = ROL(Agi, 6); -+ Ako ^= Do; -+ BCi = ROL(Ako, 25); -+ Amu ^= Du; -+ BCo = ROL(Amu, 8); -+ Asa ^= Da; -+ BCu = ROL(Asa, 18); -+ Eka = BCa ^((~BCe)& BCi ); -+ Eke = BCe ^((~BCi)& BCo ); -+ Eki = BCi ^((~BCo)& BCu ); -+ Eko = BCo ^((~BCu)& BCa ); -+ Eku = BCu ^((~BCa)& BCe ); -+ -+ Abu ^= Du; -+ BCa = ROL(Abu, 27); -+ Aga ^= Da; -+ BCe = ROL(Aga, 36); -+ Ake ^= De; -+ BCi = ROL(Ake, 10); -+ Ami ^= Di; -+ BCo = ROL(Ami, 15); -+ Aso ^= Do; -+ BCu = ROL(Aso, 56); -+ Ema = BCa ^((~BCe)& BCi ); -+ Eme = BCe ^((~BCi)& BCo ); -+ Emi = BCi ^((~BCo)& BCu ); -+ Emo = BCo ^((~BCu)& BCa ); -+ Emu = BCu ^((~BCa)& BCe ); -+ -+ Abi ^= Di; -+ BCa = ROL(Abi, 62); -+ Ago ^= Do; -+ BCe = ROL(Ago, 55); -+ Aku ^= Du; -+ BCi = ROL(Aku, 39); -+ Ama ^= Da; -+ BCo = ROL(Ama, 41); -+ Ase ^= De; -+ BCu = ROL(Ase, 2); -+ Esa = BCa ^((~BCe)& BCi ); -+ Ese = BCe ^((~BCi)& BCo ); -+ Esi = BCi ^((~BCo)& BCu ); -+ Eso = BCo ^((~BCu)& BCa ); -+ Esu = BCu ^((~BCa)& BCe ); -+ -+ // prepareTheta -+ BCa = Eba^Ega^Eka^Ema^Esa; -+ BCe = Ebe^Ege^Eke^Eme^Ese; -+ BCi = Ebi^Egi^Eki^Emi^Esi; -+ BCo = Ebo^Ego^Eko^Emo^Eso; -+ BCu = Ebu^Egu^Eku^Emu^Esu; -+ -+ //thetaRhoPiChiIotaPrepareTheta(round+1, E, A) -+ Da = BCu^ROL(BCe, 1); -+ De = BCa^ROL(BCi, 1); -+ Di = BCe^ROL(BCo, 1); -+ Do = BCi^ROL(BCu, 1); -+ Du = BCo^ROL(BCa, 1); -+ -+ Eba ^= Da; -+ BCa = Eba; -+ Ege ^= De; -+ BCe = ROL(Ege, 44); -+ Eki ^= Di; -+ BCi = ROL(Eki, 43); -+ Emo ^= Do; -+ BCo = ROL(Emo, 21); -+ Esu ^= Du; -+ BCu = ROL(Esu, 14); -+ Aba = BCa ^((~BCe)& BCi ); -+ Aba ^= (uint64_t)KeccakF_RoundConstants[round+1]; -+ Abe = BCe ^((~BCi)& BCo ); -+ Abi = BCi ^((~BCo)& BCu ); -+ Abo = BCo ^((~BCu)& BCa ); -+ Abu = BCu ^((~BCa)& BCe ); -+ -+ Ebo ^= Do; -+ BCa = ROL(Ebo, 28); -+ Egu ^= Du; -+ BCe = ROL(Egu, 20); -+ Eka ^= Da; -+ BCi = ROL(Eka, 3); -+ Eme ^= De; -+ BCo = ROL(Eme, 45); -+ Esi ^= Di; -+ BCu = ROL(Esi, 61); -+ Aga = BCa ^((~BCe)& BCi ); -+ Age = BCe ^((~BCi)& BCo ); -+ Agi = BCi ^((~BCo)& BCu ); -+ Ago = BCo ^((~BCu)& BCa ); -+ Agu = BCu ^((~BCa)& BCe ); -+ -+ Ebe ^= De; -+ BCa = ROL(Ebe, 1); -+ Egi ^= Di; -+ BCe = ROL(Egi, 6); -+ Eko ^= Do; -+ BCi = ROL(Eko, 25); -+ Emu ^= Du; -+ BCo = ROL(Emu, 8); -+ Esa ^= Da; -+ BCu = ROL(Esa, 18); -+ Aka = BCa ^((~BCe)& BCi ); -+ Ake = BCe ^((~BCi)& BCo ); -+ Aki = BCi ^((~BCo)& BCu ); -+ Ako = BCo ^((~BCu)& BCa ); -+ Aku = BCu ^((~BCa)& BCe ); -+ -+ Ebu ^= Du; -+ BCa = ROL(Ebu, 27); -+ Ega ^= Da; -+ BCe = ROL(Ega, 36); -+ Eke ^= De; -+ BCi = ROL(Eke, 10); -+ Emi ^= Di; -+ BCo = ROL(Emi, 15); -+ Eso ^= Do; -+ BCu = ROL(Eso, 56); -+ Ama = BCa ^((~BCe)& BCi ); -+ Ame = BCe ^((~BCi)& BCo ); -+ Ami = BCi ^((~BCo)& BCu ); -+ Amo = BCo ^((~BCu)& BCa ); -+ Amu = BCu ^((~BCa)& BCe ); -+ -+ Ebi ^= Di; -+ BCa = ROL(Ebi, 62); -+ Ego ^= Do; -+ BCe = ROL(Ego, 55); -+ Eku ^= Du; -+ BCi = ROL(Eku, 39); -+ Ema ^= Da; -+ BCo = ROL(Ema, 41); -+ Ese ^= De; -+ BCu = ROL(Ese, 2); -+ Asa = BCa ^((~BCe)& BCi ); -+ Ase = BCe ^((~BCi)& BCo ); -+ Asi = BCi ^((~BCo)& BCu ); -+ Aso = BCo ^((~BCu)& BCa ); -+ Asu = BCu ^((~BCa)& BCe ); -+ } -+ -+ //copyToState(state, A) -+ state[ 0] = Aba; -+ state[ 1] = Abe; -+ state[ 2] = Abi; -+ state[ 3] = Abo; -+ state[ 4] = Abu; -+ state[ 5] = Aga; -+ state[ 6] = Age; -+ state[ 7] = Agi; -+ state[ 8] = Ago; -+ state[ 9] = Agu; -+ state[10] = Aka; -+ state[11] = Ake; -+ state[12] = Aki; -+ state[13] = Ako; -+ state[14] = Aku; -+ state[15] = Ama; -+ state[16] = Ame; -+ state[17] = Ami; -+ state[18] = Amo; -+ state[19] = Amu; -+ state[20] = Asa; -+ state[21] = Ase; -+ state[22] = Asi; -+ state[23] = Aso; -+ state[24] = Asu; -+} -+ -+ -+/************************************************* -+* Name: keccak_squeeze -+* -+* Description: Squeeze step of Keccak. Squeezes arbitratrily many bytes. -+* Modifies the state. Can be called multiple times to keep -+* squeezing, i.e., is incremental. -+* -+* Arguments: - uint8_t *out: pointer to output -+* - size_t outlen: number of bytes to be squeezed (written to out) -+* - uint64_t *s: pointer to input/output Keccak state -+* - unsigned int pos: number of bytes in current block already squeezed -+* - unsigned int r: rate in bytes (e.g., 168 for SHAKE128) -+* -+* Returns new position pos in current block -+**************************************************/ -+static unsigned int keccak_squeeze(uint8_t *out, -+ size_t outlen, -+ uint64_t s[25], -+ unsigned int pos, -+ unsigned int r) -+{ -+ unsigned int i; -+ -+ while(outlen) { -+ if(pos == r) { -+ KeccakF1600_StatePermute(s); -+ pos = 0; -+ } -+ for(i=pos;i < r && i < pos+outlen; i++) -+ *out++ = s[i/8] >> 8*(i%8); -+ outlen -= i-pos; -+ pos = i; -+ } -+ -+ return pos; -+} -+ -+/************************************************* -+* Name: keccak_init -+* -+* Description: Initializes the Keccak state. -+* -+* Arguments: - uint64_t *s: pointer to Keccak state -+**************************************************/ -+static void keccak_init(uint64_t s[25]) -+{ -+ unsigned int i; -+ for(i=0;i<25;i++) -+ s[i] = 0; -+} -+ -+ -+/************************************************* -+* Name: keccak_absorb -+* -+* Description: Absorb step of Keccak; incremental. -+* -+* Arguments: - uint64_t *s: pointer to Keccak state -+* - unsigned int pos: position in current block to be absorbed -+* - unsigned int r: rate in bytes (e.g., 168 for SHAKE128) -+* - const uint8_t *in: pointer to input to be absorbed into s -+* - size_t inlen: length of input in bytes -+* -+* Returns new position pos in current block -+**************************************************/ -+static unsigned int keccak_absorb(uint64_t s[25], -+ unsigned int pos, -+ unsigned int r, -+ const uint8_t *in, -+ size_t inlen) -+{ -+ unsigned int i; -+ -+ while(pos+inlen >= r) { -+ for(i=pos;i= r) { -+ for(i=0;is, SHAKE128_RATE, in, inlen, 0x1F); -+ state->pos = SHAKE128_RATE; -+} -+ -+/************************************************* -+* Name: shake128_squeezeblocks -+* -+* Description: Squeeze step of SHAKE128 XOF. Squeezes full blocks of -+* SHAKE128_RATE bytes each. Can be called multiple times -+* to keep squeezing. Assumes new block has not yet been -+* started (state->pos = SHAKE128_RATE). -+* -+* Arguments: - uint8_t *out: pointer to output blocks -+* - size_t nblocks: number of blocks to be squeezed (written to output) -+* - keccak_state *s: pointer to input/output Keccak state -+**************************************************/ -+void shake128_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state) -+{ -+ keccak_squeezeblocks(out, nblocks, state->s, SHAKE128_RATE); -+} -+ -+/************************************************* -+* Name: shake256_squeeze -+* -+* Description: Squeeze step of SHAKE256 XOF. Squeezes arbitraily many -+* bytes. Can be called multiple times to keep squeezing. -+* -+* Arguments: - uint8_t *out: pointer to output blocks -+* - size_t outlen : number of bytes to be squeezed (written to output) -+* - keccak_state *s: pointer to input/output Keccak state -+**************************************************/ -+void shake256_squeeze(uint8_t *out, size_t outlen, keccak_state *state) -+{ -+ state->pos = keccak_squeeze(out, outlen, state->s, state->pos, SHAKE256_RATE); -+} -+ -+/************************************************* -+* Name: shake256_init -+* -+* Description: Initilizes Keccak state for use as SHAKE256 XOF -+* -+* Arguments: - keccak_state *state: pointer to (uninitialized) Keccak state -+**************************************************/ -+void shake256_init(keccak_state *state) -+{ -+ keccak_init(state->s); -+ state->pos = 0; -+} -+ -+/************************************************* -+* Name: shake256_absorb -+* -+* Description: Absorb step of the SHAKE256 XOF; incremental. -+* -+* Arguments: - keccak_state *state: pointer to (initialized) output Keccak state -+* - const uint8_t *in: pointer to input to be absorbed into s -+* - size_t inlen: length of input in bytes -+**************************************************/ -+void shake256_absorb(keccak_state *state, const uint8_t *in, size_t inlen) -+{ -+ state->pos = keccak_absorb(state->s, state->pos, SHAKE256_RATE, in, inlen); -+} -+ -+/************************************************* -+* Name: shake256_finalize -+* -+* Description: Finalize absorb step of the SHAKE256 XOF. -+* -+* Arguments: - keccak_state *state: pointer to Keccak state -+**************************************************/ -+void shake256_finalize(keccak_state *state) -+{ -+ keccak_finalize(state->s, state->pos, SHAKE256_RATE, 0x1F); -+ state->pos = SHAKE256_RATE; -+} -+ -+/************************************************* -+* Name: shake256_absorb_once -+* -+* Description: Initialize, absorb into and finalize SHAKE256 XOF; non-incremental. -+* -+* Arguments: - keccak_state *state: pointer to (uninitialized) output Keccak state -+* - const uint8_t *in: pointer to input to be absorbed into s -+* - size_t inlen: length of input in bytes -+**************************************************/ -+void shake256_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen) -+{ -+ keccak_absorb_once(state->s, SHAKE256_RATE, in, inlen, 0x1F); -+ state->pos = SHAKE256_RATE; -+} -+ -+/************************************************* -+* Name: shake256_squeezeblocks -+* -+* Description: Squeeze step of SHAKE256 XOF. Squeezes full blocks of -+* SHAKE256_RATE bytes each. Can be called multiple times -+* to keep squeezing. Assumes next block has not yet been -+* started (state->pos = SHAKE256_RATE). -+* -+* Arguments: - uint8_t *out: pointer to output blocks -+* - size_t nblocks: number of blocks to be squeezed (written to output) -+* - keccak_state *s: pointer to input/output Keccak state -+**************************************************/ -+void shake256_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state) -+{ -+ keccak_squeezeblocks(out, nblocks, state->s, SHAKE256_RATE); -+} -+ -+/************************************************* -+* Name: shake256 -+* -+* Description: SHAKE256 XOF with non-incremental API -+* -+* Arguments: - uint8_t *out: pointer to output -+* - size_t outlen: requested output length in bytes -+* - const uint8_t *in: pointer to input -+* - size_t inlen: length of input in bytes -+**************************************************/ -+void shake256(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen) -+{ -+ size_t nblocks; -+ keccak_state state; -+ -+ shake256_absorb_once(&state, in, inlen); -+ nblocks = outlen/SHAKE256_RATE; -+ shake256_squeezeblocks(out, nblocks, &state); -+ outlen -= nblocks*SHAKE256_RATE; -+ out += nblocks*SHAKE256_RATE; -+ shake256_squeeze(out, outlen, &state); -+} -+ -+/************************************************* -+* Name: sha3_256 -+* -+* Description: SHA3-256 with non-incremental API -+* -+* Arguments: - uint8_t *h: pointer to output (32 bytes) -+* - const uint8_t *in: pointer to input -+* - size_t inlen: length of input in bytes -+**************************************************/ -+void sha3_256(uint8_t h[32], const uint8_t *in, size_t inlen) -+{ -+ unsigned int i; -+ uint64_t s[25]; -+ -+ keccak_absorb_once(s, SHA3_256_RATE, in, inlen, 0x06); -+ KeccakF1600_StatePermute(s); -+ for(i=0;i<4;i++) -+ store64(h+8*i,s[i]); -+} -+ -+/************************************************* -+* Name: sha3_512 -+* -+* Description: SHA3-512 with non-incremental API -+* -+* Arguments: - uint8_t *h: pointer to output (64 bytes) -+* - const uint8_t *in: pointer to input -+* - size_t inlen: length of input in bytes -+**************************************************/ -+void sha3_512(uint8_t h[64], const uint8_t *in, size_t inlen) -+{ -+ unsigned int i; -+ uint64_t s[25]; -+ -+ keccak_absorb_once(s, SHA3_512_RATE, in, inlen, 0x06); -+ KeccakF1600_StatePermute(s); -+ for(i=0;i<8;i++) -+ store64(h+8*i,s[i]); -+} -+ -+ -diff --git a/src/crypto/kyber/fips202.h b/src/crypto/kyber/fips202.h -new file mode 100644 -index 000000000..7c37570bc ---- /dev/null -+++ b/src/crypto/kyber/fips202.h -@@ -0,0 +1,29 @@ -+#ifndef OPENSSL_HEADER_KYBER_FIPS202_H -+#define OPENSSL_HEADER_KYBER_FIPS202_H -+ -+#include -+#include -+ -+#define SHAKE128_RATE 168 -+#define SHAKE256_RATE 136 -+#define SHA3_256_RATE 136 -+#define SHA3_512_RATE 72 -+ -+typedef struct { -+ uint64_t s[25]; -+ unsigned int pos; -+} keccak_state; -+ -+void shake128_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen); -+void shake128_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state); -+void shake256_squeeze(uint8_t *out, size_t outlen, keccak_state *state); -+void shake256_init(keccak_state *state); -+void shake256_absorb(keccak_state *state, const uint8_t *in, size_t inlen); -+void shake256_finalize(keccak_state *state); -+void shake256_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen); -+void shake256_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state); -+void shake256(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen); -+void sha3_512(uint8_t h[64], const uint8_t *in, size_t inlen); -+void sha3_256(uint8_t h[32], const uint8_t *in, size_t inlen); -+ -+#endif diff --git a/src/crypto/kyber/internal.h b/src/crypto/kyber/internal.h deleted file mode 100644 index b3bfa86b8..000000000 @@ -1041,122 +292,6 @@ index b3bfa86b8..000000000 -#endif - -#endif // OPENSSL_HEADER_CRYPTO_KYBER_INTERNAL_H -diff --git a/src/crypto/kyber/ipdwing.c b/src/crypto/kyber/ipdwing.c -new file mode 100644 -index 000000000..d55cfefc9 ---- /dev/null -+++ b/src/crypto/kyber/ipdwing.c -@@ -0,0 +1,110 @@ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+#include "fips202.h" -+#include "kyber.h" -+ -+static const char *label = "\\.//^\\"; -+ -+static void combiner( -+ uint8_t out[32], -+ const uint8_t ss_m[32], -+ const uint8_t ss_x[32], -+ const uint8_t ct_x[32], -+ const uint8_t pk_x[32]) { -+ uint8_t buf[6+32*4]; -+ memcpy(buf, label, 6); -+ memcpy(buf+6, ss_m, 32); -+ memcpy(buf+6+32, ss_x, 32); -+ memcpy(buf+6+32*2, ct_x, 32); -+ memcpy(buf+6+32*3, pk_x, 32); -+ sha3_256(out, buf, 6+32*4); -+} -+ -+void IPDWING_generate_key( -+ struct IPDWING_public_key *out_pub, -+ struct IPDWING_private_key *out_priv, -+ const uint8_t seed[IPDWING_GENERATE_KEY_BYTES]) { -+ KYBER768_generate_key( -+ &out_pub->m, -+ &out_priv->m, -+ seed); -+ memcpy(out_priv->x, seed+64, 32); -+ X25519_public_from_private(out_pub->x, out_priv->x); -+ memcpy(out_priv->xpub, out_pub->x, 32); -+} -+ -+void IPDWING_encap( -+ uint8_t out_ciphertext[IPDWING_CIPHERTEXT_BYTES], -+ uint8_t ss[IPDWING_KEY_BYTES], -+ const struct IPDWING_public_key *in_pub, -+ const uint8_t seed[IPDWING_ENCAP_BYTES]) { -+ -+ uint8_t ss_m[32]; -+ uint8_t ss_x[32]; -+ uint8_t *ct_x = out_ciphertext + KYBER768_CIPHERTEXT_BYTES; -+ const uint8_t *ek_x = seed + 32; -+ X25519_public_from_private(ct_x, ek_x); -+ -+ X25519( -+ ss_x, -+ ek_x, -+ in_pub->x -+ ); -+ -+ KYBER768_encap2( -+ out_ciphertext, -+ ss_m, -+ &in_pub->m, -+ seed, -+ 1 -+ ); -+ -+ combiner(ss, ss_m, ss_x, ct_x, in_pub->x); -+} -+ -+void IPDWING_decap( -+ uint8_t out_shared_key[IPDWING_KEY_BYTES], -+ const struct IPDWING_private_key *in_priv, -+ const uint8_t *ct) { -+ -+ uint8_t ss_m[32]; -+ uint8_t ss_x[32]; -+ const uint8_t *ct_x = ct + KYBER768_CIPHERTEXT_BYTES; -+ -+ KYBER768_decap2( -+ ss_m, -+ &in_priv->m, -+ ct, -+ KYBER768_CIPHERTEXT_BYTES, -+ 1 -+ ); -+ -+ X25519( -+ ss_x, -+ in_priv->x, -+ ct_x -+ ); -+ -+ combiner(out_shared_key, ss_m, ss_x, ct_x, in_priv->xpub); -+} -+ -+void IPDWING_marshal_public_key( -+ uint8_t out[IPDWING_PUBLIC_KEY_BYTES], -+ const struct IPDWING_public_key *in) { -+ KYBER768_marshal_public_key(out, &in->m); -+ memcpy(out + KYBER768_PUBLIC_KEY_BYTES, in->x, 32); -+} -+ -+void IPDWING_parse_public_key( -+ struct IPDWING_public_key *out, -+ const uint8_t in[IPDWING_PUBLIC_KEY_BYTES]) { -+ KYBER768_parse_public_key(&out->m, in); -+ memcpy(out->x, in + KYBER768_PUBLIC_KEY_BYTES, 32); -+} -+ diff --git a/src/crypto/kyber/keccak.c b/src/crypto/kyber/keccak.c deleted file mode 100644 index f1c012d11..000000000 @@ -1368,10 +503,10 @@ index f1c012d11..000000000 - } -} diff --git a/src/crypto/kyber/kyber.c b/src/crypto/kyber/kyber.c -index 776c085f9..5acd45cd9 100644 +index 776c085f9..ccb5b3d9b 100644 --- a/src/crypto/kyber/kyber.c +++ b/src/crypto/kyber/kyber.c -@@ -1,833 +1,1706 @@ +@@ -1,833 +1,2426 @@ -/* Copyright (c) 2023, Google Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any @@ -1396,6 +531,8 @@ index 776c085f9..5acd45cd9 100644 +// - Removed 90s version. +// - Seeds are passed as paramters. +// - Changed the API to be more BoringSSL-like ++// - Mitigated timing sidechannels (Kyberslash 1 and 2). ++// (Note that these do not affect ephemeral usage as in TLS.) +// +// TODO +// @@ -1427,8 +564,6 @@ index 776c085f9..5acd45cd9 100644 +#include +#include -+#include "fips202.h" -+#include "kyber.h" #include "../internal.h" -#include "./internal.h" - @@ -1589,9 +724,6 @@ index 776c085f9..5acd45cd9 100644 +#define marshal_public_key KYBER_NAMESPACE(marshal_public_key) +#define parse_public_key KYBER_NAMESPACE(parse_public_key) + -+#define decap2 KYBER_NAMESPACE(decap2) -+#define encap2 KYBER_NAMESPACE(encap2) -+ + +// +// params.h @@ -1725,7 +857,7 @@ index 776c085f9..5acd45cd9 100644 + uint8_t sk[KYBER_INDCPA_SECRETKEYBYTES], + const uint8_t seed[KYBER_SYMBYTES]); + -+static void indcpa_enc(uint8_t c[KYBER_INDCPA_BYTES], ++static int indcpa_enc(uint8_t c[KYBER_INDCPA_BYTES], + const uint8_t m[KYBER_INDCPA_MSGBYTES], + const uint8_t pk[KYBER_INDCPA_PUBLICKEYBYTES], + const uint8_t coins[KYBER_SYMBYTES]); @@ -1738,6 +870,33 @@ index 776c085f9..5acd45cd9 100644 +// fips202.h +// + ++#define SHAKE128_RATE 168 ++#define SHAKE256_RATE 136 ++#define SHA3_256_RATE 136 ++#define SHA3_512_RATE 72 ++ ++typedef struct { ++ uint64_t s[25]; ++ unsigned int pos; ++} keccak_state; ++ ++static void shake128_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen); ++static void shake128_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state); ++ ++static void shake256_squeeze(uint8_t *out, size_t outlen, keccak_state *state); ++static void shake256_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen); ++static void shake256_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state); ++static void shake256_absorb(keccak_state *state, const uint8_t *in, size_t inlen); ++static void shake256_finalize(keccak_state *state); ++static void shake256_init(keccak_state *state); ++ ++static void shake256(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen); ++static void sha3_256(uint8_t h[32], const uint8_t *in, size_t inlen); ++static void sha3_512(uint8_t h[64], const uint8_t *in, size_t inlen); ++ ++// ++// symmetric.h ++// + +typedef keccak_state xof_state; + @@ -1748,7 +907,6 @@ index 776c085f9..5acd45cd9 100644 + +static void kyber_shake256_prf(uint8_t *out, size_t outlen, const uint8_t key[KYBER_SYMBYTES], uint8_t nonce); + -+ +#define XOF_BLOCKBYTES SHAKE128_RATE + +#define hash_h(OUT, IN, INBYTES) sha3_256(OUT, IN, INBYTES) @@ -1922,13 +1080,10 @@ index 776c085f9..5acd45cd9 100644 + a = (d >> (4*j+0)) & 0x3; + b = (d >> (4*j+2)) & 0x3; + r->coeffs[8*i+j] = a - b; - } - } - } - --static void vector_ntt(vector *a) { -- for (int i = 0; i < RANK; i++) { -- scalar_ntt(&a->v[i]); ++ } ++ } ++} ++ +/************************************************* +* Name: cbd3 +* @@ -1957,7 +1112,7 @@ index 776c085f9..5acd45cd9 100644 + a = (d >> (6*j+0)) & 0x7; + b = (d >> (6*j+3)) & 0x7; + r->coeffs[4*i+j] = a - b; -+ } + } } } +#endif @@ -1972,7 +1127,10 @@ index 776c085f9..5acd45cd9 100644 +#error "This implementation requires eta1 in {2,3}" +#endif +} -+ + +-static void vector_ntt(vector *a) { +- for (int i = 0; i < RANK; i++) { +- scalar_ntt(&a->v[i]); +static void poly_cbd_eta2(poly *r, const uint8_t buf[KYBER_ETA2*KYBER_N/4]) +{ +#if KYBER_ETA2 == 2 @@ -1981,26 +1139,7 @@ index 776c085f9..5acd45cd9 100644 +#error "This implementation requires eta2 = 2" +#endif +} - --// In place inverse number theoretic transform of a given scalar, with pairs of --// entries of s->v being interpreted as elements of GF(3329^2). Just as with the --// number theoretic transform, this leaves off the first step of the normal iFFT --// to account for the fact that 3329 does not have a 512th root of unity, using --// the precomputed 128 roots of unity stored in |kInverseNTTRoots|. --static void scalar_inverse_ntt(scalar *s) { -- int step = DEGREE / 2; -- // `int` is used here because using `size_t` throughout caused a ~5% slowdown -- // with Clang 14 on Aarch64. -- for (int offset = 2; offset < DEGREE; offset <<= 1) { -- step >>= 1; -- int k = 0; -- for (int i = 0; i < step; i++) { -- uint32_t step_root = kInverseNTTRoots[i + step]; -- for (int j = k; j < k + offset; j++) { -- uint16_t odd = s->c[j + offset]; -- uint16_t even = s->c[j]; -- s->c[j] = reduce_once(odd + even); -- s->c[j + offset] = reduce(step_root * (even - odd + kPrime)); ++ +// +// ntt.c +// @@ -2034,8 +1173,8 @@ index 776c085f9..5acd45cd9 100644 + zetas[i] -= KYBER_Q; + if(zetas[i] < -KYBER_Q/2) + zetas[i] += KYBER_Q; -+ } -+} + } + } +*/ + +static const int16_t zetas[128] = { @@ -2070,7 +1209,26 @@ index 776c085f9..5acd45cd9 100644 +static int16_t fqmul(int16_t a, int16_t b) { + return montgomery_reduce((int32_t)a*b); +} -+ + +-// In place inverse number theoretic transform of a given scalar, with pairs of +-// entries of s->v being interpreted as elements of GF(3329^2). Just as with the +-// number theoretic transform, this leaves off the first step of the normal iFFT +-// to account for the fact that 3329 does not have a 512th root of unity, using +-// the precomputed 128 roots of unity stored in |kInverseNTTRoots|. +-static void scalar_inverse_ntt(scalar *s) { +- int step = DEGREE / 2; +- // `int` is used here because using `size_t` throughout caused a ~5% slowdown +- // with Clang 14 on Aarch64. +- for (int offset = 2; offset < DEGREE; offset <<= 1) { +- step >>= 1; +- int k = 0; +- for (int i = 0; i < step; i++) { +- uint32_t step_root = kInverseNTTRoots[i + step]; +- for (int j = k; j < k + offset; j++) { +- uint16_t odd = s->c[j + offset]; +- uint16_t even = s->c[j]; +- s->c[j] = reduce_once(odd + even); +- s->c[j + offset] = reduce(step_root * (even - odd + kPrime)); +/************************************************* +* Name: ntt +* @@ -2132,11 +1290,8 @@ index 776c085f9..5acd45cd9 100644 + + for(j = 0; j < 256; j++) + r[j] = fqmul(r[j], f); - } - --static void scalar_add(scalar *lhs, const scalar *rhs) { -- for (int i = 0; i < DEGREE; i++) { -- lhs->c[i] = reduce_once(lhs->c[i] + rhs->c[i]); ++} ++ +/************************************************* +* Name: basemul +* @@ -2155,8 +1310,11 @@ index 776c085f9..5acd45cd9 100644 + r[0] += fqmul(a[0], b[0]); + r[1] = fqmul(a[0], b[1]); + r[1] += fqmul(a[1], b[0]); -+} -+ + } + +-static void scalar_add(scalar *lhs, const scalar *rhs) { +- for (int i = 0; i < DEGREE; i++) { +- lhs->c[i] = reduce_once(lhs->c[i] + rhs->c[i]); +// +// poly.c +// @@ -2195,7 +1353,7 @@ index 776c085f9..5acd45cd9 100644 + r[2] = t[4] | (t[5] << 4); + r[3] = t[6] | (t[7] << 4); + r += 4; -+ } + } +#elif (KYBER_POLYCOMPRESSEDBYTES == 160) + for(i=0;i> 4) | (t[5] << 1) | (t[6] << 6); + r[4] = (t[6] >> 2) | (t[7] << 3); + r += 5; - } ++ } +#else +#error "KYBER_POLYCOMPRESSEDBYTES needs to be in {128, 160}" +#endif @@ -2791,8 +1949,15 @@ index 776c085f9..5acd45cd9 100644 +#else +#error "KYBER_POLYVECCOMPRESSEDBYTES needs to be in {320*KYBER_K, 352*KYBER_K}" +#endif -+} -+ + } + +-// Encodes an entire vector into 32*|RANK|*|bits| bytes. Note that since 256 +-// (DEGREE) is divisible by 8, the individual vector entries will always fill a +-// whole number of bytes, so we do not need to worry about bit packing here. +-static void vector_encode(uint8_t *out, const vector *a, int bits) { +- for (int i = 0; i < RANK; i++) { +- scalar_encode(out + i * bits * DEGREE / 8, &a->v[i], bits); +- } +/************************************************* +* Name: polyvec_tobytes +* @@ -2809,12 +1974,11 @@ index 776c085f9..5acd45cd9 100644 + poly_tobytes(r+i*KYBER_POLYBYTES, &a->vec[i]); } --// Encodes an entire vector into 32*|RANK|*|bits| bytes. Note that since 256 --// (DEGREE) is divisible by 8, the individual vector entries will always fill a --// whole number of bytes, so we do not need to worry about bit packing here. --static void vector_encode(uint8_t *out, const vector *a, int bits) { -- for (int i = 0; i < RANK; i++) { -- scalar_encode(out + i * bits * DEGREE / 8, &a->v[i], bits); +-// scalar_decode parses |DEGREE * bits| bits from |in| into |DEGREE| values in +-// |out|. It returns one on success and zero if any parsed value is >= +-// |kPrime|. +-static int scalar_decode(scalar *out, const uint8_t *in, int bits) { +- assert(bits <= (int)sizeof(*out->c) * 8 && bits != 1); +/************************************************* +* Name: polyvec_frombytes +* @@ -2831,7 +1995,9 @@ index 776c085f9..5acd45cd9 100644 + for(i=0;ivec[i], a+i*KYBER_POLYBYTES); +} -+ + +- uint8_t in_byte = 0; +- int in_byte_bits_left = 0; +/************************************************* +* Name: polyvec_ntt +* @@ -2845,7 +2011,10 @@ index 776c085f9..5acd45cd9 100644 + for(i=0;ivec[i]); +} -+ + +- for (int i = 0; i < DEGREE; i++) { +- uint16_t element = 0; +- int element_bits_done = 0; +/************************************************* +* Name: polyvec_invntt_tomont +* @@ -2860,7 +2029,13 @@ index 776c085f9..5acd45cd9 100644 + for(i=0;ivec[i]); +} -+ + +- while (element_bits_done < bits) { +- if (in_byte_bits_left == 0) { +- in_byte = *in; +- in++; +- in_byte_bits_left = 8; +- } +/************************************************* +* Name: polyvec_basemul_acc_montgomery +* @@ -2880,16 +2055,18 @@ index 776c085f9..5acd45cd9 100644 + for(i=1;ivec[i], &b->vec[i]); + poly_add(r, r, &t); - } -+ -+ poly_reduce(r); - } ++ } --// scalar_decode parses |DEGREE * bits| bits from |in| into |DEGREE| values in --// |out|. It returns one on success and zero if any parsed value is >= --// |kPrime|. --static int scalar_decode(scalar *out, const uint8_t *in, int bits) { -- assert(bits <= (int)sizeof(*out->c) * 8 && bits != 1); +- int chunk_bits = bits - element_bits_done; +- if (chunk_bits > in_byte_bits_left) { +- chunk_bits = in_byte_bits_left; +- } ++ poly_reduce(r); ++} + +- element |= (in_byte & kMasks[chunk_bits - 1]) << element_bits_done; +- in_byte_bits_left -= chunk_bits; +- in_byte >>= chunk_bits; +/************************************************* +* Name: polyvec_reduce +* @@ -2906,8 +2083,8 @@ index 776c085f9..5acd45cd9 100644 + poly_reduce(&r->vec[i]); +} -- uint8_t in_byte = 0; -- int in_byte_bits_left = 0; +- element_bits_done += chunk_bits; +- } +/************************************************* +* Name: polyvec_add +* @@ -2924,19 +2101,15 @@ index 776c085f9..5acd45cd9 100644 + poly_add(&r->vec[i], &a->vec[i], &b->vec[i]); +} -- for (int i = 0; i < DEGREE; i++) { -- uint16_t element = 0; -- int element_bits_done = 0; +- if (element >= kPrime) { +- return 0; +- } +- out->c[i] = element; +- } +// +// indcpa.c +// - -- while (element_bits_done < bits) { -- if (in_byte_bits_left == 0) { -- in_byte = *in; -- in++; -- in_byte_bits_left = 8; -- } ++ +/************************************************* +* Name: pack_pk +* @@ -2957,11 +2130,7 @@ index 776c085f9..5acd45cd9 100644 + for(i=0;i in_byte_bits_left) { -- chunk_bits = in_byte_bits_left; -- } ++ +/************************************************* +* Name: unpack_pk +* @@ -2972,19 +2141,34 @@ index 776c085f9..5acd45cd9 100644 +* - uint8_t *seed: pointer to output seed to generate matrix A +* - const uint8_t *packedpk: pointer to input serialized public key +**************************************************/ -+static void unpack_pk(polyvec *pk, ++static int unpack_pk(polyvec *pk, + uint8_t seed[KYBER_SYMBYTES], + const uint8_t packedpk[KYBER_INDCPA_PUBLICKEYBYTES]) +{ + size_t i; + polyvec_frombytes(pk, packedpk); ++ ++ // FIPS 203 encapsulation key check. We'll perform it even for Kyber. ++ uint8_t repacked[KYBER_POLYVECBYTES]; ++ polyvec_tobytes(repacked, pk); ++ ++ if(verify(repacked, packedpk, KYBER_POLYVECBYTES) != 0) ++ return 0; + + for(i=0;i>= chunk_bits; +-// scalar_decode_1 is |scalar_decode| specialised for |bits| == 1. +-static void scalar_decode_1(scalar *out, const uint8_t in[32]) { +- for (int i = 0; i < DEGREE; i += 8) { +- uint8_t in_byte = *in; +- in++; +- for (int j = 0; j < 8; j++) { +- out->c[i + j] = in_byte & 1; +- in_byte >>= 1; +- } +/************************************************* +* Name: pack_sk +* @@ -2997,9 +2181,7 @@ index 776c085f9..5acd45cd9 100644 +{ + polyvec_tobytes(r, sk); +} - -- element_bits_done += chunk_bits; -- } ++ +/************************************************* +* Name: unpack_sk +* @@ -3012,12 +2194,7 @@ index 776c085f9..5acd45cd9 100644 +{ + polyvec_frombytes(sk, packedsk); +} - -- if (element >= kPrime) { -- return 0; -- } -- out->c[i] = element; -- } ++ +/************************************************* +* Name: pack_ciphertext +* @@ -3034,8 +2211,7 @@ index 776c085f9..5acd45cd9 100644 + polyvec_compress(r, b); + poly_compress(r+KYBER_POLYVECCOMPRESSEDBYTES, v); +} - -- return 1; ++ +/************************************************* +* Name: unpack_ciphertext +* @@ -3050,17 +2226,8 @@ index 776c085f9..5acd45cd9 100644 +{ + polyvec_decompress(b, c); + poly_decompress(v, c+KYBER_POLYVECCOMPRESSEDBYTES); - } - --// scalar_decode_1 is |scalar_decode| specialised for |bits| == 1. --static void scalar_decode_1(scalar *out, const uint8_t in[32]) { -- for (int i = 0; i < DEGREE; i += 8) { -- uint8_t in_byte = *in; -- in++; -- for (int j = 0; j < 8; j++) { -- out->c[i + j] = in_byte & 1; -- in_byte >>= 1; -- } ++} ++ +/************************************************* +* Name: rej_uniform +* @@ -3149,8 +2316,8 @@ index 776c085f9..5acd45cd9 100644 } } - return 1; --} -- + } + -// Compresses (lossily) an input |x| mod 3329 into |bits| many bits by grouping -// numbers close to each other together. The formula used is -// round(2^|bits|/kPrime*x) mod 2^|bits|. @@ -3192,12 +2359,6 @@ index 776c085f9..5acd45cd9 100644 -static void scalar_compress(scalar *s, int bits) { - for (int i = 0; i < DEGREE; i++) { - s->c[i] = compress(s->c[i], bits); -- } - } - --static void scalar_decompress(scalar *s, int bits) { -- for (int i = 0; i < DEGREE; i++) { -- s->c[i] = decompress(s->c[i], bits); +/************************************************* +* Name: indcpa_keypair +* @@ -3246,9 +2407,9 @@ index 776c085f9..5acd45cd9 100644 + pack_pk(pk, &pkpv, publicseed); } --static void vector_compress(vector *a, int bits) { -- for (int i = 0; i < RANK; i++) { -- scalar_compress(&a->v[i], bits); +-static void scalar_decompress(scalar *s, int bits) { +- for (int i = 0; i < DEGREE; i++) { +- s->c[i] = decompress(s->c[i], bits); - } +/************************************************* +* Name: indcpa_enc @@ -3266,7 +2427,7 @@ index 776c085f9..5acd45cd9 100644 +* (of length KYBER_SYMBYTES) to deterministically +* generate all randomness +**************************************************/ -+static void indcpa_enc(uint8_t c[KYBER_INDCPA_BYTES], ++static int indcpa_enc(uint8_t c[KYBER_INDCPA_BYTES], + const uint8_t m[KYBER_INDCPA_MSGBYTES], + const uint8_t pk[KYBER_INDCPA_PUBLICKEYBYTES], + const uint8_t coins[KYBER_SYMBYTES]) @@ -3277,7 +2438,9 @@ index 776c085f9..5acd45cd9 100644 + polyvec sp, pkpv, ep, at[KYBER_K], b; + poly v, k, epp; + -+ unpack_pk(&pkpv, seed, pk); ++ if (!unpack_pk(&pkpv, seed, pk)) ++ return 0; ++ + poly_frommsg(&k, m); + gen_at(at, seed); + @@ -3305,11 +2468,12 @@ index 776c085f9..5acd45cd9 100644 + poly_reduce(&v); + + pack_ciphertext(c, &b, &v); ++ return 1; } --static void vector_decompress(vector *a, int bits) { +-static void vector_compress(vector *a, int bits) { - for (int i = 0; i < RANK; i++) { -- scalar_decompress(&a->v[i], bits); +- scalar_compress(&a->v[i], bits); - } +/************************************************* +* Name: indcpa_dec @@ -3344,35 +2508,88 @@ index 776c085f9..5acd45cd9 100644 + poly_tomsg(m, &mp); } +-static void vector_decompress(vector *a, int bits) { +- for (int i = 0; i < RANK; i++) { +- scalar_decompress(&a->v[i], bits); +- } ++// ++// fips202.c ++// ++ ++/* Based on the public domain implementation in crypto_hash/keccakc512/simple/ from ++ * http://bench.cr.yp.to/supercop.html by Ronny Van Keer and the public domain "TweetFips202" ++ * implementation from https://twitter.com/tweetfips202 by Gilles Van Assche, Daniel J. Bernstein, ++ * and Peter Schwabe */ ++ ++#define NROUNDS 24 ++#define ROL(a, offset) ((a << offset) ^ (a >> (64-offset))) ++ ++/************************************************* ++* Name: load64 ++* ++* Description: Load 8 bytes into uint64_t in little-endian order ++* ++* Arguments: - const uint8_t *x: pointer to input byte array ++* ++* Returns the loaded 64-bit unsigned integer ++**************************************************/ ++static uint64_t load64(const uint8_t x[8]) { ++ unsigned int i; ++ uint64_t r = 0; ++ ++ for(i=0;i<8;i++) ++ r |= (uint64_t)x[i] << 8*i; ++ ++ return r; + } + -struct public_key { - vector t; - uint8_t rho[32]; - uint8_t public_key_hash[32]; - matrix m; --}; +/************************************************* -+* Name: kyber_shake128_absorb ++* Name: store64 +* -+* Description: Absorb step of the SHAKE128 specialized for the Kyber context. ++* Description: Store a 64-bit integer to array of 8 bytes in little-endian order +* -+* Arguments: - keccak_state *state: pointer to (uninitialized) output Keccak state -+* - const uint8_t *seed: pointer to KYBER_SYMBYTES input to be absorbed into state -+* - uint8_t i: additional byte of input -+* - uint8_t j: additional byte of input ++* Arguments: - uint8_t *x: pointer to the output byte array (allocated) ++* - uint64_t u: input 64-bit unsigned integer +**************************************************/ -+void kyber_shake128_absorb(keccak_state *state, -+ const uint8_t seed[KYBER_SYMBYTES], -+ uint8_t x, -+ uint8_t y) -+{ -+ uint8_t extseed[KYBER_SYMBYTES+2]; ++static void store64(uint8_t x[8], uint64_t u) { ++ unsigned int i; + -+ memcpy(extseed, seed, KYBER_SYMBYTES); -+ extseed[KYBER_SYMBYTES+0] = x; -+ extseed[KYBER_SYMBYTES+1] = y; -+ -+ shake128_absorb_once(state, extseed, sizeof(extseed)); ++ for(i=0;i<8;i++) ++ x[i] = u >> 8*i; +} ++ ++/* Keccak round constants */ ++static const uint64_t KeccakF_RoundConstants[NROUNDS] = { ++ (uint64_t)0x0000000000000001ULL, ++ (uint64_t)0x0000000000008082ULL, ++ (uint64_t)0x800000000000808aULL, ++ (uint64_t)0x8000000080008000ULL, ++ (uint64_t)0x000000000000808bULL, ++ (uint64_t)0x0000000080000001ULL, ++ (uint64_t)0x8000000080008081ULL, ++ (uint64_t)0x8000000000008009ULL, ++ (uint64_t)0x000000000000008aULL, ++ (uint64_t)0x0000000000000088ULL, ++ (uint64_t)0x0000000080008009ULL, ++ (uint64_t)0x000000008000000aULL, ++ (uint64_t)0x000000008000808bULL, ++ (uint64_t)0x800000000000008bULL, ++ (uint64_t)0x8000000000008089ULL, ++ (uint64_t)0x8000000000008003ULL, ++ (uint64_t)0x8000000000008002ULL, ++ (uint64_t)0x8000000000000080ULL, ++ (uint64_t)0x000000000000800aULL, ++ (uint64_t)0x800000008000000aULL, ++ (uint64_t)0x8000000080008081ULL, ++ (uint64_t)0x8000000000008080ULL, ++ (uint64_t)0x0000000080000001ULL, ++ (uint64_t)0x8000000080008008ULL + }; -static struct public_key *public_key_from_external( - const struct KYBER_public_key *external) { @@ -3382,24 +2599,274 @@ index 776c085f9..5acd45cd9 100644 - "Kyber public key align incorrect"); - return (struct public_key *)external; +/************************************************* -+* Name: kyber_shake256_prf ++* Name: KeccakF1600_StatePermute +* -+* Description: Usage of SHAKE256 as a PRF, concatenates secret and public input -+* and then generates outlen bytes of SHAKE256 output ++* Description: The Keccak F1600 Permutation +* -+* Arguments: - uint8_t *out: pointer to output -+* - size_t outlen: number of requested output bytes -+* - const uint8_t *key: pointer to the key (of length KYBER_SYMBYTES) -+* - uint8_t nonce: single-byte nonce (public PRF input) ++* Arguments: - uint64_t *state: pointer to input/output Keccak state +**************************************************/ -+static void kyber_shake256_prf(uint8_t *out, size_t outlen, const uint8_t key[KYBER_SYMBYTES], uint8_t nonce) ++static void KeccakF1600_StatePermute(uint64_t state[25]) +{ -+ uint8_t extkey[KYBER_SYMBYTES+1]; ++ int round; + -+ memcpy(extkey, key, KYBER_SYMBYTES); -+ extkey[KYBER_SYMBYTES] = nonce; ++ uint64_t Aba, Abe, Abi, Abo, Abu; ++ uint64_t Aga, Age, Agi, Ago, Agu; ++ uint64_t Aka, Ake, Aki, Ako, Aku; ++ uint64_t Ama, Ame, Ami, Amo, Amu; ++ uint64_t Asa, Ase, Asi, Aso, Asu; ++ uint64_t BCa, BCe, BCi, BCo, BCu; ++ uint64_t Da, De, Di, Do, Du; ++ uint64_t Eba, Ebe, Ebi, Ebo, Ebu; ++ uint64_t Ega, Ege, Egi, Ego, Egu; ++ uint64_t Eka, Eke, Eki, Eko, Eku; ++ uint64_t Ema, Eme, Emi, Emo, Emu; ++ uint64_t Esa, Ese, Esi, Eso, Esu; + -+ shake256(out, outlen, extkey, sizeof(extkey)); ++ //copyFromState(A, state) ++ Aba = state[ 0]; ++ Abe = state[ 1]; ++ Abi = state[ 2]; ++ Abo = state[ 3]; ++ Abu = state[ 4]; ++ Aga = state[ 5]; ++ Age = state[ 6]; ++ Agi = state[ 7]; ++ Ago = state[ 8]; ++ Agu = state[ 9]; ++ Aka = state[10]; ++ Ake = state[11]; ++ Aki = state[12]; ++ Ako = state[13]; ++ Aku = state[14]; ++ Ama = state[15]; ++ Ame = state[16]; ++ Ami = state[17]; ++ Amo = state[18]; ++ Amu = state[19]; ++ Asa = state[20]; ++ Ase = state[21]; ++ Asi = state[22]; ++ Aso = state[23]; ++ Asu = state[24]; ++ ++ for(round = 0; round < NROUNDS; round += 2) { ++ // prepareTheta ++ BCa = Aba^Aga^Aka^Ama^Asa; ++ BCe = Abe^Age^Ake^Ame^Ase; ++ BCi = Abi^Agi^Aki^Ami^Asi; ++ BCo = Abo^Ago^Ako^Amo^Aso; ++ BCu = Abu^Agu^Aku^Amu^Asu; ++ ++ //thetaRhoPiChiIotaPrepareTheta(round, A, E) ++ Da = BCu^ROL(BCe, 1); ++ De = BCa^ROL(BCi, 1); ++ Di = BCe^ROL(BCo, 1); ++ Do = BCi^ROL(BCu, 1); ++ Du = BCo^ROL(BCa, 1); ++ ++ Aba ^= Da; ++ BCa = Aba; ++ Age ^= De; ++ BCe = ROL(Age, 44); ++ Aki ^= Di; ++ BCi = ROL(Aki, 43); ++ Amo ^= Do; ++ BCo = ROL(Amo, 21); ++ Asu ^= Du; ++ BCu = ROL(Asu, 14); ++ Eba = BCa ^((~BCe)& BCi ); ++ Eba ^= (uint64_t)KeccakF_RoundConstants[round]; ++ Ebe = BCe ^((~BCi)& BCo ); ++ Ebi = BCi ^((~BCo)& BCu ); ++ Ebo = BCo ^((~BCu)& BCa ); ++ Ebu = BCu ^((~BCa)& BCe ); ++ ++ Abo ^= Do; ++ BCa = ROL(Abo, 28); ++ Agu ^= Du; ++ BCe = ROL(Agu, 20); ++ Aka ^= Da; ++ BCi = ROL(Aka, 3); ++ Ame ^= De; ++ BCo = ROL(Ame, 45); ++ Asi ^= Di; ++ BCu = ROL(Asi, 61); ++ Ega = BCa ^((~BCe)& BCi ); ++ Ege = BCe ^((~BCi)& BCo ); ++ Egi = BCi ^((~BCo)& BCu ); ++ Ego = BCo ^((~BCu)& BCa ); ++ Egu = BCu ^((~BCa)& BCe ); ++ ++ Abe ^= De; ++ BCa = ROL(Abe, 1); ++ Agi ^= Di; ++ BCe = ROL(Agi, 6); ++ Ako ^= Do; ++ BCi = ROL(Ako, 25); ++ Amu ^= Du; ++ BCo = ROL(Amu, 8); ++ Asa ^= Da; ++ BCu = ROL(Asa, 18); ++ Eka = BCa ^((~BCe)& BCi ); ++ Eke = BCe ^((~BCi)& BCo ); ++ Eki = BCi ^((~BCo)& BCu ); ++ Eko = BCo ^((~BCu)& BCa ); ++ Eku = BCu ^((~BCa)& BCe ); ++ ++ Abu ^= Du; ++ BCa = ROL(Abu, 27); ++ Aga ^= Da; ++ BCe = ROL(Aga, 36); ++ Ake ^= De; ++ BCi = ROL(Ake, 10); ++ Ami ^= Di; ++ BCo = ROL(Ami, 15); ++ Aso ^= Do; ++ BCu = ROL(Aso, 56); ++ Ema = BCa ^((~BCe)& BCi ); ++ Eme = BCe ^((~BCi)& BCo ); ++ Emi = BCi ^((~BCo)& BCu ); ++ Emo = BCo ^((~BCu)& BCa ); ++ Emu = BCu ^((~BCa)& BCe ); ++ ++ Abi ^= Di; ++ BCa = ROL(Abi, 62); ++ Ago ^= Do; ++ BCe = ROL(Ago, 55); ++ Aku ^= Du; ++ BCi = ROL(Aku, 39); ++ Ama ^= Da; ++ BCo = ROL(Ama, 41); ++ Ase ^= De; ++ BCu = ROL(Ase, 2); ++ Esa = BCa ^((~BCe)& BCi ); ++ Ese = BCe ^((~BCi)& BCo ); ++ Esi = BCi ^((~BCo)& BCu ); ++ Eso = BCo ^((~BCu)& BCa ); ++ Esu = BCu ^((~BCa)& BCe ); ++ ++ // prepareTheta ++ BCa = Eba^Ega^Eka^Ema^Esa; ++ BCe = Ebe^Ege^Eke^Eme^Ese; ++ BCi = Ebi^Egi^Eki^Emi^Esi; ++ BCo = Ebo^Ego^Eko^Emo^Eso; ++ BCu = Ebu^Egu^Eku^Emu^Esu; ++ ++ //thetaRhoPiChiIotaPrepareTheta(round+1, E, A) ++ Da = BCu^ROL(BCe, 1); ++ De = BCa^ROL(BCi, 1); ++ Di = BCe^ROL(BCo, 1); ++ Do = BCi^ROL(BCu, 1); ++ Du = BCo^ROL(BCa, 1); ++ ++ Eba ^= Da; ++ BCa = Eba; ++ Ege ^= De; ++ BCe = ROL(Ege, 44); ++ Eki ^= Di; ++ BCi = ROL(Eki, 43); ++ Emo ^= Do; ++ BCo = ROL(Emo, 21); ++ Esu ^= Du; ++ BCu = ROL(Esu, 14); ++ Aba = BCa ^((~BCe)& BCi ); ++ Aba ^= (uint64_t)KeccakF_RoundConstants[round+1]; ++ Abe = BCe ^((~BCi)& BCo ); ++ Abi = BCi ^((~BCo)& BCu ); ++ Abo = BCo ^((~BCu)& BCa ); ++ Abu = BCu ^((~BCa)& BCe ); ++ ++ Ebo ^= Do; ++ BCa = ROL(Ebo, 28); ++ Egu ^= Du; ++ BCe = ROL(Egu, 20); ++ Eka ^= Da; ++ BCi = ROL(Eka, 3); ++ Eme ^= De; ++ BCo = ROL(Eme, 45); ++ Esi ^= Di; ++ BCu = ROL(Esi, 61); ++ Aga = BCa ^((~BCe)& BCi ); ++ Age = BCe ^((~BCi)& BCo ); ++ Agi = BCi ^((~BCo)& BCu ); ++ Ago = BCo ^((~BCu)& BCa ); ++ Agu = BCu ^((~BCa)& BCe ); ++ ++ Ebe ^= De; ++ BCa = ROL(Ebe, 1); ++ Egi ^= Di; ++ BCe = ROL(Egi, 6); ++ Eko ^= Do; ++ BCi = ROL(Eko, 25); ++ Emu ^= Du; ++ BCo = ROL(Emu, 8); ++ Esa ^= Da; ++ BCu = ROL(Esa, 18); ++ Aka = BCa ^((~BCe)& BCi ); ++ Ake = BCe ^((~BCi)& BCo ); ++ Aki = BCi ^((~BCo)& BCu ); ++ Ako = BCo ^((~BCu)& BCa ); ++ Aku = BCu ^((~BCa)& BCe ); ++ ++ Ebu ^= Du; ++ BCa = ROL(Ebu, 27); ++ Ega ^= Da; ++ BCe = ROL(Ega, 36); ++ Eke ^= De; ++ BCi = ROL(Eke, 10); ++ Emi ^= Di; ++ BCo = ROL(Emi, 15); ++ Eso ^= Do; ++ BCu = ROL(Eso, 56); ++ Ama = BCa ^((~BCe)& BCi ); ++ Ame = BCe ^((~BCi)& BCo ); ++ Ami = BCi ^((~BCo)& BCu ); ++ Amo = BCo ^((~BCu)& BCa ); ++ Amu = BCu ^((~BCa)& BCe ); ++ ++ Ebi ^= Di; ++ BCa = ROL(Ebi, 62); ++ Ego ^= Do; ++ BCe = ROL(Ego, 55); ++ Eku ^= Du; ++ BCi = ROL(Eku, 39); ++ Ema ^= Da; ++ BCo = ROL(Ema, 41); ++ Ese ^= De; ++ BCu = ROL(Ese, 2); ++ Asa = BCa ^((~BCe)& BCi ); ++ Ase = BCe ^((~BCi)& BCo ); ++ Asi = BCi ^((~BCo)& BCu ); ++ Aso = BCo ^((~BCu)& BCa ); ++ Asu = BCu ^((~BCa)& BCe ); ++ } ++ ++ //copyToState(state, A) ++ state[ 0] = Aba; ++ state[ 1] = Abe; ++ state[ 2] = Abi; ++ state[ 3] = Abo; ++ state[ 4] = Abu; ++ state[ 5] = Aga; ++ state[ 6] = Age; ++ state[ 7] = Agi; ++ state[ 8] = Ago; ++ state[ 9] = Agu; ++ state[10] = Aka; ++ state[11] = Ake; ++ state[12] = Aki; ++ state[13] = Ako; ++ state[14] = Aku; ++ state[15] = Ama; ++ state[16] = Ame; ++ state[17] = Ami; ++ state[18] = Amo; ++ state[19] = Amu; ++ state[20] = Asa; ++ state[21] = Ase; ++ state[22] = Asi; ++ state[23] = Aso; ++ state[24] = Asu; } -struct private_key { @@ -3407,9 +2874,6 @@ index 776c085f9..5acd45cd9 100644 - vector s; - uint8_t fo_failure_secret[32]; -}; -+// -+// kem.c -+// -static struct private_key *private_key_from_external( - const struct KYBER_private_key *external) { @@ -3435,14 +2899,98 @@ index 776c085f9..5acd45cd9 100644 - uint8_t *vector_output; - if (!CBB_add_space(out, &vector_output, kEncodedVectorSize)) { - return 0; -- } ++/************************************************* ++* Name: keccak_squeeze ++* ++* Description: Squeeze step of Keccak. Squeezes arbitratrily many bytes. ++* Modifies the state. Can be called multiple times to keep ++* squeezing, i.e., is incremental. ++* ++* Arguments: - uint8_t *out: pointer to output ++* - size_t outlen: number of bytes to be squeezed (written to out) ++* - uint64_t *s: pointer to input/output Keccak state ++* - unsigned int pos: number of bytes in current block already squeezed ++* - unsigned int r: rate in bytes (e.g., 168 for SHAKE128) ++* ++* Returns new position pos in current block ++**************************************************/ ++static unsigned int keccak_squeeze(uint8_t *out, ++ size_t outlen, ++ uint64_t s[25], ++ unsigned int pos, ++ unsigned int r) ++{ ++ unsigned int i; ++ ++ while(outlen) { ++ if(pos == r) { ++ KeccakF1600_StatePermute(s); ++ pos = 0; ++ } ++ for(i=pos;i < r && i < pos+outlen; i++) ++ *out++ = s[i/8] >> 8*(i%8); ++ outlen -= i-pos; ++ pos = i; + } - vector_encode(vector_output, &pub->t, kLog2Prime); - if (!CBB_add_bytes(out, pub->rho, sizeof(pub->rho))) { - return 0; -- } ++ ++ return pos; ++} ++ ++/************************************************* ++* Name: keccak_absorb ++* ++* Description: Absorb step of Keccak; incremental. ++* ++* Arguments: - uint64_t *s: pointer to Keccak state ++* - unsigned int pos: position in current block to be absorbed ++* - unsigned int r: rate in bytes (e.g., 168 for SHAKE128) ++* - const uint8_t *in: pointer to input to be absorbed into s ++* - size_t inlen: length of input in bytes ++* ++* Returns new position pos in current block ++**************************************************/ ++static unsigned int keccak_absorb(uint64_t s[25], ++ unsigned int pos, ++ unsigned int r, ++ const uint8_t *in, ++ size_t inlen) ++{ ++ unsigned int i; ++ ++ while(pos+inlen >= r) { ++ for(i=pos;ipub)) { - abort(); -+// Modified crypto_kem_keypair to BoringSSL style API -+void generate_key(struct public_key *out_pub, struct private_key *out_priv, -+ const uint8_t seed[KYBER_GENERATE_KEY_BYTES]) ++ ++/************************************************* ++* Name: keccak_absorb_once ++* ++* Description: Absorb step of Keccak; ++* non-incremental, starts by zeroeing the state. ++* ++* Arguments: - uint64_t *s: pointer to (uninitialized) output Keccak state ++* - unsigned int r: rate in bytes (e.g., 168 for SHAKE128) ++* - const uint8_t *in: pointer to input to be absorbed into s ++* - size_t inlen: length of input in bytes ++* - uint8_t p: domain-separation byte for different Keccak-derived functions ++**************************************************/ ++static void keccak_absorb_once(uint64_t s[25], ++ unsigned int r, ++ const uint8_t *in, ++ size_t inlen, ++ uint8_t p) +{ -+ size_t i; -+ uint8_t* pk = &out_pub->opaque[0]; -+ uint8_t* sk = &out_priv->opaque[0]; ++ unsigned int i; + -+ indcpa_keypair(pk, sk, seed); -+ for(i=0;iopaque[0]; -+ uint8_t *ct = out_ciphertext; -+ -+ uint8_t buf[2*KYBER_SYMBYTES]; -+ /* Will contain key, coins */ -+ uint8_t kr[2*KYBER_SYMBYTES]; -+ -+ memcpy(buf, seed, KYBER_SYMBYTES); -+ -+ if (ipd == 0) { -+ /* Don't release system RNG output */ -+ hash_h(buf, buf, KYBER_SYMBYTES); ++ while(inlen >= r) { ++ for(i=0;ipub.public_key_hash, sizeof(priv->pub.public_key_hash), @@ -3638,20 +3180,39 @@ index 776c085f9..5acd45cd9 100644 - for (int i = 0; i < 32; i++) { - input[i] = constant_time_select_8(mask, prekey_and_randomness[i], - priv->fo_failure_secret[i]); -+ /* Multitarget countermeasure for coins + contributory KEM */ -+ hash_h(buf+KYBER_SYMBYTES, pk, KYBER_PUBLICKEYBYTES); -+ hash_g(kr, buf, 2*KYBER_SYMBYTES); ++ for(i=0;is, SHAKE128_RATE, in, inlen, 0x1F); ++ state->pos = SHAKE128_RATE; } -// kyber_parse_public_key_no_hash parses |in| into |pub| but doesn't calculate @@ -3678,11 +3247,314 @@ index 776c085f9..5acd45cd9 100644 - !vector_decode(&pub->t, CBS_data(&t_bytes), kLog2Prime) || - !CBS_copy_bytes(in, pub->rho, sizeof(pub->rho))) { - return 0; -+// Internal version that allows us to select between initial public draft -+// (when ipd=1) and round3 kyber (ipd=0). -+void decap2(uint8_t out_shared_key[KYBER_SSBYTES], +- } +- matrix_expand(&pub->m, pub->rho); +- return 1; ++/************************************************* ++* Name: shake128_squeezeblocks ++* ++* Description: Squeeze step of SHAKE128 XOF. Squeezes full blocks of ++* SHAKE128_RATE bytes each. Can be called multiple times ++* to keep squeezing. Assumes new block has not yet been ++* started (state->pos = SHAKE128_RATE). ++* ++* Arguments: - uint8_t *out: pointer to output blocks ++* - size_t nblocks: number of blocks to be squeezed (written to output) ++* - keccak_state *s: pointer to input/output Keccak state ++**************************************************/ ++static void shake128_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state) ++{ ++ keccak_squeezeblocks(out, nblocks, state->s, SHAKE128_RATE); ++} ++ ++/************************************************* ++* Name: shake256_squeeze ++* ++* Description: Squeeze step of SHAKE256 XOF. Squeezes arbitraily many ++* bytes. Can be called multiple times to keep squeezing. ++* ++* Arguments: - uint8_t *out: pointer to output blocks ++* - size_t outlen : number of bytes to be squeezed (written to output) ++* - keccak_state *s: pointer to input/output Keccak state ++**************************************************/ ++static void shake256_squeeze(uint8_t *out, size_t outlen, keccak_state *state) ++{ ++ state->pos = keccak_squeeze(out, outlen, state->s, state->pos, SHAKE256_RATE); ++} ++ ++/************************************************* ++* Name: shake256_absorb_once ++* ++* Description: Initialize, absorb into and finalize SHAKE256 XOF; non-incremental. ++* ++* Arguments: - keccak_state *state: pointer to (uninitialized) output Keccak state ++* - const uint8_t *in: pointer to input to be absorbed into s ++* - size_t inlen: length of input in bytes ++**************************************************/ ++static void shake256_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen) ++{ ++ keccak_absorb_once(state->s, SHAKE256_RATE, in, inlen, 0x1F); ++ state->pos = SHAKE256_RATE; ++} ++ ++/************************************************* ++* Name: shake256_squeezeblocks ++* ++* Description: Squeeze step of SHAKE256 XOF. Squeezes full blocks of ++* SHAKE256_RATE bytes each. Can be called multiple times ++* to keep squeezing. Assumes next block has not yet been ++* started (state->pos = SHAKE256_RATE). ++* ++* Arguments: - uint8_t *out: pointer to output blocks ++* - size_t nblocks: number of blocks to be squeezed (written to output) ++* - keccak_state *s: pointer to input/output Keccak state ++**************************************************/ ++static void shake256_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state) ++{ ++ keccak_squeezeblocks(out, nblocks, state->s, SHAKE256_RATE); ++} ++ ++/************************************************* ++* Name: shake256_absorb ++* ++* Description: Absorb step of the SHAKE256 XOF; incremental. ++* ++* Arguments: - keccak_state *state: pointer to (initialized) output Keccak state ++* - const uint8_t *in: pointer to input to be absorbed into s ++* - size_t inlen: length of input in bytes ++**************************************************/ ++static void shake256_absorb(keccak_state *state, const uint8_t *in, size_t inlen) ++{ ++ state->pos = keccak_absorb(state->s, state->pos, SHAKE256_RATE, in, inlen); ++} ++ ++/************************************************* ++* Name: shake256_finalize ++* ++* Description: Finalize absorb step of the SHAKE256 XOF. ++* ++* Arguments: - keccak_state *state: pointer to Keccak state ++**************************************************/ ++static void shake256_finalize(keccak_state *state) ++{ ++ keccak_finalize(state->s, state->pos, SHAKE256_RATE, 0x1F); ++ state->pos = SHAKE256_RATE; ++} ++ ++/************************************************* ++* Name: keccak_init ++* ++* Description: Initializes the Keccak state. ++* ++* Arguments: - uint64_t *s: pointer to Keccak state ++**************************************************/ ++static void keccak_init(uint64_t s[25]) ++{ ++ unsigned int i; ++ for(i=0;i<25;i++) ++ s[i] = 0; ++} ++ ++/************************************************* ++* Name: shake256_init ++* ++* Description: Initilizes Keccak state for use as SHAKE256 XOF ++* ++* Arguments: - keccak_state *state: pointer to (uninitialized) Keccak state ++**************************************************/ ++static void shake256_init(keccak_state *state) ++{ ++ keccak_init(state->s); ++ state->pos = 0; ++} ++ ++ ++/************************************************* ++* Name: shake256 ++* ++* Description: SHAKE256 XOF with non-incremental API ++* ++* Arguments: - uint8_t *out: pointer to output ++* - size_t outlen: requested output length in bytes ++* - const uint8_t *in: pointer to input ++* - size_t inlen: length of input in bytes ++**************************************************/ ++static void shake256(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen) ++{ ++ size_t nblocks; ++ keccak_state state; ++ ++ shake256_absorb_once(&state, in, inlen); ++ nblocks = outlen/SHAKE256_RATE; ++ shake256_squeezeblocks(out, nblocks, &state); ++ outlen -= nblocks*SHAKE256_RATE; ++ out += nblocks*SHAKE256_RATE; ++ shake256_squeeze(out, outlen, &state); ++} ++ ++/************************************************* ++* Name: sha3_256 ++* ++* Description: SHA3-256 with non-incremental API ++* ++* Arguments: - uint8_t *h: pointer to output (32 bytes) ++* - const uint8_t *in: pointer to input ++* - size_t inlen: length of input in bytes ++**************************************************/ ++static void sha3_256(uint8_t h[32], const uint8_t *in, size_t inlen) ++{ ++ unsigned int i; ++ uint64_t s[25]; ++ ++ keccak_absorb_once(s, SHA3_256_RATE, in, inlen, 0x06); ++ KeccakF1600_StatePermute(s); ++ for(i=0;i<4;i++) ++ store64(h+8*i,s[i]); ++} ++ ++/************************************************* ++* Name: sha3_512 ++* ++* Description: SHA3-512 with non-incremental API ++* ++* Arguments: - uint8_t *h: pointer to output (64 bytes) ++* - const uint8_t *in: pointer to input ++* - size_t inlen: length of input in bytes ++**************************************************/ ++static void sha3_512(uint8_t h[64], const uint8_t *in, size_t inlen) ++{ ++ unsigned int i; ++ uint64_t s[25]; ++ ++ keccak_absorb_once(s, SHA3_512_RATE, in, inlen, 0x06); ++ KeccakF1600_StatePermute(s); ++ for(i=0;i<8;i++) ++ store64(h+8*i,s[i]); ++} ++ ++// ++// symmetric-shake.c ++// ++ ++/************************************************* ++* Name: kyber_shake128_absorb ++* ++* Description: Absorb step of the SHAKE128 specialized for the Kyber context. ++* ++* Arguments: - keccak_state *state: pointer to (uninitialized) output Keccak state ++* - const uint8_t *seed: pointer to KYBER_SYMBYTES input to be absorbed into state ++* - uint8_t i: additional byte of input ++* - uint8_t j: additional byte of input ++**************************************************/ ++static void kyber_shake128_absorb(keccak_state *state, ++ const uint8_t seed[KYBER_SYMBYTES], ++ uint8_t x, ++ uint8_t y) ++{ ++ uint8_t extseed[KYBER_SYMBYTES+2]; ++ ++ memcpy(extseed, seed, KYBER_SYMBYTES); ++ extseed[KYBER_SYMBYTES+0] = x; ++ extseed[KYBER_SYMBYTES+1] = y; ++ ++ shake128_absorb_once(state, extseed, sizeof(extseed)); + } + +-int KYBER_parse_public_key(struct KYBER_public_key *public_key, CBS *in) { +- struct public_key *pub = public_key_from_external(public_key); +- CBS orig_in = *in; +- if (!kyber_parse_public_key_no_hash(pub, in) || // +- CBS_len(in) != 0) { ++/************************************************* ++* Name: kyber_shake256_prf ++* ++* Description: Usage of SHAKE256 as a PRF, concatenates secret and public input ++* and then generates outlen bytes of SHAKE256 output ++* ++* Arguments: - uint8_t *out: pointer to output ++* - size_t outlen: number of requested output bytes ++* - const uint8_t *key: pointer to the key (of length KYBER_SYMBYTES) ++* - uint8_t nonce: single-byte nonce (public PRF input) ++**************************************************/ ++static void kyber_shake256_prf(uint8_t *out, size_t outlen, const uint8_t key[KYBER_SYMBYTES], uint8_t nonce) ++{ ++ uint8_t extkey[KYBER_SYMBYTES+1]; ++ ++ memcpy(extkey, key, KYBER_SYMBYTES); ++ extkey[KYBER_SYMBYTES] = nonce; ++ ++ shake256(out, outlen, extkey, sizeof(extkey)); ++} ++ ++// ++// kem.c ++// ++ ++// Modified crypto_kem_keypair to BoringSSL style API ++void generate_key(struct public_key *out_pub, struct private_key *out_priv, ++ const uint8_t seed[KYBER_GENERATE_KEY_BYTES]) ++{ ++ size_t i; ++ uint8_t* pk = &out_pub->opaque[0]; ++ uint8_t* sk = &out_priv->opaque[0]; ++ ++ indcpa_keypair(pk, sk, seed); ++ for(i=0;iopaque[0]; ++ uint8_t *ct = out_ciphertext; ++ ++ uint8_t buf[2*KYBER_SYMBYTES]; ++ /* Will contain key, coins */ ++ uint8_t kr[2*KYBER_SYMBYTES]; ++ ++ memcpy(buf, seed, KYBER_SYMBYTES); ++ ++ /* Don't release system RNG output */ ++ hash_h(buf, buf, KYBER_SYMBYTES); ++ ++ /* Multitarget countermeasure for coins + contributory KEM */ ++ hash_h(buf+KYBER_SYMBYTES, pk, KYBER_PUBLICKEYBYTES); ++ hash_g(kr, buf, 2*KYBER_SYMBYTES); ++ ++ /* coins are in kr+KYBER_SYMBYTES */ ++ if(!indcpa_enc(ct, buf, pk, kr+KYBER_SYMBYTES)) + return 0; ++ ++ if (mlkem == 1) { ++ memcpy(ss, kr, KYBER_SYMBYTES); ++ } else { ++ /* overwrite coins in kr with H(c) */ ++ hash_h(kr+KYBER_SYMBYTES, ct, KYBER_CIPHERTEXTBYTES); ++ /* hash concatenation of pre-k and H(c) to k */ ++ kdf(ss, kr, 2*KYBER_SYMBYTES); + } +- BORINGSSL_keccak(pub->public_key_hash, sizeof(pub->public_key_hash), +- CBS_data(&orig_in), CBS_len(&orig_in), boringssl_sha3_256); + return 1; + } + +-int KYBER_marshal_private_key(CBB *out, +- const struct KYBER_private_key *private_key) { +- const struct private_key *const priv = private_key_from_external(private_key); +- uint8_t *s_output; +- if (!CBB_add_space(out, &s_output, kEncodedVectorSize)) { +- return 0; ++// Modified crypto_kem_decap to BoringSSL style API ++void decap(uint8_t out_shared_key[KYBER_SSBYTES], + const struct private_key *in_priv, -+ const uint8_t *ct, size_t ciphertext_len, int ipd) ++ const uint8_t *ct, size_t ciphertext_len, int mlkem) +{ + uint8_t *ss = out_shared_key; + const uint8_t *sk = &in_priv->opaque[0]; @@ -3708,17 +3580,15 @@ index 776c085f9..5acd45cd9 100644 + + fail = verify(ct, cmp, KYBER_CIPHERTEXTBYTES); } -- matrix_expand(&pub->m, pub->rho); -- return 1; --} - --int KYBER_parse_public_key(struct KYBER_public_key *public_key, CBS *in) { -- struct public_key *pub = public_key_from_external(public_key); -- CBS orig_in = *in; -- if (!kyber_parse_public_key_no_hash(pub, in) || // -- CBS_len(in) != 0) { +- vector_encode(s_output, &priv->s, kLog2Prime); +- if (!kyber_marshal_public_key(out, &priv->pub) || +- !CBB_add_bytes(out, priv->pub.public_key_hash, +- sizeof(priv->pub.public_key_hash)) || +- !CBB_add_bytes(out, priv->fo_failure_secret, +- sizeof(priv->fo_failure_secret))) { - return 0; -+ if (ipd == 1) { ++ ++ if (mlkem == 1) { + /* Compute shared secret in case of rejection: ss2 = PRF(z || c). */ + uint8_t ss2[KYBER_SYMBYTES]; + keccak_state ks; @@ -3745,33 +3615,17 @@ index 776c085f9..5acd45cd9 100644 + /* hash concatenation of pre-k and H(c) to k */ + kdf(ss, kr, 2*KYBER_SYMBYTES); } -- BORINGSSL_keccak(pub->public_key_hash, sizeof(pub->public_key_hash), -- CBS_data(&orig_in), CBS_len(&orig_in), boringssl_sha3_256); - return 1; } --int KYBER_marshal_private_key(CBB *out, -- const struct KYBER_private_key *private_key) { -- const struct private_key *const priv = private_key_from_external(private_key); -- uint8_t *s_output; -- if (!CBB_add_space(out, &s_output, kEncodedVectorSize)) { -- return 0; -- } -- vector_encode(s_output, &priv->s, kLog2Prime); -- if (!kyber_marshal_public_key(out, &priv->pub) || -- !CBB_add_bytes(out, priv->pub.public_key_hash, -- sizeof(priv->pub.public_key_hash)) || -- !CBB_add_bytes(out, priv->fo_failure_secret, -- sizeof(priv->fo_failure_secret))) { -- return 0; -- } -- return 1; --} -- -int KYBER_parse_private_key(struct KYBER_private_key *out_private_key, - CBS *in) { - struct private_key *const priv = private_key_from_external(out_private_key); -- ++void marshal_public_key(uint8_t out[KYBER_PUBLICKEYBYTES], ++ const struct public_key *in_pub) { ++ memcpy(out, &in_pub->opaque, KYBER_PUBLICKEYBYTES); ++} + - CBS s_bytes; - if (!CBS_get_bytes(in, &s_bytes, kEncodedVectorSize) || - !vector_decode(&priv->s, CBS_data(&s_bytes), kLog2Prime) || @@ -3784,58 +3638,10 @@ index 776c085f9..5acd45cd9 100644 - return 0; - } - return 1; -+// Modified crypto_kem_decap to BoringSSL style API -+void decap(uint8_t out_shared_key[KYBER_SSBYTES], -+ const struct private_key *in_priv, -+ const uint8_t *ct, size_t ciphertext_len) { -+ decap2(out_shared_key, in_priv, ct, ciphertext_len, 0); -+} -+ -+ -+void marshal_public_key(uint8_t out[KYBER_PUBLICKEYBYTES], -+ const struct public_key *in_pub) { -+ memcpy(out, &in_pub->opaque, KYBER_PUBLICKEYBYTES); -+} -+ +void parse_public_key(struct public_key *out, + const uint8_t in[KYBER_PUBLICKEYBYTES]) { + memcpy(&out->opaque, in, KYBER_PUBLICKEYBYTES); } -diff --git a/src/crypto/kyber/kyber.h b/src/crypto/kyber/kyber.h -new file mode 100644 -index 000000000..16e47d582 ---- /dev/null -+++ b/src/crypto/kyber/kyber.h -@@ -0,0 +1,29 @@ -+#ifndef OPENSSL_HEADER_KYBER_KYBER_H -+#define OPENSSL_HEADER_KYBER_KYBER_H -+ -+#include -+ -+#include -+#include -+ -+void KYBER512_encap2(uint8_t out_ciphertext[KYBER512_CIPHERTEXT_BYTES], -+ uint8_t ss[KYBER_KEY_BYTES], -+ const struct KYBER512_public_key *in_pub, -+ const uint8_t seed[KYBER_ENCAP_BYTES], -+ int ipd); -+ -+void KYBER512_decap2(uint8_t out_shared_key[KYBER_KEY_BYTES], -+ const struct KYBER512_private_key *in_priv, -+ const uint8_t *ct, size_t ciphertext_len, int ipd); -+ -+void KYBER768_encap2(uint8_t out_ciphertext[KYBER768_CIPHERTEXT_BYTES], -+ uint8_t ss[KYBER_KEY_BYTES], -+ const struct KYBER768_public_key *in_pub, -+ const uint8_t seed[KYBER_ENCAP_BYTES], -+ int ipd); -+ -+void KYBER768_decap2(uint8_t out_shared_key[KYBER_KEY_BYTES], -+ const struct KYBER768_private_key *in_priv, -+ const uint8_t *ct, size_t ciphertext_len, int ipd); -+ -+#endif diff --git a/src/crypto/kyber/kyber512.c b/src/crypto/kyber/kyber512.c new file mode 100644 index 000000000..21eed11a2 @@ -4093,7 +3899,7 @@ index eb76b5bd7..000000000 - FileTestGTest("crypto/kyber/kyber_tests.txt", KyberFileTest); -} diff --git a/src/crypto/obj/obj_dat.h b/src/crypto/obj/obj_dat.h -index 654b3c08e..0d3d9f24f 100644 +index 654b3c08e..6cef2c079 100644 --- a/src/crypto/obj/obj_dat.h +++ b/src/crypto/obj/obj_dat.h @@ -57,7 +57,7 @@ @@ -4115,19 +3921,11 @@ index 654b3c08e..0d3d9f24f 100644 + NULL, 0}, + {"X25519Kyber768Draft00Old", "X25519Kyber768Draft00Old", + NID_X25519Kyber768Draft00Old, 0, NULL, 0}, -+ {"IPDWing", "IPDWing", NID_IPDWing, 0, NULL, 0}, ++ {"X25519MLKEM768", "X25519MLKEM768", NID_X25519MLKEM768, 0, NULL, 0}, }; static const uint16_t kNIDsInShortNameOrder[] = { -@@ -8889,6 +8896,7 @@ static const uint16_t kNIDsInShortNameOrder[] = { - 35 /* IDEA-CFB */, - 36 /* IDEA-ECB */, - 46 /* IDEA-OFB */, -+ 968 /* IPDWing */, - 181 /* ISO */, - 183 /* ISO-US */, - 645 /* ITU-T */, -@@ -8916,6 +8924,7 @@ static const uint16_t kNIDsInShortNameOrder[] = { +@@ -8916,6 +8923,7 @@ static const uint16_t kNIDsInShortNameOrder[] = { 18 /* OU */, 749 /* Oakley-EC2N-3 */, 750 /* Oakley-EC2N-4 */, @@ -4135,25 +3933,18 @@ index 654b3c08e..0d3d9f24f 100644 9 /* PBE-MD2-DES */, 168 /* PBE-MD2-RC2-64 */, 10 /* PBE-MD5-DES */, -@@ -8982,7 +8991,9 @@ static const uint16_t kNIDsInShortNameOrder[] = { +@@ -8982,7 +8990,10 @@ static const uint16_t kNIDsInShortNameOrder[] = { 458 /* UID */, 0 /* UNDEF */, 948 /* X25519 */, + 965 /* X25519Kyber512Draft00 */, 964 /* X25519Kyber768Draft00 */, + 967 /* X25519Kyber768Draft00Old */, ++ 968 /* X25519MLKEM768 */, 961 /* X448 */, 11 /* X500 */, 378 /* X500algorithms */, -@@ -9787,6 +9798,7 @@ static const uint16_t kNIDsInLongNameOrder[] = { - 431 /* Hold Instruction None */, - 433 /* Hold Instruction Reject */, - 634 /* ICC or token signature */, -+ 968 /* IPDWing */, - 294 /* IPSec End System */, - 295 /* IPSec Tunnel */, - 296 /* IPSec User */, -@@ -9829,6 +9841,7 @@ static const uint16_t kNIDsInLongNameOrder[] = { +@@ -9829,6 +9840,7 @@ static const uint16_t kNIDsInLongNameOrder[] = { 366 /* OCSP Nonce */, 371 /* OCSP Service Locator */, 180 /* OCSP Signing */, @@ -4161,18 +3952,19 @@ index 654b3c08e..0d3d9f24f 100644 161 /* PBES2 */, 69 /* PBKDF2 */, 162 /* PBMAC1 */, -@@ -9853,7 +9866,9 @@ static const uint16_t kNIDsInLongNameOrder[] = { +@@ -9853,7 +9865,10 @@ static const uint16_t kNIDsInLongNameOrder[] = { 133 /* Time Stamping */, 375 /* Trust Root */, 948 /* X25519 */, + 965 /* X25519Kyber512Draft00 */, 964 /* X25519Kyber768Draft00 */, + 967 /* X25519Kyber768Draft00Old */, ++ 968 /* X25519MLKEM768 */, 961 /* X448 */, 12 /* X509 */, 402 /* X509v3 AC Targeting */, diff --git a/src/crypto/obj/obj_mac.num b/src/crypto/obj/obj_mac.num -index a0519acee..019770f16 100644 +index a0519acee..2a46adfe8 100644 --- a/src/crypto/obj/obj_mac.num +++ b/src/crypto/obj/obj_mac.num @@ -952,3 +952,7 @@ X448 961 @@ -4182,9 +3974,9 @@ index a0519acee..019770f16 100644 +X25519Kyber512Draft00 965 +P256Kyber768Draft00 966 +X25519Kyber768Draft00Old 967 -+IPDWing 968 ++X25519MLKEM768 968 diff --git a/src/crypto/obj/objects.txt b/src/crypto/obj/objects.txt -index 3ad32ea3d..475d278df 100644 +index 3ad32ea3d..347fc556a 100644 --- a/src/crypto/obj/objects.txt +++ b/src/crypto/obj/objects.txt @@ -1332,8 +1332,12 @@ secg-scheme 14 3 : dhSinglePass-cofactorDH-sha512kdf-scheme @@ -4197,12 +3989,12 @@ index 3ad32ea3d..475d278df 100644 : X25519Kyber768Draft00 + : P256Kyber768Draft00 + : X25519Kyber768Draft00Old -+ : IPDWing ++ : X25519MLKEM768 # See RFC 8410. 1 3 101 110 : X25519 diff --git a/src/include/openssl/kyber.h b/src/include/openssl/kyber.h -index cafae9d17..1c889a075 100644 +index cafae9d17..a05eb8957 100644 --- a/src/include/openssl/kyber.h +++ b/src/include/openssl/kyber.h @@ -1,17 +1,3 @@ @@ -4223,23 +4015,20 @@ index cafae9d17..1c889a075 100644 #ifndef OPENSSL_HEADER_KYBER_H #define OPENSSL_HEADER_KYBER_H -@@ -21,105 +7,157 @@ +@@ -21,105 +7,104 @@ extern "C" { #endif -- --// Kyber768. +#define KYBER512_PUBLIC_KEY_BYTES 800 +#define KYBER512_CIPHERTEXT_BYTES 768 +#define KYBER512_PRIVATE_KEY_BYTES 1632 +#define KYBER768_PUBLIC_KEY_BYTES 1184 +#define KYBER768_CIPHERTEXT_BYTES 1088 +#define KYBER768_PRIVATE_KEY_BYTES 2400 -+#define IPDWING_PUBLIC_KEY_BYTES 1216 -+#define IPDWING_CIPHERTEXT_BYTES 1120 -+#define IPDWING_PRIVATE_KEY_BYTES 2464 - +-// Kyber768. +- +- -// KYBER_public_key contains a Kyber768 public key. The contents of this -// object should never leave the address space since the format is unstable. -struct KYBER_public_key { @@ -4261,10 +4050,11 @@ index cafae9d17..1c889a075 100644 +struct KYBER768_private_key { + uint8_t opaque[KYBER768_PRIVATE_KEY_BYTES]; +}; -+struct IPDWING_private_key { -+ struct KYBER768_private_key m; -+ uint8_t x[32]; -+ uint8_t xpub[32]; ++struct KYBER512_public_key { ++ uint8_t opaque[KYBER512_PUBLIC_KEY_BYTES]; ++}; ++struct KYBER768_public_key { ++ uint8_t opaque[KYBER768_PUBLIC_KEY_BYTES]; }; -// KYBER_PUBLIC_KEY_BYTES is the number of bytes in an encoded Kyber768 public @@ -4343,39 +4133,18 @@ index cafae9d17..1c889a075 100644 -// there are trailing bytes in |in|. -OPENSSL_EXPORT int KYBER_parse_private_key( - struct KYBER_private_key *out_private_key, CBS *in); -+struct KYBER512_public_key { -+ uint8_t opaque[KYBER512_PUBLIC_KEY_BYTES]; -+}; -+struct KYBER768_public_key { -+ uint8_t opaque[KYBER768_PUBLIC_KEY_BYTES]; -+}; -+struct IPDWING_public_key { -+ struct KYBER768_public_key m; -+ uint8_t x[32]; -+}; - +- +// KYBER_GENERATE_KEY_BYTES is the number of bytes of entropy needed to +// generate a keypair. +#define KYBER_GENERATE_KEY_BYTES 64 + -+// IPDWING_GENERATE_KEY_BYTES is the number of bytes of entropy needed to -+// generate a keypair. -+#define IPDWING_GENERATE_KEY_BYTES 96 -+ +// KYBER_ENCAP_BYTES is the number of bytes of entropy needed to encapsulate a +// session key. +#define KYBER_ENCAP_BYTES 32 + -+// IPDWING_ENCAP_BYTES is the number of bytes of entropy needed to encapsulate a -+// session key. -+#define IPDWING_ENCAP_BYTES 64 -+ +// KYBER_KEY_BYTES is the number of bytes in a shared key. +#define KYBER_KEY_BYTES 32 + -+// IPDWING_KEY_BYTES is the number of bytes in a shared key. -+#define IPDWING_KEY_BYTES 32 -+ +// KYBER512_generate_key is a deterministic function that outputs a public and +// private key based on the given entropy. +OPENSSL_EXPORT void KYBER512_generate_key( @@ -4388,65 +4157,45 @@ index cafae9d17..1c889a075 100644 + struct KYBER768_public_key *out_pub, struct KYBER768_private_key *out_priv, + const uint8_t input[KYBER_GENERATE_KEY_BYTES]); + -+// IPDWING_generate_key is a deterministic function that outputs a public and -+// private key based on the given entropy. -+OPENSSL_EXPORT void IPDWING_generate_key( -+ struct IPDWING_public_key *out_pub, struct IPDWING_private_key *out_priv, -+ const uint8_t input[IPDWING_GENERATE_KEY_BYTES]); -+ +// KYBER512_encap is a deterministic function the generates and encrypts a random +// session key from the given entropy, writing those values to |out_shared_key| -+// and |out_ciphertext|, respectively. -+OPENSSL_EXPORT void KYBER512_encap(uint8_t out_ciphertext[KYBER512_CIPHERTEXT_BYTES], ++// and |out_ciphertext|, respectively. If |mlkem| is 1, will use ML-KEM-512. ++OPENSSL_EXPORT int KYBER512_encap(uint8_t out_ciphertext[KYBER512_CIPHERTEXT_BYTES], + uint8_t out_shared_key[KYBER_KEY_BYTES], + const struct KYBER512_public_key *in_pub, -+ const uint8_t in[KYBER_ENCAP_BYTES]); ++ const uint8_t in[KYBER_ENCAP_BYTES], ++ int mlkem); + +// KYBER768_encap is a deterministic function the generates and encrypts a random +// session key from the given entropy, writing those values to |out_shared_key| -+// and |out_ciphertext|, respectively. -+OPENSSL_EXPORT void KYBER768_encap(uint8_t out_ciphertext[KYBER768_CIPHERTEXT_BYTES], ++// and |out_ciphertext|, respectively. If |mlkem| is 1, will use ML-KEM-768. ++OPENSSL_EXPORT int KYBER768_encap(uint8_t out_ciphertext[KYBER768_CIPHERTEXT_BYTES], + uint8_t out_shared_key[KYBER_KEY_BYTES], + const struct KYBER768_public_key *in_pub, -+ const uint8_t in[KYBER_ENCAP_BYTES]); -+ -+// IPDWING_encap is a deterministic function the generates and encrypts a random -+// session key from the given entropy, writing those values to |out_shared_key| -+// and |out_ciphertext|, respectively. -+OPENSSL_EXPORT void IPDWING_encap(uint8_t out_ciphertext[IPDWING_CIPHERTEXT_BYTES], -+ uint8_t out_shared_key[IPDWING_KEY_BYTES], -+ const struct IPDWING_public_key *in_pub, -+ const uint8_t in[IPDWING_ENCAP_BYTES]); ++ const uint8_t in[KYBER_ENCAP_BYTES], ++ int mlkem); + +// KYBER_decap decrypts a session key from |ciphertext_len| bytes of +// |ciphertext|. If the ciphertext is valid, the decrypted key is written to +// |out_shared_key|. Otherwise a key dervied from |ciphertext| and a secret key (kept +// in |in_priv|) is written. If the ciphertext is the wrong length then it will +// leak which was done via side-channels. Otherwise it should perform either -+// action in constant-time. ++// action in constant-time. If |mlkem| is 1, will use ML-KEM-512. +OPENSSL_EXPORT void KYBER512_decap(uint8_t out_shared_key[KYBER_KEY_BYTES], + const struct KYBER512_private_key *in_priv, -+ const uint8_t *ciphertext, size_t ciphertext_len); ++ const uint8_t *ciphertext, size_t ciphertext_len, ++ int mlkem); + -+// KYBER768_decap decrypts a session key from |ciphertext_len| bytes of ++// KYBER_decap decrypts a session key from |ciphertext_len| bytes of +// |ciphertext|. If the ciphertext is valid, the decrypted key is written to +// |out_shared_key|. Otherwise a key dervied from |ciphertext| and a secret key (kept +// in |in_priv|) is written. If the ciphertext is the wrong length then it will +// leak which was done via side-channels. Otherwise it should perform either -+// action in constant-time. ++// action in constant-time. If |mlkem| is 1, will use ML-KEM-768. +OPENSSL_EXPORT void KYBER768_decap(uint8_t out_shared_key[KYBER_KEY_BYTES], + const struct KYBER768_private_key *in_priv, -+ const uint8_t *ciphertext, size_t ciphertext_len); -+ -+// IPDWING_decap decrypts a session key from IPDWING_CIPHERTEXT_BYTES bytes of -+// |ciphertext|. If the ciphertext is valid, the decrypted key is written to -+// |out_shared_key|. Otherwise a key dervied from |ciphertext| and a secret key (kept -+// in |in_priv|) is written. If the ciphertext is the wrong length then it will -+// leak which was done via side-channels. Otherwise it should perform either -+// action in constant-time. -+OPENSSL_EXPORT void IPDWING_decap(uint8_t out_shared_key[KYBER_KEY_BYTES], -+ const struct IPDWING_private_key *in_priv, -+ const uint8_t *ciphertext); ++ const uint8_t *ciphertext, size_t ciphertext_len, ++ int mlkem); + +// KYBER512_marshal_public_key serialises |in_pub| to |out|. +OPENSSL_EXPORT void KYBER512_marshal_public_key( @@ -4456,10 +4205,6 @@ index cafae9d17..1c889a075 100644 +OPENSSL_EXPORT void KYBER768_marshal_public_key( + uint8_t out[KYBER768_PUBLIC_KEY_BYTES], const struct KYBER768_public_key *in_pub); + -+// IPDWING_marshal_public_key serialises |in_pub| to |out|. -+OPENSSL_EXPORT void IPDWING_marshal_public_key( -+ uint8_t out[IPDWING_PUBLIC_KEY_BYTES], const struct IPDWING_public_key *in_pub); -+ +// KYBER512_parse_public_key sets |*out| to the public-key encoded in |in|. +OPENSSL_EXPORT void KYBER512_parse_public_key( + struct KYBER512_public_key *out, const uint8_t in[KYBER512_PUBLIC_KEY_BYTES]); @@ -4467,15 +4212,11 @@ index cafae9d17..1c889a075 100644 +// KYBER768_parse_public_key sets |*out| to the public-key encoded in |in|. +OPENSSL_EXPORT void KYBER768_parse_public_key( + struct KYBER768_public_key *out, const uint8_t in[KYBER768_PUBLIC_KEY_BYTES]); -+ -+// IPDWING_parse_public_key sets |*out| to the public-key encoded in |in|. -+OPENSSL_EXPORT void IPDWING_parse_public_key( -+ struct IPDWING_public_key *out, const uint8_t in[IPDWING_PUBLIC_KEY_BYTES]); #if defined(__cplusplus) } // extern C diff --git a/src/include/openssl/nid.h b/src/include/openssl/nid.h -index 4dd8841b1..09912d8bb 100644 +index 4dd8841b1..5b102c610 100644 --- a/src/include/openssl/nid.h +++ b/src/include/openssl/nid.h @@ -4255,6 +4255,18 @@ extern "C" { @@ -4491,14 +4232,14 @@ index 4dd8841b1..09912d8bb 100644 +#define SN_X25519Kyber768Draft00Old "X25519Kyber768Draft00Old" +#define NID_X25519Kyber768Draft00Old 967 + -+#define SN_IPDWing "IPDWing" -+#define NID_IPDWing 968 ++#define SN_X25519MLKEM768 "X25519MLKEM768" ++#define NID_X25519MLKEM768 968 + #if defined(__cplusplus) } /* extern C */ diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h -index 53aa9b453..3091c6849 100644 +index 53aa9b453..f9683f4cf 100644 --- a/src/include/openssl/ssl.h +++ b/src/include/openssl/ssl.h @@ -2378,6 +2378,10 @@ OPENSSL_EXPORT int SSL_set1_curves_list(SSL *ssl, const char *curves); @@ -4508,7 +4249,7 @@ index 53aa9b453..3091c6849 100644 +#define SSL_CURVE_X25519_KYBER512_DRAFT00 0xfe30 +#define SSL_CURVE_X25519_KYBER768_DRAFT00_OLD 0xfe31 +#define SSL_CURVE_P256_KYBER768_DRAFT00 0xfe32 -+#define SSL_CURVE_IPDWING 0xfe41 ++#define SSL_CURVE_X25519_MLKEM768 0x11ec // SSL_get_curve_id returns the ID of the curve used by |ssl|'s most recently // completed handshake or 0 if not applicable. @@ -4526,7 +4267,7 @@ index 5c7e881bf..3c0770cf3 100644 crypto/pkcs8/test/no_encryption.p12 crypto/pkcs8/test/nss.p12 diff --git a/src/ssl/extensions.cc b/src/ssl/extensions.cc -index 5ee280221..cf165df1f 100644 +index 5ee280221..aae3e6a7f 100644 --- a/src/ssl/extensions.cc +++ b/src/ssl/extensions.cc @@ -207,6 +207,10 @@ static bool tls1_check_duplicate_extensions(const CBS *cbs) { @@ -4536,15 +4277,23 @@ index 5ee280221..cf165df1f 100644 + case SSL_CURVE_X25519_KYBER768_DRAFT00_OLD: + case SSL_CURVE_X25519_KYBER512_DRAFT00: + case SSL_CURVE_P256_KYBER768_DRAFT00: -+ case SSL_CURVE_IPDWING: ++ case SSL_CURVE_X25519_MLKEM768: return true; default: return false; diff --git a/src/ssl/ssl_key_share.cc b/src/ssl/ssl_key_share.cc -index 09a9ad380..d9d3b9032 100644 +index 09a9ad380..d7a8f0a80 100644 --- a/src/ssl/ssl_key_share.cc +++ b/src/ssl/ssl_key_share.cc -@@ -193,63 +193,384 @@ class X25519KeyShare : public SSLKeyShare { +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -193,63 +194,292 @@ class X25519KeyShare : public SSLKeyShare { uint8_t private_key_[32]; }; @@ -4553,9 +4302,7 @@ index 09a9ad380..d9d3b9032 100644 public: - X25519Kyber768KeyShare() {} + P256Kyber768Draft00KeyShare() {} - -- uint16_t GroupID() const override { -- return SSL_CURVE_X25519_KYBER768_DRAFT00; ++ + uint16_t GroupID() const override { return SSL_CURVE_P256_KYBER768_DRAFT00; } + + bool Generate(CBB *out) override { @@ -4598,7 +4345,9 @@ index 09a9ad380..d9d3b9032 100644 + + uint8_t kyber_public_key_bytes[KYBER768_PUBLIC_KEY_BYTES]; + KYBER768_marshal_public_key(kyber_public_key_bytes, &kyber_public_key); -+ + +- uint16_t GroupID() const override { +- return SSL_CURVE_X25519_KYBER768_DRAFT00; + if (!CBB_add_bytes(out, kyber_public_key_bytes, + sizeof(kyber_public_key_bytes))) { + return false; @@ -4686,7 +4435,10 @@ index 09a9ad380..d9d3b9032 100644 + uint8_t entropy[KYBER_ENCAP_BYTES]; + RAND_bytes(entropy, sizeof(entropy)); + -+ KYBER768_encap(ciphertext, secret.data() + 32, &peer_public_key, entropy); ++ if(!KYBER768_encap(ciphertext, secret.data() + 32, &peer_public_key, entropy, 0)) { ++ *out_alert = SSL_AD_ILLEGAL_PARAMETER; ++ return false; ++ } + if(!CBB_add_bytes(out_public_key, ciphertext, sizeof(ciphertext))) { + return false; + } @@ -4757,7 +4509,7 @@ index 09a9ad380..d9d3b9032 100644 + } + + KYBER768_decap(secret.data() + 32, &kyber_private_key_, -+ peer_key.data() + 65, peer_key.size() - 65); ++ peer_key.data() + 65, peer_key.size() - 65, 0); + + *out_secret = std::move(secret); + return true; @@ -4805,14 +4557,27 @@ index 09a9ad380..d9d3b9032 100644 - uint8_t *out_alert, Span peer_key) override { + bool Encap(CBB *out_public_key, Array *out_secret, + uint8_t *out_alert, Span peer_key) override { -+ Array secret; + Array secret; +- if (!secret.Init(32 + 32)) { + if (!secret.Init(32 + KYBER_KEY_BYTES)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); -+ return false; -+ } -+ -+ uint8_t x25519_public_key[32]; -+ X25519_keypair(x25519_public_key, x25519_private_key_); + return false; + } + + uint8_t x25519_public_key[32]; + X25519_keypair(x25519_public_key, x25519_private_key_); +- KYBER_public_key peer_kyber_pub; +- CBS peer_key_cbs; +- CBS peer_x25519_cbs; +- CBS peer_kyber_cbs; +- CBS_init(&peer_key_cbs, peer_key.data(), peer_key.size()); +- if (!CBS_get_bytes(&peer_key_cbs, &peer_x25519_cbs, 32) || +- !CBS_get_bytes(&peer_key_cbs, &peer_kyber_cbs, +- KYBER_PUBLIC_KEY_BYTES) || +- CBS_len(&peer_key_cbs) != 0 || +- !X25519(secret.data(), x25519_private_key_, +- CBS_data(&peer_x25519_cbs)) || +- !KYBER_parse_public_key(&peer_kyber_pub, &peer_kyber_cbs)) { + + KYBER768_public_key peer_public_key; + if (peer_key.size() != 32 + KYBER768_PUBLIC_KEY_BYTES) { @@ -4824,27 +4589,36 @@ index 09a9ad380..d9d3b9032 100644 + KYBER768_parse_public_key(&peer_public_key, peer_key.data() + 32); + + if (!X25519(secret.data(), x25519_private_key_, peer_key.data())) { -+ *out_alert = SSL_AD_DECODE_ERROR; -+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); -+ return false; -+ } -+ + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); + return false; + } + +- uint8_t kyber_ciphertext[KYBER_CIPHERTEXT_BYTES]; +- KYBER_encap(kyber_ciphertext, secret.data() + 32, secret.size() - 32, +- &peer_kyber_pub); + uint8_t ciphertext[KYBER768_CIPHERTEXT_BYTES]; + uint8_t entropy[KYBER_ENCAP_BYTES]; + RAND_bytes(entropy, sizeof(entropy)); -+ -+ KYBER768_encap(ciphertext, secret.data() + 32, &peer_public_key, entropy); -+ if(!CBB_add_bytes(out_public_key, x25519_public_key, -+ sizeof(x25519_public_key)) || -+ !CBB_add_bytes(out_public_key, ciphertext, sizeof(ciphertext))) { + +- if (!CBB_add_bytes(out_ciphertext, x25519_public_key, ++ if(!KYBER768_encap(ciphertext, secret.data() + 32, &peer_public_key, entropy, 0)) { ++ *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return false; + } -+ -+ *out_secret = std::move(secret); -+ return true; -+ } -+ -+ bool Decap(Array *out_secret, uint8_t *out_alert, ++ if(!CBB_add_bytes(out_public_key, x25519_public_key, + sizeof(x25519_public_key)) || +- !CBB_add_bytes(out_ciphertext, kyber_ciphertext, +- sizeof(kyber_ciphertext))) { ++ !CBB_add_bytes(out_public_key, ciphertext, sizeof(ciphertext))) { + return false; + } + +@@ -258,30 +488,233 @@ class X25519Kyber768KeyShare : public SSLKeyShare { + } + + bool Decap(Array *out_secret, uint8_t *out_alert, +- Span ciphertext) override { + Span peer_key) override { + *out_alert = SSL_AD_INTERNAL_ERROR; + @@ -4862,7 +4636,7 @@ index 09a9ad380..d9d3b9032 100644 + } + + KYBER768_decap(secret.data() + 32, &kyber_private_key_, -+ peer_key.data() + 32, peer_key.size() - 32); ++ peer_key.data() + 32, peer_key.size() - 32, 0); + + *out_secret = std::move(secret); + return true; @@ -4874,6 +4648,111 @@ index 09a9ad380..d9d3b9032 100644 + uint16_t group_id_; +}; + ++class X25519MLKEM768KeyShare : public SSLKeyShare { ++ public: ++ X25519MLKEM768KeyShare() {} ++ ++ uint16_t GroupID() const override { return SSL_CURVE_X25519_MLKEM768; } ++ ++ bool Generate(CBB *out) override { ++ uint8_t x25519_public_key[32]; ++ X25519_keypair(x25519_public_key, x25519_private_key_); ++ ++ uint8_t kyber_entropy[KYBER_GENERATE_KEY_BYTES]; ++ KYBER768_public_key kyber_public_key; ++ RAND_bytes(kyber_entropy, sizeof(kyber_entropy)); ++ KYBER768_generate_key(&kyber_public_key, &kyber_private_key_, kyber_entropy); ++ ++ uint8_t kyber_public_key_bytes[KYBER768_PUBLIC_KEY_BYTES]; ++ KYBER768_marshal_public_key(kyber_public_key_bytes, &kyber_public_key); ++ ++ if (!CBB_add_bytes(out, kyber_public_key_bytes, sizeof(kyber_public_key_bytes)) || ++ !CBB_add_bytes(out, x25519_public_key, sizeof(x25519_public_key))) { ++ return false; ++ } ++ ++ return true; ++ } ++ ++ bool Encap(CBB *out_public_key, Array *out_secret, ++ uint8_t *out_alert, Span peer_key) override { ++ Array secret; ++ if (!secret.Init(32 + KYBER_KEY_BYTES)) { ++ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); ++ return false; ++ } ++ ++ uint8_t x25519_public_key[32]; ++ X25519_keypair(x25519_public_key, x25519_private_key_); ++ ++ KYBER768_public_key peer_public_key; ++ if (peer_key.size() != KYBER768_PUBLIC_KEY_BYTES + 32) { ++ *out_alert = SSL_AD_DECODE_ERROR; ++ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); ++ return false; ++ } ++ ++ KYBER768_parse_public_key(&peer_public_key, peer_key.data()); ++ ++ if (!X25519(secret.data() + 32, x25519_private_key_, ++ peer_key.data() + KYBER768_PUBLIC_KEY_BYTES)) { ++ *out_alert = SSL_AD_DECODE_ERROR; ++ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); ++ return false; ++ } ++ ++ uint8_t ciphertext[KYBER768_CIPHERTEXT_BYTES]; ++ uint8_t entropy[KYBER_ENCAP_BYTES]; ++ RAND_bytes(entropy, sizeof(entropy)); ++ ++ if(!KYBER768_encap(ciphertext, secret.data(), &peer_public_key, entropy, 1)) { ++ *out_alert = SSL_AD_ILLEGAL_PARAMETER; ++ return false; ++ } ++ if(!CBB_add_bytes(out_public_key, ciphertext, sizeof(ciphertext)) || ++ !CBB_add_bytes(out_public_key, x25519_public_key, sizeof(x25519_public_key))) { ++ return false; ++ } ++ ++ *out_secret = std::move(secret); ++ return true; ++ } ++ ++ bool Decap(Array *out_secret, uint8_t *out_alert, ++ Span peer_key) override { + *out_alert = SSL_AD_INTERNAL_ERROR; + + Array secret; +- if (!secret.Init(32 + 32)) { ++ if (!secret.Init(32 + KYBER_KEY_BYTES)) { ++ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return false; + } + +- if (ciphertext.size() != 32 + KYBER_CIPHERTEXT_BYTES || +- !X25519(secret.data(), x25519_private_key_, ciphertext.data())) { ++ if (peer_key.size() != KYBER768_CIPHERTEXT_BYTES + 32 || ++ !X25519(secret.data() + 32, x25519_private_key_, ++ peer_key.data() + KYBER768_CIPHERTEXT_BYTES )) { + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); + return false; + } + +- KYBER_decap(secret.data() + 32, secret.size() - 32, ciphertext.data() + 32, +- &kyber_private_key_); ++ KYBER768_decap(secret.data(), &kyber_private_key_, ++ peer_key.data(), peer_key.size() - 32, 1); ++ ++ *out_secret = std::move(secret); ++ return true; ++ } ++ ++ private: ++ uint8_t x25519_private_key_[32]; ++ KYBER768_private_key kyber_private_key_; ++}; ++ +class X25519Kyber512Draft00KeyShare : public SSLKeyShare { + public: + X25519Kyber512Draft00KeyShare() {} @@ -4903,27 +4782,14 @@ index 09a9ad380..d9d3b9032 100644 + + bool Encap(CBB *out_public_key, Array *out_secret, + uint8_t *out_alert, Span peer_key) override { - Array secret; -- if (!secret.Init(32 + 32)) { ++ Array secret; + if (!secret.Init(32 + KYBER_KEY_BYTES)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); - return false; - } - - uint8_t x25519_public_key[32]; - X25519_keypair(x25519_public_key, x25519_private_key_); -- KYBER_public_key peer_kyber_pub; -- CBS peer_key_cbs; -- CBS peer_x25519_cbs; -- CBS peer_kyber_cbs; -- CBS_init(&peer_key_cbs, peer_key.data(), peer_key.size()); -- if (!CBS_get_bytes(&peer_key_cbs, &peer_x25519_cbs, 32) || -- !CBS_get_bytes(&peer_key_cbs, &peer_kyber_cbs, -- KYBER_PUBLIC_KEY_BYTES) || -- CBS_len(&peer_key_cbs) != 0 || -- !X25519(secret.data(), x25519_private_key_, -- CBS_data(&peer_x25519_cbs)) || -- !KYBER_parse_public_key(&peer_kyber_pub, &peer_kyber_cbs)) { ++ return false; ++ } ++ ++ uint8_t x25519_public_key[32]; ++ X25519_keypair(x25519_public_key, x25519_private_key_); + + KYBER512_public_key peer_public_key; + if (peer_key.size() != 32 + KYBER512_PUBLIC_KEY_BYTES) { @@ -4935,113 +4801,22 @@ index 09a9ad380..d9d3b9032 100644 + KYBER512_parse_public_key(&peer_public_key, peer_key.data() + 32); + + if (!X25519(secret.data(), x25519_private_key_, peer_key.data())) { - *out_alert = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); - return false; - } - -- uint8_t kyber_ciphertext[KYBER_CIPHERTEXT_BYTES]; -- KYBER_encap(kyber_ciphertext, secret.data() + 32, secret.size() - 32, -- &peer_kyber_pub); -+ uint8_t ciphertext[KYBER512_CIPHERTEXT_BYTES]; -+ uint8_t entropy[KYBER_ENCAP_BYTES]; -+ RAND_bytes(entropy, sizeof(entropy)); - -- if (!CBB_add_bytes(out_ciphertext, x25519_public_key, -+ KYBER512_encap(ciphertext, secret.data() + 32, &peer_public_key, entropy); -+ if(!CBB_add_bytes(out_public_key, x25519_public_key, - sizeof(x25519_public_key)) || -- !CBB_add_bytes(out_ciphertext, kyber_ciphertext, -- sizeof(kyber_ciphertext))) { -+ !CBB_add_bytes(out_public_key, ciphertext, sizeof(ciphertext))) { - return false; - } - -@@ -258,30 +579,111 @@ class X25519Kyber768KeyShare : public SSLKeyShare { - } - - bool Decap(Array *out_secret, uint8_t *out_alert, -- Span ciphertext) override { -+ Span peer_key) override { - *out_alert = SSL_AD_INTERNAL_ERROR; - - Array secret; -- if (!secret.Init(32 + 32)) { -+ if (!secret.Init(32 + KYBER_KEY_BYTES)) { -+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); - return false; - } - -- if (ciphertext.size() != 32 + KYBER_CIPHERTEXT_BYTES || -- !X25519(secret.data(), x25519_private_key_, ciphertext.data())) { -+ if (peer_key.size() != 32 + KYBER512_CIPHERTEXT_BYTES || -+ !X25519(secret.data(), x25519_private_key_, peer_key.data())) { - *out_alert = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); - return false; - } - -- KYBER_decap(secret.data() + 32, secret.size() - 32, ciphertext.data() + 32, -- &kyber_private_key_); -+ KYBER512_decap(secret.data() + 32, &kyber_private_key_, -+ peer_key.data() + 32, peer_key.size() - 32); -+ - *out_secret = std::move(secret); - return true; - } - - private: - uint8_t x25519_private_key_[32]; -- KYBER_private_key kyber_private_key_; -+ KYBER512_private_key kyber_private_key_; -+}; -+ -+class IPDWingKeyShare : public SSLKeyShare { -+ public: -+ IPDWingKeyShare() {} -+ -+ uint16_t GroupID() const override { return SSL_CURVE_IPDWING; } -+ -+ bool Generate(CBB *out) override { -+ uint8_t entropy[IPDWING_GENERATE_KEY_BYTES]; -+ IPDWING_public_key public_key; -+ RAND_bytes(entropy, sizeof(entropy)); -+ IPDWING_generate_key(&public_key, &private_key_, entropy); -+ -+ uint8_t public_key_bytes[IPDWING_PUBLIC_KEY_BYTES]; -+ IPDWING_marshal_public_key(public_key_bytes, &public_key); -+ -+ if(!CBB_add_bytes(out, public_key_bytes, sizeof(public_key_bytes))) { -+ return false; -+ } -+ -+ return true; -+ } -+ -+ bool Encap(CBB *out_public_key, Array *out_secret, -+ uint8_t *out_alert, Span peer_key) override { -+ Array secret; -+ *out_alert = SSL_AD_INTERNAL_ERROR; -+ if (!secret.Init(IPDWING_KEY_BYTES)) { -+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); -+ return false; -+ } -+ -+ IPDWING_public_key peer_public_key; -+ if (peer_key.size() != IPDWING_PUBLIC_KEY_BYTES) { + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); + return false; + } + -+ IPDWING_parse_public_key(&peer_public_key, peer_key.data()); -+ -+ uint8_t ciphertext[IPDWING_CIPHERTEXT_BYTES]; -+ uint8_t entropy[IPDWING_ENCAP_BYTES]; ++ uint8_t ciphertext[KYBER512_CIPHERTEXT_BYTES]; ++ uint8_t entropy[KYBER_ENCAP_BYTES]; + RAND_bytes(entropy, sizeof(entropy)); + -+ IPDWING_encap(ciphertext, secret.data(), &peer_public_key, entropy); -+ if(!CBB_add_bytes(out_public_key, ciphertext, sizeof(ciphertext))) { ++ if(!KYBER512_encap(ciphertext, secret.data() + 32, &peer_public_key, entropy, 0)) { ++ *out_alert = SSL_AD_ILLEGAL_PARAMETER; ++ return false; ++ } ++ if(!CBB_add_bytes(out_public_key, x25519_public_key, ++ sizeof(x25519_public_key)) || ++ !CBB_add_bytes(out_public_key, ciphertext, sizeof(ciphertext))) { + return false; + } + @@ -5054,29 +4829,33 @@ index 09a9ad380..d9d3b9032 100644 + *out_alert = SSL_AD_INTERNAL_ERROR; + + Array secret; -+ if (!secret.Init(IPDWING_KEY_BYTES)) { ++ if (!secret.Init(32 + KYBER_KEY_BYTES)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return false; + } + -+ if (peer_key.size() != IPDWING_CIPHERTEXT_BYTES) { ++ if (peer_key.size() != 32 + KYBER512_CIPHERTEXT_BYTES || ++ !X25519(secret.data(), x25519_private_key_, peer_key.data())) { + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); + return false; + } + -+ IPDWING_decap(secret.data(), &private_key_, peer_key.data()); ++ KYBER512_decap(secret.data() + 32, &kyber_private_key_, ++ peer_key.data() + 32, peer_key.size() - 32, 0); + -+ *out_secret = std::move(secret); -+ return true; -+ } -+ -+ private: -+ IPDWING_private_key private_key_; + *out_secret = std::move(secret); + return true; + } + + private: + uint8_t x25519_private_key_[32]; +- KYBER_private_key kyber_private_key_; ++ KYBER512_private_key kyber_private_key_; }; constexpr NamedGroup kNamedGroups[] = { -@@ -290,8 +692,15 @@ constexpr NamedGroup kNamedGroups[] = { +@@ -290,8 +723,16 @@ constexpr NamedGroup kNamedGroups[] = { {NID_secp384r1, SSL_CURVE_SECP384R1, "P-384", "secp384r1"}, {NID_secp521r1, SSL_CURVE_SECP521R1, "P-521", "secp521r1"}, {NID_X25519, SSL_CURVE_X25519, "X25519", "x25519"}, @@ -5089,11 +4868,12 @@ index 09a9ad380..d9d3b9032 100644 + "X25519Kyber768Draft00Old", "Xyber768D00Old"}, + {NID_P256Kyber768Draft00, SSL_CURVE_P256_KYBER768_DRAFT00, + "P256Kyber768Draft00", "P256Kyber768D00"}, -+ {NID_IPDWing, SSL_CURVE_IPDWING, "IPDWing", ""} ++ {NID_X25519MLKEM768, SSL_CURVE_X25519_MLKEM768, ++ "X25519MLKEM768", "X25519MLKEM768"} }; } // namespace -@@ -312,8 +721,18 @@ UniquePtr SSLKeyShare::Create(uint16_t group_id) { +@@ -312,8 +753,18 @@ UniquePtr SSLKeyShare::Create(uint16_t group_id) { return MakeUnique(NID_secp521r1, SSL_CURVE_SECP521R1); case SSL_CURVE_X25519: return MakeUnique(); @@ -5108,8 +4888,8 @@ index 09a9ad380..d9d3b9032 100644 + group_id)); + case SSL_CURVE_P256_KYBER768_DRAFT00: + return UniquePtr(New()); -+ case SSL_CURVE_IPDWING: -+ return UniquePtr(New()); ++ case SSL_CURVE_X25519_MLKEM768: ++ return UniquePtr(New()); default: return nullptr; } @@ -5127,7 +4907,7 @@ index 838761af5..9eb201d37 100644 static const uint16_t kSigAlgs[] = { SSL_SIGN_RSA_PKCS1_SHA256, diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc -index ef43a9e98..bb79509ea 100644 +index ef43a9e98..22178b5f6 100644 --- a/src/ssl/ssl_test.cc +++ b/src/ssl/ssl_test.cc @@ -409,7 +409,34 @@ static const CurveTest kCurveTests[] = { @@ -5144,10 +4924,6 @@ index ef43a9e98..bb79509ea 100644 + { SSL_CURVE_X25519_KYBER768_DRAFT00 }, + }, + { -+ "IPDWing", -+ { SSL_CURVE_IPDWING }, -+ }, -+ { + "Xyber768D00:Xyber768D00Old", + { SSL_CURVE_X25519_KYBER768_DRAFT00, SSL_CURVE_X25519_KYBER768_DRAFT00_OLD }, + }, @@ -5160,6 +4936,10 @@ index ef43a9e98..bb79509ea 100644 + { SSL_CURVE_P256_KYBER768_DRAFT00 }, + }, + { ++ "X25519MLKEM768", ++ { SSL_CURVE_X25519_MLKEM768 }, ++ }, ++ { + "P-256:P256Kyber768D00", + { SSL_CURVE_SECP256R1, SSL_CURVE_P256_KYBER768_DRAFT00 }, + }, @@ -5167,7 +4947,7 @@ index ef43a9e98..bb79509ea 100644 "P-256:P-384:P-521:X25519", { diff --git a/src/tool/speed.cc b/src/tool/speed.cc -index 5b0205953..831875514 100644 +index 5b0205953..6b3c67dab 100644 --- a/src/tool/speed.cc +++ b/src/tool/speed.cc @@ -904,6 +904,116 @@ static bool SpeedScrypt(const std::string &selected) { @@ -5206,7 +4986,7 @@ index 5b0205953..831875514 100644 + uint8_t entropy[KYBER_ENCAP_BYTES]; + uint8_t shared_key[KYBER_KEY_BYTES]; + RAND_bytes(entropy, sizeof(entropy)); -+ KYBER768_encap(ciphertext, shared_key, &pub, entropy); ++ KYBER768_encap(ciphertext, shared_key, &pub, entropy, 0); + return true; + })) { + fprintf(stderr, "Failed to time KYBER768_encap.\n"); @@ -5217,7 +4997,7 @@ index 5b0205953..831875514 100644 + + if (!TimeFunction(&results, [&priv, &ciphertext]() -> bool { + uint8_t shared_key[KYBER_KEY_BYTES]; -+ KYBER768_decap(shared_key, &priv, ciphertext, sizeof(ciphertext)); ++ KYBER768_decap(shared_key, &priv, ciphertext, sizeof(ciphertext), 0); + return true; + })) { + fprintf(stderr, "Failed to time KYBER768_decap.\n"); @@ -5261,7 +5041,7 @@ index 5b0205953..831875514 100644 + uint8_t entropy[KYBER_ENCAP_BYTES]; + uint8_t shared_key[KYBER_KEY_BYTES]; + RAND_bytes(entropy, sizeof(entropy)); -+ KYBER512_encap(ciphertext, shared_key, &pub, entropy); ++ KYBER512_encap(ciphertext, shared_key, &pub, entropy, 0); + return true; + })) { + fprintf(stderr, "Failed to time KYBER512_encap.\n"); @@ -5272,7 +5052,7 @@ index 5b0205953..831875514 100644 + + if (!TimeFunction(&results, [&priv, &ciphertext]() -> bool { + uint8_t shared_key[KYBER_KEY_BYTES]; -+ KYBER512_decap(shared_key, &priv, ciphertext, sizeof(ciphertext)); ++ KYBER512_decap(shared_key, &priv, ciphertext, sizeof(ciphertext), 0); + return true; + })) { + fprintf(stderr, "Failed to time KYBER512_decap.\n"); @@ -5354,5 +5134,5 @@ index 5b0205953..831875514 100644 !SpeedTrustToken("TrustToken-Exp1-Batch1", TRUST_TOKEN_experiment_v1(), 1, selected) || -- -2.45.2 +2.46.0 diff --git a/boring/Cargo.toml b/boring/Cargo.toml index 98441b6c..b944f1a6 100644 --- a/boring/Cargo.toml +++ b/boring/Cargo.toml @@ -69,6 +69,7 @@ kx-client-nist-required = ["kx-safe-default"] bitflags = { workspace = true } foreign-types = { workspace = true } once_cell = { workspace = true } +openssl-macros = { workspace = true } libc = { workspace = true } boring-sys = { workspace = true } diff --git a/boring/src/dsa.rs b/boring/src/dsa.rs index 60c0c197..d9c35505 100644 --- a/boring/src/dsa.rs +++ b/boring/src/dsa.rs @@ -280,8 +280,7 @@ impl Dsa { let dsa = Dsa::from_ptr(cvt_p(ffi::DSA_new())?); cvt(DSA_set0_pqg(dsa.0, p.as_ptr(), q.as_ptr(), g.as_ptr()))?; mem::forget((p, q, g)); - cvt(DSA_set0_key(dsa.0, pub_key.as_ptr(), ptr::null_mut()))?; - mem::forget(pub_key); + cvt(DSA_set0_key(dsa.0, pub_key.into_ptr(), ptr::null_mut()))?; Ok(dsa) } } diff --git a/boring/src/lib.rs b/boring/src/lib.rs index 4d477a72..6779586a 100644 --- a/boring/src/lib.rs +++ b/boring/src/lib.rs @@ -74,19 +74,17 @@ //! 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 +//! users should stick to that one for now. Enabling this feature, adds a few other post-quantum key //! agreements: //! +//! - `X25519MLKEM768` is the successor of `X25519Kyber768Draft00`. We expect servers to switch +//! before the end of 2024. //! - `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. -//! - `IPDWing`. A preliminary version of -//! [X-Wing](https://datatracker.ietf.org/doc/draft-connolly-cfrg-xwing-kem/02/). -//! Similar to `X25519Kyber768Draft00Old`, but uses a newer (but not yet final) version of Kyber -//! called ML-KEM-ipd. Not recommended. //! //! Presently all these key agreements are deployed by Cloudflare, but we do not guarantee continued //! support for them. diff --git a/boring/src/pkey.rs b/boring/src/pkey.rs index b8a157f6..9897635e 100644 --- a/boring/src/pkey.rs +++ b/boring/src/pkey.rs @@ -72,6 +72,7 @@ pub struct Id(c_int); impl Id { pub const RSA: Id = Id(ffi::EVP_PKEY_RSA); + pub const RSAPSS: Id = Id(ffi::EVP_PKEY_RSA_PSS); pub const DSA: Id = Id(ffi::EVP_PKEY_DSA); pub const DH: Id = Id(ffi::EVP_PKEY_DH); pub const EC: Id = Id(ffi::EVP_PKEY_EC); @@ -303,6 +304,7 @@ impl fmt::Debug for PKey { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let alg = match self.id() { Id::RSA => "RSA", + Id::RSAPSS => "RSAPSS", Id::DSA => "DSA", Id::DH => "DH", Id::EC => "EC", diff --git a/boring/src/ssl/callbacks.rs b/boring/src/ssl/callbacks.rs index 7841950c..f592d9d2 100644 --- a/boring/src/ssl/callbacks.rs +++ b/boring/src/ssl/callbacks.rs @@ -2,8 +2,9 @@ use super::{ AlpnError, ClientHello, GetSessionPendingError, PrivateKeyMethod, PrivateKeyMethodError, - SelectCertError, SniError, Ssl, SslAlert, SslContext, SslContextRef, SslRef, SslSession, - SslSessionRef, SslSignatureAlgorithm, SslVerifyError, SESSION_CTX_INDEX, + SelectCertError, SniError, Ssl, SslAlert, SslContext, SslContextRef, SslInfoCallbackAlert, + SslInfoCallbackMode, SslInfoCallbackValue, SslRef, SslSession, SslSessionRef, + SslSignatureAlgorithm, SslVerifyError, SESSION_CTX_INDEX, }; use crate::error::ErrorStack; use crate::ffi; @@ -521,3 +522,32 @@ where Err(err) => err.0, } } + +pub(super) unsafe extern "C" fn raw_info_callback( + ssl: *const ffi::SSL, + mode: c_int, + value: c_int, +) where + F: Fn(&SslRef, SslInfoCallbackMode, SslInfoCallbackValue) + Send + Sync + 'static, +{ + // Due to FFI signature requirements we have to pass a *const SSL into this function, but + // foreign-types requires a *mut SSL to get the Rust SslRef + let mut_ref = ssl as *mut ffi::SSL; + + // SAFETY: boring provides valid inputs. + let ssl = unsafe { SslRef::from_ptr(mut_ref) }; + let ssl_context = ssl.ssl_context(); + + let callback = ssl_context + .ex_data(SslContext::cached_ex_index::()) + .expect("BUG: info callback missing"); + + let value = match mode { + ffi::SSL_CB_READ_ALERT | ffi::SSL_CB_WRITE_ALERT => { + SslInfoCallbackValue::Alert(SslInfoCallbackAlert(value)) + } + _ => SslInfoCallbackValue::Unit, + }; + + callback(ssl, SslInfoCallbackMode(mode), value); +} diff --git a/boring/src/ssl/mod.rs b/boring/src/ssl/mod.rs index be26aa94..9bd1fdec 100644 --- a/boring/src/ssl/mod.rs +++ b/boring/src/ssl/mod.rs @@ -57,10 +57,10 @@ //! } //! } //! ``` -use crate::ffi; use foreign_types::{ForeignType, ForeignTypeRef, Opaque}; use libc::{c_char, c_int, c_long, c_uchar, c_uint, c_void}; use once_cell::sync::Lazy; +use openssl_macros::corresponds; use std::any::TypeId; use std::collections::HashMap; use std::convert::TryInto; @@ -82,6 +82,7 @@ use crate::dh::DhRef; use crate::ec::EcKeyRef; use crate::error::ErrorStack; use crate::ex_data::Index; +use crate::ffi; use crate::nid::Nid; use crate::pkey::{HasPrivate, PKeyRef, Params, Private}; use crate::srtp::{SrtpProtectionProfile, SrtpProtectionProfileRef}; @@ -243,9 +244,7 @@ pub struct SslMethod(*const ffi::SSL_METHOD); impl SslMethod { /// Support all versions of the TLS protocol. - /// - /// This corresponds to `TLS_method` on OpenSSL 1.1.0 and `SSLv23_method` - /// on OpenSSL 1.0.x. + #[corresponds(TLS_method)] pub fn tls() -> SslMethod { unsafe { SslMethod(TLS_method()) } } @@ -257,25 +256,19 @@ impl SslMethod { } /// Support all versions of the DTLS protocol. - /// - /// This corresponds to `DTLS_method` on OpenSSL 1.1.0 and `DTLSv1_method` - /// on OpenSSL 1.0.x. + #[corresponds(DTLS_method)] pub fn dtls() -> SslMethod { unsafe { SslMethod(DTLS_method()) } } /// Support all versions of the TLS protocol, explicitly as a client. - /// - /// This corresponds to `TLS_client_method` on OpenSSL 1.1.0 and - /// `SSLv23_client_method` on OpenSSL 1.0.x. + #[corresponds(TLS_client_method)] pub fn tls_client() -> SslMethod { unsafe { SslMethod(TLS_client_method()) } } /// Support all versions of the TLS protocol, explicitly as a server. - /// - /// This corresponds to `TLS_server_method` on OpenSSL 1.1.0 and - /// `SSLv23_server_method` on OpenSSL 1.0.x. + #[corresponds(TLS_server_method)] pub fn tls_server() -> SslMethod { unsafe { SslMethod(TLS_server_method()) } } @@ -285,6 +278,7 @@ impl SslMethod { /// # Safety /// /// The caller must ensure the pointer is valid. + #[corresponds(TLS_server_method)] pub unsafe fn from_ptr(ptr: *const ffi::SSL_METHOD) -> SslMethod { SslMethod(ptr) } @@ -729,14 +723,12 @@ impl SslCurve { #[cfg(feature = "pq-experimental")] pub const P256_KYBER768_DRAFT00: SslCurve = SslCurve(ffi::SSL_CURVE_P256_KYBER768_DRAFT00 as _); - #[cfg(feature = "pq-experimental")] - pub const IPD_WING: SslCurve = SslCurve(ffi::SSL_CURVE_IPDWING); - /// Returns the curve name /// /// This corresponds to [`SSL_get_curve_name`] /// /// [`SSL_get_curve_name`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_get_curve_name + #[corresponds(SSL_get_curve_name)] pub fn name(&self) -> Option<&'static str> { unsafe { let ptr = ffi::SSL_get_curve_name(self.0 as u16); @@ -774,7 +766,7 @@ impl SslCurve { #[cfg(feature = "pq-experimental")] ffi::SSL_CURVE_P256_KYBER768_DRAFT00 => Some(ffi::NID_P256Kyber768Draft00), #[cfg(feature = "pq-experimental")] - ffi::SSL_CURVE_IPDWING => Some(ffi::NID_IPDWing), + ffi::SSL_CURVE_X25519_MLKEM768 => Some(ffi::NID_X25519MLKEM768), _ => None, } } @@ -839,6 +831,66 @@ pub fn select_next_proto<'a>(server: &[u8], client: &'a [u8]) -> Option<&'a [u8] } } +/// Options controlling the behavior of the info callback. +#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)] +pub struct SslInfoCallbackMode(i32); + +impl SslInfoCallbackMode { + /// Signaled for each alert received, warning or fatal. + pub const READ_ALERT: Self = Self(ffi::SSL_CB_READ_ALERT); + + /// Signaled for each alert sent, warning or fatal. + pub const WRITE_ALERT: Self = Self(ffi::SSL_CB_WRITE_ALERT); + + /// Signaled when a handshake begins. + pub const HANDSHAKE_START: Self = Self(ffi::SSL_CB_HANDSHAKE_START); + + /// Signaled when a handshake completes successfully. + pub const HANDSHAKE_DONE: Self = Self(ffi::SSL_CB_HANDSHAKE_DONE); + + /// Signaled when a handshake progresses to a new state. + pub const ACCEPT_LOOP: Self = Self(ffi::SSL_CB_ACCEPT_LOOP); +} + +/// The `value` argument to an info callback. The most-significant byte is the alert level, while +/// the least significant byte is the alert itself. +#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)] +pub enum SslInfoCallbackValue { + /// The unit value (1). Some BoringSSL info callback modes, like ACCEPT_LOOP, always call the + /// callback with `value` set to the unit value. If the [`SslInfoCallbackValue`] is a + /// `Unit`, it can safely be disregarded. + Unit, + /// An alert. See [`SslInfoCallbackAlert`] for details on how to manipulate the alert. This + /// variant should only be present if the info callback was called with a `READ_ALERT` or + /// `WRITE_ALERT` mode. + Alert(SslInfoCallbackAlert), +} + +#[derive(Hash, Copy, Clone, PartialOrd, Ord, Eq, PartialEq, Debug)] +pub struct SslInfoCallbackAlert(c_int); + +impl SslInfoCallbackAlert { + /// The level of the SSL alert. + pub fn alert_level(&self) -> Ssl3AlertLevel { + let value = self.0 >> 8; + Ssl3AlertLevel(value) + } + + /// The value of the SSL alert. + pub fn alert(&self) -> SslAlert { + let value = self.0 & (u8::MAX as i32); + SslAlert(value) + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Ssl3AlertLevel(c_int); + +impl Ssl3AlertLevel { + pub const WARNING: Ssl3AlertLevel = Self(ffi::SSL3_AL_WARNING); + pub const FATAL: Ssl3AlertLevel = Self(ffi::SSL3_AL_FATAL); +} + #[cfg(feature = "rpk")] extern "C" fn rpk_verify_failure_callback( _ssl: *mut ffi::SSL, @@ -858,10 +910,7 @@ pub struct SslContextBuilder { #[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 + #[corresponds(SSL_CTX_new)] pub fn new_rpk() -> Result { unsafe { init(); @@ -901,10 +950,7 @@ impl SslContextBuilder { impl SslContextBuilder { /// Creates a new `SslContextBuilder`. - /// - /// This corresponds to [`SSL_CTX_new`]. - /// - /// [`SSL_CTX_new`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_new.html + #[corresponds(SSL_CTX_new)] pub fn new(method: SslMethod) -> Result { unsafe { init(); @@ -955,10 +1001,7 @@ impl SslContextBuilder { } /// Configures the certificate verification method for new connections. - /// - /// This corresponds to [`SSL_CTX_set_verify`]. - /// - /// [`SSL_CTX_set_verify`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_verify.html + #[corresponds(SSL_CTX_set_verify)] pub fn set_verify(&mut self, mode: SslVerifyMode) { #[cfg(feature = "rpk")] assert!(!self.is_rpk, "This API is not supported for RPK"); @@ -981,13 +1024,10 @@ impl SslContextBuilder { /// Those callbacks can inspect the peer-sent chain, call [`X509StoreContextRef::verify_cert`] /// and inspect the result, or perform other operations more straightforwardly. /// - /// This corresponds to [`SSL_CTX_set_verify`]. - /// /// # Panics /// /// This method panics if this `Ssl` is associated with a RPK context. - /// - /// [`SSL_CTX_set_verify`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_verify.html + #[corresponds(SSL_CTX_set_verify)] pub fn set_verify_callback(&mut self, mode: SslVerifyMode, callback: F) where F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send, @@ -1015,6 +1055,7 @@ impl SslContextBuilder { /// # Panics /// /// This method panics if this `Ssl` is associated with a RPK context. + #[corresponds(SSL_CTX_set_custom_verify)] pub fn set_custom_verify_callback(&mut self, mode: SslVerifyMode, callback: F) where F: Fn(&mut SslRef) -> Result<(), SslVerifyError> + 'static + Sync + Send, @@ -1040,10 +1081,8 @@ impl SslContextBuilder { /// Obtain the server name with the `servername` method and then set the corresponding context /// with `set_ssl_context` /// - /// This corresponds to [`SSL_CTX_set_tlsext_servername_callback`]. - /// - /// [`SSL_CTX_set_tlsext_servername_callback`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_tlsext_servername_callback.html // FIXME tlsext prefix? + #[corresponds(SSL_CTX_set_tlsext_servername_callback)] pub fn set_servername_callback(&mut self, callback: F) where F: Fn(&mut SslRef, &mut SslAlert) -> Result<(), SniError> + 'static + Sync + Send, @@ -1069,10 +1108,7 @@ impl SslContextBuilder { /// Sets the certificate verification depth. /// /// If the peer's certificate chain is longer than this value, verification will fail. - /// - /// This corresponds to [`SSL_CTX_set_verify_depth`]. - /// - /// [`SSL_CTX_set_verify_depth`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_verify_depth.html + #[corresponds(SSL_CTX_set_verify_depth)] pub fn set_verify_depth(&mut self, depth: u32) { #[cfg(feature = "rpk")] assert!(!self.is_rpk, "This API is not supported for RPK"); @@ -1083,35 +1119,28 @@ impl SslContextBuilder { } /// Sets a custom certificate store for verifying peer certificates. - /// - /// This corresponds to [`SSL_CTX_set0_verify_cert_store`]. - /// - /// [`SSL_CTX_set0_verify_cert_store`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set0_verify_cert_store.html + #[corresponds(SSL_CTX_set0_verify_cert_store)] 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 { - let ptr = cert_store.as_ptr(); - cvt(ffi::SSL_CTX_set0_verify_cert_store(self.as_ptr(), ptr) as c_int)?; - mem::forget(cert_store); + cvt( + ffi::SSL_CTX_set0_verify_cert_store(self.as_ptr(), cert_store.into_ptr()) as c_int, + )?; Ok(()) } } /// Replaces the context's certificate store. - /// - /// This corresponds to [`SSL_CTX_set_cert_store`]. - /// - /// [`SSL_CTX_set_cert_store`]: https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_cert_store.html + #[corresponds(SSL_CTX_set_cert_store)] 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 { - ffi::SSL_CTX_set_cert_store(self.as_ptr(), cert_store.as_ptr()); - mem::forget(cert_store); + ffi::SSL_CTX_set_cert_store(self.as_ptr(), cert_store.into_ptr()); } } @@ -1121,10 +1150,7 @@ impl SslContextBuilder { /// instead of a single record at a time. /// /// It has no effect when used with DTLS. - /// - /// This corresponds to [`SSL_CTX_set_read_ahead`]. - /// - /// [`SSL_CTX_set_read_ahead`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_read_ahead.html + #[corresponds(SSL_CTX_set_read_ahead)] pub fn set_read_ahead(&mut self, read_ahead: bool) { unsafe { ffi::SSL_CTX_set_read_ahead(self.as_ptr(), read_ahead as c_int); @@ -1132,27 +1158,20 @@ impl SslContextBuilder { } /// Sets the mode used by the context, returning the new bit-mask after adding mode. - /// - /// This corresponds to [`SSL_CTX_set_mode`]. - /// - /// [`SSL_CTX_set_mode`]: https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_mode.html + #[corresponds(SSL_CTX_set_mode)] pub fn set_mode(&mut self, mode: SslMode) -> SslMode { let bits = unsafe { ffi::SSL_CTX_set_mode(self.as_ptr(), mode.bits()) }; SslMode::from_bits_retain(bits) } /// Sets the parameters to be used during ephemeral Diffie-Hellman key exchange. - /// - /// This corresponds to [`SSL_CTX_set_tmp_dh`]. - /// - /// [`SSL_CTX_set_tmp_dh`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_tmp_dh.html + #[corresponds(SSL_CTX_set_tmp_dh)] pub fn set_tmp_dh(&mut self, dh: &DhRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_CTX_set_tmp_dh(self.as_ptr(), dh.as_ptr()) as c_int).map(|_| ()) } } /// Sets the parameters to be used during ephemeral elliptic curve Diffie-Hellman key exchange. - /// - /// This corresponds to `SSL_CTX_set_tmp_ecdh`. + #[corresponds(SSL_CTX_set_tmp_ecdh)] pub fn set_tmp_ecdh(&mut self, key: &EcKeyRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_CTX_set_tmp_ecdh(self.as_ptr(), key.as_ptr()) as c_int).map(|_| ()) } } @@ -1161,10 +1180,7 @@ impl SslContextBuilder { /// /// These locations are read from the `SSL_CERT_FILE` and `SSL_CERT_DIR` environment variables /// if present, or defaults specified at OpenSSL build time otherwise. - /// - /// This corresponds to [`SSL_CTX_set_default_verify_paths`]. - /// - /// [`SSL_CTX_set_default_verify_paths`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_default_verify_paths.html + #[corresponds(SSL_CTX_set_default_verify_paths)] pub fn set_default_verify_paths(&mut self) -> Result<(), ErrorStack> { #[cfg(feature = "rpk")] assert!(!self.is_rpk, "This API is not supported for RPK"); @@ -1175,10 +1191,7 @@ impl SslContextBuilder { /// Loads trusted root certificates from a file. /// /// The file should contain a sequence of PEM-formatted CA certificates. - /// - /// This corresponds to [`SSL_CTX_load_verify_locations`]. - /// - /// [`SSL_CTX_load_verify_locations`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_load_verify_locations.html + #[corresponds(SSL_CTX_load_verify_locations)] pub fn set_ca_file>(&mut self, file: P) -> Result<(), ErrorStack> { #[cfg(feature = "rpk")] assert!(!self.is_rpk, "This API is not supported for RPK"); @@ -1198,10 +1211,7 @@ impl SslContextBuilder { /// /// The CA certificates must still be added to the trust root - they are not automatically set /// as trusted by this method. - /// - /// This corresponds to [`SSL_CTX_set_client_CA_list`]. - /// - /// [`SSL_CTX_set_client_CA_list`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_client_CA_list.html + #[corresponds(SSL_CTX_set_client_CA_list)] pub fn set_client_ca_list(&mut self, list: Stack) { #[cfg(feature = "rpk")] assert!(!self.is_rpk, "This API is not supported for RPK"); @@ -1214,10 +1224,7 @@ impl SslContextBuilder { /// Add the provided CA certificate to the list sent by the server to the client when /// requesting client-side TLS authentication. - /// - /// This corresponds to [`SSL_CTX_add_client_CA`]. - /// - /// [`SSL_CTX_add_client_CA`]: https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_client_CA_list.html + #[corresponds(SSL_CTX_add_client_CA)] 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"); @@ -1233,10 +1240,7 @@ impl SslContextBuilder { /// /// This value should be set when using client certificates, or each request will fail its /// handshake and need to be restarted. - /// - /// This corresponds to [`SSL_CTX_set_session_id_context`]. - /// - /// [`SSL_CTX_set_session_id_context`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_session_id_context.html + #[corresponds(SSL_CTX_set_session_id_context)] pub fn set_session_id_context(&mut self, sid_ctx: &[u8]) -> Result<(), ErrorStack> { unsafe { assert!(sid_ctx.len() <= c_uint::MAX as usize); @@ -1254,10 +1258,7 @@ impl SslContextBuilder { /// Only a single certificate will be loaded - use `add_extra_chain_cert` to add the remainder /// of the certificate chain, or `set_certificate_chain_file` to load the entire chain from a /// single file. - /// - /// This corresponds to [`SSL_CTX_use_certificate_file`]. - /// - /// [`SSL_CTX_use_certificate_file`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_use_certificate_file.html + #[corresponds(SSL_CTX_use_certificate_file)] pub fn set_certificate_file>( &mut self, file: P, @@ -1282,10 +1283,7 @@ impl SslContextBuilder { /// The file should contain a sequence of PEM-formatted certificates, the first being the leaf /// certificate, and the remainder forming the chain of certificates up to and including the /// trusted root certificate. - /// - /// This corresponds to [`SSL_CTX_use_certificate_chain_file`]. - /// - /// [`SSL_CTX_use_certificate_chain_file`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_use_certificate_file.html + #[corresponds(SSL_CTX_use_certificate_chain_file)] pub fn set_certificate_chain_file>( &mut self, file: P, @@ -1303,10 +1301,7 @@ impl SslContextBuilder { /// Sets the leaf certificate. /// /// Use `add_extra_chain_cert` to add the remainder of the certificate chain. - /// - /// This corresponds to [`SSL_CTX_use_certificate`]. - /// - /// [`SSL_CTX_use_certificate`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_use_certificate_file.html + #[corresponds(SSL_CTX_use_certificate)] pub fn set_certificate(&mut self, cert: &X509Ref) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_CTX_use_certificate(self.as_ptr(), cert.as_ptr())).map(|_| ()) } } @@ -1315,26 +1310,19 @@ impl SslContextBuilder { /// /// This chain should contain all certificates necessary to go from the certificate specified by /// `set_certificate` to a trusted root. - /// - /// This corresponds to [`SSL_CTX_add_extra_chain_cert`]. - /// - /// [`SSL_CTX_add_extra_chain_cert`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_add_extra_chain_cert.html + #[corresponds(SSL_CTX_add_extra_chain_cert)] 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 { - cvt(ffi::SSL_CTX_add_extra_chain_cert(self.as_ptr(), cert.as_ptr()) as c_int)?; - mem::forget(cert); + cvt(ffi::SSL_CTX_add_extra_chain_cert(self.as_ptr(), cert.into_ptr()) as c_int)?; Ok(()) } } /// Loads the private key from a file. - /// - /// This corresponds to [`SSL_CTX_use_PrivateKey_file`]. - /// - /// [`SSL_CTX_use_PrivateKey_file`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_use_PrivateKey_file.html + #[corresponds(SSL_CTX_use_PrivateKey_file)] pub fn set_private_key_file>( &mut self, file: P, @@ -1352,10 +1340,7 @@ impl SslContextBuilder { } /// Sets the private key. - /// - /// This corresponds to [`SSL_CTX_use_PrivateKey`]. - /// - /// [`SSL_CTX_use_PrivateKey`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_use_PrivateKey_file.html + #[corresponds(SSL_CTX_use_PrivateKey)] pub fn set_private_key(&mut self, key: &PKeyRef) -> Result<(), ErrorStack> where T: HasPrivate, @@ -1371,10 +1356,8 @@ impl SslContextBuilder { /// /// See [`ciphers`] for details on the format. /// - /// This corresponds to [`SSL_CTX_set_cipher_list`]. - /// - /// [`ciphers`]: https://www.openssl.org/docs/man1.1.0/apps/ciphers.html - /// [`SSL_CTX_set_cipher_list`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_cipher_list.html + /// [`ciphers`]: https://www.openssl.org/docs/manmaster/apps/ciphers.html + #[corresponds(SSL_CTX_set_cipher_list)] pub fn set_cipher_list(&mut self, cipher_list: &str) -> Result<(), ErrorStack> { let cipher_list = CString::new(cipher_list).unwrap(); unsafe { @@ -1390,44 +1373,33 @@ impl SslContextBuilder { /// /// See [`ciphers`] for details on the format /// - /// This corresponds to [`SSL_CTX_get_ciphers`]. - /// /// [`ciphers`]: https://www.openssl.org/docs/manmaster/man1/ciphers.html - /// [`SSL_CTX_set_cipher_list`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_get_ciphers.html + #[corresponds(SSL_CTX_get_ciphers)] pub fn ciphers(&self) -> Option<&StackRef> { self.ctx.ciphers() } /// Sets the options used by the context, returning the old set. /// - /// This corresponds to [`SSL_CTX_set_options`]. - /// /// # Note /// /// This *enables* the specified options, but does not disable unspecified options. Use /// `clear_options` for that. - /// - /// [`SSL_CTX_set_options`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_options.html + #[corresponds(SSL_CTX_set_options)] pub fn set_options(&mut self, option: SslOptions) -> SslOptions { let bits = unsafe { ffi::SSL_CTX_set_options(self.as_ptr(), option.bits()) }; SslOptions::from_bits_retain(bits) } /// Returns the options used by the context. - /// - /// This corresponds to [`SSL_CTX_get_options`]. - /// - /// [`SSL_CTX_get_options`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_options.html + #[corresponds(SSL_CTX_get_options)] pub fn options(&self) -> SslOptions { let bits = unsafe { ffi::SSL_CTX_get_options(self.as_ptr()) }; SslOptions::from_bits_retain(bits) } /// Clears the options used by the context, returning the old set. - /// - /// This corresponds to [`SSL_CTX_clear_options`]. - /// - /// [`SSL_CTX_clear_options`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_options.html + #[corresponds(SSL_CTX_clear_options)] pub fn clear_options(&mut self, option: SslOptions) -> SslOptions { let bits = unsafe { ffi::SSL_CTX_clear_options(self.as_ptr(), option.bits()) }; SslOptions::from_bits_retain(bits) @@ -1437,10 +1409,7 @@ impl SslContextBuilder { /// /// If version is `None`, the default minimum version is used. For BoringSSL this defaults to /// TLS 1.0. - /// - /// This corresponds to [`SSL_CTX_set_min_proto_version`]. - /// - /// [`SSL_CTX_set_min_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_min_proto_version.html + #[corresponds(SSL_CTX_set_min_proto_version)] pub fn set_min_proto_version(&mut self, version: Option) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_CTX_set_min_proto_version( @@ -1454,10 +1423,7 @@ impl SslContextBuilder { /// Sets the maximum supported protocol version. /// /// If version is `None`, the default maximum version is used. For BoringSSL this is TLS 1.3. - /// - /// This corresponds to [`SSL_CTX_set_max_proto_version`]. - /// - /// [`SSL_CTX_set_max_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_max_proto_version.html + #[corresponds(SSL_CTX_set_max_proto_version)] pub fn set_max_proto_version(&mut self, version: Option) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_CTX_set_max_proto_version( @@ -1469,10 +1435,7 @@ impl SslContextBuilder { } /// Gets the minimum supported protocol version. - /// - /// This corresponds to [`SSL_CTX_get_min_proto_version`]. - /// - /// [`SSL_CTX_get_min_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_min_proto_version.html + #[corresponds(SSL_CTX_get_min_proto_version)] pub fn min_proto_version(&mut self) -> Option { unsafe { let r = ffi::SSL_CTX_get_min_proto_version(self.as_ptr()); @@ -1485,10 +1448,7 @@ impl SslContextBuilder { } /// Gets the maximum supported protocol version. - /// - /// This corresponds to [`SSL_CTX_get_max_proto_version`]. - /// - /// [`SSL_CTX_get_max_proto_version`]: https://www.openssl.org/docs/man3.1/man3/SSL_CTX_get_max_proto_version.html + #[corresponds(SSL_CTX_get_max_proto_version)] pub fn max_proto_version(&mut self) -> Option { unsafe { let r = ffi::SSL_CTX_get_max_proto_version(self.as_ptr()); @@ -1506,10 +1466,7 @@ impl SslContextBuilder { /// names prefixed by their byte length. For example, the protocol list consisting of `spdy/1` /// and `http/1.1` is encoded as `b"\x06spdy/1\x08http/1.1"`. The protocols are ordered by /// preference. - /// - /// This corresponds to [`SSL_CTX_set_alpn_protos`]. - /// - /// [`SSL_CTX_set_alpn_protos`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_alpn_protos.html + #[corresponds(SSL_CTX_set_alpn_protos)] pub fn set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> { unsafe { #[cfg_attr(not(feature = "fips"), allow(clippy::unnecessary_cast))] @@ -1531,10 +1488,7 @@ impl SslContextBuilder { } /// Enables the DTLS extension "use_srtp" as defined in RFC5764. - /// - /// This corresponds to [`SSL_CTX_set_tlsext_use_srtp`]. - /// - /// [`SSL_CTX_set_tlsext_use_srtp`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_tlsext_use_srtp.html + #[corresponds(SSL_CTX_set_tlsext_use_srtp)] pub fn set_tlsext_use_srtp(&mut self, protocols: &str) -> Result<(), ErrorStack> { unsafe { let cstr = CString::new(protocols).unwrap(); @@ -1557,11 +1511,9 @@ impl SslContextBuilder { /// of those protocols on success. The [`select_next_proto`] function implements the standard /// protocol selection algorithm. /// - /// This corresponds to [`SSL_CTX_set_alpn_select_cb`]. - /// /// [`SslContextBuilder::set_alpn_protos`]: struct.SslContextBuilder.html#method.set_alpn_protos /// [`select_next_proto`]: fn.select_next_proto.html - /// [`SSL_CTX_set_alpn_select_cb`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_alpn_protos.html + #[corresponds(SSL_CTX_set_alpn_select_cb)] pub fn set_alpn_select_callback(&mut self, callback: F) where F: for<'a> Fn(&mut SslRef, &'a [u8]) -> Result<&'a [u8], AlpnError> + 'static + Sync + Send, @@ -1579,10 +1531,7 @@ impl SslContextBuilder { /// Sets a callback that is called before most ClientHello processing and before the decision whether /// to resume a session is made. The callback may inspect the ClientHello and configure the /// connection. - /// - /// This corresponds to [`SSL_CTX_set_select_certificate_cb`]. - /// - /// [`SSL_CTX_set_select_certificate_cb`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_select_certificate_cb.html + #[corresponds(SSL_CTX_set_select_certificate_cb)] pub fn set_select_certificate_callback(&mut self, callback: F) where F: Fn(ClientHello<'_>) -> Result<(), SelectCertError> + Sync + Send + 'static, @@ -1599,10 +1548,7 @@ impl SslContextBuilder { /// Configures a custom private key method on the context. /// /// See [`PrivateKeyMethod`] for more details. - /// - /// This corresponds to [`SSL_CTX_set_private_key_method`] - /// - /// [`SSL_CTX_set_private_key_method`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_private_key_method + #[corresponds(SSL_CTX_set_private_key_method)] pub fn set_private_key_method(&mut self, method: M) where M: PrivateKeyMethod, @@ -1622,19 +1568,13 @@ impl SslContextBuilder { } /// Checks for consistency between the private key and certificate. - /// - /// This corresponds to [`SSL_CTX_check_private_key`]. - /// - /// [`SSL_CTX_check_private_key`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_check_private_key.html + #[corresponds(SSL_CTX_check_private_key)] pub fn check_private_key(&self) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_CTX_check_private_key(self.as_ptr())).map(|_| ()) } } /// Returns a shared reference to the context's certificate store. - /// - /// This corresponds to [`SSL_CTX_get_cert_store`]. - /// - /// [`SSL_CTX_get_cert_store`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_cert_store.html + #[corresponds(SSL_CTX_get_cert_store)] pub fn cert_store(&self) -> &X509StoreBuilderRef { #[cfg(feature = "rpk")] assert!(!self.is_rpk, "This API is not supported for RPK"); @@ -1643,10 +1583,7 @@ impl SslContextBuilder { } /// Returns a mutable reference to the context's certificate store. - /// - /// This corresponds to [`SSL_CTX_get_cert_store`]. - /// - /// [`SSL_CTX_get_cert_store`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_cert_store.html + #[corresponds(SSL_CTX_get_cert_store)] pub fn cert_store_mut(&mut self) -> &mut X509StoreBuilderRef { #[cfg(feature = "rpk")] assert!(!self.is_rpk, "This API is not supported for RPK"); @@ -1666,10 +1603,7 @@ impl SslContextBuilder { /// returned to clients. The status may be set with the `SslRef::set_ocsp_status` method. A /// response of `Ok(true)` indicates that the OCSP status should be returned to the client, and /// `Ok(false)` indicates that the status should not be returned to the client. - /// - /// This corresponds to [`SSL_CTX_set_tlsext_status_cb`]. - /// - /// [`SSL_CTX_set_tlsext_status_cb`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_tlsext_status_cb.html + #[corresponds(SSL_CTX_set_tlsext_status_cb)] pub fn set_status_callback(&mut self, callback: F) -> Result<(), ErrorStack> where F: Fn(&mut SslRef) -> Result + 'static + Sync + Send, @@ -1689,10 +1623,7 @@ impl SslContextBuilder { /// The callback will be called with the SSL context, an identity hint if one was provided /// by the server, a mutable slice for each of the identity and pre-shared key bytes. The /// identity must be written as a null-terminated C string. - /// - /// This corresponds to [`SSL_CTX_set_psk_client_callback`]. - /// - /// [`SSL_CTX_set_psk_client_callback`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_psk_client_callback.html + #[corresponds(SSL_CTX_set_psk_client_callback)] pub fn set_psk_client_callback(&mut self, callback: F) where F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8], &mut [u8]) -> Result @@ -1722,10 +1653,7 @@ impl SslContextBuilder { /// The callback will be called with the SSL context, an identity provided by the client, /// and, a mutable slice for the pre-shared key bytes. The callback returns the number of /// bytes in the pre-shared key. - /// - /// This corresponds to [`SSL_CTX_set_psk_server_callback`]. - /// - /// [`SSL_CTX_set_psk_server_callback`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_psk_server_callback.html + #[corresponds(SSL_CTX_set_psk_server_callback)] pub fn set_psk_server_callback(&mut self, callback: F) where F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8]) -> Result @@ -1750,11 +1678,9 @@ impl SslContextBuilder { /// Note that session caching must be enabled for the callback to be invoked, and it defaults /// off for clients. [`set_session_cache_mode`] controls that behavior. /// - /// This corresponds to [`SSL_CTX_sess_set_new_cb`]. - /// /// [`SslRef::session`]: struct.SslRef.html#method.session /// [`set_session_cache_mode`]: #method.set_session_cache_mode - /// [`SSL_CTX_sess_set_new_cb`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_sess_set_new_cb.html + #[corresponds(SSL_CTX_sess_set_new_cb)] pub fn set_new_session_callback(&mut self, callback: F) where F: Fn(&mut SslRef, SslSession) + 'static + Sync + Send, @@ -1768,10 +1694,7 @@ impl SslContextBuilder { /// Sets the callback which is called when sessions are removed from the context. /// /// Sessions can be removed because they have timed out or because they are considered faulty. - /// - /// This corresponds to [`SSL_CTX_sess_set_remove_cb`]. - /// - /// [`SSL_CTX_sess_set_remove_cb`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_sess_set_new_cb.html + #[corresponds(SSL_CTX_sess_set_remove_cb)] pub fn set_remove_session_callback(&mut self, callback: F) where F: Fn(&SslContextRef, &SslSessionRef) + 'static + Sync + Send, @@ -1792,13 +1715,10 @@ impl SslContextBuilder { /// return the session corresponding to that ID if available. This is only used for servers, not /// clients. /// - /// This corresponds to [`SSL_CTX_sess_set_get_cb`]. - /// /// # Safety /// /// The returned [`SslSession`] must not be associated with a different [`SslContext`]. - /// - /// [`SSL_CTX_sess_set_get_cb`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_sess_set_new_cb.html + #[corresponds(SSL_CTX_sess_set_get_cb)] pub unsafe fn set_get_session_callback(&mut self, callback: F) where F: Fn(&mut SslRef, &[u8]) -> Result, GetSessionPendingError> @@ -1815,10 +1735,7 @@ impl SslContextBuilder { /// The callback is invoked whenever TLS key material is generated, and is passed a line of NSS /// SSLKEYLOGFILE-formatted text. This can be used by tools like Wireshark to decrypt message /// traffic. The line does not contain a trailing newline. - /// - /// This corresponds to [`SSL_CTX_set_keylog_callback`]. - /// - /// [`SSL_CTX_set_keylog_callback`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_keylog_callback.html + #[corresponds(SSL_CTX_set_keylog_callback)] pub fn set_keylog_callback(&mut self, callback: F) where F: Fn(&SslRef, &str) + 'static + Sync + Send, @@ -1832,10 +1749,7 @@ impl SslContextBuilder { /// Sets the session caching mode use for connections made with the context. /// /// Returns the previous session caching mode. - /// - /// This corresponds to [`SSL_CTX_set_session_cache_mode`]. - /// - /// [`SSL_CTX_set_session_cache_mode`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_session_cache_mode.html + #[corresponds(SSL_CTX_set_session_cache_mode)] pub fn set_session_cache_mode(&mut self, mode: SslSessionCacheMode) -> SslSessionCacheMode { unsafe { let bits = ffi::SSL_CTX_set_session_cache_mode(self.as_ptr(), mode.bits()); @@ -1848,12 +1762,9 @@ impl SslContextBuilder { /// This can be used to provide data to callbacks registered with the context. Use the /// `SslContext::new_ex_index` method to create an `Index`. /// - /// This corresponds to [`SSL_CTX_set_ex_data`]. - /// /// Note that if this method is called multiple times with the same index, any previous /// value stored in the `SslContextBuilder` will be leaked. - /// - /// [`SSL_CTX_set_ex_data`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_ex_data.html + #[corresponds(SSL_CTX_set_ex_data)] pub fn set_ex_data(&mut self, index: Index, data: T) { unsafe { self.ctx.set_ex_data(index, data); @@ -1865,11 +1776,8 @@ impl SslContextBuilder { /// This can be used to provide data to callbacks registered with the context. Use the /// `Ssl::new_ex_index` method to create an `Index`. /// - /// This corresponds to [`SSL_set_ex_data`]. - /// /// Any previous value will be returned and replaced by the new one. - /// - /// [`SSL_set_ex_data`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_ex_data.html + #[corresponds(SSL_CTX_set_ex_data)] pub fn replace_ex_data(&mut self, index: Index, data: T) -> Option { unsafe { self.ctx.replace_ex_data(index, data) } } @@ -1877,20 +1785,14 @@ impl SslContextBuilder { /// Sets the context's session cache size limit, returning the previous limit. /// /// A value of 0 means that the cache size is unbounded. - /// - /// This corresponds to [`SSL_CTX_sess_get_cache_size`]. - /// - /// [`SSL_CTX_sess_get_cache_size`]: https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_sess_set_cache_size.html + #[corresponds(SSL_CTX_sess_set_cache_size)] #[allow(clippy::useless_conversion)] pub fn set_session_cache_size(&mut self, size: u32) -> u64 { unsafe { ffi::SSL_CTX_sess_set_cache_size(self.as_ptr(), size.into()).into() } } /// Sets the context's supported signature algorithms. - /// - /// This corresponds to [`SSL_CTX_set1_sigalgs_list`]. - /// - /// [`SSL_CTX_set1_sigalgs_list`]: https://www.openssl.org/docs/man1.1.0/man3/SSL_CTX_set1_sigalgs_list.html + #[corresponds(SSL_CTX_set1_sigalgs_list)] pub fn set_sigalgs_list(&mut self, sigalgs: &str) -> Result<(), ErrorStack> { let sigalgs = CString::new(sigalgs).unwrap(); unsafe { @@ -1900,33 +1802,24 @@ impl SslContextBuilder { } /// Set's whether the context should enable GREASE. - /// - /// This corresponds to [`SSL_CTX_set_grease_enabled`] - /// - /// [`SSL_CTX_set_grease_enabled`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_grease_enabled + #[corresponds(SSL_CTX_set_grease_enabled)] pub fn set_grease_enabled(&mut self, enabled: bool) { unsafe { ffi::SSL_CTX_set_grease_enabled(self.as_ptr(), enabled as _) } } /// Configures whether ClientHello extensions should be permuted. /// - /// This corresponds to [`SSL_CTX_set_permute_extensions`]. - /// - /// [`SSL_CTX_set_permute_extensions`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_permute_extensions - /// /// Note: This is gated to non-fips because the fips feature builds with a separate /// version of BoringSSL which doesn't yet include these APIs. /// Once the submoduled fips commit is upgraded, these gates can be removed. + #[corresponds(SSL_CTX_set_permute_extensions)] #[cfg(not(feature = "fips"))] pub fn set_permute_extensions(&mut self, enabled: bool) { unsafe { ffi::SSL_CTX_set_permute_extensions(self.as_ptr(), enabled as _) } } /// Sets the context's supported signature verification algorithms. - /// - /// This corresponds to [`SSL_CTX_set_verify_algorithm_prefs`] - /// - /// [`SSL_CTX_set_verify_algorithm_prefs`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_verify_algorithm_prefs + #[corresponds(SSL_CTX_set_verify_algorithm_prefs)] pub fn set_verify_algorithm_prefs( &mut self, prefs: &[SslSignatureAlgorithm], @@ -1942,33 +1835,42 @@ impl SslContextBuilder { } /// Enables SCT requests on all client SSL handshakes. - /// - /// This corresponds to [`SSL_CTX_enable_signed_cert_timestamps`] - /// - /// [`SSL_CTX_enable_signed_cert_timestamps`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_enable_signed_cert_timestamps + #[corresponds(SSL_CTX_enable_signed_cert_timestamps)] pub fn enable_signed_cert_timestamps(&mut self) { unsafe { ffi::SSL_CTX_enable_signed_cert_timestamps(self.as_ptr()) } } /// Enables OCSP stapling on all client SSL handshakes. - /// - /// This corresponds to [`SSL_CTX_enable_ocsp_stapling`] - /// - /// [`SSL_CTX_enable_ocsp_stapling`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_enable_ocsp_stapling + #[corresponds(SSL_CTX_enable_ocsp_stapling)] pub fn enable_ocsp_stapling(&mut self) { unsafe { ffi::SSL_CTX_enable_ocsp_stapling(self.as_ptr()) } } /// Sets the context's supported curves. - /// - /// This corresponds to [`SSL_CTX_set1_curves`] - /// - /// [`SSL_CTX_set1_curves`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set1_curves // // If the "kx-*" flags are used to set key exchange preference, then don't allow the user to // set them here. This ensures we don't override the user's preference without telling them: // when the flags are used, the preferences are set just before connecting or accepting. #[cfg(not(feature = "kx-safe-default"))] + #[corresponds(SSL_CTX_set1_curves_list)] + pub fn set_curves_list(&mut self, curves: &str) -> Result<(), ErrorStack> { + let curves = CString::new(curves).unwrap(); + unsafe { + cvt_0i(ffi::SSL_CTX_set1_curves_list( + self.as_ptr(), + curves.as_ptr() as *const _, + )) + .map(|_| ()) + } + } + + /// Sets the context's supported curves. + // + // If the "kx-*" flags are used to set key exchange preference, then don't allow the user to + // set them here. This ensures we don't override the user's preference without telling them: + // when the flags are used, the preferences are set just before connecting or accepting. + #[corresponds(SSL_CTX_set1_curves)] + #[cfg(not(feature = "kx-safe-default"))] pub fn set_curves(&mut self, curves: &[SslCurve]) -> Result<(), ErrorStack> { let curves: Vec = curves.iter().filter_map(|curve| curve.nid()).collect(); @@ -1984,15 +1886,25 @@ impl SslContextBuilder { /// Sets the context's compliance policy. /// - /// This corresponds to [`SSL_CTX_set_compliance_policy`] - /// - /// [`SSL_CTX_set_compliance_policy`] https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_compliance_policy /// This feature isn't available in the certified version of BoringSSL. + #[corresponds(SSL_CTX_set_compliance_policy)] #[cfg(not(feature = "fips"))] pub fn set_compliance_policy(&mut self, policy: CompliancePolicy) -> Result<(), ErrorStack> { unsafe { cvt_0i(ffi::SSL_CTX_set_compliance_policy(self.as_ptr(), policy.0)).map(|_| ()) } } + /// Sets the context's info callback. + #[corresponds(SSL_CTX_set_info_callback)] + pub fn set_info_callback(&mut self, callback: F) + where + F: Fn(&SslRef, SslInfoCallbackMode, SslInfoCallbackValue) + Send + Sync + 'static, + { + unsafe { + self.replace_ex_data(SslContext::cached_ex_index::(), callback); + ffi::SSL_CTX_set_info_callback(self.as_ptr(), Some(callbacks::raw_info_callback::)); + } + } + /// Consumes the builder, returning a new `SslContext`. pub fn build(self) -> SslContext { self.ctx @@ -2044,10 +1956,7 @@ impl SslContext { /// /// Each invocation of this function is guaranteed to return a distinct index. These can be used /// to store data in the context that can be retrieved later by callbacks, for example. - /// - /// This corresponds to [`SSL_CTX_get_ex_new_index`]. - /// - /// [`SSL_CTX_get_ex_new_index`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_get_ex_new_index.html + #[corresponds(SSL_CTX_get_ex_new_index)] pub fn new_ex_index() -> Result, ErrorStack> where T: 'static + Sync + Send, @@ -2078,10 +1987,8 @@ impl SslContext { /// /// See [`ciphers`] for details on the format /// - /// This corresponds to [`SSL_CTX_get_ciphers`]. - /// /// [`ciphers`]: https://www.openssl.org/docs/manmaster/man1/ciphers.html - /// [`SSL_CTX_set_cipher_list`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_get_ciphers.html + #[corresponds(SSL_CTX_get_ciphers)] pub fn ciphers(&self) -> Option<&StackRef> { unsafe { let ciphers = ffi::SSL_CTX_get_ciphers(self.as_ptr()); @@ -2096,10 +2003,7 @@ impl SslContext { impl SslContextRef { /// Returns the certificate associated with this `SslContext`, if present. - /// - /// This corresponds to [`SSL_CTX_get0_certificate`]. - /// - /// [`SSL_CTX_get0_certificate`]: https://www.openssl.org/docs/man1.1.0/ssl/ssl.html + #[corresponds(SSL_CTX_get0_certificate)] pub fn certificate(&self) -> Option<&X509Ref> { #[cfg(feature = "rpk")] assert!(!self.is_rpk(), "This API is not supported for RPK"); @@ -2115,10 +2019,7 @@ impl SslContextRef { } /// Returns the private key associated with this `SslContext`, if present. - /// - /// This corresponds to [`SSL_CTX_get0_privatekey`]. - /// - /// [`SSL_CTX_get0_privatekey`]: https://www.openssl.org/docs/man1.1.0/ssl/ssl.html + #[corresponds(SSL_CTX_get0_privatekey)] pub fn private_key(&self) -> Option<&PKeyRef> { unsafe { let ptr = ffi::SSL_CTX_get0_privatekey(self.as_ptr()); @@ -2131,10 +2032,7 @@ impl SslContextRef { } /// Returns a shared reference to the certificate store used for verification. - /// - /// This corresponds to [`SSL_CTX_get_cert_store`]. - /// - /// [`SSL_CTX_get_cert_store`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_cert_store.html + #[corresponds(SSL_CTX_get_cert_store)] pub fn cert_store(&self) -> &X509StoreRef { #[cfg(feature = "rpk")] assert!(!self.is_rpk(), "This API is not supported for RPK"); @@ -2143,8 +2041,7 @@ impl SslContextRef { } /// Returns a shared reference to the stack of certificates making up the chain from the leaf. - /// - /// This corresponds to `SSL_CTX_get_extra_chain_certs`. + #[corresponds(SSL_CTX_get_extra_chain_certs)] pub fn extra_chain_certs(&self) -> &StackRef { unsafe { let mut chain = ptr::null_mut(); @@ -2155,10 +2052,7 @@ impl SslContextRef { } /// Returns a reference to the extra data at the specified index. - /// - /// This corresponds to [`SSL_CTX_get_ex_data`]. - /// - /// [`SSL_CTX_get_ex_data`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_get_ex_data.html + #[corresponds(SSL_CTX_get_ex_data)] pub fn ex_data(&self, index: Index) -> Option<&T> { unsafe { let data = ffi::SSL_CTX_get_ex_data(self.as_ptr(), index.as_raw()); @@ -2172,6 +2066,7 @@ impl SslContextRef { // Unsafe because SSL contexts are not guaranteed to be unique, we call // this only from SslContextBuilder. + #[corresponds(SSL_CTX_get_ex_data)] unsafe fn ex_data_mut(&mut self, index: Index) -> Option<&mut T> { let data = ffi::SSL_CTX_get_ex_data(self.as_ptr(), index.as_raw()); if data.is_null() { @@ -2183,6 +2078,7 @@ impl SslContextRef { // Unsafe because SSL contexts are not guaranteed to be unique, we call // this only from SslContextBuilder. + #[corresponds(SSL_CTX_set_ex_data)] unsafe fn set_ex_data(&mut self, index: Index, data: T) { unsafe { let data = Box::into_raw(Box::new(data)) as *mut c_void; @@ -2192,6 +2088,7 @@ impl SslContextRef { // Unsafe because SSL contexts are not guaranteed to be unique, we call // this only from SslContextBuilder. + #[corresponds(SSL_CTX_set_ex_data)] unsafe fn replace_ex_data(&mut self, index: Index, data: T) -> Option { if let Some(old) = self.ex_data_mut(index) { return Some(mem::replace(old, data)); @@ -2206,14 +2103,11 @@ impl SslContextRef { /// /// Returns `true` if the session was successfully added to the cache, and `false` if it was already present. /// - /// This corresponds to [`SSL_CTX_add_session`]. - /// /// # Safety /// /// The caller of this method is responsible for ensuring that the session has never been used with another /// `SslContext` than this one. - /// - /// [`SSL_CTX_add_session`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_remove_session.html + #[corresponds(SSL_CTX_add_session)] pub unsafe fn add_session(&self, session: &SslSessionRef) -> bool { ffi::SSL_CTX_add_session(self.as_ptr(), session.as_ptr()) != 0 } @@ -2222,14 +2116,11 @@ impl SslContextRef { /// /// Returns `true` if the session was successfully found and removed, and `false` otherwise. /// - /// This corresponds to [`SSL_CTX_remove_session`]. - /// /// # Safety /// /// The caller of this method is responsible for ensuring that the session has never been used with another /// `SslContext` than this one. - /// - /// [`SSL_CTX_remove_session`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_remove_session.html + #[corresponds(SSL_CTX_remove_session)] pub unsafe fn remove_session(&self, session: &SslSessionRef) -> bool { ffi::SSL_CTX_remove_session(self.as_ptr(), session.as_ptr()) != 0 } @@ -2237,10 +2128,7 @@ impl SslContextRef { /// Returns the context's session cache size limit. /// /// A value of 0 means that the cache size is unbounded. - /// - /// This corresponds to [`SSL_CTX_sess_get_cache_size`]. - /// - /// [`SSL_CTX_sess_get_cache_size`]: https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_sess_set_cache_size.html + #[corresponds(SSL_CTX_sess_get_cache_size)] #[allow(clippy::useless_conversion)] pub fn session_cache_size(&self) -> u64 { unsafe { ffi::SSL_CTX_sess_get_cache_size(self.as_ptr()).into() } @@ -2248,10 +2136,8 @@ impl SslContextRef { /// Returns the verify mode that was set on this context from [`SslContextBuilder::set_verify`]. /// - /// This corresponds to [`SSL_CTX_get_verify_mode`]. - /// /// [`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 + #[corresponds(SSL_CTX_get_verify_mode)] pub fn verify_mode(&self) -> SslVerifyMode { #[cfg(feature = "rpk")] assert!(!self.is_rpk(), "This API is not supported for RPK"); @@ -2353,6 +2239,7 @@ impl ClientHello<'_> { pub struct SslCipher(*mut ffi::SSL_CIPHER); impl SslCipher { + #[corresponds(SSL_get_cipher_by_value)] pub fn from_value(value: u16) -> Option { unsafe { let ptr = ffi::SSL_get_cipher_by_value(value); @@ -2409,10 +2296,7 @@ unsafe impl ForeignTypeRef for SslCipherRef { impl SslCipherRef { /// Returns the name of the cipher. - /// - /// This corresponds to [`SSL_CIPHER_get_name`]. - /// - /// [`SSL_CIPHER_get_name`]: https://www.openssl.org/docs/manmaster/man3/SSL_CIPHER_get_name.html + #[corresponds(SSL_CIPHER_get_name)] pub fn name(&self) -> &'static str { unsafe { let ptr = ffi::SSL_CIPHER_get_name(self.as_ptr()); @@ -2421,10 +2305,7 @@ impl SslCipherRef { } /// Returns the RFC-standard name of the cipher, if one exists. - /// - /// This corresponds to [`SSL_CIPHER_standard_name`]. - /// - /// [`SSL_CIPHER_standard_name`]: https://www.openssl.org/docs/manmaster/man3/SSL_CIPHER_get_name.html + #[corresponds(SSL_CIPHER_standard_name)] pub fn standard_name(&self) -> Option<&'static str> { unsafe { let ptr = ffi::SSL_CIPHER_standard_name(self.as_ptr()); @@ -2437,10 +2318,7 @@ impl SslCipherRef { } /// Returns the SSL/TLS protocol version that first defined the cipher. - /// - /// This corresponds to [`SSL_CIPHER_get_version`]. - /// - /// [`SSL_CIPHER_get_version`]: https://www.openssl.org/docs/manmaster/man3/SSL_CIPHER_get_name.html + #[corresponds(SSL_CIPHER_get_version)] pub fn version(&self) -> &'static str { let version = unsafe { let ptr = ffi::SSL_CIPHER_get_version(self.as_ptr()); @@ -2451,10 +2329,7 @@ impl SslCipherRef { } /// Returns the number of bits used for the cipher. - /// - /// This corresponds to [`SSL_CIPHER_get_bits`]. - /// - /// [`SSL_CIPHER_get_bits`]: https://www.openssl.org/docs/manmaster/man3/SSL_CIPHER_get_name.html + #[corresponds(SSL_CIPHER_get_bits)] #[allow(clippy::useless_conversion)] pub fn bits(&self) -> CipherBits { unsafe { @@ -2468,10 +2343,7 @@ impl SslCipherRef { } /// Returns a textual description of the cipher. - /// - /// This corresponds to [`SSL_CIPHER_description`]. - /// - /// [`SSL_CIPHER_description`]: https://www.openssl.org/docs/manmaster/man3/SSL_CIPHER_get_name.html + #[corresponds(SSL_CIPHER_description)] pub fn description(&self) -> String { unsafe { // SSL_CIPHER_description requires a buffer of at least 128 bytes. @@ -2482,19 +2354,13 @@ impl SslCipherRef { } /// Returns one if the cipher uses an AEAD cipher. - /// - /// This corresponds to [`SSL_CIPHER_is_aead`]. - /// - /// [`SSL_CIPHER_is_aead`]: https://www.openssl.org/docs/manmaster/man3/SSL_CIPHER_is_aead.html + #[corresponds(SSL_CIPHER_is_aead)] pub fn cipher_is_aead(&self) -> bool { unsafe { ffi::SSL_CIPHER_is_aead(self.as_ptr()) != 0 } } /// Returns the NID corresponding to the cipher's authentication type. - /// - /// This corresponds to [`SSL_CIPHER_get_auth_nid`]. - /// - /// [`SSL_CIPHER_get_auth_nid`]: https://www.openssl.org/docs/manmaster/man3/SSL_CIPHER_get_auth_nid.html + #[corresponds(SSL_CIPHER_get_auth_nid)] pub fn cipher_auth_nid(&self) -> Option { let n = unsafe { ffi::SSL_CIPHER_get_auth_nid(self.as_ptr()) }; if n == 0 { @@ -2505,10 +2371,7 @@ impl SslCipherRef { } /// Returns the NID corresponding to the cipher. - /// - /// This corresponds to [`SSL_CIPHER_get_cipher_nid`]. - /// - /// [`SSL_CIPHER_get_cipher_nid`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CIPHER_get_cipher_nid.html + #[corresponds(SSL_CIPHER_get_cipher_nid)] pub fn cipher_nid(&self) -> Option { let n = unsafe { ffi::SSL_CIPHER_get_cipher_nid(self.as_ptr()) }; if n == 0 { @@ -2562,10 +2425,7 @@ impl ToOwned for SslSessionRef { impl SslSessionRef { /// Returns the SSL session ID. - /// - /// This corresponds to [`SSL_SESSION_get_id`]. - /// - /// [`SSL_SESSION_get_id`]: https://www.openssl.org/docs/manmaster/man3/SSL_SESSION_get_id.html + #[corresponds(SSL_SESSION_get_id)] pub fn id(&self) -> &[u8] { unsafe { let mut len = 0; @@ -2575,10 +2435,7 @@ impl SslSessionRef { } /// Returns the length of the master key. - /// - /// This corresponds to [`SSL_SESSION_get_master_key`]. - /// - /// [`SSL_SESSION_get_master_key`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_SESSION_get_master_key.html + #[corresponds(SSL_SESSION_get_master_key)] pub fn master_key_len(&self) -> usize { unsafe { SSL_SESSION_get_master_key(self.as_ptr(), ptr::null_mut(), 0) } } @@ -2586,19 +2443,13 @@ impl SslSessionRef { /// Copies the master key into the provided buffer. /// /// Returns the number of bytes written, or the size of the master key if the buffer is empty. - /// - /// This corresponds to [`SSL_SESSION_get_master_key`]. - /// - /// [`SSL_SESSION_get_master_key`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_SESSION_get_master_key.html + #[corresponds(SSL_SESSION_get_master_key)] pub fn master_key(&self, buf: &mut [u8]) -> usize { unsafe { SSL_SESSION_get_master_key(self.as_ptr(), buf.as_mut_ptr(), buf.len()) } } /// Returns the time at which the session was established, in seconds since the Unix epoch. - /// - /// This corresponds to [`SSL_SESSION_get_time`]. - /// - /// [`SSL_SESSION_get_time`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_SESSION_get_time.html + #[corresponds(SSL_SESSION_get_time)] #[allow(clippy::useless_conversion)] pub fn time(&self) -> u64 { unsafe { ffi::SSL_SESSION_get_time(self.as_ptr()) } @@ -2607,20 +2458,14 @@ impl SslSessionRef { /// Returns the sessions timeout, in seconds. /// /// A session older than this time should not be used for session resumption. - /// - /// This corresponds to [`SSL_SESSION_get_timeout`]. - /// - /// [`SSL_SESSION_get_timeout`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_SESSION_get_time.html + #[corresponds(SSL_SESSION_get_timeout)] #[allow(clippy::useless_conversion)] pub fn timeout(&self) -> u32 { unsafe { ffi::SSL_SESSION_get_timeout(self.as_ptr()) } } /// Returns the session's TLS protocol version. - /// - /// This corresponds to [`SSL_SESSION_get_protocol_version`]. - /// - /// [`SSL_SESSION_get_protocol_version`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_SESSION_get_protocol_version.html + #[corresponds(SSL_SESSION_get_protocol_version)] pub fn protocol_version(&self) -> SslVersion { unsafe { let version = ffi::SSL_SESSION_get_protocol_version(self.as_ptr()); @@ -2663,10 +2508,7 @@ impl Ssl { /// /// Each invocation of this function is guaranteed to return a distinct index. These can be used /// to store data in the context that can be retrieved later by callbacks, for example. - /// - /// This corresponds to [`SSL_get_ex_new_index`]. - /// - /// [`SSL_get_ex_new_index`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_get_ex_new_index.html + #[corresponds(SSL_get_ex_new_index)] pub fn new_ex_index() -> Result, ErrorStack> where T: 'static + Sync + Send, @@ -2695,10 +2537,8 @@ impl Ssl { /// Creates a new `Ssl`. /// - /// This corresponds to [`SSL_new`]. - /// - /// [`SSL_new`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_new.html // FIXME should take &SslContextRef + #[corresponds(SSL_new)] pub fn new(ctx: &SslContext) -> Result { unsafe { let ptr = cvt_p(ffi::SSL_new(ctx.as_ptr()))?; @@ -2711,10 +2551,9 @@ impl Ssl { /// Creates a new [`Ssl`]. /// - /// This corresponds to [`SSL_new`](`ffi::SSL_new`). - /// /// This function does the same as [`Self:new`] except that it takes &[SslContextRef]. // Both functions exist for backward compatibility (no breaking API). + #[corresponds(SSL_new)] pub fn new_from_ref(ctx: &SslContextRef) -> Result { unsafe { let ptr = cvt_p(ffi::SSL_new(ctx.as_ptr()))?; @@ -2836,11 +2675,11 @@ impl SslRef { unsafe { ffi::SSL_get_rbio(self.as_ptr()) } } - #[cfg(feature = "kx-safe-default")] - fn set_curves_list(&mut self, curves: &str) -> Result<(), ErrorStack> { + #[corresponds(SSL_set1_curves_list)] + pub fn set_curves_list(&mut self, curves: &str) -> Result<(), ErrorStack> { let curves = CString::new(curves).unwrap(); unsafe { - cvt(ffi::SSL_set1_curves_list( + cvt_0i(ffi::SSL_set1_curves_list( self.as_ptr(), curves.as_ptr() as *const _, )) @@ -2881,10 +2720,7 @@ impl SslRef { } /// Returns the [`SslCurve`] used for this `SslRef`. - /// - /// This corresponds to [`SSL_get_curve_id`] - /// - /// [`SSL_get_curve_id`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_get_curve_id + #[corresponds(SSL_get_curve_id)] pub fn curve(&self) -> Option { let curve_id = unsafe { ffi::SSL_get_curve_id(self.as_ptr()) }; if curve_id == 0 { @@ -2894,20 +2730,15 @@ impl SslRef { } /// Returns an `ErrorCode` value for the most recent operation on this `SslRef`. - /// - /// This corresponds to [`SSL_get_error`]. - /// - /// [`SSL_get_error`]: https://github.com/google/boringssl/blob/master/include/openssl/ssl.h#L475 + #[corresponds(SSL_get_error)] pub fn error_code(&self, ret: c_int) -> ErrorCode { unsafe { ErrorCode::from_raw(ffi::SSL_get_error(self.as_ptr(), ret)) } } /// Like [`SslContextBuilder::set_verify`]. /// - /// This corresponds to [`SSL_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 + #[corresponds(SSL_set_verify)] pub fn set_verify(&mut self, mode: SslVerifyMode) { #[cfg(feature = "rpk")] assert!( @@ -2918,11 +2749,24 @@ impl SslRef { unsafe { ffi::SSL_set_verify(self.as_ptr(), mode.bits() as c_int, None) } } + /// Sets the certificate verification depth. + /// + /// If the peer's certificate chain is longer than this value, verification will fail. + #[corresponds(SSL_set_verify_depth)] + pub fn set_verify_depth(&mut self, depth: u32) { + #[cfg(feature = "rpk")] + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); + + unsafe { + ffi::SSL_set_verify_depth(self.as_ptr(), depth as c_int); + } + } + /// Returns the verify mode that was set using `set_verify`. - /// - /// This corresponds to [`SSL_get_verify_mode`]. - /// - /// [`SSL_get_verify_mode`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_get_verify_mode.html + #[corresponds(SSL_get_verify_mode)] pub fn verify_mode(&self) -> SslVerifyMode { #[cfg(feature = "rpk")] assert!( @@ -2947,13 +2791,10 @@ impl SslRef { /// call [`X509StoreContextRef::verify_cert`] and inspect the result, or perform /// other operations more straightforwardly. /// - /// This corresponds to [`SSL_set_verify`]. - /// /// # Panics /// /// This method panics if this `Ssl` is associated with a RPK context. - /// - /// [`SSL_set_verify`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_verify.html + #[corresponds(SSL_set_verify)] pub fn set_verify_callback(&mut self, mode: SslVerifyMode, callback: F) where F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send, @@ -2975,13 +2816,27 @@ impl SslRef { } } + /// Sets a custom certificate store for verifying peer certificates. + #[corresponds(SSL_set0_verify_cert_store)] + pub fn set_verify_cert_store(&mut self, cert_store: X509Store) -> Result<(), ErrorStack> { + #[cfg(feature = "rpk")] + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); + + unsafe { + cvt(ffi::SSL_set0_verify_cert_store(self.as_ptr(), cert_store.into_ptr()) as c_int)?; + Ok(()) + } + } + /// Like [`SslContextBuilder::set_custom_verify_callback`]. /// - /// This corresponds to [`SSL_set_custom_verify`]. - /// /// # Panics /// /// This method panics if this `Ssl` is associated with a RPK context. + #[corresponds(SSL_set_custom_verify)] pub fn set_custom_verify_callback(&mut self, mode: SslVerifyMode, callback: F) where F: Fn(&mut SslRef) -> Result<(), SslVerifyError> + 'static + Sync + Send, @@ -3005,19 +2860,16 @@ impl SslRef { /// Like [`SslContextBuilder::set_tmp_dh`]. /// - /// This corresponds to [`SSL_set_tmp_dh`]. - /// /// [`SslContextBuilder::set_tmp_dh`]: struct.SslContextBuilder.html#method.set_tmp_dh - /// [`SSL_set_tmp_dh`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_tmp_dh.html + #[corresponds(SSL_set_tmp_dh)] pub fn set_tmp_dh(&mut self, dh: &DhRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_set_tmp_dh(self.as_ptr(), dh.as_ptr()) as c_int).map(|_| ()) } } /// Like [`SslContextBuilder::set_tmp_ecdh`]. /// - /// This corresponds to `SSL_set_tmp_ecdh`. - /// /// [`SslContextBuilder::set_tmp_ecdh`]: struct.SslContextBuilder.html#method.set_tmp_ecdh + #[corresponds(SSL_set_tmp_ecdh)] pub fn set_tmp_ecdh(&mut self, key: &EcKeyRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_set_tmp_ecdh(self.as_ptr(), key.as_ptr()) as c_int).map(|_| ()) } } @@ -3038,10 +2890,8 @@ impl SslRef { /// Like [`SslContextBuilder::set_alpn_protos`]. /// - /// This corresponds to [`SSL_set_alpn_protos`]. - /// /// [`SslContextBuilder::set_alpn_protos`]: struct.SslContextBuilder.html#method.set_alpn_protos - /// [`SSL_set_alpn_protos`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_alpn_protos.html + #[corresponds(SSL_set_alpn_protos)] pub fn set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> { unsafe { #[cfg_attr(not(feature = "fips"), allow(clippy::unnecessary_cast))] @@ -3063,10 +2913,7 @@ impl SslRef { } /// Returns the stack of available SslCiphers for `SSL`, sorted by preference. - /// - /// This corresponds to [`SSL_get_ciphers`]. - /// - /// [`SSL_get_ciphers`]: https://www.openssl.org/docs/man1.0.2/man3/SSL_get_ciphers.html + #[corresponds(SSL_get_ciphers)] pub fn ciphers(&self) -> &StackRef { unsafe { let cipher_list = ffi::SSL_get_ciphers(self.as_ptr()); @@ -3075,10 +2922,7 @@ impl SslRef { } /// Returns the current cipher if the session is active. - /// - /// This corresponds to [`SSL_get_current_cipher`]. - /// - /// [`SSL_get_current_cipher`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_current_cipher.html + #[corresponds(SSL_get_current_cipher)] pub fn current_cipher(&self) -> Option<&SslCipherRef> { unsafe { let ptr = ffi::SSL_get_current_cipher(self.as_ptr()); @@ -3092,10 +2936,7 @@ impl SslRef { } /// Returns a short string describing the state of the session. - /// - /// This corresponds to [`SSL_state_string`]. - /// - /// [`SSL_state_string`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_state_string.html + #[corresponds(SSL_state_string)] pub fn state_string(&self) -> &'static str { let state = unsafe { let ptr = ffi::SSL_state_string(self.as_ptr()); @@ -3106,10 +2947,7 @@ impl SslRef { } /// Returns a longer string describing the state of the session. - /// - /// This corresponds to [`SSL_state_string_long`]. - /// - /// [`SSL_state_string_long`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_state_string_long.html + #[corresponds(SSL_state_string_long)] pub fn state_string_long(&self) -> &'static str { let state = unsafe { let ptr = ffi::SSL_state_string_long(self.as_ptr()); @@ -3122,10 +2960,7 @@ impl SslRef { /// Sets the host name to be sent to the server for Server Name Indication (SNI). /// /// It has no effect for a server-side connection. - /// - /// This corresponds to [`SSL_set_tlsext_host_name`]. - /// - /// [`SSL_set_tlsext_host_name`]: https://www.openssl.org/docs/manmaster/man3/SSL_get_servername_type.html + #[corresponds(SSL_set_tlsext_host_name)] pub fn set_hostname(&mut self, hostname: &str) -> Result<(), ErrorStack> { let cstr = CString::new(hostname).unwrap(); unsafe { @@ -3135,10 +2970,7 @@ impl SslRef { } /// Returns the peer's certificate, if present. - /// - /// This corresponds to [`SSL_get_peer_certificate`]. - /// - /// [`SSL_get_peer_certificate`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_peer_certificate.html + #[corresponds(SSL_get_peer_certificate)] pub fn peer_certificate(&self) -> Option { #[cfg(feature = "rpk")] assert!( @@ -3160,10 +2992,7 @@ impl SslRef { /// /// On the client side, the chain includes the leaf certificate, but on the server side it does /// not. Fun! - /// - /// This corresponds to [`SSL_get_peer_cert_chain`]. - /// - /// [`SSL_get_peer_cert_chain`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_peer_cert_chain.html + #[corresponds(SSL_get_peer_certificate)] pub fn peer_cert_chain(&self) -> Option<&StackRef> { #[cfg(feature = "rpk")] assert!( @@ -3182,10 +3011,7 @@ impl SslRef { } /// Like [`SslContext::certificate`]. - /// - /// This corresponds to `SSL_get_certificate`. - /// - /// [`SslContext::certificate`]: struct.SslContext.html#method.certificate + #[corresponds(SSL_get_certificate)] pub fn certificate(&self) -> Option<&X509Ref> { #[cfg(feature = "rpk")] assert!( @@ -3204,10 +3030,7 @@ impl SslRef { } /// Like [`SslContext::private_key`]. - /// - /// This corresponds to `SSL_get_privatekey`. - /// - /// [`SslContext::private_key`]: struct.SslContext.html#method.private_key + #[corresponds(SSL_get_privatekey)] pub fn private_key(&self) -> Option<&PKeyRef> { unsafe { let ptr = ffi::SSL_get_privatekey(self.as_ptr()); @@ -3225,10 +3048,7 @@ impl SslRef { } /// Returns the protocol version of the session. - /// - /// This corresponds to [`SSL_version`]. - /// - /// [`SSL_version`]: https://www.openssl.org/docs/manmaster/man3/SSL_version.html + #[corresponds(SSL_version)] pub fn version2(&self) -> Option { unsafe { let r = ffi::SSL_version(self.as_ptr()); @@ -3241,10 +3061,7 @@ impl SslRef { } /// Returns a string describing the protocol version of the session. - /// - /// This corresponds to [`SSL_get_version`]. - /// - /// [`SSL_get_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_version.html + #[corresponds(SSL_get_version)] pub fn version_str(&self) -> &'static str { let version = unsafe { let ptr = ffi::SSL_get_version(self.as_ptr()); @@ -3258,10 +3075,7 @@ impl SslRef { /// /// If version is `None`, the default minimum version is used. For BoringSSL this defaults to /// TLS 1.0. - /// - /// This corresponds to [`SSL_set_min_proto_version`]. - /// - /// [`SSL_set_min_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html + #[corresponds(SSL_set_min_proto_version)] pub fn set_min_proto_version(&mut self, version: Option) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_set_min_proto_version( @@ -3275,10 +3089,7 @@ impl SslRef { /// Sets the maximum supported protocol version. /// /// If version is `None`, the default maximum version is used. For BoringSSL this is TLS 1.3. - /// - /// This corresponds to [`SSL_set_max_proto_version`]. - /// - /// [`SSL_set_max_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_max_proto_version.html + #[corresponds(SSL_set_max_proto_version)] pub fn set_max_proto_version(&mut self, version: Option) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_set_max_proto_version( @@ -3290,10 +3101,7 @@ impl SslRef { } /// Gets the minimum supported protocol version. - /// - /// This corresponds to [`SSL_get_min_proto_version`]. - /// - /// [`SSL_get_min_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html + #[corresponds(SSL_get_min_proto_version)] pub fn min_proto_version(&mut self) -> Option { unsafe { let r = ffi::SSL_get_min_proto_version(self.as_ptr()); @@ -3306,10 +3114,7 @@ impl SslRef { } /// Gets the maximum supported protocol version. - /// - /// This corresponds to [`SSL_get_max_proto_version`]. - /// - /// [`SSL_get_max_proto_version`]: https://www.openssl.org/docs/man3.1/man3/SSL_get_max_proto_version.html + #[corresponds(SSL_get_max_proto_version)] pub fn max_proto_version(&self) -> Option { let r = unsafe { ffi::SSL_get_max_proto_version(self.as_ptr()) }; if r == 0 { @@ -3323,10 +3128,7 @@ impl SslRef { /// /// The protocol's name is returned is an opaque sequence of bytes. It is up to the client /// to interpret it. - /// - /// This corresponds to [`SSL_get0_alpn_selected`]. - /// - /// [`SSL_get0_alpn_selected`]: https://www.openssl.org/docs/manmaster/man3/SSL_get0_next_proto_negotiated.html + #[corresponds(SSL_get0_alpn_selected)] pub fn selected_alpn_protocol(&self) -> Option<&[u8]> { unsafe { let mut data: *const c_uchar = ptr::null(); @@ -3344,10 +3146,7 @@ impl SslRef { } /// Enables the DTLS extension "use_srtp" as defined in RFC5764. - /// - /// This corresponds to [`SSL_set_tlsext_use_srtp`]. - /// - /// [`SSL_set_tlsext_use_srtp`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_tlsext_use_srtp.html + #[corresponds(SSL_set_tlsext_use_srtp)] pub fn set_tlsext_use_srtp(&mut self, protocols: &str) -> Result<(), ErrorStack> { unsafe { let cstr = CString::new(protocols).unwrap(); @@ -3365,10 +3164,7 @@ impl SslRef { /// Gets all SRTP profiles that are enabled for handshake via set_tlsext_use_srtp /// /// DTLS extension "use_srtp" as defined in RFC5764 has to be enabled. - /// - /// This corresponds to [`SSL_get_srtp_profiles`]. - /// - /// [`SSL_get_srtp_profiles`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_tlsext_use_srtp.html + #[corresponds(SSL_get_strp_profiles)] pub fn srtp_profiles(&self) -> Option<&StackRef> { unsafe { let chain = ffi::SSL_get_srtp_profiles(self.as_ptr()); @@ -3384,10 +3180,7 @@ impl SslRef { /// Gets the SRTP profile selected by handshake. /// /// DTLS extension "use_srtp" as defined in RFC5764 has to be enabled. - /// - /// This corresponds to [`SSL_get_selected_srtp_profile`]. - /// - /// [`SSL_get_selected_srtp_profile`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_tlsext_use_srtp.html + #[corresponds(SSL_get_selected_srtp_profile)] pub fn selected_srtp_profile(&self) -> Option<&SrtpProtectionProfileRef> { unsafe { let profile = ffi::SSL_get_selected_srtp_profile(self.as_ptr()); @@ -3404,10 +3197,7 @@ impl SslRef { /// /// If this is greater than 0, the next call to `read` will not call down to the underlying /// stream. - /// - /// This corresponds to [`SSL_pending`]. - /// - /// [`SSL_pending`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_pending.html + #[corresponds(SSL_pending)] pub fn pending(&self) -> usize { unsafe { ffi::SSL_pending(self.as_ptr()) as usize } } @@ -3416,8 +3206,6 @@ impl SslRef { /// /// It is only useful on the server side. /// - /// This corresponds to [`SSL_get_servername`]. - /// /// # Note /// /// While the SNI specification requires that servernames be valid domain names (and therefore @@ -3425,8 +3213,8 @@ impl SslRef { /// is not valid UTF-8, this function will return `None`. The `servername_raw` method returns /// the raw bytes and does not have this restriction. /// - /// [`SSL_get_servername`]: https://www.openssl.org/docs/manmaster/man3/SSL_get_servername.html // FIXME maybe rethink in 0.11? + #[corresponds(SSL_get_servername)] pub fn servername(&self, type_: NameType) -> Option<&str> { self.servername_raw(type_) .and_then(|b| str::from_utf8(b).ok()) @@ -3436,13 +3224,10 @@ impl SslRef { /// /// It is only useful on the server side. /// - /// This corresponds to [`SSL_get_servername`]. - /// /// # Note /// /// Unlike `servername`, this method does not require the name be valid UTF-8. - /// - /// [`SSL_get_servername`]: https://www.openssl.org/docs/manmaster/man3/SSL_get_servername.html + #[corresponds(SSL_get_servername)] pub fn servername_raw(&self, type_: NameType) -> Option<&[u8]> { unsafe { let name = ffi::SSL_get_servername(self.as_ptr(), type_.0); @@ -3457,17 +3242,13 @@ impl SslRef { /// Changes the context corresponding to the current connection. /// /// It is most commonly used in the Server Name Indication (SNI) callback. - /// - /// This corresponds to `SSL_set_SSL_CTX`. + #[corresponds(SSL_set_SSL_CTX)] pub fn set_ssl_context(&mut self, ctx: &SslContextRef) -> Result<(), ErrorStack> { unsafe { cvt_p(ffi::SSL_set_SSL_CTX(self.as_ptr(), ctx.as_ptr())).map(|_| ()) } } /// Returns the context corresponding to the current connection. - /// - /// This corresponds to [`SSL_get_SSL_CTX`]. - /// - /// [`SSL_get_SSL_CTX`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_get_SSL_CTX.html + #[corresponds(SSL_get_SSL_CTX)] pub fn ssl_context(&self) -> &SslContextRef { unsafe { let ssl_ctx = ffi::SSL_get_SSL_CTX(self.as_ptr()); @@ -3476,10 +3257,7 @@ impl SslRef { } /// Returns a mutable reference to the X509 verification configuration. - /// - /// This corresponds to [`SSL_get0_param`]. - /// - /// [`SSL_get0_param`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_get0_param.html + #[corresponds(SSL_get0_param)] pub fn verify_param_mut(&mut self) -> &mut X509VerifyParamRef { #[cfg(feature = "rpk")] assert!( @@ -3496,10 +3274,7 @@ impl SslRef { } /// Returns the certificate verification result. - /// - /// This corresponds to [`SSL_get_verify_result`]. - /// - /// [`SSL_get_verify_result`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_get_verify_result.html + #[corresponds(SSL_get_verify_result)] pub fn verify_result(&self) -> X509VerifyResult { #[cfg(feature = "rpk")] assert!( @@ -3511,10 +3286,7 @@ impl SslRef { } /// Returns a shared reference to the SSL session. - /// - /// This corresponds to [`SSL_get_session`]. - /// - /// [`SSL_get_session`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_session.html + #[corresponds(SSL_get_session)] pub fn session(&self) -> Option<&SslSessionRef> { unsafe { let p = ffi::SSL_get_session(self.as_ptr()); @@ -3530,10 +3302,7 @@ impl SslRef { /// /// Returns the number of bytes copied, or if the buffer is empty, the size of the client_random /// value. - /// - /// This corresponds to [`SSL_get_client_random`]. - /// - /// [`SSL_get_client_random`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_client_random.html + #[corresponds(SSL_get_client_random)] pub fn client_random(&self, buf: &mut [u8]) -> usize { unsafe { ffi::SSL_get_client_random(self.as_ptr(), buf.as_mut_ptr() as *mut c_uchar, buf.len()) @@ -3544,10 +3313,7 @@ impl SslRef { /// /// Returns the number of bytes copied, or if the buffer is empty, the size of the server_random /// value. - /// - /// This corresponds to [`SSL_get_server_random`]. - /// - /// [`SSL_get_server_random`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_client_random.html + #[corresponds(SSL_get_server_random)] pub fn server_random(&self, buf: &mut [u8]) -> usize { unsafe { ffi::SSL_get_server_random(self.as_ptr(), buf.as_mut_ptr() as *mut c_uchar, buf.len()) @@ -3555,10 +3321,7 @@ impl SslRef { } /// Derives keying material for application use in accordance to RFC 5705. - /// - /// This corresponds to [`SSL_export_keying_material`]. - /// - /// [`SSL_export_keying_material`]: https://www.openssl.org/docs/manmaster/man3/SSL_export_keying_material.html + #[corresponds(SSL_export_keying_material)] pub fn export_keying_material( &self, out: &mut [u8], @@ -3590,32 +3353,23 @@ impl SslRef { /// session. If the server is not willing to reuse the session, a new one will be transparently /// negotiated. /// - /// This corresponds to [`SSL_set_session`]. - /// /// # Safety /// /// The caller of this method is responsible for ensuring that the session is associated /// with the same `SslContext` as this `Ssl`. - /// - /// [`SSL_set_session`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_session.html + #[corresponds(SSL_set_session)] pub unsafe fn set_session(&mut self, session: &SslSessionRef) -> Result<(), ErrorStack> { cvt(ffi::SSL_set_session(self.as_ptr(), session.as_ptr())).map(|_| ()) } /// Determines if the session provided to `set_session` was successfully reused. - /// - /// This corresponds to [`SSL_session_reused`]. - /// - /// [`SSL_session_reused`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_session_reused.html + #[corresponds(SSL_session_reused)] pub fn session_reused(&self) -> bool { unsafe { ffi::SSL_session_reused(self.as_ptr()) != 0 } } /// Sets the status response a client wishes the server to reply with. - /// - /// This corresponds to [`SSL_set_tlsext_status_type`]. - /// - /// [`SSL_set_tlsext_status_type`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_tlsext_status_type.html + #[corresponds(SSL_set_tlsext_status_type)] pub fn set_status_type(&mut self, type_: StatusType) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_set_tlsext_status_type(self.as_ptr(), type_.as_raw()) as c_int).map(|_| ()) @@ -3623,10 +3377,7 @@ impl SslRef { } /// Returns the server's OCSP response, if present. - /// - /// This corresponds to [`SSL_get_tlsext_status_ocsp_resp`]. - /// - /// [`SSL_get_tlsext_status_ocsp_resp`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_tlsext_status_type.html + #[corresponds(SSL_get_tlsext_status_ocsp_resp)] pub fn ocsp_status(&self) -> Option<&[u8]> { unsafe { let mut p = ptr::null(); @@ -3641,10 +3392,7 @@ impl SslRef { } /// Sets the OCSP response to be returned to the client. - /// - /// This corresponds to [`SSL_set_tlsext_status_ocsp_resp`]. - /// - /// [`SSL_set_tlsext_status_ocsp_resp`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_tlsext_status_type.html + #[corresponds(SSL_set_tlsext_status_ocsp_resp)] pub fn set_ocsp_status(&mut self, response: &[u8]) -> Result<(), ErrorStack> { unsafe { assert!(response.len() <= c_int::MAX as usize); @@ -3660,10 +3408,7 @@ impl SslRef { } /// Determines if this `Ssl` is configured for server-side or client-side use. - /// - /// This corresponds to [`SSL_is_server`]. - /// - /// [`SSL_is_server`]: https://www.openssl.org/docs/manmaster/man3/SSL_is_server.html + #[corresponds(SSL_is_server)] pub fn is_server(&self) -> bool { unsafe { SSL_is_server(self.as_ptr()) != 0 } } @@ -3673,12 +3418,9 @@ impl SslRef { /// This can be used to provide data to callbacks registered with the context. Use the /// `Ssl::new_ex_index` method to create an `Index`. /// - /// This corresponds to [`SSL_set_ex_data`]. - /// /// Note that if this method is called multiple times with the same index, any previous /// value stored in the `SslContextBuilder` will be leaked. - /// - /// [`SSL_set_ex_data`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_ex_data.html + #[corresponds(SSL_set_ex_data)] pub fn set_ex_data(&mut self, index: Index, data: T) { if let Some(old) = self.ex_data_mut(index) { *old = data; @@ -3701,11 +3443,8 @@ impl SslRef { /// This can be used to provide data to callbacks registered with the context. Use the /// `Ssl::new_ex_index` method to create an `Index`. /// - /// This corresponds to [`SSL_set_ex_data`]. - /// /// Any previous value will be dropped and replaced by the new one. - /// - /// [`SSL_set_ex_data`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_ex_data.html + #[corresponds(SSL_set_ex_data)] pub fn replace_ex_data(&mut self, index: Index, data: T) -> Option { if let Some(old) = self.ex_data_mut(index) { return Some(mem::replace(old, data)); @@ -3717,10 +3456,7 @@ impl SslRef { } /// Returns a reference to the extra data at the specified index. - /// - /// This corresponds to [`SSL_get_ex_data`]. - /// - /// [`SSL_get_ex_data`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_ex_data.html + #[corresponds(SSL_get_ex_data)] pub fn ex_data(&self, index: Index) -> Option<&T> { unsafe { let data = ffi::SSL_get_ex_data(self.as_ptr(), index.as_raw()); @@ -3733,10 +3469,7 @@ impl SslRef { } /// Returns a mutable reference to the extra data at the specified index. - /// - /// This corresponds to [`SSL_get_ex_data`]. - /// - /// [`SSL_get_ex_data`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_ex_data.html + #[corresponds(SSL_get_ex_data)] pub fn ex_data_mut(&mut self, index: Index) -> Option<&mut T> { unsafe { let data = ffi::SSL_get_ex_data(self.as_ptr(), index.as_raw()); @@ -3752,8 +3485,7 @@ impl SslRef { /// /// The total size of the message is returned, so this can be used to determine the size of the /// buffer required. - /// - /// This corresponds to `SSL_get_finished`. + #[corresponds(SSL_get_finished)] pub fn finished(&self, buf: &mut [u8]) -> usize { unsafe { ffi::SSL_get_finished(self.as_ptr(), buf.as_mut_ptr() as *mut c_void, buf.len()) } } @@ -3763,8 +3495,7 @@ impl SslRef { /// /// The total size of the message is returned, so this can be used to determine the size of the /// buffer required. - /// - /// This corresponds to `SSL_get_peer_finished`. + #[corresponds(SSL_get_peer_finished)] pub fn peer_finished(&self, buf: &mut [u8]) -> usize { unsafe { ffi::SSL_get_peer_finished(self.as_ptr(), buf.as_mut_ptr() as *mut c_void, buf.len()) @@ -3772,26 +3503,19 @@ impl SslRef { } /// Determines if the initial handshake has been completed. - /// - /// This corresponds to [`SSL_is_init_finished`]. - /// - /// [`SSL_is_init_finished`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_is_init_finished.html + #[corresponds(SSL_is_init_finished)] pub fn is_init_finished(&self) -> bool { unsafe { ffi::SSL_is_init_finished(self.as_ptr()) != 0 } } /// Sets the MTU used for DTLS connections. - /// - /// This corresponds to `SSL_set_mtu`. + #[corresponds(SSL_set_mtu)] pub fn set_mtu(&mut self, mtu: u32) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_set_mtu(self.as_ptr(), mtu as c_uint) as c_int).map(|_| ()) } } /// Sets the certificate. - /// - /// This corresponds to [`SSL_use_certificate`]. - /// - /// [`SSL_use_certificate`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_use_certificate.html + #[corresponds(SSL_use_certificate)] pub fn set_certificate(&mut self, cert: &X509Ref) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_use_certificate(self.as_ptr(), cert.as_ptr()))?; @@ -3800,11 +3524,24 @@ impl SslRef { Ok(()) } + /// Sets the list of CA names sent to the client. + /// + /// The CA certificates must still be added to the trust root - they are not automatically set + /// as trusted by this method. + #[corresponds(SSL_set_client_CA_list)] + pub fn set_client_ca_list(&mut self, list: Stack) { + #[cfg(feature = "rpk")] + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); + + unsafe { ffi::SSL_set_client_CA_list(self.as_ptr(), list.as_ptr()) } + mem::forget(list); + } + /// Sets the private key. - /// - /// This corresponds to [`SSL_use_PrivateKey`]. - /// - /// [`SSL_use_PrivateKey`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_use_PrivateKey.html + #[corresponds(SSL_use_PrivateKey)] pub fn set_private_key(&mut self, key: &PKeyRef) -> Result<(), ErrorStack> where T: HasPrivate, @@ -3814,10 +3551,7 @@ impl SslRef { /// Enables all modes set in `mode` in `SSL`. Returns a bitmask representing the resulting /// enabled modes. - /// - /// This corresponds to [`SSL_set_mode`]. - /// - /// [`SSL_set_mode`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_mode.html + #[corresponds(SSL_set_mode)] pub fn set_mode(&mut self, mode: SslMode) -> SslMode { let bits = unsafe { ffi::SSL_set_mode(self.as_ptr(), mode.bits()) }; SslMode::from_bits_retain(bits) @@ -3825,20 +3559,14 @@ impl SslRef { /// Disables all modes set in `mode` in `SSL`. Returns a bitmask representing the resulting /// enabled modes. - /// - /// This corresponds to [`SSL_clear_mode`]. - /// - /// [`SSL_clear_mode`]: https://www.openssl.org/docs/man3.1/man3/SSL_clear_mode.html + #[corresponds(SSL_clear_mode)] pub fn clear_mode(&mut self, mode: SslMode) -> SslMode { let bits = unsafe { ffi::SSL_clear_mode(self.as_ptr(), mode.bits()) }; SslMode::from_bits_retain(bits) } /// Appends `cert` to the chain associated with the current certificate of `SSL`. - /// - /// This corresponds to [`SSL_add1_chain_cert`]. - /// - /// [`SSL_add1_chain_cert`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_add1_chain_cert.html + #[corresponds(SSL_add1_chain_cert)] pub fn add_chain_cert(&mut self, cert: &X509Ref) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_add1_chain_cert(self.as_ptr(), cert.as_ptr())).map(|_| ()) } } @@ -4003,10 +3731,7 @@ impl SslStream { /// /// It is particularly useful with a nonblocking socket, where the error value will identify if /// OpenSSL is waiting on read or write readiness. - /// - /// This corresponds to [`SSL_read`]. - /// - /// [`SSL_read`]: https://www.openssl.org/docs/manmaster/man3/SSL_read.html + #[corresponds(SSL_read)] pub fn ssl_read(&mut self, buf: &mut [u8]) -> Result { // SAFETY: `ssl_read_uninit` does not de-initialize the buffer. unsafe { @@ -4041,10 +3766,7 @@ impl SslStream { /// /// It is particularly useful with a nonblocking socket, where the error value will identify if /// OpenSSL is waiting on read or write readiness. - /// - /// This corresponds to [`SSL_write`]. - /// - /// [`SSL_write`]: https://www.openssl.org/docs/manmaster/man3/SSL_write.html + #[corresponds(SSL_write)] pub fn ssl_write(&mut self, buf: &[u8]) -> Result { if buf.is_empty() { return Ok(0); @@ -4068,10 +3790,7 @@ impl SslStream { /// While the connection may be closed after the first step, it is recommended to fully shut the /// session down. In particular, it must be fully shut down if the connection is to be used for /// further communication in the future. - /// - /// This corresponds to [`SSL_shutdown`]. - /// - /// [`SSL_shutdown`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_shutdown.html + #[corresponds(SSL_shutdown)] pub fn shutdown(&mut self) -> Result { match unsafe { ffi::SSL_shutdown(self.ssl.as_ptr()) } { 0 => Ok(ShutdownResult::Sent), @@ -4081,10 +3800,7 @@ impl SslStream { } /// Returns the session's shutdown state. - /// - /// This corresponds to [`SSL_get_shutdown`]. - /// - /// [`SSL_get_shutdown`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_set_shutdown.html + #[corresponds(SSL_get_shutdown)] pub fn get_shutdown(&mut self) -> ShutdownState { unsafe { let bits = ffi::SSL_get_shutdown(self.ssl.as_ptr()); @@ -4096,19 +3812,13 @@ impl SslStream { /// /// This can be used to tell OpenSSL that the session should be cached even if a full two-way /// shutdown was not completed. - /// - /// This corresponds to [`SSL_set_shutdown`]. - /// - /// [`SSL_set_shutdown`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_set_shutdown.html + #[corresponds(SSL_set_shutdown)] pub fn set_shutdown(&mut self, state: ShutdownState) { unsafe { ffi::SSL_set_shutdown(self.ssl.as_ptr(), state.bits()) } } /// Initiates a client-side TLS handshake. - /// - /// This corresponds to [`SSL_connect`]. - /// - /// [`SSL_connect`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_connect.html + #[corresponds(SSL_connect)] pub fn connect(&mut self) -> Result<(), Error> { let ret = unsafe { ffi::SSL_connect(self.ssl.as_ptr()) }; if ret > 0 { @@ -4119,10 +3829,7 @@ impl SslStream { } /// Initiates a server-side TLS handshake. - /// - /// This corresponds to [`SSL_accept`]. - /// - /// [`SSL_accept`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_accept.html + #[corresponds(SSL_accept)] pub fn accept(&mut self) -> Result<(), Error> { let ret = unsafe { ffi::SSL_accept(self.ssl.as_ptr()) }; if ret > 0 { @@ -4133,10 +3840,7 @@ impl SslStream { } /// Initiates the handshake. - /// - /// This corresponds to [`SSL_do_handshake`]. - /// - /// [`SSL_do_handshake`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_do_handshake.html + #[corresponds(SSL_do_handshake)] pub fn do_handshake(&mut self) -> Result<(), Error> { let ret = unsafe { ffi::SSL_do_handshake(self.ssl.as_ptr()) }; if ret > 0 { @@ -4269,19 +3973,13 @@ where } /// Configure as an outgoing stream from a client. - /// - /// This corresponds to [`SSL_set_connect_state`]. - /// - /// [`SSL_set_connect_state`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_connect_state.html + #[corresponds(SSL_set_connect_state)] pub fn set_connect_state(&mut self) { unsafe { ffi::SSL_set_connect_state(self.inner.ssl.as_ptr()) } } /// Configure as an incoming stream to a server. - /// - /// This corresponds to [`SSL_set_accept_state`]. - /// - /// [`SSL_set_accept_state`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_accept_state.html + #[corresponds(SSL_set_accept_state)] pub fn set_accept_state(&mut self) { unsafe { ffi::SSL_set_accept_state(self.inner.ssl.as_ptr()) } } @@ -4351,10 +4049,7 @@ where /// Initiates the handshake. /// /// This will fail if `set_accept_state` or `set_connect_state` was not called first. - /// - /// This corresponds to [`SSL_do_handshake`]. - /// - /// [`SSL_do_handshake`]: https://www.openssl.org/docs/manmaster/man3/SSL_do_handshake.html + #[corresponds(SSL_do_handshake)] pub fn handshake(self) -> Result, HandshakeError> { let mut stream = self.inner; let ret = unsafe { ffi::SSL_do_handshake(stream.ssl.as_ptr()) }; diff --git a/boring/src/ssl/test/mod.rs b/boring/src/ssl/test/mod.rs index 131b1127..f3b0fd29 100644 --- a/boring/src/ssl/test/mod.rs +++ b/boring/src/ssl/test/mod.rs @@ -1052,3 +1052,17 @@ fn drop_ex_data_in_ssl() { assert_eq!(ssl.replace_ex_data(index, "camembert"), Some("comté")); assert_eq!(ssl.replace_ex_data(index, "raclette"), Some("camembert")); } + +#[test] +fn test_info_callback() { + static CALLED_BACK: AtomicBool = AtomicBool::new(false); + + let server = Server::builder().build(); + let mut client = server.client(); + client.ctx().set_info_callback(move |_, _, _| { + CALLED_BACK.store(true, Ordering::Relaxed); + }); + + client.connect(); + assert!(CALLED_BACK.load(Ordering::Relaxed)); +} diff --git a/boring/src/util.rs b/boring/src/util.rs index 21591d2f..bb6373c1 100644 --- a/boring/src/util.rs +++ b/boring/src/util.rs @@ -1,10 +1,10 @@ +use crate::error::ErrorStack; +use foreign_types::{ForeignType, ForeignTypeRef}; use libc::{c_char, c_int, c_void}; use std::any::Any; use std::panic::{self, AssertUnwindSafe}; use std::slice; -use crate::error::ErrorStack; - /// Wraps a user-supplied callback and a slot for panics thrown inside the callback (while FFI /// frames are on the stack). /// @@ -65,3 +65,30 @@ where } } } + +#[allow(dead_code)] +pub trait ForeignTypeExt: ForeignType { + unsafe fn from_ptr_opt(ptr: *mut Self::CType) -> Option { + if ptr.is_null() { + None + } else { + Some(Self::from_ptr(ptr)) + } + } +} +impl ForeignTypeExt for FT {} + +pub trait ForeignTypeRefExt: ForeignTypeRef { + unsafe fn from_const_ptr<'a>(ptr: *const Self::CType) -> &'a Self { + Self::from_ptr(ptr as *mut Self::CType) + } + + unsafe fn from_const_ptr_opt<'a>(ptr: *const Self::CType) -> Option<&'a Self> { + if ptr.is_null() { + None + } else { + Some(Self::from_const_ptr(ptr as *mut Self::CType)) + } + } +} +impl ForeignTypeRefExt for FT {} diff --git a/boring/src/x509/mod.rs b/boring/src/x509/mod.rs index 30a4b2b8..34896d6a 100644 --- a/boring/src/x509/mod.rs +++ b/boring/src/x509/mod.rs @@ -9,6 +9,7 @@ use foreign_types::{ForeignType, ForeignTypeRef}; use libc::{c_int, c_long, c_void}; +use openssl_macros::corresponds; use std::convert::TryInto; use std::error::Error; use std::ffi::{CStr, CString}; @@ -25,7 +26,7 @@ use crate::asn1::{ Asn1BitStringRef, Asn1IntegerRef, Asn1Object, Asn1ObjectRef, Asn1StringRef, Asn1TimeRef, Asn1Type, }; -use crate::bio::MemBioSlice; +use crate::bio::{MemBio, MemBioSlice}; use crate::conf::ConfRef; use crate::error::ErrorStack; use crate::ex_data::Index; @@ -36,6 +37,7 @@ use crate::pkey::{HasPrivate, HasPublic, PKey, PKeyRef, Public}; use crate::ssl::SslRef; use crate::stack::{Stack, StackRef, Stackable}; use crate::string::OpensslString; +use crate::util::ForeignTypeRefExt; use crate::x509::verify::X509VerifyParamRef; use crate::{cvt, cvt_n, cvt_p}; @@ -57,15 +59,13 @@ foreign_type_and_impl_send_sync! { impl X509StoreContext { /// Returns the index which can be used to obtain a reference to the `Ssl` associated with a /// context. + #[corresponds(SSL_get_ex_data_X509_STORE_CTX_idx)] pub fn ssl_idx() -> Result, ErrorStack> { unsafe { cvt_n(ffi::SSL_get_ex_data_X509_STORE_CTX_idx()).map(|idx| Index::from_raw(idx)) } } /// Creates a new `X509StoreContext` instance. - /// - /// This corresponds to [`X509_STORE_CTX_new`]. - /// - /// [`X509_STORE_CTX_new`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_STORE_CTX_new.html + #[corresponds(X509_STORE_CTX_new)] pub fn new() -> Result { unsafe { ffi::init(); @@ -76,10 +76,7 @@ impl X509StoreContext { impl X509StoreContextRef { /// Returns application data pertaining to an `X509` store context. - /// - /// This corresponds to [`X509_STORE_CTX_get_ex_data`]. - /// - /// [`X509_STORE_CTX_get_ex_data`]: https://www.openssl.org/docs/man1.0.2/crypto/X509_STORE_CTX_get_ex_data.html + #[corresponds(X509_STORE_CTX_get_ex_data)] pub fn ex_data(&self, index: Index) -> Option<&T> { unsafe { let data = ffi::X509_STORE_CTX_get_ex_data(self.as_ptr(), index.as_raw()); @@ -92,10 +89,7 @@ impl X509StoreContextRef { } /// Returns the verify result of the context. - /// - /// This corresponds to [`X509_STORE_CTX_get_error`]. - /// - /// [`X509_STORE_CTX_get_error`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_STORE_CTX_get_error.html + #[corresponds(X509_STORE_CTX_get_error)] pub fn verify_result(&self) -> X509VerifyResult { unsafe { X509VerifyError::from_raw(ffi::X509_STORE_CTX_get_error(self.as_ptr())) } } @@ -149,10 +143,7 @@ impl X509StoreContextRef { } /// Returns a mutable reference to the X509 verification configuration. - /// - /// This corresponds to [`X509_STORE_CTX_get0_param`]. - /// - /// [`SSL_get0_param`]: https://www.openssl.org/docs/manmaster/man3/X509_STORE_CTX_get0_param.html + #[corresponds(X509_STORE_CTX_get0_param)] pub fn verify_param_mut(&mut self) -> &mut X509VerifyParamRef { unsafe { X509VerifyParamRef::from_ptr_mut(ffi::X509_STORE_CTX_get0_param(self.as_ptr())) } } @@ -163,19 +154,13 @@ impl X509StoreContextRef { /// validation error if the certificate was not valid. /// /// This will only work inside of a call to `init`. - /// - /// This corresponds to [`X509_verify_cert`]. - /// - /// [`X509_verify_cert`]: https://www.openssl.org/docs/man1.0.2/crypto/X509_verify_cert.html + #[corresponds(X509_verify_cert)] pub fn verify_cert(&mut self) -> Result { unsafe { cvt_n(ffi::X509_verify_cert(self.as_ptr())).map(|n| n != 0) } } /// Set the verify result of the context. - /// - /// This corresponds to [`X509_STORE_CTX_set_error`]. - /// - /// [`X509_STORE_CTX_set_error`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_STORE_CTX_set_error.html + #[corresponds(X509_STORE_CTX_set_error)] pub fn set_error(&mut self, result: X509VerifyResult) { unsafe { ffi::X509_STORE_CTX_set_error( @@ -190,10 +175,7 @@ impl X509StoreContextRef { /// Returns a reference to the certificate which caused the error or None if /// no certificate is relevant to the error. - /// - /// This corresponds to [`X509_STORE_CTX_get_current_cert`]. - /// - /// [`X509_STORE_CTX_get_current_cert`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_STORE_CTX_get_current_cert.html + #[corresponds(X509_STORE_CTX_get_current_cert)] pub fn current_cert(&self) -> Option<&X509Ref> { unsafe { let ptr = ffi::X509_STORE_CTX_get_current_cert(self.as_ptr()); @@ -209,19 +191,13 @@ impl X509StoreContextRef { /// chain where the error occurred. If it is zero it occurred in the end /// entity certificate, one if it is the certificate which signed the end /// entity certificate and so on. - /// - /// This corresponds to [`X509_STORE_CTX_get_error_depth`]. - /// - /// [`X509_STORE_CTX_get_error_depth`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_STORE_CTX_get_error_depth.html + #[corresponds(X509_STORE_CTX_get_error_depth)] pub fn error_depth(&self) -> u32 { unsafe { ffi::X509_STORE_CTX_get_error_depth(self.as_ptr()) as u32 } } /// Returns a reference to a complete valid `X509` certificate chain. - /// - /// This corresponds to [`X509_STORE_CTX_get0_chain`]. - /// - /// [`X509_STORE_CTX_get0_chain`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_STORE_CTX_get0_chain.html + #[corresponds(X509_STORE_CTX_get0_chain)] pub fn chain(&self) -> Option<&StackRef> { unsafe { let chain = X509_STORE_CTX_get0_chain(self.as_ptr()); @@ -240,6 +216,7 @@ pub struct X509Builder(X509); impl X509Builder { /// Creates a new builder. + #[corresponds(X509_new)] pub fn new() -> Result { unsafe { ffi::init(); @@ -248,11 +225,13 @@ impl X509Builder { } /// Sets the notAfter constraint on the certificate. + #[corresponds(X509_set1_notAfter)] pub fn set_not_after(&mut self, not_after: &Asn1TimeRef) -> Result<(), ErrorStack> { unsafe { cvt(X509_set1_notAfter(self.0.as_ptr(), not_after.as_ptr())).map(|_| ()) } } /// Sets the notBefore constraint on the certificate. + #[corresponds(X509_set1_notBefore)] pub fn set_not_before(&mut self, not_before: &Asn1TimeRef) -> Result<(), ErrorStack> { unsafe { cvt(X509_set1_notBefore(self.0.as_ptr(), not_before.as_ptr())).map(|_| ()) } } @@ -261,11 +240,13 @@ impl X509Builder { /// /// Note that the version is zero-indexed; that is, a certificate corresponding to version 3 of /// the X.509 standard should pass `2` to this method. + #[corresponds(X509_set_version)] pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> { unsafe { cvt(ffi::X509_set_version(self.0.as_ptr(), version.into())).map(|_| ()) } } /// Sets the serial number of the certificate. + #[corresponds(X509_set_serialNumber)] pub fn set_serial_number(&mut self, serial_number: &Asn1IntegerRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::X509_set_serialNumber( @@ -277,6 +258,7 @@ impl X509Builder { } /// Sets the issuer name of the certificate. + #[corresponds(X509_set_issuer_name)] pub fn set_issuer_name(&mut self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::X509_set_issuer_name( @@ -305,6 +287,7 @@ impl X509Builder { /// let mut x509 = boring::x509::X509::builder().unwrap(); /// x509.set_subject_name(&x509_name).unwrap(); /// ``` + #[corresponds(X509_set_subject_name)] pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::X509_set_subject_name( @@ -316,6 +299,7 @@ impl X509Builder { } /// Sets the public key associated with the certificate. + #[corresponds(X509_set_pubkey)] pub fn set_pubkey(&mut self, key: &PKeyRef) -> Result<(), ErrorStack> where T: HasPublic, @@ -326,6 +310,7 @@ impl X509Builder { /// Returns a context object which is needed to create certain X509 extension values. /// /// Set `issuer` to `None` if the certificate will be self-signed. + #[corresponds(X509V3_set_ctx)] pub fn x509v3_context<'a>( &'a self, issuer: Option<&'a X509Ref>, @@ -365,10 +350,7 @@ impl X509Builder { } /// Adds an X509 extension value to the certificate. - /// - /// This corresponds to [`X509_add_ext`]. - /// - /// [`X509_add_ext`]: https://www.openssl.org/docs/man1.1.0/man3/X509_get_ext.html + #[corresponds(X509_add_ext)] pub fn append_extension2(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::X509_add_ext(self.0.as_ptr(), extension.as_ptr(), -1))?; @@ -377,6 +359,7 @@ impl X509Builder { } /// Signs the certificate with a private key. + #[corresponds(X509_sign)] pub fn sign(&mut self, key: &PKeyRef, hash: MessageDigest) -> Result<(), ErrorStack> where T: HasPrivate, @@ -400,43 +383,22 @@ foreign_type_and_impl_send_sync! { impl X509Ref { /// Returns this certificate's subject name. - /// - /// This corresponds to [`X509_get_subject_name`]. - /// - /// [`X509_get_subject_name`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_get_subject_name.html + #[corresponds(X509_get_subject_name)] pub fn subject_name(&self) -> &X509NameRef { unsafe { let name = ffi::X509_get_subject_name(self.as_ptr()); - assert!(!name.is_null()); - X509NameRef::from_ptr(name) + X509NameRef::from_const_ptr_opt(name).expect("issuer name must not be null") } } /// Returns the hash of the certificates subject - /// - /// This corresponds to `X509_subject_name_hash`. + #[corresponds(X509_subject_name_hash)] pub fn subject_name_hash(&self) -> u32 { unsafe { ffi::X509_subject_name_hash(self.as_ptr()) as u32 } } - /// Returns this certificate's issuer name. - /// - /// This corresponds to [`X509_get_issuer_name`]. - /// - /// [`X509_get_issuer_name`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_get_subject_name.html - pub fn issuer_name(&self) -> &X509NameRef { - unsafe { - let name = ffi::X509_get_issuer_name(self.as_ptr()); - assert!(!name.is_null()); - X509NameRef::from_ptr(name) - } - } - /// Returns this certificate's subject alternative name entries, if they exist. - /// - /// This corresponds to [`X509_get_ext_d2i`] called with `NID_subject_alt_name`. - /// - /// [`X509_get_ext_d2i`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_get_ext_d2i.html + #[corresponds(X509_get_ext_d2i)] pub fn subject_alt_names(&self) -> Option> { unsafe { let stack = ffi::X509_get_ext_d2i( @@ -453,11 +415,17 @@ impl X509Ref { } } + /// Returns this certificate's issuer name. + #[corresponds(X509_get_issuer_name)] + pub fn issuer_name(&self) -> &X509NameRef { + unsafe { + let name = ffi::X509_get_issuer_name(self.as_ptr()); + X509NameRef::from_const_ptr_opt(name).expect("issuer name must not be null") + } + } + /// Returns this certificate's issuer alternative name entries, if they exist. - /// - /// This corresponds to [`X509_get_ext_d2i`] called with `NID_issuer_alt_name`. - /// - /// [`X509_get_ext_d2i`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_get_ext_d2i.html + #[corresponds(X509_get_ext_d2i)] pub fn issuer_alt_names(&self) -> Option> { unsafe { let stack = ffi::X509_get_ext_d2i( @@ -474,6 +442,24 @@ impl X509Ref { } } + /// Returns this certificate's subject key id, if it exists. + #[corresponds(X509_get0_subject_key_id)] + pub fn subject_key_id(&self) -> Option<&Asn1StringRef> { + unsafe { + let data = ffi::X509_get0_subject_key_id(self.as_ptr()); + Asn1StringRef::from_const_ptr_opt(data) + } + } + + /// Returns this certificate's authority key id, if it exists. + #[corresponds(X509_get0_authority_key_id)] + pub fn authority_key_id(&self) -> Option<&Asn1StringRef> { + unsafe { + let data = ffi::X509_get0_authority_key_id(self.as_ptr()); + Asn1StringRef::from_const_ptr_opt(data) + } + } + pub fn public_key(&self) -> Result, ErrorStack> { unsafe { let pkey = cvt_p(ffi::X509_get_pubkey(self.as_ptr()))?; @@ -1058,6 +1044,20 @@ impl X509NameRef { } } + /// Returns an owned String representing the X509 name configurable via incoming flags. + /// + /// This function will return `None` if the underlying string contains invalid utf-8. + #[corresponds(X509_NAME_print_ex)] + pub fn print_ex(&self, flags: i32) -> Option { + unsafe { + let bio = MemBio::new().ok()?; + ffi::X509_NAME_print_ex(bio.as_ptr(), self.as_ptr(), 0, flags as _); + let buf = bio.get_buf().to_vec(); + let res = String::from_utf8(buf); + res.ok() + } + } + to_der! { /// Serializes the certificate into a DER-encoded X509 name structure. /// @@ -1584,9 +1584,7 @@ impl GeneralName { ffi::init(); let gn = cvt_p(ffi::GENERAL_NAME_new())?; (*gn).type_ = ffi::GEN_RID; - (*gn).d.registeredID = oid.as_ptr(); - - mem::forget(oid); + (*gn).d.registeredID = oid.into_ptr(); Ok(GeneralName::from_ptr(gn)) } diff --git a/boring/src/x509/tests/mod.rs b/boring/src/x509/tests/mod.rs index 65bb7f90..b3867ce2 100644 --- a/boring/src/x509/tests/mod.rs +++ b/boring/src/x509/tests/mod.rs @@ -179,6 +179,49 @@ fn test_subject_alt_name_iter() { assert!(subject_alt_names_iter.next().is_none()); } +#[test] +fn test_subject_key_id() { + // nid_test_cert_pem has SKI, but no AKI + let cert = include_bytes!("../../../test/nid_test_cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let ski = cert.subject_key_id().expect("unable to extract SKI"); + assert_eq!( + ski.as_slice(), + [ + 80, 107, 158, 237, 95, 61, 235, 100, 212, 115, 249, 244, 219, 163, 124, 55, 141, 2, 76, + 5 + ] + ); + + let aki = cert.authority_key_id(); + assert!(aki.is_none()); +} + +#[test] +fn test_x509_name_print_ex() { + let cert = include_bytes!("../../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let name_no_flags = cert + .subject_name() + .print_ex(0) + .expect("failed to print cert subject name"); + assert_eq!( + name_no_flags, + "C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=foobar.com" + ); + + let name_rfc2253 = cert + .subject_name() + .print_ex(ffi::XN_FLAG_RFC2253) + .expect("failed to print cert subject name"); + assert_eq!( + name_rfc2253, + "CN=foobar.com,O=Internet Widgits Pty Ltd,ST=Some-State,C=AU" + ); +} + #[test] fn x509_builder() { let pkey = pkey(); diff --git a/hyper-boring/Cargo.toml b/hyper-boring/Cargo.toml index 74fca69c..39593b24 100644 --- a/hyper-boring/Cargo.toml +++ b/hyper-boring/Cargo.toml @@ -17,7 +17,7 @@ rustdoc-args = ["--cfg", "docsrs"] [features] default = ["runtime"] -runtime = ["hyper/runtime"] +runtime = ["hyper_old/runtime"] # Use a FIPS-validated version of boringssl. fips = ["tokio-boring/fips"] @@ -28,19 +28,30 @@ fips-link-precompiled = ["tokio-boring/fips-link-precompiled"] # Enables experimental post-quantum crypto (https://blog.cloudflare.com/post-quantum-for-all/) pq-experimental = ["tokio-boring/pq-experimental"] +# Enable Hyper 1 support +hyper1 = ["dep:http", "dep:hyper", "dep:hyper-util", "dep:tower-service"] + [dependencies] antidote = { workspace = true } -http = { workspace = true } -hyper = { workspace = true, features = ["client"] } +http = { workspace = true, optional = true } +http_old = { workspace = true } +hyper = { workspace = true, optional = true } +hyper-util = { workspace = true, optional = true, features = ["client", "client-legacy"] } +hyper_old = { workspace = true, features = ["client"] } linked_hash_set = { workspace = true } once_cell = { workspace = true } boring = { workspace = true } tokio = { workspace = true } tokio-boring = { workspace = true } tower-layer = { workspace = true } +tower-service = { workspace = true, optional = true } [dev-dependencies] -hyper = { workspace = true, features = [ "full" ] } +bytes = { workspace = true } +http-body-util = { workspace = true } +hyper-util = { workspace = true, features = ["http1", "http2", "service", "tokio"] } +hyper = { workspace = true, features = ["server"] } +hyper_old = { workspace = true, features = [ "full" ] } tokio = { workspace = true, features = [ "full" ] } tower = { workspace = true, features = ["util"] } futures = { workspace = true } diff --git a/hyper-boring/src/lib.rs b/hyper-boring/src/lib.rs index 53c12a46..736a3d89 100644 --- a/hyper-boring/src/lib.rs +++ b/hyper-boring/src/lib.rs @@ -2,91 +2,27 @@ #![warn(missing_docs)] #![cfg_attr(docsrs, feature(doc_auto_cfg))] -use crate::cache::{SessionCache, SessionKey}; -use antidote::Mutex; +use crate::cache::SessionKey; use boring::error::ErrorStack; use boring::ex_data::Index; -use boring::ssl::{ - ConnectConfiguration, Ssl, SslConnector, SslConnectorBuilder, SslMethod, SslRef, - SslSessionCacheMode, -}; -use http::uri::Scheme; -use hyper::client::connect::{Connected, Connection}; -#[cfg(feature = "runtime")] -use hyper::client::HttpConnector; -use hyper::service::Service; -use hyper::Uri; +use boring::ssl::Ssl; use once_cell::sync::OnceCell; -use std::fmt::Debug; -use std::future::Future; -use std::io; -use std::net; -use std::pin::Pin; -use std::sync::Arc; -use std::task::{Context, Poll}; -use std::{error::Error, fmt}; -use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; +use std::fmt; use tokio_boring::SslStream; -use tower_layer::Layer; mod cache; -#[cfg(test)] -mod test; +mod v0; +/// Hyper 1 support. +#[cfg(feature = "hyper1")] +pub mod v1; + +pub use self::v0::*; fn key_index() -> Result, ErrorStack> { static IDX: OnceCell> = OnceCell::new(); IDX.get_or_try_init(Ssl::new_ex_index).copied() } -#[derive(Clone)] -struct Inner { - ssl: SslConnector, - cache: Arc>, - callback: Option, - ssl_callback: Option, -} - -type Callback = - Arc Result<(), ErrorStack> + Sync + Send>; -type SslCallback = Arc Result<(), ErrorStack> + Sync + Send>; - -impl Inner { - fn setup_ssl(&self, uri: &Uri, host: &str) -> Result { - let mut conf = self.ssl.configure()?; - - if let Some(ref callback) = self.callback { - callback(&mut conf, uri)?; - } - - let key = SessionKey { - host: host.to_string(), - port: uri.port_u16().unwrap_or(443), - }; - - if let Some(session) = self.cache.lock().get(&key) { - unsafe { - conf.set_session(&session)?; - } - } - - let idx = key_index()?; - conf.set_ex_data(idx, key); - - let mut ssl = conf.into_ssl(host)?; - - if let Some(ref ssl_callback) = self.ssl_callback { - ssl_callback(&mut ssl, uri)?; - } - - Ok(ssl) - } -} - -/// A layer which wraps services in an `HttpsConnector`. -pub struct HttpsLayer { - inner: Inner, -} - /// Settings for [`HttpsLayer`] pub struct HttpsLayerSettings { session_cache_capacity: usize, @@ -123,214 +59,6 @@ impl HttpsLayerSettingsBuilder { } } -impl HttpsLayer { - /// Creates a new `HttpsLayer` with default settings. - /// - /// ALPN is configured to support both HTTP/1 and HTTP/1.1. - pub fn new() -> Result { - let mut ssl = SslConnector::builder(SslMethod::tls())?; - - ssl.set_alpn_protos(b"\x02h2\x08http/1.1")?; - - Self::with_connector(ssl) - } - - /// Creates a new `HttpsLayer`. - /// - /// The session cache configuration of `ssl` will be overwritten. - pub fn with_connector(ssl: SslConnectorBuilder) -> Result { - Self::with_connector_and_settings(ssl, Default::default()) - } - - /// Creates a new `HttpsLayer` with settings - pub fn with_connector_and_settings( - mut ssl: SslConnectorBuilder, - settings: HttpsLayerSettings, - ) -> Result { - let cache = Arc::new(Mutex::new(SessionCache::with_capacity( - settings.session_cache_capacity, - ))); - - ssl.set_session_cache_mode(SslSessionCacheMode::CLIENT); - - ssl.set_new_session_callback({ - let cache = cache.clone(); - move |ssl, session| { - if let Some(key) = key_index().ok().and_then(|idx| ssl.ex_data(idx)) { - cache.lock().insert(key.clone(), session); - } - } - }); - - Ok(HttpsLayer { - inner: Inner { - ssl: ssl.build(), - cache, - callback: None, - ssl_callback: None, - }, - }) - } - - /// Registers a callback which can customize the configuration of each connection. - /// - /// Unsuitable to change verify hostflags (with `config.param_mut().set_hostflags(…)`), - /// as they are reset after the callback is executed. Use [`Self::set_ssl_callback`] - /// instead. - pub fn set_callback(&mut self, callback: F) - where - F: Fn(&mut ConnectConfiguration, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, - { - self.inner.callback = Some(Arc::new(callback)); - } - - /// Registers a callback which can customize the `Ssl` of each connection. - pub fn set_ssl_callback(&mut self, callback: F) - where - F: Fn(&mut SslRef, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, - { - self.inner.ssl_callback = Some(Arc::new(callback)); - } -} - -impl Layer for HttpsLayer { - type Service = HttpsConnector; - - fn layer(&self, inner: S) -> HttpsConnector { - HttpsConnector { - http: inner, - inner: self.inner.clone(), - } - } -} - -/// A Connector using OpenSSL to support `http` and `https` schemes. -#[derive(Clone)] -pub struct HttpsConnector { - http: T, - inner: Inner, -} - -#[cfg(feature = "runtime")] -impl HttpsConnector { - /// Creates a a new `HttpsConnector` using default settings. - /// - /// The Hyper `HttpConnector` is used to perform the TCP socket connection. ALPN is configured to support both - /// HTTP/2 and HTTP/1.1. - /// - /// Requires the `runtime` Cargo feature. - pub fn new() -> Result, ErrorStack> { - let mut http = HttpConnector::new(); - http.enforce_http(false); - - HttpsLayer::new().map(|l| l.layer(http)) - } -} - -impl HttpsConnector -where - S: Service + Send, - S::Error: Into>, - S::Future: Unpin + Send + 'static, - T: AsyncRead + AsyncWrite + Connection + Unpin + Debug + Sync + Send + 'static, -{ - /// Creates a new `HttpsConnector`. - /// - /// The session cache configuration of `ssl` will be overwritten. - pub fn with_connector( - http: S, - ssl: SslConnectorBuilder, - ) -> Result, ErrorStack> { - HttpsLayer::with_connector(ssl).map(|l| l.layer(http)) - } - - /// Registers a callback which can customize the configuration of each connection. - /// - /// Unsuitable to change verify hostflags (with `config.param_mut().set_hostflags(…)`), - /// as they are reset after the callback is executed. Use [`Self::set_ssl_callback`] - /// instead. - pub fn set_callback(&mut self, callback: F) - where - F: Fn(&mut ConnectConfiguration, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, - { - self.inner.callback = Some(Arc::new(callback)); - } - - /// Registers a callback which can customize the `Ssl` of each connection. - pub fn set_ssl_callback(&mut self, callback: F) - where - F: Fn(&mut SslRef, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, - { - self.inner.ssl_callback = Some(Arc::new(callback)); - } -} - -impl Service for HttpsConnector -where - S: Service + Send, - S::Error: Into>, - S::Future: Unpin + Send + 'static, - S::Response: AsyncRead + AsyncWrite + Connection + Unpin + Debug + Sync + Send + 'static, -{ - type Response = MaybeHttpsStream; - type Error = Box; - #[allow(clippy::type_complexity)] - type Future = Pin> + Send>>; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - self.http.poll_ready(cx).map_err(Into::into) - } - - fn call(&mut self, uri: Uri) -> Self::Future { - let is_tls_scheme = uri - .scheme() - .map(|s| s == &Scheme::HTTPS || s.as_str() == "wss") - .unwrap_or(false); - - let tls_setup = if is_tls_scheme { - Some((self.inner.clone(), uri.clone())) - } else { - None - }; - - let connect = self.http.call(uri); - - let f = async { - let conn = connect.await.map_err(Into::into)?; - - let (inner, uri) = match tls_setup { - Some((inner, uri)) => (inner, uri), - None => return Ok(MaybeHttpsStream::Http(conn)), - }; - - let mut host = uri.host().ok_or("URI missing host")?; - - // If `host` is an IPv6 address, we must strip away the square brackets that surround - // it (otherwise, boring will fail to parse the host as an IP address, eventually - // causing the handshake to fail due a hostname verification error). - if !host.is_empty() { - let last = host.len() - 1; - let mut chars = host.chars(); - - if let (Some('['), Some(']')) = (chars.next(), chars.last()) { - if host[1..last].parse::().is_ok() { - host = &host[1..last]; - } - } - } - - let ssl = inner.setup_ssl(&uri, host)?; - let stream = tokio_boring::SslStreamBuilder::new(ssl, conn) - .connect() - .await?; - - Ok(MaybeHttpsStream::Https(stream)) - }; - - Box::pin(f) - } -} - /// A stream which may be wrapped with TLS. pub enum MaybeHttpsStream { /// A raw HTTP stream. @@ -339,72 +67,6 @@ pub enum MaybeHttpsStream { Https(SslStream), } -impl AsyncRead for MaybeHttpsStream -where - T: AsyncRead + AsyncWrite + Unpin, -{ - fn poll_read( - mut self: Pin<&mut Self>, - ctx: &mut Context<'_>, - buf: &mut ReadBuf, - ) -> Poll> { - match &mut *self { - MaybeHttpsStream::Http(s) => Pin::new(s).poll_read(ctx, buf), - MaybeHttpsStream::Https(s) => Pin::new(s).poll_read(ctx, buf), - } - } -} - -impl AsyncWrite for MaybeHttpsStream -where - T: AsyncRead + AsyncWrite + Unpin, -{ - fn poll_write( - mut self: Pin<&mut Self>, - ctx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - match &mut *self { - MaybeHttpsStream::Http(s) => Pin::new(s).poll_write(ctx, buf), - MaybeHttpsStream::Https(s) => Pin::new(s).poll_write(ctx, buf), - } - } - - fn poll_flush(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { - match &mut *self { - MaybeHttpsStream::Http(s) => Pin::new(s).poll_flush(ctx), - MaybeHttpsStream::Https(s) => Pin::new(s).poll_flush(ctx), - } - } - - fn poll_shutdown(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { - match &mut *self { - MaybeHttpsStream::Http(s) => Pin::new(s).poll_shutdown(ctx), - MaybeHttpsStream::Https(s) => Pin::new(s).poll_shutdown(ctx), - } - } -} - -impl Connection for MaybeHttpsStream -where - T: Connection, -{ - fn connected(&self) -> Connected { - match self { - MaybeHttpsStream::Http(s) => s.connected(), - MaybeHttpsStream::Https(s) => { - let mut connected = s.get_ref().connected(); - - if s.ssl().selected_alpn_protocol() == Some(b"h2") { - connected = connected.negotiated_h2(); - } - - connected - } - } - } -} - impl fmt::Debug for MaybeHttpsStream { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { diff --git a/hyper-boring/src/v0.rs b/hyper-boring/src/v0.rs new file mode 100644 index 00000000..172d1640 --- /dev/null +++ b/hyper-boring/src/v0.rs @@ -0,0 +1,345 @@ +use crate::cache::{SessionCache, SessionKey}; +use crate::{key_index, HttpsLayerSettings, MaybeHttpsStream}; +use antidote::Mutex; +use boring::error::ErrorStack; +use boring::ssl::{ + ConnectConfiguration, Ssl, SslConnector, SslConnectorBuilder, SslMethod, SslRef, + SslSessionCacheMode, +}; +use http_old::uri::Scheme; +use hyper_old::client::connect::{Connected, Connection}; +use hyper_old::client::HttpConnector; +use hyper_old::service::Service; +use hyper_old::Uri; +use std::error::Error; +use std::future::Future; +use std::net; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; +use std::{fmt, io}; +use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; +use tower_layer::Layer; + +/// A Connector using OpenSSL to support `http` and `https` schemes. +#[derive(Clone)] +pub struct HttpsConnector { + http: T, + inner: Inner, +} + +#[cfg(feature = "runtime")] +impl HttpsConnector { + /// Creates a a new `HttpsConnector` using default settings. + /// + /// The Hyper `HttpConnector` is used to perform the TCP socket connection. ALPN is configured to support both + /// HTTP/2 and HTTP/1.1. + /// + /// Requires the `runtime` Cargo feature. + pub fn new() -> Result, ErrorStack> { + let mut http = HttpConnector::new(); + http.enforce_http(false); + + HttpsLayer::new().map(|l| l.layer(http)) + } +} + +impl HttpsConnector +where + S: Service + Send, + S::Error: Into>, + S::Future: Unpin + Send + 'static, + T: AsyncRead + AsyncWrite + Connection + Unpin + fmt::Debug + Sync + Send + 'static, +{ + /// Creates a new `HttpsConnector`. + /// + /// The session cache configuration of `ssl` will be overwritten. + pub fn with_connector( + http: S, + ssl: SslConnectorBuilder, + ) -> Result, ErrorStack> { + HttpsLayer::with_connector(ssl).map(|l| l.layer(http)) + } + + /// Registers a callback which can customize the configuration of each connection. + /// + /// Unsuitable to change verify hostflags (with `config.param_mut().set_hostflags(…)`), + /// as they are reset after the callback is executed. Use [`Self::set_ssl_callback`] + /// instead. + pub fn set_callback(&mut self, callback: F) + where + F: Fn(&mut ConnectConfiguration, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, + { + self.inner.callback = Some(Arc::new(callback)); + } + + /// Registers a callback which can customize the `Ssl` of each connection. + pub fn set_ssl_callback(&mut self, callback: F) + where + F: Fn(&mut SslRef, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, + { + self.inner.ssl_callback = Some(Arc::new(callback)); + } +} + +/// A layer which wraps services in an `HttpsConnector`. +pub struct HttpsLayer { + inner: Inner, +} + +#[derive(Clone)] +struct Inner { + ssl: SslConnector, + cache: Arc>, + callback: Option, + ssl_callback: Option, +} + +type Callback = + Arc Result<(), ErrorStack> + Sync + Send>; +type SslCallback = Arc Result<(), ErrorStack> + Sync + Send>; + +impl HttpsLayer { + /// Creates a new `HttpsLayer` with default settings. + /// + /// ALPN is configured to support both HTTP/1 and HTTP/1.1. + pub fn new() -> Result { + let mut ssl = SslConnector::builder(SslMethod::tls())?; + + ssl.set_alpn_protos(b"\x02h2\x08http/1.1")?; + + Self::with_connector(ssl) + } + + /// Creates a new `HttpsLayer`. + /// + /// The session cache configuration of `ssl` will be overwritten. + pub fn with_connector(ssl: SslConnectorBuilder) -> Result { + Self::with_connector_and_settings(ssl, Default::default()) + } + + /// Creates a new `HttpsLayer` with settings + pub fn with_connector_and_settings( + mut ssl: SslConnectorBuilder, + settings: HttpsLayerSettings, + ) -> Result { + let cache = Arc::new(Mutex::new(SessionCache::with_capacity( + settings.session_cache_capacity, + ))); + + ssl.set_session_cache_mode(SslSessionCacheMode::CLIENT); + + ssl.set_new_session_callback({ + let cache = cache.clone(); + move |ssl, session| { + if let Some(key) = key_index().ok().and_then(|idx| ssl.ex_data(idx)) { + cache.lock().insert(key.clone(), session); + } + } + }); + + Ok(HttpsLayer { + inner: Inner { + ssl: ssl.build(), + cache, + callback: None, + ssl_callback: None, + }, + }) + } + + /// Registers a callback which can customize the configuration of each connection. + /// + /// Unsuitable to change verify hostflags (with `config.param_mut().set_hostflags(…)`), + /// as they are reset after the callback is executed. Use [`Self::set_ssl_callback`] + /// instead. + pub fn set_callback(&mut self, callback: F) + where + F: Fn(&mut ConnectConfiguration, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, + { + self.inner.callback = Some(Arc::new(callback)); + } + + /// Registers a callback which can customize the `Ssl` of each connection. + pub fn set_ssl_callback(&mut self, callback: F) + where + F: Fn(&mut SslRef, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, + { + self.inner.ssl_callback = Some(Arc::new(callback)); + } +} + +impl Layer for HttpsLayer { + type Service = HttpsConnector; + + fn layer(&self, inner: S) -> HttpsConnector { + HttpsConnector { + http: inner, + inner: self.inner.clone(), + } + } +} + +impl Inner { + fn setup_ssl(&self, uri: &Uri, host: &str) -> Result { + let mut conf = self.ssl.configure()?; + + if let Some(ref callback) = self.callback { + callback(&mut conf, uri)?; + } + + let key = SessionKey { + host: host.to_string(), + port: uri.port_u16().unwrap_or(443), + }; + + if let Some(session) = self.cache.lock().get(&key) { + unsafe { + conf.set_session(&session)?; + } + } + + let idx = key_index()?; + conf.set_ex_data(idx, key); + + let mut ssl = conf.into_ssl(host)?; + + if let Some(ref ssl_callback) = self.ssl_callback { + ssl_callback(&mut ssl, uri)?; + } + + Ok(ssl) + } +} + +impl Service for HttpsConnector +where + S: Service + Send, + S::Error: Into>, + S::Future: Unpin + Send + 'static, + S::Response: AsyncRead + AsyncWrite + Connection + Unpin + fmt::Debug + Sync + Send + 'static, +{ + type Response = MaybeHttpsStream; + type Error = Box; + #[allow(clippy::type_complexity)] + type Future = Pin> + Send>>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.http.poll_ready(cx).map_err(Into::into) + } + + fn call(&mut self, uri: Uri) -> Self::Future { + let is_tls_scheme = uri + .scheme() + .map(|s| s == &Scheme::HTTPS || s.as_str() == "wss") + .unwrap_or(false); + + let tls_setup = if is_tls_scheme { + Some((self.inner.clone(), uri.clone())) + } else { + None + }; + + let connect = self.http.call(uri); + + let f = async { + let conn = connect.await.map_err(Into::into)?; + + let (inner, uri) = match tls_setup { + Some((inner, uri)) => (inner, uri), + None => return Ok(MaybeHttpsStream::Http(conn)), + }; + + let mut host = uri.host().ok_or("URI missing host")?; + + // If `host` is an IPv6 address, we must strip away the square brackets that surround + // it (otherwise, boring will fail to parse the host as an IP address, eventually + // causing the handshake to fail due a hostname verification error). + if !host.is_empty() { + let last = host.len() - 1; + let mut chars = host.chars(); + + if let (Some('['), Some(']')) = (chars.next(), chars.last()) { + if host[1..last].parse::().is_ok() { + host = &host[1..last]; + } + } + } + + let ssl = inner.setup_ssl(&uri, host)?; + let stream = tokio_boring::SslStreamBuilder::new(ssl, conn) + .connect() + .await?; + + Ok(MaybeHttpsStream::Https(stream)) + }; + + Box::pin(f) + } +} + +impl Connection for MaybeHttpsStream +where + T: Connection, +{ + fn connected(&self) -> Connected { + match self { + MaybeHttpsStream::Http(s) => s.connected(), + MaybeHttpsStream::Https(s) => { + let mut connected = s.get_ref().connected(); + + if s.ssl().selected_alpn_protocol() == Some(b"h2") { + connected = connected.negotiated_h2(); + } + + connected + } + } + } +} + +impl AsyncRead for MaybeHttpsStream +where + T: AsyncRead + AsyncWrite + Unpin, +{ + fn poll_read( + mut self: Pin<&mut Self>, + ctx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + match &mut *self { + MaybeHttpsStream::Http(s) => Pin::new(s).poll_read(ctx, buf), + MaybeHttpsStream::Https(s) => Pin::new(s).poll_read(ctx, buf), + } + } +} + +impl AsyncWrite for MaybeHttpsStream +where + T: AsyncRead + AsyncWrite + Unpin, +{ + fn poll_write( + mut self: Pin<&mut Self>, + ctx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + match &mut *self { + MaybeHttpsStream::Http(s) => Pin::new(s).poll_write(ctx, buf), + MaybeHttpsStream::Https(s) => Pin::new(s).poll_write(ctx, buf), + } + } + + fn poll_flush(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { + match &mut *self { + MaybeHttpsStream::Http(s) => Pin::new(s).poll_flush(ctx), + MaybeHttpsStream::Https(s) => Pin::new(s).poll_flush(ctx), + } + } + + fn poll_shutdown(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { + match &mut *self { + MaybeHttpsStream::Http(s) => Pin::new(s).poll_shutdown(ctx), + MaybeHttpsStream::Https(s) => Pin::new(s).poll_shutdown(ctx), + } + } +} diff --git a/hyper-boring/src/v1.rs b/hyper-boring/src/v1.rs new file mode 100644 index 00000000..e1f9a43d --- /dev/null +++ b/hyper-boring/src/v1.rs @@ -0,0 +1,350 @@ +use crate::cache::{SessionCache, SessionKey}; +use crate::{key_index, HttpsLayerSettings, MaybeHttpsStream}; +use antidote::Mutex; +use boring::error::ErrorStack; +use boring::ssl::{ + ConnectConfiguration, Ssl, SslConnector, SslConnectorBuilder, SslMethod, SslRef, + SslSessionCacheMode, +}; +use http::uri::Scheme; +use http::Uri; +use hyper::rt::{Read, ReadBufCursor, Write}; +use hyper_util::client::legacy::connect::{Connected, Connection, HttpConnector}; +use hyper_util::rt::TokioIo; +use std::error::Error; +use std::fmt; +use std::future::Future; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; +use std::{io, net}; +use tokio::io::{AsyncRead, AsyncWrite}; +use tower_layer::Layer; +use tower_service::Service; + +/// A Connector using BoringSSL to support `http` and `https` schemes. +#[derive(Clone)] +pub struct HttpsConnector { + http: T, + inner: Inner, +} + +#[cfg(feature = "runtime")] +impl HttpsConnector { + /// Creates a a new `HttpsConnector` using default settings. + /// + /// The Hyper `HttpConnector` is used to perform the TCP socket connection. ALPN is configured to support both + /// HTTP/2 and HTTP/1.1. + /// + /// Requires the `runtime` Cargo feature. + pub fn new() -> Result, ErrorStack> { + let mut http = HttpConnector::new(); + http.enforce_http(false); + + HttpsLayer::new().map(|l| l.layer(http)) + } +} + +impl HttpsConnector +where + S: Service> + Send, + S::Error: Into>, + S::Future: Unpin + Send + 'static, + T: AsyncRead + AsyncWrite + Connection + Unpin + fmt::Debug + Sync + Send + 'static, +{ + /// Creates a new `HttpsConnector`. + /// + /// The session cache configuration of `ssl` will be overwritten. + pub fn with_connector( + http: S, + ssl: SslConnectorBuilder, + ) -> Result, ErrorStack> { + HttpsLayer::with_connector(ssl).map(|l| l.layer(http)) + } + + /// Registers a callback which can customize the configuration of each connection. + /// + /// Unsuitable to change verify hostflags (with `config.param_mut().set_hostflags(…)`), + /// as they are reset after the callback is executed. Use [`Self::set_ssl_callback`] + /// instead. + pub fn set_callback(&mut self, callback: F) + where + F: Fn(&mut ConnectConfiguration, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, + { + self.inner.callback = Some(Arc::new(callback)); + } + + /// Registers a callback which can customize the `Ssl` of each connection. + pub fn set_ssl_callback(&mut self, callback: F) + where + F: Fn(&mut SslRef, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, + { + self.inner.ssl_callback = Some(Arc::new(callback)); + } +} + +/// A layer which wraps services in an `HttpsConnector`. +pub struct HttpsLayer { + inner: Inner, +} + +#[derive(Clone)] +struct Inner { + ssl: SslConnector, + cache: Arc>, + callback: Option, + ssl_callback: Option, +} + +type Callback = + Arc Result<(), ErrorStack> + Sync + Send>; +type SslCallback = Arc Result<(), ErrorStack> + Sync + Send>; + +impl HttpsLayer { + /// Creates a new `HttpsLayer` with default settings. + /// + /// ALPN is configured to support both HTTP/1 and HTTP/1.1. + pub fn new() -> Result { + let mut ssl = SslConnector::builder(SslMethod::tls())?; + + ssl.set_alpn_protos(b"\x02h2\x08http/1.1")?; + + Self::with_connector(ssl) + } + + /// Creates a new `HttpsLayer`. + /// + /// The session cache configuration of `ssl` will be overwritten. + pub fn with_connector(ssl: SslConnectorBuilder) -> Result { + Self::with_connector_and_settings(ssl, Default::default()) + } + + /// Creates a new `HttpsLayer` with settings + pub fn with_connector_and_settings( + mut ssl: SslConnectorBuilder, + settings: HttpsLayerSettings, + ) -> Result { + let cache = Arc::new(Mutex::new(SessionCache::with_capacity( + settings.session_cache_capacity, + ))); + + ssl.set_session_cache_mode(SslSessionCacheMode::CLIENT); + + ssl.set_new_session_callback({ + let cache = cache.clone(); + move |ssl, session| { + if let Some(key) = key_index().ok().and_then(|idx| ssl.ex_data(idx)) { + cache.lock().insert(key.clone(), session); + } + } + }); + + Ok(HttpsLayer { + inner: Inner { + ssl: ssl.build(), + cache, + callback: None, + ssl_callback: None, + }, + }) + } + + /// Registers a callback which can customize the configuration of each connection. + /// + /// Unsuitable to change verify hostflags (with `config.param_mut().set_hostflags(…)`), + /// as they are reset after the callback is executed. Use [`Self::set_ssl_callback`] + /// instead. + pub fn set_callback(&mut self, callback: F) + where + F: Fn(&mut ConnectConfiguration, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, + { + self.inner.callback = Some(Arc::new(callback)); + } + + /// Registers a callback which can customize the `Ssl` of each connection. + pub fn set_ssl_callback(&mut self, callback: F) + where + F: Fn(&mut SslRef, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, + { + self.inner.ssl_callback = Some(Arc::new(callback)); + } +} + +impl Layer for HttpsLayer { + type Service = HttpsConnector; + + fn layer(&self, inner: S) -> HttpsConnector { + HttpsConnector { + http: inner, + inner: self.inner.clone(), + } + } +} + +impl Inner { + fn setup_ssl(&self, uri: &Uri, host: &str) -> Result { + let mut conf = self.ssl.configure()?; + + if let Some(ref callback) = self.callback { + callback(&mut conf, uri)?; + } + + let key = SessionKey { + host: host.to_string(), + port: uri.port_u16().unwrap_or(443), + }; + + if let Some(session) = self.cache.lock().get(&key) { + unsafe { + conf.set_session(&session)?; + } + } + + let idx = key_index()?; + conf.set_ex_data(idx, key); + + let mut ssl = conf.into_ssl(host)?; + + if let Some(ref ssl_callback) = self.ssl_callback { + ssl_callback(&mut ssl, uri)?; + } + + Ok(ssl) + } +} + +impl Service for HttpsConnector +where + S: Service> + Send, + S::Error: Into>, + S::Future: Unpin + Send + 'static, + T: AsyncRead + AsyncWrite + Connection + Unpin + fmt::Debug + Sync + Send + 'static, +{ + type Response = MaybeHttpsStream; + type Error = Box; + #[allow(clippy::type_complexity)] + type Future = Pin> + Send>>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.http.poll_ready(cx).map_err(Into::into) + } + + fn call(&mut self, uri: Uri) -> Self::Future { + let is_tls_scheme = uri + .scheme() + .map(|s| s == &Scheme::HTTPS || s.as_str() == "wss") + .unwrap_or(false); + + let tls_setup = if is_tls_scheme { + Some((self.inner.clone(), uri.clone())) + } else { + None + }; + + let connect = self.http.call(uri); + + let f = async { + let conn = connect.await.map_err(Into::into)?.into_inner(); + + let (inner, uri) = match tls_setup { + Some((inner, uri)) => (inner, uri), + None => return Ok(MaybeHttpsStream::Http(conn)), + }; + + let mut host = uri.host().ok_or("URI missing host")?; + + // If `host` is an IPv6 address, we must strip away the square brackets that surround + // it (otherwise, boring will fail to parse the host as an IP address, eventually + // causing the handshake to fail due a hostname verification error). + if !host.is_empty() { + let last = host.len() - 1; + let mut chars = host.chars(); + + if let (Some('['), Some(']')) = (chars.next(), chars.last()) { + if host[1..last].parse::().is_ok() { + host = &host[1..last]; + } + } + } + + let ssl = inner.setup_ssl(&uri, host)?; + let stream = tokio_boring::SslStreamBuilder::new(ssl, conn) + .connect() + .await?; + + Ok(MaybeHttpsStream::Https(stream)) + }; + + Box::pin(f) + } +} + +impl Connection for MaybeHttpsStream +where + T: Connection, +{ + fn connected(&self) -> Connected { + match self { + MaybeHttpsStream::Http(s) => s.connected(), + MaybeHttpsStream::Https(s) => { + let mut connected = s.get_ref().connected(); + + if s.ssl().selected_alpn_protocol() == Some(b"h2") { + connected = connected.negotiated_h2(); + } + + connected + } + } + } +} + +impl Read for MaybeHttpsStream +where + T: AsyncRead + AsyncWrite + Unpin, +{ + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: ReadBufCursor<'_>, + ) -> Poll> { + match &mut *self { + MaybeHttpsStream::Http(inner) => Pin::new(&mut TokioIo::new(inner)).poll_read(cx, buf), + MaybeHttpsStream::Https(inner) => Pin::new(&mut TokioIo::new(inner)).poll_read(cx, buf), + } + } +} + +impl Write for MaybeHttpsStream +where + T: AsyncRead + AsyncWrite + Unpin, +{ + fn poll_write( + mut self: Pin<&mut Self>, + ctx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + match &mut *self { + MaybeHttpsStream::Http(inner) => { + Pin::new(&mut TokioIo::new(inner)).poll_write(ctx, buf) + } + MaybeHttpsStream::Https(inner) => { + Pin::new(&mut TokioIo::new(inner)).poll_write(ctx, buf) + } + } + } + + fn poll_flush(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { + match &mut *self { + MaybeHttpsStream::Http(inner) => Pin::new(&mut TokioIo::new(inner)).poll_flush(ctx), + MaybeHttpsStream::Https(inner) => Pin::new(&mut TokioIo::new(inner)).poll_flush(ctx), + } + } + + fn poll_shutdown(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { + match &mut *self { + MaybeHttpsStream::Http(inner) => Pin::new(&mut TokioIo::new(inner)).poll_shutdown(ctx), + MaybeHttpsStream::Https(inner) => Pin::new(&mut TokioIo::new(inner)).poll_shutdown(ctx), + } + } +} diff --git a/hyper-boring/test/cert.pem b/hyper-boring/tests/test/cert.pem similarity index 100% rename from hyper-boring/test/cert.pem rename to hyper-boring/tests/test/cert.pem diff --git a/hyper-boring/test/key.pem b/hyper-boring/tests/test/key.pem similarity index 100% rename from hyper-boring/test/key.pem rename to hyper-boring/tests/test/key.pem diff --git a/hyper-boring/test/root-ca.pem b/hyper-boring/tests/test/root-ca.pem similarity index 100% rename from hyper-boring/test/root-ca.pem rename to hyper-boring/tests/test/root-ca.pem diff --git a/hyper-boring/src/test.rs b/hyper-boring/tests/v0.rs similarity index 87% rename from hyper-boring/src/test.rs rename to hyper-boring/tests/v0.rs index 006d5163..08cfce12 100644 --- a/hyper-boring/src/test.rs +++ b/hyper-boring/tests/v0.rs @@ -1,12 +1,12 @@ -use super::*; -use boring::ssl::{SslAcceptor, SslFiletype, SslMethod}; +use boring::ssl::{SslAcceptor, SslConnector, SslFiletype, SslMethod}; use futures::StreamExt; -use hyper::client::HttpConnector; -use hyper::server::conn::Http; -use hyper::{service, Response}; -use hyper::{Body, Client}; +use hyper_boring::HttpsConnector; +use hyper_old::client::HttpConnector; +use hyper_old::server::conn::Http; +use hyper_old::{service, Response}; +use hyper_old::{Body, Client}; use std::convert::Infallible; -use std::iter; +use std::{io, iter}; use tokio::net::TcpListener; #[tokio::test] @@ -37,10 +37,10 @@ async fn localhost() { let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); acceptor.set_session_id_context(b"test").unwrap(); acceptor - .set_private_key_file("test/key.pem", SslFiletype::PEM) + .set_private_key_file("tests/test/key.pem", SslFiletype::PEM) .unwrap(); acceptor - .set_certificate_chain_file("test/cert.pem") + .set_certificate_chain_file("tests/test/cert.pem") .unwrap(); let acceptor = acceptor.build(); @@ -69,7 +69,7 @@ async fn localhost() { let mut ssl = SslConnector::builder(SslMethod::tls()).unwrap(); - ssl.set_ca_file("test/root-ca.pem").unwrap(); + ssl.set_ca_file("tests/test/root-ca.pem").unwrap(); use std::fs::File; use std::io::Write; @@ -104,10 +104,10 @@ async fn alpn_h2() { let server = async move { let mut acceptor = SslAcceptor::mozilla_modern(SslMethod::tls()).unwrap(); acceptor - .set_certificate_chain_file("test/cert.pem") + .set_certificate_chain_file("tests/test/cert.pem") .unwrap(); acceptor - .set_private_key_file("test/key.pem", SslFiletype::PEM) + .set_private_key_file("tests/test/key.pem", SslFiletype::PEM) .unwrap(); acceptor.set_alpn_select_callback(|_, client| { ssl::select_next_proto(b"\x02h2", client).ok_or(AlpnError::NOACK) @@ -138,7 +138,7 @@ async fn alpn_h2() { let mut ssl = SslConnector::builder(SslMethod::tls()).unwrap(); - ssl.set_ca_file("test/root-ca.pem").unwrap(); + ssl.set_ca_file("tests/test/root-ca.pem").unwrap(); let mut ssl = HttpsConnector::with_connector(connector, ssl).unwrap(); diff --git a/hyper-boring/tests/v1.rs b/hyper-boring/tests/v1.rs new file mode 100644 index 00000000..441caea6 --- /dev/null +++ b/hyper-boring/tests/v1.rs @@ -0,0 +1,160 @@ +#![cfg(feature = "hyper1")] + +use boring::ssl::{SslAcceptor, SslConnector, SslFiletype, SslMethod}; +use bytes::Bytes; +use futures::StreamExt; +use http_body_util::{BodyStream, Empty}; +use hyper::{service, Response}; +use hyper_boring::v1::HttpsConnector; +use hyper_util::client::legacy::connect::HttpConnector; +use hyper_util::client::legacy::Client; +use hyper_util::rt::{TokioExecutor, TokioIo}; +use std::convert::Infallible; +use std::{io, iter}; +use tokio::net::TcpListener; + +#[tokio::test] +async fn google() { + let ssl = HttpsConnector::new().unwrap(); + let client = Client::builder(TokioExecutor::new()) + .pool_max_idle_per_host(0) + .build::<_, Empty>(ssl); + + for _ in 0..3 { + let resp = client + .get("https://www.google.com".parse().unwrap()) + .await + .expect("connection should succeed"); + let mut body = BodyStream::new(resp.into_body()); + while body.next().await.transpose().unwrap().is_some() {} + } +} + +#[tokio::test] +async fn localhost() { + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let port = addr.port(); + + let server = async move { + let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); + acceptor.set_session_id_context(b"test").unwrap(); + acceptor + .set_private_key_file("tests/test/key.pem", SslFiletype::PEM) + .unwrap(); + acceptor + .set_certificate_chain_file("tests/test/cert.pem") + .unwrap(); + let acceptor = acceptor.build(); + + for _ in 0..3 { + let stream = listener.accept().await.unwrap().0; + let stream = tokio_boring::accept(&acceptor, stream).await.unwrap(); + + let service = service::service_fn(|_| async { + Ok::<_, io::Error>(Response::new(>::new())) + }); + + hyper::server::conn::http1::Builder::new() + .keep_alive(false) + .serve_connection(TokioIo::new(stream), service) + .await + .unwrap(); + } + }; + tokio::spawn(server); + + let resolver = + tower::service_fn(move |_name| async move { Ok::<_, Infallible>(iter::once(addr)) }); + + let mut connector = HttpConnector::new_with_resolver(resolver); + + connector.enforce_http(false); + + let mut ssl = SslConnector::builder(SslMethod::tls()).unwrap(); + + ssl.set_ca_file("tests/test/root-ca.pem").unwrap(); + + use std::fs::File; + use std::io::Write; + + let file = File::create("../target/keyfile.log").unwrap(); + ssl.set_keylog_callback(move |_, line| { + let _ = writeln!(&file, "{}", line); + }); + + let ssl = HttpsConnector::with_connector(connector, ssl).unwrap(); + let client = Client::builder(TokioExecutor::new()).build::<_, Empty>(ssl); + + for _ in 0..3 { + let resp = client + .get(format!("https://foobar.com:{}", port).parse().unwrap()) + .await + .unwrap(); + assert!(resp.status().is_success(), "{}", resp.status()); + let mut body = BodyStream::new(resp.into_body()); + while body.next().await.transpose().unwrap().is_some() {} + } +} + +#[tokio::test] +async fn alpn_h2() { + use boring::ssl::{self, AlpnError}; + + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let port = addr.port(); + + let server = async move { + let mut acceptor = SslAcceptor::mozilla_modern(SslMethod::tls()).unwrap(); + acceptor + .set_certificate_chain_file("tests/test/cert.pem") + .unwrap(); + acceptor + .set_private_key_file("tests/test/key.pem", SslFiletype::PEM) + .unwrap(); + acceptor.set_alpn_select_callback(|_, client| { + ssl::select_next_proto(b"\x02h2", client).ok_or(AlpnError::NOACK) + }); + let acceptor = acceptor.build(); + + let stream = listener.accept().await.unwrap().0; + let stream = tokio_boring::accept(&acceptor, stream).await.unwrap(); + assert_eq!(stream.ssl().selected_alpn_protocol().unwrap(), b"h2"); + + let service = service::service_fn(|_| async { + Ok::<_, io::Error>(Response::new(>::new())) + }); + + hyper::server::conn::http2::Builder::new(TokioExecutor::new()) + .serve_connection(TokioIo::new(stream), service) + .await + .unwrap(); + }; + tokio::spawn(server); + + let resolver = + tower::service_fn(move |_name| async move { Ok::<_, Infallible>(iter::once(addr)) }); + + let mut connector = HttpConnector::new_with_resolver(resolver); + + connector.enforce_http(false); + + let mut ssl = SslConnector::builder(SslMethod::tls()).unwrap(); + + ssl.set_ca_file("tests/test/root-ca.pem").unwrap(); + + let mut ssl = HttpsConnector::with_connector(connector, ssl).unwrap(); + + ssl.set_ssl_callback(|ssl, _| ssl.set_alpn_protos(b"\x02h2\x08http/1.1")); + + let client = Client::builder(TokioExecutor::new()).build::<_, Empty>(ssl); + + let resp = client + .get(format!("https://foobar.com:{}", port).parse().unwrap()) + .await + .unwrap(); + assert!(resp.status().is_success(), "{}", resp.status()); + let mut body = BodyStream::new(resp.into_body()); + while body.next().await.transpose().unwrap().is_some() {} +}