diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ffbead3..f74f2500 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,8 +25,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Install Rust - run: rustup update stable && rustup default stable + - name: Install Rustfmt + run: rustup default stable && rustup component add rustfmt - name: Check formatting run: cargo fmt --all -- --check @@ -38,10 +38,11 @@ jobs: with: submodules: 'recursive' - name: Install Rust - run: rustup update stable && rustup default stable + run: rustup update --no-self-update stable && rustup default stable && rustup component add clippy - name: Get rust version id: rust-version - run: echo "::set-output name=version::$(rustc --version)" + run: | + echo "version=$(rustc --version)" >> $GITHUB_OUTPUT - name: Cache cargo index uses: actions/cache@v4 with: @@ -157,8 +158,8 @@ jobs: apt_packages: gcc-arm-linux-gnueabi g++-arm-linux-gnueabi check_only: true custom_env: - CC: arm-linux-gnueabi-gcc - CXX: arm-linux-gnueabi-g++ + CC_arm-unknown-linux-gnueabi: arm-linux-gnueabi-gcc + CXX_arm-unknown-linux-gnueabi: arm-linux-gnueabi-g++ CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABI_LINKER: arm-linux-gnueabi-g++ extra_test_args: --workspace --exclude compio-boring2 - thing: aarch64-linux @@ -168,8 +169,8 @@ jobs: apt_packages: crossbuild-essential-arm64 check_only: true custom_env: - CC: aarch64-linux-gnu-gcc - CXX: aarch64-linux-gnu-g++ + CC_aarch64_unknown_linux_gnu: aarch64-linux-gnu-gcc + CXX_aarch64_unknown_linux_gnu: aarch64-linux-gnu-g++ CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-g++ - thing: arm64-macos target: aarch64-apple-darwin @@ -214,6 +215,10 @@ jobs: # run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} # shell: bash - run: rustup target add ${{ matrix.target }} + - name: Install golang + uses: actions/setup-go@v5 + with: + go-version: '>=1.22.0' - name: Install target-specific APT dependencies if: "matrix.apt_packages != ''" run: sudo apt update && sudo apt install -y ${{ matrix.apt_packages }} @@ -272,6 +277,10 @@ jobs: - name: Install Rust (rustup) run: rustup update stable --no-self-update && rustup default stable && rustup target add ${{ matrix.target }} shell: bash + - name: Install golang + uses: actions/setup-go@v5 + with: + go-version: '>=1.22.0' - name: Install ${{ matrix.target }} toolchain run: brew tap messense/macos-cross-toolchains && brew install ${{ matrix.target }} - name: Set BORING_BSSL_SYSROOT @@ -291,14 +300,10 @@ jobs: submodules: 'recursive' - name: Install Rust (rustup) run: rustup update stable --no-self-update && rustup default stable - - run: cargo test --features pq-experimental - name: Run `pq-experimental` tests - - run: cargo test --features kx-safe-default,pq-experimental - name: Run `kx-safe-default` tests - - run: cargo test --features pq-experimental,underscore-wildcards - name: Run `pq-experimental,underscore-wildcards` tests - - run: cargo test --features underscore-wildcards - name: Run `underscore-wildcards` tests + - name: Run `kx-safe-default` tests + run: cargo test --features kx-safe-default + - name: Run `underscore-wildcards` tests + run: cargo test --features underscore-wildcards crates: name: crates diff --git a/.gitmodules b/.gitmodules index 93bfb089..c1e5a527 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,6 +2,3 @@ path = boring-sys/deps/boringssl url = https://github.com/google/boringssl.git ignore = dirty -[submodule "boring-sys/deps/boringssl-fips"] - path = boring-sys/deps/boringssl-fips - url = https://github.com/google/boringssl.git diff --git a/Cargo.toml b/Cargo.toml index ac62a011..ea0bba5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,11 +26,12 @@ tokio-boring = { package = "tokio-boring2", version = "5.0.0-alpha.10", path = " compio-boring = { package = "compio-boring2", version = "5.0.0-alpha.10", path = "./compio-boring" } bindgen = { version = "0.72.0", default-features = false, features = ["runtime"] } +bitflags = "2.9" +brotli = "8.0" bytes = "1" -cmake = "0.1.18" +cmake = "0.1.54" fs_extra = "1.3.0" fslock = "0.2" -bitflags = "2.4" foreign-types = "0.5" libc = "0.2" hex = "0.4" @@ -42,6 +43,5 @@ antidote = "1.0.0" linked_hash_set = "0.1" openssl-macros = "0.1.1" autocfg = "1.3.0" -brotli = "8" compio = { version = "0.16.0" } compio-io = { version = "0.8.0" } diff --git a/boring-sys/Cargo.toml b/boring-sys/Cargo.toml index 67c66d85..15e6025e 100644 --- a/boring-sys/Cargo.toml +++ b/boring-sys/Cargo.toml @@ -13,13 +13,12 @@ build = "build/main.rs" readme = "README.md" categories = ["cryptography", "external-ffi-bindings"] edition = { workspace = true } +rust-version = "1.77" include = [ "/*.md", "/*.toml", "/LICENSE-MIT", "/cmake/*.cmake", - # boringssl (non-FIPS) - "/deps/boringssl/src/util/32-bit-toolchain.cmake", "/deps/boringssl/**/*.[chS]", "/deps/boringssl/**/*.asm", "/deps/boringssl/sources.json", @@ -27,34 +26,18 @@ include = [ "/deps/boringssl/src/crypto/obj/objects.txt", "/deps/boringssl/src/util/32-bit-toolchain.cmake", "/deps/boringssl/**/*.bzl", - "/deps/boringssl/src/**/*.cc", + "/deps/boringssl/**/*.cc", "/deps/boringssl/**/CMakeLists.txt", "/deps/boringssl/**/sources.cmake", + "/deps/boringssl/**/util/go_tests.txt", "/deps/boringssl/LICENSE", - # boringssl (FIPS) - "/deps/boringssl-fips/src/util/32-bit-toolchain.cmake", - "/deps/boringssl-fips/**/*.[chS]", - "/deps/boringssl-fips/**/*.asm", - "/deps/boringssl-fips/**/*.pl", - "/deps/boringssl-fips/**/*.go", - "/deps/boringssl-fips/**/go.mod", - "/deps/boringssl-fips/**/go.sum", - "/deps/boringssl-fips/sources.json", - "/deps/boringssl-fips/crypto/obj/obj_mac.num", - "/deps/boringssl-fips/crypto/obj/objects.txt", - "/deps/boringssl-fips/crypto/err/*.errordata", - "/deps/boringssl-fips/**/*.bzl", - "/deps/boringssl-fips/**/*.cc", - "/deps/boringssl-fips/**/CMakeLists.txt", - "/deps/boringssl-fips/**/sources.cmake", - "/deps/boringssl-fips/LICENSE", "/build/*", "/src", "/patches", ] [package.metadata.docs.rs] -features = ["pq-experimental", "underscore-wildcards"] +features = ["underscore-wildcards"] rustdoc-args = ["--cfg", "docsrs"] [features] @@ -66,28 +49,15 @@ rustdoc-args = ["--cfg", "docsrs"] # for instructions and more details on the boringssl FIPS flag. fips = [] -# Use a precompiled FIPS-validated version of BoringSSL. Meant to be used with -# FIPS-20230428 or newer. Users must set `BORING_BSSL_FIPS_PATH` to use this -# feature, or else the build will fail. -fips-precompiled = [] - -# Link with precompiled FIPS-validated `bcm.o` module. -fips-link-precompiled = [] - -# Applies a patch (`patches/boring-pq.patch`) to the boringSSL source code that -# enables support for PQ key exchange. This feature is necessary in order to -# compile the bindings for the default branch of boringSSL (`deps/boringssl`). -# Alternatively, a version of boringSSL that implements the same feature set -# can be provided by setting `BORING_BSSL{,_FIPS}_SOURCE_PATH`. -pq-experimental = [] - # Applies a patch (`patches/underscore-wildcards.patch`) to enable -# `ffi::X509_CHECK_FLAG_UNDERSCORE_WILDCARDS`. Same caveats as -# those for `pq-experimental` feature apply. +# `ffi::X509_CHECK_FLAG_UNDERSCORE_WILDCARDS`. This feature is necessary in +# order to compile the bindings for the default branch of boringSSL +# (`deps/boringssl`). Alternatively, a version of boringSSL that implements the +# same feature set can be provided by setting +# `BORING_BSSL{,_FIPS}_SOURCE_PATH`. underscore-wildcards = [] [build-dependencies] -autocfg = { workspace = true } bindgen = { workspace = true } cmake = { workspace = true } fs_extra = { workspace = true } diff --git a/boring-sys/build/config.rs b/boring-sys/build/config.rs index c40f611d..25aaabf4 100644 --- a/boring-sys/build/config.rs +++ b/boring-sys/build/config.rs @@ -16,9 +16,6 @@ pub(crate) struct Config { pub(crate) struct Features { pub(crate) fips: bool, - pub(crate) fips_precompiled: bool, - pub(crate) fips_link_precompiled: bool, - pub(crate) pq_experimental: bool, pub(crate) rpk: bool, pub(crate) underscore_wildcards: bool, } @@ -27,7 +24,6 @@ pub(crate) struct Env { pub(crate) path: Option, pub(crate) include_path: Option, pub(crate) source_path: Option, - pub(crate) precompiled_bcm_o: Option, pub(crate) assume_patched: bool, pub(crate) sysroot: Option, pub(crate) compiler_external_toolchain: Option, @@ -36,6 +32,9 @@ pub(crate) struct Env { pub(crate) android_ndk_home: Option, pub(crate) cmake_toolchain_file: Option, pub(crate) cpp_runtime_lib: Option, + /// C compiler (ignored if using FIPS) + pub(crate) cc: Option, + pub(crate) cxx: Option, pub(crate) docs_rs: bool, } @@ -51,10 +50,10 @@ impl Config { let features = Features::from_env(); let env = Env::from_env(&host, &target, features.is_fips_like()); - let mut is_bazel = false; - if let Some(src_path) = &env.source_path { - is_bazel = src_path.join("src").exists(); - } + let is_bazel = env + .source_path + .as_ref() + .is_some_and(|path| path.join("src").exists()); let config = Self { manifest_dir, @@ -78,10 +77,6 @@ impl Config { panic!("`fips` and `rpk` features are mutually exclusive"); } - if self.features.fips_precompiled && self.features.rpk { - panic!("`fips-precompiled` and `rpk` features are mutually exclusive"); - } - let is_precompiled_native_lib = self.env.path.is_some(); let is_external_native_lib_source = !is_precompiled_native_lib && self.env.source_path.is_none(); @@ -93,9 +88,7 @@ impl Config { ); } - let features_with_patches_enabled = self.features.rpk - || self.features.pq_experimental - || self.features.underscore_wildcards; + let features_with_patches_enabled = self.features.rpk || self.features.underscore_wildcards; let patches_required = features_with_patches_enabled && !self.env.assume_patched; @@ -104,60 +97,41 @@ impl Config { "cargo:warning=precompiled BoringSSL was provided, so patches will be ignored" ); } - - // todo(rmehra): should this even be a restriction? why not let people link a custom bcm.o? - // precompiled boringssl will include libcrypto.a - if is_precompiled_native_lib && self.features.fips_link_precompiled { - panic!("precompiled BoringSSL was provided, so FIPS configuration can't be applied"); - } - - if !is_precompiled_native_lib && self.features.fips_precompiled { - panic!("`fips-precompiled` feature requires `BORING_BSSL_FIPS_PATH` to be set"); - } } } impl Features { fn from_env() -> Self { let fips = env::var_os("CARGO_FEATURE_FIPS").is_some(); - let fips_precompiled = env::var_os("CARGO_FEATURE_FIPS_PRECOMPILED").is_some(); - let fips_link_precompiled = env::var_os("CARGO_FEATURE_FIPS_LINK_PRECOMPILED").is_some(); - let pq_experimental = env::var_os("CARGO_FEATURE_PQ_EXPERIMENTAL").is_some(); let rpk = env::var_os("CARGO_FEATURE_RPK").is_some(); let underscore_wildcards = env::var_os("CARGO_FEATURE_UNDERSCORE_WILDCARDS").is_some(); Self { fips, - fips_precompiled, - fips_link_precompiled, - pq_experimental, rpk, underscore_wildcards, } } pub(crate) fn is_fips_like(&self) -> bool { - self.fips || self.fips_precompiled || self.fips_link_precompiled + self.fips } } impl Env { - fn from_env(target: &str, host: &str, is_fips_like: bool) -> Self { + fn from_env(host: &str, target: &str, is_fips_like: bool) -> Self { const NORMAL_PREFIX: &str = "BORING_BSSL"; const FIPS_PREFIX: &str = "BORING_BSSL_FIPS"; + let var_prefix = if host == target { "HOST" } else { "TARGET" }; let target_with_underscores = target.replace('-', "_"); - // Logic stolen from cmake-rs. - let target_var = |name: &str| { - let kind = if host == target { "HOST" } else { "TARGET" }; - - // TODO(rmehra): look for just `name` first, as most people just set that + let target_only_var = |name: &str| { var(&format!("{name}_{target}")) .or_else(|| var(&format!("{name}_{target_with_underscores}"))) - .or_else(|| var(&format!("{kind}_{name}"))) - .or_else(|| var(name)) + .or_else(|| var(&format!("{var_prefix}_{name}"))) }; + let target_var = |name: &str| target_only_var(name).or_else(|| var(name)); let boringssl_var = |name: &str| { // The passed name is the non-fips version of the environment variable, @@ -175,7 +149,6 @@ impl Env { path: boringssl_var("BORING_BSSL_PATH").map(PathBuf::from), include_path: boringssl_var("BORING_BSSL_INCLUDE_PATH").map(PathBuf::from), source_path: boringssl_var("BORING_BSSL_SOURCE_PATH").map(PathBuf::from), - precompiled_bcm_o: boringssl_var("BORING_BSSL_PRECOMPILED_BCM_O").map(PathBuf::from), assume_patched: boringssl_var("BORING_BSSL_ASSUME_PATCHED") .is_some_and(|v| !v.is_empty()), sysroot: boringssl_var("BORING_BSSL_SYSROOT").map(PathBuf::from), @@ -186,6 +159,9 @@ impl Env { android_ndk_home: target_var("ANDROID_NDK_HOME").map(Into::into), cmake_toolchain_file: target_var("CMAKE_TOOLCHAIN_FILE").map(Into::into), cpp_runtime_lib: target_var("BORING_BSSL_RUST_CPPLIB"), + // matches the `cc` crate + cc: target_only_var("CC"), + cxx: target_only_var("CXX"), docs_rs: var("DOCS_RS").is_some(), } } diff --git a/boring-sys/build/main.rs b/boring-sys/build/main.rs index 84b67d70..251149dc 100644 --- a/boring-sys/build/main.rs +++ b/boring-sys/build/main.rs @@ -50,6 +50,7 @@ const CMAKE_PARAMS_APPLE: &[(&str, &[(&str, &str)])] = &[ &[ ("CMAKE_OSX_ARCHITECTURES", "arm64"), ("CMAKE_OSX_SYSROOT", "iphoneos"), + ("CMAKE_MACOSX_BUNDLE", "OFF"), ], ), ( @@ -57,6 +58,7 @@ const CMAKE_PARAMS_APPLE: &[(&str, &[(&str, &str)])] = &[ &[ ("CMAKE_OSX_ARCHITECTURES", "arm64"), ("CMAKE_OSX_SYSROOT", "iphonesimulator"), + ("CMAKE_MACOSX_BUNDLE", "OFF"), ], ), ( @@ -64,6 +66,7 @@ const CMAKE_PARAMS_APPLE: &[(&str, &[(&str, &str)])] = &[ &[ ("CMAKE_OSX_ARCHITECTURES", "x86_64"), ("CMAKE_OSX_SYSROOT", "iphonesimulator"), + ("CMAKE_MACOSX_BUNDLE", "OFF"), ], ), // macOS @@ -114,11 +117,7 @@ fn get_boringssl_source_path(config: &Config) -> &PathBuf { static SOURCE_PATH: OnceLock = OnceLock::new(); SOURCE_PATH.get_or_init(|| { - let submodule_dir = if config.features.fips { - "boringssl-fips" - } else { - "boringssl" - }; + let submodule_dir = "boringssl"; let src_path = config.out_dir.join(submodule_dir); @@ -152,7 +151,7 @@ fn get_boringssl_source_path(config: &Config) -> &PathBuf { /// /// MSVC generator on Windows place static libs in a target sub-folder, /// so adjust library location based on platform and build target. -/// See issue: https://github.com/alexcrichton/cmake-rs/issues/18 +/// See issue: fn get_boringssl_platform_output_path(config: &Config) -> String { if config.target.ends_with("-msvc") { // Code under this branch should match the logic in cmake-rs @@ -193,7 +192,7 @@ fn get_boringssl_platform_output_path(config: &Config) -> String { } } -/// Returns a new cmake::Config for building BoringSSL. +/// Returns a new `cmake::Config` for building BoringSSL. /// /// It will add platform-specific parameters if needed. fn get_boringssl_cmake_config(config: &Config) -> cmake::Config { @@ -216,6 +215,15 @@ fn get_boringssl_cmake_config(config: &Config) -> cmake::Config { .define("CMAKE_ASM_COMPILER_TARGET", &config.target); } + if !config.features.fips { + if let Some(cc) = &config.env.cc { + boringssl_cmake.define("CMAKE_C_COMPILER", cc); + } + if let Some(cxx) = &config.env.cxx { + boringssl_cmake.define("CMAKE_CXX_COMPILER", cxx); + } + } + if let Some(sysroot) = &config.env.sysroot { boringssl_cmake.define("CMAKE_SYSROOT", sysroot); } @@ -295,7 +303,7 @@ fn get_boringssl_cmake_config(config: &Config) -> cmake::Config { config .manifest_dir .join(src_path) - .join("src/util/32-bit-toolchain.cmake") + .join("util/32-bit-toolchain.cmake") .as_os_str(), ); } @@ -331,55 +339,6 @@ fn get_boringssl_cmake_config(config: &Config) -> cmake::Config { boringssl_cmake } -/// Verify that the toolchains match https://csrc.nist.gov/CSRC/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp3678.pdf -/// See "Installation Instructions" under section 12.1. -// TODO: maybe this should also verify the Go and Ninja versions? But those haven't been an issue in practice ... -fn verify_fips_clang_version() -> (&'static str, &'static str) { - fn version(tool: &str) -> Option { - let output = match Command::new(tool).arg("--version").output() { - Ok(o) => o, - Err(e) => { - println!("cargo:warning=missing {tool}, trying other compilers: {e}"); - // NOTE: hard-codes that the loop below checks the version - return None; - } - }; - if !output.status.success() { - return Some(String::new()); - } - let output = std::str::from_utf8(&output.stdout).expect("invalid utf8 output"); - Some(output.lines().next().expect("empty output").to_string()) - } - - const REQUIRED_CLANG_VERSION: &str = "12.0.0"; - for (cc, cxx) in [ - ("clang-12", "clang++-12"), - ("clang", "clang++"), - ("cc", "c++"), - ] { - let (Some(cc_version), Some(cxx_version)) = (version(cc), version(cxx)) else { - continue; - }; - - if cc_version.contains(REQUIRED_CLANG_VERSION) { - assert!( - cxx_version.contains(REQUIRED_CLANG_VERSION), - "mismatched versions of cc and c++" - ); - return (cc, cxx); - } else if cc == "cc" { - panic!( - "unsupported clang version \"{cc_version}\": FIPS requires clang {REQUIRED_CLANG_VERSION}" - ); - } else if !cc_version.is_empty() { - println!( - "cargo:warning=FIPS requires clang version {REQUIRED_CLANG_VERSION}, skipping incompatible version \"{cc_version}\"" - ); - } - } - unreachable!() -} - fn pick_best_android_ndk_toolchain(toolchains_dir: &Path) -> std::io::Result { let toolchains = std::fs::read_dir(toolchains_dir)?.collect::, _>>()?; // First look for one of the toolchains that Google has documented. @@ -475,14 +434,12 @@ fn ensure_patches_applied(config: &Config) -> io::Result<()> { ); return Ok(()); } else if config.env.source_path.is_some() - && (config.features.rpk - || config.features.pq_experimental - || config.features.underscore_wildcards) + && (config.features.rpk || config.features.underscore_wildcards) { panic!( "BORING_BSSL_ASSUME_PATCHED must be set when setting BORING_BSSL_SOURCE_PATH and using any of the following - features: rpk, pq-experimental, underscore-wildcards" + features: rpk, underscore-wildcards" ); } @@ -581,66 +538,17 @@ fn built_boring_source_path(config: &Config) -> &PathBuf { } if config.features.fips { - let (clang, clangxx) = verify_fips_clang_version(); - cfg.define("CMAKE_C_COMPILER", clang) - .define("CMAKE_CXX_COMPILER", clangxx) - .define("CMAKE_ASM_COMPILER", clang) + cfg.define("CMAKE_C_COMPILER", "clang") + .define("CMAKE_CXX_COMPILER", "clang++") + .define("CMAKE_ASM_COMPILER", "clang") .define("FIPS", "1"); } - if config.features.fips_link_precompiled { - cfg.define("FIPS", "1"); - } - cfg.build_target("ssl").build(); cfg.build_target("crypto").build() }) } -fn link_in_precompiled_bcm_o(config: &Config) { - println!("cargo:warning=linking in precompiled `bcm.o` module"); - - let bssl_dir = built_boring_source_path(config); - let bcm_o_src_path = config.env.precompiled_bcm_o.as_ref() - .expect("`fips-link-precompiled` requires `BORING_BSSL_FIPS_PRECOMPILED_BCM_O` env variable to be specified"); - - let libcrypto_path = bssl_dir - .join("build/crypto/libcrypto.a") - .canonicalize() - .unwrap(); - - let bcm_o_dst_path = bssl_dir.join("build/bcm-fips.o"); - - fs::copy(bcm_o_src_path, &bcm_o_dst_path).unwrap(); - - // check that fips module is named as expected - let out = run_command( - Command::new("ar") - .arg("t") - .arg(&libcrypto_path) - .arg("bcm.o"), - ) - .unwrap(); - - assert_eq!( - String::from_utf8(out.stdout).unwrap().trim(), - "bcm.o", - "failed to verify FIPS module name" - ); - - // insert fips bcm.o before bcm.o into libcrypto.a, - // so for all duplicate symbols the older fips bcm.o is used - // (this causes the need for extra linker flags to deal with duplicate symbols) - // (as long as the newer module does not define new symbols, one may also remove it, - // but once there are new symbols it would cause missing symbols at linking stage) - run_command( - Command::new("ar") - .args(["rb", "bcm.o"]) - .args([&libcrypto_path, &bcm_o_dst_path]), - ) - .unwrap(); -} - fn get_cpp_runtime_lib(config: &Config) -> Option { if let Some(ref cpp_lib) = config.env.cpp_runtime_lib { return cpp_lib.clone().into_string().ok(); @@ -699,10 +607,6 @@ fn emit_link_directives(config: &Config) { ); } - if config.features.fips_link_precompiled { - link_in_precompiled_bcm_o(config); - } - if let Some(cpp_lib) = get_cpp_runtime_lib(config) { println!("cargo:rustc-link-lib={cpp_lib}"); } @@ -731,12 +635,8 @@ fn generate_bindings(config: &Config) { } }); - // bindgen 0.70 replaced the run-time layout tests with compile-time ones, - // but they depend on std::mem::offset_of, stabilized in 1.77. - let supports_layout_tests = autocfg::new().probe_rustc_version(1, 77); - let Ok(target_rust_version) = bindgen::RustTarget::stable(68, 0) else { - panic!("bindgen does not recognize target rust version"); - }; + let target_rust_version = + bindgen::RustTarget::stable(77, 0).expect("bindgen does not recognize target rust version"); let mut builder = bindgen::Builder::default() .rust_target(target_rust_version) // bindgen MSRV is 1.70, so this is enough @@ -752,7 +652,7 @@ fn generate_bindings(config: &Config) { .generate_comments(true) .fit_macro_constants(false) .size_t_is_usize(true) - .layout_tests(supports_layout_tests) + .layout_tests(config.env.debug.is_some()) .prepend_enum_name(true) .blocklist_type("max_align_t") // Not supported by bindgen on all targets, not used by BoringSSL .clang_args(get_extra_clang_args_for_bindgen(config)) @@ -779,7 +679,6 @@ fn generate_bindings(config: &Config) { "des.h", "dtls1.h", "hkdf.h", - #[cfg(not(feature = "fips"))] "hpke.h", "hmac.h", "hrss.h", @@ -804,7 +703,24 @@ fn generate_bindings(config: &Config) { } let bindings = builder.generate().expect("Unable to generate bindings"); + let mut source_code = Vec::new(); bindings - .write_to_file(config.out_dir.join("bindings.rs")) - .expect("Couldn't write bindings!"); + .write(Box::new(&mut source_code)) + .expect("Couldn't serialize bindings!"); + ensure_err_lib_enum_is_named(&mut source_code); + fs::write(config.out_dir.join("bindings.rs"), source_code).expect("Couldn't write bindings!"); +} + +/// err.h has anonymous `enum { ERR_LIB_NONE = 1 }`, which makes a dodgy `_bindgen_ty_1` name +fn ensure_err_lib_enum_is_named(source_code: &mut Vec) { + let src = String::from_utf8_lossy(source_code); + let enum_type = src + .split_once("ERR_LIB_SSL:") + .and_then(|(_, def)| Some(def.split_once("=")?.0)) + .unwrap_or("_bindgen_ty_1"); + + source_code.extend_from_slice( + format!("\n/// Newtype for [`ERR_LIB_SSL`] constants\npub use {enum_type} as ErrLib;\n") + .as_bytes(), + ); } diff --git a/boring-sys/patches/boring-pq.patch b/boring-sys/patches/boring-pq.patch index 38488004..1f13962a 100644 --- a/boring-sys/patches/boring-pq.patch +++ b/boring-sys/patches/boring-pq.patch @@ -66,141 +66,26 @@ Cf RTG-2076 RTG-2051 RTG-2508 RTG-2707 RTG-2607 RTG-3239 delete mode 100644 src/crypto/kyber/kyber_test.cc delete mode 100644 src/crypto/kyber/kyber_tests.txt -diff --git a/BUILD.generated.bzl b/BUILD.generated.bzl -index 738e1055f..9466757a2 100644 ---- a/BUILD.generated.bzl -+++ b/BUILD.generated.bzl -@@ -253,7 +253,6 @@ crypto_internal_headers = [ - "src/crypto/fipsmodule/tls/internal.h", - "src/crypto/hrss/internal.h", - "src/crypto/internal.h", -- "src/crypto/kyber/internal.h", - "src/crypto/lhash/internal.h", - "src/crypto/obj/obj_dat.h", - "src/crypto/pkcs7/internal.h", -@@ -382,8 +381,8 @@ crypto_sources = [ - "src/crypto/fipsmodule/fips_shared_support.c", - "src/crypto/hpke/hpke.c", - "src/crypto/hrss/hrss.c", -- "src/crypto/kyber/keccak.c", -- "src/crypto/kyber/kyber.c", -+ "src/crypto/kyber/kyber512.c", -+ "src/crypto/kyber/kyber768.c", - "src/crypto/lhash/lhash.c", - "src/crypto/mem.c", - "src/crypto/obj/obj.c", -diff --git a/BUILD.generated_tests.bzl b/BUILD.generated_tests.bzl -index 92dec1e01..8f70dedc0 100644 ---- a/BUILD.generated_tests.bzl -+++ b/BUILD.generated_tests.bzl -@@ -40,7 +40,6 @@ test_support_sources = [ - "src/crypto/fipsmodule/tls/internal.h", - "src/crypto/hrss/internal.h", - "src/crypto/internal.h", -- "src/crypto/kyber/internal.h", - "src/crypto/lhash/internal.h", - "src/crypto/obj/obj_dat.h", - "src/crypto/pkcs7/internal.h", -@@ -124,7 +123,6 @@ crypto_test_sources = [ - "src/crypto/hpke/hpke_test.cc", - "src/crypto/hrss/hrss_test.cc", - "src/crypto/impl_dispatch_test.cc", -- "src/crypto/kyber/kyber_test.cc", - "src/crypto/lhash/lhash_test.cc", - "src/crypto/obj/obj_test.cc", - "src/crypto/pem/pem_test.cc", -@@ -218,8 +216,6 @@ crypto_test_data = [ - "src/crypto/fipsmodule/rand/ctrdrbg_vectors.txt", - "src/crypto/hmac_extra/hmac_tests.txt", - "src/crypto/hpke/hpke_test_vectors.txt", -- "src/crypto/kyber/keccak_tests.txt", -- "src/crypto/kyber/kyber_tests.txt", - "src/crypto/pkcs8/test/empty_password.p12", - "src/crypto/pkcs8/test/no_encryption.p12", - "src/crypto/pkcs8/test/nss.p12", -diff --git a/CMakeLists.txt b/CMakeLists.txt -index faed2befa..931c0e3a8 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -375,8 +375,8 @@ add_library( - src/crypto/fipsmodule/fips_shared_support.c - src/crypto/hpke/hpke.c - src/crypto/hrss/hrss.c -- src/crypto/kyber/keccak.c -- src/crypto/kyber/kyber.c -+ src/crypto/kyber/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 4c0048e1d..f6ea5c40f 100644 ---- a/sources.json -+++ b/sources.json -@@ -111,8 +111,8 @@ - "src/crypto/fipsmodule/fips_shared_support.c", - "src/crypto/hpke/hpke.c", - "src/crypto/hrss/hrss.c", -- "src/crypto/kyber/keccak.c", -- "src/crypto/kyber/kyber.c", -+ "src/crypto/kyber/kyber512.c", -+ "src/crypto/kyber/kyber768.c", - "src/crypto/lhash/lhash.c", - "src/crypto/mem.c", - "src/crypto/obj/obj.c", -@@ -549,7 +549,6 @@ - "src/crypto/hpke/hpke_test.cc", - "src/crypto/hrss/hrss_test.cc", - "src/crypto/impl_dispatch_test.cc", -- "src/crypto/kyber/kyber_test.cc", - "src/crypto/lhash/lhash_test.cc", - "src/crypto/obj/obj_test.cc", - "src/crypto/pem/pem_test.cc", -@@ -634,8 +633,6 @@ - "src/crypto/fipsmodule/rand/ctrdrbg_vectors.txt", - "src/crypto/hmac_extra/hmac_tests.txt", - "src/crypto/hpke/hpke_test_vectors.txt", -- "src/crypto/kyber/keccak_tests.txt", -- "src/crypto/kyber/kyber_tests.txt", - "src/crypto/pkcs8/test/empty_password.p12", - "src/crypto/pkcs8/test/no_encryption.p12", - "src/crypto/pkcs8/test/nss.p12", -@@ -1060,4 +1057,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 cdb5ddca1..2052fa791 100644 ---- a/src/crypto/CMakeLists.txt -+++ b/src/crypto/CMakeLists.txt -@@ -170,8 +170,8 @@ add_library( - ex_data.c +diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt +index a594b9e9d..ed468237f 100644 +--- a/crypto/CMakeLists.txt ++++ b/crypto/CMakeLists.txt +@@ -176,7 +176,8 @@ add_library( hpke/hpke.c hrss/hrss.c -- kyber/keccak.c + keccak/keccak.c - kyber/kyber.c + kyber/kyber512.c + kyber/kyber768.c lhash/lhash.c mem.c obj/obj.c -@@ -400,7 +400,6 @@ add_executable( - hmac_extra/hmac_test.cc - hrss/hrss_test.cc - impl_dispatch_test.cc -- kyber/kyber_test.cc - lhash/lhash_test.cc - obj/obj_test.cc - pem/pem_test.cc -diff --git a/src/crypto/kyber/internal.h b/src/crypto/kyber/internal.h +diff --git a/crypto/kyber/internal.h b/crypto/kyber/internal.h deleted file mode 100644 -index b3bfa86b8..000000000 ---- a/src/crypto/kyber/internal.h +index b11211726..000000000 +--- a/crypto/kyber/internal.h +++ /dev/null -@@ -1,91 +0,0 @@ +@@ -1,60 +0,0 @@ -/* Copyright (c) 2023, Google Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any @@ -235,37 +120,6 @@ index b3bfa86b8..000000000 -// necessary to generate a key. -#define KYBER_GENERATE_KEY_ENTROPY 64 - --struct BORINGSSL_keccak_st { -- uint64_t state[25]; -- size_t rate_bytes; -- size_t offset; --}; -- --enum boringssl_keccak_config_t { -- boringssl_sha3_256, -- boringssl_sha3_512, -- boringssl_shake128, -- boringssl_shake256, --}; -- --// BORINGSSL_keccak hashes |in_len| bytes from |in| and writes |out_len| bytes --// of output to |out|. If the |config| specifies a fixed-output function, like --// SHA3-256, then |out_len| must be the correct length for that function. --OPENSSL_EXPORT void BORINGSSL_keccak(uint8_t *out, size_t out_len, -- const uint8_t *in, size_t in_len, -- enum boringssl_keccak_config_t config); -- --// BORINGSSL_keccak_init absorbs |in_len| bytes from |in| and sets up |ctx| for --// squeezing. The |config| must specify a SHAKE variant, otherwise callers --// should use |BORINGSSL_keccak|. --OPENSSL_EXPORT void BORINGSSL_keccak_init( -- struct BORINGSSL_keccak_st *ctx, const uint8_t *in, size_t in_len, -- enum boringssl_keccak_config_t config); -- --// BORINGSSL_keccak_squeeze writes |out_len| bytes to |out| from |ctx|. --OPENSSL_EXPORT void BORINGSSL_keccak_squeeze(struct BORINGSSL_keccak_st *ctx, -- uint8_t *out, size_t out_len); -- -// KYBER_generate_key_external_entropy is a deterministic function to create a -// pair of Kyber768 keys, using the supplied entropy. The entropy needs to be -// uniformly random generated. This function is should only be used for tests, @@ -292,221 +146,11 @@ index b3bfa86b8..000000000 -#endif - -#endif // OPENSSL_HEADER_CRYPTO_KYBER_INTERNAL_H -diff --git a/src/crypto/kyber/keccak.c b/src/crypto/kyber/keccak.c -deleted file mode 100644 -index f1c012d11..000000000 ---- a/src/crypto/kyber/keccak.c -+++ /dev/null -@@ -1,204 +0,0 @@ --/* Copyright (c) 2023, Google Inc. -- * -- * Permission to use, copy, modify, and/or distribute this software for any -- * purpose with or without fee is hereby granted, provided that the above -- * copyright notice and this permission notice appear in all copies. -- * -- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY -- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -- * 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. */ -- --#include -- --#include --#include -- --#include "../internal.h" --#include "./internal.h" -- -- --// keccak_f implements the Keccak-1600 permutation as described at --// https://keccak.team/keccak_specs_summary.html. Each lane is represented as a --// 64-bit value and the 5×5 lanes are stored as an array in row-major order. --static void keccak_f(uint64_t state[25]) { -- static const int kNumRounds = 24; -- for (int round = 0; round < kNumRounds; round++) { -- // θ step -- uint64_t c[5]; -- for (int x = 0; x < 5; x++) { -- c[x] = state[x] ^ state[x + 5] ^ state[x + 10] ^ state[x + 15] ^ -- state[x + 20]; -- } -- -- for (int x = 0; x < 5; x++) { -- const uint64_t d = c[(x + 4) % 5] ^ CRYPTO_rotl_u64(c[(x + 1) % 5], 1); -- for (int y = 0; y < 5; y++) { -- state[y * 5 + x] ^= d; -- } -- } -- -- // ρ and π steps. -- // -- // These steps involve a mapping of the state matrix. Each input point, -- // (x,y), is rotated and written to the point (y, 2x + 3y). In the Keccak -- // pseudo-code a separate array is used because an in-place operation would -- // overwrite some values that are subsequently needed. However, the mapping -- // forms a trail through 24 of the 25 values so we can do it in place with -- // only a single temporary variable. -- // -- // Start with (1, 0). The value here will be mapped and end up at (0, 2). -- // That value will end up at (2, 1), then (1, 2), and so on. After 24 -- // steps, 24 of the 25 values have been hit (as this mapping is injective) -- // and the sequence will repeat. All that remains is to handle the element -- // at (0, 0), but the rotation for that element is zero, and it goes to (0, -- // 0), so we can ignore it. -- static const uint8_t kIndexes[24] = {10, 7, 11, 17, 18, 3, 5, 16, -- 8, 21, 24, 4, 15, 23, 19, 13, -- 12, 2, 20, 14, 22, 9, 6, 1}; -- static const uint8_t kRotations[24] = {1, 3, 6, 10, 15, 21, 28, 36, -- 45, 55, 2, 14, 27, 41, 56, 8, -- 25, 43, 62, 18, 39, 61, 20, 44}; -- uint64_t prev_value = state[1]; -- for (int i = 0; i < 24; i++) { -- const uint64_t value = CRYPTO_rotl_u64(prev_value, kRotations[i]); -- const size_t index = kIndexes[i]; -- prev_value = state[index]; -- state[index] = value; -- } -- -- // χ step -- for (int y = 0; y < 5; y++) { -- const int row_index = 5 * y; -- const uint64_t orig_x0 = state[row_index]; -- const uint64_t orig_x1 = state[row_index + 1]; -- state[row_index] ^= ~orig_x1 & state[row_index + 2]; -- state[row_index + 1] ^= ~state[row_index + 2] & state[row_index + 3]; -- state[row_index + 2] ^= ~state[row_index + 3] & state[row_index + 4]; -- state[row_index + 3] ^= ~state[row_index + 4] & orig_x0; -- state[row_index + 4] ^= ~orig_x0 & orig_x1; -- } -- -- // ι step -- // -- // From https://keccak.team/files/Keccak-reference-3.0.pdf, section -- // 1.2, the round constants are based on the output of a LFSR. Thus, as -- // suggested in the appendix of of -- // https://keccak.team/keccak_specs_summary.html, the values are -- // simply encoded here. -- static const uint64_t kRoundConstants[24] = { -- 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, -- 0x8000000080008000, 0x000000000000808b, 0x0000000080000001, -- 0x8000000080008081, 0x8000000000008009, 0x000000000000008a, -- 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, -- 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, -- 0x8000000000008003, 0x8000000000008002, 0x8000000000000080, -- 0x000000000000800a, 0x800000008000000a, 0x8000000080008081, -- 0x8000000000008080, 0x0000000080000001, 0x8000000080008008, -- }; -- -- state[0] ^= kRoundConstants[round]; -- } --} -- --static void keccak_init(struct BORINGSSL_keccak_st *ctx, -- size_t *out_required_out_len, const uint8_t *in, -- size_t in_len, enum boringssl_keccak_config_t config) { -- size_t capacity_bytes; -- uint8_t terminator; -- switch (config) { -- case boringssl_sha3_256: -- capacity_bytes = 512 / 8; -- *out_required_out_len = 32; -- terminator = 0x06; -- break; -- case boringssl_sha3_512: -- capacity_bytes = 1024 / 8; -- *out_required_out_len = 64; -- terminator = 0x06; -- break; -- case boringssl_shake128: -- capacity_bytes = 256 / 8; -- *out_required_out_len = 0; -- terminator = 0x1f; -- break; -- case boringssl_shake256: -- capacity_bytes = 512 / 8; -- *out_required_out_len = 0; -- terminator = 0x1f; -- break; -- default: -- abort(); -- } -- -- OPENSSL_memset(ctx, 0, sizeof(*ctx)); -- ctx->rate_bytes = 200 - capacity_bytes; -- assert(ctx->rate_bytes % 8 == 0); -- const size_t rate_words = ctx->rate_bytes / 8; -- -- while (in_len >= ctx->rate_bytes) { -- for (size_t i = 0; i < rate_words; i++) { -- ctx->state[i] ^= CRYPTO_load_u64_le(in + 8 * i); -- } -- keccak_f(ctx->state); -- in += ctx->rate_bytes; -- in_len -= ctx->rate_bytes; -- } -- -- // XOR the final block. Accessing |ctx->state| as a |uint8_t*| is allowed by -- // strict aliasing because we require |uint8_t| to be a character type. -- uint8_t *state_bytes = (uint8_t *)ctx->state; -- assert(in_len < ctx->rate_bytes); -- for (size_t i = 0; i < in_len; i++) { -- state_bytes[i] ^= in[i]; -- } -- state_bytes[in_len] ^= terminator; -- state_bytes[ctx->rate_bytes - 1] ^= 0x80; -- keccak_f(ctx->state); --} -- --void BORINGSSL_keccak(uint8_t *out, size_t out_len, const uint8_t *in, -- size_t in_len, enum boringssl_keccak_config_t config) { -- struct BORINGSSL_keccak_st ctx; -- size_t required_out_len; -- keccak_init(&ctx, &required_out_len, in, in_len, config); -- if (required_out_len != 0 && out_len != required_out_len) { -- abort(); -- } -- BORINGSSL_keccak_squeeze(&ctx, out, out_len); --} -- --void BORINGSSL_keccak_init(struct BORINGSSL_keccak_st *ctx, const uint8_t *in, -- size_t in_len, -- enum boringssl_keccak_config_t config) { -- size_t required_out_len; -- keccak_init(ctx, &required_out_len, in, in_len, config); -- if (required_out_len != 0) { -- abort(); -- } --} -- --void BORINGSSL_keccak_squeeze(struct BORINGSSL_keccak_st *ctx, uint8_t *out, -- size_t out_len) { -- // Accessing |ctx->state| as a |uint8_t*| is allowed by strict aliasing -- // because we require |uint8_t| to be a character type. -- const uint8_t *state_bytes = (const uint8_t *)ctx->state; -- while (out_len) { -- size_t remaining = ctx->rate_bytes - ctx->offset; -- size_t todo = out_len; -- if (todo > remaining) { -- todo = remaining; -- } -- OPENSSL_memcpy(out, &state_bytes[ctx->offset], todo); -- out += todo; -- out_len -= todo; -- ctx->offset += todo; -- if (ctx->offset == ctx->rate_bytes) { -- keccak_f(ctx->state); -- ctx->offset = 0; -- } -- } --} -diff --git a/src/crypto/kyber/kyber.c b/src/crypto/kyber/kyber.c -index 776c085f9..ccb5b3d9b 100644 ---- a/src/crypto/kyber/kyber.c -+++ b/src/crypto/kyber/kyber.c -@@ -1,833 +1,2426 @@ +diff --git a/crypto/kyber/kyber.c b/crypto/kyber/kyber.c +index d3ea02090..ccb5b3d9b 100644 +--- a/crypto/kyber/kyber.c ++++ b/crypto/kyber/kyber.c +@@ -1,835 +1,2426 @@ -/* Copyright (c) 2023, Google Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any @@ -547,17 +191,17 @@ index 776c085f9..ccb5b3d9b 100644 +// implementation or https://github.com/cloudflare/circl/tree/main/pke/kyber +// +// - Option to keep A stored in private key. - --#include ++ +#ifndef KYBER_K +#error "Don't compile this file direcly" +#endif --#include --#include -+#include + #include +#include +-#include +-#include +- -#include -#include +#include @@ -565,9 +209,29 @@ index 776c085f9..ccb5b3d9b 100644 +#include #include "../internal.h" +-#include "../keccak/internal.h" -#include "./internal.h" -- -- ++ ++#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) + + -// See -// https://pq-crystals.org/kyber/data/kyber-specification-round3-20210804.pdf - @@ -602,9 +266,10 @@ index 776c085f9..ccb5b3d9b 100644 -} matrix; - -// This bit of Python will be referenced in some of the following comments: --// + // -// p = 3329 --// ++// params.h + // -// def bitreverse(i): -// ret = 0 -// for n in range(7): @@ -613,7 +278,9 @@ index 776c085f9..ccb5b3d9b 100644 -// ret |= bit -// i >>= 1 -// return ret -- ++#define KYBER_N 256 ++#define KYBER_Q 3329 + -// kNTTRoots = [pow(17, bitreverse(i), p) for i in range(128)] -static const uint16_t kNTTRoots[128] = { - 1, 1729, 2580, 3289, 2642, 630, 1897, 848, 1062, 1919, 193, 797, @@ -627,110 +294,6 @@ index 776c085f9..ccb5b3d9b 100644 - 1584, 2298, 2037, 3220, 375, 2549, 2090, 1645, 1063, 319, 2773, 757, - 2099, 561, 2466, 2594, 2804, 1092, 403, 1026, 1143, 2150, 2775, 886, - 1722, 1212, 1874, 1029, 2110, 2935, 885, 2154, --}; - --// kInverseNTTRoots = [pow(17, -bitreverse(i), p) for i in range(128)] --static const uint16_t kInverseNTTRoots[128] = { -- 1, 1600, 40, 749, 2481, 1432, 2699, 687, 1583, 2760, 69, 543, -- 2532, 3136, 1410, 2267, 2508, 1355, 450, 936, 447, 2794, 1235, 1903, -- 1996, 1089, 3273, 283, 1853, 1990, 882, 3033, 2419, 2102, 219, 855, -- 2681, 1848, 712, 682, 927, 1795, 461, 1891, 2877, 2522, 1894, 1010, -- 1414, 2009, 3296, 464, 2697, 816, 1352, 2679, 1274, 1052, 1025, 2132, -- 1573, 76, 2998, 3040, 1175, 2444, 394, 1219, 2300, 1455, 2117, 1607, -- 2443, 554, 1179, 2186, 2303, 2926, 2237, 525, 735, 863, 2768, 1230, -- 2572, 556, 3010, 2266, 1684, 1239, 780, 2954, 109, 1292, 1031, 1745, -- 2688, 3061, 992, 2596, 941, 892, 1021, 2390, 642, 1868, 2377, 1482, -- 1540, 540, 1678, 1626, 279, 314, 1173, 2573, 3096, 48, 667, 1920, -- 2229, 1041, 2606, 1692, 680, 2746, 568, 3312, --}; -+#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 - --// kModRoots = [pow(17, 2*bitreverse(i) + 1, p) for i in range(128)] --static const uint16_t kModRoots[128] = { -- 17, 3312, 2761, 568, 583, 2746, 2649, 680, 1637, 1692, 723, 2606, -- 2288, 1041, 1100, 2229, 1409, 1920, 2662, 667, 3281, 48, 233, 3096, -- 756, 2573, 2156, 1173, 3015, 314, 3050, 279, 1703, 1626, 1651, 1678, -- 2789, 540, 1789, 1540, 1847, 1482, 952, 2377, 1461, 1868, 2687, 642, -- 939, 2390, 2308, 1021, 2437, 892, 2388, 941, 733, 2596, 2337, 992, -- 268, 3061, 641, 2688, 1584, 1745, 2298, 1031, 2037, 1292, 3220, 109, -- 375, 2954, 2549, 780, 2090, 1239, 1645, 1684, 1063, 2266, 319, 3010, -- 2773, 556, 757, 2572, 2099, 1230, 561, 2768, 2466, 863, 2594, 735, -- 2804, 525, 1092, 2237, 403, 2926, 1026, 2303, 1143, 2186, 2150, 1179, -- 2775, 554, 886, 2443, 1722, 1607, 1212, 2117, 1874, 1455, 1029, 2300, -- 2110, 1219, 2935, 394, 885, 2444, 2154, 1175, --}; -+#define public_key KYBER_NAMESPACE(public_key) -+#define private_key KYBER_NAMESPACE(private_key) - --// reduce_once reduces 0 <= x < 2*kPrime, mod kPrime. --static uint16_t reduce_once(uint16_t x) { -- assert(x < 2 * kPrime); -- const uint16_t subtracted = x - kPrime; -- uint16_t mask = 0u - (subtracted >> 15); -- // On Aarch64, omitting a |value_barrier_u16| results in a 2x speedup of Kyber -- // overall and Clang still produces constant-time code using `csel`. On other -- // platforms & compilers on godbolt that we care about, this code also -- // produces constant-time output. -- return (mask & x) | (~mask & subtracted); --} -- --// constant time reduce x mod kPrime using Barrett reduction. x must be less --// than kPrime + 2×kPrime². --static uint16_t reduce(uint32_t x) { -- assert(x < kPrime + 2u * kPrime * kPrime); -- uint64_t product = (uint64_t)x * kBarrettMultiplier; -- uint32_t quotient = product >> kBarrettShift; -- uint32_t remainder = x - quotient * kPrime; -- return reduce_once(remainder); --} -- --static void scalar_zero(scalar *out) { OPENSSL_memset(out, 0, sizeof(*out)); } -- --static void vector_zero(vector *out) { OPENSSL_memset(out, 0, sizeof(*out)); } -- --// In place number theoretic transform of a given scalar. --// Note that Kyber's kPrime 3329 does not have a 512th root of unity, so this --// transform leaves off the last iteration of the usual FFT code, with the 128 --// relevant roots of unity being stored in |kNTTRoots|. This means the output --// should be seen as 128 elements in GF(3329^2), with the coefficients of the --// elements being consecutive entries in |s->c|. --static void scalar_ntt(scalar *s) { -- int offset = DEGREE; -- // `int` is used here because using `size_t` throughout caused a ~5% slowdown -- // with Clang 14 on Aarch64. -- for (int step = 1; step < DEGREE / 2; step <<= 1) { -- offset >>= 1; -- int k = 0; -- for (int i = 0; i < step; i++) { -- const uint32_t step_root = kNTTRoots[i + step]; -- for (int j = k; j < k + offset; j++) { -- uint16_t odd = reduce(step_root * s->c[j + offset]); -- uint16_t even = s->c[j]; -- s->c[j] = reduce_once(odd + even); -- s->c[j + offset] = reduce_once(even - odd + kPrime); -- } -- k += 2 * offset; -+#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 */ + @@ -940,7 +503,7 @@ index 776c085f9..ccb5b3d9b 100644 + for(i=0;i> 63; ++ return (0-(uint64_t)r) >> 63; +} + +/************************************************* @@ -1112,9 +675,9 @@ index 776c085f9..ccb5b3d9b 100644 + 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]) @@ -1127,10 +690,7 @@ index 776c085f9..ccb5b3d9b 100644 +#error "This implementation requires eta1 in {2,3}" +#endif +} - --static void vector_ntt(vector *a) { -- for (int i = 0; i < RANK; i++) { -- scalar_ntt(&a->v[i]); ++ +static void poly_cbd_eta2(poly *r, const uint8_t buf[KYBER_ETA2*KYBER_N/4]) +{ +#if KYBER_ETA2 == 2 @@ -1157,8 +717,21 @@ index 776c085f9..ccb5b3d9b 100644 + 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 -+}; -+ + }; + +-// kInverseNTTRoots = [pow(17, -bitreverse(i), p) for i in range(128)] +-static const uint16_t kInverseNTTRoots[128] = { +- 1, 1600, 40, 749, 2481, 1432, 2699, 687, 1583, 2760, 69, 543, +- 2532, 3136, 1410, 2267, 2508, 1355, 450, 936, 447, 2794, 1235, 1903, +- 1996, 1089, 3273, 283, 1853, 1990, 882, 3033, 2419, 2102, 219, 855, +- 2681, 1848, 712, 682, 927, 1795, 461, 1891, 2877, 2522, 1894, 1010, +- 1414, 2009, 3296, 464, 2697, 816, 1352, 2679, 1274, 1052, 1025, 2132, +- 1573, 76, 2998, 3040, 1175, 2444, 394, 1219, 2300, 1455, 2117, 1607, +- 2443, 554, 1179, 2186, 2303, 2926, 2237, 525, 735, 863, 2768, 1230, +- 2572, 556, 3010, 2266, 1684, 1239, 780, 2954, 109, 1292, 1031, 1745, +- 2688, 3061, 992, 2596, 941, 892, 1021, 2390, 642, 1868, 2377, 1482, +- 1540, 540, 1678, 1626, 279, 314, 1173, 2573, 3096, 48, 667, 1920, +- 2229, 1041, 2606, 1692, 680, 2746, 568, 3312, +void init_ntt() { + unsigned int i; + int16_t tmp[128]; @@ -1173,8 +746,8 @@ index 776c085f9..ccb5b3d9b 100644 + zetas[i] -= KYBER_Q; + if(zetas[i] < -KYBER_Q/2) + zetas[i] += KYBER_Q; - } - } ++ } ++} +*/ + +static const int16_t zetas[128] = { @@ -1194,8 +767,21 @@ index 776c085f9..ccb5b3d9b 100644 + -1215, -136, 1218, -1335, -874, 220, -1187, -1659, + -1185, -1530, -1278, 794, -1510, -854, -870, 478, + -108, -308, 996, 991, 958, -1460, 1522, 1628 -+}; -+ + }; + +-// kModRoots = [pow(17, 2*bitreverse(i) + 1, p) for i in range(128)] +-static const uint16_t kModRoots[128] = { +- 17, 3312, 2761, 568, 583, 2746, 2649, 680, 1637, 1692, 723, 2606, +- 2288, 1041, 1100, 2229, 1409, 1920, 2662, 667, 3281, 48, 233, 3096, +- 756, 2573, 2156, 1173, 3015, 314, 3050, 279, 1703, 1626, 1651, 1678, +- 2789, 540, 1789, 1540, 1847, 1482, 952, 2377, 1461, 1868, 2687, 642, +- 939, 2390, 2308, 1021, 2437, 892, 2388, 941, 733, 2596, 2337, 992, +- 268, 3061, 641, 2688, 1584, 1745, 2298, 1031, 2037, 1292, 3220, 109, +- 375, 2954, 2549, 780, 2090, 1239, 1645, 1684, 1063, 2266, 319, 3010, +- 2773, 556, 757, 2572, 2099, 1230, 561, 2768, 2466, 863, 2594, 735, +- 2804, 525, 1092, 2237, 403, 2926, 1026, 2303, 1143, 2186, 2150, 1179, +- 2775, 554, 886, 2443, 1722, 1607, 1212, 2117, 1874, 1455, 1029, 2300, +- 2110, 1219, 2935, 394, 885, 2444, 2154, 1175, +/************************************************* +* Name: fqmul +* @@ -1209,26 +795,7 @@ index 776c085f9..ccb5b3d9b 100644 +static int16_t fqmul(int16_t a, int16_t b) { + return montgomery_reduce((int32_t)a*b); +} - --// In place inverse number theoretic transform of a given scalar, with pairs of --// entries of s->v being interpreted as elements of GF(3329^2). Just as with the --// number theoretic transform, this leaves off the first step of the normal iFFT --// to account for the fact that 3329 does not have a 512th root of unity, using --// the precomputed 128 roots of unity stored in |kInverseNTTRoots|. --static void scalar_inverse_ntt(scalar *s) { -- int step = DEGREE / 2; -- // `int` is used here because using `size_t` throughout caused a ~5% slowdown -- // with Clang 14 on Aarch64. -- for (int offset = 2; offset < DEGREE; offset <<= 1) { -- step >>= 1; -- int k = 0; -- for (int i = 0; i < step; i++) { -- uint32_t step_root = kInverseNTTRoots[i + step]; -- for (int j = k; j < k + offset; j++) { -- uint16_t odd = s->c[j + offset]; -- uint16_t even = s->c[j]; -- s->c[j] = reduce_once(odd + even); -- s->c[j + offset] = reduce(step_root * (even - odd + kPrime)); ++ +/************************************************* +* Name: ntt +* @@ -1249,18 +816,11 @@ index 776c085f9..ccb5b3d9b 100644 + t = fqmul(zeta, r[j + len]); + r[j + len] = r[j] - t; + r[j] = r[j] + t; - } -- k += 2 * offset; - } - } -- for (int i = 0; i < DEGREE; i++) { -- s->c[i] = reduce(s->c[i] * kInverseDegree); -- } - } - --static void vector_inverse_ntt(vector *a) { -- for (int i = 0; i < RANK; i++) { -- scalar_inverse_ntt(&a->v[i]); ++ } ++ } ++ } ++} ++ +/************************************************* +* Name: invntt_tomont +* @@ -1286,7 +846,7 @@ index 776c085f9..ccb5b3d9b 100644 + r[j + len] = fqmul(zeta, r[j + len]); + } + } - } ++ } + + for(j = 0; j < 256; j++) + r[j] = fqmul(r[j], f); @@ -1310,11 +870,8 @@ index 776c085f9..ccb5b3d9b 100644 + r[0] += fqmul(a[0], b[0]); + r[1] = fqmul(a[0], b[1]); + r[1] += fqmul(a[1], b[0]); - } - --static void scalar_add(scalar *lhs, const scalar *rhs) { -- for (int i = 0; i < DEGREE; i++) { -- lhs->c[i] = reduce_once(lhs->c[i] + rhs->c[i]); ++} ++ +// +// poly.c +// @@ -1353,7 +910,7 @@ index 776c085f9..ccb5b3d9b 100644 + r[2] = t[4] | (t[5] << 4); + r[3] = t[6] | (t[7] << 4); + r += 4; - } ++ } +#elif (KYBER_POLYCOMPRESSEDBYTES == 160) + for(i=0;ic[i] = reduce_once(lhs->c[i] - rhs->c[i] + kPrime); ++} ++ +/************************************************* +* Name: poly_decompress +* @@ -1418,29 +972,12 @@ index 776c085f9..ccb5b3d9b 100644 + + 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 - } - --// Multiplying two scalars in the number theoretically transformed state. Since --// 3329 does not have a 512th root of unity, this means we have to interpret --// the 2*ith and (2*i+1)th entries of the scalar as elements of GF(3329)[X]/(X^2 --// - 17^(2*bitreverse(i)+1)) The value of 17^(2*bitreverse(i)+1) mod 3329 is --// stored in the precomputed |kModRoots| table. Note that our Barrett transform --// only allows us to multipy two reduced numbers together, so we need some --// intermediate reduction steps, even if an uint64_t could hold 3 multiplied --// numbers. --static void scalar_mult(scalar *out, const scalar *lhs, const scalar *rhs) { -- for (int i = 0; i < DEGREE / 2; i++) { -- uint32_t real_real = (uint32_t)lhs->c[2 * i] * rhs->c[2 * i]; -- uint32_t img_img = (uint32_t)lhs->c[2 * i + 1] * rhs->c[2 * i + 1]; -- uint32_t real_img = (uint32_t)lhs->c[2 * i] * rhs->c[2 * i + 1]; -- uint32_t img_real = (uint32_t)lhs->c[2 * i + 1] * rhs->c[2 * i]; -- out->c[2 * i] = -- reduce(real_real + (uint32_t)reduce(img_img) * kModRoots[i]); -- out->c[2 * i + 1] = reduce(img_real + real_img); ++} ++ +/************************************************* +* Name: poly_tobytes +* @@ -1464,12 +1001,9 @@ index 776c085f9..ccb5b3d9b 100644 + r[3*i+0] = (t0 >> 0); + r[3*i+1] = (t0 >> 8) | (t1 << 4); + r[3*i+2] = (t1 >> 4); - } - } - --static void vector_add(vector *lhs, const vector *rhs) { -- for (int i = 0; i < RANK; i++) { -- scalar_add(&lhs->v[i], &rhs->v[i]); ++ } ++} ++ +/************************************************* +* Name: poly_frombytes +* @@ -1486,16 +1020,9 @@ index 776c085f9..ccb5b3d9b 100644 + 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; - } - } - --static void matrix_mult(vector *out, const matrix *m, const vector *a) { -- vector_zero(out); -- for (int i = 0; i < RANK; i++) { -- for (int j = 0; j < RANK; j++) { -- scalar product; -- scalar_mult(&product, &m->v[i][j], &a->v[j]); -- scalar_add(&out->v[i], &product); ++ } ++} ++ +/************************************************* +* Name: poly_frommsg +* @@ -1517,18 +1044,10 @@ index 776c085f9..ccb5b3d9b 100644 + for(j=0;j<8;j++) { + mask = -(int16_t)value_barrier_u32((msg[i] >> j)&1); + r->coeffs[8*i+j] = mask & ((KYBER_Q+1)/2); - } - } - } - --static void matrix_mult_transpose(vector *out, const matrix *m, -- const vector *a) { -- vector_zero(out); -- for (int i = 0; i < RANK; i++) { -- for (int j = 0; j < RANK; j++) { -- scalar product; -- scalar_mult(&product, &m->v[j][i], &a->v[j]); -- scalar_add(&out->v[i], &product); ++ } ++ } ++} ++ +/************************************************* +* Name: poly_tomsg +* @@ -1552,18 +1071,10 @@ index 776c085f9..ccb5b3d9b 100644 + t >>= 28; + t &= 1; + msg[i] |= t << j; - } - } - } - --static void scalar_inner_product(scalar *out, const vector *lhs, -- const vector *rhs) { -- scalar_zero(out); -- for (int i = 0; i < RANK; i++) { -- scalar product; -- scalar_mult(&product, &lhs->v[i], &rhs->v[i]); -- scalar_add(out, &product); -- } ++ } ++ } ++} ++ +/************************************************* +* Name: poly_getnoise_eta1 +* @@ -1581,32 +1092,8 @@ index 776c085f9..ccb5b3d9b 100644 + uint8_t buf[KYBER_ETA1*KYBER_N/4]; + prf(buf, sizeof(buf), seed, nonce); + poly_cbd_eta1(r, buf); - } - --// Algorithm 1 of the Kyber spec. Rejection samples a Keccak stream to get --// uniformly distributed elements. This is used for matrix expansion and only --// operates on public inputs. --static void scalar_from_keccak_vartime(scalar *out, -- struct BORINGSSL_keccak_st *keccak_ctx) { -- assert(keccak_ctx->offset == 0); -- assert(keccak_ctx->rate_bytes == 168); -- static_assert(168 % 3 == 0, "block and coefficient boundaries do not align"); -- -- int done = 0; -- while (done < DEGREE) { -- uint8_t block[168]; -- BORINGSSL_keccak_squeeze(keccak_ctx, block, sizeof(block)); -- for (size_t i = 0; i < sizeof(block) && done < DEGREE; i += 3) { -- uint16_t d1 = block[i] + 256 * (block[i + 1] % 16); -- uint16_t d2 = block[i + 1] / 16 + 16 * block[i + 2]; -- if (d1 < kPrime) { -- out->c[done++] = d1; -- } -- if (d2 < kPrime && done < DEGREE) { -- out->c[done++] = d2; -- } -- } -- } ++} ++ +/************************************************* +* Name: poly_getnoise_eta2 +* @@ -1624,34 +1111,8 @@ index 776c085f9..ccb5b3d9b 100644 + uint8_t buf[KYBER_ETA2*KYBER_N/4]; + prf(buf, sizeof(buf), seed, nonce); + poly_cbd_eta2(r, buf); - } - --// Algorithm 2 of the Kyber spec, with eta fixed to two and the PRF call --// included. Creates binominally distributed elements by sampling 2*|eta| bits, --// and setting the coefficient to the count of the first bits minus the count of --// the second bits, resulting in a centered binomial distribution. Since eta is --// two this gives -2/2 with a probability of 1/16, -1/1 with probability 1/4, --// and 0 with probability 3/8. --static void scalar_centered_binomial_distribution_eta_2_with_prf( -- scalar *out, const uint8_t input[33]) { -- uint8_t entropy[128]; -- static_assert(sizeof(entropy) == 2 * /*kEta=*/2 * DEGREE / 8, ""); -- BORINGSSL_keccak(entropy, sizeof(entropy), input, 33, boringssl_shake256); -- -- for (int i = 0; i < DEGREE; i += 2) { -- uint8_t byte = entropy[i / 2]; -- -- uint16_t value = kPrime; -- value += (byte & 1) + ((byte >> 1) & 1); -- value -= ((byte >> 2) & 1) + ((byte >> 3) & 1); -- out->c[i] = reduce_once(value); -- -- byte >>= 4; -- value = kPrime; -- value += (byte & 1) + ((byte >> 1) & 1); -- value -= ((byte >> 2) & 1) + ((byte >> 3) & 1); -- out->c[i + 1] = reduce_once(value); -- } ++} ++ + +/************************************************* +* Name: poly_ntt @@ -1666,19 +1127,8 @@ index 776c085f9..ccb5b3d9b 100644 +{ + ntt(r->coeffs); + poly_reduce(r); - } - --// Generates a secret vector by using --// |scalar_centered_binomial_distribution_eta_2_with_prf|, using the given seed --// appending and incrementing |counter| for entry of the vector. --static void vector_generate_secret_eta_2(vector *out, uint8_t *counter, -- const uint8_t seed[32]) { -- uint8_t input[33]; -- OPENSSL_memcpy(input, seed, 32); -- for (int i = 0; i < RANK; i++) { -- input[32] = (*counter)++; -- scalar_centered_binomial_distribution_eta_2_with_prf(&out->v[i], input); -- } ++} ++ +/************************************************* +* Name: poly_invntt_tomont +* @@ -1691,21 +1141,8 @@ index 776c085f9..ccb5b3d9b 100644 +static void poly_invntt_tomont(poly *r) +{ + invntt(r->coeffs); - } - --// Expands the matrix of a seed for key generation and for encaps-CPA. --static void matrix_expand(matrix *out, const uint8_t rho[32]) { -- uint8_t input[34]; -- OPENSSL_memcpy(input, rho, 32); -- for (int i = 0; i < RANK; i++) { -- for (int j = 0; j < RANK; j++) { -- input[32] = i; -- input[33] = j; -- struct BORINGSSL_keccak_st keccak_ctx; -- BORINGSSL_keccak_init(&keccak_ctx, input, sizeof(input), -- boringssl_shake128); -- scalar_from_keccak_vartime(&out->v[i][j], &keccak_ctx); -- } ++} ++ +/************************************************* +* Name: poly_basemul_montgomery +* @@ -1721,35 +1158,9 @@ index 776c085f9..ccb5b3d9b 100644 + 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]); - } - } - --static const uint8_t kMasks[8] = {0x01, 0x03, 0x07, 0x0f, -- 0x1f, 0x3f, 0x7f, 0xff}; -- --static void scalar_encode(uint8_t *out, const scalar *s, int bits) { -- assert(bits <= (int)sizeof(*s->c) * 8 && bits != 1); -- -- uint8_t out_byte = 0; -- int out_byte_bits = 0; -- -- for (int i = 0; i < DEGREE; i++) { -- uint16_t element = s->c[i]; -- int element_bits_done = 0; -- -- while (element_bits_done < bits) { -- int chunk_bits = bits - element_bits_done; -- int out_bits_remaining = 8 - out_byte_bits; -- if (chunk_bits >= out_bits_remaining) { -- chunk_bits = out_bits_remaining; -- out_byte |= (element & kMasks[chunk_bits - 1]) << out_byte_bits; -- *out = out_byte; -- out++; -- out_byte_bits = 0; -- out_byte = 0; -- } else { -- out_byte |= (element & kMasks[chunk_bits - 1]) << out_byte_bits; -- out_byte_bits += chunk_bits; ++ } ++} ++ +/************************************************* +* Name: poly_tomont +* @@ -1844,10 +1255,8 @@ index 776c085f9..ccb5b3d9b 100644 + d0 *= 645084; + d0 >>= 31; + t[k] = d0 & 0x7ff; - } - -- element_bits_done += chunk_bits; -- element >>= chunk_bits; ++ } ++ + r[ 0] = (t[0] >> 0); + r[ 1] = (t[0] >> 8) | (t[1] << 3); + r[ 2] = (t[1] >> 5) | (t[2] << 6); @@ -1860,8 +1269,8 @@ index 776c085f9..ccb5b3d9b 100644 + 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;i>= 32; + t[k] = d0 & 0x3ff; + } - -- if (out_byte_bits > 0) { -- *out = out_byte; ++ + r[0] = (t[0] >> 0); + r[1] = (t[0] >> 8) | (t[1] << 2); + r[2] = (t[1] >> 6) | (t[2] << 4); @@ -1886,18 +1293,12 @@ index 776c085f9..ccb5b3d9b 100644 + r[4] = (t[3] >> 2); + r += 5; + } - } ++ } +#else +#error "KYBER_POLYVECCOMPRESSEDBYTES needs to be in {320*KYBER_K, 352*KYBER_K}" +#endif - } - --// scalar_encode_1 is |scalar_encode| specialised for |bits| == 1. --static void scalar_encode_1(uint8_t out[32], const scalar *s) { -- for (int i = 0; i < DEGREE; i += 8) { -- uint8_t out_byte = 0; -- for (int j = 0; j < 8; j++) { -- out_byte |= (s->c[i + j] & 1) << j; ++} ++ +/************************************************* +* Name: polyvec_decompress +* @@ -1942,22 +1343,13 @@ index 776c085f9..ccb5b3d9b 100644 + + for(k=0;k<4;k++) + r->vec[i].coeffs[4*j+k] = ((uint32_t)(t[k] & 0x3FF)*KYBER_Q + 512) >> 10; - } -- *out = out_byte; -- out++; - } ++ } ++ } +#else +#error "KYBER_POLYVECCOMPRESSEDBYTES needs to be in {320*KYBER_K, 352*KYBER_K}" +#endif - } - --// Encodes an entire vector into 32*|RANK|*|bits| bytes. Note that since 256 --// (DEGREE) is divisible by 8, the individual vector entries will always fill a --// whole number of bytes, so we do not need to worry about bit packing here. --static void vector_encode(uint8_t *out, const vector *a, int bits) { -- for (int i = 0; i < RANK; i++) { -- scalar_encode(out + i * bits * DEGREE / 8, &a->v[i], bits); -- } ++} ++ +/************************************************* +* Name: polyvec_tobytes +* @@ -1972,13 +1364,8 @@ index 776c085f9..ccb5b3d9b 100644 + unsigned int i; + for(i=0;ivec[i]); - } - --// scalar_decode parses |DEGREE * bits| bits from |in| into |DEGREE| values in --// |out|. It returns one on success and zero if any parsed value is >= --// |kPrime|. --static int scalar_decode(scalar *out, const uint8_t *in, int bits) { -- assert(bits <= (int)sizeof(*out->c) * 8 && bits != 1); ++} ++ +/************************************************* +* Name: polyvec_frombytes +* @@ -1995,9 +1382,7 @@ index 776c085f9..ccb5b3d9b 100644 + for(i=0;ivec[i], a+i*KYBER_POLYBYTES); +} - -- uint8_t in_byte = 0; -- int in_byte_bits_left = 0; ++ +/************************************************* +* Name: polyvec_ntt +* @@ -2011,10 +1396,7 @@ index 776c085f9..ccb5b3d9b 100644 + for(i=0;ivec[i]); +} - -- for (int i = 0; i < DEGREE; i++) { -- uint16_t element = 0; -- int element_bits_done = 0; ++ +/************************************************* +* Name: polyvec_invntt_tomont +* @@ -2029,13 +1411,7 @@ index 776c085f9..ccb5b3d9b 100644 + for(i=0;ivec[i]); +} - -- while (element_bits_done < bits) { -- if (in_byte_bits_left == 0) { -- in_byte = *in; -- in++; -- in_byte_bits_left = 8; -- } ++ +/************************************************* +* Name: polyvec_basemul_acc_montgomery +* @@ -2056,17 +1432,10 @@ index 776c085f9..ccb5b3d9b 100644 + poly_basemul_montgomery(&t, &a->vec[i], &b->vec[i]); + poly_add(r, r, &t); + } - -- int chunk_bits = bits - element_bits_done; -- if (chunk_bits > in_byte_bits_left) { -- chunk_bits = in_byte_bits_left; -- } ++ + poly_reduce(r); +} - -- element |= (in_byte & kMasks[chunk_bits - 1]) << element_bits_done; -- in_byte_bits_left -= chunk_bits; -- in_byte >>= chunk_bits; ++ +/************************************************* +* Name: polyvec_reduce +* @@ -2082,9 +1451,7 @@ index 776c085f9..ccb5b3d9b 100644 + for(i=0;ivec[i]); +} - -- element_bits_done += chunk_bits; -- } ++ +/************************************************* +* Name: polyvec_add +* @@ -2100,12 +1467,7 @@ index 776c085f9..ccb5b3d9b 100644 + for(i=0;ivec[i], &a->vec[i], &b->vec[i]); +} - -- if (element >= kPrime) { -- return 0; -- } -- out->c[i] = element; -- } ++ +// +// indcpa.c +// @@ -2154,21 +1516,12 @@ index 776c085f9..ccb5b3d9b 100644 + + if(verify(repacked, packedpk, KYBER_POLYVECBYTES) != 0) + return 0; - ++ + for(i=0;ic[i + j] = in_byte & 1; -- in_byte >>= 1; -- } ++ return 1; ++} ++ +/************************************************* +* Name: pack_sk +* @@ -2259,17 +1612,11 @@ index 776c085f9..ccb5b3d9b 100644 + r[ctr++] = val0; + if(ctr < len && val1 < KYBER_Q) + r[ctr++] = val1; - } ++ } + + return ctr; - } - --// Decodes 32*|RANK|*|bits| bytes from |in| into |out|. It returns one on --// success or zero if any parsed value is >= |kPrime|. --static int vector_decode(vector *out, const uint8_t *in, int bits) { -- for (int i = 0; i < RANK; i++) { -- if (!scalar_decode(&out->v[i], in + i * bits * DEGREE / 8, bits)) { -- return 0; ++} ++ +#define gen_a(A,B) gen_matrix(A,B,0) +#define gen_at(A,B) gen_matrix(A,B,1) + @@ -2313,52 +1660,10 @@ index 776c085f9..ccb5b3d9b 100644 + buflen = off + XOF_BLOCKBYTES; + ctr += rej_uniform(a[i].vec[j].coeffs + ctr, KYBER_N - ctr, buf, buflen); + } - } - } -- return 1; - } - --// Compresses (lossily) an input |x| mod 3329 into |bits| many bits by grouping --// numbers close to each other together. The formula used is --// round(2^|bits|/kPrime*x) mod 2^|bits|. --// Uses Barrett reduction to achieve constant time. Since we need both the --// remainder (for rounding) and the quotient (as the result), we cannot use --// |reduce| here, but need to do the Barrett reduction directly. --static uint16_t compress(uint16_t x, int bits) { -- uint32_t product = (uint32_t)x << bits; -- uint32_t quotient = ((uint64_t)product * kBarrettMultiplier) >> kBarrettShift; -- uint32_t remainder = product - quotient * kPrime; -- -- // Adjust the quotient to round correctly: -- // 0 <= remainder <= kHalfPrime round to 0 -- // kHalfPrime < remainder <= kPrime + kHalfPrime round to 1 -- // kPrime + kHalfPrime < remainder < 2 * kPrime round to 2 -- assert(remainder < 2u * kPrime); -- quotient += 1 & constant_time_lt_w(kHalfPrime, remainder); -- quotient += 1 & constant_time_lt_w(kPrime + kHalfPrime, remainder); -- return quotient & ((1 << bits) - 1); --} -- --// Decompresses |x| by using an equi-distant representative. The formula is --// round(kPrime/2^|bits|*x). Note that 2^|bits| being the divisor allows us to --// implement this logic using only bit operations. --static uint16_t decompress(uint16_t x, int bits) { -- uint32_t product = (uint32_t)x * kPrime; -- uint32_t power = 1 << bits; -- // This is |product| % power, since |power| is a power of 2. -- uint32_t remainder = product & (power - 1); -- // This is |product| / power, since |power| is a power of 2. -- uint32_t lower = product >> bits; -- // The rounding logic works since the first half of numbers mod |power| have a -- // 0 as first bit, and the second half has a 1 as first bit, since |power| is -- // a power of 2. As a 12 bit number, |remainder| is always positive, so we -- // will shift in 0s for a right shift. -- return lower + (remainder >> (bits - 1)); --} -- --static void scalar_compress(scalar *s, int bits) { -- for (int i = 0; i < DEGREE; i++) { -- s->c[i] = compress(s->c[i], bits); ++ } ++ } ++} ++ +/************************************************* +* Name: indcpa_keypair +* @@ -2398,19 +1703,15 @@ index 776c085f9..ccb5b3d9b 100644 + for(i=0;ic[i] = decompress(s->c[i], bits); -- } ++} ++ +/************************************************* +* Name: indcpa_enc +* @@ -2469,12 +1770,8 @@ index 776c085f9..ccb5b3d9b 100644 + + pack_ciphertext(c, &b, &v); + return 1; - } - --static void vector_compress(vector *a, int bits) { -- for (int i = 0; i < RANK; i++) { -- scalar_compress(&a->v[i], bits); -- } ++} ++ +/************************************************* +* Name: indcpa_dec +* @@ -2506,12 +1803,8 @@ index 776c085f9..ccb5b3d9b 100644 + poly_reduce(&mp); + + poly_tomsg(m, &mp); - } - --static void vector_decompress(vector *a, int bits) { -- for (int i = 0; i < RANK; i++) { -- scalar_decompress(&a->v[i], bits); -- } ++} ++ +// +// fips202.c +// @@ -2541,13 +1834,8 @@ index 776c085f9..ccb5b3d9b 100644 + r |= (uint64_t)x[i] << 8*i; + + return r; - } - --struct public_key { -- vector t; -- uint8_t rho[32]; -- uint8_t public_key_hash[32]; -- matrix m; ++} ++ +/************************************************* +* Name: store64 +* @@ -2591,13 +1879,16 @@ index 776c085f9..ccb5b3d9b 100644 + (uint64_t)0x8000000080008008ULL }; --static struct public_key *public_key_from_external( -- const struct KYBER_public_key *external) { -- static_assert(sizeof(struct KYBER_public_key) >= sizeof(struct public_key), -- "Kyber public key is too small"); -- static_assert(alignof(struct KYBER_public_key) >= alignof(struct public_key), -- "Kyber public key align incorrect"); -- return (struct public_key *)external; +-// reduce_once reduces 0 <= x < 2*kPrime, mod kPrime. +-static uint16_t reduce_once(uint16_t x) { +- assert(x < 2 * kPrime); +- const uint16_t subtracted = x - kPrime; +- uint16_t mask = 0u - (subtracted >> 15); +- // On Aarch64, omitting a |value_barrier_u16| results in a 2x speedup of Kyber +- // overall and Clang still produces constant-time code using `csel`. On other +- // platforms & compilers on godbolt that we care about, this code also +- // produces constant-time output. +- return (mask & x) | (~mask & subtracted); +/************************************************* +* Name: KeccakF1600_StatePermute +* @@ -2869,36 +2160,17 @@ index 776c085f9..ccb5b3d9b 100644 + state[24] = Asu; } --struct private_key { -- struct public_key pub; -- vector s; -- uint8_t fo_failure_secret[32]; --}; +-// constant time reduce x mod kPrime using Barrett reduction. x must be less +-// than kPrime + 2×kPrime². +-static uint16_t reduce(uint32_t x) { +- assert(x < kPrime + 2u * kPrime * kPrime); +- uint64_t product = (uint64_t)x * kBarrettMultiplier; +- uint32_t quotient = (uint32_t)(product >> kBarrettShift); +- uint32_t remainder = x - quotient * kPrime; +- return reduce_once(remainder); +-} --static struct private_key *private_key_from_external( -- const struct KYBER_private_key *external) { -- static_assert(sizeof(struct KYBER_private_key) >= sizeof(struct private_key), -- "Kyber private key too small"); -- static_assert( -- alignof(struct KYBER_private_key) >= alignof(struct private_key), -- "Kyber private key align incorrect"); -- return (struct private_key *)external; --} -- --// Calls |KYBER_generate_key_external_entropy| with random bytes from --// |RAND_bytes|. --void KYBER_generate_key(uint8_t out_encoded_public_key[KYBER_PUBLIC_KEY_BYTES], -- struct KYBER_private_key *out_private_key) { -- uint8_t entropy[KYBER_GENERATE_KEY_ENTROPY]; -- RAND_bytes(entropy, sizeof(entropy)); -- KYBER_generate_key_external_entropy(out_encoded_public_key, out_private_key, -- entropy); --} -- --static int kyber_marshal_public_key(CBB *out, const struct public_key *pub) { -- uint8_t *vector_output; -- if (!CBB_add_space(out, &vector_output, kEncodedVectorSize)) { -- return 0; +-static void scalar_zero(scalar *out) { OPENSSL_memset(out, 0, sizeof(*out)); } +/************************************************* +* Name: keccak_squeeze +* @@ -2921,20 +2193,41 @@ index 776c085f9..ccb5b3d9b 100644 + unsigned int r) +{ + unsigned int i; -+ + +-static void vector_zero(vector *out) { OPENSSL_memset(out, 0, sizeof(*out)); } +- +-// In place number theoretic transform of a given scalar. +-// Note that Kyber's kPrime 3329 does not have a 512th root of unity, so this +-// transform leaves off the last iteration of the usual FFT code, with the 128 +-// relevant roots of unity being stored in |kNTTRoots|. This means the output +-// should be seen as 128 elements in GF(3329^2), with the coefficients of the +-// elements being consecutive entries in |s->c|. +-static void scalar_ntt(scalar *s) { +- int offset = DEGREE; +- // `int` is used here because using `size_t` throughout caused a ~5% slowdown +- // with Clang 14 on Aarch64. +- for (int step = 1; step < DEGREE / 2; step <<= 1) { +- offset >>= 1; +- int k = 0; +- for (int i = 0; i < step; i++) { +- const uint32_t step_root = kNTTRoots[i + step]; +- for (int j = k; j < k + offset; j++) { +- uint16_t odd = reduce(step_root * s->c[j + offset]); +- uint16_t even = s->c[j]; +- s->c[j] = reduce_once(odd + even); +- s->c[j + offset] = reduce_once(even - odd + kPrime); +- } +- k += 2 * offset; + while(outlen) { + if(pos == r) { + KeccakF1600_StatePermute(s); + pos = 0; -+ } + } + for(i=pos;i < r && i < pos+outlen; i++) + *out++ = s[i/8] >> 8*(i%8); + outlen -= i-pos; + pos = i; - } -- vector_encode(vector_output, &pub->t, kLog2Prime); -- if (!CBB_add_bytes(out, pub->rho, sizeof(pub->rho))) { -- return 0; ++ } + + return pos; +} @@ -2966,8 +2259,7 @@ index 776c085f9..ccb5b3d9b 100644 + inlen -= r-pos; + KeccakF1600_StatePermute(s); + pos = 0; - } -- return 1; ++ } + + for(i=pos;ipub.rho, hashed, sizeof(priv->pub.rho)); -- matrix_expand(&priv->pub.m, rho); -- uint8_t counter = 0; -- vector_generate_secret_eta_2(&priv->s, &counter, sigma); -- vector_ntt(&priv->s); -- vector error; -- vector_generate_secret_eta_2(&error, &counter, sigma); -- vector_ntt(&error); -- matrix_mult_transpose(&priv->pub.t, &priv->pub.m, &priv->s); -- vector_add(&priv->pub.t, &error); -- -- CBB cbb; -- CBB_init_fixed(&cbb, out_encoded_public_key, KYBER_PUBLIC_KEY_BYTES); -- if (!kyber_marshal_public_key(&cbb, &priv->pub)) { -- abort(); ++} ++ + +/************************************************* +* Name: keccak_absorb_once @@ -3048,8 +2313,824 @@ index 776c085f9..ccb5b3d9b 100644 + in += r; + inlen -= r; + KeccakF1600_StatePermute(s); ++ } ++ ++ for(i=0;iv[i]); +- } ++ ++/************************************************* ++* Name: shake128_absorb_once ++* ++* Description: Initialize, absorb into and finalize SHAKE128 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 shake128_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen) ++{ ++ keccak_absorb_once(state->s, SHAKE128_RATE, in, inlen, 0x1F); ++ state->pos = SHAKE128_RATE; + } + +-// In place inverse number theoretic transform of a given scalar, with pairs of +-// entries of s->v being interpreted as elements of GF(3329^2). Just as with the +-// number theoretic transform, this leaves off the first step of the normal iFFT +-// to account for the fact that 3329 does not have a 512th root of unity, using +-// the precomputed 128 roots of unity stored in |kInverseNTTRoots|. +-static void scalar_inverse_ntt(scalar *s) { +- int step = DEGREE / 2; +- // `int` is used here because using `size_t` throughout caused a ~5% slowdown +- // with Clang 14 on Aarch64. +- for (int offset = 2; offset < DEGREE; offset <<= 1) { +- step >>= 1; +- int k = 0; +- for (int i = 0; i < step; i++) { +- uint32_t step_root = kInverseNTTRoots[i + step]; +- for (int j = k; j < k + offset; j++) { +- uint16_t odd = s->c[j + offset]; +- uint16_t even = s->c[j]; +- s->c[j] = reduce_once(odd + even); +- s->c[j + offset] = reduce(step_root * (even - odd + kPrime)); +- } +- k += 2 * offset; +- } +- } +- for (int i = 0; i < DEGREE; i++) { +- s->c[i] = reduce(s->c[i] * kInverseDegree); +- } ++/************************************************* ++* 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); + } + +-static void vector_inverse_ntt(vector *a) { +- for (int i = 0; i < RANK; i++) { +- scalar_inverse_ntt(&a->v[i]); +- } ++/************************************************* ++* 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); + } + +-static void scalar_add(scalar *lhs, const scalar *rhs) { +- for (int i = 0; i < DEGREE; i++) { +- lhs->c[i] = reduce_once(lhs->c[i] + rhs->c[i]); +- } ++/************************************************* ++* Name: 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; + } + +-static void scalar_sub(scalar *lhs, const scalar *rhs) { +- for (int i = 0; i < DEGREE; i++) { +- lhs->c[i] = reduce_once(lhs->c[i] - rhs->c[i] + kPrime); +- } ++/************************************************* ++* 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); + } + +-// Multiplying two scalars in the number theoretically transformed state. Since +-// 3329 does not have a 512th root of unity, this means we have to interpret +-// the 2*ith and (2*i+1)th entries of the scalar as elements of GF(3329)[X]/(X^2 +-// - 17^(2*bitreverse(i)+1)) The value of 17^(2*bitreverse(i)+1) mod 3329 is +-// stored in the precomputed |kModRoots| table. Note that our Barrett transform +-// only allows us to multipy two reduced numbers together, so we need some +-// intermediate reduction steps, even if an uint64_t could hold 3 multiplied +-// numbers. +-static void scalar_mult(scalar *out, const scalar *lhs, const scalar *rhs) { +- for (int i = 0; i < DEGREE / 2; i++) { +- uint32_t real_real = (uint32_t)lhs->c[2 * i] * rhs->c[2 * i]; +- uint32_t img_img = (uint32_t)lhs->c[2 * i + 1] * rhs->c[2 * i + 1]; +- uint32_t real_img = (uint32_t)lhs->c[2 * i] * rhs->c[2 * i + 1]; +- uint32_t img_real = (uint32_t)lhs->c[2 * i + 1] * rhs->c[2 * i]; +- out->c[2 * i] = +- reduce(real_real + (uint32_t)reduce(img_img) * kModRoots[i]); +- out->c[2 * i + 1] = reduce(img_real + real_img); +- } ++/************************************************* ++* Name: shake256_absorb ++* ++* Description: Absorb step of the SHAKE256 XOF; incremental. ++* ++* Arguments: - keccak_state *state: pointer to (initialized) output Keccak state ++* - const uint8_t *in: pointer to input to be absorbed into s ++* - size_t inlen: length of input in bytes ++**************************************************/ ++static void shake256_absorb(keccak_state *state, const uint8_t *in, size_t inlen) ++{ ++ state->pos = keccak_absorb(state->s, state->pos, SHAKE256_RATE, in, inlen); + } + +-static void vector_add(vector *lhs, const vector *rhs) { +- for (int i = 0; i < RANK; i++) { +- scalar_add(&lhs->v[i], &rhs->v[i]); +- } ++/************************************************* ++* Name: shake256_finalize ++* ++* Description: Finalize absorb step of the SHAKE256 XOF. ++* ++* Arguments: - keccak_state *state: pointer to Keccak state ++**************************************************/ ++static void shake256_finalize(keccak_state *state) ++{ ++ keccak_finalize(state->s, state->pos, SHAKE256_RATE, 0x1F); ++ state->pos = SHAKE256_RATE; + } + +-static void matrix_mult(vector *out, const matrix *m, const vector *a) { +- vector_zero(out); +- for (int i = 0; i < RANK; i++) { +- for (int j = 0; j < RANK; j++) { +- scalar product; +- scalar_mult(&product, &m->v[i][j], &a->v[j]); +- scalar_add(&out->v[i], &product); +- } +- } ++/************************************************* ++* Name: keccak_init ++* ++* Description: Initializes the Keccak state. ++* ++* Arguments: - uint64_t *s: pointer to Keccak state ++**************************************************/ ++static void keccak_init(uint64_t s[25]) ++{ ++ unsigned int i; ++ for(i=0;i<25;i++) ++ s[i] = 0; + } + +-static void matrix_mult_transpose(vector *out, const matrix *m, +- const vector *a) { +- vector_zero(out); +- for (int i = 0; i < RANK; i++) { +- for (int j = 0; j < RANK; j++) { +- scalar product; +- scalar_mult(&product, &m->v[j][i], &a->v[j]); +- scalar_add(&out->v[i], &product); +- } +- } ++/************************************************* ++* Name: shake256_init ++* ++* Description: Initilizes Keccak state for use as SHAKE256 XOF ++* ++* Arguments: - keccak_state *state: pointer to (uninitialized) Keccak state ++**************************************************/ ++static void shake256_init(keccak_state *state) ++{ ++ keccak_init(state->s); ++ state->pos = 0; + } + +-static void scalar_inner_product(scalar *out, const vector *lhs, +- const vector *rhs) { +- scalar_zero(out); +- for (int i = 0; i < RANK; i++) { +- scalar product; +- scalar_mult(&product, &lhs->v[i], &rhs->v[i]); +- scalar_add(out, &product); +- } ++ ++/************************************************* ++* 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); + } + +-// Algorithm 1 of the Kyber spec. Rejection samples a Keccak stream to get +-// uniformly distributed elements. This is used for matrix expansion and only +-// operates on public inputs. +-static void scalar_from_keccak_vartime(scalar *out, +- struct BORINGSSL_keccak_st *keccak_ctx) { +- assert(keccak_ctx->squeeze_offset == 0); +- assert(keccak_ctx->rate_bytes == 168); +- static_assert(168 % 3 == 0, "block and coefficient boundaries do not align"); ++/************************************************* ++* 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]; + +- int done = 0; +- while (done < DEGREE) { +- uint8_t block[168]; +- BORINGSSL_keccak_squeeze(keccak_ctx, block, sizeof(block)); +- for (size_t i = 0; i < sizeof(block) && done < DEGREE; i += 3) { +- uint16_t d1 = block[i] + 256 * (block[i + 1] % 16); +- uint16_t d2 = block[i + 1] / 16 + 16 * block[i + 2]; +- if (d1 < kPrime) { +- out->c[done++] = d1; +- } +- if (d2 < kPrime && done < DEGREE) { +- out->c[done++] = d2; +- } +- } +- } ++ 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]); + } + +-// Algorithm 2 of the Kyber spec, with eta fixed to two and the PRF call +-// included. Creates binominally distributed elements by sampling 2*|eta| bits, +-// and setting the coefficient to the count of the first bits minus the count of +-// the second bits, resulting in a centered binomial distribution. Since eta is +-// two this gives -2/2 with a probability of 1/16, -1/1 with probability 1/4, +-// and 0 with probability 3/8. +-static void scalar_centered_binomial_distribution_eta_2_with_prf( +- scalar *out, const uint8_t input[33]) { +- uint8_t entropy[128]; +- static_assert(sizeof(entropy) == 2 * /*kEta=*/2 * DEGREE / 8, ""); +- BORINGSSL_keccak(entropy, sizeof(entropy), input, 33, boringssl_shake256); ++/************************************************* ++* 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]; + +- for (int i = 0; i < DEGREE; i += 2) { +- uint8_t byte = entropy[i / 2]; +- +- uint16_t value = kPrime; +- value += (byte & 1) + ((byte >> 1) & 1); +- value -= ((byte >> 2) & 1) + ((byte >> 3) & 1); +- out->c[i] = reduce_once(value); +- +- byte >>= 4; +- value = kPrime; +- value += (byte & 1) + ((byte >> 1) & 1); +- value -= ((byte >> 2) & 1) + ((byte >> 3) & 1); +- out->c[i + 1] = reduce_once(value); +- } ++ 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]); + } + +-// Generates a secret vector by using +-// |scalar_centered_binomial_distribution_eta_2_with_prf|, using the given seed +-// appending and incrementing |counter| for entry of the vector. +-static void vector_generate_secret_eta_2(vector *out, uint8_t *counter, +- const uint8_t seed[32]) { +- uint8_t input[33]; +- OPENSSL_memcpy(input, seed, 32); +- for (int i = 0; i < RANK; i++) { +- input[32] = (*counter)++; +- scalar_centered_binomial_distribution_eta_2_with_prf(&out->v[i], input); +- } ++// ++// 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)); + } + +-// Expands the matrix of a seed for key generation and for encaps-CPA. +-static void matrix_expand(matrix *out, const uint8_t rho[32]) { +- uint8_t input[34]; +- OPENSSL_memcpy(input, rho, 32); +- for (int i = 0; i < RANK; i++) { +- for (int j = 0; j < RANK; j++) { +- input[32] = i; +- input[33] = j; +- struct BORINGSSL_keccak_st keccak_ctx; +- BORINGSSL_keccak_init(&keccak_ctx, boringssl_shake128); +- BORINGSSL_keccak_absorb(&keccak_ctx, input, sizeof(input)); +- scalar_from_keccak_vartime(&out->v[i][j], &keccak_ctx); +- } +- } ++/************************************************* ++* 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)); + } + +-static const uint8_t kMasks[8] = {0x01, 0x03, 0x07, 0x0f, +- 0x1f, 0x3f, 0x7f, 0xff}; ++// ++// kem.c ++// + +-static void scalar_encode(uint8_t *out, const scalar *s, int bits) { +- assert(bits <= (int)sizeof(*s->c) * 8 && bits != 1); ++// 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]; + +- uint8_t out_byte = 0; +- int out_byte_bits = 0; +- +- for (int i = 0; i < DEGREE; i++) { +- uint16_t element = s->c[i]; +- int element_bits_done = 0; +- +- while (element_bits_done < bits) { +- int chunk_bits = bits - element_bits_done; +- int out_bits_remaining = 8 - out_byte_bits; +- if (chunk_bits >= out_bits_remaining) { +- chunk_bits = out_bits_remaining; +- out_byte |= (element & kMasks[chunk_bits - 1]) << out_byte_bits; +- *out = out_byte; +- out++; +- out_byte_bits = 0; +- out_byte = 0; +- } else { +- out_byte |= (element & kMasks[chunk_bits - 1]) << out_byte_bits; +- out_byte_bits += chunk_bits; +- } +- +- element_bits_done += chunk_bits; +- element >>= chunk_bits; +- } +- } +- +- if (out_byte_bits > 0) { +- *out = out_byte; +- } ++ indcpa_keypair(pk, sk, seed); ++ for(i=0;ic[i + j] & 1) << j; +- } +- *out = out_byte; +- out++; +- } +-} ++// Modified crypto_kem_enc to BoringSSL style API ++int encap(uint8_t out_ciphertext[KYBER_CIPHERTEXTBYTES], ++ uint8_t ss[KYBER_KEY_BYTES], ++ const struct public_key *in_pub, ++ const uint8_t seed[KYBER_ENCAP_BYTES], int mlkem) ++{ ++ const uint8_t *pk = &in_pub->opaque[0]; ++ uint8_t *ct = out_ciphertext; ++ ++ uint8_t buf[2*KYBER_SYMBYTES]; ++ /* Will contain key, coins */ ++ uint8_t kr[2*KYBER_SYMBYTES]; + +-// Encodes an entire vector into 32*|RANK|*|bits| bytes. Note that since 256 +-// (DEGREE) is divisible by 8, the individual vector entries will always fill a +-// whole number of bytes, so we do not need to worry about bit packing here. +-static void vector_encode(uint8_t *out, const vector *a, int bits) { +- for (int i = 0; i < RANK; i++) { +- scalar_encode(out + i * bits * DEGREE / 8, &a->v[i], bits); +- } +-} ++ memcpy(buf, seed, KYBER_SYMBYTES); + +-// scalar_decode parses |DEGREE * bits| bits from |in| into |DEGREE| values in +-// |out|. It returns one on success and zero if any parsed value is >= +-// |kPrime|. +-static int scalar_decode(scalar *out, const uint8_t *in, int bits) { +- assert(bits <= (int)sizeof(*out->c) * 8 && bits != 1); ++ /* Don't release system RNG output */ ++ hash_h(buf, buf, KYBER_SYMBYTES); + +- uint8_t in_byte = 0; +- int in_byte_bits_left = 0; ++ /* Multitarget countermeasure for coins + contributory KEM */ ++ hash_h(buf+KYBER_SYMBYTES, pk, KYBER_PUBLICKEYBYTES); ++ hash_g(kr, buf, 2*KYBER_SYMBYTES); + +- for (int i = 0; i < DEGREE; i++) { +- uint16_t element = 0; +- int element_bits_done = 0; ++ /* coins are in kr+KYBER_SYMBYTES */ ++ if(!indcpa_enc(ct, buf, pk, kr+KYBER_SYMBYTES)) ++ return 0; + +- while (element_bits_done < bits) { +- if (in_byte_bits_left == 0) { +- in_byte = *in; +- in++; +- in_byte_bits_left = 8; +- } +- +- int chunk_bits = bits - element_bits_done; +- if (chunk_bits > in_byte_bits_left) { +- chunk_bits = in_byte_bits_left; +- } +- +- element |= (in_byte & kMasks[chunk_bits - 1]) << element_bits_done; +- in_byte_bits_left -= chunk_bits; +- in_byte >>= chunk_bits; +- +- element_bits_done += chunk_bits; +- } +- +- if (element >= kPrime) { +- return 0; +- } +- out->c[i] = element; +- } +- +- return 1; +-} +- +-// scalar_decode_1 is |scalar_decode| specialised for |bits| == 1. +-static void scalar_decode_1(scalar *out, const uint8_t in[32]) { +- for (int i = 0; i < DEGREE; i += 8) { +- uint8_t in_byte = *in; +- in++; +- for (int j = 0; j < 8; j++) { +- out->c[i + j] = in_byte & 1; +- in_byte >>= 1; +- } +- } +-} +- +-// Decodes 32*|RANK|*|bits| bytes from |in| into |out|. It returns one on +-// success or zero if any parsed value is >= |kPrime|. +-static int vector_decode(vector *out, const uint8_t *in, int bits) { +- for (int i = 0; i < RANK; i++) { +- if (!scalar_decode(&out->v[i], in + i * bits * DEGREE / 8, bits)) { +- return 0; +- } ++ if (mlkem == 1) { ++ memcpy(ss, kr, KYBER_SYMBYTES); ++ } else { ++ /* overwrite coins in kr with H(c) */ ++ hash_h(kr+KYBER_SYMBYTES, ct, KYBER_CIPHERTEXTBYTES); ++ /* hash concatenation of pre-k and H(c) to k */ ++ kdf(ss, kr, 2*KYBER_SYMBYTES); + } + return 1; + } + +-// Compresses (lossily) an input |x| mod 3329 into |bits| many bits by grouping +-// numbers close to each other together. The formula used is +-// round(2^|bits|/kPrime*x) mod 2^|bits|. +-// Uses Barrett reduction to achieve constant time. Since we need both the +-// remainder (for rounding) and the quotient (as the result), we cannot use +-// |reduce| here, but need to do the Barrett reduction directly. +-static uint16_t compress(uint16_t x, int bits) { +- uint32_t shifted = (uint32_t)x << bits; +- uint64_t product = (uint64_t)shifted * kBarrettMultiplier; +- uint32_t quotient = (uint32_t)(product >> kBarrettShift); +- uint32_t remainder = shifted - quotient * kPrime; ++// Modified crypto_kem_decap to BoringSSL style API ++void decap(uint8_t out_shared_key[KYBER_SSBYTES], ++ const struct private_key *in_priv, ++ const uint8_t *ct, size_t ciphertext_len, int mlkem) ++{ ++ uint8_t *ss = out_shared_key; ++ const uint8_t *sk = &in_priv->opaque[0]; + +- // Adjust the quotient to round correctly: +- // 0 <= remainder <= kHalfPrime round to 0 +- // kHalfPrime < remainder <= kPrime + kHalfPrime round to 1 +- // kPrime + kHalfPrime < remainder < 2 * kPrime round to 2 +- assert(remainder < 2u * kPrime); +- quotient += 1 & constant_time_lt_w(kHalfPrime, remainder); +- quotient += 1 & constant_time_lt_w(kPrime + kHalfPrime, remainder); +- return quotient & ((1 << bits) - 1); +-} ++ 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; + +-// Decompresses |x| by using an equi-distant representative. The formula is +-// round(kPrime/2^|bits|*x). Note that 2^|bits| being the divisor allows us to +-// implement this logic using only bit operations. +-static uint16_t decompress(uint16_t x, int bits) { +- uint32_t product = (uint32_t)x * kPrime; +- uint32_t power = 1 << bits; +- // This is |product| % power, since |power| is a power of 2. +- uint32_t remainder = product & (power - 1); +- // This is |product| / power, since |power| is a power of 2. +- uint32_t lower = product >> bits; +- // The rounding logic works since the first half of numbers mod |power| have a +- // 0 as first bit, and the second half has a 1 as first bit, since |power| is +- // a power of 2. As a 12 bit number, |remainder| is always positive, so we +- // will shift in 0s for a right shift. +- return lower + (remainder >> (bits - 1)); +-} ++ if (ciphertext_len == KYBER_CIPHERTEXTBYTES) { ++ indcpa_dec(buf, ct, sk); + +-static void scalar_compress(scalar *s, int bits) { +- for (int i = 0; i < DEGREE; i++) { +- s->c[i] = compress(s->c[i], bits); ++ /* Multitarget countermeasure for coins + contributory KEM */ ++ for(i=0;ic[i] = decompress(s->c[i], bits); +- } ++void marshal_public_key(uint8_t out[KYBER_PUBLICKEYBYTES], ++ const struct public_key *in_pub) { ++ memcpy(out, &in_pub->opaque, KYBER_PUBLICKEYBYTES); + } + +-static void vector_compress(vector *a, int bits) { +- for (int i = 0; i < RANK; i++) { +- scalar_compress(&a->v[i], bits); +- } +-} +- +-static void vector_decompress(vector *a, int bits) { +- for (int i = 0; i < RANK; i++) { +- scalar_decompress(&a->v[i], bits); +- } +-} +- +-struct public_key { +- vector t; +- uint8_t rho[32]; +- uint8_t public_key_hash[32]; +- matrix m; +-}; +- +-static struct public_key *public_key_from_external( +- const struct KYBER_public_key *external) { +- static_assert(sizeof(struct KYBER_public_key) >= sizeof(struct public_key), +- "Kyber public key is too small"); +- static_assert(alignof(struct KYBER_public_key) >= alignof(struct public_key), +- "Kyber public key align incorrect"); +- return (struct public_key *)external; +-} +- +-struct private_key { +- struct public_key pub; +- vector s; +- uint8_t fo_failure_secret[32]; +-}; +- +-static struct private_key *private_key_from_external( +- const struct KYBER_private_key *external) { +- static_assert(sizeof(struct KYBER_private_key) >= sizeof(struct private_key), +- "Kyber private key too small"); +- static_assert( +- alignof(struct KYBER_private_key) >= alignof(struct private_key), +- "Kyber private key align incorrect"); +- return (struct private_key *)external; +-} +- +-// Calls |KYBER_generate_key_external_entropy| with random bytes from +-// |RAND_bytes|. +-void KYBER_generate_key(uint8_t out_encoded_public_key[KYBER_PUBLIC_KEY_BYTES], +- struct KYBER_private_key *out_private_key) { +- uint8_t entropy[KYBER_GENERATE_KEY_ENTROPY]; +- RAND_bytes(entropy, sizeof(entropy)); +- KYBER_generate_key_external_entropy(out_encoded_public_key, out_private_key, +- entropy); +-} +- +-static int kyber_marshal_public_key(CBB *out, const struct public_key *pub) { +- uint8_t *vector_output; +- if (!CBB_add_space(out, &vector_output, kEncodedVectorSize)) { +- return 0; +- } +- vector_encode(vector_output, &pub->t, kLog2Prime); +- if (!CBB_add_bytes(out, pub->rho, sizeof(pub->rho))) { +- return 0; +- } +- return 1; +-} +- +-// Algorithms 4 and 7 of the Kyber spec. Algorithms are combined since key +-// generation is not part of the FO transform, and the spec uses Algorithm 7 to +-// specify the actual key format. +-void KYBER_generate_key_external_entropy( +- uint8_t out_encoded_public_key[KYBER_PUBLIC_KEY_BYTES], +- struct KYBER_private_key *out_private_key, +- const uint8_t entropy[KYBER_GENERATE_KEY_ENTROPY]) { +- struct private_key *priv = private_key_from_external(out_private_key); +- uint8_t hashed[64]; +- BORINGSSL_keccak(hashed, sizeof(hashed), entropy, 32, boringssl_sha3_512); +- const uint8_t *const rho = hashed; +- const uint8_t *const sigma = hashed + 32; +- OPENSSL_memcpy(priv->pub.rho, hashed, sizeof(priv->pub.rho)); +- matrix_expand(&priv->pub.m, rho); +- uint8_t counter = 0; +- vector_generate_secret_eta_2(&priv->s, &counter, sigma); +- vector_ntt(&priv->s); +- vector error; +- vector_generate_secret_eta_2(&error, &counter, sigma); +- vector_ntt(&error); +- matrix_mult_transpose(&priv->pub.t, &priv->pub.m, &priv->s); +- vector_add(&priv->pub.t, &error); +- +- CBB cbb; +- CBB_init_fixed(&cbb, out_encoded_public_key, KYBER_PUBLIC_KEY_BYTES); +- if (!kyber_marshal_public_key(&cbb, &priv->pub)) { +- abort(); +- } +- - BORINGSSL_keccak(priv->pub.public_key_hash, sizeof(priv->pub.public_key_hash), - out_encoded_public_key, KYBER_PUBLIC_KEY_BYTES, - boringssl_sha3_256); @@ -3180,65 +3261,18 @@ index 776c085f9..ccb5b3d9b 100644 - for (int i = 0; i < 32; i++) { - input[i] = constant_time_select_8(mask, prekey_and_randomness[i], - priv->fo_failure_secret[i]); -+ for(i=0;is, SHAKE128_RATE, in, inlen, 0x1F); -+ state->pos = SHAKE128_RATE; - } - +-} +- -// kyber_parse_public_key_no_hash parses |in| into |pub| but doesn't calculate -// the value of |pub->public_key_hash|. -static int kyber_parse_public_key_no_hash(struct public_key *pub, CBS *in) { @@ -3250,336 +3284,27 @@ index 776c085f9..ccb5b3d9b 100644 - } - matrix_expand(&pub->m, pub->rho); - return 1; -+/************************************************* -+* Name: shake128_squeezeblocks -+* -+* Description: Squeeze step of SHAKE128 XOF. Squeezes full blocks of -+* SHAKE128_RATE bytes each. Can be called multiple times -+* to keep squeezing. Assumes new block has not yet been -+* started (state->pos = SHAKE128_RATE). -+* -+* Arguments: - uint8_t *out: pointer to output blocks -+* - size_t nblocks: number of blocks to be squeezed (written to output) -+* - keccak_state *s: pointer to input/output Keccak state -+**************************************************/ -+static void shake128_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state) -+{ -+ keccak_squeezeblocks(out, nblocks, state->s, SHAKE128_RATE); -+} -+ -+/************************************************* -+* Name: shake256_squeeze -+* -+* Description: Squeeze step of SHAKE256 XOF. Squeezes arbitraily many -+* bytes. Can be called multiple times to keep squeezing. -+* -+* Arguments: - uint8_t *out: pointer to output blocks -+* - size_t outlen : number of bytes to be squeezed (written to output) -+* - keccak_state *s: pointer to input/output Keccak state -+**************************************************/ -+static void shake256_squeeze(uint8_t *out, size_t outlen, keccak_state *state) -+{ -+ state->pos = keccak_squeeze(out, outlen, state->s, state->pos, SHAKE256_RATE); -+} -+ -+/************************************************* -+* Name: shake256_absorb_once -+* -+* Description: Initialize, absorb into and finalize SHAKE256 XOF; non-incremental. -+* -+* Arguments: - keccak_state *state: pointer to (uninitialized) output Keccak state -+* - const uint8_t *in: pointer to input to be absorbed into s -+* - size_t inlen: length of input in bytes -+**************************************************/ -+static void shake256_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen) -+{ -+ keccak_absorb_once(state->s, SHAKE256_RATE, in, inlen, 0x1F); -+ state->pos = SHAKE256_RATE; -+} -+ -+/************************************************* -+* Name: shake256_squeezeblocks -+* -+* Description: Squeeze step of SHAKE256 XOF. Squeezes full blocks of -+* SHAKE256_RATE bytes each. Can be called multiple times -+* to keep squeezing. Assumes next block has not yet been -+* started (state->pos = SHAKE256_RATE). -+* -+* Arguments: - uint8_t *out: pointer to output blocks -+* - size_t nblocks: number of blocks to be squeezed (written to output) -+* - keccak_state *s: pointer to input/output Keccak state -+**************************************************/ -+static void shake256_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state) -+{ -+ keccak_squeezeblocks(out, nblocks, state->s, SHAKE256_RATE); -+} -+ -+/************************************************* -+* Name: shake256_absorb -+* -+* Description: Absorb step of the SHAKE256 XOF; incremental. -+* -+* Arguments: - keccak_state *state: pointer to (initialized) output Keccak state -+* - const uint8_t *in: pointer to input to be absorbed into s -+* - size_t inlen: length of input in bytes -+**************************************************/ -+static void shake256_absorb(keccak_state *state, const uint8_t *in, size_t inlen) -+{ -+ state->pos = keccak_absorb(state->s, state->pos, SHAKE256_RATE, in, inlen); -+} -+ -+/************************************************* -+* Name: shake256_finalize -+* -+* Description: Finalize absorb step of the SHAKE256 XOF. -+* -+* Arguments: - keccak_state *state: pointer to Keccak state -+**************************************************/ -+static void shake256_finalize(keccak_state *state) -+{ -+ keccak_finalize(state->s, state->pos, SHAKE256_RATE, 0x1F); -+ state->pos = SHAKE256_RATE; -+} -+ -+/************************************************* -+* Name: keccak_init -+* -+* Description: Initializes the Keccak state. -+* -+* Arguments: - uint64_t *s: pointer to Keccak state -+**************************************************/ -+static void keccak_init(uint64_t s[25]) -+{ -+ unsigned int i; -+ for(i=0;i<25;i++) -+ s[i] = 0; -+} -+ -+/************************************************* -+* Name: shake256_init -+* -+* Description: Initilizes Keccak state for use as SHAKE256 XOF -+* -+* Arguments: - keccak_state *state: pointer to (uninitialized) Keccak state -+**************************************************/ -+static void shake256_init(keccak_state *state) -+{ -+ keccak_init(state->s); -+ state->pos = 0; -+} -+ -+ -+/************************************************* -+* Name: shake256 -+* -+* Description: SHAKE256 XOF with non-incremental API -+* -+* Arguments: - uint8_t *out: pointer to output -+* - size_t outlen: requested output length in bytes -+* - const uint8_t *in: pointer to input -+* - size_t inlen: length of input in bytes -+**************************************************/ -+static void shake256(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen) -+{ -+ size_t nblocks; -+ keccak_state state; -+ -+ shake256_absorb_once(&state, in, inlen); -+ nblocks = outlen/SHAKE256_RATE; -+ shake256_squeezeblocks(out, nblocks, &state); -+ outlen -= nblocks*SHAKE256_RATE; -+ out += nblocks*SHAKE256_RATE; -+ shake256_squeeze(out, outlen, &state); -+} -+ -+/************************************************* -+* Name: sha3_256 -+* -+* Description: SHA3-256 with non-incremental API -+* -+* Arguments: - uint8_t *h: pointer to output (32 bytes) -+* - const uint8_t *in: pointer to input -+* - size_t inlen: length of input in bytes -+**************************************************/ -+static void sha3_256(uint8_t h[32], const uint8_t *in, size_t inlen) -+{ -+ unsigned int i; -+ uint64_t s[25]; -+ -+ keccak_absorb_once(s, SHA3_256_RATE, in, inlen, 0x06); -+ KeccakF1600_StatePermute(s); -+ for(i=0;i<4;i++) -+ store64(h+8*i,s[i]); -+} -+ -+/************************************************* -+* Name: sha3_512 -+* -+* Description: SHA3-512 with non-incremental API -+* -+* Arguments: - uint8_t *h: pointer to output (64 bytes) -+* - const uint8_t *in: pointer to input -+* - size_t inlen: length of input in bytes -+**************************************************/ -+static void sha3_512(uint8_t h[64], const uint8_t *in, size_t inlen) -+{ -+ unsigned int i; -+ uint64_t s[25]; -+ -+ keccak_absorb_once(s, SHA3_512_RATE, in, inlen, 0x06); -+ KeccakF1600_StatePermute(s); -+ for(i=0;i<8;i++) -+ store64(h+8*i,s[i]); -+} -+ -+// -+// symmetric-shake.c -+// -+ -+/************************************************* -+* Name: kyber_shake128_absorb -+* -+* Description: Absorb step of the SHAKE128 specialized for the Kyber context. -+* -+* Arguments: - keccak_state *state: pointer to (uninitialized) output Keccak state -+* - const uint8_t *seed: pointer to KYBER_SYMBYTES input to be absorbed into state -+* - uint8_t i: additional byte of input -+* - uint8_t j: additional byte of input -+**************************************************/ -+static void kyber_shake128_absorb(keccak_state *state, -+ const uint8_t seed[KYBER_SYMBYTES], -+ uint8_t x, -+ uint8_t y) -+{ -+ uint8_t extseed[KYBER_SYMBYTES+2]; -+ -+ memcpy(extseed, seed, KYBER_SYMBYTES); -+ extseed[KYBER_SYMBYTES+0] = x; -+ extseed[KYBER_SYMBYTES+1] = y; -+ -+ shake128_absorb_once(state, extseed, sizeof(extseed)); - } - +-} +- -int KYBER_parse_public_key(struct KYBER_public_key *public_key, CBS *in) { - struct public_key *pub = public_key_from_external(public_key); - CBS orig_in = *in; - if (!kyber_parse_public_key_no_hash(pub, in) || // - CBS_len(in) != 0) { -+/************************************************* -+* Name: kyber_shake256_prf -+* -+* Description: Usage of SHAKE256 as a PRF, concatenates secret and public input -+* and then generates outlen bytes of SHAKE256 output -+* -+* Arguments: - uint8_t *out: pointer to output -+* - size_t outlen: number of requested output bytes -+* - const uint8_t *key: pointer to the key (of length KYBER_SYMBYTES) -+* - uint8_t nonce: single-byte nonce (public PRF input) -+**************************************************/ -+static void kyber_shake256_prf(uint8_t *out, size_t outlen, const uint8_t key[KYBER_SYMBYTES], uint8_t nonce) -+{ -+ uint8_t extkey[KYBER_SYMBYTES+1]; -+ -+ memcpy(extkey, key, KYBER_SYMBYTES); -+ extkey[KYBER_SYMBYTES] = nonce; -+ -+ shake256(out, outlen, extkey, sizeof(extkey)); -+} -+ -+// -+// kem.c -+// -+ -+// Modified crypto_kem_keypair to BoringSSL style API -+void generate_key(struct public_key *out_pub, struct private_key *out_priv, -+ const uint8_t seed[KYBER_GENERATE_KEY_BYTES]) -+{ -+ size_t i; -+ uint8_t* pk = &out_pub->opaque[0]; -+ uint8_t* sk = &out_priv->opaque[0]; -+ -+ indcpa_keypair(pk, sk, seed); -+ for(i=0;iopaque[0]; -+ uint8_t *ct = out_ciphertext; -+ -+ uint8_t buf[2*KYBER_SYMBYTES]; -+ /* Will contain key, coins */ -+ uint8_t kr[2*KYBER_SYMBYTES]; -+ -+ memcpy(buf, seed, KYBER_SYMBYTES); -+ -+ /* Don't release system RNG output */ -+ hash_h(buf, buf, KYBER_SYMBYTES); -+ -+ /* Multitarget countermeasure for coins + contributory KEM */ -+ hash_h(buf+KYBER_SYMBYTES, pk, KYBER_PUBLICKEYBYTES); -+ hash_g(kr, buf, 2*KYBER_SYMBYTES); -+ -+ /* coins are in kr+KYBER_SYMBYTES */ -+ if(!indcpa_enc(ct, buf, pk, kr+KYBER_SYMBYTES)) - return 0; -+ -+ if (mlkem == 1) { -+ memcpy(ss, kr, KYBER_SYMBYTES); -+ } else { -+ /* overwrite coins in kr with H(c) */ -+ hash_h(kr+KYBER_SYMBYTES, ct, KYBER_CIPHERTEXTBYTES); -+ /* hash concatenation of pre-k and H(c) to k */ -+ kdf(ss, kr, 2*KYBER_SYMBYTES); - } +- return 0; +- } - BORINGSSL_keccak(pub->public_key_hash, sizeof(pub->public_key_hash), - CBS_data(&orig_in), CBS_len(&orig_in), boringssl_sha3_256); - return 1; - } - +- return 1; +-} +- -int KYBER_marshal_private_key(CBB *out, - const struct KYBER_private_key *private_key) { - const struct private_key *const priv = private_key_from_external(private_key); - uint8_t *s_output; - if (!CBB_add_space(out, &s_output, kEncodedVectorSize)) { - return 0; -+// Modified crypto_kem_decap to BoringSSL style API -+void decap(uint8_t out_shared_key[KYBER_SSBYTES], -+ const struct private_key *in_priv, -+ const uint8_t *ct, size_t ciphertext_len, int mlkem) -+{ -+ 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;is, kLog2Prime); - if (!kyber_marshal_public_key(out, &priv->pub) || - !CBB_add_bytes(out, priv->pub.public_key_hash, @@ -3587,45 +3312,14 @@ index 776c085f9..ccb5b3d9b 100644 - !CBB_add_bytes(out, priv->fo_failure_secret, - sizeof(priv->fo_failure_secret))) { - return 0; -+ -+ if (mlkem == 1) { -+ /* Compute shared secret in case of rejection: ss2 = PRF(z || c). */ -+ uint8_t ss2[KYBER_SYMBYTES]; -+ keccak_state ks; -+ shake256_init(&ks); -+ shake256_absorb( -+ &ks, -+ sk + KYBER_SECRETKEYBYTES - KYBER_SYMBYTES, -+ KYBER_SYMBYTES -+ ); -+ shake256_absorb(&ks, ct, ciphertext_len); -+ shake256_finalize(&ks); -+ shake256_squeeze(ss2, KYBER_SYMBYTES, &ks); -+ -+ /* Set ss2 to the real shared secret if c = c' */ -+ cmov(ss2, kr, KYBER_SYMBYTES, 1-fail); -+ memcpy(ss, ss2, KYBER_SYMBYTES); -+ } else { -+ /* overwrite coins in kr with H(c) */ -+ hash_h(kr+KYBER_SYMBYTES, ct, ciphertext_len); -+ -+ /* Overwrite pre-k with z on re-encryption failure */ -+ cmov(kr, sk+KYBER_SECRETKEYBYTES-KYBER_SYMBYTES, KYBER_SYMBYTES, fail); -+ -+ /* hash concatenation of pre-k and H(c) to k */ -+ kdf(ss, kr, 2*KYBER_SYMBYTES); - } +- } - return 1; - } - +-} +- -int KYBER_parse_private_key(struct KYBER_private_key *out_private_key, - CBS *in) { - struct private_key *const priv = private_key_from_external(out_private_key); -+void marshal_public_key(uint8_t out[KYBER_PUBLICKEYBYTES], -+ const struct public_key *in_pub) { -+ memcpy(out, &in_pub->opaque, KYBER_PUBLICKEYBYTES); -+} - +- - CBS s_bytes; - if (!CBS_get_bytes(in, &s_bytes, kEncodedVectorSize) || - !vector_decode(&priv->s, CBS_data(&s_bytes), kLog2Prime) || @@ -3642,33 +3336,33 @@ index 776c085f9..ccb5b3d9b 100644 + 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 +diff --git a/crypto/kyber/kyber512.c b/crypto/kyber/kyber512.c new file mode 100644 index 000000000..21eed11a2 --- /dev/null -+++ b/src/crypto/kyber/kyber512.c ++++ b/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 +diff --git a/crypto/kyber/kyber768.c b/crypto/kyber/kyber768.c new file mode 100644 index 000000000..3e572b72e --- /dev/null -+++ b/src/crypto/kyber/kyber768.c ++++ b/crypto/kyber/kyber768.c @@ -0,0 +1,4 @@ +#define KYBER_K 3 + +#include "kyber.c" + -diff --git a/src/crypto/kyber/kyber_test.cc b/src/crypto/kyber/kyber_test.cc +diff --git a/crypto/kyber/kyber_test.cc b/crypto/kyber/kyber_test.cc deleted file mode 100644 -index eb76b5bd7..000000000 ---- a/src/crypto/kyber/kyber_test.cc +index b9daa87d3..000000000 +--- a/crypto/kyber/kyber_test.cc +++ /dev/null -@@ -1,229 +0,0 @@ +@@ -1,184 +0,0 @@ -/* Copyright (c) 2023, Google Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any @@ -3695,55 +3389,10 @@ index eb76b5bd7..000000000 - -#include "../test/file_test.h" -#include "../test/test_util.h" +-#include "../keccak/internal.h" -#include "./internal.h" - - --static void KeccakFileTest(FileTest *t) { -- std::vector input, sha3_256_expected, sha3_512_expected, -- shake128_expected, shake256_expected; -- ASSERT_TRUE(t->GetBytes(&input, "Input")); -- ASSERT_TRUE(t->GetBytes(&sha3_256_expected, "SHA3-256")); -- ASSERT_TRUE(t->GetBytes(&sha3_512_expected, "SHA3-512")); -- ASSERT_TRUE(t->GetBytes(&shake128_expected, "SHAKE-128")); -- ASSERT_TRUE(t->GetBytes(&shake256_expected, "SHAKE-256")); -- -- uint8_t sha3_256_digest[32]; -- BORINGSSL_keccak(sha3_256_digest, sizeof(sha3_256_digest), input.data(), -- input.size(), boringssl_sha3_256); -- uint8_t sha3_512_digest[64]; -- BORINGSSL_keccak(sha3_512_digest, sizeof(sha3_512_digest), input.data(), -- input.size(), boringssl_sha3_512); -- uint8_t shake128_output[512]; -- BORINGSSL_keccak(shake128_output, sizeof(shake128_output), input.data(), -- input.size(), boringssl_shake128); -- uint8_t shake256_output[512]; -- BORINGSSL_keccak(shake256_output, sizeof(shake256_output), input.data(), -- input.size(), boringssl_shake256); -- -- EXPECT_EQ(Bytes(sha3_256_expected), Bytes(sha3_256_digest)); -- EXPECT_EQ(Bytes(sha3_512_expected), Bytes(sha3_512_digest)); -- EXPECT_EQ(Bytes(shake128_expected), Bytes(shake128_output)); -- EXPECT_EQ(Bytes(shake256_expected), Bytes(shake256_output)); -- -- struct BORINGSSL_keccak_st ctx; -- -- BORINGSSL_keccak_init(&ctx, input.data(), input.size(), boringssl_shake128); -- for (size_t i = 0; i < sizeof(shake128_output); i++) { -- BORINGSSL_keccak_squeeze(&ctx, &shake128_output[i], 1); -- } -- EXPECT_EQ(Bytes(shake128_expected), Bytes(shake128_output)); -- -- BORINGSSL_keccak_init(&ctx, input.data(), input.size(), boringssl_shake256); -- for (size_t i = 0; i < sizeof(shake256_output); i++) { -- BORINGSSL_keccak_squeeze(&ctx, &shake256_output[i], 1); -- } -- EXPECT_EQ(Bytes(shake256_expected), Bytes(shake256_output)); --} -- --TEST(KyberTest, KeccakTestVectors) { -- FileTestGTest("crypto/kyber/keccak_tests.txt", KeccakFileTest); --} -- -template -static std::vector Marshal(int (*marshal_func)(CBB *, const T *), - const T *t) { @@ -3898,10 +3547,10 @@ index eb76b5bd7..000000000 -TEST(KyberTest, TestVectors) { - FileTestGTest("crypto/kyber/kyber_tests.txt", KyberFileTest); -} -diff --git a/src/crypto/obj/obj_dat.h b/src/crypto/obj/obj_dat.h -index 654b3c08e..6cef2c079 100644 ---- a/src/crypto/obj/obj_dat.h -+++ b/src/crypto/obj/obj_dat.h +diff --git a/crypto/obj/obj_dat.h b/crypto/obj/obj_dat.h +index 71ef2d2bd..74b99b098 100644 +--- a/crypto/obj/obj_dat.h ++++ b/crypto/obj/obj_dat.h @@ -57,7 +57,7 @@ /* This file is generated by crypto/obj/objects.go. */ @@ -3911,7 +3560,7 @@ index 654b3c08e..6cef2c079 100644 static const uint8_t kObjectData[] = { /* NID_rsadsi */ -@@ -8784,6 +8784,13 @@ static const ASN1_OBJECT kObjects[NUM_NID] = { +@@ -8783,6 +8783,13 @@ static const ASN1_OBJECT kObjects[NUM_NID] = { {"HKDF", "hkdf", NID_hkdf, 0, NULL, 0}, {"X25519Kyber768Draft00", "X25519Kyber768Draft00", NID_X25519Kyber768Draft00, 0, NULL, 0}, @@ -3925,7 +3574,7 @@ index 654b3c08e..6cef2c079 100644 }; static const uint16_t kNIDsInShortNameOrder[] = { -@@ -8916,6 +8923,7 @@ static const uint16_t kNIDsInShortNameOrder[] = { +@@ -8915,6 +8922,7 @@ static const uint16_t kNIDsInShortNameOrder[] = { 18 /* OU */, 749 /* Oakley-EC2N-3 */, 750 /* Oakley-EC2N-4 */, @@ -3933,9 +3582,9 @@ index 654b3c08e..6cef2c079 100644 9 /* PBE-MD2-DES */, 168 /* PBE-MD2-RC2-64 */, 10 /* PBE-MD5-DES */, -@@ -8982,7 +8990,10 @@ static const uint16_t kNIDsInShortNameOrder[] = { +@@ -8980,7 +8988,10 @@ static const uint16_t kNIDsInShortNameOrder[] = { + 143 /* SXNetID */, 458 /* UID */, - 0 /* UNDEF */, 948 /* X25519 */, + 965 /* X25519Kyber512Draft00 */, 964 /* X25519Kyber768Draft00 */, @@ -3944,7 +3593,7 @@ index 654b3c08e..6cef2c079 100644 961 /* X448 */, 11 /* X500 */, 378 /* X500algorithms */, -@@ -9829,6 +9840,7 @@ static const uint16_t kNIDsInLongNameOrder[] = { +@@ -9827,6 +9838,7 @@ static const uint16_t kNIDsInLongNameOrder[] = { 366 /* OCSP Nonce */, 371 /* OCSP Service Locator */, 180 /* OCSP Signing */, @@ -3952,7 +3601,7 @@ index 654b3c08e..6cef2c079 100644 161 /* PBES2 */, 69 /* PBKDF2 */, 162 /* PBMAC1 */, -@@ -9853,7 +9865,10 @@ static const uint16_t kNIDsInLongNameOrder[] = { +@@ -9851,7 +9863,10 @@ static const uint16_t kNIDsInLongNameOrder[] = { 133 /* Time Stamping */, 375 /* Trust Root */, 948 /* X25519 */, @@ -3963,10 +3612,10 @@ index 654b3c08e..6cef2c079 100644 961 /* X448 */, 12 /* X509 */, 402 /* X509v3 AC Targeting */, -diff --git a/src/crypto/obj/obj_mac.num b/src/crypto/obj/obj_mac.num +diff --git a/crypto/obj/obj_mac.num b/crypto/obj/obj_mac.num index a0519acee..2a46adfe8 100644 ---- a/src/crypto/obj/obj_mac.num -+++ b/src/crypto/obj/obj_mac.num +--- a/crypto/obj/obj_mac.num ++++ b/crypto/obj/obj_mac.num @@ -952,3 +952,7 @@ X448 961 sha512_256 962 hkdf 963 @@ -3975,10 +3624,10 @@ index a0519acee..2a46adfe8 100644 +P256Kyber768Draft00 966 +X25519Kyber768Draft00Old 967 +X25519MLKEM768 968 -diff --git a/src/crypto/obj/objects.txt b/src/crypto/obj/objects.txt +diff --git a/crypto/obj/objects.txt b/crypto/obj/objects.txt index 3ad32ea3d..347fc556a 100644 ---- a/src/crypto/obj/objects.txt -+++ b/src/crypto/obj/objects.txt +--- a/crypto/obj/objects.txt ++++ b/crypto/obj/objects.txt @@ -1332,8 +1332,12 @@ secg-scheme 14 3 : dhSinglePass-cofactorDH-sha512kdf-scheme : dh-std-kdf : dh-cofactor-kdf @@ -3993,10 +3642,10 @@ index 3ad32ea3d..347fc556a 100644 # See RFC 8410. 1 3 101 110 : X25519 -diff --git a/src/include/openssl/kyber.h b/src/include/openssl/kyber.h +diff --git a/include/openssl/kyber.h b/include/openssl/kyber.h index cafae9d17..a05eb8957 100644 ---- a/src/include/openssl/kyber.h -+++ b/src/include/openssl/kyber.h +--- a/include/openssl/kyber.h ++++ b/include/openssl/kyber.h @@ -1,17 +1,3 @@ -/* Copyright (c) 2023, Google Inc. - * @@ -4038,15 +3687,7 @@ index cafae9d17..a05eb8957 100644 - } opaque; +struct KYBER512_private_key { + uint8_t opaque[KYBER512_PRIVATE_KEY_BYTES]; - }; -- --// KYBER_private_key contains a Kyber768 private key. The contents of this --// object should never leave the address space since the format is unstable. --struct KYBER_private_key { -- union { -- uint8_t bytes[512 * (3 + 3 + 9) + 32 + 32 + 32]; -- uint16_t alignment; -- } opaque; ++}; +struct KYBER768_private_key { + uint8_t opaque[KYBER768_PRIVATE_KEY_BYTES]; +}; @@ -4057,17 +3698,34 @@ index cafae9d17..a05eb8957 100644 + uint8_t opaque[KYBER768_PUBLIC_KEY_BYTES]; }; +-// KYBER_private_key contains a Kyber768 private key. The contents of this +-// object should never leave the address space since the format is unstable. +-struct KYBER_private_key { +- union { +- uint8_t bytes[512 * (3 + 3 + 9) + 32 + 32 + 32]; +- uint16_t alignment; +- } opaque; +-}; ++// KYBER_GENERATE_KEY_BYTES is the number of bytes of entropy needed to ++// generate a keypair. ++#define KYBER_GENERATE_KEY_BYTES 64 + -// KYBER_PUBLIC_KEY_BYTES is the number of bytes in an encoded Kyber768 public -// key. -#define KYBER_PUBLIC_KEY_BYTES 1184 -- ++// KYBER_ENCAP_BYTES is the number of bytes of entropy needed to encapsulate a ++// session key. ++#define KYBER_ENCAP_BYTES 32 + -// KYBER_generate_key generates a random public/private key pair, writes the -// encoded public key to |out_encoded_public_key| and sets |out_private_key| to -// the private key. -OPENSSL_EXPORT void KYBER_generate_key( - uint8_t out_encoded_public_key[KYBER_PUBLIC_KEY_BYTES], - struct KYBER_private_key *out_private_key); -- ++// KYBER_KEY_BYTES is the number of bytes in a shared key. ++#define KYBER_KEY_BYTES 32 + -// KYBER_public_from_private sets |*out_public_key| to the public key that -// corresponds to |private_key|. (This is faster than parsing the output of -// |KYBER_generate_key| if, for some reason, you need to encapsulate to a key @@ -4075,10 +3733,20 @@ index cafae9d17..a05eb8957 100644 -OPENSSL_EXPORT void KYBER_public_from_private( - struct KYBER_public_key *out_public_key, - const struct KYBER_private_key *private_key); -- ++// 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]); + -// KYBER_CIPHERTEXT_BYTES is number of bytes in the Kyber768 ciphertext. -#define KYBER_CIPHERTEXT_BYTES 1088 -- ++// 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]); + -// KYBER_encap encrypts a random secret key of length |out_shared_secret_len| to -// |public_key|, writes the ciphertext to |ciphertext|, and writes the random -// key to |out_shared_secret|. The party calling |KYBER_decap| must already know @@ -4087,7 +3755,15 @@ index cafae9d17..a05eb8957 100644 - uint8_t *out_shared_secret, - size_t out_shared_secret_len, - const struct KYBER_public_key *public_key); -- ++// 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. If |mlkem| is 1, will use ML-KEM-512. ++OPENSSL_EXPORT int KYBER512_encap(uint8_t out_ciphertext[KYBER512_CIPHERTEXT_BYTES], ++ uint8_t out_shared_key[KYBER_KEY_BYTES], ++ const struct KYBER512_public_key *in_pub, ++ const uint8_t in[KYBER_ENCAP_BYTES], ++ int mlkem); + -// KYBER_decap decrypts a key of length |out_shared_secret_len| from -// |ciphertext| using |private_key| and writes it to |out_shared_secret|. If -// |ciphertext| is invalid, |out_shared_secret| is filled with a key that @@ -4100,23 +3776,57 @@ index cafae9d17..a05eb8957 100644 - uint8_t *out_shared_secret, size_t out_shared_secret_len, - const uint8_t ciphertext[KYBER_CIPHERTEXT_BYTES], - const struct KYBER_private_key *private_key); -- -- ++// 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. If |mlkem| is 1, will use ML-KEM-768. ++OPENSSL_EXPORT int KYBER768_encap(uint8_t out_ciphertext[KYBER768_CIPHERTEXT_BYTES], ++ uint8_t out_shared_key[KYBER_KEY_BYTES], ++ const struct KYBER768_public_key *in_pub, ++ const uint8_t in[KYBER_ENCAP_BYTES], ++ int mlkem); + ++// KYBER_decap decrypts a session key from |ciphertext_len| bytes of ++// |ciphertext|. If the ciphertext is valid, the decrypted key is written to ++// |out_shared_key|. Otherwise a key dervied from |ciphertext| and a secret key (kept ++// in |in_priv|) is written. If the ciphertext is the wrong length then it will ++// leak which was done via side-channels. Otherwise it should perform either ++// action in constant-time. If |mlkem| is 1, will use ML-KEM-512. ++OPENSSL_EXPORT void KYBER512_decap(uint8_t out_shared_key[KYBER_KEY_BYTES], ++ const struct KYBER512_private_key *in_priv, ++ const uint8_t *ciphertext, size_t ciphertext_len, ++ int mlkem); + -// Serialisation of keys. -- ++// 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. If |mlkem| is 1, will use ML-KEM-768. ++OPENSSL_EXPORT void KYBER768_decap(uint8_t out_shared_key[KYBER_KEY_BYTES], ++ const struct KYBER768_private_key *in_priv, ++ const uint8_t *ciphertext, size_t ciphertext_len, ++ int mlkem); + -// KYBER_marshal_public_key serializes |public_key| to |out| in the standard -// format for Kyber public keys. It returns one on success or zero on allocation -// error. -OPENSSL_EXPORT int KYBER_marshal_public_key( - CBB *out, const struct KYBER_public_key *public_key); -- ++// 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); + -// KYBER_parse_public_key parses a public key, in the format generated by -// |KYBER_marshal_public_key|, from |in| and writes the result to -// |out_public_key|. It returns one on success or zero on parse error or if -// there are trailing bytes in |in|. -OPENSSL_EXPORT int KYBER_parse_public_key( - struct KYBER_public_key *out_public_key, CBS *in); -- ++// 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); + -// KYBER_marshal_private_key serializes |private_key| to |out| in the standard -// format for Kyber private keys. It returns one on success or zero on -// allocation error. @@ -4133,92 +3843,20 @@ index cafae9d17..a05eb8957 100644 -// there are trailing bytes in |in|. -OPENSSL_EXPORT int KYBER_parse_private_key( - struct KYBER_private_key *out_private_key, CBS *in); -- -+// 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. If |mlkem| is 1, will use ML-KEM-512. -+OPENSSL_EXPORT int KYBER512_encap(uint8_t out_ciphertext[KYBER512_CIPHERTEXT_BYTES], -+ uint8_t out_shared_key[KYBER_KEY_BYTES], -+ const struct KYBER512_public_key *in_pub, -+ const uint8_t in[KYBER_ENCAP_BYTES], -+ int mlkem); -+ -+// KYBER768_encap is a deterministic function the generates and encrypts a random -+// session key from the given entropy, writing those values to |out_shared_key| -+// and |out_ciphertext|, respectively. If |mlkem| is 1, will use ML-KEM-768. -+OPENSSL_EXPORT int KYBER768_encap(uint8_t out_ciphertext[KYBER768_CIPHERTEXT_BYTES], -+ uint8_t out_shared_key[KYBER_KEY_BYTES], -+ const struct KYBER768_public_key *in_pub, -+ const uint8_t in[KYBER_ENCAP_BYTES], -+ int mlkem); -+ -+// KYBER_decap decrypts a session key from |ciphertext_len| bytes of -+// |ciphertext|. If the ciphertext is valid, the decrypted key is written to -+// |out_shared_key|. Otherwise a key dervied from |ciphertext| and a secret key (kept -+// in |in_priv|) is written. If the ciphertext is the wrong length then it will -+// leak which was done via side-channels. Otherwise it should perform either -+// action in constant-time. If |mlkem| is 1, will use ML-KEM-512. -+OPENSSL_EXPORT void KYBER512_decap(uint8_t out_shared_key[KYBER_KEY_BYTES], -+ const struct KYBER512_private_key *in_priv, -+ const uint8_t *ciphertext, size_t ciphertext_len, -+ int mlkem); -+ -+// KYBER_decap decrypts a session key from |ciphertext_len| bytes of -+// |ciphertext|. If the ciphertext is valid, the decrypted key is written to -+// |out_shared_key|. Otherwise a key dervied from |ciphertext| and a secret key (kept -+// in |in_priv|) is written. If the ciphertext is the wrong length then it will -+// leak which was done via side-channels. Otherwise it should perform either -+// action in constant-time. If |mlkem| is 1, will use ML-KEM-768. -+OPENSSL_EXPORT void KYBER768_decap(uint8_t out_shared_key[KYBER_KEY_BYTES], -+ const struct KYBER768_private_key *in_priv, -+ const uint8_t *ciphertext, size_t ciphertext_len, -+ int mlkem); -+ -+// 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 -diff --git a/src/include/openssl/nid.h b/src/include/openssl/nid.h +diff --git a/include/openssl/nid.h b/include/openssl/nid.h index 4dd8841b1..5b102c610 100644 ---- a/src/include/openssl/nid.h -+++ b/src/include/openssl/nid.h +--- a/include/openssl/nid.h ++++ b/include/openssl/nid.h @@ -4255,6 +4255,18 @@ extern "C" { #define SN_X25519Kyber768Draft00 "X25519Kyber768Draft00" #define NID_X25519Kyber768Draft00 964 @@ -4238,53 +3876,60 @@ index 4dd8841b1..5b102c610 100644 #if defined(__cplusplus) } /* extern C */ -diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h -index 53aa9b453..f9683f4cf 100644 ---- a/src/include/openssl/ssl.h -+++ b/src/include/openssl/ssl.h -@@ -2378,6 +2378,10 @@ OPENSSL_EXPORT int SSL_set1_curves_list(SSL *ssl, const char *curves); - #define SSL_CURVE_SECP521R1 25 - #define SSL_CURVE_X25519 29 - #define SSL_CURVE_X25519_KYBER768_DRAFT00 0x6399 -+#define SSL_CURVE_X25519_KYBER512_DRAFT00 0xfe30 -+#define SSL_CURVE_X25519_KYBER768_DRAFT00_OLD 0xfe31 -+#define SSL_CURVE_P256_KYBER768_DRAFT00 0xfe32 -+#define SSL_CURVE_X25519_MLKEM768 0x11ec +diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h +index 003e0a5f7..884685ba9 100644 +--- a/include/openssl/ssl.h ++++ b/include/openssl/ssl.h +@@ -2363,6 +2363,10 @@ OPENSSL_EXPORT size_t SSL_CTX_get_num_tickets(const SSL_CTX *ctx); + #define SSL_GROUP_SECP521R1 25 + #define SSL_GROUP_X25519 29 + #define SSL_GROUP_X25519_KYBER768_DRAFT00 0x6399 ++#define SSL_GROUP_X25519_KYBER512_DRAFT00 0xfe30 ++#define SSL_GROUP_X25519_KYBER768_DRAFT00_OLD 0xfe31 ++#define SSL_GROUP_P256_KYBER768_DRAFT00 0xfe32 ++#define SSL_GROUP_X25519_MLKEM768 0x11ec - // SSL_get_curve_id returns the ID of the curve used by |ssl|'s most recently - // completed handshake or 0 if not applicable. -diff --git a/src/sources.cmake b/src/sources.cmake -index 5c7e881bf..3c0770cf3 100644 ---- a/src/sources.cmake -+++ b/src/sources.cmake -@@ -66,8 +66,6 @@ set( - crypto/fipsmodule/rand/ctrdrbg_vectors.txt + // SSL_CTX_set1_group_ids sets the preferred groups for |ctx| to |group_ids|. + // Each element of |group_ids| should be one of the |SSL_GROUP_*| constants. It +diff --git a/sources.cmake b/sources.cmake +index ba2f5bc9e..d7ef5153a 100644 +--- a/sources.cmake ++++ b/sources.cmake +@@ -52,7 +52,6 @@ set( + crypto/hrss/hrss_test.cc + crypto/impl_dispatch_test.cc + crypto/keccak/keccak_test.cc +- crypto/kyber/kyber_test.cc + crypto/lhash/lhash_test.cc + crypto/obj/obj_test.cc + crypto/pem/pem_test.cc +@@ -145,7 +144,6 @@ set( crypto/hmac_extra/hmac_tests.txt crypto/hpke/hpke_test_vectors.txt -- crypto/kyber/keccak_tests.txt + crypto/keccak/keccak_tests.txt - crypto/kyber/kyber_tests.txt crypto/pkcs8/test/empty_password.p12 crypto/pkcs8/test/no_encryption.p12 crypto/pkcs8/test/nss.p12 -diff --git a/src/ssl/extensions.cc b/src/ssl/extensions.cc -index 5ee280221..aae3e6a7f 100644 ---- a/src/ssl/extensions.cc -+++ b/src/ssl/extensions.cc +diff --git a/ssl/extensions.cc b/ssl/extensions.cc +index b13400097..4655b1881 100644 +--- a/ssl/extensions.cc ++++ b/ssl/extensions.cc @@ -207,6 +207,10 @@ static bool tls1_check_duplicate_extensions(const CBS *cbs) { static bool is_post_quantum_group(uint16_t id) { switch (id) { - case SSL_CURVE_X25519_KYBER768_DRAFT00: -+ case SSL_CURVE_X25519_KYBER768_DRAFT00_OLD: -+ case SSL_CURVE_X25519_KYBER512_DRAFT00: -+ case SSL_CURVE_P256_KYBER768_DRAFT00: -+ case SSL_CURVE_X25519_MLKEM768: + case SSL_GROUP_X25519_KYBER768_DRAFT00: ++ case SSL_GROUP_X25519_KYBER768_DRAFT00_OLD: ++ case SSL_GROUP_X25519_KYBER512_DRAFT00: ++ case SSL_GROUP_P256_KYBER768_DRAFT00: ++ case SSL_GROUP_X25519_MLKEM768: return true; default: return false; -diff --git a/src/ssl/ssl_key_share.cc b/src/ssl/ssl_key_share.cc -index 09a9ad380..d7a8f0a80 100644 ---- a/src/ssl/ssl_key_share.cc -+++ b/src/ssl/ssl_key_share.cc +diff --git a/ssl/ssl_key_share.cc b/ssl/ssl_key_share.cc +index 694bec11d..3e4d2e7c4 100644 +--- a/ssl/ssl_key_share.cc ++++ b/ssl/ssl_key_share.cc @@ -26,6 +26,7 @@ #include #include @@ -4293,7 +3938,7 @@ index 09a9ad380..d7a8f0a80 100644 #include #include #include -@@ -193,63 +194,292 @@ class X25519KeyShare : public SSLKeyShare { +@@ -191,63 +192,145 @@ class X25519KeyShare : public SSLKeyShare { uint8_t private_key_[32]; }; @@ -4302,18 +3947,27 @@ index 09a9ad380..d7a8f0a80 100644 public: - X25519Kyber768KeyShare() {} + P256Kyber768Draft00KeyShare() {} -+ -+ uint16_t GroupID() const override { return SSL_CURVE_P256_KYBER768_DRAFT00; } -+ -+ bool Generate(CBB *out) override { + +- uint16_t GroupID() const override { +- return SSL_GROUP_X25519_KYBER768_DRAFT00; +- } ++ uint16_t GroupID() const override { return SSL_GROUP_P256_KYBER768_DRAFT00; } + + bool Generate(CBB *out) override { +- uint8_t x25519_public_key[32]; +- X25519_keypair(x25519_public_key, x25519_private_key_); + assert(!p256_private_key_); -+ + +- uint8_t kyber_public_key[KYBER_PUBLIC_KEY_BYTES]; +- KYBER_generate_key(kyber_public_key, &kyber_private_key_); + // Set up a shared |BN_CTX| for P-256 operations. + UniquePtr bn_ctx(BN_CTX_new()); + if (!bn_ctx) { + return false; + } -+ + +- if (!CBB_add_bytes(out, x25519_public_key, sizeof(x25519_public_key)) || +- !CBB_add_bytes(out, kyber_public_key, sizeof(kyber_public_key))) { + BN_CTXScope scope(bn_ctx.get()); + + // Generate a P-256 private key. @@ -4345,33 +3999,58 @@ index 09a9ad380..d7a8f0a80 100644 + + uint8_t kyber_public_key_bytes[KYBER768_PUBLIC_KEY_BYTES]; + KYBER768_marshal_public_key(kyber_public_key_bytes, &kyber_public_key); - -- uint16_t GroupID() const override { -- return SSL_CURVE_X25519_KYBER768_DRAFT00; ++ + if (!CBB_add_bytes(out, kyber_public_key_bytes, + sizeof(kyber_public_key_bytes))) { -+ return false; -+ } -+ -+ return true; + return false; + } + + return true; } +- bool Encap(CBB *out_ciphertext, Array *out_secret, +- uint8_t *out_alert, Span peer_key) override { +- Array secret; +- if (!secret.Init(32 + 32)) { +- return false; +- } + bool Encap(CBB *out_public_key, Array *out_secret, + uint8_t *out_alert, Span peer_key) override { + assert(!p256_private_key_); -+ + +- uint8_t x25519_public_key[32]; +- X25519_keypair(x25519_public_key, x25519_private_key_); +- KYBER_public_key peer_kyber_pub; +- CBS peer_key_cbs; +- CBS peer_x25519_cbs; +- CBS peer_kyber_cbs; +- CBS_init(&peer_key_cbs, peer_key.data(), peer_key.size()); +- if (!CBS_get_bytes(&peer_key_cbs, &peer_x25519_cbs, 32) || +- !CBS_get_bytes(&peer_key_cbs, &peer_kyber_cbs, +- KYBER_PUBLIC_KEY_BYTES) || +- CBS_len(&peer_key_cbs) != 0 || +- !X25519(secret.data(), x25519_private_key_, +- CBS_data(&peer_x25519_cbs)) || +- !KYBER_parse_public_key(&peer_kyber_pub, &peer_kyber_cbs)) { + 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; -+ } -+ + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); + return false; + } + +- uint8_t kyber_ciphertext[KYBER_CIPHERTEXT_BYTES]; +- KYBER_encap(kyber_ciphertext, secret.data() + 32, secret.size() - 32, +- &peer_kyber_pub); + // Set up a shared |BN_CTX| for P-256 operations. + UniquePtr bn_ctx(BN_CTX_new()); + if (!bn_ctx) { + return false; + } -+ + +- if (!CBB_add_bytes(out_ciphertext, x25519_public_key, +- sizeof(x25519_public_key)) || +- !CBB_add_bytes(out_ciphertext, kyber_ciphertext, +- sizeof(kyber_ciphertext))) { + BN_CTXScope scope(bn_ctx.get()); + + UniquePtr group; @@ -4440,30 +4119,35 @@ index 09a9ad380..d7a8f0a80 100644 + return false; + } + if(!CBB_add_bytes(out_public_key, ciphertext, sizeof(ciphertext))) { -+ return false; -+ } -+ -+ *out_secret = std::move(secret); -+ return true; -+ } -+ -+ bool Decap(Array *out_secret, uint8_t *out_alert, + return false; + } + +@@ -256,30 +339,380 @@ class X25519Kyber768KeyShare : public SSLKeyShare { + } + + bool Decap(Array *out_secret, uint8_t *out_alert, +- Span ciphertext) override { + Span peer_key) override { + assert(p256_private_key_); -+ *out_alert = SSL_AD_INTERNAL_ERROR; -+ -+ Array secret; + *out_alert = SSL_AD_INTERNAL_ERROR; + + Array secret; +- if (!secret.Init(32 + 32)) { + if (!secret.Init(32 + KYBER_KEY_BYTES)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); -+ return false; -+ } -+ + return false; + } + +- if (ciphertext.size() != 32 + KYBER_CIPHERTEXT_BYTES || +- !X25519(secret.data(), x25519_private_key_, ciphertext.data())) { + if (peer_key.size() != 65 + KYBER768_CIPHERTEXT_BYTES) { -+ *out_alert = SSL_AD_DECODE_ERROR; -+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); -+ return false; -+ } -+ + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); + return false; + } + +- KYBER_decap(secret.data() + 32, secret.size() - 32, ciphertext.data() + 32, +- &kyber_private_key_); + // Set up a shared |BN_CTX| for P-256 operations. + UniquePtr bn_ctx(BN_CTX_new()); + if (!bn_ctx) { @@ -4523,18 +4207,16 @@ index 09a9ad380..d7a8f0a80 100644 +class X25519Kyber768Draft00KeyShare : public SSLKeyShare { + public: + X25519Kyber768Draft00KeyShare(uint16_t group_id) : group_id_(group_id) { -+ assert(group_id == SSL_CURVE_X25519_KYBER768_DRAFT00 -+ || group_id == SSL_CURVE_X25519_KYBER768_DRAFT00_OLD); ++ assert(group_id == SSL_GROUP_X25519_KYBER768_DRAFT00 ++ || group_id == SSL_GROUP_X25519_KYBER768_DRAFT00_OLD); + } + + uint16_t GroupID() const override { return group_id_; } + - bool Generate(CBB *out) override { - uint8_t x25519_public_key[32]; - X25519_keypair(x25519_public_key, x25519_private_key_); - -- uint8_t kyber_public_key[KYBER_PUBLIC_KEY_BYTES]; -- KYBER_generate_key(kyber_public_key, &kyber_private_key_); ++ bool Generate(CBB *out) override { ++ uint8_t x25519_public_key[32]; ++ X25519_keypair(x25519_public_key, x25519_private_key_); ++ + uint8_t kyber_entropy[KYBER_GENERATE_KEY_BYTES]; + KYBER768_public_key kyber_public_key; + RAND_bytes(kyber_entropy, sizeof(kyber_entropy)); @@ -4542,42 +4224,26 @@ index 09a9ad380..d7a8f0a80 100644 + + 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, sizeof(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 Encap(CBB *out_ciphertext, Array *out_secret, -- uint8_t *out_alert, Span peer_key) override { ++ return false; ++ } ++ ++ return true; ++ } ++ + bool Encap(CBB *out_public_key, Array *out_secret, + uint8_t *out_alert, Span peer_key) override { - Array secret; -- if (!secret.Init(32 + 32)) { ++ Array secret; + if (!secret.Init(32 + KYBER_KEY_BYTES)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); - return false; - } - - uint8_t x25519_public_key[32]; - X25519_keypair(x25519_public_key, x25519_private_key_); -- KYBER_public_key peer_kyber_pub; -- CBS peer_key_cbs; -- CBS peer_x25519_cbs; -- CBS peer_kyber_cbs; -- CBS_init(&peer_key_cbs, peer_key.data(), peer_key.size()); -- if (!CBS_get_bytes(&peer_key_cbs, &peer_x25519_cbs, 32) || -- !CBS_get_bytes(&peer_key_cbs, &peer_kyber_cbs, -- KYBER_PUBLIC_KEY_BYTES) || -- CBS_len(&peer_key_cbs) != 0 || -- !X25519(secret.data(), x25519_private_key_, -- CBS_data(&peer_x25519_cbs)) || -- !KYBER_parse_public_key(&peer_kyber_pub, &peer_kyber_cbs)) { ++ return false; ++ } ++ ++ uint8_t x25519_public_key[32]; ++ X25519_keypair(x25519_public_key, x25519_private_key_); + + KYBER768_public_key peer_public_key; + if (peer_key.size() != 32 + KYBER768_PUBLIC_KEY_BYTES) { @@ -4589,36 +4255,30 @@ index 09a9ad380..d7a8f0a80 100644 + KYBER768_parse_public_key(&peer_public_key, peer_key.data() + 32); + + if (!X25519(secret.data(), x25519_private_key_, peer_key.data())) { - *out_alert = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); - return false; - } - -- uint8_t kyber_ciphertext[KYBER_CIPHERTEXT_BYTES]; -- KYBER_encap(kyber_ciphertext, secret.data() + 32, secret.size() - 32, -- &peer_kyber_pub); ++ *out_alert = SSL_AD_DECODE_ERROR; ++ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); ++ return false; ++ } ++ + uint8_t ciphertext[KYBER768_CIPHERTEXT_BYTES]; + uint8_t entropy[KYBER_ENCAP_BYTES]; + RAND_bytes(entropy, sizeof(entropy)); - -- if (!CBB_add_bytes(out_ciphertext, x25519_public_key, ++ + if(!KYBER768_encap(ciphertext, secret.data() + 32, &peer_public_key, entropy, 0)) { + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return false; + } + if(!CBB_add_bytes(out_public_key, x25519_public_key, - sizeof(x25519_public_key)) || -- !CBB_add_bytes(out_ciphertext, kyber_ciphertext, -- sizeof(kyber_ciphertext))) { ++ sizeof(x25519_public_key)) || + !CBB_add_bytes(out_public_key, ciphertext, sizeof(ciphertext))) { - return false; - } - -@@ -258,30 +488,233 @@ class X25519Kyber768KeyShare : public SSLKeyShare { - } - - bool Decap(Array *out_secret, uint8_t *out_alert, -- Span ciphertext) override { ++ return false; ++ } ++ ++ *out_secret = std::move(secret); ++ return true; ++ } ++ ++ bool Decap(Array *out_secret, uint8_t *out_alert, + Span peer_key) override { + *out_alert = SSL_AD_INTERNAL_ERROR; + @@ -4638,12 +4298,13 @@ index 09a9ad380..d7a8f0a80 100644 + KYBER768_decap(secret.data() + 32, &kyber_private_key_, + peer_key.data() + 32, peer_key.size() - 32, 0); + -+ *out_secret = std::move(secret); -+ return true; -+ } -+ -+ private: -+ uint8_t x25519_private_key_[32]; + *out_secret = std::move(secret); + return true; + } + + private: + uint8_t x25519_private_key_[32]; +- KYBER_private_key kyber_private_key_; + KYBER768_private_key kyber_private_key_; + uint16_t group_id_; +}; @@ -4652,7 +4313,7 @@ index 09a9ad380..d7a8f0a80 100644 + public: + X25519MLKEM768KeyShare() {} + -+ uint16_t GroupID() const override { return SSL_CURVE_X25519_MLKEM768; } ++ uint16_t GroupID() const override { return SSL_GROUP_X25519_MLKEM768; } + + bool Generate(CBB *out) override { + uint8_t x25519_public_key[32]; @@ -4720,27 +4381,22 @@ index 09a9ad380..d7a8f0a80 100644 + + bool Decap(Array *out_secret, uint8_t *out_alert, + Span peer_key) override { - *out_alert = SSL_AD_INTERNAL_ERROR; - - Array secret; -- if (!secret.Init(32 + 32)) { ++ *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 (ciphertext.size() != 32 + KYBER_CIPHERTEXT_BYTES || -- !X25519(secret.data(), x25519_private_key_, ciphertext.data())) { ++ return false; ++ } ++ + if (peer_key.size() != KYBER768_CIPHERTEXT_BYTES + 32 || + !X25519(secret.data() + 32, x25519_private_key_, + peer_key.data() + KYBER768_CIPHERTEXT_BYTES )) { - *out_alert = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); - return false; - } - -- KYBER_decap(secret.data() + 32, secret.size() - 32, ciphertext.data() + 32, -- &kyber_private_key_); ++ *out_alert = SSL_AD_DECODE_ERROR; ++ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); ++ return false; ++ } ++ + KYBER768_decap(secret.data(), &kyber_private_key_, + peer_key.data(), peer_key.size() - 32, 1); + @@ -4757,7 +4413,7 @@ index 09a9ad380..d7a8f0a80 100644 + public: + X25519Kyber512Draft00KeyShare() {} + -+ uint16_t GroupID() const override { return SSL_CURVE_X25519_KYBER512_DRAFT00; } ++ uint16_t GroupID() const override { return SSL_GROUP_X25519_KYBER512_DRAFT00; } + + bool Generate(CBB *out) override { + uint8_t x25519_public_key[32]; @@ -4844,113 +4500,112 @@ index 09a9ad380..d7a8f0a80 100644 + KYBER512_decap(secret.data() + 32, &kyber_private_key_, + peer_key.data() + 32, peer_key.size() - 32, 0); + - *out_secret = std::move(secret); - return true; - } - - private: - uint8_t x25519_private_key_[32]; -- KYBER_private_key kyber_private_key_; ++ *out_secret = std::move(secret); ++ return true; ++ } ++ ++ private: ++ uint8_t x25519_private_key_[32]; + KYBER512_private_key kyber_private_key_; }; constexpr NamedGroup kNamedGroups[] = { -@@ -290,8 +723,16 @@ constexpr NamedGroup kNamedGroups[] = { - {NID_secp384r1, SSL_CURVE_SECP384R1, "P-384", "secp384r1"}, - {NID_secp521r1, SSL_CURVE_SECP521R1, "P-521", "secp521r1"}, - {NID_X25519, SSL_CURVE_X25519, "X25519", "x25519"}, -+ {NID_X25519Kyber512Draft00, SSL_CURVE_X25519_KYBER512_DRAFT00, +@@ -288,8 +721,16 @@ constexpr NamedGroup kNamedGroups[] = { + {NID_secp384r1, SSL_GROUP_SECP384R1, "P-384", "secp384r1"}, + {NID_secp521r1, SSL_GROUP_SECP521R1, "P-521", "secp521r1"}, + {NID_X25519, SSL_GROUP_X25519, "X25519", "x25519"}, ++ {NID_X25519Kyber512Draft00, SSL_GROUP_X25519_KYBER512_DRAFT00, + "X25519Kyber512Draft00", "Xyber512D00"}, - {NID_X25519Kyber768Draft00, SSL_CURVE_X25519_KYBER768_DRAFT00, + {NID_X25519Kyber768Draft00, SSL_GROUP_X25519_KYBER768_DRAFT00, - "X25519Kyber768Draft00", ""}, + "X25519Kyber768Draft00", "Xyber768D00"}, -+ {NID_X25519Kyber768Draft00Old, SSL_CURVE_X25519_KYBER768_DRAFT00_OLD, ++ {NID_X25519Kyber768Draft00Old, SSL_GROUP_X25519_KYBER768_DRAFT00_OLD, + "X25519Kyber768Draft00Old", "Xyber768D00Old"}, -+ {NID_P256Kyber768Draft00, SSL_CURVE_P256_KYBER768_DRAFT00, ++ {NID_P256Kyber768Draft00, SSL_GROUP_P256_KYBER768_DRAFT00, + "P256Kyber768Draft00", "P256Kyber768D00"}, -+ {NID_X25519MLKEM768, SSL_CURVE_X25519_MLKEM768, ++ {NID_X25519MLKEM768, SSL_GROUP_X25519_MLKEM768, + "X25519MLKEM768", "X25519MLKEM768"} }; } // namespace -@@ -312,8 +753,18 @@ UniquePtr SSLKeyShare::Create(uint16_t group_id) { - return MakeUnique(NID_secp521r1, SSL_CURVE_SECP521R1); - case SSL_CURVE_X25519: +@@ -310,8 +751,18 @@ UniquePtr SSLKeyShare::Create(uint16_t group_id) { + return MakeUnique(EC_group_p521(), SSL_GROUP_SECP521R1); + case SSL_GROUP_X25519: return MakeUnique(); -+ case SSL_CURVE_X25519_KYBER512_DRAFT00: ++ case SSL_GROUP_X25519_KYBER512_DRAFT00: + return UniquePtr(New()); - case SSL_CURVE_X25519_KYBER768_DRAFT00: + case SSL_GROUP_X25519_KYBER768_DRAFT00: - return MakeUnique(); + return UniquePtr(New( + group_id)); -+ case SSL_CURVE_X25519_KYBER768_DRAFT00_OLD: ++ case SSL_GROUP_X25519_KYBER768_DRAFT00_OLD: + return UniquePtr(New( + group_id)); -+ case SSL_CURVE_P256_KYBER768_DRAFT00: ++ case SSL_GROUP_P256_KYBER768_DRAFT00: + return UniquePtr(New()); -+ case SSL_CURVE_X25519_MLKEM768: ++ case SSL_GROUP_X25519_MLKEM768: + return UniquePtr(New()); default: return nullptr; } -diff --git a/src/ssl/ssl_lib.cc b/src/ssl/ssl_lib.cc -index 838761af5..9eb201d37 100644 ---- a/src/ssl/ssl_lib.cc -+++ b/src/ssl/ssl_lib.cc -@@ -3151,7 +3151,7 @@ namespace fips202205 { +diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc +index 58b68e675..38c8e906c 100644 +--- a/ssl/ssl_lib.cc ++++ b/ssl/ssl_lib.cc +@@ -3260,7 +3260,7 @@ namespace fips202205 { // Section 3.3.1 // "The server shall be configured to only use cipher suites that are // composed entirely of NIST approved algorithms" --static const int kCurves[] = {NID_X9_62_prime256v1, NID_secp384r1}; -+static const int kCurves[] = {NID_P256Kyber768Draft00, NID_X9_62_prime256v1, NID_secp384r1}; +-static const uint16_t kGroups[] = {SSL_GROUP_SECP256R1, SSL_GROUP_SECP384R1}; ++static const uint16_t kGroups[] = {SSL_GROUP_P256_KYBER768_DRAFT00, SSL_GROUP_SECP256R1, SSL_GROUP_SECP384R1}; static const uint16_t kSigAlgs[] = { SSL_SIGN_RSA_PKCS1_SHA256, -diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc -index ef43a9e98..22178b5f6 100644 ---- a/src/ssl/ssl_test.cc -+++ b/src/ssl/ssl_test.cc -@@ -409,7 +409,34 @@ static const CurveTest kCurveTests[] = { +diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc +index a8f4f215b..e0ebb505e 100644 +--- a/ssl/ssl_test.cc ++++ b/ssl/ssl_test.cc +@@ -484,7 +484,34 @@ static const CurveTest kCurveTests[] = { "P-256:X25519Kyber768Draft00", - { SSL_CURVE_SECP256R1, SSL_CURVE_X25519_KYBER768_DRAFT00 }, + { SSL_GROUP_SECP256R1, SSL_GROUP_X25519_KYBER768_DRAFT00 }, }, - + { + "Xyber512D00", -+ { SSL_CURVE_X25519_KYBER512_DRAFT00 }, ++ { SSL_GROUP_X25519_KYBER512_DRAFT00 }, + }, + { + "Xyber768D00", -+ { SSL_CURVE_X25519_KYBER768_DRAFT00 }, ++ { SSL_GROUP_X25519_KYBER768_DRAFT00 }, + }, + { + "Xyber768D00:Xyber768D00Old", -+ { SSL_CURVE_X25519_KYBER768_DRAFT00, SSL_CURVE_X25519_KYBER768_DRAFT00_OLD }, ++ { SSL_GROUP_X25519_KYBER768_DRAFT00, SSL_GROUP_X25519_KYBER768_DRAFT00_OLD }, + }, + { + "P-256:Xyber512D00", -+ { SSL_CURVE_SECP256R1, SSL_CURVE_X25519_KYBER512_DRAFT00 }, ++ { SSL_GROUP_SECP256R1, SSL_GROUP_X25519_KYBER512_DRAFT00 }, + }, + { + "P256Kyber768D00", -+ { SSL_CURVE_P256_KYBER768_DRAFT00 }, ++ { SSL_GROUP_P256_KYBER768_DRAFT00 }, + }, + { + "X25519MLKEM768", -+ { SSL_CURVE_X25519_MLKEM768 }, ++ { SSL_GROUP_X25519_MLKEM768 }, + }, + { + "P-256:P256Kyber768D00", -+ { SSL_CURVE_SECP256R1, SSL_CURVE_P256_KYBER768_DRAFT00 }, ++ { SSL_GROUP_SECP256R1, SSL_GROUP_P256_KYBER768_DRAFT00 }, + }, { "P-256:P-384:P-521:X25519", { -diff --git a/src/tool/speed.cc b/src/tool/speed.cc -index 5b0205953..6b3c67dab 100644 ---- a/src/tool/speed.cc -+++ b/src/tool/speed.cc -@@ -904,6 +904,116 @@ static bool SpeedScrypt(const std::string &selected) { +diff --git a/tool/speed.cc b/tool/speed.cc +index 942dcade1..f31e9e244 100644 +--- a/tool/speed.cc ++++ b/tool/speed.cc +@@ -1018,6 +1018,116 @@ static bool SpeedScrypt(const std::string &selected) { return true; } @@ -5067,7 +4722,7 @@ index 5b0205953..6b3c67dab 100644 static bool SpeedHRSS(const std::string &selected) { if (!selected.empty() && selected != "HRSS") { return true; -@@ -958,55 +1068,6 @@ static bool SpeedHRSS(const std::string &selected) { +@@ -1079,55 +1189,6 @@ static bool SpeedHRSS(const std::string &selected) { return true; } @@ -5078,39 +4733,39 @@ index 5b0205953..6b3c67dab 100644 - - TimeResults results; - -- KYBER_private_key priv; -- uint8_t encoded_public_key[KYBER_PUBLIC_KEY_BYTES]; - uint8_t ciphertext[KYBER_CIPHERTEXT_BYTES]; - // This ciphertext is nonsense, but Kyber decap is constant-time so, for the - // purposes of timing, it's fine. - memset(ciphertext, 42, sizeof(ciphertext)); -- if (!TimeFunction(&results, -- [&priv, &encoded_public_key, &ciphertext]() -> bool { -- uint8_t shared_secret[32]; -- KYBER_generate_key(encoded_public_key, &priv); -- KYBER_decap(shared_secret, sizeof(shared_secret), -- ciphertext, &priv); -- return true; -- })) { +- if (!TimeFunctionParallel(&results, [&]() -> bool { +- KYBER_private_key priv; +- uint8_t encoded_public_key[KYBER_PUBLIC_KEY_BYTES]; +- KYBER_generate_key(encoded_public_key, &priv); +- uint8_t shared_secret[32]; +- KYBER_decap(shared_secret, sizeof(shared_secret), ciphertext, &priv); +- return true; +- })) { - fprintf(stderr, "Failed to time KYBER_generate_key + KYBER_decap.\n"); - return false; - } - - results.Print("Kyber generate + decap"); - +- KYBER_private_key priv; +- uint8_t encoded_public_key[KYBER_PUBLIC_KEY_BYTES]; +- KYBER_generate_key(encoded_public_key, &priv); - KYBER_public_key pub; -- if (!TimeFunction( -- &results, [&pub, &ciphertext, &encoded_public_key]() -> bool { -- CBS encoded_public_key_cbs; -- CBS_init(&encoded_public_key_cbs, encoded_public_key, -- sizeof(encoded_public_key)); -- if (!KYBER_parse_public_key(&pub, &encoded_public_key_cbs)) { -- return false; -- } -- uint8_t shared_secret[32]; -- KYBER_encap(ciphertext, shared_secret, sizeof(shared_secret), &pub); -- return true; -- })) { +- if (!TimeFunctionParallel(&results, [&]() -> bool { +- CBS encoded_public_key_cbs; +- CBS_init(&encoded_public_key_cbs, encoded_public_key, +- sizeof(encoded_public_key)); +- if (!KYBER_parse_public_key(&pub, &encoded_public_key_cbs)) { +- return false; +- } +- uint8_t shared_secret[32]; +- KYBER_encap(ciphertext, shared_secret, sizeof(shared_secret), &pub); +- return true; +- })) { - fprintf(stderr, "Failed to time KYBER_encap.\n"); - return false; - } @@ -5120,19 +4775,16 @@ index 5b0205953..6b3c67dab 100644 - return true; -} - - static bool SpeedHashToCurve(const std::string &selected) { - if (!selected.empty() && selected.find("hashtocurve") == std::string::npos) { + static bool SpeedSpx(const std::string &selected) { + if (!selected.empty() && selected.find("spx") == std::string::npos) { return true; -@@ -1487,7 +1548,8 @@ bool Speed(const std::vector &args) { - !SpeedScrypt(selected) || - !SpeedRSAKeyGen(selected) || - !SpeedHRSS(selected) || -- !SpeedKyber(selected) || +@@ -1661,7 +1722,8 @@ bool Speed(const std::vector &args) { + !SpeedScrypt(selected) || // + !SpeedRSAKeyGen(selected) || // + !SpeedHRSS(selected) || // +- !SpeedKyber(selected) || // + !SpeedKyber512(selected) || + !SpeedKyber768(selected) || - !SpeedHashToCurve(selected) || + !SpeedSpx(selected) || // + !SpeedHashToCurve(selected) || // !SpeedTrustToken("TrustToken-Exp1-Batch1", TRUST_TOKEN_experiment_v1(), 1, - selected) || --- -2.46.0 - diff --git a/boring-sys/patches/underscore-wildcards.patch b/boring-sys/patches/underscore-wildcards.patch index f281b3a1..38e406a2 100644 --- a/boring-sys/patches/underscore-wildcards.patch +++ b/boring-sys/patches/underscore-wildcards.patch @@ -1,21 +1,10 @@ https://github.com/google/boringssl/compare/master...cloudflare:boringssl:underscore-wildcards ---- a/src/crypto/x509v3/v3_utl.c -+++ b/src/crypto/x509v3/v3_utl.c -@@ -790,7 +790,9 @@ static int wildcard_match(const unsigned char *prefix, size_t prefix_len, - // Check that the part matched by the wildcard contains only - // permitted characters and only matches a single label. - for (p = wildcard_start; p != wildcard_end; ++p) { -- if (!OPENSSL_isalnum(*p) && *p != '-') { -+ if (!OPENSSL_isalnum(*p) && *p != '-' && -+ !(*p == '_' && -+ (flags & X509_CHECK_FLAG_UNDERSCORE_WILDCARDS))) { - return 0; - } - } ---- a/src/crypto/x509/x509_test.cc -+++ b/src/crypto/x509/x509_test.cc -@@ -4500,6 +4500,31 @@ TEST(X509Test, Names) { +diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc +index 9699b5a75..b0e9b34a6 100644 +--- a/crypto/x509/x509_test.cc ++++ b/crypto/x509/x509_test.cc +@@ -4420,6 +4420,31 @@ TEST(X509Test, Names) { /*invalid_emails=*/{}, /*flags=*/0, }, @@ -47,9 +36,26 @@ https://github.com/google/boringssl/compare/master...cloudflare:boringssl:unders }; size_t i = 0; ---- a/src/include/openssl/x509c3.h -+++ b/src/include/openssl/x509v3.h -@@ -4497,6 +4497,8 @@ OPENSSL_EXPORT int X509_PURPOSE_get_id(const X509_PURPOSE *); +diff --git a/crypto/x509v3/v3_utl.c b/crypto/x509v3/v3_utl.c +index bbc82e283..e61e1901d 100644 +--- a/crypto/x509v3/v3_utl.c ++++ b/crypto/x509v3/v3_utl.c +@@ -790,7 +790,9 @@ static int wildcard_match(const unsigned char *prefix, size_t prefix_len, + // Check that the part matched by the wildcard contains only + // permitted characters and only matches a single label. + for (p = wildcard_start; p != wildcard_end; ++p) { +- if (!OPENSSL_isalnum(*p) && *p != '-') { ++ if (!OPENSSL_isalnum(*p) && *p != '-' && ++ !(*p == '_' && ++ (flags & X509_CHECK_FLAG_UNDERSCORE_WILDCARDS))) { + return 0; + } + } +diff --git a/include/openssl/x509v3.h b/include/openssl/x509v3.h +index 2a2e02c2e..24e0604b0 100644 +--- a/include/openssl/x509v3.h ++++ b/include/openssl/x509v3.h +@@ -939,6 +939,8 @@ OPENSSL_EXPORT STACK_OF(OPENSSL_STRING) *X509_get1_ocsp(X509 *x); #define X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS 0 // Skip the subject common name fallback if subjectAltNames is missing. #define X509_CHECK_FLAG_NEVER_CHECK_SUBJECT 0x20 @@ -58,4 +64,3 @@ https://github.com/google/boringssl/compare/master...cloudflare:boringssl:unders OPENSSL_EXPORT int X509_check_host(X509 *x, const char *chk, size_t chklen, unsigned int flags, char **peername); --- diff --git a/boring/Cargo.toml b/boring/Cargo.toml index 4badf126..227ff998 100644 --- a/boring/Cargo.toml +++ b/boring/Cargo.toml @@ -13,70 +13,25 @@ edition = { workspace = true } rust-version = "1.80" [package.metadata.docs.rs] -features = ["pq-experimental", "underscore-wildcards"] +features = ["underscore-wildcards"] rustdoc-args = ["--cfg", "docsrs"] [features] # Controlling the build -# NOTE: This feature is deprecated. It is needed for the submoduled -# boringssl-fips, which is extremely old and requires modifications to the -# bindings, as some newer APIs don't exist and some function signatures have -# changed. It is highly recommended to use `fips-precompiled` instead. -# -# This feature sets `fips-compat` on behalf of the user to guarantee bindings -# compatibility with the submoduled boringssl-fips. -# # Use a FIPS-validated version of BoringSSL. -fips = ["fips-compat", "boring-sys/fips"] +fips = ["boring-sys/fips"] -# Build with compatibility for the submoduled boringssl-fips, without enabling -# the `fips` feature itself (useful e.g. if `fips-link-precompiled` is used -# with an older BoringSSL version). -fips-compat = [] +# **DO NOT USE** This will be removed without warning in future releases. +legacy-compat-deprecated = [] -# Use a precompiled FIPS-validated version of BoringSSL. Meant to be used with -# FIPS-20230428 or newer. Users must set `BORING_BSSL_FIPS_PATH` to use this -# feature, or else the build will fail. -fips-precompiled = ["boring-sys/fips-precompiled"] - -# Link with precompiled FIPS-validated `bcm.o` module. -fips-link-precompiled = ["boring-sys/fips-link-precompiled"] - -# Applies a patch to the boringSSL source code that enables support for PQ key -# exchange. This feature is necessary in order to compile the bindings for the -# default branch of boringSSL. Alternatively, a version of boringSSL that -# implements the same feature set can be provided by setting -# `BORING_BSSL{,_FIPS}_SOURCE_PATH` and `BORING_BSSL{,_FIPS}_ASSUME_PATCHED`. -pq-experimental = ["boring-sys/pq-experimental"] - -# Applies a patch to enable -# `ffi::X509_CHECK_FLAG_UNDERSCORE_WILDCARDS`. Same caveats as -# those for `pq-experimental` feature apply. +# Applies a patch to enable `ffi::X509_CHECK_FLAG_UNDERSCORE_WILDCARDS`. This +# feature is necessary in order to compile the bindings for the default branch +# of boringSSL. Alternatively, a version of boringSSL that implements the same +# feature set can be provided by setting `BORING_BSSL{,_FIPS}_SOURCE_PATH` and +# `BORING_BSSL{,_FIPS}_ASSUME_PATCHED`. underscore-wildcards = ["boring-sys/underscore-wildcards"] -# Controlling key exchange preferences at compile time - -# Choose key exchange preferences at compile time. This prevents the user from -# choosing their own preferences. -kx-safe-default = [] - -# Support PQ key exchange. The client will prefer classical key exchange, but -# will upgrade to PQ key exchange if requested by the server. This is the -# safest option if you don't know if the peer supports PQ key exchange. This -# feature implies "kx-safe-default". -kx-client-pq-supported = ["kx-safe-default"] - -# Prefer PQ key exchange. The client will prefer PQ exchange, but fallback to -# classical key exchange if requested by the server. This is the best option if -# you know the peer supports PQ key exchange. This feature implies -# "kx-safe-default" and "kx-client-pq-supported". -kx-client-pq-preferred = ["kx-safe-default", "kx-client-pq-supported"] - -# Disable key exchange involving non-NIST key exchange on the client side. -# Implies "kx-safe-default". -kx-client-nist-required = ["kx-safe-default"] - [dependencies] bitflags = { workspace = true } foreign-types = { workspace = true } diff --git a/boring/examples/mk_certs.rs b/boring/examples/mk_certs.rs index 11c0cc62..5a26c90e 100644 --- a/boring/examples/mk_certs.rs +++ b/boring/examples/mk_certs.rs @@ -43,18 +43,19 @@ fn mk_ca_cert() -> Result<(X509, PKey), ErrorStack> { let not_after = Asn1Time::days_from_now(365)?; cert_builder.set_not_after(¬_after)?; - cert_builder.append_extension(BasicConstraints::new().critical().ca().build()?)?; + cert_builder.append_extension(BasicConstraints::new().critical().ca().build()?.as_ref())?; cert_builder.append_extension( KeyUsage::new() .critical() .key_cert_sign() .crl_sign() - .build()?, + .build()? + .as_ref(), )?; let subject_key_identifier = SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(None, None))?; - cert_builder.append_extension(subject_key_identifier)?; + cert_builder.append_extension(&subject_key_identifier)?; cert_builder.sign(&privkey, MessageDigest::sha256())?; let cert = cert_builder.build(); @@ -106,7 +107,7 @@ fn mk_ca_signed_cert( let not_after = Asn1Time::days_from_now(365)?; cert_builder.set_not_after(¬_after)?; - cert_builder.append_extension(BasicConstraints::new().build()?)?; + cert_builder.append_extension(BasicConstraints::new().build()?.as_ref())?; cert_builder.append_extension( KeyUsage::new() @@ -114,24 +115,25 @@ fn mk_ca_signed_cert( .non_repudiation() .digital_signature() .key_encipherment() - .build()?, + .build()? + .as_ref(), )?; let subject_key_identifier = SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(Some(ca_cert), None))?; - cert_builder.append_extension(subject_key_identifier)?; + cert_builder.append_extension(&subject_key_identifier)?; let auth_key_identifier = AuthorityKeyIdentifier::new() .keyid(false) .issuer(false) .build(&cert_builder.x509v3_context(Some(ca_cert), None))?; - cert_builder.append_extension(auth_key_identifier)?; + cert_builder.append_extension(&auth_key_identifier)?; let subject_alt_name = SubjectAlternativeName::new() .dns("*.example.com") .dns("hello.com") .build(&cert_builder.x509v3_context(Some(ca_cert), None))?; - cert_builder.append_extension(subject_alt_name)?; + cert_builder.append_extension(&subject_alt_name)?; cert_builder.sign(ca_privkey, MessageDigest::sha256())?; let cert = cert_builder.build(); @@ -147,7 +149,7 @@ fn real_main() -> Result<(), ErrorStack> { match ca_cert.issued(&cert) { Ok(()) => println!("Certificate verified!"), Err(ver_err) => println!("Failed to verify certificate: {ver_err}"), - }; + } Ok(()) } @@ -156,5 +158,5 @@ fn main() { match real_main() { Ok(()) => println!("Finished."), Err(e) => println!("Error: {e}"), - }; + } } diff --git a/boring/src/asn1.rs b/boring/src/asn1.rs index 49d8a08e..6ce0d510 100644 --- a/boring/src/asn1.rs +++ b/boring/src/asn1.rs @@ -63,20 +63,19 @@ foreign_type_and_impl_send_sync! { impl fmt::Display for Asn1GeneralizedTimeRef { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - unsafe { - let mem_bio = match MemBio::new() { - Err(_) => return f.write_str("error"), - Ok(m) => m, - }; - let print_result = cvt(ffi::ASN1_GENERALIZEDTIME_print( - mem_bio.as_ptr(), - self.as_ptr(), - )); - match print_result { - Err(_) => f.write_str("error"), - Ok(_) => f.write_str(str::from_utf8_unchecked(mem_bio.get_buf())), - } - } + let bio = MemBio::new().ok(); + let msg = bio + .as_ref() + .and_then(|mem_bio| unsafe { + cvt(ffi::ASN1_GENERALIZEDTIME_print( + mem_bio.as_ptr(), + self.as_ptr(), + )) + .ok()?; + str::from_utf8(mem_bio.get_buf()).ok() + }) + .unwrap_or("error"); + f.write_str(msg) } } @@ -528,7 +527,20 @@ impl Asn1BitStringRef { #[corresponds(ASN1_STRING_get0_data)] #[must_use] pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr() as *mut _), self.len()) } + unsafe { + let ptr = ASN1_STRING_get0_data(self.as_ptr().cast()); + if ptr.is_null() { + return &[]; + } + slice::from_raw_parts(ptr, self.len()) + } + } + + /// Returns the Asn1BitString as a str, if possible. + #[corresponds(ASN1_STRING_get0_data)] + #[must_use] + pub fn to_str(&self) -> Option<&str> { + str::from_utf8(self.as_slice()).ok() } /// Returns the number of bytes in the string. @@ -601,10 +613,11 @@ impl fmt::Display for Asn1ObjectRef { self.as_ptr(), 0, ); - match str::from_utf8(&buf[..len as usize]) { - Err(_) => fmt.write_str("error"), - Ok(s) => fmt.write_str(s), - } + fmt.write_str( + buf.get(..len as usize) + .and_then(|s| str::from_utf8(s).ok()) + .unwrap_or("error"), + ) } } } diff --git a/boring/src/bio.rs b/boring/src/bio.rs index 6566ebb8..2e6b2572 100644 --- a/boring/src/bio.rs +++ b/boring/src/bio.rs @@ -19,9 +19,9 @@ impl Drop for MemBioSlice<'_> { impl<'a> MemBioSlice<'a> { pub fn new(buf: &'a [u8]) -> Result, ErrorStack> { - #[cfg(not(feature = "fips-compat"))] + #[cfg(not(feature = "legacy-compat-deprecated"))] type BufLen = isize; - #[cfg(feature = "fips-compat")] + #[cfg(feature = "legacy-compat-deprecated")] type BufLen = libc::c_int; ffi::init(); @@ -68,7 +68,10 @@ impl MemBio { unsafe { let mut ptr = ptr::null_mut(); let len = ffi::BIO_get_mem_data(self.0, &mut ptr); - slice::from_raw_parts(ptr as *const _ as *const _, len as usize) + if ptr.is_null() { + return &[]; + } + slice::from_raw_parts(ptr.cast_const().cast(), len as usize) } } } diff --git a/boring/src/dh.rs b/boring/src/dh.rs index c8449d86..8e0c68fb 100644 --- a/boring/src/dh.rs +++ b/boring/src/dh.rs @@ -32,7 +32,7 @@ where } to_der! { - /// Serializes the parameters into a DER-encoded PKCS#3 DHparameter structure. + /// Serializes the parameters into a DER-encoded PKCS#3 `DHparameter` structure. #[corresponds(i2d_DHparams)] params_to_der, ffi::i2d_DHparams diff --git a/boring/src/error.rs b/boring/src/error.rs index 2295c2c4..6ed411a7 100644 --- a/boring/src/error.rs +++ b/boring/src/error.rs @@ -20,6 +20,7 @@ use openssl_macros::corresponds; use std::borrow::Cow; use std::error; use std::ffi::CStr; +use std::ffi::CString; use std::fmt; use std::io; use std::ptr; @@ -27,6 +28,8 @@ use std::str; use crate::ffi; +pub use crate::ffi::ErrLib; + /// Collection of [`Error`]s from OpenSSL. /// /// [`Error`]: struct.Error.html @@ -35,6 +38,9 @@ pub struct ErrorStack(Vec); impl ErrorStack { /// Pops the contents of the OpenSSL error stack, and returns it. + /// + /// This should be used only immediately after calling Boring FFI functions, + /// otherwise the stack may be empty or a leftover from unrelated calls. #[corresponds(ERR_get_error_line_data)] #[must_use = "Use ErrorStack::clear() to drop the error stack"] pub fn get() -> ErrorStack { @@ -56,7 +62,13 @@ impl ErrorStack { /// Used to report errors from the Rust crate #[cold] pub(crate) fn internal_error(err: impl error::Error) -> Self { - Self(vec![Error::new_internal(err.to_string())]) + Self(vec![Error::new_internal(Data::String(err.to_string()))]) + } + + /// Used to report errors from the Rust crate + #[cold] + pub(crate) fn internal_error_str(message: &'static str) -> Self { + Self(vec![Error::new_internal(Data::Static(message))]) } /// Empties the current thread's error queue. @@ -91,7 +103,7 @@ impl fmt::Display for ErrorStack { write!( fmt, "[{}]", - err.reason_internal() + err.reason() .or_else(|| err.library()) .unwrap_or("unknown reason") )?; @@ -120,7 +132,15 @@ pub struct Error { code: c_uint, file: *const c_char, line: c_uint, - data: Option>, + data: Data, +} + +#[derive(Clone)] +enum Data { + None, + CString(CString), + String(String), + Static(&'static str), } unsafe impl Sync for Error {} @@ -146,11 +166,9 @@ impl Error { // The memory referenced by data is only valid until that slot is overwritten // in the error stack, so we'll need to copy it off if it's dynamic let data = if flags & ffi::ERR_FLAG_STRING != 0 { - let bytes = CStr::from_ptr(data as *const _).to_bytes(); - let data = String::from_utf8_lossy(bytes).into_owned(); - Some(data.into()) + Data::CString(CStr::from_ptr(data.cast()).to_owned()) } else { - None + Data::None }; Some(Error { code, @@ -174,31 +192,29 @@ impl Error { self.file, self.line, ); - let ptr = match self.data { - Some(Cow::Borrowed(data)) => Some(data.as_ptr() as *mut c_char), - Some(Cow::Owned(ref data)) => { - let ptr = ffi::OPENSSL_malloc((data.len() + 1) as _) as *mut c_char; - if ptr.is_null() { - None - } else { - ptr::copy_nonoverlapping(data.as_ptr(), ptr as *mut u8, data.len()); - *ptr.add(data.len()) = 0; - Some(ptr) - } - } - None => None, - }; - if let Some(ptr) = ptr { - ffi::ERR_add_error_data(1, ptr); + if let Some(cstr) = self.data_cstr() { + ffi::ERR_set_error_data(cstr.as_ptr().cast_mut(), ffi::ERR_FLAG_STRING); } } } + /// Get `{lib}_R_{reason}` reason code for the given library, or `None` if the error is from a different library. + /// + /// Libraries are identified by [`ERR_LIB_{name}`(ffi::ERR_LIB_SSL) constants. + #[inline] + #[must_use] + #[track_caller] + pub fn library_reason(&self, library_code: ErrLib) -> Option { + debug_assert!(library_code.0 < ffi::ERR_NUM_LIBS.0); + (self.library_code() == library_code.0 as c_int).then_some(self.reason_code()) + } + /// Returns a raw OpenSSL **packed** error code for this error, which **can't be reliably compared to any error constant**. /// - /// Use [`Error::library_code()`] and [`Error::reason_code()`] instead. + /// Use [`Error::library_code()`] and [`Error::library_reason()`] instead. /// Packed error codes are different than [SSL error codes](crate::ssl::ErrorCode). #[must_use] + #[deprecated(note = "use library_reason() to compare error codes")] pub fn code(&self) -> c_uint { self.code } @@ -214,14 +230,16 @@ impl Error { if cstr.is_null() { return None; } - let bytes = CStr::from_ptr(cstr as *const _).to_bytes(); - str::from_utf8(bytes).ok() + CStr::from_ptr(cstr.cast()) + .to_str() + .ok() + .filter(|&msg| msg != "unknown library") } } /// Returns the raw OpenSSL error constant for the library reporting the error (`ERR_LIB_{name}`). /// - /// Error [reason codes](Error::reason_code) are not globally unique, but scoped to each library. + /// Error [reason codes](Error::library_reason) are not globally unique, but scoped to each library. #[must_use] pub fn library_code(&self) -> c_int { ffi::ERR_GET_LIB(self.code) @@ -234,20 +252,23 @@ impl Error { /// Returns the reason for the error. #[must_use] - pub fn reason(&self) -> Option<&'static str> { + pub fn reason(&self) -> Option<&str> { + if self.is_internal() { + return self.data(); + } unsafe { let cstr = ffi::ERR_reason_error_string(self.code); if cstr.is_null() { return None; } - let bytes = CStr::from_ptr(cstr as *const _).to_bytes(); - str::from_utf8(bytes).ok() + CStr::from_ptr(cstr.cast()).to_str().ok() } } /// Returns [library-specific](Error::library_code) reason code corresponding to some of the `{lib}_R_{reason}` constants. /// /// Reason codes are ambiguous, and different libraries reuse the same numeric values for different errors. + /// Use [`Error::library_reason`] to compare error codes. /// /// For `ERR_LIB_SYS` the reason code is `errno`. `ERR_LIB_USER` can use any values. /// Other libraries may use [`ERR_R_*`](ffi::ERR_R_FATAL) or their own codes. @@ -263,8 +284,9 @@ impl Error { if self.file.is_null() { return ""; } - let bytes = CStr::from_ptr(self.file as *const _).to_bytes(); - str::from_utf8(bytes).unwrap_or_default() + CStr::from_ptr(self.file.cast()) + .to_str() + .unwrap_or_default() } } @@ -280,30 +302,37 @@ impl Error { /// Returns additional data describing the error. #[must_use] pub fn data(&self) -> Option<&str> { - self.data.as_deref() + match &self.data { + Data::None => None, + Data::CString(cstring) => cstring.to_str().ok(), + Data::String(s) => Some(s), + Data::Static(s) => Some(s), + } } - fn new_internal(msg: String) -> Self { + #[must_use] + fn data_cstr(&self) -> Option> { + let s = match &self.data { + Data::None => return None, + Data::CString(cstr) => return Some(Cow::Borrowed(cstr)), + Data::String(s) => s.as_str(), + Data::Static(s) => s, + }; + CString::new(s).ok().map(Cow::Owned) + } + + fn new_internal(msg: Data) -> Self { Self { code: ffi::ERR_PACK(ffi::ERR_LIB_NONE.0 as _, 0, 0) as _, file: BORING_INTERNAL.as_ptr(), line: 0, - data: Some(msg.into()), + data: msg, } } fn is_internal(&self) -> bool { std::ptr::eq(self.file, BORING_INTERNAL.as_ptr()) } - - // reason() needs 'static - fn reason_internal(&self) -> Option<&str> { - if self.is_internal() { - self.data() - } else { - self.reason() - } - } } impl fmt::Debug for Error { @@ -334,7 +363,7 @@ impl fmt::Display for Error { write!( fmt, "{}\n\nCode: {:08X}\nLoc: {}:{}", - self.reason_internal().unwrap_or("unknown TLS error"), + self.reason().unwrap_or("unknown TLS error"), &self.code, self.file(), self.line() diff --git a/boring/src/fips.rs b/boring/src/fips.rs index 8e451226..708b0903 100644 --- a/boring/src/fips.rs +++ b/boring/src/fips.rs @@ -15,16 +15,8 @@ pub fn enabled() -> bool { #[test] fn is_enabled() { - #[cfg(any( - feature = "fips", - feature = "fips-precompiled", - feature = "fips-link-precompiled" - ))] + #[cfg(feature = "fips")] assert!(enabled()); - #[cfg(not(any( - feature = "fips", - feature = "fips-precompiled", - feature = "fips-link-precompiled" - )))] + #[cfg(not(feature = "fips"))] assert!(!enabled()); } diff --git a/boring/src/hmac.rs b/boring/src/hmac.rs new file mode 100644 index 00000000..7e50e613 --- /dev/null +++ b/boring/src/hmac.rs @@ -0,0 +1,32 @@ +use crate::cvt; +use crate::error::ErrorStack; +use crate::foreign_types::ForeignTypeRef; +use crate::hash::MessageDigest; + +foreign_type_and_impl_send_sync! { + type CType = ffi::HMAC_CTX; + fn drop = ffi::HMAC_CTX_free; + + pub struct HmacCtx; +} + +impl HmacCtxRef { + /// Configures HmacCtx to use `md` as the hash function and `key` as the key. + /// + /// https://commondatastorage.googleapis.com/chromium-boringssl-docs/hmac.h.html#HMAC_Init_ex + pub fn init(&mut self, key: &[u8], md: &MessageDigest) -> Result<(), ErrorStack> { + ffi::init(); + + unsafe { + cvt(ffi::HMAC_Init_ex( + self.as_ptr(), + key.as_ptr().cast(), + key.len(), + md.as_ptr(), + // ENGINE api is deprecated + core::ptr::null_mut(), + )) + .map(|_| ()) + } + } +} diff --git a/boring/src/lib.rs b/boring/src/lib.rs index 3fdc829b..ce7df572 100644 --- a/boring/src/lib.rs +++ b/boring/src/lib.rs @@ -132,7 +132,7 @@ pub mod error; pub mod ex_data; pub mod fips; pub mod hash; -#[cfg(not(feature = "fips"))] +pub mod hmac; pub mod hpke; pub mod memcmp; pub mod nid; diff --git a/boring/src/nid.rs b/boring/src/nid.rs index 0e2fa114..42ec519a 100644 --- a/boring/src/nid.rs +++ b/boring/src/nid.rs @@ -88,7 +88,9 @@ impl Nid { pub fn long_name(&self) -> Result<&'static str, ErrorStack> { unsafe { let nameptr = cvt_p(ffi::OBJ_nid2ln(self.0) as *mut c_char)?; - str::from_utf8(CStr::from_ptr(nameptr).to_bytes()).map_err(ErrorStack::internal_error) + CStr::from_ptr(nameptr) + .to_str() + .map_err(ErrorStack::internal_error) } } @@ -98,7 +100,9 @@ impl Nid { pub fn short_name(&self) -> Result<&'static str, ErrorStack> { unsafe { let nameptr = cvt_p(ffi::OBJ_nid2sn(self.0) as *mut c_char)?; - str::from_utf8(CStr::from_ptr(nameptr).to_bytes()).map_err(ErrorStack::internal_error) + CStr::from_ptr(nameptr) + .to_str() + .map_err(ErrorStack::internal_error) } } diff --git a/boring/src/pkcs12.rs b/boring/src/pkcs12.rs index dd255e3a..e8fb7c12 100644 --- a/boring/src/pkcs12.rs +++ b/boring/src/pkcs12.rs @@ -260,7 +260,7 @@ mod test { .unwrap(); builder.set_subject_name(&name).unwrap(); builder.set_issuer_name(&name).unwrap(); - builder.append_extension(key_usage).unwrap(); + builder.append_extension(&key_usage).unwrap(); builder.set_pubkey(&pkey).unwrap(); builder.sign(&pkey, MessageDigest::sha256()).unwrap(); let cert = builder.build(); diff --git a/boring/src/ssl/async_callbacks.rs b/boring/src/ssl/async_callbacks.rs index e4bdf846..4ab18d11 100644 --- a/boring/src/ssl/async_callbacks.rs +++ b/boring/src/ssl/async_callbacks.rs @@ -95,7 +95,7 @@ impl SslContextBuilder { let finish = fut_result.or(Err(SelectCertError::ERROR))?; finish(client_hello).or(Err(SelectCertError::ERROR)) - }) + }); } /// Configures a custom private key method on the context. @@ -144,7 +144,7 @@ impl SslContextBuilder { } }; - self.set_get_session_callback(async_callback) + self.set_get_session_callback(async_callback); } /// Configures certificate verification. @@ -167,7 +167,7 @@ impl SslContextBuilder { where F: Fn(&mut SslRef) -> Result + Send + Sync + 'static, { - self.set_custom_verify_callback(mode, async_custom_verify_callback(callback)) + self.set_custom_verify_callback(mode, async_custom_verify_callback(callback)); } } @@ -176,7 +176,7 @@ impl SslRef { where F: Fn(&mut SslRef) -> Result + Send + Sync + 'static, { - self.set_custom_verify_callback(mode, async_custom_verify_callback(callback)) + self.set_custom_verify_callback(mode, async_custom_verify_callback(callback)); } /// Sets the task waker to be used in async callbacks installed on this `Ssl`. diff --git a/boring/src/ssl/callbacks.rs b/boring/src/ssl/callbacks.rs index 8ad4ba55..f08409b3 100644 --- a/boring/src/ssl/callbacks.rs +++ b/boring/src/ssl/callbacks.rs @@ -8,12 +8,15 @@ use super::{ }; use crate::error::ErrorStack; use crate::ffi; +use crate::hmac::HmacCtxRef; +use crate::ssl::TicketKeyCallbackResult; +use crate::symm::CipherCtxRef; use crate::x509::{X509StoreContext, X509StoreContextRef}; use foreign_types::ForeignType; use foreign_types::ForeignTypeRef; -use libc::c_char; -use libc::{c_int, c_uchar, c_uint, c_void}; +use libc::{c_char, c_int, c_uchar, c_uint, c_void}; use std::ffi::CStr; +use std::mem::{self, MaybeUninit}; use std::ptr; use std::slice; use std::str; @@ -269,6 +272,68 @@ where } } +unsafe fn to_uninit<'a, T: 'a>(ptr: *mut T) -> &'a mut MaybeUninit { + assert!(!ptr.is_null()); + unsafe { &mut *ptr.cast::>() } +} + +pub(super) unsafe extern "C" fn raw_ticket_key( + ssl: *mut ffi::SSL, + key_name: *mut u8, + iv: *mut u8, + evp_ctx: *mut ffi::EVP_CIPHER_CTX, + hmac_ctx: *mut ffi::HMAC_CTX, + encrypt: c_int, +) -> c_int +where + F: Fn( + &SslRef, + &mut [u8; 16], + &mut [u8; ffi::EVP_MAX_IV_LENGTH as usize], + &mut CipherCtxRef, + &mut HmacCtxRef, + bool, + ) -> TicketKeyCallbackResult + + 'static + + Sync + + Send, +{ + // SAFETY: boring provides valid inputs. + let ssl = unsafe { SslRef::from_ptr_mut(ssl) }; + + let ssl_context = ssl.ssl_context().to_owned(); + let callback = ssl_context + .ex_data::(SslContext::cached_ex_index::()) + .expect("expected session resumption callback"); + + // SAFETY: the callback guarantees that key_name is 16 bytes + let key_name = + unsafe { to_uninit(key_name.cast::<[u8; ffi::SSL_TICKET_KEY_NAME_LEN as usize]>()) }; + + // SAFETY: the callback provides 16 bytes iv + // + // https://github.com/google/boringssl/blob/main/ssl/ssl_session.cc#L331 + let iv = unsafe { to_uninit(iv.cast::<[u8; ffi::EVP_MAX_IV_LENGTH as usize]>()) }; + + // When encrypting a new ticket, encrypt will be one. + let encrypt = encrypt == 1; + + // Zero-initialize the key_name and iv, since the application is expected to populate these + // fields in the encrypt mode. + if encrypt { + *key_name = MaybeUninit::zeroed(); + *iv = MaybeUninit::zeroed(); + } + let key_name = unsafe { key_name.assume_init_mut() }; + let iv = unsafe { iv.assume_init_mut() }; + + // The EVP_CIPHER_CTX and HMAC_CTX are owned by boringSSL. + let evp_ctx = unsafe { CipherCtxRef::from_ptr_mut(evp_ctx) }; + let hmac_ctx = unsafe { HmacCtxRef::from_ptr_mut(hmac_ctx) }; + + callback(ssl, key_name, iv, evp_ctx, hmac_ctx, encrypt).into() +} + pub(super) unsafe extern "C" fn raw_alpn_select( ssl: *mut ffi::SSL, out: *mut *const c_uchar, @@ -399,7 +464,7 @@ pub(super) unsafe extern "C" fn raw_remove_session( .ex_data(SslContext::cached_ex_index::()) .expect("BUG: remove session callback missing"); - callback(ctx, session) + callback(ctx, session); } type DataPtr = *const c_uchar; @@ -451,14 +516,14 @@ where { // SAFETY: boring provides valid inputs. let ssl = unsafe { SslRef::from_ptr(ssl as *mut _) }; - let line = unsafe { str::from_utf8_unchecked(CStr::from_ptr(line).to_bytes()) }; + let line = unsafe { CStr::from_ptr(line).to_string_lossy() }; let callback = ssl .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: get session callback missing"); - callback(ssl, line); + callback(ssl, &line); } pub(super) unsafe extern "C" fn raw_sign( @@ -702,14 +767,10 @@ impl<'a> CryptoBufferBuilder<'a> { let buffer_capacity = unsafe { ffi::CRYPTO_BUFFER_len(self.buffer) }; if self.cursor.position() != buffer_capacity as u64 { // Make sure all bytes in buffer initialized as required by Boring SSL. - return Err(ErrorStack::get()); - } - unsafe { - let mut result = ptr::null_mut(); - ptr::swap(&mut self.buffer, &mut result); - std::mem::forget(self); - Ok(result) + return Err(ErrorStack::internal_error_str("invalid len")); } + // Drop is no-op if the buffer is null + Ok(mem::replace(&mut self.buffer, ptr::null_mut())) } } diff --git a/boring/src/ssl/mod.rs b/boring/src/ssl/mod.rs index bee4c307..afe22511 100644 --- a/boring/src/ssl/mod.rs +++ b/boring/src/ssl/mod.rs @@ -81,6 +81,7 @@ use crate::dh::DhRef; use crate::ec::EcKeyRef; use crate::error::ErrorStack; use crate::ex_data::Index; +use crate::hmac::HmacCtxRef; use crate::nid::Nid; use crate::pkey::{HasPrivate, PKeyRef, Params, Private}; use crate::srtp::{SrtpProtectionProfile, SrtpProtectionProfileRef}; @@ -90,6 +91,7 @@ use crate::ssl::callbacks::*; use crate::ssl::ech::SslEchKeys; use crate::ssl::error::InnerError; use crate::stack::{Stack, StackRef, Stackable}; +use crate::symm::CipherCtxRef; use crate::x509::store::{X509Store, X509StoreBuilder, X509StoreBuilderRef, X509StoreRef}; use crate::x509::verify::X509VerifyParamRef; use crate::x509::{ @@ -114,7 +116,6 @@ mod async_callbacks; mod bio; mod callbacks; mod connector; -#[cfg(not(feature = "fips"))] mod ech; mod error; mod mut_only; @@ -697,131 +698,13 @@ impl From for SslSignatureAlgorithm { } } -/// Numeric identifier of a TLS curve. -#[repr(transparent)] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct SslCurveNid(c_int); - -/// A TLS Curve. -#[repr(transparent)] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct SslCurve(c_int); - -impl SslCurve { - pub const SECP224R1: SslCurve = SslCurve(ffi::SSL_CURVE_SECP224R1 as _); - - pub const SECP256R1: SslCurve = SslCurve(ffi::SSL_CURVE_SECP256R1 as _); - - pub const SECP384R1: SslCurve = SslCurve(ffi::SSL_CURVE_SECP384R1 as _); - - pub const SECP521R1: SslCurve = SslCurve(ffi::SSL_CURVE_SECP521R1 as _); - - pub const X25519: SslCurve = SslCurve(ffi::SSL_CURVE_X25519 as _); - - pub const FFDHE2048: SslCurve = SslCurve(ffi::SSL_CURVE_DHE2048 as _); - - pub const FFDHE3072: SslCurve = SslCurve(ffi::SSL_CURVE_DHE3072 as _); - - #[cfg(feature = "pq-experimental")] - #[cfg(not(any(feature = "fips", feature = "fips-precompiled")))] - pub const X25519_KYBER768_DRAFT00: SslCurve = - SslCurve(ffi::SSL_CURVE_X25519_KYBER768_DRAFT00 as _); - - #[cfg(all( - not(any(feature = "fips", feature = "fips-precompiled")), - feature = "pq-experimental" - ))] - pub const X25519_KYBER768_DRAFT00_OLD: SslCurve = - SslCurve(ffi::SSL_CURVE_X25519_KYBER768_DRAFT00_OLD as _); - - #[cfg(all( - not(any(feature = "fips", feature = "fips-precompiled")), - feature = "pq-experimental" - ))] - pub const X25519_KYBER512_DRAFT00: SslCurve = - SslCurve(ffi::SSL_CURVE_X25519_KYBER512_DRAFT00 as _); - - #[cfg(all( - not(any(feature = "fips", feature = "fips-precompiled")), - feature = "pq-experimental" - ))] - pub const P256_KYBER768_DRAFT00: SslCurve = SslCurve(ffi::SSL_CURVE_P256_KYBER768_DRAFT00 as _); - - #[cfg(all( - not(any(feature = "fips", feature = "fips-precompiled")), - feature = "pq-experimental" - ))] - pub const X25519_MLKEM768: SslCurve = SslCurve(ffi::SSL_CURVE_X25519_MLKEM768 as _); - - /// Returns the curve name - #[corresponds(SSL_get_curve_name)] - #[must_use] - pub fn name(&self) -> Option<&'static str> { - unsafe { - let ptr = ffi::SSL_get_curve_name(self.0 as u16); - if ptr.is_null() { - return None; - } - - CStr::from_ptr(ptr).to_str().ok() - } - } - - // We need to allow dead_code here because `SslRef::set_curves` is conditionally compiled - // against the absence of the `kx-safe-default` feature and thus this function is never used. - // - // **NOTE**: This function only exists because the version of boringssl we currently use does - // not expose SSL_CTX_set1_group_ids. Because `SslRef::curve()` returns the public SSL_CURVE id - // as opposed to the internal NID, but `SslContextBuilder::set_curves()` requires the internal - // NID, we need this mapping in place to avoid breaking changes to the public API. Once the - // underlying boringssl version is upgraded, this should be removed in favor of the new - // SSL_CTX_set1_group_ids API. - #[allow(dead_code)] - pub fn nid(&self) -> Option { - match self.0 { - ffi::SSL_CURVE_SECP224R1 => Some(ffi::NID_secp224r1), - ffi::SSL_CURVE_SECP256R1 => Some(ffi::NID_X9_62_prime256v1), - ffi::SSL_CURVE_SECP384R1 => Some(ffi::NID_secp384r1), - ffi::SSL_CURVE_SECP521R1 => Some(ffi::NID_secp521r1), - ffi::SSL_CURVE_X25519 => Some(ffi::NID_X25519), - #[cfg(not(any(feature = "fips", feature = "fips-precompiled")))] - ffi::SSL_CURVE_X25519_KYBER768_DRAFT00 => Some(ffi::NID_X25519Kyber768Draft00), - #[cfg(all( - not(any(feature = "fips", feature = "fips-precompiled")), - feature = "pq-experimental" - ))] - ffi::SSL_CURVE_X25519_KYBER768_DRAFT00_OLD => Some(ffi::NID_X25519Kyber768Draft00Old), - #[cfg(all( - not(any(feature = "fips", feature = "fips-precompiled")), - feature = "pq-experimental" - ))] - ffi::SSL_CURVE_X25519_KYBER512_DRAFT00 => Some(ffi::NID_X25519Kyber512Draft00), - #[cfg(all( - not(any(feature = "fips", feature = "fips-precompiled")), - feature = "pq-experimental" - ))] - ffi::SSL_CURVE_P256_KYBER768_DRAFT00 => Some(ffi::NID_P256Kyber768Draft00), - #[cfg(all( - not(any(feature = "fips", feature = "fips-precompiled")), - feature = "pq-experimental" - ))] - ffi::SSL_CURVE_X25519_MLKEM768 => Some(ffi::NID_X25519MLKEM768), - ffi::SSL_CURVE_DHE2048 => Some(ffi::NID_ffdhe2048), - ffi::SSL_CURVE_DHE3072 => Some(ffi::NID_ffdhe3072), - _ => None, - } - .map(SslCurveNid) - } -} - /// A compliance policy. #[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg(not(feature = "fips-compat"))] pub struct CompliancePolicy(ffi::ssl_compliance_policy_t); -#[cfg(not(feature = "fips-compat"))] impl CompliancePolicy { /// Does nothing, however setting this does not undo other policies, so trying to set this is an error. + #[cfg(not(feature = "legacy-compat-deprecated"))] pub const NONE: Self = Self(ffi::ssl_compliance_policy_t::ssl_compliance_policy_none); /// Configures a TLS connection to try and be compliant with NIST requirements, but does not guarantee success. @@ -831,6 +714,7 @@ impl CompliancePolicy { /// Partially configures a TLS connection to be compliant with WPA3. Callers must enforce certificate chain requirements themselves. /// Use of this policy is less secure than the default and not recommended. + #[cfg(not(feature = "legacy-compat-deprecated"))] pub const WPA3_192_202304: Self = Self(ffi::ssl_compliance_policy_t::ssl_compliance_policy_wpa3_192_202304); } @@ -925,6 +809,53 @@ pub enum SslInfoCallbackValue { Alert(SslInfoCallbackAlert), } +/// Ticket key callback status. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum TicketKeyCallbackResult { + /// Abort the handshake. + Error, + + /// Continue with a full handshake. + /// + /// When in decryption mode, this indicates that the peer supplied session ticket was not + /// recognized. When in encryption mode, this instructs boring to not send a session ticket. + /// + /// # Note + /// + /// This is a decryption specific status code when using the submoduled BoringSSL. + Noop, + + /// Resumption callback was successful. + /// + /// When in decryption mode, attempt an abbreviated handshake via session resumption. When in + /// encryption mode, provide a new ticket to the client. + Success, + + /// Resumption callback was successful. Attempt an abbreviated handshake, and additionally + /// provide new session tickets to the peer. + /// + /// Session resumption short-circuits some security checks of a full-handshake, in exchange for + /// potential performance gains. For this reason, a session ticket should only be valid for a + /// limited time. Providing the peer with renewed session tickets allows them to continue + /// session resumption with the new tickets. + /// + /// # Note + /// + /// This is a decryption specific status code. + DecryptSuccessRenew, +} + +impl From for c_int { + fn from(value: TicketKeyCallbackResult) -> Self { + match value { + TicketKeyCallbackResult::Error => -1, + TicketKeyCallbackResult::Noop => 0, + TicketKeyCallbackResult::Success => 1, + TicketKeyCallbackResult::DecryptSuccessRenew => 2, + } + } +} + #[derive(Hash, Copy, Clone, PartialOrd, Ord, Eq, PartialEq, Debug)] pub struct SslInfoCallbackAlert(c_int); @@ -966,7 +897,6 @@ impl SslContextBuilder { unsafe { init(); let ctx = cvt_p(ffi::SSL_CTX_new(method.as_ptr()))?; - Ok(SslContextBuilder::from_ptr(ctx)) } } @@ -977,9 +907,10 @@ impl SslContextBuilder { /// /// The caller must ensure that the pointer is valid and uniquely owned by the builder. pub unsafe fn from_ptr(ctx: *mut ffi::SSL_CTX) -> SslContextBuilder { + let ctx = SslContext::from_ptr(ctx); SslContextBuilder { - ctx: SslContext::from_ptr(ctx), has_shared_cert_store: false, + ctx, } } @@ -1125,6 +1056,49 @@ impl SslContextBuilder { } } + /// Configures a custom session ticket key callback for session resumption. + /// + /// Session Resumption uses the security context (aka. session tickets) of a previous + /// connection to establish a new connection via an abbreviated handshake. Skipping portions of + /// a handshake can potentially yield performance gains. + /// + /// An attacker that compromises a server's session ticket key can impersonate the server and, + /// prior to TLS 1.3, retroactively decrypt all application traffic from sessions using that + /// ticket key. Thus ticket keys must be regularly rotated for forward secrecy. + /// + /// CipherCtx and HmacCtx are guaranteed to be initialized. + /// + /// # Panics + /// + /// This method panics if this `Ssl` is associated with a RPK context. + /// + /// # Safety + /// + /// The application is responsible for correctly setting the key_name, iv, encryption context + /// and hmac context. See the [`SSL_CTX_set_tlsext_ticket_key_cb`] docs for additional info. + /// + /// [`SSL_CTX_set_tlsext_ticket_key_cb`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_tlsext_ticket_key_cb + #[corresponds(SSL_CTX_set_tlsext_ticket_key_cb)] + pub unsafe fn set_ticket_key_callback(&mut self, callback: F) + where + F: Fn( + &SslRef, + &mut [u8; 16], + &mut [u8; ffi::EVP_MAX_IV_LENGTH as usize], + &mut CipherCtxRef, + &mut HmacCtxRef, + bool, + ) -> TicketKeyCallbackResult + + 'static + + Sync + + Send, + { + unsafe { + self.replace_ex_data(SslContext::cached_ex_index::(), callback); + ffi::SSL_CTX_set_tlsext_ticket_key_cb(self.as_ptr(), Some(raw_ticket_key::)) + }; + } + /// Sets the certificate verification depth. /// /// If the peer's certificate chain is longer than this value, verification will fail. @@ -1493,7 +1467,10 @@ impl SslContextBuilder { #[corresponds(SSL_CTX_set_alpn_protos)] pub fn set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> { unsafe { - #[cfg_attr(not(feature = "fips-compat"), allow(clippy::unnecessary_cast))] + #[cfg_attr( + not(feature = "legacy-compat-deprecated"), + allow(clippy::unnecessary_cast) + )] { assert!(protocols.len() <= ProtosLen::MAX as usize); } @@ -1719,7 +1696,7 @@ impl SslContextBuilder { + Sync + Send, { - self.set_psk_client_callback(callback) + self.set_psk_client_callback(callback); } /// Sets the callback for providing an identity and pre-shared key for a TLS-PSK server. @@ -1950,7 +1927,6 @@ impl SslContextBuilder { /// Sets the indices of the extensions to be permuted. #[corresponds(SSL_CTX_set_extension_order)] - #[cfg(not(feature = "fips-compat"))] pub fn set_extension_permutation( &mut self, indices: &[ExtensionType], @@ -1966,12 +1942,7 @@ impl SslContextBuilder { } /// Configures whether ClientHello extensions should be permuted. - /// - /// Note: This is gated to non-fips because the fips feature builds with a separate - /// version of BoringSSL which doesn't yet include these APIs. - /// Once the submoduled fips commit is upgraded, these gates can be removed. #[corresponds(SSL_CTX_set_permute_extensions)] - #[cfg(not(feature = "fips-compat"))] pub fn set_permute_extensions(&mut self, enabled: bool) { unsafe { ffi::SSL_CTX_set_permute_extensions(self.as_ptr(), enabled as _) } } @@ -2005,11 +1976,6 @@ impl SslContextBuilder { } /// Sets the context's supported curves. - // - // If the "kx-*" flags are used to set key exchange preference, then don't allow the user to - // set them here. This ensures we don't override the user's preference without telling them: - // when the flags are used, the preferences are set just before connecting or accepting. - #[cfg(not(feature = "kx-safe-default"))] #[corresponds(SSL_CTX_set1_curves_list)] pub fn set_curves_list(&mut self, curves: &str) -> Result<(), ErrorStack> { let curves = CString::new(curves).map_err(ErrorStack::internal_error)?; @@ -2022,34 +1988,10 @@ impl SslContextBuilder { } } - /// Sets the context's supported curves. - // - // If the "kx-*" flags are used to set key exchange preference, then don't allow the user to - // set them here. This ensures we don't override the user's preference without telling them: - // when the flags are used, the preferences are set just before connecting or accepting. - #[corresponds(SSL_CTX_set1_curves)] - #[cfg(not(feature = "kx-safe-default"))] - pub fn set_curves(&mut self, curves: &[SslCurve]) -> Result<(), ErrorStack> { - let curves: Vec = curves - .iter() - .filter_map(|curve| curve.nid().map(|nid| nid.0)) - .collect(); - - unsafe { - cvt_0i(ffi::SSL_CTX_set1_curves( - self.as_ptr(), - curves.as_ptr() as *const _, - curves.len(), - )) - .map(|_| ()) - } - } - /// Sets the context's compliance policy. /// /// This feature isn't available in the certified version of BoringSSL. #[corresponds(SSL_CTX_set_compliance_policy)] - #[cfg(not(feature = "fips-compat"))] pub fn set_compliance_policy(&mut self, policy: CompliancePolicy) -> Result<(), ErrorStack> { unsafe { cvt_0i(ffi::SSL_CTX_set_compliance_policy(self.as_ptr(), policy.0)).map(|_| ()) } } @@ -2070,7 +2012,6 @@ impl SslContextBuilder { /// ECHConfigs to allow stale DNS caches to update. Unlike most `SSL_CTX` APIs, this function /// is safe to call even after the `SSL_CTX` has been associated with connections on various /// threads. - #[cfg(not(feature = "fips"))] #[corresponds(SSL_CTX_set1_ech_keys)] pub fn set_ech_keys(&self, keys: &SslEchKeys) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_CTX_set1_ech_keys(self.as_ptr(), keys.as_ptr())).map(|_| ()) } @@ -2323,7 +2264,6 @@ impl SslContextRef { /// ECHConfigs to allow stale DNS caches to update. Unlike most `SSL_CTX` APIs, this function /// is safe to call even after the `SSL_CTX` has been associated with connections on various /// threads. - #[cfg(not(feature = "fips"))] #[corresponds(SSL_CTX_set1_ech_keys)] pub fn set_ech_keys(&self, keys: &SslEchKeys) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_CTX_set1_ech_keys(self.as_ptr(), keys.as_ptr())).map(|_| ()) } @@ -2337,9 +2277,9 @@ impl SslContextRef { #[derive(Debug)] pub struct GetSessionPendingError; -#[cfg(not(feature = "fips-compat"))] +#[cfg(not(feature = "legacy-compat-deprecated"))] type ProtosLen = usize; -#[cfg(feature = "fips-compat")] +#[cfg(feature = "legacy-compat-deprecated")] type ProtosLen = libc::c_uint; /// Information about the state of a cipher. @@ -2511,7 +2451,7 @@ impl SslCipherRef { CStr::from_ptr(ptr as *const _) }; - str::from_utf8(version.to_bytes()).unwrap() + version.to_str().unwrap() } /// Returns the number of bits used for the cipher. @@ -2537,7 +2477,7 @@ impl SslCipherRef { // SSL_CIPHER_description requires a buffer of at least 128 bytes. let mut buf = [0; 128]; let ptr = ffi::SSL_CIPHER_description(self.as_ptr(), buf.as_mut_ptr(), 128); - String::from_utf8(CStr::from_ptr(ptr as *const _).to_bytes().to_vec()).unwrap() + CStr::from_ptr(ptr.cast()).to_string_lossy().into_owned() } } @@ -2725,32 +2665,13 @@ impl Ssl { } } - /// Creates a new `Ssl`. - /// - // FIXME should take &SslContextRef - #[corresponds(SSL_new)] - pub fn new(ctx: &SslContext) -> Result { - unsafe { - let ptr = cvt_p(ffi::SSL_new(ctx.as_ptr()))?; - let mut ssl = Ssl::from_ptr(ptr); - ssl.set_ex_data(*SESSION_CTX_INDEX, ctx.clone()); - - Ok(ssl) - } - } - /// Creates a new [`Ssl`]. - /// - /// This function does the same as [`Self:new`] except that it takes &[SslContextRef]. - // Both functions exist for backward compatibility (no breaking API). #[corresponds(SSL_new)] - pub fn new_from_ref(ctx: &SslContextRef) -> Result { + pub fn new(ctx: &SslContextRef) -> Result { unsafe { let ptr = cvt_p(ffi::SSL_new(ctx.as_ptr()))?; let mut ssl = Ssl::from_ptr(ptr); - SSL_CTX_up_ref(ctx.as_ptr()); - let ctx_owned = SslContext::from_ptr(ctx.as_ptr()); - ssl.set_ex_data(*SESSION_CTX_INDEX, ctx_owned); + ssl.set_ex_data(*SESSION_CTX_INDEX, ctx.to_owned()); Ok(ssl) } @@ -2875,63 +2796,31 @@ impl SslRef { } } - /// Sets the ongoing session's supported groups by their named identifiers - /// (formerly referred to as curves). - #[corresponds(SSL_set1_groups)] - pub fn set_group_nids(&mut self, group_nids: &[SslCurveNid]) -> Result<(), ErrorStack> { - unsafe { - cvt_0i(ffi::SSL_set1_curves( - self.as_ptr(), - group_nids.as_ptr() as *const _, - group_nids.len(), - )) - .map(|_| ()) - } - } - - #[cfg(feature = "kx-safe-default")] - fn client_set_default_curves_list(&mut self) { - let curves = if cfg!(feature = "kx-client-pq-preferred") { - if cfg!(feature = "kx-client-nist-required") { - "P256Kyber768Draft00:P-256:P-384:P-521" - } else { - "X25519MLKEM768:X25519Kyber768Draft00:X25519:P256Kyber768Draft00:P-256:P-384:P-521" - } - } else if cfg!(feature = "kx-client-pq-supported") { - if cfg!(feature = "kx-client-nist-required") { - "P-256:P-384:P-521:P256Kyber768Draft00" - } else { - "X25519:P-256:P-384:P-521:X25519MLKEM768:X25519Kyber768Draft00:P256Kyber768Draft00" - } - } else { - if cfg!(feature = "kx-client-nist-required") { - "P-256:P-384:P-521" - } else { - "X25519:P-256:P-384:P-521" - } - }; - - self.set_curves_list(curves) - .expect("invalid default client curves list"); - } - - #[cfg(feature = "kx-safe-default")] - fn server_set_default_curves_list(&mut self) { - self.set_curves_list( - "X25519MLKEM768:X25519Kyber768Draft00:P256Kyber768Draft00:X25519:P-256:P-384", - ) - .expect("invalid default server curves list"); - } - - /// Returns the [`SslCurve`] used for this `SslRef`. + /// Returns the curve ID (aka group ID) used for this `SslRef`. #[corresponds(SSL_get_curve_id)] #[must_use] - pub fn curve(&self) -> Option { + pub fn curve(&self) -> Option { let curve_id = unsafe { ffi::SSL_get_curve_id(self.as_ptr()) }; if curve_id == 0 { return None; } - Some(SslCurve(curve_id.into())) + Some(curve_id) + } + + /// Returns the curve name used for this `SslRef`. + #[corresponds(SSL_get_curve_name)] + #[must_use] + pub fn curve_name(&self) -> Option<&'static str> { + let curve_id = self.curve()?; + + unsafe { + let ptr = ffi::SSL_get_curve_name(curve_id); + if ptr.is_null() { + return None; + } + + CStr::from_ptr(ptr).to_str().ok() + } } /// Returns an `ErrorCode` value for the most recent operation on this `SslRef`. @@ -3047,11 +2936,6 @@ impl SslRef { /// Configures whether ClientHello extensions should be permuted. #[corresponds(SSL_set_permute_extensions)] - /// - /// Note: This is gated to non-fips because the fips feature builds with a separate - /// version of BoringSSL which doesn't yet include these APIs. - /// Once the submoduled fips commit is upgraded, these gates can be removed. - #[cfg(not(feature = "fips-compat"))] pub fn set_permute_extensions(&mut self, enabled: bool) { unsafe { ffi::SSL_set_permute_extensions(self.as_ptr(), enabled as _) } } @@ -3062,7 +2946,10 @@ impl SslRef { #[corresponds(SSL_set_alpn_protos)] pub fn set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> { unsafe { - #[cfg_attr(not(feature = "fips-compat"), allow(clippy::unnecessary_cast))] + #[cfg_attr( + not(feature = "legacy-compat-deprecated"), + allow(clippy::unnecessary_cast) + )] { assert!(protocols.len() <= ProtosLen::MAX as usize); } @@ -3106,6 +2993,8 @@ impl SslRef { } /// Returns a short string describing the state of the session. + /// + /// Returns empty string if the state wasn't valid UTF-8. #[corresponds(SSL_state_string)] #[must_use] pub fn state_string(&self) -> &'static str { @@ -3114,10 +3003,12 @@ impl SslRef { CStr::from_ptr(ptr as *const _) }; - str::from_utf8(state.to_bytes()).unwrap() + state.to_str().unwrap_or_default() } /// Returns a longer string describing the state of the session. + /// + /// Returns empty string if the state wasn't valid UTF-8. #[corresponds(SSL_state_string_long)] #[must_use] pub fn state_string_long(&self) -> &'static str { @@ -3126,7 +3017,7 @@ impl SslRef { CStr::from_ptr(ptr as *const _) }; - str::from_utf8(state.to_bytes()).unwrap() + state.to_str().unwrap_or_default() } /// Sets the host name to be sent to the server for Server Name Indication (SNI). @@ -3220,6 +3111,8 @@ impl SslRef { } /// Returns a string describing the protocol version of the session. + /// + /// This may panic if the string isn't valid UTF-8 for some reason. Use [`Self::version2`] instead. #[corresponds(SSL_get_version)] #[must_use] pub fn version_str(&self) -> &'static str { @@ -3228,7 +3121,7 @@ impl SslRef { CStr::from_ptr(ptr as *const _) }; - str::from_utf8(version.to_bytes()).unwrap() + version.to_str().unwrap() } /// Sets the minimum supported protocol version. @@ -3734,7 +3627,6 @@ impl SslRef { /// Clients should use `get_ech_name_override` to verify the server certificate in case of ECH /// rejection, and follow up with `get_ech_retry_configs` to retry the connection with a fresh /// set of ECHConfigs. If the retry also fails, clients should report a connection failure. - #[cfg(not(feature = "fips"))] #[corresponds(SSL_set1_ech_config_list)] pub fn set_ech_config_list(&mut self, ech_config_list: &[u8]) -> Result<(), ErrorStack> { unsafe { @@ -3753,7 +3645,6 @@ impl SslRef { /// Clients should call this function when handling an `SSL_R_ECH_REJECTED` error code to /// recover from potential key mismatches. If the result is `Some`, the client should retry the /// connection using the returned `ECHConfigList`. - #[cfg(not(feature = "fips"))] #[corresponds(SSL_get0_ech_retry_configs)] #[must_use] pub fn get_ech_retry_configs(&self) -> Option<&[u8]> { @@ -3776,7 +3667,6 @@ impl SslRef { /// Clients should call this function during the certificate verification callback to /// ensure the server's certificate is valid for the public name, which is required to /// authenticate retry configs. - #[cfg(not(feature = "fips"))] #[corresponds(SSL_get0_ech_name_override)] #[must_use] pub fn get_ech_name_override(&self) -> Option<&[u8]> { @@ -3794,7 +3684,6 @@ impl SslRef { } // Whether or not `SSL` negotiated ECH. - #[cfg(not(feature = "fips"))] #[corresponds(SSL_ech_accepted)] #[must_use] pub fn ech_accepted(&self) -> bool { @@ -3802,7 +3691,6 @@ impl SslRef { } // Whether or not to enable ECH grease on `SSL`. - #[cfg(not(feature = "fips"))] #[corresponds(SSL_set_enable_ech_grease)] pub fn set_enable_ech_grease(&self, enable: bool) { let enable = if enable { 1 } else { 0 }; @@ -3813,7 +3701,6 @@ impl SslRef { } /// Sets the compliance policy on `SSL`. - #[cfg(not(feature = "fips-compat"))] #[corresponds(SSL_set_compliance_policy)] pub fn set_compliance_policy(&mut self, policy: CompliancePolicy) -> Result<(), ErrorStack> { unsafe { cvt_0i(ffi::SSL_set_compliance_policy(self.as_ptr(), policy.0)).map(|_| ()) } @@ -3882,10 +3769,11 @@ impl MidHandshakeSslStream { Ok(self.stream) } else { self.error = self.stream.make_error(ret); - match self.error.would_block() { - true => Err(HandshakeError::WouldBlock(self)), - false => Err(HandshakeError::Failure(self)), - } + Err(if self.error.would_block() { + HandshakeError::WouldBlock(self) + } else { + HandshakeError::Failure(self) + }) } } } @@ -3920,26 +3808,23 @@ where } impl SslStream { - fn new_base(ssl: Ssl, stream: S) -> Self { - unsafe { - let (bio, method) = bio::new(stream).unwrap(); - ffi::SSL_set_bio(ssl.as_ptr(), bio, bio); - - SslStream { - ssl: ManuallyDrop::new(ssl), - method: ManuallyDrop::new(method), - _p: PhantomData, - } - } - } - /// Creates a new `SslStream`. /// /// This function performs no IO; the stream will not have performed any part of the handshake /// with the peer. The `connect` and `accept` methods can be used to /// explicitly perform the handshake. pub fn new(ssl: Ssl, stream: S) -> Result { - Ok(Self::new_base(ssl, stream)) + let (bio, method) = bio::new(stream)?; + + unsafe { + ffi::SSL_set_bio(ssl.as_ptr(), bio, bio); + } + + Ok(SslStream { + ssl: ManuallyDrop::new(ssl), + method: ManuallyDrop::new(method), + _p: PhantomData, + }) } /// Constructs an `SslStream` from a pointer to the underlying OpenSSL `SSL` struct. @@ -3951,7 +3836,7 @@ impl SslStream { /// The caller must ensure the pointer is valid. pub unsafe fn from_raw_parts(ssl: *mut ffi::SSL, stream: S) -> Self { let ssl = Ssl::from_ptr(ssl); - Self::new_base(ssl, stream) + Self::new(ssl, stream).unwrap() } /// Like `read`, but takes a possibly-uninitialized slice. @@ -4218,7 +4103,7 @@ where /// Begin creating an `SslStream` atop `stream` pub fn new(ssl: Ssl, stream: S) -> Self { Self { - inner: SslStream::new_base(ssl, stream), + inner: SslStream::new(ssl, stream).unwrap(), } } @@ -4243,9 +4128,6 @@ where pub fn setup_connect(mut self) -> MidHandshakeSslStream { self.set_connect_state(); - #[cfg(feature = "kx-safe-default")] - self.inner.ssl.client_set_default_curves_list(); - MidHandshakeSslStream { stream: self.inner, error: Error { @@ -4275,9 +4157,6 @@ where pub fn setup_accept(mut self) -> MidHandshakeSslStream { self.set_accept_state(); - #[cfg(feature = "kx-safe-default")] - self.inner.ssl.server_set_default_curves_list(); - MidHandshakeSslStream { stream: self.inner, error: Error { @@ -4309,16 +4188,11 @@ where Ok(stream) } else { let error = stream.make_error(ret); - match error.would_block() { - true => Err(HandshakeError::WouldBlock(MidHandshakeSslStream { - stream, - error, - })), - false => Err(HandshakeError::Failure(MidHandshakeSslStream { - stream, - error, - })), - } + Err(if error.would_block() { + HandshakeError::WouldBlock(MidHandshakeSslStream { stream, error }) + } else { + HandshakeError::Failure(MidHandshakeSslStream { stream, error }) + }) } } } diff --git a/boring/src/ssl/test/mod.rs b/boring/src/ssl/test/mod.rs index 240fcca2..d8fd03e3 100644 --- a/boring/src/ssl/test/mod.rs +++ b/boring/src/ssl/test/mod.rs @@ -13,26 +13,24 @@ use crate::pkey::PKey; use crate::srtp::SrtpProfileId; use crate::ssl::test::server::Server; use crate::ssl::SslVersion; -use crate::ssl::{self, SslCurve}; use crate::ssl::{ - ExtensionType, ShutdownResult, ShutdownState, Ssl, SslAcceptor, SslAcceptorBuilder, + self, ExtensionType, ShutdownResult, ShutdownState, Ssl, SslAcceptor, SslAcceptorBuilder, SslConnector, SslContext, SslFiletype, SslMethod, SslOptions, SslStream, SslVerifyMode, }; use crate::x509::store::X509StoreBuilder; use crate::x509::verify::X509CheckFlags; use crate::x509::{X509Name, X509}; -#[cfg(not(feature = "fips"))] use super::CompliancePolicy; mod cert_compressor; mod cert_verify; mod custom_verify; -#[cfg(not(feature = "fips"))] mod ech; mod private_key_method; mod server; mod session; +mod session_resumption; mod verify; static ROOT_CERT: &[u8] = include_bytes!("../../../test/root-ca.pem"); @@ -954,59 +952,15 @@ fn sni_callback_swapped_ctx() { assert!(CALLED_BACK.load(Ordering::SeqCst)); } -#[cfg(feature = "kx-safe-default")] -#[test] -fn client_set_default_curves_list() { - let ssl_ctx = crate::ssl::SslContextBuilder::new(SslMethod::tls()) - .unwrap() - .build(); - let mut ssl = Ssl::new(&ssl_ctx).unwrap(); - - // Panics if Kyber768 missing in boringSSL. - ssl.client_set_default_curves_list(); -} - -#[cfg(feature = "kx-safe-default")] -#[test] -fn server_set_default_curves_list() { - let ssl_ctx = crate::ssl::SslContextBuilder::new(SslMethod::tls()) - .unwrap() - .build(); - let mut ssl = Ssl::new(&ssl_ctx).unwrap(); - - // Panics if Kyber768 missing in boringSSL. - ssl.server_set_default_curves_list(); -} - #[test] fn get_curve() { let server = Server::builder().build(); let client = server.client_with_root_ca(); let client_stream = client.connect(); - let curve = client_stream.ssl().curve().expect("curve"); - assert!(curve.name().is_some()); -} - -#[test] -fn get_curve_name() { - assert_eq!(SslCurve::SECP224R1.name(), Some("P-224")); - assert_eq!(SslCurve::SECP256R1.name(), Some("P-256")); - assert_eq!(SslCurve::SECP384R1.name(), Some("P-384")); - assert_eq!(SslCurve::SECP521R1.name(), Some("P-521")); - assert_eq!(SslCurve::X25519.name(), Some("X25519")); -} - -#[cfg(not(feature = "kx-safe-default"))] -#[test] -fn set_curves() { - let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); - ctx.set_curves(&[ - SslCurve::SECP224R1, - SslCurve::SECP256R1, - SslCurve::SECP384R1, - SslCurve::X25519, - ]) - .expect("Failed to set curves"); + let curve = client_stream.ssl().curve(); + assert!(curve.is_some()); + let curve_name = client_stream.ssl().curve_name(); + assert!(curve_name.is_some()); } #[test] @@ -1037,7 +991,6 @@ fn test_get_ciphers() { } #[test] -#[cfg(not(feature = "fips"))] fn test_set_compliance() { let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); ctx.set_compliance_policy(CompliancePolicy::FIPS_202205) @@ -1118,7 +1071,6 @@ fn test_info_callback() { assert!(CALLED_BACK.load(Ordering::Relaxed)); } -#[cfg(not(feature = "fips-compat"))] #[test] fn test_ssl_set_compliance() { let ctx = SslContext::builder(SslMethod::tls()).unwrap().build(); diff --git a/boring/src/ssl/test/session_resumption.rs b/boring/src/ssl/test/session_resumption.rs new file mode 100644 index 00000000..808abe30 --- /dev/null +++ b/boring/src/ssl/test/session_resumption.rs @@ -0,0 +1,242 @@ +use super::server::Server; +use crate::ssl::test::MessageDigest; +use crate::ssl::HmacCtxRef; +use crate::ssl::SslRef; +use crate::ssl::SslSession; +use crate::ssl::SslSessionCacheMode; +use crate::ssl::TicketKeyCallbackResult; +use crate::symm::Cipher; +use crate::symm::CipherCtxRef; +use std::sync::atomic::{AtomicU8, Ordering}; +use std::sync::OnceLock; + +static SUCCESS_ENCRYPTION_CALLED_BACK: AtomicU8 = AtomicU8::new(0); +static SUCCESS_DECRYPTION_CALLED_BACK: AtomicU8 = AtomicU8::new(0); +static NOOP_ENCRYPTION_CALLED_BACK: AtomicU8 = AtomicU8::new(0); +static NOOP_DECRYPTION_CALLED_BACK: AtomicU8 = AtomicU8::new(0); + +#[test] +fn resume_session() { + static SESSION_TICKET: OnceLock = OnceLock::new(); + static NST_RECIEVED_COUNT: AtomicU8 = AtomicU8::new(0); + + let mut server = Server::builder(); + server.expected_connections_count(2); + let server = server.build(); + + let mut client = server.client(); + client + .ctx() + .set_session_cache_mode(SslSessionCacheMode::CLIENT); + client.ctx().set_new_session_callback(|_, session| { + NST_RECIEVED_COUNT.fetch_add(1, Ordering::SeqCst); + // The server sends multiple session tickets but we only care to retrieve one. + let _ = SESSION_TICKET.set(session); + }); + let ssl_stream = client.connect(); + + assert!(!ssl_stream.ssl().session_reused()); + assert!(SESSION_TICKET.get().is_some()); + assert_eq!(NST_RECIEVED_COUNT.load(Ordering::SeqCst), 2); + + // Retrieve the session ticket + let session_ticket = SESSION_TICKET.get().unwrap(); + + // Attempt to resume the connection using the session ticket + let client_2 = server.client(); + let mut ssl_builder = client_2.build().builder(); + unsafe { ssl_builder.ssl().set_session(session_ticket).unwrap() }; + let ssl_stream_2 = ssl_builder.connect(); + + assert!(ssl_stream_2.ssl().session_reused()); +} + +#[test] +fn custom_callback_success() { + static SESSION_TICKET: OnceLock = OnceLock::new(); + static NST_RECIEVED_COUNT: AtomicU8 = AtomicU8::new(0); + + let mut server = Server::builder(); + server.expected_connections_count(2); + unsafe { + server + .ctx() + .set_ticket_key_callback(test_success_tickey_key_callback) + }; + let server = server.build(); + + let mut client = server.client(); + client + .ctx() + .set_session_cache_mode(SslSessionCacheMode::CLIENT); + client.ctx().set_new_session_callback(|_, session| { + NST_RECIEVED_COUNT.fetch_add(1, Ordering::SeqCst); + // The server sends multiple session tickets but we only care to retrieve one. + let _ = SESSION_TICKET.set(session); + }); + let ssl_stream = client.connect(); + + assert!(!ssl_stream.ssl().session_reused()); + assert!(SESSION_TICKET.get().is_some()); + assert_eq!(SUCCESS_ENCRYPTION_CALLED_BACK.load(Ordering::SeqCst), 2); + assert_eq!(SUCCESS_DECRYPTION_CALLED_BACK.load(Ordering::SeqCst), 0); + assert_eq!(NST_RECIEVED_COUNT.load(Ordering::SeqCst), 2); + + // Retrieve the session ticket + let session_ticket = SESSION_TICKET.get().unwrap(); + + // Attempt to resume the connection using the session ticket + let client_2 = server.client(); + let mut ssl_builder = client_2.build().builder(); + unsafe { ssl_builder.ssl().set_session(session_ticket).unwrap() }; + let ssl_stream_2 = ssl_builder.connect(); + + assert!(ssl_stream_2.ssl().session_reused()); + assert_eq!(SUCCESS_ENCRYPTION_CALLED_BACK.load(Ordering::SeqCst), 4); + assert_eq!(SUCCESS_DECRYPTION_CALLED_BACK.load(Ordering::SeqCst), 1); +} + +#[test] +fn custom_callback_unrecognized_decryption_ticket() { + static SESSION_TICKET: OnceLock = OnceLock::new(); + static NST_RECIEVED_COUNT: AtomicU8 = AtomicU8::new(0); + + let mut server = Server::builder(); + server.expected_connections_count(2); + unsafe { + server + .ctx() + .set_ticket_key_callback(test_noop_tickey_key_callback) + }; + let server = server.build(); + + let mut client = server.client(); + client + .ctx() + .set_session_cache_mode(SslSessionCacheMode::CLIENT); + client.ctx().set_new_session_callback(|_, session| { + NST_RECIEVED_COUNT.fetch_add(1, Ordering::SeqCst); + // The server sends multiple session tickets but we only care to retrieve one. + let _ = SESSION_TICKET.set(session); + }); + let ssl_stream = client.connect(); + + assert!(!ssl_stream.ssl().session_reused()); + assert!(SESSION_TICKET.get().is_some()); + assert_eq!(NOOP_ENCRYPTION_CALLED_BACK.load(Ordering::SeqCst), 2); + assert_eq!(NOOP_DECRYPTION_CALLED_BACK.load(Ordering::SeqCst), 0); + assert_eq!(NST_RECIEVED_COUNT.load(Ordering::SeqCst), 2); + + // Retrieve the session ticket + let session_ticket = SESSION_TICKET.get().unwrap(); + + // Attempt to resume the connection using the session ticket + let client_2 = server.client(); + let mut ssl_builder = client_2.build().builder(); + unsafe { ssl_builder.ssl().set_session(session_ticket).unwrap() }; + let ssl_stream_2 = ssl_builder.connect(); + + // Second connection was NOT resumed due to TicketKeyCallbackResult::Noop on decryption + assert!(!ssl_stream_2.ssl().session_reused()); + assert_eq!(NOOP_ENCRYPTION_CALLED_BACK.load(Ordering::SeqCst), 4); + assert_eq!(NOOP_DECRYPTION_CALLED_BACK.load(Ordering::SeqCst), 1); +} + +// Successfully return a session ticket in encryption mode but return a +// TicketKeyCallbackResult::Noop in decryption mode. +fn test_noop_tickey_key_callback( + _ssl: &SslRef, + key_name: &mut [u8; 16], + iv: &mut [u8; ffi::EVP_MAX_IV_LENGTH as usize], + evp_ctx: &mut CipherCtxRef, + hmac_ctx: &mut HmacCtxRef, + encrypt: bool, +) -> TicketKeyCallbackResult { + // These should only be used for testing purposes. + const TEST_KEY_NAME: [u8; 16] = [5; 16]; + const TEST_CBC_IV: [u8; ffi::EVP_MAX_IV_LENGTH as usize] = [1; ffi::EVP_MAX_IV_LENGTH as usize]; + const TEST_AES_128_CBC_KEY: [u8; 16] = [2; 16]; + const TEST_HMAC_KEY: [u8; 32] = [3; 32]; + + let digest = MessageDigest::sha256(); + let cipher = Cipher::aes_128_cbc(); + + if encrypt { + NOOP_ENCRYPTION_CALLED_BACK.fetch_add(1, Ordering::SeqCst); + + // Ensure key_name and iv are initialized and set test values. + assert_eq!(key_name, &[0; 16]); + assert_eq!(iv, &[0; 16]); + key_name.copy_from_slice(&TEST_KEY_NAME); + iv.copy_from_slice(&TEST_CBC_IV); + + // Set the encryption context. + evp_ctx + .init_encrypt(&cipher, &TEST_AES_128_CBC_KEY, &TEST_CBC_IV) + .unwrap(); + + // Set the hmac context. + hmac_ctx.init(&TEST_HMAC_KEY, &digest).unwrap(); + + TicketKeyCallbackResult::Success + } else { + NOOP_DECRYPTION_CALLED_BACK.fetch_add(1, Ordering::SeqCst); + + // Check key_name matches. + assert_eq!(key_name, &TEST_KEY_NAME); + + TicketKeyCallbackResult::Noop + } +} + +// Custom callback to encrypt and decrypt session tickets +fn test_success_tickey_key_callback( + _ssl: &SslRef, + key_name: &mut [u8; 16], + iv: &mut [u8; ffi::EVP_MAX_IV_LENGTH as usize], + evp_ctx: &mut CipherCtxRef, + hmac_ctx: &mut HmacCtxRef, + encrypt: bool, +) -> TicketKeyCallbackResult { + // These should only be used for testing purposes. + const TEST_KEY_NAME: [u8; 16] = [5; 16]; + const TEST_CBC_IV: [u8; ffi::EVP_MAX_IV_LENGTH as usize] = [1; ffi::EVP_MAX_IV_LENGTH as usize]; + const TEST_AES_128_CBC_KEY: [u8; 16] = [2; 16]; + const TEST_HMAC_KEY: [u8; 32] = [3; 32]; + + let digest = MessageDigest::sha256(); + let cipher = Cipher::aes_128_cbc(); + + if encrypt { + SUCCESS_ENCRYPTION_CALLED_BACK.fetch_add(1, Ordering::SeqCst); + + // Ensure key_name and iv are initialized and set test values. + assert_eq!(key_name, &[0; 16]); + assert_eq!(iv, &[0; 16]); + key_name.copy_from_slice(&TEST_KEY_NAME); + iv.copy_from_slice(&TEST_CBC_IV); + + // Set the encryption context. + evp_ctx + .init_encrypt(&cipher, &TEST_AES_128_CBC_KEY, &TEST_CBC_IV) + .unwrap(); + + // Set the hmac context. + hmac_ctx.init(&TEST_HMAC_KEY, &digest).unwrap(); + } else { + SUCCESS_DECRYPTION_CALLED_BACK.fetch_add(1, Ordering::SeqCst); + + // Check key_name matches. + assert_eq!(key_name, &TEST_KEY_NAME); + + // Set the decryption context. + evp_ctx + .init_decrypt(&cipher, &TEST_AES_128_CBC_KEY, iv) + .unwrap(); + + // Set the hmac context. + hmac_ctx.init(&TEST_HMAC_KEY, &digest).unwrap(); + } + + TicketKeyCallbackResult::Success +} diff --git a/boring/src/string.rs b/boring/src/string.rs index 05c401f1..4527f0d7 100644 --- a/boring/src/string.rs +++ b/boring/src/string.rs @@ -13,6 +13,9 @@ foreign_type_and_impl_send_sync! { type CType = c_char; fn drop = free; + /// # Safety + /// + /// MUST be UTF-8. pub struct OpensslString; } diff --git a/boring/src/symm.rs b/boring/src/symm.rs index 3aebd2a8..5cc0072c 100644 --- a/boring/src/symm.rs +++ b/boring/src/symm.rs @@ -53,6 +53,7 @@ //! ``` use crate::ffi; +use foreign_types::ForeignTypeRef; use libc::{c_int, c_uint}; use openssl_macros::corresponds; use std::cmp; @@ -68,6 +69,71 @@ pub enum Mode { Decrypt, } +foreign_type_and_impl_send_sync! { + type CType = ffi::EVP_CIPHER_CTX; + fn drop = ffi::EVP_CIPHER_CTX_free; + + pub struct CipherCtx; +} + +impl CipherCtxRef { + /// Configures CipherCtx for a fresh encryption operation using `cipher`. + /// + /// https://commondatastorage.googleapis.com/chromium-boringssl-docs/cipher.h.html#EVP_EncryptInit_ex + pub fn init_encrypt( + &mut self, + cipher: &Cipher, + key: &[u8], + iv: &[u8; ffi::EVP_MAX_IV_LENGTH as usize], + ) -> Result<(), ErrorStack> { + ffi::init(); + + if key.len() != cipher.key_len() { + return Err(ErrorStack::internal_error_str("invalid key size")); + } + + unsafe { + cvt(ffi::EVP_EncryptInit_ex( + self.as_ptr(), + cipher.as_ptr(), + // ENGINE api is deprecated + ptr::null_mut(), + key.as_ptr(), + iv.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Configures CipherCtx for a fresh decryption operation using `cipher`. + /// + /// https://commondatastorage.googleapis.com/chromium-boringssl-docs/cipher.h.html#EVP_DecryptInit_ex + pub fn init_decrypt( + &mut self, + cipher: &Cipher, + key: &[u8], + iv: &[u8; ffi::EVP_MAX_IV_LENGTH as usize], + ) -> Result<(), ErrorStack> { + ffi::init(); + + if key.len() != cipher.key_len() { + return Err(ErrorStack::internal_error_str("invalid key size")); + } + + unsafe { + cvt(ffi::EVP_DecryptInit_ex( + self.as_ptr(), + cipher.as_ptr(), + // ENGINE api is deprecated + ptr::null_mut(), + key.as_ptr(), + iv.as_ptr(), + )) + .map(|_| ()) + } + } +} + /// Represents a particular cipher algorithm. /// /// See OpenSSL doc at [`EVP_EncryptInit`] for more information on each algorithms. diff --git a/boring/src/util.rs b/boring/src/util.rs index bb6373c1..d34fd898 100644 --- a/boring/src/util.rs +++ b/boring/src/util.rs @@ -55,8 +55,8 @@ where match result { Ok(Ok(len)) => len as c_int, - Ok(Err(_)) => { - // FIXME restore error stack + Ok(Err(err)) => { + err.put(); 0 } Err(err) => { diff --git a/boring/src/x509/mod.rs b/boring/src/x509/mod.rs index 8c0c8012..3e2029a1 100644 --- a/boring/src/x509/mod.rs +++ b/boring/src/x509/mod.rs @@ -19,7 +19,6 @@ use std::mem; use std::net::IpAddr; use std::path::Path; use std::ptr; -use std::slice; use std::str; use std::sync::{LazyLock, Once}; @@ -485,16 +484,9 @@ impl X509Builder { } } - /// Adds an X509 extension value to the certificate. - /// - /// This works just as `append_extension` except it takes ownership of the `X509Extension`. - pub fn append_extension(&mut self, extension: X509Extension) -> Result<(), ErrorStack> { - self.append_extension2(&extension) - } - /// Adds an X509 extension value to the certificate. #[corresponds(X509_add_ext)] - pub fn append_extension2(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> { + pub fn append_extension(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::X509_add_ext(self.0.as_ptr(), extension.as_ptr(), -1))?; Ok(()) @@ -865,7 +857,7 @@ impl fmt::Debug for X509 { if let Ok(public_key) = &self.public_key() { debug_struct.field("public_key", public_key); - }; + } // TODO: Print extensions once they are supported on the X509 struct. debug_struct.finish() @@ -1121,9 +1113,9 @@ impl X509NameBuilder { } } -#[cfg(not(feature = "fips-compat"))] +#[cfg(not(feature = "legacy-compat-deprecated"))] type ValueLen = isize; -#[cfg(feature = "fips-compat")] +#[cfg(feature = "legacy-compat-deprecated")] type ValueLen = i32; foreign_type_and_impl_send_sync! { @@ -1535,6 +1527,8 @@ impl X509VerifyError { } /// Return a human readable error string from the verification error. + /// + /// Returns empty string if the message was not UTF-8. #[corresponds(X509_verify_cert_error_string)] #[allow(clippy::trivially_copy_pass_by_ref)] #[must_use] @@ -1543,7 +1537,7 @@ impl X509VerifyError { unsafe { let s = ffi::X509_verify_cert_error_string(c_long::from(self.0)); - str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap() + CStr::from_ptr(s).to_str().unwrap_or_default() } } } @@ -1695,14 +1689,12 @@ impl GeneralNameRef { return None; } - let ptr = ASN1_STRING_get0_data((*self.as_ptr()).d.ia5 as *mut _); - let len = ffi::ASN1_STRING_length((*self.as_ptr()).d.ia5 as *mut _); + let asn = Asn1BitStringRef::from_ptr((*self.as_ptr()).d.ia5); - let slice = slice::from_raw_parts(ptr, len as usize); // IA5Strings are stated to be ASCII (specifically IA5). Hopefully // OpenSSL checks that when loading a certificate but if not we'll // use this instead of from_utf8_unchecked just in case. - str::from_utf8(slice).ok() + asn.to_str() } } @@ -1732,10 +1724,7 @@ impl GeneralNameRef { return None; } - let ptr = ASN1_STRING_get0_data((*self.as_ptr()).d.ip as *mut _); - let len = ffi::ASN1_STRING_length((*self.as_ptr()).d.ip as *mut _); - - Some(slice::from_raw_parts(ptr, len as usize)) + Some(Asn1BitStringRef::from_ptr((*self.as_ptr()).d.ip).as_slice()) } } } @@ -1811,8 +1800,8 @@ impl Stackable for X509Object { use crate::ffi::{X509_get0_signature, X509_getm_notAfter, X509_getm_notBefore, X509_up_ref}; use crate::ffi::{ - ASN1_STRING_get0_data, X509_ALGOR_get0, X509_REQ_get_subject_name, X509_REQ_get_version, - X509_STORE_CTX_get0_chain, X509_set1_notAfter, X509_set1_notBefore, + X509_ALGOR_get0, X509_REQ_get_subject_name, X509_REQ_get_version, X509_STORE_CTX_get0_chain, + X509_set1_notAfter, X509_set1_notBefore, }; use crate::ffi::X509_OBJECT_get0_X509; diff --git a/boring/src/x509/tests/mod.rs b/boring/src/x509/tests/mod.rs index 0ab054ab..371cd9b6 100644 --- a/boring/src/x509/tests/mod.rs +++ b/boring/src/x509/tests/mod.rs @@ -250,34 +250,36 @@ fn x509_builder() { .unwrap(); let basic_constraints = BasicConstraints::new().critical().ca().build().unwrap(); - builder.append_extension(basic_constraints).unwrap(); + builder + .append_extension(basic_constraints.as_ref()) + .unwrap(); let key_usage = KeyUsage::new() .digital_signature() .key_encipherment() .build() .unwrap(); - builder.append_extension(key_usage).unwrap(); + builder.append_extension(&key_usage).unwrap(); let ext_key_usage = ExtendedKeyUsage::new() .client_auth() .server_auth() .other("2.999.1") .build() .unwrap(); - builder.append_extension(ext_key_usage).unwrap(); + builder.append_extension(&ext_key_usage).unwrap(); let subject_key_identifier = SubjectKeyIdentifier::new() .build(&builder.x509v3_context(None, None)) .unwrap(); - builder.append_extension(subject_key_identifier).unwrap(); + builder.append_extension(&subject_key_identifier).unwrap(); let authority_key_identifier = AuthorityKeyIdentifier::new() .keyid(true) .build(&builder.x509v3_context(None, None)) .unwrap(); - builder.append_extension(authority_key_identifier).unwrap(); + builder.append_extension(&authority_key_identifier).unwrap(); let subject_alternative_name = SubjectAlternativeName::new() .dns("example.com") .build(&builder.x509v3_context(None, None)) .unwrap(); - builder.append_extension(subject_alternative_name).unwrap(); + builder.append_extension(&subject_alternative_name).unwrap(); builder.sign(&pkey, MessageDigest::sha256()).unwrap(); diff --git a/boring/src/x509/tests/trusted_first.rs b/boring/src/x509/tests/trusted_first.rs index d79ff2e3..9f49ffe3 100644 --- a/boring/src/x509/tests/trusted_first.rs +++ b/boring/src/x509/tests/trusted_first.rs @@ -15,7 +15,7 @@ fn test_verify_cert() { assert_eq!(Ok(()), verify(&leaf, &[&root1], &[&intermediate], |_| {})); - #[cfg(not(feature = "fips-compat"))] + #[cfg(not(feature = "legacy-compat-deprecated"))] assert_eq!( Ok(()), verify( @@ -26,7 +26,7 @@ fn test_verify_cert() { ) ); - #[cfg(feature = "fips-compat")] + #[cfg(feature = "legacy-compat-deprecated")] assert_eq!( Err(X509VerifyError::CERT_HAS_EXPIRED), verify( @@ -61,7 +61,7 @@ fn test_verify_cert() { Ok(()), verify(&leaf, &[&root1], &[&intermediate, &root1_cross], |param| { param.clear_flags(X509Flags::TRUSTED_FIRST) - },) + }) ); } diff --git a/compio-boring/Cargo.toml b/compio-boring/Cargo.toml index 3625a530..76fd6371 100644 --- a/compio-boring/Cargo.toml +++ b/compio-boring/Cargo.toml @@ -12,29 +12,12 @@ An implementation of SSL streams for Compio backed by BoringSSL """ [package.metadata.docs.rs] -features = ["pq-experimental"] rustdoc-args = ["--cfg", "docsrs"] [features] # Use a FIPS-validated version of boringssl. fips = ["boring/fips", "boring-sys/fips"] -# Use a FIPS build of BoringSSL, but don't set "fips-compat". -# -# As of boringSSL commit a430310d6563c0734ddafca7731570dfb683dc19, we no longer -# need to make exceptions for the types of BufLen, ProtosLen, and ValueLen, -# which means the "fips-compat" feature is no longer needed. -# -# TODO(cjpatton) Delete this feature and modify "fips" so that it doesn't imply -# "fips-compat". -fips-precompiled = ["boring/fips-precompiled"] - -# Link with precompiled FIPS-validated `bcm.o` module. -fips-link-precompiled = ["boring/fips-link-precompiled", "boring-sys/fips-link-precompiled"] - -# Enables experimental post-quantum crypto (https://blog.cloudflare.com/post-quantum-for-all/) -pq-experimental = ["boring/pq-experimental"] - [dependencies] boring = { workspace = true } boring-sys = { workspace = true } diff --git a/quinn-boring/Cargo.toml b/quinn-boring/Cargo.toml index c0a8bb5f..557bea6e 100644 --- a/quinn-boring/Cargo.toml +++ b/quinn-boring/Cargo.toml @@ -15,12 +15,8 @@ maintenance = { status = "passively-maintained" } [features] default = ["runtime-tokio"] - runtime-tokio = ["quinn/runtime-tokio"] -# Enables experimental post-quantum crypto (https://blog.cloudflare.com/post-quantum-for-all/) -pq-experimental = ["boring/pq-experimental"] - [dependencies] boring = { workspace = true } boring-sys = { workspace = true } diff --git a/tokio-boring/Cargo.toml b/tokio-boring/Cargo.toml index 78d20b90..bb6a318b 100644 --- a/tokio-boring/Cargo.toml +++ b/tokio-boring/Cargo.toml @@ -12,29 +12,12 @@ An implementation of SSL streams for Tokio backed by BoringSSL """ [package.metadata.docs.rs] -features = ["pq-experimental"] rustdoc-args = ["--cfg", "docsrs"] [features] # Use a FIPS-validated version of boringssl. fips = ["boring/fips", "boring-sys/fips"] -# Use a FIPS build of BoringSSL, but don't set "fips-compat". -# -# As of boringSSL commit a430310d6563c0734ddafca7731570dfb683dc19, we no longer -# need to make exceptions for the types of BufLen, ProtosLen, and ValueLen, -# which means the "fips-compat" feature is no longer needed. -# -# TODO(cjpatton) Delete this feature and modify "fips" so that it doesn't imply -# "fips-compat". -fips-precompiled = ["boring/fips-precompiled"] - -# Link with precompiled FIPS-validated `bcm.o` module. -fips-link-precompiled = ["boring/fips-link-precompiled", "boring-sys/fips-link-precompiled"] - -# Enables experimental post-quantum crypto (https://blog.cloudflare.com/post-quantum-for-all/) -pq-experimental = ["boring/pq-experimental"] - [dependencies] boring = { workspace = true } boring-sys = { workspace = true } diff --git a/tokio-boring/src/bridge.rs b/tokio-boring/src/bridge.rs index 62ed7729..5fdd1c30 100644 --- a/tokio-boring/src/bridge.rs +++ b/tokio-boring/src/bridge.rs @@ -22,7 +22,7 @@ impl AsyncStreamBridge { } pub(crate) fn set_waker(&mut self, ctx: Option<&mut Context<'_>>) { - self.waker = ctx.map(|ctx| ctx.waker().clone()) + self.waker = ctx.map(|ctx| ctx.waker().clone()); } /// # Panics