From cfc8f2db4fc9ca1afe983f6390be6e405407ae80 Mon Sep 17 00:00:00 2001 From: Rushil Mehra Date: Fri, 2 Aug 2024 01:49:23 -0700 Subject: [PATCH 01/41] Actually Release 4.9.0 --- Cargo.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4de7bd10..88a55790 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ resolver = "2" [workspace.package] -version = "4.8.0" +version = "4.9.0" repository = "https://github.com/cloudflare/boring" edition = "2021" @@ -19,9 +19,9 @@ tag-prefix = "" publish = false [workspace.dependencies] -boring-sys = { version = "4.8.0", path = "./boring-sys" } -boring = { version = "4.8.0", path = "./boring" } -tokio-boring = { version = "4.8.0", path = "./tokio-boring" } +boring-sys = { version = "4.9.0", path = "./boring-sys" } +boring = { version = "4.9.0", path = "./boring" } +tokio-boring = { version = "4.9.0", path = "./tokio-boring" } bindgen = { version = "0.68.1", default-features = false, features = ["runtime"] } cmake = "0.1.18" From 89dc444fb3212bab7b577a0e2c3b6d74c1905e2b Mon Sep 17 00:00:00 2001 From: Rushil Mehra Date: Sun, 4 Aug 2024 01:32:59 -0700 Subject: [PATCH 02/41] Properly handle `Option` in `SslRef::set_curves` --- boring/src/ssl/mod.rs | 9 +++------ boring/src/ssl/test/mod.rs | 13 +++++++++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/boring/src/ssl/mod.rs b/boring/src/ssl/mod.rs index bce58f43..be26aa94 100644 --- a/boring/src/ssl/mod.rs +++ b/boring/src/ssl/mod.rs @@ -1970,16 +1970,13 @@ impl SslContextBuilder { // when the flags are used, the preferences are set just before connecting or accepting. #[cfg(not(feature = "kx-safe-default"))] pub fn set_curves(&mut self, curves: &[SslCurve]) -> Result<(), ErrorStack> { - let mut nid_curves = Vec::with_capacity(curves.len()); - for curve in curves { - nid_curves.push(curve.nid()) - } + let curves: Vec = curves.iter().filter_map(|curve| curve.nid()).collect(); unsafe { cvt_0i(ffi::SSL_CTX_set1_curves( self.as_ptr(), - nid_curves.as_ptr() as *const _, - nid_curves.len(), + curves.as_ptr() as *const _, + curves.len(), )) .map(|_| ()) } diff --git a/boring/src/ssl/test/mod.rs b/boring/src/ssl/test/mod.rs index 91236b54..131b1127 100644 --- a/boring/src/ssl/test/mod.rs +++ b/boring/src/ssl/test/mod.rs @@ -945,6 +945,19 @@ fn get_curve_name() { 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"); +} + #[test] fn test_get_ciphers() { let ctx_builder = SslContext::builder(SslMethod::tls()).unwrap(); From 72b343def14dce054cbc4947411b5dd088790abd Mon Sep 17 00:00:00 2001 From: Rushil Mehra <84047965+rushilmehra@users.noreply.github.com> Date: Sun, 4 Aug 2024 12:40:55 -0700 Subject: [PATCH 03/41] Release 4.9.1 (#259) --- Cargo.toml | 8 ++++---- RELEASE_NOTES | 36 ++++++++++++++++++++++-------------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 88a55790..3836084b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ resolver = "2" [workspace.package] -version = "4.9.0" +version = "4.9.1" repository = "https://github.com/cloudflare/boring" edition = "2021" @@ -19,9 +19,9 @@ tag-prefix = "" publish = false [workspace.dependencies] -boring-sys = { version = "4.9.0", path = "./boring-sys" } -boring = { version = "4.9.0", path = "./boring" } -tokio-boring = { version = "4.9.0", path = "./tokio-boring" } +boring-sys = { version = "4.9.1", path = "./boring-sys" } +boring = { version = "4.9.1", path = "./boring" } +tokio-boring = { version = "4.9.1", path = "./tokio-boring" } bindgen = { version = "0.68.1", default-features = false, features = ["runtime"] } cmake = "0.1.18" diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 6d000f47..ab943c5f 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,16 +1,20 @@ + +4.9.1 +- 2024-08-04 Properly handle `Option` in `SslRef::set_curves` + 4.9.0 -- 2024-08-02 Guard against empty strings given to select_next_proto (#252) -- 2024-08-01 Document `SslCurve::nid()` -- 2024-08-01 Add SslCurve::to_nid() and remove SslCurveId -- 2024-07-23 Fix x509_check_host return value -- 2024-07-29 Fix clippy lints re: docs indentation + unused feature -- 2024-07-29 Ignore clippy / rustfmt on autogenerated code -- 2024-07-26 Clean up legacy const_fn feature gates -- 2024-07-22 Impl From for SslVersion -- 2024-06-03 Split SSL curve identifiers into a separate enum. -- 2024-07-23 (ci): Fix macos crossbuild action by forcing brew link w python@3.11 -- 2024-07-09 Expose set_permute_extensions -- 2024-06-24 PQ: fix timing sidechannels and add IPDWing +- 2024-08-02 Guard against empty strings given to select_next_proto (#252) +- 2024-08-01 Document `SslCurve::nid()` +- 2024-08-01 Add SslCurve::to_nid() and remove SslCurveId +- 2024-07-23 Fix x509_check_host return value +- 2024-07-29 Fix clippy lints re: docs indentation + unused feature +- 2024-07-29 Ignore clippy / rustfmt on autogenerated code +- 2024-07-26 Clean up legacy const_fn feature gates +- 2024-07-23 Impl From for SslVersion +- 2024-06-03 Split SSL curve identifiers into a separate enum. +- 2024-07-23 (ci): Fix macos crossbuild action by forcing brew link w python@3.11 +- 2024-07-09 Expose set_permute_extensions +- 2024-06-24 PQ: fix timing sidechannels and add IPDWing 4.8.0 - 2024-06-28 Expose hmac_sha1 function @@ -25,6 +29,8 @@ - 2024-06-18 Add NIDs for cipher authentication types - 2024-06-14 Impl From for SslSignatureAlgorithm - 2024-03-27 Updates license field to valid SPDX format + +4.7.0 - 2024-05-31 Fix crosscompile - 2024-05-30 Expose hmac_sha256/512 functions @@ -40,8 +46,6 @@ - 2024-03-21 Add getters for client hello message - 2024-01-25 Removes vestigial build script - 2024-02-02 Introduce and use read_uninit and write_uninit duplicated from openssl-0.10.61 and tokio-openssl-0.6.4 - -4.3.0 - 2024-02-07 Introduce SslRef::set_private_key 4.4.1 @@ -52,6 +56,8 @@ - 2024-01-16 Expose `set_compliance_policy` and `get_ciphers` - 2024-01-08 Expose SSL_get_error - 2023-12-20 Fix support for fips-link-precompiled + +4.3.0 - 2024-01-03 Introduce X509Flags - 2024-01-03 Move x509 tests to a subdirectory - 2024-01-02 Rearrange imports in x509 module @@ -94,6 +100,8 @@ - 2023-11-02 Remove Sync trait bounds on callback futures - 2023-10-30 Update Cargo.toml - 2023-10-26 hyper and tokio "full" feature for dev builds only + +4.0.0-rc.1 - 2023-10-26 Specify exact versions of dependent crates in the workspace manifest - 2023-10-16 Add CI for cross-building from macOS - 2023-10-16 Introduce BORING_BSSL_SYSROOT and BORING_BSSL_EXTERNAL_TOOLCHAIN From 9053b5d905e8eaa6fd6e05a1cd713f5533fe58a5 Mon Sep 17 00:00:00 2001 From: 0x676e67 Date: Wed, 14 Aug 2024 17:53:57 +0800 Subject: [PATCH 04/41] chore(boring-sys): Fix git apply patch on Windows (#261) * chore(boring-sys): Fix git apply patch on Windows * cargo fmt --all --- boring-sys/build/main.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/boring-sys/build/main.rs b/boring-sys/build/main.rs index 4d2ab6c0..05242028 100644 --- a/boring-sys/build/main.rs +++ b/boring-sys/build/main.rs @@ -501,12 +501,16 @@ fn ensure_patches_applied(config: &Config) -> io::Result<()> { fn apply_patch(config: &Config, patch_name: &str) -> io::Result<()> { let src_path = get_boringssl_source_path(config); + #[cfg(not(windows))] let cmd_path = config .manifest_dir .join("patches") .join(patch_name) .canonicalize()?; + #[cfg(windows)] + let cmd_path = config.manifest_dir.join("patches").join(patch_name); + let mut args = vec!["apply", "-v", "--whitespace=fix"]; // non-bazel versions of BoringSSL have no src/ dir From 1b5ae3251fbc54c00ed830c07024feff6c4c7f34 Mon Sep 17 00:00:00 2001 From: Rushil Mehra Date: Fri, 9 Aug 2024 04:16:07 -0700 Subject: [PATCH 05/41] Expose mTLS related APIs --- boring/src/ssl/mod.rs | 57 ++++++++++++++++++++++++++++++++++++++++++ boring/src/x509/mod.rs | 24 ++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/boring/src/ssl/mod.rs b/boring/src/ssl/mod.rs index be26aa94..83b616a6 100644 --- a/boring/src/ssl/mod.rs +++ b/boring/src/ssl/mod.rs @@ -2918,6 +2918,25 @@ impl SslRef { unsafe { ffi::SSL_set_verify(self.as_ptr(), mode.bits() as c_int, None) } } + /// Sets the certificate verification depth. + /// + /// If the peer's certificate chain is longer than this value, verification will fail. + /// + /// This corresponds to [`SSL_set_verify_depth`]. + /// + /// [`SSL_set_verify_depth`]: https://docs.openssl.org/1.1.1/man3/SSL_CTX_set_verify/ + pub fn set_verify_depth(&mut self, depth: u32) { + #[cfg(feature = "rpk")] + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); + + unsafe { + ffi::SSL_set_verify_depth(self.as_ptr(), depth as c_int); + } + } + /// Returns the verify mode that was set using `set_verify`. /// /// This corresponds to [`SSL_get_verify_mode`]. @@ -2975,6 +2994,25 @@ impl SslRef { } } + /// Sets a custom certificate store for verifying peer certificates. + /// + /// This corresponds to [`SSL_CTX_set0_verify_cert_store`]. + /// + /// [`SSL_set0_verify_cert_store`]: https://docs.openssl.org/1.0.2/man3/SSL_CTX_set1_verify_cert_store/ + pub fn set_verify_cert_store(&mut self, cert_store: X509Store) -> Result<(), ErrorStack> { + #[cfg(feature = "rpk")] + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); + + unsafe { + cvt(ffi::SSL_set0_verify_cert_store(self.as_ptr(), cert_store.as_ptr()) as c_int)?; + mem::forget(cert_store); + Ok(()) + } + } + /// Like [`SslContextBuilder::set_custom_verify_callback`]. /// /// This corresponds to [`SSL_set_custom_verify`]. @@ -3800,6 +3838,25 @@ impl SslRef { Ok(()) } + /// Sets the list of CA names sent to the client. + /// + /// The CA certificates must still be added to the trust root - they are not automatically set + /// as trusted by this method. + /// + /// This corresponds to [`SSL_set_client_CA_list`]. + /// + /// [`SSL_set_client_CA_list`]: https://docs.openssl.org/1.1.1/man3/SSL_CTX_set0_CA_list/ + pub fn set_client_ca_list(&mut self, list: Stack) { + #[cfg(feature = "rpk")] + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); + + unsafe { ffi::SSL_set_client_CA_list(self.as_ptr(), list.as_ptr()) } + mem::forget(list); + } + /// Sets the private key. /// /// This corresponds to [`SSL_use_PrivateKey`]. diff --git a/boring/src/x509/mod.rs b/boring/src/x509/mod.rs index 30a4b2b8..1ef04454 100644 --- a/boring/src/x509/mod.rs +++ b/boring/src/x509/mod.rs @@ -474,6 +474,30 @@ impl X509Ref { } } + /// Returns this certificate's subject key id. + /// + /// This corresponds to [`X509_get0_subject_key_id`]. + /// + /// [`X509_get0_subject_key_id`]: https://docs.openssl.org/1.1.1/man3/X509_get_extension_flags/ + pub fn subject_key_id(&self) -> &Asn1StringRef { + unsafe { + let name = ffi::X509_get0_subject_key_id(self.as_ptr()); + Asn1StringRef::from_ptr(name as _) + } + } + + /// Returns this certificate's authority key id. + /// + /// This corresponds to [`X509_get0_authority_key_id`]. + /// + /// [`X509_get0_authority_key_id`]: https://docs.openssl.org/1.1.1/man3/X509_get_extension_flags/ + pub fn authority_key_id(&self) -> &Asn1StringRef { + unsafe { + let name = ffi::X509_get0_authority_key_id(self.as_ptr()); + Asn1StringRef::from_ptr(name as _) + } + } + pub fn public_key(&self) -> Result, ErrorStack> { unsafe { let pkey = cvt_p(ffi::X509_get_pubkey(self.as_ptr()))?; From 2be6e100b6335747a8aee983386e4bbde8d63b1c Mon Sep 17 00:00:00 2001 From: Rushil Mehra Date: Tue, 13 Aug 2024 17:24:42 -0700 Subject: [PATCH 06/41] Introduce ForeignTypeExt and ForeignTypeRefExt `ForeignTypeExt` and `ForeignTypeRefExt` are inspired by https://github.com/sfackler/rust-openssl/pull/1345, which make dealing with FFI safer and more ergonomic. The new APIs (e.g. from_const_ptr_opt`) also allow for gracefully handling instances where the initial API call results in `NULL`. Instead of crashing the program, `None` will be returned. --- boring/src/util.rs | 31 ++++++++++++++++++++++++-- boring/src/x509/mod.rs | 50 ++++++++++++++++-------------------------- 2 files changed, 48 insertions(+), 33 deletions(-) diff --git a/boring/src/util.rs b/boring/src/util.rs index 21591d2f..bb6373c1 100644 --- a/boring/src/util.rs +++ b/boring/src/util.rs @@ -1,10 +1,10 @@ +use crate::error::ErrorStack; +use foreign_types::{ForeignType, ForeignTypeRef}; use libc::{c_char, c_int, c_void}; use std::any::Any; use std::panic::{self, AssertUnwindSafe}; use std::slice; -use crate::error::ErrorStack; - /// Wraps a user-supplied callback and a slot for panics thrown inside the callback (while FFI /// frames are on the stack). /// @@ -65,3 +65,30 @@ where } } } + +#[allow(dead_code)] +pub trait ForeignTypeExt: ForeignType { + unsafe fn from_ptr_opt(ptr: *mut Self::CType) -> Option { + if ptr.is_null() { + None + } else { + Some(Self::from_ptr(ptr)) + } + } +} +impl ForeignTypeExt for FT {} + +pub trait ForeignTypeRefExt: ForeignTypeRef { + unsafe fn from_const_ptr<'a>(ptr: *const Self::CType) -> &'a Self { + Self::from_ptr(ptr as *mut Self::CType) + } + + unsafe fn from_const_ptr_opt<'a>(ptr: *const Self::CType) -> Option<&'a Self> { + if ptr.is_null() { + None + } else { + Some(Self::from_const_ptr(ptr as *mut Self::CType)) + } + } +} +impl ForeignTypeRefExt for FT {} diff --git a/boring/src/x509/mod.rs b/boring/src/x509/mod.rs index 1ef04454..eb55c305 100644 --- a/boring/src/x509/mod.rs +++ b/boring/src/x509/mod.rs @@ -36,6 +36,7 @@ use crate::pkey::{HasPrivate, HasPublic, PKey, PKeyRef, Public}; use crate::ssl::SslRef; use crate::stack::{Stack, StackRef, Stackable}; use crate::string::OpensslString; +use crate::util::ForeignTypeRefExt; use crate::x509::verify::X509VerifyParamRef; use crate::{cvt, cvt_n, cvt_p}; @@ -407,8 +408,7 @@ impl X509Ref { pub fn subject_name(&self) -> &X509NameRef { unsafe { let name = ffi::X509_get_subject_name(self.as_ptr()); - assert!(!name.is_null()); - X509NameRef::from_ptr(name) + X509NameRef::from_const_ptr_opt(name).expect("issuer name must not be null") } } @@ -419,19 +419,6 @@ impl X509Ref { unsafe { ffi::X509_subject_name_hash(self.as_ptr()) as u32 } } - /// Returns this certificate's issuer name. - /// - /// This corresponds to [`X509_get_issuer_name`]. - /// - /// [`X509_get_issuer_name`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_get_subject_name.html - pub fn issuer_name(&self) -> &X509NameRef { - unsafe { - let name = ffi::X509_get_issuer_name(self.as_ptr()); - assert!(!name.is_null()); - X509NameRef::from_ptr(name) - } - } - /// Returns this certificate's subject alternative name entries, if they exist. /// /// This corresponds to [`X509_get_ext_d2i`] called with `NID_subject_alt_name`. @@ -453,6 +440,15 @@ impl X509Ref { } } + /// Returns this certificate's issuer name. + #[corresponds(X509_get_issuer_name)] + pub fn issuer_name(&self) -> &X509NameRef { + unsafe { + let name = ffi::X509_get_issuer_name(self.as_ptr()); + X509NameRef::from_const_ptr_opt(name).expect("issuer name must not be null") + } + } + /// Returns this certificate's issuer alternative name entries, if they exist. /// /// This corresponds to [`X509_get_ext_d2i`] called with `NID_issuer_alt_name`. @@ -474,27 +470,19 @@ impl X509Ref { } } - /// Returns this certificate's subject key id. - /// - /// This corresponds to [`X509_get0_subject_key_id`]. - /// - /// [`X509_get0_subject_key_id`]: https://docs.openssl.org/1.1.1/man3/X509_get_extension_flags/ - pub fn subject_key_id(&self) -> &Asn1StringRef { + /// Returns this certificate's subject key id, if it exists. + pub fn subject_key_id(&self) -> Option<&Asn1StringRef> { unsafe { - let name = ffi::X509_get0_subject_key_id(self.as_ptr()); - Asn1StringRef::from_ptr(name as _) + let data = ffi::X509_get0_subject_key_id(self.as_ptr()); + Asn1StringRef::from_const_ptr_opt(data) } } - /// Returns this certificate's authority key id. - /// - /// This corresponds to [`X509_get0_authority_key_id`]. - /// - /// [`X509_get0_authority_key_id`]: https://docs.openssl.org/1.1.1/man3/X509_get_extension_flags/ - pub fn authority_key_id(&self) -> &Asn1StringRef { + /// Returns this certificate's authority key id, if it exists. + pub fn authority_key_id(&self) -> Option<&Asn1StringRef> { unsafe { - let name = ffi::X509_get0_authority_key_id(self.as_ptr()); - Asn1StringRef::from_ptr(name as _) + let data = ffi::X509_get0_authority_key_id(self.as_ptr()); + Asn1StringRef::from_const_ptr_opt(data) } } From fae2f7fbf1bb2f5eb77c5abbcaf72d8736be1ed1 Mon Sep 17 00:00:00 2001 From: Rushil Mehra Date: Mon, 12 Aug 2024 21:21:29 -0700 Subject: [PATCH 07/41] Introduce `corresponds` macro from openssl-macros Our rustdocs are miserably broken. We manually link to openssl docs in most binding definitions, and openssl keeps changing their documentation URL, so in order to fix everything I'd have to touch every single binding definition in every single file. Instead, we should use the `corresponds` macro from the openssl-macros crate which nicely adds a link to the openssl documentation on our behalf. If the openssl documentation url ever changes again in the future, a simple dependency bump should solve the issue. --- Cargo.toml | 1 + boring/Cargo.toml | 1 + boring/src/ssl/mod.rs | 793 +++++++++-------------------------------- boring/src/x509/mod.rs | 82 ++--- 4 files changed, 204 insertions(+), 673 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3836084b..e8bf6e38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,5 +40,6 @@ http = "0.2" hyper = { version = "0.14", default-features = false } linked_hash_set = "0.1" once_cell = "1.0" +openssl-macros = "0.1.1" tower = "0.4" tower-layer = "0.3" diff --git a/boring/Cargo.toml b/boring/Cargo.toml index 0a576ab4..a558eefa 100644 --- a/boring/Cargo.toml +++ b/boring/Cargo.toml @@ -69,6 +69,7 @@ kx-client-nist-required = ["kx-safe-default"] bitflags = { workspace = true } foreign-types = { workspace = true } once_cell = { workspace = true } +openssl-macros = { workspace = true } libc = { workspace = true } boring-sys = { workspace = true } diff --git a/boring/src/ssl/mod.rs b/boring/src/ssl/mod.rs index 83b616a6..12ffedf5 100644 --- a/boring/src/ssl/mod.rs +++ b/boring/src/ssl/mod.rs @@ -57,10 +57,10 @@ //! } //! } //! ``` -use crate::ffi; use foreign_types::{ForeignType, ForeignTypeRef, Opaque}; use libc::{c_char, c_int, c_long, c_uchar, c_uint, c_void}; use once_cell::sync::Lazy; +use openssl_macros::corresponds; use std::any::TypeId; use std::collections::HashMap; use std::convert::TryInto; @@ -82,6 +82,7 @@ use crate::dh::DhRef; use crate::ec::EcKeyRef; use crate::error::ErrorStack; use crate::ex_data::Index; +use crate::ffi; use crate::nid::Nid; use crate::pkey::{HasPrivate, PKeyRef, Params, Private}; use crate::srtp::{SrtpProtectionProfile, SrtpProtectionProfileRef}; @@ -243,9 +244,7 @@ pub struct SslMethod(*const ffi::SSL_METHOD); impl SslMethod { /// Support all versions of the TLS protocol. - /// - /// This corresponds to `TLS_method` on OpenSSL 1.1.0 and `SSLv23_method` - /// on OpenSSL 1.0.x. + #[corresponds(TLS_method)] pub fn tls() -> SslMethod { unsafe { SslMethod(TLS_method()) } } @@ -257,25 +256,19 @@ impl SslMethod { } /// Support all versions of the DTLS protocol. - /// - /// This corresponds to `DTLS_method` on OpenSSL 1.1.0 and `DTLSv1_method` - /// on OpenSSL 1.0.x. + #[corresponds(DTLS_method)] pub fn dtls() -> SslMethod { unsafe { SslMethod(DTLS_method()) } } /// Support all versions of the TLS protocol, explicitly as a client. - /// - /// This corresponds to `TLS_client_method` on OpenSSL 1.1.0 and - /// `SSLv23_client_method` on OpenSSL 1.0.x. + #[corresponds(TLS_client_method)] pub fn tls_client() -> SslMethod { unsafe { SslMethod(TLS_client_method()) } } /// Support all versions of the TLS protocol, explicitly as a server. - /// - /// This corresponds to `TLS_server_method` on OpenSSL 1.1.0 and - /// `SSLv23_server_method` on OpenSSL 1.0.x. + #[corresponds(TLS_server_method)] pub fn tls_server() -> SslMethod { unsafe { SslMethod(TLS_server_method()) } } @@ -285,6 +278,7 @@ impl SslMethod { /// # Safety /// /// The caller must ensure the pointer is valid. + #[corresponds(TLS_server_method)] pub unsafe fn from_ptr(ptr: *const ffi::SSL_METHOD) -> SslMethod { SslMethod(ptr) } @@ -737,6 +731,7 @@ impl SslCurve { /// This corresponds to [`SSL_get_curve_name`] /// /// [`SSL_get_curve_name`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_get_curve_name + #[corresponds(SSL_get_curve_name)] pub fn name(&self) -> Option<&'static str> { unsafe { let ptr = ffi::SSL_get_curve_name(self.0 as u16); @@ -858,10 +853,7 @@ pub struct SslContextBuilder { #[cfg(feature = "rpk")] impl SslContextBuilder { /// Creates a new `SslContextBuilder` to be used with Raw Public Key. - /// - /// This corresponds to [`SSL_CTX_new`]. - /// - /// [`SSL_CTX_new`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_new.html + #[corresponds(SSL_CTX_new)] pub fn new_rpk() -> Result { unsafe { init(); @@ -901,10 +893,7 @@ impl SslContextBuilder { impl SslContextBuilder { /// Creates a new `SslContextBuilder`. - /// - /// This corresponds to [`SSL_CTX_new`]. - /// - /// [`SSL_CTX_new`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_new.html + #[corresponds(SSL_CTX_new)] pub fn new(method: SslMethod) -> Result { unsafe { init(); @@ -955,10 +944,7 @@ impl SslContextBuilder { } /// Configures the certificate verification method for new connections. - /// - /// This corresponds to [`SSL_CTX_set_verify`]. - /// - /// [`SSL_CTX_set_verify`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_verify.html + #[corresponds(SSL_CTX_set_verify)] pub fn set_verify(&mut self, mode: SslVerifyMode) { #[cfg(feature = "rpk")] assert!(!self.is_rpk, "This API is not supported for RPK"); @@ -981,13 +967,10 @@ impl SslContextBuilder { /// Those callbacks can inspect the peer-sent chain, call [`X509StoreContextRef::verify_cert`] /// and inspect the result, or perform other operations more straightforwardly. /// - /// This corresponds to [`SSL_CTX_set_verify`]. - /// /// # Panics /// /// This method panics if this `Ssl` is associated with a RPK context. - /// - /// [`SSL_CTX_set_verify`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_verify.html + #[corresponds(SSL_CTX_set_verify)] pub fn set_verify_callback(&mut self, mode: SslVerifyMode, callback: F) where F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send, @@ -1015,6 +998,7 @@ impl SslContextBuilder { /// # Panics /// /// This method panics if this `Ssl` is associated with a RPK context. + #[corresponds(SSL_CTX_set_custom_verify)] pub fn set_custom_verify_callback(&mut self, mode: SslVerifyMode, callback: F) where F: Fn(&mut SslRef) -> Result<(), SslVerifyError> + 'static + Sync + Send, @@ -1040,10 +1024,8 @@ impl SslContextBuilder { /// Obtain the server name with the `servername` method and then set the corresponding context /// with `set_ssl_context` /// - /// This corresponds to [`SSL_CTX_set_tlsext_servername_callback`]. - /// - /// [`SSL_CTX_set_tlsext_servername_callback`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_tlsext_servername_callback.html // FIXME tlsext prefix? + #[corresponds(SSL_CTX_set_tlsext_servername_callback)] pub fn set_servername_callback(&mut self, callback: F) where F: Fn(&mut SslRef, &mut SslAlert) -> Result<(), SniError> + 'static + Sync + Send, @@ -1069,10 +1051,7 @@ impl SslContextBuilder { /// Sets the certificate verification depth. /// /// If the peer's certificate chain is longer than this value, verification will fail. - /// - /// This corresponds to [`SSL_CTX_set_verify_depth`]. - /// - /// [`SSL_CTX_set_verify_depth`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_verify_depth.html + #[corresponds(SSL_CTX_set_verify_depth)] pub fn set_verify_depth(&mut self, depth: u32) { #[cfg(feature = "rpk")] assert!(!self.is_rpk, "This API is not supported for RPK"); @@ -1083,10 +1062,7 @@ impl SslContextBuilder { } /// Sets a custom certificate store for verifying peer certificates. - /// - /// This corresponds to [`SSL_CTX_set0_verify_cert_store`]. - /// - /// [`SSL_CTX_set0_verify_cert_store`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set0_verify_cert_store.html + #[corresponds(SSL_CTX_set0_verify_cert_store)] pub fn set_verify_cert_store(&mut self, cert_store: X509Store) -> Result<(), ErrorStack> { #[cfg(feature = "rpk")] assert!(!self.is_rpk, "This API is not supported for RPK"); @@ -1101,10 +1077,7 @@ impl SslContextBuilder { } /// Replaces the context's certificate store. - /// - /// This corresponds to [`SSL_CTX_set_cert_store`]. - /// - /// [`SSL_CTX_set_cert_store`]: https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_cert_store.html + #[corresponds(SSL_CTX_set_cert_store)] pub fn set_cert_store(&mut self, cert_store: X509Store) { #[cfg(feature = "rpk")] assert!(!self.is_rpk, "This API is not supported for RPK"); @@ -1121,10 +1094,7 @@ impl SslContextBuilder { /// instead of a single record at a time. /// /// It has no effect when used with DTLS. - /// - /// This corresponds to [`SSL_CTX_set_read_ahead`]. - /// - /// [`SSL_CTX_set_read_ahead`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_read_ahead.html + #[corresponds(SSL_CTX_set_read_ahead)] pub fn set_read_ahead(&mut self, read_ahead: bool) { unsafe { ffi::SSL_CTX_set_read_ahead(self.as_ptr(), read_ahead as c_int); @@ -1132,27 +1102,20 @@ impl SslContextBuilder { } /// Sets the mode used by the context, returning the new bit-mask after adding mode. - /// - /// This corresponds to [`SSL_CTX_set_mode`]. - /// - /// [`SSL_CTX_set_mode`]: https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_mode.html + #[corresponds(SSL_CTX_set_mode)] pub fn set_mode(&mut self, mode: SslMode) -> SslMode { let bits = unsafe { ffi::SSL_CTX_set_mode(self.as_ptr(), mode.bits()) }; SslMode::from_bits_retain(bits) } /// Sets the parameters to be used during ephemeral Diffie-Hellman key exchange. - /// - /// This corresponds to [`SSL_CTX_set_tmp_dh`]. - /// - /// [`SSL_CTX_set_tmp_dh`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_tmp_dh.html + #[corresponds(SSL_CTX_set_tmp_dh)] pub fn set_tmp_dh(&mut self, dh: &DhRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_CTX_set_tmp_dh(self.as_ptr(), dh.as_ptr()) as c_int).map(|_| ()) } } /// Sets the parameters to be used during ephemeral elliptic curve Diffie-Hellman key exchange. - /// - /// This corresponds to `SSL_CTX_set_tmp_ecdh`. + #[corresponds(SSL_CTX_set_tmp_ecdh)] pub fn set_tmp_ecdh(&mut self, key: &EcKeyRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_CTX_set_tmp_ecdh(self.as_ptr(), key.as_ptr()) as c_int).map(|_| ()) } } @@ -1161,10 +1124,7 @@ impl SslContextBuilder { /// /// These locations are read from the `SSL_CERT_FILE` and `SSL_CERT_DIR` environment variables /// if present, or defaults specified at OpenSSL build time otherwise. - /// - /// This corresponds to [`SSL_CTX_set_default_verify_paths`]. - /// - /// [`SSL_CTX_set_default_verify_paths`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_default_verify_paths.html + #[corresponds(SSL_CTX_set_default_verify_paths)] pub fn set_default_verify_paths(&mut self) -> Result<(), ErrorStack> { #[cfg(feature = "rpk")] assert!(!self.is_rpk, "This API is not supported for RPK"); @@ -1175,10 +1135,7 @@ impl SslContextBuilder { /// Loads trusted root certificates from a file. /// /// The file should contain a sequence of PEM-formatted CA certificates. - /// - /// This corresponds to [`SSL_CTX_load_verify_locations`]. - /// - /// [`SSL_CTX_load_verify_locations`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_load_verify_locations.html + #[corresponds(SSL_CTX_load_verify_locations)] pub fn set_ca_file>(&mut self, file: P) -> Result<(), ErrorStack> { #[cfg(feature = "rpk")] assert!(!self.is_rpk, "This API is not supported for RPK"); @@ -1198,10 +1155,7 @@ impl SslContextBuilder { /// /// The CA certificates must still be added to the trust root - they are not automatically set /// as trusted by this method. - /// - /// This corresponds to [`SSL_CTX_set_client_CA_list`]. - /// - /// [`SSL_CTX_set_client_CA_list`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_client_CA_list.html + #[corresponds(SSL_CTX_set_client_CA_list)] pub fn set_client_ca_list(&mut self, list: Stack) { #[cfg(feature = "rpk")] assert!(!self.is_rpk, "This API is not supported for RPK"); @@ -1214,10 +1168,7 @@ impl SslContextBuilder { /// Add the provided CA certificate to the list sent by the server to the client when /// requesting client-side TLS authentication. - /// - /// This corresponds to [`SSL_CTX_add_client_CA`]. - /// - /// [`SSL_CTX_add_client_CA`]: https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_client_CA_list.html + #[corresponds(SSL_CTX_add_client_CA)] pub fn add_client_ca(&mut self, cacert: &X509Ref) -> Result<(), ErrorStack> { #[cfg(feature = "rpk")] assert!(!self.is_rpk, "This API is not supported for RPK"); @@ -1233,10 +1184,7 @@ impl SslContextBuilder { /// /// This value should be set when using client certificates, or each request will fail its /// handshake and need to be restarted. - /// - /// This corresponds to [`SSL_CTX_set_session_id_context`]. - /// - /// [`SSL_CTX_set_session_id_context`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_session_id_context.html + #[corresponds(SSL_CTX_set_session_id_context)] pub fn set_session_id_context(&mut self, sid_ctx: &[u8]) -> Result<(), ErrorStack> { unsafe { assert!(sid_ctx.len() <= c_uint::MAX as usize); @@ -1254,10 +1202,7 @@ impl SslContextBuilder { /// Only a single certificate will be loaded - use `add_extra_chain_cert` to add the remainder /// of the certificate chain, or `set_certificate_chain_file` to load the entire chain from a /// single file. - /// - /// This corresponds to [`SSL_CTX_use_certificate_file`]. - /// - /// [`SSL_CTX_use_certificate_file`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_use_certificate_file.html + #[corresponds(SSL_CTX_use_certificate_file)] pub fn set_certificate_file>( &mut self, file: P, @@ -1282,10 +1227,7 @@ impl SslContextBuilder { /// The file should contain a sequence of PEM-formatted certificates, the first being the leaf /// certificate, and the remainder forming the chain of certificates up to and including the /// trusted root certificate. - /// - /// This corresponds to [`SSL_CTX_use_certificate_chain_file`]. - /// - /// [`SSL_CTX_use_certificate_chain_file`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_use_certificate_file.html + #[corresponds(SSL_CTX_use_certificate_chain_file)] pub fn set_certificate_chain_file>( &mut self, file: P, @@ -1303,10 +1245,7 @@ impl SslContextBuilder { /// Sets the leaf certificate. /// /// Use `add_extra_chain_cert` to add the remainder of the certificate chain. - /// - /// This corresponds to [`SSL_CTX_use_certificate`]. - /// - /// [`SSL_CTX_use_certificate`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_use_certificate_file.html + #[corresponds(SSL_CTX_use_certificate)] pub fn set_certificate(&mut self, cert: &X509Ref) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_CTX_use_certificate(self.as_ptr(), cert.as_ptr())).map(|_| ()) } } @@ -1315,10 +1254,7 @@ impl SslContextBuilder { /// /// This chain should contain all certificates necessary to go from the certificate specified by /// `set_certificate` to a trusted root. - /// - /// This corresponds to [`SSL_CTX_add_extra_chain_cert`]. - /// - /// [`SSL_CTX_add_extra_chain_cert`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_add_extra_chain_cert.html + #[corresponds(SSL_CTX_add_extra_chain_cert)] pub fn add_extra_chain_cert(&mut self, cert: X509) -> Result<(), ErrorStack> { #[cfg(feature = "rpk")] assert!(!self.is_rpk, "This API is not supported for RPK"); @@ -1331,10 +1267,7 @@ impl SslContextBuilder { } /// Loads the private key from a file. - /// - /// This corresponds to [`SSL_CTX_use_PrivateKey_file`]. - /// - /// [`SSL_CTX_use_PrivateKey_file`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_use_PrivateKey_file.html + #[corresponds(SSL_CTX_use_PrivateKey_file)] pub fn set_private_key_file>( &mut self, file: P, @@ -1352,10 +1285,7 @@ impl SslContextBuilder { } /// Sets the private key. - /// - /// This corresponds to [`SSL_CTX_use_PrivateKey`]. - /// - /// [`SSL_CTX_use_PrivateKey`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_use_PrivateKey_file.html + #[corresponds(SSL_CTX_use_PrivateKey)] pub fn set_private_key(&mut self, key: &PKeyRef) -> Result<(), ErrorStack> where T: HasPrivate, @@ -1371,10 +1301,8 @@ impl SslContextBuilder { /// /// See [`ciphers`] for details on the format. /// - /// This corresponds to [`SSL_CTX_set_cipher_list`]. - /// - /// [`ciphers`]: https://www.openssl.org/docs/man1.1.0/apps/ciphers.html - /// [`SSL_CTX_set_cipher_list`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_cipher_list.html + /// [`ciphers`]: https://www.openssl.org/docs/manmaster/apps/ciphers.html + #[corresponds(SSL_CTX_set_cipher_list)] pub fn set_cipher_list(&mut self, cipher_list: &str) -> Result<(), ErrorStack> { let cipher_list = CString::new(cipher_list).unwrap(); unsafe { @@ -1390,44 +1318,33 @@ impl SslContextBuilder { /// /// See [`ciphers`] for details on the format /// - /// This corresponds to [`SSL_CTX_get_ciphers`]. - /// /// [`ciphers`]: https://www.openssl.org/docs/manmaster/man1/ciphers.html - /// [`SSL_CTX_set_cipher_list`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_get_ciphers.html + #[corresponds(SSL_CTX_get_ciphers)] pub fn ciphers(&self) -> Option<&StackRef> { self.ctx.ciphers() } /// Sets the options used by the context, returning the old set. /// - /// This corresponds to [`SSL_CTX_set_options`]. - /// /// # Note /// /// This *enables* the specified options, but does not disable unspecified options. Use /// `clear_options` for that. - /// - /// [`SSL_CTX_set_options`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_options.html + #[corresponds(SSL_CTX_set_options)] pub fn set_options(&mut self, option: SslOptions) -> SslOptions { let bits = unsafe { ffi::SSL_CTX_set_options(self.as_ptr(), option.bits()) }; SslOptions::from_bits_retain(bits) } /// Returns the options used by the context. - /// - /// This corresponds to [`SSL_CTX_get_options`]. - /// - /// [`SSL_CTX_get_options`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_options.html + #[corresponds(SSL_CTX_get_options)] pub fn options(&self) -> SslOptions { let bits = unsafe { ffi::SSL_CTX_get_options(self.as_ptr()) }; SslOptions::from_bits_retain(bits) } /// Clears the options used by the context, returning the old set. - /// - /// This corresponds to [`SSL_CTX_clear_options`]. - /// - /// [`SSL_CTX_clear_options`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_options.html + #[corresponds(SSL_CTX_clear_options)] pub fn clear_options(&mut self, option: SslOptions) -> SslOptions { let bits = unsafe { ffi::SSL_CTX_clear_options(self.as_ptr(), option.bits()) }; SslOptions::from_bits_retain(bits) @@ -1437,10 +1354,7 @@ impl SslContextBuilder { /// /// If version is `None`, the default minimum version is used. For BoringSSL this defaults to /// TLS 1.0. - /// - /// This corresponds to [`SSL_CTX_set_min_proto_version`]. - /// - /// [`SSL_CTX_set_min_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_min_proto_version.html + #[corresponds(SSL_CTX_set_min_proto_version)] pub fn set_min_proto_version(&mut self, version: Option) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_CTX_set_min_proto_version( @@ -1454,10 +1368,7 @@ impl SslContextBuilder { /// Sets the maximum supported protocol version. /// /// If version is `None`, the default maximum version is used. For BoringSSL this is TLS 1.3. - /// - /// This corresponds to [`SSL_CTX_set_max_proto_version`]. - /// - /// [`SSL_CTX_set_max_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_max_proto_version.html + #[corresponds(SSL_CTX_set_max_proto_version)] pub fn set_max_proto_version(&mut self, version: Option) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_CTX_set_max_proto_version( @@ -1469,10 +1380,7 @@ impl SslContextBuilder { } /// Gets the minimum supported protocol version. - /// - /// This corresponds to [`SSL_CTX_get_min_proto_version`]. - /// - /// [`SSL_CTX_get_min_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_min_proto_version.html + #[corresponds(SSL_CTX_get_min_proto_version)] pub fn min_proto_version(&mut self) -> Option { unsafe { let r = ffi::SSL_CTX_get_min_proto_version(self.as_ptr()); @@ -1485,10 +1393,7 @@ impl SslContextBuilder { } /// Gets the maximum supported protocol version. - /// - /// This corresponds to [`SSL_CTX_get_max_proto_version`]. - /// - /// [`SSL_CTX_get_max_proto_version`]: https://www.openssl.org/docs/man3.1/man3/SSL_CTX_get_max_proto_version.html + #[corresponds(SSL_CTX_get_max_proto_version)] pub fn max_proto_version(&mut self) -> Option { unsafe { let r = ffi::SSL_CTX_get_max_proto_version(self.as_ptr()); @@ -1506,10 +1411,7 @@ impl SslContextBuilder { /// names prefixed by their byte length. For example, the protocol list consisting of `spdy/1` /// and `http/1.1` is encoded as `b"\x06spdy/1\x08http/1.1"`. The protocols are ordered by /// preference. - /// - /// This corresponds to [`SSL_CTX_set_alpn_protos`]. - /// - /// [`SSL_CTX_set_alpn_protos`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_alpn_protos.html + #[corresponds(SSL_CTX_set_alpn_protos)] pub fn set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> { unsafe { #[cfg_attr(not(feature = "fips"), allow(clippy::unnecessary_cast))] @@ -1531,10 +1433,7 @@ impl SslContextBuilder { } /// Enables the DTLS extension "use_srtp" as defined in RFC5764. - /// - /// This corresponds to [`SSL_CTX_set_tlsext_use_srtp`]. - /// - /// [`SSL_CTX_set_tlsext_use_srtp`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_tlsext_use_srtp.html + #[corresponds(SSL_CTX_set_tlsext_use_srtp)] pub fn set_tlsext_use_srtp(&mut self, protocols: &str) -> Result<(), ErrorStack> { unsafe { let cstr = CString::new(protocols).unwrap(); @@ -1557,11 +1456,9 @@ impl SslContextBuilder { /// of those protocols on success. The [`select_next_proto`] function implements the standard /// protocol selection algorithm. /// - /// This corresponds to [`SSL_CTX_set_alpn_select_cb`]. - /// /// [`SslContextBuilder::set_alpn_protos`]: struct.SslContextBuilder.html#method.set_alpn_protos /// [`select_next_proto`]: fn.select_next_proto.html - /// [`SSL_CTX_set_alpn_select_cb`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_alpn_protos.html + #[corresponds(SSL_CTX_set_alpn_select_cb)] pub fn set_alpn_select_callback(&mut self, callback: F) where F: for<'a> Fn(&mut SslRef, &'a [u8]) -> Result<&'a [u8], AlpnError> + 'static + Sync + Send, @@ -1579,10 +1476,7 @@ impl SslContextBuilder { /// Sets a callback that is called before most ClientHello processing and before the decision whether /// to resume a session is made. The callback may inspect the ClientHello and configure the /// connection. - /// - /// This corresponds to [`SSL_CTX_set_select_certificate_cb`]. - /// - /// [`SSL_CTX_set_select_certificate_cb`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_select_certificate_cb.html + #[corresponds(SSL_CTX_set_select_certificate_cb)] pub fn set_select_certificate_callback(&mut self, callback: F) where F: Fn(ClientHello<'_>) -> Result<(), SelectCertError> + Sync + Send + 'static, @@ -1599,10 +1493,7 @@ impl SslContextBuilder { /// Configures a custom private key method on the context. /// /// See [`PrivateKeyMethod`] for more details. - /// - /// This corresponds to [`SSL_CTX_set_private_key_method`] - /// - /// [`SSL_CTX_set_private_key_method`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_private_key_method + #[corresponds(SSL_CTX_set_private_key_method)] pub fn set_private_key_method(&mut self, method: M) where M: PrivateKeyMethod, @@ -1622,19 +1513,13 @@ impl SslContextBuilder { } /// Checks for consistency between the private key and certificate. - /// - /// This corresponds to [`SSL_CTX_check_private_key`]. - /// - /// [`SSL_CTX_check_private_key`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_check_private_key.html + #[corresponds(SSL_CTX_check_private_key)] pub fn check_private_key(&self) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_CTX_check_private_key(self.as_ptr())).map(|_| ()) } } /// Returns a shared reference to the context's certificate store. - /// - /// This corresponds to [`SSL_CTX_get_cert_store`]. - /// - /// [`SSL_CTX_get_cert_store`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_cert_store.html + #[corresponds(SSL_CTX_get_cert_store)] pub fn cert_store(&self) -> &X509StoreBuilderRef { #[cfg(feature = "rpk")] assert!(!self.is_rpk, "This API is not supported for RPK"); @@ -1643,10 +1528,7 @@ impl SslContextBuilder { } /// Returns a mutable reference to the context's certificate store. - /// - /// This corresponds to [`SSL_CTX_get_cert_store`]. - /// - /// [`SSL_CTX_get_cert_store`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_cert_store.html + #[corresponds(SSL_CTX_get_cert_store)] pub fn cert_store_mut(&mut self) -> &mut X509StoreBuilderRef { #[cfg(feature = "rpk")] assert!(!self.is_rpk, "This API is not supported for RPK"); @@ -1666,10 +1548,7 @@ impl SslContextBuilder { /// returned to clients. The status may be set with the `SslRef::set_ocsp_status` method. A /// response of `Ok(true)` indicates that the OCSP status should be returned to the client, and /// `Ok(false)` indicates that the status should not be returned to the client. - /// - /// This corresponds to [`SSL_CTX_set_tlsext_status_cb`]. - /// - /// [`SSL_CTX_set_tlsext_status_cb`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_tlsext_status_cb.html + #[corresponds(SSL_CTX_set_tlsext_status_cb)] pub fn set_status_callback(&mut self, callback: F) -> Result<(), ErrorStack> where F: Fn(&mut SslRef) -> Result + 'static + Sync + Send, @@ -1689,10 +1568,7 @@ impl SslContextBuilder { /// The callback will be called with the SSL context, an identity hint if one was provided /// by the server, a mutable slice for each of the identity and pre-shared key bytes. The /// identity must be written as a null-terminated C string. - /// - /// This corresponds to [`SSL_CTX_set_psk_client_callback`]. - /// - /// [`SSL_CTX_set_psk_client_callback`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_psk_client_callback.html + #[corresponds(SSL_CTX_set_psk_client_callback)] pub fn set_psk_client_callback(&mut self, callback: F) where F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8], &mut [u8]) -> Result @@ -1722,10 +1598,7 @@ impl SslContextBuilder { /// The callback will be called with the SSL context, an identity provided by the client, /// and, a mutable slice for the pre-shared key bytes. The callback returns the number of /// bytes in the pre-shared key. - /// - /// This corresponds to [`SSL_CTX_set_psk_server_callback`]. - /// - /// [`SSL_CTX_set_psk_server_callback`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_psk_server_callback.html + #[corresponds(SSL_CTX_set_psk_server_callback)] pub fn set_psk_server_callback(&mut self, callback: F) where F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8]) -> Result @@ -1750,11 +1623,9 @@ impl SslContextBuilder { /// Note that session caching must be enabled for the callback to be invoked, and it defaults /// off for clients. [`set_session_cache_mode`] controls that behavior. /// - /// This corresponds to [`SSL_CTX_sess_set_new_cb`]. - /// /// [`SslRef::session`]: struct.SslRef.html#method.session /// [`set_session_cache_mode`]: #method.set_session_cache_mode - /// [`SSL_CTX_sess_set_new_cb`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_sess_set_new_cb.html + #[corresponds(SSL_CTX_sess_set_new_cb)] pub fn set_new_session_callback(&mut self, callback: F) where F: Fn(&mut SslRef, SslSession) + 'static + Sync + Send, @@ -1768,10 +1639,7 @@ impl SslContextBuilder { /// Sets the callback which is called when sessions are removed from the context. /// /// Sessions can be removed because they have timed out or because they are considered faulty. - /// - /// This corresponds to [`SSL_CTX_sess_set_remove_cb`]. - /// - /// [`SSL_CTX_sess_set_remove_cb`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_sess_set_new_cb.html + #[corresponds(SSL_CTX_sess_set_remove_cb)] pub fn set_remove_session_callback(&mut self, callback: F) where F: Fn(&SslContextRef, &SslSessionRef) + 'static + Sync + Send, @@ -1792,13 +1660,10 @@ impl SslContextBuilder { /// return the session corresponding to that ID if available. This is only used for servers, not /// clients. /// - /// This corresponds to [`SSL_CTX_sess_set_get_cb`]. - /// /// # Safety /// /// The returned [`SslSession`] must not be associated with a different [`SslContext`]. - /// - /// [`SSL_CTX_sess_set_get_cb`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_sess_set_new_cb.html + #[corresponds(SSL_CTX_sess_set_get_cb)] pub unsafe fn set_get_session_callback(&mut self, callback: F) where F: Fn(&mut SslRef, &[u8]) -> Result, GetSessionPendingError> @@ -1815,10 +1680,7 @@ impl SslContextBuilder { /// The callback is invoked whenever TLS key material is generated, and is passed a line of NSS /// SSLKEYLOGFILE-formatted text. This can be used by tools like Wireshark to decrypt message /// traffic. The line does not contain a trailing newline. - /// - /// This corresponds to [`SSL_CTX_set_keylog_callback`]. - /// - /// [`SSL_CTX_set_keylog_callback`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_keylog_callback.html + #[corresponds(SSL_CTX_set_keylog_callback)] pub fn set_keylog_callback(&mut self, callback: F) where F: Fn(&SslRef, &str) + 'static + Sync + Send, @@ -1832,10 +1694,7 @@ impl SslContextBuilder { /// Sets the session caching mode use for connections made with the context. /// /// Returns the previous session caching mode. - /// - /// This corresponds to [`SSL_CTX_set_session_cache_mode`]. - /// - /// [`SSL_CTX_set_session_cache_mode`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_session_cache_mode.html + #[corresponds(SSL_CTX_set_session_cache_mode)] pub fn set_session_cache_mode(&mut self, mode: SslSessionCacheMode) -> SslSessionCacheMode { unsafe { let bits = ffi::SSL_CTX_set_session_cache_mode(self.as_ptr(), mode.bits()); @@ -1848,12 +1707,9 @@ impl SslContextBuilder { /// This can be used to provide data to callbacks registered with the context. Use the /// `SslContext::new_ex_index` method to create an `Index`. /// - /// This corresponds to [`SSL_CTX_set_ex_data`]. - /// /// Note that if this method is called multiple times with the same index, any previous /// value stored in the `SslContextBuilder` will be leaked. - /// - /// [`SSL_CTX_set_ex_data`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_ex_data.html + #[corresponds(SSL_CTX_set_ex_data)] pub fn set_ex_data(&mut self, index: Index, data: T) { unsafe { self.ctx.set_ex_data(index, data); @@ -1865,11 +1721,8 @@ impl SslContextBuilder { /// This can be used to provide data to callbacks registered with the context. Use the /// `Ssl::new_ex_index` method to create an `Index`. /// - /// This corresponds to [`SSL_set_ex_data`]. - /// /// Any previous value will be returned and replaced by the new one. - /// - /// [`SSL_set_ex_data`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_ex_data.html + #[corresponds(SSL_CTX_set_ex_data)] pub fn replace_ex_data(&mut self, index: Index, data: T) -> Option { unsafe { self.ctx.replace_ex_data(index, data) } } @@ -1877,20 +1730,14 @@ impl SslContextBuilder { /// Sets the context's session cache size limit, returning the previous limit. /// /// A value of 0 means that the cache size is unbounded. - /// - /// This corresponds to [`SSL_CTX_sess_get_cache_size`]. - /// - /// [`SSL_CTX_sess_get_cache_size`]: https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_sess_set_cache_size.html + #[corresponds(SSL_CTX_sess_set_cache_size)] #[allow(clippy::useless_conversion)] pub fn set_session_cache_size(&mut self, size: u32) -> u64 { unsafe { ffi::SSL_CTX_sess_set_cache_size(self.as_ptr(), size.into()).into() } } /// Sets the context's supported signature algorithms. - /// - /// This corresponds to [`SSL_CTX_set1_sigalgs_list`]. - /// - /// [`SSL_CTX_set1_sigalgs_list`]: https://www.openssl.org/docs/man1.1.0/man3/SSL_CTX_set1_sigalgs_list.html + #[corresponds(SSL_CTX_set1_sigalgs_list)] pub fn set_sigalgs_list(&mut self, sigalgs: &str) -> Result<(), ErrorStack> { let sigalgs = CString::new(sigalgs).unwrap(); unsafe { @@ -1900,33 +1747,24 @@ impl SslContextBuilder { } /// Set's whether the context should enable GREASE. - /// - /// This corresponds to [`SSL_CTX_set_grease_enabled`] - /// - /// [`SSL_CTX_set_grease_enabled`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_grease_enabled + #[corresponds(SSL_CTX_set_grease_enabled)] pub fn set_grease_enabled(&mut self, enabled: bool) { unsafe { ffi::SSL_CTX_set_grease_enabled(self.as_ptr(), enabled as _) } } /// Configures whether ClientHello extensions should be permuted. /// - /// This corresponds to [`SSL_CTX_set_permute_extensions`]. - /// - /// [`SSL_CTX_set_permute_extensions`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_permute_extensions - /// /// Note: This is gated to non-fips because the fips feature builds with a separate /// version of BoringSSL which doesn't yet include these APIs. /// Once the submoduled fips commit is upgraded, these gates can be removed. + #[corresponds(SSL_CTX_set_permute_extensions)] #[cfg(not(feature = "fips"))] pub fn set_permute_extensions(&mut self, enabled: bool) { unsafe { ffi::SSL_CTX_set_permute_extensions(self.as_ptr(), enabled as _) } } /// Sets the context's supported signature verification algorithms. - /// - /// This corresponds to [`SSL_CTX_set_verify_algorithm_prefs`] - /// - /// [`SSL_CTX_set_verify_algorithm_prefs`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_verify_algorithm_prefs + #[corresponds(SSL_CTX_set_verify_algorithm_prefs)] pub fn set_verify_algorithm_prefs( &mut self, prefs: &[SslSignatureAlgorithm], @@ -1942,32 +1780,23 @@ impl SslContextBuilder { } /// Enables SCT requests on all client SSL handshakes. - /// - /// This corresponds to [`SSL_CTX_enable_signed_cert_timestamps`] - /// - /// [`SSL_CTX_enable_signed_cert_timestamps`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_enable_signed_cert_timestamps + #[corresponds(SSL_CTX_enable_signed_cert_timestamps)] pub fn enable_signed_cert_timestamps(&mut self) { unsafe { ffi::SSL_CTX_enable_signed_cert_timestamps(self.as_ptr()) } } /// Enables OCSP stapling on all client SSL handshakes. - /// - /// This corresponds to [`SSL_CTX_enable_ocsp_stapling`] - /// - /// [`SSL_CTX_enable_ocsp_stapling`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_enable_ocsp_stapling + #[corresponds(SSL_CTX_enable_ocsp_stapling)] pub fn enable_ocsp_stapling(&mut self) { unsafe { ffi::SSL_CTX_enable_ocsp_stapling(self.as_ptr()) } } /// Sets the context's supported curves. - /// - /// This corresponds to [`SSL_CTX_set1_curves`] - /// - /// [`SSL_CTX_set1_curves`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set1_curves // // If the "kx-*" flags are used to set key exchange preference, then don't allow the user to // set them here. This ensures we don't override the user's preference without telling them: // when the flags are used, the preferences are set just before connecting or accepting. + #[corresponds(SSL_CTX_set1_curves)] #[cfg(not(feature = "kx-safe-default"))] pub fn set_curves(&mut self, curves: &[SslCurve]) -> Result<(), ErrorStack> { let curves: Vec = curves.iter().filter_map(|curve| curve.nid()).collect(); @@ -1984,10 +1813,8 @@ impl SslContextBuilder { /// Sets the context's compliance policy. /// - /// This corresponds to [`SSL_CTX_set_compliance_policy`] - /// - /// [`SSL_CTX_set_compliance_policy`] https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_compliance_policy /// This feature isn't available in the certified version of BoringSSL. + #[corresponds(SSL_CTX_set_compliance_policy)] #[cfg(not(feature = "fips"))] pub fn set_compliance_policy(&mut self, policy: CompliancePolicy) -> Result<(), ErrorStack> { unsafe { cvt_0i(ffi::SSL_CTX_set_compliance_policy(self.as_ptr(), policy.0)).map(|_| ()) } @@ -2044,10 +1871,7 @@ impl SslContext { /// /// Each invocation of this function is guaranteed to return a distinct index. These can be used /// to store data in the context that can be retrieved later by callbacks, for example. - /// - /// This corresponds to [`SSL_CTX_get_ex_new_index`]. - /// - /// [`SSL_CTX_get_ex_new_index`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_get_ex_new_index.html + #[corresponds(SSL_CTX_get_ex_new_index)] pub fn new_ex_index() -> Result, ErrorStack> where T: 'static + Sync + Send, @@ -2078,10 +1902,8 @@ impl SslContext { /// /// See [`ciphers`] for details on the format /// - /// This corresponds to [`SSL_CTX_get_ciphers`]. - /// /// [`ciphers`]: https://www.openssl.org/docs/manmaster/man1/ciphers.html - /// [`SSL_CTX_set_cipher_list`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_get_ciphers.html + #[corresponds(SSL_CTX_get_ciphers)] pub fn ciphers(&self) -> Option<&StackRef> { unsafe { let ciphers = ffi::SSL_CTX_get_ciphers(self.as_ptr()); @@ -2096,10 +1918,7 @@ impl SslContext { impl SslContextRef { /// Returns the certificate associated with this `SslContext`, if present. - /// - /// This corresponds to [`SSL_CTX_get0_certificate`]. - /// - /// [`SSL_CTX_get0_certificate`]: https://www.openssl.org/docs/man1.1.0/ssl/ssl.html + #[corresponds(SSL_CTX_get0_certificate)] pub fn certificate(&self) -> Option<&X509Ref> { #[cfg(feature = "rpk")] assert!(!self.is_rpk(), "This API is not supported for RPK"); @@ -2115,10 +1934,7 @@ impl SslContextRef { } /// Returns the private key associated with this `SslContext`, if present. - /// - /// This corresponds to [`SSL_CTX_get0_privatekey`]. - /// - /// [`SSL_CTX_get0_privatekey`]: https://www.openssl.org/docs/man1.1.0/ssl/ssl.html + #[corresponds(SSL_CTX_get0_privatekey)] pub fn private_key(&self) -> Option<&PKeyRef> { unsafe { let ptr = ffi::SSL_CTX_get0_privatekey(self.as_ptr()); @@ -2131,10 +1947,7 @@ impl SslContextRef { } /// Returns a shared reference to the certificate store used for verification. - /// - /// This corresponds to [`SSL_CTX_get_cert_store`]. - /// - /// [`SSL_CTX_get_cert_store`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_cert_store.html + #[corresponds(SSL_CTX_get_cert_store)] pub fn cert_store(&self) -> &X509StoreRef { #[cfg(feature = "rpk")] assert!(!self.is_rpk(), "This API is not supported for RPK"); @@ -2143,8 +1956,7 @@ impl SslContextRef { } /// Returns a shared reference to the stack of certificates making up the chain from the leaf. - /// - /// This corresponds to `SSL_CTX_get_extra_chain_certs`. + #[corresponds(SSL_CTX_get_extra_chain_certs)] pub fn extra_chain_certs(&self) -> &StackRef { unsafe { let mut chain = ptr::null_mut(); @@ -2155,10 +1967,7 @@ impl SslContextRef { } /// Returns a reference to the extra data at the specified index. - /// - /// This corresponds to [`SSL_CTX_get_ex_data`]. - /// - /// [`SSL_CTX_get_ex_data`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_get_ex_data.html + #[corresponds(SSL_CTX_get_ex_data)] pub fn ex_data(&self, index: Index) -> Option<&T> { unsafe { let data = ffi::SSL_CTX_get_ex_data(self.as_ptr(), index.as_raw()); @@ -2172,6 +1981,7 @@ impl SslContextRef { // Unsafe because SSL contexts are not guaranteed to be unique, we call // this only from SslContextBuilder. + #[corresponds(SSL_CTX_get_ex_data)] unsafe fn ex_data_mut(&mut self, index: Index) -> Option<&mut T> { let data = ffi::SSL_CTX_get_ex_data(self.as_ptr(), index.as_raw()); if data.is_null() { @@ -2183,6 +1993,7 @@ impl SslContextRef { // Unsafe because SSL contexts are not guaranteed to be unique, we call // this only from SslContextBuilder. + #[corresponds(SSL_CTX_set_ex_data)] unsafe fn set_ex_data(&mut self, index: Index, data: T) { unsafe { let data = Box::into_raw(Box::new(data)) as *mut c_void; @@ -2192,6 +2003,7 @@ impl SslContextRef { // Unsafe because SSL contexts are not guaranteed to be unique, we call // this only from SslContextBuilder. + #[corresponds(SSL_CTX_set_ex_data)] unsafe fn replace_ex_data(&mut self, index: Index, data: T) -> Option { if let Some(old) = self.ex_data_mut(index) { return Some(mem::replace(old, data)); @@ -2206,14 +2018,11 @@ impl SslContextRef { /// /// Returns `true` if the session was successfully added to the cache, and `false` if it was already present. /// - /// This corresponds to [`SSL_CTX_add_session`]. - /// /// # Safety /// /// The caller of this method is responsible for ensuring that the session has never been used with another /// `SslContext` than this one. - /// - /// [`SSL_CTX_add_session`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_remove_session.html + #[corresponds(SSL_CTX_add_session)] pub unsafe fn add_session(&self, session: &SslSessionRef) -> bool { ffi::SSL_CTX_add_session(self.as_ptr(), session.as_ptr()) != 0 } @@ -2222,14 +2031,11 @@ impl SslContextRef { /// /// Returns `true` if the session was successfully found and removed, and `false` otherwise. /// - /// This corresponds to [`SSL_CTX_remove_session`]. - /// /// # Safety /// /// The caller of this method is responsible for ensuring that the session has never been used with another /// `SslContext` than this one. - /// - /// [`SSL_CTX_remove_session`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_remove_session.html + #[corresponds(SSL_CTX_remove_session)] pub unsafe fn remove_session(&self, session: &SslSessionRef) -> bool { ffi::SSL_CTX_remove_session(self.as_ptr(), session.as_ptr()) != 0 } @@ -2237,10 +2043,7 @@ impl SslContextRef { /// Returns the context's session cache size limit. /// /// A value of 0 means that the cache size is unbounded. - /// - /// This corresponds to [`SSL_CTX_sess_get_cache_size`]. - /// - /// [`SSL_CTX_sess_get_cache_size`]: https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_sess_set_cache_size.html + #[corresponds(SSL_CTX_sess_get_cache_size)] #[allow(clippy::useless_conversion)] pub fn session_cache_size(&self) -> u64 { unsafe { ffi::SSL_CTX_sess_get_cache_size(self.as_ptr()).into() } @@ -2248,10 +2051,8 @@ impl SslContextRef { /// Returns the verify mode that was set on this context from [`SslContextBuilder::set_verify`]. /// - /// This corresponds to [`SSL_CTX_get_verify_mode`]. - /// /// [`SslContextBuilder::set_verify`]: struct.SslContextBuilder.html#method.set_verify - /// [`SSL_CTX_get_verify_mode`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_get_verify_mode.html + #[corresponds(SSL_CTX_get_verify_mode)] pub fn verify_mode(&self) -> SslVerifyMode { #[cfg(feature = "rpk")] assert!(!self.is_rpk(), "This API is not supported for RPK"); @@ -2353,6 +2154,7 @@ impl ClientHello<'_> { pub struct SslCipher(*mut ffi::SSL_CIPHER); impl SslCipher { + #[corresponds(SSL_get_cipher_by_value)] pub fn from_value(value: u16) -> Option { unsafe { let ptr = ffi::SSL_get_cipher_by_value(value); @@ -2409,10 +2211,7 @@ unsafe impl ForeignTypeRef for SslCipherRef { impl SslCipherRef { /// Returns the name of the cipher. - /// - /// This corresponds to [`SSL_CIPHER_get_name`]. - /// - /// [`SSL_CIPHER_get_name`]: https://www.openssl.org/docs/manmaster/man3/SSL_CIPHER_get_name.html + #[corresponds(SSL_CIPHER_get_name)] pub fn name(&self) -> &'static str { unsafe { let ptr = ffi::SSL_CIPHER_get_name(self.as_ptr()); @@ -2421,10 +2220,7 @@ impl SslCipherRef { } /// Returns the RFC-standard name of the cipher, if one exists. - /// - /// This corresponds to [`SSL_CIPHER_standard_name`]. - /// - /// [`SSL_CIPHER_standard_name`]: https://www.openssl.org/docs/manmaster/man3/SSL_CIPHER_get_name.html + #[corresponds(SSL_CIPHER_standard_name)] pub fn standard_name(&self) -> Option<&'static str> { unsafe { let ptr = ffi::SSL_CIPHER_standard_name(self.as_ptr()); @@ -2437,10 +2233,7 @@ impl SslCipherRef { } /// Returns the SSL/TLS protocol version that first defined the cipher. - /// - /// This corresponds to [`SSL_CIPHER_get_version`]. - /// - /// [`SSL_CIPHER_get_version`]: https://www.openssl.org/docs/manmaster/man3/SSL_CIPHER_get_name.html + #[corresponds(SSL_CIPHER_get_version)] pub fn version(&self) -> &'static str { let version = unsafe { let ptr = ffi::SSL_CIPHER_get_version(self.as_ptr()); @@ -2451,10 +2244,7 @@ impl SslCipherRef { } /// Returns the number of bits used for the cipher. - /// - /// This corresponds to [`SSL_CIPHER_get_bits`]. - /// - /// [`SSL_CIPHER_get_bits`]: https://www.openssl.org/docs/manmaster/man3/SSL_CIPHER_get_name.html + #[corresponds(SSL_CIPHER_get_bits)] #[allow(clippy::useless_conversion)] pub fn bits(&self) -> CipherBits { unsafe { @@ -2468,10 +2258,7 @@ impl SslCipherRef { } /// Returns a textual description of the cipher. - /// - /// This corresponds to [`SSL_CIPHER_description`]. - /// - /// [`SSL_CIPHER_description`]: https://www.openssl.org/docs/manmaster/man3/SSL_CIPHER_get_name.html + #[corresponds(SSL_CIPHER_description)] pub fn description(&self) -> String { unsafe { // SSL_CIPHER_description requires a buffer of at least 128 bytes. @@ -2482,19 +2269,13 @@ impl SslCipherRef { } /// Returns one if the cipher uses an AEAD cipher. - /// - /// This corresponds to [`SSL_CIPHER_is_aead`]. - /// - /// [`SSL_CIPHER_is_aead`]: https://www.openssl.org/docs/manmaster/man3/SSL_CIPHER_is_aead.html + #[corresponds(SSL_CIPHER_is_aead)] pub fn cipher_is_aead(&self) -> bool { unsafe { ffi::SSL_CIPHER_is_aead(self.as_ptr()) != 0 } } /// Returns the NID corresponding to the cipher's authentication type. - /// - /// This corresponds to [`SSL_CIPHER_get_auth_nid`]. - /// - /// [`SSL_CIPHER_get_auth_nid`]: https://www.openssl.org/docs/manmaster/man3/SSL_CIPHER_get_auth_nid.html + #[corresponds(SSL_CIPHER_get_auth_nid)] pub fn cipher_auth_nid(&self) -> Option { let n = unsafe { ffi::SSL_CIPHER_get_auth_nid(self.as_ptr()) }; if n == 0 { @@ -2505,10 +2286,7 @@ impl SslCipherRef { } /// Returns the NID corresponding to the cipher. - /// - /// This corresponds to [`SSL_CIPHER_get_cipher_nid`]. - /// - /// [`SSL_CIPHER_get_cipher_nid`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CIPHER_get_cipher_nid.html + #[corresponds(SSL_CIPHER_get_cipher_nid)] pub fn cipher_nid(&self) -> Option { let n = unsafe { ffi::SSL_CIPHER_get_cipher_nid(self.as_ptr()) }; if n == 0 { @@ -2562,10 +2340,7 @@ impl ToOwned for SslSessionRef { impl SslSessionRef { /// Returns the SSL session ID. - /// - /// This corresponds to [`SSL_SESSION_get_id`]. - /// - /// [`SSL_SESSION_get_id`]: https://www.openssl.org/docs/manmaster/man3/SSL_SESSION_get_id.html + #[corresponds(SSL_SESSION_get_id)] pub fn id(&self) -> &[u8] { unsafe { let mut len = 0; @@ -2575,10 +2350,7 @@ impl SslSessionRef { } /// Returns the length of the master key. - /// - /// This corresponds to [`SSL_SESSION_get_master_key`]. - /// - /// [`SSL_SESSION_get_master_key`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_SESSION_get_master_key.html + #[corresponds(SSL_SESSION_get_master_key)] pub fn master_key_len(&self) -> usize { unsafe { SSL_SESSION_get_master_key(self.as_ptr(), ptr::null_mut(), 0) } } @@ -2586,19 +2358,13 @@ impl SslSessionRef { /// Copies the master key into the provided buffer. /// /// Returns the number of bytes written, or the size of the master key if the buffer is empty. - /// - /// This corresponds to [`SSL_SESSION_get_master_key`]. - /// - /// [`SSL_SESSION_get_master_key`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_SESSION_get_master_key.html + #[corresponds(SSL_SESSION_get_master_key)] pub fn master_key(&self, buf: &mut [u8]) -> usize { unsafe { SSL_SESSION_get_master_key(self.as_ptr(), buf.as_mut_ptr(), buf.len()) } } /// Returns the time at which the session was established, in seconds since the Unix epoch. - /// - /// This corresponds to [`SSL_SESSION_get_time`]. - /// - /// [`SSL_SESSION_get_time`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_SESSION_get_time.html + #[corresponds(SSL_SESSION_get_time)] #[allow(clippy::useless_conversion)] pub fn time(&self) -> u64 { unsafe { ffi::SSL_SESSION_get_time(self.as_ptr()) } @@ -2607,20 +2373,14 @@ impl SslSessionRef { /// Returns the sessions timeout, in seconds. /// /// A session older than this time should not be used for session resumption. - /// - /// This corresponds to [`SSL_SESSION_get_timeout`]. - /// - /// [`SSL_SESSION_get_timeout`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_SESSION_get_time.html + #[corresponds(SSL_SESSION_get_timeout)] #[allow(clippy::useless_conversion)] pub fn timeout(&self) -> u32 { unsafe { ffi::SSL_SESSION_get_timeout(self.as_ptr()) } } /// Returns the session's TLS protocol version. - /// - /// This corresponds to [`SSL_SESSION_get_protocol_version`]. - /// - /// [`SSL_SESSION_get_protocol_version`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_SESSION_get_protocol_version.html + #[corresponds(SSL_SESSION_get_protocol_version)] pub fn protocol_version(&self) -> SslVersion { unsafe { let version = ffi::SSL_SESSION_get_protocol_version(self.as_ptr()); @@ -2663,10 +2423,7 @@ impl Ssl { /// /// Each invocation of this function is guaranteed to return a distinct index. These can be used /// to store data in the context that can be retrieved later by callbacks, for example. - /// - /// This corresponds to [`SSL_get_ex_new_index`]. - /// - /// [`SSL_get_ex_new_index`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_get_ex_new_index.html + #[corresponds(SSL_get_ex_new_index)] pub fn new_ex_index() -> Result, ErrorStack> where T: 'static + Sync + Send, @@ -2695,10 +2452,8 @@ impl Ssl { /// Creates a new `Ssl`. /// - /// This corresponds to [`SSL_new`]. - /// - /// [`SSL_new`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_new.html // FIXME should take &SslContextRef + #[corresponds(SSL_new)] pub fn new(ctx: &SslContext) -> Result { unsafe { let ptr = cvt_p(ffi::SSL_new(ctx.as_ptr()))?; @@ -2711,10 +2466,9 @@ impl Ssl { /// Creates a new [`Ssl`]. /// - /// This corresponds to [`SSL_new`](`ffi::SSL_new`). - /// /// This function does the same as [`Self:new`] except that it takes &[SslContextRef]. // Both functions exist for backward compatibility (no breaking API). + #[corresponds(SSL_new)] pub fn new_from_ref(ctx: &SslContextRef) -> Result { unsafe { let ptr = cvt_p(ffi::SSL_new(ctx.as_ptr()))?; @@ -2836,6 +2590,7 @@ impl SslRef { unsafe { ffi::SSL_get_rbio(self.as_ptr()) } } + #[corresponds(SSL_set1_curves_list)] #[cfg(feature = "kx-safe-default")] fn set_curves_list(&mut self, curves: &str) -> Result<(), ErrorStack> { let curves = CString::new(curves).unwrap(); @@ -2881,10 +2636,7 @@ impl SslRef { } /// Returns the [`SslCurve`] used for this `SslRef`. - /// - /// This corresponds to [`SSL_get_curve_id`] - /// - /// [`SSL_get_curve_id`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_get_curve_id + #[corresponds(SSL_get_curve_id)] pub fn curve(&self) -> Option { let curve_id = unsafe { ffi::SSL_get_curve_id(self.as_ptr()) }; if curve_id == 0 { @@ -2894,20 +2646,15 @@ impl SslRef { } /// Returns an `ErrorCode` value for the most recent operation on this `SslRef`. - /// - /// This corresponds to [`SSL_get_error`]. - /// - /// [`SSL_get_error`]: https://github.com/google/boringssl/blob/master/include/openssl/ssl.h#L475 + #[corresponds(SSL_get_error)] pub fn error_code(&self, ret: c_int) -> ErrorCode { unsafe { ErrorCode::from_raw(ffi::SSL_get_error(self.as_ptr(), ret)) } } /// Like [`SslContextBuilder::set_verify`]. /// - /// This corresponds to [`SSL_set_verify`]. - /// /// [`SslContextBuilder::set_verify`]: struct.SslContextBuilder.html#method.set_verify - /// [`SSL_set_verify`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_verify.html + #[corresponds(SSL_set_verify)] pub fn set_verify(&mut self, mode: SslVerifyMode) { #[cfg(feature = "rpk")] assert!( @@ -2921,10 +2668,7 @@ impl SslRef { /// Sets the certificate verification depth. /// /// If the peer's certificate chain is longer than this value, verification will fail. - /// - /// This corresponds to [`SSL_set_verify_depth`]. - /// - /// [`SSL_set_verify_depth`]: https://docs.openssl.org/1.1.1/man3/SSL_CTX_set_verify/ + #[corresponds(SSL_set_verify_depth)] pub fn set_verify_depth(&mut self, depth: u32) { #[cfg(feature = "rpk")] assert!( @@ -2938,10 +2682,7 @@ impl SslRef { } /// Returns the verify mode that was set using `set_verify`. - /// - /// This corresponds to [`SSL_get_verify_mode`]. - /// - /// [`SSL_get_verify_mode`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_get_verify_mode.html + #[corresponds(SSL_get_verify_mode)] pub fn verify_mode(&self) -> SslVerifyMode { #[cfg(feature = "rpk")] assert!( @@ -2966,13 +2707,10 @@ impl SslRef { /// call [`X509StoreContextRef::verify_cert`] and inspect the result, or perform /// other operations more straightforwardly. /// - /// This corresponds to [`SSL_set_verify`]. - /// /// # Panics /// /// This method panics if this `Ssl` is associated with a RPK context. - /// - /// [`SSL_set_verify`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_verify.html + #[corresponds(SSL_set_verify)] pub fn set_verify_callback(&mut self, mode: SslVerifyMode, callback: F) where F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send, @@ -2995,10 +2733,7 @@ impl SslRef { } /// Sets a custom certificate store for verifying peer certificates. - /// - /// This corresponds to [`SSL_CTX_set0_verify_cert_store`]. - /// - /// [`SSL_set0_verify_cert_store`]: https://docs.openssl.org/1.0.2/man3/SSL_CTX_set1_verify_cert_store/ + #[corresponds(SSL_set0_verify_cert_store)] pub fn set_verify_cert_store(&mut self, cert_store: X509Store) -> Result<(), ErrorStack> { #[cfg(feature = "rpk")] assert!( @@ -3015,11 +2750,10 @@ impl SslRef { /// Like [`SslContextBuilder::set_custom_verify_callback`]. /// - /// This corresponds to [`SSL_set_custom_verify`]. - /// /// # Panics /// /// This method panics if this `Ssl` is associated with a RPK context. + #[corresponds(SSL_set_custom_verify)] pub fn set_custom_verify_callback(&mut self, mode: SslVerifyMode, callback: F) where F: Fn(&mut SslRef) -> Result<(), SslVerifyError> + 'static + Sync + Send, @@ -3043,19 +2777,16 @@ impl SslRef { /// Like [`SslContextBuilder::set_tmp_dh`]. /// - /// This corresponds to [`SSL_set_tmp_dh`]. - /// /// [`SslContextBuilder::set_tmp_dh`]: struct.SslContextBuilder.html#method.set_tmp_dh - /// [`SSL_set_tmp_dh`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_tmp_dh.html + #[corresponds(SSL_set_tmp_dh)] pub fn set_tmp_dh(&mut self, dh: &DhRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_set_tmp_dh(self.as_ptr(), dh.as_ptr()) as c_int).map(|_| ()) } } /// Like [`SslContextBuilder::set_tmp_ecdh`]. /// - /// This corresponds to `SSL_set_tmp_ecdh`. - /// /// [`SslContextBuilder::set_tmp_ecdh`]: struct.SslContextBuilder.html#method.set_tmp_ecdh + #[corresponds(SSL_set_tmp_ecdh)] pub fn set_tmp_ecdh(&mut self, key: &EcKeyRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_set_tmp_ecdh(self.as_ptr(), key.as_ptr()) as c_int).map(|_| ()) } } @@ -3076,10 +2807,8 @@ impl SslRef { /// Like [`SslContextBuilder::set_alpn_protos`]. /// - /// This corresponds to [`SSL_set_alpn_protos`]. - /// /// [`SslContextBuilder::set_alpn_protos`]: struct.SslContextBuilder.html#method.set_alpn_protos - /// [`SSL_set_alpn_protos`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_alpn_protos.html + #[corresponds(SSL_set_alpn_protos)] pub fn set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> { unsafe { #[cfg_attr(not(feature = "fips"), allow(clippy::unnecessary_cast))] @@ -3101,10 +2830,7 @@ impl SslRef { } /// Returns the stack of available SslCiphers for `SSL`, sorted by preference. - /// - /// This corresponds to [`SSL_get_ciphers`]. - /// - /// [`SSL_get_ciphers`]: https://www.openssl.org/docs/man1.0.2/man3/SSL_get_ciphers.html + #[corresponds(SSL_get_ciphers)] pub fn ciphers(&self) -> &StackRef { unsafe { let cipher_list = ffi::SSL_get_ciphers(self.as_ptr()); @@ -3113,10 +2839,7 @@ impl SslRef { } /// Returns the current cipher if the session is active. - /// - /// This corresponds to [`SSL_get_current_cipher`]. - /// - /// [`SSL_get_current_cipher`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_current_cipher.html + #[corresponds(SSL_get_current_cipher)] pub fn current_cipher(&self) -> Option<&SslCipherRef> { unsafe { let ptr = ffi::SSL_get_current_cipher(self.as_ptr()); @@ -3130,10 +2853,7 @@ impl SslRef { } /// Returns a short string describing the state of the session. - /// - /// This corresponds to [`SSL_state_string`]. - /// - /// [`SSL_state_string`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_state_string.html + #[corresponds(SSL_state_string)] pub fn state_string(&self) -> &'static str { let state = unsafe { let ptr = ffi::SSL_state_string(self.as_ptr()); @@ -3144,10 +2864,7 @@ impl SslRef { } /// Returns a longer string describing the state of the session. - /// - /// This corresponds to [`SSL_state_string_long`]. - /// - /// [`SSL_state_string_long`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_state_string_long.html + #[corresponds(SSL_state_string_long)] pub fn state_string_long(&self) -> &'static str { let state = unsafe { let ptr = ffi::SSL_state_string_long(self.as_ptr()); @@ -3160,10 +2877,7 @@ impl SslRef { /// Sets the host name to be sent to the server for Server Name Indication (SNI). /// /// It has no effect for a server-side connection. - /// - /// This corresponds to [`SSL_set_tlsext_host_name`]. - /// - /// [`SSL_set_tlsext_host_name`]: https://www.openssl.org/docs/manmaster/man3/SSL_get_servername_type.html + #[corresponds(SSL_set_tlsext_host_name)] pub fn set_hostname(&mut self, hostname: &str) -> Result<(), ErrorStack> { let cstr = CString::new(hostname).unwrap(); unsafe { @@ -3173,10 +2887,7 @@ impl SslRef { } /// Returns the peer's certificate, if present. - /// - /// This corresponds to [`SSL_get_peer_certificate`]. - /// - /// [`SSL_get_peer_certificate`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_peer_certificate.html + #[corresponds(SSL_get_peer_certificate)] pub fn peer_certificate(&self) -> Option { #[cfg(feature = "rpk")] assert!( @@ -3198,10 +2909,7 @@ impl SslRef { /// /// On the client side, the chain includes the leaf certificate, but on the server side it does /// not. Fun! - /// - /// This corresponds to [`SSL_get_peer_cert_chain`]. - /// - /// [`SSL_get_peer_cert_chain`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_peer_cert_chain.html + #[corresponds(SSL_get_peer_certificate)] pub fn peer_cert_chain(&self) -> Option<&StackRef> { #[cfg(feature = "rpk")] assert!( @@ -3220,10 +2928,7 @@ impl SslRef { } /// Like [`SslContext::certificate`]. - /// - /// This corresponds to `SSL_get_certificate`. - /// - /// [`SslContext::certificate`]: struct.SslContext.html#method.certificate + #[corresponds(SSL_get_certificate)] pub fn certificate(&self) -> Option<&X509Ref> { #[cfg(feature = "rpk")] assert!( @@ -3242,10 +2947,7 @@ impl SslRef { } /// Like [`SslContext::private_key`]. - /// - /// This corresponds to `SSL_get_privatekey`. - /// - /// [`SslContext::private_key`]: struct.SslContext.html#method.private_key + #[corresponds(SSL_get_privatekey)] pub fn private_key(&self) -> Option<&PKeyRef> { unsafe { let ptr = ffi::SSL_get_privatekey(self.as_ptr()); @@ -3263,10 +2965,7 @@ impl SslRef { } /// Returns the protocol version of the session. - /// - /// This corresponds to [`SSL_version`]. - /// - /// [`SSL_version`]: https://www.openssl.org/docs/manmaster/man3/SSL_version.html + #[corresponds(SSL_version)] pub fn version2(&self) -> Option { unsafe { let r = ffi::SSL_version(self.as_ptr()); @@ -3279,10 +2978,7 @@ impl SslRef { } /// Returns a string describing the protocol version of the session. - /// - /// This corresponds to [`SSL_get_version`]. - /// - /// [`SSL_get_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_version.html + #[corresponds(SSL_get_version)] pub fn version_str(&self) -> &'static str { let version = unsafe { let ptr = ffi::SSL_get_version(self.as_ptr()); @@ -3296,10 +2992,7 @@ impl SslRef { /// /// If version is `None`, the default minimum version is used. For BoringSSL this defaults to /// TLS 1.0. - /// - /// This corresponds to [`SSL_set_min_proto_version`]. - /// - /// [`SSL_set_min_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html + #[corresponds(SSL_set_min_proto_version)] pub fn set_min_proto_version(&mut self, version: Option) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_set_min_proto_version( @@ -3313,10 +3006,7 @@ impl SslRef { /// Sets the maximum supported protocol version. /// /// If version is `None`, the default maximum version is used. For BoringSSL this is TLS 1.3. - /// - /// This corresponds to [`SSL_set_max_proto_version`]. - /// - /// [`SSL_set_max_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_max_proto_version.html + #[corresponds(SSL_set_max_proto_version)] pub fn set_max_proto_version(&mut self, version: Option) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_set_max_proto_version( @@ -3328,10 +3018,7 @@ impl SslRef { } /// Gets the minimum supported protocol version. - /// - /// This corresponds to [`SSL_get_min_proto_version`]. - /// - /// [`SSL_get_min_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html + #[corresponds(SSL_get_min_proto_version)] pub fn min_proto_version(&mut self) -> Option { unsafe { let r = ffi::SSL_get_min_proto_version(self.as_ptr()); @@ -3344,10 +3031,7 @@ impl SslRef { } /// Gets the maximum supported protocol version. - /// - /// This corresponds to [`SSL_get_max_proto_version`]. - /// - /// [`SSL_get_max_proto_version`]: https://www.openssl.org/docs/man3.1/man3/SSL_get_max_proto_version.html + #[corresponds(SSL_get_max_proto_version)] pub fn max_proto_version(&self) -> Option { let r = unsafe { ffi::SSL_get_max_proto_version(self.as_ptr()) }; if r == 0 { @@ -3361,10 +3045,7 @@ impl SslRef { /// /// The protocol's name is returned is an opaque sequence of bytes. It is up to the client /// to interpret it. - /// - /// This corresponds to [`SSL_get0_alpn_selected`]. - /// - /// [`SSL_get0_alpn_selected`]: https://www.openssl.org/docs/manmaster/man3/SSL_get0_next_proto_negotiated.html + #[corresponds(SSL_get0_alpn_selected)] pub fn selected_alpn_protocol(&self) -> Option<&[u8]> { unsafe { let mut data: *const c_uchar = ptr::null(); @@ -3382,10 +3063,7 @@ impl SslRef { } /// Enables the DTLS extension "use_srtp" as defined in RFC5764. - /// - /// This corresponds to [`SSL_set_tlsext_use_srtp`]. - /// - /// [`SSL_set_tlsext_use_srtp`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_tlsext_use_srtp.html + #[corresponds(SSL_set_tlsext_use_srtp)] pub fn set_tlsext_use_srtp(&mut self, protocols: &str) -> Result<(), ErrorStack> { unsafe { let cstr = CString::new(protocols).unwrap(); @@ -3403,10 +3081,7 @@ impl SslRef { /// Gets all SRTP profiles that are enabled for handshake via set_tlsext_use_srtp /// /// DTLS extension "use_srtp" as defined in RFC5764 has to be enabled. - /// - /// This corresponds to [`SSL_get_srtp_profiles`]. - /// - /// [`SSL_get_srtp_profiles`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_tlsext_use_srtp.html + #[corresponds(SSL_get_strp_profiles)] pub fn srtp_profiles(&self) -> Option<&StackRef> { unsafe { let chain = ffi::SSL_get_srtp_profiles(self.as_ptr()); @@ -3422,10 +3097,7 @@ impl SslRef { /// Gets the SRTP profile selected by handshake. /// /// DTLS extension "use_srtp" as defined in RFC5764 has to be enabled. - /// - /// This corresponds to [`SSL_get_selected_srtp_profile`]. - /// - /// [`SSL_get_selected_srtp_profile`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_tlsext_use_srtp.html + #[corresponds(SSL_get_selected_srtp_profile)] pub fn selected_srtp_profile(&self) -> Option<&SrtpProtectionProfileRef> { unsafe { let profile = ffi::SSL_get_selected_srtp_profile(self.as_ptr()); @@ -3442,10 +3114,7 @@ impl SslRef { /// /// If this is greater than 0, the next call to `read` will not call down to the underlying /// stream. - /// - /// This corresponds to [`SSL_pending`]. - /// - /// [`SSL_pending`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_pending.html + #[corresponds(SSL_pending)] pub fn pending(&self) -> usize { unsafe { ffi::SSL_pending(self.as_ptr()) as usize } } @@ -3454,8 +3123,6 @@ impl SslRef { /// /// It is only useful on the server side. /// - /// This corresponds to [`SSL_get_servername`]. - /// /// # Note /// /// While the SNI specification requires that servernames be valid domain names (and therefore @@ -3463,8 +3130,8 @@ impl SslRef { /// is not valid UTF-8, this function will return `None`. The `servername_raw` method returns /// the raw bytes and does not have this restriction. /// - /// [`SSL_get_servername`]: https://www.openssl.org/docs/manmaster/man3/SSL_get_servername.html // FIXME maybe rethink in 0.11? + #[corresponds(SSL_get_servername)] pub fn servername(&self, type_: NameType) -> Option<&str> { self.servername_raw(type_) .and_then(|b| str::from_utf8(b).ok()) @@ -3474,13 +3141,10 @@ impl SslRef { /// /// It is only useful on the server side. /// - /// This corresponds to [`SSL_get_servername`]. - /// /// # Note /// /// Unlike `servername`, this method does not require the name be valid UTF-8. - /// - /// [`SSL_get_servername`]: https://www.openssl.org/docs/manmaster/man3/SSL_get_servername.html + #[corresponds(SSL_get_servername)] pub fn servername_raw(&self, type_: NameType) -> Option<&[u8]> { unsafe { let name = ffi::SSL_get_servername(self.as_ptr(), type_.0); @@ -3495,17 +3159,13 @@ impl SslRef { /// Changes the context corresponding to the current connection. /// /// It is most commonly used in the Server Name Indication (SNI) callback. - /// - /// This corresponds to `SSL_set_SSL_CTX`. + #[corresponds(SSL_set_SSL_CTX)] pub fn set_ssl_context(&mut self, ctx: &SslContextRef) -> Result<(), ErrorStack> { unsafe { cvt_p(ffi::SSL_set_SSL_CTX(self.as_ptr(), ctx.as_ptr())).map(|_| ()) } } /// Returns the context corresponding to the current connection. - /// - /// This corresponds to [`SSL_get_SSL_CTX`]. - /// - /// [`SSL_get_SSL_CTX`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_get_SSL_CTX.html + #[corresponds(SSL_get_SSL_CTX)] pub fn ssl_context(&self) -> &SslContextRef { unsafe { let ssl_ctx = ffi::SSL_get_SSL_CTX(self.as_ptr()); @@ -3514,10 +3174,7 @@ impl SslRef { } /// Returns a mutable reference to the X509 verification configuration. - /// - /// This corresponds to [`SSL_get0_param`]. - /// - /// [`SSL_get0_param`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_get0_param.html + #[corresponds(SSL_get0_param)] pub fn verify_param_mut(&mut self) -> &mut X509VerifyParamRef { #[cfg(feature = "rpk")] assert!( @@ -3534,10 +3191,7 @@ impl SslRef { } /// Returns the certificate verification result. - /// - /// This corresponds to [`SSL_get_verify_result`]. - /// - /// [`SSL_get_verify_result`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_get_verify_result.html + #[corresponds(SSL_get_verify_result)] pub fn verify_result(&self) -> X509VerifyResult { #[cfg(feature = "rpk")] assert!( @@ -3549,10 +3203,7 @@ impl SslRef { } /// Returns a shared reference to the SSL session. - /// - /// This corresponds to [`SSL_get_session`]. - /// - /// [`SSL_get_session`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_session.html + #[corresponds(SSL_get_session)] pub fn session(&self) -> Option<&SslSessionRef> { unsafe { let p = ffi::SSL_get_session(self.as_ptr()); @@ -3568,10 +3219,7 @@ impl SslRef { /// /// Returns the number of bytes copied, or if the buffer is empty, the size of the client_random /// value. - /// - /// This corresponds to [`SSL_get_client_random`]. - /// - /// [`SSL_get_client_random`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_client_random.html + #[corresponds(SSL_get_client_random)] pub fn client_random(&self, buf: &mut [u8]) -> usize { unsafe { ffi::SSL_get_client_random(self.as_ptr(), buf.as_mut_ptr() as *mut c_uchar, buf.len()) @@ -3582,10 +3230,7 @@ impl SslRef { /// /// Returns the number of bytes copied, or if the buffer is empty, the size of the server_random /// value. - /// - /// This corresponds to [`SSL_get_server_random`]. - /// - /// [`SSL_get_server_random`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_client_random.html + #[corresponds(SSL_get_server_random)] pub fn server_random(&self, buf: &mut [u8]) -> usize { unsafe { ffi::SSL_get_server_random(self.as_ptr(), buf.as_mut_ptr() as *mut c_uchar, buf.len()) @@ -3593,10 +3238,7 @@ impl SslRef { } /// Derives keying material for application use in accordance to RFC 5705. - /// - /// This corresponds to [`SSL_export_keying_material`]. - /// - /// [`SSL_export_keying_material`]: https://www.openssl.org/docs/manmaster/man3/SSL_export_keying_material.html + #[corresponds(SSL_export_keying_material)] pub fn export_keying_material( &self, out: &mut [u8], @@ -3628,32 +3270,23 @@ impl SslRef { /// session. If the server is not willing to reuse the session, a new one will be transparently /// negotiated. /// - /// This corresponds to [`SSL_set_session`]. - /// /// # Safety /// /// The caller of this method is responsible for ensuring that the session is associated /// with the same `SslContext` as this `Ssl`. - /// - /// [`SSL_set_session`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_session.html + #[corresponds(SSL_set_session)] pub unsafe fn set_session(&mut self, session: &SslSessionRef) -> Result<(), ErrorStack> { cvt(ffi::SSL_set_session(self.as_ptr(), session.as_ptr())).map(|_| ()) } /// Determines if the session provided to `set_session` was successfully reused. - /// - /// This corresponds to [`SSL_session_reused`]. - /// - /// [`SSL_session_reused`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_session_reused.html + #[corresponds(SSL_session_reused)] pub fn session_reused(&self) -> bool { unsafe { ffi::SSL_session_reused(self.as_ptr()) != 0 } } /// Sets the status response a client wishes the server to reply with. - /// - /// This corresponds to [`SSL_set_tlsext_status_type`]. - /// - /// [`SSL_set_tlsext_status_type`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_tlsext_status_type.html + #[corresponds(SSL_set_tlsext_status_type)] pub fn set_status_type(&mut self, type_: StatusType) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_set_tlsext_status_type(self.as_ptr(), type_.as_raw()) as c_int).map(|_| ()) @@ -3661,10 +3294,7 @@ impl SslRef { } /// Returns the server's OCSP response, if present. - /// - /// This corresponds to [`SSL_get_tlsext_status_ocsp_resp`]. - /// - /// [`SSL_get_tlsext_status_ocsp_resp`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_tlsext_status_type.html + #[corresponds(SSL_get_tlsext_status_ocsp_resp)] pub fn ocsp_status(&self) -> Option<&[u8]> { unsafe { let mut p = ptr::null(); @@ -3679,10 +3309,7 @@ impl SslRef { } /// Sets the OCSP response to be returned to the client. - /// - /// This corresponds to [`SSL_set_tlsext_status_ocsp_resp`]. - /// - /// [`SSL_set_tlsext_status_ocsp_resp`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_tlsext_status_type.html + #[corresponds(SSL_set_tlsext_status_ocsp_resp)] pub fn set_ocsp_status(&mut self, response: &[u8]) -> Result<(), ErrorStack> { unsafe { assert!(response.len() <= c_int::MAX as usize); @@ -3698,10 +3325,7 @@ impl SslRef { } /// Determines if this `Ssl` is configured for server-side or client-side use. - /// - /// This corresponds to [`SSL_is_server`]. - /// - /// [`SSL_is_server`]: https://www.openssl.org/docs/manmaster/man3/SSL_is_server.html + #[corresponds(SSL_is_server)] pub fn is_server(&self) -> bool { unsafe { SSL_is_server(self.as_ptr()) != 0 } } @@ -3711,12 +3335,9 @@ impl SslRef { /// This can be used to provide data to callbacks registered with the context. Use the /// `Ssl::new_ex_index` method to create an `Index`. /// - /// This corresponds to [`SSL_set_ex_data`]. - /// /// Note that if this method is called multiple times with the same index, any previous /// value stored in the `SslContextBuilder` will be leaked. - /// - /// [`SSL_set_ex_data`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_ex_data.html + #[corresponds(SSL_set_ex_data)] pub fn set_ex_data(&mut self, index: Index, data: T) { if let Some(old) = self.ex_data_mut(index) { *old = data; @@ -3739,11 +3360,8 @@ impl SslRef { /// This can be used to provide data to callbacks registered with the context. Use the /// `Ssl::new_ex_index` method to create an `Index`. /// - /// This corresponds to [`SSL_set_ex_data`]. - /// /// Any previous value will be dropped and replaced by the new one. - /// - /// [`SSL_set_ex_data`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_ex_data.html + #[corresponds(SSL_set_ex_data)] pub fn replace_ex_data(&mut self, index: Index, data: T) -> Option { if let Some(old) = self.ex_data_mut(index) { return Some(mem::replace(old, data)); @@ -3755,10 +3373,7 @@ impl SslRef { } /// Returns a reference to the extra data at the specified index. - /// - /// This corresponds to [`SSL_get_ex_data`]. - /// - /// [`SSL_get_ex_data`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_ex_data.html + #[corresponds(SSL_get_ex_data)] pub fn ex_data(&self, index: Index) -> Option<&T> { unsafe { let data = ffi::SSL_get_ex_data(self.as_ptr(), index.as_raw()); @@ -3771,10 +3386,7 @@ impl SslRef { } /// Returns a mutable reference to the extra data at the specified index. - /// - /// This corresponds to [`SSL_get_ex_data`]. - /// - /// [`SSL_get_ex_data`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_ex_data.html + #[corresponds(SSL_get_ex_data)] pub fn ex_data_mut(&mut self, index: Index) -> Option<&mut T> { unsafe { let data = ffi::SSL_get_ex_data(self.as_ptr(), index.as_raw()); @@ -3790,8 +3402,7 @@ impl SslRef { /// /// The total size of the message is returned, so this can be used to determine the size of the /// buffer required. - /// - /// This corresponds to `SSL_get_finished`. + #[corresponds(SSL_get_finished)] pub fn finished(&self, buf: &mut [u8]) -> usize { unsafe { ffi::SSL_get_finished(self.as_ptr(), buf.as_mut_ptr() as *mut c_void, buf.len()) } } @@ -3801,8 +3412,7 @@ impl SslRef { /// /// The total size of the message is returned, so this can be used to determine the size of the /// buffer required. - /// - /// This corresponds to `SSL_get_peer_finished`. + #[corresponds(SSL_get_peer_finished)] pub fn peer_finished(&self, buf: &mut [u8]) -> usize { unsafe { ffi::SSL_get_peer_finished(self.as_ptr(), buf.as_mut_ptr() as *mut c_void, buf.len()) @@ -3810,26 +3420,19 @@ impl SslRef { } /// Determines if the initial handshake has been completed. - /// - /// This corresponds to [`SSL_is_init_finished`]. - /// - /// [`SSL_is_init_finished`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_is_init_finished.html + #[corresponds(SSL_is_init_finished)] pub fn is_init_finished(&self) -> bool { unsafe { ffi::SSL_is_init_finished(self.as_ptr()) != 0 } } /// Sets the MTU used for DTLS connections. - /// - /// This corresponds to `SSL_set_mtu`. + #[corresponds(SSL_set_mtu)] pub fn set_mtu(&mut self, mtu: u32) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_set_mtu(self.as_ptr(), mtu as c_uint) as c_int).map(|_| ()) } } /// Sets the certificate. - /// - /// This corresponds to [`SSL_use_certificate`]. - /// - /// [`SSL_use_certificate`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_use_certificate.html + #[corresponds(SSL_use_certificate)] pub fn set_certificate(&mut self, cert: &X509Ref) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_use_certificate(self.as_ptr(), cert.as_ptr()))?; @@ -3842,10 +3445,7 @@ impl SslRef { /// /// The CA certificates must still be added to the trust root - they are not automatically set /// as trusted by this method. - /// - /// This corresponds to [`SSL_set_client_CA_list`]. - /// - /// [`SSL_set_client_CA_list`]: https://docs.openssl.org/1.1.1/man3/SSL_CTX_set0_CA_list/ + #[corresponds(SSL_set_client_CA_list)] pub fn set_client_ca_list(&mut self, list: Stack) { #[cfg(feature = "rpk")] assert!( @@ -3858,10 +3458,7 @@ impl SslRef { } /// Sets the private key. - /// - /// This corresponds to [`SSL_use_PrivateKey`]. - /// - /// [`SSL_use_PrivateKey`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_use_PrivateKey.html + #[corresponds(SSL_use_PrivateKey)] pub fn set_private_key(&mut self, key: &PKeyRef) -> Result<(), ErrorStack> where T: HasPrivate, @@ -3871,10 +3468,7 @@ impl SslRef { /// Enables all modes set in `mode` in `SSL`. Returns a bitmask representing the resulting /// enabled modes. - /// - /// This corresponds to [`SSL_set_mode`]. - /// - /// [`SSL_set_mode`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_mode.html + #[corresponds(SSL_set_mode)] pub fn set_mode(&mut self, mode: SslMode) -> SslMode { let bits = unsafe { ffi::SSL_set_mode(self.as_ptr(), mode.bits()) }; SslMode::from_bits_retain(bits) @@ -3882,20 +3476,14 @@ impl SslRef { /// Disables all modes set in `mode` in `SSL`. Returns a bitmask representing the resulting /// enabled modes. - /// - /// This corresponds to [`SSL_clear_mode`]. - /// - /// [`SSL_clear_mode`]: https://www.openssl.org/docs/man3.1/man3/SSL_clear_mode.html + #[corresponds(SSL_clear_mode)] pub fn clear_mode(&mut self, mode: SslMode) -> SslMode { let bits = unsafe { ffi::SSL_clear_mode(self.as_ptr(), mode.bits()) }; SslMode::from_bits_retain(bits) } /// Appends `cert` to the chain associated with the current certificate of `SSL`. - /// - /// This corresponds to [`SSL_add1_chain_cert`]. - /// - /// [`SSL_add1_chain_cert`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_add1_chain_cert.html + #[corresponds(SSL_add1_chain_cert)] pub fn add_chain_cert(&mut self, cert: &X509Ref) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_add1_chain_cert(self.as_ptr(), cert.as_ptr())).map(|_| ()) } } @@ -4060,10 +3648,7 @@ impl SslStream { /// /// It is particularly useful with a nonblocking socket, where the error value will identify if /// OpenSSL is waiting on read or write readiness. - /// - /// This corresponds to [`SSL_read`]. - /// - /// [`SSL_read`]: https://www.openssl.org/docs/manmaster/man3/SSL_read.html + #[corresponds(SSL_read)] pub fn ssl_read(&mut self, buf: &mut [u8]) -> Result { // SAFETY: `ssl_read_uninit` does not de-initialize the buffer. unsafe { @@ -4098,10 +3683,7 @@ impl SslStream { /// /// It is particularly useful with a nonblocking socket, where the error value will identify if /// OpenSSL is waiting on read or write readiness. - /// - /// This corresponds to [`SSL_write`]. - /// - /// [`SSL_write`]: https://www.openssl.org/docs/manmaster/man3/SSL_write.html + #[corresponds(SSL_write)] pub fn ssl_write(&mut self, buf: &[u8]) -> Result { if buf.is_empty() { return Ok(0); @@ -4125,10 +3707,7 @@ impl SslStream { /// While the connection may be closed after the first step, it is recommended to fully shut the /// session down. In particular, it must be fully shut down if the connection is to be used for /// further communication in the future. - /// - /// This corresponds to [`SSL_shutdown`]. - /// - /// [`SSL_shutdown`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_shutdown.html + #[corresponds(SSL_shutdown)] pub fn shutdown(&mut self) -> Result { match unsafe { ffi::SSL_shutdown(self.ssl.as_ptr()) } { 0 => Ok(ShutdownResult::Sent), @@ -4138,10 +3717,7 @@ impl SslStream { } /// Returns the session's shutdown state. - /// - /// This corresponds to [`SSL_get_shutdown`]. - /// - /// [`SSL_get_shutdown`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_set_shutdown.html + #[corresponds(SSL_get_shutdown)] pub fn get_shutdown(&mut self) -> ShutdownState { unsafe { let bits = ffi::SSL_get_shutdown(self.ssl.as_ptr()); @@ -4153,19 +3729,13 @@ impl SslStream { /// /// This can be used to tell OpenSSL that the session should be cached even if a full two-way /// shutdown was not completed. - /// - /// This corresponds to [`SSL_set_shutdown`]. - /// - /// [`SSL_set_shutdown`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_set_shutdown.html + #[corresponds(SSL_set_shutdown)] pub fn set_shutdown(&mut self, state: ShutdownState) { unsafe { ffi::SSL_set_shutdown(self.ssl.as_ptr(), state.bits()) } } /// Initiates a client-side TLS handshake. - /// - /// This corresponds to [`SSL_connect`]. - /// - /// [`SSL_connect`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_connect.html + #[corresponds(SSL_connect)] pub fn connect(&mut self) -> Result<(), Error> { let ret = unsafe { ffi::SSL_connect(self.ssl.as_ptr()) }; if ret > 0 { @@ -4176,10 +3746,7 @@ impl SslStream { } /// Initiates a server-side TLS handshake. - /// - /// This corresponds to [`SSL_accept`]. - /// - /// [`SSL_accept`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_accept.html + #[corresponds(SSL_accept)] pub fn accept(&mut self) -> Result<(), Error> { let ret = unsafe { ffi::SSL_accept(self.ssl.as_ptr()) }; if ret > 0 { @@ -4190,10 +3757,7 @@ impl SslStream { } /// Initiates the handshake. - /// - /// This corresponds to [`SSL_do_handshake`]. - /// - /// [`SSL_do_handshake`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_do_handshake.html + #[corresponds(SSL_do_handshake)] pub fn do_handshake(&mut self) -> Result<(), Error> { let ret = unsafe { ffi::SSL_do_handshake(self.ssl.as_ptr()) }; if ret > 0 { @@ -4326,19 +3890,13 @@ where } /// Configure as an outgoing stream from a client. - /// - /// This corresponds to [`SSL_set_connect_state`]. - /// - /// [`SSL_set_connect_state`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_connect_state.html + #[corresponds(SSL_set_connect_state)] pub fn set_connect_state(&mut self) { unsafe { ffi::SSL_set_connect_state(self.inner.ssl.as_ptr()) } } /// Configure as an incoming stream to a server. - /// - /// This corresponds to [`SSL_set_accept_state`]. - /// - /// [`SSL_set_accept_state`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_accept_state.html + #[corresponds(SSL_set_accept_state)] pub fn set_accept_state(&mut self) { unsafe { ffi::SSL_set_accept_state(self.inner.ssl.as_ptr()) } } @@ -4408,10 +3966,7 @@ where /// Initiates the handshake. /// /// This will fail if `set_accept_state` or `set_connect_state` was not called first. - /// - /// This corresponds to [`SSL_do_handshake`]. - /// - /// [`SSL_do_handshake`]: https://www.openssl.org/docs/manmaster/man3/SSL_do_handshake.html + #[corresponds(SSL_do_handshake)] pub fn handshake(self) -> Result, HandshakeError> { let mut stream = self.inner; let ret = unsafe { ffi::SSL_do_handshake(stream.ssl.as_ptr()) }; diff --git a/boring/src/x509/mod.rs b/boring/src/x509/mod.rs index eb55c305..a90a4934 100644 --- a/boring/src/x509/mod.rs +++ b/boring/src/x509/mod.rs @@ -9,6 +9,7 @@ use foreign_types::{ForeignType, ForeignTypeRef}; use libc::{c_int, c_long, c_void}; +use openssl_macros::corresponds; use std::convert::TryInto; use std::error::Error; use std::ffi::{CStr, CString}; @@ -58,15 +59,13 @@ foreign_type_and_impl_send_sync! { impl X509StoreContext { /// Returns the index which can be used to obtain a reference to the `Ssl` associated with a /// context. + #[corresponds(SSL_get_ex_data_X509_STORE_CTX_idx)] pub fn ssl_idx() -> Result, ErrorStack> { unsafe { cvt_n(ffi::SSL_get_ex_data_X509_STORE_CTX_idx()).map(|idx| Index::from_raw(idx)) } } /// Creates a new `X509StoreContext` instance. - /// - /// This corresponds to [`X509_STORE_CTX_new`]. - /// - /// [`X509_STORE_CTX_new`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_STORE_CTX_new.html + #[corresponds(X509_STORE_CTX_new)] pub fn new() -> Result { unsafe { ffi::init(); @@ -77,10 +76,7 @@ impl X509StoreContext { impl X509StoreContextRef { /// Returns application data pertaining to an `X509` store context. - /// - /// This corresponds to [`X509_STORE_CTX_get_ex_data`]. - /// - /// [`X509_STORE_CTX_get_ex_data`]: https://www.openssl.org/docs/man1.0.2/crypto/X509_STORE_CTX_get_ex_data.html + #[corresponds(X509_STORE_CTX_get_ex_data)] pub fn ex_data(&self, index: Index) -> Option<&T> { unsafe { let data = ffi::X509_STORE_CTX_get_ex_data(self.as_ptr(), index.as_raw()); @@ -93,10 +89,7 @@ impl X509StoreContextRef { } /// Returns the verify result of the context. - /// - /// This corresponds to [`X509_STORE_CTX_get_error`]. - /// - /// [`X509_STORE_CTX_get_error`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_STORE_CTX_get_error.html + #[corresponds(X509_STORE_CTX_get_error)] pub fn verify_result(&self) -> X509VerifyResult { unsafe { X509VerifyError::from_raw(ffi::X509_STORE_CTX_get_error(self.as_ptr())) } } @@ -150,10 +143,7 @@ impl X509StoreContextRef { } /// Returns a mutable reference to the X509 verification configuration. - /// - /// This corresponds to [`X509_STORE_CTX_get0_param`]. - /// - /// [`SSL_get0_param`]: https://www.openssl.org/docs/manmaster/man3/X509_STORE_CTX_get0_param.html + #[corresponds(X509_STORE_CTX_get0_param)] pub fn verify_param_mut(&mut self) -> &mut X509VerifyParamRef { unsafe { X509VerifyParamRef::from_ptr_mut(ffi::X509_STORE_CTX_get0_param(self.as_ptr())) } } @@ -164,19 +154,13 @@ impl X509StoreContextRef { /// validation error if the certificate was not valid. /// /// This will only work inside of a call to `init`. - /// - /// This corresponds to [`X509_verify_cert`]. - /// - /// [`X509_verify_cert`]: https://www.openssl.org/docs/man1.0.2/crypto/X509_verify_cert.html + #[corresponds(X509_verify_cert)] pub fn verify_cert(&mut self) -> Result { unsafe { cvt_n(ffi::X509_verify_cert(self.as_ptr())).map(|n| n != 0) } } /// Set the verify result of the context. - /// - /// This corresponds to [`X509_STORE_CTX_set_error`]. - /// - /// [`X509_STORE_CTX_set_error`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_STORE_CTX_set_error.html + #[corresponds(X509_STORE_CTX_set_error)] pub fn set_error(&mut self, result: X509VerifyResult) { unsafe { ffi::X509_STORE_CTX_set_error( @@ -191,10 +175,7 @@ impl X509StoreContextRef { /// Returns a reference to the certificate which caused the error or None if /// no certificate is relevant to the error. - /// - /// This corresponds to [`X509_STORE_CTX_get_current_cert`]. - /// - /// [`X509_STORE_CTX_get_current_cert`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_STORE_CTX_get_current_cert.html + #[corresponds(X509_STORE_CTX_get_current_cert)] pub fn current_cert(&self) -> Option<&X509Ref> { unsafe { let ptr = ffi::X509_STORE_CTX_get_current_cert(self.as_ptr()); @@ -210,19 +191,13 @@ impl X509StoreContextRef { /// chain where the error occurred. If it is zero it occurred in the end /// entity certificate, one if it is the certificate which signed the end /// entity certificate and so on. - /// - /// This corresponds to [`X509_STORE_CTX_get_error_depth`]. - /// - /// [`X509_STORE_CTX_get_error_depth`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_STORE_CTX_get_error_depth.html + #[corresponds(X509_STORE_CTX_get_error_depth)] pub fn error_depth(&self) -> u32 { unsafe { ffi::X509_STORE_CTX_get_error_depth(self.as_ptr()) as u32 } } /// Returns a reference to a complete valid `X509` certificate chain. - /// - /// This corresponds to [`X509_STORE_CTX_get0_chain`]. - /// - /// [`X509_STORE_CTX_get0_chain`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_STORE_CTX_get0_chain.html + #[corresponds(X509_STORE_CTX_get0_chain)] pub fn chain(&self) -> Option<&StackRef> { unsafe { let chain = X509_STORE_CTX_get0_chain(self.as_ptr()); @@ -241,6 +216,7 @@ pub struct X509Builder(X509); impl X509Builder { /// Creates a new builder. + #[corresponds(X509_new)] pub fn new() -> Result { unsafe { ffi::init(); @@ -249,11 +225,13 @@ impl X509Builder { } /// Sets the notAfter constraint on the certificate. + #[corresponds(X509_set1_notAfter)] pub fn set_not_after(&mut self, not_after: &Asn1TimeRef) -> Result<(), ErrorStack> { unsafe { cvt(X509_set1_notAfter(self.0.as_ptr(), not_after.as_ptr())).map(|_| ()) } } /// Sets the notBefore constraint on the certificate. + #[corresponds(X509_set1_notBefore)] pub fn set_not_before(&mut self, not_before: &Asn1TimeRef) -> Result<(), ErrorStack> { unsafe { cvt(X509_set1_notBefore(self.0.as_ptr(), not_before.as_ptr())).map(|_| ()) } } @@ -262,11 +240,13 @@ impl X509Builder { /// /// Note that the version is zero-indexed; that is, a certificate corresponding to version 3 of /// the X.509 standard should pass `2` to this method. + #[corresponds(X509_set_version)] pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> { unsafe { cvt(ffi::X509_set_version(self.0.as_ptr(), version.into())).map(|_| ()) } } /// Sets the serial number of the certificate. + #[corresponds(X509_set_serialNumber)] pub fn set_serial_number(&mut self, serial_number: &Asn1IntegerRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::X509_set_serialNumber( @@ -278,6 +258,7 @@ impl X509Builder { } /// Sets the issuer name of the certificate. + #[corresponds(X509_set_issuer_name)] pub fn set_issuer_name(&mut self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::X509_set_issuer_name( @@ -306,6 +287,7 @@ impl X509Builder { /// let mut x509 = boring::x509::X509::builder().unwrap(); /// x509.set_subject_name(&x509_name).unwrap(); /// ``` + #[corresponds(X509_set_subject_name)] pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::X509_set_subject_name( @@ -317,6 +299,7 @@ impl X509Builder { } /// Sets the public key associated with the certificate. + #[corresponds(X509_set_pubkey)] pub fn set_pubkey(&mut self, key: &PKeyRef) -> Result<(), ErrorStack> where T: HasPublic, @@ -327,6 +310,7 @@ impl X509Builder { /// Returns a context object which is needed to create certain X509 extension values. /// /// Set `issuer` to `None` if the certificate will be self-signed. + #[corresponds(X509V3_set_ctx)] pub fn x509v3_context<'a>( &'a self, issuer: Option<&'a X509Ref>, @@ -366,10 +350,7 @@ impl X509Builder { } /// Adds an X509 extension value to the certificate. - /// - /// This corresponds to [`X509_add_ext`]. - /// - /// [`X509_add_ext`]: https://www.openssl.org/docs/man1.1.0/man3/X509_get_ext.html + #[corresponds(X509_add_ext)] pub fn append_extension2(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::X509_add_ext(self.0.as_ptr(), extension.as_ptr(), -1))?; @@ -378,6 +359,7 @@ impl X509Builder { } /// Signs the certificate with a private key. + #[corresponds(X509_sign)] pub fn sign(&mut self, key: &PKeyRef, hash: MessageDigest) -> Result<(), ErrorStack> where T: HasPrivate, @@ -401,10 +383,7 @@ foreign_type_and_impl_send_sync! { impl X509Ref { /// Returns this certificate's subject name. - /// - /// This corresponds to [`X509_get_subject_name`]. - /// - /// [`X509_get_subject_name`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_get_subject_name.html + #[corresponds(X509_get_subject_name)] pub fn subject_name(&self) -> &X509NameRef { unsafe { let name = ffi::X509_get_subject_name(self.as_ptr()); @@ -413,17 +392,13 @@ impl X509Ref { } /// Returns the hash of the certificates subject - /// - /// This corresponds to `X509_subject_name_hash`. + #[corresponds(X509_subject_name_hash)] pub fn subject_name_hash(&self) -> u32 { unsafe { ffi::X509_subject_name_hash(self.as_ptr()) as u32 } } /// Returns this certificate's subject alternative name entries, if they exist. - /// - /// This corresponds to [`X509_get_ext_d2i`] called with `NID_subject_alt_name`. - /// - /// [`X509_get_ext_d2i`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_get_ext_d2i.html + #[corresponds(X509_get_ext_d2i)] pub fn subject_alt_names(&self) -> Option> { unsafe { let stack = ffi::X509_get_ext_d2i( @@ -450,10 +425,7 @@ impl X509Ref { } /// Returns this certificate's issuer alternative name entries, if they exist. - /// - /// This corresponds to [`X509_get_ext_d2i`] called with `NID_issuer_alt_name`. - /// - /// [`X509_get_ext_d2i`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_get_ext_d2i.html + #[corresponds(X509_get_ext_d2i)] pub fn issuer_alt_names(&self) -> Option> { unsafe { let stack = ffi::X509_get_ext_d2i( @@ -471,6 +443,7 @@ impl X509Ref { } /// Returns this certificate's subject key id, if it exists. + #[corresponds(X509_get0_subject_key_id)] pub fn subject_key_id(&self) -> Option<&Asn1StringRef> { unsafe { let data = ffi::X509_get0_subject_key_id(self.as_ptr()); @@ -479,6 +452,7 @@ impl X509Ref { } /// Returns this certificate's authority key id, if it exists. + #[corresponds(X509_get0_authority_key_id)] pub fn authority_key_id(&self) -> Option<&Asn1StringRef> { unsafe { let data = ffi::X509_get0_authority_key_id(self.as_ptr()); From 96981dd6c6d73a7274a2f5efa924419972f2e286 Mon Sep 17 00:00:00 2001 From: Rushil Mehra Date: Tue, 13 Aug 2024 17:35:43 -0700 Subject: [PATCH 08/41] Expose X509NameRef::print_ex --- boring/src/x509/mod.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/boring/src/x509/mod.rs b/boring/src/x509/mod.rs index a90a4934..b234bd2b 100644 --- a/boring/src/x509/mod.rs +++ b/boring/src/x509/mod.rs @@ -26,7 +26,7 @@ use crate::asn1::{ Asn1BitStringRef, Asn1IntegerRef, Asn1Object, Asn1ObjectRef, Asn1StringRef, Asn1TimeRef, Asn1Type, }; -use crate::bio::MemBioSlice; +use crate::bio::{MemBio, MemBioSlice}; use crate::conf::ConfRef; use crate::error::ErrorStack; use crate::ex_data::Index; @@ -1044,6 +1044,20 @@ impl X509NameRef { } } + /// Returns an owned String representing the X509 name configurable via incoming flags. + /// + /// This function will return `None` if the underlying string contains invalid utf-8. + #[corresponds(X509_NAME_print_ex)] + pub fn print_ex(&self, flags: i32) -> Option { + unsafe { + let bio = MemBio::new().ok()?; + ffi::X509_NAME_print_ex(bio.as_ptr(), self.as_ptr(), 0, flags as _); + let buf = bio.get_buf().to_vec(); + let res = String::from_utf8(buf); + res.ok() + } + } + to_der! { /// Serializes the certificate into a DER-encoded X509 name structure. /// From ef8146be7ce1a9b2cb6790f718b51ea92036dd07 Mon Sep 17 00:00:00 2001 From: Rushil Mehra Date: Thu, 15 Aug 2024 13:42:12 -0700 Subject: [PATCH 09/41] Add tests for X509Ref::subject_key_id, X509Ref::authority_key_id, and X509NameRef::print_ex --- boring/src/x509/tests/mod.rs | 43 ++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/boring/src/x509/tests/mod.rs b/boring/src/x509/tests/mod.rs index 65bb7f90..b3867ce2 100644 --- a/boring/src/x509/tests/mod.rs +++ b/boring/src/x509/tests/mod.rs @@ -179,6 +179,49 @@ fn test_subject_alt_name_iter() { assert!(subject_alt_names_iter.next().is_none()); } +#[test] +fn test_subject_key_id() { + // nid_test_cert_pem has SKI, but no AKI + let cert = include_bytes!("../../../test/nid_test_cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let ski = cert.subject_key_id().expect("unable to extract SKI"); + assert_eq!( + ski.as_slice(), + [ + 80, 107, 158, 237, 95, 61, 235, 100, 212, 115, 249, 244, 219, 163, 124, 55, 141, 2, 76, + 5 + ] + ); + + let aki = cert.authority_key_id(); + assert!(aki.is_none()); +} + +#[test] +fn test_x509_name_print_ex() { + let cert = include_bytes!("../../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let name_no_flags = cert + .subject_name() + .print_ex(0) + .expect("failed to print cert subject name"); + assert_eq!( + name_no_flags, + "C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=foobar.com" + ); + + let name_rfc2253 = cert + .subject_name() + .print_ex(ffi::XN_FLAG_RFC2253) + .expect("failed to print cert subject name"); + assert_eq!( + name_rfc2253, + "CN=foobar.com,O=Internet Widgits Pty Ltd,ST=Some-State,C=AU" + ); +} + #[test] fn x509_builder() { let pkey = pkey(); From a7bfe0d92c01f55faf21c0dca378eeb5753a351e Mon Sep 17 00:00:00 2001 From: Rushil Mehra Date: Thu, 15 Aug 2024 13:42:28 -0700 Subject: [PATCH 10/41] Fix macos FIPS crossbuild --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cb68529c..fb3c07cb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -322,7 +322,8 @@ jobs: working-directory: ${{ runner.temp }}/llvm/bin run: ln -s clang clang++-12 - name: Install ${{ matrix.target }} toolchain - run: brew tap messense/macos-cross-toolchains && brew install --overwrite python@3.11 && brew install ${{ matrix.target }} + # TODO(rmehra): find a better way to overwrite the python3 version without specifying version + run: brew tap messense/macos-cross-toolchains && brew install --overwrite python@3.12 && brew install ${{ matrix.target }} - name: Set BORING_BSSL_FIPS_COMPILER_EXTERNAL_TOOLCHAIN run: echo "BORING_BSSL_FIPS_COMPILER_EXTERNAL_TOOLCHAIN=$(brew --prefix ${{ matrix.target }})/toolchain" >> $GITHUB_ENV shell: bash From e5b6627efcb6decd00b9e1dfe6a34dbbaabb7ed4 Mon Sep 17 00:00:00 2001 From: Evan Rittenhouse Date: Mon, 19 Aug 2024 17:26:33 -0500 Subject: [PATCH 11/41] Expose RSAPSS public key Id type --- boring/src/pkey.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/boring/src/pkey.rs b/boring/src/pkey.rs index b8a157f6..9897635e 100644 --- a/boring/src/pkey.rs +++ b/boring/src/pkey.rs @@ -72,6 +72,7 @@ pub struct Id(c_int); impl Id { pub const RSA: Id = Id(ffi::EVP_PKEY_RSA); + pub const RSAPSS: Id = Id(ffi::EVP_PKEY_RSA_PSS); pub const DSA: Id = Id(ffi::EVP_PKEY_DSA); pub const DH: Id = Id(ffi::EVP_PKEY_DH); pub const EC: Id = Id(ffi::EVP_PKEY_EC); @@ -303,6 +304,7 @@ impl fmt::Debug for PKey { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let alg = match self.id() { Id::RSA => "RSA", + Id::RSAPSS => "RSAPSS", Id::DSA => "DSA", Id::DH => "DH", Id::EC => "EC", From 7324db2b75abbf32cc47bb3497c6c8cc32af41f0 Mon Sep 17 00:00:00 2001 From: Rushil Mehra Date: Tue, 3 Sep 2024 08:04:30 -0700 Subject: [PATCH 12/41] Use ForeignType::into_ptr wherever applicable --- boring/src/dsa.rs | 3 +-- boring/src/ssl/mod.rs | 15 ++++++--------- boring/src/x509/mod.rs | 4 +--- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/boring/src/dsa.rs b/boring/src/dsa.rs index 60c0c197..d9c35505 100644 --- a/boring/src/dsa.rs +++ b/boring/src/dsa.rs @@ -280,8 +280,7 @@ impl Dsa { let dsa = Dsa::from_ptr(cvt_p(ffi::DSA_new())?); cvt(DSA_set0_pqg(dsa.0, p.as_ptr(), q.as_ptr(), g.as_ptr()))?; mem::forget((p, q, g)); - cvt(DSA_set0_key(dsa.0, pub_key.as_ptr(), ptr::null_mut()))?; - mem::forget(pub_key); + cvt(DSA_set0_key(dsa.0, pub_key.into_ptr(), ptr::null_mut()))?; Ok(dsa) } } diff --git a/boring/src/ssl/mod.rs b/boring/src/ssl/mod.rs index 12ffedf5..04e0ee43 100644 --- a/boring/src/ssl/mod.rs +++ b/boring/src/ssl/mod.rs @@ -1068,9 +1068,9 @@ impl SslContextBuilder { assert!(!self.is_rpk, "This API is not supported for RPK"); unsafe { - let ptr = cert_store.as_ptr(); - cvt(ffi::SSL_CTX_set0_verify_cert_store(self.as_ptr(), ptr) as c_int)?; - mem::forget(cert_store); + cvt( + ffi::SSL_CTX_set0_verify_cert_store(self.as_ptr(), cert_store.into_ptr()) as c_int, + )?; Ok(()) } @@ -1083,8 +1083,7 @@ impl SslContextBuilder { assert!(!self.is_rpk, "This API is not supported for RPK"); unsafe { - ffi::SSL_CTX_set_cert_store(self.as_ptr(), cert_store.as_ptr()); - mem::forget(cert_store); + ffi::SSL_CTX_set_cert_store(self.as_ptr(), cert_store.into_ptr()); } } @@ -1260,8 +1259,7 @@ impl SslContextBuilder { assert!(!self.is_rpk, "This API is not supported for RPK"); unsafe { - cvt(ffi::SSL_CTX_add_extra_chain_cert(self.as_ptr(), cert.as_ptr()) as c_int)?; - mem::forget(cert); + cvt(ffi::SSL_CTX_add_extra_chain_cert(self.as_ptr(), cert.into_ptr()) as c_int)?; Ok(()) } } @@ -2742,8 +2740,7 @@ impl SslRef { ); unsafe { - cvt(ffi::SSL_set0_verify_cert_store(self.as_ptr(), cert_store.as_ptr()) as c_int)?; - mem::forget(cert_store); + cvt(ffi::SSL_set0_verify_cert_store(self.as_ptr(), cert_store.into_ptr()) as c_int)?; Ok(()) } } diff --git a/boring/src/x509/mod.rs b/boring/src/x509/mod.rs index b234bd2b..34896d6a 100644 --- a/boring/src/x509/mod.rs +++ b/boring/src/x509/mod.rs @@ -1584,9 +1584,7 @@ impl GeneralName { ffi::init(); let gn = cvt_p(ffi::GENERAL_NAME_new())?; (*gn).type_ = ffi::GEN_RID; - (*gn).d.registeredID = oid.as_ptr(); - - mem::forget(oid); + (*gn).d.registeredID = oid.into_ptr(); Ok(GeneralName::from_ptr(gn)) } From b2525f2ed24147e13b98a0ee3979cc7b74ab67d4 Mon Sep 17 00:00:00 2001 From: Evan Rittenhouse Date: Wed, 11 Sep 2024 03:35:51 -0500 Subject: [PATCH 13/41] Expose SSL_CTX_set_info_callback (#266) Model callback arguments as structs --- boring/src/ssl/callbacks.rs | 34 ++++++++++++++++-- boring/src/ssl/mod.rs | 72 +++++++++++++++++++++++++++++++++++++ boring/src/ssl/test/mod.rs | 14 ++++++++ 3 files changed, 118 insertions(+), 2 deletions(-) diff --git a/boring/src/ssl/callbacks.rs b/boring/src/ssl/callbacks.rs index 7841950c..f592d9d2 100644 --- a/boring/src/ssl/callbacks.rs +++ b/boring/src/ssl/callbacks.rs @@ -2,8 +2,9 @@ use super::{ AlpnError, ClientHello, GetSessionPendingError, PrivateKeyMethod, PrivateKeyMethodError, - SelectCertError, SniError, Ssl, SslAlert, SslContext, SslContextRef, SslRef, SslSession, - SslSessionRef, SslSignatureAlgorithm, SslVerifyError, SESSION_CTX_INDEX, + SelectCertError, SniError, Ssl, SslAlert, SslContext, SslContextRef, SslInfoCallbackAlert, + SslInfoCallbackMode, SslInfoCallbackValue, SslRef, SslSession, SslSessionRef, + SslSignatureAlgorithm, SslVerifyError, SESSION_CTX_INDEX, }; use crate::error::ErrorStack; use crate::ffi; @@ -521,3 +522,32 @@ where Err(err) => err.0, } } + +pub(super) unsafe extern "C" fn raw_info_callback( + ssl: *const ffi::SSL, + mode: c_int, + value: c_int, +) where + F: Fn(&SslRef, SslInfoCallbackMode, SslInfoCallbackValue) + Send + Sync + 'static, +{ + // Due to FFI signature requirements we have to pass a *const SSL into this function, but + // foreign-types requires a *mut SSL to get the Rust SslRef + let mut_ref = ssl as *mut ffi::SSL; + + // SAFETY: boring provides valid inputs. + let ssl = unsafe { SslRef::from_ptr(mut_ref) }; + let ssl_context = ssl.ssl_context(); + + let callback = ssl_context + .ex_data(SslContext::cached_ex_index::()) + .expect("BUG: info callback missing"); + + let value = match mode { + ffi::SSL_CB_READ_ALERT | ffi::SSL_CB_WRITE_ALERT => { + SslInfoCallbackValue::Alert(SslInfoCallbackAlert(value)) + } + _ => SslInfoCallbackValue::Unit, + }; + + callback(ssl, SslInfoCallbackMode(mode), value); +} diff --git a/boring/src/ssl/mod.rs b/boring/src/ssl/mod.rs index 04e0ee43..be32e545 100644 --- a/boring/src/ssl/mod.rs +++ b/boring/src/ssl/mod.rs @@ -834,6 +834,66 @@ pub fn select_next_proto<'a>(server: &[u8], client: &'a [u8]) -> Option<&'a [u8] } } +/// Options controlling the behavior of the info callback. +#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)] +pub struct SslInfoCallbackMode(i32); + +impl SslInfoCallbackMode { + /// Signaled for each alert received, warning or fatal. + pub const READ_ALERT: Self = Self(ffi::SSL_CB_READ_ALERT); + + /// Signaled for each alert sent, warning or fatal. + pub const WRITE_ALERT: Self = Self(ffi::SSL_CB_WRITE_ALERT); + + /// Signaled when a handshake begins. + pub const HANDSHAKE_START: Self = Self(ffi::SSL_CB_HANDSHAKE_START); + + /// Signaled when a handshake completes successfully. + pub const HANDSHAKE_DONE: Self = Self(ffi::SSL_CB_HANDSHAKE_DONE); + + /// Signaled when a handshake progresses to a new state. + pub const ACCEPT_LOOP: Self = Self(ffi::SSL_CB_ACCEPT_LOOP); +} + +/// The `value` argument to an info callback. The most-significant byte is the alert level, while +/// the least significant byte is the alert itself. +#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)] +pub enum SslInfoCallbackValue { + /// The unit value (1). Some BoringSSL info callback modes, like ACCEPT_LOOP, always call the + /// callback with `value` set to the unit value. If the [`SslInfoCallbackValue`] is a + /// `Unit`, it can safely be disregarded. + Unit, + /// An alert. See [`SslInfoCallbackAlert`] for details on how to manipulate the alert. This + /// variant should only be present if the info callback was called with a `READ_ALERT` or + /// `WRITE_ALERT` mode. + Alert(SslInfoCallbackAlert), +} + +#[derive(Hash, Copy, Clone, PartialOrd, Ord, Eq, PartialEq, Debug)] +pub struct SslInfoCallbackAlert(c_int); + +impl SslInfoCallbackAlert { + /// The level of the SSL alert. + pub fn alert_level(&self) -> Ssl3AlertLevel { + let value = self.0 >> 8; + Ssl3AlertLevel(value) + } + + /// The value of the SSL alert. + pub fn alert(&self) -> SslAlert { + let value = self.0 & (u8::MAX as i32); + SslAlert(value) + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Ssl3AlertLevel(c_int); + +impl Ssl3AlertLevel { + pub const WARNING: Ssl3AlertLevel = Self(ffi::SSL3_AL_WARNING); + pub const FATAL: Ssl3AlertLevel = Self(ffi::SSL3_AL_FATAL); +} + #[cfg(feature = "rpk")] extern "C" fn rpk_verify_failure_callback( _ssl: *mut ffi::SSL, @@ -1818,6 +1878,18 @@ impl SslContextBuilder { unsafe { cvt_0i(ffi::SSL_CTX_set_compliance_policy(self.as_ptr(), policy.0)).map(|_| ()) } } + /// Sets the context's info callback. + #[corresponds(SSL_CTX_set_info_callback)] + pub fn set_info_callback(&mut self, callback: F) + where + F: Fn(&SslRef, SslInfoCallbackMode, SslInfoCallbackValue) + Send + Sync + 'static, + { + unsafe { + self.replace_ex_data(SslContext::cached_ex_index::(), callback); + ffi::SSL_CTX_set_info_callback(self.as_ptr(), Some(callbacks::raw_info_callback::)); + } + } + /// Consumes the builder, returning a new `SslContext`. pub fn build(self) -> SslContext { self.ctx diff --git a/boring/src/ssl/test/mod.rs b/boring/src/ssl/test/mod.rs index 131b1127..f3b0fd29 100644 --- a/boring/src/ssl/test/mod.rs +++ b/boring/src/ssl/test/mod.rs @@ -1052,3 +1052,17 @@ fn drop_ex_data_in_ssl() { assert_eq!(ssl.replace_ex_data(index, "camembert"), Some("comté")); assert_eq!(ssl.replace_ex_data(index, "raclette"), Some("camembert")); } + +#[test] +fn test_info_callback() { + static CALLED_BACK: AtomicBool = AtomicBool::new(false); + + let server = Server::builder().build(); + let mut client = server.client(); + client.ctx().set_info_callback(move |_, _, _| { + CALLED_BACK.store(true, Ordering::Relaxed); + }); + + client.connect(); + assert!(CALLED_BACK.load(Ordering::Relaxed)); +} From 4b37d88b803f3d86bf3d7899b2c5aeb048e560f1 Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Tue, 17 Sep 2024 10:00:25 +0200 Subject: [PATCH 14/41] Expose SSL(_CTX)_set1_curves_list (#270) set_surves_list is similar to set_curves, but the curves are specified by a string. This makes it convenient when the supported curves of the underlying BoringSSL is not known at compile time. Also fix a bug in checking return value of SSL_set1_curves_list. --- boring/src/ssl/mod.rs | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/boring/src/ssl/mod.rs b/boring/src/ssl/mod.rs index be32e545..6a9361e1 100644 --- a/boring/src/ssl/mod.rs +++ b/boring/src/ssl/mod.rs @@ -1849,6 +1849,24 @@ impl SslContextBuilder { unsafe { ffi::SSL_CTX_enable_ocsp_stapling(self.as_ptr()) } } + /// 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).unwrap(); + unsafe { + cvt_0i(ffi::SSL_CTX_set1_curves_list( + self.as_ptr(), + curves.as_ptr() as *const _, + )) + .map(|_| ()) + } + } + /// Sets the context's supported curves. // // If the "kx-*" flags are used to set key exchange preference, then don't allow the user to @@ -2661,11 +2679,10 @@ impl SslRef { } #[corresponds(SSL_set1_curves_list)] - #[cfg(feature = "kx-safe-default")] - fn set_curves_list(&mut self, curves: &str) -> Result<(), ErrorStack> { + pub fn set_curves_list(&mut self, curves: &str) -> Result<(), ErrorStack> { let curves = CString::new(curves).unwrap(); unsafe { - cvt(ffi::SSL_set1_curves_list( + cvt_0i(ffi::SSL_set1_curves_list( self.as_ptr(), curves.as_ptr() as *const _, )) From 8cb5da61a68446c8473ce5337b89b03f24b56df2 Mon Sep 17 00:00:00 2001 From: Mike Aizatsky Date: Tue, 17 Sep 2024 15:45:56 -0700 Subject: [PATCH 15/41] Update bindgen to 0.70.1 bindgen has had a lot of improvements since 0.68, and this newer version seems to able to compile libbssl from within edgeworker --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e8bf6e38..f848893f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ boring-sys = { version = "4.9.1", path = "./boring-sys" } boring = { version = "4.9.1", path = "./boring" } tokio-boring = { version = "4.9.1", path = "./tokio-boring" } -bindgen = { version = "0.68.1", default-features = false, features = ["runtime"] } +bindgen = { version = "0.70.1", default-features = false, features = ["runtime"] } cmake = "0.1.18" fs_extra = "1.3.0" fslock = "0.2" From 2c0a14253a0abdbb1e09eaf4b681093a4cf41e76 Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Tue, 10 Sep 2024 13:16:14 +0200 Subject: [PATCH 16/41] Revert "PQ: fix timing sidechannels and add IPDWing" For TLS, early adopters prefer X25519MLKEM768. Remove IPDWing in preparation for adding X25519MLKEM768. https://datatracker.ietf.org/doc/draft-kwiatkowski-tls-ecdhe-mlkem/ This reverts commit 4725a930d5fd1118517cc929fc520b2de3e6eff1. --- boring-sys/patches/boring-pq.patch | 5858 +++++++++++++--------------- boring/src/lib.rs | 4 - boring/src/ssl/mod.rs | 5 - 3 files changed, 2668 insertions(+), 3199 deletions(-) diff --git a/boring-sys/patches/boring-pq.patch b/boring-sys/patches/boring-pq.patch index d4294dc5..2ffeee6c 100644 --- a/boring-sys/patches/boring-pq.patch +++ b/boring-sys/patches/boring-pq.patch @@ -1,4 +1,4 @@ -From 836d390deaf8b50fed0cafd55b17a63e80454d7f Mon Sep 17 00:00:00 2001 +From 4cba2164726c8d2647e38548a266a70c4942d567 Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Fri, 22 Jul 2022 16:43:48 +0200 Subject: [PATCH] Add temporary post-quantum key agreements @@ -20,55 +20,43 @@ This patch adds: key agreement should only be used for testing: to see if the smaller keyshare makes a difference. -4. Supportfor IPDWing under codepoint 0xfe41. This key agreement - is a preliminary version of X-Wing using the initial public draft - of ML-KEM. It should not be used. - The patch also replaces Google's implementation of Kyber, by the portable reference implementation, so as to support Kyber512. Cf RTG-2076 RTG-2051 RTG-2508 RTG-2707 RTG-2607 --- - BUILD.generated.bzl | 7 +- + BUILD.generated.bzl | 5 +- BUILD.generated_tests.bzl | 4 - - CMakeLists.txt | 6 +- - sources.json | 11 +- - src/crypto/CMakeLists.txt | 7 +- - src/crypto/kyber/fips202.c | 699 +++++++ - src/crypto/kyber/fips202.h | 29 + + CMakeLists.txt | 4 +- + sources.json | 9 +- + src/crypto/CMakeLists.txt | 5 +- src/crypto/kyber/internal.h | 91 - - src/crypto/kyber/ipdwing.c | 110 ++ src/crypto/kyber/keccak.c | 204 -- - src/crypto/kyber/kyber.c | 2319 +++++++++++++++------- - src/crypto/kyber/kyber.h | 29 + + src/crypto/kyber/kyber.c | 2865 ++++++++++++++++++++------- src/crypto/kyber/kyber512.c | 5 + src/crypto/kyber/kyber768.c | 4 + src/crypto/kyber/kyber_test.cc | 229 --- - src/crypto/obj/obj_dat.h | 17 +- - src/crypto/obj/obj_mac.num | 4 + - src/crypto/obj/objects.txt | 6 +- - src/include/openssl/kyber.h | 252 ++- - src/include/openssl/nid.h | 12 + - src/include/openssl/ssl.h | 4 + + src/crypto/obj/obj_dat.h | 14 +- + src/crypto/obj/obj_mac.num | 3 + + src/crypto/obj/objects.txt | 5 +- + src/include/openssl/kyber.h | 199 +- + src/include/openssl/nid.h | 9 + + src/include/openssl/ssl.h | 3 + src/sources.cmake | 2 - - src/ssl/extensions.cc | 4 + - src/ssl/ssl_key_share.cc | 493 ++++- + src/ssl/extensions.cc | 3 + + src/ssl/ssl_key_share.cc | 412 +++- src/ssl/ssl_lib.cc | 2 +- - src/ssl/ssl_test.cc | 29 +- + src/ssl/ssl_test.cc | 25 +- src/tool/speed.cc | 162 +- - 30 files changed, 3276 insertions(+), 5445 deletions(-) - create mode 100644 src/crypto/kyber/fips202.c - create mode 100644 src/crypto/kyber/fips202.h + 26 files changed, 2797 insertions(+), 5447 deletions(-) delete mode 100644 src/crypto/kyber/internal.h - create mode 100644 src/crypto/kyber/ipdwing.c delete mode 100644 src/crypto/kyber/keccak.c - create mode 100644 src/crypto/kyber/kyber.h create mode 100644 src/crypto/kyber/kyber512.c create mode 100644 src/crypto/kyber/kyber768.c delete mode 100644 src/crypto/kyber/kyber_test.cc diff --git a/BUILD.generated.bzl b/BUILD.generated.bzl -index 738e1055f..d1d232399 100644 +index 738e1055f..9466757a2 100644 --- a/BUILD.generated.bzl +++ b/BUILD.generated.bzl @@ -253,7 +253,6 @@ crypto_internal_headers = [ @@ -79,16 +67,14 @@ index 738e1055f..d1d232399 100644 "src/crypto/lhash/internal.h", "src/crypto/obj/obj_dat.h", "src/crypto/pkcs7/internal.h", -@@ -382,8 +381,10 @@ crypto_sources = [ +@@ -382,8 +381,8 @@ crypto_sources = [ "src/crypto/fipsmodule/fips_shared_support.c", "src/crypto/hpke/hpke.c", "src/crypto/hrss/hrss.c", - "src/crypto/kyber/keccak.c", - "src/crypto/kyber/kyber.c", -+ "src/crypto/kyber/fips202.c", + "src/crypto/kyber/kyber512.c", + "src/crypto/kyber/kyber768.c", -+ "src/crypto/kyber/ipdwing.c", "src/crypto/lhash/lhash.c", "src/crypto/mem.c", "src/crypto/obj/obj.c", @@ -122,40 +108,36 @@ index 92dec1e01..8f70dedc0 100644 "src/crypto/pkcs8/test/no_encryption.p12", "src/crypto/pkcs8/test/nss.p12", diff --git a/CMakeLists.txt b/CMakeLists.txt -index faed2befa..678a0167a 100644 +index faed2befa..931c0e3a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -375,8 +375,10 @@ add_library( +@@ -375,8 +375,8 @@ add_library( src/crypto/fipsmodule/fips_shared_support.c src/crypto/hpke/hpke.c src/crypto/hrss/hrss.c - src/crypto/kyber/keccak.c - src/crypto/kyber/kyber.c -+ src/crypto/kyber/fips202.c + src/crypto/kyber/kyber512.c + src/crypto/kyber/kyber768.c -+ src/crypto/kyber/ipdwing.c src/crypto/lhash/lhash.c src/crypto/mem.c src/crypto/obj/obj.c diff --git a/sources.json b/sources.json -index 4c0048e1d..d021a14b1 100644 +index 4c0048e1d..f6ea5c40f 100644 --- a/sources.json +++ b/sources.json -@@ -111,8 +111,10 @@ +@@ -111,8 +111,8 @@ "src/crypto/fipsmodule/fips_shared_support.c", "src/crypto/hpke/hpke.c", "src/crypto/hrss/hrss.c", - "src/crypto/kyber/keccak.c", - "src/crypto/kyber/kyber.c", -+ "src/crypto/kyber/fips202.c", + "src/crypto/kyber/kyber512.c", + "src/crypto/kyber/kyber768.c", -+ "src/crypto/kyber/ipdwing.c", "src/crypto/lhash/lhash.c", "src/crypto/mem.c", "src/crypto/obj/obj.c", -@@ -549,7 +551,6 @@ +@@ -549,7 +549,6 @@ "src/crypto/hpke/hpke_test.cc", "src/crypto/hrss/hrss_test.cc", "src/crypto/impl_dispatch_test.cc", @@ -163,7 +145,7 @@ index 4c0048e1d..d021a14b1 100644 "src/crypto/lhash/lhash_test.cc", "src/crypto/obj/obj_test.cc", "src/crypto/pem/pem_test.cc", -@@ -634,8 +635,6 @@ +@@ -634,8 +633,6 @@ "src/crypto/fipsmodule/rand/ctrdrbg_vectors.txt", "src/crypto/hmac_extra/hmac_tests.txt", "src/crypto/hpke/hpke_test_vectors.txt", @@ -172,7 +154,7 @@ index 4c0048e1d..d021a14b1 100644 "src/crypto/pkcs8/test/empty_password.p12", "src/crypto/pkcs8/test/no_encryption.p12", "src/crypto/pkcs8/test/nss.p12", -@@ -1060,4 +1059,4 @@ +@@ -1060,4 +1057,4 @@ "urandom_test": [ "src/crypto/fipsmodule/rand/urandom_test.cc" ] @@ -180,23 +162,21 @@ index 4c0048e1d..d021a14b1 100644 \ No newline at end of file +} diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt -index cdb5ddca1..9dcb7a566 100644 +index cdb5ddca1..2052fa791 100644 --- a/src/crypto/CMakeLists.txt +++ b/src/crypto/CMakeLists.txt -@@ -170,8 +170,10 @@ add_library( +@@ -170,8 +170,8 @@ add_library( ex_data.c hpke/hpke.c hrss/hrss.c - kyber/keccak.c - kyber/kyber.c -+ kyber/fips202.c + kyber/kyber512.c + kyber/kyber768.c -+ kyber/ipdwing.c lhash/lhash.c mem.c obj/obj.c -@@ -400,7 +402,6 @@ add_executable( +@@ -400,7 +400,6 @@ add_executable( hmac_extra/hmac_test.cc hrss/hrss_test.cc impl_dispatch_test.cc @@ -204,746 +184,6 @@ index cdb5ddca1..9dcb7a566 100644 lhash/lhash_test.cc obj/obj_test.cc pem/pem_test.cc -diff --git a/src/crypto/kyber/fips202.c b/src/crypto/kyber/fips202.c -new file mode 100644 -index 000000000..9713a4f7e ---- /dev/null -+++ b/src/crypto/kyber/fips202.c -@@ -0,0 +1,699 @@ -+/* Based on the public domain implementation in crypto_hash/keccakc512/simple/ from -+ * http://bench.cr.yp.to/supercop.html by Ronny Van Keer and the public domain "TweetFips202" -+ * implementation from https://twitter.com/tweetfips202 by Gilles Van Assche, Daniel J. Bernstein, -+ * and Peter Schwabe */ -+ -+#include "fips202.h" -+ -+#define NROUNDS 24 -+#define ROL(a, offset) ((a << offset) ^ (a >> (64-offset))) -+ -+/************************************************* -+* Name: load64 -+* -+* Description: Load 8 bytes into uint64_t in little-endian order -+* -+* Arguments: - const uint8_t *x: pointer to input byte array -+* -+* Returns the loaded 64-bit unsigned integer -+**************************************************/ -+static uint64_t load64(const uint8_t x[8]) { -+ unsigned int i; -+ uint64_t r = 0; -+ -+ for(i=0;i<8;i++) -+ r |= (uint64_t)x[i] << 8*i; -+ -+ return r; -+} -+ -+/************************************************* -+* Name: store64 -+* -+* Description: Store a 64-bit integer to array of 8 bytes in little-endian order -+* -+* Arguments: - uint8_t *x: pointer to the output byte array (allocated) -+* - uint64_t u: input 64-bit unsigned integer -+**************************************************/ -+static void store64(uint8_t x[8], uint64_t u) { -+ unsigned int i; -+ -+ for(i=0;i<8;i++) -+ x[i] = u >> 8*i; -+} -+ -+/* Keccak round constants */ -+static const uint64_t KeccakF_RoundConstants[NROUNDS] = { -+ (uint64_t)0x0000000000000001ULL, -+ (uint64_t)0x0000000000008082ULL, -+ (uint64_t)0x800000000000808aULL, -+ (uint64_t)0x8000000080008000ULL, -+ (uint64_t)0x000000000000808bULL, -+ (uint64_t)0x0000000080000001ULL, -+ (uint64_t)0x8000000080008081ULL, -+ (uint64_t)0x8000000000008009ULL, -+ (uint64_t)0x000000000000008aULL, -+ (uint64_t)0x0000000000000088ULL, -+ (uint64_t)0x0000000080008009ULL, -+ (uint64_t)0x000000008000000aULL, -+ (uint64_t)0x000000008000808bULL, -+ (uint64_t)0x800000000000008bULL, -+ (uint64_t)0x8000000000008089ULL, -+ (uint64_t)0x8000000000008003ULL, -+ (uint64_t)0x8000000000008002ULL, -+ (uint64_t)0x8000000000000080ULL, -+ (uint64_t)0x000000000000800aULL, -+ (uint64_t)0x800000008000000aULL, -+ (uint64_t)0x8000000080008081ULL, -+ (uint64_t)0x8000000000008080ULL, -+ (uint64_t)0x0000000080000001ULL, -+ (uint64_t)0x8000000080008008ULL -+}; -+ -+/************************************************* -+* Name: KeccakF1600_StatePermute -+* -+* Description: The Keccak F1600 Permutation -+* -+* Arguments: - uint64_t *state: pointer to input/output Keccak state -+**************************************************/ -+static void KeccakF1600_StatePermute(uint64_t state[25]) -+{ -+ int round; -+ -+ uint64_t Aba, Abe, Abi, Abo, Abu; -+ uint64_t Aga, Age, Agi, Ago, Agu; -+ uint64_t Aka, Ake, Aki, Ako, Aku; -+ uint64_t Ama, Ame, Ami, Amo, Amu; -+ uint64_t Asa, Ase, Asi, Aso, Asu; -+ uint64_t BCa, BCe, BCi, BCo, BCu; -+ uint64_t Da, De, Di, Do, Du; -+ uint64_t Eba, Ebe, Ebi, Ebo, Ebu; -+ uint64_t Ega, Ege, Egi, Ego, Egu; -+ uint64_t Eka, Eke, Eki, Eko, Eku; -+ uint64_t Ema, Eme, Emi, Emo, Emu; -+ uint64_t Esa, Ese, Esi, Eso, Esu; -+ -+ //copyFromState(A, state) -+ Aba = state[ 0]; -+ Abe = state[ 1]; -+ Abi = state[ 2]; -+ Abo = state[ 3]; -+ Abu = state[ 4]; -+ Aga = state[ 5]; -+ Age = state[ 6]; -+ Agi = state[ 7]; -+ Ago = state[ 8]; -+ Agu = state[ 9]; -+ Aka = state[10]; -+ Ake = state[11]; -+ Aki = state[12]; -+ Ako = state[13]; -+ Aku = state[14]; -+ Ama = state[15]; -+ Ame = state[16]; -+ Ami = state[17]; -+ Amo = state[18]; -+ Amu = state[19]; -+ Asa = state[20]; -+ Ase = state[21]; -+ Asi = state[22]; -+ Aso = state[23]; -+ Asu = state[24]; -+ -+ for(round = 0; round < NROUNDS; round += 2) { -+ // prepareTheta -+ BCa = Aba^Aga^Aka^Ama^Asa; -+ BCe = Abe^Age^Ake^Ame^Ase; -+ BCi = Abi^Agi^Aki^Ami^Asi; -+ BCo = Abo^Ago^Ako^Amo^Aso; -+ BCu = Abu^Agu^Aku^Amu^Asu; -+ -+ //thetaRhoPiChiIotaPrepareTheta(round, A, E) -+ Da = BCu^ROL(BCe, 1); -+ De = BCa^ROL(BCi, 1); -+ Di = BCe^ROL(BCo, 1); -+ Do = BCi^ROL(BCu, 1); -+ Du = BCo^ROL(BCa, 1); -+ -+ Aba ^= Da; -+ BCa = Aba; -+ Age ^= De; -+ BCe = ROL(Age, 44); -+ Aki ^= Di; -+ BCi = ROL(Aki, 43); -+ Amo ^= Do; -+ BCo = ROL(Amo, 21); -+ Asu ^= Du; -+ BCu = ROL(Asu, 14); -+ Eba = BCa ^((~BCe)& BCi ); -+ Eba ^= (uint64_t)KeccakF_RoundConstants[round]; -+ Ebe = BCe ^((~BCi)& BCo ); -+ Ebi = BCi ^((~BCo)& BCu ); -+ Ebo = BCo ^((~BCu)& BCa ); -+ Ebu = BCu ^((~BCa)& BCe ); -+ -+ Abo ^= Do; -+ BCa = ROL(Abo, 28); -+ Agu ^= Du; -+ BCe = ROL(Agu, 20); -+ Aka ^= Da; -+ BCi = ROL(Aka, 3); -+ Ame ^= De; -+ BCo = ROL(Ame, 45); -+ Asi ^= Di; -+ BCu = ROL(Asi, 61); -+ Ega = BCa ^((~BCe)& BCi ); -+ Ege = BCe ^((~BCi)& BCo ); -+ Egi = BCi ^((~BCo)& BCu ); -+ Ego = BCo ^((~BCu)& BCa ); -+ Egu = BCu ^((~BCa)& BCe ); -+ -+ Abe ^= De; -+ BCa = ROL(Abe, 1); -+ Agi ^= Di; -+ BCe = ROL(Agi, 6); -+ Ako ^= Do; -+ BCi = ROL(Ako, 25); -+ Amu ^= Du; -+ BCo = ROL(Amu, 8); -+ Asa ^= Da; -+ BCu = ROL(Asa, 18); -+ Eka = BCa ^((~BCe)& BCi ); -+ Eke = BCe ^((~BCi)& BCo ); -+ Eki = BCi ^((~BCo)& BCu ); -+ Eko = BCo ^((~BCu)& BCa ); -+ Eku = BCu ^((~BCa)& BCe ); -+ -+ Abu ^= Du; -+ BCa = ROL(Abu, 27); -+ Aga ^= Da; -+ BCe = ROL(Aga, 36); -+ Ake ^= De; -+ BCi = ROL(Ake, 10); -+ Ami ^= Di; -+ BCo = ROL(Ami, 15); -+ Aso ^= Do; -+ BCu = ROL(Aso, 56); -+ Ema = BCa ^((~BCe)& BCi ); -+ Eme = BCe ^((~BCi)& BCo ); -+ Emi = BCi ^((~BCo)& BCu ); -+ Emo = BCo ^((~BCu)& BCa ); -+ Emu = BCu ^((~BCa)& BCe ); -+ -+ Abi ^= Di; -+ BCa = ROL(Abi, 62); -+ Ago ^= Do; -+ BCe = ROL(Ago, 55); -+ Aku ^= Du; -+ BCi = ROL(Aku, 39); -+ Ama ^= Da; -+ BCo = ROL(Ama, 41); -+ Ase ^= De; -+ BCu = ROL(Ase, 2); -+ Esa = BCa ^((~BCe)& BCi ); -+ Ese = BCe ^((~BCi)& BCo ); -+ Esi = BCi ^((~BCo)& BCu ); -+ Eso = BCo ^((~BCu)& BCa ); -+ Esu = BCu ^((~BCa)& BCe ); -+ -+ // prepareTheta -+ BCa = Eba^Ega^Eka^Ema^Esa; -+ BCe = Ebe^Ege^Eke^Eme^Ese; -+ BCi = Ebi^Egi^Eki^Emi^Esi; -+ BCo = Ebo^Ego^Eko^Emo^Eso; -+ BCu = Ebu^Egu^Eku^Emu^Esu; -+ -+ //thetaRhoPiChiIotaPrepareTheta(round+1, E, A) -+ Da = BCu^ROL(BCe, 1); -+ De = BCa^ROL(BCi, 1); -+ Di = BCe^ROL(BCo, 1); -+ Do = BCi^ROL(BCu, 1); -+ Du = BCo^ROL(BCa, 1); -+ -+ Eba ^= Da; -+ BCa = Eba; -+ Ege ^= De; -+ BCe = ROL(Ege, 44); -+ Eki ^= Di; -+ BCi = ROL(Eki, 43); -+ Emo ^= Do; -+ BCo = ROL(Emo, 21); -+ Esu ^= Du; -+ BCu = ROL(Esu, 14); -+ Aba = BCa ^((~BCe)& BCi ); -+ Aba ^= (uint64_t)KeccakF_RoundConstants[round+1]; -+ Abe = BCe ^((~BCi)& BCo ); -+ Abi = BCi ^((~BCo)& BCu ); -+ Abo = BCo ^((~BCu)& BCa ); -+ Abu = BCu ^((~BCa)& BCe ); -+ -+ Ebo ^= Do; -+ BCa = ROL(Ebo, 28); -+ Egu ^= Du; -+ BCe = ROL(Egu, 20); -+ Eka ^= Da; -+ BCi = ROL(Eka, 3); -+ Eme ^= De; -+ BCo = ROL(Eme, 45); -+ Esi ^= Di; -+ BCu = ROL(Esi, 61); -+ Aga = BCa ^((~BCe)& BCi ); -+ Age = BCe ^((~BCi)& BCo ); -+ Agi = BCi ^((~BCo)& BCu ); -+ Ago = BCo ^((~BCu)& BCa ); -+ Agu = BCu ^((~BCa)& BCe ); -+ -+ Ebe ^= De; -+ BCa = ROL(Ebe, 1); -+ Egi ^= Di; -+ BCe = ROL(Egi, 6); -+ Eko ^= Do; -+ BCi = ROL(Eko, 25); -+ Emu ^= Du; -+ BCo = ROL(Emu, 8); -+ Esa ^= Da; -+ BCu = ROL(Esa, 18); -+ Aka = BCa ^((~BCe)& BCi ); -+ Ake = BCe ^((~BCi)& BCo ); -+ Aki = BCi ^((~BCo)& BCu ); -+ Ako = BCo ^((~BCu)& BCa ); -+ Aku = BCu ^((~BCa)& BCe ); -+ -+ Ebu ^= Du; -+ BCa = ROL(Ebu, 27); -+ Ega ^= Da; -+ BCe = ROL(Ega, 36); -+ Eke ^= De; -+ BCi = ROL(Eke, 10); -+ Emi ^= Di; -+ BCo = ROL(Emi, 15); -+ Eso ^= Do; -+ BCu = ROL(Eso, 56); -+ Ama = BCa ^((~BCe)& BCi ); -+ Ame = BCe ^((~BCi)& BCo ); -+ Ami = BCi ^((~BCo)& BCu ); -+ Amo = BCo ^((~BCu)& BCa ); -+ Amu = BCu ^((~BCa)& BCe ); -+ -+ Ebi ^= Di; -+ BCa = ROL(Ebi, 62); -+ Ego ^= Do; -+ BCe = ROL(Ego, 55); -+ Eku ^= Du; -+ BCi = ROL(Eku, 39); -+ Ema ^= Da; -+ BCo = ROL(Ema, 41); -+ Ese ^= De; -+ BCu = ROL(Ese, 2); -+ Asa = BCa ^((~BCe)& BCi ); -+ Ase = BCe ^((~BCi)& BCo ); -+ Asi = BCi ^((~BCo)& BCu ); -+ Aso = BCo ^((~BCu)& BCa ); -+ Asu = BCu ^((~BCa)& BCe ); -+ } -+ -+ //copyToState(state, A) -+ state[ 0] = Aba; -+ state[ 1] = Abe; -+ state[ 2] = Abi; -+ state[ 3] = Abo; -+ state[ 4] = Abu; -+ state[ 5] = Aga; -+ state[ 6] = Age; -+ state[ 7] = Agi; -+ state[ 8] = Ago; -+ state[ 9] = Agu; -+ state[10] = Aka; -+ state[11] = Ake; -+ state[12] = Aki; -+ state[13] = Ako; -+ state[14] = Aku; -+ state[15] = Ama; -+ state[16] = Ame; -+ state[17] = Ami; -+ state[18] = Amo; -+ state[19] = Amu; -+ state[20] = Asa; -+ state[21] = Ase; -+ state[22] = Asi; -+ state[23] = Aso; -+ state[24] = Asu; -+} -+ -+ -+/************************************************* -+* Name: keccak_squeeze -+* -+* Description: Squeeze step of Keccak. Squeezes arbitratrily many bytes. -+* Modifies the state. Can be called multiple times to keep -+* squeezing, i.e., is incremental. -+* -+* Arguments: - uint8_t *out: pointer to output -+* - size_t outlen: number of bytes to be squeezed (written to out) -+* - uint64_t *s: pointer to input/output Keccak state -+* - unsigned int pos: number of bytes in current block already squeezed -+* - unsigned int r: rate in bytes (e.g., 168 for SHAKE128) -+* -+* Returns new position pos in current block -+**************************************************/ -+static unsigned int keccak_squeeze(uint8_t *out, -+ size_t outlen, -+ uint64_t s[25], -+ unsigned int pos, -+ unsigned int r) -+{ -+ unsigned int i; -+ -+ while(outlen) { -+ if(pos == r) { -+ KeccakF1600_StatePermute(s); -+ pos = 0; -+ } -+ for(i=pos;i < r && i < pos+outlen; i++) -+ *out++ = s[i/8] >> 8*(i%8); -+ outlen -= i-pos; -+ pos = i; -+ } -+ -+ return pos; -+} -+ -+/************************************************* -+* Name: keccak_init -+* -+* Description: Initializes the Keccak state. -+* -+* Arguments: - uint64_t *s: pointer to Keccak state -+**************************************************/ -+static void keccak_init(uint64_t s[25]) -+{ -+ unsigned int i; -+ for(i=0;i<25;i++) -+ s[i] = 0; -+} -+ -+ -+/************************************************* -+* Name: keccak_absorb -+* -+* Description: Absorb step of Keccak; incremental. -+* -+* Arguments: - uint64_t *s: pointer to Keccak state -+* - unsigned int pos: position in current block to be absorbed -+* - unsigned int r: rate in bytes (e.g., 168 for SHAKE128) -+* - const uint8_t *in: pointer to input to be absorbed into s -+* - size_t inlen: length of input in bytes -+* -+* Returns new position pos in current block -+**************************************************/ -+static unsigned int keccak_absorb(uint64_t s[25], -+ unsigned int pos, -+ unsigned int r, -+ const uint8_t *in, -+ size_t inlen) -+{ -+ unsigned int i; -+ -+ while(pos+inlen >= r) { -+ for(i=pos;i= r) { -+ for(i=0;is, SHAKE128_RATE, in, inlen, 0x1F); -+ state->pos = SHAKE128_RATE; -+} -+ -+/************************************************* -+* Name: shake128_squeezeblocks -+* -+* Description: Squeeze step of SHAKE128 XOF. Squeezes full blocks of -+* SHAKE128_RATE bytes each. Can be called multiple times -+* to keep squeezing. Assumes new block has not yet been -+* started (state->pos = SHAKE128_RATE). -+* -+* Arguments: - uint8_t *out: pointer to output blocks -+* - size_t nblocks: number of blocks to be squeezed (written to output) -+* - keccak_state *s: pointer to input/output Keccak state -+**************************************************/ -+void shake128_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state) -+{ -+ keccak_squeezeblocks(out, nblocks, state->s, SHAKE128_RATE); -+} -+ -+/************************************************* -+* Name: shake256_squeeze -+* -+* Description: Squeeze step of SHAKE256 XOF. Squeezes arbitraily many -+* bytes. Can be called multiple times to keep squeezing. -+* -+* Arguments: - uint8_t *out: pointer to output blocks -+* - size_t outlen : number of bytes to be squeezed (written to output) -+* - keccak_state *s: pointer to input/output Keccak state -+**************************************************/ -+void shake256_squeeze(uint8_t *out, size_t outlen, keccak_state *state) -+{ -+ state->pos = keccak_squeeze(out, outlen, state->s, state->pos, SHAKE256_RATE); -+} -+ -+/************************************************* -+* Name: shake256_init -+* -+* Description: Initilizes Keccak state for use as SHAKE256 XOF -+* -+* Arguments: - keccak_state *state: pointer to (uninitialized) Keccak state -+**************************************************/ -+void shake256_init(keccak_state *state) -+{ -+ keccak_init(state->s); -+ state->pos = 0; -+} -+ -+/************************************************* -+* Name: shake256_absorb -+* -+* Description: Absorb step of the SHAKE256 XOF; incremental. -+* -+* Arguments: - keccak_state *state: pointer to (initialized) output Keccak state -+* - const uint8_t *in: pointer to input to be absorbed into s -+* - size_t inlen: length of input in bytes -+**************************************************/ -+void shake256_absorb(keccak_state *state, const uint8_t *in, size_t inlen) -+{ -+ state->pos = keccak_absorb(state->s, state->pos, SHAKE256_RATE, in, inlen); -+} -+ -+/************************************************* -+* Name: shake256_finalize -+* -+* Description: Finalize absorb step of the SHAKE256 XOF. -+* -+* Arguments: - keccak_state *state: pointer to Keccak state -+**************************************************/ -+void shake256_finalize(keccak_state *state) -+{ -+ keccak_finalize(state->s, state->pos, SHAKE256_RATE, 0x1F); -+ state->pos = SHAKE256_RATE; -+} -+ -+/************************************************* -+* Name: shake256_absorb_once -+* -+* Description: Initialize, absorb into and finalize SHAKE256 XOF; non-incremental. -+* -+* Arguments: - keccak_state *state: pointer to (uninitialized) output Keccak state -+* - const uint8_t *in: pointer to input to be absorbed into s -+* - size_t inlen: length of input in bytes -+**************************************************/ -+void shake256_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen) -+{ -+ keccak_absorb_once(state->s, SHAKE256_RATE, in, inlen, 0x1F); -+ state->pos = SHAKE256_RATE; -+} -+ -+/************************************************* -+* Name: shake256_squeezeblocks -+* -+* Description: Squeeze step of SHAKE256 XOF. Squeezes full blocks of -+* SHAKE256_RATE bytes each. Can be called multiple times -+* to keep squeezing. Assumes next block has not yet been -+* started (state->pos = SHAKE256_RATE). -+* -+* Arguments: - uint8_t *out: pointer to output blocks -+* - size_t nblocks: number of blocks to be squeezed (written to output) -+* - keccak_state *s: pointer to input/output Keccak state -+**************************************************/ -+void shake256_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state) -+{ -+ keccak_squeezeblocks(out, nblocks, state->s, SHAKE256_RATE); -+} -+ -+/************************************************* -+* Name: shake256 -+* -+* Description: SHAKE256 XOF with non-incremental API -+* -+* Arguments: - uint8_t *out: pointer to output -+* - size_t outlen: requested output length in bytes -+* - const uint8_t *in: pointer to input -+* - size_t inlen: length of input in bytes -+**************************************************/ -+void shake256(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen) -+{ -+ size_t nblocks; -+ keccak_state state; -+ -+ shake256_absorb_once(&state, in, inlen); -+ nblocks = outlen/SHAKE256_RATE; -+ shake256_squeezeblocks(out, nblocks, &state); -+ outlen -= nblocks*SHAKE256_RATE; -+ out += nblocks*SHAKE256_RATE; -+ shake256_squeeze(out, outlen, &state); -+} -+ -+/************************************************* -+* Name: sha3_256 -+* -+* Description: SHA3-256 with non-incremental API -+* -+* Arguments: - uint8_t *h: pointer to output (32 bytes) -+* - const uint8_t *in: pointer to input -+* - size_t inlen: length of input in bytes -+**************************************************/ -+void sha3_256(uint8_t h[32], const uint8_t *in, size_t inlen) -+{ -+ unsigned int i; -+ uint64_t s[25]; -+ -+ keccak_absorb_once(s, SHA3_256_RATE, in, inlen, 0x06); -+ KeccakF1600_StatePermute(s); -+ for(i=0;i<4;i++) -+ store64(h+8*i,s[i]); -+} -+ -+/************************************************* -+* Name: sha3_512 -+* -+* Description: SHA3-512 with non-incremental API -+* -+* Arguments: - uint8_t *h: pointer to output (64 bytes) -+* - const uint8_t *in: pointer to input -+* - size_t inlen: length of input in bytes -+**************************************************/ -+void sha3_512(uint8_t h[64], const uint8_t *in, size_t inlen) -+{ -+ unsigned int i; -+ uint64_t s[25]; -+ -+ keccak_absorb_once(s, SHA3_512_RATE, in, inlen, 0x06); -+ KeccakF1600_StatePermute(s); -+ for(i=0;i<8;i++) -+ store64(h+8*i,s[i]); -+} -+ -+ -diff --git a/src/crypto/kyber/fips202.h b/src/crypto/kyber/fips202.h -new file mode 100644 -index 000000000..7c37570bc ---- /dev/null -+++ b/src/crypto/kyber/fips202.h -@@ -0,0 +1,29 @@ -+#ifndef OPENSSL_HEADER_KYBER_FIPS202_H -+#define OPENSSL_HEADER_KYBER_FIPS202_H -+ -+#include -+#include -+ -+#define SHAKE128_RATE 168 -+#define SHAKE256_RATE 136 -+#define SHA3_256_RATE 136 -+#define SHA3_512_RATE 72 -+ -+typedef struct { -+ uint64_t s[25]; -+ unsigned int pos; -+} keccak_state; -+ -+void shake128_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen); -+void shake128_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state); -+void shake256_squeeze(uint8_t *out, size_t outlen, keccak_state *state); -+void shake256_init(keccak_state *state); -+void shake256_absorb(keccak_state *state, const uint8_t *in, size_t inlen); -+void shake256_finalize(keccak_state *state); -+void shake256_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen); -+void shake256_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state); -+void shake256(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen); -+void sha3_512(uint8_t h[64], const uint8_t *in, size_t inlen); -+void sha3_256(uint8_t h[32], const uint8_t *in, size_t inlen); -+ -+#endif diff --git a/src/crypto/kyber/internal.h b/src/crypto/kyber/internal.h deleted file mode 100644 index b3bfa86b8..000000000 @@ -1025,138 +265,22 @@ index b3bfa86b8..000000000 - struct KYBER_private_key *out_private_key, - const uint8_t entropy[KYBER_GENERATE_KEY_ENTROPY]); - --// KYBER_encap_external_entropy is a deterministic function to encapsulate --// |out_shared_secret_len| bytes of |out_shared_secret| to |ciphertext|, using --// |KYBER_ENCAP_ENTROPY| bytes of |entropy| for randomization. The --// decapsulating side will be able to recover |entropy| in full. This --// function is should only be used for tests, regular callers should use the --// non-deterministic |KYBER_encap| directly. --OPENSSL_EXPORT void KYBER_encap_external_entropy( -- uint8_t out_ciphertext[KYBER_CIPHERTEXT_BYTES], uint8_t *out_shared_secret, -- size_t out_shared_secret_len, const struct KYBER_public_key *public_key, -- const uint8_t entropy[KYBER_ENCAP_ENTROPY]); -- --#if defined(__cplusplus) --} --#endif -- --#endif // OPENSSL_HEADER_CRYPTO_KYBER_INTERNAL_H -diff --git a/src/crypto/kyber/ipdwing.c b/src/crypto/kyber/ipdwing.c -new file mode 100644 -index 000000000..d55cfefc9 ---- /dev/null -+++ b/src/crypto/kyber/ipdwing.c -@@ -0,0 +1,110 @@ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+#include "fips202.h" -+#include "kyber.h" -+ -+static const char *label = "\\.//^\\"; -+ -+static void combiner( -+ uint8_t out[32], -+ const uint8_t ss_m[32], -+ const uint8_t ss_x[32], -+ const uint8_t ct_x[32], -+ const uint8_t pk_x[32]) { -+ uint8_t buf[6+32*4]; -+ memcpy(buf, label, 6); -+ memcpy(buf+6, ss_m, 32); -+ memcpy(buf+6+32, ss_x, 32); -+ memcpy(buf+6+32*2, ct_x, 32); -+ memcpy(buf+6+32*3, pk_x, 32); -+ sha3_256(out, buf, 6+32*4); -+} -+ -+void IPDWING_generate_key( -+ struct IPDWING_public_key *out_pub, -+ struct IPDWING_private_key *out_priv, -+ const uint8_t seed[IPDWING_GENERATE_KEY_BYTES]) { -+ KYBER768_generate_key( -+ &out_pub->m, -+ &out_priv->m, -+ seed); -+ memcpy(out_priv->x, seed+64, 32); -+ X25519_public_from_private(out_pub->x, out_priv->x); -+ memcpy(out_priv->xpub, out_pub->x, 32); -+} -+ -+void IPDWING_encap( -+ uint8_t out_ciphertext[IPDWING_CIPHERTEXT_BYTES], -+ uint8_t ss[IPDWING_KEY_BYTES], -+ const struct IPDWING_public_key *in_pub, -+ const uint8_t seed[IPDWING_ENCAP_BYTES]) { -+ -+ uint8_t ss_m[32]; -+ uint8_t ss_x[32]; -+ uint8_t *ct_x = out_ciphertext + KYBER768_CIPHERTEXT_BYTES; -+ const uint8_t *ek_x = seed + 32; -+ X25519_public_from_private(ct_x, ek_x); -+ -+ X25519( -+ ss_x, -+ ek_x, -+ in_pub->x -+ ); -+ -+ KYBER768_encap2( -+ out_ciphertext, -+ ss_m, -+ &in_pub->m, -+ seed, -+ 1 -+ ); -+ -+ combiner(ss, ss_m, ss_x, ct_x, in_pub->x); -+} -+ -+void IPDWING_decap( -+ uint8_t out_shared_key[IPDWING_KEY_BYTES], -+ const struct IPDWING_private_key *in_priv, -+ const uint8_t *ct) { -+ -+ uint8_t ss_m[32]; -+ uint8_t ss_x[32]; -+ const uint8_t *ct_x = ct + KYBER768_CIPHERTEXT_BYTES; -+ -+ KYBER768_decap2( -+ ss_m, -+ &in_priv->m, -+ ct, -+ KYBER768_CIPHERTEXT_BYTES, -+ 1 -+ ); -+ -+ X25519( -+ ss_x, -+ in_priv->x, -+ ct_x -+ ); -+ -+ combiner(out_shared_key, ss_m, ss_x, ct_x, in_priv->xpub); -+} -+ -+void IPDWING_marshal_public_key( -+ uint8_t out[IPDWING_PUBLIC_KEY_BYTES], -+ const struct IPDWING_public_key *in) { -+ KYBER768_marshal_public_key(out, &in->m); -+ memcpy(out + KYBER768_PUBLIC_KEY_BYTES, in->x, 32); -+} -+ -+void IPDWING_parse_public_key( -+ struct IPDWING_public_key *out, -+ const uint8_t in[IPDWING_PUBLIC_KEY_BYTES]) { -+ KYBER768_parse_public_key(&out->m, in); -+ memcpy(out->x, in + KYBER768_PUBLIC_KEY_BYTES, 32); -+} -+ +-// KYBER_encap_external_entropy is a deterministic function to encapsulate +-// |out_shared_secret_len| bytes of |out_shared_secret| to |ciphertext|, using +-// |KYBER_ENCAP_ENTROPY| bytes of |entropy| for randomization. The +-// decapsulating side will be able to recover |entropy| in full. This +-// function is should only be used for tests, regular callers should use the +-// non-deterministic |KYBER_encap| directly. +-OPENSSL_EXPORT void KYBER_encap_external_entropy( +- uint8_t out_ciphertext[KYBER_CIPHERTEXT_BYTES], uint8_t *out_shared_secret, +- size_t out_shared_secret_len, const struct KYBER_public_key *public_key, +- const uint8_t entropy[KYBER_ENCAP_ENTROPY]); +- +-#if defined(__cplusplus) +-} +-#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 @@ -1368,10 +492,10 @@ index f1c012d11..000000000 - } -} diff --git a/src/crypto/kyber/kyber.c b/src/crypto/kyber/kyber.c -index 776c085f9..5acd45cd9 100644 +index 776c085f9..346d4daec 100644 --- a/src/crypto/kyber/kyber.c +++ b/src/crypto/kyber/kyber.c -@@ -1,833 +1,1706 @@ +@@ -1,833 +1,2252 @@ -/* Copyright (c) 2023, Google Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any @@ -1390,1966 +514,2818 @@ index 776c085f9..5acd45cd9 100644 +// https://github.com/pq-crystals/kyber +// 8e00ec73035147d18b27d06048dff322f8de1f29 +// -+// with some small modifications: ++// with some small modifications: ++// ++// - Merged into one file. ++// - Removed 90s version. ++// - Seeds are passed as paramters. ++// - Changed the API to be more BoringSSL-like ++// ++// TODO ++// ++// - Optimizations ++// ++// The majority of Kyber's time is spent in keccak: generating the matrix ++// A, hashing the public key, et cetera. This can be sped up dramatically ++// by using a multiway keccak implementation such as f1600x4 on AVX2. ++// ++// Also the NTT and other operations can be sped up with SIMD. This is ++// more complex and the gains are more modest. See the avx2 reference ++// implementation or https://github.com/cloudflare/circl/tree/main/pke/kyber ++// ++// - Option to keep A stored in private key. ++ ++#ifndef KYBER_K ++#error "Don't compile this file direcly" ++#endif + + #include ++#include + +-#include +-#include +- +-#include +-#include +- +-#include "../internal.h" +-#include "./internal.h" +- +- +-// See +-// https://pq-crystals.org/kyber/data/kyber-specification-round3-20210804.pdf +- +-#define DEGREE 256 +-#define RANK 3 +- +-static const size_t kBarrettMultiplier = 5039; +-static const unsigned kBarrettShift = 24; +-static const uint16_t kPrime = 3329; +-static const int kLog2Prime = 12; +-static const uint16_t kHalfPrime = (/*kPrime=*/3329 - 1) / 2; +-static const int kDU = 10; +-static const int kDV = 4; +-// kInverseDegree is 128^-1 mod 3329; 128 because kPrime does not have a 512th +-// root of unity. +-static const uint16_t kInverseDegree = 3303; +-static const size_t kEncodedVectorSize = +- (/*kLog2Prime=*/12 * DEGREE / 8) * RANK; +-static const size_t kCompressedVectorSize = /*kDU=*/10 * RANK * DEGREE / 8; +- +-typedef struct scalar { +- // On every function entry and exit, 0 <= c < kPrime. +- uint16_t c[DEGREE]; +-} scalar; +- +-typedef struct vector { +- scalar v[RANK]; +-} vector; +- +-typedef struct matrix { +- scalar v[RANK][RANK]; +-} matrix; +- +-// This bit of Python will be referenced in some of the following comments: +-// +-// p = 3329 +-// +-// def bitreverse(i): +-// ret = 0 +-// for n in range(7): +-// bit = i & 1 +-// ret <<= 1 +-// ret |= bit +-// i >>= 1 +-// return ret +- +-// 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, +- 2786, 3260, 569, 1746, 296, 2447, 1339, 1476, 3046, 56, 2240, 1333, +- 1426, 2094, 535, 2882, 2393, 2879, 1974, 821, 289, 331, 3253, 1756, +- 1197, 2304, 2277, 2055, 650, 1977, 2513, 632, 2865, 33, 1320, 1915, +- 2319, 1435, 807, 452, 1438, 2868, 1534, 2402, 2647, 2617, 1481, 648, +- 2474, 3110, 1227, 910, 17, 2761, 583, 2649, 1637, 723, 2288, 1100, +- 1409, 2662, 3281, 233, 756, 2156, 3015, 3050, 1703, 1651, 2789, 1789, +- 1847, 952, 1461, 2687, 939, 2308, 2437, 2388, 733, 2337, 268, 641, +- 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, +-}; ++#include ++#include ++#include + +-// 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 */ ++ ++#define KYBER_POLYBYTES 384 ++#define KYBER_POLYVECBYTES (KYBER_K * KYBER_POLYBYTES) ++ ++#if KYBER_K == 2 ++#define KYBER_ETA1 3 ++#define KYBER_POLYCOMPRESSEDBYTES 128 ++#define KYBER_POLYVECCOMPRESSEDBYTES (KYBER_K * 320) ++#elif KYBER_K == 3 ++#define KYBER_ETA1 2 ++#define KYBER_POLYCOMPRESSEDBYTES 128 ++#define KYBER_POLYVECCOMPRESSEDBYTES (KYBER_K * 320) ++#elif KYBER_K == 4 ++#define KYBER_ETA1 2 ++#define KYBER_POLYCOMPRESSEDBYTES 160 ++#define KYBER_POLYVECCOMPRESSEDBYTES (KYBER_K * 352) ++#endif ++ ++#define KYBER_ETA2 2 ++ ++#define KYBER_INDCPA_MSGBYTES (KYBER_SYMBYTES) ++#define KYBER_INDCPA_PUBLICKEYBYTES (KYBER_POLYVECBYTES + KYBER_SYMBYTES) ++#define KYBER_INDCPA_SECRETKEYBYTES (KYBER_POLYVECBYTES) ++#define KYBER_INDCPA_BYTES (KYBER_POLYVECCOMPRESSEDBYTES + KYBER_POLYCOMPRESSEDBYTES) ++ ++#define KYBER_PUBLICKEYBYTES (KYBER_INDCPA_PUBLICKEYBYTES) ++/* 32 bytes of additional space to save H(pk) */ ++#define KYBER_SECRETKEYBYTES (KYBER_INDCPA_SECRETKEYBYTES + KYBER_INDCPA_PUBLICKEYBYTES + 2*KYBER_SYMBYTES) ++#define KYBER_CIPHERTEXTBYTES (KYBER_INDCPA_BYTES) ++ ++// ++// verify.h ++// ++static int verify(const uint8_t *a, const uint8_t *b, size_t len); ++static void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b); ++ ++// ++// reduce.h +// -+// - Merged into one file. -+// - Removed 90s version. -+// - Seeds are passed as paramters. -+// - Changed the API to be more BoringSSL-like ++#define MONT -1044 // 2^16 mod q ++#define QINV -3327 // q^-1 mod 2^16 ++ ++static int16_t montgomery_reduce(int32_t a); ++static int16_t barrett_reduce(int16_t a); ++ +// -+// TODO ++// ntt.h +// -+// - Optimizations ++static void ntt(int16_t poly[256]); ++static void invntt(int16_t poly[256]); ++static void basemul(int16_t r[2], const int16_t a[2], const int16_t b[2], int16_t zeta); ++ +// -+// The majority of Kyber's time is spent in keccak: generating the matrix -+// A, hashing the public key, et cetera. This can be sped up dramatically -+// by using a multiway keccak implementation such as f1600x4 on AVX2. ++// poly.h +// -+// Also the NTT and other operations can be sped up with SIMD. This is -+// more complex and the gains are more modest. See the avx2 reference -+// implementation or https://github.com/cloudflare/circl/tree/main/pke/kyber ++ ++/* ++ * Elements of R_q = Z_q[X]/(X^n + 1). Represents polynomial ++ * coeffs[0] + X*coeffs[1] + X^2*xoeffs[2] + ... + X^{n-1}*coeffs[n-1] ++ */ ++typedef struct{ ++ int16_t coeffs[KYBER_N]; ++} poly; ++ ++static void poly_compress(uint8_t r[KYBER_POLYCOMPRESSEDBYTES], const poly *a); ++static void poly_decompress(poly *r, const uint8_t a[KYBER_POLYCOMPRESSEDBYTES]); ++ ++static void poly_tobytes(uint8_t r[KYBER_POLYBYTES], const poly *a); ++static void poly_frombytes(poly *r, const uint8_t a[KYBER_POLYBYTES]); ++ ++static void poly_frommsg(poly *r, const uint8_t msg[KYBER_INDCPA_MSGBYTES]); ++static void poly_tomsg(uint8_t msg[KYBER_INDCPA_MSGBYTES], const poly *r); ++ ++static void poly_getnoise_eta1(poly *r, const uint8_t seed[KYBER_SYMBYTES], uint8_t nonce); ++static void poly_getnoise_eta2(poly *r, const uint8_t seed[KYBER_SYMBYTES], uint8_t nonce); ++ ++static void poly_ntt(poly *r); ++static void poly_invntt_tomont(poly *r); ++static void poly_basemul_montgomery(poly *r, const poly *a, const poly *b); ++static void poly_tomont(poly *r); ++ ++static void poly_reduce(poly *r); ++ ++static void poly_add(poly *r, const poly *a, const poly *b); ++static void poly_sub(poly *r, const poly *a, const poly *b); ++ +// -+// - Option to keep A stored in private key. - --#include -+#ifndef KYBER_K -+#error "Don't compile this file direcly" ++// cbd.h ++// ++static void poly_cbd_eta1(poly *r, const uint8_t buf[KYBER_ETA1*KYBER_N/4]); ++static void poly_cbd_eta2(poly *r, const uint8_t buf[KYBER_ETA2*KYBER_N/4]); ++ ++// ++// polyvec.h ++// ++ ++typedef struct{ ++ poly vec[KYBER_K]; ++} polyvec; ++ ++static void polyvec_compress(uint8_t r[KYBER_POLYVECCOMPRESSEDBYTES], const polyvec *a); ++static void polyvec_decompress(polyvec *r, const uint8_t a[KYBER_POLYVECCOMPRESSEDBYTES]); ++ ++static void polyvec_tobytes(uint8_t r[KYBER_POLYVECBYTES], const polyvec *a); ++static void polyvec_frombytes(polyvec *r, const uint8_t a[KYBER_POLYVECBYTES]); ++ ++static void polyvec_ntt(polyvec *r); ++static void polyvec_invntt_tomont(polyvec *r); ++ ++static void polyvec_basemul_acc_montgomery(poly *r, const polyvec *a, const polyvec *b); ++ ++static void polyvec_reduce(polyvec *r); ++ ++static void polyvec_add(polyvec *r, const polyvec *a, const polyvec *b); ++ ++// ++// indcpa.h ++// ++ ++static void gen_matrix(polyvec *a, const uint8_t seed[KYBER_SYMBYTES], int transposed); ++static void indcpa_keypair(uint8_t pk[KYBER_INDCPA_PUBLICKEYBYTES], ++ uint8_t sk[KYBER_INDCPA_SECRETKEYBYTES], ++ const uint8_t seed[KYBER_SYMBYTES]); ++ ++static void indcpa_enc(uint8_t c[KYBER_INDCPA_BYTES], ++ const uint8_t m[KYBER_INDCPA_MSGBYTES], ++ const uint8_t pk[KYBER_INDCPA_PUBLICKEYBYTES], ++ const uint8_t coins[KYBER_SYMBYTES]); ++ ++static void indcpa_dec(uint8_t m[KYBER_INDCPA_MSGBYTES], ++ const uint8_t c[KYBER_INDCPA_BYTES], ++ const uint8_t sk[KYBER_INDCPA_SECRETKEYBYTES]); ++ ++// ++// fips202.h ++// ++ ++#define SHAKE128_RATE 168 ++#define SHAKE256_RATE 136 ++#define SHA3_256_RATE 136 ++#define SHA3_512_RATE 72 ++ ++typedef struct { ++ uint64_t s[25]; ++ unsigned int pos; ++} keccak_state; ++ ++static void shake128_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen); ++static void shake128_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state); ++ ++static void shake256_squeeze(uint8_t *out, size_t outlen, keccak_state *state); ++static void shake256_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen); ++static void shake256_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state); ++ ++static void shake256(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen); ++static void sha3_256(uint8_t h[32], const uint8_t *in, size_t inlen); ++static void sha3_512(uint8_t h[64], const uint8_t *in, size_t inlen); ++ ++// ++// symmetric.h ++// ++ ++typedef keccak_state xof_state; ++ ++static void kyber_shake128_absorb(keccak_state *s, ++ const uint8_t seed[KYBER_SYMBYTES], ++ uint8_t x, ++ uint8_t y); ++ ++static void kyber_shake256_prf(uint8_t *out, size_t outlen, const uint8_t key[KYBER_SYMBYTES], uint8_t nonce); ++ ++#define XOF_BLOCKBYTES SHAKE128_RATE ++ ++#define hash_h(OUT, IN, INBYTES) sha3_256(OUT, IN, INBYTES) ++#define hash_g(OUT, IN, INBYTES) sha3_512(OUT, IN, INBYTES) ++#define xof_absorb(STATE, SEED, X, Y) kyber_shake128_absorb(STATE, SEED, X, Y) ++#define xof_squeezeblocks(OUT, OUTBLOCKS, STATE) shake128_squeezeblocks(OUT, OUTBLOCKS, STATE) ++#define prf(OUT, OUTBYTES, KEY, NONCE) kyber_shake256_prf(OUT, OUTBYTES, KEY, NONCE) ++#define kdf(OUT, IN, INBYTES) shake256(OUT, KYBER_SSBYTES, IN, INBYTES) ++ ++ ++// ++// verify.c ++// ++ ++/************************************************* ++* Name: verify ++* ++* Description: Compare two arrays for equality in constant time. ++* ++* Arguments: const uint8_t *a: pointer to first byte array ++* const uint8_t *b: pointer to second byte array ++* size_t len: length of the byte arrays ++* ++* Returns 0 if the byte arrays are equal, 1 otherwise ++**************************************************/ ++static int verify(const uint8_t *a, const uint8_t *b, size_t len) ++{ ++ size_t i; ++ uint8_t r = 0; ++ ++ for(i=0;i> 63; ++} ++ ++/************************************************* ++* Name: cmov ++* ++* Description: Copy len bytes from x to r if b is 1; ++* don't modify x if b is 0. Requires b to be in {0,1}; ++* assumes two's complement representation of negative integers. ++* Runs in constant time. ++* ++* Arguments: uint8_t *r: pointer to output byte array ++* const uint8_t *x: pointer to input byte array ++* size_t len: Amount of bytes to be copied ++* uint8_t b: Condition bit; has to be in {0,1} ++**************************************************/ ++static void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b) ++{ ++ size_t i; ++ ++ b = -b; ++ for(i=0;i> 16; ++ return t; ++} ++ ++/************************************************* ++* Name: barrett_reduce ++* ++* Description: Barrett reduction; given a 16-bit integer a, computes ++* centered representative congruent to a mod q in {-(q-1)/2,...,(q-1)/2} ++* ++* Arguments: - int16_t a: input integer to be reduced ++* ++* Returns: integer in {-(q-1)/2,...,(q-1)/2} congruent to a modulo q. ++**************************************************/ ++static int16_t barrett_reduce(int16_t a) { ++ int16_t t; ++ const int16_t v = ((1<<26) + KYBER_Q/2)/KYBER_Q; ++ ++ t = ((int32_t)v*a + (1<<25)) >> 26; ++ t *= KYBER_Q; ++ return a - t; ++} ++ ++// ++// cbd.c ++// ++ ++/************************************************* ++* Name: load32_littleendian ++* ++* Description: load 4 bytes into a 32-bit integer ++* in little-endian order ++* ++* Arguments: - const uint8_t *x: pointer to input byte array ++* ++* Returns 32-bit unsigned integer loaded from x ++**************************************************/ ++static uint32_t load32_littleendian(const uint8_t x[4]) ++{ ++ uint32_t r; ++ r = (uint32_t)x[0]; ++ r |= (uint32_t)x[1] << 8; ++ r |= (uint32_t)x[2] << 16; ++ r |= (uint32_t)x[3] << 24; ++ return r; ++} ++ ++/************************************************* ++* Name: load24_littleendian ++* ++* Description: load 3 bytes into a 32-bit integer ++* in little-endian order. ++* This function is only needed for Kyber-512 ++* ++* Arguments: - const uint8_t *x: pointer to input byte array ++* ++* Returns 32-bit unsigned integer loaded from x (most significant byte is zero) ++**************************************************/ ++#if KYBER_ETA1 == 3 ++static uint32_t load24_littleendian(const uint8_t x[3]) ++{ ++ uint32_t r; ++ r = (uint32_t)x[0]; ++ r |= (uint32_t)x[1] << 8; ++ r |= (uint32_t)x[2] << 16; ++ return r; ++} +#endif ++ ++ ++/************************************************* ++* Name: cbd2 ++* ++* Description: Given an array of uniformly random bytes, compute ++* polynomial with coefficients distributed according to ++* a centered binomial distribution with parameter eta=2 ++* ++* Arguments: - poly *r: pointer to output polynomial ++* - const uint8_t *buf: pointer to input byte array ++**************************************************/ ++static void cbd2(poly *r, const uint8_t buf[2*KYBER_N/4]) ++{ ++ unsigned int i,j; ++ uint32_t t,d; ++ int16_t a,b; ++ ++ for(i=0;i>1) & 0x55555555; ++ ++ for(j=0;j<8;j++) { ++ a = (d >> (4*j+0)) & 0x3; ++ b = (d >> (4*j+2)) & 0x3; ++ r->coeffs[8*i+j] = a - b; + } + } + } --#include --#include -+#include -+#include - --#include --#include -+#include -+#include -+#include - -+#include "fips202.h" -+#include "kyber.h" - #include "../internal.h" --#include "./internal.h" -- -- --// See --// https://pq-crystals.org/kyber/data/kyber-specification-round3-20210804.pdf -- --#define DEGREE 256 --#define RANK 3 -- --static const size_t kBarrettMultiplier = 5039; --static const unsigned kBarrettShift = 24; --static const uint16_t kPrime = 3329; --static const int kLog2Prime = 12; --static const uint16_t kHalfPrime = (/*kPrime=*/3329 - 1) / 2; --static const int kDU = 10; --static const int kDV = 4; --// kInverseDegree is 128^-1 mod 3329; 128 because kPrime does not have a 512th --// root of unity. --static const uint16_t kInverseDegree = 3303; --static const size_t kEncodedVectorSize = -- (/*kLog2Prime=*/12 * DEGREE / 8) * RANK; --static const size_t kCompressedVectorSize = /*kDU=*/10 * RANK * DEGREE / 8; -- --typedef struct scalar { -- // On every function entry and exit, 0 <= c < kPrime. -- uint16_t c[DEGREE]; --} scalar; -- --typedef struct vector { -- scalar v[RANK]; --} vector; -- --typedef struct matrix { -- scalar v[RANK][RANK]; --} matrix; -- --// This bit of Python will be referenced in some of the following comments: --// --// p = 3329 --// --// def bitreverse(i): --// ret = 0 --// for n in range(7): --// bit = i & 1 --// ret <<= 1 --// ret |= bit --// i >>= 1 --// return ret -- --// 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, -- 2786, 3260, 569, 1746, 296, 2447, 1339, 1476, 3046, 56, 2240, 1333, -- 1426, 2094, 535, 2882, 2393, 2879, 1974, 821, 289, 331, 3253, 1756, -- 1197, 2304, 2277, 2055, 650, 1977, 2513, 632, 2865, 33, 1320, 1915, -- 2319, 1435, 807, 452, 1438, 2868, 1534, 2402, 2647, 2617, 1481, 648, -- 2474, 3110, 1227, 910, 17, 2761, 583, 2649, 1637, 723, 2288, 1100, -- 1409, 2662, 3281, 233, 756, 2156, 3015, 3050, 1703, 1651, 2789, 1789, -- 1847, 952, 1461, 2687, 939, 2308, 2437, 2388, 733, 2337, 268, 641, -- 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 +-static void vector_ntt(vector *a) { +- for (int i = 0; i < RANK; i++) { +- scalar_ntt(&a->v[i]); ++/************************************************* ++* Name: cbd3 ++* ++* Description: Given an array of uniformly random bytes, compute ++* polynomial with coefficients distributed according to ++* a centered binomial distribution with parameter eta=3. ++* This function is only needed for Kyber-512 ++* ++* Arguments: - poly *r: pointer to output polynomial ++* - const uint8_t *buf: pointer to input byte array ++**************************************************/ ++#if KYBER_ETA1 == 3 ++static void cbd3(poly *r, const uint8_t buf[3*KYBER_N/4]) ++{ ++ unsigned int i,j; ++ uint32_t t,d; ++ int16_t a,b; ++ ++ for(i=0;i>1) & 0x00249249; ++ d += (t>>2) & 0x00249249; ++ ++ for(j=0;j<4;j++) { ++ a = (d >> (6*j+0)) & 0x7; ++ b = (d >> (6*j+3)) & 0x7; ++ r->coeffs[4*i+j] = a - b; ++ } + } + } ++#endif ++ ++static void poly_cbd_eta1(poly *r, const uint8_t buf[KYBER_ETA1*KYBER_N/4]) ++{ ++#if KYBER_ETA1 == 2 ++ cbd2(r, buf); ++#elif KYBER_ETA1 == 3 ++ cbd3(r, buf); +#else -+#error "KYBER_K must be in {2,3,4}" ++#error "This implementation requires eta1 in {2,3}" +#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; +-// 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 step = 1; step < DEGREE / 2; step <<= 1) { -- offset >>= 1; +- for (int offset = 2; offset < DEGREE; offset <<= 1) { +- step >>= 1; - int k = 0; - for (int i = 0; i < step; i++) { -- const uint32_t step_root = kNTTRoots[i + step]; +- uint32_t step_root = kInverseNTTRoots[i + step]; - for (int j = k; j < k + offset; j++) { -- uint16_t odd = reduce(step_root * s->c[j + offset]); +- 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_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) -+ -+#define decap2 KYBER_NAMESPACE(decap2) -+#define encap2 KYBER_NAMESPACE(encap2) -+ -+ -+// -+// params.h -+// -+#define KYBER_N 256 -+#define KYBER_Q 3329 -+ -+#define KYBER_SYMBYTES 32 /* size in bytes of hashes, and seeds */ -+#define KYBER_SSBYTES 32 /* size in bytes of shared key */ -+ -+#define KYBER_POLYBYTES 384 -+#define KYBER_POLYVECBYTES (KYBER_K * KYBER_POLYBYTES) -+ -+#if KYBER_K == 2 -+#define KYBER_ETA1 3 -+#define KYBER_POLYCOMPRESSEDBYTES 128 -+#define KYBER_POLYVECCOMPRESSEDBYTES (KYBER_K * 320) -+#elif KYBER_K == 3 -+#define KYBER_ETA1 2 -+#define KYBER_POLYCOMPRESSEDBYTES 128 -+#define KYBER_POLYVECCOMPRESSEDBYTES (KYBER_K * 320) -+#elif KYBER_K == 4 -+#define KYBER_ETA1 2 -+#define KYBER_POLYCOMPRESSEDBYTES 160 -+#define KYBER_POLYVECCOMPRESSEDBYTES (KYBER_K * 352) +- s->c[j + offset] = reduce(step_root * (even - odd + kPrime)); ++static void poly_cbd_eta2(poly *r, const uint8_t buf[KYBER_ETA2*KYBER_N/4]) ++{ ++#if KYBER_ETA2 == 2 ++ cbd2(r, buf); ++#else ++#error "This implementation requires eta2 = 2" +#endif -+ -+#define KYBER_ETA2 2 -+ -+#define KYBER_INDCPA_MSGBYTES (KYBER_SYMBYTES) -+#define KYBER_INDCPA_PUBLICKEYBYTES (KYBER_POLYVECBYTES + KYBER_SYMBYTES) -+#define KYBER_INDCPA_SECRETKEYBYTES (KYBER_POLYVECBYTES) -+#define KYBER_INDCPA_BYTES (KYBER_POLYVECCOMPRESSEDBYTES + KYBER_POLYCOMPRESSEDBYTES) -+ -+#define KYBER_PUBLICKEYBYTES (KYBER_INDCPA_PUBLICKEYBYTES) -+/* 32 bytes of additional space to save H(pk) */ -+#define KYBER_SECRETKEYBYTES (KYBER_INDCPA_SECRETKEYBYTES + KYBER_INDCPA_PUBLICKEYBYTES + 2*KYBER_SYMBYTES) -+#define KYBER_CIPHERTEXTBYTES (KYBER_INDCPA_BYTES) -+ -+// -+// verify.h -+// -+static int verify(const uint8_t *a, const uint8_t *b, size_t len); -+static void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b); -+ -+// -+// reduce.h -+// -+#define MONT -1044 // 2^16 mod q -+#define QINV -3327 // q^-1 mod 2^16 -+ -+static int16_t montgomery_reduce(int32_t a); -+static int16_t barrett_reduce(int16_t a); -+ -+// -+// ntt.h -+// -+static void ntt(int16_t poly[256]); -+static void invntt(int16_t poly[256]); -+static void basemul(int16_t r[2], const int16_t a[2], const int16_t b[2], int16_t zeta); -+ -+// -+// poly.h -+// -+ -+/* -+ * Elements of R_q = Z_q[X]/(X^n + 1). Represents polynomial -+ * coeffs[0] + X*coeffs[1] + X^2*xoeffs[2] + ... + X^{n-1}*coeffs[n-1] -+ */ -+typedef struct{ -+ int16_t coeffs[KYBER_N]; -+} poly; -+ -+static void poly_compress(uint8_t r[KYBER_POLYCOMPRESSEDBYTES], const poly *a); -+static void poly_decompress(poly *r, const uint8_t a[KYBER_POLYCOMPRESSEDBYTES]); -+ -+static void poly_tobytes(uint8_t r[KYBER_POLYBYTES], const poly *a); -+static void poly_frombytes(poly *r, const uint8_t a[KYBER_POLYBYTES]); -+ -+static void poly_frommsg(poly *r, const uint8_t msg[KYBER_INDCPA_MSGBYTES]); -+static void poly_tomsg(uint8_t msg[KYBER_INDCPA_MSGBYTES], const poly *r); -+ -+static void poly_getnoise_eta1(poly *r, const uint8_t seed[KYBER_SYMBYTES], uint8_t nonce); -+static void poly_getnoise_eta2(poly *r, const uint8_t seed[KYBER_SYMBYTES], uint8_t nonce); -+ -+static void poly_ntt(poly *r); -+static void poly_invntt_tomont(poly *r); -+static void poly_basemul_montgomery(poly *r, const poly *a, const poly *b); -+static void poly_tomont(poly *r); -+ -+static void poly_reduce(poly *r); -+ -+static void poly_add(poly *r, const poly *a, const poly *b); -+static void poly_sub(poly *r, const poly *a, const poly *b); ++} + +// -+// cbd.h ++// ntt.c +// -+static void poly_cbd_eta1(poly *r, const uint8_t buf[KYBER_ETA1*KYBER_N/4]); -+static void poly_cbd_eta2(poly *r, const uint8_t buf[KYBER_ETA2*KYBER_N/4]); + -+// -+// polyvec.h -+// ++/* Code to generate zetas and zetas_inv used in the number-theoretic transform: + -+typedef struct{ -+ poly vec[KYBER_K]; -+} polyvec; ++#define KYBER_ROOT_OF_UNITY 17 + -+static void polyvec_compress(uint8_t r[KYBER_POLYVECCOMPRESSEDBYTES], const polyvec *a); -+static void polyvec_decompress(polyvec *r, const uint8_t a[KYBER_POLYVECCOMPRESSEDBYTES]); ++static const uint8_t tree[128] = { ++ 0, 64, 32, 96, 16, 80, 48, 112, 8, 72, 40, 104, 24, 88, 56, 120, ++ 4, 68, 36, 100, 20, 84, 52, 116, 12, 76, 44, 108, 28, 92, 60, 124, ++ 2, 66, 34, 98, 18, 82, 50, 114, 10, 74, 42, 106, 26, 90, 58, 122, ++ 6, 70, 38, 102, 22, 86, 54, 118, 14, 78, 46, 110, 30, 94, 62, 126, ++ 1, 65, 33, 97, 17, 81, 49, 113, 9, 73, 41, 105, 25, 89, 57, 121, ++ 5, 69, 37, 101, 21, 85, 53, 117, 13, 77, 45, 109, 29, 93, 61, 125, ++ 3, 67, 35, 99, 19, 83, 51, 115, 11, 75, 43, 107, 27, 91, 59, 123, ++ 7, 71, 39, 103, 23, 87, 55, 119, 15, 79, 47, 111, 31, 95, 63, 127 ++}; + -+static void polyvec_tobytes(uint8_t r[KYBER_POLYVECBYTES], const polyvec *a); -+static void polyvec_frombytes(polyvec *r, const uint8_t a[KYBER_POLYVECBYTES]); ++void init_ntt() { ++ unsigned int i; ++ int16_t tmp[128]; + -+static void polyvec_ntt(polyvec *r); -+static void polyvec_invntt_tomont(polyvec *r); ++ tmp[0] = MONT; ++ for(i=1;i<128;i++) ++ tmp[i] = fqmul(tmp[i-1],MONT*KYBER_ROOT_OF_UNITY % KYBER_Q); + -+static void polyvec_basemul_acc_montgomery(poly *r, const polyvec *a, const polyvec *b); ++ for(i=0;i<128;i++) { ++ zetas[i] = tmp[tree[i]]; ++ if(zetas[i] > KYBER_Q/2) ++ zetas[i] -= KYBER_Q; ++ if(zetas[i] < -KYBER_Q/2) ++ zetas[i] += KYBER_Q; ++ } ++} ++*/ + -+static void polyvec_reduce(polyvec *r); ++static const int16_t zetas[128] = { ++ -1044, -758, -359, -1517, 1493, 1422, 287, 202, ++ -171, 622, 1577, 182, 962, -1202, -1474, 1468, ++ 573, -1325, 264, 383, -829, 1458, -1602, -130, ++ -681, 1017, 732, 608, -1542, 411, -205, -1571, ++ 1223, 652, -552, 1015, -1293, 1491, -282, -1544, ++ 516, -8, -320, -666, -1618, -1162, 126, 1469, ++ -853, -90, -271, 830, 107, -1421, -247, -951, ++ -398, 961, -1508, -725, 448, -1065, 677, -1275, ++ -1103, 430, 555, 843, -1251, 871, 1550, 105, ++ 422, 587, 177, -235, -291, -460, 1574, 1653, ++ -246, 778, 1159, -147, -777, 1483, -602, 1119, ++ -1590, 644, -872, 349, 418, 329, -156, -75, ++ 817, 1097, 603, 610, 1322, -1285, -1465, 384, ++ -1215, -136, 1218, -1335, -874, 220, -1187, -1659, ++ -1185, -1530, -1278, 794, -1510, -854, -870, 478, ++ -108, -308, 996, 991, 958, -1460, 1522, 1628 ++}; + -+static void polyvec_add(polyvec *r, const polyvec *a, const polyvec *b); ++/************************************************* ++* Name: fqmul ++* ++* Description: Multiplication followed by Montgomery reduction ++* ++* Arguments: - int16_t a: first factor ++* - int16_t b: second factor ++* ++* Returns 16-bit integer congruent to a*b*R^{-1} mod q ++**************************************************/ ++static int16_t fqmul(int16_t a, int16_t b) { ++ return montgomery_reduce((int32_t)a*b); ++} + -+// -+// indcpa.h -+// ++/************************************************* ++* Name: ntt ++* ++* Description: Inplace number-theoretic transform (NTT) in Rq. ++* input is in standard order, output is in bitreversed order ++* ++* Arguments: - int16_t r[256]: pointer to input/output vector of elements of Zq ++**************************************************/ ++static void ntt(int16_t r[256]) { ++ unsigned int len, start, j, k; ++ int16_t t, zeta; + -+static void gen_matrix(polyvec *a, const uint8_t seed[KYBER_SYMBYTES], int transposed); -+static void indcpa_keypair(uint8_t pk[KYBER_INDCPA_PUBLICKEYBYTES], -+ uint8_t sk[KYBER_INDCPA_SECRETKEYBYTES], -+ const uint8_t seed[KYBER_SYMBYTES]); ++ k = 1; ++ for(len = 128; len >= 2; len >>= 1) { ++ for(start = 0; start < 256; start = j + len) { ++ zeta = zetas[k++]; ++ for(j = start; j < start + len; j++) { ++ t = fqmul(zeta, r[j + len]); ++ r[j + len] = r[j] - t; ++ r[j] = r[j] + t; + } +- 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 ++* ++* Description: Inplace inverse number-theoretic transform in Rq and ++* multiplication by Montgomery factor 2^16. ++* Input is in bitreversed order, output is in standard order ++* ++* Arguments: - int16_t r[256]: pointer to input/output vector of elements of Zq ++**************************************************/ ++static void invntt(int16_t r[256]) { ++ unsigned int start, len, j, k; ++ int16_t t, zeta; ++ const int16_t f = 1441; // mont^2/128 + -+static void indcpa_enc(uint8_t c[KYBER_INDCPA_BYTES], -+ const uint8_t m[KYBER_INDCPA_MSGBYTES], -+ const uint8_t pk[KYBER_INDCPA_PUBLICKEYBYTES], -+ const uint8_t coins[KYBER_SYMBYTES]); ++ k = 127; ++ for(len = 2; len <= 128; len <<= 1) { ++ for(start = 0; start < 256; start = j + len) { ++ zeta = zetas[k--]; ++ for(j = start; j < start + len; j++) { ++ t = r[j]; ++ r[j] = barrett_reduce(t + r[j + len]); ++ r[j + len] = r[j + len] - t; ++ r[j + len] = fqmul(zeta, r[j + len]); ++ } ++ } + } + -+static void indcpa_dec(uint8_t m[KYBER_INDCPA_MSGBYTES], -+ const uint8_t c[KYBER_INDCPA_BYTES], -+ const uint8_t sk[KYBER_INDCPA_SECRETKEYBYTES]); ++ for(j = 0; j < 256; j++) ++ r[j] = fqmul(r[j], f); ++} + ++/************************************************* ++* Name: basemul ++* ++* Description: Multiplication of polynomials in Zq[X]/(X^2-zeta) ++* used for multiplication of elements in Rq in NTT domain ++* ++* Arguments: - int16_t r[2]: pointer to the output polynomial ++* - const int16_t a[2]: pointer to the first factor ++* - const int16_t b[2]: pointer to the second factor ++* - int16_t zeta: integer defining the reduction polynomial ++**************************************************/ ++static void basemul(int16_t r[2], const int16_t a[2], const int16_t b[2], int16_t zeta) ++{ ++ r[0] = fqmul(a[1], b[1]); ++ r[0] = fqmul(r[0], zeta); ++ r[0] += fqmul(a[0], b[0]); ++ r[1] = fqmul(a[0], b[1]); ++ r[1] += fqmul(a[1], b[0]); + } + +-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]); +// -+// fips202.h ++// poly.c +// + ++/************************************************* ++* Name: poly_compress ++* ++* Description: Compression and subsequent serialization of a polynomial ++* ++* Arguments: - uint8_t *r: pointer to output byte array ++* (of length KYBER_POLYCOMPRESSEDBYTES) ++* - const poly *a: pointer to input polynomial ++**************************************************/ ++static void poly_compress(uint8_t r[KYBER_POLYCOMPRESSEDBYTES], const poly *a) ++{ ++ unsigned int i,j; ++ int16_t u; ++ uint8_t t[8]; + -+typedef keccak_state xof_state; ++#if (KYBER_POLYCOMPRESSEDBYTES == 128) ++ for(i=0;icoeffs[8*i+j]; ++ u += (u >> 15) & KYBER_Q; ++ t[j] = ((((uint16_t)u << 4) + KYBER_Q/2)/KYBER_Q) & 15; ++ } + -+static void kyber_shake128_absorb(keccak_state *s, -+ const uint8_t seed[KYBER_SYMBYTES], -+ uint8_t x, -+ uint8_t y); ++ r[0] = t[0] | (t[1] << 4); ++ r[1] = t[2] | (t[3] << 4); ++ r[2] = t[4] | (t[5] << 4); ++ r[3] = t[6] | (t[7] << 4); ++ r += 4; + } ++#elif (KYBER_POLYCOMPRESSEDBYTES == 160) ++ for(i=0;icoeffs[8*i+j]; ++ u += (u >> 15) & KYBER_Q; ++ t[j] = ((((uint32_t)u << 5) + KYBER_Q/2)/KYBER_Q) & 31; ++ } + -+static void kyber_shake256_prf(uint8_t *out, size_t outlen, const uint8_t key[KYBER_SYMBYTES], uint8_t nonce); ++ r[0] = (t[0] >> 0) | (t[1] << 5); ++ r[1] = (t[1] >> 3) | (t[2] << 2) | (t[3] << 7); ++ r[2] = (t[3] >> 1) | (t[4] << 4); ++ r[3] = (t[4] >> 4) | (t[5] << 1) | (t[6] << 6); ++ r[4] = (t[6] >> 2) | (t[7] << 3); ++ r += 5; ++ } ++#else ++#error "KYBER_POLYCOMPRESSEDBYTES needs to be in {128, 160}" ++#endif + } + +-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: poly_decompress ++* ++* Description: De-serialization and subsequent decompression of a polynomial; ++* approximate inverse of poly_compress ++* ++* Arguments: - poly *r: pointer to output polynomial ++* - const uint8_t *a: pointer to input byte array ++* (of length KYBER_POLYCOMPRESSEDBYTES bytes) ++**************************************************/ ++static void poly_decompress(poly *r, const uint8_t a[KYBER_POLYCOMPRESSEDBYTES]) ++{ ++ unsigned int i; + ++#if (KYBER_POLYCOMPRESSEDBYTES == 128) ++ for(i=0;icoeffs[2*i+0] = (((uint16_t)(a[0] & 15)*KYBER_Q) + 8) >> 4; ++ r->coeffs[2*i+1] = (((uint16_t)(a[0] >> 4)*KYBER_Q) + 8) >> 4; ++ a += 1; ++ } ++#elif (KYBER_POLYCOMPRESSEDBYTES == 160) ++ unsigned int j; ++ uint8_t t[8]; ++ for(i=0;i> 0); ++ t[1] = (a[0] >> 5) | (a[1] << 3); ++ t[2] = (a[1] >> 2); ++ t[3] = (a[1] >> 7) | (a[2] << 1); ++ t[4] = (a[2] >> 4) | (a[3] << 4); ++ t[5] = (a[3] >> 1); ++ t[6] = (a[3] >> 6) | (a[4] << 2); ++ t[7] = (a[4] >> 3); ++ a += 5; + -+#define XOF_BLOCKBYTES SHAKE128_RATE ++ 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 ++* ++* Description: Serialization of a polynomial ++* ++* Arguments: - uint8_t *r: pointer to output byte array ++* (needs space for KYBER_POLYBYTES bytes) ++* - const poly *a: pointer to input polynomial ++**************************************************/ ++static void poly_tobytes(uint8_t r[KYBER_POLYBYTES], const poly *a) ++{ ++ unsigned int i; ++ uint16_t t0, t1; + -+#define hash_h(OUT, IN, INBYTES) sha3_256(OUT, IN, INBYTES) -+#define hash_g(OUT, IN, INBYTES) sha3_512(OUT, IN, INBYTES) -+#define xof_absorb(STATE, SEED, X, Y) kyber_shake128_absorb(STATE, SEED, X, Y) -+#define xof_squeezeblocks(OUT, OUTBLOCKS, STATE) shake128_squeezeblocks(OUT, OUTBLOCKS, STATE) -+#define prf(OUT, OUTBYTES, KEY, NONCE) kyber_shake256_prf(OUT, OUTBYTES, KEY, NONCE) -+#define kdf(OUT, IN, INBYTES) shake256(OUT, KYBER_SSBYTES, IN, INBYTES) ++ for(i=0;icoeffs[2*i]; ++ t0 += ((int16_t)t0 >> 15) & KYBER_Q; ++ t1 = a->coeffs[2*i+1]; ++ t1 += ((int16_t)t1 >> 15) & KYBER_Q; ++ r[3*i+0] = (t0 >> 0); ++ r[3*i+1] = (t0 >> 8) | (t1 << 4); ++ r[3*i+2] = (t1 >> 4); + } + } + +-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 ++* ++* Description: De-serialization of a polynomial; ++* inverse of poly_tobytes ++* ++* Arguments: - poly *r: pointer to output polynomial ++* - const uint8_t *a: pointer to input byte array ++* (of KYBER_POLYBYTES bytes) ++**************************************************/ ++static void poly_frombytes(poly *r, const uint8_t a[KYBER_POLYBYTES]) ++{ ++ unsigned int i; ++ for(i=0;icoeffs[2*i] = ((a[3*i+0] >> 0) | ((uint16_t)a[3*i+1] << 8)) & 0xFFF; ++ r->coeffs[2*i+1] = ((a[3*i+1] >> 4) | ((uint16_t)a[3*i+2] << 4)) & 0xFFF; + } + } + +-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 ++* ++* Description: Convert 32-byte message to polynomial ++* ++* Arguments: - poly *r: pointer to output polynomial ++* - const uint8_t *msg: pointer to input message ++**************************************************/ ++static void poly_frommsg(poly *r, const uint8_t msg[KYBER_INDCPA_MSGBYTES]) ++{ ++ unsigned int i,j; ++ int16_t mask; + ++#if (KYBER_INDCPA_MSGBYTES != KYBER_N/8) ++#error "KYBER_INDCPA_MSGBYTES must be equal to KYBER_N/8 bytes!" ++#endif + -+// -+// verify.c -+// ++ for(i=0;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 ++* ++* Description: Convert polynomial to 32-byte message ++* ++* Arguments: - uint8_t *msg: pointer to output message ++* - const poly *a: pointer to input polynomial ++**************************************************/ ++static void poly_tomsg(uint8_t msg[KYBER_INDCPA_MSGBYTES], const poly *a) ++{ ++ unsigned int i,j; ++ uint16_t t; + ++ for(i=0;icoeffs[8*i+j]; ++ t += ((int16_t)t >> 15) & KYBER_Q; ++ t = (((t << 1) + KYBER_Q/2)/KYBER_Q) & 1; ++ msg[i] |= t << j; + } + } + } + +-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: verify -+* -+* Description: Compare two arrays for equality in constant time. ++* Name: poly_getnoise_eta1 +* -+* Arguments: const uint8_t *a: pointer to first byte array -+* const uint8_t *b: pointer to second byte array -+* size_t len: length of the byte arrays ++* Description: Sample a polynomial deterministically from a seed and a nonce, ++* with output polynomial close to centered binomial distribution ++* with parameter KYBER_ETA1 +* -+* Returns 0 if the byte arrays are equal, 1 otherwise ++* Arguments: - poly *r: pointer to output polynomial ++* - const uint8_t *seed: pointer to input seed ++* (of length KYBER_SYMBYTES bytes) ++* - uint8_t nonce: one-byte input nonce +**************************************************/ -+static int verify(const uint8_t *a, const uint8_t *b, size_t len) ++static void poly_getnoise_eta1(poly *r, const uint8_t seed[KYBER_SYMBYTES], uint8_t nonce) +{ -+ size_t i; -+ uint8_t r = 0; -+ -+ for(i=0;i> 63; -+} -+ ++ 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: cmov ++* Name: poly_getnoise_eta2 +* -+* Description: Copy len bytes from x to r if b is 1; -+* don't modify x if b is 0. Requires b to be in {0,1}; -+* assumes two's complement representation of negative integers. -+* Runs in constant time. ++* Description: Sample a polynomial deterministically from a seed and a nonce, ++* with output polynomial close to centered binomial distribution ++* with parameter KYBER_ETA2 +* -+* Arguments: uint8_t *r: pointer to output byte array -+* const uint8_t *x: pointer to input byte array -+* size_t len: Amount of bytes to be copied -+* uint8_t b: Condition bit; has to be in {0,1} ++* Arguments: - poly *r: pointer to output polynomial ++* - const uint8_t *seed: pointer to input seed ++* (of length KYBER_SYMBYTES bytes) ++* - uint8_t nonce: one-byte input nonce +**************************************************/ -+static void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b) ++static void poly_getnoise_eta2(poly *r, const uint8_t seed[KYBER_SYMBYTES], uint8_t nonce) +{ -+ size_t i; -+ -+ b = -b; -+ for(i=0;i> 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: montgomery_reduce ++* Name: poly_ntt +* -+* Description: Montgomery reduction; given a 32-bit integer a, computes -+* 16-bit integer congruent to a * R^-1 mod q, where R=2^16 ++* Description: Computes negacyclic number-theoretic transform (NTT) of ++* a polynomial in place; ++* inputs assumed to be in normal order, output in bitreversed order +* -+* Arguments: - int32_t a: input integer to be reduced; -+* has to be in {-q2^15,...,q2^15-1} ++* Arguments: - uint16_t *r: pointer to in/output polynomial ++**************************************************/ ++static void poly_ntt(poly *r) ++{ ++ 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 +* -+* Returns: integer in {-q+1,...,q-1} congruent to a * R^-1 modulo q. ++* Description: Computes inverse of negacyclic number-theoretic transform (NTT) ++* of a polynomial in place; ++* inputs assumed to be in bitreversed order, output in normal order ++* ++* Arguments: - uint16_t *a: pointer to in/output polynomial +**************************************************/ -+static int16_t montgomery_reduce(int32_t a) ++static void poly_invntt_tomont(poly *r) +{ -+ int16_t t; -+ -+ t = (int16_t)a*QINV; -+ t = (a - (int32_t)t*KYBER_Q) >> 16; -+ return t; -+} -+ ++ 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: barrett_reduce ++* Name: poly_basemul_montgomery +* -+* Description: Barrett reduction; given a 16-bit integer a, computes -+* centered representative congruent to a mod q in {-(q-1)/2,...,(q-1)/2} ++* Description: Multiplication of two polynomials in NTT domain +* -+* Arguments: - int16_t a: input integer to be reduced ++* Arguments: - poly *r: pointer to output polynomial ++* - const poly *a: pointer to first input polynomial ++* - const poly *b: pointer to second input polynomial ++**************************************************/ ++static void poly_basemul_montgomery(poly *r, const poly *a, const poly *b) ++{ ++ unsigned int i; ++ for(i=0;icoeffs[4*i], &a->coeffs[4*i], &b->coeffs[4*i], zetas[64+i]); ++ basemul(&r->coeffs[4*i+2], &a->coeffs[4*i+2], &b->coeffs[4*i+2], -zetas[64+i]); + } + } + +-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 +* -+* Returns: integer in {-(q-1)/2,...,(q-1)/2} congruent to a modulo q. ++* Description: Inplace conversion of all coefficients of a polynomial ++* from normal domain to Montgomery domain ++* ++* Arguments: - poly *r: pointer to input/output polynomial +**************************************************/ -+static int16_t barrett_reduce(int16_t a) { -+ int16_t t; -+ const int16_t v = ((1<<26) + KYBER_Q/2)/KYBER_Q; -+ -+ t = ((int32_t)v*a + (1<<25)) >> 26; -+ t *= KYBER_Q; -+ return a - t; ++static void poly_tomont(poly *r) ++{ ++ unsigned int i; ++ const int16_t f = (1ULL << 32) % KYBER_Q; ++ for(i=0;icoeffs[i] = montgomery_reduce((int32_t)r->coeffs[i]*f); +} + -+// -+// cbd.c -+// -+ +/************************************************* -+* Name: load32_littleendian -+* -+* Description: load 4 bytes into a 32-bit integer -+* in little-endian order ++* Name: poly_reduce +* -+* Arguments: - const uint8_t *x: pointer to input byte array ++* Description: Applies Barrett reduction to all coefficients of a polynomial ++* for details of the Barrett reduction see comments in reduce.c +* -+* Returns 32-bit unsigned integer loaded from x ++* Arguments: - poly *r: pointer to input/output polynomial +**************************************************/ -+static uint32_t load32_littleendian(const uint8_t x[4]) ++static void poly_reduce(poly *r) +{ -+ uint32_t r; -+ r = (uint32_t)x[0]; -+ r |= (uint32_t)x[1] << 8; -+ r |= (uint32_t)x[2] << 16; -+ r |= (uint32_t)x[3] << 24; -+ return r; ++ unsigned int i; ++ for(i=0;icoeffs[i] = barrett_reduce(r->coeffs[i]); +} + +/************************************************* -+* Name: load24_littleendian -+* -+* Description: load 3 bytes into a 32-bit integer -+* in little-endian order. -+* This function is only needed for Kyber-512 ++* Name: poly_add +* -+* Arguments: - const uint8_t *x: pointer to input byte array ++* Description: Add two polynomials; no modular reduction is performed +* -+* Returns 32-bit unsigned integer loaded from x (most significant byte is zero) ++* Arguments: - poly *r: pointer to output polynomial ++* - const poly *a: pointer to first input polynomial ++* - const poly *b: pointer to second input polynomial +**************************************************/ -+#if KYBER_ETA1 == 3 -+static uint32_t load24_littleendian(const uint8_t x[3]) ++static void poly_add(poly *r, const poly *a, const poly *b) +{ -+ uint32_t r; -+ r = (uint32_t)x[0]; -+ r |= (uint32_t)x[1] << 8; -+ r |= (uint32_t)x[2] << 16; -+ return r; ++ unsigned int i; ++ for(i=0;icoeffs[i] = a->coeffs[i] + b->coeffs[i]; +} -+#endif -+ + +/************************************************* -+* Name: cbd2 ++* Name: poly_sub +* -+* Description: Given an array of uniformly random bytes, compute -+* polynomial with coefficients distributed according to -+* a centered binomial distribution with parameter eta=2 ++* Description: Subtract two polynomials; no modular reduction is performed +* -+* Arguments: - poly *r: pointer to output polynomial -+* - const uint8_t *buf: pointer to input byte array ++* Arguments: - poly *r: pointer to output polynomial ++* - const poly *a: pointer to first input polynomial ++* - const poly *b: pointer to second input polynomial +**************************************************/ -+static void cbd2(poly *r, const uint8_t buf[2*KYBER_N/4]) ++static void poly_sub(poly *r, const poly *a, const poly *b) +{ -+ unsigned int i,j; -+ uint32_t t,d; -+ int16_t a,b; ++ unsigned int i; ++ for(i=0;icoeffs[i] = a->coeffs[i] - b->coeffs[i]; ++} + -+ for(i=0;i>1) & 0x55555555; ++// ++// polyvec.c ++// + -+ for(j=0;j<8;j++) { -+ a = (d >> (4*j+0)) & 0x3; -+ b = (d >> (4*j+2)) & 0x3; -+ r->coeffs[8*i+j] = a - b; - } - } - } - --static void vector_ntt(vector *a) { -- for (int i = 0; i < RANK; i++) { -- scalar_ntt(&a->v[i]); +/************************************************* -+* Name: cbd3 ++* Name: polyvec_compress +* -+* Description: Given an array of uniformly random bytes, compute -+* polynomial with coefficients distributed according to -+* a centered binomial distribution with parameter eta=3. -+* This function is only needed for Kyber-512 ++* Description: Compress and serialize vector of polynomials +* -+* Arguments: - poly *r: pointer to output polynomial -+* - const uint8_t *buf: pointer to input byte array ++* Arguments: - uint8_t *r: pointer to output byte array ++* (needs space for KYBER_POLYVECCOMPRESSEDBYTES) ++* - const polyvec *a: pointer to input vector of polynomials +**************************************************/ -+#if KYBER_ETA1 == 3 -+static void cbd3(poly *r, const uint8_t buf[3*KYBER_N/4]) ++static void polyvec_compress(uint8_t r[KYBER_POLYVECCOMPRESSEDBYTES], const polyvec *a) +{ -+ unsigned int i,j; -+ uint32_t t,d; -+ int16_t a,b; -+ -+ for(i=0;i>1) & 0x00249249; -+ d += (t>>2) & 0x00249249; ++ unsigned int i,j,k; + -+ for(j=0;j<4;j++) { -+ a = (d >> (6*j+0)) & 0x7; -+ b = (d >> (6*j+3)) & 0x7; -+ r->coeffs[4*i+j] = a - b; ++#if (KYBER_POLYVECCOMPRESSEDBYTES == (KYBER_K * 352)) ++ uint16_t t[8]; ++ for(i=0;ivec[i].coeffs[8*j+k]; ++ t[k] += ((int16_t)t[k] >> 15) & KYBER_Q; ++ t[k] = ((((uint32_t)t[k] << 11) + KYBER_Q/2)/KYBER_Q) & 0x7ff; + } + +- 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); ++ r[ 3] = (t[2] >> 2); ++ r[ 4] = (t[2] >> 10) | (t[3] << 1); ++ r[ 5] = (t[3] >> 7) | (t[4] << 4); ++ r[ 6] = (t[4] >> 4) | (t[5] << 7); ++ r[ 7] = (t[5] >> 1); ++ r[ 8] = (t[5] >> 9) | (t[6] << 2); ++ r[ 9] = (t[6] >> 6) | (t[7] << 5); ++ r[10] = (t[7] >> 3); ++ r += 11; + } + } ++#elif (KYBER_POLYVECCOMPRESSEDBYTES == (KYBER_K * 320)) ++ uint16_t t[4]; ++ for(i=0;ivec[i].coeffs[4*j+k]; ++ t[k] += ((int16_t)t[k] >> 15) & KYBER_Q; ++ t[k] = ((((uint32_t)t[k] << 10) + KYBER_Q/2)/ KYBER_Q) & 0x3ff; ++ } + +- 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); ++ r[3] = (t[2] >> 4) | (t[3] << 6); ++ r[4] = (t[3] >> 2); ++ r += 5; + } } - } -+#endif -+ -+static void poly_cbd_eta1(poly *r, const uint8_t buf[KYBER_ETA1*KYBER_N/4]) -+{ -+#if KYBER_ETA1 == 2 -+ cbd2(r, buf); -+#elif KYBER_ETA1 == 3 -+ cbd3(r, buf); -+#else -+#error "This implementation requires eta1 in {2,3}" -+#endif -+} -+ -+static void poly_cbd_eta2(poly *r, const uint8_t buf[KYBER_ETA2*KYBER_N/4]) -+{ -+#if KYBER_ETA2 == 2 -+ cbd2(r, buf); +#else -+#error "This implementation requires eta2 = 2" ++#error "KYBER_POLYVECCOMPRESSEDBYTES needs to be in {320*KYBER_K, 352*KYBER_K}" +#endif -+} + } --// In place inverse number theoretic transform of a given scalar, with pairs of --// entries of s->v being interpreted as elements of GF(3329^2). Just as with the --// number theoretic transform, this leaves off the first step of the normal iFFT --// to account for the fact that 3329 does not have a 512th root of unity, using --// the precomputed 128 roots of unity stored in |kInverseNTTRoots|. --static void scalar_inverse_ntt(scalar *s) { -- int step = DEGREE / 2; -- // `int` is used here because using `size_t` throughout caused a ~5% slowdown -- // with Clang 14 on Aarch64. -- for (int offset = 2; offset < DEGREE; offset <<= 1) { -- step >>= 1; -- int k = 0; -- for (int i = 0; i < step; i++) { -- uint32_t step_root = kInverseNTTRoots[i + step]; -- for (int j = k; j < k + offset; j++) { -- uint16_t odd = s->c[j + offset]; -- uint16_t even = s->c[j]; -- s->c[j] = reduce_once(odd + even); -- s->c[j + offset] = reduce(step_root * (even - odd + kPrime)); -+// -+// ntt.c -+// -+ -+/* Code to generate zetas and zetas_inv used in the number-theoretic transform: -+ -+#define KYBER_ROOT_OF_UNITY 17 -+ -+static const uint8_t tree[128] = { -+ 0, 64, 32, 96, 16, 80, 48, 112, 8, 72, 40, 104, 24, 88, 56, 120, -+ 4, 68, 36, 100, 20, 84, 52, 116, 12, 76, 44, 108, 28, 92, 60, 124, -+ 2, 66, 34, 98, 18, 82, 50, 114, 10, 74, 42, 106, 26, 90, 58, 122, -+ 6, 70, 38, 102, 22, 86, 54, 118, 14, 78, 46, 110, 30, 94, 62, 126, -+ 1, 65, 33, 97, 17, 81, 49, 113, 9, 73, 41, 105, 25, 89, 57, 121, -+ 5, 69, 37, 101, 21, 85, 53, 117, 13, 77, 45, 109, 29, 93, 61, 125, -+ 3, 67, 35, 99, 19, 83, 51, 115, 11, 75, 43, 107, 27, 91, 59, 123, -+ 7, 71, 39, 103, 23, 87, 55, 119, 15, 79, 47, 111, 31, 95, 63, 127 -+}; -+ -+void init_ntt() { -+ unsigned int i; -+ int16_t tmp[128]; +-// 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 ++* ++* Description: De-serialize and decompress vector of polynomials; ++* approximate inverse of polyvec_compress ++* ++* Arguments: - polyvec *r: pointer to output vector of polynomials ++* - const uint8_t *a: pointer to input byte array ++* (of length KYBER_POLYVECCOMPRESSEDBYTES) ++**************************************************/ ++static void polyvec_decompress(polyvec *r, const uint8_t a[KYBER_POLYVECCOMPRESSEDBYTES]) ++{ ++ unsigned int i,j,k; + -+ tmp[0] = MONT; -+ for(i=1;i<128;i++) -+ tmp[i] = fqmul(tmp[i-1],MONT*KYBER_ROOT_OF_UNITY % KYBER_Q); ++#if (KYBER_POLYVECCOMPRESSEDBYTES == (KYBER_K * 352)) ++ uint16_t t[8]; ++ for(i=0;i> 0) | ((uint16_t)a[ 1] << 8); ++ t[1] = (a[1] >> 3) | ((uint16_t)a[ 2] << 5); ++ t[2] = (a[2] >> 6) | ((uint16_t)a[ 3] << 2) | ((uint16_t)a[4] << 10); ++ t[3] = (a[4] >> 1) | ((uint16_t)a[ 5] << 7); ++ t[4] = (a[5] >> 4) | ((uint16_t)a[ 6] << 4); ++ t[5] = (a[6] >> 7) | ((uint16_t)a[ 7] << 1) | ((uint16_t)a[8] << 9); ++ t[6] = (a[8] >> 2) | ((uint16_t)a[ 9] << 6); ++ t[7] = (a[9] >> 5) | ((uint16_t)a[10] << 3); ++ a += 11; + -+ for(i=0;i<128;i++) { -+ zetas[i] = tmp[tree[i]]; -+ if(zetas[i] > KYBER_Q/2) -+ zetas[i] -= KYBER_Q; -+ if(zetas[i] < -KYBER_Q/2) -+ zetas[i] += KYBER_Q; ++ for(k=0;k<8;k++) ++ r->vec[i].coeffs[8*j+k] = ((uint32_t)(t[k] & 0x7FF)*KYBER_Q + 1024) >> 11; ++ } + } -+} -+*/ ++#elif (KYBER_POLYVECCOMPRESSEDBYTES == (KYBER_K * 320)) ++ uint16_t t[4]; ++ for(i=0;i> 0) | ((uint16_t)a[1] << 8); ++ t[1] = (a[1] >> 2) | ((uint16_t)a[2] << 6); ++ t[2] = (a[2] >> 4) | ((uint16_t)a[3] << 4); ++ t[3] = (a[3] >> 6) | ((uint16_t)a[4] << 2); ++ a += 5; + -+static const int16_t zetas[128] = { -+ -1044, -758, -359, -1517, 1493, 1422, 287, 202, -+ -171, 622, 1577, 182, 962, -1202, -1474, 1468, -+ 573, -1325, 264, 383, -829, 1458, -1602, -130, -+ -681, 1017, 732, 608, -1542, 411, -205, -1571, -+ 1223, 652, -552, 1015, -1293, 1491, -282, -1544, -+ 516, -8, -320, -666, -1618, -1162, 126, 1469, -+ -853, -90, -271, 830, 107, -1421, -247, -951, -+ -398, 961, -1508, -725, 448, -1065, 677, -1275, -+ -1103, 430, 555, 843, -1251, 871, 1550, 105, -+ 422, 587, 177, -235, -291, -460, 1574, 1653, -+ -246, 778, 1159, -147, -777, 1483, -602, 1119, -+ -1590, 644, -872, 349, 418, 329, -156, -75, -+ 817, 1097, 603, 610, 1322, -1285, -1465, 384, -+ -1215, -136, 1218, -1335, -874, 220, -1187, -1659, -+ -1185, -1530, -1278, 794, -1510, -854, -870, 478, -+ -108, -308, 996, 991, 958, -1460, 1522, 1628 -+}; ++ 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 ++} + +/************************************************* -+* Name: fqmul -+* -+* Description: Multiplication followed by Montgomery reduction ++* Name: polyvec_tobytes +* -+* Arguments: - int16_t a: first factor -+* - int16_t b: second factor ++* Description: Serialize vector of polynomials +* -+* Returns 16-bit integer congruent to a*b*R^{-1} mod q ++* Arguments: - uint8_t *r: pointer to output byte array ++* (needs space for KYBER_POLYVECBYTES) ++* - const polyvec *a: pointer to input vector of polynomials +**************************************************/ -+static int16_t fqmul(int16_t a, int16_t b) { -+ return montgomery_reduce((int32_t)a*b); ++static void polyvec_tobytes(uint8_t r[KYBER_POLYVECBYTES], const polyvec *a) ++{ ++ unsigned int i; ++ for(i=0;ivec[i]); +} + +/************************************************* -+* Name: ntt ++* Name: polyvec_frombytes +* -+* Description: Inplace number-theoretic transform (NTT) in Rq. -+* input is in standard order, output is in bitreversed order ++* Description: De-serialize vector of polynomials; ++* inverse of polyvec_tobytes +* -+* Arguments: - int16_t r[256]: pointer to input/output vector of elements of Zq ++* Arguments: - uint8_t *r: pointer to output byte array ++* - const polyvec *a: pointer to input vector of polynomials ++* (of length KYBER_POLYVECBYTES) +**************************************************/ -+static void ntt(int16_t r[256]) { -+ unsigned int len, start, j, k; -+ int16_t t, zeta; -+ -+ k = 1; -+ for(len = 128; len >= 2; len >>= 1) { -+ for(start = 0; start < 256; start = j + len) { -+ zeta = zetas[k++]; -+ for(j = start; j < start + len; j++) { -+ t = fqmul(zeta, r[j + len]); -+ r[j + len] = r[j] - t; -+ r[j] = r[j] + t; - } -- k += 2 * offset; - } - } -- for (int i = 0; i < DEGREE; i++) { -- s->c[i] = reduce(s->c[i] * kInverseDegree); -- } ++static void polyvec_frombytes(polyvec *r, const uint8_t a[KYBER_POLYVECBYTES]) ++{ ++ unsigned int i; ++ for(i=0;ivec[i], a+i*KYBER_POLYBYTES); } --static void vector_inverse_ntt(vector *a) { +-// 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_inverse_ntt(&a->v[i]); +- scalar_encode(out + i * bits * DEGREE / 8, &a->v[i], bits); +/************************************************* -+* Name: invntt_tomont ++* Name: polyvec_ntt +* -+* Description: Inplace inverse number-theoretic transform in Rq and -+* multiplication by Montgomery factor 2^16. -+* Input is in bitreversed order, output is in standard order ++* Description: Apply forward NTT to all elements of a vector of polynomials +* -+* Arguments: - int16_t r[256]: pointer to input/output vector of elements of Zq ++* Arguments: - polyvec *r: pointer to in/output vector of polynomials +**************************************************/ -+static void invntt(int16_t r[256]) { -+ unsigned int start, len, j, k; -+ int16_t t, zeta; -+ const int16_t f = 1441; // mont^2/128 -+ -+ k = 127; -+ for(len = 2; len <= 128; len <<= 1) { -+ for(start = 0; start < 256; start = j + len) { -+ zeta = zetas[k--]; -+ for(j = start; j < start + len; j++) { -+ t = r[j]; -+ r[j] = barrett_reduce(t + r[j + len]); -+ r[j + len] = r[j + len] - t; -+ r[j + len] = fqmul(zeta, r[j + len]); -+ } -+ } - } ++static void polyvec_ntt(polyvec *r) ++{ ++ unsigned int i; ++ for(i=0;ivec[i]); ++} + -+ for(j = 0; j < 256; j++) -+ r[j] = fqmul(r[j], f); - } - --static void scalar_add(scalar *lhs, const scalar *rhs) { -- for (int i = 0; i < DEGREE; i++) { -- lhs->c[i] = reduce_once(lhs->c[i] + rhs->c[i]); +/************************************************* -+* Name: basemul ++* Name: polyvec_invntt_tomont +* -+* Description: Multiplication of polynomials in Zq[X]/(X^2-zeta) -+* used for multiplication of elements in Rq in NTT domain ++* Description: Apply inverse NTT to all elements of a vector of polynomials ++* and multiply by Montgomery factor 2^16 +* -+* Arguments: - int16_t r[2]: pointer to the output polynomial -+* - const int16_t a[2]: pointer to the first factor -+* - const int16_t b[2]: pointer to the second factor -+* - int16_t zeta: integer defining the reduction polynomial ++* Arguments: - polyvec *r: pointer to in/output vector of polynomials +**************************************************/ -+static void basemul(int16_t r[2], const int16_t a[2], const int16_t b[2], int16_t zeta) ++static void polyvec_invntt_tomont(polyvec *r) +{ -+ r[0] = fqmul(a[1], b[1]); -+ r[0] = fqmul(r[0], zeta); -+ r[0] += fqmul(a[0], b[0]); -+ r[1] = fqmul(a[0], b[1]); -+ r[1] += fqmul(a[1], b[0]); ++ unsigned int i; ++ for(i=0;ivec[i]); +} + -+// -+// poly.c -+// -+ +/************************************************* -+* Name: poly_compress ++* Name: polyvec_basemul_acc_montgomery +* -+* Description: Compression and subsequent serialization of a polynomial ++* Description: Multiply elements of a and b in NTT domain, accumulate into r, ++* and multiply by 2^-16. +* -+* Arguments: - uint8_t *r: pointer to output byte array -+* (of length KYBER_POLYCOMPRESSEDBYTES) -+* - const poly *a: pointer to input polynomial ++* Arguments: - poly *r: pointer to output polynomial ++* - const polyvec *a: pointer to first input vector of polynomials ++* - const polyvec *b: pointer to second input vector of polynomials +**************************************************/ -+static void poly_compress(uint8_t r[KYBER_POLYCOMPRESSEDBYTES], const poly *a) ++static void polyvec_basemul_acc_montgomery(poly *r, const polyvec *a, const polyvec *b) +{ -+ unsigned int i,j; -+ int16_t u; -+ uint32_t d0; -+ uint8_t t[8]; -+ -+#if (KYBER_POLYCOMPRESSEDBYTES == 128) -+ for(i=0;icoeffs[8*i+j]; -+ u += (u >> 15) & KYBER_Q; -+ d0 = u << 4; -+ d0 += 1665; -+ d0 *= 80635; -+ d0 >>= 28; -+ t[j] = d0 & 0xf; -+ } -+ -+ r[0] = t[0] | (t[1] << 4); -+ r[1] = t[2] | (t[3] << 4); -+ r[2] = t[4] | (t[5] << 4); -+ r[3] = t[6] | (t[7] << 4); -+ r += 4; -+ } -+#elif (KYBER_POLYCOMPRESSEDBYTES == 160) -+ for(i=0;icoeffs[8*i+j]; -+ u += (u >> 15) & KYBER_Q; -+ d0 = u << 5; -+ d0 += 1664; -+ d0 *= 40318; -+ d0 >>= 27; -+ t[j] = d0 & 0x1f; -+ } ++ unsigned int i; ++ poly t; + -+ r[0] = (t[0] >> 0) | (t[1] << 5); -+ r[1] = (t[1] >> 3) | (t[2] << 2) | (t[3] << 7); -+ r[2] = (t[3] >> 1) | (t[4] << 4); -+ r[3] = (t[4] >> 4) | (t[5] << 1) | (t[6] << 6); -+ r[4] = (t[6] >> 2) | (t[7] << 3); -+ r += 5; ++ poly_basemul_montgomery(r, &a->vec[0], &b->vec[0]); ++ for(i=1;ivec[i], &b->vec[i]); ++ poly_add(r, r, &t); } -+#else -+#error "KYBER_POLYCOMPRESSEDBYTES needs to be in {128, 160}" -+#endif ++ ++ poly_reduce(r); } --static void scalar_sub(scalar *lhs, const scalar *rhs) { +-// 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_reduce ++* ++* Description: Applies Barrett reduction to each coefficient ++* of each element of a vector of polynomials; ++* for details of the Barrett reduction see comments in reduce.c ++* ++* Arguments: - polyvec *r: pointer to input/output polynomial ++**************************************************/ ++static void polyvec_reduce(polyvec *r) ++{ ++ unsigned int i; ++ for(i=0;ivec[i]); ++} + +- uint8_t in_byte = 0; +- int in_byte_bits_left = 0; ++/************************************************* ++* Name: polyvec_add ++* ++* Description: Add vectors of polynomials ++* ++* Arguments: - polyvec *r: pointer to output vector of polynomials ++* - const polyvec *a: pointer to first input vector of polynomials ++* - const polyvec *b: pointer to second input vector of polynomials ++**************************************************/ ++static void polyvec_add(polyvec *r, const polyvec *a, const polyvec *b) ++{ ++ unsigned int i; ++ for(i=0;ivec[i], &a->vec[i], &b->vec[i]); ++} + - for (int i = 0; i < DEGREE; i++) { -- lhs->c[i] = reduce_once(lhs->c[i] - rhs->c[i] + kPrime); +- uint16_t element = 0; +- int element_bits_done = 0; ++// ++// indcpa.c ++// + +- while (element_bits_done < bits) { +- if (in_byte_bits_left == 0) { +- in_byte = *in; +- in++; +- in_byte_bits_left = 8; +- } +/************************************************* -+* Name: poly_decompress ++* Name: pack_pk +* -+* Description: De-serialization and subsequent decompression of a polynomial; -+* approximate inverse of poly_compress ++* Description: Serialize the public key as concatenation of the ++* serialized vector of polynomials pk ++* and the public seed used to generate the matrix A. +* -+* Arguments: - poly *r: pointer to output polynomial -+* - const uint8_t *a: pointer to input byte array -+* (of length KYBER_POLYCOMPRESSEDBYTES bytes) ++* Arguments: uint8_t *r: pointer to the output serialized public key ++* polyvec *pk: pointer to the input public-key polyvec ++* const uint8_t *seed: pointer to the input public seed +**************************************************/ -+static void poly_decompress(poly *r, const uint8_t a[KYBER_POLYCOMPRESSEDBYTES]) ++static void pack_pk(uint8_t r[KYBER_INDCPA_PUBLICKEYBYTES], ++ polyvec *pk, ++ const uint8_t seed[KYBER_SYMBYTES]) +{ -+ unsigned int i; -+ -+#if (KYBER_POLYCOMPRESSEDBYTES == 128) -+ for(i=0;icoeffs[2*i+0] = (((uint16_t)(a[0] & 15)*KYBER_Q) + 8) >> 4; -+ r->coeffs[2*i+1] = (((uint16_t)(a[0] >> 4)*KYBER_Q) + 8) >> 4; -+ a += 1; -+ } -+#elif (KYBER_POLYCOMPRESSEDBYTES == 160) -+ unsigned int j; -+ uint8_t t[8]; -+ for(i=0;i> 0); -+ t[1] = (a[0] >> 5) | (a[1] << 3); -+ t[2] = (a[1] >> 2); -+ t[3] = (a[1] >> 7) | (a[2] << 1); -+ t[4] = (a[2] >> 4) | (a[3] << 4); -+ t[5] = (a[3] >> 1); -+ t[6] = (a[3] >> 6) | (a[4] << 2); -+ t[7] = (a[4] >> 3); -+ a += 5; -+ -+ for(j=0;j<8;j++) -+ r->coeffs[8*i+j] = ((uint32_t)(t[j] & 31)*KYBER_Q + 16) >> 5; - } -+#else -+#error "KYBER_POLYCOMPRESSEDBYTES needs to be in {128, 160}" -+#endif - } ++ size_t i; ++ polyvec_tobytes(r, pk); ++ for(i=0;ic[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); +- int chunk_bits = bits - element_bits_done; +- if (chunk_bits > in_byte_bits_left) { +- chunk_bits = in_byte_bits_left; +- } +/************************************************* -+* Name: poly_tobytes ++* Name: unpack_pk +* -+* Description: Serialization of a polynomial ++* Description: De-serialize public key from a byte array; ++* approximate inverse of pack_pk +* -+* Arguments: - uint8_t *r: pointer to output byte array -+* (needs space for KYBER_POLYBYTES bytes) -+* - const poly *a: pointer to input polynomial ++* Arguments: - polyvec *pk: pointer to output public-key polynomial vector ++* - uint8_t *seed: pointer to output seed to generate matrix A ++* - const uint8_t *packedpk: pointer to input serialized public key +**************************************************/ -+static void poly_tobytes(uint8_t r[KYBER_POLYBYTES], const poly *a) ++static void unpack_pk(polyvec *pk, ++ uint8_t seed[KYBER_SYMBYTES], ++ const uint8_t packedpk[KYBER_INDCPA_PUBLICKEYBYTES]) +{ -+ unsigned int i; -+ uint16_t t0, t1; -+ -+ for(i=0;icoeffs[2*i]; -+ t0 += ((int16_t)t0 >> 15) & KYBER_Q; -+ t1 = a->coeffs[2*i+1]; -+ t1 += ((int16_t)t1 >> 15) & KYBER_Q; -+ r[3*i+0] = (t0 >> 0); -+ r[3*i+1] = (t0 >> 8) | (t1 << 4); -+ r[3*i+2] = (t1 >> 4); - } - } ++ size_t i; ++ polyvec_frombytes(pk, packedpk); ++ for(i=0;iv[i], &rhs->v[i]); +- element |= (in_byte & kMasks[chunk_bits - 1]) << element_bits_done; +- in_byte_bits_left -= chunk_bits; +- in_byte >>= chunk_bits; +/************************************************* -+* Name: poly_frombytes ++* Name: pack_sk +* -+* Description: De-serialization of a polynomial; -+* inverse of poly_tobytes ++* Description: Serialize the secret key +* -+* Arguments: - poly *r: pointer to output polynomial -+* - const uint8_t *a: pointer to input byte array -+* (of KYBER_POLYBYTES bytes) ++* Arguments: - uint8_t *r: pointer to output serialized secret key ++* - polyvec *sk: pointer to input vector of polynomials (secret key) +**************************************************/ -+static void poly_frombytes(poly *r, const uint8_t a[KYBER_POLYBYTES]) ++static void pack_sk(uint8_t r[KYBER_INDCPA_SECRETKEYBYTES], polyvec *sk) +{ -+ unsigned int i; -+ for(i=0;icoeffs[2*i] = ((a[3*i+0] >> 0) | ((uint16_t)a[3*i+1] << 8)) & 0xFFF; -+ r->coeffs[2*i+1] = ((a[3*i+1] >> 4) | ((uint16_t)a[3*i+2] << 4)) & 0xFFF; - } - } ++ polyvec_tobytes(r, sk); ++} --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); +- element_bits_done += chunk_bits; +- } +/************************************************* -+* Name: poly_frommsg ++* Name: unpack_sk +* -+* Description: Convert 32-byte message to polynomial ++* Description: De-serialize the secret key; inverse of pack_sk +* -+* Arguments: - poly *r: pointer to output polynomial -+* - const uint8_t *msg: pointer to input message ++* Arguments: - polyvec *sk: pointer to output vector of polynomials (secret key) ++* - const uint8_t *packedsk: pointer to input serialized secret key +**************************************************/ -+static void poly_frommsg(poly *r, const uint8_t msg[KYBER_INDCPA_MSGBYTES]) ++static void unpack_sk(polyvec *sk, const uint8_t packedsk[KYBER_INDCPA_SECRETKEYBYTES]) +{ -+ unsigned int i,j; -+ int16_t mask; -+ -+#if (KYBER_INDCPA_MSGBYTES != KYBER_N/8) -+#error "KYBER_INDCPA_MSGBYTES must be equal to KYBER_N/8 bytes!" -+#endif -+ -+ for(i=0;i> j)&1); -+ r->coeffs[8*i+j] = mask & ((KYBER_Q+1)/2); - } - } - } ++ polyvec_frombytes(sk, packedsk); ++} --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); +- if (element >= kPrime) { +- return 0; +- } +- out->c[i] = element; +- } +/************************************************* -+* Name: poly_tomsg ++* Name: pack_ciphertext +* -+* Description: Convert polynomial to 32-byte message ++* Description: Serialize the ciphertext as concatenation of the ++* compressed and serialized vector of polynomials b ++* and the compressed and serialized polynomial v +* -+* Arguments: - uint8_t *msg: pointer to output message -+* - const poly *a: pointer to input polynomial ++* Arguments: uint8_t *r: pointer to the output serialized ciphertext ++* poly *pk: pointer to the input vector of polynomials b ++* poly *v: pointer to the input polynomial v +**************************************************/ -+static void poly_tomsg(uint8_t msg[KYBER_INDCPA_MSGBYTES], const poly *a) ++static void pack_ciphertext(uint8_t r[KYBER_INDCPA_BYTES], polyvec *b, poly *v) +{ -+ unsigned int i,j; -+ uint32_t t; -+ -+ for(i=0;icoeffs[8*i+j]; -+ t <<= 1; -+ t += 1665; -+ t *= 80635; -+ t >>= 28; -+ t &= 1; -+ msg[i] |= t << j; - } - } ++ polyvec_compress(r, b); ++ poly_compress(r+KYBER_POLYVECCOMPRESSEDBYTES, v); ++} + +- return 1; ++/************************************************* ++* Name: unpack_ciphertext ++* ++* Description: De-serialize and decompress ciphertext from a byte array; ++* approximate inverse of pack_ciphertext ++* ++* Arguments: - polyvec *b: pointer to the output vector of polynomials b ++* - poly *v: pointer to the output polynomial v ++* - const uint8_t *c: pointer to the input serialized ciphertext ++**************************************************/ ++static void unpack_ciphertext(polyvec *b, poly *v, const uint8_t c[KYBER_INDCPA_BYTES]) ++{ ++ polyvec_decompress(b, c); ++ poly_decompress(v, c+KYBER_POLYVECCOMPRESSEDBYTES); } --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); -- } +-// scalar_decode_1 is |scalar_decode| specialised for |bits| == 1. +-static void scalar_decode_1(scalar *out, const uint8_t in[32]) { +- for (int i = 0; i < DEGREE; i += 8) { +- uint8_t in_byte = *in; +- in++; +- for (int j = 0; j < 8; j++) { +- out->c[i + j] = in_byte & 1; +- in_byte >>= 1; +- } +/************************************************* -+* Name: poly_getnoise_eta1 ++* Name: rej_uniform +* -+* Description: Sample a polynomial deterministically from a seed and a nonce, -+* with output polynomial close to centered binomial distribution -+* with parameter KYBER_ETA1 ++* Description: Run rejection sampling on uniform random bytes to generate ++* uniform random integers mod q +* -+* Arguments: - poly *r: pointer to output polynomial -+* - const uint8_t *seed: pointer to input seed -+* (of length KYBER_SYMBYTES bytes) -+* - uint8_t nonce: one-byte input nonce ++* Arguments: - int16_t *r: pointer to output buffer ++* - unsigned int len: requested number of 16-bit integers (uniform mod q) ++* - const uint8_t *buf: pointer to input buffer (assumed to be uniformly random bytes) ++* - unsigned int buflen: length of input buffer in bytes ++* ++* Returns number of sampled 16-bit integers (at most len) +**************************************************/ -+static void poly_getnoise_eta1(poly *r, const uint8_t seed[KYBER_SYMBYTES], uint8_t nonce) ++static unsigned int rej_uniform(int16_t *r, ++ unsigned int len, ++ const uint8_t *buf, ++ unsigned int buflen) +{ -+ uint8_t buf[KYBER_ETA1*KYBER_N/4]; -+ prf(buf, sizeof(buf), seed, nonce); -+ poly_cbd_eta1(r, buf); ++ unsigned int ctr, pos; ++ uint16_t val0, val1; ++ ++ ctr = pos = 0; ++ while(ctr < len && pos + 3 <= buflen) { ++ val0 = ((buf[pos+0] >> 0) | ((uint16_t)buf[pos+1] << 8)) & 0xFFF; ++ val1 = ((buf[pos+1] >> 4) | ((uint16_t)buf[pos+2] << 4)) & 0xFFF; ++ pos += 3; ++ ++ if(val0 < KYBER_Q) ++ r[ctr++] = val0; ++ if(ctr < len && val1 < KYBER_Q) ++ r[ctr++] = val1; + } ++ ++ return ctr; } --// 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; -- } -- } -- } +-// 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) ++ +/************************************************* -+* Name: poly_getnoise_eta2 ++* Name: gen_matrix +* -+* Description: Sample a polynomial deterministically from a seed and a nonce, -+* with output polynomial close to centered binomial distribution -+* with parameter KYBER_ETA2 ++* Description: Deterministically generate matrix A (or the transpose of A) ++* from a seed. Entries of the matrix are polynomials that look ++* uniformly random. Performs rejection sampling on output of ++* a XOF +* -+* Arguments: - poly *r: pointer to output polynomial ++* Arguments: - polyvec *a: pointer to ouptput matrix A +* - const uint8_t *seed: pointer to input seed -+* (of length KYBER_SYMBYTES bytes) -+* - uint8_t nonce: one-byte input nonce ++* - int transposed: boolean deciding whether A or A^T is generated +**************************************************/ -+static void poly_getnoise_eta2(poly *r, const uint8_t seed[KYBER_SYMBYTES], uint8_t nonce) ++#define GEN_MATRIX_NBLOCKS ((12*KYBER_N/8*(1 << 12)/KYBER_Q + XOF_BLOCKBYTES)/XOF_BLOCKBYTES) ++// Not static for benchmarking ++static void gen_matrix(polyvec *a, const uint8_t seed[KYBER_SYMBYTES], int transposed) +{ -+ 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); ++ unsigned int ctr, i, j, k; ++ unsigned int buflen, off; ++ uint8_t buf[GEN_MATRIX_NBLOCKS*XOF_BLOCKBYTES+2]; ++ xof_state state; ++ ++ for(i=0;i> kBarrettShift; +- uint32_t remainder = product - quotient * kPrime; - -- uint16_t value = kPrime; -- value += (byte & 1) + ((byte >> 1) & 1); -- value -= ((byte >> 2) & 1) + ((byte >> 3) & 1); -- out->c[i] = reduce_once(value); +- // 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); +-} - -- byte >>= 4; -- value = kPrime; -- value += (byte & 1) + ((byte >> 1) & 1); -- value -= ((byte >> 2) & 1) + ((byte >> 3) & 1); -- out->c[i + 1] = reduce_once(value); +-// 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); - } -+ + } + +-static void scalar_decompress(scalar *s, int bits) { +- for (int i = 0; i < DEGREE; i++) { +- s->c[i] = decompress(s->c[i], bits); +/************************************************* -+* Name: poly_ntt ++* Name: indcpa_keypair +* -+* Description: Computes negacyclic number-theoretic transform (NTT) of -+* a polynomial in place; -+* inputs assumed to be in normal order, output in bitreversed order ++* Description: Generates public and private key for the CPA-secure ++* public-key encryption scheme underlying Kyber +* -+* Arguments: - uint16_t *r: pointer to in/output polynomial ++* Arguments: - uint8_t *pk: pointer to output public key ++* (of length KYBER_INDCPA_PUBLICKEYBYTES bytes) ++* - uint8_t *sk: pointer to output private key ++ (of length KYBER_INDCPA_SECRETKEYBYTES bytes) +**************************************************/ -+static void poly_ntt(poly *r) ++static void indcpa_keypair(uint8_t pk[KYBER_INDCPA_PUBLICKEYBYTES], ++ uint8_t sk[KYBER_INDCPA_SECRETKEYBYTES], ++ const uint8_t seed[KYBER_SYMBYTES]) +{ -+ ntt(r->coeffs); -+ poly_reduce(r); ++ unsigned int i; ++ uint8_t buf[2*KYBER_SYMBYTES]; ++ const uint8_t *publicseed = buf; ++ const uint8_t *noiseseed = buf+KYBER_SYMBYTES; ++ uint8_t nonce = 0; ++ polyvec a[KYBER_K], e, pkpv, skpv; ++ ++ memcpy(buf, seed, KYBER_SYMBYTES); ++ hash_g(buf, buf, KYBER_SYMBYTES); ++ ++ gen_a(a, publicseed); ++ ++ for(i=0;iv[i], input); +- scalar_compress(&a->v[i], bits); - } +/************************************************* -+* Name: poly_invntt_tomont ++* Name: indcpa_enc +* -+* Description: Computes inverse of negacyclic number-theoretic transform (NTT) -+* of a polynomial in place; -+* inputs assumed to be in bitreversed order, output in normal order ++* Description: Encryption function of the CPA-secure ++* public-key encryption scheme underlying Kyber. +* -+* Arguments: - uint16_t *a: pointer to in/output polynomial ++* Arguments: - uint8_t *c: pointer to output ciphertext ++* (of length KYBER_INDCPA_BYTES bytes) ++* - const uint8_t *m: pointer to input message ++* (of length KYBER_INDCPA_MSGBYTES bytes) ++* - const uint8_t *pk: pointer to input public key ++* (of length KYBER_INDCPA_PUBLICKEYBYTES) ++* - const uint8_t *coins: pointer to input random coins used as seed ++* (of length KYBER_SYMBYTES) to deterministically ++* generate all randomness +**************************************************/ -+static void poly_invntt_tomont(poly *r) ++static void indcpa_enc(uint8_t c[KYBER_INDCPA_BYTES], ++ const uint8_t m[KYBER_INDCPA_MSGBYTES], ++ const uint8_t pk[KYBER_INDCPA_PUBLICKEYBYTES], ++ const uint8_t coins[KYBER_SYMBYTES]) +{ -+ invntt(r->coeffs); ++ unsigned int i; ++ uint8_t seed[KYBER_SYMBYTES]; ++ uint8_t nonce = 0; ++ polyvec sp, pkpv, ep, at[KYBER_K], b; ++ poly v, k, epp; ++ ++ unpack_pk(&pkpv, seed, pk); ++ poly_frommsg(&k, m); ++ gen_at(at, seed); ++ ++ for(i=0;iv[i][j], &keccak_ctx); -- } +- scalar_decompress(&a->v[i], bits); +- } +/************************************************* -+* Name: poly_basemul_montgomery ++* Name: indcpa_dec +* -+* Description: Multiplication of two polynomials in NTT domain ++* Description: Decryption function of the CPA-secure ++* public-key encryption scheme underlying Kyber. +* -+* Arguments: - poly *r: pointer to output polynomial -+* - const poly *a: pointer to first input polynomial -+* - const poly *b: pointer to second input polynomial ++* Arguments: - uint8_t *m: pointer to output decrypted message ++* (of length KYBER_INDCPA_MSGBYTES) ++* - const uint8_t *c: pointer to input ciphertext ++* (of length KYBER_INDCPA_BYTES) ++* - const uint8_t *sk: pointer to input secret key ++* (of length KYBER_INDCPA_SECRETKEYBYTES) +**************************************************/ -+static void poly_basemul_montgomery(poly *r, const poly *a, const poly *b) ++static void indcpa_dec(uint8_t m[KYBER_INDCPA_MSGBYTES], ++ const uint8_t c[KYBER_INDCPA_BYTES], ++ const uint8_t sk[KYBER_INDCPA_SECRETKEYBYTES]) +{ -+ unsigned int i; -+ for(i=0;icoeffs[4*i], &a->coeffs[4*i], &b->coeffs[4*i], zetas[64+i]); -+ basemul(&r->coeffs[4*i+2], &a->coeffs[4*i+2], &b->coeffs[4*i+2], -zetas[64+i]); - } ++ polyvec b, skpv; ++ poly v, mp; ++ ++ unpack_ciphertext(&b, &v, c); ++ unpack_sk(&skpv, sk); ++ ++ polyvec_ntt(&b); ++ polyvec_basemul_acc_montgomery(&mp, &skpv, &b); ++ poly_invntt_tomont(&mp); ++ ++ poly_sub(&mp, &v, &mp); ++ poly_reduce(&mp); ++ ++ poly_tomsg(m, &mp); } --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; +-struct public_key { +- vector t; +- uint8_t rho[32]; +- uint8_t public_key_hash[32]; +- matrix m; +-}; ++// ++// fips202.c ++// ++ ++/* Based on the public domain implementation in crypto_hash/keccakc512/simple/ from ++ * http://bench.cr.yp.to/supercop.html by Ronny Van Keer and the public domain "TweetFips202" ++ * implementation from https://twitter.com/tweetfips202 by Gilles Van Assche, Daniel J. Bernstein, ++ * and Peter Schwabe */ ++ ++#define NROUNDS 24 ++#define ROL(a, offset) ((a << offset) ^ (a >> (64-offset))) ++ +/************************************************* -+* Name: poly_tomont ++* Name: load64 +* -+* Description: Inplace conversion of all coefficients of a polynomial -+* from normal domain to Montgomery domain ++* Description: Load 8 bytes into uint64_t in little-endian order +* -+* Arguments: - poly *r: pointer to input/output polynomial ++* Arguments: - const uint8_t *x: pointer to input byte array ++* ++* Returns the loaded 64-bit unsigned integer +**************************************************/ -+static void poly_tomont(poly *r) -+{ ++static uint64_t load64(const uint8_t x[8]) { + unsigned int i; -+ const int16_t f = (1ULL << 32) % KYBER_Q; -+ for(i=0;icoeffs[i] = montgomery_reduce((int32_t)r->coeffs[i]*f); -+} ++ uint64_t r = 0; ++ ++ for(i=0;i<8;i++) ++ r |= (uint64_t)x[i] << 8*i; + ++ return r; ++} + +-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; +/************************************************* -+* Name: poly_reduce ++* Name: store64 +* -+* Description: Applies Barrett reduction to all coefficients of a polynomial -+* for details of the Barrett reduction see comments in reduce.c ++* Description: Store a 64-bit integer to array of 8 bytes in little-endian order +* -+* Arguments: - poly *r: pointer to input/output polynomial ++* Arguments: - uint8_t *x: pointer to the output byte array (allocated) ++* - uint64_t u: input 64-bit unsigned integer +**************************************************/ -+static void poly_reduce(poly *r) -+{ ++static void store64(uint8_t x[8], uint64_t u) { + unsigned int i; -+ for(i=0;icoeffs[i] = barrett_reduce(r->coeffs[i]); -+} + ++ for(i=0;i<8;i++) ++ x[i] = u >> 8*i; + } + +-struct private_key { +- struct public_key pub; +- vector s; +- uint8_t fo_failure_secret[32]; ++/* Keccak round constants */ ++static const uint64_t KeccakF_RoundConstants[NROUNDS] = { ++ (uint64_t)0x0000000000000001ULL, ++ (uint64_t)0x0000000000008082ULL, ++ (uint64_t)0x800000000000808aULL, ++ (uint64_t)0x8000000080008000ULL, ++ (uint64_t)0x000000000000808bULL, ++ (uint64_t)0x0000000080000001ULL, ++ (uint64_t)0x8000000080008081ULL, ++ (uint64_t)0x8000000000008009ULL, ++ (uint64_t)0x000000000000008aULL, ++ (uint64_t)0x0000000000000088ULL, ++ (uint64_t)0x0000000080008009ULL, ++ (uint64_t)0x000000008000000aULL, ++ (uint64_t)0x000000008000808bULL, ++ (uint64_t)0x800000000000008bULL, ++ (uint64_t)0x8000000000008089ULL, ++ (uint64_t)0x8000000000008003ULL, ++ (uint64_t)0x8000000000008002ULL, ++ (uint64_t)0x8000000000000080ULL, ++ (uint64_t)0x000000000000800aULL, ++ (uint64_t)0x800000008000000aULL, ++ (uint64_t)0x8000000080008081ULL, ++ (uint64_t)0x8000000000008080ULL, ++ (uint64_t)0x0000000080000001ULL, ++ (uint64_t)0x8000000080008008ULL + }; + +-static struct 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; +/************************************************* -+* Name: poly_add ++* Name: KeccakF1600_StatePermute +* -+* Description: Add two polynomials; no modular reduction is performed ++* Description: The Keccak F1600 Permutation +* -+* Arguments: - poly *r: pointer to output polynomial -+* - const poly *a: pointer to first input polynomial -+* - const poly *b: pointer to second input polynomial ++* Arguments: - uint64_t *state: pointer to input/output Keccak state +**************************************************/ -+static void poly_add(poly *r, const poly *a, const poly *b) ++static void KeccakF1600_StatePermute(uint64_t state[25]) +{ -+ unsigned int i; -+ for(i=0;icoeffs[i] = a->coeffs[i] + b->coeffs[i]; -+} ++ int round; + -+/************************************************* -+* Name: poly_sub -+* -+* Description: Subtract two polynomials; no modular reduction is performed -+* -+* Arguments: - poly *r: pointer to output polynomial -+* - const poly *a: pointer to first input polynomial -+* - const poly *b: pointer to second input polynomial -+**************************************************/ -+static void poly_sub(poly *r, const poly *a, const poly *b) -+{ -+ unsigned int i; -+ for(i=0;icoeffs[i] = a->coeffs[i] - b->coeffs[i]; -+} ++ uint64_t Aba, Abe, Abi, Abo, Abu; ++ uint64_t Aga, Age, Agi, Ago, Agu; ++ uint64_t Aka, Ake, Aki, Ako, Aku; ++ uint64_t Ama, Ame, Ami, Amo, Amu; ++ uint64_t Asa, Ase, Asi, Aso, Asu; ++ uint64_t BCa, BCe, BCi, BCo, BCu; ++ uint64_t Da, De, Di, Do, Du; ++ uint64_t Eba, Ebe, Ebi, Ebo, Ebu; ++ uint64_t Ega, Ege, Egi, Ego, Egu; ++ uint64_t Eka, Eke, Eki, Eko, Eku; ++ uint64_t Ema, Eme, Emi, Emo, Emu; ++ uint64_t Esa, Ese, Esi, Eso, Esu; + -+// -+// polyvec.c -+// ++ //copyFromState(A, state) ++ Aba = state[ 0]; ++ Abe = state[ 1]; ++ Abi = state[ 2]; ++ Abo = state[ 3]; ++ Abu = state[ 4]; ++ Aga = state[ 5]; ++ Age = state[ 6]; ++ Agi = state[ 7]; ++ Ago = state[ 8]; ++ Agu = state[ 9]; ++ Aka = state[10]; ++ Ake = state[11]; ++ Aki = state[12]; ++ Ako = state[13]; ++ Aku = state[14]; ++ Ama = state[15]; ++ Ame = state[16]; ++ Ami = state[17]; ++ Amo = state[18]; ++ Amu = state[19]; ++ Asa = state[20]; ++ Ase = state[21]; ++ Asi = state[22]; ++ Aso = state[23]; ++ Asu = state[24]; + -+/************************************************* -+* Name: polyvec_compress -+* -+* Description: Compress and serialize vector of polynomials -+* -+* Arguments: - uint8_t *r: pointer to output byte array -+* (needs space for KYBER_POLYVECCOMPRESSEDBYTES) -+* - const polyvec *a: pointer to input vector of polynomials -+**************************************************/ -+static void polyvec_compress(uint8_t r[KYBER_POLYVECCOMPRESSEDBYTES], const polyvec *a) -+{ -+ unsigned int i,j,k; -+ uint64_t d0; ++ for(round = 0; round < NROUNDS; round += 2) { ++ // prepareTheta ++ BCa = Aba^Aga^Aka^Ama^Asa; ++ BCe = Abe^Age^Ake^Ame^Ase; ++ BCi = Abi^Agi^Aki^Ami^Asi; ++ BCo = Abo^Ago^Ako^Amo^Aso; ++ BCu = Abu^Agu^Aku^Amu^Asu; + -+#if (KYBER_POLYVECCOMPRESSEDBYTES == (KYBER_K * 352)) -+ uint16_t t[8]; -+ for(i=0;ivec[i].coeffs[8*j+k]; -+ t[k] += ((int16_t)t[k] >> 15) & KYBER_Q; -+ d0 = t[k]; -+ d0 <<= 11; -+ d0 += 1664; -+ 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); -+ r[ 3] = (t[2] >> 2); -+ r[ 4] = (t[2] >> 10) | (t[3] << 1); -+ r[ 5] = (t[3] >> 7) | (t[4] << 4); -+ r[ 6] = (t[4] >> 4) | (t[5] << 7); -+ r[ 7] = (t[5] >> 1); -+ r[ 8] = (t[5] >> 9) | (t[6] << 2); -+ r[ 9] = (t[6] >> 6) | (t[7] << 5); -+ r[10] = (t[7] >> 3); -+ r += 11; - } - } -+#elif (KYBER_POLYVECCOMPRESSEDBYTES == (KYBER_K * 320)) -+ uint16_t t[4]; -+ for(i=0;ivec[i].coeffs[4*j+k]; -+ t[k] += ((int16_t)t[k] >> 15) & KYBER_Q; -+ d0 = t[k]; -+ d0 <<= 10; -+ d0 += 1665; -+ d0 *= 1290167; -+ d0 >>= 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); -+ r[3] = (t[2] >> 4) | (t[3] << 6); -+ r[4] = (t[3] >> 2); -+ r += 5; -+ } - } -+#else -+#error "KYBER_POLYVECCOMPRESSEDBYTES needs to be in {320*KYBER_K, 352*KYBER_K}" -+#endif - } - --// 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 -+* -+* Description: De-serialize and decompress vector of polynomials; -+* approximate inverse of polyvec_compress -+* -+* Arguments: - polyvec *r: pointer to output vector of polynomials -+* - const uint8_t *a: pointer to input byte array -+* (of length KYBER_POLYVECCOMPRESSEDBYTES) -+**************************************************/ -+static void polyvec_decompress(polyvec *r, const uint8_t a[KYBER_POLYVECCOMPRESSEDBYTES]) -+{ -+ unsigned int i,j,k; ++ //thetaRhoPiChiIotaPrepareTheta(round, A, E) ++ Da = BCu^ROL(BCe, 1); ++ De = BCa^ROL(BCi, 1); ++ Di = BCe^ROL(BCo, 1); ++ Do = BCi^ROL(BCu, 1); ++ Du = BCo^ROL(BCa, 1); + -+#if (KYBER_POLYVECCOMPRESSEDBYTES == (KYBER_K * 352)) -+ uint16_t t[8]; -+ for(i=0;i> 0) | ((uint16_t)a[ 1] << 8); -+ t[1] = (a[1] >> 3) | ((uint16_t)a[ 2] << 5); -+ t[2] = (a[2] >> 6) | ((uint16_t)a[ 3] << 2) | ((uint16_t)a[4] << 10); -+ t[3] = (a[4] >> 1) | ((uint16_t)a[ 5] << 7); -+ t[4] = (a[5] >> 4) | ((uint16_t)a[ 6] << 4); -+ t[5] = (a[6] >> 7) | ((uint16_t)a[ 7] << 1) | ((uint16_t)a[8] << 9); -+ t[6] = (a[8] >> 2) | ((uint16_t)a[ 9] << 6); -+ t[7] = (a[9] >> 5) | ((uint16_t)a[10] << 3); -+ a += 11; ++ Aba ^= Da; ++ BCa = Aba; ++ Age ^= De; ++ BCe = ROL(Age, 44); ++ Aki ^= Di; ++ BCi = ROL(Aki, 43); ++ Amo ^= Do; ++ BCo = ROL(Amo, 21); ++ Asu ^= Du; ++ BCu = ROL(Asu, 14); ++ Eba = BCa ^((~BCe)& BCi ); ++ Eba ^= (uint64_t)KeccakF_RoundConstants[round]; ++ Ebe = BCe ^((~BCi)& BCo ); ++ Ebi = BCi ^((~BCo)& BCu ); ++ Ebo = BCo ^((~BCu)& BCa ); ++ Ebu = BCu ^((~BCa)& BCe ); + -+ for(k=0;k<8;k++) -+ r->vec[i].coeffs[8*j+k] = ((uint32_t)(t[k] & 0x7FF)*KYBER_Q + 1024) >> 11; -+ } -+ } -+#elif (KYBER_POLYVECCOMPRESSEDBYTES == (KYBER_K * 320)) -+ uint16_t t[4]; -+ for(i=0;i> 0) | ((uint16_t)a[1] << 8); -+ t[1] = (a[1] >> 2) | ((uint16_t)a[2] << 6); -+ t[2] = (a[2] >> 4) | ((uint16_t)a[3] << 4); -+ t[3] = (a[3] >> 6) | ((uint16_t)a[4] << 2); -+ a += 5; ++ Abo ^= Do; ++ BCa = ROL(Abo, 28); ++ Agu ^= Du; ++ BCe = ROL(Agu, 20); ++ Aka ^= Da; ++ BCi = ROL(Aka, 3); ++ Ame ^= De; ++ BCo = ROL(Ame, 45); ++ Asi ^= Di; ++ BCu = ROL(Asi, 61); ++ Ega = BCa ^((~BCe)& BCi ); ++ Ege = BCe ^((~BCi)& BCo ); ++ Egi = BCi ^((~BCo)& BCu ); ++ Ego = BCo ^((~BCu)& BCa ); ++ Egu = BCu ^((~BCa)& BCe ); ++ ++ Abe ^= De; ++ BCa = ROL(Abe, 1); ++ Agi ^= Di; ++ BCe = ROL(Agi, 6); ++ Ako ^= Do; ++ BCi = ROL(Ako, 25); ++ Amu ^= Du; ++ BCo = ROL(Amu, 8); ++ Asa ^= Da; ++ BCu = ROL(Asa, 18); ++ Eka = BCa ^((~BCe)& BCi ); ++ Eke = BCe ^((~BCi)& BCo ); ++ Eki = BCi ^((~BCo)& BCu ); ++ Eko = BCo ^((~BCu)& BCa ); ++ Eku = BCu ^((~BCa)& BCe ); ++ ++ Abu ^= Du; ++ BCa = ROL(Abu, 27); ++ Aga ^= Da; ++ BCe = ROL(Aga, 36); ++ Ake ^= De; ++ BCi = ROL(Ake, 10); ++ Ami ^= Di; ++ BCo = ROL(Ami, 15); ++ Aso ^= Do; ++ BCu = ROL(Aso, 56); ++ Ema = BCa ^((~BCe)& BCi ); ++ Eme = BCe ^((~BCi)& BCo ); ++ Emi = BCi ^((~BCo)& BCu ); ++ Emo = BCo ^((~BCu)& BCa ); ++ Emu = BCu ^((~BCa)& BCe ); + -+ 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 -+} ++ Abi ^= Di; ++ BCa = ROL(Abi, 62); ++ Ago ^= Do; ++ BCe = ROL(Ago, 55); ++ Aku ^= Du; ++ BCi = ROL(Aku, 39); ++ Ama ^= Da; ++ BCo = ROL(Ama, 41); ++ Ase ^= De; ++ BCu = ROL(Ase, 2); ++ Esa = BCa ^((~BCe)& BCi ); ++ Ese = BCe ^((~BCi)& BCo ); ++ Esi = BCi ^((~BCo)& BCu ); ++ Eso = BCo ^((~BCu)& BCa ); ++ Esu = BCu ^((~BCa)& BCe ); + -+/************************************************* -+* Name: polyvec_tobytes -+* -+* Description: Serialize vector of polynomials -+* -+* Arguments: - uint8_t *r: pointer to output byte array -+* (needs space for KYBER_POLYVECBYTES) -+* - const polyvec *a: pointer to input vector of polynomials -+**************************************************/ -+static void polyvec_tobytes(uint8_t r[KYBER_POLYVECBYTES], const polyvec *a) -+{ -+ unsigned int i; -+ for(i=0;ivec[i]); - } - --// 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_frombytes -+* -+* Description: De-serialize vector of polynomials; -+* inverse of polyvec_tobytes -+* -+* Arguments: - uint8_t *r: pointer to output byte array -+* - const polyvec *a: pointer to input vector of polynomials -+* (of length KYBER_POLYVECBYTES) -+**************************************************/ -+static void polyvec_frombytes(polyvec *r, const uint8_t a[KYBER_POLYVECBYTES]) -+{ -+ unsigned int i; -+ for(i=0;ivec[i], a+i*KYBER_POLYBYTES); -+} ++ // prepareTheta ++ BCa = Eba^Ega^Eka^Ema^Esa; ++ BCe = Ebe^Ege^Eke^Eme^Ese; ++ BCi = Ebi^Egi^Eki^Emi^Esi; ++ BCo = Ebo^Ego^Eko^Emo^Eso; ++ BCu = Ebu^Egu^Eku^Emu^Esu; + -+/************************************************* -+* Name: polyvec_ntt -+* -+* Description: Apply forward NTT to all elements of a vector of polynomials -+* -+* Arguments: - polyvec *r: pointer to in/output vector of polynomials -+**************************************************/ -+static void polyvec_ntt(polyvec *r) -+{ -+ unsigned int i; -+ for(i=0;ivec[i]); -+} ++ //thetaRhoPiChiIotaPrepareTheta(round+1, E, A) ++ Da = BCu^ROL(BCe, 1); ++ De = BCa^ROL(BCi, 1); ++ Di = BCe^ROL(BCo, 1); ++ Do = BCi^ROL(BCu, 1); ++ Du = BCo^ROL(BCa, 1); + -+/************************************************* -+* Name: polyvec_invntt_tomont -+* -+* Description: Apply inverse NTT to all elements of a vector of polynomials -+* and multiply by Montgomery factor 2^16 -+* -+* Arguments: - polyvec *r: pointer to in/output vector of polynomials -+**************************************************/ -+static void polyvec_invntt_tomont(polyvec *r) -+{ -+ unsigned int i; -+ for(i=0;ivec[i]); -+} ++ Eba ^= Da; ++ BCa = Eba; ++ Ege ^= De; ++ BCe = ROL(Ege, 44); ++ Eki ^= Di; ++ BCi = ROL(Eki, 43); ++ Emo ^= Do; ++ BCo = ROL(Emo, 21); ++ Esu ^= Du; ++ BCu = ROL(Esu, 14); ++ Aba = BCa ^((~BCe)& BCi ); ++ Aba ^= (uint64_t)KeccakF_RoundConstants[round+1]; ++ Abe = BCe ^((~BCi)& BCo ); ++ Abi = BCi ^((~BCo)& BCu ); ++ Abo = BCo ^((~BCu)& BCa ); ++ Abu = BCu ^((~BCa)& BCe ); + -+/************************************************* -+* Name: polyvec_basemul_acc_montgomery -+* -+* Description: Multiply elements of a and b in NTT domain, accumulate into r, -+* and multiply by 2^-16. -+* -+* Arguments: - poly *r: pointer to output polynomial -+* - const polyvec *a: pointer to first input vector of polynomials -+* - const polyvec *b: pointer to second input vector of polynomials -+**************************************************/ -+static void polyvec_basemul_acc_montgomery(poly *r, const polyvec *a, const polyvec *b) -+{ -+ unsigned int i; -+ poly t; ++ Ebo ^= Do; ++ BCa = ROL(Ebo, 28); ++ Egu ^= Du; ++ BCe = ROL(Egu, 20); ++ Eka ^= Da; ++ BCi = ROL(Eka, 3); ++ Eme ^= De; ++ BCo = ROL(Eme, 45); ++ Esi ^= Di; ++ BCu = ROL(Esi, 61); ++ Aga = BCa ^((~BCe)& BCi ); ++ Age = BCe ^((~BCi)& BCo ); ++ Agi = BCi ^((~BCo)& BCu ); ++ Ago = BCo ^((~BCu)& BCa ); ++ Agu = BCu ^((~BCa)& BCe ); + -+ poly_basemul_montgomery(r, &a->vec[0], &b->vec[0]); -+ for(i=1;ivec[i], &b->vec[i]); -+ poly_add(r, r, &t); - } ++ Ebe ^= De; ++ BCa = ROL(Ebe, 1); ++ Egi ^= Di; ++ BCe = ROL(Egi, 6); ++ Eko ^= Do; ++ BCi = ROL(Eko, 25); ++ Emu ^= Du; ++ BCo = ROL(Emu, 8); ++ Esa ^= Da; ++ BCu = ROL(Esa, 18); ++ Aka = BCa ^((~BCe)& BCi ); ++ Ake = BCe ^((~BCi)& BCo ); ++ Aki = BCi ^((~BCo)& BCu ); ++ Ako = BCo ^((~BCu)& BCa ); ++ Aku = BCu ^((~BCa)& BCe ); + -+ poly_reduce(r); - } - --// scalar_decode parses |DEGREE * bits| bits from |in| into |DEGREE| values in --// |out|. It returns one on success and zero if any parsed value is >= --// |kPrime|. --static int scalar_decode(scalar *out, const uint8_t *in, int bits) { -- assert(bits <= (int)sizeof(*out->c) * 8 && bits != 1); -+/************************************************* -+* Name: polyvec_reduce -+* -+* Description: Applies Barrett reduction to each coefficient -+* of each element of a vector of polynomials; -+* for details of the Barrett reduction see comments in reduce.c -+* -+* Arguments: - polyvec *r: pointer to input/output polynomial -+**************************************************/ -+static void polyvec_reduce(polyvec *r) -+{ -+ unsigned int i; -+ for(i=0;ivec[i]); -+} - -- uint8_t in_byte = 0; -- int in_byte_bits_left = 0; -+/************************************************* -+* Name: polyvec_add -+* -+* Description: Add vectors of polynomials -+* -+* Arguments: - polyvec *r: pointer to output vector of polynomials -+* - const polyvec *a: pointer to first input vector of polynomials -+* - const polyvec *b: pointer to second input vector of polynomials -+**************************************************/ -+static void polyvec_add(polyvec *r, const polyvec *a, const polyvec *b) -+{ -+ unsigned int i; -+ for(i=0;ivec[i], &a->vec[i], &b->vec[i]); -+} - -- for (int i = 0; i < DEGREE; i++) { -- uint16_t element = 0; -- int element_bits_done = 0; -+// -+// indcpa.c -+// - -- while (element_bits_done < bits) { -- if (in_byte_bits_left == 0) { -- in_byte = *in; -- in++; -- in_byte_bits_left = 8; -- } -+/************************************************* -+* Name: pack_pk -+* -+* Description: Serialize the public key as concatenation of the -+* serialized vector of polynomials pk -+* and the public seed used to generate the matrix A. -+* -+* Arguments: uint8_t *r: pointer to the output serialized public key -+* polyvec *pk: pointer to the input public-key polyvec -+* const uint8_t *seed: pointer to the input public seed -+**************************************************/ -+static void pack_pk(uint8_t r[KYBER_INDCPA_PUBLICKEYBYTES], -+ polyvec *pk, -+ const uint8_t seed[KYBER_SYMBYTES]) -+{ -+ size_t i; -+ polyvec_tobytes(r, pk); -+ for(i=0;i in_byte_bits_left) { -- chunk_bits = in_byte_bits_left; -- } ++ ++ +/************************************************* -+* Name: unpack_pk -+* -+* Description: De-serialize public key from a byte array; -+* approximate inverse of pack_pk ++* Name: keccak_squeeze +* -+* Arguments: - polyvec *pk: pointer to output public-key polynomial vector -+* - uint8_t *seed: pointer to output seed to generate matrix A -+* - const uint8_t *packedpk: pointer to input serialized public key -+**************************************************/ -+static void unpack_pk(polyvec *pk, -+ uint8_t seed[KYBER_SYMBYTES], -+ const uint8_t packedpk[KYBER_INDCPA_PUBLICKEYBYTES]) -+{ -+ size_t i; -+ polyvec_frombytes(pk, packedpk); -+ for(i=0;i>= chunk_bits; -+/************************************************* -+* Name: pack_sk ++* Description: Squeeze step of Keccak. Squeezes arbitratrily many bytes. ++* Modifies the state. Can be called multiple times to keep ++* squeezing, i.e., is incremental. +* -+* Description: Serialize the secret key ++* Arguments: - uint8_t *out: pointer to output ++* - size_t outlen: number of bytes to be squeezed (written to out) ++* - uint64_t *s: pointer to input/output Keccak state ++* - unsigned int pos: number of bytes in current block already squeezed ++* - unsigned int r: rate in bytes (e.g., 168 for SHAKE128) +* -+* Arguments: - uint8_t *r: pointer to output serialized secret key -+* - polyvec *sk: pointer to input vector of polynomials (secret key) ++* Returns new position pos in current block +**************************************************/ -+static void pack_sk(uint8_t r[KYBER_INDCPA_SECRETKEYBYTES], polyvec *sk) ++static unsigned int keccak_squeeze(uint8_t *out, ++ size_t outlen, ++ uint64_t s[25], ++ unsigned int pos, ++ unsigned int r) +{ -+ polyvec_tobytes(r, sk); ++ unsigned int i; ++ ++ while(outlen) { ++ if(pos == r) { ++ KeccakF1600_StatePermute(s); ++ pos = 0; ++ } ++ for(i=pos;i < r && i < pos+outlen; i++) ++ *out++ = s[i/8] >> 8*(i%8); ++ outlen -= i-pos; ++ pos = i; + } +- return 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(); ++ ++ return pos; +} - -- element_bits_done += chunk_bits; -- } ++ ++ +/************************************************* -+* Name: unpack_sk ++* Name: keccak_absorb_once +* -+* Description: De-serialize the secret key; inverse of pack_sk ++* Description: Absorb step of Keccak; ++* non-incremental, starts by zeroeing the state. +* -+* Arguments: - polyvec *sk: pointer to output vector of polynomials (secret key) -+* - const uint8_t *packedsk: pointer to input serialized secret key ++* Arguments: - uint64_t *s: pointer to (uninitialized) output Keccak state ++* - unsigned int r: rate in bytes (e.g., 168 for SHAKE128) ++* - const uint8_t *in: pointer to input to be absorbed into s ++* - size_t inlen: length of input in bytes ++* - uint8_t p: domain-separation byte for different Keccak-derived functions +**************************************************/ -+static void unpack_sk(polyvec *sk, const uint8_t packedsk[KYBER_INDCPA_SECRETKEYBYTES]) ++static void keccak_absorb_once(uint64_t s[25], ++ unsigned int r, ++ const uint8_t *in, ++ size_t inlen, ++ uint8_t p) +{ -+ polyvec_frombytes(sk, packedsk); -+} ++ unsigned int i; ++ ++ for(i=0;i<25;i++) ++ s[i] = 0; ++ ++ while(inlen >= r) { ++ for(i=0;i= kPrime) { -- return 0; -- } -- out->c[i] = element; -- } -+/************************************************* -+* Name: pack_ciphertext -+* -+* Description: Serialize the ciphertext as concatenation of the -+* compressed and serialized vector of polynomials b -+* and the compressed and serialized polynomial v -+* -+* Arguments: uint8_t *r: pointer to the output serialized ciphertext -+* poly *pk: pointer to the input vector of polynomials b -+* poly *v: pointer to the input polynomial v -+**************************************************/ -+static void pack_ciphertext(uint8_t r[KYBER_INDCPA_BYTES], polyvec *b, poly *v) -+{ -+ polyvec_compress(r, b); -+ poly_compress(r+KYBER_POLYVECCOMPRESSEDBYTES, v); +- BORINGSSL_keccak(priv->pub.public_key_hash, sizeof(priv->pub.public_key_hash), +- out_encoded_public_key, KYBER_PUBLIC_KEY_BYTES, +- boringssl_sha3_256); +- OPENSSL_memcpy(priv->fo_failure_secret, entropy + 32, 32); +-} +- +-void KYBER_public_from_private(struct KYBER_public_key *out_public_key, +- const struct KYBER_private_key *private_key) { +- struct public_key *const pub = public_key_from_external(out_public_key); +- const struct private_key *const priv = private_key_from_external(private_key); +- *pub = priv->pub; +-} +- +-// Algorithm 5 of the Kyber spec. Encrypts a message with given randomness to +-// the ciphertext in |out|. Without applying the Fujisaki-Okamoto transform this +-// would not result in a CCA secure scheme, since lattice schemes are vulnerable +-// to decryption failure oracles. +-static void encrypt_cpa(uint8_t out[KYBER_CIPHERTEXT_BYTES], +- const struct public_key *pub, const uint8_t message[32], +- const uint8_t randomness[32]) { +- uint8_t counter = 0; +- vector secret; +- vector_generate_secret_eta_2(&secret, &counter, randomness); +- vector_ntt(&secret); +- vector error; +- vector_generate_secret_eta_2(&error, &counter, randomness); +- uint8_t input[33]; +- OPENSSL_memcpy(input, randomness, 32); +- input[32] = counter; +- scalar scalar_error; +- scalar_centered_binomial_distribution_eta_2_with_prf(&scalar_error, input); +- vector u; +- matrix_mult(&u, &pub->m, &secret); +- vector_inverse_ntt(&u); +- vector_add(&u, &error); +- scalar v; +- scalar_inner_product(&v, &pub->t, &secret); +- scalar_inverse_ntt(&v); +- scalar_add(&v, &scalar_error); +- scalar expanded_message; +- scalar_decode_1(&expanded_message, message); +- scalar_decompress(&expanded_message, 1); +- scalar_add(&v, &expanded_message); +- vector_compress(&u, kDU); +- vector_encode(out, &u, kDU); +- scalar_compress(&v, kDV); +- scalar_encode(out + kCompressedVectorSize, &v, kDV); +-} +- +-// Calls KYBER_encap_external_entropy| with random bytes from |RAND_bytes| +-void KYBER_encap(uint8_t out_ciphertext[KYBER_CIPHERTEXT_BYTES], +- uint8_t *out_shared_secret, size_t out_shared_secret_len, +- const struct KYBER_public_key *public_key) { +- uint8_t entropy[KYBER_ENCAP_ENTROPY]; +- RAND_bytes(entropy, KYBER_ENCAP_ENTROPY); +- KYBER_encap_external_entropy(out_ciphertext, out_shared_secret, +- out_shared_secret_len, public_key, entropy); +-} +- +-// Algorithm 8 of the Kyber spec, safe for line 2 of the spec. The spec there +-// hashes the output of the system's random number generator, since the FO +-// transform will reveal it to the decrypting party. There is no reason to do +-// this when a secure random number generator is used. When an insecure random +-// number generator is used, the caller should switch to a secure one before +-// calling this method. +-void KYBER_encap_external_entropy( +- uint8_t out_ciphertext[KYBER_CIPHERTEXT_BYTES], uint8_t *out_shared_secret, +- size_t out_shared_secret_len, const struct KYBER_public_key *public_key, +- const uint8_t entropy[KYBER_ENCAP_ENTROPY]) { +- const struct public_key *pub = public_key_from_external(public_key); +- uint8_t input[64]; +- OPENSSL_memcpy(input, entropy, KYBER_ENCAP_ENTROPY); +- OPENSSL_memcpy(input + KYBER_ENCAP_ENTROPY, pub->public_key_hash, +- sizeof(input) - KYBER_ENCAP_ENTROPY); +- uint8_t prekey_and_randomness[64]; +- BORINGSSL_keccak(prekey_and_randomness, sizeof(prekey_and_randomness), input, +- sizeof(input), boringssl_sha3_512); +- encrypt_cpa(out_ciphertext, pub, entropy, prekey_and_randomness + 32); +- BORINGSSL_keccak(prekey_and_randomness + 32, 32, out_ciphertext, +- KYBER_CIPHERTEXT_BYTES, boringssl_sha3_256); +- BORINGSSL_keccak(out_shared_secret, out_shared_secret_len, +- prekey_and_randomness, sizeof(prekey_and_randomness), +- boringssl_shake256); +-} +- +-// Algorithm 6 of the Kyber spec. +-static void decrypt_cpa(uint8_t out[32], const struct private_key *priv, +- const uint8_t ciphertext[KYBER_CIPHERTEXT_BYTES]) { +- vector u; +- vector_decode(&u, ciphertext, kDU); +- vector_decompress(&u, kDU); +- vector_ntt(&u); +- scalar v; +- scalar_decode(&v, ciphertext + kCompressedVectorSize, kDV); +- scalar_decompress(&v, kDV); +- scalar mask; +- scalar_inner_product(&mask, &priv->s, &u); +- scalar_inverse_ntt(&mask); +- scalar_sub(&v, &mask); +- scalar_compress(&v, 1); +- scalar_encode_1(out, &v); +-} +- +-// Algorithm 9 of the Kyber spec, performing the FO transform by running +-// encrypt_cpa on the decrypted message. The spec does not allow the decryption +-// failure to be passed on to the caller, and instead returns a result that is +-// deterministic but unpredictable to anyone without knowledge of the private +-// key. +-void KYBER_decap(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) { +- const struct private_key *priv = private_key_from_external(private_key); +- uint8_t decrypted[64]; +- decrypt_cpa(decrypted, priv, ciphertext); +- OPENSSL_memcpy(decrypted + 32, priv->pub.public_key_hash, +- sizeof(decrypted) - 32); +- uint8_t prekey_and_randomness[64]; +- BORINGSSL_keccak(prekey_and_randomness, sizeof(prekey_and_randomness), +- decrypted, sizeof(decrypted), boringssl_sha3_512); +- uint8_t expected_ciphertext[KYBER_CIPHERTEXT_BYTES]; +- encrypt_cpa(expected_ciphertext, &priv->pub, decrypted, +- prekey_and_randomness + 32); +- uint8_t mask = +- constant_time_eq_int_8(CRYPTO_memcmp(ciphertext, expected_ciphertext, +- sizeof(expected_ciphertext)), +- 0); +- uint8_t input[64]; +- 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;ic[i + j] = in_byte & 1; -- in_byte >>= 1; -- } ++ +/************************************************* -+* Name: rej_uniform -+* -+* Description: Run rejection sampling on uniform random bytes to generate -+* uniform random integers mod q ++* Name: keccak_squeezeblocks +* -+* Arguments: - int16_t *r: pointer to output buffer -+* - unsigned int len: requested number of 16-bit integers (uniform mod q) -+* - const uint8_t *buf: pointer to input buffer (assumed to be uniformly random bytes) -+* - unsigned int buflen: length of input buffer in bytes ++* Description: Squeeze step of Keccak. Squeezes full blocks of r bytes each. ++* Modifies the state. Can be called multiple times to keep ++* squeezing, i.e., is incremental. Assumes zero bytes of current ++* block have already been squeezed. +* -+* Returns number of sampled 16-bit integers (at most len) ++* Arguments: - uint8_t *out: pointer to output blocks ++* - size_t nblocks: number of blocks to be squeezed (written to out) ++* - uint64_t *s: pointer to input/output Keccak state ++* - unsigned int r: rate in bytes (e.g., 168 for SHAKE128) +**************************************************/ -+static unsigned int rej_uniform(int16_t *r, -+ unsigned int len, -+ const uint8_t *buf, -+ unsigned int buflen) ++static void keccak_squeezeblocks(uint8_t *out, ++ size_t nblocks, ++ uint64_t s[25], ++ unsigned int r) +{ -+ unsigned int ctr, pos; -+ uint16_t val0, val1; -+ -+ ctr = pos = 0; -+ while(ctr < len && pos + 3 <= buflen) { -+ val0 = ((buf[pos+0] >> 0) | ((uint16_t)buf[pos+1] << 8)) & 0xFFF; -+ val1 = ((buf[pos+1] >> 4) | ((uint16_t)buf[pos+2] << 4)) & 0xFFF; -+ pos += 3; ++ unsigned int i; + -+ if(val0 < KYBER_Q) -+ r[ctr++] = val0; -+ if(ctr < len && val1 < KYBER_Q) -+ r[ctr++] = val1; ++ while(nblocks) { ++ KeccakF1600_StatePermute(s); ++ for(i=0;i= |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) +-int KYBER_marshal_public_key(CBB *out, +- const struct KYBER_public_key *public_key) { +- return kyber_marshal_public_key(out, public_key_from_external(public_key)); + +/************************************************* -+* Name: gen_matrix ++* Name: shake128_absorb_once +* -+* Description: Deterministically generate matrix A (or the transpose of A) -+* from a seed. Entries of the matrix are polynomials that look -+* uniformly random. Performs rejection sampling on output of -+* a XOF ++* Description: Initialize, absorb into and finalize SHAKE128 XOF; non-incremental. +* -+* Arguments: - polyvec *a: pointer to ouptput matrix A -+* - const uint8_t *seed: pointer to input seed -+* - int transposed: boolean deciding whether A or A^T is generated ++* 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 +**************************************************/ -+#define GEN_MATRIX_NBLOCKS ((12*KYBER_N/8*(1 << 12)/KYBER_Q + XOF_BLOCKBYTES)/XOF_BLOCKBYTES) -+// Not static for benchmarking -+static void gen_matrix(polyvec *a, const uint8_t seed[KYBER_SYMBYTES], int transposed) ++static void shake128_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen) +{ -+ unsigned int ctr, i, j, k; -+ unsigned int buflen, off; -+ uint8_t buf[GEN_MATRIX_NBLOCKS*XOF_BLOCKBYTES+2]; -+ xof_state state; -+ -+ for(i=0;i> 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); ++ keccak_absorb_once(state->s, 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) { +- CBS t_bytes; +- if (!CBS_get_bytes(in, &t_bytes, kEncodedVectorSize) || +- !vector_decode(&pub->t, CBS_data(&t_bytes), kLog2Prime) || +- !CBS_copy_bytes(in, pub->rho, sizeof(pub->rho))) { +- return 0; - } +- 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); } --static void scalar_decompress(scalar *s, int bits) { -- for (int i = 0; i < DEGREE; i++) { -- s->c[i] = decompress(s->c[i], bits); +-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) { +- 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; +/************************************************* -+* Name: indcpa_keypair ++* Name: shake256_squeeze +* -+* Description: Generates public and private key for the CPA-secure -+* public-key encryption scheme underlying Kyber ++* Description: Squeeze step of SHAKE256 XOF. Squeezes arbitraily many ++* bytes. Can be called multiple times to keep squeezing. +* -+* Arguments: - uint8_t *pk: pointer to output public key -+* (of length KYBER_INDCPA_PUBLICKEYBYTES bytes) -+* - uint8_t *sk: pointer to output private key -+ (of length KYBER_INDCPA_SECRETKEYBYTES bytes) ++* 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 indcpa_keypair(uint8_t pk[KYBER_INDCPA_PUBLICKEYBYTES], -+ uint8_t sk[KYBER_INDCPA_SECRETKEYBYTES], -+ const uint8_t seed[KYBER_SYMBYTES]) ++static void shake256_squeeze(uint8_t *out, size_t outlen, keccak_state *state) +{ -+ unsigned int i; -+ uint8_t buf[2*KYBER_SYMBYTES]; -+ const uint8_t *publicseed = buf; -+ const uint8_t *noiseseed = buf+KYBER_SYMBYTES; -+ uint8_t nonce = 0; -+ polyvec a[KYBER_K], e, pkpv, skpv; -+ -+ memcpy(buf, seed, KYBER_SYMBYTES); -+ hash_g(buf, buf, KYBER_SYMBYTES); -+ -+ gen_a(a, publicseed); -+ -+ for(i=0;ipos = keccak_squeeze(out, outlen, state->s, state->pos, SHAKE256_RATE); } --static void vector_compress(vector *a, int bits) { -- for (int i = 0; i < RANK; i++) { -- scalar_compress(&a->v[i], bits); +-int KYBER_marshal_private_key(CBB *out, +- const struct KYBER_private_key *private_key) { +- const struct private_key *const priv = private_key_from_external(private_key); +- uint8_t *s_output; +- if (!CBB_add_space(out, &s_output, kEncodedVectorSize)) { +- return 0; +- } +- vector_encode(s_output, &priv->s, kLog2Prime); +- if (!kyber_marshal_public_key(out, &priv->pub) || +- !CBB_add_bytes(out, priv->pub.public_key_hash, +- sizeof(priv->pub.public_key_hash)) || +- !CBB_add_bytes(out, priv->fo_failure_secret, +- sizeof(priv->fo_failure_secret))) { +- return 0; - } +- return 1; +-} +- +-int KYBER_parse_private_key(struct KYBER_private_key *out_private_key, +- CBS *in) { +- struct private_key *const priv = private_key_from_external(out_private_key); +- +- CBS s_bytes; +- if (!CBS_get_bytes(in, &s_bytes, kEncodedVectorSize) || +- !vector_decode(&priv->s, CBS_data(&s_bytes), kLog2Prime) || +- !kyber_parse_public_key_no_hash(&priv->pub, in) || +- !CBS_copy_bytes(in, priv->pub.public_key_hash, +- sizeof(priv->pub.public_key_hash)) || +- !CBS_copy_bytes(in, priv->fo_failure_secret, +- sizeof(priv->fo_failure_secret)) || +- CBS_len(in) != 0) { +- return 0; +/************************************************* -+* Name: indcpa_enc ++* Name: shake256_absorb_once +* -+* Description: Encryption function of the CPA-secure -+* public-key encryption scheme underlying Kyber. ++* Description: Initialize, absorb into and finalize SHAKE256 XOF; non-incremental. +* -+* Arguments: - uint8_t *c: pointer to output ciphertext -+* (of length KYBER_INDCPA_BYTES bytes) -+* - const uint8_t *m: pointer to input message -+* (of length KYBER_INDCPA_MSGBYTES bytes) -+* - const uint8_t *pk: pointer to input public key -+* (of length KYBER_INDCPA_PUBLICKEYBYTES) -+* - const uint8_t *coins: pointer to input random coins used as seed -+* (of length KYBER_SYMBYTES) to deterministically -+* generate all randomness ++* 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 indcpa_enc(uint8_t c[KYBER_INDCPA_BYTES], -+ const uint8_t m[KYBER_INDCPA_MSGBYTES], -+ const uint8_t pk[KYBER_INDCPA_PUBLICKEYBYTES], -+ const uint8_t coins[KYBER_SYMBYTES]) ++static void shake256_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen) +{ -+ unsigned int i; -+ uint8_t seed[KYBER_SYMBYTES]; -+ uint8_t nonce = 0; -+ polyvec sp, pkpv, ep, at[KYBER_K], b; -+ poly v, k, epp; -+ -+ unpack_pk(&pkpv, seed, pk); -+ poly_frommsg(&k, m); -+ gen_at(at, seed); -+ -+ for(i=0;is, SHAKE256_RATE, in, inlen, 0x1F); ++ state->pos = SHAKE256_RATE; ++} + -+ polyvec_basemul_acc_montgomery(&v, &pkpv, &sp); ++/************************************************* ++* 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); ++} + -+ polyvec_invntt_tomont(&b); -+ poly_invntt_tomont(&v); ++/************************************************* ++* 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; + -+ polyvec_add(&b, &b, &ep); -+ poly_add(&v, &v, &epp); -+ poly_add(&v, &v, &k); -+ polyvec_reduce(&b); -+ poly_reduce(&v); ++ 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); ++} + -+ pack_ciphertext(c, &b, &v); - } - --static void vector_decompress(vector *a, int bits) { -- for (int i = 0; i < RANK; i++) { -- scalar_decompress(&a->v[i], bits); -- } +/************************************************* -+* Name: indcpa_dec ++* Name: sha3_256 +* -+* Description: Decryption function of the CPA-secure -+* public-key encryption scheme underlying Kyber. ++* Description: SHA3-256 with non-incremental API +* -+* Arguments: - uint8_t *m: pointer to output decrypted message -+* (of length KYBER_INDCPA_MSGBYTES) -+* - const uint8_t *c: pointer to input ciphertext -+* (of length KYBER_INDCPA_BYTES) -+* - const uint8_t *sk: pointer to input secret key -+* (of length KYBER_INDCPA_SECRETKEYBYTES) ++* 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 indcpa_dec(uint8_t m[KYBER_INDCPA_MSGBYTES], -+ const uint8_t c[KYBER_INDCPA_BYTES], -+ const uint8_t sk[KYBER_INDCPA_SECRETKEYBYTES]) ++static void sha3_256(uint8_t h[32], const uint8_t *in, size_t inlen) +{ -+ polyvec b, skpv; -+ poly v, mp; ++ unsigned int i; ++ uint64_t s[25]; + -+ unpack_ciphertext(&b, &v, c); -+ unpack_sk(&skpv, sk); ++ 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]); ++} + -+ polyvec_ntt(&b); -+ polyvec_basemul_acc_montgomery(&mp, &skpv, &b); -+ poly_invntt_tomont(&mp); ++/************************************************* ++* 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]; + -+ poly_sub(&mp, &v, &mp); -+ poly_reduce(&mp); ++ 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 ++// + -+ poly_tomsg(m, &mp); - } - --struct public_key { -- vector t; -- uint8_t rho[32]; -- uint8_t public_key_hash[32]; -- matrix m; --}; +/************************************************* +* Name: kyber_shake128_absorb +* @@ -3360,7 +3336,7 @@ index 776c085f9..5acd45cd9 100644 +* - uint8_t i: additional byte of input +* - uint8_t j: additional byte of input +**************************************************/ -+void kyber_shake128_absorb(keccak_state *state, ++static void kyber_shake128_absorb(keccak_state *state, + const uint8_t seed[KYBER_SYMBYTES], + uint8_t x, + uint8_t y) @@ -3373,14 +3349,7 @@ index 776c085f9..5acd45cd9 100644 + + shake128_absorb_once(state, extseed, sizeof(extseed)); +} - --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; ++ +/************************************************* +* Name: kyber_shake256_prf +* @@ -3400,76 +3369,12 @@ index 776c085f9..5acd45cd9 100644 + extkey[KYBER_SYMBYTES] = nonce; + + shake256(out, outlen, extkey, sizeof(extkey)); - } - --struct private_key { -- struct public_key pub; -- vector s; -- uint8_t fo_failure_secret[32]; --}; ++} ++ +// +// kem.c +// - --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(); ++ +// 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]) @@ -3486,13 +3391,11 @@ index 776c085f9..5acd45cd9 100644 + memcpy(sk+KYBER_SECRETKEYBYTES-KYBER_SYMBYTES, seed+KYBER_SYMBYTES, KYBER_SYMBYTES); +} + -+// Internal version that allows us to select between initial public draft -+// (when ipd=1) and round3 kyber (ipd=0). -+void encap2(uint8_t out_ciphertext[KYBER_CIPHERTEXTBYTES], ++// Modified crypto_kem_enc to BoringSSL style API ++void 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 ipd) ++ const uint8_t seed[KYBER_ENCAP_BYTES]) +{ + const uint8_t *pk = &in_pub->opaque[0]; + uint8_t *ct = out_ciphertext; @@ -3502,142 +3405,9 @@ index 776c085f9..5acd45cd9 100644 + uint8_t kr[2*KYBER_SYMBYTES]; + + memcpy(buf, seed, KYBER_SYMBYTES); ++ /* Don't release system RNG output */ ++ hash_h(buf, buf, KYBER_SYMBYTES); + -+ if (ipd == 0) { -+ /* Don't release system RNG output */ -+ hash_h(buf, buf, KYBER_SYMBYTES); - } - -- BORINGSSL_keccak(priv->pub.public_key_hash, sizeof(priv->pub.public_key_hash), -- out_encoded_public_key, KYBER_PUBLIC_KEY_BYTES, -- boringssl_sha3_256); -- OPENSSL_memcpy(priv->fo_failure_secret, entropy + 32, 32); --} -- --void KYBER_public_from_private(struct KYBER_public_key *out_public_key, -- const struct KYBER_private_key *private_key) { -- struct public_key *const pub = public_key_from_external(out_public_key); -- const struct private_key *const priv = private_key_from_external(private_key); -- *pub = priv->pub; --} -- --// Algorithm 5 of the Kyber spec. Encrypts a message with given randomness to --// the ciphertext in |out|. Without applying the Fujisaki-Okamoto transform this --// would not result in a CCA secure scheme, since lattice schemes are vulnerable --// to decryption failure oracles. --static void encrypt_cpa(uint8_t out[KYBER_CIPHERTEXT_BYTES], -- const struct public_key *pub, const uint8_t message[32], -- const uint8_t randomness[32]) { -- uint8_t counter = 0; -- vector secret; -- vector_generate_secret_eta_2(&secret, &counter, randomness); -- vector_ntt(&secret); -- vector error; -- vector_generate_secret_eta_2(&error, &counter, randomness); -- uint8_t input[33]; -- OPENSSL_memcpy(input, randomness, 32); -- input[32] = counter; -- scalar scalar_error; -- scalar_centered_binomial_distribution_eta_2_with_prf(&scalar_error, input); -- vector u; -- matrix_mult(&u, &pub->m, &secret); -- vector_inverse_ntt(&u); -- vector_add(&u, &error); -- scalar v; -- scalar_inner_product(&v, &pub->t, &secret); -- scalar_inverse_ntt(&v); -- scalar_add(&v, &scalar_error); -- scalar expanded_message; -- scalar_decode_1(&expanded_message, message); -- scalar_decompress(&expanded_message, 1); -- scalar_add(&v, &expanded_message); -- vector_compress(&u, kDU); -- vector_encode(out, &u, kDU); -- scalar_compress(&v, kDV); -- scalar_encode(out + kCompressedVectorSize, &v, kDV); --} -- --// Calls KYBER_encap_external_entropy| with random bytes from |RAND_bytes| --void KYBER_encap(uint8_t out_ciphertext[KYBER_CIPHERTEXT_BYTES], -- uint8_t *out_shared_secret, size_t out_shared_secret_len, -- const struct KYBER_public_key *public_key) { -- uint8_t entropy[KYBER_ENCAP_ENTROPY]; -- RAND_bytes(entropy, KYBER_ENCAP_ENTROPY); -- KYBER_encap_external_entropy(out_ciphertext, out_shared_secret, -- out_shared_secret_len, public_key, entropy); --} -- --// Algorithm 8 of the Kyber spec, safe for line 2 of the spec. The spec there --// hashes the output of the system's random number generator, since the FO --// transform will reveal it to the decrypting party. There is no reason to do --// this when a secure random number generator is used. When an insecure random --// number generator is used, the caller should switch to a secure one before --// calling this method. --void KYBER_encap_external_entropy( -- uint8_t out_ciphertext[KYBER_CIPHERTEXT_BYTES], uint8_t *out_shared_secret, -- size_t out_shared_secret_len, const struct KYBER_public_key *public_key, -- const uint8_t entropy[KYBER_ENCAP_ENTROPY]) { -- const struct public_key *pub = public_key_from_external(public_key); -- uint8_t input[64]; -- OPENSSL_memcpy(input, entropy, KYBER_ENCAP_ENTROPY); -- OPENSSL_memcpy(input + KYBER_ENCAP_ENTROPY, pub->public_key_hash, -- sizeof(input) - KYBER_ENCAP_ENTROPY); -- uint8_t prekey_and_randomness[64]; -- BORINGSSL_keccak(prekey_and_randomness, sizeof(prekey_and_randomness), input, -- sizeof(input), boringssl_sha3_512); -- encrypt_cpa(out_ciphertext, pub, entropy, prekey_and_randomness + 32); -- BORINGSSL_keccak(prekey_and_randomness + 32, 32, out_ciphertext, -- KYBER_CIPHERTEXT_BYTES, boringssl_sha3_256); -- BORINGSSL_keccak(out_shared_secret, out_shared_secret_len, -- prekey_and_randomness, sizeof(prekey_and_randomness), -- boringssl_shake256); --} -- --// Algorithm 6 of the Kyber spec. --static void decrypt_cpa(uint8_t out[32], const struct private_key *priv, -- const uint8_t ciphertext[KYBER_CIPHERTEXT_BYTES]) { -- vector u; -- vector_decode(&u, ciphertext, kDU); -- vector_decompress(&u, kDU); -- vector_ntt(&u); -- scalar v; -- scalar_decode(&v, ciphertext + kCompressedVectorSize, kDV); -- scalar_decompress(&v, kDV); -- scalar mask; -- scalar_inner_product(&mask, &priv->s, &u); -- scalar_inverse_ntt(&mask); -- scalar_sub(&v, &mask); -- scalar_compress(&v, 1); -- scalar_encode_1(out, &v); --} -- --// Algorithm 9 of the Kyber spec, performing the FO transform by running --// encrypt_cpa on the decrypted message. The spec does not allow the decryption --// failure to be passed on to the caller, and instead returns a result that is --// deterministic but unpredictable to anyone without knowledge of the private --// key. --void KYBER_decap(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) { -- const struct private_key *priv = private_key_from_external(private_key); -- uint8_t decrypted[64]; -- decrypt_cpa(decrypted, priv, ciphertext); -- OPENSSL_memcpy(decrypted + 32, priv->pub.public_key_hash, -- sizeof(decrypted) - 32); -- uint8_t prekey_and_randomness[64]; -- BORINGSSL_keccak(prekey_and_randomness, sizeof(prekey_and_randomness), -- decrypted, sizeof(decrypted), boringssl_sha3_512); -- uint8_t expected_ciphertext[KYBER_CIPHERTEXT_BYTES]; -- encrypt_cpa(expected_ciphertext, &priv->pub, decrypted, -- prekey_and_randomness + 32); -- uint8_t mask = -- constant_time_eq_int_8(CRYPTO_memcmp(ciphertext, expected_ciphertext, -- sizeof(expected_ciphertext)), -- 0); -- uint8_t input[64]; -- for (int i = 0; i < 32; i++) { -- input[i] = constant_time_select_8(mask, prekey_and_randomness[i], -- priv->fo_failure_secret[i]); + /* Multitarget countermeasure for coins + contributory KEM */ + hash_h(buf+KYBER_SYMBYTES, pk, KYBER_PUBLICKEYBYTES); + hash_g(kr, buf, 2*KYBER_SYMBYTES); @@ -3645,44 +3415,16 @@ index 776c085f9..5acd45cd9 100644 + /* coins are in kr+KYBER_SYMBYTES */ + indcpa_enc(ct, buf, pk, kr+KYBER_SYMBYTES); + -+ if (ipd == 1) { -+ memcpy(ss, kr, KYBER_SYMBYTES); -+ } else { -+ /* overwrite coins in kr with H(c) */ -+ hash_h(kr+KYBER_SYMBYTES, ct, KYBER_CIPHERTEXTBYTES); -+ /* hash concatenation of pre-k and H(c) to k */ -+ kdf(ss, kr, 2*KYBER_SYMBYTES); - } -- BORINGSSL_keccak(input + 32, 32, ciphertext, KYBER_CIPHERTEXT_BYTES, -- boringssl_sha3_256); -- BORINGSSL_keccak(out_shared_secret, out_shared_secret_len, input, -- sizeof(input), boringssl_shake256); - } - --int KYBER_marshal_public_key(CBB *out, -- const struct KYBER_public_key *public_key) { -- return kyber_marshal_public_key(out, public_key_from_external(public_key)); -+// Modified crypto_kem_enc to BoringSSL style API. -+void 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]) { -+ encap2(out_ciphertext, ss, in_pub, seed, 0); - } - --// 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) { -- CBS t_bytes; -- if (!CBS_get_bytes(in, &t_bytes, kEncodedVectorSize) || -- !vector_decode(&pub->t, CBS_data(&t_bytes), kLog2Prime) || -- !CBS_copy_bytes(in, pub->rho, sizeof(pub->rho))) { -- return 0; -+// Internal version that allows us to select between initial public draft -+// (when ipd=1) and round3 kyber (ipd=0). -+void decap2(uint8_t out_shared_key[KYBER_SSBYTES], ++ /* overwrite coins in kr with H(c) */ ++ hash_h(kr+KYBER_SYMBYTES, ct, KYBER_CIPHERTEXTBYTES); ++ /* hash concatenation of pre-k and H(c) to k */ ++ kdf(ss, kr, 2*KYBER_SYMBYTES); ++} ++ ++// Modified crypto_kem_decap to BoringSSL style API ++void decap(uint8_t out_shared_key[KYBER_SSBYTES], + const struct private_key *in_priv, -+ const uint8_t *ct, size_t ciphertext_len, int ipd) ++ const uint8_t *ct, size_t ciphertext_len) +{ + uint8_t *ss = out_shared_key; + const uint8_t *sk = &in_priv->opaque[0]; @@ -3708,89 +3450,17 @@ index 776c085f9..5acd45cd9 100644 + + fail = verify(ct, cmp, KYBER_CIPHERTEXTBYTES); } -- matrix_expand(&pub->m, pub->rho); -- return 1; --} - --int KYBER_parse_public_key(struct KYBER_public_key *public_key, CBS *in) { -- struct public_key *pub = public_key_from_external(public_key); -- CBS orig_in = *in; -- if (!kyber_parse_public_key_no_hash(pub, in) || // -- CBS_len(in) != 0) { -- return 0; -+ if (ipd == 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); - } -- BORINGSSL_keccak(pub->public_key_hash, sizeof(pub->public_key_hash), -- CBS_data(&orig_in), CBS_len(&orig_in), boringssl_sha3_256); -- return 1; - } - --int KYBER_marshal_private_key(CBB *out, -- const struct KYBER_private_key *private_key) { -- const struct private_key *const priv = private_key_from_external(private_key); -- uint8_t *s_output; -- if (!CBB_add_space(out, &s_output, kEncodedVectorSize)) { -- return 0; -- } -- vector_encode(s_output, &priv->s, kLog2Prime); -- if (!kyber_marshal_public_key(out, &priv->pub) || -- !CBB_add_bytes(out, priv->pub.public_key_hash, -- sizeof(priv->pub.public_key_hash)) || -- !CBB_add_bytes(out, priv->fo_failure_secret, -- sizeof(priv->fo_failure_secret))) { -- return 0; -- } -- return 1; --} -- --int KYBER_parse_private_key(struct KYBER_private_key *out_private_key, -- CBS *in) { -- struct private_key *const priv = private_key_from_external(out_private_key); -- -- CBS s_bytes; -- if (!CBS_get_bytes(in, &s_bytes, kEncodedVectorSize) || -- !vector_decode(&priv->s, CBS_data(&s_bytes), kLog2Prime) || -- !kyber_parse_public_key_no_hash(&priv->pub, in) || -- !CBS_copy_bytes(in, priv->pub.public_key_hash, -- sizeof(priv->pub.public_key_hash)) || -- !CBS_copy_bytes(in, priv->fo_failure_secret, -- sizeof(priv->fo_failure_secret)) || -- CBS_len(in) != 0) { -- return 0; -- } - return 1; -+// Modified crypto_kem_decap to BoringSSL style API -+void decap(uint8_t out_shared_key[KYBER_SSBYTES], -+ const struct private_key *in_priv, -+ const uint8_t *ct, size_t ciphertext_len) { -+ decap2(out_shared_key, in_priv, ct, ciphertext_len, 0); -+} + ++ /* 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); ++} + +void marshal_public_key(uint8_t out[KYBER_PUBLICKEYBYTES], + const struct public_key *in_pub) { @@ -3801,41 +3471,6 @@ index 776c085f9..5acd45cd9 100644 + const uint8_t in[KYBER_PUBLICKEYBYTES]) { + memcpy(&out->opaque, in, KYBER_PUBLICKEYBYTES); } -diff --git a/src/crypto/kyber/kyber.h b/src/crypto/kyber/kyber.h -new file mode 100644 -index 000000000..16e47d582 ---- /dev/null -+++ b/src/crypto/kyber/kyber.h -@@ -0,0 +1,29 @@ -+#ifndef OPENSSL_HEADER_KYBER_KYBER_H -+#define OPENSSL_HEADER_KYBER_KYBER_H -+ -+#include -+ -+#include -+#include -+ -+void KYBER512_encap2(uint8_t out_ciphertext[KYBER512_CIPHERTEXT_BYTES], -+ uint8_t ss[KYBER_KEY_BYTES], -+ const struct KYBER512_public_key *in_pub, -+ const uint8_t seed[KYBER_ENCAP_BYTES], -+ int ipd); -+ -+void KYBER512_decap2(uint8_t out_shared_key[KYBER_KEY_BYTES], -+ const struct KYBER512_private_key *in_priv, -+ const uint8_t *ct, size_t ciphertext_len, int ipd); -+ -+void KYBER768_encap2(uint8_t out_ciphertext[KYBER768_CIPHERTEXT_BYTES], -+ uint8_t ss[KYBER_KEY_BYTES], -+ const struct KYBER768_public_key *in_pub, -+ const uint8_t seed[KYBER_ENCAP_BYTES], -+ int ipd); -+ -+void KYBER768_decap2(uint8_t out_shared_key[KYBER_KEY_BYTES], -+ const struct KYBER768_private_key *in_priv, -+ const uint8_t *ct, size_t ciphertext_len, int ipd); -+ -+#endif diff --git a/src/crypto/kyber/kyber512.c b/src/crypto/kyber/kyber512.c new file mode 100644 index 000000000..21eed11a2 @@ -4093,7 +3728,7 @@ index eb76b5bd7..000000000 - FileTestGTest("crypto/kyber/kyber_tests.txt", KyberFileTest); -} diff --git a/src/crypto/obj/obj_dat.h b/src/crypto/obj/obj_dat.h -index 654b3c08e..0d3d9f24f 100644 +index 654b3c08e..06f80f971 100644 --- a/src/crypto/obj/obj_dat.h +++ b/src/crypto/obj/obj_dat.h @@ -57,7 +57,7 @@ @@ -4101,11 +3736,11 @@ index 654b3c08e..0d3d9f24f 100644 -#define NUM_NID 965 -+#define NUM_NID 969 ++#define NUM_NID 968 static const uint8_t kObjectData[] = { /* NID_rsadsi */ -@@ -8784,6 +8784,13 @@ static const ASN1_OBJECT kObjects[NUM_NID] = { +@@ -8784,6 +8784,12 @@ static const ASN1_OBJECT kObjects[NUM_NID] = { {"HKDF", "hkdf", NID_hkdf, 0, NULL, 0}, {"X25519Kyber768Draft00", "X25519Kyber768Draft00", NID_X25519Kyber768Draft00, 0, NULL, 0}, @@ -4115,19 +3750,10 @@ index 654b3c08e..0d3d9f24f 100644 + NULL, 0}, + {"X25519Kyber768Draft00Old", "X25519Kyber768Draft00Old", + NID_X25519Kyber768Draft00Old, 0, NULL, 0}, -+ {"IPDWing", "IPDWing", NID_IPDWing, 0, NULL, 0}, }; static const uint16_t kNIDsInShortNameOrder[] = { -@@ -8889,6 +8896,7 @@ static const uint16_t kNIDsInShortNameOrder[] = { - 35 /* IDEA-CFB */, - 36 /* IDEA-ECB */, - 46 /* IDEA-OFB */, -+ 968 /* IPDWing */, - 181 /* ISO */, - 183 /* ISO-US */, - 645 /* ITU-T */, -@@ -8916,6 +8924,7 @@ static const uint16_t kNIDsInShortNameOrder[] = { +@@ -8916,6 +8922,7 @@ static const uint16_t kNIDsInShortNameOrder[] = { 18 /* OU */, 749 /* Oakley-EC2N-3 */, 750 /* Oakley-EC2N-4 */, @@ -4135,7 +3761,7 @@ index 654b3c08e..0d3d9f24f 100644 9 /* PBE-MD2-DES */, 168 /* PBE-MD2-RC2-64 */, 10 /* PBE-MD5-DES */, -@@ -8982,7 +8991,9 @@ static const uint16_t kNIDsInShortNameOrder[] = { +@@ -8982,7 +8989,9 @@ static const uint16_t kNIDsInShortNameOrder[] = { 458 /* UID */, 0 /* UNDEF */, 948 /* X25519 */, @@ -4145,15 +3771,7 @@ index 654b3c08e..0d3d9f24f 100644 961 /* X448 */, 11 /* X500 */, 378 /* X500algorithms */, -@@ -9787,6 +9798,7 @@ static const uint16_t kNIDsInLongNameOrder[] = { - 431 /* Hold Instruction None */, - 433 /* Hold Instruction Reject */, - 634 /* ICC or token signature */, -+ 968 /* IPDWing */, - 294 /* IPSec End System */, - 295 /* IPSec Tunnel */, - 296 /* IPSec User */, -@@ -9829,6 +9841,7 @@ static const uint16_t kNIDsInLongNameOrder[] = { +@@ -9829,6 +9838,7 @@ static const uint16_t kNIDsInLongNameOrder[] = { 366 /* OCSP Nonce */, 371 /* OCSP Service Locator */, 180 /* OCSP Signing */, @@ -4161,7 +3779,7 @@ index 654b3c08e..0d3d9f24f 100644 161 /* PBES2 */, 69 /* PBKDF2 */, 162 /* PBMAC1 */, -@@ -9853,7 +9866,9 @@ static const uint16_t kNIDsInLongNameOrder[] = { +@@ -9853,7 +9863,9 @@ static const uint16_t kNIDsInLongNameOrder[] = { 133 /* Time Stamping */, 375 /* Trust Root */, 948 /* X25519 */, @@ -4172,22 +3790,21 @@ index 654b3c08e..0d3d9f24f 100644 12 /* X509 */, 402 /* X509v3 AC Targeting */, diff --git a/src/crypto/obj/obj_mac.num b/src/crypto/obj/obj_mac.num -index a0519acee..019770f16 100644 +index a0519acee..caeb5eaed 100644 --- a/src/crypto/obj/obj_mac.num +++ b/src/crypto/obj/obj_mac.num -@@ -952,3 +952,7 @@ X448 961 +@@ -952,3 +952,6 @@ X448 961 sha512_256 962 hkdf 963 X25519Kyber768Draft00 964 +X25519Kyber512Draft00 965 +P256Kyber768Draft00 966 +X25519Kyber768Draft00Old 967 -+IPDWing 968 diff --git a/src/crypto/obj/objects.txt b/src/crypto/obj/objects.txt -index 3ad32ea3d..475d278df 100644 +index 3ad32ea3d..aa1404d83 100644 --- a/src/crypto/obj/objects.txt +++ b/src/crypto/obj/objects.txt -@@ -1332,8 +1332,12 @@ secg-scheme 14 3 : dhSinglePass-cofactorDH-sha512kdf-scheme +@@ -1332,8 +1332,11 @@ secg-scheme 14 3 : dhSinglePass-cofactorDH-sha512kdf-scheme : dh-std-kdf : dh-cofactor-kdf @@ -4197,12 +3814,11 @@ index 3ad32ea3d..475d278df 100644 : X25519Kyber768Draft00 + : P256Kyber768Draft00 + : X25519Kyber768Draft00Old -+ : IPDWing # See RFC 8410. 1 3 101 110 : X25519 diff --git a/src/include/openssl/kyber.h b/src/include/openssl/kyber.h -index cafae9d17..1c889a075 100644 +index cafae9d17..074ac5906 100644 --- a/src/include/openssl/kyber.h +++ b/src/include/openssl/kyber.h @@ -1,17 +1,3 @@ @@ -4223,23 +3839,20 @@ index cafae9d17..1c889a075 100644 #ifndef OPENSSL_HEADER_KYBER_H #define OPENSSL_HEADER_KYBER_H -@@ -21,105 +7,157 @@ +@@ -21,105 +7,100 @@ extern "C" { #endif -- --// Kyber768. +#define KYBER512_PUBLIC_KEY_BYTES 800 +#define KYBER512_CIPHERTEXT_BYTES 768 +#define KYBER512_PRIVATE_KEY_BYTES 1632 +#define KYBER768_PUBLIC_KEY_BYTES 1184 +#define KYBER768_CIPHERTEXT_BYTES 1088 +#define KYBER768_PRIVATE_KEY_BYTES 2400 -+#define IPDWING_PUBLIC_KEY_BYTES 1216 -+#define IPDWING_CIPHERTEXT_BYTES 1120 -+#define IPDWING_PRIVATE_KEY_BYTES 2464 - +-// Kyber768. +- +- -// KYBER_public_key contains a Kyber768 public key. The contents of this -// object should never leave the address space since the format is unstable. -struct KYBER_public_key { @@ -4261,10 +3874,11 @@ index cafae9d17..1c889a075 100644 +struct KYBER768_private_key { + uint8_t opaque[KYBER768_PRIVATE_KEY_BYTES]; +}; -+struct IPDWING_private_key { -+ struct KYBER768_private_key m; -+ uint8_t x[32]; -+ uint8_t xpub[32]; ++struct KYBER512_public_key { ++ uint8_t opaque[KYBER512_PUBLIC_KEY_BYTES]; ++}; ++struct KYBER768_public_key { ++ uint8_t opaque[KYBER768_PUBLIC_KEY_BYTES]; }; -// KYBER_PUBLIC_KEY_BYTES is the number of bytes in an encoded Kyber768 public @@ -4343,39 +3957,18 @@ index cafae9d17..1c889a075 100644 -// there are trailing bytes in |in|. -OPENSSL_EXPORT int KYBER_parse_private_key( - struct KYBER_private_key *out_private_key, CBS *in); -+struct KYBER512_public_key { -+ uint8_t opaque[KYBER512_PUBLIC_KEY_BYTES]; -+}; -+struct KYBER768_public_key { -+ uint8_t opaque[KYBER768_PUBLIC_KEY_BYTES]; -+}; -+struct IPDWING_public_key { -+ struct KYBER768_public_key m; -+ uint8_t x[32]; -+}; - +- +// KYBER_GENERATE_KEY_BYTES is the number of bytes of entropy needed to +// generate a keypair. +#define KYBER_GENERATE_KEY_BYTES 64 + -+// IPDWING_GENERATE_KEY_BYTES is the number of bytes of entropy needed to -+// generate a keypair. -+#define IPDWING_GENERATE_KEY_BYTES 96 -+ +// KYBER_ENCAP_BYTES is the number of bytes of entropy needed to encapsulate a +// session key. +#define KYBER_ENCAP_BYTES 32 + -+// IPDWING_ENCAP_BYTES is the number of bytes of entropy needed to encapsulate a -+// session key. -+#define IPDWING_ENCAP_BYTES 64 -+ +// KYBER_KEY_BYTES is the number of bytes in a shared key. +#define KYBER_KEY_BYTES 32 + -+// IPDWING_KEY_BYTES is the number of bytes in a shared key. -+#define IPDWING_KEY_BYTES 32 -+ +// KYBER512_generate_key is a deterministic function that outputs a public and +// private key based on the given entropy. +OPENSSL_EXPORT void KYBER512_generate_key( @@ -4388,12 +3981,6 @@ index cafae9d17..1c889a075 100644 + struct KYBER768_public_key *out_pub, struct KYBER768_private_key *out_priv, + const uint8_t input[KYBER_GENERATE_KEY_BYTES]); + -+// IPDWING_generate_key is a deterministic function that outputs a public and -+// private key based on the given entropy. -+OPENSSL_EXPORT void IPDWING_generate_key( -+ struct IPDWING_public_key *out_pub, struct IPDWING_private_key *out_priv, -+ const uint8_t input[IPDWING_GENERATE_KEY_BYTES]); -+ +// KYBER512_encap is a deterministic function the generates and encrypts a random +// session key from the given entropy, writing those values to |out_shared_key| +// and |out_ciphertext|, respectively. @@ -4410,14 +3997,6 @@ index cafae9d17..1c889a075 100644 + const struct KYBER768_public_key *in_pub, + const uint8_t in[KYBER_ENCAP_BYTES]); + -+// IPDWING_encap is a deterministic function the generates and encrypts a random -+// session key from the given entropy, writing those values to |out_shared_key| -+// and |out_ciphertext|, respectively. -+OPENSSL_EXPORT void IPDWING_encap(uint8_t out_ciphertext[IPDWING_CIPHERTEXT_BYTES], -+ uint8_t out_shared_key[IPDWING_KEY_BYTES], -+ const struct IPDWING_public_key *in_pub, -+ const uint8_t in[IPDWING_ENCAP_BYTES]); -+ +// 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 @@ -4428,7 +4007,7 @@ index cafae9d17..1c889a075 100644 + const struct KYBER512_private_key *in_priv, + const uint8_t *ciphertext, size_t ciphertext_len); + -+// KYBER768_decap decrypts a session key from |ciphertext_len| bytes of ++// KYBER_decap decrypts a session key from |ciphertext_len| bytes of +// |ciphertext|. If the ciphertext is valid, the decrypted key is written to +// |out_shared_key|. Otherwise a key dervied from |ciphertext| and a secret key (kept +// in |in_priv|) is written. If the ciphertext is the wrong length then it will @@ -4438,16 +4017,6 @@ index cafae9d17..1c889a075 100644 + const struct KYBER768_private_key *in_priv, + const uint8_t *ciphertext, size_t ciphertext_len); + -+// IPDWING_decap decrypts a session key from IPDWING_CIPHERTEXT_BYTES bytes of -+// |ciphertext|. If the ciphertext is valid, the decrypted key is written to -+// |out_shared_key|. Otherwise a key dervied from |ciphertext| and a secret key (kept -+// in |in_priv|) is written. If the ciphertext is the wrong length then it will -+// leak which was done via side-channels. Otherwise it should perform either -+// action in constant-time. -+OPENSSL_EXPORT void IPDWING_decap(uint8_t out_shared_key[KYBER_KEY_BYTES], -+ const struct IPDWING_private_key *in_priv, -+ const uint8_t *ciphertext); -+ +// 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); @@ -4456,10 +4025,6 @@ index cafae9d17..1c889a075 100644 +OPENSSL_EXPORT void KYBER768_marshal_public_key( + uint8_t out[KYBER768_PUBLIC_KEY_BYTES], const struct KYBER768_public_key *in_pub); + -+// IPDWING_marshal_public_key serialises |in_pub| to |out|. -+OPENSSL_EXPORT void IPDWING_marshal_public_key( -+ uint8_t out[IPDWING_PUBLIC_KEY_BYTES], const struct IPDWING_public_key *in_pub); -+ +// KYBER512_parse_public_key sets |*out| to the public-key encoded in |in|. +OPENSSL_EXPORT void KYBER512_parse_public_key( + struct KYBER512_public_key *out, const uint8_t in[KYBER512_PUBLIC_KEY_BYTES]); @@ -4467,18 +4032,14 @@ index cafae9d17..1c889a075 100644 +// KYBER768_parse_public_key sets |*out| to the public-key encoded in |in|. +OPENSSL_EXPORT void KYBER768_parse_public_key( + struct KYBER768_public_key *out, const uint8_t in[KYBER768_PUBLIC_KEY_BYTES]); -+ -+// IPDWING_parse_public_key sets |*out| to the public-key encoded in |in|. -+OPENSSL_EXPORT void IPDWING_parse_public_key( -+ struct IPDWING_public_key *out, const uint8_t in[IPDWING_PUBLIC_KEY_BYTES]); #if defined(__cplusplus) } // extern C diff --git a/src/include/openssl/nid.h b/src/include/openssl/nid.h -index 4dd8841b1..09912d8bb 100644 +index 4dd8841b1..8237efb74 100644 --- a/src/include/openssl/nid.h +++ b/src/include/openssl/nid.h -@@ -4255,6 +4255,18 @@ extern "C" { +@@ -4255,6 +4255,15 @@ extern "C" { #define SN_X25519Kyber768Draft00 "X25519Kyber768Draft00" #define NID_X25519Kyber768Draft00 964 @@ -4490,25 +4051,21 @@ index 4dd8841b1..09912d8bb 100644 + +#define SN_X25519Kyber768Draft00Old "X25519Kyber768Draft00Old" +#define NID_X25519Kyber768Draft00Old 967 -+ -+#define SN_IPDWing "IPDWing" -+#define NID_IPDWing 968 + #if defined(__cplusplus) } /* extern C */ diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h -index 53aa9b453..3091c6849 100644 +index 53aa9b453..8233ad210 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); +@@ -2378,6 +2378,9 @@ OPENSSL_EXPORT int SSL_set1_curves_list(SSL *ssl, const char *curves); #define SSL_CURVE_SECP521R1 25 #define SSL_CURVE_X25519 29 #define SSL_CURVE_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_IPDWING 0xfe41 // SSL_get_curve_id returns the ID of the curve used by |ssl|'s most recently // completed handshake or 0 if not applicable. @@ -4526,36 +4083,40 @@ index 5c7e881bf..3c0770cf3 100644 crypto/pkcs8/test/no_encryption.p12 crypto/pkcs8/test/nss.p12 diff --git a/src/ssl/extensions.cc b/src/ssl/extensions.cc -index 5ee280221..cf165df1f 100644 +index 5ee280221..0a706c411 100644 --- a/src/ssl/extensions.cc +++ b/src/ssl/extensions.cc -@@ -207,6 +207,10 @@ static bool tls1_check_duplicate_extensions(const CBS *cbs) { +@@ -207,6 +207,9 @@ 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_IPDWING: return true; default: return false; diff --git a/src/ssl/ssl_key_share.cc b/src/ssl/ssl_key_share.cc -index 09a9ad380..d9d3b9032 100644 +index 09a9ad380..f7d2226e3 100644 --- a/src/ssl/ssl_key_share.cc +++ b/src/ssl/ssl_key_share.cc -@@ -193,63 +193,384 @@ class X25519KeyShare : public SSLKeyShare { +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -193,63 +194,384 @@ class X25519KeyShare : public SSLKeyShare { uint8_t private_key_[32]; }; -class X25519Kyber768KeyShare : public SSLKeyShare { +class P256Kyber768Draft00KeyShare : public SSLKeyShare { - public: -- X25519Kyber768KeyShare() {} ++ public: + P256Kyber768Draft00KeyShare() {} - -- uint16_t GroupID() const override { -- return SSL_CURVE_X25519_KYBER768_DRAFT00; ++ + uint16_t GroupID() const override { return SSL_CURVE_P256_KYBER768_DRAFT00; } + + bool Generate(CBB *out) override { @@ -4605,8 +4166,8 @@ index 09a9ad380..d9d3b9032 100644 + } + + return true; - } - ++ } ++ + bool Encap(CBB *out_public_key, Array *out_secret, + uint8_t *out_alert, Span peer_key) override { + assert(!p256_private_key_); @@ -4769,7 +4330,8 @@ index 09a9ad380..d9d3b9032 100644 +}; + +class X25519Kyber768Draft00KeyShare : public SSLKeyShare { -+ public: + public: +- X25519Kyber768KeyShare() {} + 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); @@ -4777,12 +4339,10 @@ index 09a9ad380..d9d3b9032 100644 + + 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)); @@ -4790,19 +4350,16 @@ index 09a9ad380..d9d3b9032 100644 + + uint8_t kyber_public_key_bytes[KYBER768_PUBLIC_KEY_BYTES]; + KYBER768_marshal_public_key(kyber_public_key_bytes, &kyber_public_key); - - 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; @@ -4863,11 +4420,13 @@ index 09a9ad380..d9d3b9032 100644 + + KYBER768_decap(secret.data() + 32, &kyber_private_key_, + peer_key.data() + 32, peer_key.size() - 32); -+ + +- uint16_t GroupID() const override { +- return SSL_CURVE_X25519_KYBER768_DRAFT00; + *out_secret = std::move(secret); + return true; -+ } -+ + } + + private: + uint8_t x25519_private_key_[32]; + KYBER768_private_key kyber_private_key_; @@ -4880,10 +4439,12 @@ index 09a9ad380..d9d3b9032 100644 + + uint16_t GroupID() const override { return SSL_CURVE_X25519_KYBER512_DRAFT00; } + -+ bool Generate(CBB *out) override { -+ uint8_t x25519_public_key[32]; -+ X25519_keypair(x25519_public_key, x25519_private_key_); -+ + 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_); + uint8_t kyber_entropy[KYBER_GENERATE_KEY_BYTES]; + KYBER512_public_key kyber_public_key; + RAND_bytes(kyber_entropy, sizeof(kyber_entropy)); @@ -4891,16 +4452,19 @@ index 09a9ad380..d9d3b9032 100644 + + uint8_t kyber_public_key_bytes[KYBER512_PUBLIC_KEY_BYTES]; + KYBER512_marshal_public_key(kyber_public_key_bytes, &kyber_public_key); -+ -+ if (!CBB_add_bytes(out, x25519_public_key, sizeof(x25519_public_key)) || + + if (!CBB_add_bytes(out, x25519_public_key, sizeof(x25519_public_key)) || +- !CBB_add_bytes(out, kyber_public_key, sizeof(kyber_public_key))) { + !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 { + bool Encap(CBB *out_public_key, Array *out_secret, + uint8_t *out_alert, Span peer_key) override { Array secret; @@ -4957,7 +4521,7 @@ index 09a9ad380..d9d3b9032 100644 return false; } -@@ -258,30 +579,111 @@ class X25519Kyber768KeyShare : public SSLKeyShare { +@@ -258,30 +580,32 @@ class X25519Kyber768KeyShare : public SSLKeyShare { } bool Decap(Array *out_secret, uint8_t *out_alert, @@ -4994,89 +4558,10 @@ index 09a9ad380..d9d3b9032 100644 uint8_t x25519_private_key_[32]; - KYBER_private_key kyber_private_key_; + KYBER512_private_key kyber_private_key_; -+}; -+ -+class IPDWingKeyShare : public SSLKeyShare { -+ public: -+ IPDWingKeyShare() {} -+ -+ uint16_t GroupID() const override { return SSL_CURVE_IPDWING; } -+ -+ bool Generate(CBB *out) override { -+ uint8_t entropy[IPDWING_GENERATE_KEY_BYTES]; -+ IPDWING_public_key public_key; -+ RAND_bytes(entropy, sizeof(entropy)); -+ IPDWING_generate_key(&public_key, &private_key_, entropy); -+ -+ uint8_t public_key_bytes[IPDWING_PUBLIC_KEY_BYTES]; -+ IPDWING_marshal_public_key(public_key_bytes, &public_key); -+ -+ if(!CBB_add_bytes(out, public_key_bytes, sizeof(public_key_bytes))) { -+ return false; -+ } -+ -+ return true; -+ } -+ -+ bool Encap(CBB *out_public_key, Array *out_secret, -+ uint8_t *out_alert, Span peer_key) override { -+ Array secret; -+ *out_alert = SSL_AD_INTERNAL_ERROR; -+ if (!secret.Init(IPDWING_KEY_BYTES)) { -+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); -+ return false; -+ } -+ -+ IPDWING_public_key peer_public_key; -+ if (peer_key.size() != IPDWING_PUBLIC_KEY_BYTES) { -+ *out_alert = SSL_AD_DECODE_ERROR; -+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); -+ return false; -+ } -+ -+ IPDWING_parse_public_key(&peer_public_key, peer_key.data()); -+ -+ uint8_t ciphertext[IPDWING_CIPHERTEXT_BYTES]; -+ uint8_t entropy[IPDWING_ENCAP_BYTES]; -+ RAND_bytes(entropy, sizeof(entropy)); -+ -+ IPDWING_encap(ciphertext, secret.data(), &peer_public_key, entropy); -+ 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, -+ Span peer_key) override { -+ *out_alert = SSL_AD_INTERNAL_ERROR; -+ -+ Array secret; -+ if (!secret.Init(IPDWING_KEY_BYTES)) { -+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); -+ return false; -+ } -+ -+ if (peer_key.size() != IPDWING_CIPHERTEXT_BYTES) { -+ *out_alert = SSL_AD_DECODE_ERROR; -+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); -+ return false; -+ } -+ -+ IPDWING_decap(secret.data(), &private_key_, peer_key.data()); -+ -+ *out_secret = std::move(secret); -+ return true; -+ } -+ -+ private: -+ IPDWING_private_key private_key_; }; constexpr NamedGroup kNamedGroups[] = { -@@ -290,8 +692,15 @@ constexpr NamedGroup kNamedGroups[] = { +@@ -290,8 +614,14 @@ 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"}, @@ -5088,12 +4573,11 @@ index 09a9ad380..d9d3b9032 100644 + {NID_X25519Kyber768Draft00Old, SSL_CURVE_X25519_KYBER768_DRAFT00_OLD, + "X25519Kyber768Draft00Old", "Xyber768D00Old"}, + {NID_P256Kyber768Draft00, SSL_CURVE_P256_KYBER768_DRAFT00, -+ "P256Kyber768Draft00", "P256Kyber768D00"}, -+ {NID_IPDWing, SSL_CURVE_IPDWING, "IPDWing", ""} ++ "P256Kyber768Draft00", "P256Kyber768D00"} }; } // namespace -@@ -312,8 +721,18 @@ UniquePtr SSLKeyShare::Create(uint16_t group_id) { +@@ -312,8 +642,16 @@ UniquePtr SSLKeyShare::Create(uint16_t group_id) { return MakeUnique(NID_secp521r1, SSL_CURVE_SECP521R1); case SSL_CURVE_X25519: return MakeUnique(); @@ -5108,8 +4592,6 @@ index 09a9ad380..d9d3b9032 100644 + group_id)); + case SSL_CURVE_P256_KYBER768_DRAFT00: + return UniquePtr(New()); -+ case SSL_CURVE_IPDWING: -+ return UniquePtr(New()); default: return nullptr; } @@ -5127,10 +4609,10 @@ index 838761af5..9eb201d37 100644 static const uint16_t kSigAlgs[] = { SSL_SIGN_RSA_PKCS1_SHA256, diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc -index ef43a9e98..bb79509ea 100644 +index ef43a9e98..9756fd2a0 100644 --- a/src/ssl/ssl_test.cc +++ b/src/ssl/ssl_test.cc -@@ -409,7 +409,34 @@ static const CurveTest kCurveTests[] = { +@@ -409,7 +409,30 @@ static const CurveTest kCurveTests[] = { "P-256:X25519Kyber768Draft00", { SSL_CURVE_SECP256R1, SSL_CURVE_X25519_KYBER768_DRAFT00 }, }, @@ -5144,10 +4626,6 @@ index ef43a9e98..bb79509ea 100644 + { SSL_CURVE_X25519_KYBER768_DRAFT00 }, + }, + { -+ "IPDWing", -+ { SSL_CURVE_IPDWING }, -+ }, -+ { + "Xyber768D00:Xyber768D00Old", + { SSL_CURVE_X25519_KYBER768_DRAFT00, SSL_CURVE_X25519_KYBER768_DRAFT00_OLD }, + }, @@ -5354,5 +4832,5 @@ index 5b0205953..831875514 100644 !SpeedTrustToken("TrustToken-Exp1-Batch1", TRUST_TOKEN_experiment_v1(), 1, selected) || -- -2.45.2 +2.41.0 diff --git a/boring/src/lib.rs b/boring/src/lib.rs index 4d477a72..6b3ade0f 100644 --- a/boring/src/lib.rs +++ b/boring/src/lib.rs @@ -83,10 +83,6 @@ //! boxes. //! - `P256Kyber768Draft00`. Similar again to `X25519Kyber768Draft00`, but uses P256 as classical //! part. It uses a non-standard codepoint. Not recommended. -//! - `IPDWing`. A preliminary version of -//! [X-Wing](https://datatracker.ietf.org/doc/draft-connolly-cfrg-xwing-kem/02/). -//! Similar to `X25519Kyber768Draft00Old`, but uses a newer (but not yet final) version of Kyber -//! called ML-KEM-ipd. Not recommended. //! //! Presently all these key agreements are deployed by Cloudflare, but we do not guarantee continued //! support for them. diff --git a/boring/src/ssl/mod.rs b/boring/src/ssl/mod.rs index 6a9361e1..277002b7 100644 --- a/boring/src/ssl/mod.rs +++ b/boring/src/ssl/mod.rs @@ -723,9 +723,6 @@ impl SslCurve { #[cfg(feature = "pq-experimental")] pub const P256_KYBER768_DRAFT00: SslCurve = SslCurve(ffi::SSL_CURVE_P256_KYBER768_DRAFT00 as _); - #[cfg(feature = "pq-experimental")] - pub const IPD_WING: SslCurve = SslCurve(ffi::SSL_CURVE_IPDWING); - /// Returns the curve name /// /// This corresponds to [`SSL_get_curve_name`] @@ -768,8 +765,6 @@ impl SslCurve { ffi::SSL_CURVE_X25519_KYBER512_DRAFT00 => Some(ffi::NID_X25519Kyber512Draft00), #[cfg(feature = "pq-experimental")] ffi::SSL_CURVE_P256_KYBER768_DRAFT00 => Some(ffi::NID_P256Kyber768Draft00), - #[cfg(feature = "pq-experimental")] - ffi::SSL_CURVE_IPDWING => Some(ffi::NID_IPDWing), _ => None, } } From 6d3639f17384fdac1f6b8d9c2386b45e97e8a0a1 Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Tue, 17 Sep 2024 16:07:57 +0200 Subject: [PATCH 17/41] Add post-quantum key agreement X25519MLKEM768 This is the successor of X25519Kyber768Draft00. Spec: https://datatracker.ietf.org/doc/draft-kwiatkowski-tls-ecdhe-mlkem/02/ IANA has assigned the codepoint. https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8 Upstream BoringSSL support landed in. https://github.com/google/boringssl/commit/7fb4d3da5082225c7180267e9daad291887ce982 The version of BoringSSL we patch does not include it, so we add it manually. Chrome and Firefox are planning to enable in October. This PR is based on the IPD-Wing patch reviewed here: https://github.com/cloudflare/boring/pull/243 There are two changes. First we simplify the patch a bit as we do not need IPD-Wing. Secondly, we perform the encapsulation key check, which was a last minute addition of NIST. We perform this check also for Kyber. --- boring-sys/patches/boring-pq.patch | 1203 ++++++++++++++++++---------- boring/src/lib.rs | 4 +- boring/src/ssl/mod.rs | 12 +- 3 files changed, 775 insertions(+), 444 deletions(-) diff --git a/boring-sys/patches/boring-pq.patch b/boring-sys/patches/boring-pq.patch index 2ffeee6c..e6601d91 100644 --- a/boring-sys/patches/boring-pq.patch +++ b/boring-sys/patches/boring-pq.patch @@ -1,59 +1,71 @@ -From 4cba2164726c8d2647e38548a266a70c4942d567 Mon Sep 17 00:00:00 2001 +From b98d803dbecc9d6848d8cbffa62b5c943fb75f70 Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Fri, 22 Jul 2022 16:43:48 +0200 -Subject: [PATCH] Add temporary post-quantum key agreements +Subject: [PATCH] Add additional post-quantum key agreements -BoringSSL upstream support X25519Kyber768Draft00 already under -codepoint 0x6399, which is the recommended post-quantum key -agreement to use +BoringSSL upstream has supported the temporary post-quantum +key agreement X25519Kyber768Draft00 (0x6399) for a while. +At the time of writing X25519Kyber768Draft00 is widely deployed by browsers. + +Recent BoringSSL adds support for X25519MLKEM768 (0x11ec), +which will be the long term post-quantum key agreement of choice, +and many browsers are expected to switch to it before the end of 2024. This patch adds: -1. Supports for P256Kyber768Draft00 under 0xfe32, which we temporarily +1. Support for MLKEM768X25519 under the codepoint 0x11ec. The version + of BoringSSL we patch against did not support it yet. + +2. Supports for P256Kyber768Draft00 under 0xfe32, which we temporarily need for compliance reasons. (Note that this is not the codepoint allocated for that exchange in the IANA table.) It also enables it in FIPS mode. -2. Support for X25519Kyber768Draft00 under the old codepoint 0xfe31. +3. Support for X25519Kyber768Draft00 under the old codepoint 0xfe31. -3. Support for X25519Kyber512Draft00 under the codepoint 0xfe30. This +4. Support for X25519Kyber512Draft00 under the codepoint 0xfe30. This key agreement should only be used for testing: to see if the smaller keyshare makes a difference. The patch also replaces Google's implementation of Kyber, by the portable reference implementation, so as to support Kyber512. -Cf RTG-2076 RTG-2051 RTG-2508 RTG-2707 RTG-2607 +Cf RTG-2076 RTG-2051 RTG-2508 RTG-2707 RTG-2607 RTG-3239 --- BUILD.generated.bzl | 5 +- BUILD.generated_tests.bzl | 4 - CMakeLists.txt | 4 +- + crypto_test_data.cc | 4 - sources.json | 9 +- src/crypto/CMakeLists.txt | 5 +- src/crypto/kyber/internal.h | 91 - src/crypto/kyber/keccak.c | 204 -- - src/crypto/kyber/kyber.c | 2865 ++++++++++++++++++++------- + src/crypto/kyber/keccak_tests.txt | 3071 ----------------------------- + src/crypto/kyber/kyber.c | 3011 +++++++++++++++++++++------- src/crypto/kyber/kyber512.c | 5 + src/crypto/kyber/kyber768.c | 4 + src/crypto/kyber/kyber_test.cc | 229 --- - src/crypto/obj/obj_dat.h | 14 +- - src/crypto/obj/obj_mac.num | 3 + - src/crypto/obj/objects.txt | 5 +- - src/include/openssl/kyber.h | 199 +- - src/include/openssl/nid.h | 9 + - src/include/openssl/ssl.h | 3 + + src/crypto/kyber/kyber_tests.txt | 905 --------- + src/crypto/obj/obj_dat.h | 17 +- + src/crypto/obj/obj_mac.num | 4 + + src/crypto/obj/objects.txt | 6 +- + src/include/openssl/kyber.h | 203 +- + src/include/openssl/nid.h | 12 + + src/include/openssl/ssl.h | 4 + src/sources.cmake | 2 - - src/ssl/extensions.cc | 3 + - src/ssl/ssl_key_share.cc | 412 +++- + src/ssl/extensions.cc | 4 + + src/ssl/ssl_key_share.cc | 525 ++++- src/ssl/ssl_lib.cc | 2 +- - src/ssl/ssl_test.cc | 25 +- + src/ssl/ssl_test.cc | 29 +- src/tool/speed.cc | 162 +- - 26 files changed, 2797 insertions(+), 5447 deletions(-) + 26 files changed, 3088 insertions(+), 5433 deletions(-) delete mode 100644 src/crypto/kyber/internal.h delete mode 100644 src/crypto/kyber/keccak.c + delete mode 100644 src/crypto/kyber/keccak_tests.txt create mode 100644 src/crypto/kyber/kyber512.c create mode 100644 src/crypto/kyber/kyber768.c delete mode 100644 src/crypto/kyber/kyber_test.cc + delete mode 100644 src/crypto/kyber/kyber_tests.txt diff --git a/BUILD.generated.bzl b/BUILD.generated.bzl index 738e1055f..9466757a2 100644 @@ -122,6 +134,28 @@ index faed2befa..931c0e3a8 100644 src/crypto/lhash/lhash.c src/crypto/mem.c src/crypto/obj/obj.c +diff --git a/crypto_test_data.cc b/crypto_test_data.cc +index 2268533f8..19b344af1 100644 +--- a/crypto_test_data.cc ++++ b/crypto_test_data.cc +@@ -74,7 +74,6 @@ + * crypto/fipsmodule/rand/ctrdrbg_vectors.txt \ + * crypto/hmac_extra/hmac_tests.txt \ + * crypto/hpke/hpke_test_vectors.txt \ +- * crypto/kyber/keccak_tests.txt \ + * crypto/kyber/kyber_tests.txt \ + * crypto/pkcs8/test/empty_password.p12 \ + * crypto/pkcs8/test/no_encryption.p12 \ +@@ -5269,9 +5268,6 @@ std::string GetTestData(const char *path) { + if (strcmp(path, "crypto/hpke/hpke_test_vectors.txt") == 0) { + return AssembleString(kData59, kLen59); + } +- if (strcmp(path, "crypto/kyber/keccak_tests.txt") == 0) { +- return AssembleString(kData60, kLen60); +- } + if (strcmp(path, "crypto/kyber/kyber_tests.txt") == 0) { + return AssembleString(kData61, kLen61); + } diff --git a/sources.json b/sources.json index 4c0048e1d..f6ea5c40f 100644 --- a/sources.json @@ -492,10 +526,10 @@ index f1c012d11..000000000 - } -} diff --git a/src/crypto/kyber/kyber.c b/src/crypto/kyber/kyber.c -index 776c085f9..346d4daec 100644 +index 776c085f9..ccb5b3d9b 100644 --- a/src/crypto/kyber/kyber.c +++ b/src/crypto/kyber/kyber.c -@@ -1,833 +1,2252 @@ +@@ -1,833 +1,2426 @@ -/* Copyright (c) 2023, Google Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any @@ -520,6 +554,8 @@ index 776c085f9..346d4daec 100644 +// - Removed 90s version. +// - Seeds are passed as paramters. +// - Changed the API to be more BoringSSL-like ++// - Mitigated timing sidechannels (Kyberslash 1 and 2). ++// (Note that these do not affect ephemeral usage as in TLS.) +// +// TODO +// @@ -534,21 +570,24 @@ index 776c085f9..346d4daec 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 "../internal.h" ++#include ++#include ++#include + + #include "../internal.h" -#include "./internal.h" - - @@ -612,9 +651,6 @@ index 776c085f9..346d4daec 100644 - 2099, 561, 2466, 2594, 2804, 1092, 403, 1026, 1143, 2150, 2775, 886, - 1722, 1212, 1874, 1029, 2110, 2935, 885, 2154, -}; -+#include -+#include -+#include -// kInverseNTTRoots = [pow(17, -bitreverse(i), p) for i in range(128)] -static const uint16_t kInverseNTTRoots[128] = { @@ -844,7 +880,7 @@ index 776c085f9..346d4daec 100644 + uint8_t sk[KYBER_INDCPA_SECRETKEYBYTES], + const uint8_t seed[KYBER_SYMBYTES]); + -+static void indcpa_enc(uint8_t c[KYBER_INDCPA_BYTES], ++static int indcpa_enc(uint8_t c[KYBER_INDCPA_BYTES], + const uint8_t m[KYBER_INDCPA_MSGBYTES], + const uint8_t pk[KYBER_INDCPA_PUBLICKEYBYTES], + const uint8_t coins[KYBER_SYMBYTES]); @@ -873,6 +909,9 @@ index 776c085f9..346d4daec 100644 +static void shake256_squeeze(uint8_t *out, size_t outlen, keccak_state *state); +static void shake256_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen); +static void shake256_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state); ++static void shake256_absorb(keccak_state *state, const uint8_t *in, size_t inlen); ++static void shake256_finalize(keccak_state *state); ++static void shake256_init(keccak_state *state); + +static void shake256(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen); +static void sha3_256(uint8_t h[32], const uint8_t *in, size_t inlen); @@ -1064,13 +1103,10 @@ index 776c085f9..346d4daec 100644 + a = (d >> (4*j+0)) & 0x3; + b = (d >> (4*j+2)) & 0x3; + r->coeffs[8*i+j] = a - b; - } - } - } - --static void vector_ntt(vector *a) { -- for (int i = 0; i < RANK; i++) { -- scalar_ntt(&a->v[i]); ++ } ++ } ++} ++ +/************************************************* +* Name: cbd3 +* @@ -1099,7 +1135,7 @@ index 776c085f9..346d4daec 100644 + a = (d >> (6*j+0)) & 0x7; + b = (d >> (6*j+3)) & 0x7; + r->coeffs[4*i+j] = a - b; -+ } + } } } +#endif @@ -1115,25 +1151,9 @@ index 776c085f9..346d4daec 100644 +#endif +} --// In place inverse number theoretic transform of a given scalar, with pairs of --// entries of s->v being interpreted as elements of GF(3329^2). Just as with the --// number theoretic transform, this leaves off the first step of the normal iFFT --// to account for the fact that 3329 does not have a 512th root of unity, using --// the precomputed 128 roots of unity stored in |kInverseNTTRoots|. --static void scalar_inverse_ntt(scalar *s) { -- int step = DEGREE / 2; -- // `int` is used here because using `size_t` throughout caused a ~5% slowdown -- // with Clang 14 on Aarch64. -- for (int offset = 2; offset < DEGREE; offset <<= 1) { -- step >>= 1; -- int k = 0; -- for (int i = 0; i < step; i++) { -- uint32_t step_root = kInverseNTTRoots[i + step]; -- for (int j = k; j < k + offset; j++) { -- uint16_t odd = s->c[j + offset]; -- uint16_t even = s->c[j]; -- s->c[j] = reduce_once(odd + even); -- s->c[j + offset] = reduce(step_root * (even - odd + kPrime)); +-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 @@ -1176,8 +1196,8 @@ index 776c085f9..346d4daec 100644 + zetas[i] -= KYBER_Q; + if(zetas[i] < -KYBER_Q/2) + zetas[i] += KYBER_Q; -+ } -+} + } + } +*/ + +static const int16_t zetas[128] = { @@ -1212,7 +1232,26 @@ index 776c085f9..346d4daec 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 +* @@ -1316,6 +1355,7 @@ index 776c085f9..346d4daec 100644 +{ + unsigned int i,j; + int16_t u; ++ uint32_t d0; + uint8_t t[8]; + +#if (KYBER_POLYCOMPRESSEDBYTES == 128) @@ -1324,7 +1364,11 @@ index 776c085f9..346d4daec 100644 + // map to positive standard representatives + u = a->coeffs[8*i+j]; + u += (u >> 15) & KYBER_Q; -+ t[j] = ((((uint16_t)u << 4) + KYBER_Q/2)/KYBER_Q) & 15; ++ d0 = u << 4; ++ d0 += 1665; ++ d0 *= 80635; ++ d0 >>= 28; ++ t[j] = d0 & 0xf; + } + + r[0] = t[0] | (t[1] << 4); @@ -1339,7 +1383,11 @@ index 776c085f9..346d4daec 100644 + // map to positive standard representatives + u = a->coeffs[8*i+j]; + u += (u >> 15) & KYBER_Q; -+ t[j] = ((((uint32_t)u << 5) + KYBER_Q/2)/KYBER_Q) & 31; ++ d0 = u << 5; ++ d0 += 1664; ++ d0 *= 40318; ++ d0 >>= 27; ++ t[j] = d0 & 0x1f; + } + + r[0] = (t[0] >> 0) | (t[1] << 5); @@ -1490,7 +1538,7 @@ index 776c085f9..346d4daec 100644 + + for(i=0;i> j)&1); ++ mask = -(int16_t)value_barrier_u32((msg[i] >> j)&1); + r->coeffs[8*i+j] = mask & ((KYBER_Q+1)/2); } } @@ -1515,14 +1563,17 @@ index 776c085f9..346d4daec 100644 +static void poly_tomsg(uint8_t msg[KYBER_INDCPA_MSGBYTES], const poly *a) +{ + unsigned int i,j; -+ uint16_t t; ++ uint32_t t; + + for(i=0;icoeffs[8*i+j]; -+ t += ((int16_t)t >> 15) & KYBER_Q; -+ t = (((t << 1) + KYBER_Q/2)/KYBER_Q) & 1; ++ t <<= 1; ++ t += 1665; ++ t *= 80635; ++ t >>= 28; ++ t &= 1; + msg[i] |= t << j; } } @@ -1801,6 +1852,7 @@ index 776c085f9..346d4daec 100644 +static void polyvec_compress(uint8_t r[KYBER_POLYVECCOMPRESSEDBYTES], const polyvec *a) +{ + unsigned int i,j,k; ++ uint64_t d0; + +#if (KYBER_POLYVECCOMPRESSEDBYTES == (KYBER_K * 352)) + uint16_t t[8]; @@ -1809,7 +1861,12 @@ index 776c085f9..346d4daec 100644 + for(k=0;k<8;k++) { + t[k] = a->vec[i].coeffs[8*j+k]; + t[k] += ((int16_t)t[k] >> 15) & KYBER_Q; -+ t[k] = ((((uint32_t)t[k] << 11) + KYBER_Q/2)/KYBER_Q) & 0x7ff; ++ d0 = t[k]; ++ d0 <<= 11; ++ d0 += 1664; ++ d0 *= 645084; ++ d0 >>= 31; ++ t[k] = d0 & 0x7ff; } - element_bits_done += chunk_bits; @@ -1835,7 +1892,12 @@ index 776c085f9..346d4daec 100644 + for(k=0;k<4;k++) { + t[k] = a->vec[i].coeffs[4*j+k]; + t[k] += ((int16_t)t[k] >> 15) & KYBER_Q; -+ t[k] = ((((uint32_t)t[k] << 10) + KYBER_Q/2)/ KYBER_Q) & 0x3ff; ++ d0 = t[k]; ++ d0 <<= 10; ++ d0 += 1665; ++ d0 *= 1290167; ++ d0 >>= 32; ++ t[k] = d0 & 0x3ff; + } - if (out_byte_bits > 0) { @@ -1910,8 +1972,15 @@ index 776c085f9..346d4daec 100644 +#else +#error "KYBER_POLYVECCOMPRESSEDBYTES needs to be in {320*KYBER_K, 352*KYBER_K}" +#endif -+} -+ + } + +-// Encodes an entire vector into 32*|RANK|*|bits| bytes. Note that since 256 +-// (DEGREE) is divisible by 8, the individual vector entries will always fill a +-// whole number of bytes, so we do not need to worry about bit packing here. +-static void vector_encode(uint8_t *out, const vector *a, int bits) { +- for (int i = 0; i < RANK; i++) { +- scalar_encode(out + i * bits * DEGREE / 8, &a->v[i], bits); +- } +/************************************************* +* Name: polyvec_tobytes +* @@ -1926,8 +1995,13 @@ index 776c085f9..346d4daec 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 +* @@ -1943,14 +2017,10 @@ index 776c085f9..346d4daec 100644 + unsigned int i; + for(i=0;ivec[i], a+i*KYBER_POLYBYTES); - } ++} --// 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); +- uint8_t in_byte = 0; +- int in_byte_bits_left = 0; +/************************************************* +* Name: polyvec_ntt +* @@ -1964,7 +2034,10 @@ index 776c085f9..346d4daec 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 +* @@ -1979,7 +2052,13 @@ index 776c085f9..346d4daec 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 +* @@ -1999,16 +2078,18 @@ index 776c085f9..346d4daec 100644 + for(i=1;ivec[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); - } ++} --// 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); +- element |= (in_byte & kMasks[chunk_bits - 1]) << element_bits_done; +- in_byte_bits_left -= chunk_bits; +- in_byte >>= chunk_bits; +/************************************************* +* Name: polyvec_reduce +* @@ -2025,8 +2106,8 @@ index 776c085f9..346d4daec 100644 + poly_reduce(&r->vec[i]); +} -- uint8_t in_byte = 0; -- int in_byte_bits_left = 0; +- element_bits_done += chunk_bits; +- } +/************************************************* +* Name: polyvec_add +* @@ -2043,19 +2124,15 @@ index 776c085f9..346d4daec 100644 + poly_add(&r->vec[i], &a->vec[i], &b->vec[i]); +} -- for (int i = 0; i < DEGREE; i++) { -- uint16_t element = 0; -- int element_bits_done = 0; +- if (element >= kPrime) { +- return 0; +- } +- out->c[i] = element; +- } +// +// indcpa.c +// - -- while (element_bits_done < bits) { -- if (in_byte_bits_left == 0) { -- in_byte = *in; -- in++; -- in_byte_bits_left = 8; -- } ++ +/************************************************* +* Name: pack_pk +* @@ -2076,11 +2153,7 @@ index 776c085f9..346d4daec 100644 + for(i=0;i in_byte_bits_left) { -- chunk_bits = in_byte_bits_left; -- } ++ +/************************************************* +* Name: unpack_pk +* @@ -2091,19 +2164,34 @@ index 776c085f9..346d4daec 100644 +* - uint8_t *seed: pointer to output seed to generate matrix A +* - const uint8_t *packedpk: pointer to input serialized public key +**************************************************/ -+static void unpack_pk(polyvec *pk, ++static int unpack_pk(polyvec *pk, + uint8_t seed[KYBER_SYMBYTES], + const uint8_t packedpk[KYBER_INDCPA_PUBLICKEYBYTES]) +{ + size_t i; + polyvec_frombytes(pk, packedpk); ++ ++ // FIPS 203 encapsulation key check. We'll perform it even for Kyber. ++ uint8_t repacked[KYBER_POLYVECBYTES]; ++ polyvec_tobytes(repacked, pk); ++ ++ if(verify(repacked, packedpk, KYBER_POLYVECBYTES) != 0) ++ return 0; + + for(i=0;i>= chunk_bits; +-// scalar_decode_1 is |scalar_decode| specialised for |bits| == 1. +-static void scalar_decode_1(scalar *out, const uint8_t in[32]) { +- for (int i = 0; i < DEGREE; i += 8) { +- uint8_t in_byte = *in; +- in++; +- for (int j = 0; j < 8; j++) { +- out->c[i + j] = in_byte & 1; +- in_byte >>= 1; +- } +/************************************************* +* Name: pack_sk +* @@ -2116,9 +2204,7 @@ index 776c085f9..346d4daec 100644 +{ + polyvec_tobytes(r, sk); +} - -- element_bits_done += chunk_bits; -- } ++ +/************************************************* +* Name: unpack_sk +* @@ -2131,12 +2217,7 @@ index 776c085f9..346d4daec 100644 +{ + polyvec_frombytes(sk, packedsk); +} - -- if (element >= kPrime) { -- return 0; -- } -- out->c[i] = element; -- } ++ +/************************************************* +* Name: pack_ciphertext +* @@ -2153,8 +2234,7 @@ index 776c085f9..346d4daec 100644 + polyvec_compress(r, b); + poly_compress(r+KYBER_POLYVECCOMPRESSEDBYTES, v); +} - -- return 1; ++ +/************************************************* +* Name: unpack_ciphertext +* @@ -2169,17 +2249,8 @@ index 776c085f9..346d4daec 100644 +{ + polyvec_decompress(b, c); + poly_decompress(v, c+KYBER_POLYVECCOMPRESSEDBYTES); - } - --// scalar_decode_1 is |scalar_decode| specialised for |bits| == 1. --static void scalar_decode_1(scalar *out, const uint8_t in[32]) { -- for (int i = 0; i < DEGREE; i += 8) { -- uint8_t in_byte = *in; -- in++; -- for (int j = 0; j < 8; j++) { -- out->c[i + j] = in_byte & 1; -- in_byte >>= 1; -- } ++} ++ +/************************************************* +* Name: rej_uniform +* @@ -2268,8 +2339,8 @@ index 776c085f9..346d4daec 100644 } } - return 1; --} -- + } + -// Compresses (lossily) an input |x| mod 3329 into |bits| many bits by grouping -// numbers close to each other together. The formula used is -// round(2^|bits|/kPrime*x) mod 2^|bits|. @@ -2311,12 +2382,6 @@ index 776c085f9..346d4daec 100644 -static void scalar_compress(scalar *s, int bits) { - for (int i = 0; i < DEGREE; i++) { - s->c[i] = compress(s->c[i], bits); -- } - } - --static void scalar_decompress(scalar *s, int bits) { -- for (int i = 0; i < DEGREE; i++) { -- s->c[i] = decompress(s->c[i], bits); +/************************************************* +* Name: indcpa_keypair +* @@ -2365,9 +2430,9 @@ index 776c085f9..346d4daec 100644 + pack_pk(pk, &pkpv, publicseed); } --static void vector_compress(vector *a, int bits) { -- for (int i = 0; i < RANK; i++) { -- scalar_compress(&a->v[i], bits); +-static void scalar_decompress(scalar *s, int bits) { +- for (int i = 0; i < DEGREE; i++) { +- s->c[i] = decompress(s->c[i], bits); - } +/************************************************* +* Name: indcpa_enc @@ -2385,7 +2450,7 @@ index 776c085f9..346d4daec 100644 +* (of length KYBER_SYMBYTES) to deterministically +* generate all randomness +**************************************************/ -+static void indcpa_enc(uint8_t c[KYBER_INDCPA_BYTES], ++static int indcpa_enc(uint8_t c[KYBER_INDCPA_BYTES], + const uint8_t m[KYBER_INDCPA_MSGBYTES], + const uint8_t pk[KYBER_INDCPA_PUBLICKEYBYTES], + const uint8_t coins[KYBER_SYMBYTES]) @@ -2396,7 +2461,9 @@ index 776c085f9..346d4daec 100644 + polyvec sp, pkpv, ep, at[KYBER_K], b; + poly v, k, epp; + -+ unpack_pk(&pkpv, seed, pk); ++ if (!unpack_pk(&pkpv, seed, pk)) ++ return 0; ++ + poly_frommsg(&k, m); + gen_at(at, seed); + @@ -2424,11 +2491,12 @@ index 776c085f9..346d4daec 100644 + poly_reduce(&v); + + pack_ciphertext(c, &b, &v); ++ return 1; } --static void vector_decompress(vector *a, int bits) { +-static void vector_compress(vector *a, int bits) { - for (int i = 0; i < RANK; i++) { -- scalar_decompress(&a->v[i], bits); +- scalar_compress(&a->v[i], bits); - } +/************************************************* +* Name: indcpa_dec @@ -2463,12 +2531,10 @@ index 776c085f9..346d4daec 100644 + poly_tomsg(m, &mp); } --struct public_key { -- vector t; -- uint8_t rho[32]; -- uint8_t public_key_hash[32]; -- matrix m; --}; +-static void vector_decompress(vector *a, int bits) { +- for (int i = 0; i < RANK; i++) { +- scalar_decompress(&a->v[i], bits); +- } +// +// fips202.c +// @@ -2498,16 +2564,14 @@ index 776c085f9..346d4daec 100644 + r |= (uint64_t)x[i] << 8*i; + + return r; -+} + } --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 public_key { +- vector t; +- uint8_t rho[32]; +- uint8_t public_key_hash[32]; +- matrix m; ++/************************************************* +* Name: store64 +* +* Description: Store a 64-bit integer to array of 8 bytes in little-endian order @@ -2520,12 +2584,8 @@ index 776c085f9..346d4daec 100644 + + for(i=0;i<8;i++) + x[i] = u >> 8*i; - } - --struct private_key { -- struct public_key pub; -- vector s; -- uint8_t fo_failure_secret[32]; ++} ++ +/* Keccak round constants */ +static const uint64_t KeccakF_RoundConstants[NROUNDS] = { + (uint64_t)0x0000000000000001ULL, @@ -2554,34 +2614,13 @@ index 776c085f9..346d4daec 100644 + (uint64_t)0x8000000080008008ULL }; --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; +-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; +/************************************************* +* Name: KeccakF1600_StatePermute +* @@ -2851,9 +2890,38 @@ index 776c085f9..346d4daec 100644 + state[22] = Asi; + state[23] = Aso; + state[24] = Asu; -+} -+ -+ + } + +-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; +/************************************************* +* Name: keccak_squeeze +* @@ -2887,9 +2955,65 @@ index 776c085f9..346d4daec 100644 + outlen -= i-pos; + pos = i; } +- vector_encode(vector_output, &pub->t, kLog2Prime); +- if (!CBB_add_bytes(out, pub->rho, sizeof(pub->rho))) { +- return 0; ++ ++ return pos; ++} ++ ++/************************************************* ++* Name: keccak_absorb ++* ++* Description: Absorb step of Keccak; incremental. ++* ++* Arguments: - uint64_t *s: pointer to Keccak state ++* - unsigned int pos: position in current block to be absorbed ++* - unsigned int r: rate in bytes (e.g., 168 for SHAKE128) ++* - const uint8_t *in: pointer to input to be absorbed into s ++* - size_t inlen: length of input in bytes ++* ++* Returns new position pos in current block ++**************************************************/ ++static unsigned int keccak_absorb(uint64_t s[25], ++ unsigned int pos, ++ unsigned int r, ++ const uint8_t *in, ++ size_t inlen) ++{ ++ unsigned int i; ++ ++ while(pos+inlen >= r) { ++ for(i=pos;ipub)) { - abort(); + -+ return pos; -+} -+ -+ +/************************************************* +* Name: keccak_absorb_once +* @@ -3168,18 +3288,8 @@ index 776c085f9..346d4daec 100644 +static void shake128_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state) +{ + keccak_squeezeblocks(out, nblocks, state->s, SHAKE128_RATE); - } - --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) { -- 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; ++} ++ +/************************************************* +* Name: shake256_squeeze +* @@ -3193,40 +3303,8 @@ index 776c085f9..346d4daec 100644 +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); - } - --int KYBER_marshal_private_key(CBB *out, -- const struct KYBER_private_key *private_key) { -- const struct private_key *const priv = private_key_from_external(private_key); -- uint8_t *s_output; -- if (!CBB_add_space(out, &s_output, kEncodedVectorSize)) { -- return 0; -- } -- vector_encode(s_output, &priv->s, kLog2Prime); -- if (!kyber_marshal_public_key(out, &priv->pub) || -- !CBB_add_bytes(out, priv->pub.public_key_hash, -- sizeof(priv->pub.public_key_hash)) || -- !CBB_add_bytes(out, priv->fo_failure_secret, -- sizeof(priv->fo_failure_secret))) { -- return 0; -- } -- return 1; --} -- --int KYBER_parse_private_key(struct KYBER_private_key *out_private_key, -- CBS *in) { -- struct private_key *const priv = private_key_from_external(out_private_key); -- -- CBS s_bytes; -- if (!CBS_get_bytes(in, &s_bytes, kEncodedVectorSize) || -- !vector_decode(&priv->s, CBS_data(&s_bytes), kLog2Prime) || -- !kyber_parse_public_key_no_hash(&priv->pub, in) || -- !CBS_copy_bytes(in, priv->pub.public_key_hash, -- sizeof(priv->pub.public_key_hash)) || -- !CBS_copy_bytes(in, priv->fo_failure_secret, -- sizeof(priv->fo_failure_secret)) || -- CBS_len(in) != 0) { -- return 0; ++} ++ +/************************************************* +* Name: shake256_absorb_once +* @@ -3260,6 +3338,61 @@ index 776c085f9..346d4daec 100644 +} + +/************************************************* ++* 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 @@ -3348,8 +3481,13 @@ index 776c085f9..346d4daec 100644 + 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 +* @@ -3392,10 +3530,10 @@ index 776c085f9..346d4daec 100644 +} + +// Modified crypto_kem_enc to BoringSSL style API -+void encap(uint8_t out_ciphertext[KYBER_CIPHERTEXTBYTES], ++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]) ++ const uint8_t seed[KYBER_ENCAP_BYTES], int mlkem) +{ + const uint8_t *pk = &in_pub->opaque[0]; + uint8_t *ct = out_ciphertext; @@ -3405,6 +3543,7 @@ index 776c085f9..346d4daec 100644 + uint8_t kr[2*KYBER_SYMBYTES]; + + memcpy(buf, seed, KYBER_SYMBYTES); ++ + /* Don't release system RNG output */ + hash_h(buf, buf, KYBER_SYMBYTES); + @@ -3413,18 +3552,32 @@ index 776c085f9..346d4daec 100644 + hash_g(kr, buf, 2*KYBER_SYMBYTES); + + /* coins are in kr+KYBER_SYMBYTES */ -+ indcpa_enc(ct, buf, pk, kr+KYBER_SYMBYTES); -+ -+ /* overwrite coins in kr with H(c) */ -+ hash_h(kr+KYBER_SYMBYTES, ct, KYBER_CIPHERTEXTBYTES); -+ /* hash concatenation of pre-k and H(c) to k */ -+ kdf(ss, kr, 2*KYBER_SYMBYTES); -+} -+ ++ if(!indcpa_enc(ct, buf, pk, kr+KYBER_SYMBYTES)) + return 0; ++ ++ if (mlkem == 1) { ++ memcpy(ss, kr, KYBER_SYMBYTES); ++ } else { ++ /* overwrite coins in kr with H(c) */ ++ hash_h(kr+KYBER_SYMBYTES, ct, KYBER_CIPHERTEXTBYTES); ++ /* hash concatenation of pre-k and H(c) to k */ ++ kdf(ss, kr, 2*KYBER_SYMBYTES); + } +- BORINGSSL_keccak(pub->public_key_hash, sizeof(pub->public_key_hash), +- CBS_data(&orig_in), CBS_len(&orig_in), boringssl_sha3_256); + return 1; + } + +-int KYBER_marshal_private_key(CBB *out, +- const struct KYBER_private_key *private_key) { +- const struct private_key *const priv = private_key_from_external(private_key); +- uint8_t *s_output; +- if (!CBB_add_space(out, &s_output, kEncodedVectorSize)) { +- return 0; +// Modified crypto_kem_decap to BoringSSL style API +void decap(uint8_t out_shared_key[KYBER_SSBYTES], + const struct private_key *in_priv, -+ const uint8_t *ct, size_t ciphertext_len) ++ const uint8_t *ct, size_t ciphertext_len, int mlkem) +{ + uint8_t *ss = out_shared_key; + const uint8_t *sk = &in_priv->opaque[0]; @@ -3450,27 +3603,68 @@ index 776c085f9..346d4daec 100644 + + fail = verify(ct, cmp, KYBER_CIPHERTEXTBYTES); } -- return 1; -+ -+ /* 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); -+} +- vector_encode(s_output, &priv->s, kLog2Prime); +- if (!kyber_marshal_public_key(out, &priv->pub) || +- !CBB_add_bytes(out, priv->pub.public_key_hash, +- sizeof(priv->pub.public_key_hash)) || +- !CBB_add_bytes(out, priv->fo_failure_secret, +- sizeof(priv->fo_failure_secret))) { +- return 0; + ++ if (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); +} -+ -+void parse_public_key(struct public_key *out, -+ const uint8_t in[KYBER_PUBLICKEYBYTES]) { -+ memcpy(&out->opaque, in, KYBER_PUBLICKEYBYTES); - } + +- CBS s_bytes; +- if (!CBS_get_bytes(in, &s_bytes, kEncodedVectorSize) || +- !vector_decode(&priv->s, CBS_data(&s_bytes), kLog2Prime) || +- !kyber_parse_public_key_no_hash(&priv->pub, in) || +- !CBS_copy_bytes(in, priv->pub.public_key_hash, +- sizeof(priv->pub.public_key_hash)) || +- !CBS_copy_bytes(in, priv->fo_failure_secret, +- sizeof(priv->fo_failure_secret)) || +- CBS_len(in) != 0) { +- return 0; +- } +- return 1; ++void parse_public_key(struct public_key *out, ++ const uint8_t in[KYBER_PUBLICKEYBYTES]) { ++ memcpy(&out->opaque, in, KYBER_PUBLICKEYBYTES); + } diff --git a/src/crypto/kyber/kyber512.c b/src/crypto/kyber/kyber512.c new file mode 100644 index 000000000..21eed11a2 @@ -3728,7 +3922,7 @@ index eb76b5bd7..000000000 - FileTestGTest("crypto/kyber/kyber_tests.txt", KyberFileTest); -} diff --git a/src/crypto/obj/obj_dat.h b/src/crypto/obj/obj_dat.h -index 654b3c08e..06f80f971 100644 +index 654b3c08e..6cef2c079 100644 --- a/src/crypto/obj/obj_dat.h +++ b/src/crypto/obj/obj_dat.h @@ -57,7 +57,7 @@ @@ -3736,11 +3930,11 @@ index 654b3c08e..06f80f971 100644 -#define NUM_NID 965 -+#define NUM_NID 968 ++#define NUM_NID 969 static const uint8_t kObjectData[] = { /* NID_rsadsi */ -@@ -8784,6 +8784,12 @@ static const ASN1_OBJECT kObjects[NUM_NID] = { +@@ -8784,6 +8784,13 @@ static const ASN1_OBJECT kObjects[NUM_NID] = { {"HKDF", "hkdf", NID_hkdf, 0, NULL, 0}, {"X25519Kyber768Draft00", "X25519Kyber768Draft00", NID_X25519Kyber768Draft00, 0, NULL, 0}, @@ -3750,10 +3944,11 @@ index 654b3c08e..06f80f971 100644 + NULL, 0}, + {"X25519Kyber768Draft00Old", "X25519Kyber768Draft00Old", + NID_X25519Kyber768Draft00Old, 0, NULL, 0}, ++ {"X25519MLKEM768", "X25519MLKEM768", NID_X25519MLKEM768, 0, NULL, 0}, }; static const uint16_t kNIDsInShortNameOrder[] = { -@@ -8916,6 +8922,7 @@ static const uint16_t kNIDsInShortNameOrder[] = { +@@ -8916,6 +8923,7 @@ static const uint16_t kNIDsInShortNameOrder[] = { 18 /* OU */, 749 /* Oakley-EC2N-3 */, 750 /* Oakley-EC2N-4 */, @@ -3761,17 +3956,18 @@ index 654b3c08e..06f80f971 100644 9 /* PBE-MD2-DES */, 168 /* PBE-MD2-RC2-64 */, 10 /* PBE-MD5-DES */, -@@ -8982,7 +8989,9 @@ static const uint16_t kNIDsInShortNameOrder[] = { +@@ -8982,7 +8990,10 @@ static const uint16_t kNIDsInShortNameOrder[] = { 458 /* UID */, 0 /* UNDEF */, 948 /* X25519 */, + 965 /* X25519Kyber512Draft00 */, 964 /* X25519Kyber768Draft00 */, + 967 /* X25519Kyber768Draft00Old */, ++ 968 /* X25519MLKEM768 */, 961 /* X448 */, 11 /* X500 */, 378 /* X500algorithms */, -@@ -9829,6 +9838,7 @@ static const uint16_t kNIDsInLongNameOrder[] = { +@@ -9829,6 +9840,7 @@ static const uint16_t kNIDsInLongNameOrder[] = { 366 /* OCSP Nonce */, 371 /* OCSP Service Locator */, 180 /* OCSP Signing */, @@ -3779,32 +3975,34 @@ index 654b3c08e..06f80f971 100644 161 /* PBES2 */, 69 /* PBKDF2 */, 162 /* PBMAC1 */, -@@ -9853,7 +9863,9 @@ static const uint16_t kNIDsInLongNameOrder[] = { +@@ -9853,7 +9865,10 @@ static const uint16_t kNIDsInLongNameOrder[] = { 133 /* Time Stamping */, 375 /* Trust Root */, 948 /* X25519 */, + 965 /* X25519Kyber512Draft00 */, 964 /* X25519Kyber768Draft00 */, + 967 /* X25519Kyber768Draft00Old */, ++ 968 /* X25519MLKEM768 */, 961 /* X448 */, 12 /* X509 */, 402 /* X509v3 AC Targeting */, diff --git a/src/crypto/obj/obj_mac.num b/src/crypto/obj/obj_mac.num -index a0519acee..caeb5eaed 100644 +index a0519acee..2a46adfe8 100644 --- a/src/crypto/obj/obj_mac.num +++ b/src/crypto/obj/obj_mac.num -@@ -952,3 +952,6 @@ X448 961 +@@ -952,3 +952,7 @@ X448 961 sha512_256 962 hkdf 963 X25519Kyber768Draft00 964 +X25519Kyber512Draft00 965 +P256Kyber768Draft00 966 +X25519Kyber768Draft00Old 967 ++X25519MLKEM768 968 diff --git a/src/crypto/obj/objects.txt b/src/crypto/obj/objects.txt -index 3ad32ea3d..aa1404d83 100644 +index 3ad32ea3d..347fc556a 100644 --- a/src/crypto/obj/objects.txt +++ b/src/crypto/obj/objects.txt -@@ -1332,8 +1332,11 @@ secg-scheme 14 3 : dhSinglePass-cofactorDH-sha512kdf-scheme +@@ -1332,8 +1332,12 @@ secg-scheme 14 3 : dhSinglePass-cofactorDH-sha512kdf-scheme : dh-std-kdf : dh-cofactor-kdf @@ -3814,11 +4012,12 @@ index 3ad32ea3d..aa1404d83 100644 : X25519Kyber768Draft00 + : P256Kyber768Draft00 + : X25519Kyber768Draft00Old ++ : X25519MLKEM768 # See RFC 8410. 1 3 101 110 : X25519 diff --git a/src/include/openssl/kyber.h b/src/include/openssl/kyber.h -index cafae9d17..074ac5906 100644 +index cafae9d17..a05eb8957 100644 --- a/src/include/openssl/kyber.h +++ b/src/include/openssl/kyber.h @@ -1,17 +1,3 @@ @@ -3839,7 +4038,7 @@ index cafae9d17..074ac5906 100644 #ifndef OPENSSL_HEADER_KYBER_H #define OPENSSL_HEADER_KYBER_H -@@ -21,105 +7,100 @@ +@@ -21,105 +7,104 @@ extern "C" { #endif @@ -3983,39 +4182,43 @@ index cafae9d17..074ac5906 100644 + +// KYBER512_encap is a deterministic function the generates and encrypts a random +// session key from the given entropy, writing those values to |out_shared_key| -+// and |out_ciphertext|, respectively. -+OPENSSL_EXPORT void KYBER512_encap(uint8_t out_ciphertext[KYBER512_CIPHERTEXT_BYTES], ++// and |out_ciphertext|, respectively. If |mlkem| is 1, will use ML-KEM-512. ++OPENSSL_EXPORT int KYBER512_encap(uint8_t out_ciphertext[KYBER512_CIPHERTEXT_BYTES], + uint8_t out_shared_key[KYBER_KEY_BYTES], + const struct KYBER512_public_key *in_pub, -+ const uint8_t in[KYBER_ENCAP_BYTES]); ++ const uint8_t in[KYBER_ENCAP_BYTES], ++ int mlkem); + +// KYBER768_encap is a deterministic function the generates and encrypts a random +// session key from the given entropy, writing those values to |out_shared_key| -+// and |out_ciphertext|, respectively. -+OPENSSL_EXPORT void KYBER768_encap(uint8_t out_ciphertext[KYBER768_CIPHERTEXT_BYTES], ++// and |out_ciphertext|, respectively. If |mlkem| is 1, will use ML-KEM-768. ++OPENSSL_EXPORT int KYBER768_encap(uint8_t out_ciphertext[KYBER768_CIPHERTEXT_BYTES], + uint8_t out_shared_key[KYBER_KEY_BYTES], + const struct KYBER768_public_key *in_pub, -+ const uint8_t in[KYBER_ENCAP_BYTES]); ++ const uint8_t in[KYBER_ENCAP_BYTES], ++ int mlkem); + +// KYBER_decap decrypts a session key from |ciphertext_len| bytes of +// |ciphertext|. If the ciphertext is valid, the decrypted key is written to +// |out_shared_key|. Otherwise a key dervied from |ciphertext| and a secret key (kept +// in |in_priv|) is written. If the ciphertext is the wrong length then it will +// leak which was done via side-channels. Otherwise it should perform either -+// action in constant-time. ++// action in constant-time. If |mlkem| is 1, will use ML-KEM-512. +OPENSSL_EXPORT void KYBER512_decap(uint8_t out_shared_key[KYBER_KEY_BYTES], + const struct KYBER512_private_key *in_priv, -+ const uint8_t *ciphertext, size_t ciphertext_len); ++ const uint8_t *ciphertext, size_t ciphertext_len, ++ int mlkem); + +// KYBER_decap decrypts a session key from |ciphertext_len| bytes of +// |ciphertext|. If the ciphertext is valid, the decrypted key is written to +// |out_shared_key|. Otherwise a key dervied from |ciphertext| and a secret key (kept +// in |in_priv|) is written. If the ciphertext is the wrong length then it will +// leak which was done via side-channels. Otherwise it should perform either -+// action in constant-time. ++// action in constant-time. If |mlkem| is 1, will use ML-KEM-768. +OPENSSL_EXPORT void KYBER768_decap(uint8_t out_shared_key[KYBER_KEY_BYTES], + const struct KYBER768_private_key *in_priv, -+ const uint8_t *ciphertext, size_t ciphertext_len); ++ 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( @@ -4036,10 +4239,10 @@ index cafae9d17..074ac5906 100644 #if defined(__cplusplus) } // extern C diff --git a/src/include/openssl/nid.h b/src/include/openssl/nid.h -index 4dd8841b1..8237efb74 100644 +index 4dd8841b1..5b102c610 100644 --- a/src/include/openssl/nid.h +++ b/src/include/openssl/nid.h -@@ -4255,6 +4255,15 @@ extern "C" { +@@ -4255,6 +4255,18 @@ extern "C" { #define SN_X25519Kyber768Draft00 "X25519Kyber768Draft00" #define NID_X25519Kyber768Draft00 964 @@ -4051,21 +4254,25 @@ index 4dd8841b1..8237efb74 100644 + +#define SN_X25519Kyber768Draft00Old "X25519Kyber768Draft00Old" +#define NID_X25519Kyber768Draft00Old 967 ++ ++#define SN_X25519MLKEM768 "X25519MLKEM768" ++#define NID_X25519MLKEM768 968 + #if defined(__cplusplus) } /* extern C */ diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h -index 53aa9b453..8233ad210 100644 +index 53aa9b453..f9683f4cf 100644 --- a/src/include/openssl/ssl.h +++ b/src/include/openssl/ssl.h -@@ -2378,6 +2378,9 @@ OPENSSL_EXPORT int SSL_set1_curves_list(SSL *ssl, const char *curves); +@@ -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 // SSL_get_curve_id returns the ID of the curve used by |ssl|'s most recently // completed handshake or 0 if not applicable. @@ -4083,21 +4290,22 @@ index 5c7e881bf..3c0770cf3 100644 crypto/pkcs8/test/no_encryption.p12 crypto/pkcs8/test/nss.p12 diff --git a/src/ssl/extensions.cc b/src/ssl/extensions.cc -index 5ee280221..0a706c411 100644 +index 5ee280221..aae3e6a7f 100644 --- a/src/ssl/extensions.cc +++ b/src/ssl/extensions.cc -@@ -207,6 +207,9 @@ static bool tls1_check_duplicate_extensions(const CBS *cbs) { +@@ -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: return true; default: return false; diff --git a/src/ssl/ssl_key_share.cc b/src/ssl/ssl_key_share.cc -index 09a9ad380..f7d2226e3 100644 +index 09a9ad380..d7a8f0a80 100644 --- a/src/ssl/ssl_key_share.cc +++ b/src/ssl/ssl_key_share.cc @@ -26,6 +26,7 @@ @@ -4108,13 +4316,14 @@ index 09a9ad380..f7d2226e3 100644 #include #include #include -@@ -193,63 +194,384 @@ class X25519KeyShare : public SSLKeyShare { +@@ -193,63 +194,292 @@ class X25519KeyShare : public SSLKeyShare { uint8_t private_key_[32]; }; -class X25519Kyber768KeyShare : public SSLKeyShare { +class P256Kyber768Draft00KeyShare : public SSLKeyShare { -+ public: + public: +- X25519Kyber768KeyShare() {} + P256Kyber768Draft00KeyShare() {} + + uint16_t GroupID() const override { return SSL_CURVE_P256_KYBER768_DRAFT00; } @@ -4159,15 +4368,17 @@ index 09a9ad380..f7d2226e3 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; -+ } -+ + } + + bool Encap(CBB *out_public_key, Array *out_secret, + uint8_t *out_alert, Span peer_key) override { + assert(!p256_private_key_); @@ -4247,7 +4458,10 @@ index 09a9ad380..f7d2226e3 100644 + uint8_t entropy[KYBER_ENCAP_BYTES]; + RAND_bytes(entropy, sizeof(entropy)); + -+ KYBER768_encap(ciphertext, secret.data() + 32, &peer_public_key, entropy); ++ if(!KYBER768_encap(ciphertext, secret.data() + 32, &peer_public_key, entropy, 0)) { ++ *out_alert = SSL_AD_ILLEGAL_PARAMETER; ++ return false; ++ } + if(!CBB_add_bytes(out_public_key, ciphertext, sizeof(ciphertext))) { + return false; + } @@ -4318,7 +4532,7 @@ index 09a9ad380..f7d2226e3 100644 + } + + KYBER768_decap(secret.data() + 32, &kyber_private_key_, -+ peer_key.data() + 65, peer_key.size() - 65); ++ peer_key.data() + 65, peer_key.size() - 65, 0); + + *out_secret = std::move(secret); + return true; @@ -4330,14 +4544,138 @@ index 09a9ad380..f7d2226e3 100644 +}; + +class X25519Kyber768Draft00KeyShare : public SSLKeyShare { - public: -- X25519Kyber768KeyShare() {} ++ 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); + } + + 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_); ++ uint8_t kyber_entropy[KYBER_GENERATE_KEY_BYTES]; ++ KYBER768_public_key kyber_public_key; ++ RAND_bytes(kyber_entropy, sizeof(kyber_entropy)); ++ KYBER768_generate_key(&kyber_public_key, &kyber_private_key_, kyber_entropy); ++ ++ uint8_t kyber_public_key_bytes[KYBER768_PUBLIC_KEY_BYTES]; ++ KYBER768_marshal_public_key(kyber_public_key_bytes, &kyber_public_key); + + if (!CBB_add_bytes(out, x25519_public_key, sizeof(x25519_public_key)) || +- !CBB_add_bytes(out, kyber_public_key, sizeof(kyber_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 { ++ bool Encap(CBB *out_public_key, Array *out_secret, ++ uint8_t *out_alert, Span peer_key) override { + Array secret; +- if (!secret.Init(32 + 32)) { ++ if (!secret.Init(32 + KYBER_KEY_BYTES)) { ++ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return false; + } + + uint8_t x25519_public_key[32]; + X25519_keypair(x25519_public_key, x25519_private_key_); +- KYBER_public_key peer_kyber_pub; +- CBS peer_key_cbs; +- CBS peer_x25519_cbs; +- CBS peer_kyber_cbs; +- CBS_init(&peer_key_cbs, peer_key.data(), peer_key.size()); +- if (!CBS_get_bytes(&peer_key_cbs, &peer_x25519_cbs, 32) || +- !CBS_get_bytes(&peer_key_cbs, &peer_kyber_cbs, +- KYBER_PUBLIC_KEY_BYTES) || +- CBS_len(&peer_key_cbs) != 0 || +- !X25519(secret.data(), x25519_private_key_, +- CBS_data(&peer_x25519_cbs)) || +- !KYBER_parse_public_key(&peer_kyber_pub, &peer_kyber_cbs)) { ++ ++ KYBER768_public_key peer_public_key; ++ if (peer_key.size() != 32 + KYBER768_PUBLIC_KEY_BYTES) { ++ *out_alert = SSL_AD_DECODE_ERROR; ++ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); ++ return false; ++ } ++ ++ KYBER768_parse_public_key(&peer_public_key, peer_key.data() + 32); ++ ++ if (!X25519(secret.data(), x25519_private_key_, peer_key.data())) { + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); + return false; + } + +- uint8_t kyber_ciphertext[KYBER_CIPHERTEXT_BYTES]; +- KYBER_encap(kyber_ciphertext, secret.data() + 32, secret.size() - 32, +- &peer_kyber_pub); ++ uint8_t ciphertext[KYBER768_CIPHERTEXT_BYTES]; ++ uint8_t entropy[KYBER_ENCAP_BYTES]; ++ RAND_bytes(entropy, sizeof(entropy)); + +- 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))) { ++ !CBB_add_bytes(out_public_key, ciphertext, sizeof(ciphertext))) { + return false; + } + +@@ -258,30 +488,233 @@ class X25519Kyber768KeyShare : public SSLKeyShare { + } + + bool Decap(Array *out_secret, uint8_t *out_alert, +- Span ciphertext) override { ++ Span peer_key) override { ++ *out_alert = SSL_AD_INTERNAL_ERROR; ++ ++ Array secret; ++ if (!secret.Init(32 + KYBER_KEY_BYTES)) { ++ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); ++ return false; ++ } ++ ++ if (peer_key.size() != 32 + KYBER768_CIPHERTEXT_BYTES || ++ !X25519(secret.data(), x25519_private_key_, peer_key.data())) { ++ *out_alert = SSL_AD_DECODE_ERROR; ++ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); ++ return false; ++ } ++ ++ KYBER768_decap(secret.data() + 32, &kyber_private_key_, ++ peer_key.data() + 32, peer_key.size() - 32, 0); ++ ++ *out_secret = std::move(secret); ++ return true; ++ } ++ ++ private: ++ uint8_t x25519_private_key_[32]; ++ KYBER768_private_key kyber_private_key_; ++ uint16_t group_id_; ++}; ++ ++class X25519MLKEM768KeyShare : public SSLKeyShare { ++ public: ++ X25519MLKEM768KeyShare() {} ++ ++ uint16_t GroupID() const override { return SSL_CURVE_X25519_MLKEM768; } + + bool Generate(CBB *out) override { + uint8_t x25519_public_key[32]; @@ -4351,9 +4689,8 @@ index 09a9ad380..f7d2226e3 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_bytes, -+ sizeof(kyber_public_key_bytes))) { ++ if (!CBB_add_bytes(out, kyber_public_key_bytes, sizeof(kyber_public_key_bytes)) || ++ !CBB_add_bytes(out, x25519_public_key, sizeof(x25519_public_key))) { + return false; + } + @@ -4372,15 +4709,16 @@ index 09a9ad380..f7d2226e3 100644 + X25519_keypair(x25519_public_key, x25519_private_key_); + + KYBER768_public_key peer_public_key; -+ if (peer_key.size() != 32 + KYBER768_PUBLIC_KEY_BYTES) { ++ if (peer_key.size() != KYBER768_PUBLIC_KEY_BYTES + 32) { + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); + return false; + } + -+ KYBER768_parse_public_key(&peer_public_key, peer_key.data() + 32); ++ KYBER768_parse_public_key(&peer_public_key, peer_key.data()); + -+ if (!X25519(secret.data(), x25519_private_key_, peer_key.data())) { ++ if (!X25519(secret.data() + 32, x25519_private_key_, ++ peer_key.data() + KYBER768_PUBLIC_KEY_BYTES)) { + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); + return false; @@ -4390,10 +4728,12 @@ index 09a9ad380..f7d2226e3 100644 + uint8_t entropy[KYBER_ENCAP_BYTES]; + RAND_bytes(entropy, sizeof(entropy)); + -+ KYBER768_encap(ciphertext, secret.data() + 32, &peer_public_key, entropy); -+ if(!CBB_add_bytes(out_public_key, x25519_public_key, -+ sizeof(x25519_public_key)) || -+ !CBB_add_bytes(out_public_key, ciphertext, sizeof(ciphertext))) { ++ if(!KYBER768_encap(ciphertext, secret.data(), &peer_public_key, entropy, 1)) { ++ *out_alert = SSL_AD_ILLEGAL_PARAMETER; ++ return false; ++ } ++ if(!CBB_add_bytes(out_public_key, ciphertext, sizeof(ciphertext)) || ++ !CBB_add_bytes(out_public_key, x25519_public_key, sizeof(x25519_public_key))) { + return false; + } + @@ -4403,34 +4743,37 @@ index 09a9ad380..f7d2226e3 100644 + + bool Decap(Array *out_secret, uint8_t *out_alert, + Span peer_key) override { -+ *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; -+ } -+ -+ if (peer_key.size() != 32 + KYBER768_CIPHERTEXT_BYTES || -+ !X25519(secret.data(), x25519_private_key_, peer_key.data())) { -+ *out_alert = SSL_AD_DECODE_ERROR; -+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); -+ return false; -+ } -+ -+ KYBER768_decap(secret.data() + 32, &kyber_private_key_, -+ peer_key.data() + 32, peer_key.size() - 32); + return false; + } -- uint16_t GroupID() const override { -- return SSL_CURVE_X25519_KYBER768_DRAFT00; +- if (ciphertext.size() != 32 + KYBER_CIPHERTEXT_BYTES || +- !X25519(secret.data(), x25519_private_key_, ciphertext.data())) { ++ if (peer_key.size() != KYBER768_CIPHERTEXT_BYTES + 32 || ++ !X25519(secret.data() + 32, x25519_private_key_, ++ peer_key.data() + KYBER768_CIPHERTEXT_BYTES )) { + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); + return false; + } + +- KYBER_decap(secret.data() + 32, secret.size() - 32, ciphertext.data() + 32, +- &kyber_private_key_); ++ KYBER768_decap(secret.data(), &kyber_private_key_, ++ peer_key.data(), peer_key.size() - 32, 1); ++ + *out_secret = std::move(secret); + return true; - } - ++ } ++ + private: + uint8_t x25519_private_key_[32]; + KYBER768_private_key kyber_private_key_; -+ uint16_t group_id_; +}; + +class X25519Kyber512Draft00KeyShare : public SSLKeyShare { @@ -4439,12 +4782,10 @@ index 09a9ad380..f7d2226e3 100644 + + uint16_t GroupID() const override { return SSL_CURVE_X25519_KYBER512_DRAFT00; } + - 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]; + KYBER512_public_key kyber_public_key; + RAND_bytes(kyber_entropy, sizeof(kyber_entropy)); @@ -4452,42 +4793,26 @@ index 09a9ad380..f7d2226e3 100644 + + uint8_t kyber_public_key_bytes[KYBER512_PUBLIC_KEY_BYTES]; + KYBER512_marshal_public_key(kyber_public_key_bytes, &kyber_public_key); - - if (!CBB_add_bytes(out, x25519_public_key, sizeof(x25519_public_key)) || -- !CBB_add_bytes(out, kyber_public_key, 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_); + + KYBER512_public_key peer_public_key; + if (peer_key.size() != 32 + KYBER512_PUBLIC_KEY_BYTES) { @@ -4499,56 +4824,48 @@ index 09a9ad380..f7d2226e3 100644 + KYBER512_parse_public_key(&peer_public_key, peer_key.data() + 32); + + if (!X25519(secret.data(), x25519_private_key_, peer_key.data())) { - *out_alert = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); - return false; - } - -- uint8_t kyber_ciphertext[KYBER_CIPHERTEXT_BYTES]; -- KYBER_encap(kyber_ciphertext, secret.data() + 32, secret.size() - 32, -- &peer_kyber_pub); ++ *out_alert = SSL_AD_DECODE_ERROR; ++ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); ++ return false; ++ } ++ + uint8_t ciphertext[KYBER512_CIPHERTEXT_BYTES]; + uint8_t entropy[KYBER_ENCAP_BYTES]; + RAND_bytes(entropy, sizeof(entropy)); - -- if (!CBB_add_bytes(out_ciphertext, x25519_public_key, -+ KYBER512_encap(ciphertext, secret.data() + 32, &peer_public_key, entropy); ++ ++ if(!KYBER512_encap(ciphertext, secret.data() + 32, &peer_public_key, entropy, 0)) { ++ *out_alert = SSL_AD_ILLEGAL_PARAMETER; ++ return false; ++ } + if(!CBB_add_bytes(out_public_key, x25519_public_key, - sizeof(x25519_public_key)) || -- !CBB_add_bytes(out_ciphertext, kyber_ciphertext, -- sizeof(kyber_ciphertext))) { ++ sizeof(x25519_public_key)) || + !CBB_add_bytes(out_public_key, ciphertext, sizeof(ciphertext))) { - return false; - } - -@@ -258,30 +580,32 @@ 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; - - 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() != 32 + KYBER512_CIPHERTEXT_BYTES || + !X25519(secret.data(), x25519_private_key_, peer_key.data())) { - *out_alert = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); - return false; - } - -- KYBER_decap(secret.data() + 32, secret.size() - 32, ciphertext.data() + 32, -- &kyber_private_key_); ++ *out_alert = SSL_AD_DECODE_ERROR; ++ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); ++ return false; ++ } ++ + KYBER512_decap(secret.data() + 32, &kyber_private_key_, -+ peer_key.data() + 32, peer_key.size() - 32); ++ peer_key.data() + 32, peer_key.size() - 32, 0); + *out_secret = std::move(secret); return true; @@ -4561,7 +4878,7 @@ index 09a9ad380..f7d2226e3 100644 }; constexpr NamedGroup kNamedGroups[] = { -@@ -290,8 +614,14 @@ 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"}, @@ -4573,11 +4890,13 @@ index 09a9ad380..f7d2226e3 100644 + {NID_X25519Kyber768Draft00Old, SSL_CURVE_X25519_KYBER768_DRAFT00_OLD, + "X25519Kyber768Draft00Old", "Xyber768D00Old"}, + {NID_P256Kyber768Draft00, SSL_CURVE_P256_KYBER768_DRAFT00, -+ "P256Kyber768Draft00", "P256Kyber768D00"} ++ "P256Kyber768Draft00", "P256Kyber768D00"}, ++ {NID_X25519MLKEM768, SSL_CURVE_X25519_MLKEM768, ++ "X25519MLKEM768", "X25519MLKEM768"} }; } // namespace -@@ -312,8 +642,16 @@ UniquePtr SSLKeyShare::Create(uint16_t group_id) { +@@ -312,8 +753,18 @@ UniquePtr SSLKeyShare::Create(uint16_t group_id) { return MakeUnique(NID_secp521r1, SSL_CURVE_SECP521R1); case SSL_CURVE_X25519: return MakeUnique(); @@ -4592,6 +4911,8 @@ index 09a9ad380..f7d2226e3 100644 + group_id)); + case SSL_CURVE_P256_KYBER768_DRAFT00: + return UniquePtr(New()); ++ case SSL_CURVE_X25519_MLKEM768: ++ return UniquePtr(New()); default: return nullptr; } @@ -4609,10 +4930,10 @@ index 838761af5..9eb201d37 100644 static const uint16_t kSigAlgs[] = { SSL_SIGN_RSA_PKCS1_SHA256, diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc -index ef43a9e98..9756fd2a0 100644 +index ef43a9e98..22178b5f6 100644 --- a/src/ssl/ssl_test.cc +++ b/src/ssl/ssl_test.cc -@@ -409,7 +409,30 @@ static const CurveTest kCurveTests[] = { +@@ -409,7 +409,34 @@ static const CurveTest kCurveTests[] = { "P-256:X25519Kyber768Draft00", { SSL_CURVE_SECP256R1, SSL_CURVE_X25519_KYBER768_DRAFT00 }, }, @@ -4638,6 +4959,10 @@ index ef43a9e98..9756fd2a0 100644 + { SSL_CURVE_P256_KYBER768_DRAFT00 }, + }, + { ++ "X25519MLKEM768", ++ { SSL_CURVE_X25519_MLKEM768 }, ++ }, ++ { + "P-256:P256Kyber768D00", + { SSL_CURVE_SECP256R1, SSL_CURVE_P256_KYBER768_DRAFT00 }, + }, @@ -4645,7 +4970,7 @@ index ef43a9e98..9756fd2a0 100644 "P-256:P-384:P-521:X25519", { diff --git a/src/tool/speed.cc b/src/tool/speed.cc -index 5b0205953..831875514 100644 +index 5b0205953..6b3c67dab 100644 --- a/src/tool/speed.cc +++ b/src/tool/speed.cc @@ -904,6 +904,116 @@ static bool SpeedScrypt(const std::string &selected) { @@ -4684,7 +5009,7 @@ index 5b0205953..831875514 100644 + uint8_t entropy[KYBER_ENCAP_BYTES]; + uint8_t shared_key[KYBER_KEY_BYTES]; + RAND_bytes(entropy, sizeof(entropy)); -+ KYBER768_encap(ciphertext, shared_key, &pub, entropy); ++ KYBER768_encap(ciphertext, shared_key, &pub, entropy, 0); + return true; + })) { + fprintf(stderr, "Failed to time KYBER768_encap.\n"); @@ -4695,7 +5020,7 @@ index 5b0205953..831875514 100644 + + if (!TimeFunction(&results, [&priv, &ciphertext]() -> bool { + uint8_t shared_key[KYBER_KEY_BYTES]; -+ KYBER768_decap(shared_key, &priv, ciphertext, sizeof(ciphertext)); ++ KYBER768_decap(shared_key, &priv, ciphertext, sizeof(ciphertext), 0); + return true; + })) { + fprintf(stderr, "Failed to time KYBER768_decap.\n"); @@ -4739,7 +5064,7 @@ index 5b0205953..831875514 100644 + uint8_t entropy[KYBER_ENCAP_BYTES]; + uint8_t shared_key[KYBER_KEY_BYTES]; + RAND_bytes(entropy, sizeof(entropy)); -+ KYBER512_encap(ciphertext, shared_key, &pub, entropy); ++ KYBER512_encap(ciphertext, shared_key, &pub, entropy, 0); + return true; + })) { + fprintf(stderr, "Failed to time KYBER512_encap.\n"); @@ -4750,7 +5075,7 @@ index 5b0205953..831875514 100644 + + if (!TimeFunction(&results, [&priv, &ciphertext]() -> bool { + uint8_t shared_key[KYBER_KEY_BYTES]; -+ KYBER512_decap(shared_key, &priv, ciphertext, sizeof(ciphertext)); ++ KYBER512_decap(shared_key, &priv, ciphertext, sizeof(ciphertext), 0); + return true; + })) { + fprintf(stderr, "Failed to time KYBER512_decap.\n"); @@ -4832,5 +5157,5 @@ index 5b0205953..831875514 100644 !SpeedTrustToken("TrustToken-Exp1-Batch1", TRUST_TOKEN_experiment_v1(), 1, selected) || -- -2.41.0 +2.46.0 diff --git a/boring/src/lib.rs b/boring/src/lib.rs index 6b3ade0f..6779586a 100644 --- a/boring/src/lib.rs +++ b/boring/src/lib.rs @@ -74,9 +74,11 @@ //! support by turning on `post-quantum` compilation feature. //! //! Upstream BoringSSL support the post-quantum hybrid key agreement `X25519Kyber768Draft00`. Most -//! users should stick to that one. Enabling this feature, adds a few other post-quantum key +//! users should stick to that one for now. Enabling this feature, adds a few other post-quantum key //! agreements: //! +//! - `X25519MLKEM768` is the successor of `X25519Kyber768Draft00`. We expect servers to switch +//! before the end of 2024. //! - `X25519Kyber768Draft00Old` is the same as `X25519Kyber768Draft00`, but under its old codepoint. //! - `X25519Kyber512Draft00`. Similar to `X25519Kyber768Draft00`, but uses level 1 parameter set for //! Kyber. Not recommended. It's useful to test whether the shorter ClientHello upsets fewer middle diff --git a/boring/src/ssl/mod.rs b/boring/src/ssl/mod.rs index 277002b7..9bc27bc3 100644 --- a/boring/src/ssl/mod.rs +++ b/boring/src/ssl/mod.rs @@ -765,6 +765,8 @@ impl SslCurve { ffi::SSL_CURVE_X25519_KYBER512_DRAFT00 => Some(ffi::NID_X25519Kyber512Draft00), #[cfg(feature = "pq-experimental")] ffi::SSL_CURVE_P256_KYBER768_DRAFT00 => Some(ffi::NID_P256Kyber768Draft00), + #[cfg(feature = "pq-experimental")] + ffi::SSL_CURVE_X25519_MLKEM768 => Some(ffi::NID_X25519MLKEM768), _ => None, } } @@ -2691,13 +2693,13 @@ impl SslRef { if cfg!(feature = "kx-client-nist-required") { "P256Kyber768Draft00:P-256:P-384:P-521" } else { - "X25519Kyber768Draft00:X25519:P256Kyber768Draft00:P-256:P-384:P-521" + "X25519Kyber768Draft00:X25519MLKEM768: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:X25519Kyber768Draft00:P256Kyber768Draft00" + "X25519:P-256:P-384:P-521:X25519MLKEM768:X25519Kyber768Draft00:P256Kyber768Draft00" } } else { if cfg!(feature = "kx-client-nist-required") { @@ -2713,8 +2715,10 @@ impl SslRef { #[cfg(feature = "kx-safe-default")] fn server_set_default_curves_list(&mut self) { - self.set_curves_list("X25519Kyber768Draft00:P256Kyber768Draft00:X25519:P-256:P-384") - .expect("invalid default server curves list"); + self.set_curves_list( + "X25519Kyber768Draft00:X25519MLKEM768:P256Kyber768Draft00:X25519:P-256:P-384", + ) + .expect("invalid default server curves list"); } /// Returns the [`SslCurve`] used for this `SslRef`. From 193bf3b9d7ac06adfef5e614f077970c9d2a2f8e Mon Sep 17 00:00:00 2001 From: Anthony Ramine <123095+nox@users.noreply.github.com> Date: Wed, 18 Sep 2024 13:24:35 +0200 Subject: [PATCH 18/41] Implement optional Hyper 1 support in hyper-boring (#246) --- .github/workflows/ci.yml | 2 + Cargo.toml | 10 +- boring-sys/Cargo.toml | 3 + hyper-boring/Cargo.toml | 19 +- hyper-boring/src/lib.rs | 356 +--------------------- hyper-boring/src/v0.rs | 345 +++++++++++++++++++++ hyper-boring/src/v1.rs | 350 +++++++++++++++++++++ hyper-boring/{ => tests}/test/cert.pem | 0 hyper-boring/{ => tests}/test/key.pem | 0 hyper-boring/{ => tests}/test/root-ca.pem | 0 hyper-boring/{src/test.rs => tests/v0.rs} | 26 +- hyper-boring/tests/v1.rs | 160 ++++++++++ 12 files changed, 905 insertions(+), 366 deletions(-) create mode 100644 hyper-boring/src/v0.rs create mode 100644 hyper-boring/src/v1.rs rename hyper-boring/{ => tests}/test/cert.pem (100%) rename hyper-boring/{ => tests}/test/key.pem (100%) rename hyper-boring/{ => tests}/test/root-ca.pem (100%) rename hyper-boring/{src/test.rs => tests/v0.rs} (87%) create mode 100644 hyper-boring/tests/v1.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fb3c07cb..083ee77c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -361,3 +361,5 @@ jobs: name: Run `rpk,underscore-wildcards` tests - run: cargo test --features pq-experimental,rpk,underscore-wildcards name: Run `pq-experimental,rpk,underscore-wildcards` tests + - run: cargo test -p hyper-boring --features hyper1 + name: Run hyper 1.0 tests for hyper-boring diff --git a/Cargo.toml b/Cargo.toml index f848893f..a067f74f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ boring = { version = "4.9.1", path = "./boring" } tokio-boring = { version = "4.9.1", path = "./tokio-boring" } bindgen = { version = "0.70.1", default-features = false, features = ["runtime"] } +bytes = "1" cmake = "0.1.18" fs_extra = "1.3.0" fslock = "0.2" @@ -36,10 +37,15 @@ futures = "0.3" tokio = "1" anyhow = "1" antidote = "1.0.0" -http = "0.2" -hyper = { version = "0.14", default-features = false } +http = "1" +http-body-util = "0.1.2" +http_old = { package = "http", version = "0.2" } +hyper = "1" +hyper-util = "0.1.6" +hyper_old = { package = "hyper", version = "0.14", default-features = false } linked_hash_set = "0.1" once_cell = "1.0" openssl-macros = "0.1.1" tower = "0.4" tower-layer = "0.3" +tower-service = "0.3" diff --git a/boring-sys/Cargo.toml b/boring-sys/Cargo.toml index c1471f20..5b623181 100644 --- a/boring-sys/Cargo.toml +++ b/boring-sys/Cargo.toml @@ -81,3 +81,6 @@ bindgen = { workspace = true } cmake = { workspace = true } fs_extra = { workspace = true } fslock = { workspace = true } + +[lints.rust] +unexpected_cfgs = { level = "allow", check-cfg = ['cfg(const_fn)'] } diff --git a/hyper-boring/Cargo.toml b/hyper-boring/Cargo.toml index b8c5c9d0..1e30d035 100644 --- a/hyper-boring/Cargo.toml +++ b/hyper-boring/Cargo.toml @@ -17,7 +17,7 @@ rustdoc-args = ["--cfg", "docsrs"] [features] default = ["runtime"] -runtime = ["hyper/runtime"] +runtime = ["hyper_old/runtime"] # Use a FIPS-validated version of boringssl. fips = ["tokio-boring/fips"] @@ -28,19 +28,30 @@ fips-link-precompiled = ["tokio-boring/fips-link-precompiled"] # Enables experimental post-quantum crypto (https://blog.cloudflare.com/post-quantum-for-all/) pq-experimental = ["tokio-boring/pq-experimental"] +# Enable Hyper 1 support +hyper1 = ["dep:http", "dep:hyper", "dep:hyper-util", "dep:tower-service"] + [dependencies] antidote = { workspace = true } -http = { workspace = true } -hyper = { workspace = true, features = ["client"] } +http = { workspace = true, optional = true } +http_old = { workspace = true } +hyper = { workspace = true, optional = true } +hyper-util = { workspace = true, optional = true, features = ["client", "client-legacy"] } +hyper_old = { workspace = true, features = ["client"] } linked_hash_set = { workspace = true } once_cell = { workspace = true } boring = { workspace = true } tokio = { workspace = true } tokio-boring = { workspace = true } tower-layer = { workspace = true } +tower-service = { workspace = true, optional = true } [dev-dependencies] -hyper = { workspace = true, features = [ "full" ] } +bytes = { workspace = true } +http-body-util = { workspace = true } +hyper-util = { workspace = true, features = ["http1", "http2", "service", "tokio"] } +hyper = { workspace = true, features = ["server"] } +hyper_old = { workspace = true, features = [ "full" ] } tokio = { workspace = true, features = [ "full" ] } tower = { workspace = true, features = ["util"] } futures = { workspace = true } diff --git a/hyper-boring/src/lib.rs b/hyper-boring/src/lib.rs index 53c12a46..736a3d89 100644 --- a/hyper-boring/src/lib.rs +++ b/hyper-boring/src/lib.rs @@ -2,91 +2,27 @@ #![warn(missing_docs)] #![cfg_attr(docsrs, feature(doc_auto_cfg))] -use crate::cache::{SessionCache, SessionKey}; -use antidote::Mutex; +use crate::cache::SessionKey; use boring::error::ErrorStack; use boring::ex_data::Index; -use boring::ssl::{ - ConnectConfiguration, Ssl, SslConnector, SslConnectorBuilder, SslMethod, SslRef, - SslSessionCacheMode, -}; -use http::uri::Scheme; -use hyper::client::connect::{Connected, Connection}; -#[cfg(feature = "runtime")] -use hyper::client::HttpConnector; -use hyper::service::Service; -use hyper::Uri; +use boring::ssl::Ssl; use once_cell::sync::OnceCell; -use std::fmt::Debug; -use std::future::Future; -use std::io; -use std::net; -use std::pin::Pin; -use std::sync::Arc; -use std::task::{Context, Poll}; -use std::{error::Error, fmt}; -use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; +use std::fmt; use tokio_boring::SslStream; -use tower_layer::Layer; mod cache; -#[cfg(test)] -mod test; +mod v0; +/// Hyper 1 support. +#[cfg(feature = "hyper1")] +pub mod v1; + +pub use self::v0::*; fn key_index() -> Result, ErrorStack> { static IDX: OnceCell> = OnceCell::new(); IDX.get_or_try_init(Ssl::new_ex_index).copied() } -#[derive(Clone)] -struct Inner { - ssl: SslConnector, - cache: Arc>, - callback: Option, - ssl_callback: Option, -} - -type Callback = - Arc Result<(), ErrorStack> + Sync + Send>; -type SslCallback = Arc Result<(), ErrorStack> + Sync + Send>; - -impl Inner { - fn setup_ssl(&self, uri: &Uri, host: &str) -> Result { - let mut conf = self.ssl.configure()?; - - if let Some(ref callback) = self.callback { - callback(&mut conf, uri)?; - } - - let key = SessionKey { - host: host.to_string(), - port: uri.port_u16().unwrap_or(443), - }; - - if let Some(session) = self.cache.lock().get(&key) { - unsafe { - conf.set_session(&session)?; - } - } - - let idx = key_index()?; - conf.set_ex_data(idx, key); - - let mut ssl = conf.into_ssl(host)?; - - if let Some(ref ssl_callback) = self.ssl_callback { - ssl_callback(&mut ssl, uri)?; - } - - Ok(ssl) - } -} - -/// A layer which wraps services in an `HttpsConnector`. -pub struct HttpsLayer { - inner: Inner, -} - /// Settings for [`HttpsLayer`] pub struct HttpsLayerSettings { session_cache_capacity: usize, @@ -123,214 +59,6 @@ impl HttpsLayerSettingsBuilder { } } -impl HttpsLayer { - /// Creates a new `HttpsLayer` with default settings. - /// - /// ALPN is configured to support both HTTP/1 and HTTP/1.1. - pub fn new() -> Result { - let mut ssl = SslConnector::builder(SslMethod::tls())?; - - ssl.set_alpn_protos(b"\x02h2\x08http/1.1")?; - - Self::with_connector(ssl) - } - - /// Creates a new `HttpsLayer`. - /// - /// The session cache configuration of `ssl` will be overwritten. - pub fn with_connector(ssl: SslConnectorBuilder) -> Result { - Self::with_connector_and_settings(ssl, Default::default()) - } - - /// Creates a new `HttpsLayer` with settings - pub fn with_connector_and_settings( - mut ssl: SslConnectorBuilder, - settings: HttpsLayerSettings, - ) -> Result { - let cache = Arc::new(Mutex::new(SessionCache::with_capacity( - settings.session_cache_capacity, - ))); - - ssl.set_session_cache_mode(SslSessionCacheMode::CLIENT); - - ssl.set_new_session_callback({ - let cache = cache.clone(); - move |ssl, session| { - if let Some(key) = key_index().ok().and_then(|idx| ssl.ex_data(idx)) { - cache.lock().insert(key.clone(), session); - } - } - }); - - Ok(HttpsLayer { - inner: Inner { - ssl: ssl.build(), - cache, - callback: None, - ssl_callback: None, - }, - }) - } - - /// Registers a callback which can customize the configuration of each connection. - /// - /// Unsuitable to change verify hostflags (with `config.param_mut().set_hostflags(…)`), - /// as they are reset after the callback is executed. Use [`Self::set_ssl_callback`] - /// instead. - pub fn set_callback(&mut self, callback: F) - where - F: Fn(&mut ConnectConfiguration, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, - { - self.inner.callback = Some(Arc::new(callback)); - } - - /// Registers a callback which can customize the `Ssl` of each connection. - pub fn set_ssl_callback(&mut self, callback: F) - where - F: Fn(&mut SslRef, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, - { - self.inner.ssl_callback = Some(Arc::new(callback)); - } -} - -impl Layer for HttpsLayer { - type Service = HttpsConnector; - - fn layer(&self, inner: S) -> HttpsConnector { - HttpsConnector { - http: inner, - inner: self.inner.clone(), - } - } -} - -/// A Connector using OpenSSL to support `http` and `https` schemes. -#[derive(Clone)] -pub struct HttpsConnector { - http: T, - inner: Inner, -} - -#[cfg(feature = "runtime")] -impl HttpsConnector { - /// Creates a a new `HttpsConnector` using default settings. - /// - /// The Hyper `HttpConnector` is used to perform the TCP socket connection. ALPN is configured to support both - /// HTTP/2 and HTTP/1.1. - /// - /// Requires the `runtime` Cargo feature. - pub fn new() -> Result, ErrorStack> { - let mut http = HttpConnector::new(); - http.enforce_http(false); - - HttpsLayer::new().map(|l| l.layer(http)) - } -} - -impl HttpsConnector -where - S: Service + Send, - S::Error: Into>, - S::Future: Unpin + Send + 'static, - T: AsyncRead + AsyncWrite + Connection + Unpin + Debug + Sync + Send + 'static, -{ - /// Creates a new `HttpsConnector`. - /// - /// The session cache configuration of `ssl` will be overwritten. - pub fn with_connector( - http: S, - ssl: SslConnectorBuilder, - ) -> Result, ErrorStack> { - HttpsLayer::with_connector(ssl).map(|l| l.layer(http)) - } - - /// Registers a callback which can customize the configuration of each connection. - /// - /// Unsuitable to change verify hostflags (with `config.param_mut().set_hostflags(…)`), - /// as they are reset after the callback is executed. Use [`Self::set_ssl_callback`] - /// instead. - pub fn set_callback(&mut self, callback: F) - where - F: Fn(&mut ConnectConfiguration, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, - { - self.inner.callback = Some(Arc::new(callback)); - } - - /// Registers a callback which can customize the `Ssl` of each connection. - pub fn set_ssl_callback(&mut self, callback: F) - where - F: Fn(&mut SslRef, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, - { - self.inner.ssl_callback = Some(Arc::new(callback)); - } -} - -impl Service for HttpsConnector -where - S: Service + Send, - S::Error: Into>, - S::Future: Unpin + Send + 'static, - S::Response: AsyncRead + AsyncWrite + Connection + Unpin + Debug + Sync + Send + 'static, -{ - type Response = MaybeHttpsStream; - type Error = Box; - #[allow(clippy::type_complexity)] - type Future = Pin> + Send>>; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - self.http.poll_ready(cx).map_err(Into::into) - } - - fn call(&mut self, uri: Uri) -> Self::Future { - let is_tls_scheme = uri - .scheme() - .map(|s| s == &Scheme::HTTPS || s.as_str() == "wss") - .unwrap_or(false); - - let tls_setup = if is_tls_scheme { - Some((self.inner.clone(), uri.clone())) - } else { - None - }; - - let connect = self.http.call(uri); - - let f = async { - let conn = connect.await.map_err(Into::into)?; - - let (inner, uri) = match tls_setup { - Some((inner, uri)) => (inner, uri), - None => return Ok(MaybeHttpsStream::Http(conn)), - }; - - let mut host = uri.host().ok_or("URI missing host")?; - - // If `host` is an IPv6 address, we must strip away the square brackets that surround - // it (otherwise, boring will fail to parse the host as an IP address, eventually - // causing the handshake to fail due a hostname verification error). - if !host.is_empty() { - let last = host.len() - 1; - let mut chars = host.chars(); - - if let (Some('['), Some(']')) = (chars.next(), chars.last()) { - if host[1..last].parse::().is_ok() { - host = &host[1..last]; - } - } - } - - let ssl = inner.setup_ssl(&uri, host)?; - let stream = tokio_boring::SslStreamBuilder::new(ssl, conn) - .connect() - .await?; - - Ok(MaybeHttpsStream::Https(stream)) - }; - - Box::pin(f) - } -} - /// A stream which may be wrapped with TLS. pub enum MaybeHttpsStream { /// A raw HTTP stream. @@ -339,72 +67,6 @@ pub enum MaybeHttpsStream { Https(SslStream), } -impl AsyncRead for MaybeHttpsStream -where - T: AsyncRead + AsyncWrite + Unpin, -{ - fn poll_read( - mut self: Pin<&mut Self>, - ctx: &mut Context<'_>, - buf: &mut ReadBuf, - ) -> Poll> { - match &mut *self { - MaybeHttpsStream::Http(s) => Pin::new(s).poll_read(ctx, buf), - MaybeHttpsStream::Https(s) => Pin::new(s).poll_read(ctx, buf), - } - } -} - -impl AsyncWrite for MaybeHttpsStream -where - T: AsyncRead + AsyncWrite + Unpin, -{ - fn poll_write( - mut self: Pin<&mut Self>, - ctx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - match &mut *self { - MaybeHttpsStream::Http(s) => Pin::new(s).poll_write(ctx, buf), - MaybeHttpsStream::Https(s) => Pin::new(s).poll_write(ctx, buf), - } - } - - fn poll_flush(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { - match &mut *self { - MaybeHttpsStream::Http(s) => Pin::new(s).poll_flush(ctx), - MaybeHttpsStream::Https(s) => Pin::new(s).poll_flush(ctx), - } - } - - fn poll_shutdown(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { - match &mut *self { - MaybeHttpsStream::Http(s) => Pin::new(s).poll_shutdown(ctx), - MaybeHttpsStream::Https(s) => Pin::new(s).poll_shutdown(ctx), - } - } -} - -impl Connection for MaybeHttpsStream -where - T: Connection, -{ - fn connected(&self) -> Connected { - match self { - MaybeHttpsStream::Http(s) => s.connected(), - MaybeHttpsStream::Https(s) => { - let mut connected = s.get_ref().connected(); - - if s.ssl().selected_alpn_protocol() == Some(b"h2") { - connected = connected.negotiated_h2(); - } - - connected - } - } - } -} - impl fmt::Debug for MaybeHttpsStream { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { diff --git a/hyper-boring/src/v0.rs b/hyper-boring/src/v0.rs new file mode 100644 index 00000000..172d1640 --- /dev/null +++ b/hyper-boring/src/v0.rs @@ -0,0 +1,345 @@ +use crate::cache::{SessionCache, SessionKey}; +use crate::{key_index, HttpsLayerSettings, MaybeHttpsStream}; +use antidote::Mutex; +use boring::error::ErrorStack; +use boring::ssl::{ + ConnectConfiguration, Ssl, SslConnector, SslConnectorBuilder, SslMethod, SslRef, + SslSessionCacheMode, +}; +use http_old::uri::Scheme; +use hyper_old::client::connect::{Connected, Connection}; +use hyper_old::client::HttpConnector; +use hyper_old::service::Service; +use hyper_old::Uri; +use std::error::Error; +use std::future::Future; +use std::net; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; +use std::{fmt, io}; +use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; +use tower_layer::Layer; + +/// A Connector using OpenSSL to support `http` and `https` schemes. +#[derive(Clone)] +pub struct HttpsConnector { + http: T, + inner: Inner, +} + +#[cfg(feature = "runtime")] +impl HttpsConnector { + /// Creates a a new `HttpsConnector` using default settings. + /// + /// The Hyper `HttpConnector` is used to perform the TCP socket connection. ALPN is configured to support both + /// HTTP/2 and HTTP/1.1. + /// + /// Requires the `runtime` Cargo feature. + pub fn new() -> Result, ErrorStack> { + let mut http = HttpConnector::new(); + http.enforce_http(false); + + HttpsLayer::new().map(|l| l.layer(http)) + } +} + +impl HttpsConnector +where + S: Service + Send, + S::Error: Into>, + S::Future: Unpin + Send + 'static, + T: AsyncRead + AsyncWrite + Connection + Unpin + fmt::Debug + Sync + Send + 'static, +{ + /// Creates a new `HttpsConnector`. + /// + /// The session cache configuration of `ssl` will be overwritten. + pub fn with_connector( + http: S, + ssl: SslConnectorBuilder, + ) -> Result, ErrorStack> { + HttpsLayer::with_connector(ssl).map(|l| l.layer(http)) + } + + /// Registers a callback which can customize the configuration of each connection. + /// + /// Unsuitable to change verify hostflags (with `config.param_mut().set_hostflags(…)`), + /// as they are reset after the callback is executed. Use [`Self::set_ssl_callback`] + /// instead. + pub fn set_callback(&mut self, callback: F) + where + F: Fn(&mut ConnectConfiguration, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, + { + self.inner.callback = Some(Arc::new(callback)); + } + + /// Registers a callback which can customize the `Ssl` of each connection. + pub fn set_ssl_callback(&mut self, callback: F) + where + F: Fn(&mut SslRef, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, + { + self.inner.ssl_callback = Some(Arc::new(callback)); + } +} + +/// A layer which wraps services in an `HttpsConnector`. +pub struct HttpsLayer { + inner: Inner, +} + +#[derive(Clone)] +struct Inner { + ssl: SslConnector, + cache: Arc>, + callback: Option, + ssl_callback: Option, +} + +type Callback = + Arc Result<(), ErrorStack> + Sync + Send>; +type SslCallback = Arc Result<(), ErrorStack> + Sync + Send>; + +impl HttpsLayer { + /// Creates a new `HttpsLayer` with default settings. + /// + /// ALPN is configured to support both HTTP/1 and HTTP/1.1. + pub fn new() -> Result { + let mut ssl = SslConnector::builder(SslMethod::tls())?; + + ssl.set_alpn_protos(b"\x02h2\x08http/1.1")?; + + Self::with_connector(ssl) + } + + /// Creates a new `HttpsLayer`. + /// + /// The session cache configuration of `ssl` will be overwritten. + pub fn with_connector(ssl: SslConnectorBuilder) -> Result { + Self::with_connector_and_settings(ssl, Default::default()) + } + + /// Creates a new `HttpsLayer` with settings + pub fn with_connector_and_settings( + mut ssl: SslConnectorBuilder, + settings: HttpsLayerSettings, + ) -> Result { + let cache = Arc::new(Mutex::new(SessionCache::with_capacity( + settings.session_cache_capacity, + ))); + + ssl.set_session_cache_mode(SslSessionCacheMode::CLIENT); + + ssl.set_new_session_callback({ + let cache = cache.clone(); + move |ssl, session| { + if let Some(key) = key_index().ok().and_then(|idx| ssl.ex_data(idx)) { + cache.lock().insert(key.clone(), session); + } + } + }); + + Ok(HttpsLayer { + inner: Inner { + ssl: ssl.build(), + cache, + callback: None, + ssl_callback: None, + }, + }) + } + + /// Registers a callback which can customize the configuration of each connection. + /// + /// Unsuitable to change verify hostflags (with `config.param_mut().set_hostflags(…)`), + /// as they are reset after the callback is executed. Use [`Self::set_ssl_callback`] + /// instead. + pub fn set_callback(&mut self, callback: F) + where + F: Fn(&mut ConnectConfiguration, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, + { + self.inner.callback = Some(Arc::new(callback)); + } + + /// Registers a callback which can customize the `Ssl` of each connection. + pub fn set_ssl_callback(&mut self, callback: F) + where + F: Fn(&mut SslRef, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, + { + self.inner.ssl_callback = Some(Arc::new(callback)); + } +} + +impl Layer for HttpsLayer { + type Service = HttpsConnector; + + fn layer(&self, inner: S) -> HttpsConnector { + HttpsConnector { + http: inner, + inner: self.inner.clone(), + } + } +} + +impl Inner { + fn setup_ssl(&self, uri: &Uri, host: &str) -> Result { + let mut conf = self.ssl.configure()?; + + if let Some(ref callback) = self.callback { + callback(&mut conf, uri)?; + } + + let key = SessionKey { + host: host.to_string(), + port: uri.port_u16().unwrap_or(443), + }; + + if let Some(session) = self.cache.lock().get(&key) { + unsafe { + conf.set_session(&session)?; + } + } + + let idx = key_index()?; + conf.set_ex_data(idx, key); + + let mut ssl = conf.into_ssl(host)?; + + if let Some(ref ssl_callback) = self.ssl_callback { + ssl_callback(&mut ssl, uri)?; + } + + Ok(ssl) + } +} + +impl Service for HttpsConnector +where + S: Service + Send, + S::Error: Into>, + S::Future: Unpin + Send + 'static, + S::Response: AsyncRead + AsyncWrite + Connection + Unpin + fmt::Debug + Sync + Send + 'static, +{ + type Response = MaybeHttpsStream; + type Error = Box; + #[allow(clippy::type_complexity)] + type Future = Pin> + Send>>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.http.poll_ready(cx).map_err(Into::into) + } + + fn call(&mut self, uri: Uri) -> Self::Future { + let is_tls_scheme = uri + .scheme() + .map(|s| s == &Scheme::HTTPS || s.as_str() == "wss") + .unwrap_or(false); + + let tls_setup = if is_tls_scheme { + Some((self.inner.clone(), uri.clone())) + } else { + None + }; + + let connect = self.http.call(uri); + + let f = async { + let conn = connect.await.map_err(Into::into)?; + + let (inner, uri) = match tls_setup { + Some((inner, uri)) => (inner, uri), + None => return Ok(MaybeHttpsStream::Http(conn)), + }; + + let mut host = uri.host().ok_or("URI missing host")?; + + // If `host` is an IPv6 address, we must strip away the square brackets that surround + // it (otherwise, boring will fail to parse the host as an IP address, eventually + // causing the handshake to fail due a hostname verification error). + if !host.is_empty() { + let last = host.len() - 1; + let mut chars = host.chars(); + + if let (Some('['), Some(']')) = (chars.next(), chars.last()) { + if host[1..last].parse::().is_ok() { + host = &host[1..last]; + } + } + } + + let ssl = inner.setup_ssl(&uri, host)?; + let stream = tokio_boring::SslStreamBuilder::new(ssl, conn) + .connect() + .await?; + + Ok(MaybeHttpsStream::Https(stream)) + }; + + Box::pin(f) + } +} + +impl Connection for MaybeHttpsStream +where + T: Connection, +{ + fn connected(&self) -> Connected { + match self { + MaybeHttpsStream::Http(s) => s.connected(), + MaybeHttpsStream::Https(s) => { + let mut connected = s.get_ref().connected(); + + if s.ssl().selected_alpn_protocol() == Some(b"h2") { + connected = connected.negotiated_h2(); + } + + connected + } + } + } +} + +impl AsyncRead for MaybeHttpsStream +where + T: AsyncRead + AsyncWrite + Unpin, +{ + fn poll_read( + mut self: Pin<&mut Self>, + ctx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + match &mut *self { + MaybeHttpsStream::Http(s) => Pin::new(s).poll_read(ctx, buf), + MaybeHttpsStream::Https(s) => Pin::new(s).poll_read(ctx, buf), + } + } +} + +impl AsyncWrite for MaybeHttpsStream +where + T: AsyncRead + AsyncWrite + Unpin, +{ + fn poll_write( + mut self: Pin<&mut Self>, + ctx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + match &mut *self { + MaybeHttpsStream::Http(s) => Pin::new(s).poll_write(ctx, buf), + MaybeHttpsStream::Https(s) => Pin::new(s).poll_write(ctx, buf), + } + } + + fn poll_flush(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { + match &mut *self { + MaybeHttpsStream::Http(s) => Pin::new(s).poll_flush(ctx), + MaybeHttpsStream::Https(s) => Pin::new(s).poll_flush(ctx), + } + } + + fn poll_shutdown(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { + match &mut *self { + MaybeHttpsStream::Http(s) => Pin::new(s).poll_shutdown(ctx), + MaybeHttpsStream::Https(s) => Pin::new(s).poll_shutdown(ctx), + } + } +} diff --git a/hyper-boring/src/v1.rs b/hyper-boring/src/v1.rs new file mode 100644 index 00000000..e1f9a43d --- /dev/null +++ b/hyper-boring/src/v1.rs @@ -0,0 +1,350 @@ +use crate::cache::{SessionCache, SessionKey}; +use crate::{key_index, HttpsLayerSettings, MaybeHttpsStream}; +use antidote::Mutex; +use boring::error::ErrorStack; +use boring::ssl::{ + ConnectConfiguration, Ssl, SslConnector, SslConnectorBuilder, SslMethod, SslRef, + SslSessionCacheMode, +}; +use http::uri::Scheme; +use http::Uri; +use hyper::rt::{Read, ReadBufCursor, Write}; +use hyper_util::client::legacy::connect::{Connected, Connection, HttpConnector}; +use hyper_util::rt::TokioIo; +use std::error::Error; +use std::fmt; +use std::future::Future; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; +use std::{io, net}; +use tokio::io::{AsyncRead, AsyncWrite}; +use tower_layer::Layer; +use tower_service::Service; + +/// A Connector using BoringSSL to support `http` and `https` schemes. +#[derive(Clone)] +pub struct HttpsConnector { + http: T, + inner: Inner, +} + +#[cfg(feature = "runtime")] +impl HttpsConnector { + /// Creates a a new `HttpsConnector` using default settings. + /// + /// The Hyper `HttpConnector` is used to perform the TCP socket connection. ALPN is configured to support both + /// HTTP/2 and HTTP/1.1. + /// + /// Requires the `runtime` Cargo feature. + pub fn new() -> Result, ErrorStack> { + let mut http = HttpConnector::new(); + http.enforce_http(false); + + HttpsLayer::new().map(|l| l.layer(http)) + } +} + +impl HttpsConnector +where + S: Service> + Send, + S::Error: Into>, + S::Future: Unpin + Send + 'static, + T: AsyncRead + AsyncWrite + Connection + Unpin + fmt::Debug + Sync + Send + 'static, +{ + /// Creates a new `HttpsConnector`. + /// + /// The session cache configuration of `ssl` will be overwritten. + pub fn with_connector( + http: S, + ssl: SslConnectorBuilder, + ) -> Result, ErrorStack> { + HttpsLayer::with_connector(ssl).map(|l| l.layer(http)) + } + + /// Registers a callback which can customize the configuration of each connection. + /// + /// Unsuitable to change verify hostflags (with `config.param_mut().set_hostflags(…)`), + /// as they are reset after the callback is executed. Use [`Self::set_ssl_callback`] + /// instead. + pub fn set_callback(&mut self, callback: F) + where + F: Fn(&mut ConnectConfiguration, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, + { + self.inner.callback = Some(Arc::new(callback)); + } + + /// Registers a callback which can customize the `Ssl` of each connection. + pub fn set_ssl_callback(&mut self, callback: F) + where + F: Fn(&mut SslRef, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, + { + self.inner.ssl_callback = Some(Arc::new(callback)); + } +} + +/// A layer which wraps services in an `HttpsConnector`. +pub struct HttpsLayer { + inner: Inner, +} + +#[derive(Clone)] +struct Inner { + ssl: SslConnector, + cache: Arc>, + callback: Option, + ssl_callback: Option, +} + +type Callback = + Arc Result<(), ErrorStack> + Sync + Send>; +type SslCallback = Arc Result<(), ErrorStack> + Sync + Send>; + +impl HttpsLayer { + /// Creates a new `HttpsLayer` with default settings. + /// + /// ALPN is configured to support both HTTP/1 and HTTP/1.1. + pub fn new() -> Result { + let mut ssl = SslConnector::builder(SslMethod::tls())?; + + ssl.set_alpn_protos(b"\x02h2\x08http/1.1")?; + + Self::with_connector(ssl) + } + + /// Creates a new `HttpsLayer`. + /// + /// The session cache configuration of `ssl` will be overwritten. + pub fn with_connector(ssl: SslConnectorBuilder) -> Result { + Self::with_connector_and_settings(ssl, Default::default()) + } + + /// Creates a new `HttpsLayer` with settings + pub fn with_connector_and_settings( + mut ssl: SslConnectorBuilder, + settings: HttpsLayerSettings, + ) -> Result { + let cache = Arc::new(Mutex::new(SessionCache::with_capacity( + settings.session_cache_capacity, + ))); + + ssl.set_session_cache_mode(SslSessionCacheMode::CLIENT); + + ssl.set_new_session_callback({ + let cache = cache.clone(); + move |ssl, session| { + if let Some(key) = key_index().ok().and_then(|idx| ssl.ex_data(idx)) { + cache.lock().insert(key.clone(), session); + } + } + }); + + Ok(HttpsLayer { + inner: Inner { + ssl: ssl.build(), + cache, + callback: None, + ssl_callback: None, + }, + }) + } + + /// Registers a callback which can customize the configuration of each connection. + /// + /// Unsuitable to change verify hostflags (with `config.param_mut().set_hostflags(…)`), + /// as they are reset after the callback is executed. Use [`Self::set_ssl_callback`] + /// instead. + pub fn set_callback(&mut self, callback: F) + where + F: Fn(&mut ConnectConfiguration, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, + { + self.inner.callback = Some(Arc::new(callback)); + } + + /// Registers a callback which can customize the `Ssl` of each connection. + pub fn set_ssl_callback(&mut self, callback: F) + where + F: Fn(&mut SslRef, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, + { + self.inner.ssl_callback = Some(Arc::new(callback)); + } +} + +impl Layer for HttpsLayer { + type Service = HttpsConnector; + + fn layer(&self, inner: S) -> HttpsConnector { + HttpsConnector { + http: inner, + inner: self.inner.clone(), + } + } +} + +impl Inner { + fn setup_ssl(&self, uri: &Uri, host: &str) -> Result { + let mut conf = self.ssl.configure()?; + + if let Some(ref callback) = self.callback { + callback(&mut conf, uri)?; + } + + let key = SessionKey { + host: host.to_string(), + port: uri.port_u16().unwrap_or(443), + }; + + if let Some(session) = self.cache.lock().get(&key) { + unsafe { + conf.set_session(&session)?; + } + } + + let idx = key_index()?; + conf.set_ex_data(idx, key); + + let mut ssl = conf.into_ssl(host)?; + + if let Some(ref ssl_callback) = self.ssl_callback { + ssl_callback(&mut ssl, uri)?; + } + + Ok(ssl) + } +} + +impl Service for HttpsConnector +where + S: Service> + Send, + S::Error: Into>, + S::Future: Unpin + Send + 'static, + T: AsyncRead + AsyncWrite + Connection + Unpin + fmt::Debug + Sync + Send + 'static, +{ + type Response = MaybeHttpsStream; + type Error = Box; + #[allow(clippy::type_complexity)] + type Future = Pin> + Send>>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.http.poll_ready(cx).map_err(Into::into) + } + + fn call(&mut self, uri: Uri) -> Self::Future { + let is_tls_scheme = uri + .scheme() + .map(|s| s == &Scheme::HTTPS || s.as_str() == "wss") + .unwrap_or(false); + + let tls_setup = if is_tls_scheme { + Some((self.inner.clone(), uri.clone())) + } else { + None + }; + + let connect = self.http.call(uri); + + let f = async { + let conn = connect.await.map_err(Into::into)?.into_inner(); + + let (inner, uri) = match tls_setup { + Some((inner, uri)) => (inner, uri), + None => return Ok(MaybeHttpsStream::Http(conn)), + }; + + let mut host = uri.host().ok_or("URI missing host")?; + + // If `host` is an IPv6 address, we must strip away the square brackets that surround + // it (otherwise, boring will fail to parse the host as an IP address, eventually + // causing the handshake to fail due a hostname verification error). + if !host.is_empty() { + let last = host.len() - 1; + let mut chars = host.chars(); + + if let (Some('['), Some(']')) = (chars.next(), chars.last()) { + if host[1..last].parse::().is_ok() { + host = &host[1..last]; + } + } + } + + let ssl = inner.setup_ssl(&uri, host)?; + let stream = tokio_boring::SslStreamBuilder::new(ssl, conn) + .connect() + .await?; + + Ok(MaybeHttpsStream::Https(stream)) + }; + + Box::pin(f) + } +} + +impl Connection for MaybeHttpsStream +where + T: Connection, +{ + fn connected(&self) -> Connected { + match self { + MaybeHttpsStream::Http(s) => s.connected(), + MaybeHttpsStream::Https(s) => { + let mut connected = s.get_ref().connected(); + + if s.ssl().selected_alpn_protocol() == Some(b"h2") { + connected = connected.negotiated_h2(); + } + + connected + } + } + } +} + +impl Read for MaybeHttpsStream +where + T: AsyncRead + AsyncWrite + Unpin, +{ + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: ReadBufCursor<'_>, + ) -> Poll> { + match &mut *self { + MaybeHttpsStream::Http(inner) => Pin::new(&mut TokioIo::new(inner)).poll_read(cx, buf), + MaybeHttpsStream::Https(inner) => Pin::new(&mut TokioIo::new(inner)).poll_read(cx, buf), + } + } +} + +impl Write for MaybeHttpsStream +where + T: AsyncRead + AsyncWrite + Unpin, +{ + fn poll_write( + mut self: Pin<&mut Self>, + ctx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + match &mut *self { + MaybeHttpsStream::Http(inner) => { + Pin::new(&mut TokioIo::new(inner)).poll_write(ctx, buf) + } + MaybeHttpsStream::Https(inner) => { + Pin::new(&mut TokioIo::new(inner)).poll_write(ctx, buf) + } + } + } + + fn poll_flush(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { + match &mut *self { + MaybeHttpsStream::Http(inner) => Pin::new(&mut TokioIo::new(inner)).poll_flush(ctx), + MaybeHttpsStream::Https(inner) => Pin::new(&mut TokioIo::new(inner)).poll_flush(ctx), + } + } + + fn poll_shutdown(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { + match &mut *self { + MaybeHttpsStream::Http(inner) => Pin::new(&mut TokioIo::new(inner)).poll_shutdown(ctx), + MaybeHttpsStream::Https(inner) => Pin::new(&mut TokioIo::new(inner)).poll_shutdown(ctx), + } + } +} diff --git a/hyper-boring/test/cert.pem b/hyper-boring/tests/test/cert.pem similarity index 100% rename from hyper-boring/test/cert.pem rename to hyper-boring/tests/test/cert.pem diff --git a/hyper-boring/test/key.pem b/hyper-boring/tests/test/key.pem similarity index 100% rename from hyper-boring/test/key.pem rename to hyper-boring/tests/test/key.pem diff --git a/hyper-boring/test/root-ca.pem b/hyper-boring/tests/test/root-ca.pem similarity index 100% rename from hyper-boring/test/root-ca.pem rename to hyper-boring/tests/test/root-ca.pem diff --git a/hyper-boring/src/test.rs b/hyper-boring/tests/v0.rs similarity index 87% rename from hyper-boring/src/test.rs rename to hyper-boring/tests/v0.rs index 006d5163..08cfce12 100644 --- a/hyper-boring/src/test.rs +++ b/hyper-boring/tests/v0.rs @@ -1,12 +1,12 @@ -use super::*; -use boring::ssl::{SslAcceptor, SslFiletype, SslMethod}; +use boring::ssl::{SslAcceptor, SslConnector, SslFiletype, SslMethod}; use futures::StreamExt; -use hyper::client::HttpConnector; -use hyper::server::conn::Http; -use hyper::{service, Response}; -use hyper::{Body, Client}; +use hyper_boring::HttpsConnector; +use hyper_old::client::HttpConnector; +use hyper_old::server::conn::Http; +use hyper_old::{service, Response}; +use hyper_old::{Body, Client}; use std::convert::Infallible; -use std::iter; +use std::{io, iter}; use tokio::net::TcpListener; #[tokio::test] @@ -37,10 +37,10 @@ async fn localhost() { let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); acceptor.set_session_id_context(b"test").unwrap(); acceptor - .set_private_key_file("test/key.pem", SslFiletype::PEM) + .set_private_key_file("tests/test/key.pem", SslFiletype::PEM) .unwrap(); acceptor - .set_certificate_chain_file("test/cert.pem") + .set_certificate_chain_file("tests/test/cert.pem") .unwrap(); let acceptor = acceptor.build(); @@ -69,7 +69,7 @@ async fn localhost() { let mut ssl = SslConnector::builder(SslMethod::tls()).unwrap(); - ssl.set_ca_file("test/root-ca.pem").unwrap(); + ssl.set_ca_file("tests/test/root-ca.pem").unwrap(); use std::fs::File; use std::io::Write; @@ -104,10 +104,10 @@ async fn alpn_h2() { let server = async move { let mut acceptor = SslAcceptor::mozilla_modern(SslMethod::tls()).unwrap(); acceptor - .set_certificate_chain_file("test/cert.pem") + .set_certificate_chain_file("tests/test/cert.pem") .unwrap(); acceptor - .set_private_key_file("test/key.pem", SslFiletype::PEM) + .set_private_key_file("tests/test/key.pem", SslFiletype::PEM) .unwrap(); acceptor.set_alpn_select_callback(|_, client| { ssl::select_next_proto(b"\x02h2", client).ok_or(AlpnError::NOACK) @@ -138,7 +138,7 @@ async fn alpn_h2() { let mut ssl = SslConnector::builder(SslMethod::tls()).unwrap(); - ssl.set_ca_file("test/root-ca.pem").unwrap(); + ssl.set_ca_file("tests/test/root-ca.pem").unwrap(); let mut ssl = HttpsConnector::with_connector(connector, ssl).unwrap(); diff --git a/hyper-boring/tests/v1.rs b/hyper-boring/tests/v1.rs new file mode 100644 index 00000000..441caea6 --- /dev/null +++ b/hyper-boring/tests/v1.rs @@ -0,0 +1,160 @@ +#![cfg(feature = "hyper1")] + +use boring::ssl::{SslAcceptor, SslConnector, SslFiletype, SslMethod}; +use bytes::Bytes; +use futures::StreamExt; +use http_body_util::{BodyStream, Empty}; +use hyper::{service, Response}; +use hyper_boring::v1::HttpsConnector; +use hyper_util::client::legacy::connect::HttpConnector; +use hyper_util::client::legacy::Client; +use hyper_util::rt::{TokioExecutor, TokioIo}; +use std::convert::Infallible; +use std::{io, iter}; +use tokio::net::TcpListener; + +#[tokio::test] +async fn google() { + let ssl = HttpsConnector::new().unwrap(); + let client = Client::builder(TokioExecutor::new()) + .pool_max_idle_per_host(0) + .build::<_, Empty>(ssl); + + for _ in 0..3 { + let resp = client + .get("https://www.google.com".parse().unwrap()) + .await + .expect("connection should succeed"); + let mut body = BodyStream::new(resp.into_body()); + while body.next().await.transpose().unwrap().is_some() {} + } +} + +#[tokio::test] +async fn localhost() { + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let port = addr.port(); + + let server = async move { + let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); + acceptor.set_session_id_context(b"test").unwrap(); + acceptor + .set_private_key_file("tests/test/key.pem", SslFiletype::PEM) + .unwrap(); + acceptor + .set_certificate_chain_file("tests/test/cert.pem") + .unwrap(); + let acceptor = acceptor.build(); + + for _ in 0..3 { + let stream = listener.accept().await.unwrap().0; + let stream = tokio_boring::accept(&acceptor, stream).await.unwrap(); + + let service = service::service_fn(|_| async { + Ok::<_, io::Error>(Response::new(>::new())) + }); + + hyper::server::conn::http1::Builder::new() + .keep_alive(false) + .serve_connection(TokioIo::new(stream), service) + .await + .unwrap(); + } + }; + tokio::spawn(server); + + let resolver = + tower::service_fn(move |_name| async move { Ok::<_, Infallible>(iter::once(addr)) }); + + let mut connector = HttpConnector::new_with_resolver(resolver); + + connector.enforce_http(false); + + let mut ssl = SslConnector::builder(SslMethod::tls()).unwrap(); + + ssl.set_ca_file("tests/test/root-ca.pem").unwrap(); + + use std::fs::File; + use std::io::Write; + + let file = File::create("../target/keyfile.log").unwrap(); + ssl.set_keylog_callback(move |_, line| { + let _ = writeln!(&file, "{}", line); + }); + + let ssl = HttpsConnector::with_connector(connector, ssl).unwrap(); + let client = Client::builder(TokioExecutor::new()).build::<_, Empty>(ssl); + + for _ in 0..3 { + let resp = client + .get(format!("https://foobar.com:{}", port).parse().unwrap()) + .await + .unwrap(); + assert!(resp.status().is_success(), "{}", resp.status()); + let mut body = BodyStream::new(resp.into_body()); + while body.next().await.transpose().unwrap().is_some() {} + } +} + +#[tokio::test] +async fn alpn_h2() { + use boring::ssl::{self, AlpnError}; + + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let port = addr.port(); + + let server = async move { + let mut acceptor = SslAcceptor::mozilla_modern(SslMethod::tls()).unwrap(); + acceptor + .set_certificate_chain_file("tests/test/cert.pem") + .unwrap(); + acceptor + .set_private_key_file("tests/test/key.pem", SslFiletype::PEM) + .unwrap(); + acceptor.set_alpn_select_callback(|_, client| { + ssl::select_next_proto(b"\x02h2", client).ok_or(AlpnError::NOACK) + }); + let acceptor = acceptor.build(); + + let stream = listener.accept().await.unwrap().0; + let stream = tokio_boring::accept(&acceptor, stream).await.unwrap(); + assert_eq!(stream.ssl().selected_alpn_protocol().unwrap(), b"h2"); + + let service = service::service_fn(|_| async { + Ok::<_, io::Error>(Response::new(>::new())) + }); + + hyper::server::conn::http2::Builder::new(TokioExecutor::new()) + .serve_connection(TokioIo::new(stream), service) + .await + .unwrap(); + }; + tokio::spawn(server); + + let resolver = + tower::service_fn(move |_name| async move { Ok::<_, Infallible>(iter::once(addr)) }); + + let mut connector = HttpConnector::new_with_resolver(resolver); + + connector.enforce_http(false); + + let mut ssl = SslConnector::builder(SslMethod::tls()).unwrap(); + + ssl.set_ca_file("tests/test/root-ca.pem").unwrap(); + + let mut ssl = HttpsConnector::with_connector(connector, ssl).unwrap(); + + ssl.set_ssl_callback(|ssl, _| ssl.set_alpn_protos(b"\x02h2\x08http/1.1")); + + let client = Client::builder(TokioExecutor::new()).build::<_, Empty>(ssl); + + let resp = client + .get(format!("https://foobar.com:{}", port).parse().unwrap()) + .await + .unwrap(); + assert!(resp.status().is_success(), "{}", resp.status()); + let mut body = BodyStream::new(resp.into_body()); + while body.next().await.transpose().unwrap().is_some() {} +} From b7f47dec46c8828c15953ab016cbf0c25fcdae76 Mon Sep 17 00:00:00 2001 From: Rushil Mehra <84047965+rushilmehra@users.noreply.github.com> Date: Wed, 18 Sep 2024 14:20:28 +0200 Subject: [PATCH 19/41] Release 4.10.0 (#274) --- Cargo.toml | 8 ++++---- RELEASE_NOTES | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a067f74f..137ceb8b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ resolver = "2" [workspace.package] -version = "4.9.1" +version = "4.10.0" repository = "https://github.com/cloudflare/boring" edition = "2021" @@ -19,9 +19,9 @@ tag-prefix = "" publish = false [workspace.dependencies] -boring-sys = { version = "4.9.1", path = "./boring-sys" } -boring = { version = "4.9.1", path = "./boring" } -tokio-boring = { version = "4.9.1", path = "./tokio-boring" } +boring-sys = { version = "4.10.0", path = "./boring-sys" } +boring = { version = "4.10.0", path = "./boring" } +tokio-boring = { version = "4.10.0", path = "./tokio-boring" } bindgen = { version = "0.70.1", default-features = false, features = ["runtime"] } bytes = "1" diff --git a/RELEASE_NOTES b/RELEASE_NOTES index ab943c5f..529d20e1 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,19 @@ +4.10.0 +- 2024-09-18 Implement optional Hyper 1 support in hyper-boring (#246) +- 2024-09-17 Add post-quantum key agreement X25519MLKEM768 +- 2024-09-10 Revert "PQ: fix timing sidechannels and add IPDWing" +- 2024-09-17 Update bindgen to 0.70.1 +- 2024-09-17 Expose SSL(_CTX)_set1_curves_list (#270) +- 2024-09-11 Expose SSL_CTX_set_info_callback (#266) +- 2024-09-03 Use ForeignType::into_ptr wherever applicable +- 2024-08-19 Expose RSAPSS public key Id type +- 2024-08-15 Fix macos FIPS crossbuild +- 2024-08-15 Add tests for X509Ref::subject_key_id, X509Ref::authority_key_id, and X509NameRef::print_ex +- 2024-08-14 Expose X509NameRef::print_ex +- 2024-08-13 Introduce `corresponds` macro from openssl-macros +- 2024-08-14 Introduce ForeignTypeExt and ForeignTypeRefExt +- 2024-08-09 Expose mTLS related APIs +- 2024-08-14 chore(boring-sys): Fix git apply patch on Windows (#261) 4.9.1 - 2024-08-04 Properly handle `Option` in `SslRef::set_curves` From b7eaa5bc95764c670655a00e5f41957409821f81 Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Wed, 18 Sep 2024 22:22:16 +0200 Subject: [PATCH 20/41] Don't support X25519MLKEM768 by default (yet) --- boring/src/ssl/mod.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/boring/src/ssl/mod.rs b/boring/src/ssl/mod.rs index 9bc27bc3..9bd1fdec 100644 --- a/boring/src/ssl/mod.rs +++ b/boring/src/ssl/mod.rs @@ -2693,13 +2693,13 @@ impl SslRef { if cfg!(feature = "kx-client-nist-required") { "P256Kyber768Draft00:P-256:P-384:P-521" } else { - "X25519Kyber768Draft00:X25519MLKEM768:X25519:P256Kyber768Draft00:P-256:P-384:P-521" + "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" + "X25519:P-256:P-384:P-521:X25519Kyber768Draft00:P256Kyber768Draft00" } } else { if cfg!(feature = "kx-client-nist-required") { @@ -2715,10 +2715,8 @@ impl SslRef { #[cfg(feature = "kx-safe-default")] fn server_set_default_curves_list(&mut self) { - self.set_curves_list( - "X25519Kyber768Draft00:X25519MLKEM768:P256Kyber768Draft00:X25519:P-256:P-384", - ) - .expect("invalid default server curves list"); + self.set_curves_list("X25519Kyber768Draft00:P256Kyber768Draft00:X25519:P-256:P-384") + .expect("invalid default server curves list"); } /// Returns the [`SslCurve`] used for this `SslRef`. From df1c4f55e6b79fce4e283e35a54cafbf594ca5ba Mon Sep 17 00:00:00 2001 From: Rushil Mehra <84047965+rushilmehra@users.noreply.github.com> Date: Wed, 18 Sep 2024 22:53:02 +0200 Subject: [PATCH 21/41] Release 4.10.1 (#276) --- Cargo.toml | 8 ++++---- RELEASE_NOTES | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 137ceb8b..d5f2002d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ resolver = "2" [workspace.package] -version = "4.10.0" +version = "4.10.1" repository = "https://github.com/cloudflare/boring" edition = "2021" @@ -19,9 +19,9 @@ tag-prefix = "" publish = false [workspace.dependencies] -boring-sys = { version = "4.10.0", path = "./boring-sys" } -boring = { version = "4.10.0", path = "./boring" } -tokio-boring = { version = "4.10.0", path = "./tokio-boring" } +boring-sys = { version = "4.10.1", path = "./boring-sys" } +boring = { version = "4.10.1", path = "./boring" } +tokio-boring = { version = "4.10.1", path = "./tokio-boring" } bindgen = { version = "0.70.1", default-features = false, features = ["runtime"] } bytes = "1" diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 529d20e1..edd13900 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,6 @@ +4.10.1 +- 2024-09-18 Don't support X25519MLKEM768 by default (yet) + 4.10.0 - 2024-09-18 Implement optional Hyper 1 support in hyper-boring (#246) - 2024-09-17 Add post-quantum key agreement X25519MLKEM768 From b1a7434c1917d36e09aee8496cb0c42fc42d7c3b Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Thu, 19 Sep 2024 00:04:04 +0200 Subject: [PATCH 22/41] boring-pq.patch Fix by not updating crypto_test_data.cc crypto_test_data.cc is not included anymore since 2475ef386bd6158151f5ad5c4cf697d02ceca2b0 --- boring-sys/patches/boring-pq.patch | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/boring-sys/patches/boring-pq.patch b/boring-sys/patches/boring-pq.patch index e6601d91..38488004 100644 --- a/boring-sys/patches/boring-pq.patch +++ b/boring-sys/patches/boring-pq.patch @@ -35,7 +35,6 @@ Cf RTG-2076 RTG-2051 RTG-2508 RTG-2707 RTG-2607 RTG-3239 BUILD.generated.bzl | 5 +- BUILD.generated_tests.bzl | 4 - CMakeLists.txt | 4 +- - crypto_test_data.cc | 4 - sources.json | 9 +- src/crypto/CMakeLists.txt | 5 +- src/crypto/kyber/internal.h | 91 - @@ -134,28 +133,6 @@ index faed2befa..931c0e3a8 100644 src/crypto/lhash/lhash.c src/crypto/mem.c src/crypto/obj/obj.c -diff --git a/crypto_test_data.cc b/crypto_test_data.cc -index 2268533f8..19b344af1 100644 ---- a/crypto_test_data.cc -+++ b/crypto_test_data.cc -@@ -74,7 +74,6 @@ - * crypto/fipsmodule/rand/ctrdrbg_vectors.txt \ - * crypto/hmac_extra/hmac_tests.txt \ - * crypto/hpke/hpke_test_vectors.txt \ -- * crypto/kyber/keccak_tests.txt \ - * crypto/kyber/kyber_tests.txt \ - * crypto/pkcs8/test/empty_password.p12 \ - * crypto/pkcs8/test/no_encryption.p12 \ -@@ -5269,9 +5268,6 @@ std::string GetTestData(const char *path) { - if (strcmp(path, "crypto/hpke/hpke_test_vectors.txt") == 0) { - return AssembleString(kData59, kLen59); - } -- if (strcmp(path, "crypto/kyber/keccak_tests.txt") == 0) { -- return AssembleString(kData60, kLen60); -- } - if (strcmp(path, "crypto/kyber/kyber_tests.txt") == 0) { - return AssembleString(kData61, kLen61); - } diff --git a/sources.json b/sources.json index 4c0048e1d..f6ea5c40f 100644 --- a/sources.json From 1a00540c165c5296061e01d775d7488776b7ee5f Mon Sep 17 00:00:00 2001 From: Rushil Mehra <84047965+rushilmehra@users.noreply.github.com> Date: Thu, 19 Sep 2024 00:38:25 +0200 Subject: [PATCH 23/41] Release 4.10.2 (#278) --- Cargo.toml | 8 ++++---- RELEASE_NOTES | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d5f2002d..ffe403b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ resolver = "2" [workspace.package] -version = "4.10.1" +version = "4.10.2" repository = "https://github.com/cloudflare/boring" edition = "2021" @@ -19,9 +19,9 @@ tag-prefix = "" publish = false [workspace.dependencies] -boring-sys = { version = "4.10.1", path = "./boring-sys" } -boring = { version = "4.10.1", path = "./boring" } -tokio-boring = { version = "4.10.1", path = "./tokio-boring" } +boring-sys = { version = "4.10.2", path = "./boring-sys" } +boring = { version = "4.10.2", path = "./boring" } +tokio-boring = { version = "4.10.2", path = "./tokio-boring" } bindgen = { version = "0.70.1", default-features = false, features = ["runtime"] } bytes = "1" diff --git a/RELEASE_NOTES b/RELEASE_NOTES index edd13900..f1531f74 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,6 @@ +4.10.2 +- 2024-09-18 boring-pq.patch Fix by not updating crypto_test_data.cc + 4.10.1 - 2024-09-18 Don't support X25519MLKEM768 by default (yet) From 0596b2dcab5c776cba60e8b28b215545fa6ff9aa Mon Sep 17 00:00:00 2001 From: Yuchen Wu Date: Sat, 21 Sep 2024 12:42:39 -0700 Subject: [PATCH 24/41] Set MSRV to 1.70 (#279) With the bindgen 0.70 upgrade, the default rust target is set to be 1.77, which becomes the de facto MSRV of boring-sys since the change. This change makes sure that the MSRV of boring-sys is kept at 1.70, which is the same as that of bindgen. --- boring-sys/build/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/boring-sys/build/main.rs b/boring-sys/build/main.rs index 05242028..9b65492a 100644 --- a/boring-sys/build/main.rs +++ b/boring-sys/build/main.rs @@ -688,6 +688,7 @@ fn main() { }); let mut builder = bindgen::Builder::default() + .rust_target(bindgen::RustTarget::Stable_1_68) // bindgen MSRV is 1.70, so this is enough .derive_copy(true) .derive_debug(true) .derive_default(true) From 294796f7f6b84f9a86aaf37c4ac9055ea6a405c5 Mon Sep 17 00:00:00 2001 From: Rushil Mehra <84047965+rushilmehra@users.noreply.github.com> Date: Mon, 23 Sep 2024 10:06:51 +0200 Subject: [PATCH 25/41] Release 4.10.3 (#280) --- Cargo.toml | 8 ++++---- RELEASE_NOTES | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ffe403b2..1581b939 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ resolver = "2" [workspace.package] -version = "4.10.2" +version = "4.10.3" repository = "https://github.com/cloudflare/boring" edition = "2021" @@ -19,9 +19,9 @@ tag-prefix = "" publish = false [workspace.dependencies] -boring-sys = { version = "4.10.2", path = "./boring-sys" } -boring = { version = "4.10.2", path = "./boring" } -tokio-boring = { version = "4.10.2", path = "./tokio-boring" } +boring-sys = { version = "4.10.3", path = "./boring-sys" } +boring = { version = "4.10.3", path = "./boring" } +tokio-boring = { version = "4.10.3", path = "./tokio-boring" } bindgen = { version = "0.70.1", default-features = false, features = ["runtime"] } bytes = "1" diff --git a/RELEASE_NOTES b/RELEASE_NOTES index f1531f74..8e36ab05 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,6 @@ +4.10.3 +- 2024-09-21 Set MSRV to 1.70 (#279) + 4.10.2 - 2024-09-18 boring-pq.patch Fix by not updating crypto_test_data.cc From d3177a4300224867f06f59c31b1c928812cf946f Mon Sep 17 00:00:00 2001 From: Hrushikesh Deshpande <161167942+hrushikeshdeshpande@users.noreply.github.com> Date: Tue, 24 Sep 2024 22:48:19 -0400 Subject: [PATCH 26/41] Create semgrep.yml Creating Semgrep.yml file - Semgrep is a tool that will be used to scan Cloudflare's public repos for Supply chain, code and secrets. This work is part of Application & Product Security team's initiative to onboard Semgrep onto all of Cloudflare's public repos. In case of any questions, please reach out to "Hrushikesh Deshpande" on cf internal chat. --- .github/workflows/semgrep.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/semgrep.yml diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml new file mode 100644 index 00000000..d98c3fbd --- /dev/null +++ b/.github/workflows/semgrep.yml @@ -0,0 +1,23 @@ +on: + pull_request: {} + workflow_dispatch: {} + push: + branches: + - master + schedule: + - cron: "0 0 * * *" +name: Semgrep config +jobs: + semgrep: + name: semgrep/ci + runs-on: ubuntu-latest + env: + SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} + SEMGREP_URL: https://cloudflare.semgrep.dev + SEMGREP_APP_URL: https://cloudflare.semgrep.dev + SEMGREP_VERSION_CHECK_URL: https://cloudflare.semgrep.dev/api/check-version + container: + image: semgrep/semgrep + steps: + - uses: actions/checkout@v4 + - run: semgrep ci From a57cb3a9979580fd6ccc7f5ef48b5b7f6d23db9d Mon Sep 17 00:00:00 2001 From: Alessandro Ghedini Date: Thu, 17 Oct 2024 17:58:02 +0100 Subject: [PATCH 27/41] Add "fips-compat" feature (#286) This adds a feature to build against a BoringSSL version compatible with the current boringssl-fips, but _without_ actually enabling the `fips` feature. This can be useful to use with `fips-link-precompiled` while using a custom BoringSSL version based on the older FIPS branch. --- boring/Cargo.toml | 7 ++++++- boring/src/bio.rs | 4 ++-- boring/src/ssl/mod.rs | 18 +++++++++--------- boring/src/x509/mod.rs | 4 ++-- boring/src/x509/tests/trusted_first.rs | 4 ++-- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/boring/Cargo.toml b/boring/Cargo.toml index a558eefa..19e79432 100644 --- a/boring/Cargo.toml +++ b/boring/Cargo.toml @@ -19,7 +19,12 @@ rustdoc-args = ["--cfg", "docsrs"] # Controlling the build # Use a FIPS-validated version of boringssl. -fips = ["boring-sys/fips"] +fips = ["fips-compat", "boring-sys/fips"] + +# Build with compatibility for the BoringSSL FIPS version, without enabling the +# `fips` feature itself (useful e.g. if `fips-link-precompiled` is used with an +# older BoringSSL version). +fips-compat = [] # Link with precompiled FIPS-validated `bcm.o` module. fips-link-precompiled = ["boring-sys/fips-link-precompiled"] diff --git a/boring/src/bio.rs b/boring/src/bio.rs index b6d52e09..a0c882a2 100644 --- a/boring/src/bio.rs +++ b/boring/src/bio.rs @@ -19,9 +19,9 @@ impl<'a> Drop for MemBioSlice<'a> { impl<'a> MemBioSlice<'a> { pub fn new(buf: &'a [u8]) -> Result, ErrorStack> { - #[cfg(not(feature = "fips"))] + #[cfg(not(feature = "fips-compat"))] type BufLen = isize; - #[cfg(feature = "fips")] + #[cfg(feature = "fips-compat")] type BufLen = libc::c_int; ffi::init(); diff --git a/boring/src/ssl/mod.rs b/boring/src/ssl/mod.rs index 9bd1fdec..64fcf8bb 100644 --- a/boring/src/ssl/mod.rs +++ b/boring/src/ssl/mod.rs @@ -774,10 +774,10 @@ impl SslCurve { /// A compliance policy. #[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg(not(feature = "fips"))] +#[cfg(not(feature = "fips-compat"))] pub struct CompliancePolicy(ffi::ssl_compliance_policy_t); -#[cfg(not(feature = "fips"))] +#[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. pub const NONE: Self = Self(ffi::ssl_compliance_policy_t::ssl_compliance_policy_none); @@ -1469,7 +1469,7 @@ 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"), allow(clippy::unnecessary_cast))] + #[cfg_attr(not(feature = "fips-compat"), allow(clippy::unnecessary_cast))] { assert!(protocols.len() <= ProtosLen::MAX as usize); } @@ -1813,7 +1813,7 @@ impl SslContextBuilder { /// 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"))] + #[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 _) } } @@ -1888,7 +1888,7 @@ impl SslContextBuilder { /// /// This feature isn't available in the certified version of BoringSSL. #[corresponds(SSL_CTX_set_compliance_policy)] - #[cfg(not(feature = "fips"))] + #[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(|_| ()) } } @@ -2160,9 +2160,9 @@ impl SslContextRef { #[derive(Debug)] pub struct GetSessionPendingError; -#[cfg(not(feature = "fips"))] +#[cfg(not(feature = "fips-compat"))] type ProtosLen = usize; -#[cfg(feature = "fips")] +#[cfg(feature = "fips-compat")] type ProtosLen = libc::c_uint; /// Information about the state of a cipher. @@ -2883,7 +2883,7 @@ impl SslRef { /// 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"))] + #[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 _) } } @@ -2894,7 +2894,7 @@ impl SslRef { #[corresponds(SSL_set_alpn_protos)] pub fn set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> { unsafe { - #[cfg_attr(not(feature = "fips"), allow(clippy::unnecessary_cast))] + #[cfg_attr(not(feature = "fips-compat"), allow(clippy::unnecessary_cast))] { assert!(protocols.len() <= ProtosLen::MAX as usize); } diff --git a/boring/src/x509/mod.rs b/boring/src/x509/mod.rs index 34896d6a..af1d5040 100644 --- a/boring/src/x509/mod.rs +++ b/boring/src/x509/mod.rs @@ -981,9 +981,9 @@ impl X509NameBuilder { } } -#[cfg(not(feature = "fips"))] +#[cfg(not(feature = "fips-compat"))] type ValueLen = isize; -#[cfg(feature = "fips")] +#[cfg(feature = "fips-compat")] type ValueLen = i32; foreign_type_and_impl_send_sync! { diff --git a/boring/src/x509/tests/trusted_first.rs b/boring/src/x509/tests/trusted_first.rs index 951d1da5..9823072f 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"))] + #[cfg(not(feature = "fips-compat"))] assert_eq!( Ok(()), verify( @@ -26,7 +26,7 @@ fn test_verify_cert() { ) ); - #[cfg(feature = "fips")] + #[cfg(feature = "fips-compat")] assert_eq!( Err(X509VerifyError::CERT_HAS_EXPIRED), verify( From 80e3aab37dccc2eb0ab58f1c65a41212680852f4 Mon Sep 17 00:00:00 2001 From: Evan Rittenhouse Date: Wed, 16 Oct 2024 20:18:54 -0500 Subject: [PATCH 28/41] boring-sys: include HPKE header file for bindgen BoringSSL doesn't expose these APIs for FIPs builds, so we gate them here as well --- boring-sys/build/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/boring-sys/build/main.rs b/boring-sys/build/main.rs index 9b65492a..7966e215 100644 --- a/boring-sys/build/main.rs +++ b/boring-sys/build/main.rs @@ -728,6 +728,8 @@ fn main() { "des.h", "dtls1.h", "hkdf.h", + #[cfg(not(feature = "fips"))] + "hpke.h", "hmac.h", "hrss.h", "md4.h", From ec3b412cc8063ec122db5924bebc2b427c3779ac Mon Sep 17 00:00:00 2001 From: Evan Rittenhouse Date: Wed, 16 Oct 2024 20:40:13 -0500 Subject: [PATCH 29/41] Release 4.11.0 --- Cargo.toml | 8 ++++---- RELEASE_NOTES | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1581b939..d22c0e5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ resolver = "2" [workspace.package] -version = "4.10.3" +version = "4.11.0" repository = "https://github.com/cloudflare/boring" edition = "2021" @@ -19,9 +19,9 @@ tag-prefix = "" publish = false [workspace.dependencies] -boring-sys = { version = "4.10.3", path = "./boring-sys" } -boring = { version = "4.10.3", path = "./boring" } -tokio-boring = { version = "4.10.3", path = "./tokio-boring" } +boring-sys = { version = "4.11.0", path = "./boring-sys" } +boring = { version = "4.11.0", path = "./boring" } +tokio-boring = { version = "4.11.0", path = "./tokio-boring" } bindgen = { version = "0.70.1", default-features = false, features = ["runtime"] } bytes = "1" diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 8e36ab05..c205d73b 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,8 @@ +4.11.0 +- 2024-10-17 boring-sys: include HPKE header file for bindgen +- 2024-10-17 Add "fips-compat" feature +- 2024-09-25 Create semgrep.yml + 4.10.3 - 2024-09-21 Set MSRV to 1.70 (#279) From bb373e555002ab22d6fdc3fd372cbfeca0d62ae0 Mon Sep 17 00:00:00 2001 From: James Larisch Date: Fri, 18 Oct 2024 16:08:35 -0400 Subject: [PATCH 30/41] Add `set_cert_verify_callback` (`SSL_CTX_set_cert_verify`) Add a wrapper for `SSL_CTX_set_cert_verify`, which allows consumers to override the default certificate verification behavior. The binding resembles `SSL_CTX_set_verify`'s. See https://docs.openssl.org/master/man3/SSL_CTX_set_cert_verify_callback/ for more details. --- boring/src/ssl/callbacks.rs | 28 +++++++++ boring/src/ssl/mod.rs | 43 +++++++++++++ boring/src/ssl/test/cert_verify.rs | 99 ++++++++++++++++++++++++++++++ boring/src/ssl/test/mod.rs | 1 + 4 files changed, 171 insertions(+) create mode 100644 boring/src/ssl/test/cert_verify.rs diff --git a/boring/src/ssl/callbacks.rs b/boring/src/ssl/callbacks.rs index f592d9d2..f41108d5 100644 --- a/boring/src/ssl/callbacks.rs +++ b/boring/src/ssl/callbacks.rs @@ -64,6 +64,34 @@ where unsafe { raw_custom_verify_callback(ssl, out_alert, callback) } } +pub(super) unsafe extern "C" fn raw_cert_verify( + x509_ctx: *mut ffi::X509_STORE_CTX, + _arg: *mut c_void, +) -> c_int +where + F: Fn(&mut X509StoreContextRef) -> bool + 'static + Sync + Send, +{ + // SAFETY: boring provides valid inputs. + let ctx = unsafe { X509StoreContextRef::from_ptr_mut(x509_ctx) }; + + let ssl_idx = X509StoreContext::ssl_idx().expect("BUG: store context ssl index missing"); + let verify_idx = SslContext::cached_ex_index::(); + + let verify = ctx + .ex_data(ssl_idx) + .expect("BUG: store context missing ssl") + .ssl_context() + .ex_data(verify_idx) + .expect("BUG: verify callback missing"); + + // SAFETY: The callback won't outlive the context it's associated with + // because there is no way to get a mutable reference to the `SslContext`, + // so the callback can't replace itself. + let verify = unsafe { &*(verify as *const F) }; + + verify(ctx) as c_int +} + pub(super) unsafe extern "C" fn ssl_raw_custom_verify( ssl: *mut ffi::SSL, out_alert: *mut u8, diff --git a/boring/src/ssl/mod.rs b/boring/src/ssl/mod.rs index 64fcf8bb..8ea6ede9 100644 --- a/boring/src/ssl/mod.rs +++ b/boring/src/ssl/mod.rs @@ -1000,6 +1000,49 @@ impl SslContextBuilder { self.ctx.as_ptr() } + /// Registers a certificate verification callback that replaces the default verification + /// process. + /// + /// The callback returns true if the certificate chain is valid, and false if not. + /// A viable verification result value (either `Ok(())` or an `Err(X509VerifyError)`) must be + /// reflected in the error member of `X509StoreContextRef`, which can be done by calling + /// `X509StoreContextRef::set_error`. However, the callback's return value determines + /// whether the chain is accepted or not. + /// + /// *Warning*: Providing a complete verification procedure is a complex task. See + /// https://docs.openssl.org/master/man3/SSL_CTX_set_cert_verify_callback/#notes for more + /// information. + /// + /// TODO: Add the ability to unset the callback by either adding a new function or wrapping the + /// callback in an `Option`. + /// + /// # Panics + /// + /// This method panics if this `SslContext` is associated with a RPK context. + #[corresponds(SSL_CTX_set_cert_verify_callback)] + pub fn set_cert_verify_callback(&mut self, callback: F) + where + F: Fn(&mut X509StoreContextRef) -> bool + 'static + Sync + Send, + { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk, "This API is not supported for RPK"); + + // NOTE(jlarisch): Q: Why don't we wrap the callback in an Arc, since + // `set_verify_callback` does? + // A: I don't think that Arc is necessary, and I don't think one is necessary here. + // There's no way to get a mutable reference to the `Ssl` or `SslContext`, which + // is what you need to register a new callback. + // See the NOTE in `ssl_raw_verify` for confirmation. + self.replace_ex_data(SslContext::cached_ex_index::(), callback); + unsafe { + ffi::SSL_CTX_set_cert_verify_callback( + self.as_ptr(), + Some(raw_cert_verify::), + ptr::null_mut(), + ); + } + } + /// Configures the certificate verification method for new connections. #[corresponds(SSL_CTX_set_verify)] pub fn set_verify(&mut self, mode: SslVerifyMode) { diff --git a/boring/src/ssl/test/cert_verify.rs b/boring/src/ssl/test/cert_verify.rs new file mode 100644 index 00000000..929db48c --- /dev/null +++ b/boring/src/ssl/test/cert_verify.rs @@ -0,0 +1,99 @@ +use crate::hash::MessageDigest; +use crate::ssl::test::Server; +use crate::ssl::SslVerifyMode; + +#[test] +fn error_when_trusted_but_callback_returns_false() { + let mut server = Server::builder(); + server.should_error(); + let server = server.build(); + let mut client = server.client_with_root_ca(); + client.ctx().set_verify(SslVerifyMode::PEER); + client.ctx().set_cert_verify_callback(|x509| { + // The cert is OK + assert!(x509.verify_cert().unwrap()); + assert!(x509.current_cert().is_some()); + assert!(x509.verify_result().is_ok()); + // But we return false + false + }); + + client.connect_err(); +} + +#[test] +fn no_error_when_untrusted_but_callback_returns_true() { + let server = Server::builder().build(); + let mut client = server.client(); + client.ctx().set_verify(SslVerifyMode::PEER); + client.ctx().set_cert_verify_callback(|x509| { + // The cert is not OK + assert!(!x509.verify_cert().unwrap()); + assert!(x509.current_cert().is_some()); + assert!(x509.verify_result().is_err()); + // But we return true + true + }); + + client.connect(); +} + +#[test] +fn no_error_when_trusted_and_callback_returns_true() { + let server = Server::builder().build(); + let mut client = server.client_with_root_ca(); + client.ctx().set_verify(SslVerifyMode::PEER); + client.ctx().set_cert_verify_callback(|x509| { + // The cert is OK + assert!(x509.verify_cert().unwrap()); + assert!(x509.current_cert().is_some()); + assert!(x509.verify_result().is_ok()); + // And we return true + true + }); + client.connect(); +} + +#[test] +fn callback_receives_correct_certificate() { + let server = Server::builder().build(); + let mut client = server.client(); + let expected = "59172d9313e84459bcff27f967e79e6e9217e584"; + client.ctx().set_verify(SslVerifyMode::PEER); + client.ctx().set_cert_verify_callback(move |x509| { + assert!(!x509.verify_cert().unwrap()); + assert!(x509.current_cert().is_some()); + assert!(x509.verify_result().is_err()); + let cert = x509.current_cert().unwrap(); + let digest = cert.digest(MessageDigest::sha1()).unwrap(); + assert_eq!(hex::encode(digest), expected); + true + }); + + client.connect(); +} + +#[test] +fn callback_receives_correct_chain() { + let server = Server::builder().build(); + let mut client = server.client_with_root_ca(); + let leaf_sha1 = "59172d9313e84459bcff27f967e79e6e9217e584"; + let root_sha1 = "c0cbdf7cdd03c9773e5468e1f6d2da7d5cbb1875"; + client.ctx().set_verify(SslVerifyMode::PEER); + client.ctx().set_cert_verify_callback(move |x509| { + assert!(x509.verify_cert().unwrap()); + assert!(x509.current_cert().is_some()); + assert!(x509.verify_result().is_ok()); + let chain = x509.chain().unwrap(); + assert!(chain.len() == 2); + let leaf_cert = chain.get(0).unwrap(); + let leaf_digest = leaf_cert.digest(MessageDigest::sha1()).unwrap(); + assert_eq!(hex::encode(leaf_digest), leaf_sha1); + let root_cert = chain.get(1).unwrap(); + let root_digest = root_cert.digest(MessageDigest::sha1()).unwrap(); + assert_eq!(hex::encode(root_digest), root_sha1); + true + }); + + client.connect(); +} diff --git a/boring/src/ssl/test/mod.rs b/boring/src/ssl/test/mod.rs index f3b0fd29..6010cf98 100644 --- a/boring/src/ssl/test/mod.rs +++ b/boring/src/ssl/test/mod.rs @@ -24,6 +24,7 @@ use crate::x509::{X509Name, X509}; #[cfg(not(feature = "fips"))] use super::CompliancePolicy; +mod cert_verify; mod custom_verify; mod private_key_method; mod server; From 3044e9ba98fcbac852cb9df9ee699d8d6b9ae1fb Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Tue, 22 Oct 2024 15:47:21 -0700 Subject: [PATCH 31/41] Skip bindgen 0.70's layout tests before Rust 1.77 --- Cargo.toml | 1 + boring-sys/Cargo.toml | 1 + boring-sys/build/main.rs | 6 +++++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d22c0e5e..7fa0d4d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,3 +49,4 @@ openssl-macros = "0.1.1" tower = "0.4" tower-layer = "0.3" tower-service = "0.3" +autocfg = "1.3.0" diff --git a/boring-sys/Cargo.toml b/boring-sys/Cargo.toml index 5b623181..36327950 100644 --- a/boring-sys/Cargo.toml +++ b/boring-sys/Cargo.toml @@ -77,6 +77,7 @@ pq-experimental = [] underscore-wildcards = [] [build-dependencies] +autocfg = { workspace = true } bindgen = { workspace = true } cmake = { workspace = true } fs_extra = { workspace = true } diff --git a/boring-sys/build/main.rs b/boring-sys/build/main.rs index 7966e215..86075d3c 100644 --- a/boring-sys/build/main.rs +++ b/boring-sys/build/main.rs @@ -687,6 +687,10 @@ fn main() { } }); + // 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 mut builder = bindgen::Builder::default() .rust_target(bindgen::RustTarget::Stable_1_68) // bindgen MSRV is 1.70, so this is enough .derive_copy(true) @@ -701,7 +705,7 @@ fn main() { .generate_comments(true) .fit_macro_constants(false) .size_t_is_usize(true) - .layout_tests(true) + .layout_tests(supports_layout_tests) .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)) From 7bb3647406640eb1e520286ef59a5fd6ccaded5b Mon Sep 17 00:00:00 2001 From: Rushil Mehra Date: Tue, 22 Oct 2024 16:27:26 -0700 Subject: [PATCH 32/41] (ci): brew link x86 toolchain for macos13 runner It seems we need to manually symlink the x86_64-unknown-linux-gnu toolchain for the macos13 runner. Also, we don't need to overwrite the python version anymore Fixes https://github.com/cloudflare/boring/issues/285 --- .github/workflows/ci.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 083ee77c..32aa3afe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -313,6 +313,8 @@ jobs: 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 }} && brew link x86_64-unknown-linux-gnu - name: Install Clang-12 uses: KyleMayes/install-llvm-action@v1 with: @@ -320,10 +322,7 @@ jobs: directory: ${{ runner.temp }}/llvm - name: Add clang++-12 link working-directory: ${{ runner.temp }}/llvm/bin - run: ln -s clang clang++-12 - - name: Install ${{ matrix.target }} toolchain - # TODO(rmehra): find a better way to overwrite the python3 version without specifying version - run: brew tap messense/macos-cross-toolchains && brew install --overwrite python@3.12 && brew install ${{ matrix.target }} + run: ln -s clang++ clang++-12 - name: Set BORING_BSSL_FIPS_COMPILER_EXTERNAL_TOOLCHAIN run: echo "BORING_BSSL_FIPS_COMPILER_EXTERNAL_TOOLCHAIN=$(brew --prefix ${{ matrix.target }})/toolchain" >> $GITHUB_ENV shell: bash From c113ec5a0d7aa80b7394d57bc39c7f1647f3c04f Mon Sep 17 00:00:00 2001 From: Evan Rittenhouse Date: Wed, 20 Nov 2024 09:58:40 -0600 Subject: [PATCH 33/41] Add bindings for SSL_CB_ACCEPT_EXIT and SSL_CB_CONNECT_EXIT --- boring/src/ssl/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/boring/src/ssl/mod.rs b/boring/src/ssl/mod.rs index 8ea6ede9..f0849589 100644 --- a/boring/src/ssl/mod.rs +++ b/boring/src/ssl/mod.rs @@ -850,6 +850,12 @@ impl SslInfoCallbackMode { /// Signaled when a handshake progresses to a new state. pub const ACCEPT_LOOP: Self = Self(ffi::SSL_CB_ACCEPT_LOOP); + + /// Signaled when the current iteration of the server-side handshake state machine completes. + pub const ACCEPT_EXIT: Self = Self(ffi::SSL_CB_ACCEPT_EXIT); + + /// Signaled when the current iteration of the client-side handshake state machine completes. + pub const CONNECT_EXIT: Self = Self(ffi::SSL_CB_CONNECT_EXIT); } /// The `value` argument to an info callback. The most-significant byte is the alert level, while From 2b75e1e1275f24885c2d686a8c1fd53e90140b84 Mon Sep 17 00:00:00 2001 From: Evan Rittenhouse Date: Wed, 20 Nov 2024 10:20:31 -0600 Subject: [PATCH 34/41] Release 4.12.0 --- Cargo.toml | 8 ++++---- RELEASE_NOTES | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7fa0d4d8..bad88ec2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ resolver = "2" [workspace.package] -version = "4.11.0" +version = "4.12.0" repository = "https://github.com/cloudflare/boring" edition = "2021" @@ -19,9 +19,9 @@ tag-prefix = "" publish = false [workspace.dependencies] -boring-sys = { version = "4.11.0", path = "./boring-sys" } -boring = { version = "4.11.0", path = "./boring" } -tokio-boring = { version = "4.11.0", path = "./tokio-boring" } +boring-sys = { version = "4.12.0", path = "./boring-sys" } +boring = { version = "4.12.0", path = "./boring" } +tokio-boring = { version = "4.12.0", path = "./tokio-boring" } bindgen = { version = "0.70.1", default-features = false, features = ["runtime"] } bytes = "1" diff --git a/RELEASE_NOTES b/RELEASE_NOTES index c205d73b..b6405f43 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,9 @@ +4.12.0 +- 2024-11-20 Add bindings for SSL_CB_ACCEPT_EXIT and SSL_CB_CONNECT_EXIT +- 2024-10-22 (ci): brew link x86 toolchain for macos13 runner +- 2024-10-22 Skip bindgen 0.70's layout tests before Rust 1.77 +- 2024-10-18 Add `set_cert_verify_callback` (`SSL_CTX_set_cert_verify`) + 4.11.0 - 2024-10-17 boring-sys: include HPKE header file for bindgen - 2024-10-17 Add "fips-compat" feature From 7ac0d2105c3682b0033f2df8aa74ab870b9ad277 Mon Sep 17 00:00:00 2001 From: Paul Mabileau Date: Mon, 11 Mar 2024 10:25:27 +0100 Subject: [PATCH 35/41] Fix Windows build Only with this does it work for us. Signed-off-by: Paul Mabileau --- boring-sys/build/main.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/boring-sys/build/main.rs b/boring-sys/build/main.rs index 86075d3c..3ff818b3 100644 --- a/boring-sys/build/main.rs +++ b/boring-sys/build/main.rs @@ -663,6 +663,10 @@ fn main() { bssl_dir.display(), build_path ); + println!( + "cargo:rustc-link-search=native={}/build", + bssl_dir.display(), + ); } if config.features.fips_link_precompiled { From 81e84c26a21e3ba7a8e08fd1800a0c60841ca0c4 Mon Sep 17 00:00:00 2001 From: Kornel Date: Thu, 28 Nov 2024 18:46:22 +0000 Subject: [PATCH 36/41] Clippy --- boring/Cargo.toml | 1 + boring/src/asn1.rs | 4 ++-- boring/src/bio.rs | 2 +- boring/src/bn.rs | 22 +++++++++++----------- boring/src/derive.rs | 6 +++--- boring/src/sign.rs | 16 ++++++++-------- boring/src/ssl/bio.rs | 2 +- boring/src/stack.rs | 4 ++-- boring/src/x509/mod.rs | 4 ++-- 9 files changed, 31 insertions(+), 30 deletions(-) diff --git a/boring/Cargo.toml b/boring/Cargo.toml index 19e79432..96bfbc92 100644 --- a/boring/Cargo.toml +++ b/boring/Cargo.toml @@ -10,6 +10,7 @@ readme = "README.md" keywords = ["crypto", "tls", "ssl", "dtls"] categories = ["cryptography", "api-bindings"] edition = { workspace = true } +rust-version = "1.70" [package.metadata.docs.rs] features = ["rpk", "pq-experimental", "underscore-wildcards"] diff --git a/boring/src/asn1.rs b/boring/src/asn1.rs index 02e601fc..8a196a7d 100644 --- a/boring/src/asn1.rs +++ b/boring/src/asn1.rs @@ -240,7 +240,7 @@ impl PartialEq for Asn1TimeRef { } } -impl<'a> PartialEq for &'a Asn1TimeRef { +impl PartialEq for &Asn1TimeRef { fn eq(&self, other: &Asn1Time) -> bool { self.diff(other) .map(|t| t.days == 0 && t.secs == 0) @@ -260,7 +260,7 @@ impl PartialOrd for Asn1TimeRef { } } -impl<'a> PartialOrd for &'a Asn1TimeRef { +impl PartialOrd for &Asn1TimeRef { fn partial_cmp(&self, other: &Asn1Time) -> Option { self.compare(other).ok() } diff --git a/boring/src/bio.rs b/boring/src/bio.rs index a0c882a2..6566ebb8 100644 --- a/boring/src/bio.rs +++ b/boring/src/bio.rs @@ -9,7 +9,7 @@ use crate::error::ErrorStack; pub struct MemBioSlice<'a>(*mut ffi::BIO, PhantomData<&'a [u8]>); -impl<'a> Drop for MemBioSlice<'a> { +impl Drop for MemBioSlice<'_> { fn drop(&mut self) { unsafe { ffi::BIO_free_all(self.0); diff --git a/boring/src/bn.rs b/boring/src/bn.rs index b5b02077..27fdeb32 100644 --- a/boring/src/bn.rs +++ b/boring/src/bn.rs @@ -1126,7 +1126,7 @@ macro_rules! delegate { }; } -impl<'a, 'b> Add<&'b BigNumRef> for &'a BigNumRef { +impl Add<&BigNumRef> for &BigNumRef { type Output = BigNum; fn add(self, oth: &BigNumRef) -> BigNum { @@ -1138,7 +1138,7 @@ impl<'a, 'b> Add<&'b BigNumRef> for &'a BigNumRef { delegate!(Add, add); -impl<'a, 'b> Sub<&'b BigNumRef> for &'a BigNumRef { +impl Sub<&BigNumRef> for &BigNumRef { type Output = BigNum; fn sub(self, oth: &BigNumRef) -> BigNum { @@ -1150,7 +1150,7 @@ impl<'a, 'b> Sub<&'b BigNumRef> for &'a BigNumRef { delegate!(Sub, sub); -impl<'a, 'b> Mul<&'b BigNumRef> for &'a BigNumRef { +impl Mul<&BigNumRef> for &BigNumRef { type Output = BigNum; fn mul(self, oth: &BigNumRef) -> BigNum { @@ -1163,7 +1163,7 @@ impl<'a, 'b> Mul<&'b BigNumRef> for &'a BigNumRef { delegate!(Mul, mul); -impl<'a, 'b> Div<&'b BigNumRef> for &'a BigNumRef { +impl<'b> Div<&'b BigNumRef> for &BigNumRef { type Output = BigNum; fn div(self, oth: &'b BigNumRef) -> BigNum { @@ -1176,7 +1176,7 @@ impl<'a, 'b> Div<&'b BigNumRef> for &'a BigNumRef { delegate!(Div, div); -impl<'a, 'b> Rem<&'b BigNumRef> for &'a BigNumRef { +impl<'b> Rem<&'b BigNumRef> for &BigNumRef { type Output = BigNum; fn rem(self, oth: &'b BigNumRef) -> BigNum { @@ -1189,7 +1189,7 @@ impl<'a, 'b> Rem<&'b BigNumRef> for &'a BigNumRef { delegate!(Rem, rem); -impl<'a> Shl for &'a BigNumRef { +impl Shl for &BigNumRef { type Output = BigNum; fn shl(self, n: i32) -> BigNum { @@ -1199,7 +1199,7 @@ impl<'a> Shl for &'a BigNumRef { } } -impl<'a> Shl for &'a BigNum { +impl Shl for &BigNum { type Output = BigNum; fn shl(self, n: i32) -> BigNum { @@ -1207,7 +1207,7 @@ impl<'a> Shl for &'a BigNum { } } -impl<'a> Shr for &'a BigNumRef { +impl Shr for &BigNumRef { type Output = BigNum; fn shr(self, n: i32) -> BigNum { @@ -1217,7 +1217,7 @@ impl<'a> Shr for &'a BigNumRef { } } -impl<'a> Shr for &'a BigNum { +impl Shr for &BigNum { type Output = BigNum; fn shr(self, n: i32) -> BigNum { @@ -1225,7 +1225,7 @@ impl<'a> Shr for &'a BigNum { } } -impl<'a> Neg for &'a BigNumRef { +impl Neg for &BigNumRef { type Output = BigNum; fn neg(self) -> BigNum { @@ -1233,7 +1233,7 @@ impl<'a> Neg for &'a BigNumRef { } } -impl<'a> Neg for &'a BigNum { +impl Neg for &BigNum { type Output = BigNum; fn neg(self) -> BigNum { diff --git a/boring/src/derive.rs b/boring/src/derive.rs index 08a47dfb..66df434f 100644 --- a/boring/src/derive.rs +++ b/boring/src/derive.rs @@ -11,10 +11,10 @@ use crate::{cvt, cvt_p}; /// A type used to derive a shared secret between two keys. pub struct Deriver<'a>(*mut ffi::EVP_PKEY_CTX, PhantomData<&'a ()>); -unsafe impl<'a> Sync for Deriver<'a> {} -unsafe impl<'a> Send for Deriver<'a> {} +unsafe impl Sync for Deriver<'_> {} +unsafe impl Send for Deriver<'_> {} -impl<'a> Drop for Deriver<'a> { +impl Drop for Deriver<'_> { fn drop(&mut self) { unsafe { ffi::EVP_PKEY_CTX_free(self.0); diff --git a/boring/src/sign.rs b/boring/src/sign.rs index 5bfa2bc4..ee68bc7d 100644 --- a/boring/src/sign.rs +++ b/boring/src/sign.rs @@ -78,10 +78,10 @@ pub struct Signer<'a> { _p: PhantomData<&'a ()>, } -unsafe impl<'a> Sync for Signer<'a> {} -unsafe impl<'a> Send for Signer<'a> {} +unsafe impl Sync for Signer<'_> {} +unsafe impl Send for Signer<'_> {} -impl<'a> Drop for Signer<'a> { +impl Drop for Signer<'_> { fn drop(&mut self) { // pkey_ctx is owned by the md_ctx, so no need to explicitly free it. unsafe { @@ -337,7 +337,7 @@ impl<'a> Signer<'a> { } } -impl<'a> Write for Signer<'a> { +impl Write for Signer<'_> { fn write(&mut self, buf: &[u8]) -> io::Result { self.update(buf)?; Ok(buf.len()) @@ -354,10 +354,10 @@ pub struct Verifier<'a> { pkey_pd: PhantomData<&'a ()>, } -unsafe impl<'a> Sync for Verifier<'a> {} -unsafe impl<'a> Send for Verifier<'a> {} +unsafe impl Sync for Verifier<'_> {} +unsafe impl Send for Verifier<'_> {} -impl<'a> Drop for Verifier<'a> { +impl Drop for Verifier<'_> { fn drop(&mut self) { // pkey_ctx is owned by the md_ctx, so no need to explicitly free it. unsafe { @@ -560,7 +560,7 @@ impl<'a> Verifier<'a> { } } -impl<'a> Write for Verifier<'a> { +impl Write for Verifier<'_> { fn write(&mut self, buf: &[u8]) -> io::Result { self.update(buf)?; Ok(buf.len()) diff --git a/boring/src/ssl/bio.rs b/boring/src/ssl/bio.rs index 04f43558..37e2866b 100644 --- a/boring/src/ssl/bio.rs +++ b/boring/src/ssl/bio.rs @@ -219,7 +219,7 @@ struct BIO_METHOD(*mut ffi::BIO_METHOD); impl BIO_METHOD { fn new() -> BIO_METHOD { unsafe { - let ptr = ffi::BIO_meth_new(ffi::BIO_TYPE_NONE, b"rust\0".as_ptr() as *const _); + let ptr = ffi::BIO_meth_new(ffi::BIO_TYPE_NONE, b"rust\0".as_ptr().cast()); assert!(!ptr.is_null()); let ret = BIO_METHOD(ptr); assert!(ffi::BIO_meth_set_write(ptr, Some(bwrite::)) != 0); diff --git a/boring/src/stack.rs b/boring/src/stack.rs index 67b95273..78b2773b 100644 --- a/boring/src/stack.rs +++ b/boring/src/stack.rs @@ -338,7 +338,7 @@ impl<'a, T: Stackable> DoubleEndedIterator for Iter<'a, T> { } } -impl<'a, T: Stackable> ExactSizeIterator for Iter<'a, T> {} +impl ExactSizeIterator for Iter<'_, T> {} /// A mutable iterator over the stack's contents. pub struct IterMut<'a, T: Stackable + 'a> { @@ -372,4 +372,4 @@ impl<'a, T: Stackable> DoubleEndedIterator for IterMut<'a, T> { } } -impl<'a, T: Stackable> ExactSizeIterator for IterMut<'a, T> {} +impl ExactSizeIterator for IterMut<'_, T> {} diff --git a/boring/src/x509/mod.rs b/boring/src/x509/mod.rs index af1d5040..eca2eca9 100644 --- a/boring/src/x509/mod.rs +++ b/boring/src/x509/mod.rs @@ -121,7 +121,7 @@ impl X509StoreContextRef { { struct Cleanup<'a>(&'a mut X509StoreContextRef); - impl<'a> Drop for Cleanup<'a> { + impl Drop for Cleanup<'_> { fn drop(&mut self) { unsafe { ffi::X509_STORE_CTX_cleanup(self.0.as_ptr()); @@ -735,7 +735,7 @@ impl Stackable for X509 { /// A context object required to construct certain `X509` extension values. pub struct X509v3Context<'a>(ffi::X509V3_CTX, PhantomData<(&'a X509Ref, &'a ConfRef)>); -impl<'a> X509v3Context<'a> { +impl X509v3Context<'_> { pub fn as_ptr(&self) -> *mut ffi::X509V3_CTX { &self.0 as *const _ as *mut _ } From 1946603e15e23aa227b9a262c09130fc1a9ff76f Mon Sep 17 00:00:00 2001 From: Kornel Date: Thu, 28 Nov 2024 19:01:10 +0000 Subject: [PATCH 37/41] Work around Rust settings inconsistent iOS SDK version --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 32aa3afe..d587a4a7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -113,16 +113,22 @@ jobs: target: aarch64-apple-ios os: macos-latest check_only: true + custom_env: + IPHONEOS_DEPLOYMENT_TARGET: 17.5 # It's... theoretically possible to run tests on iPhone Simulator, # but for now, make sure that BoringSSL only builds. - thing: aarch64-ios-sim target: aarch64-apple-ios-sim os: macos-latest check_only: true + custom_env: + IPHONEOS_DEPLOYMENT_TARGET: 17.5 - thing: x86_64-ios target: x86_64-apple-ios os: macos-latest check_only: true + custom_env: + IPHONEOS_DEPLOYMENT_TARGET: 17.5 - thing: i686-linux target: i686-unknown-linux-gnu rust: stable From 6ef5c28239e61cbdfff358eddc7c517b91ccadbc Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 20 Nov 2021 19:10:20 -0500 Subject: [PATCH 38/41] More corresponds from openssl --- boring/src/aes.rs | 7 +- boring/src/asn1.rs | 43 +++----- boring/src/base64.rs | 11 +- boring/src/bn.rs | 220 +++++++++----------------------------- boring/src/x509/mod.rs | 43 +++----- boring/src/x509/store.rs | 5 + boring/src/x509/verify.rs | 18 ++-- 7 files changed, 103 insertions(+), 244 deletions(-) diff --git a/boring/src/aes.rs b/boring/src/aes.rs index 2bf95466..376d356a 100644 --- a/boring/src/aes.rs +++ b/boring/src/aes.rs @@ -41,6 +41,7 @@ use crate::ffi; use libc::{c_int, c_uint, size_t}; use std::mem::MaybeUninit; use std::ptr; +use openssl_macros::corresponds; /// Provides Error handling for parsing keys. #[derive(Debug)] @@ -55,7 +56,7 @@ impl AesKey { /// # Failure /// /// Returns an error if the key is not 128, 192, or 256 bits. - #[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566 + #[corresponds(AES_set_encrypt_key)] pub fn new_encrypt(key: &[u8]) -> Result { unsafe { assert!(key.len() <= c_int::MAX as usize / 8); @@ -79,7 +80,7 @@ impl AesKey { /// # Failure /// /// Returns an error if the key is not 128, 192, or 256 bits. - #[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566 + #[corresponds(AES_set_decrypt_key)] pub fn new_decrypt(key: &[u8]) -> Result { unsafe { assert!(key.len() <= c_int::MAX as usize / 8); @@ -113,6 +114,7 @@ impl AesKey { /// /// Panics if either `out` or `in_` do not have sizes that are a multiple of 8, or if /// `out` is not 8 bytes longer than `in_` +#[corresponds(AES_wrap_key)] pub fn wrap_key( key: &AesKey, iv: Option<[u8; 8]>, @@ -151,6 +153,7 @@ pub fn wrap_key( /// /// Panics if either `out` or `in_` do not have sizes that are a multiple of 8, or /// if `in_` is not 8 bytes longer than `out` +#[corresponds(AES_unwrap_key)] pub fn unwrap_key( key: &AesKey, iv: Option<[u8; 8]>, diff --git a/boring/src/asn1.rs b/boring/src/asn1.rs index 8a196a7d..9fb3fcaf 100644 --- a/boring/src/asn1.rs +++ b/boring/src/asn1.rs @@ -41,6 +41,7 @@ use crate::nid::Nid; use crate::stack::Stackable; use crate::string::OpensslString; use crate::{cvt, cvt_p}; +use openssl_macros::corresponds; foreign_type_and_impl_send_sync! { type CType = ffi::ASN1_GENERALIZEDTIME; @@ -187,10 +188,7 @@ foreign_type_and_impl_send_sync! { impl Asn1TimeRef { /// Find difference between two times - /// - /// This corresponds to [`ASN1_TIME_diff`]. - /// - /// [`ASN1_TIME_diff`]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_TIME_diff.html + #[corresponds(ASN1_TIME_diff)] pub fn diff(&self, compare: &Self) -> Result { let mut days = 0; let mut secs = 0; @@ -205,12 +203,7 @@ impl Asn1TimeRef { } /// Compare two times - /// - /// This corresponds to [`ASN1_TIME_compare`] but is implemented using [`diff`] so that it is - /// also supported on older versions of OpenSSL. - /// - /// [`ASN1_TIME_compare`]: https://www.openssl.org/docs/man1.1.1/man3/ASN1_TIME_compare.html - /// [`diff`]: struct.Asn1TimeRef.html#method.diff + #[corresponds(ASN1_TIME_compare)] pub fn compare(&self, other: &Self) -> Result { let d = self.diff(other)?; if d.days > 0 || d.secs > 0 { @@ -289,6 +282,7 @@ impl fmt::Debug for Asn1TimeRef { } impl Asn1Time { + #[corresponds(ASN1_TIME_new)] fn new() -> Result { ffi::init(); @@ -298,6 +292,7 @@ impl Asn1Time { } } + #[corresponds(X509_gmtime_adj)] fn from_period(period: c_long) -> Result { ffi::init(); @@ -313,6 +308,7 @@ impl Asn1Time { } /// Creates a new time from the specified `time_t` value + #[corresponds(ASN1_TIME_set)] pub fn from_unix(time: time_t) -> Result { ffi::init(); @@ -323,10 +319,7 @@ impl Asn1Time { } /// Creates a new time corresponding to the specified ASN1 time string. - /// - /// This corresponds to [`ASN1_TIME_set_string`]. - /// - /// [`ASN1_TIME_set_string`]: https://www.openssl.org/docs/manmaster/man3/ASN1_TIME_set_string.html + #[corresponds(ASN1_TIME_set_string)] #[allow(clippy::should_implement_trait)] pub fn from_str(s: &str) -> Result { unsafe { @@ -401,6 +394,7 @@ impl Asn1StringRef { /// ASN.1 strings may utilize UTF-16, ASCII, BMP, or UTF8. This is important to /// consume the string in a meaningful way without knowing the underlying /// format. + #[corresponds(ASN1_STRING_to_UTF8)] pub fn as_utf8(&self) -> Result { unsafe { let mut ptr = ptr::null_mut(); @@ -419,11 +413,13 @@ impl Asn1StringRef { /// strings in rust, it is preferable to use [`as_utf8`] /// /// [`as_utf8`]: struct.Asn1String.html#method.as_utf8 + #[corresponds(ASN1_STRING_get0_data)] pub fn as_slice(&self) -> &[u8] { unsafe { slice::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr()), self.len()) } } /// Returns the number of bytes in the string. + #[corresponds(ASN1_STRING_length)] pub fn len(&self) -> usize { unsafe { ffi::ASN1_STRING_length(self.as_ptr()) as usize } } @@ -481,10 +477,7 @@ impl Asn1IntegerRef { } /// Converts the integer to a `BigNum`. - /// - /// This corresponds to [`ASN1_INTEGER_to_BN`]. - /// - /// [`ASN1_INTEGER_to_BN`]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_INTEGER_get.html + #[corresponds(ASN1_INTEGER_to_BN)] pub fn to_bn(&self) -> Result { unsafe { cvt_p(crate::ffi::ASN1_INTEGER_to_BN( @@ -498,10 +491,8 @@ impl Asn1IntegerRef { /// Sets the ASN.1 value to the value of a signed 32-bit integer, for larger numbers /// see [`bn`]. /// - /// OpenSSL documentation at [`ASN1_INTEGER_set`] - /// /// [`bn`]: ../bn/struct.BigNumRef.html#method.to_asn1_integer - /// [`ASN1_INTEGER_set`]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_INTEGER_set.html + #[corresponds(ASN1_INTEGER_set)] pub fn set(&mut self, value: i32) -> Result<(), ErrorStack> { unsafe { cvt(crate::ffi::ASN1_INTEGER_set(self.as_ptr(), value as c_long)).map(|_| ()) } } @@ -521,11 +512,13 @@ foreign_type_and_impl_send_sync! { impl Asn1BitStringRef { /// Returns the Asn1BitString as a slice. + #[corresponds(ASN1_STRING_get0_data)] pub fn as_slice(&self) -> &[u8] { unsafe { slice::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr() as *mut _), self.len()) } } /// Returns the number of bytes in the string. + #[corresponds(ASN1_STRING_length)] pub fn len(&self) -> usize { unsafe { ffi::ASN1_STRING_length(self.as_ptr() as *const _) as usize } } @@ -561,12 +554,8 @@ impl Stackable for Asn1Object { } impl Asn1Object { - /// Constructs an ASN.1 Object Identifier from a string representation of - /// the OID. - /// - /// This corresponds to [`OBJ_txt2obj`]. - /// - /// [`OBJ_txt2obj`]: https://www.openssl.org/docs/man1.1.0/man3/OBJ_txt2obj.html + /// Constructs an ASN.1 Object Identifier from a string representation of the OID. + #[corresponds(OBJ_txt2obj)] #[allow(clippy::should_implement_trait)] pub fn from_str(txt: &str) -> Result { unsafe { diff --git a/boring/src/base64.rs b/boring/src/base64.rs index 9b556384..75cc9cc8 100644 --- a/boring/src/base64.rs +++ b/boring/src/base64.rs @@ -3,16 +3,14 @@ use crate::cvt_n; use crate::error::ErrorStack; use crate::ffi; use libc::c_int; +use openssl_macros::corresponds; /// Encodes a slice of bytes to a base64 string. /// -/// This corresponds to [`EVP_EncodeBlock`]. -/// /// # Panics /// /// Panics if the input length or computed output length overflow a signed C integer. -/// -/// [`EVP_EncodeBlock`]: https://www.openssl.org/docs/man1.1.1/man3/EVP_DecodeBlock.html +#[corresponds(EVP_EncodeBlock)] pub fn encode_block(src: &[u8]) -> String { assert!(src.len() <= c_int::MAX as usize); let src_len = src.len(); @@ -33,13 +31,10 @@ pub fn encode_block(src: &[u8]) -> String { /// Decodes a base64-encoded string to bytes. /// -/// This corresponds to [`EVP_DecodeBlock`]. -/// /// # Panics /// /// Panics if the input length or computed output length overflow a signed C integer. -/// -/// [`EVP_DecodeBlock`]: https://www.openssl.org/docs/man1.1.1/man3/EVP_DecodeBlock.html +#[corresponds(EVP_DecodeBlock)] pub fn decode_block(src: &str) -> Result, ErrorStack> { let src = src.trim(); diff --git a/boring/src/bn.rs b/boring/src/bn.rs index 27fdeb32..58edbabb 100644 --- a/boring/src/bn.rs +++ b/boring/src/bn.rs @@ -35,6 +35,7 @@ use crate::error::ErrorStack; use crate::ffi::BN_is_negative; use crate::string::OpensslString; use crate::{cvt, cvt_n, cvt_p}; +use openssl_macros::corresponds; /// Options for the most significant bits of a randomly generated `BigNum`. pub struct MsbOption(c_int); @@ -69,10 +70,7 @@ foreign_type_and_impl_send_sync! { impl BigNumContext { /// Returns a new `BigNumContext`. - /// - /// See OpenSSL documentation at [`BN_CTX_new`]. - /// - /// [`BN_CTX_new`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_CTX_new.html + #[corresponds(BN_CTX_new)] pub fn new() -> Result { unsafe { ffi::init(); @@ -115,46 +113,31 @@ impl BigNumRef { /// Erases the memory used by this `BigNum`, resetting its value to 0. /// /// This can be used to destroy sensitive data such as keys when they are no longer needed. - /// - /// OpenSSL documentation at [`BN_clear`] - /// - /// [`BN_clear`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_clear.html + #[corresponds(BN_clear)] pub fn clear(&mut self) { unsafe { ffi::BN_clear(self.as_ptr()) } } /// Adds a `u32` to `self`. - /// - /// OpenSSL documentation at [`BN_add_word`] - /// - /// [`BN_add_word`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_add_word.html + #[corresponds(BN_add_word)] pub fn add_word(&mut self, w: u32) -> Result<(), ErrorStack> { unsafe { cvt(ffi::BN_add_word(self.as_ptr(), w as ffi::BN_ULONG)).map(|_| ()) } } /// Subtracts a `u32` from `self`. - /// - /// OpenSSL documentation at [`BN_sub_word`] - /// - /// [`BN_sub_word`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_sub_word.html + #[corresponds(BN_sub_word)] pub fn sub_word(&mut self, w: u32) -> Result<(), ErrorStack> { unsafe { cvt(ffi::BN_sub_word(self.as_ptr(), w as ffi::BN_ULONG)).map(|_| ()) } } /// Multiplies a `u32` by `self`. - /// - /// OpenSSL documentation at [`BN_mul_word`] - /// - /// [`BN_mul_word`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mul_word.html + #[corresponds(BN_mul_word)] pub fn mul_word(&mut self, w: u32) -> Result<(), ErrorStack> { unsafe { cvt(ffi::BN_mul_word(self.as_ptr(), w as ffi::BN_ULONG)).map(|_| ()) } } /// Divides `self` by a `u32`, returning the remainder. - /// - /// OpenSSL documentation at [`BN_div_word`] - /// - /// [`BN_div_word`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_div_word.html + #[corresponds(BN_div_word)] #[allow(clippy::useless_conversion)] pub fn div_word(&mut self, w: u32) -> Result { unsafe { @@ -168,10 +151,7 @@ impl BigNumRef { } /// Returns the result of `self` modulo `w`. - /// - /// OpenSSL documentation at [`BN_mod_word`] - /// - /// [`BN_mod_word`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mod_word.html + #[corresponds(BN_mod_word)] #[allow(clippy::useless_conversion)] pub fn mod_word(&self, w: u32) -> Result { unsafe { @@ -186,19 +166,13 @@ impl BigNumRef { /// Places a cryptographically-secure pseudo-random nonnegative /// number less than `self` in `rnd`. - /// - /// OpenSSL documentation at [`BN_rand_range`] - /// - /// [`BN_rand_range`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_rand_range.html + #[corresponds(BN_rand_range)] pub fn rand_range(&self, rnd: &mut BigNumRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::BN_rand_range(rnd.as_ptr(), self.as_ptr())).map(|_| ()) } } /// The cryptographically weak counterpart to `rand_in_range`. - /// - /// OpenSSL documentation at [`BN_pseudo_rand_range`] - /// - /// [`BN_pseudo_rand_range`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_pseudo_rand_range.html + #[corresponds(BN_pseudo_rand_range)] pub fn pseudo_rand_range(&self, rnd: &mut BigNumRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::BN_pseudo_rand_range(rnd.as_ptr(), self.as_ptr())).map(|_| ()) } } @@ -206,10 +180,7 @@ impl BigNumRef { /// Sets bit `n`. Equivalent to `self |= (1 << n)`. /// /// When setting a bit outside of `self`, it is expanded. - /// - /// OpenSSL documentation at [`BN_set_bit`] - /// - /// [`BN_set_bit`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_set_bit.html + #[corresponds(BN_set_bit)] #[allow(clippy::useless_conversion)] pub fn set_bit(&mut self, n: i32) -> Result<(), ErrorStack> { unsafe { cvt(ffi::BN_set_bit(self.as_ptr(), n.into())).map(|_| ()) } @@ -218,20 +189,14 @@ impl BigNumRef { /// Clears bit `n`, setting it to 0. Equivalent to `self &= ~(1 << n)`. /// /// When clearing a bit outside of `self`, an error is returned. - /// - /// OpenSSL documentation at [`BN_clear_bit`] - /// - /// [`BN_clear_bit`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_clear_bit.html + #[corresponds(BN_clear_bit)] #[allow(clippy::useless_conversion)] pub fn clear_bit(&mut self, n: i32) -> Result<(), ErrorStack> { unsafe { cvt(ffi::BN_clear_bit(self.as_ptr(), n.into())).map(|_| ()) } } /// Returns `true` if the `n`th bit of `self` is set to 1, `false` otherwise. - /// - /// OpenSSL documentation at [`BN_is_bit_set`] - /// - /// [`BN_is_bit_set`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_is_bit_set.html + #[corresponds(BN_is_bit_set)] #[allow(clippy::useless_conversion)] pub fn is_bit_set(&self, n: i32) -> bool { unsafe { ffi::BN_is_bit_set(self.as_ptr(), n.into()) == 1 } @@ -240,94 +205,69 @@ impl BigNumRef { /// Truncates `self` to the lowest `n` bits. /// /// An error occurs if `self` is already shorter than `n` bits. - /// - /// OpenSSL documentation at [`BN_mask_bits`] - /// - /// [`BN_mask_bits`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mask_bits.html + #[corresponds(BN_mask_bits)] #[allow(clippy::useless_conversion)] pub fn mask_bits(&mut self, n: i32) -> Result<(), ErrorStack> { unsafe { cvt(ffi::BN_mask_bits(self.as_ptr(), n.into())).map(|_| ()) } } /// Places `a << 1` in `self`. Equivalent to `self * 2`. - /// - /// OpenSSL documentation at [`BN_lshift1`] - /// - /// [`BN_lshift1`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_lshift1.html + #[corresponds(BN_lshift1)] pub fn lshift1(&mut self, a: &BigNumRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::BN_lshift1(self.as_ptr(), a.as_ptr())).map(|_| ()) } } /// Places `a >> 1` in `self`. Equivalent to `self / 2`. - /// - /// OpenSSL documentation at [`BN_rshift1`] - /// - /// [`BN_rshift1`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_rshift1.html + #[corresponds(BN_rshift1)] pub fn rshift1(&mut self, a: &BigNumRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::BN_rshift1(self.as_ptr(), a.as_ptr())).map(|_| ()) } } /// Places `a + b` in `self`. [`core::ops::Add`] is also implemented for `BigNumRef`. /// - /// OpenSSL documentation at [`BN_add`] - /// /// [`core::ops::Add`]: struct.BigNumRef.html#method.add - /// [`BN_add`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_add.html + #[corresponds(BN_add)] pub fn checked_add(&mut self, a: &BigNumRef, b: &BigNumRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::BN_add(self.as_ptr(), a.as_ptr(), b.as_ptr())).map(|_| ()) } } /// Places `a - b` in `self`. [`core::ops::Sub`] is also implemented for `BigNumRef`. /// - /// OpenSSL documentation at [`BN_sub`] - /// /// [`core::ops::Sub`]: struct.BigNumRef.html#method.sub - /// [`BN_sub`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_sub.html + #[corresponds(BN_sub)] pub fn checked_sub(&mut self, a: &BigNumRef, b: &BigNumRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::BN_sub(self.as_ptr(), a.as_ptr(), b.as_ptr())).map(|_| ()) } } /// Places `a << n` in `self`. Equivalent to `a * 2 ^ n`. - /// - /// OpenSSL documentation at [`BN_lshift`] - /// - /// [`BN_lshift`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_lshift.html + #[corresponds(BN_lshift)] #[allow(clippy::useless_conversion)] pub fn lshift(&mut self, a: &BigNumRef, n: i32) -> Result<(), ErrorStack> { unsafe { cvt(ffi::BN_lshift(self.as_ptr(), a.as_ptr(), n.into())).map(|_| ()) } } /// Places `a >> n` in `self`. Equivalent to `a / 2 ^ n`. - /// - /// OpenSSL documentation at [`BN_rshift`] - /// - /// [`BN_rshift`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_rshift.html + #[corresponds(BN_rshift)] #[allow(clippy::useless_conversion)] pub fn rshift(&mut self, a: &BigNumRef, n: i32) -> Result<(), ErrorStack> { unsafe { cvt(ffi::BN_rshift(self.as_ptr(), a.as_ptr(), n.into())).map(|_| ()) } } /// Creates a new BigNum with the same value. - /// - /// OpenSSL documentation at [`BN_dup`] - /// - /// [`BN_dup`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_dup.html + #[corresponds(BN_dup)] pub fn to_owned(&self) -> Result { unsafe { cvt_p(ffi::BN_dup(self.as_ptr())).map(|b| BigNum::from_ptr(b)) } } /// Sets the sign of `self`. Pass true to set `self` to a negative. False sets /// `self` positive. + #[corresponds(BN_set_negative)] pub fn set_negative(&mut self, negative: bool) { unsafe { ffi::BN_set_negative(self.as_ptr(), negative as c_int) } } /// Compare the absolute values of `self` and `oth`. /// - /// OpenSSL documentation at [`BN_ucmp`] - /// - /// [`BN_ucmp`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_ucmp.html - /// /// # Examples /// /// ``` @@ -338,20 +278,19 @@ impl BigNumRef { /// /// assert_eq!(s.ucmp(&o), Ordering::Equal); /// ``` + #[corresponds(BN_ucmp)] pub fn ucmp(&self, oth: &BigNumRef) -> Ordering { unsafe { ffi::BN_ucmp(self.as_ptr(), oth.as_ptr()).cmp(&0) } } /// Returns `true` if `self` is negative. + #[corresponds(BN_is_negative)] pub fn is_negative(&self) -> bool { unsafe { BN_is_negative(self.as_ptr()) == 1 } } /// Returns the number of significant bits in `self`. - /// - /// OpenSSL documentation at [`BN_num_bits`] - /// - /// [`BN_num_bits`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_num_bits.html + #[corresponds(BN_num_bits)] pub fn num_bits(&self) -> i32 { unsafe { ffi::BN_num_bits(self.as_ptr()) as i32 } } @@ -384,10 +323,8 @@ impl BigNumRef { /// } /// ``` /// - /// OpenSSL documentation at [`BN_rand`] - /// /// [`constants`]: index.html#constants - /// [`BN_rand`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_rand.html + #[corresponds(BN_rand)] #[allow(clippy::useless_conversion)] pub fn rand(&mut self, bits: i32, msb: MsbOption, odd: bool) -> Result<(), ErrorStack> { unsafe { @@ -402,10 +339,7 @@ impl BigNumRef { } /// The cryptographically weak counterpart to `rand`. Not suitable for key generation. - /// - /// OpenSSL documentation at [`BN_pseudo_rand`] - /// - /// [`BN_pseudo_rand`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_pseudo_rand.html + #[corresponds(BN_pseudo_rand)] #[allow(clippy::useless_conversion)] pub fn pseudo_rand(&mut self, bits: i32, msb: MsbOption, odd: bool) -> Result<(), ErrorStack> { unsafe { @@ -442,10 +376,7 @@ impl BigNumRef { /// Ok((big)) /// } /// ``` - /// - /// OpenSSL documentation at [`BN_generate_prime_ex`] - /// - /// [`BN_generate_prime_ex`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_generate_prime_ex.html + #[corresponds(BN_generate_prime_ex)] pub fn generate_prime( &mut self, bits: i32, @@ -469,10 +400,8 @@ impl BigNumRef { /// Places the result of `a * b` in `self`. /// [`core::ops::Mul`] is also implemented for `BigNumRef`. /// - /// OpenSSL documentation at [`BN_mul`] - /// /// [`core::ops::Mul`]: struct.BigNumRef.html#method.mul - /// [`BN_mul`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mul.html + #[corresponds(BN_mul)] pub fn checked_mul( &mut self, a: &BigNumRef, @@ -493,10 +422,8 @@ impl BigNumRef { /// Places the result of `a / b` in `self`. The remainder is discarded. /// [`core::ops::Div`] is also implemented for `BigNumRef`. /// - /// OpenSSL documentation at [`BN_div`] - /// /// [`core::ops::Div`]: struct.BigNumRef.html#method.div - /// [`BN_div`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_div.html + #[corresponds(BN_div)] pub fn checked_div( &mut self, a: &BigNumRef, @@ -516,10 +443,7 @@ impl BigNumRef { } /// Places the result of `a % b` in `self`. - /// - /// OpenSSL documentation at [`BN_div`] - /// - /// [`BN_div`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_div.html + #[corresponds(BN_div)] pub fn checked_rem( &mut self, a: &BigNumRef, @@ -539,10 +463,7 @@ impl BigNumRef { } /// Places the result of `a / b` in `self` and `a % b` in `rem`. - /// - /// OpenSSL documentation at [`BN_div`] - /// - /// [`BN_div`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_div.html + #[corresponds(BN_div)] pub fn div_rem( &mut self, rem: &mut BigNumRef, @@ -563,20 +484,14 @@ impl BigNumRef { } /// Places the result of `a²` in `self`. - /// - /// OpenSSL documentation at [`BN_sqr`] - /// - /// [`BN_sqr`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_sqr.html + #[corresponds(BN_sqr)] pub fn sqr(&mut self, a: &BigNumRef, ctx: &mut BigNumContextRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::BN_sqr(self.as_ptr(), a.as_ptr(), ctx.as_ptr())).map(|_| ()) } } /// Places the result of `a mod m` in `self`. As opposed to `div_rem` /// the result is non-negative. - /// - /// OpenSSL documentation at [`BN_nnmod`] - /// - /// [`BN_nnmod`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_nnmod.html + #[corresponds(BN_nnmod)] pub fn nnmod( &mut self, a: &BigNumRef, @@ -595,10 +510,7 @@ impl BigNumRef { } /// Places the result of `(a + b) mod m` in `self`. - /// - /// OpenSSL documentation at [`BN_mod_add`] - /// - /// [`BN_mod_add`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mod_add.html + #[corresponds(BN_mod_add)] pub fn mod_add( &mut self, a: &BigNumRef, @@ -619,10 +531,7 @@ impl BigNumRef { } /// Places the result of `(a - b) mod m` in `self`. - /// - /// OpenSSL documentation at [`BN_mod_sub`] - /// - /// [`BN_mod_sub`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mod_sub.html + #[corresponds(BN_mod_sub)] pub fn mod_sub( &mut self, a: &BigNumRef, @@ -643,10 +552,7 @@ impl BigNumRef { } /// Places the result of `(a * b) mod m` in `self`. - /// - /// OpenSSL documentation at [`BN_mod_mul`] - /// - /// [`BN_mod_mul`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mod_mul.html + #[corresponds(BN_mod_mul)] pub fn mod_mul( &mut self, a: &BigNumRef, @@ -667,10 +573,7 @@ impl BigNumRef { } /// Places the result of `a² mod m` in `self`. - /// - /// OpenSSL documentation at [`BN_mod_sqr`] - /// - /// [`BN_mod_sqr`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mod_sqr.html + #[corresponds(BN_mod_sqr)] pub fn mod_sqr( &mut self, a: &BigNumRef, @@ -689,10 +592,7 @@ impl BigNumRef { } /// Places the result of `a^p` in `self`. - /// - /// OpenSSL documentation at [`BN_exp`] - /// - /// [`BN_exp`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_exp.html + #[corresponds(BN_exp)] pub fn exp( &mut self, a: &BigNumRef, @@ -711,10 +611,7 @@ impl BigNumRef { } /// Places the result of `a^p mod m` in `self`. - /// - /// OpenSSL documentation at [`BN_mod_exp`] - /// - /// [`BN_mod_exp`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mod_exp.html + #[corresponds(BN_mod_exp)] pub fn mod_exp( &mut self, a: &BigNumRef, @@ -735,6 +632,7 @@ impl BigNumRef { } /// Places the inverse of `a` modulo `n` in `self`. + #[corresponds(BN_mod_inverse)] pub fn mod_inverse( &mut self, a: &BigNumRef, @@ -753,10 +651,7 @@ impl BigNumRef { } /// Places the greatest common denominator of `a` and `b` in `self`. - /// - /// OpenSSL documentation at [`BN_gcd`] - /// - /// [`BN_gcd`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_gcd.html + #[corresponds(BN_gcd)] pub fn gcd( &mut self, a: &BigNumRef, @@ -778,13 +673,10 @@ impl BigNumRef { /// /// Performs a Miller-Rabin probabilistic primality test with `checks` iterations. /// - /// OpenSSL documentation at [`BN_is_prime_ex`] - /// - /// [`BN_is_prime_ex`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_is_prime_ex.html - /// /// # Return Value /// /// Returns `true` if `self` is prime with an error probability of less than `0.25 ^ checks`. + #[corresponds(BN_is_prime_ex)] #[allow(clippy::useless_conversion)] pub fn is_prime(&self, checks: i32, ctx: &mut BigNumContextRef) -> Result { unsafe { @@ -804,13 +696,10 @@ impl BigNumRef { /// Then, like `is_prime`, performs a Miller-Rabin probabilistic primality test with `checks` /// iterations. /// - /// OpenSSL documentation at [`BN_is_prime_fasttest_ex`] - /// - /// [`BN_is_prime_fasttest_ex`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_is_prime_fasttest_ex.html - /// /// # Return Value /// /// Returns `true` if `self` is prime with an error probability of less than `0.25 ^ checks`. + #[corresponds(BN_is_prime_fasttest_ex)] #[allow(clippy::useless_conversion)] pub fn is_prime_fasttest( &self, @@ -842,6 +731,7 @@ impl BigNumRef { /// let s_vec = s.to_vec(); /// assert_eq!(BigNum::from_slice(&s_vec).unwrap(), r); /// ``` + #[corresponds(BN_bn2bin)] pub fn to_vec(&self) -> Vec { let size = self.num_bytes() as usize; let mut v = Vec::with_capacity(size); @@ -890,6 +780,7 @@ impl BigNumRef { /// /// assert_eq!(&**s.to_dec_str().unwrap(), "-12345"); /// ``` + #[corresponds(BN_bn2dec)] pub fn to_dec_str(&self) -> Result { unsafe { let buf = cvt_p(ffi::BN_bn2dec(self.as_ptr()))?; @@ -905,6 +796,7 @@ impl BigNumRef { /// /// assert_eq!(&**s.to_hex_str().unwrap(), "-99ff"); /// ``` + #[corresponds(BN_bn2hex)] pub fn to_hex_str(&self) -> Result { unsafe { let buf = cvt_p(ffi::BN_bn2hex(self.as_ptr()))?; @@ -913,6 +805,7 @@ impl BigNumRef { } /// Returns an `Asn1Integer` containing the value of `self`. + #[corresponds(BN_to_ASN1_INTEGER)] pub fn to_asn1_integer(&self) -> Result { unsafe { cvt_p(ffi::BN_to_ASN1_INTEGER(self.as_ptr(), ptr::null_mut())) @@ -923,6 +816,7 @@ impl BigNumRef { impl BigNum { /// Creates a new `BigNum` with the value 0. + #[corresponds(BN_new)] pub fn new() -> Result { unsafe { ffi::init(); @@ -932,10 +826,7 @@ impl BigNum { } /// Creates a new `BigNum` with the given value. - /// - /// OpenSSL documentation at [`BN_set_word`] - /// - /// [`BN_set_word`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_set_word.html + #[corresponds(BN_set_word)] pub fn from_u32(n: u32) -> Result { BigNum::new().and_then(|v| unsafe { cvt(ffi::BN_set_word(v.as_ptr(), n as ffi::BN_ULONG)).map(|_| v) @@ -943,10 +834,7 @@ impl BigNum { } /// Creates a `BigNum` from a decimal string. - /// - /// OpenSSL documentation at [`BN_dec2bn`] - /// - /// [`BN_dec2bn`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_dec2bn.html + #[corresponds(BN_dec2bn)] pub fn from_dec_str(s: &str) -> Result { unsafe { ffi::init(); @@ -958,10 +846,7 @@ impl BigNum { } /// Creates a `BigNum` from a hexadecimal string. - /// - /// OpenSSL documentation at [`BN_hex2bn`] - /// - /// [`BN_hex2bn`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_hex2bn.html + #[corresponds(BN_hex2bn)] pub fn from_hex_str(s: &str) -> Result { unsafe { ffi::init(); @@ -984,6 +869,7 @@ impl BigNum { /// /// assert_eq!(bignum, BigNum::from_u32(0x120034).unwrap()); /// ``` + #[corresponds(BN_bin2bn)] pub fn from_slice(n: &[u8]) -> Result { unsafe { ffi::init(); diff --git a/boring/src/x509/mod.rs b/boring/src/x509/mod.rs index eca2eca9..c09f4df8 100644 --- a/boring/src/x509/mod.rs +++ b/boring/src/x509/mod.rs @@ -460,6 +460,7 @@ impl X509Ref { } } + #[corresponds(X509_get_pubkey)] pub fn public_key(&self) -> Result, ErrorStack> { unsafe { let pkey = cvt_p(ffi::X509_get_pubkey(self.as_ptr()))?; @@ -468,10 +469,7 @@ impl X509Ref { } /// Returns a digest of the DER representation of the certificate. - /// - /// This corresponds to [`X509_digest`]. - /// - /// [`X509_digest`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_digest.html + #[corresponds(X509_digest)] pub fn digest(&self, hash_type: MessageDigest) -> Result { unsafe { let mut digest = DigestBytes { @@ -497,6 +495,7 @@ impl X509Ref { } /// Returns the certificate's Not After validity period. + #[corresponds(X509_getm_notAfter)] pub fn not_after(&self) -> &Asn1TimeRef { unsafe { let date = X509_getm_notAfter(self.as_ptr()); @@ -506,6 +505,7 @@ impl X509Ref { } /// Returns the certificate's Not Before validity period. + #[corresponds(X509_getm_notBefore)] pub fn not_before(&self) -> &Asn1TimeRef { unsafe { let date = X509_getm_notBefore(self.as_ptr()); @@ -515,6 +515,7 @@ impl X509Ref { } /// Returns the certificate's signature + #[corresponds(X509_get0_signature)] pub fn signature(&self) -> &Asn1BitStringRef { unsafe { let mut signature = ptr::null(); @@ -525,6 +526,7 @@ impl X509Ref { } /// Returns the certificate's signature algorithm. + #[corresponds(X509_get0_signature)] pub fn signature_algorithm(&self) -> &X509AlgorithmRef { unsafe { let mut algor = ptr::null(); @@ -536,11 +538,13 @@ impl X509Ref { /// Returns the list of OCSP responder URLs specified in the certificate's Authority Information /// Access field. + #[corresponds(X509_get1_ocsp)] pub fn ocsp_responders(&self) -> Result, ErrorStack> { unsafe { cvt_p(ffi::X509_get1_ocsp(self.as_ptr())).map(|p| Stack::from_ptr(p)) } } /// Checks that this certificate issued `subject`. + #[corresponds(X509_check_issued)] pub fn issued(&self, subject: &X509Ref) -> X509VerifyResult { unsafe { let r = ffi::X509_check_issued(self.as_ptr(), subject.as_ptr()); @@ -554,10 +558,7 @@ impl X509Ref { /// are performed. /// /// Returns `true` if verification succeeds. - /// - /// This corresponds to [`X509_verify`]. - /// - /// [`X509_verify`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_verify.html + #[corresponds(X509_verify)] pub fn verify(&self, key: &PKeyRef) -> Result where T: HasPublic, @@ -566,10 +567,7 @@ impl X509Ref { } /// Returns this certificate's serial number. - /// - /// This corresponds to [`X509_get_serialNumber`]. - /// - /// [`X509_get_serialNumber`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_get_serialNumber.html + #[corresponds(X509_get_serialNumber)] pub fn serial_number(&self) -> &Asn1IntegerRef { unsafe { let r = ffi::X509_get_serialNumber(self.as_ptr()); @@ -595,20 +593,14 @@ impl X509Ref { /// Serializes the certificate into a PEM-encoded X509 structure. /// /// The output will have a header of `-----BEGIN CERTIFICATE-----`. - /// - /// This corresponds to [`PEM_write_bio_X509`]. - /// - /// [`PEM_write_bio_X509`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_write_bio_X509.html + #[corresponds(PEM_write_bio_X509)] to_pem, ffi::PEM_write_bio_X509 } to_der! { /// Serializes the certificate into a DER-encoded X509 structure. - /// - /// This corresponds to [`i2d_X509`]. - /// - /// [`i2d_X509`]: https://www.openssl.org/docs/man1.1.0/crypto/i2d_X509.html + #[corresponds(i2d_X509)] to_der, ffi::i2d_X509 } @@ -635,10 +627,7 @@ impl X509 { /// Deserializes a PEM-encoded X509 structure. /// /// The input should have a header of `-----BEGIN CERTIFICATE-----`. - /// - /// This corresponds to [`PEM_read_bio_X509`]. - /// - /// [`PEM_read_bio_X509`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_read_bio_X509.html + #[corresponds(PEM_read_bio_X509)] from_pem, X509, ffi::PEM_read_bio_X509 @@ -646,10 +635,7 @@ impl X509 { from_der! { /// Deserializes a DER-encoded X509 structure. - /// - /// This corresponds to [`d2i_X509`]. - /// - /// [`d2i_X509`]: https://www.openssl.org/docs/manmaster/man3/d2i_X509.html + #[corresponds(d2i_X509)] from_der, X509, ffi::d2i_X509, @@ -657,6 +643,7 @@ impl X509 { } /// Deserializes a list of PEM-formatted certificates. + #[corresponds(PEM_read_bio_X509)] pub fn stack_from_pem(pem: &[u8]) -> Result, ErrorStack> { unsafe { ffi::init(); diff --git a/boring/src/x509/store.rs b/boring/src/x509/store.rs index ee3fb52f..2c55d705 100644 --- a/boring/src/x509/store.rs +++ b/boring/src/x509/store.rs @@ -48,6 +48,7 @@ use crate::x509::{X509Object, X509}; use crate::{cvt, cvt_p}; use foreign_types::{ForeignType, ForeignTypeRef}; use std::mem; +use openssl_macros::corresponds; foreign_type_and_impl_send_sync! { type CType = ffi::X509_STORE; @@ -80,6 +81,7 @@ impl X509StoreBuilder { impl X509StoreBuilderRef { /// Adds a certificate to the certificate store. // FIXME should take an &X509Ref + #[corresponds(X509_STORE_add_cert)] pub fn add_cert(&mut self, cert: X509) -> Result<(), ErrorStack> { unsafe { cvt(ffi::X509_STORE_add_cert(self.as_ptr(), cert.as_ptr())).map(|_| ()) } } @@ -89,6 +91,7 @@ impl X509StoreBuilderRef { /// These locations are read from the `SSL_CERT_FILE` and `SSL_CERT_DIR` /// environment variables if present, or defaults specified at OpenSSL /// build time otherwise. + #[corresponds(X509_STORE_set_default_paths)] pub fn set_default_paths(&mut self) -> Result<(), ErrorStack> { unsafe { cvt(ffi::X509_STORE_set_default_paths(self.as_ptr())).map(|_| ()) } } @@ -98,6 +101,7 @@ impl X509StoreBuilderRef { /// This corresponds to [`X509_STORE_set_flags`]. /// /// [`X509_STORE_set_flags`]: https://www.openssl.org/docs/manmaster/man3/X509_STORE_set_flags.html + #[corresponds(X509_STORE_set_flags)] pub fn set_flags(&mut self, flags: X509Flags) { unsafe { ffi::X509_STORE_set_flags(self.as_ptr(), flags.bits()); @@ -124,6 +128,7 @@ foreign_type_and_impl_send_sync! { impl X509StoreRef { /// Get a reference to the cache of certificates in this store. + #[corresponds(X509_STORE_get0_objects)] pub fn objects(&self) -> &StackRef { unsafe { StackRef::from_ptr(ffi::X509_STORE_get0_objects(self.as_ptr())) } } diff --git a/boring/src/x509/verify.rs b/boring/src/x509/verify.rs index 7ea4ddeb..933fbfe4 100644 --- a/boring/src/x509/verify.rs +++ b/boring/src/x509/verify.rs @@ -2,6 +2,7 @@ use crate::ffi; use foreign_types::ForeignTypeRef; use libc::{c_uint, c_ulong}; use std::net::IpAddr; +use openssl_macros::corresponds; use crate::cvt; use crate::error::ErrorStack; @@ -46,6 +47,7 @@ impl X509VerifyParamRef { /// This corresponds to [`X509_VERIFY_PARAM_set_flags`]. /// /// [`X509_VERIFY_PARAM_set_flags`]: https://www.openssl.org/docs/man3.2/man3/X509_VERIFY_PARAM_set_flags.html + #[corresponds(X509_VERIFY_PARAM_set_flags)] pub fn set_flags(&mut self, flags: X509Flags) { unsafe { ffi::X509_VERIFY_PARAM_set_flags(self.as_ptr(), flags.bits()); @@ -59,6 +61,7 @@ impl X509VerifyParamRef { /// This corresponds to [`X509_VERIFY_PARAM_clear_flags`]. /// /// [`X509_VERIFY_PARAM_set_flags`]: https://www.openssl.org/docs/man3.2/man3/X509_VERIFY_PARAM_set_flags.html + #[corresponds(X509_VERIFY_PARAM_clear_flags)] pub fn clear_flags(&mut self, flags: X509Flags) { unsafe { ffi::X509_VERIFY_PARAM_clear_flags(self.as_ptr(), flags.bits()); @@ -67,10 +70,7 @@ impl X509VerifyParamRef { /// /// Set the host flags. - /// - /// This corresponds to [`X509_VERIFY_PARAM_set_hostflags`]. - /// - /// [`X509_VERIFY_PARAM_set_hostflags`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_VERIFY_PARAM_set_hostflags.html + #[corresponds(X509_VERIFY_PARAM_set_hostflags)] pub fn set_hostflags(&mut self, hostflags: X509CheckFlags) { unsafe { ffi::X509_VERIFY_PARAM_set_hostflags(self.as_ptr(), hostflags.bits()); @@ -78,10 +78,7 @@ impl X509VerifyParamRef { } /// Set the expected DNS hostname. - /// - /// This corresponds to [`X509_VERIFY_PARAM_set1_host`]. - /// - /// [`X509_VERIFY_PARAM_set1_host`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_VERIFY_PARAM_set1_host.html + #[corresponds(X509_VERIFY_PARAM_set1_host)] pub fn set_host(&mut self, host: &str) -> Result<(), ErrorStack> { unsafe { cvt(ffi::X509_VERIFY_PARAM_set1_host( @@ -94,10 +91,7 @@ impl X509VerifyParamRef { } /// Set the expected IPv4 or IPv6 address. - /// - /// This corresponds to [`X509_VERIFY_PARAM_set1_ip`]. - /// - /// [`X509_VERIFY_PARAM_set1_ip`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_VERIFY_PARAM_set1_ip.html + #[corresponds(X509_VERIFY_PARAM_set1_ip)] pub fn set_ip(&mut self, ip: IpAddr) -> Result<(), ErrorStack> { unsafe { let mut buf = [0; 16]; From b26b78611b9a39200729a950794513092cc1679b Mon Sep 17 00:00:00 2001 From: Kornel Date: Tue, 26 Nov 2024 14:34:18 +0000 Subject: [PATCH 39/41] Sync X509VerifyFlags with openssl --- boring/src/x509/verify.rs | 103 ++++++++++++++++++++++++++++++-------- 1 file changed, 82 insertions(+), 21 deletions(-) diff --git a/boring/src/x509/verify.rs b/boring/src/x509/verify.rs index 933fbfe4..1968e4db 100644 --- a/boring/src/x509/verify.rs +++ b/boring/src/x509/verify.rs @@ -1,15 +1,16 @@ use crate::ffi; -use foreign_types::ForeignTypeRef; -use libc::{c_uint, c_ulong}; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::{c_int, c_uint, c_ulong, time_t}; use std::net::IpAddr; use openssl_macros::corresponds; -use crate::cvt; use crate::error::ErrorStack; +use crate::{cvt, cvt_p}; bitflags! { /// Flags used to check an `X509` certificate. #[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)] + #[repr(transparent)] pub struct X509CheckFlags: c_uint { const ALWAYS_CHECK_SUBJECT = ffi::X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT as _; const NO_WILDCARDS = ffi::X509_CHECK_FLAG_NO_WILDCARDS as _; @@ -25,11 +26,33 @@ bitflags! { } } +#[doc(hidden)] +#[deprecated(note = "X509Flags renamed to X509VerifyFlags")] +pub use X509VerifyFlags as X509Flags; + bitflags! { /// Flags used to check an `X509` certificate. #[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)] - pub struct X509Flags: c_ulong { + #[repr(transparent)] + pub struct X509VerifyFlags: c_ulong { + const CB_ISSUER_CHECK = ffi::X509_V_FLAG_CB_ISSUER_CHECK as _; + const USE_CHECK_TIME = ffi::X509_V_FLAG_USE_CHECK_TIME as _; + const CRL_CHECK = ffi::X509_V_FLAG_CRL_CHECK as _; + const CRL_CHECK_ALL = ffi::X509_V_FLAG_CRL_CHECK_ALL as _; + const IGNORE_CRITICAL = ffi::X509_V_FLAG_IGNORE_CRITICAL as _; + const X509_STRICT = ffi::X509_V_FLAG_X509_STRICT as _; + const ALLOW_PROXY_CERTS = ffi::X509_V_FLAG_ALLOW_PROXY_CERTS as _; + const POLICY_CHECK = ffi::X509_V_FLAG_POLICY_CHECK as _; + const EXPLICIT_POLICY = ffi::X509_V_FLAG_EXPLICIT_POLICY as _; + const INHIBIT_ANY = ffi::X509_V_FLAG_INHIBIT_ANY as _; + const INHIBIT_MAP = ffi::X509_V_FLAG_INHIBIT_MAP as _; + const NOTIFY_POLICY = ffi::X509_V_FLAG_NOTIFY_POLICY as _; + const EXTENDED_CRL_SUPPORT = ffi::X509_V_FLAG_EXTENDED_CRL_SUPPORT as _; + const USE_DELTAS = ffi::X509_V_FLAG_USE_DELTAS as _; + const CHECK_SS_SIGNATURE = ffi::X509_V_FLAG_CHECK_SS_SIGNATURE as _; const TRUSTED_FIRST = ffi::X509_V_FLAG_TRUSTED_FIRST as _; + const PARTIAL_CHAIN = ffi::X509_V_FLAG_PARTIAL_CHAIN as _; + const NO_ALT_CHAINS = ffi::X509_V_FLAG_NO_ALT_CHAINS as _; } } @@ -41,30 +64,32 @@ foreign_type_and_impl_send_sync! { pub struct X509VerifyParam; } +impl X509VerifyParam { + /// Create an X509VerifyParam + #[corresponds(X509_VERIFY_PARAM_new)] + pub fn new() -> Result { + unsafe { + ffi::init(); + let handle = cvt_p(ffi::X509_VERIFY_PARAM_new())?; + Ok(Self::from_ptr(handle)) + } + } +} + impl X509VerifyParamRef { - /// Set flags. - /// - /// This corresponds to [`X509_VERIFY_PARAM_set_flags`]. - /// - /// [`X509_VERIFY_PARAM_set_flags`]: https://www.openssl.org/docs/man3.2/man3/X509_VERIFY_PARAM_set_flags.html + /// Set verification flags. #[corresponds(X509_VERIFY_PARAM_set_flags)] - pub fn set_flags(&mut self, flags: X509Flags) { + pub fn set_flags(&mut self, flags: X509VerifyFlags) { unsafe { - ffi::X509_VERIFY_PARAM_set_flags(self.as_ptr(), flags.bits()); + cvt(ffi::X509_VERIFY_PARAM_set_flags(self.as_ptr(), flags.bits())).unwrap(); } } - /// Clear flags. - /// - /// Useful to clear out default flags, such as `X509Flags::TRUSTED_FIRST` when the fips feature is off. - /// - /// This corresponds to [`X509_VERIFY_PARAM_clear_flags`]. - /// - /// [`X509_VERIFY_PARAM_set_flags`]: https://www.openssl.org/docs/man3.2/man3/X509_VERIFY_PARAM_set_flags.html + /// Clear verification flags. #[corresponds(X509_VERIFY_PARAM_clear_flags)] - pub fn clear_flags(&mut self, flags: X509Flags) { + pub fn clear_flags(&mut self, flags: X509VerifyFlags) { unsafe { - ffi::X509_VERIFY_PARAM_clear_flags(self.as_ptr(), flags.bits()); + cvt(ffi::X509_VERIFY_PARAM_clear_flags(self.as_ptr(), flags.bits())).unwrap(); } } @@ -77,19 +102,43 @@ impl X509VerifyParamRef { } } + /// Gets verification flags. + #[corresponds(X509_VERIFY_PARAM_get_flags)] + pub fn flags(&self) -> X509VerifyFlags { + let bits = unsafe { ffi::X509_VERIFY_PARAM_get_flags(self.as_ptr()) }; + X509VerifyFlags::from_bits_retain(bits) + } + /// Set the expected DNS hostname. #[corresponds(X509_VERIFY_PARAM_set1_host)] pub fn set_host(&mut self, host: &str) -> Result<(), ErrorStack> { unsafe { + // len == 0 means "run strlen" :( + let raw_host = if host.is_empty() { "\0" } else { host }; cvt(ffi::X509_VERIFY_PARAM_set1_host( self.as_ptr(), - host.as_ptr() as *const _, + raw_host.as_ptr() as *const _, host.len(), )) .map(|_| ()) } } + /// Set the expected email address. + #[corresponds(X509_VERIFY_PARAM_set1_email)] + pub fn set_email(&mut self, email: &str) -> Result<(), ErrorStack> { + unsafe { + // len == 0 means "run strlen" :( + let raw_email = if email.is_empty() { "\0" } else { email }; + cvt(ffi::X509_VERIFY_PARAM_set1_email( + self.as_ptr(), + raw_email.as_ptr() as *const _, + email.len(), + )) + .map(|_| ()) + } + } + /// Set the expected IPv4 or IPv6 address. #[corresponds(X509_VERIFY_PARAM_set1_ip)] pub fn set_ip(&mut self, ip: IpAddr) -> Result<(), ErrorStack> { @@ -113,4 +162,16 @@ impl X509VerifyParamRef { .map(|_| ()) } } + + /// Set the verification time, where time is of type time_t, traditionaly defined as seconds since the epoch + #[corresponds(X509_VERIFY_PARAM_set_time)] + pub fn set_time(&mut self, time: time_t) { + unsafe { ffi::X509_VERIFY_PARAM_set_time(self.as_ptr(), time) } + } + + /// Set the verification depth + #[corresponds(X509_VERIFY_PARAM_set_depth)] + pub fn set_depth(&mut self, depth: c_int) { + unsafe { ffi::X509_VERIFY_PARAM_set_depth(self.as_ptr(), depth) } + } } From 57fbe0f59411d39175ccdbf9225588aaba4151db Mon Sep 17 00:00:00 2001 From: Kornel Date: Tue, 26 Nov 2024 15:30:49 +0000 Subject: [PATCH 40/41] Sync X509StoreBuilder with openssl --- boring/src/aes.rs | 2 +- boring/src/ssl/test/custom_verify.rs | 4 ++-- boring/src/x509/store.rs | 35 +++++++++++++++++++++------- boring/src/x509/verify.rs | 14 ++++++++--- 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/boring/src/aes.rs b/boring/src/aes.rs index 376d356a..8a47cbaf 100644 --- a/boring/src/aes.rs +++ b/boring/src/aes.rs @@ -39,9 +39,9 @@ //! use crate::ffi; use libc::{c_int, c_uint, size_t}; +use openssl_macros::corresponds; use std::mem::MaybeUninit; use std::ptr; -use openssl_macros::corresponds; /// Provides Error handling for parsing keys. #[derive(Debug)] diff --git a/boring/src/ssl/test/custom_verify.rs b/boring/src/ssl/test/custom_verify.rs index e7feb56c..64e8f89b 100644 --- a/boring/src/ssl/test/custom_verify.rs +++ b/boring/src/ssl/test/custom_verify.rs @@ -64,7 +64,7 @@ fn untrusted_with_set_cert() { let cert = ssl.peer_certificate().unwrap(); let cert_chain = ssl.peer_cert_chain().unwrap(); - assert_eq!(store.objects().len(), 0); + assert_eq!(store.objects_len(), 0); X509StoreContext::new() .unwrap() @@ -94,7 +94,7 @@ fn trusted_with_set_cert() { let cert = ssl.peer_certificate().unwrap(); let cert_chain = ssl.peer_cert_chain().unwrap(); - assert_eq!(store.objects().len(), 1); + assert_eq!(store.objects_len(), 1); X509StoreContext::new() .unwrap() diff --git a/boring/src/x509/store.rs b/boring/src/x509/store.rs index 2c55d705..068c759c 100644 --- a/boring/src/x509/store.rs +++ b/boring/src/x509/store.rs @@ -43,12 +43,12 @@ use crate::error::ErrorStack; use crate::ffi; use crate::stack::StackRef; -use crate::x509::verify::{X509Flags, X509VerifyParamRef}; +use crate::x509::verify::{X509VerifyFlags, X509VerifyParamRef}; use crate::x509::{X509Object, X509}; use crate::{cvt, cvt_p}; use foreign_types::{ForeignType, ForeignTypeRef}; -use std::mem; use openssl_macros::corresponds; +use std::mem; foreign_type_and_impl_send_sync! { type CType = ffi::X509_STORE; @@ -96,15 +96,11 @@ impl X509StoreBuilderRef { unsafe { cvt(ffi::X509_STORE_set_default_paths(self.as_ptr())).map(|_| ()) } } - /// Sets verify flags. - /// - /// This corresponds to [`X509_STORE_set_flags`]. - /// - /// [`X509_STORE_set_flags`]: https://www.openssl.org/docs/manmaster/man3/X509_STORE_set_flags.html + /// Sets certificate chain validation related flags. #[corresponds(X509_STORE_set_flags)] - pub fn set_flags(&mut self, flags: X509Flags) { + pub fn set_flags(&mut self, flags: X509VerifyFlags) { unsafe { - ffi::X509_STORE_set_flags(self.as_ptr(), flags.bits()); + cvt(ffi::X509_STORE_set_flags(self.as_ptr(), flags.bits())).unwrap(); } } @@ -116,6 +112,12 @@ impl X509StoreBuilderRef { pub fn verify_param_mut(&mut self) -> &mut X509VerifyParamRef { unsafe { X509VerifyParamRef::from_ptr_mut(ffi::X509_STORE_get0_param(self.as_ptr())) } } + + /// Sets certificate chain validation related parameters. + #[corresponds(X509_STORE_set1_param)] + pub fn set_param(&mut self, param: &X509VerifyParamRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::X509_STORE_set1_param(self.as_ptr(), param.as_ptr())).map(|_| ()) } + } } foreign_type_and_impl_send_sync! { @@ -127,9 +129,24 @@ foreign_type_and_impl_send_sync! { } impl X509StoreRef { + /// **Warning: this method is unsound** + /// /// Get a reference to the cache of certificates in this store. + /// + /// # Safety + /// References may be invalidated by any access to the shared cache. + #[deprecated( + note = "This method is unsound https://github.com/sfackler/rust-openssl/issues/2096" + )] #[corresponds(X509_STORE_get0_objects)] pub fn objects(&self) -> &StackRef { unsafe { StackRef::from_ptr(ffi::X509_STORE_get0_objects(self.as_ptr())) } } + + /// For testing only, where it doesn't have to expose an unsafe pointer + #[cfg(test)] + #[allow(deprecated)] + pub fn objects_len(&self) -> usize { + self.objects().len() + } } diff --git a/boring/src/x509/verify.rs b/boring/src/x509/verify.rs index 1968e4db..7546442d 100644 --- a/boring/src/x509/verify.rs +++ b/boring/src/x509/verify.rs @@ -1,8 +1,8 @@ use crate::ffi; use foreign_types::{ForeignType, ForeignTypeRef}; use libc::{c_int, c_uint, c_ulong, time_t}; -use std::net::IpAddr; use openssl_macros::corresponds; +use std::net::IpAddr; use crate::error::ErrorStack; use crate::{cvt, cvt_p}; @@ -81,7 +81,11 @@ impl X509VerifyParamRef { #[corresponds(X509_VERIFY_PARAM_set_flags)] pub fn set_flags(&mut self, flags: X509VerifyFlags) { unsafe { - cvt(ffi::X509_VERIFY_PARAM_set_flags(self.as_ptr(), flags.bits())).unwrap(); + cvt(ffi::X509_VERIFY_PARAM_set_flags( + self.as_ptr(), + flags.bits(), + )) + .unwrap(); } } @@ -89,7 +93,11 @@ impl X509VerifyParamRef { #[corresponds(X509_VERIFY_PARAM_clear_flags)] pub fn clear_flags(&mut self, flags: X509VerifyFlags) { unsafe { - cvt(ffi::X509_VERIFY_PARAM_clear_flags(self.as_ptr(), flags.bits())).unwrap(); + cvt(ffi::X509_VERIFY_PARAM_clear_flags( + self.as_ptr(), + flags.bits(), + )) + .unwrap(); } } From 4685af00bbe12446f319fdbd1287c3d19951394e Mon Sep 17 00:00:00 2001 From: Rushil Mehra Date: Thu, 28 Nov 2024 12:24:29 -0800 Subject: [PATCH 41/41] Release 4.13.0 --- Cargo.toml | 8 ++++---- RELEASE_NOTES | 8 ++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bad88ec2..e4dd842c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ resolver = "2" [workspace.package] -version = "4.12.0" +version = "4.13.0" repository = "https://github.com/cloudflare/boring" edition = "2021" @@ -19,9 +19,9 @@ tag-prefix = "" publish = false [workspace.dependencies] -boring-sys = { version = "4.12.0", path = "./boring-sys" } -boring = { version = "4.12.0", path = "./boring" } -tokio-boring = { version = "4.12.0", path = "./tokio-boring" } +boring-sys = { version = "4.13.0", path = "./boring-sys" } +boring = { version = "4.13.0", path = "./boring" } +tokio-boring = { version = "4.13.0", path = "./tokio-boring" } bindgen = { version = "0.70.1", default-features = false, features = ["runtime"] } bytes = "1" diff --git a/RELEASE_NOTES b/RELEASE_NOTES index b6405f43..e59bc8e1 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,11 @@ +4.13.0 +- 2024-11-26 Sync X509StoreBuilder with openssl +- 2024-11-26 Sync X509VerifyFlags with openssl +- 2021-11-21 More corresponds from openssl +- 2024-11-28 Work around Rust settings inconsistent iOS SDK version +- 2024-11-28 Clippy +- 2024-03-11 Fix Windows build + 4.12.0 - 2024-11-20 Add bindings for SSL_CB_ACCEPT_EXIT and SSL_CB_CONNECT_EXIT - 2024-10-22 (ci): brew link x86 toolchain for macos13 runner