diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9dc1367c..a14887ac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -197,3 +197,16 @@ jobs: run: ln -s clang clang++-12 - run: cargo test --features fips name: Run tests + + test-rpk: + name: Test RPK + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + with: + submodules: 'recursive' + - name: Install Rust (rustup) + run: rustup update stable --no-self-update && rustup default stable + shell: bash + - run: cargo test --features rpk + name: Run tests diff --git a/Cargo.toml b/Cargo.toml index 517858d0..58476d81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,3 +5,4 @@ members = [ "tokio-boring", "hyper-boring" ] +resolver = "2" \ No newline at end of file diff --git a/boring-sys/Cargo.toml b/boring-sys/Cargo.toml index e8ec9fca..99d85781 100644 --- a/boring-sys/Cargo.toml +++ b/boring-sys/Cargo.toml @@ -11,7 +11,7 @@ documentation = "https://docs.rs/boring-sys" links = "boringssl" readme = "README.md" categories = ["cryptography", "external-ffi-bindings"] -edition = "2018" +edition = "2021" include = [ "/*.md", "/*.toml", @@ -24,12 +24,21 @@ include = [ "/deps/boringssl/LICENSE", "/build.rs", "/src", + "/patches", + "/scripts" ] [build-dependencies] bindgen = { version = "0.65.1", default-features = false, features = ["runtime"] } cmake = "0.1" +fslock = "0.2.1" [features] # Use a FIPS-validated version of boringssl. fips = [] + +# Enables Raw public key API (https://datatracker.ietf.org/doc/html/rfc7250) +rpk = [] + +# Enables post-quantum crypto (https://blog.cloudflare.com/post-quantum-for-all/) +post-quantum-crypto = [] diff --git a/boring-sys/build.rs b/boring-sys/build.rs index de76a78c..6c8a85bd 100644 --- a/boring-sys/build.rs +++ b/boring-sys/build.rs @@ -1,3 +1,7 @@ +use fslock::LockFile; +use std::env; +use std::io; +use std::io::Write; use std::path::{Path, PathBuf}; use std::process::Command; @@ -285,7 +289,6 @@ fn get_extra_clang_args_for_bindgen() -> Vec { #[allow(clippy::single_match)] match os.as_ref() { "ios" => { - use std::io::Write; // When cross-compiling for iOS, tell bindgen to use iOS sysroot, // and *don't* use system headers of the host macOS. let sdk = get_ios_sdk_name(); @@ -325,28 +328,96 @@ fn get_extra_clang_args_for_bindgen() -> Vec { params } -fn main() { - use std::env; +fn ensure_patches_applied() -> io::Result<()> { + let mut lock_file = LockFile::open(&PathBuf::from(BORING_SSL_PATH).join(".patch_lock"))?; + + if lock_file.try_lock().is_err() { + return Ok(()); + } + + apply_feature_patches() +} + +fn apply_feature_patches() -> io::Result<()> { + let mut cmd = Command::new("git"); + + cmd.args(["reset", "--hard"]) + .current_dir(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(BORING_SSL_PATH)); + + run_command(&mut cmd)?; + + if cfg!(feature = "post-quantum-crypto") { + println!("cargo:warning=applying post quantum crypto patch to boringssl"); + run_apply_patch_script("scripts/apply_pq_patch.sh", "")?; + } + + if cfg!(feature = "rpk") { + println!("cargo:warning=applying RPK patch to boringssl"); + run_apply_patch_script("scripts/apply_rpk_patch.sh", "src")?; + } + + Ok(()) +} + +fn run_command(command: &mut Command) -> io::Result<()> { + let exit_status = command.spawn()?.wait()?; + + if !exit_status.success() { + let err = match exit_status.code() { + Some(code) => format!("{:?} exited with status: {}", command, code), + None => format!("{:?} was terminated by signal", command), + }; + + return Err(io::Error::new(io::ErrorKind::Other, err)); + } + + Ok(()) +} + +fn run_apply_patch_script( + script_path: impl AsRef, + from_dir: impl AsRef, +) -> io::Result<()> { + let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + + let src_path = manifest_dir + .join(BORING_SSL_PATH) + .join(from_dir) + .canonicalize()?; + + let cmd_path = manifest_dir.join(script_path).canonicalize()?; + + let mut cmd = Command::new(cmd_path); + cmd.current_dir(src_path); + run_command(&mut cmd)?; + + Ok(()) +} +fn main() { println!("cargo:rerun-if-env-changed=BORING_BSSL_PATH"); - let bssl_dir = std::env::var("BORING_BSSL_PATH").unwrap_or_else(|_| { - if !Path::new(BORING_SSL_PATH).join("CMakeLists.txt").exists() { - println!("cargo:warning=fetching boringssl git submodule"); - // fetch the boringssl submodule - let status = Command::new("git") - .args([ - "submodule", - "update", - "--init", - "--recursive", - BORING_SSL_PATH, - ]) - .status(); - if !status.map_or(false, |status| status.success()) { - panic!("failed to fetch submodule - consider running `git submodule update --init --recursive deps/boringssl` yourself"); - } + + if !Path::new(BORING_SSL_PATH).join("CMakeLists.txt").exists() { + println!("cargo:warning=fetching boringssl git submodule"); + // fetch the boringssl submodule + let status = Command::new("git") + .args([ + "submodule", + "update", + "--init", + "--recursive", + BORING_SSL_PATH, + ]) + .status(); + + if !status.map_or(false, |status| status.success()) { + panic!("failed to fetch submodule - consider running `git submodule update --init --recursive deps/boringssl` yourself"); } + } + ensure_patches_applied().unwrap(); + + let bssl_dir = std::env::var("BORING_BSSL_PATH").unwrap_or_else(|_| { let mut cfg = get_boringssl_cmake_config(); if cfg!(feature = "fuzzing") { @@ -385,12 +456,6 @@ fn main() { println!("cargo:rustc-link-lib=static=crypto"); println!("cargo:rustc-link-lib=static=ssl"); - // MacOS: Allow cdylib to link with undefined symbols - let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); - if target_os == "macos" { - println!("cargo:rustc-cdylib-link-arg=-Wl,-undefined,dynamic_lookup"); - } - println!("cargo:rerun-if-env-changed=BORING_BSSL_INCLUDE_PATH"); let include_path = std::env::var("BORING_BSSL_INCLUDE_PATH").unwrap_or_else(|_| { if cfg!(feature = "fips") { diff --git a/boring-sys/patches/boring-pq.patch b/boring-sys/patches/boring-pq.patch new file mode 100644 index 00000000..d6cb459d --- /dev/null +++ b/boring-sys/patches/boring-pq.patch @@ -0,0 +1,3303 @@ +From d3f8e78ff611976f81cca75a2cfced25534960be Mon Sep 17 00:00:00 2001 +From: Bas Westerbaan +Date: Fri, 22 Jul 2022 16:43:48 +0200 +Subject: [PATCH] Add X25519Kyber{512,768}Draft00 key exchange + +Also adds temporary P256Kyber768Draft00 key exchange. + +Cf RTG-2076 RTG-2051 RTG-2508 +--- + BUILD.generated.bzl | 3 + + CMakeLists.txt | 2 + + sources.json | 5 +- + src/crypto/CMakeLists.txt | 2 + + src/crypto/kyber/kyber.c | 2252 +++++++++++++++++++++++++++++++++++ + src/crypto/kyber/kyber512.c | 5 + + src/crypto/kyber/kyber768.c | 4 + + src/crypto/obj/obj_dat.h | 18 +- + src/crypto/obj/obj_mac.num | 3 + + src/crypto/obj/objects.txt | 5 + + src/include/openssl/kyber.h | 109 ++ + src/include/openssl/nid.h | 9 + + src/include/openssl/ssl.h | 3 + + src/ssl/internal.h | 2 +- + src/ssl/ssl_key_share.cc | 423 +++++++ + src/ssl/ssl_test.cc | 21 +- + src/ssl/t1_lib.cc | 10 +- + src/tool/speed.cc | 113 ++ + 18 files changed, 2981 insertions(+), 8 deletions(-) + create mode 100644 src/crypto/kyber/kyber.c + create mode 100644 src/crypto/kyber/kyber512.c + create mode 100644 src/crypto/kyber/kyber768.c + create mode 100644 src/include/openssl/kyber.h + +diff --git a/BUILD.generated.bzl b/BUILD.generated.bzl +index 6aba1a22a..0c99721df 100644 +--- a/BUILD.generated.bzl ++++ b/BUILD.generated.bzl +@@ -168,6 +168,7 @@ crypto_headers = [ + "src/include/openssl/hmac.h", + "src/include/openssl/hrss.h", + "src/include/openssl/is_boringssl.h", ++ "src/include/openssl/kyber.h", + "src/include/openssl/lhash.h", + "src/include/openssl/md4.h", + "src/include/openssl/md5.h", +@@ -370,6 +371,8 @@ crypto_sources = [ + "src/crypto/hkdf/hkdf.c", + "src/crypto/hpke/hpke.c", + "src/crypto/hrss/hrss.c", ++ "src/crypto/kyber/kyber512.c", ++ "src/crypto/kyber/kyber768.c", + "src/crypto/lhash/lhash.c", + "src/crypto/mem.c", + "src/crypto/obj/obj.c", +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 1645a264a..f174f9f56 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -471,6 +471,8 @@ add_library( + src/crypto/hkdf/hkdf.c + src/crypto/hpke/hpke.c + src/crypto/hrss/hrss.c ++ src/crypto/kyber/kyber512.c ++ src/crypto/kyber/kyber768.c + src/crypto/lhash/lhash.c + src/crypto/mem.c + src/crypto/obj/obj.c +diff --git a/sources.json b/sources.json +index 584a816d8..b06ae8363 100644 +--- a/sources.json ++++ b/sources.json +@@ -114,6 +114,8 @@ + "src/crypto/hkdf/hkdf.c", + "src/crypto/hpke/hpke.c", + "src/crypto/hrss/hrss.c", ++ "src/crypto/kyber/kyber512.c", ++ "src/crypto/kyber/kyber768.c", + "src/crypto/lhash/lhash.c", + "src/crypto/mem.c", + "src/crypto/obj/obj.c", +@@ -276,6 +278,7 @@ + "src/include/openssl/hkdf.h", + "src/include/openssl/hmac.h", + "src/include/openssl/hrss.h", ++ "src/include/openssl/kyber.h", + "src/include/openssl/is_boringssl.h", + "src/include/openssl/lhash.h", + "src/include/openssl/md4.h", +@@ -971,4 +974,4 @@ + "urandom_test": [ + "src/crypto/fipsmodule/rand/urandom_test.cc" + ] +-} +\ No newline at end of file ++} +diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt +index cde92b591..294ae42e7 100644 +--- a/src/crypto/CMakeLists.txt ++++ b/src/crypto/CMakeLists.txt +@@ -307,6 +307,8 @@ add_library( + hkdf/hkdf.c + hpke/hpke.c + hrss/hrss.c ++ kyber/kyber512.c ++ kyber/kyber768.c + lhash/lhash.c + mem.c + obj/obj.c +diff --git a/src/crypto/kyber/kyber.c b/src/crypto/kyber/kyber.c +new file mode 100644 +index 000000000..346d4daec +--- /dev/null ++++ b/src/crypto/kyber/kyber.c +@@ -0,0 +1,2252 @@ ++// Taken from round 3 public domain reference implementation ++// ++// https://github.com/pq-crystals/kyber ++// 8e00ec73035147d18b27d06048dff322f8de1f29 ++// ++// with some small modifications: ++// ++// - Merged into one file. ++// - Removed 90s version. ++// - Seeds are passed as paramters. ++// - Changed the API to be more BoringSSL-like ++// ++// TODO ++// ++// - Optimizations ++// ++// The majority of Kyber's time is spent in keccak: generating the matrix ++// A, hashing the public key, et cetera. This can be sped up dramatically ++// by using a multiway keccak implementation such as f1600x4 on AVX2. ++// ++// Also the NTT and other operations can be sped up with SIMD. This is ++// more complex and the gains are more modest. See the avx2 reference ++// implementation or https://github.com/cloudflare/circl/tree/main/pke/kyber ++// ++// - Option to keep A stored in private key. ++ ++#ifndef KYBER_K ++#error "Don't compile this file direcly" ++#endif ++ ++#include ++#include ++ ++#include ++#include ++#include ++ ++#if (KYBER_K == 2) ++#define KYBER_NAMESPACE(s) KYBER512_##s ++#elif (KYBER_K == 3) ++#define KYBER_NAMESPACE(s) KYBER768_##s ++#elif (KYBER_K == 4) ++#define KYBER_NAMESPACE(s) KYBER1024_##s ++#else ++#error "KYBER_K must be in {2,3,4}" ++#endif ++ ++#define public_key KYBER_NAMESPACE(public_key) ++#define private_key KYBER_NAMESPACE(private_key) ++ ++#define generate_key KYBER_NAMESPACE(generate_key) ++#define encap KYBER_NAMESPACE(encap) ++#define decap KYBER_NAMESPACE(decap) ++#define marshal_public_key KYBER_NAMESPACE(marshal_public_key) ++#define parse_public_key KYBER_NAMESPACE(parse_public_key) ++ ++ ++// ++// params.h ++// ++#define KYBER_N 256 ++#define KYBER_Q 3329 ++ ++#define KYBER_SYMBYTES 32 /* size in bytes of hashes, and seeds */ ++#define KYBER_SSBYTES 32 /* size in bytes of shared key */ ++ ++#define KYBER_POLYBYTES 384 ++#define KYBER_POLYVECBYTES (KYBER_K * KYBER_POLYBYTES) ++ ++#if KYBER_K == 2 ++#define KYBER_ETA1 3 ++#define KYBER_POLYCOMPRESSEDBYTES 128 ++#define KYBER_POLYVECCOMPRESSEDBYTES (KYBER_K * 320) ++#elif KYBER_K == 3 ++#define KYBER_ETA1 2 ++#define KYBER_POLYCOMPRESSEDBYTES 128 ++#define KYBER_POLYVECCOMPRESSEDBYTES (KYBER_K * 320) ++#elif KYBER_K == 4 ++#define KYBER_ETA1 2 ++#define KYBER_POLYCOMPRESSEDBYTES 160 ++#define KYBER_POLYVECCOMPRESSEDBYTES (KYBER_K * 352) ++#endif ++ ++#define KYBER_ETA2 2 ++ ++#define KYBER_INDCPA_MSGBYTES (KYBER_SYMBYTES) ++#define KYBER_INDCPA_PUBLICKEYBYTES (KYBER_POLYVECBYTES + KYBER_SYMBYTES) ++#define KYBER_INDCPA_SECRETKEYBYTES (KYBER_POLYVECBYTES) ++#define KYBER_INDCPA_BYTES (KYBER_POLYVECCOMPRESSEDBYTES + KYBER_POLYCOMPRESSEDBYTES) ++ ++#define KYBER_PUBLICKEYBYTES (KYBER_INDCPA_PUBLICKEYBYTES) ++/* 32 bytes of additional space to save H(pk) */ ++#define KYBER_SECRETKEYBYTES (KYBER_INDCPA_SECRETKEYBYTES + KYBER_INDCPA_PUBLICKEYBYTES + 2*KYBER_SYMBYTES) ++#define KYBER_CIPHERTEXTBYTES (KYBER_INDCPA_BYTES) ++ ++// ++// verify.h ++// ++static int verify(const uint8_t *a, const uint8_t *b, size_t len); ++static void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b); ++ ++// ++// reduce.h ++// ++#define MONT -1044 // 2^16 mod q ++#define QINV -3327 // q^-1 mod 2^16 ++ ++static int16_t montgomery_reduce(int32_t a); ++static int16_t barrett_reduce(int16_t a); ++ ++// ++// ntt.h ++// ++static void ntt(int16_t poly[256]); ++static void invntt(int16_t poly[256]); ++static void basemul(int16_t r[2], const int16_t a[2], const int16_t b[2], int16_t zeta); ++ ++// ++// poly.h ++// ++ ++/* ++ * Elements of R_q = Z_q[X]/(X^n + 1). Represents polynomial ++ * coeffs[0] + X*coeffs[1] + X^2*xoeffs[2] + ... + X^{n-1}*coeffs[n-1] ++ */ ++typedef struct{ ++ int16_t coeffs[KYBER_N]; ++} poly; ++ ++static void poly_compress(uint8_t r[KYBER_POLYCOMPRESSEDBYTES], const poly *a); ++static void poly_decompress(poly *r, const uint8_t a[KYBER_POLYCOMPRESSEDBYTES]); ++ ++static void poly_tobytes(uint8_t r[KYBER_POLYBYTES], const poly *a); ++static void poly_frombytes(poly *r, const uint8_t a[KYBER_POLYBYTES]); ++ ++static void poly_frommsg(poly *r, const uint8_t msg[KYBER_INDCPA_MSGBYTES]); ++static void poly_tomsg(uint8_t msg[KYBER_INDCPA_MSGBYTES], const poly *r); ++ ++static void poly_getnoise_eta1(poly *r, const uint8_t seed[KYBER_SYMBYTES], uint8_t nonce); ++static void poly_getnoise_eta2(poly *r, const uint8_t seed[KYBER_SYMBYTES], uint8_t nonce); ++ ++static void poly_ntt(poly *r); ++static void poly_invntt_tomont(poly *r); ++static void poly_basemul_montgomery(poly *r, const poly *a, const poly *b); ++static void poly_tomont(poly *r); ++ ++static void poly_reduce(poly *r); ++ ++static void poly_add(poly *r, const poly *a, const poly *b); ++static void poly_sub(poly *r, const poly *a, const poly *b); ++ ++// ++// cbd.h ++// ++static void poly_cbd_eta1(poly *r, const uint8_t buf[KYBER_ETA1*KYBER_N/4]); ++static void poly_cbd_eta2(poly *r, const uint8_t buf[KYBER_ETA2*KYBER_N/4]); ++ ++// ++// polyvec.h ++// ++ ++typedef struct{ ++ poly vec[KYBER_K]; ++} polyvec; ++ ++static void polyvec_compress(uint8_t r[KYBER_POLYVECCOMPRESSEDBYTES], const polyvec *a); ++static void polyvec_decompress(polyvec *r, const uint8_t a[KYBER_POLYVECCOMPRESSEDBYTES]); ++ ++static void polyvec_tobytes(uint8_t r[KYBER_POLYVECBYTES], const polyvec *a); ++static void polyvec_frombytes(polyvec *r, const uint8_t a[KYBER_POLYVECBYTES]); ++ ++static void polyvec_ntt(polyvec *r); ++static void polyvec_invntt_tomont(polyvec *r); ++ ++static void polyvec_basemul_acc_montgomery(poly *r, const polyvec *a, const polyvec *b); ++ ++static void polyvec_reduce(polyvec *r); ++ ++static void polyvec_add(polyvec *r, const polyvec *a, const polyvec *b); ++ ++// ++// indcpa.h ++// ++ ++static void gen_matrix(polyvec *a, const uint8_t seed[KYBER_SYMBYTES], int transposed); ++static void indcpa_keypair(uint8_t pk[KYBER_INDCPA_PUBLICKEYBYTES], ++ uint8_t sk[KYBER_INDCPA_SECRETKEYBYTES], ++ const uint8_t seed[KYBER_SYMBYTES]); ++ ++static void 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]); ++ ++static void indcpa_dec(uint8_t m[KYBER_INDCPA_MSGBYTES], ++ const uint8_t c[KYBER_INDCPA_BYTES], ++ const uint8_t sk[KYBER_INDCPA_SECRETKEYBYTES]); ++ ++// ++// 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(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; ++ ++static void kyber_shake128_absorb(keccak_state *s, ++ const uint8_t seed[KYBER_SYMBYTES], ++ uint8_t x, ++ uint8_t y); ++ ++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) ++#define hash_g(OUT, IN, INBYTES) sha3_512(OUT, IN, INBYTES) ++#define xof_absorb(STATE, SEED, X, Y) kyber_shake128_absorb(STATE, SEED, X, Y) ++#define xof_squeezeblocks(OUT, OUTBLOCKS, STATE) shake128_squeezeblocks(OUT, OUTBLOCKS, STATE) ++#define prf(OUT, OUTBYTES, KEY, NONCE) kyber_shake256_prf(OUT, OUTBYTES, KEY, NONCE) ++#define kdf(OUT, IN, INBYTES) shake256(OUT, KYBER_SSBYTES, IN, INBYTES) ++ ++ ++// ++// verify.c ++// ++ ++/************************************************* ++* Name: verify ++* ++* Description: Compare two arrays for equality in constant time. ++* ++* Arguments: const uint8_t *a: pointer to first byte array ++* const uint8_t *b: pointer to second byte array ++* size_t len: length of the byte arrays ++* ++* Returns 0 if the byte arrays are equal, 1 otherwise ++**************************************************/ ++static int verify(const uint8_t *a, const uint8_t *b, size_t len) ++{ ++ size_t i; ++ uint8_t r = 0; ++ ++ for(i=0;i> 63; ++} ++ ++/************************************************* ++* Name: cmov ++* ++* Description: Copy len bytes from x to r if b is 1; ++* don't modify x if b is 0. Requires b to be in {0,1}; ++* assumes two's complement representation of negative integers. ++* Runs in constant time. ++* ++* Arguments: uint8_t *r: pointer to output byte array ++* const uint8_t *x: pointer to input byte array ++* size_t len: Amount of bytes to be copied ++* uint8_t b: Condition bit; has to be in {0,1} ++**************************************************/ ++static void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b) ++{ ++ size_t i; ++ ++ b = -b; ++ for(i=0;i> 16; ++ return t; ++} ++ ++/************************************************* ++* Name: barrett_reduce ++* ++* Description: Barrett reduction; given a 16-bit integer a, computes ++* centered representative congruent to a mod q in {-(q-1)/2,...,(q-1)/2} ++* ++* Arguments: - int16_t a: input integer to be reduced ++* ++* Returns: integer in {-(q-1)/2,...,(q-1)/2} congruent to a modulo q. ++**************************************************/ ++static int16_t barrett_reduce(int16_t a) { ++ int16_t t; ++ const int16_t v = ((1<<26) + KYBER_Q/2)/KYBER_Q; ++ ++ t = ((int32_t)v*a + (1<<25)) >> 26; ++ t *= KYBER_Q; ++ return a - t; ++} ++ ++// ++// cbd.c ++// ++ ++/************************************************* ++* Name: load32_littleendian ++* ++* Description: load 4 bytes into a 32-bit integer ++* in little-endian order ++* ++* Arguments: - const uint8_t *x: pointer to input byte array ++* ++* Returns 32-bit unsigned integer loaded from x ++**************************************************/ ++static uint32_t load32_littleendian(const uint8_t x[4]) ++{ ++ uint32_t r; ++ r = (uint32_t)x[0]; ++ r |= (uint32_t)x[1] << 8; ++ r |= (uint32_t)x[2] << 16; ++ r |= (uint32_t)x[3] << 24; ++ return r; ++} ++ ++/************************************************* ++* Name: load24_littleendian ++* ++* Description: load 3 bytes into a 32-bit integer ++* in little-endian order. ++* This function is only needed for Kyber-512 ++* ++* Arguments: - const uint8_t *x: pointer to input byte array ++* ++* Returns 32-bit unsigned integer loaded from x (most significant byte is zero) ++**************************************************/ ++#if KYBER_ETA1 == 3 ++static uint32_t load24_littleendian(const uint8_t x[3]) ++{ ++ uint32_t r; ++ r = (uint32_t)x[0]; ++ r |= (uint32_t)x[1] << 8; ++ r |= (uint32_t)x[2] << 16; ++ return r; ++} ++#endif ++ ++ ++/************************************************* ++* Name: cbd2 ++* ++* Description: Given an array of uniformly random bytes, compute ++* polynomial with coefficients distributed according to ++* a centered binomial distribution with parameter eta=2 ++* ++* Arguments: - poly *r: pointer to output polynomial ++* - const uint8_t *buf: pointer to input byte array ++**************************************************/ ++static void cbd2(poly *r, const uint8_t buf[2*KYBER_N/4]) ++{ ++ unsigned int i,j; ++ uint32_t t,d; ++ int16_t a,b; ++ ++ for(i=0;i>1) & 0x55555555; ++ ++ for(j=0;j<8;j++) { ++ a = (d >> (4*j+0)) & 0x3; ++ b = (d >> (4*j+2)) & 0x3; ++ r->coeffs[8*i+j] = a - b; ++ } ++ } ++} ++ ++/************************************************* ++* Name: cbd3 ++* ++* Description: Given an array of uniformly random bytes, compute ++* polynomial with coefficients distributed according to ++* a centered binomial distribution with parameter eta=3. ++* This function is only needed for Kyber-512 ++* ++* Arguments: - poly *r: pointer to output polynomial ++* - const uint8_t *buf: pointer to input byte array ++**************************************************/ ++#if KYBER_ETA1 == 3 ++static void cbd3(poly *r, const uint8_t buf[3*KYBER_N/4]) ++{ ++ unsigned int i,j; ++ uint32_t t,d; ++ int16_t a,b; ++ ++ for(i=0;i>1) & 0x00249249; ++ d += (t>>2) & 0x00249249; ++ ++ for(j=0;j<4;j++) { ++ a = (d >> (6*j+0)) & 0x7; ++ b = (d >> (6*j+3)) & 0x7; ++ r->coeffs[4*i+j] = a - b; ++ } ++ } ++} ++#endif ++ ++static void poly_cbd_eta1(poly *r, const uint8_t buf[KYBER_ETA1*KYBER_N/4]) ++{ ++#if KYBER_ETA1 == 2 ++ cbd2(r, buf); ++#elif KYBER_ETA1 == 3 ++ cbd3(r, buf); ++#else ++#error "This implementation requires eta1 in {2,3}" ++#endif ++} ++ ++static void poly_cbd_eta2(poly *r, const uint8_t buf[KYBER_ETA2*KYBER_N/4]) ++{ ++#if KYBER_ETA2 == 2 ++ cbd2(r, buf); ++#else ++#error "This implementation requires eta2 = 2" ++#endif ++} ++ ++// ++// ntt.c ++// ++ ++/* Code to generate zetas and zetas_inv used in the number-theoretic transform: ++ ++#define KYBER_ROOT_OF_UNITY 17 ++ ++static const uint8_t tree[128] = { ++ 0, 64, 32, 96, 16, 80, 48, 112, 8, 72, 40, 104, 24, 88, 56, 120, ++ 4, 68, 36, 100, 20, 84, 52, 116, 12, 76, 44, 108, 28, 92, 60, 124, ++ 2, 66, 34, 98, 18, 82, 50, 114, 10, 74, 42, 106, 26, 90, 58, 122, ++ 6, 70, 38, 102, 22, 86, 54, 118, 14, 78, 46, 110, 30, 94, 62, 126, ++ 1, 65, 33, 97, 17, 81, 49, 113, 9, 73, 41, 105, 25, 89, 57, 121, ++ 5, 69, 37, 101, 21, 85, 53, 117, 13, 77, 45, 109, 29, 93, 61, 125, ++ 3, 67, 35, 99, 19, 83, 51, 115, 11, 75, 43, 107, 27, 91, 59, 123, ++ 7, 71, 39, 103, 23, 87, 55, 119, 15, 79, 47, 111, 31, 95, 63, 127 ++}; ++ ++void init_ntt() { ++ unsigned int i; ++ int16_t tmp[128]; ++ ++ tmp[0] = MONT; ++ for(i=1;i<128;i++) ++ tmp[i] = fqmul(tmp[i-1],MONT*KYBER_ROOT_OF_UNITY % KYBER_Q); ++ ++ for(i=0;i<128;i++) { ++ zetas[i] = tmp[tree[i]]; ++ if(zetas[i] > KYBER_Q/2) ++ zetas[i] -= KYBER_Q; ++ if(zetas[i] < -KYBER_Q/2) ++ zetas[i] += KYBER_Q; ++ } ++} ++*/ ++ ++static const int16_t zetas[128] = { ++ -1044, -758, -359, -1517, 1493, 1422, 287, 202, ++ -171, 622, 1577, 182, 962, -1202, -1474, 1468, ++ 573, -1325, 264, 383, -829, 1458, -1602, -130, ++ -681, 1017, 732, 608, -1542, 411, -205, -1571, ++ 1223, 652, -552, 1015, -1293, 1491, -282, -1544, ++ 516, -8, -320, -666, -1618, -1162, 126, 1469, ++ -853, -90, -271, 830, 107, -1421, -247, -951, ++ -398, 961, -1508, -725, 448, -1065, 677, -1275, ++ -1103, 430, 555, 843, -1251, 871, 1550, 105, ++ 422, 587, 177, -235, -291, -460, 1574, 1653, ++ -246, 778, 1159, -147, -777, 1483, -602, 1119, ++ -1590, 644, -872, 349, 418, 329, -156, -75, ++ 817, 1097, 603, 610, 1322, -1285, -1465, 384, ++ -1215, -136, 1218, -1335, -874, 220, -1187, -1659, ++ -1185, -1530, -1278, 794, -1510, -854, -870, 478, ++ -108, -308, 996, 991, 958, -1460, 1522, 1628 ++}; ++ ++/************************************************* ++* Name: fqmul ++* ++* Description: Multiplication followed by Montgomery reduction ++* ++* Arguments: - int16_t a: first factor ++* - int16_t b: second factor ++* ++* Returns 16-bit integer congruent to a*b*R^{-1} mod q ++**************************************************/ ++static int16_t fqmul(int16_t a, int16_t b) { ++ return montgomery_reduce((int32_t)a*b); ++} ++ ++/************************************************* ++* Name: ntt ++* ++* Description: Inplace number-theoretic transform (NTT) in Rq. ++* input is in standard order, output is in bitreversed order ++* ++* Arguments: - int16_t r[256]: pointer to input/output vector of elements of Zq ++**************************************************/ ++static void ntt(int16_t r[256]) { ++ unsigned int len, start, j, k; ++ int16_t t, zeta; ++ ++ k = 1; ++ for(len = 128; len >= 2; len >>= 1) { ++ for(start = 0; start < 256; start = j + len) { ++ zeta = zetas[k++]; ++ for(j = start; j < start + len; j++) { ++ t = fqmul(zeta, r[j + len]); ++ r[j + len] = r[j] - t; ++ r[j] = r[j] + t; ++ } ++ } ++ } ++} ++ ++/************************************************* ++* Name: invntt_tomont ++* ++* Description: Inplace inverse number-theoretic transform in Rq and ++* multiplication by Montgomery factor 2^16. ++* Input is in bitreversed order, output is in standard order ++* ++* Arguments: - int16_t r[256]: pointer to input/output vector of elements of Zq ++**************************************************/ ++static void invntt(int16_t r[256]) { ++ unsigned int start, len, j, k; ++ int16_t t, zeta; ++ const int16_t f = 1441; // mont^2/128 ++ ++ k = 127; ++ for(len = 2; len <= 128; len <<= 1) { ++ for(start = 0; start < 256; start = j + len) { ++ zeta = zetas[k--]; ++ for(j = start; j < start + len; j++) { ++ t = r[j]; ++ r[j] = barrett_reduce(t + r[j + len]); ++ r[j + len] = r[j + len] - t; ++ r[j + len] = fqmul(zeta, r[j + len]); ++ } ++ } ++ } ++ ++ for(j = 0; j < 256; j++) ++ r[j] = fqmul(r[j], f); ++} ++ ++/************************************************* ++* Name: basemul ++* ++* Description: Multiplication of polynomials in Zq[X]/(X^2-zeta) ++* used for multiplication of elements in Rq in NTT domain ++* ++* Arguments: - int16_t r[2]: pointer to the output polynomial ++* - const int16_t a[2]: pointer to the first factor ++* - const int16_t b[2]: pointer to the second factor ++* - int16_t zeta: integer defining the reduction polynomial ++**************************************************/ ++static void basemul(int16_t r[2], const int16_t a[2], const int16_t b[2], int16_t zeta) ++{ ++ r[0] = fqmul(a[1], b[1]); ++ r[0] = fqmul(r[0], zeta); ++ r[0] += fqmul(a[0], b[0]); ++ r[1] = fqmul(a[0], b[1]); ++ r[1] += fqmul(a[1], b[0]); ++} ++ ++// ++// poly.c ++// ++ ++/************************************************* ++* Name: poly_compress ++* ++* Description: Compression and subsequent serialization of a polynomial ++* ++* Arguments: - uint8_t *r: pointer to output byte array ++* (of length KYBER_POLYCOMPRESSEDBYTES) ++* - const poly *a: pointer to input polynomial ++**************************************************/ ++static void poly_compress(uint8_t r[KYBER_POLYCOMPRESSEDBYTES], const poly *a) ++{ ++ unsigned int i,j; ++ int16_t u; ++ uint8_t t[8]; ++ ++#if (KYBER_POLYCOMPRESSEDBYTES == 128) ++ for(i=0;icoeffs[8*i+j]; ++ u += (u >> 15) & KYBER_Q; ++ t[j] = ((((uint16_t)u << 4) + KYBER_Q/2)/KYBER_Q) & 15; ++ } ++ ++ r[0] = t[0] | (t[1] << 4); ++ r[1] = t[2] | (t[3] << 4); ++ r[2] = t[4] | (t[5] << 4); ++ r[3] = t[6] | (t[7] << 4); ++ r += 4; ++ } ++#elif (KYBER_POLYCOMPRESSEDBYTES == 160) ++ for(i=0;icoeffs[8*i+j]; ++ u += (u >> 15) & KYBER_Q; ++ t[j] = ((((uint32_t)u << 5) + KYBER_Q/2)/KYBER_Q) & 31; ++ } ++ ++ r[0] = (t[0] >> 0) | (t[1] << 5); ++ r[1] = (t[1] >> 3) | (t[2] << 2) | (t[3] << 7); ++ r[2] = (t[3] >> 1) | (t[4] << 4); ++ r[3] = (t[4] >> 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 ++} ++ ++/************************************************* ++* Name: poly_decompress ++* ++* Description: De-serialization and subsequent decompression of a polynomial; ++* approximate inverse of poly_compress ++* ++* Arguments: - poly *r: pointer to output polynomial ++* - const uint8_t *a: pointer to input byte array ++* (of length KYBER_POLYCOMPRESSEDBYTES bytes) ++**************************************************/ ++static void poly_decompress(poly *r, const uint8_t a[KYBER_POLYCOMPRESSEDBYTES]) ++{ ++ unsigned int i; ++ ++#if (KYBER_POLYCOMPRESSEDBYTES == 128) ++ for(i=0;icoeffs[2*i+0] = (((uint16_t)(a[0] & 15)*KYBER_Q) + 8) >> 4; ++ r->coeffs[2*i+1] = (((uint16_t)(a[0] >> 4)*KYBER_Q) + 8) >> 4; ++ a += 1; ++ } ++#elif (KYBER_POLYCOMPRESSEDBYTES == 160) ++ unsigned int j; ++ uint8_t t[8]; ++ for(i=0;i> 0); ++ t[1] = (a[0] >> 5) | (a[1] << 3); ++ t[2] = (a[1] >> 2); ++ t[3] = (a[1] >> 7) | (a[2] << 1); ++ t[4] = (a[2] >> 4) | (a[3] << 4); ++ t[5] = (a[3] >> 1); ++ t[6] = (a[3] >> 6) | (a[4] << 2); ++ t[7] = (a[4] >> 3); ++ a += 5; ++ ++ for(j=0;j<8;j++) ++ r->coeffs[8*i+j] = ((uint32_t)(t[j] & 31)*KYBER_Q + 16) >> 5; ++ } ++#else ++#error "KYBER_POLYCOMPRESSEDBYTES needs to be in {128, 160}" ++#endif ++} ++ ++/************************************************* ++* Name: poly_tobytes ++* ++* Description: Serialization of a polynomial ++* ++* Arguments: - uint8_t *r: pointer to output byte array ++* (needs space for KYBER_POLYBYTES bytes) ++* - const poly *a: pointer to input polynomial ++**************************************************/ ++static void poly_tobytes(uint8_t r[KYBER_POLYBYTES], const poly *a) ++{ ++ unsigned int i; ++ uint16_t t0, t1; ++ ++ for(i=0;icoeffs[2*i]; ++ t0 += ((int16_t)t0 >> 15) & KYBER_Q; ++ t1 = a->coeffs[2*i+1]; ++ t1 += ((int16_t)t1 >> 15) & KYBER_Q; ++ r[3*i+0] = (t0 >> 0); ++ r[3*i+1] = (t0 >> 8) | (t1 << 4); ++ r[3*i+2] = (t1 >> 4); ++ } ++} ++ ++/************************************************* ++* Name: poly_frombytes ++* ++* Description: De-serialization of a polynomial; ++* inverse of poly_tobytes ++* ++* Arguments: - poly *r: pointer to output polynomial ++* - const uint8_t *a: pointer to input byte array ++* (of KYBER_POLYBYTES bytes) ++**************************************************/ ++static void poly_frombytes(poly *r, const uint8_t a[KYBER_POLYBYTES]) ++{ ++ unsigned int i; ++ for(i=0;icoeffs[2*i] = ((a[3*i+0] >> 0) | ((uint16_t)a[3*i+1] << 8)) & 0xFFF; ++ r->coeffs[2*i+1] = ((a[3*i+1] >> 4) | ((uint16_t)a[3*i+2] << 4)) & 0xFFF; ++ } ++} ++ ++/************************************************* ++* Name: poly_frommsg ++* ++* Description: Convert 32-byte message to polynomial ++* ++* Arguments: - poly *r: pointer to output polynomial ++* - const uint8_t *msg: pointer to input message ++**************************************************/ ++static void poly_frommsg(poly *r, const uint8_t msg[KYBER_INDCPA_MSGBYTES]) ++{ ++ unsigned int i,j; ++ int16_t mask; ++ ++#if (KYBER_INDCPA_MSGBYTES != KYBER_N/8) ++#error "KYBER_INDCPA_MSGBYTES must be equal to KYBER_N/8 bytes!" ++#endif ++ ++ for(i=0;i> j)&1); ++ r->coeffs[8*i+j] = mask & ((KYBER_Q+1)/2); ++ } ++ } ++} ++ ++/************************************************* ++* Name: poly_tomsg ++* ++* Description: Convert polynomial to 32-byte message ++* ++* Arguments: - uint8_t *msg: pointer to output message ++* - const poly *a: pointer to input polynomial ++**************************************************/ ++static void poly_tomsg(uint8_t msg[KYBER_INDCPA_MSGBYTES], const poly *a) ++{ ++ unsigned int i,j; ++ uint16_t t; ++ ++ for(i=0;icoeffs[8*i+j]; ++ t += ((int16_t)t >> 15) & KYBER_Q; ++ t = (((t << 1) + KYBER_Q/2)/KYBER_Q) & 1; ++ msg[i] |= t << j; ++ } ++ } ++} ++ ++/************************************************* ++* Name: poly_getnoise_eta1 ++* ++* Description: Sample a polynomial deterministically from a seed and a nonce, ++* with output polynomial close to centered binomial distribution ++* with parameter KYBER_ETA1 ++* ++* Arguments: - poly *r: pointer to output polynomial ++* - const uint8_t *seed: pointer to input seed ++* (of length KYBER_SYMBYTES bytes) ++* - uint8_t nonce: one-byte input nonce ++**************************************************/ ++static void poly_getnoise_eta1(poly *r, const uint8_t seed[KYBER_SYMBYTES], uint8_t nonce) ++{ ++ uint8_t buf[KYBER_ETA1*KYBER_N/4]; ++ prf(buf, sizeof(buf), seed, nonce); ++ poly_cbd_eta1(r, buf); ++} ++ ++/************************************************* ++* Name: poly_getnoise_eta2 ++* ++* Description: Sample a polynomial deterministically from a seed and a nonce, ++* with output polynomial close to centered binomial distribution ++* with parameter KYBER_ETA2 ++* ++* Arguments: - poly *r: pointer to output polynomial ++* - const uint8_t *seed: pointer to input seed ++* (of length KYBER_SYMBYTES bytes) ++* - uint8_t nonce: one-byte input nonce ++**************************************************/ ++static void poly_getnoise_eta2(poly *r, const uint8_t seed[KYBER_SYMBYTES], uint8_t nonce) ++{ ++ uint8_t buf[KYBER_ETA2*KYBER_N/4]; ++ prf(buf, sizeof(buf), seed, nonce); ++ poly_cbd_eta2(r, buf); ++} ++ ++ ++/************************************************* ++* Name: poly_ntt ++* ++* Description: Computes negacyclic number-theoretic transform (NTT) of ++* a polynomial in place; ++* inputs assumed to be in normal order, output in bitreversed order ++* ++* Arguments: - uint16_t *r: pointer to in/output polynomial ++**************************************************/ ++static void poly_ntt(poly *r) ++{ ++ ntt(r->coeffs); ++ poly_reduce(r); ++} ++ ++/************************************************* ++* Name: poly_invntt_tomont ++* ++* Description: Computes inverse of negacyclic number-theoretic transform (NTT) ++* of a polynomial in place; ++* inputs assumed to be in bitreversed order, output in normal order ++* ++* Arguments: - uint16_t *a: pointer to in/output polynomial ++**************************************************/ ++static void poly_invntt_tomont(poly *r) ++{ ++ invntt(r->coeffs); ++} ++ ++/************************************************* ++* Name: poly_basemul_montgomery ++* ++* Description: Multiplication of two polynomials in NTT domain ++* ++* Arguments: - poly *r: pointer to output polynomial ++* - const poly *a: pointer to first input polynomial ++* - const poly *b: pointer to second input polynomial ++**************************************************/ ++static void poly_basemul_montgomery(poly *r, const poly *a, const poly *b) ++{ ++ unsigned int i; ++ for(i=0;icoeffs[4*i], &a->coeffs[4*i], &b->coeffs[4*i], zetas[64+i]); ++ basemul(&r->coeffs[4*i+2], &a->coeffs[4*i+2], &b->coeffs[4*i+2], -zetas[64+i]); ++ } ++} ++ ++/************************************************* ++* Name: poly_tomont ++* ++* Description: Inplace conversion of all coefficients of a polynomial ++* from normal domain to Montgomery domain ++* ++* Arguments: - poly *r: pointer to input/output polynomial ++**************************************************/ ++static void poly_tomont(poly *r) ++{ ++ unsigned int i; ++ const int16_t f = (1ULL << 32) % KYBER_Q; ++ for(i=0;icoeffs[i] = montgomery_reduce((int32_t)r->coeffs[i]*f); ++} ++ ++/************************************************* ++* Name: poly_reduce ++* ++* Description: Applies Barrett reduction to all coefficients of a polynomial ++* for details of the Barrett reduction see comments in reduce.c ++* ++* Arguments: - poly *r: pointer to input/output polynomial ++**************************************************/ ++static void poly_reduce(poly *r) ++{ ++ unsigned int i; ++ for(i=0;icoeffs[i] = barrett_reduce(r->coeffs[i]); ++} ++ ++/************************************************* ++* Name: poly_add ++* ++* Description: Add two polynomials; no modular reduction is performed ++* ++* Arguments: - poly *r: pointer to output polynomial ++* - const poly *a: pointer to first input polynomial ++* - const poly *b: pointer to second input polynomial ++**************************************************/ ++static void poly_add(poly *r, const poly *a, const poly *b) ++{ ++ unsigned int i; ++ for(i=0;icoeffs[i] = a->coeffs[i] + b->coeffs[i]; ++} ++ ++/************************************************* ++* Name: poly_sub ++* ++* Description: Subtract two polynomials; no modular reduction is performed ++* ++* Arguments: - poly *r: pointer to output polynomial ++* - const poly *a: pointer to first input polynomial ++* - const poly *b: pointer to second input polynomial ++**************************************************/ ++static void poly_sub(poly *r, const poly *a, const poly *b) ++{ ++ unsigned int i; ++ for(i=0;icoeffs[i] = a->coeffs[i] - b->coeffs[i]; ++} ++ ++// ++// polyvec.c ++// ++ ++/************************************************* ++* Name: polyvec_compress ++* ++* Description: Compress and serialize vector of polynomials ++* ++* Arguments: - uint8_t *r: pointer to output byte array ++* (needs space for KYBER_POLYVECCOMPRESSEDBYTES) ++* - const polyvec *a: pointer to input vector of polynomials ++**************************************************/ ++static void polyvec_compress(uint8_t r[KYBER_POLYVECCOMPRESSEDBYTES], const polyvec *a) ++{ ++ unsigned int i,j,k; ++ ++#if (KYBER_POLYVECCOMPRESSEDBYTES == (KYBER_K * 352)) ++ uint16_t t[8]; ++ for(i=0;ivec[i].coeffs[8*j+k]; ++ t[k] += ((int16_t)t[k] >> 15) & KYBER_Q; ++ t[k] = ((((uint32_t)t[k] << 11) + KYBER_Q/2)/KYBER_Q) & 0x7ff; ++ } ++ ++ r[ 0] = (t[0] >> 0); ++ r[ 1] = (t[0] >> 8) | (t[1] << 3); ++ r[ 2] = (t[1] >> 5) | (t[2] << 6); ++ r[ 3] = (t[2] >> 2); ++ r[ 4] = (t[2] >> 10) | (t[3] << 1); ++ r[ 5] = (t[3] >> 7) | (t[4] << 4); ++ r[ 6] = (t[4] >> 4) | (t[5] << 7); ++ r[ 7] = (t[5] >> 1); ++ r[ 8] = (t[5] >> 9) | (t[6] << 2); ++ r[ 9] = (t[6] >> 6) | (t[7] << 5); ++ r[10] = (t[7] >> 3); ++ r += 11; ++ } ++ } ++#elif (KYBER_POLYVECCOMPRESSEDBYTES == (KYBER_K * 320)) ++ uint16_t t[4]; ++ for(i=0;ivec[i].coeffs[4*j+k]; ++ t[k] += ((int16_t)t[k] >> 15) & KYBER_Q; ++ t[k] = ((((uint32_t)t[k] << 10) + KYBER_Q/2)/ KYBER_Q) & 0x3ff; ++ } ++ ++ r[0] = (t[0] >> 0); ++ r[1] = (t[0] >> 8) | (t[1] << 2); ++ r[2] = (t[1] >> 6) | (t[2] << 4); ++ r[3] = (t[2] >> 4) | (t[3] << 6); ++ r[4] = (t[3] >> 2); ++ r += 5; ++ } ++ } ++#else ++#error "KYBER_POLYVECCOMPRESSEDBYTES needs to be in {320*KYBER_K, 352*KYBER_K}" ++#endif ++} ++ ++/************************************************* ++* Name: polyvec_decompress ++* ++* Description: De-serialize and decompress vector of polynomials; ++* approximate inverse of polyvec_compress ++* ++* Arguments: - polyvec *r: pointer to output vector of polynomials ++* - const uint8_t *a: pointer to input byte array ++* (of length KYBER_POLYVECCOMPRESSEDBYTES) ++**************************************************/ ++static void polyvec_decompress(polyvec *r, const uint8_t a[KYBER_POLYVECCOMPRESSEDBYTES]) ++{ ++ unsigned int i,j,k; ++ ++#if (KYBER_POLYVECCOMPRESSEDBYTES == (KYBER_K * 352)) ++ uint16_t t[8]; ++ for(i=0;i> 0) | ((uint16_t)a[ 1] << 8); ++ t[1] = (a[1] >> 3) | ((uint16_t)a[ 2] << 5); ++ t[2] = (a[2] >> 6) | ((uint16_t)a[ 3] << 2) | ((uint16_t)a[4] << 10); ++ t[3] = (a[4] >> 1) | ((uint16_t)a[ 5] << 7); ++ t[4] = (a[5] >> 4) | ((uint16_t)a[ 6] << 4); ++ t[5] = (a[6] >> 7) | ((uint16_t)a[ 7] << 1) | ((uint16_t)a[8] << 9); ++ t[6] = (a[8] >> 2) | ((uint16_t)a[ 9] << 6); ++ t[7] = (a[9] >> 5) | ((uint16_t)a[10] << 3); ++ a += 11; ++ ++ for(k=0;k<8;k++) ++ r->vec[i].coeffs[8*j+k] = ((uint32_t)(t[k] & 0x7FF)*KYBER_Q + 1024) >> 11; ++ } ++ } ++#elif (KYBER_POLYVECCOMPRESSEDBYTES == (KYBER_K * 320)) ++ uint16_t t[4]; ++ for(i=0;i> 0) | ((uint16_t)a[1] << 8); ++ t[1] = (a[1] >> 2) | ((uint16_t)a[2] << 6); ++ t[2] = (a[2] >> 4) | ((uint16_t)a[3] << 4); ++ t[3] = (a[3] >> 6) | ((uint16_t)a[4] << 2); ++ a += 5; ++ ++ for(k=0;k<4;k++) ++ r->vec[i].coeffs[4*j+k] = ((uint32_t)(t[k] & 0x3FF)*KYBER_Q + 512) >> 10; ++ } ++ } ++#else ++#error "KYBER_POLYVECCOMPRESSEDBYTES needs to be in {320*KYBER_K, 352*KYBER_K}" ++#endif ++} ++ ++/************************************************* ++* Name: polyvec_tobytes ++* ++* Description: Serialize vector of polynomials ++* ++* Arguments: - uint8_t *r: pointer to output byte array ++* (needs space for KYBER_POLYVECBYTES) ++* - const polyvec *a: pointer to input vector of polynomials ++**************************************************/ ++static void polyvec_tobytes(uint8_t r[KYBER_POLYVECBYTES], const polyvec *a) ++{ ++ unsigned int i; ++ for(i=0;ivec[i]); ++} ++ ++/************************************************* ++* Name: polyvec_frombytes ++* ++* Description: De-serialize vector of polynomials; ++* inverse of polyvec_tobytes ++* ++* Arguments: - uint8_t *r: pointer to output byte array ++* - const polyvec *a: pointer to input vector of polynomials ++* (of length KYBER_POLYVECBYTES) ++**************************************************/ ++static void polyvec_frombytes(polyvec *r, const uint8_t a[KYBER_POLYVECBYTES]) ++{ ++ unsigned int i; ++ for(i=0;ivec[i], a+i*KYBER_POLYBYTES); ++} ++ ++/************************************************* ++* Name: polyvec_ntt ++* ++* Description: Apply forward NTT to all elements of a vector of polynomials ++* ++* Arguments: - polyvec *r: pointer to in/output vector of polynomials ++**************************************************/ ++static void polyvec_ntt(polyvec *r) ++{ ++ unsigned int i; ++ for(i=0;ivec[i]); ++} ++ ++/************************************************* ++* Name: polyvec_invntt_tomont ++* ++* Description: Apply inverse NTT to all elements of a vector of polynomials ++* and multiply by Montgomery factor 2^16 ++* ++* Arguments: - polyvec *r: pointer to in/output vector of polynomials ++**************************************************/ ++static void polyvec_invntt_tomont(polyvec *r) ++{ ++ unsigned int i; ++ for(i=0;ivec[i]); ++} ++ ++/************************************************* ++* Name: polyvec_basemul_acc_montgomery ++* ++* Description: Multiply elements of a and b in NTT domain, accumulate into r, ++* and multiply by 2^-16. ++* ++* Arguments: - poly *r: pointer to output polynomial ++* - const polyvec *a: pointer to first input vector of polynomials ++* - const polyvec *b: pointer to second input vector of polynomials ++**************************************************/ ++static void polyvec_basemul_acc_montgomery(poly *r, const polyvec *a, const polyvec *b) ++{ ++ unsigned int i; ++ poly t; ++ ++ poly_basemul_montgomery(r, &a->vec[0], &b->vec[0]); ++ for(i=1;ivec[i], &b->vec[i]); ++ poly_add(r, r, &t); ++ } ++ ++ poly_reduce(r); ++} ++ ++/************************************************* ++* Name: polyvec_reduce ++* ++* Description: Applies Barrett reduction to each coefficient ++* of each element of a vector of polynomials; ++* for details of the Barrett reduction see comments in reduce.c ++* ++* Arguments: - polyvec *r: pointer to input/output polynomial ++**************************************************/ ++static void polyvec_reduce(polyvec *r) ++{ ++ unsigned int i; ++ for(i=0;ivec[i]); ++} ++ ++/************************************************* ++* Name: polyvec_add ++* ++* Description: Add vectors of polynomials ++* ++* Arguments: - polyvec *r: pointer to output vector of polynomials ++* - const polyvec *a: pointer to first input vector of polynomials ++* - const polyvec *b: pointer to second input vector of polynomials ++**************************************************/ ++static void polyvec_add(polyvec *r, const polyvec *a, const polyvec *b) ++{ ++ unsigned int i; ++ for(i=0;ivec[i], &a->vec[i], &b->vec[i]); ++} ++ ++// ++// indcpa.c ++// ++ ++/************************************************* ++* Name: pack_pk ++* ++* Description: Serialize the public key as concatenation of the ++* serialized vector of polynomials pk ++* and the public seed used to generate the matrix A. ++* ++* Arguments: uint8_t *r: pointer to the output serialized public key ++* polyvec *pk: pointer to the input public-key polyvec ++* const uint8_t *seed: pointer to the input public seed ++**************************************************/ ++static void pack_pk(uint8_t r[KYBER_INDCPA_PUBLICKEYBYTES], ++ polyvec *pk, ++ const uint8_t seed[KYBER_SYMBYTES]) ++{ ++ size_t i; ++ polyvec_tobytes(r, pk); ++ for(i=0;i> 0) | ((uint16_t)buf[pos+1] << 8)) & 0xFFF; ++ val1 = ((buf[pos+1] >> 4) | ((uint16_t)buf[pos+2] << 4)) & 0xFFF; ++ pos += 3; ++ ++ if(val0 < KYBER_Q) ++ r[ctr++] = val0; ++ if(ctr < len && val1 < KYBER_Q) ++ r[ctr++] = val1; ++ } ++ ++ return ctr; ++} ++ ++#define gen_a(A,B) gen_matrix(A,B,0) ++#define gen_at(A,B) gen_matrix(A,B,1) ++ ++/************************************************* ++* Name: gen_matrix ++* ++* Description: Deterministically generate matrix A (or the transpose of A) ++* from a seed. Entries of the matrix are polynomials that look ++* uniformly random. Performs rejection sampling on output of ++* a XOF ++* ++* Arguments: - polyvec *a: pointer to ouptput matrix A ++* - const uint8_t *seed: pointer to input seed ++* - int transposed: boolean deciding whether A or A^T is generated ++**************************************************/ ++#define GEN_MATRIX_NBLOCKS ((12*KYBER_N/8*(1 << 12)/KYBER_Q + XOF_BLOCKBYTES)/XOF_BLOCKBYTES) ++// Not static for benchmarking ++static void gen_matrix(polyvec *a, const uint8_t seed[KYBER_SYMBYTES], int transposed) ++{ ++ unsigned int ctr, i, j, k; ++ unsigned int buflen, off; ++ uint8_t buf[GEN_MATRIX_NBLOCKS*XOF_BLOCKBYTES+2]; ++ xof_state state; ++ ++ for(i=0;i> (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_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) ++{ ++ unsigned int i; ++ ++ for(i=0;i<25;i++) ++ s[i] = 0; ++ ++ while(inlen >= 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 ++**************************************************/ ++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 ++* ++* 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)); ++} ++ ++/************************************************* ++* 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 */ ++ indcpa_enc(ct, buf, pk, kr+KYBER_SYMBYTES); ++ ++ /* 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); ++} ++ ++// 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) ++{ ++ uint8_t *ss = out_shared_key; ++ const uint8_t *sk = &in_priv->opaque[0]; ++ ++ size_t i; ++ int fail = 1; ++ uint8_t buf[2*KYBER_SYMBYTES]; ++ /* Will contain key, coins */ ++ uint8_t kr[2*KYBER_SYMBYTES]; ++ uint8_t cmp[KYBER_CIPHERTEXTBYTES]; ++ const uint8_t *pk = sk+KYBER_INDCPA_SECRETKEYBYTES; ++ ++ if (ciphertext_len == KYBER_CIPHERTEXTBYTES) { ++ indcpa_dec(buf, ct, sk); ++ ++ /* Multitarget countermeasure for coins + contributory KEM */ ++ for(i=0;iopaque, 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/kyber512.c b/src/crypto/kyber/kyber512.c +new file mode 100644 +index 000000000..21eed11a2 +--- /dev/null ++++ b/src/crypto/kyber/kyber512.c +@@ -0,0 +1,5 @@ ++#define KYBER_K 2 ++ ++#include "kyber.c" ++ ++ +diff --git a/src/crypto/kyber/kyber768.c b/src/crypto/kyber/kyber768.c +new file mode 100644 +index 000000000..3e572b72e +--- /dev/null ++++ b/src/crypto/kyber/kyber768.c +@@ -0,0 +1,4 @@ ++#define KYBER_K 3 ++ ++#include "kyber.c" ++ +diff --git a/src/crypto/obj/obj_dat.h b/src/crypto/obj/obj_dat.h +index 778d8e3cf..8956aa6a9 100644 +--- a/src/crypto/obj/obj_dat.h ++++ b/src/crypto/obj/obj_dat.h +@@ -57,7 +57,7 @@ + /* This file is generated by crypto/obj/objects.go. */ + + +-#define NUM_NID 963 ++#define NUM_NID 970 + + static const uint8_t kObjectData[] = { + /* NID_rsadsi */ +@@ -8781,6 +8781,16 @@ static const ASN1_OBJECT kObjects[NUM_NID] = { + {"ED448", "ED448", NID_ED448, 3, &kObjectData[6181], 0}, + {"X448", "X448", NID_X448, 3, &kObjectData[6184], 0}, + {"SHA512-256", "sha512-256", NID_sha512_256, 9, &kObjectData[6187], 0}, ++ {NULL, NULL, NID_undef, 0, NULL, 0}, ++ {"X25519Kyber512Draft00", "X25519Kyber512Draft00", ++ NID_X25519Kyber512Draft00, 0, NULL, 0}, ++ {"X25519Kyber768Draft00", "X25519Kyber768Draft00", ++ NID_X25519Kyber768Draft00, 0, NULL, 0}, ++ {NULL, NULL, NID_undef, 0, NULL, 0}, ++ {NULL, NULL, NID_undef, 0, NULL, 0}, ++ {NULL, NULL, NID_undef, 0, NULL, 0}, ++ {"P256Kyber768Draft00", "P256Kyber768Draft00", NID_P256Kyber768Draft00, 0, ++ NULL, 0}, + }; + + static const uint16_t kNIDsInShortNameOrder[] = { +@@ -8913,6 +8923,7 @@ static const uint16_t kNIDsInShortNameOrder[] = { + 18 /* OU */, + 749 /* Oakley-EC2N-3 */, + 750 /* Oakley-EC2N-4 */, ++ 969 /* P256Kyber768Draft00 */, + 9 /* PBE-MD2-DES */, + 168 /* PBE-MD2-RC2-64 */, + 10 /* PBE-MD5-DES */, +@@ -8979,6 +8990,8 @@ static const uint16_t kNIDsInShortNameOrder[] = { + 458 /* UID */, + 0 /* UNDEF */, + 948 /* X25519 */, ++ 964 /* X25519Kyber512Draft00 */, ++ 965 /* X25519Kyber768Draft00 */, + 961 /* X448 */, + 11 /* X500 */, + 378 /* X500algorithms */, +@@ -9826,6 +9839,7 @@ static const uint16_t kNIDsInLongNameOrder[] = { + 366 /* OCSP Nonce */, + 371 /* OCSP Service Locator */, + 180 /* OCSP Signing */, ++ 969 /* P256Kyber768Draft00 */, + 161 /* PBES2 */, + 69 /* PBKDF2 */, + 162 /* PBMAC1 */, +@@ -9850,6 +9864,8 @@ static const uint16_t kNIDsInLongNameOrder[] = { + 133 /* Time Stamping */, + 375 /* Trust Root */, + 948 /* X25519 */, ++ 964 /* X25519Kyber512Draft00 */, ++ 965 /* X25519Kyber768Draft00 */, + 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 f110ee929..802692f36 100644 +--- a/src/crypto/obj/obj_mac.num ++++ b/src/crypto/obj/obj_mac.num +@@ -951,3 +951,6 @@ CECPQ2 959 + ED448 960 + X448 961 + sha512_256 962 ++X25519Kyber512Draft00 964 ++X25519Kyber768Draft00 965 ++P256Kyber768Draft00 969 +diff --git a/src/crypto/obj/objects.txt b/src/crypto/obj/objects.txt +index b88342dd8..458218c59 100644 +--- a/src/crypto/obj/objects.txt ++++ b/src/crypto/obj/objects.txt +@@ -1335,6 +1335,11 @@ secg-scheme 14 3 : dhSinglePass-cofactorDH-sha512kdf-scheme + # NID for CECPQ2 (no corresponding OID). + : CECPQ2 + ++# NID for Kyber hybrids (no corresponding OID). ++ : X25519Kyber512Draft00 ++ : X25519Kyber768Draft00 ++ : P256Kyber768Draft00 ++ + # See RFC 8410. + 1 3 101 110 : X25519 + 1 3 101 111 : X448 +diff --git a/src/include/openssl/kyber.h b/src/include/openssl/kyber.h +new file mode 100644 +index 000000000..074ac5906 +--- /dev/null ++++ b/src/include/openssl/kyber.h +@@ -0,0 +1,109 @@ ++#ifndef OPENSSL_HEADER_KYBER_H ++#define OPENSSL_HEADER_KYBER_H ++ ++#include ++ ++#if defined(__cplusplus) ++extern "C" { ++#endif ++ ++#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 ++ ++struct KYBER512_private_key { ++ uint8_t opaque[KYBER512_PRIVATE_KEY_BYTES]; ++}; ++struct KYBER768_private_key { ++ uint8_t opaque[KYBER768_PRIVATE_KEY_BYTES]; ++}; ++struct KYBER512_public_key { ++ uint8_t opaque[KYBER512_PUBLIC_KEY_BYTES]; ++}; ++struct KYBER768_public_key { ++ uint8_t opaque[KYBER768_PUBLIC_KEY_BYTES]; ++}; ++ ++// KYBER_GENERATE_KEY_BYTES is the number of bytes of entropy needed to ++// generate a keypair. ++#define KYBER_GENERATE_KEY_BYTES 64 ++ ++// KYBER_ENCAP_BYTES is the number of bytes of entropy needed to encapsulate a ++// session key. ++#define KYBER_ENCAP_BYTES 32 ++ ++// KYBER_KEY_BYTES is the number of bytes in a shared key. ++#define KYBER_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( ++ struct KYBER512_public_key *out_pub, struct KYBER512_private_key *out_priv, ++ const uint8_t input[KYBER_GENERATE_KEY_BYTES]); ++ ++// KYBER768_generate_key is a deterministic function that outputs a public and ++// private key based on the given entropy. ++OPENSSL_EXPORT void KYBER768_generate_key( ++ struct KYBER768_public_key *out_pub, struct KYBER768_private_key *out_priv, ++ const uint8_t input[KYBER_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], ++ uint8_t out_shared_key[KYBER_KEY_BYTES], ++ const struct KYBER512_public_key *in_pub, ++ const uint8_t in[KYBER_ENCAP_BYTES]); ++ ++// 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], ++ uint8_t out_shared_key[KYBER_KEY_BYTES], ++ const struct KYBER768_public_key *in_pub, ++ const uint8_t in[KYBER_ENCAP_BYTES]); ++ ++// 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. ++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); ++ ++// 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. ++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); ++ ++// KYBER512_marshal_public_key serialises |in_pub| to |out|. ++OPENSSL_EXPORT void KYBER512_marshal_public_key( ++ uint8_t out[KYBER512_PUBLIC_KEY_BYTES], const struct KYBER512_public_key *in_pub); ++ ++// KYBER768_marshal_public_key serialises |in_pub| to |out|. ++OPENSSL_EXPORT void KYBER768_marshal_public_key( ++ uint8_t out[KYBER768_PUBLIC_KEY_BYTES], const struct KYBER768_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]); ++ ++// 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]); ++ ++#if defined(__cplusplus) ++} // extern C ++#endif ++ ++#endif // OPENSSL_HEADER_KYBER_H +diff --git a/src/include/openssl/nid.h b/src/include/openssl/nid.h +index bf7f3da5f..3c146db3c 100644 +--- a/src/include/openssl/nid.h ++++ b/src/include/openssl/nid.h +@@ -4251,6 +4251,15 @@ extern "C" { + #define NID_sha512_256 962 + #define OBJ_sha512_256 2L, 16L, 840L, 1L, 101L, 3L, 4L, 2L, 6L + ++#define SN_X25519Kyber512Draft00 "X25519Kyber512Draft00" ++#define NID_X25519Kyber512Draft00 964 ++ ++#define SN_X25519Kyber768Draft00 "X25519Kyber768Draft00" ++#define NID_X25519Kyber768Draft00 965 ++ ++#define SN_P256Kyber768Draft00 "P256Kyber768Draft00" ++#define NID_P256Kyber768Draft00 969 ++ + + #if defined(__cplusplus) + } /* extern C */ +diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h +index 7ff7e72c8..6833ce6de 100644 +--- a/src/include/openssl/ssl.h ++++ b/src/include/openssl/ssl.h +@@ -2271,6 +2271,9 @@ OPENSSL_EXPORT int SSL_set1_curves_list(SSL *ssl, const char *curves); + #define SSL_CURVE_SECP521R1 25 + #define SSL_CURVE_X25519 29 + #define SSL_CURVE_CECPQ2 16696 ++#define SSL_CURVE_X25519KYBER512DRAFT00 65072 ++#define SSL_CURVE_X25519KYBER768DRAFT00 65073 ++#define SSL_CURVE_P256KYBER768DRAFT00 65074 + + // SSL_get_curve_id returns the ID of the curve used by |ssl|'s most recently + // completed handshake or 0 if not applicable. +diff --git a/src/ssl/internal.h b/src/ssl/internal.h +index b3b754053..7d3880f79 100644 +--- a/src/ssl/internal.h ++++ b/src/ssl/internal.h +@@ -1102,7 +1102,7 @@ class SSLKeyShare { + struct NamedGroup { + int nid; + uint16_t group_id; +- const char name[8], alias[11]; ++ const char name[23], alias[23]; + }; + + // NamedGroups returns all supported groups. +diff --git a/src/ssl/ssl_key_share.cc b/src/ssl/ssl_key_share.cc +index 6cac3cf22..4bcdb2014 100644 +--- a/src/ssl/ssl_key_share.cc ++++ b/src/ssl/ssl_key_share.cc +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -208,6 +209,416 @@ class X25519KeyShare : public SSLKeyShare { + uint8_t private_key_[32]; + }; + ++class P256Kyber768Draft00KeyShare : public SSLKeyShare { ++ public: ++ P256Kyber768Draft00KeyShare() {} ++ ++ uint16_t GroupID() const override { return SSL_CURVE_P256KYBER768DRAFT00; } ++ ++ bool Offer(CBB *out) override { ++ assert(!p256_private_key_); ++ ++ // Set up a shared |BN_CTX| for P-256 operations. ++ UniquePtr bn_ctx(BN_CTX_new()); ++ if (!bn_ctx) { ++ return false; ++ } ++ ++ BN_CTXScope scope(bn_ctx.get()); ++ ++ // Generate a P-256 private key. ++ UniquePtr group; ++ group.reset(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); ++ p256_private_key_.reset(BN_new()); ++ if (!group || !p256_private_key_ || ++ !BN_rand_range_ex(p256_private_key_.get(), 1, ++ EC_GROUP_get0_order(group.get()))) { ++ return false; ++ } ++ ++ // Compute the corresponding P-256 public key and serialize it. ++ UniquePtr p256_public_key(EC_POINT_new(group.get())); ++ if (!p256_public_key || ++ !EC_POINT_mul(group.get(), p256_public_key.get(), p256_private_key_.get(), ++ NULL, NULL, bn_ctx.get()) || ++ !EC_POINT_point2cbb(out, group.get(), p256_public_key.get(), ++ POINT_CONVERSION_UNCOMPRESSED, bn_ctx.get())) { ++ return false; ++ } ++ ++ ++ // Kyber ++ 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))) { ++ return false; ++ } ++ ++ return true; ++ } ++ ++ bool Accept(CBB *out_public_key, Array *out_secret, ++ uint8_t *out_alert, Span peer_key) override { ++ assert(!p256_private_key_); ++ ++ if (peer_key.size() != 65 + KYBER768_PUBLIC_KEY_BYTES) { ++ *out_alert = SSL_AD_DECODE_ERROR; ++ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); ++ return false; ++ } ++ ++ // Set up a shared |BN_CTX| for P-256 operations. ++ UniquePtr bn_ctx(BN_CTX_new()); ++ if (!bn_ctx) { ++ return false; ++ } ++ ++ BN_CTXScope scope(bn_ctx.get()); ++ ++ UniquePtr group; ++ group.reset(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); ++ if (!group) { ++ return false; ++ } ++ ++ // Parse peer point ++ UniquePtr peer_point(EC_POINT_new(group.get())); ++ UniquePtr result(EC_POINT_new(group.get())); ++ BIGNUM *x = BN_CTX_get(bn_ctx.get()); ++ if (!peer_point || !result || !x) { ++ return false; ++ } ++ ++ if (peer_key.empty() || peer_key[0] != POINT_CONVERSION_UNCOMPRESSED || ++ !EC_POINT_oct2point(group.get(), peer_point.get(), peer_key.data(), ++ 65, bn_ctx.get())) { ++ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); ++ *out_alert = SSL_AD_DECODE_ERROR; ++ return false; ++ } ++ ++ p256_private_key_.reset(BN_new()); ++ if (!p256_private_key_ || !BN_rand_range_ex(p256_private_key_.get(), 1, ++ EC_GROUP_get0_order(group.get()))) { ++ return false; ++ } ++ ++ // Compute the corresponding P-256 public key and serialize it. ++ UniquePtr p256_public_key(EC_POINT_new(group.get())); ++ if (!p256_public_key || ++ !EC_POINT_mul(group.get(), p256_public_key.get(), p256_private_key_.get(), ++ NULL, NULL, bn_ctx.get()) || ++ !EC_POINT_point2cbb(out_public_key, group.get(), p256_public_key.get(), ++ POINT_CONVERSION_UNCOMPRESSED, bn_ctx.get())) { ++ return false; ++ } ++ ++ // Compute the x-coordinate of |peer_key| * |p256_private_key_|. ++ if (!EC_POINT_mul(group.get(), result.get(), NULL, peer_point.get(), ++ p256_private_key_.get(), bn_ctx.get()) || ++ !EC_POINT_get_affine_coordinates_GFp(group.get(), result.get(), x, NULL, ++ bn_ctx.get())) { ++ return false; ++ } ++ ++ // Encode the x-coordinate left-padded with zeros. ++ Array secret; ++ if (!secret.Init(32 + KYBER_KEY_BYTES) || ++ !BN_bn2bin_padded(secret.data(), 32, x)) { ++ return false; ++ } ++ ++ ++ KYBER768_public_key peer_public_key; ++ KYBER768_parse_public_key(&peer_public_key, peer_key.data() + 65); ++ ++ 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, ciphertext, sizeof(ciphertext))) { ++ return false; ++ } ++ ++ *out_secret = std::move(secret); ++ return true; ++ } ++ ++ bool Finish(Array *out_secret, uint8_t *out_alert, ++ Span peer_key) override { ++ assert(p256_private_key_); ++ *out_alert = SSL_AD_INTERNAL_ERROR; ++ ++ Array secret; ++ if (!secret.Init(32 + KYBER_KEY_BYTES)) { ++ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); ++ return false; ++ } ++ ++ if (peer_key.size() != 65 + KYBER768_CIPHERTEXT_BYTES) { ++ *out_alert = SSL_AD_DECODE_ERROR; ++ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); ++ return false; ++ } ++ ++ // Set up a shared |BN_CTX| for P-256 operations. ++ UniquePtr bn_ctx(BN_CTX_new()); ++ if (!bn_ctx) { ++ return false; ++ } ++ ++ BN_CTXScope scope(bn_ctx.get()); ++ ++ UniquePtr group; ++ group.reset(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); ++ if (!group) { ++ return false; ++ } ++ ++ // Parse peer point ++ UniquePtr peer_point(EC_POINT_new(group.get())); ++ UniquePtr result(EC_POINT_new(group.get())); ++ BIGNUM *x = BN_CTX_get(bn_ctx.get()); ++ if (!peer_point || !result || !x) { ++ return false; ++ } ++ ++ if (peer_key.empty() || peer_key[0] != POINT_CONVERSION_UNCOMPRESSED || ++ !EC_POINT_oct2point(group.get(), peer_point.get(), peer_key.data(), ++ 65, bn_ctx.get())) { ++ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); ++ *out_alert = SSL_AD_DECODE_ERROR; ++ return false; ++ } ++ ++ // Compute the x-coordinate of |peer_key| * |p256_private_key_|. ++ if (!EC_POINT_mul(group.get(), result.get(), NULL, peer_point.get(), ++ p256_private_key_.get(), bn_ctx.get()) || ++ !EC_POINT_get_affine_coordinates_GFp(group.get(), result.get(), x, NULL, ++ bn_ctx.get())) { ++ return false; ++ } ++ ++ // Encode the x-coordinate left-padded with zeros. ++ if (!secret.Init(32 + KYBER_KEY_BYTES) || ++ !BN_bn2bin_padded(secret.data(), 32, x)) { ++ return false; ++ } ++ ++ KYBER768_decap(secret.data() + 32, &kyber_private_key_, ++ peer_key.data() + 65, peer_key.size() - 65); ++ ++ *out_secret = std::move(secret); ++ return true; ++ } ++ ++ private: ++ UniquePtr p256_private_key_; ++ KYBER768_private_key kyber_private_key_; ++}; ++ ++class X25519Kyber768Draft00KeyShare : public SSLKeyShare { ++ public: ++ X25519Kyber768Draft00KeyShare() {} ++ ++ uint16_t GroupID() const override { return SSL_CURVE_X25519KYBER768DRAFT00; } ++ ++ bool Offer(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, x25519_public_key, sizeof(x25519_public_key)) || ++ !CBB_add_bytes(out, kyber_public_key_bytes, ++ sizeof(kyber_public_key_bytes))) { ++ return false; ++ } ++ ++ return true; ++ } ++ ++ bool Accept(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() != 32 + KYBER768_PUBLIC_KEY_BYTES) { ++ *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() + 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 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))) { ++ return false; ++ } ++ ++ *out_secret = std::move(secret); ++ return true; ++ } ++ ++ bool Finish(Array *out_secret, uint8_t *out_alert, ++ Span peer_key) override { ++ *out_alert = SSL_AD_INTERNAL_ERROR; ++ ++ Array secret; ++ if (!secret.Init(32 + KYBER_KEY_BYTES)) { ++ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); ++ return false; ++ } ++ ++ if (peer_key.size() != 32 + KYBER768_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; ++ } ++ ++ KYBER768_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]; ++ KYBER768_private_key kyber_private_key_; ++}; ++ ++class X25519Kyber512Draft00KeyShare : public SSLKeyShare { ++ public: ++ X25519Kyber512Draft00KeyShare() {} ++ ++ uint16_t GroupID() const override { return SSL_CURVE_X25519KYBER512DRAFT00; } ++ ++ bool Offer(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]; ++ KYBER512_public_key kyber_public_key; ++ RAND_bytes(kyber_entropy, sizeof(kyber_entropy)); ++ KYBER512_generate_key(&kyber_public_key, &kyber_private_key_, kyber_entropy); ++ ++ uint8_t kyber_public_key_bytes[KYBER512_PUBLIC_KEY_BYTES]; ++ KYBER512_marshal_public_key(kyber_public_key_bytes, &kyber_public_key); ++ ++ if (!CBB_add_bytes(out, x25519_public_key, sizeof(x25519_public_key)) || ++ !CBB_add_bytes(out, kyber_public_key_bytes, ++ sizeof(kyber_public_key_bytes))) { ++ return false; ++ } ++ ++ return true; ++ } ++ ++ bool Accept(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_); ++ ++ KYBER512_public_key peer_public_key; ++ if (peer_key.size() != 32 + KYBER512_PUBLIC_KEY_BYTES) { ++ *out_alert = SSL_AD_DECODE_ERROR; ++ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); ++ return false; ++ } ++ ++ 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 ciphertext[KYBER512_CIPHERTEXT_BYTES]; ++ uint8_t entropy[KYBER_ENCAP_BYTES]; ++ RAND_bytes(entropy, sizeof(entropy)); ++ ++ 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_public_key, ciphertext, sizeof(ciphertext))) { ++ return false; ++ } ++ ++ *out_secret = std::move(secret); ++ return true; ++ } ++ ++ bool Finish(Array *out_secret, uint8_t *out_alert, ++ Span peer_key) override { ++ *out_alert = SSL_AD_INTERNAL_ERROR; ++ ++ Array secret; ++ if (!secret.Init(32 + KYBER_KEY_BYTES)) { ++ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); ++ return false; ++ } ++ ++ 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; ++ } ++ ++ 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]; ++ KYBER512_private_key kyber_private_key_; ++}; ++ + class CECPQ2KeyShare : public SSLKeyShare { + public: + CECPQ2KeyShare() {} +@@ -306,6 +717,12 @@ CONSTEXPR_ARRAY NamedGroup kNamedGroups[] = { + {NID_secp521r1, SSL_CURVE_SECP521R1, "P-521", "secp521r1"}, + {NID_X25519, SSL_CURVE_X25519, "X25519", "x25519"}, + {NID_CECPQ2, SSL_CURVE_CECPQ2, "CECPQ2", "CECPQ2"}, ++ {NID_X25519Kyber512Draft00, SSL_CURVE_X25519KYBER512DRAFT00, ++ "X25519Kyber512Draft00", "Xyber512D00"}, ++ {NID_X25519Kyber768Draft00, SSL_CURVE_X25519KYBER768DRAFT00, ++ "X25519Kyber768Draft00", "Xyber768D00"}, ++ {NID_P256Kyber768Draft00, SSL_CURVE_P256KYBER768DRAFT00, ++ "P256Kyber768Draft00", "P256Kyber768D00"} + }; + + } // namespace +@@ -332,6 +749,12 @@ UniquePtr SSLKeyShare::Create(uint16_t group_id) { + return UniquePtr(New()); + case SSL_CURVE_CECPQ2: + return UniquePtr(New()); ++ case SSL_CURVE_X25519KYBER512DRAFT00: ++ return UniquePtr(New()); ++ case SSL_CURVE_X25519KYBER768DRAFT00: ++ return UniquePtr(New()); ++ case SSL_CURVE_P256KYBER768DRAFT00: ++ return UniquePtr(New()); + default: + return nullptr; + } +diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc +index 637f4d5c6..a0d6d9922 100644 +--- a/src/ssl/ssl_test.cc ++++ b/src/ssl/ssl_test.cc +@@ -399,7 +399,26 @@ static const CurveTest kCurveTests[] = { + "P-256:CECPQ2", + { SSL_CURVE_SECP256R1, SSL_CURVE_CECPQ2 }, + }, +- ++ { ++ "Xyber512D00", ++ { SSL_CURVE_X25519KYBER512DRAFT00 }, ++ }, ++ { ++ "Xyber768D00", ++ { SSL_CURVE_X25519KYBER768DRAFT00 }, ++ }, ++ { ++ "P-256:Xyber512D00", ++ { SSL_CURVE_SECP256R1, SSL_CURVE_X25519KYBER512DRAFT00 }, ++ }, ++ { ++ "P256Kyber768D00", ++ { SSL_CURVE_P256KYBER768DRAFT00 }, ++ }, ++ { ++ "P-256:P256Kyber768D00", ++ { SSL_CURVE_SECP256R1, SSL_CURVE_P256KYBER768DRAFT00 }, ++ }, + { + "P-256:P-384:P-521:X25519", + { +diff --git a/src/ssl/t1_lib.cc b/src/ssl/t1_lib.cc +index 342c17021..4d368d89d 100644 +--- a/src/ssl/t1_lib.cc ++++ b/src/ssl/t1_lib.cc +@@ -205,7 +205,9 @@ static bool tls1_check_duplicate_extensions(const CBS *cbs) { + } + + static bool is_post_quantum_group(uint16_t id) { +- return id == SSL_CURVE_CECPQ2; ++ return id == SSL_CURVE_CECPQ2 || id == SSL_CURVE_X25519KYBER512DRAFT00 || ++ id == SSL_CURVE_X25519KYBER768DRAFT00 || ++ id == SSL_CURVE_P256KYBER768DRAFT00; + } + + bool ssl_client_hello_init(const SSL *ssl, SSL_CLIENT_HELLO *out, +@@ -334,8 +336,8 @@ bool tls1_get_shared_group(SSL_HANDSHAKE *hs, uint16_t *out_group_id) { + for (uint16_t pref_group : pref) { + for (uint16_t supp_group : supp) { + if (pref_group == supp_group && +- // CECPQ2(b) doesn't fit in the u8-length-prefixed ECPoint field in +- // TLS 1.2 and below. ++ // CECPQ2(b) and Kyber don't fit in the u8-length-prefixed ECPoint ++ // field in TLS 1.2 and below. + (ssl_protocol_version(ssl) >= TLS1_3_VERSION || + !is_post_quantum_group(pref_group))) { + *out_group_id = pref_group; +@@ -401,7 +403,7 @@ bool tls1_set_curves_list(Array *out_group_ids, const char *curves) { + bool tls1_check_group_id(const SSL_HANDSHAKE *hs, uint16_t group_id) { + if (is_post_quantum_group(group_id) && + ssl_protocol_version(hs->ssl) < TLS1_3_VERSION) { +- // CECPQ2(b) requires TLS 1.3. ++ // CECPQ2(b) and Kyber requires TLS 1.3. + return false; + } + +diff --git a/src/tool/speed.cc b/src/tool/speed.cc +index 1b89b42f5..9f02a6783 100644 +--- a/src/tool/speed.cc ++++ b/src/tool/speed.cc +@@ -36,6 +36,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -890,6 +891,116 @@ static bool SpeedScrypt(const std::string &selected) { + return true; + } + ++static bool SpeedKyber768(const std::string &selected) { ++ if (!selected.empty() && selected != "Kyber768") { ++ return true; ++ } ++ ++ TimeResults results; ++ ++ if (!TimeFunction(&results, []() -> bool { ++ struct KYBER768_public_key pub; ++ struct KYBER768_private_key priv; ++ uint8_t entropy[KYBER_GENERATE_KEY_BYTES]; ++ RAND_bytes(entropy, sizeof(entropy)); ++ KYBER768_generate_key(&pub, &priv, entropy); ++ return true; ++ })) { ++ fprintf(stderr, "Failed to time KYBER768_generate_key.\n"); ++ return false; ++ } ++ ++ results.Print("Kyber768 generate"); ++ ++ struct KYBER768_public_key pub; ++ struct KYBER768_private_key priv; ++ uint8_t key_entropy[KYBER_GENERATE_KEY_BYTES]; ++ RAND_bytes(key_entropy, sizeof(key_entropy)); ++ KYBER768_generate_key(&pub, &priv, key_entropy); ++ ++ uint8_t ciphertext[KYBER768_CIPHERTEXT_BYTES]; ++ if (!TimeFunction(&results, [&pub, &ciphertext]() -> bool { ++ 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); ++ return true; ++ })) { ++ fprintf(stderr, "Failed to time KYBER768_encap.\n"); ++ return false; ++ } ++ ++ results.Print("Kyber768 encap"); ++ ++ if (!TimeFunction(&results, [&priv, &ciphertext]() -> bool { ++ uint8_t shared_key[KYBER_KEY_BYTES]; ++ KYBER768_decap(shared_key, &priv, ciphertext, sizeof(ciphertext)); ++ return true; ++ })) { ++ fprintf(stderr, "Failed to time KYBER768_decap.\n"); ++ return false; ++ } ++ ++ results.Print("Kyber768 decap"); ++ ++ return true; ++} ++ ++static bool SpeedKyber512(const std::string &selected) { ++ if (!selected.empty() && selected != "Kyber512") { ++ return true; ++ } ++ ++ TimeResults results; ++ ++ if (!TimeFunction(&results, []() -> bool { ++ struct KYBER512_public_key pub; ++ struct KYBER512_private_key priv; ++ uint8_t entropy[KYBER_GENERATE_KEY_BYTES]; ++ RAND_bytes(entropy, sizeof(entropy)); ++ KYBER512_generate_key(&pub, &priv, entropy); ++ return true; ++ })) { ++ fprintf(stderr, "Failed to time KYBER512_generate_key.\n"); ++ return false; ++ } ++ ++ results.Print("Kyber512 generate"); ++ ++ struct KYBER512_public_key pub; ++ struct KYBER512_private_key priv; ++ uint8_t key_entropy[KYBER_GENERATE_KEY_BYTES]; ++ RAND_bytes(key_entropy, sizeof(key_entropy)); ++ KYBER512_generate_key(&pub, &priv, key_entropy); ++ ++ uint8_t ciphertext[KYBER512_CIPHERTEXT_BYTES]; ++ if (!TimeFunction(&results, [&pub, &ciphertext]() -> bool { ++ 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); ++ return true; ++ })) { ++ fprintf(stderr, "Failed to time KYBER512_encap.\n"); ++ return false; ++ } ++ ++ results.Print("Kyber512 encap"); ++ ++ if (!TimeFunction(&results, [&priv, &ciphertext]() -> bool { ++ uint8_t shared_key[KYBER_KEY_BYTES]; ++ KYBER512_decap(shared_key, &priv, ciphertext, sizeof(ciphertext)); ++ return true; ++ })) { ++ fprintf(stderr, "Failed to time KYBER512_decap.\n"); ++ return false; ++ } ++ ++ results.Print("Kyber512 decap"); ++ ++ return true; ++} ++ + static bool SpeedHRSS(const std::string &selected) { + if (!selected.empty() && selected != "HRSS") { + return true; +@@ -1371,6 +1482,8 @@ bool Speed(const std::vector &args) { + !SpeedScrypt(selected) || + !SpeedRSAKeyGen(selected) || + !SpeedHRSS(selected) || ++ !SpeedKyber512(selected) || ++ !SpeedKyber768(selected) || + !SpeedHashToCurve(selected) || + !SpeedTrustToken("TrustToken-Exp1-Batch1", TRUST_TOKEN_experiment_v1(), 1, + selected) || +-- +2.40.0 + diff --git a/boring-sys/patches/rpk-patch/include/openssl/ssl.h.patch b/boring-sys/patches/rpk-patch/include/openssl/ssl.h.patch new file mode 100644 index 00000000..cf2f220a --- /dev/null +++ b/boring-sys/patches/rpk-patch/include/openssl/ssl.h.patch @@ -0,0 +1,84 @@ +--- google_boringssl/include/openssl/ssl.h 2021-02-03 18:29:04.000000000 -0800 ++++ boringssl/include/openssl/ssl.h 2021-02-03 20:24:49.000000000 -0800 +@@ -138,6 +138,25 @@ + * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR + * OTHERWISE. + */ ++/* ==================================================================== ++ * Copyright 2020 Apple Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the “Software”), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom ++ * the Software is furnished to do so, subject to the following conditions: ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS ++ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ + + #ifndef OPENSSL_HEADER_SSL_H + #define OPENSSL_HEADER_SSL_H +@@ -1102,6 +1121,16 @@ + SSL_CTX *ctx, CRYPTO_BUFFER *const *certs, size_t num_certs, + EVP_PKEY *privkey, const SSL_PRIVATE_KEY_METHOD *privkey_method); + ++// SSL_CTX_set_nullchain_and_key sets the private key for a ++// TLS client or server. Reference to the given |EVP_PKEY| ++// object is added as needed. Exactly one of |privkey| or |privkey_method| ++// may be non-NULL. Returns one on success and zero on error. ++// Note the lack of a corresponding public-key certificate. ++// See SSL_CTX_set_server_raw_public_key_certificate. ++OPENSSL_EXPORT int SSL_CTX_set_nullchain_and_key( ++ SSL_CTX *ctx, ++ EVP_PKEY *privkey, const SSL_PRIVATE_KEY_METHOD *privkey_method); ++ + // SSL_set_chain_and_key sets the certificate chain and private key for a TLS + // client or server. References to the given |CRYPTO_BUFFER| and |EVP_PKEY| + // objects are added as needed. Exactly one of |privkey| or |privkey_method| +@@ -1110,6 +1139,16 @@ + SSL *ssl, CRYPTO_BUFFER *const *certs, size_t num_certs, EVP_PKEY *privkey, + const SSL_PRIVATE_KEY_METHOD *privkey_method); + ++// SSL_set_nullchain_and_key sets the private key for a TLS ++// client or server. Reference to the given |EVP_PKEY| ++// object is added as needed. Exactly one of |privkey| or |privkey_method| ++// may be non-NULL. Returns one on success and zero on error. ++// Note the lack of a corresponding public-key certificate. ++// See SSL_set_server_raw_public_key_certificate. ++OPENSSL_EXPORT int SSL_set_nullchain_and_key( ++ SSL *ssl, EVP_PKEY *privkey, ++ const SSL_PRIVATE_KEY_METHOD *privkey_method); ++ + // SSL_CTX_get0_chain returns the list of |CRYPTO_BUFFER|s that were set by + // |SSL_CTX_set_chain_and_key|. Reference counts are not incremented by this + // call. The return value may be |NULL| if no chain has been set. +@@ -2821,6 +2860,21 @@ + OPENSSL_EXPORT int SSL_has_application_settings(const SSL *ssl); + + ++// Server Certificate Type. ++ ++#define TLSEXT_CERTIFICATETYPE_X509 0 ++#define TLSEXT_CERTIFICATETYPE_RAW_PUBLIC_KEY 2 ++ ++OPENSSL_EXPORT int SSL_CTX_set_server_raw_public_key_certificate( ++ SSL_CTX *ctx, const uint8_t *raw_public_key, unsigned raw_public_key_len); ++ ++OPENSSL_EXPORT int SSL_CTX_has_server_raw_public_key_certificate(SSL_CTX *ctx); ++ ++OPENSSL_EXPORT int SSL_set_server_raw_public_key_certificate( ++ SSL *ssl, const uint8_t *raw_public_key, unsigned raw_public_key_len); ++ ++OPENSSL_EXPORT int SSL_has_server_raw_public_key_certificate(SSL *ssl); ++ + // Certificate compression. + // + // Certificates in TLS 1.3 can be compressed (RFC 8879). BoringSSL supports this diff --git a/boring-sys/patches/rpk-patch/include/openssl/tls1.h.patch b/boring-sys/patches/rpk-patch/include/openssl/tls1.h.patch new file mode 100644 index 00000000..de1fb345 --- /dev/null +++ b/boring-sys/patches/rpk-patch/include/openssl/tls1.h.patch @@ -0,0 +1,38 @@ +--- google_boringssl/include/openssl/tls1.h 2021-02-03 18:29:04.000000000 -0800 ++++ boringssl/include/openssl/tls1.h 2021-02-03 20:24:49.000000000 -0800 +@@ -146,6 +146,25 @@ + * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR + * OTHERWISE. + */ ++/* ==================================================================== ++ * Copyright 2020 Apple Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the “Software”), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom ++ * the Software is furnished to do so, subject to the following conditions: ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS ++ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ + + #ifndef OPENSSL_HEADER_TLS1_H + #define OPENSSL_HEADER_TLS1_H +@@ -197,6 +216,9 @@ + // ExtensionType value from RFC 7301 + #define TLSEXT_TYPE_application_layer_protocol_negotiation 16 + ++// ExtensionType value from RFC 7250 ++#define TLSEXT_TYPE_server_certificate_type 20 ++ + // ExtensionType value from RFC 7685 + #define TLSEXT_TYPE_padding 21 + diff --git a/boring-sys/patches/rpk-patch/ssl/extensions.cc.patch b/boring-sys/patches/rpk-patch/ssl/extensions.cc.patch new file mode 100644 index 00000000..99de7e2a --- /dev/null +++ b/boring-sys/patches/rpk-patch/ssl/extensions.cc.patch @@ -0,0 +1,189 @@ +--- google_boringssl/ssl/extensions.cc 2021-02-03 18:29:04.000000000 -0800 ++++ boringssl/ssl/extensions.cc 2021-02-03 20:24:49.000000000 -0800 +@@ -105,6 +105,25 @@ + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). */ ++/* ==================================================================== ++ * Copyright 2020 Apple Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the “Software”), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom ++ * the Software is furnished to do so, subject to the following conditions: ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS ++ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ + + #include + +@@ -3234,6 +3253,146 @@ + return true; + } + ++// Server Certificate Type ++ ++static bool ext_server_certificate_type_add_clienthello(const SSL_HANDSHAKE *hs, ++ CBB *out, ++ CBB *out_compressible, ++ ssl_client_hello_type_t type) { ++ ++ if (hs->max_version <= TLS1_2_VERSION) { ++ return true; ++ } ++ ++ if (hs->config->server_certificate_type_list.empty()) { ++ return true; ++ } ++ ++ CBB contents, server_certificate_types; ++ if (!CBB_add_u16(out, TLSEXT_TYPE_server_certificate_type) || ++ !CBB_add_u16_length_prefixed(out, &contents) || ++ !CBB_add_u8_length_prefixed(&contents, &server_certificate_types) || ++ !CBB_add_bytes(&server_certificate_types, ++ hs->config->server_certificate_type_list.data(), ++ hs->config->server_certificate_type_list.size()) || ++ !CBB_flush(out)) { ++ return false; ++ } ++ ++ return true; ++} ++ ++static bool ssl_is_certificate_type_allowed(CBS *certificate_type_list, ++ uint8_t certificate_type) ++{ ++ uint8_t supported_certificate_type; ++ while (CBS_len(certificate_type_list) > 0) { ++ if (!CBS_get_u8(certificate_type_list, ++ &supported_certificate_type)) { ++ break; ++ } ++ ++ if (supported_certificate_type != certificate_type) { ++ continue; ++ } ++ ++ return true; ++ } ++ ++ return false; ++} ++ ++static bool ext_server_certificate_type_parse_serverhello(SSL_HANDSHAKE *hs, ++ uint8_t *out_alert, ++ CBS *content) ++{ ++ if (hs->max_version <= TLS1_2_VERSION || ++ hs->config->server_certificate_type_list.empty()) { ++ return true; ++ } ++ ++ // Strict ++ if (!content) { ++ OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE); ++ *out_alert = SSL_AD_ILLEGAL_PARAMETER; ++ return false; ++ } ++ ++ CBS certificate_type_list = ++ MakeConstSpan(hs->config->server_certificate_type_list); ++ ++ uint8_t certificate_type; ++ if (CBS_get_u8(content, &certificate_type) && ++ ssl_is_certificate_type_allowed(&certificate_type_list, ++ certificate_type)) { ++ hs->server_certificate_type = certificate_type; ++ hs->server_certificate_type_negotiated = 1; ++ return true; ++ } ++ ++ OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE); ++ *out_alert = SSL_AD_ILLEGAL_PARAMETER; ++ return false; ++} ++ ++static bool ext_server_certificate_type_parse_clienthello(SSL_HANDSHAKE *hs, ++ uint8_t *out_alert, ++ CBS *content) ++{ ++ if (!content) { ++ return true; ++ } ++ ++ if (hs->max_version <= TLS1_2_VERSION || ++ hs->config->server_certificate_type_list.empty()) { ++ return true; ++ } ++ ++ CBS certificate_type_list = ++ MakeConstSpan(hs->config->server_certificate_type_list); ++ ++ CBS type_list; ++ if (!CBS_get_u8_length_prefixed(content, &type_list)) { ++ type_list.len = 0; ++ } ++ ++ uint8_t type; ++ while(CBS_len(&type_list) > 0) { ++ if (!CBS_get_u8(&type_list, &type)) { ++ break; ++ } ++ ++ if (!ssl_is_certificate_type_allowed(&certificate_type_list, type)) { ++ continue; ++ } ++ ++ hs->server_certificate_type = type; ++ hs->server_certificate_type_negotiated = 1; ++ return true; ++ } ++ ++ *out_alert = SSL_AD_ILLEGAL_PARAMETER; ++ return false; ++} ++ ++static bool ext_server_certificate_type_add_serverhello(SSL_HANDSHAKE *hs, ++ CBB *out) ++{ ++ if (!hs->server_certificate_type_negotiated) { ++ return true; ++ } ++ ++ CBB contents; ++ if (!CBB_add_u16(out, TLSEXT_TYPE_server_certificate_type) || ++ !CBB_add_u16_length_prefixed(out, &contents) || ++ !CBB_add_u8(&contents, hs->server_certificate_type) || ++ !CBB_flush(out)) { ++ return false; ++ } ++ ++ return true; ++} ++ + // kExtensions contains all the supported extensions. + static const struct tls_extension kExtensions[] = { + { +@@ -3447,6 +3604,13 @@ + ignore_parse_clienthello, + ext_alps_add_serverhello, + }, ++ { ++ TLSEXT_TYPE_server_certificate_type, ++ ext_server_certificate_type_add_clienthello, ++ ext_server_certificate_type_parse_serverhello, ++ ext_server_certificate_type_parse_clienthello, ++ ext_server_certificate_type_add_serverhello, ++ }, + }; + + #define kNumExtensions (sizeof(kExtensions) / sizeof(struct tls_extension)) diff --git a/boring-sys/patches/rpk-patch/ssl/handshake.cc.patch b/boring-sys/patches/rpk-patch/ssl/handshake.cc.patch new file mode 100644 index 00000000..96b99502 --- /dev/null +++ b/boring-sys/patches/rpk-patch/ssl/handshake.cc.patch @@ -0,0 +1,59 @@ +--- google_boringssl/ssl/handshake.cc 2021-02-03 18:29:04.000000000 -0800 ++++ boringssl/ssl/handshake.cc 2021-02-03 20:24:49.000000000 -0800 +@@ -109,6 +109,25 @@ + * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. + * ECC cipher suite support in OpenSSL originally developed by + * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project. */ ++/* ==================================================================== ++ * Copyright 2020 Apple Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the “Software”), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom ++ * the Software is furnished to do so, subject to the following conditions: ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS ++ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ + + #include + +@@ -149,6 +168,7 @@ + cert_compression_negotiated(false), + apply_jdk11_workaround(false), + can_release_private_key(false), ++ server_certificate_type_negotiated(false), + channel_id_negotiated(false) { + assert(ssl); + +@@ -333,7 +353,21 @@ + + uint8_t alert = SSL_AD_CERTIFICATE_UNKNOWN; + enum ssl_verify_result_t ret; +- if (hs->config->custom_verify_callback != nullptr) { ++ if (hs->server_certificate_type_negotiated && ++ hs->server_certificate_type == TLSEXT_CERTIFICATETYPE_RAW_PUBLIC_KEY) { ++ ret = ssl_verify_invalid; ++ EVP_PKEY *peer_pubkey = hs->peer_pubkey.get(); ++ CBS spki = MakeConstSpan(ssl->config->server_raw_public_key_certificate); ++ EVP_PKEY *pubkey = EVP_parse_public_key(&spki); ++ if (!pubkey) { ++ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); ++ alert = SSL_AD_INTERNAL_ERROR; ++ } else if (EVP_PKEY_cmp(peer_pubkey, pubkey) == 1 /* Equal */) { ++ ret = ssl_verify_ok; ++ } else { ++ alert = SSL_AD_BAD_CERTIFICATE; ++ } ++ } else if (hs->config->custom_verify_callback != nullptr) { + ret = hs->config->custom_verify_callback(ssl, &alert); + switch (ret) { + case ssl_verify_ok: diff --git a/boring-sys/patches/rpk-patch/ssl/internal.h.patch b/boring-sys/patches/rpk-patch/ssl/internal.h.patch new file mode 100644 index 00000000..f730ea15 --- /dev/null +++ b/boring-sys/patches/rpk-patch/ssl/internal.h.patch @@ -0,0 +1,75 @@ +--- google_boringssl/ssl/internal.h 2021-02-03 18:29:04.000000000 -0800 ++++ boringssl/ssl/internal.h 2021-02-03 20:24:49.000000000 -0800 +@@ -138,6 +138,25 @@ + * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR + * OTHERWISE. + */ ++/* ==================================================================== ++ * Copyright 2020 Apple Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the “Software”), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom ++ * the Software is furnished to do so, subject to the following conditions: ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS ++ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ + + #ifndef OPENSSL_HEADER_SSL_INTERNAL_H + #define OPENSSL_HEADER_SSL_INTERNAL_H +@@ -1279,6 +1298,8 @@ + // configured. + bool ssl_has_certificate(const SSL_HANDSHAKE *hs); + ++bool ssl_has_raw_public_key_certificate(const SSL_HANDSHAKE *hs); ++ + // ssl_parse_cert_chain parses a certificate list from |cbs| in the format used + // by a TLS Certificate message. On success, it advances |cbs| and returns + // true. Otherwise, it returns false and sets |*out_alert| to an alert to send +@@ -1687,6 +1708,8 @@ + // |cert_compression_negotiated| is true. + uint16_t cert_compression_alg_id; + ++ uint8_t server_certificate_type; ++ + // ech_hpke_ctx is the HPKE context used in ECH. On the server, it is + // initialized if |ech_status| is |ssl_ech_accepted|. On the client, it is + // initialized if |selected_ech_config| is not nullptr. +@@ -1817,6 +1840,8 @@ + // cert_compression_negotiated is true iff |cert_compression_alg_id| is valid. + bool cert_compression_negotiated : 1; + ++ bool server_certificate_type_negotiated : 1; ++ + // apply_jdk11_workaround is true if the peer is probably a JDK 11 client + // which implemented TLS 1.3 incorrectly. + bool apply_jdk11_workaround : 1; +@@ -2731,6 +2756,9 @@ + // along with their corresponding ALPS values. + GrowableArray alps_configs; + ++ Array server_certificate_type_list; ++ Array server_raw_public_key_certificate; ++ + // Contains the QUIC transport params that this endpoint will send. + Array quic_transport_params; + +@@ -3306,6 +3334,9 @@ + // format. + bssl::Array alpn_client_proto_list; + ++ bssl::Array server_certificate_type_list; ++ bssl::Array server_raw_public_key_certificate; ++ + // SRTP profiles we are willing to do from RFC 5764 + bssl::UniquePtr srtp_profiles; + diff --git a/boring-sys/patches/rpk-patch/ssl/ssl_cert.cc.patch b/boring-sys/patches/rpk-patch/ssl/ssl_cert.cc.patch new file mode 100644 index 00000000..29d8d392 --- /dev/null +++ b/boring-sys/patches/rpk-patch/ssl/ssl_cert.cc.patch @@ -0,0 +1,118 @@ +--- google_boringssl/ssl/ssl_cert.cc 2021-02-03 18:29:04.000000000 -0800 ++++ boringssl/ssl/ssl_cert.cc 2021-02-03 20:24:49.000000000 -0800 +@@ -111,6 +111,25 @@ + * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. + * ECC cipher suite support in OpenSSL originally developed by + * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project. */ ++/* ==================================================================== ++ * Copyright 2020 Apple Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the “Software”), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom ++ * the Software is furnished to do so, subject to the following conditions: ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS ++ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ + + #include + +@@ -302,6 +321,25 @@ + return 1; + } + ++static int cert_set_key( ++ CERT *cert, ++ EVP_PKEY *privkey, const SSL_PRIVATE_KEY_METHOD *privkey_method) { ++ if (privkey == NULL && privkey_method == NULL) { ++ OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); ++ return 0; ++ } ++ ++ if (privkey != NULL && privkey_method != NULL) { ++ OPENSSL_PUT_ERROR(SSL, SSL_R_CANNOT_HAVE_BOTH_PRIVKEY_AND_METHOD); ++ return 0; ++ } ++ ++ cert->privatekey = UpRef(privkey); ++ cert->key_method = privkey_method; ++ ++ return 1; ++} ++ + bool ssl_set_cert(CERT *cert, UniquePtr buffer) { + switch (check_leaf_cert_and_privkey(buffer.get(), cert->privatekey.get())) { + case leaf_cert_and_privkey_error: +@@ -343,6 +381,12 @@ + ssl_has_private_key(hs); + } + ++bool ssl_has_raw_public_key_certificate(const SSL_HANDSHAKE *hs) { ++ return hs->server_certificate_type_negotiated && ++ hs->server_certificate_type == TLSEXT_CERTIFICATETYPE_RAW_PUBLIC_KEY && ++ ssl_has_private_key(hs); ++} ++ + bool ssl_parse_cert_chain(uint8_t *out_alert, + UniquePtr *out_chain, + UniquePtr *out_pubkey, +@@ -727,11 +771,20 @@ + + bool ssl_on_certificate_selected(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; +- if (!ssl_has_certificate(hs)) { ++ if (!ssl_has_certificate(hs) && ++ !ssl_has_raw_public_key_certificate(hs)) { + // Nothing to do. + return true; + } + ++ if (ssl_has_raw_public_key_certificate(hs)) { ++ CBS spki = MakeConstSpan( ++ ssl->config->server_raw_public_key_certificate.data(), ++ ssl->config->server_raw_public_key_certificate.size()); ++ hs->local_pubkey = UniquePtr(EVP_parse_public_key(&spki)); ++ return hs->local_pubkey != NULL; ++ } ++ + if (!ssl->ctx->x509_method->ssl_auto_chain_if_needed(hs)) { + return false; + } +@@ -886,6 +939,15 @@ + privkey, privkey_method); + } + ++int SSL_set_nullchain_and_key(SSL *ssl, ++ EVP_PKEY *privkey, ++ const SSL_PRIVATE_KEY_METHOD *privkey_method) { ++ if (!ssl->config) { ++ return 0; ++ } ++ return cert_set_key(ssl->config->cert.get(), privkey, privkey_method); ++} ++ + int SSL_CTX_set_chain_and_key(SSL_CTX *ctx, CRYPTO_BUFFER *const *certs, + size_t num_certs, EVP_PKEY *privkey, + const SSL_PRIVATE_KEY_METHOD *privkey_method) { +@@ -893,6 +955,12 @@ + privkey_method); + } + ++int SSL_CTX_set_nullchain_and_key(SSL_CTX *ctx, ++ EVP_PKEY *privkey, ++ const SSL_PRIVATE_KEY_METHOD *privkey_method) { ++ return cert_set_key(ctx->cert.get(), privkey, privkey_method); ++} ++ + const STACK_OF(CRYPTO_BUFFER)* SSL_CTX_get0_chain(const SSL_CTX *ctx) { + return ctx->cert->chain.get(); + } diff --git a/boring-sys/patches/rpk-patch/ssl/ssl_lib.cc.patch b/boring-sys/patches/rpk-patch/ssl/ssl_lib.cc.patch new file mode 100644 index 00000000..94a824fc --- /dev/null +++ b/boring-sys/patches/rpk-patch/ssl/ssl_lib.cc.patch @@ -0,0 +1,94 @@ +--- google_boringssl/ssl/ssl_lib.cc 2021-02-03 18:29:04.000000000 -0800 ++++ boringssl/ssl/ssl_lib.cc 2021-02-03 20:24:49.000000000 -0800 +@@ -137,6 +137,25 @@ + * SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY + * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR + * OTHERWISE. */ ++/* ==================================================================== ++ * Copyright 2020 Apple Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the “Software”), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom ++ * the Software is furnished to do so, subject to the following conditions: ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS ++ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ + + #include + +@@ -712,6 +731,11 @@ + ssl->config->handoff = ctx->handoff; + ssl->quic_method = ctx->quic_method; + ++ ssl->config->server_certificate_type_list.CopyFrom( ++ ctx->server_certificate_type_list); ++ ssl->config->server_raw_public_key_certificate.CopyFrom( ++ ctx->server_raw_public_key_certificate); ++ + if (!ssl->method->ssl_new(ssl.get()) || + !ssl->ctx->x509_method->ssl_new(ssl->s3->hs.get())) { + return nullptr; +@@ -3124,5 +3148,52 @@ + ctx->legacy_ocsp_callback_arg = arg; + return 1; + } ++ ++int SSL_CTX_set_server_raw_public_key_certificate(SSL_CTX *ctx, ++ const uint8_t *raw_public_key, unsigned raw_public_key_len) { ++ if (!ctx->server_raw_public_key_certificate.CopyFrom( ++ MakeConstSpan(raw_public_key, raw_public_key_len))) { ++ return 0; /* Failure */ ++ } ++ ++ if (!ctx->server_certificate_type_list.Init(1)) { ++ return 0; ++ } ++ ctx->server_certificate_type_list[0] = TLSEXT_CERTIFICATETYPE_RAW_PUBLIC_KEY; ++ ++ return 1; /* Success */ ++} ++ ++int SSL_CTX_has_server_raw_public_key_certificate(SSL_CTX *ctx) { ++ return !ctx->server_raw_public_key_certificate.empty(); ++} ++ ++int SSL_set_server_raw_public_key_certificate(SSL *ssl, ++ const uint8_t *raw_public_key, unsigned raw_public_key_len) { ++ if (!ssl->config) { ++ return 0; /* Failure */ ++ } ++ ++ if (!ssl->config->server_raw_public_key_certificate.CopyFrom( ++ MakeConstSpan(raw_public_key, raw_public_key_len))) { ++ return 0; ++ } ++ ++ if (!ssl->config->server_certificate_type_list.Init(1)) { ++ return 0; ++ } ++ ssl->config->server_certificate_type_list[0] = ++ TLSEXT_CERTIFICATETYPE_RAW_PUBLIC_KEY; ++ ++ return 1; /* Success */ ++} ++ ++int SSL_has_server_raw_public_key_certificate(SSL *ssl) { ++ if (!ssl->config) { ++ return 0; /* Failure */ ++ } ++ ++ return !ssl->config->server_raw_public_key_certificate.empty(); ++} + + namespace fips202205 { + \ No newline at end of file diff --git a/boring-sys/patches/rpk-patch/ssl/tls13_both.cc.patch b/boring-sys/patches/rpk-patch/ssl/tls13_both.cc.patch new file mode 100644 index 00000000..fd4b1813 --- /dev/null +++ b/boring-sys/patches/rpk-patch/ssl/tls13_both.cc.patch @@ -0,0 +1,79 @@ +--- google_boringssl/ssl/tls13_both.cc 2021-02-03 18:29:04.000000000 -0800 ++++ boringssl/ssl/tls13_both.cc 2021-02-03 20:24:49.000000000 -0800 +@@ -11,6 +11,25 @@ + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ ++/* ==================================================================== ++ * Copyright 2020 Apple Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the “Software”), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom ++ * the Software is furnished to do so, subject to the following conditions: ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS ++ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ + + #include + +@@ -203,7 +222,16 @@ + return false; + } + +- if (sk_CRYPTO_BUFFER_num(certs.get()) == 0) { ++ if (hs->server_certificate_type_negotiated && ++ hs->server_certificate_type == TLSEXT_CERTIFICATETYPE_RAW_PUBLIC_KEY) { ++ pkey = UniquePtr(EVP_parse_public_key(&certificate)); ++ if (!pkey) { ++ ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); ++ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); ++ return false; ++ } ++ } ++ else if (sk_CRYPTO_BUFFER_num(certs.get()) == 0) { + pkey = ssl_cert_parse_pubkey(&certificate); + if (!pkey) { + ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); +@@ -319,7 +347,10 @@ + } + + if (sk_CRYPTO_BUFFER_num(hs->new_session->certs.get()) == 0) { +- if (!allow_anonymous) { ++ if (!allow_anonymous && ++ !(hs->server_certificate_type_negotiated && ++ hs->server_certificate_type == ++ TLSEXT_CERTIFICATETYPE_RAW_PUBLIC_KEY)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE); + ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_CERTIFICATE_REQUIRED); + return false; +@@ -436,6 +467,20 @@ + return false; + } + ++ if (hs->server_certificate_type_negotiated && ++ hs->server_certificate_type == TLSEXT_CERTIFICATETYPE_RAW_PUBLIC_KEY) { ++ CBB leaf, extensions; ++ if (!CBB_add_u24_length_prefixed(&certificate_list, &leaf) || ++ !CBB_add_bytes(&leaf, ++ ssl->config->server_raw_public_key_certificate.data(), ++ ssl->config->server_raw_public_key_certificate.size()) || ++ !CBB_add_u16_length_prefixed(&certificate_list, &extensions)) { ++ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); ++ return false; ++ } ++ return ssl_add_message_cbb(ssl, cbb.get()); ++ } ++ + if (!ssl_has_certificate(hs)) { + return ssl_add_message_cbb(ssl, cbb.get()); + } diff --git a/boring-sys/patches/rpk-patch/ssl/tls13_server.cc.patch b/boring-sys/patches/rpk-patch/ssl/tls13_server.cc.patch new file mode 100644 index 00000000..7fab6ad4 --- /dev/null +++ b/boring-sys/patches/rpk-patch/ssl/tls13_server.cc.patch @@ -0,0 +1,38 @@ +--- google_boringssl/ssl/tls13_server.cc 2021-02-03 18:29:04.000000000 -0800 ++++ boringssl/ssl/tls13_server.cc 2021-02-03 20:24:49.000000000 -0800 +@@ -11,6 +11,25 @@ + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ ++/* ==================================================================== ++ * Copyright 2020 Apple Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the “Software”), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom ++ * the Software is furnished to do so, subject to the following conditions: ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS ++ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ + + #include + +@@ -759,7 +778,8 @@ + + // Send the server Certificate message, if necessary. + if (!ssl->s3->session_reused) { +- if (!ssl_has_certificate(hs)) { ++ if (!ssl_has_certificate(hs) && ++ !ssl_has_raw_public_key_certificate(hs)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET); + return ssl_hs_error; + } diff --git a/boring-sys/scripts/apply_pq_patch.sh b/boring-sys/scripts/apply_pq_patch.sh new file mode 100755 index 00000000..cc4b73c0 --- /dev/null +++ b/boring-sys/scripts/apply_pq_patch.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -euo pipefail + +git apply -v --whitespace=fix ../../patches/boring-pq.patch diff --git a/boring-sys/scripts/apply_rpk_patch.sh b/boring-sys/scripts/apply_rpk_patch.sh new file mode 100755 index 00000000..6cca5d17 --- /dev/null +++ b/boring-sys/scripts/apply_rpk_patch.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -euo pipefail + +git apply -v --whitespace=fix ../../../patches/rpk-patch/include/*/*.patch ../../../patches/rpk-patch/ssl/*.patch diff --git a/boring/Cargo.toml b/boring/Cargo.toml index dec24880..ae7d4458 100644 --- a/boring/Cargo.toml +++ b/boring/Cargo.toml @@ -9,7 +9,7 @@ documentation = "https://docs.rs/boring" readme = "README.md" keywords = ["crypto", "tls", "ssl", "dtls"] categories = ["cryptography", "api-bindings"] -edition = "2018" +edition = "2021" [dependencies] bitflags = "1.0" @@ -25,3 +25,9 @@ rusty-hook = "^0.11" [features] # Use a FIPS-validated version of boringssl. fips = ["boring-sys/fips"] + +# Enables Raw public key API (https://datatracker.ietf.org/doc/html/rfc7250) +rpk = ["boring-sys/rpk"] + +# Enables post-quantum crypto (https://blog.cloudflare.com/post-quantum-for-all/) +post-quantum-crypto = ["boring-sys/post-quantum-crypto"] \ No newline at end of file diff --git a/boring/src/ssl/connector.rs b/boring/src/ssl/connector.rs index 1336fafe..7be740d3 100644 --- a/boring/src/ssl/connector.rs +++ b/boring/src/ssl/connector.rs @@ -20,9 +20,19 @@ ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg== -----END DH PARAMETERS----- "; +enum ContextType { + WithMethod(SslMethod), + #[cfg(feature = "rpk")] + Rpk, +} + #[allow(clippy::inconsistent_digit_grouping)] -fn ctx(method: SslMethod) -> Result { - let mut ctx = SslContextBuilder::new(method)?; +fn ctx(ty: ContextType) -> Result { + let mut ctx = match ty { + ContextType::WithMethod(method) => SslContextBuilder::new(method), + #[cfg(feature = "rpk")] + ContextType::Rpk => SslContextBuilder::new_rpk(), + }?; let mut opts = SslOptions::ALL | SslOptions::NO_COMPRESSION @@ -64,7 +74,7 @@ impl SslConnector { /// /// The default configuration is subject to change, and is currently derived from Python. pub fn builder(method: SslMethod) -> Result { - let mut ctx = ctx(method)?; + let mut ctx = ctx(ContextType::WithMethod(method))?; ctx.set_default_verify_paths()?; ctx.set_cipher_list( "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK", @@ -74,6 +84,17 @@ impl SslConnector { Ok(SslConnectorBuilder(ctx)) } + /// Creates a new builder for TLS connections with raw public key. + #[cfg(feature = "rpk")] + pub fn rpk_builder() -> Result { + let mut ctx = ctx(ContextType::Rpk)?; + ctx.set_cipher_list( + "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK", + )?; + + Ok(SslConnectorBuilder(ctx)) + } + /// Initiates a client-side TLS session on a stream. /// /// The domain is used for SNI and hostname verification. @@ -179,7 +200,13 @@ impl ConnectConfiguration { self.ssl.set_hostname(domain)?; } - if self.verify_hostname { + #[cfg(feature = "rpk")] + let verify_hostname = !self.ssl.ssl_context().is_rpk() && self.verify_hostname; + + #[cfg(not(feature = "rpk"))] + let verify_hostname = self.verify_hostname; + + if verify_hostname { setup_verify_hostname(&mut self.ssl, domain)?; } @@ -209,6 +236,21 @@ impl DerefMut for ConnectConfiguration { pub struct SslAcceptor(SslContext); impl SslAcceptor { + /// Creates a new builder configured to connect to clients that support Raw Public Keys. + #[cfg(feature = "rpk")] + pub fn rpk() -> Result { + let mut ctx = ctx(ContextType::Rpk)?; + ctx.set_options(SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1); + let dh = Dh::params_from_pem(FFDHE_2048.as_bytes())?; + ctx.set_tmp_dh(&dh)?; + ctx.set_cipher_list( + "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:\ + ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\ + DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384" + )?; + Ok(SslAcceptorBuilder(ctx)) + } + /// Creates a new builder configured to connect to non-legacy clients. This should generally be /// considered a reasonable default choice. /// @@ -217,7 +259,7 @@ impl SslAcceptor { /// /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS pub fn mozilla_intermediate_v5(method: SslMethod) -> Result { - let mut ctx = ctx(method)?; + let mut ctx = ctx(ContextType::WithMethod(method))?; ctx.set_options(SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1); let dh = Dh::params_from_pem(FFDHE_2048.as_bytes())?; ctx.set_tmp_dh(&dh)?; @@ -238,7 +280,7 @@ impl SslAcceptor { /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS // FIXME remove in next major version pub fn mozilla_intermediate(method: SslMethod) -> Result { - let mut ctx = ctx(method)?; + let mut ctx = ctx(ContextType::WithMethod(method))?; ctx.set_options(SslOptions::CIPHER_SERVER_PREFERENCE); ctx.set_options(SslOptions::NO_TLSV1_3); let dh = Dh::params_from_pem(FFDHE_2048.as_bytes())?; @@ -264,7 +306,7 @@ impl SslAcceptor { /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS // FIXME remove in next major version pub fn mozilla_modern(method: SslMethod) -> Result { - let mut ctx = ctx(method)?; + let mut ctx = ctx(ContextType::WithMethod(method))?; ctx.set_options( SslOptions::CIPHER_SERVER_PREFERENCE | SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1, ); diff --git a/boring/src/ssl/mod.rs b/boring/src/ssl/mod.rs index 2407d290..7baa39b3 100644 --- a/boring/src/ssl/mod.rs +++ b/boring/src/ssl/mod.rs @@ -239,6 +239,12 @@ impl SslMethod { unsafe { SslMethod(TLS_method()) } } + /// Same as `tls`, but doesn't create X509 for certificates. + #[cfg(feature = "rpk")] + pub fn tls_with_buffer() -> SslMethod { + unsafe { SslMethod(ffi::TLS_with_buffers_method()) } + } + /// Support all versions of the DTLS protocol. /// /// This corresponds to `DTLS_method` on OpenSSL 1.1.0 and `DTLSv1_method` @@ -413,6 +419,11 @@ lazy_static! { static ref SESSION_CTX_INDEX: Index = Ssl::new_ex_index().unwrap(); } +#[cfg(feature = "rpk")] +lazy_static! { + static ref RPK_FLAG_INDEX: Index = SslContext::new_ex_index().unwrap(); +} + unsafe extern "C" fn free_data_box( _parent: *mut c_void, ptr: *mut c_void, @@ -656,8 +667,65 @@ pub fn select_next_proto<'a>(server: &[u8], client: &'a [u8]) -> Option<&'a [u8] } } +#[cfg(feature = "rpk")] +extern "C" fn rpk_verify_failure_callback( + _ssl: *mut ffi::SSL, + _out_alert: *mut u8, +) -> ffi::ssl_verify_result_t { + // Always verify the peer. + ffi::ssl_verify_result_t::ssl_verify_invalid +} + /// A builder for `SslContext`s. -pub struct SslContextBuilder(SslContext); +pub struct SslContextBuilder { + ctx: SslContext, + #[cfg(feature = "rpk")] + is_rpk: bool, +} + +#[cfg(feature = "rpk")] +impl SslContextBuilder { + /// Creates a new `SslContextBuilder` to be used with Raw Public Key. + /// + /// This corresponds to [`SSL_CTX_new`]. + /// + /// [`SSL_CTX_new`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_new.html + pub fn new_rpk() -> Result { + unsafe { + init(); + let ctx = cvt_p(ffi::SSL_CTX_new(SslMethod::tls_with_buffer().as_ptr()))?; + + Ok(SslContextBuilder::from_ptr(ctx, true)) + } + } + + /// Sets raw public key certificate in DER format. + pub fn set_rpk_certificate(&mut self, cert: &[u8]) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::SSL_CTX_set_server_raw_public_key_certificate( + self.as_ptr(), + cert.as_ptr(), + cert.len() as u32, + )) + .map(|_| ()) + } + } + + /// Sets RPK null chain private key. + pub fn set_null_chain_private_key(&mut self, key: &PKeyRef) -> Result<(), ErrorStack> + where + T: HasPrivate, + { + unsafe { + cvt(ffi::SSL_CTX_set_nullchain_and_key( + self.as_ptr(), + key.as_ptr(), + ptr::null_mut(), + )) + .map(|_| ()) + } + } +} impl SslContextBuilder { /// Creates a new `SslContextBuilder`. @@ -670,7 +738,15 @@ impl SslContextBuilder { init(); let ctx = cvt_p(ffi::SSL_CTX_new(method.as_ptr()))?; - Ok(SslContextBuilder::from_ptr(ctx)) + #[cfg(feature = "rpk")] + { + Ok(SslContextBuilder::from_ptr(ctx, false)) + } + + #[cfg(not(feature = "rpk"))] + { + Ok(SslContextBuilder::from_ptr(ctx)) + } } } @@ -679,13 +755,31 @@ impl SslContextBuilder { /// # Safety /// /// The caller must ensure that the pointer is valid and uniquely owned by the builder. + #[cfg(feature = "rpk")] + pub unsafe fn from_ptr(ctx: *mut ffi::SSL_CTX, is_rpk: bool) -> SslContextBuilder { + let ctx = SslContext::from_ptr(ctx); + let mut builder = SslContextBuilder { ctx, is_rpk }; + + builder.set_ex_data(*RPK_FLAG_INDEX, is_rpk); + + builder + } + + /// Creates an `SslContextBuilder` from a pointer to a raw OpenSSL value. + /// + /// # Safety + /// + /// The caller must ensure that the pointer is valid and uniquely owned by the builder. + #[cfg(not(feature = "rpk"))] pub unsafe fn from_ptr(ctx: *mut ffi::SSL_CTX) -> SslContextBuilder { - SslContextBuilder(SslContext::from_ptr(ctx)) + SslContextBuilder { + ctx: SslContext::from_ptr(ctx), + } } /// Returns a pointer to the raw OpenSSL value. pub fn as_ptr(&self) -> *mut ffi::SSL_CTX { - self.0.as_ptr() + self.ctx.as_ptr() } /// Configures the certificate verification method for new connections. @@ -694,6 +788,9 @@ impl SslContextBuilder { /// /// [`SSL_CTX_set_verify`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_verify.html pub fn set_verify(&mut self, mode: SslVerifyMode) { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk, "This API is not supported for RPK"); + unsafe { ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits as c_int, None); } @@ -713,6 +810,9 @@ impl SslContextBuilder { where F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send, { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk, "This API is not supported for RPK"); + unsafe { self.set_ex_data(SslContext::cached_ex_index::(), verify); ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits as c_int, Some(raw_verify::)); @@ -757,6 +857,9 @@ impl SslContextBuilder { /// /// [`SSL_CTX_set_verify_depth`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_verify_depth.html pub fn set_verify_depth(&mut self, depth: u32) { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk, "This API is not supported for RPK"); + unsafe { ffi::SSL_CTX_set_verify_depth(self.as_ptr(), depth as c_int); } @@ -768,6 +871,9 @@ impl SslContextBuilder { /// /// [`SSL_CTX_set0_verify_cert_store`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set0_verify_cert_store.html 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)?; @@ -783,6 +889,9 @@ impl SslContextBuilder { /// /// [`SSL_CTX_set_cert_store`]: https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_cert_store.html pub fn set_cert_store(&mut self, cert_store: X509Store) { + #[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); @@ -842,6 +951,9 @@ impl SslContextBuilder { /// /// [`SSL_CTX_set_default_verify_paths`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_default_verify_paths.html pub fn set_default_verify_paths(&mut self) -> Result<(), ErrorStack> { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk, "This API is not supported for RPK"); + unsafe { cvt(ffi::SSL_CTX_set_default_verify_paths(self.as_ptr())).map(|_| ()) } } @@ -853,6 +965,9 @@ impl SslContextBuilder { /// /// [`SSL_CTX_load_verify_locations`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_load_verify_locations.html pub fn set_ca_file>(&mut self, file: P) -> Result<(), ErrorStack> { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk, "This API is not supported for RPK"); + let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap(); unsafe { cvt(ffi::SSL_CTX_load_verify_locations( @@ -873,6 +988,9 @@ impl SslContextBuilder { /// /// [`SSL_CTX_set_client_CA_list`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_client_CA_list.html pub fn set_client_ca_list(&mut self, list: Stack) { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk, "This API is not supported for RPK"); + unsafe { ffi::SSL_CTX_set_client_CA_list(self.as_ptr(), list.as_ptr()); mem::forget(list); @@ -886,6 +1004,9 @@ impl SslContextBuilder { /// /// [`SSL_CTX_add_client_CA`]: https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_client_CA_list.html pub fn add_client_ca(&mut self, cacert: &X509Ref) -> Result<(), ErrorStack> { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk, "This API is not supported for RPK"); + unsafe { cvt(ffi::SSL_CTX_add_client_CA(self.as_ptr(), cacert.as_ptr())).map(|_| ()) } } @@ -927,6 +1048,9 @@ impl SslContextBuilder { file: P, file_type: SslFiletype, ) -> Result<(), ErrorStack> { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk, "This API is not supported for RPK"); + let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap(); unsafe { cvt(ffi::SSL_CTX_use_certificate_file( @@ -981,6 +1105,9 @@ impl SslContextBuilder { /// /// [`SSL_CTX_add_extra_chain_cert`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_add_extra_chain_cert.html pub fn add_extra_chain_cert(&mut self, cert: X509) -> Result<(), ErrorStack> { + #[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); @@ -1261,6 +1388,9 @@ impl SslContextBuilder { /// /// [`SSL_CTX_get_cert_store`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_cert_store.html pub fn cert_store(&self) -> &X509StoreBuilderRef { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk, "This API is not supported for RPK"); + unsafe { X509StoreBuilderRef::from_ptr(ffi::SSL_CTX_get_cert_store(self.as_ptr())) } } @@ -1270,6 +1400,9 @@ impl SslContextBuilder { /// /// [`SSL_CTX_get_cert_store`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_cert_store.html pub fn cert_store_mut(&mut self) -> &mut X509StoreBuilderRef { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk, "This API is not supported for RPK"); + unsafe { X509StoreBuilderRef::from_ptr_mut(ffi::SSL_CTX_get_cert_store(self.as_ptr())) } } @@ -1571,7 +1704,7 @@ impl SslContextBuilder { /// Consumes the builder, returning a new `SslContext`. pub fn build(self) -> SslContext { - self.0 + self.ctx } } @@ -1650,6 +1783,9 @@ impl SslContextRef { /// /// [`SSL_CTX_get0_certificate`]: https://www.openssl.org/docs/man1.1.0/ssl/ssl.html pub fn certificate(&self) -> Option<&X509Ref> { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk(), "This API is not supported for RPK"); + unsafe { let ptr = ffi::SSL_CTX_get0_certificate(self.as_ptr()); if ptr.is_null() { @@ -1682,6 +1818,9 @@ impl SslContextRef { /// /// [`SSL_CTX_get_cert_store`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_cert_store.html pub fn cert_store(&self) -> &X509StoreRef { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk(), "This API is not supported for RPK"); + unsafe { X509StoreRef::from_ptr(ffi::SSL_CTX_get_cert_store(self.as_ptr())) } } @@ -1764,9 +1903,18 @@ impl SslContextRef { /// [`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 pub fn verify_mode(&self) -> SslVerifyMode { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk(), "This API is not supported for RPK"); + let mode = unsafe { ffi::SSL_CTX_get_verify_mode(self.as_ptr()) }; SslVerifyMode::from_bits(mode).expect("SSL_CTX_get_verify_mode returned invalid mode") } + + /// Returns `true` if context was created for Raw Public Key verification + #[cfg(feature = "rpk")] + pub fn is_rpk(&self) -> bool { + self.ex_data(*RPK_FLAG_INDEX).copied().unwrap_or_default() + } } #[cfg(not(feature = "fips"))] @@ -2176,16 +2324,40 @@ impl Ssl { where S: Read + Write, { + #[cfg(feature = "rpk")] + { + let ctx = self.ssl_context(); + + if ctx.is_rpk() { + unsafe { + ffi::SSL_CTX_set_custom_verify( + ctx.as_ptr(), + SslVerifyMode::PEER.bits(), + Some(rpk_verify_failure_callback), + ); + } + } + } + SslStreamBuilder::new(self, stream).accept() } } impl fmt::Debug for SslRef { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("Ssl") - .field("state", &self.state_string_long()) - .field("verify_result", &self.verify_result()) - .finish() + let mut builder = fmt.debug_struct("Ssl"); + + builder.field("state", &self.state_string_long()); + + #[cfg(feature = "rpk")] + if !self.ssl_context().is_rpk() { + builder.field("verify_result", &self.verify_result()); + } + + #[cfg(not(feature = "rpk"))] + builder.field("verify_result", &self.verify_result()); + + builder.finish() } } @@ -2215,6 +2387,12 @@ impl SslRef { /// [`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 pub fn set_verify(&mut self, mode: SslVerifyMode) { + #[cfg(feature = "rpk")] + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); + unsafe { ffi::SSL_set_verify(self.as_ptr(), mode.bits as c_int, None) } } @@ -2224,6 +2402,12 @@ impl SslRef { /// /// [`SSL_get_verify_mode`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_get_verify_mode.html pub fn verify_mode(&self) -> SslVerifyMode { + #[cfg(feature = "rpk")] + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); + let mode = unsafe { ffi::SSL_get_verify_mode(self.as_ptr()) }; SslVerifyMode::from_bits(mode).expect("SSL_get_verify_mode returned invalid mode") } @@ -2238,6 +2422,12 @@ impl SslRef { where F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send, { + #[cfg(feature = "rpk")] + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); + unsafe { // this needs to be in an Arc since the callback can register a new callback! self.set_ex_data(Ssl::cached_ex_index(), Arc::new(verify)); @@ -2356,6 +2546,12 @@ impl SslRef { /// /// [`SSL_get_peer_certificate`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_peer_certificate.html pub fn peer_certificate(&self) -> Option { + #[cfg(feature = "rpk")] + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); + unsafe { let ptr = ffi::SSL_get_peer_certificate(self.as_ptr()); if ptr.is_null() { @@ -2375,6 +2571,12 @@ impl SslRef { /// /// [`SSL_get_peer_cert_chain`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_peer_cert_chain.html pub fn peer_cert_chain(&self) -> Option<&StackRef> { + #[cfg(feature = "rpk")] + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); + unsafe { let ptr = ffi::SSL_get_peer_cert_chain(self.as_ptr()); if ptr.is_null() { @@ -2391,6 +2593,12 @@ impl SslRef { /// /// [`SslContext::certificate`]: struct.SslContext.html#method.certificate pub fn certificate(&self) -> Option<&X509Ref> { + #[cfg(feature = "rpk")] + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); + unsafe { let ptr = ffi::SSL_get_certificate(self.as_ptr()); if ptr.is_null() { @@ -2614,6 +2822,12 @@ impl SslRef { /// /// [`SSL_get0_param`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_get0_param.html pub fn param_mut(&mut self) -> &mut X509VerifyParamRef { + #[cfg(feature = "rpk")] + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); + unsafe { X509VerifyParamRef::from_ptr_mut(ffi::SSL_get0_param(self.as_ptr())) } } @@ -2623,6 +2837,12 @@ impl SslRef { /// /// [`SSL_get_verify_result`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_get_verify_result.html pub fn verify_result(&self) -> X509VerifyResult { + #[cfg(feature = "rpk")] + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); + unsafe { X509VerifyResult::from_raw(ffi::SSL_get_verify_result(self.as_ptr()) as c_int) } } diff --git a/hyper-boring/Cargo.toml b/hyper-boring/Cargo.toml index 8ca02969..f557c5f9 100644 --- a/hyper-boring/Cargo.toml +++ b/hyper-boring/Cargo.toml @@ -2,7 +2,7 @@ name = "hyper-boring" version = "2.1.2" authors = ["Steven Fackler ", "Ivan Nikulin "] -edition = "2018" +edition = "2021" description = "Hyper TLS support via BoringSSL" license = "MIT/Apache-2.0" repository = "https://github.com/cloudflare/boring" @@ -14,8 +14,17 @@ exclude = ["test/*"] default = ["runtime"] runtime = ["hyper/runtime"] + +# Use a FIPS-validated version of boringssl. fips = ["tokio-boring/fips"] +# Enables Raw public key API (https://datatracker.ietf.org/doc/html/rfc7250) +rpk = ["tokio-boring/rpk"] + +# Enables post-quantum crypto (https://blog.cloudflare.com/post-quantum-for-all/) +post-quantum-crypto = ["tokio-boring/post-quantum-crypto"] + + [dependencies] antidote = "1.0.0" http = "0.2" diff --git a/tokio-boring/Cargo.toml b/tokio-boring/Cargo.toml index 57fa8b30..ad09adaf 100644 --- a/tokio-boring/Cargo.toml +++ b/tokio-boring/Cargo.toml @@ -3,7 +3,7 @@ name = "tokio-boring" version = "2.1.5" authors = ["Alex Crichton ", "Ivan Nikulin "] license = "MIT/Apache-2.0" -edition = "2018" +edition = "2021" repository = "https://github.com/cloudflare/boring" homepage = "https://github.com/cloudflare/boring" documentation = "https://docs.rs/tokio-boring" @@ -22,4 +22,11 @@ tokio = { version = "1", features = ["full"] } anyhow = "1" [features] +# Use a FIPS-validated version of boringssl. fips = ["boring/fips"] + +# Enables Raw public key API (https://datatracker.ietf.org/doc/html/rfc7250) +rpk = ["boring/rpk"] + +# Enables post-quantum crypto (https://blog.cloudflare.com/post-quantum-for-all/) +post-quantum-crypto = ["boring/post-quantum-crypto"] diff --git a/tokio-boring/tests/google.rs b/tokio-boring/tests/client_server.rs similarity index 100% rename from tokio-boring/tests/google.rs rename to tokio-boring/tests/client_server.rs diff --git a/tokio-boring/tests/pubkey.der b/tokio-boring/tests/pubkey.der new file mode 100644 index 00000000..7e5944f1 Binary files /dev/null and b/tokio-boring/tests/pubkey.der differ diff --git a/tokio-boring/tests/pubkey2.der b/tokio-boring/tests/pubkey2.der new file mode 100644 index 00000000..c1142b4b Binary files /dev/null and b/tokio-boring/tests/pubkey2.der differ diff --git a/tokio-boring/tests/rpk.rs b/tokio-boring/tests/rpk.rs new file mode 100644 index 00000000..2fb90862 --- /dev/null +++ b/tokio-boring/tests/rpk.rs @@ -0,0 +1,106 @@ +#[cfg(feature = "rpk")] +mod test_rpk { + use boring::pkey::PKey; + use boring::ssl::{SslAcceptor, SslConnector}; + use futures::future; + use std::future::Future; + use std::net::SocketAddr; + use std::pin::Pin; + use tokio::io::{AsyncReadExt, AsyncWrite, AsyncWriteExt}; + use tokio::net::{TcpListener, TcpStream}; + use tokio_boring::{HandshakeError, SslStream}; + + fn create_server() -> ( + impl Future, HandshakeError>>, + SocketAddr, + ) { + let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); + + listener.set_nonblocking(true).unwrap(); + + let listener = TcpListener::from_std(listener).unwrap(); + let addr = listener.local_addr().unwrap(); + + let server = async move { + let mut acceptor = SslAcceptor::rpk().unwrap(); + let pkey = std::fs::read("tests/key.pem").unwrap(); + let pkey = PKey::private_key_from_pem(&pkey).unwrap(); + let cert = std::fs::read("tests/pubkey.der").unwrap(); + + acceptor.set_rpk_certificate(&cert).unwrap(); + acceptor.set_null_chain_private_key(&pkey).unwrap(); + + let acceptor = acceptor.build(); + + let stream = listener.accept().await.unwrap().0; + + tokio_boring::accept(&acceptor, stream).await + }; + + (server, addr) + } + + #[tokio::test] + async fn server_rpk() { + let (stream, addr) = create_server(); + + let server = async { + let mut stream = stream.await.unwrap(); + let mut buf = [0; 4]; + stream.read_exact(&mut buf).await.unwrap(); + assert_eq!(&buf, b"asdf"); + + stream.write_all(b"jkl;").await.unwrap(); + + future::poll_fn(|ctx| Pin::new(&mut stream).poll_shutdown(ctx)) + .await + .unwrap(); + }; + + let client = async { + let mut connector = SslConnector::rpk_builder().unwrap(); + let cert = std::fs::read("tests/pubkey.der").unwrap(); + + connector.set_rpk_certificate(&cert).unwrap(); + let config = connector.build().configure().unwrap(); + + let stream = TcpStream::connect(&addr).await.unwrap(); + let mut stream = tokio_boring::connect(config, "localhost", stream) + .await + .unwrap(); + + stream.write_all(b"asdf").await.unwrap(); + + let mut buf = vec![]; + stream.read_to_end(&mut buf).await.unwrap(); + assert_eq!(buf, b"jkl;"); + }; + + future::join(server, client).await; + } + + #[tokio::test] + async fn client_rpk_unknown_cert() { + let (stream, addr) = create_server(); + + let server = async { + assert!(stream.await.is_err()); + }; + + let client = async { + let mut connector = SslConnector::rpk_builder().unwrap(); + let cert = std::fs::read("tests/pubkey2.der").unwrap(); + + connector.set_rpk_certificate(&cert).unwrap(); + let config = connector.build().configure().unwrap(); + + let stream = TcpStream::connect(&addr).await.unwrap(); + + assert!(tokio_boring::connect(config, "localhost", stream) + .await + .is_err()); + }; + + future::join(server, client).await; + } +}