From 1eaf29ef6c49ff5c98430c143acf79b7e4d3630e Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Thu, 18 Jun 2026 15:51:58 +0200 Subject: [PATCH 001/349] Remove direct includes of windows.h where possible It should be included via e_os.h instead. Reviewed-by: Milan Broz Reviewed-by: Tim Hudson Reviewed-by: Simo Sorce Reviewed-by: Frederik Wedel-Heinen Reviewed-by: Neil Horman MergeDate: Sat Jun 20 11:00:42 2026 (Merged from https://github.com/openssl/openssl/pull/31587) --- crypto/LPdir_win.c | 2 +- crypto/armcap.c | 2 +- crypto/async/arch/async_win.c | 2 +- crypto/async/async_local.h | 2 +- crypto/comp/c_brotli.c | 5 +---- crypto/comp/c_zlib.c | 5 +---- crypto/comp/c_zstd.c | 11 +---------- crypto/mem_sec.c | 1 - crypto/rand/rand_deprecated.c | 1 - crypto/rand/randfile.c | 1 - crypto/sleep.c | 1 - crypto/thread/arch/thread_win.c | 4 ++-- crypto/threads_win.c | 7 +++---- crypto/ui/ui_openssl.c | 1 - include/internal/thread_arch.h | 5 +---- providers/implementations/rands/seeding/rand_win.c | 2 +- 16 files changed, 14 insertions(+), 38 deletions(-) diff --git a/crypto/LPdir_win.c b/crypto/LPdir_win.c index bc5cec35d9..e8141cab86 100644 --- a/crypto/LPdir_win.c +++ b/crypto/LPdir_win.c @@ -36,8 +36,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include +#include "internal/e_os.h" #include "internal/numbers.h" #ifndef LPDIR_H #include "LPdir.h" diff --git a/crypto/armcap.c b/crypto/armcap.c index cdb8336b13..bd978dc86d 100644 --- a/crypto/armcap.c +++ b/crypto/armcap.c @@ -19,7 +19,7 @@ #endif #include "internal/cryptlib.h" #ifdef _WIN32 -#include +#include "internal/e_os.h" #else #include #endif diff --git a/crypto/async/arch/async_win.c b/crypto/async/arch/async_win.c index 849da5c3c4..2ca4ed6a93 100644 --- a/crypto/async/arch/async_win.c +++ b/crypto/async/arch/async_win.c @@ -12,7 +12,7 @@ #ifdef ASYNC_WIN -#include +#include "internal/e_os.h" #include "internal/cryptlib.h" int ASYNC_is_capable(void) diff --git a/crypto/async/async_local.h b/crypto/async/async_local.h index e1d1113464..f10a6745f8 100644 --- a/crypto/async/async_local.h +++ b/crypto/async/async_local.h @@ -21,6 +21,7 @@ #include #include +#include typedef struct async_ctx_st async_ctx; typedef struct async_pool_st async_pool; @@ -29,7 +30,6 @@ typedef struct async_pool_st async_pool; #define ASYNC_WIN #define ASYNC_ARCH -#include #include "internal/cryptlib.h" typedef struct async_fibre_st { diff --git a/crypto/comp/c_brotli.c b/crypto/comp/c_brotli.c index d262ec6a4e..9c99e066b7 100644 --- a/crypto/comp/c_brotli.c +++ b/crypto/comp/c_brotli.c @@ -13,6 +13,7 @@ #include #include #include +#include "internal/e_os.h" #include "internal/comp.h" #include #include "crypto/cryptlib.h" @@ -46,10 +47,6 @@ static void brotli_free(void *opaque, void *address) * work. Therefore, all BROTLI routines are loaded at run time * and we do not link to a .LIB file when BROTLI_SHARED is set. */ -#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) -#include -#endif - #ifdef BROTLI_SHARED #include "internal/dso.h" diff --git a/crypto/comp/c_zlib.c b/crypto/comp/c_zlib.c index c90c7b090d..4af4e30b64 100644 --- a/crypto/comp/c_zlib.c +++ b/crypto/comp/c_zlib.c @@ -11,6 +11,7 @@ #include #include #include +#include "internal/e_os.h" #include "internal/comp.h" #include #include "crypto/cryptlib.h" @@ -64,10 +65,6 @@ static COMP_METHOD zlib_stateful_method = { * work. Therefore, all ZLIB routines are loaded at run time * and we do not link to a .LIB file when ZLIB_SHARED is set. */ -#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) -#include -#endif /* !(OPENSSL_SYS_WINDOWS || \ - * OPENSSL_SYS_WIN32) */ #ifdef ZLIB_SHARED #include "internal/dso.h" diff --git a/crypto/comp/c_zstd.c b/crypto/comp/c_zstd.c index a9c881f8f9..c5c6cd6eef 100644 --- a/crypto/comp/c_zstd.c +++ b/crypto/comp/c_zstd.c @@ -16,6 +16,7 @@ #include #include #include +#include "internal/e_os.h" #include "internal/comp.h" #include #include "crypto/cryptlib.h" @@ -62,16 +63,6 @@ static ZSTD_customMem zstd_mem_funcs = { }; #endif -/* - * When OpenSSL is built on Windows, we do not want to require that - * the LIBZSTD.DLL be available in order for the OpenSSL DLLs to - * work. Therefore, all ZSTD routines are loaded at run time - * and we do not link to a .LIB file when ZSTD_SHARED is set. - */ -#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) -#include -#endif - #ifdef ZSTD_SHARED #include "internal/dso.h" diff --git a/crypto/mem_sec.c b/crypto/mem_sec.c index 3c5de4de8f..a727d2008d 100644 --- a/crypto/mem_sec.c +++ b/crypto/mem_sec.c @@ -23,7 +23,6 @@ #ifndef OPENSSL_NO_SECURE_MEMORY #if defined(_WIN32) -#include #if defined(WINAPI_FAMILY_PARTITION) #if !defined(WINAPI_PARTITION_SYSTEM) #define WINAPI_PARTITION_SYSTEM 0 diff --git a/crypto/rand/rand_deprecated.c b/crypto/rand/rand_deprecated.c index d838f3cd70..07b6ff04e8 100644 --- a/crypto/rand/rand_deprecated.c +++ b/crypto/rand/rand_deprecated.c @@ -12,7 +12,6 @@ #include #if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) -#include #ifndef OPENSSL_NO_DEPRECATED_1_1_0 #define DEPRECATED_RAND_FUNCTIONS_DEFINED diff --git a/crypto/rand/randfile.c b/crypto/rand/randfile.c index ab059bb5e8..b1881dac97 100644 --- a/crypto/rand/randfile.c +++ b/crypto/rand/randfile.c @@ -36,7 +36,6 @@ #include #include #if defined(_WIN32) && !defined(_WIN32_WCE) -#include #include #define stat _stat #define chmod _chmod diff --git a/crypto/sleep.c b/crypto/sleep.c index 9273995be6..3d8be852c9 100644 --- a/crypto/sleep.c +++ b/crypto/sleep.c @@ -67,7 +67,6 @@ static void ossl_sleep_millis(uint64_t millis) #endif #elif defined(_WIN32) && !defined(OPENSSL_SYS_UEFI) -#include static void ossl_sleep_millis(uint64_t millis) { diff --git a/crypto/thread/arch/thread_win.c b/crypto/thread/arch/thread_win.c index b26a1d917a..1026ed3de3 100644 --- a/crypto/thread/arch/thread_win.c +++ b/crypto/thread/arch/thread_win.c @@ -7,11 +7,11 @@ * https://www.openssl.org/source/license.html */ -#include +#include "internal/thread_arch.h" +#include "internal/e_os.h" #if defined(OPENSSL_THREADS_WINNT) #include -#include static unsigned __stdcall thread_start_thunk(LPVOID vthread) { diff --git a/crypto/threads_win.c b/crypto/threads_win.c index 4fedf24e90..448c909630 100644 --- a/crypto/threads_win.c +++ b/crypto/threads_win.c @@ -7,14 +7,13 @@ * https://www.openssl.org/source/license.html */ -#if defined(_WIN32) -#include +#include "internal/e_os.h" + #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600 #define USE_RWLOCK #endif -#endif -#include +#include #include #include #include "internal/common.h" diff --git a/crypto/ui/ui_openssl.c b/crypto/ui/ui_openssl.c index 1da5369287..8715ff068e 100644 --- a/crypto/ui/ui_openssl.c +++ b/crypto/ui/ui_openssl.c @@ -60,7 +60,6 @@ #endif #ifdef WIN_CONSOLE_BUG -#include #ifndef OPENSSL_SYS_WINCE #include #endif diff --git a/include/internal/thread_arch.h b/include/internal/thread_arch.h index d7fc08a8af..73e6e0d1e7 100644 --- a/include/internal/thread_arch.h +++ b/include/internal/thread_arch.h @@ -11,12 +11,9 @@ #define OSSL_INTERNAL_THREAD_ARCH_H #include #include +#include "internal/e_os.h" #include "internal/time.h" -#if defined(_WIN32) -#include -#endif - #if defined(OPENSSL_THREADS) && defined(OPENSSL_SYS_UNIX) #define OPENSSL_THREADS_POSIX #elif defined(OPENSSL_THREADS) && defined(OPENSSL_SYS_VMS) diff --git a/providers/implementations/rands/seeding/rand_win.c b/providers/implementations/rands/seeding/rand_win.c index 63b523b729..9b889e38bc 100644 --- a/providers/implementations/rands/seeding/rand_win.c +++ b/providers/implementations/rands/seeding/rand_win.c @@ -7,6 +7,7 @@ * https://www.openssl.org/source/license.html */ +#include "internal/e_os.h" /* For windows.h */ #include "internal/cryptlib.h" #include #include "crypto/rand_pool.h" @@ -19,7 +20,6 @@ #error "Unsupported seeding method configured; must be os" #endif -#include /* On Windows Vista or higher use BCrypt instead of the legacy CryptoAPI */ #if defined(_MSC_VER) && _MSC_VER > 1500 /* 1500 = Visual Studio 2008 */ \ && defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 From 4d9e2a5797ec74a20426a6185df01d10a770ccc5 Mon Sep 17 00:00:00 2001 From: Loganaden Velvindron Date: Wed, 10 Jun 2026 17:18:19 +0400 Subject: [PATCH 002/349] ssl/quic/quic_ackm.c: fix use after free for apkt in ackm_on_pkts_acked() Store in_flight flag in a local variable for later use, as apkt->on_acked() may free apkt. Fixes: 427a02ad0a71 "QUIC ACKM: Don't record non-inflight packets in CC" Signed-off-by: Loganaden Velvindron Reviewed-by: Eugene Syromiatnikov Reviewed-by: Norbert Pocs MergeDate: Sun Jun 21 13:55:08 2026 (Merged from https://github.com/openssl/openssl/pull/31447) --- ssl/quic/quic_ackm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ssl/quic/quic_ackm.c b/ssl/quic/quic_ackm.c index 3d419c478b..d1ac3b88e9 100644 --- a/ssl/quic/quic_ackm.c +++ b/ssl/quic/quic_ackm.c @@ -1003,6 +1003,7 @@ static void ackm_on_pkts_acked(OSSL_ACKM *ackm, const OSSL_ACKM_TX_PKT *apkt) const OSSL_ACKM_TX_PKT *anext; QUIC_PN last_pn_acked = 0; OSSL_CC_ACK_INFO ainfo = { 0 }; + unsigned int is_inflight; for (; apkt != NULL; apkt = anext) { if (apkt->is_inflight) { @@ -1027,10 +1028,11 @@ static void ackm_on_pkts_acked(OSSL_ACKM *ackm, const OSSL_ACKM_TX_PKT *apkt) ainfo.tx_time = apkt->time; ainfo.tx_size = apkt->num_bytes; + is_inflight = apkt->is_inflight; anext = apkt->anext; apkt->on_acked(apkt->cb_arg); /* may free apkt */ - if (apkt->is_inflight) + if (is_inflight) ackm->cc_method->on_data_acked(ackm->cc_data, &ainfo); } } From 036db3a8ef21cd5a5c1b89808b6ddab7cb5d4f0c Mon Sep 17 00:00:00 2001 From: Ingo Franzki Date: Fri, 12 Jun 2026 14:58:30 +0200 Subject: [PATCH 003/349] s390x: Don't ignore errors from s390x_HMAC_init() Currently errors from s390x_HMAC_init() are silently ignored and the software path is used as fallback. Change this to only take the software path if s390x_HMAC_init() returns -1 to indicate that it does not support the HMAC acceleration. In case of errors, return them to the caller. Errors could be memory allocation failures or errors during digest operations. Those should not be ignored, but reported as failure. This also fixes failures of the test_rsa_pkcs1_mfail test case that found the memory allocation failures that got ignored. References: https://github.com/openssl/openssl/issues/31480 Signed-off-by: Ingo Franzki Reviewed-by: Tomas Mraz Reviewed-by: Dmitry Belyavskiy Reviewed-by: Eugene Syromiatnikov MergeDate: Sun Jun 21 14:19:53 2026 (Merged from https://github.com/openssl/openssl/pull/31482) --- crypto/hmac/hmac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/hmac/hmac.c b/crypto/hmac/hmac.c index 400dde4d40..ab0f2b5ebd 100644 --- a/crypto/hmac/hmac.c +++ b/crypto/hmac/hmac.c @@ -54,7 +54,7 @@ int HMAC_Init_ex(HMAC_CTX *ctx, const void *key, int len, #ifdef OPENSSL_HMAC_S390X rv = s390x_HMAC_init(ctx, key, len); - if (rv >= 1) + if (rv != -1) return rv; #endif From 1a1d088d2ace4fc655c5f464405ca62c9df2d118 Mon Sep 17 00:00:00 2001 From: Ingo Franzki Date: Fri, 12 Jun 2026 15:30:54 +0200 Subject: [PATCH 004/349] s390x: Don't ignore errors from s390x_mod_exp_hw() and s390x_crt() Currently errors from s390x_mod_exp_hw() and s390x_crt() are silently ignored and the software path is used as fallback. Change this to only take the software path if s390x_mod_exp_hw() and s390x_crt() returns 0 to indicate that it does not support the RSA acceleration. In case of errors, return them to the caller. Errors could be memory allocation failures or errors during BIGNUM calls. Those should not be ignored, but reported as failure. Note that it can happen that the ioctl's fail, but this is not to be reported as error. Those are situations where for example no suitable crypto adapter is available, or the file descriptor has been closed by a sandbox. Those situations disable the RSA acceleration for further RSA requests, but the current operation should still be performed via the software fallback. For cases where the RSA key size is too large for acceleration, the operation must also be performed via the software fallback, and not reported as an error. This also fixes failures of the test_rsa_pkcs1_mfail test case that found the memory allocation failures that got ignored. Resolves: https://github.com/openssl/openssl/issues/31480 Signed-off-by: Ingo Franzki Reviewed-by: Tomas Mraz Reviewed-by: Dmitry Belyavskiy Reviewed-by: Eugene Syromiatnikov MergeDate: Sun Jun 21 14:19:54 2026 (Merged from https://github.com/openssl/openssl/pull/31482) --- crypto/bn/bn_s390x.c | 39 +++++++++++++++++++++++++++++---------- crypto/rsa/rsa_ossl.c | 7 +++++-- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/crypto/bn/bn_s390x.c b/crypto/bn/bn_s390x.c index ef96161ced..4e7aba35f6 100644 --- a/crypto/bn/bn_s390x.c +++ b/crypto/bn/bn_s390x.c @@ -20,16 +20,20 @@ #include #include +/* + * Returns 1 for success, 0 for failure, and -1 to tell the caller to use the + * SW-fallback. + */ static int s390x_mod_exp_hw(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m) { struct ica_rsa_modexpo me; unsigned char *buffer; size_t size; - int res = 0; + int res = -1; if (OPENSSL_s390xcex == -1 || OPENSSL_s390xcex_nodev) - return 0; + return -1; size = BN_num_bytes(m); buffer = OPENSSL_calloc(size, 4); if (buffer == NULL) @@ -42,11 +46,15 @@ static int s390x_mod_exp_hw(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, me.n_modulus = buffer + 3 * size; if (BN_bn2binpad(a, me.inputdata, size) == -1 || BN_bn2binpad(p, me.b_key, size) == -1 - || BN_bn2binpad(m, me.n_modulus, size) == -1) + || BN_bn2binpad(m, me.n_modulus, size) == -1) { + res = 0; goto dealloc; + } if (ioctl(OPENSSL_s390xcex, ICARSAMODEXPO, &me) != -1) { if (BN_bin2bn(me.outputdata, size, r) != NULL) res = 1; + else + res = 0; } else if (errno == EBADF || errno == ENOTTY) { /* * In this cases, someone (e.g. a sandbox) closed the fd. @@ -71,27 +79,34 @@ dealloc: int s390x_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx) { - if (s390x_mod_exp_hw(r, a, p, m) == 1) - return 1; - return BN_mod_exp_mont(r, a, p, m, ctx, m_ctx); + int rc; + + rc = s390x_mod_exp_hw(r, a, p, m); + if (rc < 0) + return BN_mod_exp_mont(r, a, p, m, ctx, m_ctx); + return rc; } +/* + * Returns 1 for success, 0 for failure, and -1 to tell the caller to use the + * SW-fallback. + */ int s390x_crt(BIGNUM *r, const BIGNUM *i, const BIGNUM *p, const BIGNUM *q, const BIGNUM *dmp, const BIGNUM *dmq, const BIGNUM *iqmp) { struct ica_rsa_modexpo_crt crt; unsigned char *buffer, *part; size_t size, plen, qlen; - int res = 0; + int res = -1; if (OPENSSL_s390xcex == -1 || OPENSSL_s390xcex_nodev) - return 0; + return -1; /*- * Hardware-accelerated CRT can only deal with p>q. Fall back to * software in the (hopefully rare) other cases. */ if (BN_ucmp(p, q) != 1) - return 0; + return -1; plen = BN_num_bytes(p); qlen = BN_num_bytes(q); size = (plen > qlen ? plen : qlen); @@ -119,11 +134,15 @@ int s390x_crt(BIGNUM *r, const BIGNUM *i, const BIGNUM *p, const BIGNUM *q, || BN_bn2binpad(q, crt.nq_prime, size) == -1 || BN_bn2binpad(dmp, crt.bp_key, size + 8) == -1 || BN_bn2binpad(dmq, crt.bq_key, size) == -1 - || BN_bn2binpad(iqmp, crt.u_mult_inv, size + 8) == -1) + || BN_bn2binpad(iqmp, crt.u_mult_inv, size + 8) == -1) { + res = 0; goto dealloc; + } if (ioctl(OPENSSL_s390xcex, ICARSACRT, &crt) != -1) { if (BN_bin2bn(crt.outputdata, crt.outputdatalength, r) != NULL) res = 1; + else + res = 0; } else if (errno == EBADF || errno == ENOTTY) { /* * In this cases, someone (e.g. a sandbox) closed the fd. diff --git a/crypto/rsa/rsa_ossl.c b/crypto/rsa/rsa_ossl.c index b883baa58f..674ee4b10d 100644 --- a/crypto/rsa/rsa_ossl.c +++ b/crypto/rsa/rsa_ossl.c @@ -1184,9 +1184,12 @@ static int rsa_ossl_finish(RSA *rsa) static int rsa_ossl_s390x_mod_exp(BIGNUM *r0, const BIGNUM *i, RSA *rsa, BN_CTX *ctx) { + int rc; + if (rsa->version != RSA_ASN1_VERSION_MULTI) { - if (s390x_crt(r0, i, rsa->p, rsa->q, rsa->dmp1, rsa->dmq1, rsa->iqmp) == 1) - return 1; + rc = s390x_crt(r0, i, rsa->p, rsa->q, rsa->dmp1, rsa->dmq1, rsa->iqmp); + if (rc >= 0) + return rc; } return rsa_ossl_mod_exp(r0, i, rsa, ctx); } From 66c36af0777392c74cfc1a4a64938f19d6b657e5 Mon Sep 17 00:00:00 2001 From: sunnyqeen Date: Wed, 7 Feb 2024 11:58:44 +0100 Subject: [PATCH 005/349] Fix unix Makefile template to avoid command line too long error on windows On cygwin/msys systems that run on Windows, command line length is limited. using response file instead of putting objects on the command line will avoid this error CLA: trivial Reviewed-by: Richard Levitte Reviewed-by: Tomas Mraz Reviewed-by: Tim Hudson Reviewed-by: Neil Horman MergeDate: Sun Jun 21 15:56:08 2026 (Merged from https://github.com/openssl/openssl/pull/23077) --- Configurations/unix-Makefile.tmpl | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Configurations/unix-Makefile.tmpl b/Configurations/unix-Makefile.tmpl index de345a5e8a..f27517e565 100644 --- a/Configurations/unix-Makefile.tmpl +++ b/Configurations/unix-Makefile.tmpl @@ -11,6 +11,7 @@ our $makedepcmd = platform->makedepcmd(); sub windowsdll { $config{target} =~ /^(?:Cygwin|mingw)/ } + sub run_on_windows { $^O =~ /^(?:cygwin|msys|MSWin32)/ } # Shared AIX support is special. We put libcrypto[64].so.ver into # libcrypto.a and use libcrypto_a.a as static one, unless using @@ -1895,13 +1896,27 @@ $import: $full EOF } } - $recipe .= <<"EOF"; + if (!run_on_windows()) { + $recipe .= <<"EOF"; $full: $fulldeps \$(CC) \$(LIB_CFLAGS) $linkflags\$(LIB_LDFLAGS)$shared_soname$shared_imp \\ -o $full$shared_def \\ $fullobjs \\ $linklibs \$(LIB_EX_LIBS) EOF + } else { + $recipe .= <<"EOF"; +$full: $fulldeps + \$(file >\$@.lst, \\ + $fullobjs \\ + ) + \$(CC) \$(LIB_CFLAGS) $linkflags\$(LIB_LDFLAGS)$shared_soname$shared_imp \\ + -o $full$shared_def \\ + @\$@.lst \\ + $linklibs \$(LIB_EX_LIBS) + rm -f \$@.lst +EOF + } if (windowsdll()) { $recipe .= <<"EOF"; rm -f apps/$full From bb3d8b9c52a40af636e88dcaa28fc4c428b4a8ec Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 15 Jun 2026 18:11:30 +0200 Subject: [PATCH 006/349] apps: test pkeyutl -derive peer key setup This tests currently uncovered setup_peer function and some failure scenarios in it. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Matt Caswell Reviewed-by: Nikola Pajkovsky Reviewed-by: Neil Horman MergeDate: Sun Jun 21 16:04:59 2026 (Merged from https://github.com/openssl/openssl/pull/31518) --- test/recipes/20-test_pkeyutl.t | 51 +++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/test/recipes/20-test_pkeyutl.t b/test/recipes/20-test_pkeyutl.t index 09797eb321..4458887f5c 100644 --- a/test/recipes/20-test_pkeyutl.t +++ b/test/recipes/20-test_pkeyutl.t @@ -17,7 +17,7 @@ use File::Compare qw/compare_text compare/; setup("test_pkeyutl"); -plan tests => 30; +plan tests => 31; # For the tests below we use the cert itself as the TBS file @@ -370,3 +370,52 @@ subtest "pkeyutl -pkeyopt_passin" => sub { "Fail on unknown pkey option via passin"); }); }; + +SKIP: { + skip "EC is not supported by this OpenSSL build", 1 + if disabled("ec"); + + subtest "pkeyutl -derive peer key setup" => sub { + my $eckey = srctop_file("test", "testec-p256.pem"); + my $ecpub = srctop_file("test", "testecpub-p256.pem"); + my $rsapub = srctop_file("test", "testrsapub.pem"); + + plan tests => 5; + + # ECDH derive against a matching peer public key + ok(run(app(['openssl', 'pkeyutl', '-derive', + '-inkey', $eckey, '-peerkey', $ecpub, + '-out', 'derive_secret.bin'])), + "Derive shared secret with matching peer key"); + + # setup_peer: peer key file cannot be loaded + with({ exit_checker => sub { return shift == 1; } }, + sub { + ok(run(app(['openssl', 'pkeyutl', '-derive', + '-inkey', $eckey, '-peerkey', 'no_such_peer.pem'])), + "Fail when the peer key cannot be read"); + }); + + # setup_peer: peer key type does not match the private key type + with({ exit_checker => sub { return shift == 1; } }, + sub { + ok(run(app(['openssl', 'pkeyutl', '-derive', + '-inkey', $eckey, '-peerkey', $rsapub])), + "Fail when peer key type does not match private key"); + }); + + # main: -derive requires -peerkey + with({ exit_checker => sub { return shift == 1; } }, + sub { + ok(run(app(['openssl', 'pkeyutl', '-derive', '-inkey', $eckey])), + "Fail when -derive is given without -peerkey"); + }); + + # main: -peerkey is only valid with -derive + with({ exit_checker => sub { return shift == 1; } }, + sub { + ok(run(app(['openssl', 'pkeyutl', '-inkey', $eckey, '-peerkey', $ecpub])), + "Fail when -peerkey is given without -derive"); + }); + }; +} From b298b4446601301316718e9cd4fc3ecb9856ea8d Mon Sep 17 00:00:00 2001 From: Shmael13 Date: Mon, 15 Jun 2026 21:07:37 +0500 Subject: [PATCH 007/349] demos/http3: fix missing NUL terminator on h3ssl->url In the HTTP/3 demo server's :path handler, when the path value does not begin with '/', the value is copied into the fixed-size url[MAXURL] buffer with memcpy(h3ssl->url, vvalue.base, len) and no terminator is written. len is capped at MAXURL, so a :path value of MAXURL or more bytes fills the entire buffer, overwriting the zeroes from the preceding memset and leaving url without a NUL terminator. The buffer is later used as a C string by strcat() and strcmp() when building the file name, resulting in a heap out-of-bounds read and a possible overflow of the filename[PATH_MAX] buffer. This is reachable from a client-supplied :path header. Cap the length at MAXURL - 1 so that the trailing byte zeroed by the memset always remains, guaranteeing url is NUL-terminated in every branch. The '/'-prefixed branches are unaffected as they already write an explicit terminator within the smaller bound. Fixes #31516 Reviewed-by: Tim Hudson Reviewed-by: Norbert Pocs Reviewed-by: Nikola Pajkovsky Reviewed-by: Neil Horman MergeDate: Sun Jun 21 16:19:08 2026 (Merged from https://github.com/openssl/openssl/pull/31520) --- demos/http3/ossl-nghttp3-demo-server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demos/http3/ossl-nghttp3-demo-server.c b/demos/http3/ossl-nghttp3-demo-server.c index 92cc10c067..4529e35268 100644 --- a/demos/http3/ossl-nghttp3-demo-server.c +++ b/demos/http3/ossl-nghttp3-demo-server.c @@ -291,7 +291,7 @@ static int on_recv_header(nghttp3_conn *conn, int64_t stream_id, int32_t token, fprintf(stdout, "\n"); if (token == NGHTTP3_QPACK_TOKEN__PATH) { - int len = (((vvalue.len) < (MAXURL)) ? (vvalue.len) : (MAXURL)); + int len = (((vvalue.len) < (MAXURL)) ? (vvalue.len) : (MAXURL - 1)); memset(h3ssl->url, 0, sizeof(h3ssl->url)); if (vvalue.base[0] == '/') { From 7a5476687ef1b1efd14fabb05f0a05ba494d1c1e Mon Sep 17 00:00:00 2001 From: Carlo Deutschmann Date: Thu, 18 Jun 2026 19:36:47 +0200 Subject: [PATCH 008/349] Fix spelling mistakes in documentation Correct a number of typos found in the man pages: * doc/man3/SSL_CTX_set1_curves.pod: attenion -> attention * doc/man3/CMS_EncryptedData_decrypt.pod: decypted -> decrypted * doc/man3/X509_get_default_cert_file.pod: delimeter -> delimiter * doc/man3/SSL_CTX_set_msg_callback.pod: diagostic -> diagnostic * doc/man7/openssl-core_dispatch.h.pod: dipatch -> dispatch * doc/man3/BIO_s_datagram.pod: hecause -> because * doc/man3/ASN1_aux_cb.pod: auxiliarly -> auxiliary CLA: trivial Fixes: 3d9d1ce52904 "Add documentation for newly added ASN1 functions" Fixes: 408622b73a18 "BIO_s_dgram: add documentation and hazard warnings" Fixes: e2f6960fc5fe "CMS: Export CMS_EnvelopedData and add CMS_EnvelopedData_decrypt()" Fixes: 9efd7e9e98a9 "Fix group tuple handling in DEFAULT expansion" Fixes: bfcf1356f9fd "Update the msg_callback documentation" Fixes: 606e0426a148 "Add support for loading root CAs from Windows crypto API" Fixes: 329b2a2cde48 "DOCS: add openssl-core_numbers.h(7)" Reviewed-by: Paul Dale Reviewed-by: Kurt Roeckx Reviewed-by: Tim Hudson Reviewed-by: Eugene Syromiatnikov MergeDate: Sun Jun 21 22:50:14 2026 (Merged from https://github.com/openssl/openssl/pull/31596) --- doc/man3/ASN1_aux_cb.pod | 2 +- doc/man3/BIO_s_datagram.pod | 2 +- doc/man3/CMS_EncryptedData_decrypt.pod | 2 +- doc/man3/SSL_CTX_set1_curves.pod | 2 +- doc/man3/SSL_CTX_set_msg_callback.pod | 2 +- doc/man3/X509_get_default_cert_file.pod | 2 +- doc/man7/openssl-core_dispatch.h.pod | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/man3/ASN1_aux_cb.pod b/doc/man3/ASN1_aux_cb.pod index 9963ea1350..68b230ca1a 100644 --- a/doc/man3/ASN1_aux_cb.pod +++ b/doc/man3/ASN1_aux_cb.pod @@ -59,7 +59,7 @@ Arbitrary application data =item I -Flags which indicate the auxiliarly functionality supported. +Flags which indicate the auxiliary functionality supported. The B flag indicates that objects support reference counting. diff --git a/doc/man3/BIO_s_datagram.pod b/doc/man3/BIO_s_datagram.pod index 634c3a9af4..b28dcf088c 100644 --- a/doc/man3/BIO_s_datagram.pod +++ b/doc/man3/BIO_s_datagram.pod @@ -65,7 +65,7 @@ the underlying socket is configured and how it is to be used; see below. =item -Use of BIO_s_datagram() with an unconnected network socket is hazardous hecause +Use of BIO_s_datagram() with an unconnected network socket is hazardous because any successful call to BIO_read() results in the peer address used for any subsequent call to BIO_write() being set to the source address of the datagram received by that call to BIO_read(). Thus, unless the caller calls diff --git a/doc/man3/CMS_EncryptedData_decrypt.pod b/doc/man3/CMS_EncryptedData_decrypt.pod index 80bbdcc95f..f7375f2c58 100644 --- a/doc/man3/CMS_EncryptedData_decrypt.pod +++ b/doc/man3/CMS_EncryptedData_decrypt.pod @@ -46,7 +46,7 @@ are used when retrieving algorithms from providers. CMS_EncryptedData_decrypt() returns 0 if an error occurred otherwise returns 1. CMS_EnvelopedData_decrypt() returns NULL if an error occurred, -otherwise a BIO containing the decypted content. +otherwise a BIO containing the decrypted content. =head1 SEE ALSO diff --git a/doc/man3/SSL_CTX_set1_curves.pod b/doc/man3/SSL_CTX_set1_curves.pod index 02a6e6ea10..64321df5f4 100755 --- a/doc/man3/SSL_CTX_set1_curves.pod +++ b/doc/man3/SSL_CTX_set1_curves.pod @@ -113,7 +113,7 @@ levels, and can specify which predicted key shares should be sent by a client. Group tuples are used by OpenSSL TLS servers to decide whether to request a stronger keyshare than those predicted by sending a Hello Retry Request (B) even if some of the predicted groups are supported. -OpenSSL clients largely ignore tuple boundaries, and pay attenion only to the +OpenSSL clients largely ignore tuple boundaries, and pay attention only to the overall order of I elements and which groups are selected as predicted keyshares as described below. Tuple boundaries do however affect the behaviour of keyshare predictions diff --git a/doc/man3/SSL_CTX_set_msg_callback.pod b/doc/man3/SSL_CTX_set_msg_callback.pod index f046c77b53..c1748bbd8a 100644 --- a/doc/man3/SSL_CTX_set_msg_callback.pod +++ b/doc/man3/SSL_CTX_set_msg_callback.pod @@ -92,7 +92,7 @@ The SSL_trace() function can be used as a pre-written callback in a call to SSL_CTX_set_msg_callback() or SSL_set_msg_callback(). It requires a BIO to be set as the callback argument via SSL_CTX_set_msg_callback_arg() or SSL_set_msg_callback_arg(). Setting this callback will cause human readable -diagostic tracing information about an SSL/TLS/QUIC connection to be written to +diagnostic tracing information about an SSL/TLS/QUIC connection to be written to the BIO. =head1 NOTES diff --git a/doc/man3/X509_get_default_cert_file.pod b/doc/man3/X509_get_default_cert_file.pod index 1e65b22700..a604cf5571 100644 --- a/doc/man3/X509_get_default_cert_file.pod +++ b/doc/man3/X509_get_default_cert_file.pod @@ -24,7 +24,7 @@ the default path when it is asked to load trusted CA certificates from a file and no other path is specified. If the file exists, CA certificates are loaded from the file. -The X509_get_default_cert_dir() function returns a default delimeter-separated +The X509_get_default_cert_dir() function returns a default delimiter-separated list of paths to a directories containing trusted CA certificates named in the hashed format. OpenSSL will use this as the default list of paths when it is asked to load trusted CA certificates from a directory and no other path is diff --git a/doc/man7/openssl-core_dispatch.h.pod b/doc/man7/openssl-core_dispatch.h.pod index a19e1331fa..7f99fe2a81 100644 --- a/doc/man7/openssl-core_dispatch.h.pod +++ b/doc/man7/openssl-core_dispatch.h.pod @@ -24,7 +24,7 @@ are named as follows: These macros have the form C>. -=item dipatch numbers +=item dispatch numbers These macros have the form C_I>, where C> is the same as in the macro for the operation this From 39f46844c6e06e26bea34f300c7fc61c06bb20b8 Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Tue, 16 Jun 2026 12:58:45 +0200 Subject: [PATCH 009/349] asn1: centralize aux const-callback dispatch to avoid function pointer cast Replace the per-call-site `(ASN1_aux_const_cb *)aux->asn1_cb` cast in ASN1_item_ex_i2d() and asn1_item_print_ctx() with a shared helper, ossl_asn1_aux_const_cb(), which invokes the legacy non-const callback through its real type. This avoids the UBSAN function-pointer-type mismatch while preserving backward compatibility. Fixes: https://github.com/openssl/project/issues/1970 Signed-off-by: Nikola Pajkovsky Reviewed-by: Bob Beck Reviewed-by: Eugene Syromiatnikov Reviewed-by: Neil Horman MergeDate: Sun Jun 21 23:38:12 2026 (Merged from https://github.com/openssl/openssl/pull/31541) --- crypto/asn1/asn1_local.h | 3 +++ crypto/asn1/tasn_enc.c | 14 ++++---------- crypto/asn1/tasn_prn.c | 23 ++++++++--------------- crypto/asn1/tasn_utl.c | 16 ++++++++++++++++ 4 files changed, 31 insertions(+), 25 deletions(-) diff --git a/crypto/asn1/asn1_local.h b/crypto/asn1/asn1_local.h index 46d7aca7a2..0fd1d0a842 100644 --- a/crypto/asn1/asn1_local.h +++ b/crypto/asn1/asn1_local.h @@ -12,6 +12,7 @@ #if !defined(OSSL_LIBCRYPTO_ASN1_ASN1_LOCAL_H) #define OSSL_LIBCRYPTO_ASN1_ASN1_LOCAL_H +#include #include "crypto/asn1.h" typedef const ASN1_VALUE const_ASN1_VALUE; @@ -99,5 +100,7 @@ int ossl_asn1_item_ex_new_intern(ASN1_VALUE **pval, const ASN1_ITEM *it, OSSL_LIB_CTX *libctx, const char *propq); int ossl_asn1_time_time_t_to_tm(const time_t *time, struct tm *out_tm); int ossl_asn1_time_tm_to_time_t(const struct tm *tm, time_t *out); +int ossl_asn1_call_aux_cb(const ASN1_AUX *aux, int operation, + const ASN1_VALUE **in, const ASN1_ITEM *it, void *exarg); #endif /* !defined(OSSL_LIBCRYPTO_ASN1_ASN1_LOCAL_H) */ diff --git a/crypto/asn1/tasn_enc.c b/crypto/asn1/tasn_enc.c index 834d087ebc..e489e29a0c 100644 --- a/crypto/asn1/tasn_enc.c +++ b/crypto/asn1/tasn_enc.c @@ -85,16 +85,10 @@ int ASN1_item_ex_i2d(const ASN1_VALUE **pval, unsigned char **out, int i, seqcontlen, seqlen, ndef = 1; const ASN1_EXTERN_FUNCS *ef; const ASN1_AUX *aux = it->funcs; - ASN1_aux_const_cb *asn1_cb = NULL; if ((it->itype != ASN1_ITYPE_PRIMITIVE) && *pval == NULL) return 0; - if (aux != NULL) { - asn1_cb = ((aux->flags & ASN1_AFLG_CONST_CB) != 0) ? aux->asn1_const_cb - : (ASN1_aux_const_cb *)aux->asn1_cb; /* backward compatibility */ - } - switch (it->itype) { case ASN1_ITYPE_PRIMITIVE: @@ -123,7 +117,7 @@ int ASN1_item_ex_i2d(const ASN1_VALUE **pval, unsigned char **out, ERR_raise(ERR_LIB_ASN1, ASN1_R_BAD_TEMPLATE); return -1; } - if (asn1_cb && !asn1_cb(ASN1_OP_I2D_PRE, pval, it, NULL)) + if (!ossl_asn1_call_aux_cb(aux, ASN1_OP_I2D_PRE, pval, it, NULL)) return 0; i = ossl_asn1_get_choice_selector_const(pval, it); if ((i >= 0) && (i < it->tcount)) { @@ -134,7 +128,7 @@ int ASN1_item_ex_i2d(const ASN1_VALUE **pval, unsigned char **out, return asn1_template_ex_i2d(pchval, out, chtt, -1, aclass); } /* Fixme: error condition if selector out of range */ - if (asn1_cb && !asn1_cb(ASN1_OP_I2D_POST, pval, it, NULL)) + if (!ossl_asn1_call_aux_cb(aux, ASN1_OP_I2D_POST, pval, it, NULL)) return 0; break; @@ -166,7 +160,7 @@ int ASN1_item_ex_i2d(const ASN1_VALUE **pval, unsigned char **out, aclass = (aclass & ~ASN1_TFLG_TAG_CLASS) | V_ASN1_UNIVERSAL; } - if (asn1_cb && !asn1_cb(ASN1_OP_I2D_PRE, pval, it, NULL)) + if (!ossl_asn1_call_aux_cb(aux, ASN1_OP_I2D_PRE, pval, it, NULL)) return 0; /* First work out sequence content length */ for (i = 0, tt = it->templates; i < it->tcount; tt++, i++) { @@ -200,7 +194,7 @@ int ASN1_item_ex_i2d(const ASN1_VALUE **pval, unsigned char **out, } if (ndef == 2) ASN1_put_eoc(out); - if (asn1_cb && !asn1_cb(ASN1_OP_I2D_POST, pval, it, NULL)) + if (!ossl_asn1_call_aux_cb(aux, ASN1_OP_I2D_POST, pval, it, NULL)) return 0; return seqlen; diff --git a/crypto/asn1/tasn_prn.c b/crypto/asn1/tasn_prn.c index 080b5623e4..72922c6530 100644 --- a/crypto/asn1/tasn_prn.c +++ b/crypto/asn1/tasn_prn.c @@ -138,15 +138,12 @@ static int asn1_item_print_ctx(BIO *out, const ASN1_VALUE **fld, int indent, const ASN1_EXTERN_FUNCS *ef; const ASN1_VALUE **tmpfld; const ASN1_AUX *aux = it->funcs; - ASN1_aux_const_cb *asn1_cb = NULL; ASN1_PRINT_ARG parg; int i; if (aux != NULL) { parg.out = out; parg.indent = indent; parg.pctx = pctx; - asn1_cb = ((aux->flags & ASN1_AFLG_CONST_CB) != 0) ? aux->asn1_const_cb - : (ASN1_aux_const_cb *)aux->asn1_cb; /* backward compatibility */ } if (((it->itype != ASN1_ITYPE_PRIMITIVE) @@ -220,13 +217,11 @@ static int asn1_item_print_ctx(BIO *out, const ASN1_VALUE **fld, int indent, } } - if (asn1_cb) { - i = asn1_cb(ASN1_OP_PRINT_PRE, fld, it, &parg); - if (i == 0) - return 0; - if (i == 2) - return 1; - } + i = ossl_asn1_call_aux_cb(aux, ASN1_OP_PRINT_PRE, fld, it, &parg); + if (i == 0) + return 0; + if (i == 2) + return 1; /* Print each field entry */ for (i = 0, tt = it->templates; i < it->tcount; i++, tt++) { @@ -244,11 +239,9 @@ static int asn1_item_print_ctx(BIO *out, const ASN1_VALUE **fld, int indent, return 0; } - if (asn1_cb) { - i = asn1_cb(ASN1_OP_PRINT_POST, fld, it, &parg); - if (i == 0) - return 0; - } + i = ossl_asn1_call_aux_cb(aux, ASN1_OP_PRINT_POST, fld, it, &parg); + if (i == 0) + return 0; break; default: diff --git a/crypto/asn1/tasn_utl.c b/crypto/asn1/tasn_utl.c index 5e82a10fbd..a4ab762960 100644 --- a/crypto/asn1/tasn_utl.c +++ b/crypto/asn1/tasn_utl.c @@ -288,3 +288,19 @@ err: ERR_raise(ERR_LIB_ASN1, ASN1_R_UNSUPPORTED_ANY_DEFINED_BY_TYPE); return NULL; } + +int ossl_asn1_call_aux_cb(const ASN1_AUX *aux, int operation, + const ASN1_VALUE **in, const ASN1_ITEM *it, void *exarg) +{ + if (aux == NULL) + return 1; + + if ((aux->flags & ASN1_AFLG_CONST_CB) != 0) { + if (aux->asn1_const_cb != NULL) + return aux->asn1_const_cb(operation, in, it, exarg); + } else if (aux->asn1_cb != NULL) { + return aux->asn1_cb(operation, (ASN1_VALUE **)in, it, exarg); + } + + return 1; +} From d41a9225196be4863e7b72a152769a93f9faf917 Mon Sep 17 00:00:00 2001 From: Abel Tom Date: Thu, 18 Jun 2026 13:58:35 +0200 Subject: [PATCH 010/349] tls_common.c: prevent max_early_data overflow in rlayer_early_data_count_ok() Make the local max_early_data variable uint64_t so an overflow cannot occur if the max_early_data field in the record layer struct has the maximum value: UNT32_MAX (0xFFFFFFFF). Resolves: https://github.com/openssl/openssl/issues/31533 Reviewed-by: Eugene Syromiatnikov Reviewed-by: Frederik Wedel-Heinen Reviewed-by: Tim Hudson MergeDate: Sun Jun 21 23:50:02 2026 (Merged from https://github.com/openssl/openssl/pull/31538) --- ssl/record/methods/tls_common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ssl/record/methods/tls_common.c b/ssl/record/methods/tls_common.c index 685139531e..e149d09898 100644 --- a/ssl/record/methods/tls_common.c +++ b/ssl/record/methods/tls_common.c @@ -497,7 +497,7 @@ static int tls_record_app_data_waiting(OSSL_RECORD_LAYER *rl) static int rlayer_early_data_count_ok(OSSL_RECORD_LAYER *rl, size_t length, size_t overhead, int send) { - uint32_t max_early_data = rl->max_early_data; + uint64_t max_early_data = rl->max_early_data; if (max_early_data == 0) { RLAYERfatal(rl, send ? SSL_AD_INTERNAL_ERROR : SSL_AD_UNEXPECTED_MESSAGE, @@ -506,7 +506,7 @@ static int rlayer_early_data_count_ok(OSSL_RECORD_LAYER *rl, size_t length, } /* If we are dealing with ciphertext we need to allow for the overhead */ - max_early_data += (uint32_t)overhead; + max_early_data += overhead; if (rl->early_data_count + length > max_early_data) { RLAYERfatal(rl, send ? SSL_AD_INTERNAL_ERROR : SSL_AD_UNEXPECTED_MESSAGE, From b593fb151eb7373cb85f352e86bcb7225f9850d9 Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Thu, 18 Jun 2026 12:02:06 -0600 Subject: [PATCH 011/349] Fix OSSL_ATOMICS_LOCKLESS detection for Windows toolchains The check referenced USE_INTERLOCKEDOR64, but the macro defined above for MSVC (with the right architecture/version) and 64-bit MinGW is OSSL_USE_INTERLOCKEDOR64. As a result, OSSL_ATOMICS_LOCKLESS was never defined on Windows, even though those toolchains do provide lockless atomics. Reported-by: Mounir IDRASSI Fixes: 26c57423933c "Use the actually correct define for solaris and gcc" Reviewed-by: Neil Horman Reviewed-by: Milan Broz Reviewed-by: Tim Hudson Reviewed-by: Eugene Syromiatnikov MergeDate: Mon Jun 22 07:22:31 2026 (Merged from https://github.com/openssl/openssl/pull/31600) --- include/internal/threads_common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/internal/threads_common.h b/include/internal/threads_common.h index b6dea47ab9..e32257d630 100644 --- a/include/internal/threads_common.h +++ b/include/internal/threads_common.h @@ -88,7 +88,7 @@ void CRYPTO_THREAD_clean_local(void); #endif /* Allow us to know if atomics will be implemented with a fallback lock or not. */ -#if defined(OSSL_USE_GCC_ATOMICS) || defined(OSSL_USE_SOLARIS_ATOMICS) || defined(USE_INTERLOCKEDOR64) +#if defined(OSSL_USE_GCC_ATOMICS) || defined(OSSL_USE_SOLARIS_ATOMICS) || defined(OSSL_USE_INTERLOCKEDOR64) #define OSSL_ATOMICS_LOCKLESS #endif From 206ec55fec48d37960e01df1e12f0ed71a200c35 Mon Sep 17 00:00:00 2001 From: Abel Tom Date: Wed, 17 Jun 2026 12:11:29 +0200 Subject: [PATCH 012/349] crypto/pkcs7/pk7_lib.c: fix return value in PKCS7_set_digest() Return 0 when the passed object to PKCS7_set_digest() is not of type PKCS7 digest. Fixes: c5a55463892d "Add support for digested data PKCS#7 type." Resolves: https://github.com/openssl/openssl/issues/31551 Reviewed-by: Matt Caswell Reviewed-by: Norbert Pocs Reviewed-by: Eugene Syromiatnikov Reviewed-by: Tim Hudson MergeDate: Mon Jun 22 08:17:32 2026 (Merged from https://github.com/openssl/openssl/pull/31559) --- crypto/pkcs7/pk7_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/pkcs7/pk7_lib.c b/crypto/pkcs7/pk7_lib.c index 0a1d0f61a5..a9640769cf 100644 --- a/crypto/pkcs7/pk7_lib.c +++ b/crypto/pkcs7/pk7_lib.c @@ -537,7 +537,7 @@ int PKCS7_set_digest(PKCS7 *p7, const EVP_MD *md) } ERR_raise(ERR_LIB_PKCS7, PKCS7_R_WRONG_CONTENT_TYPE); - return 1; + return 0; } STACK_OF(PKCS7_SIGNER_INFO) *PKCS7_get_signer_info(PKCS7 *p7) From f23329efa0fe3785131c715849f2577bc27b128d Mon Sep 17 00:00:00 2001 From: Jon Ericson Date: Thu, 18 Jun 2026 12:14:57 -0700 Subject: [PATCH 013/349] CONTRIBUTING.md: add the AI declaration policy References: https://github.com/openssl/general-policies/pull/85 Reviewed-by: Milan Broz Reviewed-by: Bob Beck Reviewed-by: Tim Hudson Reviewed-by: Eugene Syromiatnikov MergeDate: Mon Jun 22 10:36:52 2026 (Merged from https://github.com/openssl/openssl/pull/31604) --- CONTRIBUTING.md | 48 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3813c1e23d..3dea8a8fe3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -80,7 +80,37 @@ guidelines: git push -f [ []] ``` - 2. All source files should start with the following text (with + 2. Similarly, if a non-trivial portion of a contribution was created + by an AI, you must declare which agent and model were used. This + is done by adding `Assisted-by: {agent}:{model}` below the commit + message: + + ``` + One-line summary of change with AI-created portions + + Assisted-by: Claude:claude-sonnet-4-6 + ``` + + Multiple Assisted-by trailers can be included if multiple tools were used: + + ``` + Assisted-by: Claude:claude-sonnet-4-6 + Assisted-by: ChatGPT:gpt-4o + Assisted-by: GitHub Copilot:gpt-4.1 + ``` + + You will need to have signed a v1.1 or later CLA in order to + include AI-generated content in your contribution. CLAs signed + after June 2026 will have the requisite clauses. + + Consult the [OpenSSL AI Code and Documentation Contribution + Policy] if an AI model assisted with the creation of your + contribution. + + [OpenSSL AI Code and Documentation Contribution + Policy]: + + 3. All source files should start with the following text (with appropriate comment characters at the start of each line and the year(s) updated): @@ -93,11 +123,11 @@ guidelines: https://www.openssl.org/source/license.html ``` - 3. Patches should be as current as possible; expect to have to rebase + 4. Patches should be as current as possible; expect to have to rebase often. We do not accept merge commits, you will have to remove them (usually by rebasing) before it will be acceptable. - 4. Code provided should follow our [coding style] and [documentation policy] + 5. Code provided should follow our [coding style] and [documentation policy] and compile without warnings. Consistent formatting is enforced by using `clang-format` with configuration stored in [.clang-format](.clang-format). OpenSSL uses `WebKit` style. @@ -115,16 +145,16 @@ guidelines: [coding style]: https://openssl-library.org/policies/technical/coding-style/ [documentation policy]: https://openssl-library.org/policies/technical/documentation-policy/ - 5. When at all possible, code contributions should include tests. These can + 6. When at all possible, code contributions should include tests. These can either be added to an existing test, or completely new. Please see [test/README.md](test/README.md) for information on the test framework. - 6. New features or changed functionality must include + 7. New features or changed functionality must include documentation. Please look at the `.pod` files in `doc/man[1357]` for examples of our style. Run `make doc-nits` to make sure that your documentation changes are clean. - 7. For user visible changes (API changes, behaviour changes, ...), + 8. For user visible changes (API changes, behaviour changes, ...), consider adding a note in [CHANGES.md](CHANGES.md). This could be a summarising description of the change, and could explain the grander details. @@ -135,10 +165,10 @@ guidelines: with a specific release without having to sift through the higher noise ratio in git-log. - 8. Guidelines on how to integrate error output of new crypto library modules + 9. Guidelines on how to integrate error output of new crypto library modules can be found in [crypto/err/README.md](crypto/err/README.md). - 9. Once your Pull Request gets to the stage of being reviewed fixup commits +10. Once your Pull Request gets to the stage of being reviewed fixup commits should be used where possible. Fixup commits are squashed when the PR is finally merged. Fixup commits are done in the following way: @@ -161,7 +191,7 @@ guidelines: git log ``` -10. If a Pull Request addresses an [issue](https://github.com/openssl/openssl/issues/) +11. If a Pull Request addresses an [issue](https://github.com/openssl/openssl/issues/) the commit should include the line: ``` From bf41baa2bf3215625d44f7c0dc438ab1b2e3a3d0 Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Tue, 16 Jun 2026 12:49:52 -0600 Subject: [PATCH 014/349] doc/man3/ASN1_aux_cb.pod: correct return code documentation for the callbacks Attempt to make the documentation match the code. Not attempting to change what the code does at this point, it's all very random, and since it's been there, it is effectively established public API now. Reviewed-by: Nikola Pajkovsky Reviewed-by: Tomas Mraz Reviewed-by: Tim Hudson Reviewed-by: Eugene Syromiatnikov MergeDate: Mon Jun 22 11:56:58 2026 (Merged from https://github.com/openssl/openssl/pull/31549) --- doc/man3/ASN1_aux_cb.pod | 58 +++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/doc/man3/ASN1_aux_cb.pod b/doc/man3/ASN1_aux_cb.pod index 68b230ca1a..9a38ab168f 100644 --- a/doc/man3/ASN1_aux_cb.pod +++ b/doc/man3/ASN1_aux_cb.pod @@ -106,9 +106,11 @@ During the processing of an B object the callbacks set via I or I will be invoked as a result of various events indicated via the I parameter. The value of I<*in> will be the B object being processed based on the template in I. An -additional operation specific parameter may be passed in I. The currently -supported operations are as follows. The callbacks should return a positive -value on success or zero on error, unless otherwise noted below. +additional operation specific parameter may be passed in I. The +currently supported operations are as follows. Unless noted otherwise below, +the callbacks should return a positive value on success and zero on error; +some operations recognise additional return values, and a few do not consult +the return value at all. =over 4 @@ -130,13 +132,15 @@ I<*pval>. Invoked when processing a B, B or B structure immediately before an B is freed. If the callback originally constructed the B via B then it should free it at -this point and return 2 from the callback. Otherwise it should return 1 for -success or 0 on error. +this point and return 2; the caller will then skip its normal freeing. Any +other return value (including zero) causes the caller to proceed with normal +freeing; the hook cannot signal an error. =item B Invoked when processing a B, B or B structure -immediately after B sub-structures are freed. +immediately after B sub-structures are freed. The caller does not +consult the return value from this hook. =item B @@ -162,7 +166,10 @@ immediately after a "i2d" operation for the B. Invoked when processing a B or B structure immediately before printing the B. The I argument will be a pointer to an -B structure (see below). +B structure (see below). If the callback has fully printed the +value itself it should return 2; the caller will then skip the per-field +printing loop and the matching B callback. Return zero on +error or any other positive value to continue with normal printing. =item B @@ -260,8 +267,41 @@ The streaming I/O boundary. =head1 RETURN VALUES -The callbacks return 0 on error and a positive value on success. Some operations -require specific positive success values as noted above. +In general the callbacks return zero on error and a positive value on +success. Several operations have additional or different return-value +semantics, summarised here: + +=over 4 + +=item * + +B recognises a return of 2, meaning that the callback has +allocated the B itself and normal allocation should be skipped. + +=item * + +B recognises a return of 2, meaning that the callback has +freed the B itself and normal freeing should be skipped. Other +return values (including zero) cause normal freeing to proceed; the hook +cannot signal an error. + +=item * + +B's return value is not consulted by the caller. + +=item * + +B recognises a return of 2, meaning that the callback +has printed the value itself; the caller will skip the per-field printing +loop and the matching B invocation. + +=item * + +B, B, B, and +B treat any non-positive return value (zero or +negative) as an error. + +=back =head1 SEE ALSO From 60a174cd6cb271c869c1bdfcf04c2d28dcc87016 Mon Sep 17 00:00:00 2001 From: Frederik Wedel-Heinen Date: Fri, 19 Jun 2026 12:49:10 +0200 Subject: [PATCH 015/349] ssl/record: remove remnants of SSLv2-specific code Reviewed-by: Dmitry Belyavskiy Reviewed-by: Eugene Syromiatnikov MergeDate: Tue Jun 23 07:09:07 2026 (Merged from https://github.com/openssl/openssl/pull/31627) --- ssl/record/methods/tls_common.c | 15 ++------------- ssl/record/rec_layer_s3.c | 10 ++-------- 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/ssl/record/methods/tls_common.c b/ssl/record/methods/tls_common.c index e149d09898..190e61bb1c 100644 --- a/ssl/record/methods/tls_common.c +++ b/ssl/record/methods/tls_common.c @@ -526,8 +526,6 @@ static int rlayer_early_data_count_ok(OSSL_RECORD_LAYER *rl, size_t length, */ #define MAX_EMPTY_RECORDS 32 -#define SSL2_RT_HEADER_LENGTH 2 - /*- * Call this to buffer new input records in rl->rrec. * It will return a OSSL_RECORD_RETURN_* value. @@ -626,12 +624,7 @@ int tls_get_more_records(OSSL_RECORD_LAYER *rl) * rl->rstate == SSL_ST_READ_BODY, get and decode the data. Calculate * how much more data we need to read for the rest of the record */ - if (thisrr->rec_version == SSL2_VERSION) { - more = thisrr->length + SSL2_RT_HEADER_LENGTH - - SSL3_RT_HEADER_LENGTH; - } else { - more = thisrr->length; - } + more = thisrr->length; if (more > 0) { /* now rl->packet_length == SSL3_RT_HEADER_LENGTH */ @@ -646,13 +639,9 @@ int tls_get_more_records(OSSL_RECORD_LAYER *rl) /* * At this point, rl->packet_length == SSL3_RT_HEADER_LENGTH - * + thisrr->length, or rl->packet_length == SSL2_RT_HEADER_LENGTH * + thisrr->length and we have that many bytes in rl->packet */ - if (thisrr->rec_version == SSL2_VERSION) - thisrr->input = &(rl->packet[SSL2_RT_HEADER_LENGTH]); - else - thisrr->input = &(rl->packet[SSL3_RT_HEADER_LENGTH]); + thisrr->input = &(rl->packet[SSL3_RT_HEADER_LENGTH]); /* * ok, we can now read from 'rl->packet' data into 'thisrr'. diff --git a/ssl/record/rec_layer_s3.c b/ssl/record/rec_layer_s3.c index e68d8f8fa1..548104e73b 100644 --- a/ssl/record/rec_layer_s3.c +++ b/ssl/record/rec_layer_s3.c @@ -835,15 +835,9 @@ start: */ /* - * Lets just double check that we've not got an SSLv2 record + * Lets just double check that we've got a supported record version */ - if (rr->version == SSL2_VERSION) { - /* - * Should never happen. ssl3_get_record() should only give us an SSLv2 - * record back if this is the first packet and we are looking for an - * initial ClientHello. Therefore |type| should always be equal to - * |rr->type|. If not then something has gone horribly wrong - */ + if (rr->version < TLS1_VERSION || rr->version > TLS1_3_VERSION) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return -1; } From 7045b8fde5f8d1d3228831ec0c4dc7de46b0ca2f Mon Sep 17 00:00:00 2001 From: Alexandr Nedvedicky Date: Tue, 16 Jun 2026 17:08:59 +0200 Subject: [PATCH 016/349] This change should allow us to move QUIC test scripts from quic_multistream test to radix without dealing with conflict+rebase. the idea is there will be one PR for each script, so more people will be able to submit those PRs without rsiking a conflicts around the scripts array. This should allow for smoot reciew/git push flow. Reviewed-by: Neil Horman Reviewed-by: Norbert Pocs MergeDate: Tue Jun 23 12:21:25 2026 (Merged from https://github.com/openssl/openssl/pull/31547) --- test/radix/quic_tests.c | 523 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 523 insertions(+) diff --git a/test/radix/quic_tests.c b/test/radix/quic_tests.c index ed2685d14c..d1d8c54949 100644 --- a/test/radix/quic_tests.c +++ b/test/radix/quic_tests.c @@ -765,6 +765,427 @@ DEF_SCRIPT(check_ctx_cbks, "Check new_pending and client_hello callbacks") OP_FUNC(check_pending); } +/* + * script_5 - script_106 are place holders for tests we + * currently keep in test/quic_multistream_test.c. + * We need to move those here so we can get rid off + * QUIC T-server mock-up. + * + * there should be one PR for each script being moved here, + * to make reviewer's life easier. Once all scripts will be + * moved we can find better names for script_5, ..., script_106. + * + * The scaffolding here hopes to avoid conflicts in 'scripts' + * array below when more PRs will be in flight. + */ +DEF_SCRIPT(script_5, "place holder for multistram script_5") +{ +} + +DEF_SCRIPT(script_6, "place holder for multistram script_6") +{ +} + +DEF_SCRIPT(script_7, "place holder for multistrem script_7") +{ +} + +DEF_SCRIPT(script_8, "place holder for multistrem script_8") +{ +} + +DEF_SCRIPT(script_9, "place holder for multistrem script_9") +{ +} + +DEF_SCRIPT(script_10, "place holder for multistrem script_10") +{ +} + +DEF_SCRIPT(script_11, "place holder for multistrem script_11") +{ +} + +DEF_SCRIPT(script_12, "place holder for multistrem script_12") +{ +} + +DEF_SCRIPT(script_13, "place holder for multistrem script_13") +{ +} + +DEF_SCRIPT(script_14, "place holder for multistrem script_14") +{ +} + +DEF_SCRIPT(script_15, "place holder for multistrem script_15") +{ +} + +DEF_SCRIPT(script_16, "place holder for multistrem script_16") +{ +} + +DEF_SCRIPT(script_17, "place holder for multistrem script_17") +{ +} + +DEF_SCRIPT(script_18, "place holder for multistrem script_18") +{ +} + +DEF_SCRIPT(script_19, "place holder for multistrem script_19") +{ +} + +DEF_SCRIPT(script_20, "place holder for multistrem script_20") +{ +} + +DEF_SCRIPT(script_21, "place holder for multistrem script_21") +{ +} + +DEF_SCRIPT(script_22, "place holder for multistrem script_22") +{ +} + +DEF_SCRIPT(script_23, "place holder for multistrem script_23") +{ +} + +DEF_SCRIPT(script_24, "place holder for multistrem script_24") +{ +} + +DEF_SCRIPT(script_25, "place holder for multistrem script_25") +{ +} + +DEF_SCRIPT(script_26, "place holder for multistrem script_26") +{ +} + +DEF_SCRIPT(script_27, "place holder for multistrem script_27") +{ +} + +DEF_SCRIPT(script_28, "place holder for multistrem script_28") +{ +} + +DEF_SCRIPT(script_29, "place holder for multistrem script_29") +{ +} + +DEF_SCRIPT(script_30, "place holder for multistrem script_30") +{ +} + +DEF_SCRIPT(script_31, "place holder for multistrem script_31") +{ +} + +DEF_SCRIPT(script_32, "place holder for multistrem script_32") +{ +} + +DEF_SCRIPT(script_33, "place holder for multistrem script_33") +{ +} + +DEF_SCRIPT(script_34, "place holder for multistrem script_34") +{ +} + +DEF_SCRIPT(script_35, "place holder for multistrem script_35") +{ +} + +DEF_SCRIPT(script_36, "place holder for multistrem script_36") +{ +} + +DEF_SCRIPT(script_37, "place holder for multistrem script_37") +{ +} + +DEF_SCRIPT(script_38, "place holder for multistrem script_38") +{ +} + +DEF_SCRIPT(script_39, "place holder for multistrem script_39") +{ +} + +DEF_SCRIPT(script_40, "place holder for multistrem script_40") +{ +} + +DEF_SCRIPT(script_41, "place holder for multistrem script_41") +{ +} + +DEF_SCRIPT(script_42, "place holder for multistrem script_42") +{ +} + +DEF_SCRIPT(script_43, "place holder for multistrem script_43") +{ +} + +DEF_SCRIPT(script_44, "place holder for multistrem script_44") +{ +} + +DEF_SCRIPT(script_45, "place holder for multistrem script_45") +{ +} + +DEF_SCRIPT(script_46, "place holder for multistrem script_46") +{ +} + +DEF_SCRIPT(script_47, "place holder for multistrem script_47") +{ +} + +DEF_SCRIPT(script_48, "place holder for multistrem script_48") +{ +} + +DEF_SCRIPT(script_49, "place holder for multistrem script_49") +{ +} + +DEF_SCRIPT(script_50, "place holder for multistrem script_50") +{ +} + +DEF_SCRIPT(script_51, "place holder for multistrem script_51") +{ +} + +DEF_SCRIPT(script_52, "place holder for multistrem script_52") +{ +} + +DEF_SCRIPT(script_53, "place holder for multistrem script_53") +{ +} + +DEF_SCRIPT(script_54, "place holder for multistrem script_54") +{ +} + +DEF_SCRIPT(script_55, "place holder for multistrem script_55") +{ +} + +DEF_SCRIPT(script_56, "place holder for multistrem script_56") +{ +} + +DEF_SCRIPT(script_57, "place holder for multistrem script_57") +{ +} + +DEF_SCRIPT(script_58, "place holder for multistrem script_58") +{ +} + +DEF_SCRIPT(script_59, "place holder for multistrem script_59") +{ +} + +DEF_SCRIPT(script_60, "place holder for multistrem script_60") +{ +} + +DEF_SCRIPT(script_61, "place holder for multistrem script_61") +{ +} + +DEF_SCRIPT(script_62, "place holder for multistrem script_62") +{ +} + +DEF_SCRIPT(script_63, "place holder for multistrem script_63") +{ +} + +DEF_SCRIPT(script_64, "place holder for multistrem script_64") +{ +} + +DEF_SCRIPT(script_65, "place holder for multistrem script_65") +{ +} + +DEF_SCRIPT(script_66, "place holder for multistrem script_66") +{ +} + +DEF_SCRIPT(script_67, "place holder for multistrem script_67") +{ +} + +DEF_SCRIPT(script_68, "place holder for multistrem script_68") +{ +} + +DEF_SCRIPT(script_69, "place holder for multistrem script_69") +{ +} + +DEF_SCRIPT(script_70, "place holder for multistrem script_70") +{ +} + +DEF_SCRIPT(script_71, "place holder for multistrem script_71") +{ +} + +DEF_SCRIPT(script_72, "place holder for multistrem script_72") +{ +} + +DEF_SCRIPT(script_73, "place holder for multistrem script_73") +{ +} + +DEF_SCRIPT(script_74, "place holder for multistrem script_74") +{ +} + +DEF_SCRIPT(script_75, "place holder for multistrem script_75") +{ +} + +DEF_SCRIPT(script_76, "place holder for multistrem script_76") +{ +} + +DEF_SCRIPT(script_77, "place holder for multistrem script_77") +{ +} + +DEF_SCRIPT(script_78, "place holder for multistrem script_78") +{ +} + +DEF_SCRIPT(script_79, "place holder for multistrem script_79") +{ +} + +DEF_SCRIPT(script_80, "place holder for multistrem script_80") +{ +} + +DEF_SCRIPT(script_81, "place holder for multistrem script_81") +{ +} + +DEF_SCRIPT(script_82, "place holder for multistrem script_82") +{ +} + +DEF_SCRIPT(script_83, "place holder for multistrem script_83") +{ +} + +DEF_SCRIPT(script_84, "place holder for multistrem script_84") +{ +} + +DEF_SCRIPT(script_85, "place holder for multistrem script_85") +{ +} + +DEF_SCRIPT(script_86, "place holder for multistrem script_86") +{ +} + +DEF_SCRIPT(script_87, "place holder for multistrem script_87") +{ +} + +DEF_SCRIPT(script_88, "place holder for multistrem script_88") +{ +} + +DEF_SCRIPT(script_89, "place holder for multistrem script_89") +{ +} + +DEF_SCRIPT(script_90, "place holder for multistrem script_90") +{ +} + +DEF_SCRIPT(script_91, "place holder for multistrem script_91") +{ +} + +DEF_SCRIPT(script_92, "place holder for multistrem script_92") +{ +} + +DEF_SCRIPT(script_93, "place holder for multistrem script_93") +{ +} + +DEF_SCRIPT(script_94, "place holder for multistrem script_94") +{ +} + +DEF_SCRIPT(script_95, "place holder for multistrem script_95") +{ +} + +DEF_SCRIPT(script_96, "place holder for multistrem script_96") +{ +} + +DEF_SCRIPT(script_97, "place holder for multistrem script_97") +{ +} + +DEF_SCRIPT(script_98, "place holder for multistrem script_98") +{ +} + +DEF_SCRIPT(script_99, "place holder for multistrem script_99") +{ +} + +DEF_SCRIPT(script_100, "place holder for multistrem script_100") +{ +} + +DEF_SCRIPT(script_101, "place holder for multistrem script_101") +{ +} + +DEF_SCRIPT(script_102, "place holder for multistrem script_102") +{ +} + +DEF_SCRIPT(script_103, "place holder for multistrem script_103") +{ +} + +DEF_SCRIPT(script_104, "place holder for multistrem script_104") +{ +} + +DEF_SCRIPT(script_105, "place holder for multistrem script_105") +{ +} + +DEF_SCRIPT(script_106, "place holder for multistrem script_106") +{ +} + /* * List of Test Scripts * ============================================================================ @@ -778,4 +1199,106 @@ static SCRIPT_INFO *const scripts[] = { USE(check_cwm), USE(check_pc_flood), USE(check_ctx_cbks), + USE(script_5), + USE(script_6), + USE(script_7), + USE(script_8), + USE(script_9), + USE(script_10), + USE(script_11), + USE(script_12), + USE(script_13), + USE(script_14), + USE(script_15), + USE(script_16), + USE(script_17), + USE(script_18), + USE(script_19), + USE(script_20), + USE(script_21), + USE(script_22), + USE(script_23), + USE(script_24), + USE(script_25), + USE(script_26), + USE(script_27), + USE(script_28), + USE(script_29), + USE(script_30), + USE(script_31), + USE(script_32), + USE(script_33), + USE(script_34), + USE(script_35), + USE(script_36), + USE(script_37), + USE(script_38), + USE(script_39), + USE(script_40), + USE(script_41), + USE(script_42), + USE(script_43), + USE(script_44), + USE(script_45), + USE(script_46), + USE(script_47), + USE(script_48), + USE(script_49), + USE(script_50), + USE(script_51), + USE(script_52), + USE(script_53), + USE(script_54), + USE(script_55), + USE(script_56), + USE(script_57), + USE(script_58), + USE(script_59), + USE(script_60), + USE(script_61), + USE(script_62), + USE(script_63), + USE(script_64), + USE(script_65), + USE(script_66), + USE(script_67), + USE(script_68), + USE(script_69), + USE(script_70), + USE(script_71), + USE(script_72), + USE(script_73), + USE(script_74), + USE(script_75), + USE(script_76), + USE(script_77), + USE(script_78), + USE(script_79), + USE(script_80), + USE(script_81), + USE(script_82), + USE(script_83), + USE(script_84), + USE(script_85), + USE(script_86), + USE(script_87), + USE(script_88), + USE(script_89), + USE(script_90), + USE(script_91), + USE(script_92), + USE(script_93), + USE(script_94), + USE(script_95), + USE(script_96), + USE(script_97), + USE(script_98), + USE(script_99), + USE(script_100), + USE(script_101), + USE(script_102), + USE(script_103), + USE(script_104), + USE(script_105), + USE(script_106), }; From 43ce59231eaff9e9907bf36c7b46d52feb3ebe3a Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 7 May 2026 19:22:49 +0200 Subject: [PATCH 017/349] Add mfail test for SSL_new() with ctx QUIC client method Reviewed-by: Nikola Pajkovsky Reviewed-by: Tomas Mraz MergeDate: Tue Jun 23 13:16:52 2026 (Merged from https://github.com/openssl/openssl/pull/31249) --- test/quicapitest.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/quicapitest.c b/test/quicapitest.c index 97c759dffc..c9b0dce585 100644 --- a/test/quicapitest.c +++ b/test/quicapitest.c @@ -3563,6 +3563,29 @@ end: return ret; } +static int test_ssl_new_mfail(void) +{ + int ret = 0; + SSL_CTX *cctx = NULL; + SSL *clientquic = NULL; + + if (!TEST_ptr(cctx = SSL_CTX_new_ex(libctx, NULL, OSSL_QUIC_client_method()))) + goto err; + + MFAIL_start(); + clientquic = SSL_new(cctx); + MFAIL_end(); + + if (clientquic != NULL) + ret = 1; + +err: + SSL_free(clientquic); + SSL_CTX_free(cctx); + + return ret; +} + /***********************************************************************************/ OPT_TEST_DECLARE_USAGE("provider config certsdir datadir\n") @@ -3675,6 +3698,11 @@ int setup_tests(void) ADD_TEST(test_quic_peer_addr_v4); ADD_TEST(test_ech); ADD_TEST(test_quic_resize_txe); +#ifdef OPENSSL_NO_CACHED_FETCH + ADD_MFAIL_NO_CHECK_TEST(test_ssl_new_mfail); +#else + ADD_MFAIL_TEST(test_ssl_new_mfail); +#endif return 1; err: From 8ccf576564e20201fd5e20e3732f30f3ec4e2780 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 18 Jun 2026 18:02:05 +0200 Subject: [PATCH 018/349] quic: add mfail test for QUIC SRT generator This slightly improves its coverage. Reviewed-by: Daniel Kubec Reviewed-by: Nikola Pajkovsky Reviewed-by: Tomas Mraz MergeDate: Tue Jun 23 14:01:21 2026 (Merged from https://github.com/openssl/openssl/pull/31589) --- test/quic_srt_gen_test.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/quic_srt_gen_test.c b/test/quic_srt_gen_test.c index fcbf4aea22..d4a8f66896 100644 --- a/test/quic_srt_gen_test.c +++ b/test/quic_srt_gen_test.c @@ -70,8 +70,30 @@ err: return testresult; } +static int test_srt_gen_new_mfail(int idx) +{ + const struct test_case *t = &tests[idx]; + QUIC_SRT_GEN *srt_gen = NULL; + + MFAIL_start(); + srt_gen = ossl_quic_srt_gen_new(NULL, NULL, t->key, t->key_len); + MFAIL_end(); + + if (srt_gen == NULL) { + return 0; + } + + ossl_quic_srt_gen_free(srt_gen); + return 1; +} + int setup_tests(void) { ADD_ALL_TESTS(test_srt_gen, OSSL_NELEM(tests)); +#ifdef OPENSSL_NO_CACHED_FETCH + ADD_MFAIL_ALL_NO_CHECK_TESTS(test_srt_gen_new_mfail, OSSL_NELEM(tests)); +#else + ADD_MFAIL_ALL_TESTS(test_srt_gen_new_mfail, OSSL_NELEM(tests)); +#endif return 1; } From 8d0f837db83586b6e4b12a24ee68c5e2ea643d75 Mon Sep 17 00:00:00 2001 From: 007bsd <22483432+007bsd@users.noreply.github.com> Date: Fri, 15 May 2026 12:04:58 +0300 Subject: [PATCH 019/349] Fix key2ms_newctx() pointer type mismatch in MSBLOB/PVK key encoder key2ms_newctx() returned struct key2ms_ctx_st *, but is registered as OSSL_FUNC_ENCODER_NEWCTX and called through OSSL_FUNC_encoder_newctx_fn (void *(*)(void *)), which is a case of undefined behavior, flagged by -fsanitize=function. Same class as [1], missed there. Give it the correct signature and add the self-check forward declaration, as key2ms_freectx() already has. [1] https://github.com/openssl/openssl/pull/31078 CLA: trivial Fixes: 0cc0164d193f "PROV: Add MSBLOB and PVK encoders" Reviewed-by: Frederik Wedel-Heinen Reviewed-by: Viktor Dukhovni Reviewed-by: Eugene Syromiatnikov Reviewed-by: Tomas Mraz Reviewed-by: Paul Dale MergeDate: Tue Jun 23 14:45:54 2026 (Merged from https://github.com/openssl/openssl/pull/31189) --- providers/implementations/encode_decode/encode_key2ms.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/providers/implementations/encode_decode/encode_key2ms.c b/providers/implementations/encode_decode/encode_key2ms.c index 8ae4022e51..87b4242923 100644 --- a/providers/implementations/encode_decode/encode_key2ms.c +++ b/providers/implementations/encode_decode/encode_key2ms.c @@ -68,10 +68,11 @@ static int write_pvk(struct key2ms_ctx_st *ctx, OSSL_CORE_BIO *cout, return ret; } +static OSSL_FUNC_encoder_newctx_fn key2ms_newctx; static OSSL_FUNC_encoder_freectx_fn key2ms_freectx; static OSSL_FUNC_encoder_does_selection_fn key2ms_does_selection; -static struct key2ms_ctx_st *key2ms_newctx(void *provctx) +static void *key2ms_newctx(void *provctx) { struct key2ms_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx)); From 31274b3d309ceedd4d6c2878c531948c1a517b7c Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Wed, 20 May 2026 12:18:52 +0200 Subject: [PATCH 020/349] ssl/t1_trce.c: use macros from tlssigalgs.h, reorder accordingly The macro definitions for the SignatureScheme values and names are available "include/internal/tlssigalgs.h" (contrary to what comment says), use them and also order the items in the order they are present in the header file. Signed-off-by: Eugene Syromiatnikov Reviewed-by: Bob Beck Reviewed-by: Neil Horman MergeDate: Tue Jun 23 16:29:59 2026 (Merged from https://github.com/openssl/openssl/pull/31248) --- ssl/t1_trce.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/ssl/t1_trce.c b/ssl/t1_trce.c index b2b00b3f7c..b4d67d9d24 100644 --- a/ssl/t1_trce.c +++ b/ssl/t1_trce.c @@ -586,8 +586,6 @@ static const ssl_trace_tbl ssl_sigalg_tbl[] = { { TLSEXT_SIGALG_ecdsa_secp384r1_sha384, TLSEXT_SIGALG_ecdsa_secp384r1_sha384_name }, { TLSEXT_SIGALG_ecdsa_secp521r1_sha512, TLSEXT_SIGALG_ecdsa_secp521r1_sha512_name }, { TLSEXT_SIGALG_ecdsa_sha224, TLSEXT_SIGALG_ecdsa_sha224_name }, - { TLSEXT_SIGALG_ed25519, TLSEXT_SIGALG_ed25519_name }, - { TLSEXT_SIGALG_ed448, TLSEXT_SIGALG_ed448_name }, { TLSEXT_SIGALG_ecdsa_sha1, TLSEXT_SIGALG_ecdsa_sha1_name }, { TLSEXT_SIGALG_rsa_pss_rsae_sha256, TLSEXT_SIGALG_rsa_pss_rsae_sha256_name }, { TLSEXT_SIGALG_rsa_pss_rsae_sha384, TLSEXT_SIGALG_rsa_pss_rsae_sha384_name }, @@ -610,18 +608,15 @@ static const ssl_trace_tbl ssl_sigalg_tbl[] = { { TLSEXT_SIGALG_gostr34102012_256_gostr34112012_256, TLSEXT_SIGALG_gostr34102012_256_gostr34112012_256_name }, { TLSEXT_SIGALG_gostr34102012_512_gostr34112012_512, TLSEXT_SIGALG_gostr34102012_512_gostr34112012_512_name }, { TLSEXT_SIGALG_gostr34102001_gostr3411, TLSEXT_SIGALG_gostr34102001_gostr3411_name }, + { TLSEXT_SIGALG_sm2sig_sm3, TLSEXT_SIGALG_sm2sig_sm3_name }, + { TLSEXT_SIGALG_ed25519, TLSEXT_SIGALG_ed25519_name }, + { TLSEXT_SIGALG_ed448, TLSEXT_SIGALG_ed448_name }, { TLSEXT_SIGALG_ecdsa_brainpoolP256r1_sha256, TLSEXT_SIGALG_ecdsa_brainpoolP256r1_sha256_name }, { TLSEXT_SIGALG_ecdsa_brainpoolP384r1_sha384, TLSEXT_SIGALG_ecdsa_brainpoolP384r1_sha384_name }, { TLSEXT_SIGALG_ecdsa_brainpoolP512r1_sha512, TLSEXT_SIGALG_ecdsa_brainpoolP512r1_sha512_name }, - /* - * Well known sigalgs that we happen to know about, but only come from - * provider capability declarations (hence no macros for the - * codepoints/names) - */ - { 0x0904, "mldsa44" }, - { 0x0905, "mldsa65" }, - { 0x0906, "mldsa87" }, - { 0x0708, "sm2sig_sm3" }, + { TLSEXT_SIGALG_mldsa44, TLSEXT_SIGALG_mldsa44_name }, + { TLSEXT_SIGALG_mldsa65, TLSEXT_SIGALG_mldsa65_name }, + { TLSEXT_SIGALG_mldsa87, TLSEXT_SIGALG_mldsa87_name }, }; static const ssl_trace_tbl ssl_ctype_tbl[] = { From 4a08aa1346aa00151b09cc45fb2ce01ddee27230 Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Wed, 20 May 2026 12:24:25 +0200 Subject: [PATCH 021/349] Add TLS 1.3 SignatureScheme definitions for SLH-DSA In accordance with [1]. [1] https://datatracker.ietf.org/doc/html/draft-reddy-tls-slhdsa-02 Resolves: https://github.com/openssl/project/issues/1945 Signed-off-by: Eugene Syromiatnikov Reviewed-by: Bob Beck Reviewed-by: Neil Horman MergeDate: Tue Jun 23 16:30:01 2026 (Merged from https://github.com/openssl/openssl/pull/31248) --- include/internal/tlssigalgs.h | 26 ++++++++++++++++ providers/common/capabilities.c | 30 +++++++++++++++++-- ssl/t1_trce.c | 12 ++++++++ test/quicapitest.c | 2 +- .../75-test_quicapi_data/ssltraceref-zlib.txt | 12 ++++++++ .../75-test_quicapi_data/ssltraceref.txt | 12 ++++++++ .../90-test_sslapi_data/ssltraceref-zlib.txt | 12 ++++++++ .../90-test_sslapi_data/ssltraceref.txt | 12 ++++++++ .../cert.json.in | 4 +-- test/sslapitest.c | 1 + 10 files changed, 118 insertions(+), 5 deletions(-) diff --git a/include/internal/tlssigalgs.h b/include/internal/tlssigalgs.h index 1d6c9df49f..f5aea80f64 100644 --- a/include/internal/tlssigalgs.h +++ b/include/internal/tlssigalgs.h @@ -49,6 +49,19 @@ #define TLSEXT_SIGALG_mldsa65 0x0905 #define TLSEXT_SIGALG_mldsa87 0x0906 +#define TLSEXT_SIGALG_slhdsa_sha2_128s 0x0911 +#define TLSEXT_SIGALG_slhdsa_sha2_128f 0x0912 +#define TLSEXT_SIGALG_slhdsa_sha2_192s 0x0913 +#define TLSEXT_SIGALG_slhdsa_sha2_192f 0x0914 +#define TLSEXT_SIGALG_slhdsa_sha2_256s 0x0915 +#define TLSEXT_SIGALG_slhdsa_sha2_256f 0x0916 +#define TLSEXT_SIGALG_slhdsa_shake_128s 0x0917 +#define TLSEXT_SIGALG_slhdsa_shake_128f 0x0918 +#define TLSEXT_SIGALG_slhdsa_shake_192s 0x0919 +#define TLSEXT_SIGALG_slhdsa_shake_192f 0x091a +#define TLSEXT_SIGALG_slhdsa_shake_256s 0x091b +#define TLSEXT_SIGALG_slhdsa_shake_256f 0x091c + /* Sigalgs names */ #define TLSEXT_SIGALG_ecdsa_secp256r1_sha256_name "ecdsa_secp256r1_sha256" #define TLSEXT_SIGALG_ecdsa_secp384r1_sha384_name "ecdsa_secp384r1_sha384" @@ -92,4 +105,17 @@ #define TLSEXT_SIGALG_mldsa65_name "mldsa65" #define TLSEXT_SIGALG_mldsa87_name "mldsa87" +#define TLSEXT_SIGALG_slhdsa_sha2_128s_name "slhdsa_sha2_128s" +#define TLSEXT_SIGALG_slhdsa_sha2_128f_name "slhdsa_sha2_128f" +#define TLSEXT_SIGALG_slhdsa_sha2_192s_name "slhdsa_sha2_192s" +#define TLSEXT_SIGALG_slhdsa_sha2_192f_name "slhdsa_sha2_192f" +#define TLSEXT_SIGALG_slhdsa_sha2_256s_name "slhdsa_sha2_256s" +#define TLSEXT_SIGALG_slhdsa_sha2_256f_name "slhdsa_sha2_256f" +#define TLSEXT_SIGALG_slhdsa_shake_128s_name "slhdsa_shake_128s" +#define TLSEXT_SIGALG_slhdsa_shake_128f_name "slhdsa_shake_128f" +#define TLSEXT_SIGALG_slhdsa_shake_192s_name "slhdsa_shake_192s" +#define TLSEXT_SIGALG_slhdsa_shake_192f_name "slhdsa_shake_192f" +#define TLSEXT_SIGALG_slhdsa_shake_256s_name "slhdsa_shake_256s" +#define TLSEXT_SIGALG_slhdsa_shake_256f_name "slhdsa_shake_256f" + #endif diff --git a/providers/common/capabilities.c b/providers/common/capabilities.c index b97d4d6bcd..f461ec222a 100644 --- a/providers/common/capabilities.c +++ b/providers/common/capabilities.c @@ -281,7 +281,7 @@ static int tls_group_capability(OSSL_CALLBACK *cb, void *arg) /* --------------------------------------------------------------- */ -#if !defined(OPENSSL_NO_ML_DSA) \ +#if !defined(OPENSSL_NO_ML_DSA) || !defined(OPENSSL_NO_SLH_DSA) \ || (!defined(FIPS_MODULE) && !defined(OPENSSL_NO_SM2) && !defined(OPENSSL_NO_SM3)) typedef struct tls_sigalg_constants_st { @@ -297,6 +297,18 @@ static const TLS_SIGALG_CONSTANTS sigalg_constants_list[] = { { TLSEXT_SIGALG_mldsa44, 128, TLS1_3_VERSION, 0, -1, -1 }, { TLSEXT_SIGALG_mldsa65, 192, TLS1_3_VERSION, 0, -1, -1 }, { TLSEXT_SIGALG_mldsa87, 256, TLS1_3_VERSION, 0, -1, -1 }, + { TLSEXT_SIGALG_slhdsa_sha2_128s, 128, TLS1_3_VERSION, 0, -1, -1 }, + { TLSEXT_SIGALG_slhdsa_sha2_128f, 128, TLS1_3_VERSION, 0, -1, -1 }, + { TLSEXT_SIGALG_slhdsa_sha2_192s, 192, TLS1_3_VERSION, 0, -1, -1 }, + { TLSEXT_SIGALG_slhdsa_sha2_192f, 192, TLS1_3_VERSION, 0, -1, -1 }, + { TLSEXT_SIGALG_slhdsa_sha2_256s, 256, TLS1_3_VERSION, 0, -1, -1 }, + { TLSEXT_SIGALG_slhdsa_sha2_256f, 256, TLS1_3_VERSION, 0, -1, -1 }, + { TLSEXT_SIGALG_slhdsa_shake_128s, 128, TLS1_3_VERSION, 0, -1, -1 }, + { TLSEXT_SIGALG_slhdsa_shake_128f, 128, TLS1_3_VERSION, 0, -1, -1 }, + { TLSEXT_SIGALG_slhdsa_shake_192s, 192, TLS1_3_VERSION, 0, -1, -1 }, + { TLSEXT_SIGALG_slhdsa_shake_192f, 192, TLS1_3_VERSION, 0, -1, -1 }, + { TLSEXT_SIGALG_slhdsa_shake_256s, 256, TLS1_3_VERSION, 0, -1, -1 }, + { TLSEXT_SIGALG_slhdsa_shake_256f, 256, TLS1_3_VERSION, 0, -1, -1 }, { TLSEXT_SIGALG_sm2sig_sm3, 128, TLS1_3_VERSION, 0, -1, -1 }, }; @@ -329,8 +341,22 @@ static const OSSL_PARAM param_sigalg_list[][10] = { TLS_SIGALG_ENTRY("mldsa65", "ML-DSA-65", "2.16.840.1.101.3.4.3.18", 1), TLS_SIGALG_ENTRY("mldsa87", "ML-DSA-87", "2.16.840.1.101.3.4.3.19", 2), #endif +#ifndef OPENSSL_NO_SLH_DSA + TLS_SIGALG_ENTRY("slhdsa_sha2_128s", "SLH-DSA-SHA2-128s", "2.16.840.1.101.3.4.3.20", 3), + TLS_SIGALG_ENTRY("slhdsa_sha2_128f", "SLH-DSA-SHA2-128f", "2.16.840.1.101.3.4.3.21", 4), + TLS_SIGALG_ENTRY("slhdsa_sha2_192s", "SLH-DSA-SHA2-192s", "2.16.840.1.101.3.4.3.22", 5), + TLS_SIGALG_ENTRY("slhdsa_sha2_192f", "SLH-DSA-SHA2-192f", "2.16.840.1.101.3.4.3.23", 6), + TLS_SIGALG_ENTRY("slhdsa_sha2_256s", "SLH-DSA-SHA2-256s", "2.16.840.1.101.3.4.3.24", 7), + TLS_SIGALG_ENTRY("slhdsa_sha2_256f", "SLH-DSA-SHA2-256f", "2.16.840.1.101.3.4.3.25", 8), + TLS_SIGALG_ENTRY("slhdsa_shake_128s", "SLH-DSA-SHAKE-128s", "2.16.840.1.101.3.4.3.26", 9), + TLS_SIGALG_ENTRY("slhdsa_shake_128f", "SLH-DSA-SHAKE-128f", "2.16.840.1.101.3.4.3.27", 10), + TLS_SIGALG_ENTRY("slhdsa_shake_192s", "SLH-DSA-SHAKE-192s", "2.16.840.1.101.3.4.3.28", 11), + TLS_SIGALG_ENTRY("slhdsa_shake_192f", "SLH-DSA-SHAKE-192f", "2.16.840.1.101.3.4.3.29", 12), + TLS_SIGALG_ENTRY("slhdsa_shake_256s", "SLH-DSA-SHAKE-256s", "2.16.840.1.101.3.4.3.30", 13), + TLS_SIGALG_ENTRY("slhdsa_shake_256f", "SLH-DSA-SHAKE-256f", "2.16.840.1.101.3.4.3.31", 14), +#endif #if !defined(FIPS_MODULE) && !defined(OPENSSL_NO_SM2) && !defined(OPENSSL_NO_SM3) - TLS_SIGALG_ENTRY("sm2sig_sm3", "SM2", "1.2.156.10197.1 501", 3), + TLS_SIGALG_ENTRY("sm2sig_sm3", "SM2", "1.2.156.10197.1 501", 15), #endif }; #endif diff --git a/ssl/t1_trce.c b/ssl/t1_trce.c index b4d67d9d24..0e8ddbb8f8 100644 --- a/ssl/t1_trce.c +++ b/ssl/t1_trce.c @@ -617,6 +617,18 @@ static const ssl_trace_tbl ssl_sigalg_tbl[] = { { TLSEXT_SIGALG_mldsa44, TLSEXT_SIGALG_mldsa44_name }, { TLSEXT_SIGALG_mldsa65, TLSEXT_SIGALG_mldsa65_name }, { TLSEXT_SIGALG_mldsa87, TLSEXT_SIGALG_mldsa87_name }, + { TLSEXT_SIGALG_slhdsa_sha2_128s, TLSEXT_SIGALG_slhdsa_sha2_128s_name }, + { TLSEXT_SIGALG_slhdsa_sha2_128f, TLSEXT_SIGALG_slhdsa_sha2_128f_name }, + { TLSEXT_SIGALG_slhdsa_sha2_192s, TLSEXT_SIGALG_slhdsa_sha2_192s_name }, + { TLSEXT_SIGALG_slhdsa_sha2_192f, TLSEXT_SIGALG_slhdsa_sha2_192f_name }, + { TLSEXT_SIGALG_slhdsa_sha2_256s, TLSEXT_SIGALG_slhdsa_sha2_256s_name }, + { TLSEXT_SIGALG_slhdsa_sha2_256f, TLSEXT_SIGALG_slhdsa_sha2_256f_name }, + { TLSEXT_SIGALG_slhdsa_shake_128s, TLSEXT_SIGALG_slhdsa_shake_128s_name }, + { TLSEXT_SIGALG_slhdsa_shake_128f, TLSEXT_SIGALG_slhdsa_shake_128f_name }, + { TLSEXT_SIGALG_slhdsa_shake_192s, TLSEXT_SIGALG_slhdsa_shake_192s_name }, + { TLSEXT_SIGALG_slhdsa_shake_192f, TLSEXT_SIGALG_slhdsa_shake_192f_name }, + { TLSEXT_SIGALG_slhdsa_shake_256s, TLSEXT_SIGALG_slhdsa_shake_256s_name }, + { TLSEXT_SIGALG_slhdsa_shake_256f, TLSEXT_SIGALG_slhdsa_shake_256f_name }, }; static const ssl_trace_tbl ssl_ctype_tbl[] = { diff --git a/test/quicapitest.c b/test/quicapitest.c index c9b0dce585..5b7cdb6a3c 100644 --- a/test/quicapitest.c +++ b/test/quicapitest.c @@ -46,7 +46,7 @@ static int qc_init(SSL *qconn, BIO_ADDR *dst_addr); && defined(OPENSSL_NO_BROTLI) && defined(OPENSSL_NO_ZSTD) \ && !defined(OPENSSL_NO_ECX) && !defined(OPENSSL_NO_DH) \ && !defined(OPENSSL_NO_ML_DSA) && !defined(OPENSSL_NO_ML_KEM) \ - && !defined(OPENSSL_NO_SM2) + && !defined(OPENSSL_NO_SLH_DSA) && !defined(OPENSSL_NO_SM2) #define DO_SSL_TRACE_TEST #endif diff --git a/test/recipes/75-test_quicapi_data/ssltraceref-zlib.txt b/test/recipes/75-test_quicapi_data/ssltraceref-zlib.txt index bd8d4c2f6c..1bce022109 100644 --- a/test/recipes/75-test_quicapi_data/ssltraceref-zlib.txt +++ b/test/recipes/75-test_quicapi_data/ssltraceref-zlib.txt @@ -55,6 +55,18 @@ Header: rsa_pkcs1_sha256 (0x0401) rsa_pkcs1_sha384 (0x0501) rsa_pkcs1_sha512 (0x0601) + slhdsa_sha2_128s (0x0911) + slhdsa_sha2_128f (0x0912) + slhdsa_sha2_192s (0x0913) + slhdsa_sha2_192f (0x0914) + slhdsa_sha2_256s (0x0915) + slhdsa_sha2_256f (0x0916) + slhdsa_shake_128s (0x0917) + slhdsa_shake_128f (0x0918) + slhdsa_shake_192s (0x0919) + slhdsa_shake_192f (0x091a) + slhdsa_shake_256s (0x091b) + slhdsa_shake_256f (0x091c) sm2sig_sm3 (0x0708) extension_type=supported_versions(43), length=3 TLS 1.3 (772) diff --git a/test/recipes/75-test_quicapi_data/ssltraceref.txt b/test/recipes/75-test_quicapi_data/ssltraceref.txt index c453344987..300b4ca456 100644 --- a/test/recipes/75-test_quicapi_data/ssltraceref.txt +++ b/test/recipes/75-test_quicapi_data/ssltraceref.txt @@ -55,6 +55,18 @@ Header: rsa_pkcs1_sha256 (0x0401) rsa_pkcs1_sha384 (0x0501) rsa_pkcs1_sha512 (0x0601) + slhdsa_sha2_128s (0x0911) + slhdsa_sha2_128f (0x0912) + slhdsa_sha2_192s (0x0913) + slhdsa_sha2_192f (0x0914) + slhdsa_sha2_256s (0x0915) + slhdsa_sha2_256f (0x0916) + slhdsa_shake_128s (0x0917) + slhdsa_shake_128f (0x0918) + slhdsa_shake_192s (0x0919) + slhdsa_shake_192f (0x091a) + slhdsa_shake_256s (0x091b) + slhdsa_shake_256f (0x091c) sm2sig_sm3 (0x0708) extension_type=supported_versions(43), length=3 TLS 1.3 (772) diff --git a/test/recipes/90-test_sslapi_data/ssltraceref-zlib.txt b/test/recipes/90-test_sslapi_data/ssltraceref-zlib.txt index 68d46c8b38..e6c193fa82 100644 --- a/test/recipes/90-test_sslapi_data/ssltraceref-zlib.txt +++ b/test/recipes/90-test_sslapi_data/ssltraceref-zlib.txt @@ -48,6 +48,18 @@ Header: rsa_pkcs1_sha256 (0x0401) rsa_pkcs1_sha384 (0x0501) rsa_pkcs1_sha512 (0x0601) + slhdsa_sha2_128s (0x0911) + slhdsa_sha2_128f (0x0912) + slhdsa_sha2_192s (0x0913) + slhdsa_sha2_192f (0x0914) + slhdsa_sha2_256s (0x0915) + slhdsa_sha2_256f (0x0916) + slhdsa_shake_128s (0x0917) + slhdsa_shake_128f (0x0918) + slhdsa_shake_192s (0x0919) + slhdsa_shake_192f (0x091a) + slhdsa_shake_256s (0x091b) + slhdsa_shake_256f (0x091c) sm2sig_sm3 (0x0708) extension_type=supported_versions(43), length=3 TLS 1.3 (772) diff --git a/test/recipes/90-test_sslapi_data/ssltraceref.txt b/test/recipes/90-test_sslapi_data/ssltraceref.txt index d84d0e02fb..b5d8931e3d 100644 --- a/test/recipes/90-test_sslapi_data/ssltraceref.txt +++ b/test/recipes/90-test_sslapi_data/ssltraceref.txt @@ -48,6 +48,18 @@ Header: rsa_pkcs1_sha256 (0x0401) rsa_pkcs1_sha384 (0x0501) rsa_pkcs1_sha512 (0x0601) + slhdsa_sha2_128s (0x0911) + slhdsa_sha2_128f (0x0912) + slhdsa_sha2_192s (0x0913) + slhdsa_sha2_192f (0x0914) + slhdsa_sha2_256s (0x0915) + slhdsa_sha2_256f (0x0916) + slhdsa_shake_128s (0x0917) + slhdsa_shake_128f (0x0918) + slhdsa_shake_192s (0x0919) + slhdsa_shake_192f (0x091a) + slhdsa_shake_256s (0x091b) + slhdsa_shake_256f (0x091c) sm2sig_sm3 (0x0708) extension_type=supported_versions(43), length=3 TLS 1.3 (772) diff --git a/test/recipes/95-test_external_tlsfuzzer_data/cert.json.in b/test/recipes/95-test_external_tlsfuzzer_data/cert.json.in index 2ed47253b8..e22ffd2641 100644 --- a/test/recipes/95-test_external_tlsfuzzer_data/cert.json.in +++ b/test/recipes/95-test_external_tlsfuzzer_data/cert.json.in @@ -12,12 +12,12 @@ {"name" : "test-tls13-certificate-verify.py", "arguments" : ["-k", "tests/clientX509Key.pem", "-c", "tests/clientX509Cert.pem", - "-s", "9+5 9+6 9+4 ecdsa_secp256r1_sha256 ecdsa_secp384r1_sha384 ecdsa_secp521r1_sha512 ed25519 ed448 8+26 8+27 8+28 rsa_pss_pss_sha256 rsa_pss_pss_sha384 rsa_pss_pss_sha512 rsa_pss_rsae_sha256 rsa_pss_rsae_sha384 rsa_pss_rsae_sha512 rsa_pkcs1_sha256 rsa_pkcs1_sha384 rsa_pkcs1_sha512 7+8", + "-s", "9+5 9+6 9+4 ecdsa_secp256r1_sha256 ecdsa_secp384r1_sha384 ecdsa_secp521r1_sha512 ed25519 ed448 8+26 8+27 8+28 rsa_pss_pss_sha256 rsa_pss_pss_sha384 rsa_pss_pss_sha512 rsa_pss_rsae_sha256 rsa_pss_rsae_sha384 rsa_pss_rsae_sha512 rsa_pkcs1_sha256 rsa_pkcs1_sha384 rsa_pkcs1_sha512 9+17 9+18 9+19 9+20 9+21 9+22 9+23 9+24 9+25 9+26 9+27 9+28 7+8", "-p", "@PORT@"]}, {"name" : "test-tls13-ecdsa-in-certificate-verify.py", "arguments" : ["-k", "tests/serverECKey.pem", "-c", "tests/serverECCert.pem", - "-s", "9+5 9+6 9+4 ecdsa_secp256r1_sha256 ecdsa_secp384r1_sha384 ecdsa_secp521r1_sha512 ed25519 ed448 8+26 8+27 8+28 rsa_pss_pss_sha256 rsa_pss_pss_sha384 rsa_pss_pss_sha512 rsa_pss_rsae_sha256 rsa_pss_rsae_sha384 rsa_pss_rsae_sha512 rsa_pkcs1_sha256 rsa_pkcs1_sha384 rsa_pkcs1_sha512 7+8", + "-s", "9+5 9+6 9+4 ecdsa_secp256r1_sha256 ecdsa_secp384r1_sha384 ecdsa_secp521r1_sha512 ed25519 ed448 8+26 8+27 8+28 rsa_pss_pss_sha256 rsa_pss_pss_sha384 rsa_pss_pss_sha512 rsa_pss_rsae_sha256 rsa_pss_rsae_sha384 rsa_pss_rsae_sha512 rsa_pkcs1_sha256 rsa_pkcs1_sha384 rsa_pkcs1_sha512 9+17 9+18 9+19 9+20 9+21 9+22 9+23 9+24 9+25 9+26 9+27 9+28 7+8", "-p", "@PORT@"]} ] }, diff --git a/test/sslapitest.c b/test/sslapitest.c index c1e15c1933..91a2181b61 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -136,6 +136,7 @@ static const char *ocsp_signer_cert = "subinterCA.pem"; && defined(OPENSSL_NO_BROTLI) && defined(OPENSSL_NO_ZSTD) \ && !defined(OPENSSL_NO_ECX) && !defined(OPENSSL_NO_DH) \ && !defined(OPENSSL_NO_ML_DSA) && !defined(OPENSSL_NO_ML_KEM) \ + && !defined(OPENSSL_NO_SLH_DSA) \ && !defined(OPENSSL_NO_TLS1_3) && !defined(OPENSSL_NO_SM2) #define DO_SSL_TRACE_TEST #endif From 67bddd0f33a88f5083f6f6465cebc618a16e7bb7 Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Wed, 20 May 2026 12:30:01 +0200 Subject: [PATCH 022/349] apps/lib/s_cb.c: actualise signature_tls13_scheme_list Update signature_tls13_scheme_list array in accordance with the current state of the "TLS SignatureScheme" table at [1]; leave TLS 1.2 GOST definitions be for now. Note: these are used only for debugging output when -security_debug option is provided to s_client/s_server commands. [1] https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-signaturescheme Signed-off-by: Eugene Syromiatnikov Reviewed-by: Bob Beck Reviewed-by: Neil Horman MergeDate: Tue Jun 23 16:30:02 2026 (Merged from https://github.com/openssl/openssl/pull/31248) --- apps/lib/s_cb.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/apps/lib/s_cb.c b/apps/lib/s_cb.c index d4dcf12a07..c556e961fd 100644 --- a/apps/lib/s_cb.c +++ b/apps/lib/s_cb.c @@ -775,8 +775,8 @@ static const STRINT_PAIR tlsext_types[] = { { NULL } }; -/* from rfc8446 4.2.3. + gost (https://tools.ietf.org/id/draft-smyshlyaev-tls12-gost-suites-04.html) */ static STRINT_PAIR signature_tls13_scheme_list[] = { + /* RFC 8446 4.2.3 */ { "rsa_pkcs1_sha1", 0x0201 /* TLSEXT_SIGALG_rsa_pkcs1_sha1 */ }, { "ecdsa_sha1", 0x0203 /* TLSEXT_SIGALG_ecdsa_sha1 */ }, /* {"rsa_pkcs1_sha224", 0x0301 TLSEXT_SIGALG_rsa_pkcs1_sha224}, not in rfc8446 */ @@ -795,9 +795,59 @@ static STRINT_PAIR signature_tls13_scheme_list[] = { { "rsa_pss_pss_sha256", 0x0809 /* TLSEXT_SIGALG_rsa_pss_pss_sha256 */ }, { "rsa_pss_pss_sha384", 0x080a /* TLSEXT_SIGALG_rsa_pss_pss_sha384 */ }, { "rsa_pss_pss_sha512", 0x080b /* TLSEXT_SIGALG_rsa_pss_pss_sha512 */ }, + + /* RFC 8734 */ + { "ecdsa_brainpoolP256r1tls13_sha256", 0x81a }, + { "ecdsa_brainpoolP256r1tls13_sha384", 0x81b }, + { "ecdsa_brainpoolP256r1tls13_sha512", 0x81c }, + + /* RFC 8998 */ + { "sm2sig_sm3", 0x0708 /* TLSEXT_SIGALG_sm2sig_sm3 */ }, + + /* RFC 9367 */ + { "gostr34102012_256a", 0x709 }, + { "gostr34102012_256b", 0x70a }, + { "gostr34102012_256c", 0x70b }, + { "gostr34102012_256d", 0x70c }, + { "gostr34102012_512a", 0x70d }, + { "gostr34102012_512b", 0x70e }, + { "gostr34102012_512c", 0x70f }, + + /* RFC 9963 */ + { "rsa_pkcs1_sha256_legacy", 0x0420 }, + { "rsa_pkcs1_sha384_legacy", 0x0520 }, + { "rsa_pkcs1_sha512_legacy", 0x0620 }, + + /* IBS (https://datatracker.ietf.org/doc/html/draft-wang-tls-raw-public-key-with-ibc-02) */ + { "eccsi_sha256", 0x0704 }, + { "iso_ibs1", 0x0705 }, + { "iso_ibs2", 0x0706 }, + { "iso_chinese_ibs", 0x0707 }, + + /* ML-DSA (https://datatracker.ietf.org/doc/html/draft-ietf-tls-mldsa-00) */ + { "mldsa44", 0x0904 }, + { "mldsa65", 0x0905 }, + { "mldsa87", 0x0906 }, + + /* SLH-DSA (https://datatracker.ietf.org/doc/html/draft-reddy-tls-slhdsa-01) */ + { "slhdsa_sha2_128s", 0x0911 }, + { "slhdsa_sha2_128f", 0x0912 }, + { "slhdsa_sha2_192s", 0x0913 }, + { "slhdsa_sha2_192f", 0x0914 }, + { "slhdsa_sha2_256s", 0x0915 }, + { "slhdsa_sha2_256f", 0x0916 }, + { "slhdsa_shake_128s", 0x0917 }, + { "slhdsa_shake_128f", 0x0918 }, + { "slhdsa_shake_192s", 0x0919 }, + { "slhdsa_shake_192f", 0x091a }, + { "slhdsa_shake_256s", 0x091b }, + { "slhdsa_shake_256f", 0x091c }, + + /* GOST (https://tools.ietf.org/id/draft-smyshlyaev-tls12-gost-suites-04.html) */ { "gostr34102001", 0xeded /* TLSEXT_SIGALG_gostr34102001_gostr3411 */ }, { "gostr34102012_256", 0xeeee /* TLSEXT_SIGALG_gostr34102012_256_gostr34112012_256 */ }, { "gostr34102012_512", 0xefef /* TLSEXT_SIGALG_gostr34102012_512_gostr34112012_512 */ }, + { NULL } }; From a226dc352037f98d579c506b2815f9191324af27 Mon Sep 17 00:00:00 2001 From: Carlo Deutschmann Date: Fri, 19 Jun 2026 10:30:31 +0200 Subject: [PATCH 023/349] Avoid NULL dereference if RSA_PSS_PARAMS_dup() fails in ossl_rsa_dup() RSA_PSS_PARAMS_dup() can return NULL on failure (e.g. memory allocation failure). The subsequent code dereferenced dupkey->pss unconditionally when checking dupkey->pss->maskGenAlgorithm, which would result in a NULL pointer dereference. Check the return value and jump to the error handling instead, which properly frees the partially constructed key. Reviewed-by: Shane Lontis Reviewed-by: Daniel Kubec Reviewed-by: Tomas Mraz MergeDate: Tue Jun 23 16:32:24 2026 (Merged from https://github.com/openssl/openssl/pull/31619) --- crypto/rsa/rsa_backend.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crypto/rsa/rsa_backend.c b/crypto/rsa/rsa_backend.c index 00b4880c80..efacdcf9fe 100644 --- a/crypto/rsa/rsa_backend.c +++ b/crypto/rsa/rsa_backend.c @@ -532,7 +532,8 @@ RSA *ossl_rsa_dup(const RSA *rsa, int selection) } if (rsa->pss != NULL) { - dupkey->pss = RSA_PSS_PARAMS_dup(rsa->pss); + if ((dupkey->pss = RSA_PSS_PARAMS_dup(rsa->pss)) == NULL) + goto err; if (rsa->pss->maskGenAlgorithm != NULL && dupkey->pss->maskGenAlgorithm == NULL) { dupkey->pss->maskHash = ossl_x509_algor_mgf1_decode(rsa->pss->maskGenAlgorithm); From 24137d12a2e65db94118e01bf370ccce1d68c80f Mon Sep 17 00:00:00 2001 From: Haiyang Huang Date: Thu, 18 Jun 2026 09:19:42 +0800 Subject: [PATCH 024/349] quic: reject ACK of an unsent packet number MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ossl_ackm_on_rx_ack_frame() stored ack_ranges[0].end into largest_acked_pkt[pkt_space] without checking it against the highest packet number actually sent in that space. Because largest_acked_pkt only ever increases and drives loss detection, an ACK acknowledging a packet number that was never sent (up to 2**62 - 1) pins the value and causes every in-flight and subsequently-sent packet to be declared lost, permanently corrupting loss detection for the connection. RFC 9000 s. 13.1 recommends treating an acknowledgment for a packet the endpoint did not send as a connection error of type PROTOCOL_VIOLATION, where it can be detected. Reject any ACK whose largest acknowledged packet number exceeds the highest packet number sent in that space; the bound, highest_sent, is already tracked. The depacketiser raises PROTOCOL_VIOLATION when the ACK manager rejects the frame. Update the QUIC tests for the new behaviour: cases 7 and 8 now assert rejection, case 14 covers the 2**62 - 1 boundary, two pre-existing fixtures that acknowledged one packet past the highest sent are corrected, and the "fictional PN" script now expects a PROTOCOL_VIOLATION close. Fixes: fa4e92a70a5f "QUIC ACK Manager, Statistics Manager and Congestion Control API" Assisted-by: Claude:claude-opus-4.6 Reviewed-by: Saša Nedvědický Reviewed-by: Tomas Mraz MergeDate: Tue Jun 23 16:36:27 2026 (Merged from https://github.com/openssl/openssl/pull/31582) --- ssl/quic/quic_ackm.c | 13 +++++++ ssl/quic/quic_rx_depack.c | 15 ++++++-- test/quic_ackm_test.c | 66 +++++++++++++++++++++++++++++++----- test/quic_multistream_test.c | 7 +++- 4 files changed, 89 insertions(+), 12 deletions(-) diff --git a/ssl/quic/quic_ackm.c b/ssl/quic/quic_ackm.c index d1ac3b88e9..24acb8635c 100644 --- a/ssl/quic/quic_ackm.c +++ b/ssl/quic/quic_ackm.c @@ -1169,8 +1169,21 @@ int ossl_ackm_on_rx_ack_frame(OSSL_ACKM *ackm, const OSSL_QUIC_FRAME_ACK *ack, int pkt_space, OSSL_TIME rx_time) { OSSL_ACKM_TX_PKT *na_pkts, *lost_pkts; + struct tx_pkt_history_st *h = get_tx_history(ackm, pkt_space); int must_set_timer = 0; + /* + * RFC 9000 s. 13.1 recommends treating an acknowledgment for a packet we + * did not send as a PROTOCOL_VIOLATION, where detectable. The largest + * acknowledged PN is ack_ranges[0].end; if it exceeds the highest PN we have + * sent in this space, reject the ACK. Otherwise the peer-controlled value is + * stored into largest_acked_pkt below, which only ever increases and drives + * loss detection, so a single such ACK would permanently force every + * in-flight and subsequently-sent packet to be declared lost. + */ + if (ack->ack_ranges[0].end > h->highest_sent) + return 0; + if (ackm->largest_acked_pkt[pkt_space] == QUIC_PN_INVALID) ackm->largest_acked_pkt[pkt_space] = ack->ack_ranges[0].end; else diff --git a/ssl/quic/quic_rx_depack.c b/ssl/quic/quic_rx_depack.c index 730b0cf621..74cfceed05 100644 --- a/ssl/quic/quic_rx_depack.c +++ b/ssl/quic/quic_rx_depack.c @@ -125,8 +125,19 @@ static int depack_do_frame_ack(PACKET *pkt, QUIC_CHANNEL *ch, } if (!ossl_ackm_on_rx_ack_frame(ch->ackm, &ack, - packet_space, received)) - goto malformed; + packet_space, received)) { + /* + * The ACK manager rejects the frame if it acknowledges a packet number + * we have not sent. RFC 9000 s. 13.1 recommends treating this as a + * PROTOCOL_VIOLATION connection error (distinct from a frame decoding + * error, which is handled at the malformed label below). + */ + ossl_quic_channel_raise_protocol_error(ch, + OSSL_QUIC_ERR_PROTOCOL_VIOLATION, + frame_type, + "ACK for unsent packet number"); + return 0; + } ++ch->diag_num_rx_ack; return 1; diff --git a/test/quic_ackm_test.c b/test/quic_ackm_test.c index a4b488fafc..8598bf8398 100644 --- a/test/quic_ackm_test.c +++ b/test/quic_ackm_test.c @@ -11,6 +11,7 @@ #include #include "internal/quic_ackm.h" #include "internal/quic_cc.h" +#include "internal/quic_vlint.h" static OSSL_TIME fake_time = { 0 }; @@ -147,13 +148,26 @@ struct tx_ack_test_case { const OSSL_QUIC_ACK_RANGE *ack_ranges; size_t num_ack_ranges; const char *expect_ack; /* 1=ack, 2=lost, 4=discarded */ + int expect_reject; /* if nonzero the ACK must be rejected (returns 0) */ }; #define DEFINE_TX_ACK_CASE(n, pntable) \ static const struct tx_ack_test_case tx_ack_case_##n = { \ (pntable), OSSL_NELEM(pntable), \ tx_ack_range_##n, OSSL_NELEM(tx_ack_range_##n), \ - tx_ack_expect_##n \ + tx_ack_expect_##n, 0 \ + } + +/* + * As DEFINE_TX_ACK_CASE, but the ACK acknowledges a packet number that was + * never sent and so must be rejected by ossl_ackm_on_rx_ack_frame() + * (RFC 9000 s. 13.1). + */ +#define DEFINE_TX_ACK_CASE_REJECT(n, pntable) \ + static const struct tx_ack_test_case tx_ack_case_##n = { \ + (pntable), OSSL_NELEM(pntable), \ + tx_ack_range_##n, OSSL_NELEM(tx_ack_range_##n), \ + tx_ack_expect_##n, 1 \ } /* One range, partial coverage of space */ @@ -207,32 +221,32 @@ static const char tx_ack_expect_5[] = { }; DEFINE_TX_ACK_CASE(5, linear_20); -/* One range, covering entire space */ +/* One range covering the whole space (0..19, highest sent PN is 19): all acked */ static const OSSL_QUIC_ACK_RANGE tx_ack_range_6[] = { - { 0, 20 }, + { 0, 19 }, }; static const char tx_ack_expect_6[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; DEFINE_TX_ACK_CASE(6, linear_20); -/* One range, covering more space than exists */ +/* One range above the highest sent PN (30 > 19): ACK rejected */ static const OSSL_QUIC_ACK_RANGE tx_ack_range_7[] = { { 0, 30 }, }; static const char tx_ack_expect_7[] = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -DEFINE_TX_ACK_CASE(7, linear_20); +DEFINE_TX_ACK_CASE_REJECT(7, linear_20); -/* One range, covering nothing (too high) */ +/* One range entirely above the sent PNs (21..30): ACK rejected */ static const OSSL_QUIC_ACK_RANGE tx_ack_range_8[] = { { 21, 30 }, }; static const char tx_ack_expect_8[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -DEFINE_TX_ACK_CASE(8, linear_20); +DEFINE_TX_ACK_CASE_REJECT(8, linear_20); /* One range, covering nothing (too low) */ static const OSSL_QUIC_ACK_RANGE tx_ack_range_9[] = { @@ -289,6 +303,20 @@ static const char tx_ack_expect_13[] = { }; DEFINE_TX_ACK_CASE(13, high_linear_20); +/* + * Largest range claims the maximum PN (2**62 - 1, never sent) plus a second + * range over real packets so loss detection would otherwise run. ACK rejected; + * otherwise largest_acked_pkt pins at the maximum and every in-flight packet is + * declared lost. + */ +static const OSSL_QUIC_ACK_RANGE tx_ack_range_14[] = { + { OSSL_QUIC_VLINT_MAX, OSSL_QUIC_VLINT_MAX }, { 15, 19 } +}; +static const char tx_ack_expect_14[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +DEFINE_TX_ACK_CASE_REJECT(14, linear_20); + static const struct tx_ack_test_case *const tx_ack_cases[] = { &tx_ack_case_1, &tx_ack_case_2, @@ -303,6 +331,7 @@ static const struct tx_ack_test_case *const tx_ack_cases[] = { &tx_ack_case_11, &tx_ack_case_12, &tx_ack_case_13, + &tx_ack_case_14, }; enum { @@ -402,6 +431,25 @@ static int test_tx_ack_case_actual(int tidx, int space, int mode) /* Try acknowledging. */ ack.ack_ranges = (OSSL_QUIC_ACK_RANGE *)c->ack_ranges; ack.num_ack_ranges = c->num_ack_ranges; + + if (c->expect_reject) { + /* ACK of an unsent PN: rejected without touching loss detection. */ + if (!TEST_int_eq(ossl_ackm_on_rx_ack_frame(h.ackm, &ack, space, + fake_time), + 0)) + goto err; + + for (i = 0; i < c->pn_table_len; ++i) { + if (!TEST_int_eq(h.pkts[i].acked, 0) + || !TEST_int_eq(h.pkts[i].lost, 0) + || !TEST_int_eq(h.pkts[i].discarded, 0)) + goto err; + } + + testresult = 1; + goto err; + } + if (!TEST_int_eq(ossl_ackm_on_rx_ack_frame(h.ackm, &ack, space, fake_time), 1)) goto err; @@ -577,7 +625,7 @@ static int test_tx_ack_time_script(int tidx) ack.num_ack_ranges = 1; ack_range.start = s->pn; - ack_range.end = s->pn + s->num_pn; + ack_range.end = s->pn + s->num_pn - 1; fake_time = ossl_time_add(fake_time, ossl_ticks2time(s->time_advance)); diff --git a/test/quic_multistream_test.c b/test/quic_multistream_test.c index de7ea27a49..4edd53e15f 100644 --- a/test/quic_multistream_test.c +++ b/test/quic_multistream_test.c @@ -3886,7 +3886,12 @@ static const struct script_op script_49[] = { OP_SET_INJECT_WORD(4, 0), OP_S_WRITE(a, "Strawberry", 10), - OP_C_READ_EXPECT(DEFAULT, "Strawberry", 10), + /* + * The injected ACK acknowledges a packet number we have not sent, which the + * peer is expected to treat as a PROTOCOL_VIOLATION, so the connection is + * closed rather than the stream data being delivered. + */ + OP_C_EXPECT_CONN_CLOSE_INFO(OSSL_QUIC_ERR_PROTOCOL_VIOLATION, 0, 0), OP_END }; From 77c979faf47b777afb07c325f738d8be882f6a97 Mon Sep 17 00:00:00 2001 From: "Randall S. Becker" Date: Mon, 22 Jun 2026 13:28:51 -0600 Subject: [PATCH 025/349] Fix unqualified reference to openssl in 25-test_verify_store.t This problem resulted in the wrong location of openssl being used for one step in subtest 7. The error condition is hidden if openssl appears in the PATH. Resolves: https://github.com/openssl/openssl/issues/31496 Fixes: 3638ffc38015 "Refactor cache_objects() loop and object type handling" Signed-off-by: Randall S. Becker Reviewed-by: Nikola Pajkovsky Reviewed-by: David von Oheimb Reviewed-by: Matt Caswell Reviewed-by: Eugene Syromiatnikov Reviewed-by: Bob Beck MergeDate: Wed Jun 24 08:47:58 2026 (Merged from https://github.com/openssl/openssl/pull/31647) --- test/recipes/25-test_verify_store.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/recipes/25-test_verify_store.t b/test/recipes/25-test_verify_store.t index 6338b862b5..0db8e6238d 100644 --- a/test/recipes/25-test_verify_store.t +++ b/test/recipes/25-test_verify_store.t @@ -73,7 +73,7 @@ SKIP: { $CAcert ); open(my $out, '>', $CAobjects) or die $!; - my $pubkey = qx(openssl x509 -pubkey -noout -in $CAcert); + my $pubkey = run(app(["openssl", "x509", "-pubkey", "-noout", "-in", $CAcert])); print $out $pubkey; my @files; push @files, srctop_file("test", "certs", "dhp2048.pem") From b44fd71741b7b5092c087d227b32298c7fedb520 Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Tue, 21 Apr 2026 10:32:50 +0900 Subject: [PATCH 026/349] pkcs7: Fix negative index handling in PKCS7_get_issuer_and_serial() Reject negative indices before looking up the recipient info stack entry. This makes negative out-of-range indices match the existing behavior for too-large positive indices and avoids dereferencing a NULL recipient info. Add a regression test for the negative index case. Resolves: https://github.com/openssl/openssl/issues/30910 Reviewed-by: Eugene Syromiatnikov Reviewed-by: Nikola Pajkovsky Reviewed-by: David von Oheimb MergeDate: Wed Jun 24 09:10:22 2026 (Merged from https://github.com/openssl/openssl/pull/30914) --- crypto/pkcs7/pk7_doit.c | 2 +- test/pkcs7_test.c | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/crypto/pkcs7/pk7_doit.c b/crypto/pkcs7/pk7_doit.c index 7b6a3b36b4..1878b4aac2 100644 --- a/crypto/pkcs7/pk7_doit.c +++ b/crypto/pkcs7/pk7_doit.c @@ -1171,7 +1171,7 @@ PKCS7_ISSUER_AND_SERIAL *PKCS7_get_issuer_and_serial(PKCS7 *p7, int idx) rsk = p7->d.signed_and_enveloped->recipientinfo; if (rsk == NULL) return NULL; - if (sk_PKCS7_RECIP_INFO_num(rsk) <= idx) + if (idx < 0 || sk_PKCS7_RECIP_INFO_num(rsk) <= idx) return NULL; ri = sk_PKCS7_RECIP_INFO_value(rsk, idx); return ri->issuer_and_serial; diff --git a/test/pkcs7_test.c b/test/pkcs7_test.c index adf069695e..3fe68f60aa 100644 --- a/test/pkcs7_test.c +++ b/test/pkcs7_test.c @@ -15,6 +15,30 @@ #include "internal/nelem.h" #include "testutil.h" +static int pkcs7_issuer_and_serial_negative_idx_test(void) +{ + PKCS7 *p7 = NULL; + PKCS7_RECIP_INFO *ri = NULL; + int ret = 0; + + if (!TEST_ptr(p7 = PKCS7_new()) + || !TEST_true(PKCS7_set_type(p7, NID_pkcs7_signedAndEnveloped)) + || !TEST_ptr(ri = PKCS7_RECIP_INFO_new()) + || !TEST_true(PKCS7_add_recipient_info(p7, ri))) + goto end; + ri = NULL; + + if (!TEST_ptr(PKCS7_get_issuer_and_serial(p7, 0)) + || !TEST_ptr_null(PKCS7_get_issuer_and_serial(p7, -1))) + goto end; + + ret = 1; +end: + PKCS7_RECIP_INFO_free(ri); + PKCS7_free(p7); + return ret; +} + #ifndef OPENSSL_NO_EC static const unsigned char cert_der[] = { 0x30, 0x82, 0x01, 0x51, 0x30, 0x81, 0xf7, 0xa0, 0x03, 0x02, 0x01, 0x02, @@ -389,6 +413,7 @@ end: int setup_tests(void) { + ADD_TEST(pkcs7_issuer_and_serial_negative_idx_test); #ifndef OPENSSL_NO_EC ADD_TEST(pkcs7_verify_test); ADD_TEST(pkcs7_inner_content_verify_test); From 773400d3664570e276a8016110ae0c75da98065e Mon Sep 17 00:00:00 2001 From: dannyward630 Date: Thu, 18 Jun 2026 19:44:01 +0200 Subject: [PATCH 027/349] crypto/ec/curve448/eddsa.c: document Ed448 property query parameter Document the propq argument in the Ed448 key generation, signing, and verification helper comments. CLA: trivial Reviewed-by: Paul Dale Reviewed-by: Eugene Syromiatnikov MergeDate: Wed Jun 24 11:00:00 2026 (Merged from https://github.com/openssl/openssl/pull/31599) --- crypto/ec/curve448/eddsa.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crypto/ec/curve448/eddsa.c b/crypto/ec/curve448/eddsa.c index 8615b19dfc..1c375413ab 100644 --- a/crypto/ec/curve448/eddsa.c +++ b/crypto/ec/curve448/eddsa.c @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2017-2026 The OpenSSL Project Authors. All Rights Reserved. * Copyright 2015-2016 Cryptography Research, Inc. * * Licensed under the Apache License 2.0 (the "License"). You may not use @@ -94,6 +94,7 @@ static c448_error_t hash_init_with_dom(OSSL_LIB_CTX *ctx, EVP_MD_CTX *hashctx, * * pubkey (out): The public key. * privkey (in): The private key. + * propq (in): The property query used to fetch SHAKE256. */ static c448_error_t c448_ed448_derive_public_key( @@ -155,6 +156,7 @@ c448_ed448_derive_public_key( * you want to sign. * context (in): A "context" for this signature of up to 255 bytes. * context_len (in): Length of the context. + * propq (in): The property query used to fetch SHAKE256. * * For Ed25519, it is unsafe to use the same key for both prehashed and * non-prehashed messages, at least without some very careful protocol-level @@ -292,6 +294,7 @@ c448_ed448_pubkey_verify(const uint8_t *pub, size_t pub_len) * want to verify. * context (in): A "context" for this signature of up to 255 bytes. * context_len (in): Length of the context. + * propq (in): The property query used to fetch SHAKE256. * * For Ed25519, it is unsafe to use the same key for both prehashed and * non-prehashed messages, at least without some very careful protocol-level From 1cfec91eb67b82066ad493b1150c253231cc2a9f Mon Sep 17 00:00:00 2001 From: Alicja Kario Date: Tue, 16 Jun 2026 17:12:14 +0200 Subject: [PATCH 028/349] test/evp_extra_test.c: add test case for CVE-2026-42770 Signed-off-by: Alicja Kario Reviewed-by: Igor Ustinov Reviewed-by: Dmitry Belyavskiy MergeDate: Wed Jun 24 11:56:27 2026 (Merged from https://github.com/openssl/openssl/pull/31548) --- test/evp_extra_test.c | 134 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c index 3033255103..1e3207cd85 100644 --- a/test/evp_extra_test.c +++ b/test/evp_extra_test.c @@ -4440,6 +4440,139 @@ err: return ret; } #endif /* !OPENSSL_NO_DEPRECATED_3_0 */ + +/* Test that DHX (X9.42) rejects a malicious peer key during the + * derivation phase (specifically EVP_PKEY_derive_set_peer) when the + * remote 'q' does not match the local domain parameters but is still + * consistent with the remote key share. + * (CVE-2026-42770) + */ +static int test_dhx_derive_rejects_bad_peer_q(void) +{ + int ret = 0; + EVP_PKEY *local_key = NULL, *remote_key = NULL; + EVP_PKEY_CTX *pctx = NULL, *derive_ctx = NULL; + OSSL_PARAM_BLD *bld = NULL; + OSSL_PARAM *params = NULL; + + BIGNUM *p = NULL, *g = NULL; + BIGNUM *q_valid = NULL, *pub_local = NULL, *priv_local = NULL; + BIGNUM *q_bad = NULL, *pub_bad = NULL; + + const char *hex_p = "87a8e61db4b6663cffbbd19c651959998ceef608660dd0f25d2ceed4435e3b00" + "e00df8f1d61957d4faf7df4561b2aa3016c3d91134096faa3bf4296d830e9a7c" + "209e0c6497517abd5a8a9d306bcf67ed91f9e6725b4758c022e0b1ef4275bf7b" + "6c5bfc11d45f9088b941f54eb1e59bb8bc39a0bf12307f5c4fdb70c581b23f76" + "b63acae1caa6b7902d52526735488a0ef13c6d9a51bfa4ab3ad8347796524d8e" + "f6a167b5a41825d967e144e5140564251ccacb83e6b486f6b3ca3f7971506026" + "c0b857f689962856ded4010abd0be621c3a3960a54e710c375f26375d7014103" + "a4b54330c198af126116d2276e11715f693877fad7ef09cadb094ae91e1a1597"; + const char *hex_g = "3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF205407F4793A1A0BA125" + "10DBC15077BE463FFF4FED4AAC0BB555BE3A6C1B0C6B47B1BC3773BF7E8C6F62" + "901228F8C28CBB18A55AE31341000A650196F931C77A57F2DDF463E5E9EC144B" + "777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC831D14348F6F2F9193" + "B5045AF2767164E1DFC967C1FB3F2E55A4BD1BFFE83B9C80D052B985D182EA0A" + "DB2A3B7313D3FE14C8484B1E052588B9B7D2BBD2DF016199ECD06E1557CD0915" + "B3353BBB64E0EC377FD028370DF92B52C7891428CDC67EB6184B523D1DB246C3" + "2F63078490F00EF8D647D148D47954515E2327CFEF98C582664B4C0F6CC41659"; + + const char *hex_q_valid = "8CF83642A709A097B447997640129DA299B1A47D1EB3750BA308B0FE64F5FBD3"; + const char *hex_local_pub = "796e15431470ac86fa8a78b8bcdd1f3589dbf15ffe0e0a7a41dd8640887f3cc3" + "f0439e281f4cf3800bac2dbdfcda589b26cc828512085ce0d3e57aa13cd9e7a4" + "66d881bace91ed10c6064ab36e0d66367c4bfed56a9f907e4daec16732fb5c54" + "891cb0d26251fd61c32040774246b3f8bdcd5ef60e6847cdd69bd6d318d1cda0" + "e8a30a716de4dc1a4eb99b0686b77120c4b69b0005f6a8c3ae768d23c08c85bd" + "1d58f40dc0138d6277436137ae69779fdc218c071c14826f471562033e85ffc9" + "9a2147d539e274136a4a1e7f1db97583b51dc0385a52d7383963758d893398a8" + "d013fdbad20ddf30fbe05fbb2249913ae6751b6b246ae5622ba26c4827417c2d"; + const char *hex_local_priv = "1122334455667788990011223344556677889900112233445566778899001122"; + + /* Remote malicious parameters */ + const char *hex_remote_q = "09f5"; + const char *hex_remote_pub = "54c057903d362235a65c03f001d8a3ea252836b3580250abdc0a1083451af012" + "6fd150f9e8d212b384ae0c23aa7c67fe851368114ccc061a661e986bd7e63d25" + "7513339a6914cbfab209ad793ef25704cd532ddfb7e693de701d17e629ef3c18" + "4d40d5fea1f9edb89c5bf8d7aa19e3374f805932159ca7b5d573b9e2f3c94fe7" + "47c4a3b09e31afa3788d35833aaf2ac8ae8bc48500131464e793a2e0352e7c3e" + "d9da9fcf89b121bc1cee83c544214ceb3338b14ac6891968351746eaf62bb517" + "eb98fc633d8d235bac37bc08e47f1851d05501949a6733965adbfe8e43f7c3b9" + "3ca7515cd6ab36d7ef26bb0fd6033abc39613e880fffc8729b03bfeaddf08833"; + + if (!TEST_true(BN_hex2bn(&p, hex_p)) || !TEST_true(BN_hex2bn(&g, hex_g))) + goto err; + + if (!TEST_true(BN_hex2bn(&q_valid, hex_q_valid)) + || !TEST_true(BN_hex2bn(&pub_local, hex_local_pub)) + || !TEST_true(BN_hex2bn(&priv_local, hex_local_priv))) + goto err; + + if (!TEST_ptr(bld = OSSL_PARAM_BLD_new()) + || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_P, p)) + || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_Q, q_valid)) + || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_G, g)) + || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PUB_KEY, pub_local)) + || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, priv_local)) + || !TEST_ptr(params = OSSL_PARAM_BLD_to_param(bld))) + goto err; + + if (!TEST_ptr(pctx = EVP_PKEY_CTX_new_from_name(testctx, "DHX", testpropq)) + || !TEST_int_gt(EVP_PKEY_fromdata_init(pctx), 0) + || !TEST_int_gt(EVP_PKEY_fromdata(pctx, &local_key, EVP_PKEY_KEYPAIR, params), 0)) + goto err; + + OSSL_PARAM_free(params); + OSSL_PARAM_BLD_free(bld); + EVP_PKEY_CTX_free(pctx); + params = NULL; + bld = NULL; + pctx = NULL; + + if (!TEST_true(BN_hex2bn(&q_bad, hex_remote_q)) + || !TEST_true(BN_hex2bn(&pub_bad, hex_remote_pub))) + goto err; + + if (!TEST_ptr(bld = OSSL_PARAM_BLD_new()) + || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_P, p)) + || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_Q, q_bad)) + || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_G, g)) + || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PUB_KEY, pub_bad)) + || !TEST_ptr(params = OSSL_PARAM_BLD_to_param(bld))) + goto err; + + if (!TEST_ptr(pctx = EVP_PKEY_CTX_new_from_name(testctx, "DHX", testpropq)) + || !TEST_int_gt(EVP_PKEY_fromdata_init(pctx), 0) + || !TEST_int_gt(EVP_PKEY_fromdata(pctx, &remote_key, EVP_PKEY_PUBLIC_KEY, params), 0)) + goto err; + + if (!TEST_ptr(derive_ctx = EVP_PKEY_CTX_new(local_key, NULL)) + || !TEST_int_gt(EVP_PKEY_derive_init(derive_ctx), 0)) + goto err; + + /* reject the remote key share, even if it is self-consistent, correct + * code needs to use local q, not remote-provided q. */ + if (!TEST_int_le(EVP_PKEY_derive_set_peer(derive_ctx, remote_key), 0)) { + TEST_error("EVP_PKEY_derive_set_peer incorrectly accepted a peer with malicious 'q'"); + goto err; + } + + ret = 1; + +err: + BN_free(p); + BN_free(g); + BN_free(q_valid); + BN_free(pub_local); + BN_free(priv_local); + BN_free(q_bad); + BN_free(pub_bad); + OSSL_PARAM_free(params); + OSSL_PARAM_BLD_free(bld); + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_CTX_free(derive_ctx); + EVP_PKEY_free(local_key); + EVP_PKEY_free(remote_key); + return ret; +} #endif /* !OPENSSL_NO_DH */ /* @@ -8525,6 +8658,7 @@ int setup_tests(void) #ifndef OPENSSL_NO_DEPRECATED_3_0 ADD_TEST(test_EVP_PKEY_set1_DH); #endif + ADD_TEST(test_dhx_derive_rejects_bad_peer_q); #endif #ifndef OPENSSL_NO_EC ADD_TEST(test_EC_priv_pub); From a933605a6261dae1bd30bd6e952717d0c1d2db03 Mon Sep 17 00:00:00 2001 From: Billy Brumley Date: Wed, 17 Jun 2026 02:17:45 -0400 Subject: [PATCH 029/349] [test] various zero-length message positive and negative tests for AEAD ciphers A zero-length AEAD message driven through the one-shot EVP_Cipher() interface must agree with the streaming EVP_CipherFinal_ex() path. This checks: - an empty message yields the same tag via both interfaces - the true tag passes verification on decrypt - the modified tag fails verification on decrypt Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Dmitry Belyavskiy Reviewed-by: Neil Horman MergeDate: Wed Jun 24 12:47:11 2026 (Merged from https://github.com/openssl/openssl/pull/31555) --- test/evp_extra_test.c | 219 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c index 1e3207cd85..b0c37e476e 100644 --- a/test/evp_extra_test.c +++ b/test/evp_extra_test.c @@ -5711,6 +5711,224 @@ err: EVP_CIPHER_CTX_free(ctx_onestep); return testresult; } + +/*- + * A zero-length AEAD message driven through the one-shot EVP_Cipher() interface + * must agree with the streaming EVP_CipherFinal_ex() path. This checks: + * - an empty message yields the same tag via both interfaces + * - the true tag passes verification on decrypt + * - the modified tag fails verification on decrypt + */ +static int test_evp_oneshot_aead_zerolen(int idx) +{ + const EVP_CIPHER_TEST_INFO *info = &cipher_list[idx]; + EVP_CIPHER_CTX *ctx_stream = NULL; /* reference: final only */ + EVP_CIPHER_CTX *ctx_oneshot = NULL; /* encrypt via EVP_Cipher(in == NULL) */ + EVP_CIPHER_CTX *ctx_dec = NULL; /* decrypt via EVP_Cipher(in == NULL) */ + EVP_CIPHER_CTX *ctx_dec_bad = NULL; /* decrypt with a corrupted tag */ + EVP_CIPHER_CTX *ctx_dec_s = NULL; /* streaming decrypt */ + EVP_CIPHER_CTX *ctx_dec_s_bad = NULL; /* streaming decrypt, corrupted tag */ + + OSSL_PARAM get_tagparams[2]; + OSSL_PARAM set_tagparams[2]; + + int taglen = info->taglen; + unsigned char key[EVP_MAX_KEY_LENGTH] = { 0 }; + unsigned char iv[EVP_MAX_IV_LENGTH] = { 0 }; + + unsigned char ct[16] = { 0 }; /* scratch out; AEAD finalize writes no data */ + int finlen = 0, oneshot_flen = 0, dec_flen = 0; + + unsigned char tag_stream[EVPTEST_TAG_LEN_MAX] = { 0 }; + unsigned char tag_oneshot[EVPTEST_TAG_LEN_MAX] = { 0 }; + unsigned char tag_bad[EVPTEST_TAG_LEN_MAX] = { 0 }; + + int i = 0, testresult = 1; + char *errmsg = NULL; + + /* filter out various modes */ + if (info->taglen == 0 + || info->mode == EVP_CIPH_CCM_MODE + || info->mode == EVP_CIPH_OCB_MODE + || info->mode == EVP_CIPH_GCM_SIV_MODE + /* skip TLS stitched MTE cipher */ + || EVP_CIPHER_is_a(info->ciph, "AES-128-CBC-HMAC-SHA1") + /* skip TLS stitched MTE cipher */ + || EVP_CIPHER_is_a(info->ciph, "AES-256-CBC-HMAC-SHA1") + /* skip TLS stitched MTE cipher */ + || EVP_CIPHER_is_a(info->ciph, "AES-128-CBC-HMAC-SHA256") + /* skip TLS stitched MTE cipher */ + || EVP_CIPHER_is_a(info->ciph, "AES-256-CBC-HMAC-SHA256") + || EVP_CIPHER_is_a(info->ciph, "ChaCha20-Poly1305")) + return 1; + + for (i = 0; i < info->keylen && i < (int)sizeof(key); i++) + key[i] = (unsigned char)(0xA0 + i); + for (i = 0; i < info->ivlen && i < (int)sizeof(iv); i++) + iv[i] = (unsigned char)(0xB0 + i); + + /* reference: streaming finalize of an empty message */ + if (!TEST_ptr(ctx_stream = EVP_CIPHER_CTX_new())) { + errmsg = "STREAM_ALLOC"; + goto err; + } + if (!TEST_true(EVP_EncryptInit_ex2(ctx_stream, info->ciph, key, iv, NULL))) { + errmsg = "STREAM_INIT"; + goto err; + } + if (!TEST_true(EVP_EncryptFinal_ex(ctx_stream, ct, &finlen))) { + errmsg = "STREAM_FINAL"; + goto err; + } + + /* clamp to the negotiated tag length if the cipher reports one */ + i = EVP_CIPHER_CTX_get_tag_length(ctx_stream); + if (i > 0) + taglen = i; + + /* bound the memcpy, should never happen but helps static analysis */ + if (taglen > EVPTEST_TAG_LEN_MAX) { + errmsg = "TAGLEN_EXCEEDS_BUF"; + goto err; + } + + get_tagparams[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + tag_stream, taglen); + get_tagparams[1] = OSSL_PARAM_construct_end(); + if (!TEST_true(EVP_CIPHER_CTX_get_params(ctx_stream, get_tagparams))) { + errmsg = "STREAM_GET_TAG"; + goto err; + } + + /* one-shot encrypt: finalize the empty message with in == NULL */ + if (!TEST_ptr(ctx_oneshot = EVP_CIPHER_CTX_new())) { + errmsg = "ONESHOT_ALLOC"; + goto err; + } + if (!TEST_true(EVP_EncryptInit_ex2(ctx_oneshot, info->ciph, key, iv, NULL))) { + errmsg = "ONESHOT_INIT"; + goto err; + } + oneshot_flen = EVP_Cipher(ctx_oneshot, ct, NULL, 0); + if (!TEST_int_ge(oneshot_flen, 0)) { + errmsg = "ONESHOT_FINAL_NULL"; + goto err; + } + + get_tagparams[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + tag_oneshot, taglen); + if (!TEST_true(EVP_CIPHER_CTX_get_params(ctx_oneshot, get_tagparams))) { + errmsg = "ONESHOT_GET_TAG"; + goto err; + } + if (!TEST_mem_eq(tag_oneshot, taglen, tag_stream, taglen)) { + errmsg = "TAG_MISMATCH_ONESHOT_vs_STREAM"; + goto err; + } + + /* one-shot decrypt: the correct tag must verify via in == NULL */ + if (!TEST_ptr(ctx_dec = EVP_CIPHER_CTX_new())) { + errmsg = "DEC_ALLOC"; + goto err; + } + if (!TEST_true(EVP_DecryptInit_ex2(ctx_dec, info->ciph, key, iv, NULL))) { + errmsg = "DEC_INIT"; + goto err; + } + set_tagparams[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + tag_stream, taglen); + set_tagparams[1] = OSSL_PARAM_construct_end(); + if (!TEST_true(EVP_CIPHER_CTX_set_params(ctx_dec, set_tagparams))) { + errmsg = "DEC_SET_TAG"; + goto err; + } + dec_flen = EVP_Cipher(ctx_dec, ct, NULL, 0); + if (!TEST_int_ge(dec_flen, 0)) { + errmsg = "DEC_VERIFY_NULL"; + goto err; + } + + /* + * ...and a corrupted tag must be rejected. EVP_Cipher() returns < 0 on a + * failed one-shot, so a non-negative result here means verification was + * skipped or wrongly accepted the bad tag. + */ + memcpy(tag_bad, tag_stream, taglen); + tag_bad[0] ^= 0x01; + if (!TEST_ptr(ctx_dec_bad = EVP_CIPHER_CTX_new())) { + errmsg = "DEC_BAD_ALLOC"; + goto err; + } + if (!TEST_true(EVP_DecryptInit_ex2(ctx_dec_bad, info->ciph, key, iv, NULL))) { + errmsg = "DEC_BAD_INIT"; + goto err; + } + set_tagparams[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + tag_bad, taglen); + if (!TEST_true(EVP_CIPHER_CTX_set_params(ctx_dec_bad, set_tagparams))) { + errmsg = "DEC_BAD_SET_TAG"; + goto err; + } + if (!TEST_int_lt(EVP_Cipher(ctx_dec_bad, ct, NULL, 0), 0)) { + errmsg = "DEC_BADTAG_NOT_REJECTED"; + goto err; + } + + /* streaming decrypt: the correct tag must verify via EVP_DecryptFinal_ex */ + if (!TEST_ptr(ctx_dec_s = EVP_CIPHER_CTX_new())) { + errmsg = "DEC_STREAM_ALLOC"; + goto err; + } + if (!TEST_true(EVP_DecryptInit_ex2(ctx_dec_s, info->ciph, key, iv, NULL))) { + errmsg = "DEC_STREAM_INIT"; + goto err; + } + set_tagparams[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + tag_stream, taglen); + if (!TEST_true(EVP_CIPHER_CTX_set_params(ctx_dec_s, set_tagparams))) { + errmsg = "DEC_STREAM_SET_TAG"; + goto err; + } + if (!TEST_true(EVP_DecryptFinal_ex(ctx_dec_s, ct, &finlen))) { + errmsg = "DEC_STREAM_VERIFY"; + goto err; + } + + /* streaming decrypt: a corrupted tag must be rejected */ + if (!TEST_ptr(ctx_dec_s_bad = EVP_CIPHER_CTX_new())) { + errmsg = "DEC_STREAM_BAD_ALLOC"; + goto err; + } + if (!TEST_true(EVP_DecryptInit_ex2(ctx_dec_s_bad, info->ciph, key, iv, NULL))) { + errmsg = "DEC_STREAM_BAD_INIT"; + goto err; + } + set_tagparams[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + tag_bad, taglen); + if (!TEST_true(EVP_CIPHER_CTX_set_params(ctx_dec_s_bad, set_tagparams))) { + errmsg = "DEC_STREAM_BAD_SET_TAG"; + goto err; + } + if (!TEST_false(EVP_DecryptFinal_ex(ctx_dec_s_bad, ct, &finlen))) { + errmsg = "DEC_STREAM_BADTAG_NOT_REJECTED"; + goto err; + } + +err: + if (errmsg != NULL) { + TEST_info("test_evp_oneshot_aead_zerolen %d, %s: %s", + idx, errmsg, info->name); + testresult = 0; + } + EVP_CIPHER_CTX_free(ctx_stream); + EVP_CIPHER_CTX_free(ctx_oneshot); + EVP_CIPHER_CTX_free(ctx_dec); + EVP_CIPHER_CTX_free(ctx_dec_bad); + EVP_CIPHER_CTX_free(ctx_dec_s); + EVP_CIPHER_CTX_free(ctx_dec_s_bad); + return testresult; +} + /* * Verify stale key is not being used after providing a new key in multiple steps. * This test performs a full round of encryption and then changes the @@ -8693,6 +8911,7 @@ int setup_tests(void) ADD_ALL_TESTS(test_evp_diff_order_init, cipher_list_n); ADD_ALL_TESTS(test_evp_stale_key_reinit, cipher_list_n); ADD_ALL_TESTS(test_evp_decrypt_roundtrip_multistep, cipher_list_n); + ADD_ALL_TESTS(test_evp_oneshot_aead_zerolen, cipher_list_n); ADD_ALL_TESTS(test_evp_init_seq, OSSL_NELEM(evp_init_tests)); ADD_ALL_TESTS(test_evp_reset, OSSL_NELEM(evp_reset_tests)); From 25b37961f7341de7ccb0f01e61fcdefc61a30e36 Mon Sep 17 00:00:00 2001 From: Bernd Edlinger Date: Mon, 15 Jun 2026 20:10:07 +0200 Subject: [PATCH 030/349] Prevent integer overflow in ASN1_mbstring_ncopy This prevents a theoretically possible integer overflow in OPENSSL_malloc(outlen + 1) at the end of ASN1_mbstring_ncopy, when outlen is exactly INT_MAX. That affects conversions from MBSTRING_ASC to MBSTRING_UTF8 and MBSTRING_UTF8 to MBSTRING_ASC, because a terminating zero has to be added to the result. And also conversions MBSTRING_BMP to MBSTRING_UTF8 in cases when UTF8 characters 0x800..0xFFFF are encoded as 3-byte UTF8-characters and the resulting UTF8-string is exactly INT_MAX in size. Fixes: 97f6b621f7af ("Reject oversized inputs in ASN1_mbstring_ncopy()") Reviewed-by: Eugene Syromiatnikov Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz MergeDate: Wed Jun 24 12:49:08 2026 (Merged from https://github.com/openssl/openssl/pull/31527) --- crypto/asn1/a_mbstr.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crypto/asn1/a_mbstr.c b/crypto/asn1/a_mbstr.c index 236082ec39..9329472e9b 100644 --- a/crypto/asn1/a_mbstr.c +++ b/crypto/asn1/a_mbstr.c @@ -69,6 +69,9 @@ int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len, if (len < 0) { ERR_raise(ERR_LIB_ASN1, ERR_R_PASSED_INVALID_ARGUMENT); return -1; + } else if (len >= INT_MAX) { + ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG); + return -1; } /* First do a string check and work out the number of characters */ @@ -305,7 +308,7 @@ static int out_utf8(uint32_t value, void *arg) return len; } outlen = arg; - if (*outlen > INT_MAX - len) { + if (*outlen >= INT_MAX - len) { ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG); return -1; } From 54aef373f3265a6b3f2d757a2f24473381157dea Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Wed, 17 Jun 2026 20:35:05 +0900 Subject: [PATCH 031/349] Reject HelloRequest in TLS 1.3 TLS 1.3 reserves handshake message type 0 and must not silently ignore HelloRequest records. The legacy client-side HelloRequest skip path in tls_get_message_header() could run before the TLS 1.3 state machine had a chance to reject the message, so a zero-length HelloRequest injected after ClientHello was discarded instead of triggering unexpected_message. Restrict the skip to cases where TLS 1.3 is no longer possible. Before ServerHello selects a version, s->version is the configured maximum; after ServerHello or during renegotiation, it is the negotiated version. Skip only when that value is below TLS 1.3, preserving the existing TLS 1.2-and-below behavior. Add TLSProxy regression tests covering rejection while TLS 1.3 is possible and the preserved TLS 1.2 skip after ServerHello. Fixes #31531 Reviewed-by: Bob Beck Reviewed-by: Norbert Pocs Reviewed-by: Eugene Syromiatnikov Reviewed-by: Tomas Mraz MergeDate: Wed Jun 24 13:01:12 2026 (Merged from https://github.com/openssl/openssl/pull/31577) --- ssl/statem/statem_lib.c | 20 +++++- test/recipes/70-test_tls13messages.t | 91 +++++++++++++++++++++++++++- 2 files changed, 109 insertions(+), 2 deletions(-) diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c index bacbd58218..c9d76fe8a7 100644 --- a/ssl/statem/statem_lib.c +++ b/ssl/statem/statem_lib.c @@ -1535,6 +1535,23 @@ WORK_STATE tls_finish_handshake(SSL_CONNECTION *s, ossl_unused WORK_STATE wst, return WORK_FINISHED_STOP; } +/* + * TLS 1.3 reserves handshake message type 0, so a HelloRequest must reach the + * state machine and be rejected there whenever TLS 1.3 is still possible. + * + * By the time a client reads a server handshake message, s->version is either + * the configured maximum for an initial pre-ServerHello handshake, or the + * already negotiated version after ServerHello or during renegotiation. Skip + * only when that version is below TLS 1.3. + */ +static int should_skip_hello_request(const SSL_CONNECTION *s) +{ + if (SSL_CONNECTION_IS_TLS13(s)) + return 0; + + return s->version > 0 && s->version < TLS1_3_VERSION; +} + int tls_get_message_header(SSL_CONNECTION *s, int *mt) { /* s->init_num < SSL3_HM_HEADER_LENGTH */ @@ -1594,7 +1611,8 @@ int tls_get_message_header(SSL_CONNECTION *s, int *mt) skip_message = 0; if (!s->server) if (s->statem.hand_state != TLS_ST_OK - && p[0] == SSL3_MT_HELLO_REQUEST) + && p[0] == SSL3_MT_HELLO_REQUEST + && should_skip_hello_request(s)) /* * The server may always send 'Hello Request' messages -- * we are doing a handshake anyway now, so ignore them if diff --git a/test/recipes/70-test_tls13messages.t b/test/recipes/70-test_tls13messages.t index f3a3f4789f..b2e356763c 100644 --- a/test/recipes/70-test_tls13messages.t +++ b/test/recipes/70-test_tls13messages.t @@ -211,6 +211,9 @@ my $proxy = TLSProxy::Proxy->new( (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE}), have_IPv6() ); +my $fatal_alert = 0; +my $hello_request_added = 0; +my $hello_request_after_server_hello = 0; #Test 1: Check we get all the right messages for a default handshake (undef, my $session) = tempfile(); @@ -219,7 +222,7 @@ $proxy->cipherc("DEFAULT:\@SECLEVEL=2"); $proxy->clientflags("-no_rx_cert_comp -sess_out ".$session); $proxy->sessionfile($session); $proxy->start() or plan skip_all => "Unable to start up Proxy for tests"; -plan tests => 17; +plan tests => 19; checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE, checkhandshake::DEFAULT_EXTENSIONS, "Default handshake test"); @@ -419,4 +422,90 @@ checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE, | checkhandshake::SUPPORTED_GROUPS_SRV_EXTENSION, "Acceptable but non preferred key_share"); +#Test 18: HelloRequest is reserved in TLSv1.3 +$proxy->clear(); +$fatal_alert = 0; +$hello_request_added = 0; +$hello_request_after_server_hello = 0; +$proxy->filter(\&inject_hello_request); +$proxy->cipherc("DEFAULT:\@SECLEVEL=2"); +$proxy->clientflags("-no_rx_cert_comp"); +$proxy->start(); +ok($fatal_alert, "HelloRequest rejected in TLSv1.3"); + +#Test 19: A HelloRequest received after selecting TLSv1.2 in the initial +# handshake is still ignored, confirming the legacy skip path is +# preserved even when TLSv1.3 was initially enabled. +SKIP: { + skip "TLSv1.2 disabled", 1 if disabled("tls1_2"); + + $proxy->clear(); + $fatal_alert = 0; + $hello_request_added = 0; + $hello_request_after_server_hello = 1; + $proxy->filter(\&inject_hello_request); + $proxy->cipherc("DEFAULT:\@SECLEVEL=2"); + $proxy->clientflags("-no_rx_cert_comp"); + $proxy->serverflags("-no_tls1_3"); + $proxy->start(); + ok(TLSProxy::Message->success() && !$fatal_alert, + "HelloRequest ignored in TLSv1.2"); +} + unlink $session; + +sub inject_hello_request +{ + my $proxy = shift; + my $records = $proxy->record_list; + my $hello_request; + my $record; + my $server_hello_record; + my $i; + + if ($hello_request_added) { + $fatal_alert = 1 + if @{$records}[-1]->is_fatal_alert(0) + == TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE; + return; + } + + return if $proxy->flight != 1; + + $hello_request = pack("C4", TLSProxy::Message::MT_HELLO_REQUEST, + 0, 0, 0); + $record = TLSProxy::Record->new( + 1, + TLSProxy::Record::RT_HANDSHAKE, + TLSProxy::Record::VERS_TLS_1_2, + length($hello_request), + length($hello_request), + length($hello_request), + $hello_request, + $hello_request + ); + + if ($hello_request_after_server_hello) { + foreach my $message (@{$proxy->message_list}) { + next if $message->mt != TLSProxy::Message::MT_SERVER_HELLO + || ${$message->records}[0]->flight != 1; + + $server_hello_record = @{$message->records}[-1]; + last; + } + + return if !defined $server_hello_record; + + for ($i = 0; $i < @{$records}; $i++) { + last if ${$records}[$i] == $server_hello_record; + } + $i++; + } else { + for ($i = 0; ${$records}[$i]->flight() < 1; $i++) { + next; + } + } + + splice @{$records}, $i, 0, $record; + $hello_request_added = 1; +} From 1a65972ad0b5e3a02e33de3cd7bda61764067710 Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Fri, 29 May 2026 05:39:40 -0600 Subject: [PATCH 032/349] Add documentation for NAME_CONSTRAINTS_check We document which names and name constraints will be evaluated as well as the limits that will be placed on the evauluation on a per certificate basis. We call out in the BUGS section that the RFC 5280 requires a byte per byte match of name constraints unless the higher level protocol has defines a different matching method for wildcards. This "deferall of specification" and corresponding lack of specification by upper level protocols means that across implementations encountering the default behaviour is to be expected, and that therefore relying on excluded names to constrain signers in a PKI from signing wildcards is ill advised. This is then cross referenced in the documentation for X509_verify_cert and the maximum possible comparisons which can be forces in a certificate validtion noted in the BUGS section of X509_verify_cert. Fixes: https://github.com/openssl/openssl/issues/30706 Reviewed-by: Neil Horman Reviewed-by: Nikola Pajkovsky MergeDate: Wed Jun 24 13:03:54 2026 (Merged from https://github.com/openssl/openssl/pull/31334) --- doc/build.info | 6 + doc/man3/NAME_CONSTRAINTS_check.pod | 211 ++++++++++++++++++++++++++++ doc/man3/X509_verify_cert.pod | 13 +- util/missingcrypto.txt | 2 - 4 files changed, 229 insertions(+), 3 deletions(-) create mode 100644 doc/man3/NAME_CONSTRAINTS_check.pod diff --git a/doc/build.info b/doc/build.info index 6e8dfedb5c..44b06941e4 100644 --- a/doc/build.info +++ b/doc/build.info @@ -1499,6 +1499,10 @@ DEPEND[html/man3/MDC2_Init.html]=man3/MDC2_Init.pod GENERATE[html/man3/MDC2_Init.html]=man3/MDC2_Init.pod DEPEND[man/man3/MDC2_Init.3]=man3/MDC2_Init.pod GENERATE[man/man3/MDC2_Init.3]=man3/MDC2_Init.pod +DEPEND[html/man3/NAME_CONSTRAINTS_check.html]=man3/NAME_CONSTRAINTS_check.pod +GENERATE[html/man3/NAME_CONSTRAINTS_check.html]=man3/NAME_CONSTRAINTS_check.pod +DEPEND[man/man3/NAME_CONSTRAINTS_check.3]=man3/NAME_CONSTRAINTS_check.pod +GENERATE[man/man3/NAME_CONSTRAINTS_check.3]=man3/NAME_CONSTRAINTS_check.pod DEPEND[html/man3/NCONF_new_ex.html]=man3/NCONF_new_ex.pod GENERATE[html/man3/NCONF_new_ex.html]=man3/NCONF_new_ex.pod DEPEND[man/man3/NCONF_new_ex.3]=man3/NCONF_new_ex.pod @@ -3426,6 +3430,7 @@ html/man3/GENERAL_NAME.html \ html/man3/HMAC.html \ html/man3/MD5.html \ html/man3/MDC2_Init.html \ +html/man3/NAME_CONSTRAINTS_check.html \ html/man3/NCONF_new_ex.html \ html/man3/OBJ_nid2obj.html \ html/man3/OCSP_REQUEST_new.html \ @@ -4101,6 +4106,7 @@ man/man3/GENERAL_NAME.3 \ man/man3/HMAC.3 \ man/man3/MD5.3 \ man/man3/MDC2_Init.3 \ +man/man3/NAME_CONSTRAINTS_check.3 \ man/man3/NCONF_new_ex.3 \ man/man3/OBJ_nid2obj.3 \ man/man3/OCSP_REQUEST_new.3 \ diff --git a/doc/man3/NAME_CONSTRAINTS_check.pod b/doc/man3/NAME_CONSTRAINTS_check.pod new file mode 100644 index 0000000000..3dfe39ea29 --- /dev/null +++ b/doc/man3/NAME_CONSTRAINTS_check.pod @@ -0,0 +1,211 @@ +=pod + +=head1 NAME + +NAME_CONSTRAINTS_check, +NAME_CONSTRAINTS_check_CN - check a certificate's names against a name +constraints extension + +=head1 SYNOPSIS + + #include + + int NAME_CONSTRAINTS_check(const X509 *x, NAME_CONSTRAINTS *nc); + int NAME_CONSTRAINTS_check_CN(const X509 *x, NAME_CONSTRAINTS *nc); + +=head1 DESCRIPTION + +NAME_CONSTRAINTS_check() tests whether the names asserted by certificate +I satisfy the name constraints I. It implements the matching +primitive of RFC 5280 section 4.2.1.10: given a constraint set (a +B structure containing zero or more B +and B) and a candidate certificate, decide whether the +certificate's names fall within the permitted subtrees and outside the +excluded subtrees. + +The names considered by NAME_CONSTRAINTS_check() are: + +=over 4 + +=item * + +The certificate's subject distinguished name, matched as a B +general-name type. The subject is considered only when it is nonempty. + +=item * + +Each B attribute appearing within the subject distinguished +name, matched as an B general-name type. These attributes are +the historical, pre-SAN way of expressing an email address in a +certificate's subject, and RFC 5280 requires that they be subjected to +name-constraint checking. + +=item * + +Each entry in the certificate's subject alternative name extension, matched +according to its declared general-name type. + +=back + +NAME_CONSTRAINTS_check() implements matching for the following +general-name types: B, B, B, +B, and B. The B form +B (RFC 8398) is additionally matched against +B subtrees. Any other general-name type, including +B, B, B, and other B +forms, yields B. + +For each name considered, the function evaluates two conditions: + +=over 4 + +=item * + +If I contains at least one B of the same general-name +type as the name, the name must match at least one of those permitted +subtrees. If I contains no permitted subtrees of that type, no +permitted-subtrees test is imposed on names of that type. + +=item * + +The name must not match any B of the same general-name +type in I. + +=back + +The function returns at the first violation encountered; it does not +collect or report multiple failures. + +For B entries, matching follows the byte/label algorithm of +RFC 5280 section 4.2.1.10, which RFC 5280 mandates when no +protocol-specific matching rules apply. Because this match is performed +without awareness of any specific higher-level protocol, additional +matching rules defined by later or more specific protocols must be +applied independently of this function to the certificate chain. + +NAME_CONSTRAINTS_check() performs only the constraint match for a single +certificate against a single constraint set. It does B perform the +chain-wide enforcement of RFC 5280 section 6.1.4(g)-(j): callers wishing to +enforce name constraints across an entire certification path must walk the +chain themselves and apply each ancestor's constraint set to certificates +lower in the chain, observing the usual exceptions (for example, +self-issued intermediate certificates are exempt from constraints imposed +by certificates above them, except when they are the leaf of the chain). +For full RFC 5280 name-constraint enforcement integrated with chain +validation, applications should use L, which performs +this internally. + +NAME_CONSTRAINTS_check() enforces an implementation limit on the product +of the certificate's name count and the constraint set's subtree count, to +prevent computationally expensive matching on pathological input. If that +limit is exceeded the function returns B without +performing any matching. The current limit is 2**20 (1,048,576) on the +product of the name count (subject DN entries plus B +entries) and the subtree count (B plus +B). + +=head1 RETURN VALUES + +NAME_CONSTRAINTS_check() returns B if every name considered +satisfies the constraints. Otherwise it returns one of the following +B codes: + +=over 4 + +=item B + +A name of a type for which I contains at least one permitted subtree +failed to match any of those subtrees. + +=item B + +A name matched an excluded subtree. + +=item B + +A subtree in I specified a B other than 0 or a B at +all. RFC 5280 requires that these B fields not be used, +and a constraint set that uses them cannot be processed. + +=item B + +A general-name type for which matching is not implemented was encountered. +The list of supported types is given in the DESCRIPTION above. + +=item B + +A name in the certificate is encoded in a way that cannot be matched (for +example, an B attribute in the subject that is not encoded +as an B). + +=item B + +The product of the certificate's name count and the constraint set's +subtree count exceeded the implementation limit; no matching was +performed. + +=back + +Other B codes may be returned by deeper name-matching +helpers (for example, codes arising from individual general-name type +comparisons). Callers should treat the return value as the authoritative +success/failure signal and treat any value other than B as a +failure, rather than enumerating the specific codes above. + +=head1 NOTES + +NAME_CONSTRAINTS_check() does not match the certificate's commonName +against B name constraints; that check is provided by a separate +function, B(). The commonName-as-DNS-identity +practice is a legacy concern: modern certificates assert DNS identities +through B entries in the subject alternative name extension, +which NAME_CONSTRAINTS_check() already covers. NAME_CONSTRAINTS_check_CN() +is required only for older certificates that express a DNS identity +through their commonName instead of, or in addition to, the SAN; for +certificates conforming to modern profiles a call to NAME_CONSTRAINTS_check() +alone is generally sufficient. + +=head1 BUGS + +RFC 9525's wildcard semantics apply only to presented-identifier +matching for TLS service identity, and explicitly call out they are not +valid for any other purpose; they do not define wildcard handling +for name-constraint matching. NAME_CONSTRAINTS_check() therefore +follows RFC 5280's requirements for when this is undefined, and treats +the B<*> character in a B as a literal label component, per +the RFC 5280 algorithm, which is often contrary to caller expectation. + +Even if specified in the future, due to the "fallback implementation" +nature of matching wildcards in SAN B entries specified by +RFC 5280, name constraint behaviour in the presence of wildcards +should not be strictly relied upon across implementations and +protocols. This matters most for the use of B +constraints, which should not be relied upon to reliably constrain +signing certificates for a PKI in a security dependent manner unless +the consumers of these certificates are themselves known to be +constrained by other means to only use implementations that provide +different semantics, or the PKI can be constrained by other means to +ensure that wildcards are never issued from such signing +certificates. + +=head1 SEE ALSO + +L, +L + +=head1 HISTORY + +NAME_CONSTRAINTS_check() was added in OpenSSL 1.0.0. + +NAME_CONSTRAINTS_check_CN() was added in OpenSSL 1.1.0. + +=head1 COPYRIGHT + +Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the Apache License 2.0 (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +L. + +=cut diff --git a/doc/man3/X509_verify_cert.pod b/doc/man3/X509_verify_cert.pod index 5beb41f560..aaaaace4d6 100644 --- a/doc/man3/X509_verify_cert.pod +++ b/doc/man3/X509_verify_cert.pod @@ -104,7 +104,9 @@ terminator. =item 7. -B per RFC 5280 section 4.2.1.10. +B per RFC 5280 section 4.2.1.10; see +L for details of the matching primitive and +the general-name types it covers. =item 8. @@ -450,8 +452,17 @@ above it. Callers should nevertheless be aware that the chain returned in this mode does not necessarily terminate at an RFC 5280-style trust anchor. +L caps each per-pair check at 2**20 +comparisons, but the chain orchestrator issues B(B-1)/2 such +checks for an B-certificate chain. An adversary submitting a +maximally constructed chain can therefore force up to approximately +6.3 million name-constraint comparisons in a four-certificate chain +(one leaf and three name-constrained signers), or approximately 5.2 +billion at the default chain-depth limit of 100. + =head1 SEE ALSO +L, L, L, L, diff --git a/util/missingcrypto.txt b/util/missingcrypto.txt index 57a255a4a5..5e8126d12c 100644 --- a/util/missingcrypto.txt +++ b/util/missingcrypto.txt @@ -570,8 +570,6 @@ LONG_it(3) MD2_options(3) MD4_Transform(3) MD5_Transform(3) -NAME_CONSTRAINTS_check(3) -NAME_CONSTRAINTS_check_CN(3) NAME_CONSTRAINTS_it(3) NAMING_AUTHORITY_it(3) NCONF_WIN32(3) From c80ce1084120e8286214132b059b95f233921aef Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Tue, 23 Jun 2026 09:57:32 +0200 Subject: [PATCH 033/349] Always ignore the contents of the legacy record version As per RFC8446 this value must be ignored. Reviewed-by: Matt Caswell Reviewed-by: Daniel Kubec MergeDate: Wed Jun 24 13:06:55 2026 (Merged from https://github.com/openssl/openssl/pull/31662) --- ssl/record/rec_layer_s3.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/ssl/record/rec_layer_s3.c b/ssl/record/rec_layer_s3.c index 548104e73b..75278f39ee 100644 --- a/ssl/record/rec_layer_s3.c +++ b/ssl/record/rec_layer_s3.c @@ -834,14 +834,6 @@ start: * were actually expecting a CCS). */ - /* - * Lets just double check that we've got a supported record version - */ - if (rr->version < TLS1_VERSION || rr->version > TLS1_3_VERSION) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - return -1; - } - if (ssl->method->version == TLS_ANY_VERSION && (s->server || rr->type != SSL3_RT_ALERT)) { /* @@ -850,7 +842,14 @@ start: * with. We shouldn't be receiving anything other than a ClientHello * if we are a server. */ - s->version = rr->version; + int min_version, max_version; + + if (ssl_get_min_max_version(s, &min_version, &max_version, NULL) != 0) { + SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE, ERR_R_INTERNAL_ERROR); + return -1; + } + + s->version = min_version; SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_UNEXPECTED_MESSAGE); return -1; } From 029d629db23e95c6e827d09dae89ee8b4b54f3e2 Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Tue, 23 Jun 2026 10:06:04 +0200 Subject: [PATCH 034/349] include/internal/hashtable.h: avoid OOB read in ossl_ht_strcase() Avoid accessing src[len] by swapping the check order and bound check the iterator variable before the access. Found by cppcheck. Fixes: cc4ea5e00028 "Introduce new internal hashtable implementation" Signed-off-by: Eugene Syromiatnikov Reviewed-by: Nikola Pajkovsky Reviewed-by: Neil Horman Reviewed-by: Paul Dale MergeDate: Wed Jun 24 13:09:25 2026 (Merged from https://github.com/openssl/openssl/pull/31663) --- include/internal/hashtable.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/internal/hashtable.h b/include/internal/hashtable.h index 7c4150ba28..9aad653097 100644 --- a/include/internal/hashtable.h +++ b/include/internal/hashtable.h @@ -357,7 +357,7 @@ static ossl_inline ossl_unused void ossl_ht_strcase(HT_KEY *key, char *tgt, cons if (key != NULL && key->keysize + len > key->bufsize) len = (size_t)(key->bufsize - key->keysize); - for (i = 0; src[i] != '\0' && i < len; i++) + for (i = 0; i < len && src[i] != '\0'; i++) tgt[i] = case_adjust & src[i]; } From 3e4955cce945d60c04c279d087fc45a2822f3a18 Mon Sep 17 00:00:00 2001 From: Kirill Ermoshin Date: Mon, 22 Jun 2026 12:51:12 +0300 Subject: [PATCH 035/349] Fix typo of 512 gost sigalg name from bcff020 Reviewed-by: Eugene Syromiatnikov Reviewed-by: Paul Dale Reviewed-by: Bob Beck Reviewed-by: Tomas Mraz MergeDate: Wed Jun 24 15:07:02 2026 (Merged from https://github.com/openssl/openssl/pull/31562) --- ssl/t1_lib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index 2a15378570..85bed6401d 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -2280,8 +2280,8 @@ static const SIGALG_LOOKUP sigalg_lookup_tbl[] = { NID_id_GostR3410_2012_256, SSL_PKEY_GOST12_256, NID_undef, NID_undef, 1, 0, TLS1_2_VERSION, TLS1_2_VERSION, DTLS1_2_VERSION, DTLS1_2_VERSION }, - { TLSEXT_SIGALG_gostr34102012_256_intrinsic_alias, /* RFC9189 */ - TLSEXT_SIGALG_gostr34102012_256_intrinsic_name, + { TLSEXT_SIGALG_gostr34102012_512_intrinsic_alias, /* RFC9189 */ + TLSEXT_SIGALG_gostr34102012_512_intrinsic_name, TLSEXT_SIGALG_gostr34102012_512_intrinsic, NID_id_GostR3411_2012_512, SSL_MD_GOST12_512_IDX, NID_id_GostR3410_2012_512, SSL_PKEY_GOST12_512, From 8d7bec32e6c62689f095f97be67b17c0e93f768f Mon Sep 17 00:00:00 2001 From: Kirill Ermoshin Date: Mon, 22 Jun 2026 12:52:39 +0300 Subject: [PATCH 036/349] Remove aliases for IANA-GOST2012-GOST8912-GOST8912 "gost2012_256/512" sigalgs aliases of IANA-GOST2012-GOST8912-GOST8912 equals to sigalgs of LEAGACY-GOST2012-GOST8912-GOST8912 so we can't distinguish between them for the legacy algorithm Reviewed-by: Eugene Syromiatnikov Reviewed-by: Paul Dale Reviewed-by: Bob Beck Reviewed-by: Tomas Mraz MergeDate: Wed Jun 24 15:07:04 2026 (Merged from https://github.com/openssl/openssl/pull/31562) --- ssl/t1_lib.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index 85bed6401d..b6e487a1ab 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -2273,16 +2273,14 @@ static const SIGALG_LOOKUP sigalg_lookup_tbl[] = { TLS1_2_VERSION, TLS1_2_VERSION, DTLS1_2_VERSION, DTLS1_2_VERSION }, #ifndef OPENSSL_NO_GOST - { TLSEXT_SIGALG_gostr34102012_256_intrinsic_alias, /* RFC9189 */ - TLSEXT_SIGALG_gostr34102012_256_intrinsic_name, - TLSEXT_SIGALG_gostr34102012_256_intrinsic, + { TLSEXT_SIGALG_gostr34102012_256_intrinsic_name, /* RFC9189 */ + NULL, TLSEXT_SIGALG_gostr34102012_256_intrinsic, NID_id_GostR3411_2012_256, SSL_MD_GOST12_256_IDX, NID_id_GostR3410_2012_256, SSL_PKEY_GOST12_256, NID_undef, NID_undef, 1, 0, TLS1_2_VERSION, TLS1_2_VERSION, DTLS1_2_VERSION, DTLS1_2_VERSION }, - { TLSEXT_SIGALG_gostr34102012_512_intrinsic_alias, /* RFC9189 */ - TLSEXT_SIGALG_gostr34102012_512_intrinsic_name, - TLSEXT_SIGALG_gostr34102012_512_intrinsic, + { TLSEXT_SIGALG_gostr34102012_512_intrinsic_name, /* RFC9189 */ + NULL, TLSEXT_SIGALG_gostr34102012_512_intrinsic, NID_id_GostR3411_2012_512, SSL_MD_GOST12_512_IDX, NID_id_GostR3410_2012_512, SSL_PKEY_GOST12_512, NID_undef, NID_undef, 1, 0, From 2fabd5d2741a577e2c26c74baf4f50405ece6814 Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Sat, 14 Mar 2026 20:47:06 +1100 Subject: [PATCH 037/349] Once initialised, ML-DSA keys should be immutable ML-DSA keys should become immutable once key material has been added. This is already the case for at least ML-KEM keys, and should generally be the case across all key types. - Added the requisite check in the key management provider ml_dsa_import() function. - Also, consolidated the ML-KEM checks in ml_kem_import(). These were previously partly in ml_kem_key_fromdata(). Reviewed-by: Shane Lontis Reviewed-by: Paul Dale MergeDate: Thu Jun 25 02:03:35 2026 (Merged from https://github.com/openssl/openssl/pull/30421) --- crypto/err/openssl.txt | 1 + include/openssl/proverr.h | 1 + providers/common/provider_err.c | 2 + .../implementations/keymgmt/ml_dsa_kmgmt.c | 10 ++ .../implementations/keymgmt/ml_kem_kmgmt.c | 21 +-- test/ml_dsa_test.c | 112 +++++++++++++ test/ml_kem_evp_extra_test.c | 150 ++++++++++++++++++ 7 files changed, 287 insertions(+), 10 deletions(-) diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index d993f02d69..14658d7634 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -1106,6 +1106,7 @@ PROV_R_INVALID_THREAD_POOL_SIZE:234:invalid thread pool size PROV_R_INVALID_UKM_LENGTH:200:invalid ukm length PROV_R_INVALID_X931_DIGEST:170:invalid x931 digest PROV_R_IN_ERROR_STATE:192:in error state +PROV_R_KEY_IMMUTABLE_ONCE_SET:266:key immutable once set PROV_R_KEY_SETUP_FAILED:101:key setup failed PROV_R_KEY_SIZE_TOO_SMALL:171:key size too small PROV_R_LENGTH_TOO_LARGE:202:length too large diff --git a/include/openssl/proverr.h b/include/openssl/proverr.h index 79bbc6628e..0db3a82b47 100644 --- a/include/openssl/proverr.h +++ b/include/openssl/proverr.h @@ -97,6 +97,7 @@ #define PROV_R_INVALID_UKM_LENGTH 200 #define PROV_R_INVALID_X931_DIGEST 170 #define PROV_R_IN_ERROR_STATE 192 +#define PROV_R_KEY_IMMUTABLE_ONCE_SET 266 #define PROV_R_KEY_SETUP_FAILED 101 #define PROV_R_KEY_SIZE_TOO_SMALL 171 #define PROV_R_LENGTH_TOO_LARGE 202 diff --git a/providers/common/provider_err.c b/providers/common/provider_err.c index 7e3e189dd2..33e7edb454 100644 --- a/providers/common/provider_err.c +++ b/providers/common/provider_err.c @@ -144,6 +144,8 @@ static const ERR_STRING_DATA PROV_str_reasons[] = { { ERR_PACK(ERR_LIB_PROV, 0, PROV_R_INVALID_X931_DIGEST), "invalid x931 digest" }, { ERR_PACK(ERR_LIB_PROV, 0, PROV_R_IN_ERROR_STATE), "in error state" }, + { ERR_PACK(ERR_LIB_PROV, 0, PROV_R_KEY_IMMUTABLE_ONCE_SET), + "key immutable once set" }, { ERR_PACK(ERR_LIB_PROV, 0, PROV_R_KEY_SETUP_FAILED), "key setup failed" }, { ERR_PACK(ERR_LIB_PROV, 0, PROV_R_KEY_SIZE_TOO_SMALL), "key size too small" }, diff --git a/providers/implementations/keymgmt/ml_dsa_kmgmt.c b/providers/implementations/keymgmt/ml_dsa_kmgmt.c index 83217066aa..24406ac602 100644 --- a/providers/implementations/keymgmt/ml_dsa_kmgmt.c +++ b/providers/implementations/keymgmt/ml_dsa_kmgmt.c @@ -293,8 +293,18 @@ static int ml_dsa_import(void *keydata, int selection, const OSSL_PARAM params[] int include_priv; int res; + /* + * Once a key is fully initialised (has at least a public component), + * further mutation is no longer safe and disallowed. + */ if (!ossl_prov_is_running() || key == NULL) return 0; + if (ossl_ml_dsa_key_has(key, OSSL_KEYMGMT_SELECT_PUBLIC_KEY)) { + /* Invalid attempt to mutate a key. */ + ERR_raise_data(ERR_LIB_PROV, PROV_R_KEY_IMMUTABLE_ONCE_SET, + "Keys are immutable once key material has been loaded or generated"); + return 0; + } if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) return 0; diff --git a/providers/implementations/keymgmt/ml_kem_kmgmt.c b/providers/implementations/keymgmt/ml_kem_kmgmt.c index f82f3e6f68..d7f2d87685 100644 --- a/providers/implementations/keymgmt/ml_kem_kmgmt.c +++ b/providers/implementations/keymgmt/ml_kem_kmgmt.c @@ -401,10 +401,7 @@ static int ml_kem_key_fromdata(ML_KEM_KEY *key, const OSSL_PARAM params[], const ML_KEM_VINFO *v; struct ml_kem_import_params_st p; - /* Invalid attempt to mutate a key, what is the right error to report? */ - if (key == NULL - || ossl_ml_kem_have_pubkey(key) - || !ml_kem_import_params_decoder(params, &p)) + if (!ml_kem_import_params_decoder(params, &p)) return 0; v = ossl_ml_kem_key_vinfo(key); @@ -489,11 +486,16 @@ static int ml_kem_import(void *vkey, int selection, const OSSL_PARAM params[]) int include_private; int res; - if (!ossl_prov_is_running() || key == NULL) + if (!ossl_prov_is_running() + || (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0 + || key == NULL) return 0; - - if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + if (ossl_ml_kem_have_pubkey(key)) { + /* Invalid attempt to mutate a key. */ + ERR_raise_data(ERR_LIB_PROV, PROV_R_KEY_IMMUTABLE_ONCE_SET, + "Keys are immutable once key material has been loaded or generated"); return 0; + } include_private = selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0; res = ml_kem_key_fromdata(key, params, include_private); @@ -686,9 +688,8 @@ static int ml_kem_set_params(void *vkey, const OSSL_PARAM params[]) /* Key mutation is reportedly generally not allowed */ if (ossl_ml_kem_have_pubkey(key)) { - ERR_raise_data(ERR_LIB_PROV, - PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE, - "ML-KEM keys cannot be mutated"); + ERR_raise_data(ERR_LIB_PROV, PROV_R_KEY_IMMUTABLE_ONCE_SET, + "Keys are immutable once key material has been loaded or generated"); return 0; } diff --git a/test/ml_dsa_test.c b/test/ml_dsa_test.c index 1553f66c8f..ca1c1225e2 100644 --- a/test/ml_dsa_test.c +++ b/test/ml_dsa_test.c @@ -8,10 +8,13 @@ */ #include +#include #include +#include #include "internal/nelem.h" #include "testutil.h" #include "ml_dsa.inc" +#include "crypto/evp.h" #include "crypto/ml_dsa.h" typedef enum OPTION_choice { @@ -644,6 +647,108 @@ err: return ret; } +/* + * Test that the keymgmt import dispatch refuses to import into a key whose + * public component is already set, i.e. the key is immutable once initialised. + * + * Four sub-cases are exercised: + * 1. public-key-only → re-import public key → must fail + * 2. public-key-only → import keypair → must fail + * 3. full keypair → re-import keypair → must fail + * 4. full keypair → import public key only → must fail + * + * All failures must raise PROV_R_KEY_IMMUTABLE_ONCE_SET. + */ +static int ml_dsa_key_immutable_test(void) +{ + int ret = 0; + EVP_KEYMGMT *keymgmt = NULL; + void *keydata = NULL; + const ML_DSA_KEYGEN_TEST_DATA *tst = &ml_dsa_keygen_testdata[0]; + OSSL_PARAM pub_params[2], keypair_params[3]; + + pub_params[0] = OSSL_PARAM_construct_octet_string( + OSSL_PKEY_PARAM_PUB_KEY, (void *)tst->pub, tst->pub_len); + pub_params[1] = OSSL_PARAM_construct_end(); + + keypair_params[0] = OSSL_PARAM_construct_octet_string( + OSSL_PKEY_PARAM_PRIV_KEY, (void *)tst->priv, tst->priv_len); + keypair_params[1] = OSSL_PARAM_construct_octet_string( + OSSL_PKEY_PARAM_PUB_KEY, (void *)tst->pub, tst->pub_len); + keypair_params[2] = OSSL_PARAM_construct_end(); + + if (!TEST_ptr(keymgmt = EVP_KEYMGMT_fetch(lib_ctx, tst->name, NULL))) + goto end; + + /* Sub-case 1 & 2: start from a public-key-only import */ + if (!TEST_ptr(keydata = evp_keymgmt_newdata(keymgmt, NULL))) + goto end; + + if (!TEST_true(evp_keymgmt_import(keymgmt, keydata, + OSSL_KEYMGMT_SELECT_PUBLIC_KEY, + pub_params))) + goto end; + + /* Re-import of the same public key must fail */ + if (!TEST_false(evp_keymgmt_import(keymgmt, keydata, + OSSL_KEYMGMT_SELECT_PUBLIC_KEY, + pub_params))) + goto end; + if (!TEST_int_eq(ERR_GET_REASON(ERR_peek_last_error()), + PROV_R_KEY_IMMUTABLE_ONCE_SET)) + goto end; + ERR_clear_error(); + + /* Import of a full keypair into a public-key-only key must also fail */ + if (!TEST_false(evp_keymgmt_import(keymgmt, keydata, + OSSL_KEYMGMT_SELECT_KEYPAIR, + keypair_params))) + goto end; + if (!TEST_int_eq(ERR_GET_REASON(ERR_peek_last_error()), + PROV_R_KEY_IMMUTABLE_ONCE_SET)) + goto end; + ERR_clear_error(); + + evp_keymgmt_freedata(keymgmt, keydata); + keydata = NULL; + + /* Sub-case 3 & 4: start from a full keypair import */ + if (!TEST_ptr(keydata = evp_keymgmt_newdata(keymgmt, NULL))) + goto end; + + if (!TEST_true(evp_keymgmt_import(keymgmt, keydata, + OSSL_KEYMGMT_SELECT_KEYPAIR, + keypair_params))) + goto end; + + /* Re-import of the same keypair must fail */ + if (!TEST_false(evp_keymgmt_import(keymgmt, keydata, + OSSL_KEYMGMT_SELECT_KEYPAIR, + keypair_params))) + goto end; + if (!TEST_int_eq(ERR_GET_REASON(ERR_peek_last_error()), + PROV_R_KEY_IMMUTABLE_ONCE_SET)) + goto end; + ERR_clear_error(); + + /* Import of a public-key-only into a full keypair must also fail */ + if (!TEST_false(evp_keymgmt_import(keymgmt, keydata, + OSSL_KEYMGMT_SELECT_PUBLIC_KEY, + pub_params))) + goto end; + if (!TEST_int_eq(ERR_GET_REASON(ERR_peek_last_error()), + PROV_R_KEY_IMMUTABLE_ONCE_SET)) + goto end; + ERR_clear_error(); + + ret = 1; +end: + if (keymgmt != NULL) + evp_keymgmt_freedata(keymgmt, keydata); + EVP_KEYMGMT_free(keymgmt); + return ret; +} + const OPTIONS *test_get_options(void) { static const OPTIONS options[] = { @@ -692,6 +797,13 @@ int setup_tests(void) ADD_TEST(from_data_bad_input_test); ADD_TEST(ml_dsa_digest_sign_verify_test); ADD_TEST(ml_dsa_priv_pub_bad_t0_test); + + /* + * Tested only in the default configuration, with a non-default provider + * configuration this test is expected to fail for some older providers. + */ + if (config_file == NULL) + ADD_TEST(ml_dsa_key_immutable_test); return 1; } diff --git a/test/ml_kem_evp_extra_test.c b/test/ml_kem_evp_extra_test.c index 4d8e506c5f..2aa704427a 100644 --- a/test/ml_kem_evp_extra_test.c +++ b/test/ml_kem_evp_extra_test.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include "crypto/evp.h" @@ -492,6 +493,154 @@ err: } #endif /* OPENSSL_NO_EC */ +/* + * Test that ML-KEM keys are immutable once key material is set. + * + * Part 1 — keymgmt import dispatch (ml_kem_import): + * Sub-case A: public-key-only first import succeeds; re-import of the + * same public key and any keypair import must fail with + * PROV_R_KEY_IMMUTABLE_ONCE_SET. + * Sub-case B: full keypair first import succeeds; re-import of the + * keypair and public-key-only import must fail with + * PROV_R_KEY_IMMUTABLE_ONCE_SET. + * + * Part 2 — EVP_PKEY_set1_encoded_public_key (ml_kem_set_params): + * The second call on a key that already has a public component must + * also fail with PROV_R_KEY_IMMUTABLE_ONCE_SET.. + */ +static int test_ml_kem_key_immutable(void) +{ + int ret = 0; + EVP_PKEY *akey = NULL, *bkey = NULL; + EVP_KEYMGMT *keymgmt = NULL; + void *keydata = NULL; + uint8_t *rawpub = NULL, *rawprv = NULL; + size_t publen = 0, prvlen = 0; + OSSL_PARAM pub_params[2], keypair_params[3]; + + /* Generate a key pair and extract the raw public and private key bytes. */ + if (!TEST_ptr(akey = EVP_PKEY_Q_keygen(testctx, NULL, "ML-KEM-768"))) + goto end; + if (!TEST_int_eq(EVP_PKEY_get_raw_public_key(akey, NULL, &publen), 1) + || !TEST_ptr(rawpub = OPENSSL_malloc(publen)) + || !TEST_int_eq(EVP_PKEY_get_raw_public_key(akey, rawpub, &publen), 1)) + goto end; + if (!TEST_int_eq(EVP_PKEY_get_raw_private_key(akey, NULL, &prvlen), 1) + || !TEST_ptr(rawprv = OPENSSL_malloc(prvlen)) + || !TEST_int_eq(EVP_PKEY_get_raw_private_key(akey, rawprv, &prvlen), 1)) + goto end; + + pub_params[0] = OSSL_PARAM_construct_octet_string( + OSSL_PKEY_PARAM_PUB_KEY, rawpub, publen); + pub_params[1] = OSSL_PARAM_construct_end(); + + keypair_params[0] = OSSL_PARAM_construct_octet_string( + OSSL_PKEY_PARAM_PRIV_KEY, rawprv, prvlen); + keypair_params[1] = OSSL_PARAM_construct_octet_string( + OSSL_PKEY_PARAM_PUB_KEY, rawpub, publen); + keypair_params[2] = OSSL_PARAM_construct_end(); + + if (!TEST_ptr(keymgmt = EVP_KEYMGMT_fetch(testctx, "ML-KEM-768", NULL))) + goto end; + + /* --- Part 1A: public-key-only import then re-import --- */ + if (!TEST_ptr(keydata = evp_keymgmt_newdata(keymgmt, NULL))) + goto end; + + if (!TEST_true(evp_keymgmt_import(keymgmt, keydata, + OSSL_KEYMGMT_SELECT_PUBLIC_KEY, + pub_params))) + goto end; + + /* Re-import of the same public key must fail */ + if (!TEST_false(evp_keymgmt_import(keymgmt, keydata, + OSSL_KEYMGMT_SELECT_PUBLIC_KEY, + pub_params))) + goto end; + if (!TEST_int_eq(ERR_GET_REASON(ERR_peek_last_error()), + PROV_R_KEY_IMMUTABLE_ONCE_SET)) + goto end; + ERR_clear_error(); + + /* Import of a full keypair into a public-key-only key must also fail */ + if (!TEST_false(evp_keymgmt_import(keymgmt, keydata, + OSSL_KEYMGMT_SELECT_KEYPAIR, + keypair_params))) + goto end; + if (!TEST_int_eq(ERR_GET_REASON(ERR_peek_last_error()), + PROV_R_KEY_IMMUTABLE_ONCE_SET)) + goto end; + ERR_clear_error(); + + evp_keymgmt_freedata(keymgmt, keydata); + keydata = NULL; + + /* --- Part 1B: full keypair import then re-import --- */ + if (!TEST_ptr(keydata = evp_keymgmt_newdata(keymgmt, NULL))) + goto end; + + if (!TEST_true(evp_keymgmt_import(keymgmt, keydata, + OSSL_KEYMGMT_SELECT_KEYPAIR, + keypair_params))) + goto end; + + /* Re-import of the same keypair must fail */ + if (!TEST_false(evp_keymgmt_import(keymgmt, keydata, + OSSL_KEYMGMT_SELECT_KEYPAIR, + keypair_params))) + goto end; + if (!TEST_int_eq(ERR_GET_REASON(ERR_peek_last_error()), + PROV_R_KEY_IMMUTABLE_ONCE_SET)) + goto end; + ERR_clear_error(); + + /* Import of a public-key-only into a full keypair must also fail */ + if (!TEST_false(evp_keymgmt_import(keymgmt, keydata, + OSSL_KEYMGMT_SELECT_PUBLIC_KEY, + pub_params))) + goto end; + if (!TEST_int_eq(ERR_GET_REASON(ERR_peek_last_error()), + PROV_R_KEY_IMMUTABLE_ONCE_SET)) + goto end; + ERR_clear_error(); + + evp_keymgmt_freedata(keymgmt, keydata); + keydata = NULL; + + /* --- Part 2: EVP_PKEY_set1_encoded_public_key immutability --- */ + + /* + * Create an empty typed key (algorithm set, no key material) by + * copying parameters from the generated key. + */ + if (!TEST_ptr(bkey = EVP_PKEY_new()) + || !TEST_int_gt(EVP_PKEY_copy_parameters(bkey, akey), 0)) + goto end; + + /* First call must succeed: the key is still embryonic */ + if (!TEST_int_eq(EVP_PKEY_set1_encoded_public_key(bkey, rawpub, publen), 1)) + goto end; + + /* Second call must fail: the key now has a public component */ + if (!TEST_int_eq(EVP_PKEY_set1_encoded_public_key(bkey, rawpub, publen), 0)) + goto end; + if (!TEST_int_eq(ERR_GET_REASON(ERR_peek_last_error()), + PROV_R_KEY_IMMUTABLE_ONCE_SET)) + goto end; + ERR_clear_error(); + + ret = 1; +end: + if (keymgmt != NULL) + evp_keymgmt_freedata(keymgmt, keydata); + EVP_KEYMGMT_free(keymgmt); + EVP_PKEY_free(akey); + EVP_PKEY_free(bkey); + OPENSSL_free(rawpub); + OPENSSL_free(rawprv); + return ret; +} + int setup_tests(void) { int test_rand = 0; @@ -520,6 +669,7 @@ int setup_tests(void) ADD_TEST(test_ml_kem); ADD_TEST(test_ml_kem_from_data_propq); + ADD_TEST(test_ml_kem_key_immutable); #ifndef OPENSSL_NO_EC ADD_ALL_TESTS(test_mlx_kem_dup_partial_selection, OSSL_NELEM(mlx_kem_algs)); #endif From 26516294b5a0443598febce7e982bd41578da849 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 22 Jun 2026 23:30:49 +0200 Subject: [PATCH 038/349] apps: cover the ec -conv_form option in the test recipe The -conv_form option was not covered. Add a subtest that checks a valid form changes the public key encoding and that an invalid form is rejected. The DER encodings are also compared against committed reference files, as they are deterministic for testec-p256.pem. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Paul Dale Reviewed-by: Eugene Syromiatnikov Reviewed-by: Tomas Mraz MergeDate: Thu Jun 25 06:08:38 2026 (Merged from https://github.com/openssl/openssl/pull/31652) --- test/recipes/15-test_ec.t | 30 ++++++++++++++++-- test/recipes/15-test_ec_data/ec-conv-comp.der | Bin 0 -> 59 bytes test/recipes/15-test_ec_data/ec-conv-unc.der | Bin 0 -> 91 bytes 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 test/recipes/15-test_ec_data/ec-conv-comp.der create mode 100644 test/recipes/15-test_ec_data/ec-conv-unc.der diff --git a/test/recipes/15-test_ec.t b/test/recipes/15-test_ec.t index 9bf946e81b..5ae3943cb6 100644 --- a/test/recipes/15-test_ec.t +++ b/test/recipes/15-test_ec.t @@ -11,14 +11,15 @@ use strict; use warnings; use File::Spec; -use OpenSSL::Test qw/:DEFAULT srctop_file/; +use File::Compare qw(compare); +use OpenSSL::Test qw/:DEFAULT srctop_file data_file/; use OpenSSL::Test::Utils; setup("test_ec"); plan skip_all => 'EC is not supported in this build' if disabled('ec'); -plan tests => 16; +plan tests => 17; my $no_fips = disabled('fips') || ($ENV{NO_FIPS} // 0); @@ -102,6 +103,31 @@ SKIP: { }; } +subtest 'EC point conversion form (-conv_form)' => sub { + plan tests => 6; + + my $key = srctop_file("test", "testec-p256.pem"); + + ok(run(app(['openssl', 'ec', '-in', $key, '-pubout', + '-outform', 'DER', '-out', 'ec-conv-unc.der'])), + "writing public key with default (uncompressed) conversion form"); + ok(run(app(['openssl', 'ec', '-in', $key, '-pubout', + '-conv_form', 'compressed', + '-outform', 'DER', '-out', 'ec-conv-comp.der'])), + "writing public key with compressed conversion form"); + ok((-s 'ec-conv-comp.der') < (-s 'ec-conv-unc.der'), + "compressed point encoding is smaller than uncompressed"); + # The encodings are deterministic for a fixed key, so compare them + # against the checked-in reference files. + is(compare('ec-conv-unc.der', data_file('ec-conv-unc.der')), 0, + "uncompressed encoding matches the reference file"); + is(compare('ec-conv-comp.der', data_file('ec-conv-comp.der')), 0, + "compressed encoding matches the reference file"); + ok(!run(app(['openssl', 'ec', '-in', $key, '-noout', + '-conv_form', 'bogus'])), + "an invalid conversion form is rejected"); +}; + subtest 'Check loading of fips and non-fips keys' => sub { plan skip_all => "FIPS is disabled" if $no_fips; diff --git a/test/recipes/15-test_ec_data/ec-conv-comp.der b/test/recipes/15-test_ec_data/ec-conv-comp.der new file mode 100644 index 0000000000000000000000000000000000000000..cdae088783de0c61a378532568c1c0a39b118363 GIT binary patch literal 59 zcmXr8G!SNE*J|@PXUoLM#sOw9GqN))F)*vvFqE`BvI!RoIyO-`^XXaR!1?c2y@>r2 NxAD*H)AF|u0086q7Bc_< literal 0 HcmV?d00001 diff --git a/test/recipes/15-test_ec_data/ec-conv-unc.der b/test/recipes/15-test_ec_data/ec-conv-unc.der new file mode 100644 index 0000000000000000000000000000000000000000..7a75bb5cf8d6bc05e8ca1a238ab9b024f16c2908 GIT binary patch literal 91 zcmXqrG!SNE*J|@PXUoLM#sOw9GqN)~F|erCFqE`BvI!RoIyO-`^XXaR!1?c2y@>r2 vxAD*H)AF|uT=<>5B7e%1b3c?rb>-fBd90|L?OE`^_Pm Date: Tue, 23 Jun 2026 11:05:18 +0200 Subject: [PATCH 039/349] crypto/armcap.c: reformat MIDR CPU-model conditionals for readability break long chain of MIDR_IS_CPU_MODEL() alternatives packed onto a single line, which was effectively unreadable and impossible to review or diff one model at a time. move the OPENSSL_armcap_P feature-flag test to the front of the expression so the guard is obvious before the model list. This is a formatting-only change. Signed-off-by: Nikola Pajkovsky Reviewed-by: Eugene Syromiatnikov Reviewed-by: Bob Beck MergeDate: Thu Jun 25 06:10:45 2026 (Merged from https://github.com/openssl/openssl/pull/31664) --- crypto/armcap.c | 46 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/crypto/armcap.c b/crypto/armcap.c index bd978dc86d..1b8ebb5025 100644 --- a/crypto/armcap.c +++ b/crypto/armcap.c @@ -415,15 +415,51 @@ void OPENSSL_cpuid_setup(void) if (OPENSSL_armcap_P & ARMV8_CPUID) OPENSSL_arm_midr = _armv8_cpuid_probe(); - if ((MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A72) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_N1)) && (OPENSSL_armcap_P & ARMV7_NEON)) { + if ((OPENSSL_armcap_P & ARMV7_NEON) + && (MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A72) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_N1))) OPENSSL_armv8_rsa_neonized = 1; - } - if ((MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_V1) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_N2) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_QCOMM, QCOM_CPU_PART_ORYON_X1) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_MICROSOFT, MICROSOFT_CPU_PART_COBALT_100) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_V2) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_N3) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_V3_AE) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_V3) || MIDR_IMPLEMENTER(OPENSSL_arm_midr) == ARM_CPU_IMP_AMPERE) && (OPENSSL_armcap_P & ARMV8_SHA3)) + + if ((OPENSSL_armcap_P & ARMV8_SHA3) + && (MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_V1) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_N2) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_QCOMM, QCOM_CPU_PART_ORYON_X1) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_MICROSOFT, MICROSOFT_CPU_PART_COBALT_100) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_V2) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_N3) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_V3_AE) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_V3) + || MIDR_IMPLEMENTER(OPENSSL_arm_midr) == ARM_CPU_IMP_AMPERE)) OPENSSL_armcap_P |= ARMV8_UNROLL8_EOR3; - if ((MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_V1) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_V2) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_V3_AE) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_V3) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_N2) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_N3) || MIDR_IMPLEMENTER(OPENSSL_arm_midr) == ARM_CPU_IMP_AMPERE || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_NVIDIA, NVIDIA_CPU_PART_OLYMPUS)) && (OPENSSL_armcap_P & ARMV8_SHA3)) + + if ((OPENSSL_armcap_P & ARMV8_SHA3) + && (MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_V1) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_V2) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_V3_AE) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_V3) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_N2) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_N3) + || MIDR_IMPLEMENTER(OPENSSL_arm_midr) == ARM_CPU_IMP_AMPERE + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_NVIDIA, NVIDIA_CPU_PART_OLYMPUS))) OPENSSL_armcap_P |= ARMV8_UNROLL12_EOR3; - if ((MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_FIRESTORM) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_ICESTORM) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_FIRESTORM_PRO) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_ICESTORM_PRO) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_FIRESTORM_MAX) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_ICESTORM_MAX) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_AVALANCHE) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_BLIZZARD) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_AVALANCHE_PRO) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_BLIZZARD_PRO) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_AVALANCHE_MAX) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_BLIZZARD_MAX) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_QCOMM, QCOM_CPU_PART_ORYON_X1) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_NVIDIA, NVIDIA_CPU_PART_OLYMPUS)) && (OPENSSL_armcap_P & ARMV8_SHA3)) + + if ((OPENSSL_armcap_P & ARMV8_SHA3) + && (MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_FIRESTORM) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_ICESTORM) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_FIRESTORM_PRO) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_ICESTORM_PRO) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_FIRESTORM_MAX) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_ICESTORM_MAX) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_AVALANCHE) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_BLIZZARD) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_AVALANCHE_PRO) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_BLIZZARD_PRO) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_AVALANCHE_MAX) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_BLIZZARD_MAX) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_QCOMM, QCOM_CPU_PART_ORYON_X1) + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_NVIDIA, NVIDIA_CPU_PART_OLYMPUS))) OPENSSL_armcap_P |= ARMV8_HAVE_SHA3_AND_WORTH_USING; + if (OPENSSL_armcap_P & ARMV9_SVE2) { uint64_t vl_bytes = _armv8_sve_get_vl_bytes(); From fc972e592ede98d7a51e1a25e4509bb9086f1f4e Mon Sep 17 00:00:00 2001 From: Daniel Kubec Date: Tue, 23 Jun 2026 14:10:45 +0200 Subject: [PATCH 040/349] AEAD: reject late AAD in ChaCha20-Poly1305 after plaintext update Align behavior with AES GCM, which already rejects this misuse with a hard error, by tracking whether plaintext processing has started and returning an error if AAD is supplied afterwards. Fixes #31188 Reviewed-by: Tomas Mraz Reviewed-by: Bob Beck MergeDate: Thu Jun 25 07:01:44 2026 (Merged from https://github.com/openssl/openssl/pull/31673) --- .../ciphers/cipher_chacha20_poly1305_hw.c | 2 ++ test/evp_extra_test.c | 25 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/providers/implementations/ciphers/cipher_chacha20_poly1305_hw.c b/providers/implementations/ciphers/cipher_chacha20_poly1305_hw.c index 47f4c3cb68..ffd2ee744c 100644 --- a/providers/implementations/ciphers/cipher_chacha20_poly1305_hw.c +++ b/providers/implementations/ciphers/cipher_chacha20_poly1305_hw.c @@ -301,6 +301,8 @@ static int chacha20_poly1305_aead_cipher(PROV_CIPHER_CTX *bctx, if (in != NULL) { /* aad or text */ if (out == NULL) { /* aad */ + if (ctx->len.text != 0) + goto err; Poly1305_Update(poly, in, inl); ctx->len.aad += inl; ctx->aad = 1; diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c index b0c37e476e..236991f1f5 100644 --- a/test/evp_extra_test.c +++ b/test/evp_extra_test.c @@ -7636,6 +7636,30 @@ err: return ret; } +#if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305) +static int test_chacha20_poly1305_late_aad(void) +{ + EVP_CIPHER_CTX *ctx = NULL; + EVP_CIPHER *c = NULL; + unsigned char key[32] = { 0 }; + unsigned char iv[12] = { 0 }; + unsigned char aad[4] = "aad"; + unsigned char msg[8] = "message"; + unsigned char out[32]; + int len, test; + + test = TEST_ptr(ctx = EVP_CIPHER_CTX_new()) + && TEST_ptr(c = EVP_CIPHER_fetch(testctx, "ChaCha20-Poly1305", testpropq)) + && TEST_true(EVP_EncryptInit_ex2(ctx, c, key, iv, NULL)) + && TEST_true(EVP_EncryptUpdate(ctx, NULL, &len, aad, sizeof(aad))) + && TEST_true(EVP_EncryptUpdate(ctx, out, &len, msg, sizeof(msg))) + && TEST_false(EVP_EncryptUpdate(ctx, NULL, &len, aad, sizeof(aad))); + + EVP_CIPHER_free(c); + EVP_CIPHER_CTX_free(ctx); + return test; +} +#endif /* * AES-SIV reuse-without-rekey: * msg1: legit non-empty CT, tag verifies, final_ret=0 @@ -8870,6 +8894,7 @@ int setup_tests(void) #endif #if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305) ADD_TEST(test_decrypt_null_chunks); + ADD_TEST(test_chacha20_poly1305_late_aad); #endif #ifndef OPENSSL_NO_DH ADD_TEST(test_DH_priv_pub); From 93a2b9be5ee0a438474f7a9b35d7edbaeb6f4311 Mon Sep 17 00:00:00 2001 From: Dmitry Misharov Date: Tue, 23 Jun 2026 15:10:30 +0200 Subject: [PATCH 041/349] remove make-release.yml, it will be executed on OpenSSL Jenkins instance Reviewed-by: Eugene Syromiatnikov Reviewed-by: Bob Beck MergeDate: Thu Jun 25 07:04:01 2026 (Merged from https://github.com/openssl/openssl/pull/31674) --- .github/workflows/make-release.yml | 48 ------------------------------ 1 file changed, 48 deletions(-) delete mode 100644 .github/workflows/make-release.yml diff --git a/.github/workflows/make-release.yml b/.github/workflows/make-release.yml deleted file mode 100644 index 746da6e059..0000000000 --- a/.github/workflows/make-release.yml +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright 2021-2026 The OpenSSL Project Authors. All Rights Reserved. -# -# Licensed under the Apache License 2.0 (the "License"). You may not use -# this file except in compliance with the License. You can obtain a copy -# in the file LICENSE in the source distribution or at -# https://www.openssl.org/source/license.html - -name: "Make release" - -on: - push: - tags: - - "openssl-*" - -permissions: {} - -jobs: - release: - runs-on: "releaser" - steps: - - name: "Checkout" - uses: "actions/checkout@v6" - with: - fetch-depth: 1 - ref: ${{ github.ref_name }} - github-server-url: "https://github.openssl.org/" - repository: "openssl/openssl" - token: ${{ secrets.GHE_TOKEN }} - path: ${{ github.ref_name }} - persist-credentials: false - - name: "Prepare assets" - env: - SIGNING_KEY_UID: ${{ vars.signing_key_uid }} - run: | - cd "$GITHUB_REF_NAME" - ./util/mktar.sh - mkdir -p assets && mv "$GITHUB_REF_NAME.tar.gz" assets/ && cd assets - openssl sha1 -r "$GITHUB_REF_NAME.tar.gz" > "$GITHUB_REF_NAME.tar.gz.sha1" - openssl sha256 -r "$GITHUB_REF_NAME.tar.gz" > "$GITHUB_REF_NAME.tar.gz.sha256" - gpg -u "$SIGNING_KEY_UID" -o "$GITHUB_REF_NAME.tar.gz.asc" -sba "$GITHUB_REF_NAME.tar.gz" - - name: "Create release" - env: - GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} - run: | - VERSION=$(echo "$GITHUB_REF_NAME" | cut -d "-" -f 2-) - PRE_RELEASE=$([[ "$GITHUB_REF_NAME" =~ alpha|beta ]] && echo "-p" || echo "") - NOTES=$(curl -s "https://api.openssl.org/release-metadata/news/?version=$VERSION&capture_title=False") - gh release create "$GITHUB_REF_NAME" $PRE_RELEASE -t "OpenSSL $VERSION" -d --notes "$NOTES" -R "$GITHUB_REPOSITORY" "$GITHUB_REF_NAME/assets/"* From 5fb218a35c1701ed4a39b47745ac933765a18a5d Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 18 Jun 2026 19:26:47 +0200 Subject: [PATCH 042/349] quic: add mfail tests for QUIC SRTM This cover various function for SRTM. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Nikola Pajkovsky Reviewed-by: Tomas Mraz MergeDate: Thu Jun 25 07:06:31 2026 (Merged from https://github.com/openssl/openssl/pull/31593) --- test/quic_srtm_test.c | 50 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/test/quic_srtm_test.c b/test/quic_srtm_test.c index cbdf847632..db29198918 100644 --- a/test/quic_srtm_test.c +++ b/test/quic_srtm_test.c @@ -72,8 +72,58 @@ err: return testresult; } +static int test_srtm_new_mfail(void) +{ + QUIC_SRTM *srtm; + + MFAIL_start(); + srtm = ossl_quic_srtm_new(NULL, NULL); + MFAIL_end(); + + ossl_quic_srtm_free(srtm); + return srtm != NULL; +} + +static int test_srtm_ops_mfail(void) +{ + int testresult = 0; + QUIC_SRTM *srtm; + void *opaque = NULL; + uint64_t seq_num = 0; + + if (!TEST_ptr(srtm = ossl_quic_srtm_new(NULL, NULL))) + goto err; + + MFAIL_start(); + + if (!ossl_quic_srtm_add(srtm, ptrs + 0, 0, &token_1) + || !ossl_quic_srtm_add(srtm, ptrs + 0, 1, &token_1) + || !ossl_quic_srtm_add(srtm, ptrs + 0, 2, &token_1) + || !ossl_quic_srtm_add(srtm, ptrs + 1, 0, &token_1) + || !ossl_quic_srtm_add(srtm, ptrs + 2, 0, &token_2) + || !ossl_quic_srtm_add(srtm, ptrs + 3, 3, &token_2) + || !ossl_quic_srtm_remove(srtm, ptrs + 3, 3) + || !ossl_quic_srtm_lookup(srtm, &token_1, 0, &opaque, &seq_num) + || !ossl_quic_srtm_cull(srtm, ptrs + 0) + || !ossl_quic_srtm_lookup(srtm, &token_2, 0, &opaque, &seq_num) + || !ossl_quic_srtm_remove(srtm, ptrs + 2, 0)) + goto err; + + testresult = 1; +err: + MFAIL_end(); + ossl_quic_srtm_free(srtm); + return testresult; +} + int setup_tests(void) { ADD_TEST(test_srtm); +#ifdef OPENSSL_NO_CACHED_FETCH + ADD_MFAIL_NO_CHECK_TEST(test_srtm_new_mfail); +#else + ADD_MFAIL_TEST(test_srtm_new_mfail); +#endif + ADD_MFAIL_TEST(test_srtm_ops_mfail); return 1; } From df3a538affb29c2b2208c764125b9502e5455e69 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 22 Jun 2026 21:33:12 +0200 Subject: [PATCH 043/349] apps: add error-path test recipe for skeyutl Cover the help, option-parsing and error paths of the skeyutl command. The successful -genkey path is not exercised as no built-in provider implements opaque symmetric key generation yet. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Tomas Mraz Reviewed-by: Dmitry Belyavskiy MergeDate: Thu Jun 25 07:10:05 2026 (Merged from https://github.com/openssl/openssl/pull/31648) --- test/recipes/20-test_skeyutl.t | 80 ++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 test/recipes/20-test_skeyutl.t diff --git a/test/recipes/20-test_skeyutl.t b/test/recipes/20-test_skeyutl.t new file mode 100644 index 0000000000..e173ba1d13 --- /dev/null +++ b/test/recipes/20-test_skeyutl.t @@ -0,0 +1,80 @@ +#! /usr/bin/env perl +# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +use strict; +use warnings; + +use OpenSSL::Test qw/:DEFAULT with/; +use OpenSSL::Test::Utils; + +setup("test_skeyutl"); + +plan tests => 14; + +# Helper: run skeyutl expecting a non-zero (failure) exit code, and optionally +# check that stderr matches a regular expression. +sub skeyutl_fails { + my ($testtext, $re, @args) = @_; + + my $stderr_file = "skeyutl_err.txt"; + my $err = ''; + + with({ exit_checker => sub { return shift != 0; } }, + sub { + ok(run(app(['openssl', 'skeyutl', @args], stderr => $stderr_file)), + $testtext); + }); + + if (defined $re) { + if (open(my $fh, '<', $stderr_file)) { + $err = do { local $/; <$fh> }; + close($fh); + } + ok($err =~ $re, "$testtext: stderr matches"); + } + unlink($stderr_file) if -f $stderr_file; +} + +# -help exits successfully +ok(run(app(['openssl', 'skeyutl', '-help'])), + "skeyutl -help succeeds"); + +# Neither -cipher nor -skeymgmt is given +skeyutl_fails("skeyutl without -cipher or -skeymgmt fails", + qr/Either -skeymgmt -or -cipher option should be specified/); + +# -genkey but neither -cipher nor -skeymgmt is given: same early check +skeyutl_fails("skeyutl -genkey without -cipher or -skeymgmt fails", + qr/Either -skeymgmt -or -cipher option should be specified/, + '-genkey'); + +# A cipher is given but -genkey is not: generation is the only operation +skeyutl_fails("skeyutl without -genkey reports unsupported operation", + qr/Key generation is the only supported operation/, + '-cipher', 'AES-128-CBC'); + +# -genkey with a valid skey management name: reaches the generation path +# (no built-in provider supports opaque key generation yet) +skeyutl_fails("skeyutl -genkey with valid -skeymgmt reaches generation", + qr/Error creating opaque key for skeymgmt AES/, + '-genkey', '-skeymgmt', 'AES'); + +# -genkey with an unknown skey management name: fetch fails +skeyutl_fails("skeyutl -genkey with unknown -skeymgmt fails", + undef, + '-genkey', '-skeymgmt', 'NoSuchSkeyMgmt'); + +# An unknown cipher name is rejected by option parsing +skeyutl_fails("skeyutl with an unknown cipher fails", + qr/Unknown option or cipher/, + '-genkey', '-cipher', 'NoSuchCipher'); + +# An unknown option is rejected +skeyutl_fails("skeyutl with an unknown option fails", + qr/Unknown option/, + '-not-an-option'); From ea7dbd4fee74c42a5432768a88d1123994477031 Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Mon, 22 Jun 2026 07:56:45 +0200 Subject: [PATCH 044/349] rec_layer_s3.c: prevent max_early_data overflow in ossl_early_data_count_ok() Apply change similar to the one made in d41a9225196b "tls_common.c: prevent max_early_data overflow in rlayer_early_data_count_ok()" to ossl_early_data_count_ok(), that has similar logic in it (as rlayer_early_data_count_ok() has been copied from ossl_early_data_count_ok() in 9dd90232d537 "Move early data counting out of the SSL object and into the record layer"). Complements: d41a9225196b "tls_common.c: prevent max_early_data overflow in rlayer_early_data_count_ok()" Fixes: 70ef40a05e06 "Check max_early_data against the amount of early data we actually receive" Signed-off-by: Eugene Syromiatnikov Reviewed-by: Paul Dale Reviewed-by: Daniel Kubec MergeDate: Thu Jun 25 07:13:07 2026 (Merged from https://github.com/openssl/openssl/pull/31628) --- ssl/record/rec_layer_s3.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ssl/record/rec_layer_s3.c b/ssl/record/rec_layer_s3.c index 75278f39ee..d87001ad7b 100644 --- a/ssl/record/rec_layer_s3.c +++ b/ssl/record/rec_layer_s3.c @@ -150,7 +150,7 @@ static uint32_t ossl_get_max_early_data(SSL_CONNECTION *s) static int ossl_early_data_count_ok(SSL_CONNECTION *s, size_t length, size_t overhead, int send) { - uint32_t max_early_data; + uint64_t max_early_data; max_early_data = ossl_get_max_early_data(s); @@ -161,7 +161,7 @@ static int ossl_early_data_count_ok(SSL_CONNECTION *s, size_t length, } /* If we are dealing with ciphertext we need to allow for the overhead */ - max_early_data += (uint32_t)overhead; + max_early_data += overhead; if (s->early_data_count + length > max_early_data) { SSLfatal(s, send ? SSL_AD_INTERNAL_ERROR : SSL_AD_UNEXPECTED_MESSAGE, From a51f1834c06f36061136e46a1068e251d47b0bbf Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Tue, 23 Jun 2026 09:48:25 +0200 Subject: [PATCH 045/349] crypto/ctype.c: fix off-by-one OOB in ossl_toascii()/ossl_fromascii() Incorrect check for the upper bound allowed the value of 256 to slip through, which could lead to OOB read one element beyound the end of the os_toascii/os_toebcdic arrays. Fix that by changing the comparison with 256 from strictly great to great-or-equal. Found by cppcheck. Fixes: a1df06b36347 "This has been added to avoid the situation where some host ctype.h functions return true for characters > 127. I.e. they are allowing extended ASCII characters through which then cause problems. E.g. marking superscript '2' as a number then causes the common (ch - '0') conversion to number to fail miserably. Likewise letters with diacritical marks can also cause problems." Signed-off-by: Eugene Syromiatnikov Reviewed-by: Nikola Pajkovsky Reviewed-by: Daniel Kubec MergeDate: Thu Jun 25 07:19:30 2026 (Merged from https://github.com/openssl/openssl/pull/31661) --- crypto/ctype.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crypto/ctype.c b/crypto/ctype.c index 686fe64165..75192b11f4 100644 --- a/crypto/ctype.c +++ b/crypto/ctype.c @@ -226,7 +226,7 @@ static const unsigned short ctype_char_map[128] = { #ifdef CHARSET_EBCDIC int ossl_toascii(int c) { - if (c < -128 || c > 256 || c == EOF) + if (c < -128 || c >= 256 || c == EOF) return c; /* * Adjust negatively signed characters. @@ -241,7 +241,7 @@ int ossl_toascii(int c) int ossl_fromascii(int c) { - if (c < -128 || c > 256 || c == EOF) + if (c < -128 || c >= 256 || c == EOF) return c; if (c < 0) c += 256; From 7d810b0df2480c719a25fc831e300293cbf74df3 Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Mon, 22 Jun 2026 19:46:51 +0200 Subject: [PATCH 046/349] CONTRIBUTING.md: tweak wording with regards to ML tooling usage ML tools do not satisfy the authorship requirement, so they are merely used, and not "create" on their own. Signed-off-by: Eugene Syromiatnikov Reviewed-by: Paul Dale Reviewed-by: Kurt Roeckx MergeDate: Thu Jun 25 07:22:26 2026 (Merged from https://github.com/openssl/openssl/pull/31643) --- CONTRIBUTING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3dea8a8fe3..63eaf4397d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -81,12 +81,12 @@ guidelines: ``` 2. Similarly, if a non-trivial portion of a contribution was created - by an AI, you must declare which agent and model were used. This - is done by adding `Assisted-by: {agent}:{model}` below the commit + using an AI tool, you must declare which agent and model were used. + This is done by adding `Assisted-by: {agent}:{model}` below the commit message: ``` - One-line summary of change with AI-created portions + One-line summary of change with AI-generated portions Assisted-by: Claude:claude-sonnet-4-6 ``` From bb6881506742d0d64836021f3ca9e571d5252f21 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 22 Jun 2026 23:42:08 +0200 Subject: [PATCH 047/349] apps: cover the pkcs8 -inform/-outform DER options Only PEM input/output was exercised. Add a subtest that round trips a key through DER, for both unencrypted and encrypted PKCS#8. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Neil Horman Reviewed-by: Dmitry Belyavskiy MergeDate: Thu Jun 25 07:24:30 2026 (Merged from https://github.com/openssl/openssl/pull/31653) --- test/recipes/25-test_pkcs8.t | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/test/recipes/25-test_pkcs8.t b/test/recipes/25-test_pkcs8.t index 50cb01a407..bd7224459b 100644 --- a/test/recipes/25-test_pkcs8.t +++ b/test/recipes/25-test_pkcs8.t @@ -16,7 +16,7 @@ use OpenSSL::Test qw/:DEFAULT srctop_file ok_nofips is_nofips/; setup("test_pkcs8"); -plan tests => 18; +plan tests => 19; my $pc5_key = srctop_file('test', 'certs', 'pc5-key.pem'); @@ -128,6 +128,38 @@ ok(run(app(([ 'openssl', 'asn1parse', "Check the size of the PBKDF2 PARAM 'salt length' is 8"); +subtest 'PKCS#8 DER inform/outform round trip' => sub { + plan tests => 6; + + # PEM -> DER, unencrypted PKCS#8 (exercises -outform DER) + ok(run(app(['openssl', 'pkcs8', '-topk8', '-nocrypt', + '-in', $pc5_key, '-outform', 'DER', + '-out', 'p8-nocrypt.der'])), + "write unencrypted PKCS#8 in DER form"); + # DER -> PEM (exercises -inform DER) + ok(run(app(['openssl', 'pkcs8', '-nocrypt', + '-inform', 'DER', '-in', 'p8-nocrypt.der', + '-out', 'p8-roundtrip.pem'])), + "read unencrypted PKCS#8 from DER form"); + # PEM -> DER again, the result must match the original DER output + ok(run(app(['openssl', 'pkcs8', '-topk8', '-nocrypt', + '-in', 'p8-roundtrip.pem', '-outform', 'DER', + '-out', 'p8-roundtrip.der'])), + "re-encode the round-tripped key to DER"); + is(compare('p8-nocrypt.der', 'p8-roundtrip.der'), 0, + "DER output is identical after a PEM/DER round trip"); + + # The same for an encrypted PKCS#8 structure + ok(run(app(['openssl', 'pkcs8', '-topk8', + '-in', $pc5_key, '-outform', 'DER', + '-out', 'p8-enc.der', '-passout', 'pass:password'])), + "write encrypted PKCS#8 in DER form"); + ok(run(app(['openssl', 'pkcs8', + '-inform', 'DER', '-in', 'p8-enc.der', + '-out', 'p8-dec.pem', '-passin', 'pass:password'])), + "read encrypted PKCS#8 from DER form"); +}; + SKIP: { skip "SM2, SM3 or SM4 is not supported by this OpenSSL build", 3 if disabled("sm2") || disabled("sm3") || disabled("sm4"); From c60fddbbcf887ee5a05c389af1c8f5681c660082 Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Thu, 18 Jun 2026 11:50:37 -0600 Subject: [PATCH 048/349] Drop Windows CE support. Windows CE has been out of mainstream support since 2018 and will not have a modern toolchain capable of compiling a modern OpenSSL. The vc_wince_info Perl helper, the crypto/LPdir_wince.c directory backend, and a long tail of _WIN32_WCE / OPENSSL_SYS_WINCE guards across the Windows code paths get removed. Spotted by idrassi. Reviewed-by: Nikola Pajkovsky Reviewed-by: Tomas Mraz MergeDate: Thu Jun 25 07:27:18 2026 (Merged from https://github.com/openssl/openssl/pull/31601) --- CHANGES.md | 5 ++ Configurations/10-main.conf | 111 --------------------------------- INSTALL.md | 7 +-- apps/lib/apps.c | 4 -- apps/lib/opt.c | 2 - crypto/LPdir_win.c | 12 ---- crypto/bio/bss_log.c | 3 +- crypto/bn/bn_lib.c | 12 ---- crypto/cryptlib.c | 4 -- crypto/dso/dso_win32.c | 51 --------------- crypto/getenv.c | 2 +- crypto/o_dir.c | 2 - crypto/o_str.c | 2 +- crypto/rand/randfile.c | 7 ++- crypto/threads_win.c | 4 -- crypto/ui/ui_openssl.c | 20 ++---- include/internal/e_os.h | 17 +---- include/internal/e_winsock.h | 4 +- include/internal/refcount.h | 12 +--- include/internal/sockets.h | 9 +-- include/internal/tsan_assist.h | 2 +- include/openssl/e_os2.h | 5 +- 22 files changed, 28 insertions(+), 269 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3cdace8517..671e700a72 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -64,6 +64,11 @@ OpenSSL Releases *Jakub Zelenka* + * Windows CE support was dropped - Windows CE has been unsupported since + 2018 and does not have a modern C99 toolchain. + + *Bob Beck* + * Improved DTLS handshake robustness under UDP reordering by buffering and replaying early ChangeCipherSpec (CCS) records at the expected state. diff --git a/Configurations/10-main.conf b/Configurations/10-main.conf index a497bcce4f..6b2d1cffe4 100644 --- a/Configurations/10-main.conf +++ b/Configurations/10-main.conf @@ -61,70 +61,6 @@ sub vc_win32_info { return $vc_win32_info; } -my $vc_wince_info = {}; -sub vc_wince_info { - unless (%$vc_wince_info) { - # sanity check - $die->('%OSVERSION% is not defined') if (!defined(env('OSVERSION'))); - $die->('%PLATFORM% is not defined') if (!defined(env('PLATFORM'))); - $die->('%TARGETCPU% is not defined') if (!defined(env('TARGETCPU'))); - - # - # Idea behind this is to mimic flags set by eVC++ IDE... - # - my $wcevers = env('OSVERSION'); # WCENNN - my $wcevernum; - my $wceverdotnum; - if ($wcevers =~ /^WCE([1-9])([0-9]{2})$/) { - $wcevernum = "$1$2"; - $wceverdotnum = "$1.$2"; - } else { - $die->('%OSVERSION% value is insane'); - $wcevernum = "{unknown}"; - $wceverdotnum = "{unknown}"; - } - my $wcecdefs = "-D_WIN32_WCE=$wcevernum -DUNDER_CE=$wcevernum"; # -D_WIN32_WCE=NNN - my $wcelflag = "/subsystem:windowsce,$wceverdotnum"; # ...,N.NN - - my $wceplatf = env('PLATFORM'); - - $wceplatf =~ tr/a-z0-9 /A-Z0-9_/; - $wcecdefs .= " -DWCE_PLATFORM_$wceplatf"; - - my $wcetgt = env('TARGETCPU'); # just shorter name... - SWITCH: for($wcetgt) { - /^X86/ && do { $wcecdefs.=" -Dx86 -D_X86_ -D_i386_ -Di_386_"; - $wcelflag.=" /machine:X86"; last; }; - /^ARMV4[IT]/ && do { $wcecdefs.=" -DARM -D_ARM_ -D$wcetgt"; - $wcecdefs.=" -DTHUMB -D_THUMB_" if($wcetgt=~/T$/); - $wcecdefs.=" -QRarch4T -QRinterwork-return"; - $wcelflag.=" /machine:THUMB"; last; }; - /^ARM/ && do { $wcecdefs.=" -DARM -D_ARM_ -D$wcetgt"; - $wcelflag.=" /machine:ARM"; last; }; - /^MIPSIV/ && do { $wcecdefs.=" -DMIPS -D_MIPS_ -DR4000 -D$wcetgt"; - $wcecdefs.=" -D_MIPS64 -QMmips4 -QMn32"; - $wcelflag.=" /machine:MIPSFPU"; last; }; - /^MIPS16/ && do { $wcecdefs.=" -DMIPS -D_MIPS_ -DR4000 -D$wcetgt"; - $wcecdefs.=" -DMIPSII -QMmips16"; - $wcelflag.=" /machine:MIPS16"; last; }; - /^MIPSII/ && do { $wcecdefs.=" -DMIPS -D_MIPS_ -DR4000 -D$wcetgt"; - $wcecdefs.=" -QMmips2"; - $wcelflag.=" /machine:MIPS"; last; }; - /^R4[0-9]{3}/ && do { $wcecdefs.=" -DMIPS -D_MIPS_ -DR4000"; - $wcelflag.=" /machine:MIPS"; last; }; - /^SH[0-9]/ && do { $wcecdefs.=" -D$wcetgt -D_${wcetgt}_ -DSHx"; - $wcecdefs.=" -Qsh4" if ($wcetgt =~ /^SH4/); - $wcelflag.=" /machine:$wcetgt"; last; }; - { $wcecdefs.=" -D$wcetgt -D_${wcetgt}_"; - $wcelflag.=" /machine:$wcetgt"; last; }; - } - - $vc_wince_info = { cppflags => $wcecdefs, - lflags => $wcelflag }; - } - return $vc_wince_info; -} - # Helper functions for the VMS configs my $vms_info = {}; sub vms_info { @@ -1645,53 +1581,6 @@ my %targets = ( # some installation path heuristics in windows-makefile.tmpl... build_scheme => add("VC-WOW", { separator => undef }), }, - "VC-CE" => { - inherit_from => [ "VC-common" ], - CFLAGS => add(picker(debug => "/Od", - release => "/O1i")), - CPPDEFINES => picker(debug => [ "DEBUG", "_DEBUG" ]), - LDFLAGS => add("/nologo /opt:ref"), - cflags => - combine('/GF /Gy', - sub { vc_wince_info()->{cflags}; }, - sub { `cl 2>&1` =~ /Version ([0-9]+)\./ && $1>=14 - ? ($disabled{shared} ? " /MT" : ($disabled{"static-vcruntime"} ? " /MD" : "")) - : " /MC"; }), - cppflags => sub { vc_wince_info()->{cppflags}; }, - lib_defines => add("NO_CHMOD", "OPENSSL_SMALL_FOOTPRINT"), - lib_cppflags => sub { vc_wince_info()->{cppflags}; }, - includes => - add(combine(sub { defined(env('WCECOMPAT')) - ? '$(WCECOMPAT)/include' : (); }, - sub { defined(env('PORTSDK_LIBPATH')) - ? '$(PORTSDK_LIBPATH)/../../include' - : (); })), - lflags => add(combine(sub { vc_wince_info()->{lflags}; }, - sub { defined(env('PORTSDK_LIBPATH')) - ? "/entry:mainCRTstartup" : (); })), - sys_id => "WINCE", - bn_ops => add("BN_LLONG"), - ex_libs => add(sub { - my @ex_libs = (); - push @ex_libs, 'ws2.lib' unless $disabled{sock}; - push @ex_libs, 'crypt32.lib'; - if (defined(env('WCECOMPAT'))) { - my $x = '$(WCECOMPAT)/lib'; - if (-f "$x/env('TARGETCPU')/wcecompatex.lib") { - $x .= '/$(TARGETCPU)/wcecompatex.lib'; - } else { - $x .= '/wcecompatex.lib'; - } - push @ex_libs, $x; - } - push @ex_libs, '$(PORTSDK_LIBPATH)/portlib.lib' - if (defined(env('PORTSDK_LIBPATH'))); - push @ex_libs, '/nodefaultlib coredll.lib corelibc.lib' - if (env('TARGETCPU') =~ /^X86|^ARMV4[IT]/); - return join(" ", @ex_libs); - }), - }, - #### MinGW "mingw-common" => { inherit_from => [ 'BASE_unix' ], diff --git a/INSTALL.md b/INSTALL.md index b44e2705c0..f087ab93fd 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -169,13 +169,12 @@ issue the following commands to build OpenSSL. $ nmake test As mentioned in the [Choices](#choices) section, you need to pick one -of the four Configure targets in the first command. +of the Configure targets in the first command. Most likely you will be using the `VC-WIN64A`/`VC-WIN64A-HYBRIDCRT` target for 64bit Windows binaries (AMD64) or `VC-WIN32`/`VC-WIN32-HYBRIDCRT` for 32bit -Windows binaries (X86). -The other two options are `VC-WIN64I` (Intel IA64, Itanium) and -`VC-CE` (Windows CE) are rather uncommon nowadays. +Windows binaries (X86). `VC-WIN64I` (Intel IA64, Itanium) is also available +but rather uncommon nowadays. Installing OpenSSL ------------------ diff --git a/apps/lib/apps.c b/apps/lib/apps.c index b2756b3b1d..2c55c0af21 100644 --- a/apps/lib/apps.c +++ b/apps/lib/apps.c @@ -3117,14 +3117,10 @@ static int WIN32_rename(const char *from, const char *to) if (tfrom == NULL) goto err; tto = tfrom + flen; -#if !defined(_WIN32_WCE) || _WIN32_WCE >= 101 if (!MultiByteToWideChar(CP_ACP, 0, from, (int)flen, (WCHAR *)tfrom, (int)flen)) -#endif for (i = 0; i < flen; i++) tfrom[i] = (TCHAR)from[i]; -#if !defined(_WIN32_WCE) || _WIN32_WCE >= 101 if (!MultiByteToWideChar(CP_ACP, 0, to, (int)tlen, (WCHAR *)tto, (int)tlen)) -#endif for (i = 0; i < tlen; i++) tto[i] = (TCHAR)to[i]; } diff --git a/apps/lib/opt.c b/apps/lib/opt.c index 9c6041230b..d139346cc2 100644 --- a/apps/lib/opt.c +++ b/apps/lib/opt.c @@ -1236,9 +1236,7 @@ int opt_isdir(const char *name) if (len_0 > MAX_PATH) return -1; -#if !defined(_WIN32_WCE) || _WIN32_WCE >= 101 if (!MultiByteToWideChar(CP_ACP, 0, name, (int)len_0, tempname, MAX_PATH)) -#endif for (i = 0; i < len_0; i++) tempname[i] = (WCHAR)name[i]; diff --git a/crypto/LPdir_win.c b/crypto/LPdir_win.c index e8141cab86..425a7962d0 100644 --- a/crypto/LPdir_win.c +++ b/crypto/LPdir_win.c @@ -43,18 +43,6 @@ #include "LPdir.h" #endif -/* - * We're most likely overcautious here, but let's reserve for broken WinCE - * headers and explicitly opt for UNICODE call. Keep in mind that our WinCE - * builds are compiled with -DUNICODE [as well as -D_UNICODE]. - */ -#if defined(LP_SYS_WINCE) && !defined(FindFirstFile) -#define FindFirstFile FindFirstFileW -#endif -#if defined(LP_SYS_WINCE) && !defined(FindNextFile) -#define FindNextFile FindNextFileW -#endif - #ifndef NAME_MAX #define NAME_MAX 255 #endif diff --git a/crypto/bio/bss_log.c b/crypto/bio/bss_log.c index 74a5b6c5c5..2928ae5c41 100644 --- a/crypto/bio/bss_log.c +++ b/crypto/bio/bss_log.c @@ -22,8 +22,7 @@ #include "bio_local.h" #include "internal/cryptlib.h" -#if defined(OPENSSL_SYS_WINCE) -#elif defined(OPENSSL_SYS_WIN32) +#if defined(OPENSSL_SYS_WIN32) #elif defined(__wasi__) #define NO_SYSLOG #elif defined(OPENSSL_SYS_VMS) diff --git a/crypto/bn/bn_lib.c b/crypto/bn/bn_lib.c index 84d5151b62..161f4e10a7 100644 --- a/crypto/bn/bn_lib.c +++ b/crypto/bn/bn_lib.c @@ -89,15 +89,6 @@ const BIGNUM *BN_value_one(void) return &const_one; } -/* - * Old Visual Studio ARM compiler miscompiles BN_num_bits_word() - * https://mta.openssl.org/pipermail/openssl-users/2018-August/008465.html - */ -#if defined(_MSC_VER) && defined(_ARM_) && defined(_WIN32_WCE) \ - && _MSC_VER >= 1400 && _MSC_VER < 1501 -#define MS_BROKEN_BN_num_bits_word -#pragma optimize("", off) -#endif int BN_num_bits_word(BN_ULONG l) { BN_ULONG x, mask; @@ -142,9 +133,6 @@ int BN_num_bits_word(BN_ULONG l) return bits; } -#ifdef MS_BROKEN_BN_num_bits_word -#pragma optimize("", on) -#endif /* * This function still leaks `a->dmax`: it's caller's responsibility to diff --git a/crypto/cryptlib.c b/crypto/cryptlib.c index 7624574103..d62ebfae0b 100644 --- a/crypto/cryptlib.c +++ b/crypto/cryptlib.c @@ -110,8 +110,6 @@ void OPENSSL_showfatal(const char *fmta, ...) /* * First check if it's a console application, in which case the * error message would be printed to standard error. - * Windows CE does not have a concept of a console application, - * so we need to guard the check. */ #ifdef STD_ERROR_HANDLE HANDLE h; @@ -260,9 +258,7 @@ void OPENSSL_die(const char *message, const char *file, int line) /* * Win32 abort() customarily shows a dialog, but we just did that... */ -#if !defined(_WIN32_WCE) raise(SIGABRT); -#endif _exit(3); #endif } diff --git a/crypto/dso/dso_win32.c b/crypto/dso/dso_win32.c index 78cc649324..6ac6727fda 100644 --- a/crypto/dso/dso_win32.c +++ b/crypto/dso/dso_win32.c @@ -12,49 +12,6 @@ #if defined(DSO_WIN32) -#ifdef _WIN32_WCE -#if _WIN32_WCE < 300 -static FARPROC GetProcAddressA(HMODULE hModule, LPCSTR lpProcName) -{ - WCHAR lpProcNameW[64]; - int i; - - for (i = 0; lpProcName[i] && i < 64; i++) - lpProcNameW[i] = (WCHAR)lpProcName[i]; - if (i == 64) - return NULL; - lpProcNameW[i] = 0; - - return GetProcAddressW(hModule, lpProcNameW); -} -#endif -#undef GetProcAddress -#define GetProcAddress GetProcAddressA - -static HINSTANCE LoadLibraryA(LPCSTR lpLibFileName) -{ - WCHAR *fnamw; - size_t len_0 = strlen(lpLibFileName) + 1, i; - -#ifdef _MSC_VER - fnamw = (WCHAR *)_alloca(len_0 * sizeof(WCHAR)); -#else - fnamw = (WCHAR *)alloca(len_0 * sizeof(WCHAR)); -#endif - if (fnamw == NULL) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return NULL; - } -#if defined(_WIN32_WCE) && _WIN32_WCE >= 101 - if (!MultiByteToWideChar(CP_ACP, 0, lpLibFileName, len_0, fnamw, len_0)) -#endif - for (i = 0; i < len_0; i++) - fnamw[i] = (WCHAR)lpLibFileName[i]; - - return LoadLibraryW(fnamw); -} -#endif - #define GETPROCADDRESS(h, name, type) ((type)(void (*)(void))GetProcAddress((h), (name))) /* Part of the hack in "win32_load" ... */ @@ -472,14 +429,10 @@ static const char *openssl_strnchr(const char *string, int c, size_t len) } #include -#ifdef _WIN32_WCE -#define DLLNAME "TOOLHELP.DLL" -#else #ifdef MODULEENTRY32 #undef MODULEENTRY32 /* unmask the ASCII version! */ #endif #define DLLNAME "KERNEL32.DLL" -#endif typedef HANDLE(WINAPI *CREATETOOLHELP32SNAPSHOT)(DWORD, DWORD); typedef BOOL(WINAPI *CLOSETOOLHELP32SNAPSHOT)(HANDLE); @@ -582,11 +535,7 @@ static void *win32_globallookup(const char *name) return NULL; } /* We take the rest for granted... */ -#ifdef _WIN32_WCE - close_snap = GETPROCADDRESS(dll, "CloseToolhelp32Snapshot", CLOSETOOLHELP32SNAPSHOT); -#else close_snap = (CLOSETOOLHELP32SNAPSHOT)CloseHandle; -#endif module_first = GETPROCADDRESS(dll, "Module32First", MODULE32); module_next = GETPROCADDRESS(dll, "Module32Next", MODULE32); diff --git a/crypto/getenv.c b/crypto/getenv.c index aa01ea8af7..8ea7128864 100644 --- a/crypto/getenv.c +++ b/crypto/getenv.c @@ -17,7 +17,7 @@ char *ossl_safe_getenv(const char *name) { -#if defined(_WIN32) && defined(CP_UTF8) && !defined(_WIN32_WCE) +#if defined(_WIN32) && defined(CP_UTF8) if (GetEnvironmentVariableW(L"OPENSSL_WIN32_UTF8", NULL, 0) != 0) { char *val = NULL; int vallen = 0; diff --git a/crypto/o_dir.c b/crypto/o_dir.c index 36d33fbed4..ed92b9f130 100644 --- a/crypto/o_dir.c +++ b/crypto/o_dir.c @@ -31,8 +31,6 @@ # include "LPdir_vms.c" #elif defined OPENSSL_SYS_WIN32 # include "LPdir_win32.c" -#elif defined OPENSSL_SYS_WINCE -# include "LPdir_wince.c" #else # include "LPdir_nyi.c" #endif diff --git a/crypto/o_str.c b/crypto/o_str.c index c2ec1fc261..2192d48775 100644 --- a/crypto/o_str.c +++ b/crypto/o_str.c @@ -377,7 +377,7 @@ char *OPENSSL_buf2hexstr(const unsigned char *buf, long buflen) int openssl_strerror_r(int errnum, char *buf, size_t buflen) { -#if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(_WIN32_WCE) +#if defined(_MSC_VER) && _MSC_VER >= 1400 return !strerror_s(buf, buflen, errnum); #elif defined(_GNU_SOURCE) char *err; diff --git a/crypto/rand/randfile.c b/crypto/rand/randfile.c index b1881dac97..c2be28a122 100644 --- a/crypto/rand/randfile.c +++ b/crypto/rand/randfile.c @@ -35,7 +35,7 @@ #ifndef OPENSSL_NO_POSIX_IO #include #include -#if defined(_WIN32) && !defined(_WIN32_WCE) +#if defined(_WIN32) #include #define stat _stat #define chmod _chmod @@ -69,7 +69,8 @@ * This declaration is a nasty hack to get around vms' extension to fopen for * passing in sharing options being disabled by /STANDARD=ANSI89 */ -static __FILE_ptr32 (*const vms_fopen)(const char *, const char *, ...) = (__FILE_ptr32 (*)(const char *, const char *, ...))fopen; +static __FILE_ptr32 (*const vms_fopen)(const char *, const char *, ...) + = (__FILE_ptr32 (*)(const char *, const char *, ...))fopen; #define VMS_OPEN_ATTRS \ "shr=get,put,upd,del", "ctx=bin,stm", "rfm=stm", "rat=none", "mrs=0" #define openssl_fopen(fname, mode) vms_fopen((fname), (mode), VMS_OPEN_ATTRS) @@ -271,7 +272,7 @@ const char *RAND_file_name(char *buf, size_t size) size_t len; int use_randfile = 1; -#if defined(_WIN32) && defined(CP_UTF8) && !defined(_WIN32_WCE) +#if defined(_WIN32) && defined(CP_UTF8) DWORD envlen; WCHAR *var; diff --git a/crypto/threads_win.c b/crypto/threads_win.c index 448c909630..b52c8dd520 100644 --- a/crypto/threads_win.c +++ b/crypto/threads_win.c @@ -442,15 +442,11 @@ CRYPTO_RWLOCK *CRYPTO_THREAD_lock_new(void) /* Don't set error, to avoid recursion blowup. */ return NULL; -#if !defined(_WIN32_WCE) /* 0x400 is the spin count value suggested in the documentation */ if (!InitializeCriticalSectionAndSpinCount(lock, 0x400)) { OPENSSL_free(lock); return NULL; } -#else - InitializeCriticalSection(lock); -#endif #endif return lock; diff --git a/crypto/ui/ui_openssl.c b/crypto/ui/ui_openssl.c index 8715ff068e..5b97cc6448 100644 --- a/crypto/ui/ui_openssl.c +++ b/crypto/ui/ui_openssl.c @@ -60,10 +60,8 @@ #endif #ifdef WIN_CONSOLE_BUG -#ifndef OPENSSL_SYS_WINCE #include #endif -#endif /* * There are 6 types of terminal interface supported, TERMIO, TERMIOS, VMS, @@ -165,7 +163,7 @@ static long tty_orig[3], tty_new[3]; /* XXX Is there any guarantee that this * structures? */ static long status; static unsigned short channel = 0; -#elif defined(_WIN32) && !defined(_WIN32_WCE) +#elif defined(_WIN32) static DWORD tty_orig, tty_new; #else #if !defined(OPENSSL_SYS_MSDOS) || defined(__DJGPP__) @@ -176,12 +174,10 @@ static FILE *tty_in, *tty_out; static int is_a_tty; /* Declare static functions */ -#if !defined(OPENSSL_SYS_WINCE) static int read_till_nl(FILE *); static void recsig(int); static void pushsig(void); static void popsig(void); -#endif #if defined(OPENSSL_SYS_MSDOS) && !defined(_WIN32) static int noecho_fgets(char *buf, int size, FILE *tty); #endif @@ -255,7 +251,6 @@ static int read_string(UI *ui, UI_STRING *uis) return 1; } -#if !defined(OPENSSL_SYS_WINCE) /* Internal functions to read a string without echoing */ static int read_till_nl(FILE *in) { @@ -270,7 +265,6 @@ static int read_till_nl(FILE *in) } static volatile sig_atomic_t intr_signal; -#endif static int read_string_inner(UI *ui, UI_STRING *uis, int echo, int strip_nl) { @@ -278,7 +272,6 @@ static int read_string_inner(UI *ui, UI_STRING *uis, int echo, int strip_nl) int ok; char result[BUFSIZ]; int maxsize = BUFSIZ - 1; -#if !defined(OPENSSL_SYS_WINCE) char *p = NULL; int echo_eol = !echo; @@ -358,9 +351,6 @@ error: if (ps >= 1) popsig(); -#else - ok = 1; -#endif OPENSSL_cleanse(result, BUFSIZ); return ok; @@ -376,7 +366,7 @@ static int open_console(UI *ui) #if defined(OPENSSL_SYS_VXWORKS) tty_in = stdin; tty_out = stderr; -#elif defined(_WIN32) && !defined(_WIN32_WCE) +#elif defined(_WIN32) if ((tty_out = fopen("conout$", "w")) == NULL) tty_out = stderr; @@ -505,7 +495,7 @@ static int noecho_console(UI *ui) } } #endif -#if defined(_WIN32) && !defined(_WIN32_WCE) +#if defined(_WIN32) if (is_a_tty) { tty_new = tty_orig; tty_new &= ~ENABLE_ECHO_INPUT; @@ -537,7 +527,7 @@ static int echo_console(UI *ui) } } #endif -#if defined(_WIN32) && !defined(_WIN32_WCE) +#if defined(_WIN32) if (is_a_tty) { tty_new = tty_orig; SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), tty_new); @@ -567,7 +557,6 @@ static int close_console(UI *ui) return ret; } -#if !defined(OPENSSL_SYS_WINCE) /* Internal functions to handle signals and act on them */ static void pushsig(void) { @@ -648,7 +637,6 @@ static void recsig(int i) { intr_signal = i; } -#endif /* Internal functions specific for Windows */ #if defined(OPENSSL_SYS_MSDOS) && !defined(_WIN32) diff --git a/include/internal/e_os.h b/include/internal/e_os.h index 3ea75209cf..b54f4d5a70 100644 --- a/include/internal/e_os.h +++ b/include/internal/e_os.h @@ -83,7 +83,7 @@ #endif #ifdef WINDOWS -#if !defined(_WIN32_WCE) && !defined(_WIN32_WINNT) +#if !defined(_WIN32_WINNT) /* * The _WIN32_WINNT is described here: * https://learn.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt?view=msvc-170 @@ -115,12 +115,9 @@ #include #include #include -#if defined(_WIN32_WCE) && !defined(EACCES) -#define EACCES 13 -#endif #include #include -#if defined(_MSC_VER) && !defined(_WIN32_WCE) && !defined(_DLL) && defined(stdin) +#if defined(_MSC_VER) && !defined(_DLL) && defined(stdin) #if _MSC_VER >= 1300 && _MSC_VER < 1600 #undef stdin #undef stdout @@ -136,10 +133,6 @@ FILE *__iob_func(void); #include #include -#ifdef OPENSSL_SYS_WINCE -#define OPENSSL_NO_POSIX_IO -#endif - #define EXIT(n) exit(n) #define LIST_SEPARATOR_CHAR ';' #ifndef W_OK @@ -148,11 +141,7 @@ FILE *__iob_func(void); #ifndef R_OK #define R_OK 4 #endif -#ifdef OPENSSL_SYS_WINCE -#define DEFAULT_HOME "" -#else #define DEFAULT_HOME "C:" -#endif /* Avoid Visual Studio 13 GetVersion deprecated problems */ #if defined(_MSC_VER) && _MSC_VER >= 1800 @@ -232,7 +221,7 @@ FILE *__iob_func(void); /***********************************************/ #if defined(OPENSSL_SYS_WINDOWS) -#if defined(_MSC_VER) && (_MSC_VER >= 1310) && !defined(_WIN32_WCE) +#if defined(_MSC_VER) && (_MSC_VER >= 1310) #define open _open #define fdopen _fdopen #define close _close diff --git a/include/internal/e_winsock.h b/include/internal/e_winsock.h index 6fe7fd0fae..1ea7383348 100644 --- a/include/internal/e_winsock.h +++ b/include/internal/e_winsock.h @@ -12,7 +12,7 @@ #pragma once #ifdef WINDOWS -#if !defined(_WIN32_WCE) && !defined(_WIN32_WINNT) +#if !defined(_WIN32_WINNT) /* * The _WIN32_WINNT is described here: * https://learn.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt?view=msvc-170 @@ -40,7 +40,7 @@ */ #define _WIN32_WINNT 0x0501 #endif -#if defined(_WIN32_WINNT) || defined(_WIN32_WCE) +#if defined(_WIN32_WINNT) /* * Just like defining _WIN32_WINNT including winsock2.h implies * certain "discipline" for maintaining [broad] binary compatibility. diff --git a/include/internal/refcount.h b/include/internal/refcount.h index 61eb78ae41..11f207f881 100644 --- a/include/internal/refcount.h +++ b/include/internal/refcount.h @@ -135,7 +135,7 @@ typedef struct { volatile int val; } CRYPTO_REF_COUNT; -#if (defined(_M_ARM) && _M_ARM >= 7 && !defined(_WIN32_WCE)) || defined(_M_ARM64) +#if (defined(_M_ARM) && _M_ARM >= 7) || defined(_M_ARM64) #include #if defined(_M_ARM64) && !defined(_ARM_BARRIER_ISH) #define _ARM_BARRIER_ISH _ARM64_BARRIER_ISH @@ -160,17 +160,7 @@ static __inline int CRYPTO_GET_REF(CRYPTO_REF_COUNT *refcnt, int *ret) } #else -#if !defined(_WIN32_WCE) #pragma intrinsic(_InterlockedExchangeAdd) -#else -#if _WIN32_WCE >= 0x600 -extern long __cdecl _InterlockedExchangeAdd(long volatile *, long); -#else -/* under Windows CE we still have old-style Interlocked* functions */ -extern long __cdecl InterlockedExchangeAdd(long volatile *, long); -#define _InterlockedExchangeAdd InterlockedExchangeAdd -#endif -#endif static __inline int CRYPTO_UP_REF(CRYPTO_REF_COUNT *refcnt, int *ret) { diff --git a/include/internal/sockets.h b/include/internal/sockets.h index ba8432c0a5..1da34aa7b0 100644 --- a/include/internal/sockets.h +++ b/include/internal/sockets.h @@ -37,18 +37,11 @@ #include #include #include -#elif defined(_WIN32_WCE) && _WIN32_WCE < 410 -#define getservbyname _masked_declaration_getservbyname #endif #if !defined(IPPROTO_IP) /* winsock[2].h was included already? */ #include "internal/e_winsock.h" #endif -#ifdef getservbyname -/* this is used to be wcecompat/include/winsock_extras.h */ -#undef getservbyname -struct servent *PASCAL getservbyname(const char *, const char *); -#endif #ifdef _WIN64 /* @@ -230,7 +223,7 @@ static ossl_inline int writesocket_ex(int s, const void *b, int n, int f) #endif /* also in apps/include/apps.h */ -#if defined(OPENSSL_SYS_WIN32) || defined(OPENSSL_SYS_WINCE) +#if defined(OPENSSL_SYS_WIN32) #define openssl_fdset(a, b) FD_SET((unsigned int)(a), b) #else #define openssl_fdset(a, b) FD_SET(a, b) diff --git a/include/internal/tsan_assist.h b/include/internal/tsan_assist.h index db011f19f0..a4e87504a1 100644 --- a/include/internal/tsan_assist.h +++ b/include/internal/tsan_assist.h @@ -78,7 +78,7 @@ #endif #elif defined(_MSC_VER) && _MSC_VER >= 1200 \ - && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_X64) || defined(_M_ARM64) || (defined(_M_ARM) && _M_ARM >= 7 && !defined(_WIN32_WCE))) + && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_X64) || defined(_M_ARM64) || (defined(_M_ARM) && _M_ARM >= 7)) /* * There is subtle dependency on /volatile: command-line option. * "ms" implies same semantic as memory_order_acquire for loads and diff --git a/include/openssl/e_os2.h b/include/openssl/e_os2.h index bacc161bd6..5f390037d7 100644 --- a/include/openssl/e_os2.h +++ b/include/openssl/e_os2.h @@ -72,14 +72,11 @@ extern "C" { #if defined(OPENSSL_SYS_WINNT) #undef OPENSSL_SYS_UNIX #endif -#if defined(OPENSSL_SYS_WINCE) -#undef OPENSSL_SYS_UNIX -#endif #endif #endif /* Anything that tries to look like Microsoft is "Windows" */ -#if defined(OPENSSL_SYS_WIN32) || defined(OPENSSL_SYS_WIN64) || defined(OPENSSL_SYS_WINNT) || defined(OPENSSL_SYS_WINCE) +#if defined(OPENSSL_SYS_WIN32) || defined(OPENSSL_SYS_WIN64) || defined(OPENSSL_SYS_WINNT) #undef OPENSSL_SYS_UNIX #define OPENSSL_SYS_WINDOWS #ifndef OPENSSL_SYS_MSDOS From 30db55ac33cf6b815067149c233f45e4199df9b0 Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Tue, 19 May 2026 15:42:47 +0200 Subject: [PATCH 049/349] util: set thunks for sk_${nametype}_new(_reserve) call OPENSSL_sk_set_thunks for sk_${nametype}_new and sk_${nametype}_new_reserve, and wrap the long generated stack constructor and deep-copy macros across multiple lines so the nested thunk setup is easier to read and maintain. Signed-off-by: Nikola Pajkovsky Reviewed-by: Neil Horman Reviewed-by: Milan Broz MergeDate: Thu Jun 25 12:12:46 2026 (Merged from https://github.com/openssl/openssl/pull/31523) --- util/perl/OpenSSL/stackhash.pm | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/util/perl/OpenSSL/stackhash.pm b/util/perl/OpenSSL/stackhash.pm index 39f71e1ce7..e49e2d4def 100644 --- a/util/perl/OpenSSL/stackhash.pm +++ b/util/perl/OpenSSL/stackhash.pm @@ -28,9 +28,30 @@ sub generate_stack_macros_int { SKM_DEFINE_STACK_OF_INTERNAL(${nametype}, ${realtype}, ${plaintype}) #define sk_${nametype}_num(sk) OPENSSL_sk_num(ossl_check_const_${nametype}_sk_type(sk)) #define sk_${nametype}_value(sk, idx) ((${realtype} *)OPENSSL_sk_value(ossl_check_const_${nametype}_sk_type(sk), (idx))) -#define sk_${nametype}_new(cmp) ((STACK_OF(${nametype}) *)OPENSSL_sk_set_copy_thunks(OPENSSL_sk_set_cmp_thunks(OPENSSL_sk_new(ossl_check_${nametype}_compfunc_type(cmp)), sk_${nametype}_cmpfunc_thunk), sk_${nametype}_copyfunc_thunk)) -#define sk_${nametype}_new_null() ((STACK_OF(${nametype}) *)OPENSSL_sk_set_thunks(OPENSSL_sk_set_copy_thunks(OPENSSL_sk_new_null(), sk_${nametype}_copyfunc_thunk), sk_${nametype}_freefunc_thunk)) -#define sk_${nametype}_new_reserve(cmp, n) ((STACK_OF(${nametype}) *)OPENSSL_sk_set_copy_thunks(OPENSSL_sk_set_cmp_thunks(OPENSSL_sk_new_reserve(ossl_check_${nametype}_compfunc_type(cmp), (n)), sk_${nametype}_cmpfunc_thunk), sk_${nametype}_copyfunc_thunk)) +#define sk_${nametype}_new(cmp) \\ + ((STACK_OF(${nametype}) *)OPENSSL_sk_set_thunks( \\ + OPENSSL_sk_set_copy_thunks( \\ + OPENSSL_sk_set_cmp_thunks( \\ + OPENSSL_sk_new(ossl_check_${nametype}_compfunc_type(cmp)), \\ + sk_${nametype}_cmpfunc_thunk), \\ + sk_${nametype}_copyfunc_thunk), \\ + sk_${nametype}_freefunc_thunk)) +#define sk_${nametype}_new_null() \\ + ((STACK_OF(${nametype}) *)OPENSSL_sk_set_thunks( \\ + OPENSSL_sk_set_copy_thunks( \\ + OPENSSL_sk_set_cmp_thunks( \\ + OPENSSL_sk_new_null(), \\ + sk_${nametype}_cmpfunc_thunk), \\ + sk_${nametype}_copyfunc_thunk), \\ + sk_${nametype}_freefunc_thunk)) +#define sk_${nametype}_new_reserve(cmp, n) \\ + ((STACK_OF(${nametype}) *)OPENSSL_sk_set_thunks( \\ + OPENSSL_sk_set_copy_thunks( \\ + OPENSSL_sk_set_cmp_thunks( \\ + OPENSSL_sk_new_reserve(ossl_check_${nametype}_compfunc_type(cmp), (n)), \\ + sk_${nametype}_cmpfunc_thunk), \\ + sk_${nametype}_copyfunc_thunk), \\ + sk_${nametype}_freefunc_thunk)) #define sk_${nametype}_reserve(sk, n) OPENSSL_sk_reserve(ossl_check_${nametype}_sk_type(sk), (n)) #define sk_${nametype}_free(sk) OPENSSL_sk_free(ossl_check_${nametype}_sk_type(sk)) #define sk_${nametype}_zero(sk) OPENSSL_sk_zero(ossl_check_${nametype}_sk_type(sk)) @@ -49,7 +70,11 @@ SKM_DEFINE_STACK_OF_INTERNAL(${nametype}, ${realtype}, ${plaintype}) #define sk_${nametype}_sort(sk) OPENSSL_sk_sort(ossl_check_${nametype}_sk_type(sk)) #define sk_${nametype}_is_sorted(sk) OPENSSL_sk_is_sorted(ossl_check_const_${nametype}_sk_type(sk)) #define sk_${nametype}_dup(sk) ((STACK_OF(${nametype}) *)OPENSSL_sk_dup(ossl_check_const_${nametype}_sk_type(sk))) -#define sk_${nametype}_deep_copy(sk, copyfunc, freefunc) ((STACK_OF(${nametype}) *)OPENSSL_sk_deep_copy(ossl_check_const_${nametype}_sk_type(sk), ossl_check_${nametype}_copyfunc_type(copyfunc), ossl_check_${nametype}_freefunc_type(freefunc))) +#define sk_${nametype}_deep_copy(sk, copyfunc, freefunc) \\ + ((STACK_OF(${nametype}) *)OPENSSL_sk_deep_copy( \\ + ossl_check_const_${nametype}_sk_type(sk), \\ + ossl_check_${nametype}_copyfunc_type(copyfunc), \\ + ossl_check_${nametype}_freefunc_type(freefunc))) #define sk_${nametype}_set_cmp_func(sk, cmp) ((sk_${nametype}_compfunc)OPENSSL_sk_set_cmp_func(ossl_check_${nametype}_sk_type(sk), ossl_check_${nametype}_compfunc_type(cmp))) END_MACROS From a1e4bd14e59e62943fe048b944eac894cd0d8489 Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Wed, 20 May 2026 08:51:07 +0200 Subject: [PATCH 050/349] preserve stack thunks across sk_TYPE_dup ensure typed stack duplicates keep their compare, copy, and free thunks so later operations do not call typed callbacks through erased void * function pointer signatures. Signed-off-by: Nikola Pajkovsky Reviewed-by: Neil Horman Reviewed-by: Milan Broz MergeDate: Thu Jun 25 12:12:48 2026 (Merged from https://github.com/openssl/openssl/pull/31523) --- include/openssl/safestack.h.in | 16 +++++- test/stack_test.c | 101 +++++++++++++++++++++++++++++++++ util/perl/OpenSSL/stackhash.pm | 23 ++++++-- 3 files changed, 133 insertions(+), 7 deletions(-) diff --git a/include/openssl/safestack.h.in b/include/openssl/safestack.h.in index a4c178d189..64ac28be04 100644 --- a/include/openssl/safestack.h.in +++ b/include/openssl/safestack.h.in @@ -222,15 +222,27 @@ extern "C" { } \ static ossl_unused ossl_inline STACK_OF(t1) *sk_##t1##_dup(const STACK_OF(t1) *sk) \ { \ - return (STACK_OF(t1) *)OPENSSL_sk_dup((const OPENSSL_STACK *)sk); \ + OPENSSL_STACK *ret = OPENSSL_sk_dup((const OPENSSL_STACK *)sk); \ + OPENSSL_sk_freefunc_thunk f_thunk; \ + \ + f_thunk = (OPENSSL_sk_freefunc_thunk)sk_##t1##_freefunc_thunk; \ + OPENSSL_sk_set_cmp_thunks(ret, sk_##t1##_cmpfunc_thunk); \ + OPENSSL_sk_set_copy_thunks(ret, sk_##t1##_copyfunc_thunk); \ + return (STACK_OF(t1) *)OPENSSL_sk_set_thunks(ret, f_thunk); \ } \ static ossl_unused ossl_inline STACK_OF(t1) *sk_##t1##_deep_copy(const STACK_OF(t1) *sk, \ sk_##t1##_copyfunc copyfunc, \ sk_##t1##_freefunc freefunc) \ { \ - return (STACK_OF(t1) *)OPENSSL_sk_deep_copy((const OPENSSL_STACK *)sk, \ + OPENSSL_STACK *ret = OPENSSL_sk_deep_copy((const OPENSSL_STACK *)sk, \ (OPENSSL_sk_copyfunc)copyfunc, \ (OPENSSL_sk_freefunc)freefunc); \ + OPENSSL_sk_freefunc_thunk f_thunk; \ + \ + f_thunk = (OPENSSL_sk_freefunc_thunk)sk_##t1##_freefunc_thunk; \ + OPENSSL_sk_set_cmp_thunks(ret, sk_##t1##_cmpfunc_thunk); \ + OPENSSL_sk_set_copy_thunks(ret, sk_##t1##_copyfunc_thunk); \ + return (STACK_OF(t1) *)OPENSSL_sk_set_thunks(ret, f_thunk); \ } \ static ossl_unused ossl_inline sk_##t1##_compfunc sk_##t1##_set_cmp_func(STACK_OF(t1) *sk, sk_##t1##_compfunc compare) \ { \ diff --git a/test/stack_test.c b/test/stack_test.c index 848c843931..16d72f809e 100644 --- a/test/stack_test.c +++ b/test/stack_test.c @@ -344,11 +344,35 @@ static char *string_copy(const char *p) return OPENSSL_strdup(p); } +static int string_cmp(const char *const *a, const char *const *b) +{ + return strcmp(*a, *b); +} + static void string_free(char *p) { OPENSSL_free(p); } +static int push_string(STACK_OF(OPENSSL_STRING) *s, const char *str) +{ + char *p = OPENSSL_strdup(str); + int expected = sk_OPENSSL_STRING_num(s) + 1; + int pushed; + + if (!TEST_ptr(p)) + return 0; + + pushed = sk_OPENSSL_STRING_push(s, p); + if (!TEST_int_eq(pushed, expected)) { + if (pushed <= 0) + OPENSSL_free(p); + return 0; + } + + return 1; +} + static int test_SS_stack(void) { STACK_OF(TST_SS) *s = sk_TST_SS_new_null(); @@ -508,11 +532,88 @@ end: return testresult; } +static int test_STRING_STACK_stack_ubsan(int idx) +{ + STACK_OF(OPENSSL_STRING) *s = NULL; + STACK_OF(OPENSSL_STRING) *t = NULL; + STACK_OF(OPENSSL_STRING) *u = NULL; + int testresult = 0; + + switch (idx) { + case 0: + s = sk_OPENSSL_STRING_new_null(); + break; + case 1: + s = sk_OPENSSL_STRING_new(string_cmp); + break; + case 2: + s = sk_OPENSSL_STRING_new_reserve(string_cmp, 2); + break; + case 3: + s = sk_OPENSSL_STRING_deep_copy(NULL, string_copy, string_free); + break; + case 4: + t = sk_OPENSSL_STRING_new(string_cmp); + if (!TEST_ptr(t)) + goto end; + if (!push_string(t, "b") + || !push_string(t, "a")) + goto end; + s = sk_OPENSSL_STRING_dup(t); + if (s != NULL) { + sk_OPENSSL_STRING_free(t); + t = NULL; + } + break; + case 5: + t = sk_OPENSSL_STRING_deep_copy(NULL, string_copy, string_free); + if (!TEST_ptr(t)) + goto end; + if (!push_string(t, "b") + || !push_string(t, "a")) + goto end; + s = sk_OPENSSL_STRING_deep_copy(t, string_copy, string_free); + break; + default: + goto end; + } + if (!TEST_ptr(s)) + goto end; + + if (idx < 4) { + if (!push_string(s, "b") + || !push_string(s, "a")) + goto end; + } + + sk_OPENSSL_STRING_set_cmp_func(s, string_cmp); + sk_OPENSSL_STRING_sort(s); + if (!TEST_str_eq(sk_OPENSSL_STRING_value(s, 0), "a") + || !TEST_int_eq(sk_OPENSSL_STRING_find(s, "b"), 1)) + goto end; + + u = sk_OPENSSL_STRING_deep_copy(s, string_copy, string_free); + if (!TEST_ptr(u) + || !TEST_int_eq(sk_OPENSSL_STRING_num(u), 2) + || !TEST_ptr_ne(sk_OPENSSL_STRING_value(u, 0), + sk_OPENSSL_STRING_value(s, 0)) + || !TEST_str_eq(sk_OPENSSL_STRING_value(u, 0), "a")) + goto end; + + testresult = 1; +end: + sk_OPENSSL_STRING_pop_free(u, string_free); + sk_OPENSSL_STRING_pop_free(s, string_free); + sk_OPENSSL_STRING_pop_free(t, string_free); + return testresult; +} + int setup_tests(void) { ADD_ALL_TESTS(test_int_stack, 4); ADD_ALL_TESTS(test_uchar_stack, 4); ADD_TEST(test_SS_stack); + ADD_ALL_TESTS(test_STRING_STACK_stack_ubsan, 6); ADD_TEST(test_SU_stack); ADD_MFAIL_TEST(test_OPENSSL_STRING_deep_copy_mfail); return 1; diff --git a/util/perl/OpenSSL/stackhash.pm b/util/perl/OpenSSL/stackhash.pm index e49e2d4def..b3446f7747 100644 --- a/util/perl/OpenSSL/stackhash.pm +++ b/util/perl/OpenSSL/stackhash.pm @@ -69,12 +69,25 @@ SKM_DEFINE_STACK_OF_INTERNAL(${nametype}, ${realtype}, ${plaintype}) #define sk_${nametype}_find_all(sk, ptr, pnum) OPENSSL_sk_find_all(ossl_check_${nametype}_sk_type(sk), ossl_check_${nametype}_type(ptr), pnum) #define sk_${nametype}_sort(sk) OPENSSL_sk_sort(ossl_check_${nametype}_sk_type(sk)) #define sk_${nametype}_is_sorted(sk) OPENSSL_sk_is_sorted(ossl_check_const_${nametype}_sk_type(sk)) -#define sk_${nametype}_dup(sk) ((STACK_OF(${nametype}) *)OPENSSL_sk_dup(ossl_check_const_${nametype}_sk_type(sk))) +#define sk_${nametype}_dup(sk) \\ + ((STACK_OF(${nametype}) *)OPENSSL_sk_set_thunks( \\ + OPENSSL_sk_set_copy_thunks( \\ + OPENSSL_sk_set_cmp_thunks( \\ + OPENSSL_sk_dup(ossl_check_const_${nametype}_sk_type(sk)), \\ + sk_${nametype}_cmpfunc_thunk), \\ + sk_${nametype}_copyfunc_thunk), \\ + sk_${nametype}_freefunc_thunk)) #define sk_${nametype}_deep_copy(sk, copyfunc, freefunc) \\ - ((STACK_OF(${nametype}) *)OPENSSL_sk_deep_copy( \\ - ossl_check_const_${nametype}_sk_type(sk), \\ - ossl_check_${nametype}_copyfunc_type(copyfunc), \\ - ossl_check_${nametype}_freefunc_type(freefunc))) + ((STACK_OF(${nametype}) *)OPENSSL_sk_set_thunks( \\ + OPENSSL_sk_set_copy_thunks( \\ + OPENSSL_sk_set_cmp_thunks( \\ + OPENSSL_sk_deep_copy( \\ + ossl_check_const_${nametype}_sk_type(sk), \\ + ossl_check_${nametype}_copyfunc_type(copyfunc), \\ + ossl_check_${nametype}_freefunc_type(freefunc)), \\ + sk_${nametype}_cmpfunc_thunk), \\ + sk_${nametype}_copyfunc_thunk), \\ + sk_${nametype}_freefunc_thunk)) #define sk_${nametype}_set_cmp_func(sk, cmp) ((sk_${nametype}_compfunc)OPENSSL_sk_set_cmp_func(ossl_check_${nametype}_sk_type(sk), ossl_check_${nametype}_compfunc_type(cmp))) END_MACROS From b3003ff1ecac4118d5779f4917c684d83c9c091a Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Tue, 19 May 2026 09:36:49 +0200 Subject: [PATCH 051/349] fix function pointer type mismatch in OCSP_REQUEST/RESPONSE bio macros d2i_OCSP_REQUEST/d2i_OCSP_RESPONSE are declared with type-specific signatures returning OCSP_REQUEST*/OCSP_RESPONSE*, but the d2i_OCSP_{REQUEST,RESPONSE}_bio macros routed them through ASN1_d2i_bio_of -> ASN1_d2i_bio, which calls them via a d2i_of_void* pointer: void *(*)(void **, const unsigned char **, long) The analogous i2d_OCSP_{REQUEST,RESPONSE}_bio macros did the same through ASN1_i2d_bio_of -> ASN1_i2d_bio with i2d_of_void*. Rewrite the macros to dispatch through ASN1_item_d2i_bio/ASN1_item_i2d_bio with ASN1_ITEM_rptr(OCSP_*) directly. This eliminates the function-pointer cast entirely; both old and new paths bottom out in the same ASN1_item_d2i_ex / ASN1_item_i2d call, so behavior is identical. Signed-off-by: Nikola Pajkovsky Reviewed-by: Neil Horman Reviewed-by: Milan Broz MergeDate: Thu Jun 25 12:12:50 2026 (Merged from https://github.com/openssl/openssl/pull/31523) --- include/openssl/ocsp.h.in | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/include/openssl/ocsp.h.in b/include/openssl/ocsp.h.in index 86ed7f5869..ce33a4d8fc 100644 --- a/include/openssl/ocsp.h.in +++ b/include/openssl/ocsp.h.in @@ -144,9 +144,13 @@ typedef struct ocsp_service_locator_st OCSP_SERVICELOC; #define PEM_STRING_OCSP_REQUEST "OCSP REQUEST" #define PEM_STRING_OCSP_RESPONSE "OCSP RESPONSE" -#define d2i_OCSP_REQUEST_bio(bp, p) ASN1_d2i_bio_of(OCSP_REQUEST, OCSP_REQUEST_new, d2i_OCSP_REQUEST, bp, p) +#define d2i_OCSP_REQUEST_bio(bp, p) \ + ((OCSP_REQUEST *)ASN1_item_d2i_bio(ASN1_ITEM_rptr(OCSP_REQUEST), \ + (bp), CHECKED_PPTR_OF(OCSP_REQUEST, p))) -#define d2i_OCSP_RESPONSE_bio(bp, p) ASN1_d2i_bio_of(OCSP_RESPONSE, OCSP_RESPONSE_new, d2i_OCSP_RESPONSE, bp, p) +#define d2i_OCSP_RESPONSE_bio(bp, p) \ + ((OCSP_RESPONSE *)ASN1_item_d2i_bio(ASN1_ITEM_rptr(OCSP_RESPONSE), \ + (bp), CHECKED_PPTR_OF(OCSP_RESPONSE, p))) #define PEM_read_bio_OCSP_REQUEST(bp, x, cb) (OCSP_REQUEST *)PEM_ASN1_read_bio( \ (d2i_of_void *)d2i_OCSP_REQUEST, PEM_STRING_OCSP_REQUEST, \ @@ -164,9 +168,13 @@ typedef struct ocsp_service_locator_st OCSP_SERVICELOC; PEM_ASN1_write_bio((i2d_of_void *)i2d_OCSP_RESPONSE, PEM_STRING_OCSP_RESPONSE, \ bp, (char *)(o), NULL, NULL, 0, NULL, NULL) -#define i2d_OCSP_RESPONSE_bio(bp, o) ASN1_i2d_bio_of(OCSP_RESPONSE, i2d_OCSP_RESPONSE, bp, o) +#define i2d_OCSP_RESPONSE_bio(bp, o) \ + ASN1_item_i2d_bio(ASN1_ITEM_rptr(OCSP_RESPONSE), \ + (bp), CHECKED_PTR_OF(const OCSP_RESPONSE, o)) -#define i2d_OCSP_REQUEST_bio(bp, o) ASN1_i2d_bio_of(OCSP_REQUEST, i2d_OCSP_REQUEST, bp, o) +#define i2d_OCSP_REQUEST_bio(bp, o) \ + ASN1_item_i2d_bio(ASN1_ITEM_rptr(OCSP_REQUEST), \ + (bp), CHECKED_PTR_OF(const OCSP_REQUEST, o)) #define ASN1_BIT_STRING_digest(data, type, md, len) \ ASN1_item_digest(ASN1_ITEM_rptr(ASN1_BIT_STRING), type, data, md, len) From 0e8f2844ed3e6c8fde0e5da0db6322735eb6593f Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Tue, 19 May 2026 10:50:02 +0200 Subject: [PATCH 052/349] fix function pointer type mismatch in PEM_X509_INFO_read_bio_ex PEM_X509_INFO_read_bio_ex stored d2i_X509, d2i_X509_AUX, d2i_X509_CRL or d2i_AutoPrivateKey -- each with a distinct, type-specific signature returning X509*, X509_CRL* or EVP_PKEY* -- in a single d2i_of_void* variable and dispatched through it: d2i = (D2I_OF(void))d2i_X509; ... d2i(pp, &p, len) Replace the function-pointer dispatch with an enum tag (pem_info_type) and a switch that calls each typed d2i_* directly with the correctly type. Signed-off-by: Nikola Pajkovsky Reviewed-by: Neil Horman Reviewed-by: Milan Broz MergeDate: Thu Jun 25 12:12:53 2026 (Merged from https://github.com/openssl/openssl/pull/31523) --- crypto/pem/pem_info.c | 52 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/crypto/pem/pem_info.c b/crypto/pem/pem_info.c index 8f38fe0580..d9bbb7256e 100644 --- a/crypto/pem/pem_info.c +++ b/crypto/pem/pem_info.c @@ -24,6 +24,14 @@ #include #include "crypto/evp.h" +typedef enum { + PEM_INFO_NONE, + PEM_INFO_X509, + PEM_INFO_X509_AUX, + PEM_INFO_X509_CRL, + PEM_INFO_PKEY +} pem_info_type; + #ifndef OPENSSL_NO_STDIO STACK_OF(X509_INFO) *PEM_X509_INFO_read_ex(FILE *fp, STACK_OF(X509_INFO) *sk, pem_password_cb *cb, @@ -63,7 +71,7 @@ STACK_OF(X509_INFO) *PEM_X509_INFO_read_bio_ex(BIO *bp, STACK_OF(X509_INFO) *sk, int ok = 0; STACK_OF(X509_INFO) *ret = NULL; unsigned int i, raw, ptype; - d2i_of_void *d2i = 0; + pem_info_type itype = PEM_INFO_NONE; if (sk == NULL) { if ((ret = sk_X509_INFO_new_null()) == NULL) { @@ -78,6 +86,7 @@ STACK_OF(X509_INFO) *PEM_X509_INFO_read_bio_ex(BIO *bp, STACK_OF(X509_INFO) *sk, for (;;) { raw = 0; ptype = 0; + itype = PEM_INFO_NONE; ERR_set_mark(); i = PEM_read_bio(bp, &name, &header, &data, &len); if (i == 0) { @@ -102,15 +111,15 @@ STACK_OF(X509_INFO) *PEM_X509_INFO_read_bio_ex(BIO *bp, STACK_OF(X509_INFO) *sk, goto start; } if ((strcmp(name, PEM_STRING_X509_TRUSTED) == 0)) - d2i = (D2I_OF(void))d2i_X509_AUX; + itype = PEM_INFO_X509_AUX; else - d2i = (D2I_OF(void))d2i_X509; + itype = PEM_INFO_X509; xi->x509 = X509_new_ex(libctx, propq); if (xi->x509 == NULL) goto err; pp = &(xi->x509); } else if (strcmp(name, PEM_STRING_X509_CRL) == 0) { - d2i = (D2I_OF(void))d2i_X509_CRL; + itype = PEM_INFO_X509_CRL; if (xi->crl != NULL) { if (!sk_X509_INFO_push(ret, xi)) goto err; @@ -137,7 +146,7 @@ STACK_OF(X509_INFO) *PEM_X509_INFO_read_bio_ex(BIO *bp, STACK_OF(X509_INFO) *sk, xi->enc_data = NULL; xi->enc_len = 0; - d2i = (D2I_OF(void))d2i_AutoPrivateKey; + itype = PEM_INFO_PKEY; xi->x_pkey = X509_PKEY_new(); if (xi->x_pkey == NULL) goto err; @@ -146,11 +155,11 @@ STACK_OF(X509_INFO) *PEM_X509_INFO_read_bio_ex(BIO *bp, STACK_OF(X509_INFO) *sk, || strcmp(name, PEM_STRING_PKCS8) == 0) raw = 1; } else { /* unknown */ - d2i = NULL; + itype = PEM_INFO_NONE; pp = NULL; } - if (d2i != NULL) { + if (itype != PEM_INFO_NONE) { if (!raw) { EVP_CIPHER_INFO cipher; @@ -160,15 +169,36 @@ STACK_OF(X509_INFO) *PEM_X509_INFO_read_bio_ex(BIO *bp, STACK_OF(X509_INFO) *sk, goto err; p = data; if (ptype) { - if (d2i_PrivateKey_ex(ptype, pp, &p, len, + if (d2i_PrivateKey_ex(ptype, (EVP_PKEY **)pp, &p, len, libctx, propq) == NULL) { ERR_raise(ERR_LIB_PEM, ERR_R_ASN1_LIB); goto err; } - } else if (d2i(pp, &p, len) == NULL) { - ERR_raise(ERR_LIB_PEM, ERR_R_ASN1_LIB); - goto err; + } else { + void *decoded = NULL; + + switch (itype) { + case PEM_INFO_X509: + decoded = d2i_X509((X509 **)pp, &p, len); + break; + case PEM_INFO_X509_AUX: + decoded = d2i_X509_AUX((X509 **)pp, &p, len); + break; + case PEM_INFO_X509_CRL: + decoded = d2i_X509_CRL((X509_CRL **)pp, &p, len); + break; + case PEM_INFO_PKEY: + decoded = d2i_AutoPrivateKey_ex((EVP_PKEY **)pp, &p, + len, libctx, propq); + break; + case PEM_INFO_NONE: + break; + } + if (decoded == NULL) { + ERR_raise(ERR_LIB_PEM, ERR_R_ASN1_LIB); + goto err; + } } } else { /* encrypted key data */ if (!PEM_get_EVP_CIPHER_INFO(header, &xi->enc_cipher)) From ba15a13ffeca1e8b1a3042bdd383c3d45e7906d1 Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Thu, 21 May 2026 07:14:07 +0200 Subject: [PATCH 053/349] decode_der2key: wrap key-free callbacks to avoid function pointer casts Signed-off-by: Nikola Pajkovsky Reviewed-by: Neil Horman Reviewed-by: Milan Broz MergeDate: Thu Jun 25 12:12:55 2026 (Merged from https://github.com/openssl/openssl/pull/31523) --- .../encode_decode/decode_der2key.c | 105 +++++++++++++----- 1 file changed, 75 insertions(+), 30 deletions(-) diff --git a/providers/implementations/encode_decode/decode_der2key.c b/providers/implementations/encode_decode/decode_der2key.c index 964a86ff17..6ed99e68fa 100644 --- a/providers/implementations/encode_decode/decode_der2key.c +++ b/providers/implementations/encode_decode/decode_der2key.c @@ -404,11 +404,16 @@ static int der2key_export_object(void *vctx, /* ---------------------------------------------------------------------- */ #ifndef OPENSSL_NO_DH +static void dh_free_key(void *key) +{ + DH_free(key); +} + #define dh_evp_type EVP_PKEY_DH #define dh_d2i_private_key NULL #define dh_d2i_public_key NULL #define dh_d2i_key_params (d2i_of_void *)d2i_DHparams -#define dh_free (free_key_fn *)DH_free +#define dh_free dh_free_key #define dh_check NULL static void *dh_d2i_PKCS8(const unsigned char **der, long der_len, @@ -431,7 +436,7 @@ static void dh_adjust(void *key, struct der2key_ctx_st *ctx) #define dhx_d2i_public_key NULL #define dhx_d2i_key_params (d2i_of_void *)d2i_DHxparams #define dhx_d2i_PKCS8 dh_d2i_PKCS8 -#define dhx_free (free_key_fn *)DH_free +#define dhx_free dh_free_key #define dhx_check NULL #define dhx_adjust dh_adjust #endif @@ -439,11 +444,16 @@ static void dh_adjust(void *key, struct der2key_ctx_st *ctx) /* ---------------------------------------------------------------------- */ #ifndef OPENSSL_NO_DSA +static void dsa_free_key(void *key) +{ + DSA_free(key); +} + #define dsa_evp_type EVP_PKEY_DSA #define dsa_d2i_private_key (d2i_of_void *)d2i_DSAPrivateKey #define dsa_d2i_public_key (d2i_of_void *)d2i_DSAPublicKey #define dsa_d2i_key_params (d2i_of_void *)d2i_DSAparams -#define dsa_free (free_key_fn *)DSA_free +#define dsa_free dsa_free_key #define dsa_check NULL static void *dsa_d2i_PKCS8(const unsigned char **der, long der_len, @@ -464,11 +474,16 @@ static void dsa_adjust(void *key, struct der2key_ctx_st *ctx) /* ---------------------------------------------------------------------- */ #ifndef OPENSSL_NO_EC +static void ec_free_key(void *key) +{ + EC_KEY_free(key); +} + #define ec_evp_type EVP_PKEY_EC #define ec_d2i_private_key (d2i_of_void *)d2i_ECPrivateKey #define ec_d2i_public_key NULL #define ec_d2i_key_params (d2i_of_void *)d2i_ECParameters -#define ec_free (free_key_fn *)EC_KEY_free +#define ec_free ec_free_key static void *ec_d2i_PKCS8(const unsigned char **der, long der_len, struct der2key_ctx_st *ctx) @@ -522,12 +537,17 @@ static void ecx_key_adjust(void *key, struct der2key_ctx_st *ctx) ossl_ecx_key_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx)); } +static void ecx_free_key(void *key) +{ + ossl_ecx_key_free(key); +} + #define ed25519_evp_type EVP_PKEY_ED25519 #define ed25519_d2i_private_key NULL #define ed25519_d2i_public_key NULL #define ed25519_d2i_key_params NULL #define ed25519_d2i_PKCS8 ecx_d2i_PKCS8 -#define ed25519_free (free_key_fn *)ossl_ecx_key_free +#define ed25519_free ecx_free_key #define ed25519_check NULL #define ed25519_adjust ecx_key_adjust @@ -536,7 +556,7 @@ static void ecx_key_adjust(void *key, struct der2key_ctx_st *ctx) #define ed448_d2i_public_key NULL #define ed448_d2i_key_params NULL #define ed448_d2i_PKCS8 ecx_d2i_PKCS8 -#define ed448_free (free_key_fn *)ossl_ecx_key_free +#define ed448_free ecx_free_key #define ed448_check NULL #define ed448_adjust ecx_key_adjust @@ -545,7 +565,7 @@ static void ecx_key_adjust(void *key, struct der2key_ctx_st *ctx) #define x25519_d2i_public_key NULL #define x25519_d2i_key_params NULL #define x25519_d2i_PKCS8 ecx_d2i_PKCS8 -#define x25519_free (free_key_fn *)ossl_ecx_key_free +#define x25519_free ecx_free_key #define x25519_check NULL #define x25519_adjust ecx_key_adjust @@ -554,7 +574,7 @@ static void ecx_key_adjust(void *key, struct der2key_ctx_st *ctx) #define x448_d2i_public_key NULL #define x448_d2i_key_params NULL #define x448_d2i_PKCS8 ecx_d2i_PKCS8 -#define x448_free (free_key_fn *)ossl_ecx_key_free +#define x448_free ecx_free_key #define x448_check NULL #define x448_adjust ecx_key_adjust #endif /* OPENSSL_NO_ECX */ @@ -565,7 +585,7 @@ static void ecx_key_adjust(void *key, struct der2key_ctx_st *ctx) #define sm2_d2i_public_key NULL #define sm2_d2i_key_params (d2i_of_void *)d2i_ECParameters #define sm2_d2i_PUBKEY ec_d2i_PUBKEY -#define sm2_free (free_key_fn *)EC_KEY_free +#define sm2_free ec_free_key #define sm2_check ec_check #define sm2_adjust ec_adjust @@ -607,13 +627,18 @@ ml_kem_d2i_PUBKEY(const uint8_t **der, long der_len, return key; } +static void ml_kem_free_key(void *key) +{ + ossl_ml_kem_key_free(key); +} + #define ml_kem_512_evp_type EVP_PKEY_ML_KEM_512 #define ml_kem_512_d2i_private_key NULL #define ml_kem_512_d2i_public_key NULL #define ml_kem_512_d2i_key_params NULL #define ml_kem_512_d2i_PUBKEY ml_kem_d2i_PUBKEY #define ml_kem_512_d2i_PKCS8 ml_kem_d2i_PKCS8 -#define ml_kem_512_free (free_key_fn *)ossl_ml_kem_key_free +#define ml_kem_512_free ml_kem_free_key #define ml_kem_512_check NULL #define ml_kem_512_adjust NULL @@ -623,7 +648,7 @@ ml_kem_d2i_PUBKEY(const uint8_t **der, long der_len, #define ml_kem_768_d2i_key_params NULL #define ml_kem_768_d2i_PUBKEY ml_kem_d2i_PUBKEY #define ml_kem_768_d2i_PKCS8 ml_kem_d2i_PKCS8 -#define ml_kem_768_free (free_key_fn *)ossl_ml_kem_key_free +#define ml_kem_768_free ml_kem_free_key #define ml_kem_768_check NULL #define ml_kem_768_adjust NULL @@ -633,13 +658,18 @@ ml_kem_d2i_PUBKEY(const uint8_t **der, long der_len, #define ml_kem_1024_d2i_PUBKEY ml_kem_d2i_PUBKEY #define ml_kem_1024_d2i_PKCS8 ml_kem_d2i_PKCS8 #define ml_kem_1024_d2i_key_params NULL -#define ml_kem_1024_free (free_key_fn *)ossl_ml_kem_key_free +#define ml_kem_1024_free ml_kem_free_key #define ml_kem_1024_check NULL #define ml_kem_1024_adjust NULL #endif #ifndef OPENSSL_NO_SLH_DSA +static void slh_dsa_free_key(void *key) +{ + ossl_slh_dsa_key_free(key); +} + static void * slh_dsa_d2i_PKCS8(const uint8_t **der, long der_len, struct der2key_ctx_st *ctx) { @@ -768,7 +798,7 @@ err: #define slh_dsa_sha2_128s_d2i_key_params NULL #define slh_dsa_sha2_128s_d2i_PKCS8 slh_dsa_d2i_PKCS8 #define slh_dsa_sha2_128s_d2i_PUBKEY slh_dsa_d2i_PUBKEY -#define slh_dsa_sha2_128s_free (free_key_fn *)ossl_slh_dsa_key_free +#define slh_dsa_sha2_128s_free slh_dsa_free_key #define slh_dsa_sha2_128s_check NULL #define slh_dsa_sha2_128s_adjust NULL @@ -778,7 +808,7 @@ err: #define slh_dsa_sha2_128f_d2i_key_params NULL #define slh_dsa_sha2_128f_d2i_PKCS8 slh_dsa_d2i_PKCS8 #define slh_dsa_sha2_128f_d2i_PUBKEY slh_dsa_d2i_PUBKEY -#define slh_dsa_sha2_128f_free (free_key_fn *)ossl_slh_dsa_key_free +#define slh_dsa_sha2_128f_free slh_dsa_free_key #define slh_dsa_sha2_128f_check NULL #define slh_dsa_sha2_128f_adjust NULL @@ -788,7 +818,7 @@ err: #define slh_dsa_sha2_192s_d2i_key_params NULL #define slh_dsa_sha2_192s_d2i_PKCS8 slh_dsa_d2i_PKCS8 #define slh_dsa_sha2_192s_d2i_PUBKEY slh_dsa_d2i_PUBKEY -#define slh_dsa_sha2_192s_free (free_key_fn *)ossl_slh_dsa_key_free +#define slh_dsa_sha2_192s_free slh_dsa_free_key #define slh_dsa_sha2_192s_check NULL #define slh_dsa_sha2_192s_adjust NULL @@ -798,7 +828,7 @@ err: #define slh_dsa_sha2_192f_d2i_key_params NULL #define slh_dsa_sha2_192f_d2i_PKCS8 slh_dsa_d2i_PKCS8 #define slh_dsa_sha2_192f_d2i_PUBKEY slh_dsa_d2i_PUBKEY -#define slh_dsa_sha2_192f_free (free_key_fn *)ossl_slh_dsa_key_free +#define slh_dsa_sha2_192f_free slh_dsa_free_key #define slh_dsa_sha2_192f_check NULL #define slh_dsa_sha2_192f_adjust NULL @@ -808,7 +838,7 @@ err: #define slh_dsa_sha2_256s_d2i_key_params NULL #define slh_dsa_sha2_256s_d2i_PKCS8 slh_dsa_d2i_PKCS8 #define slh_dsa_sha2_256s_d2i_PUBKEY slh_dsa_d2i_PUBKEY -#define slh_dsa_sha2_256s_free (free_key_fn *)ossl_slh_dsa_key_free +#define slh_dsa_sha2_256s_free slh_dsa_free_key #define slh_dsa_sha2_256s_check NULL #define slh_dsa_sha2_256s_adjust NULL @@ -818,7 +848,7 @@ err: #define slh_dsa_sha2_256f_d2i_key_params NULL #define slh_dsa_sha2_256f_d2i_PKCS8 slh_dsa_d2i_PKCS8 #define slh_dsa_sha2_256f_d2i_PUBKEY slh_dsa_d2i_PUBKEY -#define slh_dsa_sha2_256f_free (free_key_fn *)ossl_slh_dsa_key_free +#define slh_dsa_sha2_256f_free slh_dsa_free_key #define slh_dsa_sha2_256f_check NULL #define slh_dsa_sha2_256f_adjust NULL @@ -828,7 +858,7 @@ err: #define slh_dsa_shake_128s_d2i_key_params NULL #define slh_dsa_shake_128s_d2i_PKCS8 slh_dsa_d2i_PKCS8 #define slh_dsa_shake_128s_d2i_PUBKEY slh_dsa_d2i_PUBKEY -#define slh_dsa_shake_128s_free (free_key_fn *)ossl_slh_dsa_key_free +#define slh_dsa_shake_128s_free slh_dsa_free_key #define slh_dsa_shake_128s_check NULL #define slh_dsa_shake_128s_adjust NULL @@ -838,7 +868,7 @@ err: #define slh_dsa_shake_128f_d2i_key_params NULL #define slh_dsa_shake_128f_d2i_PKCS8 slh_dsa_d2i_PKCS8 #define slh_dsa_shake_128f_d2i_PUBKEY slh_dsa_d2i_PUBKEY -#define slh_dsa_shake_128f_free (free_key_fn *)ossl_slh_dsa_key_free +#define slh_dsa_shake_128f_free slh_dsa_free_key #define slh_dsa_shake_128f_check NULL #define slh_dsa_shake_128f_adjust NULL @@ -848,7 +878,7 @@ err: #define slh_dsa_shake_192s_d2i_key_params NULL #define slh_dsa_shake_192s_d2i_PKCS8 slh_dsa_d2i_PKCS8 #define slh_dsa_shake_192s_d2i_PUBKEY slh_dsa_d2i_PUBKEY -#define slh_dsa_shake_192s_free (free_key_fn *)ossl_slh_dsa_key_free +#define slh_dsa_shake_192s_free slh_dsa_free_key #define slh_dsa_shake_192s_check NULL #define slh_dsa_shake_192s_adjust NULL @@ -858,7 +888,7 @@ err: #define slh_dsa_shake_192f_d2i_key_params NULL #define slh_dsa_shake_192f_d2i_PKCS8 slh_dsa_d2i_PKCS8 #define slh_dsa_shake_192f_d2i_PUBKEY slh_dsa_d2i_PUBKEY -#define slh_dsa_shake_192f_free (free_key_fn *)ossl_slh_dsa_key_free +#define slh_dsa_shake_192f_free slh_dsa_free_key #define slh_dsa_shake_192f_check NULL #define slh_dsa_shake_192f_adjust NULL @@ -868,7 +898,7 @@ err: #define slh_dsa_shake_256s_d2i_key_params NULL #define slh_dsa_shake_256s_d2i_PKCS8 slh_dsa_d2i_PKCS8 #define slh_dsa_shake_256s_d2i_PUBKEY slh_dsa_d2i_PUBKEY -#define slh_dsa_shake_256s_free (free_key_fn *)ossl_slh_dsa_key_free +#define slh_dsa_shake_256s_free slh_dsa_free_key #define slh_dsa_shake_256s_check NULL #define slh_dsa_shake_256s_adjust NULL @@ -878,18 +908,23 @@ err: #define slh_dsa_shake_256f_d2i_key_params NULL #define slh_dsa_shake_256f_d2i_PKCS8 slh_dsa_d2i_PKCS8 #define slh_dsa_shake_256f_d2i_PUBKEY slh_dsa_d2i_PUBKEY -#define slh_dsa_shake_256f_free (free_key_fn *)ossl_slh_dsa_key_free +#define slh_dsa_shake_256f_free slh_dsa_free_key #define slh_dsa_shake_256f_check NULL #define slh_dsa_shake_256f_adjust NULL #endif /* OPENSSL_NO_SLH_DSA */ /* ---------------------------------------------------------------------- */ +static void rsa_free_key(void *key) +{ + RSA_free(key); +} + #define rsa_evp_type EVP_PKEY_RSA #define rsa_d2i_private_key (d2i_of_void *)d2i_RSAPrivateKey #define rsa_d2i_public_key (d2i_of_void *)d2i_RSAPublicKey #define rsa_d2i_key_params NULL -#define rsa_free (free_key_fn *)RSA_free +#define rsa_free rsa_free_key static void *rsa_d2i_PKCS8(const unsigned char **der, long der_len, struct der2key_ctx_st *ctx) @@ -937,13 +972,18 @@ static void rsa_adjust(void *key, struct der2key_ctx_st *ctx) #define rsapss_d2i_key_params NULL #define rsapss_d2i_PKCS8 rsa_d2i_PKCS8 #define rsapss_d2i_PUBKEY rsa_d2i_PUBKEY -#define rsapss_free (free_key_fn *)RSA_free +#define rsapss_free rsa_free_key #define rsapss_check rsa_check #define rsapss_adjust rsa_adjust /* ---------------------------------------------------------------------- */ #ifndef OPENSSL_NO_ML_DSA +static void ml_dsa_free_key(void *key) +{ + ossl_ml_dsa_key_free(key); +} + static void * ml_dsa_d2i_PKCS8(const uint8_t **der, long der_len, struct der2key_ctx_st *ctx) { @@ -974,7 +1014,7 @@ static ossl_inline void *ml_dsa_d2i_PUBKEY(const uint8_t **der, long der_len, #define ml_dsa_44_d2i_key_params NULL #define ml_dsa_44_d2i_PUBKEY ml_dsa_d2i_PUBKEY #define ml_dsa_44_d2i_PKCS8 ml_dsa_d2i_PKCS8 -#define ml_dsa_44_free (free_key_fn *)ossl_ml_dsa_key_free +#define ml_dsa_44_free ml_dsa_free_key #define ml_dsa_44_check NULL #define ml_dsa_44_adjust NULL @@ -984,7 +1024,7 @@ static ossl_inline void *ml_dsa_d2i_PUBKEY(const uint8_t **der, long der_len, #define ml_dsa_65_d2i_key_params NULL #define ml_dsa_65_d2i_PUBKEY ml_dsa_d2i_PUBKEY #define ml_dsa_65_d2i_PKCS8 ml_dsa_d2i_PKCS8 -#define ml_dsa_65_free (free_key_fn *)ossl_ml_dsa_key_free +#define ml_dsa_65_free ml_dsa_free_key #define ml_dsa_65_check NULL #define ml_dsa_65_adjust NULL @@ -994,7 +1034,7 @@ static ossl_inline void *ml_dsa_d2i_PUBKEY(const uint8_t **der, long der_len, #define ml_dsa_87_d2i_PUBKEY ml_dsa_d2i_PUBKEY #define ml_dsa_87_d2i_PKCS8 ml_dsa_d2i_PKCS8 #define ml_dsa_87_d2i_key_params NULL -#define ml_dsa_87_free (free_key_fn *)ossl_ml_dsa_key_free +#define ml_dsa_87_free ml_dsa_free_key #define ml_dsa_87_check NULL #define ml_dsa_87_adjust NULL @@ -1003,8 +1043,13 @@ static ossl_inline void *ml_dsa_d2i_PUBKEY(const uint8_t **der, long der_len, /* ---------------------------------------------------------------------- */ #ifndef OPENSSL_NO_LMS +static void lms_free_key(void *key) +{ + ossl_lms_key_free(key); +} + #define lms_evp_type EVP_PKEY_HSS_LMS -#define lms_free (free_key_fn *)ossl_lms_key_free +#define lms_free lms_free_key #define lms_check NULL #define lms_adjust NULL From f6efce32a024298a1b3d87ec4914d49bcab34366 Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Tue, 19 May 2026 11:39:41 +0200 Subject: [PATCH 054/349] avoid mismatched d2i callbacks in PEM readers decode PEM payloads through the generated typed d2i_* functions in the PEM read macros instead of routing them through the d2i_of_void callback path. This avoids UBSan function type mismatch reports while preserving the public PEM reader APIs. Signed-off-by: Nikola Pajkovsky Reviewed-by: Neil Horman Reviewed-by: Milan Broz MergeDate: Thu Jun 25 12:12:57 2026 (Merged from https://github.com/openssl/openssl/pull/31523) --- include/openssl/pem.h | 46 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/include/openssl/pem.h b/include/openssl/pem.h index ccde1b93d5..115c8f2733 100644 --- a/include/openssl/pem.h +++ b/include/openssl/pem.h @@ -117,8 +117,26 @@ extern "C" { #define IMPLEMENT_PEM_read_fp(name, type, str, asn1) \ type *PEM_read_##name(FILE *fp, type **x, pem_password_cb *cb, void *u) \ { \ - return PEM_ASN1_read((d2i_of_void *)d2i_##asn1, str, fp, \ - (void **)x, cb, u); \ + BIO *b; \ + const unsigned char *p = NULL; \ + unsigned char *data = NULL; \ + long len; \ + type *ret = NULL; \ + \ + if ((b = BIO_new(BIO_s_file())) == NULL) { \ + ERR_raise(ERR_LIB_PEM, ERR_R_BUF_LIB); \ + return NULL; \ + } \ + BIO_set_fp(b, fp, BIO_NOCLOSE); \ + if (PEM_bytes_read_bio(&data, &len, NULL, str, b, cb, u)) { \ + p = data; \ + ret = d2i_##asn1(x, &p, len); \ + if (ret == NULL) \ + ERR_raise(ERR_LIB_PEM, ERR_R_ASN1_LIB); \ + } \ + BIO_free(b); \ + OPENSSL_free(data); \ + return ret; \ } #define IMPLEMENT_PEM_write_fp(name, type, str, asn1) \ @@ -146,12 +164,24 @@ extern "C" { #endif #endif -#define IMPLEMENT_PEM_read_bio(name, type, str, asn1) \ - type *PEM_read_bio_##name(BIO *bp, type **x, \ - pem_password_cb *cb, void *u) \ - { \ - return PEM_ASN1_read_bio((d2i_of_void *)d2i_##asn1, str, bp, \ - (void **)x, cb, u); \ +#define IMPLEMENT_PEM_read_bio(name, type, str, asn1) \ + type *PEM_read_bio_##name(BIO *bp, type **x, \ + pem_password_cb *cb, void *u) \ + { \ + const unsigned char *p = NULL; \ + unsigned char *data = NULL; \ + long len; \ + type *ret = NULL; \ + \ + if (!PEM_bytes_read_bio(&data, &len, NULL, str, \ + bp, cb, u)) \ + return NULL; \ + p = data; \ + ret = d2i_##asn1(x, &p, len); \ + if (ret == NULL) \ + ERR_raise(ERR_LIB_PEM, ERR_R_ASN1_LIB); \ + OPENSSL_free(data); \ + return ret; \ } #define IMPLEMENT_PEM_write_bio(name, type, str, asn1) \ From ad47ffd79af936d06e7978b9c2984a2a33845135 Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Wed, 20 May 2026 08:19:46 +0200 Subject: [PATCH 055/349] avoid d2i_of_void asn1 callbacks in GENERAL_NAME_dup use IMPLEMENT_ASN1_DUP_FUNCTION(GENERAL_NAME) so duplication goes through ASN1_item_dup() instead of calling ASN1_dup() with cast i2d/d2i function pointers. This avoids UBSan function type errors while preserving the public GENERAL_NAME_dup() API. Signed-off-by: Nikola Pajkovsky Reviewed-by: Neil Horman Reviewed-by: Milan Broz MergeDate: Thu Jun 25 12:12:59 2026 (Merged from https://github.com/openssl/openssl/pull/31523) --- crypto/x509/v3_genn.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/crypto/x509/v3_genn.c b/crypto/x509/v3_genn.c index d63168a77a..23a2435842 100644 --- a/crypto/x509/v3_genn.c +++ b/crypto/x509/v3_genn.c @@ -49,13 +49,7 @@ ASN1_ITEM_TEMPLATE(GENERAL_NAMES) = ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, ASN1_ITEM_TEMPLATE_END(GENERAL_NAMES) IMPLEMENT_ASN1_FUNCTIONS(GENERAL_NAMES) - -GENERAL_NAME *GENERAL_NAME_dup(const GENERAL_NAME *a) -{ - return (GENERAL_NAME *)ASN1_dup((i2d_of_void *)i2d_GENERAL_NAME, - (d2i_of_void *)d2i_GENERAL_NAME, - (char *)a); -} +IMPLEMENT_ASN1_DUP_FUNCTION(GENERAL_NAME) int GENERAL_NAME_set1_X509_NAME(GENERAL_NAME **tgt, const X509_NAME *src) { From c392d3052f63fa3f2d8f36723ec07dd5ff90b748 Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Wed, 20 May 2026 08:20:33 +0200 Subject: [PATCH 056/349] x_all.c: avoid mismatched d2i/i2d function pointer casts the d2i_*/i2d_* fp and bio wrappers for RSA_PUBKEY, the DSA and EC key types, and the EVP_PKEY Private/PUBKEY converters passed their type-specific functions to ASN1_{d2i,i2d}_{fp,bio}, which invoke them through the generic d2i_of_void / i2d_of_void / void *(*)(void) pointer types. Add file-local thunks with the exact generic signatures that forward to the concrete functions, and pass those instead of cast pointers. This removes the mismatched calls while keeping the public API unchanged. Signed-off-by: Nikola Pajkovsky Reviewed-by: Neil Horman Reviewed-by: Milan Broz MergeDate: Thu Jun 25 12:13:01 2026 (Merged from https://github.com/openssl/openssl/pull/31523) --- crypto/x509/x_all.c | 208 +++++++++++++++++++++++++++++++++----------- 1 file changed, 159 insertions(+), 49 deletions(-) diff --git a/crypto/x509/x_all.c b/crypto/x509/x_all.c index 2659886748..ef16a7fc88 100644 --- a/crypto/x509/x_all.c +++ b/crypto/x509/x_all.c @@ -32,6 +32,103 @@ #include "crypto/rsa.h" #include "x509_local.h" +static void *RSA_new_thunk(void) +{ + return RSA_new(); +} + +static void *d2i_RSA_PUBKEY_thunk(void **a, const unsigned char **in, long len) +{ + return d2i_RSA_PUBKEY((RSA **)a, in, len); +} + +static int i2d_RSA_PUBKEY_thunk(const void *a, unsigned char **out) +{ + return i2d_RSA_PUBKEY((const RSA *)a, out); +} + +static void *EVP_PKEY_new_thunk(void) +{ + return EVP_PKEY_new(); +} + +static void *d2i_AutoPrivateKey_thunk(void **a, const unsigned char **in, + long len) +{ + return d2i_AutoPrivateKey((EVP_PKEY **)a, in, len); +} + +static void *d2i_PUBKEY_thunk(void **a, const unsigned char **in, long len) +{ + return d2i_PUBKEY((EVP_PKEY **)a, in, len); +} + +static int i2d_PrivateKey_thunk(const void *a, unsigned char **out) +{ + return i2d_PrivateKey((const EVP_PKEY *)a, out); +} + +static int i2d_PUBKEY_thunk(const void *a, unsigned char **out) +{ + return i2d_PUBKEY((const EVP_PKEY *)a, out); +} + +#ifndef OPENSSL_NO_DSA +static void *DSA_new_thunk(void) +{ + return DSA_new(); +} + +static void *d2i_DSAPrivateKey_thunk(void **a, const unsigned char **in, + long len) +{ + return d2i_DSAPrivateKey((DSA **)a, in, len); +} + +static int i2d_DSAPrivateKey_thunk(const void *a, unsigned char **out) +{ + return i2d_DSAPrivateKey((const DSA *)a, out); +} + +static void *d2i_DSA_PUBKEY_thunk(void **a, const unsigned char **in, long len) +{ + return d2i_DSA_PUBKEY((DSA **)a, in, len); +} + +static int i2d_DSA_PUBKEY_thunk(const void *a, unsigned char **out) +{ + return i2d_DSA_PUBKEY((const DSA *)a, out); +} +#endif + +#ifndef OPENSSL_NO_EC +static void *EC_KEY_new_thunk(void) +{ + return EC_KEY_new(); +} + +static void *d2i_EC_PUBKEY_thunk(void **a, const unsigned char **in, long len) +{ + return d2i_EC_PUBKEY((EC_KEY **)a, in, len); +} + +static int i2d_EC_PUBKEY_thunk(const void *a, unsigned char **out) +{ + return i2d_EC_PUBKEY((const EC_KEY *)a, out); +} + +static void *d2i_ECPrivateKey_thunk(void **a, const unsigned char **in, + long len) +{ + return d2i_ECPrivateKey((EC_KEY **)a, in, len); +} + +static int i2d_ECPrivateKey_thunk(const void *a, unsigned char **out) +{ + return i2d_ECPrivateKey((const EC_KEY *)a, out); +} +#endif + int X509_verify(const X509 *a, EVP_PKEY *r) { if (X509_ALGOR_cmp(&a->sig_alg, &a->cert_info.signature) != 0) @@ -390,10 +487,8 @@ RSA *d2i_RSAPublicKey_fp(FILE *fp, RSA **rsa) RSA *d2i_RSA_PUBKEY_fp(FILE *fp, RSA **rsa) { - return ASN1_d2i_fp((void *(*)(void)) - RSA_new, - (D2I_OF(void))d2i_RSA_PUBKEY, fp, - (void **)rsa); + return ASN1_d2i_fp(RSA_new_thunk, d2i_RSA_PUBKEY_thunk, fp, + CHECKED_PPTR_OF(RSA, rsa)); } int i2d_RSAPublicKey_fp(FILE *fp, const RSA *rsa) @@ -403,7 +498,7 @@ int i2d_RSAPublicKey_fp(FILE *fp, const RSA *rsa) int i2d_RSA_PUBKEY_fp(FILE *fp, const RSA *rsa) { - return ASN1_i2d_fp((I2D_OF(void))i2d_RSA_PUBKEY, fp, rsa); + return ASN1_i2d_fp(i2d_RSA_PUBKEY_thunk, fp, rsa); } #endif @@ -424,7 +519,8 @@ RSA *d2i_RSAPublicKey_bio(BIO *bp, RSA **rsa) RSA *d2i_RSA_PUBKEY_bio(BIO *bp, RSA **rsa) { - return ASN1_d2i_bio_of(RSA, RSA_new, d2i_RSA_PUBKEY, bp, rsa); + return ASN1_d2i_bio(RSA_new_thunk, d2i_RSA_PUBKEY_thunk, bp, + CHECKED_PPTR_OF(RSA, rsa)); } int i2d_RSAPublicKey_bio(BIO *bp, const RSA *rsa) @@ -434,50 +530,55 @@ int i2d_RSAPublicKey_bio(BIO *bp, const RSA *rsa) int i2d_RSA_PUBKEY_bio(BIO *bp, const RSA *rsa) { - return ASN1_i2d_bio_of(RSA, i2d_RSA_PUBKEY, bp, rsa); + return ASN1_i2d_bio(i2d_RSA_PUBKEY_thunk, bp, rsa); } #ifndef OPENSSL_NO_DSA #ifndef OPENSSL_NO_STDIO DSA *d2i_DSAPrivateKey_fp(FILE *fp, DSA **dsa) { - return ASN1_d2i_fp_of(DSA, DSA_new, d2i_DSAPrivateKey, fp, dsa); + return ASN1_d2i_fp(DSA_new_thunk, d2i_DSAPrivateKey_thunk, fp, + CHECKED_PPTR_OF(DSA, dsa)); } int i2d_DSAPrivateKey_fp(FILE *fp, const DSA *dsa) { - return ASN1_i2d_fp_of(DSA, i2d_DSAPrivateKey, fp, dsa); + return ASN1_i2d_fp(i2d_DSAPrivateKey_thunk, fp, + CHECKED_PTR_OF(const DSA, dsa)); } DSA *d2i_DSA_PUBKEY_fp(FILE *fp, DSA **dsa) { - return ASN1_d2i_fp_of(DSA, DSA_new, d2i_DSA_PUBKEY, fp, dsa); + return ASN1_d2i_fp(DSA_new_thunk, d2i_DSA_PUBKEY_thunk, fp, + CHECKED_PPTR_OF(DSA, dsa)); } int i2d_DSA_PUBKEY_fp(FILE *fp, const DSA *dsa) { - return ASN1_i2d_fp_of(DSA, i2d_DSA_PUBKEY, fp, dsa); + return ASN1_i2d_fp(i2d_DSA_PUBKEY_thunk, fp, + CHECKED_PTR_OF(const DSA, dsa)); } #endif DSA *d2i_DSAPrivateKey_bio(BIO *bp, DSA **dsa) { - return ASN1_d2i_bio_of(DSA, DSA_new, d2i_DSAPrivateKey, bp, dsa); + return ASN1_d2i_bio(DSA_new_thunk, d2i_DSAPrivateKey_thunk, bp, (void **)dsa); } int i2d_DSAPrivateKey_bio(BIO *bp, const DSA *dsa) { - return ASN1_i2d_bio_of(DSA, i2d_DSAPrivateKey, bp, dsa); + return ASN1_i2d_bio(i2d_DSAPrivateKey_thunk, bp, + CHECKED_PTR_OF(const DSA, dsa)); } DSA *d2i_DSA_PUBKEY_bio(BIO *bp, DSA **dsa) { - return ASN1_d2i_bio_of(DSA, DSA_new, d2i_DSA_PUBKEY, bp, dsa); + return ASN1_d2i_bio(DSA_new_thunk, d2i_DSA_PUBKEY_thunk, bp, (void **)dsa); } int i2d_DSA_PUBKEY_bio(BIO *bp, const DSA *dsa) { - return ASN1_i2d_bio_of(DSA, i2d_DSA_PUBKEY, bp, dsa); + return ASN1_i2d_bio(i2d_DSA_PUBKEY_thunk, bp, dsa); } #endif @@ -486,42 +587,49 @@ int i2d_DSA_PUBKEY_bio(BIO *bp, const DSA *dsa) #ifndef OPENSSL_NO_STDIO EC_KEY *d2i_EC_PUBKEY_fp(FILE *fp, EC_KEY **eckey) { - return ASN1_d2i_fp_of(EC_KEY, EC_KEY_new, d2i_EC_PUBKEY, fp, eckey); + return ASN1_d2i_fp(EC_KEY_new_thunk, d2i_EC_PUBKEY_thunk, fp, + CHECKED_PPTR_OF(EC_KEY, eckey)); } int i2d_EC_PUBKEY_fp(FILE *fp, const EC_KEY *eckey) { - return ASN1_i2d_fp_of(EC_KEY, i2d_EC_PUBKEY, fp, eckey); + return ASN1_i2d_fp(i2d_EC_PUBKEY_thunk, fp, CHECKED_PTR_OF(const EC_KEY, eckey)); } EC_KEY *d2i_ECPrivateKey_fp(FILE *fp, EC_KEY **eckey) { - return ASN1_d2i_fp_of(EC_KEY, EC_KEY_new, d2i_ECPrivateKey, fp, eckey); + return ASN1_d2i_fp(EC_KEY_new_thunk, d2i_ECPrivateKey_thunk, fp, + CHECKED_PPTR_OF(EC_KEY, eckey)); } int i2d_ECPrivateKey_fp(FILE *fp, const EC_KEY *eckey) { - return ASN1_i2d_fp_of(EC_KEY, i2d_ECPrivateKey, fp, eckey); + return ASN1_i2d_fp(i2d_ECPrivateKey_thunk, fp, + CHECKED_PTR_OF(const EC_KEY, eckey)); } #endif EC_KEY *d2i_EC_PUBKEY_bio(BIO *bp, EC_KEY **eckey) { - return ASN1_d2i_bio_of(EC_KEY, EC_KEY_new, d2i_EC_PUBKEY, bp, eckey); + return ASN1_d2i_bio(EC_KEY_new_thunk, d2i_EC_PUBKEY_thunk, bp, + CHECKED_PPTR_OF(EC_KEY, eckey)); } int i2d_EC_PUBKEY_bio(BIO *bp, const EC_KEY *ecdsa) { - return ASN1_i2d_bio_of(EC_KEY, i2d_EC_PUBKEY, bp, ecdsa); + return ASN1_i2d_bio(i2d_EC_PUBKEY_thunk, bp, + CHECKED_PTR_OF(const EC_KEY, ecdsa)); } EC_KEY *d2i_ECPrivateKey_bio(BIO *bp, EC_KEY **eckey) { - return ASN1_d2i_bio_of(EC_KEY, EC_KEY_new, d2i_ECPrivateKey, bp, eckey); + return ASN1_d2i_bio(EC_KEY_new_thunk, d2i_ECPrivateKey_thunk, bp, + CHECKED_PPTR_OF(EC_KEY, eckey)); } int i2d_ECPrivateKey_bio(BIO *bp, const EC_KEY *eckey) { - return ASN1_i2d_bio_of(EC_KEY, i2d_ECPrivateKey, bp, eckey); + return ASN1_i2d_bio(i2d_ECPrivateKey_thunk, bp, + CHECKED_PTR_OF(const EC_KEY, eckey)); } #endif @@ -689,61 +797,57 @@ int PKCS7_ISSUER_AND_SERIAL_digest(PKCS7_ISSUER_AND_SERIAL *data, #ifndef OPENSSL_NO_STDIO X509_SIG *d2i_PKCS8_fp(FILE *fp, X509_SIG **p8) { - return ASN1_d2i_fp_of(X509_SIG, X509_SIG_new, d2i_X509_SIG, fp, p8); + return ASN1_item_d2i_fp(ASN1_ITEM_rptr(X509_SIG), fp, p8); } int i2d_PKCS8_fp(FILE *fp, const X509_SIG *p8) { - return ASN1_i2d_fp_of(X509_SIG, i2d_X509_SIG, fp, p8); + return ASN1_item_i2d_fp(ASN1_ITEM_rptr(X509_SIG), fp, p8); } #endif X509_SIG *d2i_PKCS8_bio(BIO *bp, X509_SIG **p8) { - return ASN1_d2i_bio_of(X509_SIG, X509_SIG_new, d2i_X509_SIG, bp, p8); + return ASN1_item_d2i_bio(ASN1_ITEM_rptr(X509_SIG), bp, p8); } int i2d_PKCS8_bio(BIO *bp, const X509_SIG *p8) { - return ASN1_i2d_bio_of(X509_SIG, i2d_X509_SIG, bp, p8); + return ASN1_item_i2d_bio(ASN1_ITEM_rptr(X509_SIG), bp, p8); } #ifndef OPENSSL_NO_STDIO X509_PUBKEY *d2i_X509_PUBKEY_fp(FILE *fp, X509_PUBKEY **xpk) { - return ASN1_d2i_fp_of(X509_PUBKEY, X509_PUBKEY_new, d2i_X509_PUBKEY, - fp, xpk); + return ASN1_item_d2i_fp(ASN1_ITEM_rptr(X509_PUBKEY), fp, xpk); } int i2d_X509_PUBKEY_fp(FILE *fp, const X509_PUBKEY *xpk) { - return ASN1_i2d_fp_of(X509_PUBKEY, i2d_X509_PUBKEY, fp, xpk); + return ASN1_item_i2d_fp(ASN1_ITEM_rptr(X509_PUBKEY), fp, xpk); } #endif X509_PUBKEY *d2i_X509_PUBKEY_bio(BIO *bp, X509_PUBKEY **xpk) { - return ASN1_d2i_bio_of(X509_PUBKEY, X509_PUBKEY_new, d2i_X509_PUBKEY, - bp, xpk); + return ASN1_item_d2i_bio(ASN1_ITEM_rptr(X509_PUBKEY), bp, xpk); } int i2d_X509_PUBKEY_bio(BIO *bp, const X509_PUBKEY *xpk) { - return ASN1_i2d_bio_of(X509_PUBKEY, i2d_X509_PUBKEY, bp, xpk); + return ASN1_item_i2d_bio(ASN1_ITEM_rptr(X509_PUBKEY), bp, xpk); } #ifndef OPENSSL_NO_STDIO PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO_fp(FILE *fp, PKCS8_PRIV_KEY_INFO **p8inf) { - return ASN1_d2i_fp_of(PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_new, - d2i_PKCS8_PRIV_KEY_INFO, fp, p8inf); + return ASN1_item_d2i_fp(ASN1_ITEM_rptr(PKCS8_PRIV_KEY_INFO), fp, p8inf); } int i2d_PKCS8_PRIV_KEY_INFO_fp(FILE *fp, const PKCS8_PRIV_KEY_INFO *p8inf) { - return ASN1_i2d_fp_of(PKCS8_PRIV_KEY_INFO, i2d_PKCS8_PRIV_KEY_INFO, fp, - p8inf); + return ASN1_item_i2d_fp(ASN1_ITEM_rptr(PKCS8_PRIV_KEY_INFO), fp, p8inf); } int i2d_PKCS8PrivateKeyInfo_fp(FILE *fp, const EVP_PKEY *key) @@ -761,12 +865,14 @@ int i2d_PKCS8PrivateKeyInfo_fp(FILE *fp, const EVP_PKEY *key) int i2d_PrivateKey_fp(FILE *fp, const EVP_PKEY *pkey) { - return ASN1_i2d_fp_of(EVP_PKEY, i2d_PrivateKey, fp, pkey); + return ASN1_i2d_fp(i2d_PrivateKey_thunk, fp, + CHECKED_PTR_OF(const EVP_PKEY, pkey)); } EVP_PKEY *d2i_PrivateKey_fp(FILE *fp, EVP_PKEY **a) { - return ASN1_d2i_fp_of(EVP_PKEY, EVP_PKEY_new, d2i_AutoPrivateKey, fp, a); + return ASN1_d2i_fp(EVP_PKEY_new_thunk, d2i_AutoPrivateKey_thunk, + fp, CHECKED_PPTR_OF(EVP_PKEY, a)); } EVP_PKEY *d2i_PrivateKey_ex_fp(FILE *fp, EVP_PKEY **a, OSSL_LIB_CTX *libctx, @@ -787,7 +893,8 @@ EVP_PKEY *d2i_PrivateKey_ex_fp(FILE *fp, EVP_PKEY **a, OSSL_LIB_CTX *libctx, int i2d_PUBKEY_fp(FILE *fp, const EVP_PKEY *pkey) { - return ASN1_i2d_fp_of(EVP_PKEY, i2d_PUBKEY, fp, pkey); + return ASN1_i2d_fp(i2d_PUBKEY_thunk, fp, + CHECKED_PTR_OF(const EVP_PKEY, pkey)); } EVP_PKEY *d2i_PUBKEY_ex_fp(FILE *fp, EVP_PKEY **a, OSSL_LIB_CTX *libctx, @@ -808,7 +915,8 @@ EVP_PKEY *d2i_PUBKEY_ex_fp(FILE *fp, EVP_PKEY **a, OSSL_LIB_CTX *libctx, EVP_PKEY *d2i_PUBKEY_fp(FILE *fp, EVP_PKEY **a) { - return ASN1_d2i_fp_of(EVP_PKEY, EVP_PKEY_new, d2i_PUBKEY, fp, a); + return ASN1_d2i_fp(EVP_PKEY_new_thunk, d2i_PUBKEY_thunk, fp, + CHECKED_PPTR_OF(EVP_PKEY, a)); } #endif @@ -816,14 +924,12 @@ EVP_PKEY *d2i_PUBKEY_fp(FILE *fp, EVP_PKEY **a) PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO_bio(BIO *bp, PKCS8_PRIV_KEY_INFO **p8inf) { - return ASN1_d2i_bio_of(PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_new, - d2i_PKCS8_PRIV_KEY_INFO, bp, p8inf); + return ASN1_item_d2i_bio(ASN1_ITEM_rptr(PKCS8_PRIV_KEY_INFO), bp, p8inf); } int i2d_PKCS8_PRIV_KEY_INFO_bio(BIO *bp, const PKCS8_PRIV_KEY_INFO *p8inf) { - return ASN1_i2d_bio_of(PKCS8_PRIV_KEY_INFO, i2d_PKCS8_PRIV_KEY_INFO, bp, - p8inf); + return ASN1_item_i2d_bio(ASN1_ITEM_rptr(PKCS8_PRIV_KEY_INFO), bp, p8inf); } int i2d_PKCS8PrivateKeyInfo_bio(BIO *bp, const EVP_PKEY *key) @@ -841,12 +947,14 @@ int i2d_PKCS8PrivateKeyInfo_bio(BIO *bp, const EVP_PKEY *key) int i2d_PrivateKey_bio(BIO *bp, const EVP_PKEY *pkey) { - return ASN1_i2d_bio_of(EVP_PKEY, i2d_PrivateKey, bp, pkey); + return ASN1_i2d_bio(i2d_PrivateKey_thunk, bp, + CHECKED_PTR_OF(const EVP_PKEY, pkey)); } EVP_PKEY *d2i_PrivateKey_bio(BIO *bp, EVP_PKEY **a) { - return ASN1_d2i_bio_of(EVP_PKEY, EVP_PKEY_new, d2i_AutoPrivateKey, bp, a); + return ASN1_d2i_bio(EVP_PKEY_new_thunk, d2i_AutoPrivateKey_thunk, + bp, CHECKED_PPTR_OF(EVP_PKEY, a)); } EVP_PKEY *d2i_PrivateKey_ex_bio(BIO *bp, EVP_PKEY **a, OSSL_LIB_CTX *libctx, @@ -870,7 +978,8 @@ err: int i2d_PUBKEY_bio(BIO *bp, const EVP_PKEY *pkey) { - return ASN1_i2d_bio_of(EVP_PKEY, i2d_PUBKEY, bp, pkey); + return ASN1_i2d_bio(i2d_PUBKEY_thunk, bp, + CHECKED_PTR_OF(const EVP_PKEY, pkey)); } EVP_PKEY *d2i_PUBKEY_ex_bio(BIO *bp, EVP_PKEY **a, OSSL_LIB_CTX *libctx, @@ -894,7 +1003,8 @@ err: EVP_PKEY *d2i_PUBKEY_bio(BIO *bp, EVP_PKEY **a) { - return ASN1_d2i_bio_of(EVP_PKEY, EVP_PKEY_new, d2i_PUBKEY, bp, a); + return ASN1_d2i_bio(EVP_PKEY_new_thunk, d2i_PUBKEY_thunk, bp, + CHECKED_PPTR_OF(EVP_PKEY, a)); } #ifndef OPENSSL_NO_STDIO From da7eadb59322b00b75e050d095f23efc4e37dcc4 Mon Sep 17 00:00:00 2001 From: dannyward630 Date: Thu, 18 Jun 2026 19:42:34 +0200 Subject: [PATCH 057/349] Mention removed ERR macros in changelog Update the existing 4.0.0 changelog entry for the ERR state cleanup to list the removed ERR_FLAG_MARK, ERR_FLAG_CLEAR, and ERR_NUM_ERRORS macros. Fixes #31056 CLA: trivial Reviewed-by: Eugene Syromiatnikov Reviewed-by: Kurt Roeckx Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz MergeDate: Thu Jun 25 16:37:57 2026 (Merged from https://github.com/openssl/openssl/pull/31598) --- CHANGES.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 671e700a72..ea505a94e5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1273,7 +1273,9 @@ OpenSSL 4.0 *Tomáš Mráz* * Removed deprecated functions `ERR_get_state()`, `ERR_remove_state()` - and `ERR_remove_thread_state()`. The `ERR_STATE` object is now always opaque. + and `ERR_remove_thread_state()`, as well as the `ERR_FLAG_MARK`, + `ERR_FLAG_CLEAR` and `ERR_NUM_ERRORS` macros. The `ERR_STATE` object is now + always opaque. *Tomáš Mráz* From fe44705758b57f68428b6ed1ee74d6caee64ea8b Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Wed, 24 Jun 2026 12:02:02 +0200 Subject: [PATCH 058/349] 25-test_verify_store.t: Add missing capture for bare run() We want to add the printed out pubkey and not just 1. Also add a comment clarifying intention of this exercise. Fixes 77c979faf47b Reviewed-by: Eugene Syromiatnikov Reviewed-by: David von Oheimb Reviewed-by: Bob Beck MergeDate: Thu Jun 25 16:44:51 2026 (Merged from https://github.com/openssl/openssl/pull/31700) --- test/recipes/25-test_verify_store.t | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/recipes/25-test_verify_store.t b/test/recipes/25-test_verify_store.t index 0db8e6238d..bfac17c7f4 100644 --- a/test/recipes/25-test_verify_store.t +++ b/test/recipes/25-test_verify_store.t @@ -72,9 +72,11 @@ SKIP: { -CAstore => $CAcert, $CAcert ); + # Put a pubkey and DH params to the store to test + # that other things in the store are just ignored open(my $out, '>', $CAobjects) or die $!; - my $pubkey = run(app(["openssl", "x509", "-pubkey", "-noout", "-in", $CAcert])); - print $out $pubkey; + my @pubkey = run(app([qw(openssl x509 -pubkey -noout -in), $CAcert]), capture => 1); + print $out @pubkey; my @files; push @files, srctop_file("test", "certs", "dhp2048.pem") unless disabled("dh"); From f9e7cb8cd45aac3e52a309f10d084c447c3945ed Mon Sep 17 00:00:00 2001 From: rootvector2 Date: Sun, 31 May 2026 22:52:26 +0530 Subject: [PATCH 059/349] lms: free previous encoded public key in ossl_lms_pubkey_decode ossl_lms_pubkey_decode() only freed pkey->encoded when the new public key had a different length, so re-decoding a same-length key on the documented repeated-call path overwrote the old buffer without freeing it. Always free the existing buffer first. Also clear pkey->encodedlen on the error path so a failed decode leaves the key in a consistent state instead of keeping a stale length. CLA: trivial Reviewed-by: Nikola Pajkovsky Reviewed-by: Bob Beck Reviewed-by: Tomas Mraz MergeDate: Thu Jun 25 16:58:46 2026 (Merged from https://github.com/openssl/openssl/pull/31345) --- crypto/lms/lms_pubkey_decode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crypto/lms/lms_pubkey_decode.c b/crypto/lms/lms_pubkey_decode.c index 8c2ee0ff5e..29ca1d44af 100644 --- a/crypto/lms/lms_pubkey_decode.c +++ b/crypto/lms/lms_pubkey_decode.c @@ -95,7 +95,7 @@ int ossl_lms_pubkey_decode(const unsigned char *pub, size_t publen, { LMS_PUB_KEY *pkey = &lmskey->pub; - if (pkey->encoded != NULL && pkey->encodedlen != publen) { + if (pkey->encoded != NULL) { OPENSSL_free(pkey->encoded); pkey->encodedlen = 0; } @@ -110,6 +110,7 @@ int ossl_lms_pubkey_decode(const unsigned char *pub, size_t publen, err: OPENSSL_free(pkey->encoded); pkey->encoded = NULL; + pkey->encodedlen = 0; return 0; } From ff1dcf6a7514a6766874290d8847f9ed0eedd7fa Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 22 Jun 2026 19:47:23 +0200 Subject: [PATCH 060/349] apps: cover pkeyutl oneshot buffer path with empty file input The non-mmap buffer fallback in do_raw_keyop() for oneshot (only_nomd) keys was never exercised on Unix, since any file with non-zero size takes the mmap path and returns before reaching it. Signing and verifying an empty file gives filesize 0, which skips the mmap block (guarded by filesize > 0) and falls through to the buffer switch, covering both the SIGN and VERIFY arms. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Daniel Kubec Reviewed-by: Tomas Mraz MergeDate: Thu Jun 25 17:00:36 2026 (Merged from https://github.com/openssl/openssl/pull/31644) --- test/recipes/20-test_pkeyutl.t | 39 ++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/test/recipes/20-test_pkeyutl.t b/test/recipes/20-test_pkeyutl.t index 4458887f5c..e7552a8021 100644 --- a/test/recipes/20-test_pkeyutl.t +++ b/test/recipes/20-test_pkeyutl.t @@ -17,7 +17,7 @@ use File::Compare qw/compare_text compare/; setup("test_pkeyutl"); -plan tests => 31; +plan tests => 32; # For the tests below we use the cert itself as the TBS file @@ -214,7 +214,7 @@ SKIP: { } SKIP: { - skip "EdDSA is not supported by this OpenSSL build", 6 + skip "EdDSA is not supported by this OpenSSL build", 7 if disabled("ecx"); subtest "pkeyutl -rawin oneshot with file input (mmap or buffer path)" => sub { @@ -273,6 +273,41 @@ SKIP: { unlink($stderr_file) if -f $stderr_file; }; + subtest "pkeyutl -rawin oneshot with empty file (buffer path, filesize 0)" => sub { + my $ed25519_key = srctop_file("test", "tested25519.pem"); + my $ed25519_pub = srctop_file("test", "tested25519pub.pem"); + my $empty = "pkeyutl_empty.bin"; + my $sigfile = "rawin_empty_ed25519.sig"; + # Ed25519 is deterministic, so signing the empty message with + # tested25519.pem always yields this exact signature. + my $expected_sig = + "42a443bd375c962f571dbf7402654219655b30c395dee06e" . + "d2a4a41342686da620889e374807266a3aab535345985c96" . + "cbb7475c8b0df47968d29fbf3d352e0c"; + + plan tests => 3; + + # create a zero-length input file + open(my $fh, '>', $empty) or die "cannot create $empty: $!"; + close($fh); + + ok(run(app(['openssl', 'pkeyutl', '-sign', '-rawin', '-inkey', $ed25519_key, + '-in', $empty, '-out', $sigfile])), + "Ed25519 -rawin sign from empty file (filesize 0 buffer path)"); + ok(run(app(['openssl', 'pkeyutl', '-verify', '-rawin', '-pubin', '-inkey', $ed25519_pub, + '-sigfile', $sigfile, '-in', $empty])), + "Ed25519 -rawin verify from empty file"); + + # check the produced signature matches the known reference value + open(my $sfh, '<:raw', $sigfile) or die "cannot open $sigfile: $!"; + read($sfh, my $sig, -s $sigfile); + close($sfh); + is(unpack("H*", $sig), $expected_sig, + "Ed25519 -rawin empty file signature matches the reference value"); + + unlink($empty); + }; + subtest "Ed2559 CLI signature generation and verification" => sub { tsignverify("Ed25519", srctop_file("test","tested25519.pem"), From 259a14a27f431e8d9230034ff4aacd9dd61d47c3 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 23 Jun 2026 15:10:33 +0200 Subject: [PATCH 061/349] apps: fix Ed25519 typo in pkeyutl recipe subtest names Two subtest descriptions read "Ed2559" instead of "Ed25519". Reviewed-by: Daniel Kubec Reviewed-by: Tomas Mraz MergeDate: Thu Jun 25 17:00:37 2026 (Merged from https://github.com/openssl/openssl/pull/31644) --- test/recipes/20-test_pkeyutl.t | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/recipes/20-test_pkeyutl.t b/test/recipes/20-test_pkeyutl.t index e7552a8021..7964a0db06 100644 --- a/test/recipes/20-test_pkeyutl.t +++ b/test/recipes/20-test_pkeyutl.t @@ -308,7 +308,7 @@ SKIP: { unlink($empty); }; - subtest "Ed2559 CLI signature generation and verification" => sub { + subtest "Ed25519 CLI signature generation and verification" => sub { tsignverify("Ed25519", srctop_file("test","tested25519.pem"), srctop_file("test","tested25519pub.pem"), @@ -322,7 +322,7 @@ SKIP: { "-rawin"); }; - subtest "Ed2559 CLI signature generation and verification, no -rawin" => sub { + subtest "Ed25519 CLI signature generation and verification, no -rawin" => sub { tsignverify("Ed25519", srctop_file("test","tested25519.pem"), srctop_file("test","tested25519pub.pem")); From dbeea0f418e42924c945d184a8c21100c52a971e Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 22 Jun 2026 23:15:15 +0200 Subject: [PATCH 062/349] apps: cover the kdf -cipher option in the test recipe The -cipher option was not covered. Add KRB5KDF test cases that exercise it, both via -cipher and the equivalent -kdfopt cipher: form. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz MergeDate: Thu Jun 25 17:01:33 2026 (Merged from https://github.com/openssl/openssl/pull/31651) --- test/recipes/20-test_kdf.t | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/recipes/20-test_kdf.t b/test/recipes/20-test_kdf.t index 55767871d6..8d926d68b3 100755 --- a/test/recipes/20-test_kdf.t +++ b/test/recipes/20-test_kdf.t @@ -75,12 +75,26 @@ my @sskdf_tests = ( desc => 'SSKDF HMAC SHA256'}, ); +my @krb5kdf_tests = ( + { cmd => [qw{openssl kdf -keylen 16 -cipher AES-128-CBC -kdfopt hexkey:42263C6E89F4FC28B8DF68EE09799F15 -kdfopt hexconstant:0000000299 KRB5KDF}], + expected => '34:28:0A:38:2B:C9:27:69:B2:DA:2F:9E:F0:66:85:4B', + desc => 'KRB5KDF AES-128-CBC'}, + { cmd => [qw{openssl kdf -keylen 32 -cipher AES-256-CBC -kdfopt hexkey:FE697B52BC0D3CE14432BA036A92E65BBB52280990A2FA27883998D72AF30161 -kdfopt hexconstant:0000000299 KRB5KDF}], + expected => 'BF:AB:38:8B:DC:B2:38:E9:F9:C9:8D:6A:87:83:04:F0:4D:30:C8:25:56:37:5A:C5:07:A7:A8:52:79:0F:46:74', + desc => 'KRB5KDF AES-256-CBC'}, + # Using the -kdfopt cipher: option instead of -cipher + { cmd => [qw{openssl kdf -keylen 16 -kdfopt cipher:AES-128-CBC -kdfopt hexkey:42263C6E89F4FC28B8DF68EE09799F15 -kdfopt hexconstant:0000000299 KRB5KDF}], + expected => '34:28:0A:38:2B:C9:27:69:B2:DA:2F:9E:F0:66:85:4B', + desc => 'KRB5KDF AES-128-CBC'}, +); + my @scrypt_tests = ( { cmd => [qw{openssl kdf -keylen 64 -kdfopt pass:password -kdfopt salt:NaCl -kdfopt n:1024 -kdfopt r:8 -kdfopt p:16 -kdfopt maxmem_bytes:10485760 id-scrypt}], expected => 'fd:ba:be:1c:9d:34:72:00:78:56:e7:19:0d:01:e9:fe:7c:6a:d7:cb:c8:23:78:30:e7:73:76:63:4b:37:31:62:2e:af:30:d9:2e:22:a3:88:6f:f1:09:27:9d:98:30:da:c7:27:af:b9:4a:83:ee:6d:83:60:cb:df:a2:cc:06:40', desc => 'SCRYPT' }, ); +push @kdf_tests, @krb5kdf_tests unless disabled("krb5kdf"); push @kdf_tests, @scrypt_tests unless disabled("scrypt"); push @kdf_tests, @sshkdf_tests unless disabled("sshkdf"); push @kdf_tests, @sskdf_tests unless disabled("sskdf"); From b34958b877d8fc253c0eb060c45a15527c734c47 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Thu, 7 May 2026 17:06:03 -0400 Subject: [PATCH 063/349] Don't take reference counts on cache lookups Reviewed-by: Bob Beck Reviewed-by: Nikola Pajkovsky MergeDate: Thu Jun 25 21:25:45 2026 (Merged from https://github.com/openssl/openssl/pull/31143) --- crypto/evp/evp_fetch.c | 2 ++ crypto/property/property.c | 18 ++++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/crypto/evp/evp_fetch.c b/crypto/evp/evp_fetch.c index a446f976df..9fa049cdce 100644 --- a/crypto/evp/evp_fetch.c +++ b/crypto/evp/evp_fetch.c @@ -351,7 +351,9 @@ inner_evp_generic_fetch(struct evp_method_data_st *methdata, if (name_id == 0) { ERR_raise_data(ERR_LIB_EVP, ERR_R_FETCH_FAILED, "Algorithm %s cannot be found", name != NULL ? name : ""); +#ifdef OPENSSL_NO_CACHED_FETCH free_method(method); +#endif method = NULL; } else { meth_id = evp_method_id(name_id, operation_id); diff --git a/crypto/property/property.c b/crypto/property/property.c index e807f06995..aa0bb283f1 100644 --- a/crypto/property/property.c +++ b/crypto/property/property.c @@ -830,10 +830,18 @@ int ossl_method_store_fetch(OSSL_METHOD_STORE *store, } } fin: - if (ret && ossl_method_up_ref(&best_impl->method)) { + if (ret) { *method = best_impl->method.method; if (prov_rw != NULL) *prov_rw = best_impl->provider; +#ifdef OPENSSL_NO_CACHED_FETCH + if (!ossl_method_up_ref(&best_impl->method)) { + ret = 0; + *method = NULL; + if (prov_rw != NULL) + *prov_rw = NULL; + } +#endif } else { ret = 0; } @@ -938,9 +946,15 @@ static ossl_inline int ossl_method_store_cache_get_atomic(OSSL_METHOD_STORE *sto r = ossl_method_store_atomic_find_in_list(sa, nid, prov, prop_query); - if (r != NULL && ossl_method_up_ref(&r->method)) { + if (r != NULL) { *method = r->method.method; res = 1; +#ifdef OPENSSL_NO_CACHED_FETCH + if (!ossl_method_up_ref(&r->method)) { + *method = NULL; + res = 0; + } +#endif } return res; From 6d94352026ab4569dcc0ac7f65535e520dc34d75 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Thu, 7 May 2026 17:04:41 -0400 Subject: [PATCH 064/349] Make EVP_MD's not need to take reference counts Reviewed-by: Bob Beck Reviewed-by: Nikola Pajkovsky MergeDate: Thu Jun 25 21:25:47 2026 (Merged from https://github.com/openssl/openssl/pull/31143) --- crypto/evp/digest.c | 56 +++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/crypto/evp/digest.c b/crypto/evp/digest.c index b6c01daeee..d18b707380 100644 --- a/crypto/evp/digest.c +++ b/crypto/evp/digest.c @@ -23,6 +23,8 @@ #include +static void evp_md_free(void *m); + void evp_md_ctx_clear_digest(EVP_MD_CTX *ctx, int force, int keep_fetched) { if (ctx->algctx != NULL) { @@ -965,31 +967,13 @@ static void *evp_md_from_algorithm(int name_id, return md; err: - EVP_MD_free(md); + evp_md_free(md); return NULL; } -static int evp_md_up_ref(void *md) -{ - return EVP_MD_up_ref(md); -} - -static void evp_md_free(void *md) -{ - EVP_MD_free(md); -} - -EVP_MD *EVP_MD_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, - const char *properties) -{ - EVP_MD *md = evp_generic_fetch(ctx, OSSL_OP_DIGEST, algorithm, properties, - evp_md_from_algorithm, evp_md_up_ref, evp_md_free); - - return md; -} - -int EVP_MD_up_ref(EVP_MD *md) +static int evp_md_up_ref(void *m) { + EVP_MD *md = (EVP_MD *)m; int ref = 0; if (md->origin == EVP_ORIG_DYNAMIC) @@ -997,8 +981,9 @@ int EVP_MD_up_ref(EVP_MD *md) return 1; } -void EVP_MD_free(EVP_MD *md) +static void evp_md_free(void *m) { + EVP_MD *md = (EVP_MD *)m; int i; if (md == NULL || md->origin != EVP_ORIG_DYNAMIC) @@ -1014,6 +999,33 @@ void EVP_MD_free(EVP_MD *md) OPENSSL_free(md); } +EVP_MD *EVP_MD_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, + const char *properties) +{ + EVP_MD *md = evp_generic_fetch(ctx, OSSL_OP_DIGEST, algorithm, properties, + evp_md_from_algorithm, evp_md_up_ref, evp_md_free); + + return md; +} + +int EVP_MD_up_ref(EVP_MD *md) +{ +#ifdef OPENSSL_NO_CACHED_FETCH + return evp_md_up_ref(md); +#else + return 1; +#endif +} + +void EVP_MD_free(EVP_MD *md) +{ +#ifdef OPENSSL_NO_CACHED_FETCH + evp_md_free(md); +#else + return; +#endif +} + void EVP_MD_do_all_provided(OSSL_LIB_CTX *libctx, void (*fn)(EVP_MD *mac, void *arg), void *arg) From d031b650a11dec0ac1976a29bc79445e044a4a2b Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Thu, 7 May 2026 17:05:11 -0400 Subject: [PATCH 065/349] Make EVP_CIPHERS not need to take reference counts Reviewed-by: Bob Beck Reviewed-by: Nikola Pajkovsky MergeDate: Thu Jun 25 21:25:49 2026 (Merged from https://github.com/openssl/openssl/pull/31143) --- crypto/evp/evp_enc.c | 56 +++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/crypto/evp/evp_enc.c b/crypto/evp/evp_enc.c index a23846fdf1..07efde94e9 100644 --- a/crypto/evp/evp_enc.c +++ b/crypto/evp/evp_enc.c @@ -1340,6 +1340,30 @@ static void set_legacy_nid(const char *name, void *vlegacy_nid) } #endif +static int evp_cipher_up_ref(void *c) +{ + EVP_CIPHER *cipher = (EVP_CIPHER *)c; + int ref = 0; + + if (cipher->origin == EVP_ORIG_DYNAMIC) + CRYPTO_UP_REF(&cipher->refcnt, &ref); + return 1; +} + +static void evp_cipher_free(void *c) +{ + EVP_CIPHER *cipher = (EVP_CIPHER *)c; + int i; + + if (cipher == NULL || cipher->origin != EVP_ORIG_DYNAMIC) + return; + + CRYPTO_DOWN_REF(&cipher->refcnt, &i); + if (i > 0) + return; + evp_cipher_free_int(cipher); +} + static void *evp_cipher_from_algorithm(const int name_id, const OSSL_ALGORITHM *algodef, OSSL_PROVIDER *prov) @@ -1511,20 +1535,10 @@ static void *evp_cipher_from_algorithm(const int name_id, return cipher; err: - EVP_CIPHER_free(cipher); + evp_cipher_free(cipher); return NULL; } -static int evp_cipher_up_ref(void *cipher) -{ - return EVP_CIPHER_up_ref(cipher); -} - -static void evp_cipher_free(void *cipher) -{ - EVP_CIPHER_free(cipher); -} - EVP_CIPHER *EVP_CIPHER_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, const char *properties) { @@ -1557,11 +1571,11 @@ int EVP_CIPHER_can_pipeline(const EVP_CIPHER *cipher, int enc) int EVP_CIPHER_up_ref(EVP_CIPHER *cipher) { - int ref = 0; - - if (cipher->origin == EVP_ORIG_DYNAMIC) - CRYPTO_UP_REF(&cipher->refcnt, &ref); +#ifdef OPENSSL_NO_CACHED_FETCH + return evp_cipher_up_ref(cipher); +#else return 1; +#endif } void evp_cipher_free_int(EVP_CIPHER *cipher) @@ -1574,15 +1588,9 @@ void evp_cipher_free_int(EVP_CIPHER *cipher) void EVP_CIPHER_free(EVP_CIPHER *cipher) { - int i; - - if (cipher == NULL || cipher->origin != EVP_ORIG_DYNAMIC) - return; - - CRYPTO_DOWN_REF(&cipher->refcnt, &i); - if (i > 0) - return; - evp_cipher_free_int(cipher); +#ifdef OPENSSL_NO_CACHED_FETCH + evp_cipher_free(cipher); +#endif } void EVP_CIPHER_do_all_provided(OSSL_LIB_CTX *libctx, From ae23b9ae022b6399fe337b0378cc63bab9b509d4 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Thu, 7 May 2026 17:16:40 -0400 Subject: [PATCH 066/349] Make SKEYMGMT not do ref counting Reviewed-by: Bob Beck Reviewed-by: Nikola Pajkovsky MergeDate: Thu Jun 25 21:25:51 2026 (Merged from https://github.com/openssl/openssl/pull/31143) --- crypto/evp/skeymgmt_meth.c | 68 ++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/crypto/evp/skeymgmt_meth.c b/crypto/evp/skeymgmt_meth.c index 75c4fef675..88f0a6a133 100644 --- a/crypto/evp/skeymgmt_meth.c +++ b/crypto/evp/skeymgmt_meth.c @@ -17,6 +17,8 @@ #include "crypto/evp.h" #include "evp_local.h" +static void evp_skeymgmt_free(void *s); + void *evp_skeymgmt_generate(const EVP_SKEYMGMT *skeymgmt, const OSSL_PARAM params[]) { void *provctx = ossl_provider_ctx(EVP_SKEYMGMT_get0_provider(skeymgmt)); @@ -45,16 +47,6 @@ void evp_skeymgmt_freedata(const EVP_SKEYMGMT *skeymgmt, void *keydata) skeymgmt->free(keydata); } -static int evp_skeymgmt_up_ref(void *skeymgmt) -{ - return EVP_SKEYMGMT_up_ref(skeymgmt); -} - -static void evp_skeymgmt_free(void *skeymgmt) -{ - EVP_SKEYMGMT_free(skeymgmt); -} - static void *skeymgmt_new(void) { EVP_SKEYMGMT *skeymgmt = NULL; @@ -62,7 +54,7 @@ static void *skeymgmt_new(void) if ((skeymgmt = OPENSSL_zalloc(sizeof(*skeymgmt))) == NULL) return NULL; if (!CRYPTO_NEW_REF(&skeymgmt->refcnt, 1)) { - EVP_SKEYMGMT_free(skeymgmt); + evp_skeymgmt_free(skeymgmt); return NULL; } return skeymgmt; @@ -80,7 +72,7 @@ static void *skeymgmt_from_algorithm(int name_id, skeymgmt->name_id = name_id; if ((skeymgmt->type_name = ossl_algorithm_get1_first_name(algodef)) == NULL) { - EVP_SKEYMGMT_free(skeymgmt); + evp_skeymgmt_free(skeymgmt); return NULL; } skeymgmt->description = algodef->algorithm_description; @@ -122,13 +114,13 @@ static void *skeymgmt_from_algorithm(int name_id, if (skeymgmt->free == NULL || skeymgmt->import == NULL || skeymgmt->export == NULL) { - EVP_SKEYMGMT_free(skeymgmt); + evp_skeymgmt_free(skeymgmt); ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_PROVIDER_FUNCTIONS); return NULL; } if (!ossl_provider_up_ref(prov)) { - EVP_SKEYMGMT_free(skeymgmt); + evp_skeymgmt_free(skeymgmt); ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR); return NULL; } @@ -137,6 +129,32 @@ static void *skeymgmt_from_algorithm(int name_id, return skeymgmt; } +static int evp_skeymgmt_up_ref(void *s) +{ + EVP_SKEYMGMT *skeymgmt = (EVP_SKEYMGMT *)s; + int ref = 0; + + CRYPTO_UP_REF(&skeymgmt->refcnt, &ref); + return 1; +} + +static void evp_skeymgmt_free(void *s) +{ + EVP_SKEYMGMT *skeymgmt = (EVP_SKEYMGMT *)s; + int ref = 0; + + if (skeymgmt == NULL) + return; + + CRYPTO_DOWN_REF(&skeymgmt->refcnt, &ref); + if (ref > 0) + return; + OPENSSL_free(skeymgmt->type_name); + ossl_provider_free(skeymgmt->prov); + CRYPTO_FREE_REF(&skeymgmt->refcnt); + OPENSSL_free(skeymgmt); +} + EVP_SKEYMGMT *evp_skeymgmt_fetch_from_prov(OSSL_PROVIDER *prov, const char *name, const char *properties) @@ -160,26 +178,18 @@ EVP_SKEYMGMT *EVP_SKEYMGMT_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, int EVP_SKEYMGMT_up_ref(EVP_SKEYMGMT *skeymgmt) { - int ref = 0; - - CRYPTO_UP_REF(&skeymgmt->refcnt, &ref); +#ifdef OPENSSL_NO_CACHED_FETCH + return evp_skeymgmt_up_ref(skeymgmt); +#else return 1; +#endif } void EVP_SKEYMGMT_free(EVP_SKEYMGMT *skeymgmt) { - int ref = 0; - - if (skeymgmt == NULL) - return; - - CRYPTO_DOWN_REF(&skeymgmt->refcnt, &ref); - if (ref > 0) - return; - OPENSSL_free(skeymgmt->type_name); - ossl_provider_free(skeymgmt->prov); - CRYPTO_FREE_REF(&skeymgmt->refcnt); - OPENSSL_free(skeymgmt); +#ifdef OPENSSL_NO_CACHED_FETCH + evp_skeymgmt_free(skeymgmt); +#endif } const OSSL_PROVIDER *EVP_SKEYMGMT_get0_provider(const EVP_SKEYMGMT *skeymgmt) From e8bc71e66648ed22dae9d02053aa630e578b551e Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Thu, 7 May 2026 17:29:37 -0400 Subject: [PATCH 067/349] Make EVP_MAC not do refcounting Reviewed-by: Bob Beck Reviewed-by: Nikola Pajkovsky MergeDate: Thu Jun 25 21:25:53 2026 (Merged from https://github.com/openssl/openssl/pull/31143) --- crypto/evp/mac_meth.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crypto/evp/mac_meth.c b/crypto/evp/mac_meth.c index ba47e95870..439a6fc2cf 100644 --- a/crypto/evp/mac_meth.c +++ b/crypto/evp/mac_meth.c @@ -182,12 +182,18 @@ EVP_MAC *EVP_MAC_fetch(OSSL_LIB_CTX *libctx, const char *algorithm, int EVP_MAC_up_ref(EVP_MAC *mac) { +#ifdef OPENSSL_NO_CACHED_FETCH return evp_mac_up_ref(mac); +#else + return 1; +#endif } void EVP_MAC_free(EVP_MAC *mac) { +#ifdef OPENSSL_NO_CACHED_FETCH evp_mac_free(mac); +#endif } const OSSL_PROVIDER *EVP_MAC_get0_provider(const EVP_MAC *mac) From e2f82554eec5711178cf560680532ac5f7111522 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 8 May 2026 10:04:37 -0400 Subject: [PATCH 068/349] Make EVP_KDF not do ref counting Reviewed-by: Bob Beck Reviewed-by: Nikola Pajkovsky MergeDate: Thu Jun 25 21:25:55 2026 (Merged from https://github.com/openssl/openssl/pull/31143) --- crypto/evp/kdf_meth.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crypto/evp/kdf_meth.c b/crypto/evp/kdf_meth.c index 31680ee664..fba2f9fc75 100644 --- a/crypto/evp/kdf_meth.c +++ b/crypto/evp/kdf_meth.c @@ -176,12 +176,18 @@ EVP_KDF *EVP_KDF_fetch(OSSL_LIB_CTX *libctx, const char *algorithm, int EVP_KDF_up_ref(EVP_KDF *kdf) { +#ifdef OPENSSL_NO_CACHED_FETCH return evp_kdf_up_ref(kdf); +#else + return 1; +#endif } void EVP_KDF_free(EVP_KDF *kdf) { +#ifdef OPENSSL_NO_CACHED_FETCH evp_kdf_free(kdf); +#endif } const OSSL_PARAM *EVP_KDF_gettable_params(const EVP_KDF *kdf) From 4971f4830c03e8aaa07fa1478e7c027711b237da Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 8 May 2026 10:10:05 -0400 Subject: [PATCH 069/349] make EVP_KEM not do refcounting Reviewed-by: Bob Beck Reviewed-by: Nikola Pajkovsky MergeDate: Thu Jun 25 21:25:57 2026 (Merged from https://github.com/openssl/openssl/pull/31143) --- crypto/evp/kem.c | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/crypto/evp/kem.c b/crypto/evp/kem.c index 92db961892..48de927ce4 100644 --- a/crypto/evp/kem.c +++ b/crypto/evp/kem.c @@ -19,12 +19,28 @@ static void evp_kem_free(void *data) { - EVP_KEM_free(data); + EVP_KEM *kem = (EVP_KEM *)data; + int i; + + if (kem == NULL) + return; + + CRYPTO_DOWN_REF(&kem->refcnt, &i); + if (i > 0) + return; + OPENSSL_free(kem->type_name); + ossl_provider_free(kem->prov); + CRYPTO_FREE_REF(&kem->refcnt); + OPENSSL_free(kem); } static int evp_kem_up_ref(void *data) { - return EVP_KEM_up_ref(data); + EVP_KEM *kem = (EVP_KEM *)data; + int ref = 0; + + CRYPTO_UP_REF(&kem->refcnt, &ref); + return 1; } static int evp_kem_init(EVP_PKEY_CTX *ctx, int operation, @@ -432,26 +448,18 @@ err: void EVP_KEM_free(EVP_KEM *kem) { - int i; - - if (kem == NULL) - return; - - CRYPTO_DOWN_REF(&kem->refcnt, &i); - if (i > 0) - return; - OPENSSL_free(kem->type_name); - ossl_provider_free(kem->prov); - CRYPTO_FREE_REF(&kem->refcnt); - OPENSSL_free(kem); +#ifdef OPENSSL_NO_CACHED_FETCH + evp_kem_free(kem); +#endif } int EVP_KEM_up_ref(EVP_KEM *kem) { - int ref = 0; - - CRYPTO_UP_REF(&kem->refcnt, &ref); +#ifdef OPENSSL_NO_CACHED_FETCH + return evp_kem_up_ref(kem); +#else return 1; +#endif } OSSL_PROVIDER *EVP_KEM_get0_provider(const EVP_KEM *kem) From d3cb703b35b11432959836a1864cc4e52d723879 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 8 May 2026 10:13:59 -0400 Subject: [PATCH 070/349] Make EVP_SIGNATURE not do ref counting Reviewed-by: Bob Beck Reviewed-by: Nikola Pajkovsky MergeDate: Thu Jun 25 21:26:00 2026 (Merged from https://github.com/openssl/openssl/pull/31143) --- crypto/evp/signature.c | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/crypto/evp/signature.c b/crypto/evp/signature.c index 3737bc6ba4..15e4116400 100644 --- a/crypto/evp/signature.c +++ b/crypto/evp/signature.c @@ -22,12 +22,27 @@ static void evp_signature_free(void *data) { - EVP_SIGNATURE_free(data); + EVP_SIGNATURE *signature = (EVP_SIGNATURE *)data; + int i; + + if (signature == NULL) + return; + CRYPTO_DOWN_REF(&signature->refcnt, &i); + if (i > 0) + return; + OPENSSL_free(signature->type_name); + ossl_provider_free(signature->prov); + CRYPTO_FREE_REF(&signature->refcnt); + OPENSSL_free(signature); } static int evp_signature_up_ref(void *data) { - return EVP_SIGNATURE_up_ref(data); + EVP_SIGNATURE *signature = (EVP_SIGNATURE *)data; + int ref = 0; + + CRYPTO_UP_REF(&signature->refcnt, &ref); + return 1; } static EVP_SIGNATURE *evp_signature_new(OSSL_PROVIDER *prov) @@ -454,25 +469,18 @@ err: void EVP_SIGNATURE_free(EVP_SIGNATURE *signature) { - int i; - - if (signature == NULL) - return; - CRYPTO_DOWN_REF(&signature->refcnt, &i); - if (i > 0) - return; - OPENSSL_free(signature->type_name); - ossl_provider_free(signature->prov); - CRYPTO_FREE_REF(&signature->refcnt); - OPENSSL_free(signature); +#ifdef OPENSSL_NO_CACHED_FETCH + evp_signature_free(signature); +#endif } int EVP_SIGNATURE_up_ref(EVP_SIGNATURE *signature) { - int ref = 0; - - CRYPTO_UP_REF(&signature->refcnt, &ref); +#ifdef OPENSSL_NO_CACHED_FETCH + return evp_signature_up_ref(signature); +#else return 1; +#endif } OSSL_PROVIDER *EVP_SIGNATURE_get0_provider(const EVP_SIGNATURE *signature) From a3ba2f057555b0b4d0e42f1fb72927bd5d6ad8be Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 8 May 2026 10:18:52 -0400 Subject: [PATCH 071/349] Make EVP_ASYM_CIPHER not do ref counting Reviewed-by: Bob Beck Reviewed-by: Nikola Pajkovsky MergeDate: Thu Jun 25 21:26:02 2026 (Merged from https://github.com/openssl/openssl/pull/31143) --- crypto/evp/asymcipher.c | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/crypto/evp/asymcipher.c b/crypto/evp/asymcipher.c index e31e601c63..78dbb7e2f5 100644 --- a/crypto/evp/asymcipher.c +++ b/crypto/evp/asymcipher.c @@ -19,12 +19,27 @@ static void evp_asym_cipher_free(void *data) { - EVP_ASYM_CIPHER_free(data); + EVP_ASYM_CIPHER *cipher = (EVP_ASYM_CIPHER *)data; + int i; + + if (cipher == NULL) + return; + CRYPTO_DOWN_REF(&cipher->refcnt, &i); + if (i > 0) + return; + OPENSSL_free(cipher->type_name); + ossl_provider_free(cipher->prov); + CRYPTO_FREE_REF(&cipher->refcnt); + OPENSSL_free(cipher); } static int evp_asym_cipher_up_ref(void *data) { - return EVP_ASYM_CIPHER_up_ref(data); + EVP_ASYM_CIPHER *cipher = (EVP_ASYM_CIPHER *)data; + int ref = 0; + + CRYPTO_UP_REF(&cipher->refcnt, &ref); + return 1; } static int evp_pkey_asym_cipher_init(EVP_PKEY_CTX *ctx, int operation, @@ -444,25 +459,18 @@ err: void EVP_ASYM_CIPHER_free(EVP_ASYM_CIPHER *cipher) { - int i; - - if (cipher == NULL) - return; - CRYPTO_DOWN_REF(&cipher->refcnt, &i); - if (i > 0) - return; - OPENSSL_free(cipher->type_name); - ossl_provider_free(cipher->prov); - CRYPTO_FREE_REF(&cipher->refcnt); - OPENSSL_free(cipher); +#ifdef OPENSSL_NO_CACHED_FETCH + evp_asym_cipher_free(cipher); +#endif } int EVP_ASYM_CIPHER_up_ref(EVP_ASYM_CIPHER *cipher) { - int ref = 0; - - CRYPTO_UP_REF(&cipher->refcnt, &ref); +#ifdef OPENSSL_NO_CACHED_FETCH + return evp_asym_cipher_up_ref(cipher); +#else return 1; +#endif } OSSL_PROVIDER *EVP_ASYM_CIPHER_get0_provider(const EVP_ASYM_CIPHER *cipher) From 32f23d770eeee87ba4657c2b9a551c28949cc5b4 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 8 May 2026 10:23:00 -0400 Subject: [PATCH 072/349] Make EVP_KEYMGMT not do ref counting Reviewed-by: Bob Beck Reviewed-by: Nikola Pajkovsky MergeDate: Thu Jun 25 21:26:04 2026 (Merged from https://github.com/openssl/openssl/pull/31143) --- crypto/evp/keymgmt_meth.c | 42 +++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/crypto/evp/keymgmt_meth.c b/crypto/evp/keymgmt_meth.c index e7f00d091f..a84c634441 100644 --- a/crypto/evp/keymgmt_meth.c +++ b/crypto/evp/keymgmt_meth.c @@ -19,12 +19,28 @@ static void evp_keymgmt_free(void *data) { - EVP_KEYMGMT_free(data); + EVP_KEYMGMT *keymgmt = (EVP_KEYMGMT *)data; + int ref = 0; + + if (keymgmt == NULL) + return; + + CRYPTO_DOWN_REF(&keymgmt->refcnt, &ref); + if (ref > 0) + return; + OPENSSL_free(keymgmt->type_name); + ossl_provider_free(keymgmt->prov); + CRYPTO_FREE_REF(&keymgmt->refcnt); + OPENSSL_free(keymgmt); } static int evp_keymgmt_up_ref(void *data) { - return EVP_KEYMGMT_up_ref(data); + EVP_KEYMGMT *keymgmt = (EVP_KEYMGMT *)data; + int ref = 0; + + CRYPTO_UP_REF(&keymgmt->refcnt, &ref); + return 1; } static void *keymgmt_new(void) @@ -293,26 +309,18 @@ EVP_KEYMGMT *EVP_KEYMGMT_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, int EVP_KEYMGMT_up_ref(EVP_KEYMGMT *keymgmt) { - int ref = 0; - - CRYPTO_UP_REF(&keymgmt->refcnt, &ref); +#ifdef OPENSSL_NO_CACHED_FETCH + return evp_keymgmt_up_ref(keymgmt); +#else return 1; +#endif } void EVP_KEYMGMT_free(EVP_KEYMGMT *keymgmt) { - int ref = 0; - - if (keymgmt == NULL) - return; - - CRYPTO_DOWN_REF(&keymgmt->refcnt, &ref); - if (ref > 0) - return; - OPENSSL_free(keymgmt->type_name); - ossl_provider_free(keymgmt->prov); - CRYPTO_FREE_REF(&keymgmt->refcnt); - OPENSSL_free(keymgmt); +#ifdef OPENSSL_NO_CACHED_FETCH + evp_keymgmt_free(keymgmt); +#endif } const OSSL_PROVIDER *EVP_KEYMGMT_get0_provider(const EVP_KEYMGMT *keymgmt) From 47e9e36946c069c437465ca6b389bfc0d00515dc Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 8 May 2026 10:30:16 -0400 Subject: [PATCH 073/349] Make EVP_KEYEXCH not do ref counting Reviewed-by: Bob Beck Reviewed-by: Nikola Pajkovsky MergeDate: Thu Jun 25 21:26:05 2026 (Merged from https://github.com/openssl/openssl/pull/31143) --- crypto/evp/exchange.c | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/crypto/evp/exchange.c b/crypto/evp/exchange.c index 849bc21514..1d05f8e857 100644 --- a/crypto/evp/exchange.c +++ b/crypto/evp/exchange.c @@ -21,12 +21,27 @@ static void evp_keyexch_free(void *data) { - EVP_KEYEXCH_free(data); + EVP_KEYEXCH *exchange = (EVP_KEYEXCH *)data; + int i; + + if (exchange == NULL) + return; + CRYPTO_DOWN_REF(&exchange->refcnt, &i); + if (i > 0) + return; + OPENSSL_free(exchange->type_name); + ossl_provider_free(exchange->prov); + CRYPTO_FREE_REF(&exchange->refcnt); + OPENSSL_free(exchange); } static int evp_keyexch_up_ref(void *data) { - return EVP_KEYEXCH_up_ref(data); + EVP_KEYEXCH *exchange = (EVP_KEYEXCH *)data; + int ref = 0; + + CRYPTO_UP_REF(&exchange->refcnt, &ref); + return 1; } static EVP_KEYEXCH *evp_keyexch_new(OSSL_PROVIDER *prov) @@ -160,25 +175,18 @@ err: void EVP_KEYEXCH_free(EVP_KEYEXCH *exchange) { - int i; - - if (exchange == NULL) - return; - CRYPTO_DOWN_REF(&exchange->refcnt, &i); - if (i > 0) - return; - OPENSSL_free(exchange->type_name); - ossl_provider_free(exchange->prov); - CRYPTO_FREE_REF(&exchange->refcnt); - OPENSSL_free(exchange); +#ifdef OPENSSL_NO_CACHED_FETCH + evp_keyexch_free(exchange); +#endif } int EVP_KEYEXCH_up_ref(EVP_KEYEXCH *exchange) { - int ref = 0; - - CRYPTO_UP_REF(&exchange->refcnt, &ref); +#ifdef OPENSSL_NO_CACHED_FETCH + return evp_keyexch_up_ref(exchange); +#else return 1; +#endif } OSSL_PROVIDER *EVP_KEYEXCH_get0_provider(const EVP_KEYEXCH *exchange) From 7dfc6344d8f92291b4815038a317af8f7c213bc5 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 8 May 2026 10:33:49 -0400 Subject: [PATCH 074/349] make EVP_RAND not do ref counting Reviewed-by: Bob Beck Reviewed-by: Nikola Pajkovsky MergeDate: Thu Jun 25 21:26:07 2026 (Merged from https://github.com/openssl/openssl/pull/31143) --- crypto/evp/evp_rand.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crypto/evp/evp_rand.c b/crypto/evp/evp_rand.c index 623b87135a..857ffb3350 100644 --- a/crypto/evp/evp_rand.c +++ b/crypto/evp/evp_rand.c @@ -289,12 +289,18 @@ EVP_RAND *EVP_RAND_fetch(OSSL_LIB_CTX *libctx, const char *algorithm, int EVP_RAND_up_ref(EVP_RAND *rand) { +#ifdef OPENSSL_NO_CACHED_FETCH return evp_rand_up_ref(rand); +#else + return 1; +#endif } void EVP_RAND_free(EVP_RAND *rand) { +#ifdef OPENSSL_NO_CACHED_FETCH evp_rand_free(rand); +#endif } int evp_rand_get_number(const EVP_RAND *rand) From 2480844a113268b602e43adc48fe27a0b88a9a5c Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 8 May 2026 10:40:33 -0400 Subject: [PATCH 075/349] Make OSSL_ENCODER not do ref counting Reviewed-by: Bob Beck Reviewed-by: Nikola Pajkovsky MergeDate: Thu Jun 25 21:26:09 2026 (Merged from https://github.com/openssl/openssl/pull/31143) --- crypto/encode_decode/decoder_meth.c | 49 ++++++++++++++++++----------- crypto/encode_decode/encoder_meth.c | 22 ++++--------- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/crypto/encode_decode/decoder_meth.c b/crypto/encode_decode/decoder_meth.c index 4d460af1c2..b38f1adf5b 100644 --- a/crypto/encode_decode/decoder_meth.c +++ b/crypto/encode_decode/decoder_meth.c @@ -26,12 +26,29 @@ static void ossl_decoder_free(void *data) { - OSSL_DECODER_free(data); + OSSL_DECODER *decoder = (OSSL_DECODER *)data; + int ref = 0; + + if (decoder == NULL) + return; + + CRYPTO_DOWN_REF(&decoder->base.refcnt, &ref); + if (ref > 0) + return; + OPENSSL_free(decoder->base.name); + ossl_property_free(decoder->base.parsed_propdef); + ossl_provider_free(decoder->base.prov); + CRYPTO_FREE_REF(&decoder->base.refcnt); + OPENSSL_free(decoder); } static int ossl_decoder_up_ref(void *data) { - return OSSL_DECODER_up_ref(data); + OSSL_DECODER *decoder = (OSSL_DECODER *)data; + int ref = 0; + + CRYPTO_UP_REF(&decoder->base.refcnt, &ref); + return 1; } /* Simple method structure constructor and destructor */ @@ -51,27 +68,23 @@ static OSSL_DECODER *ossl_decoder_new(void) int OSSL_DECODER_up_ref(OSSL_DECODER *decoder) { - int ref = 0; - - CRYPTO_UP_REF(&decoder->base.refcnt, &ref); +#ifdef OSSL_DECODER_fetch + return ossl_decoder_up_ref(decoder); +#else + if (decoder->base.id == 0) + return ossl_decoder_up_ref(decoder); return 1; +#endif } void OSSL_DECODER_free(OSSL_DECODER *decoder) { - int ref = 0; - - if (decoder == NULL) - return; - - CRYPTO_DOWN_REF(&decoder->base.refcnt, &ref); - if (ref > 0) - return; - OPENSSL_free(decoder->base.name); - ossl_property_free(decoder->base.parsed_propdef); - ossl_provider_free(decoder->base.prov); - CRYPTO_FREE_REF(&decoder->base.refcnt); - OPENSSL_free(decoder); +#ifdef OSSL_DECODER_fetch + ossl_decoder_free(decoder); +#else + if (decoder != NULL && decoder->base.id == 0) + ossl_decoder_free(decoder); +#endif } /* Data to be passed through ossl_method_construct() */ diff --git a/crypto/encode_decode/encoder_meth.c b/crypto/encode_decode/encoder_meth.c index 74ba83fc3d..0c9647e51f 100644 --- a/crypto/encode_decode/encoder_meth.c +++ b/crypto/encode_decode/encoder_meth.c @@ -52,27 +52,17 @@ static OSSL_ENCODER *ossl_encoder_new(void) int OSSL_ENCODER_up_ref(OSSL_ENCODER *encoder) { - int ref = 0; - - CRYPTO_UP_REF(&encoder->base.refcnt, &ref); +#ifdef OPENSSL_NO_CACHED_FETCH + return ossl_encoder_up_ref(encoder); +#else return 1; } void OSSL_ENCODER_free(OSSL_ENCODER *encoder) { - int ref = 0; - - if (encoder == NULL) - return; - - CRYPTO_DOWN_REF(&encoder->base.refcnt, &ref); - if (ref > 0) - return; - OPENSSL_free(encoder->base.name); - ossl_property_free(encoder->base.parsed_propdef); - ossl_provider_free(encoder->base.prov); - CRYPTO_FREE_REF(&encoder->base.refcnt); - OPENSSL_free(encoder); +#ifdef OPENSSL_NO_CACHED_FETCH + ossl_encoder_free(encoder); +#endif } /* Data to be passed through ossl_method_construct() */ From 7933e2ca6ead9a2fb16aa3fde783e66386570592 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 8 May 2026 10:46:54 -0400 Subject: [PATCH 076/349] make OSSL_DECODER no do ref counting Reviewed-by: Bob Beck Reviewed-by: Nikola Pajkovsky MergeDate: Thu Jun 25 21:26:11 2026 (Merged from https://github.com/openssl/openssl/pull/31143) --- crypto/encode_decode/decoder_meth.c | 36 +++++++++++------------ crypto/encode_decode/encoder_meth.c | 44 +++++++++++++++++------------ 2 files changed, 43 insertions(+), 37 deletions(-) diff --git a/crypto/encode_decode/decoder_meth.c b/crypto/encode_decode/decoder_meth.c index b38f1adf5b..315c99d8c6 100644 --- a/crypto/encode_decode/decoder_meth.c +++ b/crypto/encode_decode/decoder_meth.c @@ -59,7 +59,7 @@ static OSSL_DECODER *ossl_decoder_new(void) if ((decoder = OPENSSL_zalloc(sizeof(*decoder))) == NULL) return NULL; if (!CRYPTO_NEW_REF(&decoder->base.refcnt, 1)) { - OSSL_DECODER_free(decoder); + ossl_decoder_free(decoder); return NULL; } @@ -68,9 +68,17 @@ static OSSL_DECODER *ossl_decoder_new(void) int OSSL_DECODER_up_ref(OSSL_DECODER *decoder) { -#ifdef OSSL_DECODER_fetch +#ifdef OPENSSL_NO_CACHED_FETCH return ossl_decoder_up_ref(decoder); #else + /* + * DECODERS do something weird. They manually build methods rather than + * attempt to fetch them from the method store or construct them through + * the ossl_generic_fetch mechanism. As such they don't make use of the refcounting + * that we rely on in the method store, and so we always need to refcount them here + * We can identify them based on the fact that they never have a registered nid (i.e. + * its always zero) + */ if (decoder->base.id == 0) return ossl_decoder_up_ref(decoder); return 1; @@ -79,7 +87,7 @@ int OSSL_DECODER_up_ref(OSSL_DECODER *decoder) void OSSL_DECODER_free(OSSL_DECODER *decoder) { -#ifdef OSSL_DECODER_fetch +#ifdef OPENSSL_NO_CACHED_FETCH ossl_decoder_free(decoder); #else if (decoder != NULL && decoder->base.id == 0) @@ -230,14 +238,14 @@ void *ossl_decoder_from_algorithm(int id, const OSSL_ALGORITHM *algodef, return NULL; decoder->base.id = id; if ((decoder->base.name = ossl_algorithm_get1_first_name(algodef)) == NULL) { - OSSL_DECODER_free(decoder); + ossl_decoder_free(decoder); return NULL; } decoder->base.algodef = algodef; if ((decoder->base.parsed_propdef = ossl_parse_property(libctx, algodef->property_definition)) == NULL) { - OSSL_DECODER_free(decoder); + ossl_decoder_free(decoder); return NULL; } @@ -289,13 +297,13 @@ void *ossl_decoder_from_algorithm(int id, const OSSL_ALGORITHM *algodef, if (!((decoder->newctx == NULL && decoder->freectx == NULL) || (decoder->newctx != NULL && decoder->freectx != NULL)) || decoder->decode == NULL) { - OSSL_DECODER_free(decoder); + ossl_decoder_free(decoder); ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_INVALID_PROVIDER_FUNCTIONS); return NULL; } if (prov != NULL && !ossl_provider_up_ref(prov)) { - OSSL_DECODER_free(decoder); + ossl_decoder_free(decoder); return NULL; } @@ -341,17 +349,7 @@ static void *construct_decoder(const OSSL_ALGORITHM *algodef, /* Intermediary function to avoid ugly casts, used below */ static void destruct_decoder(void *method, void *data) { - OSSL_DECODER_free(method); -} - -static int up_ref_decoder(void *method) -{ - return OSSL_DECODER_up_ref(method); -} - -static void free_decoder(void *method) -{ - OSSL_DECODER_free(method); + ossl_decoder_free(method); } /* Fetching support. Can fetch by numeric identity or by name */ @@ -409,7 +407,7 @@ inner_ossl_decoder_fetch(struct decoder_data_st *methdata, id = ossl_namemap_name2num(namemap, name); if (id != 0) ossl_method_store_cache_set(store, prov, id, propq, method, - up_ref_decoder, free_decoder); + ossl_decoder_up_ref, ossl_decoder_free); } /* diff --git a/crypto/encode_decode/encoder_meth.c b/crypto/encode_decode/encoder_meth.c index 0c9647e51f..611dc787d7 100644 --- a/crypto/encode_decode/encoder_meth.c +++ b/crypto/encode_decode/encoder_meth.c @@ -27,12 +27,29 @@ static void ossl_encoder_free(void *data) { - OSSL_ENCODER_free(data); + OSSL_ENCODER *encoder = (OSSL_ENCODER *)data; + int ref = 0; + + if (encoder == NULL) + return; + + CRYPTO_DOWN_REF(&encoder->base.refcnt, &ref); + if (ref > 0) + return; + OPENSSL_free(encoder->base.name); + ossl_property_free(encoder->base.parsed_propdef); + ossl_provider_free(encoder->base.prov); + CRYPTO_FREE_REF(&encoder->base.refcnt); + OPENSSL_free(encoder); } static int ossl_encoder_up_ref(void *data) { - return OSSL_ENCODER_up_ref(data); + OSSL_ENCODER *encoder = (OSSL_ENCODER *)data; + int ref = 0; + + CRYPTO_UP_REF(&encoder->base.refcnt, &ref); + return 1; } /* Simple method structure constructor and destructor */ @@ -56,6 +73,7 @@ int OSSL_ENCODER_up_ref(OSSL_ENCODER *encoder) return ossl_encoder_up_ref(encoder); #else return 1; +#endif } void OSSL_ENCODER_free(OSSL_ENCODER *encoder) @@ -208,14 +226,14 @@ static void *encoder_from_algorithm(int id, const OSSL_ALGORITHM *algodef, return NULL; encoder->base.id = id; if ((encoder->base.name = ossl_algorithm_get1_first_name(algodef)) == NULL) { - OSSL_ENCODER_free(encoder); + ossl_encoder_free(encoder); return NULL; } encoder->base.algodef = algodef; if ((encoder->base.parsed_propdef = ossl_parse_property(libctx, algodef->property_definition)) == NULL) { - OSSL_ENCODER_free(encoder); + ossl_encoder_free(encoder); return NULL; } @@ -273,13 +291,13 @@ static void *encoder_from_algorithm(int id, const OSSL_ALGORITHM *algodef, || (encoder->import_object != NULL && encoder->free_object != NULL) || (encoder->import_object == NULL && encoder->free_object == NULL)) || encoder->encode == NULL) { - OSSL_ENCODER_free(encoder); + ossl_encoder_free(encoder); ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_INVALID_PROVIDER_FUNCTIONS); return NULL; } if (prov != NULL && !ossl_provider_up_ref(prov)) { - OSSL_ENCODER_free(encoder); + ossl_encoder_free(encoder); return NULL; } @@ -325,17 +343,7 @@ static void *construct_encoder(const OSSL_ALGORITHM *algodef, /* Intermediary function to avoid ugly casts, used below */ static void destruct_encoder(void *method, void *data) { - OSSL_ENCODER_free(method); -} - -static int up_ref_encoder(void *method) -{ - return OSSL_ENCODER_up_ref(method); -} - -static void free_encoder(void *method) -{ - OSSL_ENCODER_free(method); + ossl_encoder_free(method); } /* Fetching support. Can fetch by numeric identity or by name */ @@ -392,7 +400,7 @@ inner_ossl_encoder_fetch(struct encoder_data_st *methdata, if (id == 0) id = ossl_namemap_name2num(namemap, name); ossl_method_store_cache_set(store, prov, id, propq, method, - up_ref_encoder, free_encoder); + ossl_encoder_up_ref, ossl_encoder_free); } /* From 2d71463446b2892e5c671a620fb72ac6f44c7e2c Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 8 May 2026 10:47:16 -0400 Subject: [PATCH 077/349] Makle STORE_LOADER not do ref counting Reviewed-by: Bob Beck Reviewed-by: Nikola Pajkovsky MergeDate: Thu Jun 25 21:26:13 2026 (Merged from https://github.com/openssl/openssl/pull/31143) --- crypto/store/store_meth.c | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/crypto/store/store_meth.c b/crypto/store/store_meth.c index 04c8a8d5f9..840ddc1a4a 100644 --- a/crypto/store/store_meth.c +++ b/crypto/store/store_meth.c @@ -16,8 +16,9 @@ #include "store_local.h" #include "crypto/context.h" -int OSSL_STORE_LOADER_up_ref(OSSL_STORE_LOADER *loader) +static int up_ref_loader(void *method) { + OSSL_STORE_LOADER *loader = (OSSL_STORE_LOADER *)method; int ref = 0; if (loader->prov != NULL) @@ -25,8 +26,10 @@ int OSSL_STORE_LOADER_up_ref(OSSL_STORE_LOADER *loader) return 1; } -void OSSL_STORE_LOADER_free(OSSL_STORE_LOADER *loader) +static void free_loader(void *method) { + OSSL_STORE_LOADER *loader = (OSSL_STORE_LOADER *)method; + if (loader != NULL && loader->prov != NULL) { int i; @@ -39,6 +42,22 @@ void OSSL_STORE_LOADER_free(OSSL_STORE_LOADER *loader) OPENSSL_free(loader); } +int OSSL_STORE_LOADER_up_ref(OSSL_STORE_LOADER *loader) +{ +#ifdef OPENSSL_NO_CACHED_FETCH + return up_ref_loader(loader); +#else + return 1; +#endif +} + +void OSSL_STORE_LOADER_free(OSSL_STORE_LOADER *loader) +{ +#ifdef OPENSSL_NO_CACHED_FETCH + free_loader(loader); +#endif +} + /* * OSSL_STORE_LOADER_new() expects the scheme as a constant string, * which we currently don't have, so we need an alternative allocator. @@ -61,16 +80,6 @@ static OSSL_STORE_LOADER *new_loader(OSSL_PROVIDER *prov) return loader; } -static int up_ref_loader(void *method) -{ - return OSSL_STORE_LOADER_up_ref(method); -} - -static void free_loader(void *method) -{ - OSSL_STORE_LOADER_free(method); -} - /* Data to be passed through ossl_method_construct() */ struct loader_data_st { OSSL_LIB_CTX *libctx; @@ -237,7 +246,7 @@ static void *loader_from_algorithm(int scheme_id, const OSSL_ALGORITHM *algodef, || loader->p_eof == NULL || loader->p_close == NULL) { /* Only set_ctx_params is optional */ - OSSL_STORE_LOADER_free(loader); + free_loader(loader); ERR_raise(ERR_LIB_OSSL_STORE, OSSL_STORE_R_LOADER_INCOMPLETE); return NULL; } @@ -282,7 +291,7 @@ static void *construct_loader(const OSSL_ALGORITHM *algodef, /* Intermediary function to avoid ugly casts, used below */ static void destruct_loader(void *method, void *data) { - OSSL_STORE_LOADER_free(method); + free_loader(method); } /* Fetching support. Can fetch by numeric identity or by scheme */ From 4a8052bb8bb07f36f9d3bd098fd835e9ddb04c47 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 8 May 2026 19:16:28 -0400 Subject: [PATCH 078/349] delay method store free in context_deinit_objs Reviewed-by: Bob Beck Reviewed-by: Nikola Pajkovsky MergeDate: Thu Jun 25 21:26:15 2026 (Merged from https://github.com/openssl/openssl/pull/31143) --- crypto/context.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/crypto/context.c b/crypto/context.c index 3596c0e068..66d7e42955 100644 --- a/crypto/context.c +++ b/crypto/context.c @@ -255,12 +255,6 @@ err: static void context_deinit_objs(OSSL_LIB_CTX *ctx) { - /* P2. We want evp_method_store to be cleaned up before the provider store */ - if (ctx->evp_method_store != NULL) { - ossl_method_store_free(ctx->evp_method_store); - ctx->evp_method_store = NULL; - } - /* P2. */ if (ctx->drbg != NULL) { ossl_rand_ctx_free(ctx->drbg); @@ -278,14 +272,14 @@ static void context_deinit_objs(OSSL_LIB_CTX *ctx) * P2. We want decoder_store/decoder_cache to be cleaned up before the * provider store */ - if (ctx->decoder_store != NULL) { - ossl_method_store_free(ctx->decoder_store); - ctx->decoder_store = NULL; - } if (ctx->decoder_cache != NULL) { ossl_decoder_cache_free(ctx->decoder_cache); ctx->decoder_cache = NULL; } + if (ctx->decoder_store != NULL) { + ossl_method_store_free(ctx->decoder_store); + ctx->decoder_store = NULL; + } /* P2. We want encoder_store to be cleaned up before the provider store */ if (ctx->encoder_store != NULL) { @@ -306,6 +300,12 @@ static void context_deinit_objs(OSSL_LIB_CTX *ctx) ctx->provider_store = NULL; } + /* P2. We want evp_method_store to be cleaned up before the provider store */ + if (ctx->evp_method_store != NULL) { + ossl_method_store_free(ctx->evp_method_store); + ctx->evp_method_store = NULL; + } + /* Default priority. */ if (ctx->property_string_data != NULL) { ossl_property_string_data_free(ctx->property_string_data); From 1f1ce7cad32746f2dbbba8bbc5e82492241cb94b Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Sat, 9 May 2026 10:26:57 -0400 Subject: [PATCH 079/349] Fix up property test Because the duplicate test expects the cache get operation to take a reference count (which we no longer do), we need to not count it down before checking the resulting ref count Reviewed-by: Bob Beck Reviewed-by: Nikola Pajkovsky MergeDate: Thu Jun 25 21:26:17 2026 (Merged from https://github.com/openssl/openssl/pull/31143) --- test/property_test.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/property_test.c b/test/property_test.c index f827d3c435..47422b4049 100644 --- a/test/property_test.c +++ b/test/property_test.c @@ -678,7 +678,9 @@ static int test_query_cache_set_duplicate(void) || !TEST_ptr_eq(result, &refs)) goto err; +#ifdef OPENSSL_NO_CACHED_FETCH counted_down_ref(result); +#endif result = NULL; res = 1; From 1f5a44a66df44751d999fc90751ce098975f18a6 Mon Sep 17 00:00:00 2001 From: 007bsd <22483432+007bsd@users.noreply.github.com> Date: Tue, 23 Jun 2026 21:42:29 +0300 Subject: [PATCH 080/349] crypto/x509/pcy_cache.c: fix ext_pcons leak in policy_cache_new() Two early-return paths in policy_cache_new() bypass the just_cleanup: label and leak the POLICY_CONSTRAINTS object ext_pcons: (1) when certificatePolicies is absent but policyConstraints is present, and (2) when policy_cache_create() returns <= 0. Free ext_pcons before each early return. Assisted-by: Claude:claude-sonnet-4-6 CLA: trivial Fixes: 4acc3e907d29 "Initial support for certificate policy checking and evaluation." Reviewed-by: Tomas Mraz Reviewed-by: Nikola Pajkovsky Reviewed-by: Eugene Syromiatnikov MergeDate: Fri Jun 26 15:26:07 2026 (Merged from https://github.com/openssl/openssl/pull/31678) --- crypto/x509/pcy_cache.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crypto/x509/pcy_cache.c b/crypto/x509/pcy_cache.c index d1ee35377b..bffa96fd6c 100644 --- a/crypto/x509/pcy_cache.c +++ b/crypto/x509/pcy_cache.c @@ -134,6 +134,7 @@ static int policy_cache_new(X509 *x) /* If not absent some problem with extension */ if (i != -1) goto bad_cache; + POLICY_CONSTRAINTS_free(ext_pcons); return 1; } @@ -141,8 +142,10 @@ static int policy_cache_new(X509 *x) /* NB: ext_cpols freed by policy_cache_set_policies */ - if (i <= 0) + if (i <= 0) { + POLICY_CONSTRAINTS_free(ext_pcons); return i; + } ext_pmaps = X509_get_ext_d2i(x, NID_policy_mappings, &i, NULL); From 8be7309f34eecac16b23edfa2ea8de7c52a33b3d Mon Sep 17 00:00:00 2001 From: 007bsd <22483432+007bsd@users.noreply.github.com> Date: Tue, 23 Jun 2026 21:42:33 +0300 Subject: [PATCH 081/349] crypto/x509/v3_ncons.c: fix scheme buffer leak in nc_uri() for schemeless URIs OSSL_parse_url() allocates a 1-byte empty-string buffer for the scheme even when the URI has no scheme. The empty-scheme error branch freed uri_copy and jumped to end: which only frees host, leaving scheme unfreed. Add OPENSSL_free(scheme) before the goto. Assisted-by: Claude:claude-sonnet-4-6 CLA: trivial Fixes: e599893a9fec "x509: allow SAN URIs to contain userinfo" Reviewed-by: Tomas Mraz Reviewed-by: Nikola Pajkovsky Reviewed-by: Eugene Syromiatnikov MergeDate: Fri Jun 26 15:26:09 2026 (Merged from https://github.com/openssl/openssl/pull/31678) --- crypto/x509/v3_ncons.c | 1 + 1 file changed, 1 insertion(+) diff --git a/crypto/x509/v3_ncons.c b/crypto/x509/v3_ncons.c index 1a9cf61122..8217bedc7b 100644 --- a/crypto/x509/v3_ncons.c +++ b/crypto/x509/v3_ncons.c @@ -791,6 +791,7 @@ static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base) if (scheme == NULL || *scheme == '\0') { ERR_raise_data(ERR_LIB_X509V3, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, "x509: missing scheme in URI: %s\n", uri_copy); + OPENSSL_free(scheme); OPENSSL_free(uri_copy); ret = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; goto end; From 1111694d86d1beba85db84fdf54c04f8a8d136a0 Mon Sep 17 00:00:00 2001 From: dannyward630 Date: Thu, 18 Jun 2026 19:30:54 +0200 Subject: [PATCH 082/349] doc/man7/EVP_KDF-SNMPKDF.pod: fix password parameter documentation Document the SNMPKDF "pass" parameter with OSSL_KDF_PARAM_PASSWORD, matching the implementation and other KDF documentation. Also, fix the nearby grammar. Resolves: https://github.com/openssl/openssl/issues/31543 Fixes: 1b035166bdb2 "Add SNMPKDF implementation" CLA: trivial Reviewed-by: Shane Lontis Reviewed-by: Paul Dale Reviewed-by: Eugene Syromiatnikov Reviewed-by: Norbert Pocs MergeDate: Fri Jun 26 16:29:54 2026 (Merged from https://github.com/openssl/openssl/pull/31592) --- doc/man7/EVP_KDF-SNMPKDF.pod | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/man7/EVP_KDF-SNMPKDF.pod b/doc/man7/EVP_KDF-SNMPKDF.pod index c0bcb70dee..9a2973722b 100644 --- a/doc/man7/EVP_KDF-SNMPKDF.pod +++ b/doc/man7/EVP_KDF-SNMPKDF.pod @@ -29,9 +29,9 @@ The supported parameters are: =item "digest" (B) -=item "pass" (B) +=item "pass" (B) -These parameters works as described in L. +These parameters work as described in L. =item "eid" (B) @@ -70,7 +70,7 @@ This example derives an 8 byte IV using SHA1 with a 1K "key" and appropriate *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, SN_sha1, strlen(SN_sha1)); - *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_PASS, + *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_PASSWORD, pass, sizeof(pass)); *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SNMPKDF_EID, eid, sizeof(eid)); @@ -100,7 +100,7 @@ This functionality was added in OpenSSL 4.0. =head1 COPYRIGHT -Copyright 2025 The OpenSSL Project Authors. All Rights Reserved. +Copyright 2025-2026 The OpenSSL Project Authors. All Rights Reserved. Licensed under the Apache License 2.0 (the "License"). You may not use this file except in compliance with the License. You can obtain a copy From f0d8e14c0e2035ada2301530fb540d4eaaae662c Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Tue, 23 Jun 2026 08:50:30 +0700 Subject: [PATCH 083/349] test/run_tests.pl: return error if running a non-existent test Resolves: https://github.com/openssl/openssl/issues/15510 Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Neil Horman Reviewed-by: Bob Beck Reviewed-by: Norbert Pocs Reviewed-by: Eugene Syromiatnikov MergeDate: Fri Jun 26 16:40:23 2026 (Merged from https://github.com/openssl/openssl/pull/31639) --- test/run_tests.pl | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/test/run_tests.pl b/test/run_tests.pl index f9b2d2d2c0..f31c181a3d 100644 --- a/test/run_tests.pl +++ b/test/run_tests.pl @@ -128,6 +128,7 @@ open $openssl_args{'tap_copy'}, ">$outfilename" my @alltests = find_matching_tests("*"); my %tests = (); +my $has_nonexistent_test = 0; sub reorder { my $key = pop; @@ -152,6 +153,7 @@ foreach my $arg (@ARGV ? @ARGV : ('alltests')) { warn "'alltests' encountered, ignoring everything before that...\n" unless $initial_arg; %tests = map { $_ => 1 } @alltests; + $has_nonexistent_test = 0; } elsif ($arg =~ m/^(-?)(.*)/) { my $sign = $1; my $test = $2; @@ -162,10 +164,12 @@ foreach my $arg (@ARGV ? @ARGV : ('alltests')) { %tests = map { $_ => 1 } @alltests; } + # Flag non-existent test so we can return an error if (scalar @matches == 0) { warn "Test $test found no match, skipping ", ($sign eq '-' ? "removal" : "addition"), "...\n"; + $has_nonexistent_test = 1 unless $sign eq '-'; } else { foreach $test (@matches) { if ($sign eq '-') { @@ -391,9 +395,10 @@ if (ref($ret) ne "TAP::Parser::Aggregator" || !$ret->has_errors) { # If this is a TAP::Parser::Aggregator, $ret->has_errors is the count of # tests that failed. We don't bother with that exact number, just exit -# with an appropriate exit code when it isn't zero. +# with an appropriate exit code when it isn't zero. We also return an error +# if attempting to run a non-existent test. if (ref($ret) eq "TAP::Parser::Aggregator") { - exit 0 unless $ret->has_errors; + exit 0 unless $ret->has_errors || $has_nonexistent_test; exit 1 unless $^O eq 'VMS'; # On VMS, perl converts an exit 1 to SS$_ABORT (%SYSTEM-F-ABORT), which # is a bit harsh. As per perl recommendations, we explicitly use the @@ -409,4 +414,6 @@ if (ref($ret) eq "TAP::Parser::Aggregator") { # If this isn't a TAP::Parser::Aggregator, it's the pre-TAP test harness, # which simply dies at the end if any test failed, so we don't need to bother -# with any exit code in that case. +# with any exit code in that case. The only exception is if we have a +# non-existent test). +exit 1 if $has_nonexistent_test; From 353248330ed550e9e57f1ad1a03b6305f6dc4712 Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Tue, 23 Jun 2026 08:50:48 +0700 Subject: [PATCH 084/349] Update CI to validate erroring on non-existent test selection Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Neil Horman Reviewed-by: Bob Beck Reviewed-by: Norbert Pocs Reviewed-by: Eugene Syromiatnikov MergeDate: Fri Jun 26 16:40:25 2026 (Merged from https://github.com/openssl/openssl/pull/31639) --- .github/workflows/ci.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8086c9207c..2de5c58009 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -729,10 +729,6 @@ jobs: run: | sudo apt-get update sudo apt-get -yq install bison gettext keyutils ldap-utils libldap2-dev libkeyutils-dev python3 python3-paste python3-pyrad slapd tcsh python3-virtualenv virtualenv python3-kdcproxy gdb libtls-dev wget gpg - - name: install cpanm and Test2::V0 for gost_engine testing - uses: perl-actions/install-with-cpanm@10d60f00b4073f484fc29d45bfbe2f776397ab3d # v1.7 - with: - install: Test2::V0 - name: setup hostname workaround run: sudo hostname localhost - name: config @@ -746,8 +742,9 @@ jobs: run: | cat /proc/cpuinfo ./util/opensslwrap.sh version -c - - name: test external gost-engine - run: make test TESTS="test_external_gost_engine" + - name: test failure when selecting non-existing test case + run: | + ! make test TESTS="test_external_gost_engine" - name: test external krb5 run: make test TESTS="test_external_krb5" - name: test external tlsfuzzer From 5a47730f01e8d7b56de5b78674dd2a1d80fdb310 Mon Sep 17 00:00:00 2001 From: Norbert Pocs Date: Thu, 25 Jun 2026 09:29:51 +0200 Subject: [PATCH 085/349] crypto/LPdir_wince.c: remove dead source file After removal of Win CE support in commit c60fddbbcf88 "Drop Windows CE support.", this file is no longer used. Remove it. Complements: c60fddbbcf88 "Drop Windows CE support." Reported-by: Nikola Pajkovsky Signed-off-by: Norbert Pocs Reviewed-by: Frederik Wedel-Heinen Reviewed-by: Eugene Syromiatnikov MergeDate: Fri Jun 26 17:36:16 2026 (Merged from https://github.com/openssl/openssl/pull/31718) --- crypto/LPdir_wince.c | 46 -------------------------------------------- 1 file changed, 46 deletions(-) delete mode 100644 crypto/LPdir_wince.c diff --git a/crypto/LPdir_wince.c b/crypto/LPdir_wince.c deleted file mode 100644 index e4c883dcef..0000000000 --- a/crypto/LPdir_wince.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/* - * This file is dual-licensed and is also available under the following - * terms: - * - * Copyright (c) 2004, Richard Levitte - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#define LP_SYS_WINCE -/* - * We might want to define LP_MULTIBYTE_AVAILABLE here. It's currently under - * investigation what the exact conditions would be - */ -/* clang-format off */ -#include "LPdir_win.c" -/* clang-format on */ From 87efbd952873ebc99e22edcb2f6feaa48c7683ec Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Mon, 8 Jun 2026 11:59:33 -0400 Subject: [PATCH 086/349] Untangle rats nest of .inc files for AES_GCM These files have no reason not to be just regular .c files. Additionally apply formatting fixes which were not done on .inc files Signed-off-by: Simo Sorce Reviewed-by: Dmitry Belyavskiy Reviewed-by: Norbert Pocs Reviewed-by: Shane Lontis MergeDate: Sat Jun 27 09:05:35 2026 (Merged from https://github.com/openssl/openssl/pull/31472) --- providers/fips.module.sources | 9 +- providers/implementations/ciphers/build.info | 5 +- .../implementations/ciphers/cipher_aes_gcm.h | 24 ++++ .../ciphers/cipher_aes_gcm_hw.c | 41 ++++--- ...s_avx512.inc => cipher_aes_gcm_hw_aesni.c} | 92 +++++++++----- .../ciphers/cipher_aes_gcm_hw_aesni.inc | 47 -------- .../ciphers/cipher_aes_gcm_hw_armv8.c | 113 ++++++++++++++++++ .../ciphers/cipher_aes_gcm_hw_armv8.inc | 108 ----------------- ...gcm_hw_ppc.inc => cipher_aes_gcm_hw_ppc.c} | 45 +++---- ...hw_rv32i.inc => cipher_aes_gcm_hw_rv32i.c} | 22 ++-- ...hw_rv64i.inc => cipher_aes_gcm_hw_rv64i.c} | 44 ++++--- ...hw_s390x.inc => cipher_aes_gcm_hw_s390x.c} | 49 ++++---- ...s_gcm_hw_t4.inc => cipher_aes_gcm_hw_t4.c} | 16 ++- 13 files changed, 337 insertions(+), 278 deletions(-) rename providers/implementations/ciphers/{cipher_aes_gcm_hw_vaes_avx512.inc => cipher_aes_gcm_hw_aesni.c} (71%) delete mode 100644 providers/implementations/ciphers/cipher_aes_gcm_hw_aesni.inc create mode 100644 providers/implementations/ciphers/cipher_aes_gcm_hw_armv8.c delete mode 100644 providers/implementations/ciphers/cipher_aes_gcm_hw_armv8.inc rename providers/implementations/ciphers/{cipher_aes_gcm_hw_ppc.inc => cipher_aes_gcm_hw_ppc.c} (73%) rename providers/implementations/ciphers/{cipher_aes_gcm_hw_rv32i.inc => cipher_aes_gcm_hw_rv32i.c} (80%) rename providers/implementations/ciphers/{cipher_aes_gcm_hw_rv64i.inc => cipher_aes_gcm_hw_rv64i.c} (74%) rename providers/implementations/ciphers/{cipher_aes_gcm_hw_s390x.inc => cipher_aes_gcm_hw_s390x.c} (87%) rename providers/implementations/ciphers/{cipher_aes_gcm_hw_t4.inc => cipher_aes_gcm_hw_t4.c} (80%) diff --git a/providers/fips.module.sources b/providers/fips.module.sources index f9a12d7105..c584493244 100644 --- a/providers/fips.module.sources +++ b/providers/fips.module.sources @@ -668,8 +668,13 @@ providers/implementations/ciphers/cipher_aes_cts.inc providers/implementations/ciphers/cipher_aes_gcm.c providers/implementations/ciphers/cipher_aes_gcm.h providers/implementations/ciphers/cipher_aes_gcm_hw.c -providers/implementations/ciphers/cipher_aes_gcm_hw_aesni.inc -providers/implementations/ciphers/cipher_aes_gcm_hw_vaes_avx512.inc +providers/implementations/ciphers/cipher_aes_gcm_hw_aesni.c +providers/implementations/ciphers/cipher_aes_gcm_hw_s390x.c +providers/implementations/ciphers/cipher_aes_gcm_hw_armv8.c +providers/implementations/ciphers/cipher_aes_gcm_hw_ppc.c +providers/implementations/ciphers/cipher_aes_gcm_hw_rv32i.c +providers/implementations/ciphers/cipher_aes_gcm_hw_rv64i.c +providers/implementations/ciphers/cipher_aes_gcm_hw_t4.c providers/implementations/ciphers/cipher_aes_hw.c providers/implementations/ciphers/cipher_aes_hw_aesni.inc providers/implementations/ciphers/cipher_aes_ocb.c diff --git a/providers/implementations/ciphers/build.info b/providers/implementations/ciphers/build.info index dc43b8980c..b724c0baa9 100644 --- a/providers/implementations/ciphers/build.info +++ b/providers/implementations/ciphers/build.info @@ -106,7 +106,10 @@ SOURCE[$AES_GOAL]=\ cipher_aes.c cipher_aes_hw.c \ cipher_aes_xts.c cipher_aes_xts_hw.c \ cipher_aes_cfb_hw.c \ - cipher_aes_gcm.c cipher_aes_gcm_hw.c \ + cipher_aes_gcm.c cipher_aes_gcm_hw.c cipher_aes_gcm_hw_aesni.c \ + cipher_aes_gcm_hw_armv8.c cipher_aes_gcm_hw_ppc.c \ + cipher_aes_gcm_hw_rv32i.c cipher_aes_gcm_hw_rv64i.c \ + cipher_aes_gcm_hw_s390x.c cipher_aes_gcm_hw_t4.c \ cipher_aes_ccm.c cipher_aes_ccm_hw.c \ cipher_aes_wrp.c \ cipher_aes_cbc_hmac_sha.c \ diff --git a/providers/implementations/ciphers/cipher_aes_gcm.h b/providers/implementations/ciphers/cipher_aes_gcm.h index 33819705de..2b03c584b9 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm.h +++ b/providers/implementations/ciphers/cipher_aes_gcm.h @@ -45,6 +45,30 @@ typedef struct prov_aes_gcm_ctx_st { } plat; } PROV_AES_GCM_CTX; +int generic_aes_gcm_cipher_update(PROV_GCM_CTX *ctx, const unsigned char *in, + size_t len, unsigned char *out); + const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits); +#if defined(AESNI_CAPABLE) +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_aesni(size_t keybits); +#endif +#if defined(AES_PMULL_CAPABLE) && defined(AES_GCM_ASM) +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_armv8(size_t keybits); +#endif +#if defined(PPC_AES_GCM_CAPABLE) && defined(_ARCH_PPC64) +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_ppc(size_t keybits); +#endif +#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_rv64i(size_t keybits); +#endif +#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_rv32i(size_t keybits); +#endif +#if defined(S390X_aes_128_CAPABLE) +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_s390x(size_t keybits); +#endif +#if defined(SPARC_AES_CAPABLE) +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_t4(size_t keybits); +#endif #endif /* !defined(OSSL_PROVIDERS_IMPLEMENTATIONS_CIPHERS_CIPHER_AES_GCM_H) */ diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw.c b/providers/implementations/ciphers/cipher_aes_gcm_hw.c index d2b7437cde..6d41cdb700 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw.c +++ b/providers/implementations/ciphers/cipher_aes_gcm_hw.c @@ -58,7 +58,7 @@ static int aes_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, return 1; } -static int generic_aes_gcm_cipher_update(PROV_GCM_CTX *ctx, const unsigned char *in, +int generic_aes_gcm_cipher_update(PROV_GCM_CTX *ctx, const unsigned char *in, size_t len, unsigned char *out) { if (ctx->enc) { @@ -132,23 +132,28 @@ static const PROV_GCM_HW aes_gcm = { ossl_gcm_one_shot }; -#if defined(S390X_aes_128_CAPABLE) -#include "cipher_aes_gcm_hw_s390x.inc" -#elif defined(AESNI_CAPABLE) -#include "cipher_aes_gcm_hw_aesni.inc" -#elif defined(SPARC_AES_CAPABLE) -#include "cipher_aes_gcm_hw_t4.inc" -#elif defined(AES_PMULL_CAPABLE) && defined(AES_GCM_ASM) -#include "cipher_aes_gcm_hw_armv8.inc" -#elif defined(PPC_AES_GCM_CAPABLE) && defined(_ARCH_PPC64) -#include "cipher_aes_gcm_hw_ppc.inc" -#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 -#include "cipher_aes_gcm_hw_rv64i.inc" -#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 -#include "cipher_aes_gcm_hw_rv32i.inc" -#else const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits) { - return &aes_gcm; -} + const PROV_GCM_HW *aes_gcm_hw = NULL; + +#if defined(AESNI_CAPABLE) + aes_gcm_hw = ossl_prov_aes_hw_gcm_aesni(keybits); +#elif defined(AES_PMULL_CAPABLE) && defined(AES_GCM_ASM) + aes_gcm_hw = ossl_prov_aes_hw_gcm_armv8(keybits); +#elif defined(PPC_AES_GCM_CAPABLE) && defined(_ARCH_PPC64) + aes_gcm_hw = ossl_prov_aes_hw_gcm_ppc(keybits); +#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 + aes_gcm_hw = ossl_prov_aes_hw_gcm_rv64i(keybits); +#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 + aes_gcm_hw = ossl_prov_aes_hw_gcm_rv32i(keybits); +#elif defined(S390X_aes_128_CAPABLE) + aes_gcm_hw = ossl_prov_aes_hw_gcm_s390x(keybits); +#elif defined(SPARC_AES_CAPABLE) + aes_gcm_hw = ossl_prov_aes_hw_gcm_t4(keybits); #endif + + if (aes_gcm_hw == NULL) + aes_gcm_hw = &aes_gcm; + + return aes_gcm_hw; +} diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw_vaes_avx512.inc b/providers/implementations/ciphers/cipher_aes_gcm_hw_aesni.c similarity index 71% rename from providers/implementations/ciphers/cipher_aes_gcm_hw_vaes_avx512.inc rename to providers/implementations/ciphers/cipher_aes_gcm_hw_aesni.c index 5406548f3e..d6f1af75dc 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw_vaes_avx512.inc +++ b/providers/implementations/ciphers/cipher_aes_gcm_hw_aesni.c @@ -1,6 +1,5 @@ /* - * Copyright 2021-2025 The OpenSSL Project Authors. All Rights Reserved. - * Copyright (c) 2021, Intel Corporation. All Rights Reserved. + * Copyright 2001-2026 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -8,41 +7,63 @@ * https://www.openssl.org/source/license.html */ +/*- + * AES-NI support for AES GCM. + * This file is used by cipher_aes_gcm_hw.c + */ +#include "internal/deprecated.h" +#include "cipher_aes_gcm.h" + +#if defined(AESNI_CAPABLE) + +static int aesni_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + AES_KEY *ks = &actx->ks.ks; + GCM_HW_SET_KEY_CTR_FN(ks, aesni_set_encrypt_key, aesni_encrypt, + aesni_ctr32_encrypt_blocks); + return 1; +} + +static const PROV_GCM_HW aesni_gcm = { + aesni_gcm_initkey, + ossl_gcm_setiv, + ossl_gcm_aad_update, + generic_aes_gcm_cipher_update, + ossl_gcm_cipher_final, + ossl_gcm_one_shot +}; + /*- * AVX512 VAES + VPCLMULDQD support for AES GCM. - * This file is included by cipher_aes_gcm_hw_aesni.inc */ #undef VAES_GCM_ENABLED -#if (defined(__x86_64) || defined(__x86_64__) || \ - defined(_M_AMD64) || defined(_M_X64)) -# define VAES_GCM_ENABLED +#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_X64)) +#define VAES_GCM_ENABLED /* Returns non-zero when AVX512F + VAES + VPCLMULDQD combination is available */ int ossl_vaes_vpclmulqdq_capable(void); -# define OSSL_AES_GCM_UPDATE(direction) \ - void ossl_aes_gcm_ ## direction ## _avx512(const void *ks, \ - void *gcm128ctx, \ - unsigned int *pblocklen, \ - const unsigned char *in, \ - size_t len, \ - unsigned char *out); - -OSSL_AES_GCM_UPDATE(encrypt) -OSSL_AES_GCM_UPDATE(decrypt) +void ossl_aes_gcm_encrypt_avx512(const void *ks, void *gcm128ctx, + unsigned int *pblocklen, const unsigned char *in, size_t len, + unsigned char *out); +void ossl_aes_gcm_decrypt_avx512(const void *ks, void *gcm128ctx, + unsigned int *pblocklen, const unsigned char *in, size_t len, + unsigned char *out); void ossl_aes_gcm_init_avx512(const void *ks, void *gcm128ctx); void ossl_aes_gcm_setiv_avx512(const void *ks, void *gcm128ctx, - const unsigned char *iv, size_t ivlen); + const unsigned char *iv, size_t ivlen); void ossl_aes_gcm_update_aad_avx512(void *gcm128ctx, const unsigned char *aad, - size_t aadlen); + size_t aadlen); void ossl_aes_gcm_finalize_avx512(void *gcm128ctx, unsigned int pblocklen); void ossl_gcm_gmult_avx512(uint64_t Xi[2], const void *gcm128ctx); static int vaes_gcm_setkey(PROV_GCM_CTX *ctx, const unsigned char *key, - size_t keylen) + size_t keylen) { GCM128_CONTEXT *gcmctx = &ctx->gcm; PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; @@ -59,16 +80,16 @@ static int vaes_gcm_setkey(PROV_GCM_CTX *ctx, const unsigned char *key, } static int vaes_gcm_setiv(PROV_GCM_CTX *ctx, const unsigned char *iv, - size_t ivlen) + size_t ivlen) { GCM128_CONTEXT *gcmctx = &ctx->gcm; - gcmctx->Yi.u[0] = 0; /* Current counter */ + gcmctx->Yi.u[0] = 0; /* Current counter */ gcmctx->Yi.u[1] = 0; - gcmctx->Xi.u[0] = 0; /* AAD hash */ + gcmctx->Xi.u[0] = 0; /* AAD hash */ gcmctx->Xi.u[1] = 0; - gcmctx->len.u[0] = 0; /* AAD length */ - gcmctx->len.u[1] = 0; /* Message length */ + gcmctx->len.u[0] = 0; /* AAD length */ + gcmctx->len.u[1] = 0; /* Message length */ gcmctx->ares = 0; gcmctx->mres = 0; @@ -82,8 +103,8 @@ static int vaes_gcm_setiv(PROV_GCM_CTX *ctx, const unsigned char *iv, } static int vaes_gcm_aadupdate(PROV_GCM_CTX *ctx, - const unsigned char *aad, - size_t aad_len) + const unsigned char *aad, + size_t aad_len) { GCM128_CONTEXT *gcmctx = &ctx->gcm; uint64_t alen = gcmctx->len.u[0]; @@ -143,7 +164,7 @@ static int vaes_gcm_aadupdate(PROV_GCM_CTX *ctx, } static int vaes_gcm_cipherupdate(PROV_GCM_CTX *ctx, const unsigned char *in, - size_t len, unsigned char *out) + size_t len, unsigned char *out) { GCM128_CONTEXT *gcmctx = &ctx->gcm; uint64_t mlen = gcmctx->len.u[1]; @@ -182,8 +203,7 @@ static int vaes_gcm_cipherfinal(PROV_GCM_CTX *ctx, unsigned char *tag) if (ctx->enc) { ctx->taglen = GCM_TAG_MAX_SIZE; memcpy(tag, gcmctx->Xi.c, - ctx->taglen <= sizeof(gcmctx->Xi.c) ? ctx->taglen : - sizeof(gcmctx->Xi.c)); + ctx->taglen <= sizeof(gcmctx->Xi.c) ? ctx->taglen : sizeof(gcmctx->Xi.c)); *res = 0; } else { return !CRYPTO_memcmp(gcmctx->Xi.c, tag, ctx->taglen); @@ -202,3 +222,17 @@ static const PROV_GCM_HW vaes_gcm = { }; #endif + +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_aesni(size_t keybits) +{ +#ifdef VAES_GCM_ENABLED + if (ossl_vaes_vpclmulqdq_capable()) + return &vaes_gcm; +#endif + if (AESNI_CAPABLE) + return &aesni_gcm; + + return NULL; +} + +#endif diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw_aesni.inc b/providers/implementations/ciphers/cipher_aes_gcm_hw_aesni.inc deleted file mode 100644 index 92f41b8cd6..0000000000 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw_aesni.inc +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2001-2022 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/*- - * AES-NI support for AES GCM. - * This file is included by cipher_aes_gcm_hw.c - */ - -static int aesni_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, - size_t keylen) -{ - PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; - AES_KEY *ks = &actx->ks.ks; - GCM_HW_SET_KEY_CTR_FN(ks, aesni_set_encrypt_key, aesni_encrypt, - aesni_ctr32_encrypt_blocks); - return 1; -} - -static const PROV_GCM_HW aesni_gcm = { - aesni_gcm_initkey, - ossl_gcm_setiv, - ossl_gcm_aad_update, - generic_aes_gcm_cipher_update, - ossl_gcm_cipher_final, - ossl_gcm_one_shot -}; - -#include "cipher_aes_gcm_hw_vaes_avx512.inc" - -const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits) -{ -#ifdef VAES_GCM_ENABLED - if (ossl_vaes_vpclmulqdq_capable()) - return &vaes_gcm; - else -#endif - if (AESNI_CAPABLE) - return &aesni_gcm; - else - return &aes_gcm; -} diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw_armv8.c b/providers/implementations/ciphers/cipher_aes_gcm_hw_armv8.c new file mode 100644 index 0000000000..00df7b2c0e --- /dev/null +++ b/providers/implementations/ciphers/cipher_aes_gcm_hw_armv8.c @@ -0,0 +1,113 @@ +/* + * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * Crypto extension support for AES GCM. + * This file is used by cipher_aes_gcm_hw.c + */ +#include "internal/deprecated.h" +#include "cipher_aes_gcm.h" + +#if defined(AES_PMULL_CAPABLE) && defined(AES_GCM_ASM) + +size_t armv8_aes_gcm_encrypt(const unsigned char *in, unsigned char *out, size_t len, + const void *key, unsigned char ivec[16], uint64_t *Xi) +{ + AES_KEY *aes_key = (AES_KEY *)key; + size_t align_bytes = len - len % 16; + + switch (aes_key->rounds) { + case 10: + if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { + unroll8_eor3_aes_gcm_enc_128_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } else { + aes_gcm_enc_128_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } + break; + case 12: + if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { + unroll8_eor3_aes_gcm_enc_192_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } else { + aes_gcm_enc_192_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } + break; + case 14: + if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { + unroll8_eor3_aes_gcm_enc_256_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } else { + aes_gcm_enc_256_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } + break; + } + return align_bytes; +} + +size_t armv8_aes_gcm_decrypt(const unsigned char *in, unsigned char *out, size_t len, + const void *key, unsigned char ivec[16], uint64_t *Xi) +{ + AES_KEY *aes_key = (AES_KEY *)key; + size_t align_bytes = len - len % 16; + + switch (aes_key->rounds) { + case 10: + if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { + unroll8_eor3_aes_gcm_dec_128_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } else { + aes_gcm_dec_128_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } + break; + case 12: + if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { + unroll8_eor3_aes_gcm_dec_192_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } else { + aes_gcm_dec_192_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } + break; + case 14: + if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { + unroll8_eor3_aes_gcm_dec_256_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } else { + aes_gcm_dec_256_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } + break; + } + return align_bytes; +} + +static int armv8_aes_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + AES_KEY *ks = &actx->ks.ks; + + if (AES_UNROLL12_EOR3_CAPABLE) { + GCM_HW_SET_KEY_CTR_FN(ks, aes_v8_set_encrypt_key, aes_v8_encrypt, + aes_v8_ctr32_encrypt_blocks_unroll12_eor3); + } else { + GCM_HW_SET_KEY_CTR_FN(ks, aes_v8_set_encrypt_key, aes_v8_encrypt, + aes_v8_ctr32_encrypt_blocks); + } + return 1; +} + +static const PROV_GCM_HW armv8_aes_gcm = { + armv8_aes_gcm_initkey, + ossl_gcm_setiv, + ossl_gcm_aad_update, + generic_aes_gcm_cipher_update, + ossl_gcm_cipher_final, + ossl_gcm_one_shot +}; + +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_armv8(size_t keybits) +{ + return AES_PMULL_CAPABLE ? &armv8_aes_gcm : NULL; +} + +#endif diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw_armv8.inc b/providers/implementations/ciphers/cipher_aes_gcm_hw_armv8.inc deleted file mode 100644 index 42a305a00d..0000000000 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw_armv8.inc +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/* - * Crypto extension support for AES GCM. - * This file is included by cipher_aes_gcm_hw.c - */ - -size_t armv8_aes_gcm_encrypt(const unsigned char *in, unsigned char *out, size_t len, - const void *key, unsigned char ivec[16], uint64_t *Xi) -{ - AES_KEY *aes_key = (AES_KEY *)key; - size_t align_bytes = len - len % 16; - - switch(aes_key->rounds) { - case 10: - if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { - unroll8_eor3_aes_gcm_enc_128_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); - } else { - aes_gcm_enc_128_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); - } - break; - case 12: - if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { - unroll8_eor3_aes_gcm_enc_192_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); - } else { - aes_gcm_enc_192_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); - } - break; - case 14: - if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { - unroll8_eor3_aes_gcm_enc_256_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); - } else { - aes_gcm_enc_256_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); - } - break; - } - return align_bytes; -} - -size_t armv8_aes_gcm_decrypt(const unsigned char *in, unsigned char *out, size_t len, - const void *key, unsigned char ivec[16], uint64_t *Xi) -{ - AES_KEY *aes_key = (AES_KEY *)key; - size_t align_bytes = len - len % 16; - - switch(aes_key->rounds) { - case 10: - if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { - unroll8_eor3_aes_gcm_dec_128_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); - } else { - aes_gcm_dec_128_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); - } - break; - case 12: - if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { - unroll8_eor3_aes_gcm_dec_192_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); - } else { - aes_gcm_dec_192_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); - } - break; - case 14: - if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { - unroll8_eor3_aes_gcm_dec_256_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); - } else { - aes_gcm_dec_256_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); - } - break; - } - return align_bytes; -} - -static int armv8_aes_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, - size_t keylen) -{ - PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; - AES_KEY *ks = &actx->ks.ks; - - if (AES_UNROLL12_EOR3_CAPABLE) { - GCM_HW_SET_KEY_CTR_FN(ks, aes_v8_set_encrypt_key, aes_v8_encrypt, - aes_v8_ctr32_encrypt_blocks_unroll12_eor3); - } else { - GCM_HW_SET_KEY_CTR_FN(ks, aes_v8_set_encrypt_key, aes_v8_encrypt, - aes_v8_ctr32_encrypt_blocks); - } - return 1; -} - - -static const PROV_GCM_HW armv8_aes_gcm = { - armv8_aes_gcm_initkey, - ossl_gcm_setiv, - ossl_gcm_aad_update, - generic_aes_gcm_cipher_update, - ossl_gcm_cipher_final, - ossl_gcm_one_shot -}; - -const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits) -{ - return AES_PMULL_CAPABLE ? &armv8_aes_gcm : &aes_gcm; -} diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw_ppc.inc b/providers/implementations/ciphers/cipher_aes_gcm_hw_ppc.c similarity index 73% rename from providers/implementations/ciphers/cipher_aes_gcm_hw_ppc.inc rename to providers/implementations/ciphers/cipher_aes_gcm_hw_ppc.c index 4d1238a744..0b500d64b2 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw_ppc.inc +++ b/providers/implementations/ciphers/cipher_aes_gcm_hw_ppc.c @@ -9,23 +9,27 @@ /*- * PPC support for AES GCM. - * This file is included by cipher_aes_gcm_hw.c + * This file is used by cipher_aes_gcm_hw.c */ +#include "internal/deprecated.h" +#include "cipher_aes_gcm.h" + +#if defined(PPC_AES_GCM_CAPABLE) && defined(_ARCH_PPC64) static int aes_ppc_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, - size_t keylen) + size_t keylen) { PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; AES_KEY *ks = &actx->ks.ks; GCM_HW_SET_KEY_CTR_FN(ks, aes_p8_set_encrypt_key, aes_p8_encrypt, - aes_p8_ctr32_encrypt_blocks); + aes_p8_ctr32_encrypt_blocks); return 1; } static inline uint32_t UTO32(unsigned char *buf) { - return ((uint32_t) buf[0] << 24) | ((uint32_t) buf[1] << 16) | ((uint32_t) buf[2] << 8) | ((uint32_t) buf[3]); + return ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) | ((uint32_t)buf[2] << 8) | ((uint32_t)buf[3]); } static inline uint32_t add32TOU(unsigned char buf[4], uint32_t n) @@ -34,15 +38,15 @@ static inline uint32_t add32TOU(unsigned char buf[4], uint32_t n) r = UTO32(buf); r += n; - buf[0] = (unsigned char) (r >> 24) & 0xFF; - buf[1] = (unsigned char) (r >> 16) & 0xFF; - buf[2] = (unsigned char) (r >> 8) & 0xFF; - buf[3] = (unsigned char) r & 0xFF; + buf[0] = (unsigned char)(r >> 24) & 0xFF; + buf[1] = (unsigned char)(r >> 16) & 0xFF; + buf[2] = (unsigned char)(r >> 8) & 0xFF; + buf[3] = (unsigned char)r & 0xFF; return r; } static size_t ppc_aes_gcm_crypt(const unsigned char *in, unsigned char *out, size_t len, - const void *key, unsigned char ivec[16], uint64_t *Xi, int encrypt) + const void *key, unsigned char ivec[16], uint64_t *Xi, int encrypt) { size_t s = 0; size_t ndone = 0; @@ -55,7 +59,7 @@ static size_t ppc_aes_gcm_crypt(const unsigned char *in, unsigned char *out, siz memcpy(ctr_saved, ivec, 12); while (nb) { - blocks_unused = (uint64_t) 0xffffffffU + 1 - (uint64_t) UTO32 (ivec + 12); + blocks_unused = (uint64_t)0xffffffffU + 1 - (uint64_t)UTO32(ivec + 12); if (nb > blocks_unused) { len = blocks_unused * 16; nb -= blocks_unused; @@ -71,7 +75,7 @@ static size_t ppc_aes_gcm_crypt(const unsigned char *in, unsigned char *out, siz : ppc_aes_gcm_decrypt(in, out, len, key, ivec, Xi); /* add counter to ivec */ - add32TOU(ivec + 12, (uint32_t) next_ctr); + add32TOU(ivec + 12, (uint32_t)next_ctr); if (ctr_reset) { ctr_reset = 0; in += len; @@ -85,7 +89,7 @@ static size_t ppc_aes_gcm_crypt(const unsigned char *in, unsigned char *out, siz } static int ppc_aes_gcm_cipher_update(PROV_GCM_CTX *ctx, const unsigned char *in, - size_t len, unsigned char *out) + size_t len, unsigned char *out) { if (ctx->enc) { if (ctx->ctr != NULL) { @@ -98,14 +102,14 @@ static int ppc_aes_gcm_cipher_update(PROV_GCM_CTX *ctx, const unsigned char *in, return 0; bulk = ppc_aes_gcm_crypt(in + res, out + res, len - res, - ctx->gcm.key, - ctx->gcm.Yi.c, ctx->gcm.Xi.u, 1); + ctx->gcm.key, + ctx->gcm.Yi.c, ctx->gcm.Xi.u, 1); ctx->gcm.len.u[1] += bulk; bulk += res; } if (CRYPTO_gcm128_encrypt_ctr32(&ctx->gcm, in + bulk, out + bulk, - len - bulk, ctx->ctr)) + len - bulk, ctx->ctr)) return 0; } else { if (CRYPTO_gcm128_encrypt(&ctx->gcm, in, out, len)) @@ -122,14 +126,14 @@ static int ppc_aes_gcm_cipher_update(PROV_GCM_CTX *ctx, const unsigned char *in, return 0; bulk = ppc_aes_gcm_crypt(in + res, out + res, len - res, - ctx->gcm.key, - ctx->gcm.Yi.c, ctx->gcm.Xi.u, 0); + ctx->gcm.key, + ctx->gcm.Yi.c, ctx->gcm.Xi.u, 0); ctx->gcm.len.u[1] += bulk; bulk += res; } if (CRYPTO_gcm128_decrypt_ctr32(&ctx->gcm, in + bulk, out + bulk, - len - bulk, ctx->ctr)) + len - bulk, ctx->ctr)) return 0; } else { if (CRYPTO_gcm128_decrypt(&ctx->gcm, in, out, len)) @@ -148,8 +152,9 @@ static const PROV_GCM_HW aes_ppc_gcm = { ossl_gcm_one_shot }; -const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits) +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_ppc(size_t keybits) { - return PPC_AES_GCM_CAPABLE ? &aes_ppc_gcm : &aes_gcm; + return PPC_AES_GCM_CAPABLE ? &aes_ppc_gcm : NULL; } +#endif diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw_rv32i.inc b/providers/implementations/ciphers/cipher_aes_gcm_hw_rv32i.c similarity index 80% rename from providers/implementations/ciphers/cipher_aes_gcm_hw_rv32i.inc rename to providers/implementations/ciphers/cipher_aes_gcm_hw_rv32i.c index bf3f98df16..a5dee80453 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw_rv32i.inc +++ b/providers/implementations/ciphers/cipher_aes_gcm_hw_rv32i.c @@ -9,29 +9,33 @@ /*- * RISC-V 32 ZKND ZKNE support for AES GCM. - * This file is included by cipher_aes_gcm_hw.c + * This file is used by cipher_aes_gcm_hw.c */ +#include "internal/deprecated.h" +#include "cipher_aes_gcm.h" + +#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 static int rv32i_zknd_zkne_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, - size_t keylen) + size_t keylen) { PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; AES_KEY *ks = &actx->ks.ks; GCM_HW_SET_KEY_CTR_FN(ks, rv32i_zkne_set_encrypt_key, rv32i_zkne_encrypt, - NULL); + NULL); return 1; } static int rv32i_zbkb_zknd_zkne_gcm_initkey(PROV_GCM_CTX *ctx, - const unsigned char *key, - size_t keylen) + const unsigned char *key, + size_t keylen) { PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; AES_KEY *ks = &actx->ks.ks; GCM_HW_SET_KEY_CTR_FN(ks, rv32i_zbkb_zkne_set_encrypt_key, rv32i_zkne_encrypt, - NULL); + NULL); return 1; } @@ -53,11 +57,13 @@ static const PROV_GCM_HW rv32i_zbkb_zknd_zkne_gcm = { ossl_gcm_one_shot }; -const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits) +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_rv32i(size_t keybits) { if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE()) return &rv32i_zbkb_zknd_zkne_gcm; if (RISCV_HAS_ZKND_AND_ZKNE()) return &rv32i_zknd_zkne_gcm; - return &aes_gcm; + return NULL; } + +#endif diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw_rv64i.inc b/providers/implementations/ciphers/cipher_aes_gcm_hw_rv64i.c similarity index 74% rename from providers/implementations/ciphers/cipher_aes_gcm_hw_rv64i.inc rename to providers/implementations/ciphers/cipher_aes_gcm_hw_rv64i.c index dae9beae07..74de38f175 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw_rv64i.inc +++ b/providers/implementations/ciphers/cipher_aes_gcm_hw_rv64i.c @@ -9,19 +9,23 @@ /*- * RISC-V 64 support for AES GCM. - * This file is included by cipher_aes_gcm_hw.c + * This file is used by cipher_aes_gcm_hw.c */ +#include "internal/deprecated.h" +#include "cipher_aes_gcm.h" + +#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 /*- * RISC-V 64 ZKND and ZKNE support for AES GCM. */ static int rv64i_zknd_zkne_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, - size_t keylen) + size_t keylen) { PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; AES_KEY *ks = &actx->ks.ks; GCM_HW_SET_KEY_CTR_FN(ks, rv64i_zkne_set_encrypt_key, rv64i_zkne_encrypt, - NULL); + NULL); return 1; } @@ -38,7 +42,7 @@ static const PROV_GCM_HW rv64i_zknd_zkne_gcm = { * RISC-V RV64 ZVKNED support for AES GCM. */ static int rv64i_zvkned_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, - size_t keylen) + size_t keylen) { PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; AES_KEY *ks = &actx->ks.ks; @@ -49,10 +53,10 @@ static int rv64i_zvkned_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, */ if (keylen * 8 == 128 || keylen * 8 == 256) { GCM_HW_SET_KEY_CTR_FN(ks, rv64i_zvkned_set_encrypt_key, - rv64i_zvkned_encrypt, NULL); + rv64i_zvkned_encrypt, NULL); } else { GCM_HW_SET_KEY_CTR_FN(ks, AES_set_encrypt_key, - rv64i_zvkned_encrypt, NULL); + rv64i_zvkned_encrypt, NULL); } return 1; @@ -71,8 +75,9 @@ static const PROV_GCM_HW rv64i_zvkned_gcm = { * RISC-V RV64 ZVKB, ZVKG and ZVKNED support for AES GCM. */ static int rv64i_zvkb_zvkg_zvkned_gcm_initkey(PROV_GCM_CTX *ctx, - const unsigned char *key, - size_t keylen) { + const unsigned char *key, + size_t keylen) +{ PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; AES_KEY *ks = &actx->ks.ks; @@ -82,12 +87,12 @@ static int rv64i_zvkb_zvkg_zvkned_gcm_initkey(PROV_GCM_CTX *ctx, */ if (keylen * 8 == 128 || keylen * 8 == 256) { GCM_HW_SET_KEY_CTR_FN(ks, rv64i_zvkned_set_encrypt_key, - rv64i_zvkned_encrypt, - rv64i_zvkb_zvkned_ctr32_encrypt_blocks); + rv64i_zvkned_encrypt, + rv64i_zvkb_zvkned_ctr32_encrypt_blocks); } else { GCM_HW_SET_KEY_CTR_FN(ks, AES_set_encrypt_key, - rv64i_zvkned_encrypt, - rv64i_zvkb_zvkned_ctr32_encrypt_blocks); + rv64i_zvkned_encrypt, + rv64i_zvkb_zvkned_ctr32_encrypt_blocks); } return 1; @@ -102,16 +107,19 @@ static const PROV_GCM_HW rv64i_zvkb_zvkg_zvkned_gcm = { ossl_gcm_one_shot }; -const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits) { +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_rv64i(size_t keybits) +{ if (RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) { - if (RISCV_HAS_ZVKB() && RISCV_HAS_ZVKG()) - return &rv64i_zvkb_zvkg_zvkned_gcm; - return &rv64i_zvkned_gcm; + if (RISCV_HAS_ZVKB() && RISCV_HAS_ZVKG()) + return &rv64i_zvkb_zvkg_zvkned_gcm; + return &rv64i_zvkned_gcm; } if (RISCV_HAS_ZKND_AND_ZKNE()) { - return &rv64i_zknd_zkne_gcm; + return &rv64i_zknd_zkne_gcm; } - return &aes_gcm; + return NULL; } + +#endif diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw_s390x.inc b/providers/implementations/ciphers/cipher_aes_gcm_hw_s390x.c similarity index 87% rename from providers/implementations/ciphers/cipher_aes_gcm_hw_s390x.inc rename to providers/implementations/ciphers/cipher_aes_gcm_hw_s390x.c index a36c48e3ec..4c154744e0 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw_s390x.inc +++ b/providers/implementations/ciphers/cipher_aes_gcm_hw_s390x.c @@ -9,20 +9,23 @@ /*- * IBM S390X support for AES GCM. - * This file is included by cipher_aes_gcm_hw.c + * This file is used by cipher_aes_gcm_hw.c */ +#include "internal/deprecated.h" +#include "cipher_aes_gcm.h" + +#if defined(S390X_aes_128_CAPABLE) /* iv + padding length for iv lengths != 12 */ -#define S390X_gcm_ivpadlen(i) ((((i) + 15) >> 4 << 4) + 16) +#define S390X_gcm_ivpadlen(i) ((((i) + 15) >> 4 << 4) + 16) /* Additional flag or'ed to fc for decryption */ #define S390X_gcm_decrypt_flag(ctx) (((ctx)->enc) ? 0 : S390X_DECRYPT) -#define S390X_gcm_fc(A,C) ((A)->plat.s390x.fc | (A)->plat.s390x.hsflag |\ - S390X_gcm_decrypt_flag((C))) +#define S390X_gcm_fc(A, C) ((A)->plat.s390x.fc | (A)->plat.s390x.hsflag | S390X_gcm_decrypt_flag((C))) static int s390x_aes_gcm_initkey(PROV_GCM_CTX *ctx, - const unsigned char *key, size_t keylen) + const unsigned char *key, size_t keylen) { PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; @@ -33,7 +36,7 @@ static int s390x_aes_gcm_initkey(PROV_GCM_CTX *ctx, } static int s390x_aes_gcm_setiv(PROV_GCM_CTX *ctx, const unsigned char *iv, - size_t ivlen) + size_t ivlen) { PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma; @@ -93,8 +96,8 @@ static int s390x_aes_gcm_cipher_final(PROV_GCM_CTX *ctx, unsigned char *tag) kma->tpcl <<= 3; fc = S390X_gcm_fc(actx, ctx) | S390X_KMA_LAAD | S390X_KMA_LPC; s390x_kma(actx->plat.s390x.ares, actx->plat.s390x.areslen, - actx->plat.s390x.mres, actx->plat.s390x.mreslen, out, - fc, kma); + actx->plat.s390x.mres, actx->plat.s390x.mreslen, out, + fc, kma); /* gctx->mres already returned to the caller */ OPENSSL_cleanse(out, actx->plat.s390x.mreslen); @@ -110,10 +113,10 @@ static int s390x_aes_gcm_cipher_final(PROV_GCM_CTX *ctx, unsigned char *tag) } static int s390x_aes_gcm_one_shot(PROV_GCM_CTX *ctx, - unsigned char *aad, size_t aad_len, - const unsigned char *in, size_t in_len, - unsigned char *out, - unsigned char *tag, size_t taglen) + unsigned char *aad, size_t aad_len, + const unsigned char *in, size_t in_len, + unsigned char *out, + unsigned char *tag, size_t taglen) { PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma; @@ -139,7 +142,7 @@ static int s390x_aes_gcm_one_shot(PROV_GCM_CTX *ctx, * big-endian. */ static int s390x_aes_gcm_aad_update(PROV_GCM_CTX *ctx, - const unsigned char *aad, size_t len) + const unsigned char *aad, size_t len) { PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma; @@ -203,8 +206,8 @@ static int s390x_aes_gcm_aad_update(PROV_GCM_CTX *ctx, * success. Code is big-endian. */ static int s390x_aes_gcm_cipher_update(PROV_GCM_CTX *ctx, - const unsigned char *in, size_t len, - unsigned char *out) + const unsigned char *in, size_t len, + unsigned char *out) { PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma; @@ -237,7 +240,7 @@ static int s390x_aes_gcm_cipher_update(PROV_GCM_CTX *ctx, /* ctx->mres contains a complete block if offset has wrapped around */ if (!n) { s390x_kma(actx->plat.s390x.ares, actx->plat.s390x.areslen, - actx->plat.s390x.mres, 16, buf.b, fc, kma); + actx->plat.s390x.mres, 16, buf.b, fc, kma); actx->plat.s390x.hsflag = S390X_KMA_HS; fc |= S390X_KMA_HS; actx->plat.s390x.areslen = 0; @@ -261,7 +264,7 @@ static int s390x_aes_gcm_cipher_update(PROV_GCM_CTX *ctx, len &= ~(size_t)0xf; if (len) { s390x_kma(actx->plat.s390x.ares, actx->plat.s390x.areslen, in, len, out, - fc, kma); + fc, kma); in += len; out += len; actx->plat.s390x.hsflag = S390X_KMA_HS; @@ -280,7 +283,7 @@ static int s390x_aes_gcm_cipher_update(PROV_GCM_CTX *ctx, buf.w[2] = kma->j0.w[2]; buf.w[3] = kma->cv.w + 1; s390x_km(buf.b, 16, actx->plat.s390x.kres, - fc & 0x1f, &kma->k); + fc & 0x1f, &kma->k); } n = actx->plat.s390x.mreslen; @@ -302,11 +305,13 @@ static const PROV_GCM_HW s390x_aes_gcm = { s390x_aes_gcm_one_shot }; -const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits) +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_s390x(size_t keybits) { if ((keybits == 128 && S390X_aes_128_gcm_CAPABLE) - || (keybits == 192 && S390X_aes_192_gcm_CAPABLE) - || (keybits == 256 && S390X_aes_256_gcm_CAPABLE)) + || (keybits == 192 && S390X_aes_192_gcm_CAPABLE) + || (keybits == 256 && S390X_aes_256_gcm_CAPABLE)) return &s390x_aes_gcm; - return &aes_gcm; + return NULL; } + +#endif diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw_t4.inc b/providers/implementations/ciphers/cipher_aes_gcm_hw_t4.c similarity index 80% rename from providers/implementations/ciphers/cipher_aes_gcm_hw_t4.inc rename to providers/implementations/ciphers/cipher_aes_gcm_hw_t4.c index 2b3a6d1d5e..4625c66e4d 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw_t4.inc +++ b/providers/implementations/ciphers/cipher_aes_gcm_hw_t4.c @@ -9,17 +9,20 @@ /*- * Fujitsu SPARC64 X support for AES GCM. - * This file is included by cipher_aes_gcm_hw.c + * This file is used by cipher_aes_gcm_hw.c */ +#include "internal/deprecated.h" +#include "cipher_aes_gcm.h" + +#if defined(SPARC_AES_CAPABLE) static int t4_aes_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, - size_t keylen) + size_t keylen) { ctr128_f ctr; PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; AES_KEY *ks = &actx->ks.ks; - switch (keylen) { case 16: ctr = (ctr128_f)aes128_t4_ctr32_encrypt; @@ -46,7 +49,10 @@ static const PROV_GCM_HW t4_aes_gcm = { ossl_gcm_cipher_final, ossl_gcm_one_shot }; -const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits) + +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_t4(size_t keybits) { - return SPARC_AES_CAPABLE ? &t4_aes_gcm : &aes_gcm; + return SPARC_AES_CAPABLE ? &t4_aes_gcm : NULL; } + +#endif From e1c0726f486118f417ddf26b08e8e3ccd8de34b1 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Mon, 8 Jun 2026 18:23:00 -0400 Subject: [PATCH 087/349] Replace GCM_HW_SET_KEY_CTR_FN with a function The `GCM_HW_SET_KEY_CTR_FN` macro has been removed and replaced with a proper function, `aes_gcm_hw_initkey`, for AES GCM hardware implementations. For ARIA GCM, the macro expansion was manually inlined. This refactoring eliminates a multi-line macro to improve debuggability and type safety. Crucially, the new function checks the return value of the underlying key setup routines, ensuring that key initialization failures are now correctly caught and propagated instead of being ignored. Signed-off-by: Simo Sorce Reviewed-by: Dmitry Belyavskiy Reviewed-by: Norbert Pocs Reviewed-by: Shane Lontis MergeDate: Sat Jun 27 09:05:38 2026 (Merged from https://github.com/openssl/openssl/pull/31472) --- .../implementations/ciphers/cipher_aes_gcm.h | 10 +++++ .../ciphers/cipher_aes_gcm_hw.c | 45 +++++++++++++------ .../ciphers/cipher_aes_gcm_hw_aesni.c | 7 +-- .../ciphers/cipher_aes_gcm_hw_armv8.c | 10 ++--- .../ciphers/cipher_aes_gcm_hw_ppc.c | 8 +--- .../ciphers/cipher_aes_gcm_hw_rv32i.c | 16 ++----- .../ciphers/cipher_aes_gcm_hw_rv64i.c | 37 +++++---------- .../ciphers/cipher_aes_gcm_hw_t4.c | 22 ++++----- .../ciphers/cipher_aria_gcm_hw.c | 6 ++- .../include/prov/ciphercommon_gcm.h | 6 --- 10 files changed, 80 insertions(+), 87 deletions(-) diff --git a/providers/implementations/ciphers/cipher_aes_gcm.h b/providers/implementations/ciphers/cipher_aes_gcm.h index 2b03c584b9..ef4170c255 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm.h +++ b/providers/implementations/ciphers/cipher_aes_gcm.h @@ -45,6 +45,16 @@ typedef struct prov_aes_gcm_ctx_st { } plat; } PROV_AES_GCM_CTX; +typedef void (*aes_block128_f)(const unsigned char in[16], + unsigned char out[16], const AES_KEY *key); + +typedef int (*aes_set_encrypt_key_fn)(const unsigned char *key, + int bits, AES_KEY *ks); + +int aes_gcm_hw_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, + size_t keylen, aes_set_encrypt_key_fn fn_set_key, + aes_block128_f fn_block, ctr128_f fn_ctr); + int generic_aes_gcm_cipher_update(PROV_GCM_CTX *ctx, const unsigned char *in, size_t len, unsigned char *out); diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw.c b/providers/implementations/ciphers/cipher_aes_gcm_hw.c index 6d41cdb700..bcdb879400 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw.c +++ b/providers/implementations/ciphers/cipher_aes_gcm_hw.c @@ -14,48 +14,67 @@ * non-internal use) in order to implement provider AES ciphers. */ #include "internal/deprecated.h" - +#include #include "cipher_aes_gcm.h" -static int aes_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, - size_t keylen) +int aes_gcm_hw_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, + size_t keylen, aes_set_encrypt_key_fn fn_set_key, + aes_block128_f fn_block, ctr128_f fn_ctr) { PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; AES_KEY *ks = &actx->ks.ks; + int ret = fn_set_key(key, (int)(keylen * 8), ks); + if (ret < 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED); + return 0; + } + + CRYPTO_gcm128_init(&ctx->gcm, ks, (block128_f)fn_block); + ctx->ctr = fn_ctr; + ctx->key_set = 1; + + return 1; +} + +static int aes_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ #ifdef HWAES_CAPABLE if (HWAES_CAPABLE) { #ifdef HWAES_ctr32_encrypt_blocks - GCM_HW_SET_KEY_CTR_FN(ks, HWAES_set_encrypt_key, HWAES_encrypt, - HWAES_ctr32_encrypt_blocks); + return aes_gcm_hw_initkey(ctx, key, keylen, HWAES_set_encrypt_key, + HWAES_encrypt, HWAES_ctr32_encrypt_blocks); #else - GCM_HW_SET_KEY_CTR_FN(ks, HWAES_set_encrypt_key, HWAES_encrypt, NULL); + return aes_gcm_hw_initkey(ctx, key, keylen, HWAES_set_encrypt_key, + HWAES_encrypt, NULL); #endif /* HWAES_ctr32_encrypt_blocks */ } else #endif /* HWAES_CAPABLE */ #ifdef BSAES_CAPABLE if (BSAES_CAPABLE) { - GCM_HW_SET_KEY_CTR_FN(ks, AES_set_encrypt_key, AES_encrypt, - ossl_bsaes_ctr32_encrypt_blocks); + return aes_gcm_hw_initkey(ctx, key, keylen, AES_set_encrypt_key, + AES_encrypt, (ctr128_f)ossl_bsaes_ctr32_encrypt_blocks); } else #endif /* BSAES_CAPABLE */ #ifdef VPAES_CAPABLE if (VPAES_CAPABLE) { - GCM_HW_SET_KEY_CTR_FN(ks, vpaes_set_encrypt_key, vpaes_encrypt, NULL); + return aes_gcm_hw_initkey(ctx, key, keylen, vpaes_set_encrypt_key, + vpaes_encrypt, NULL); } else #endif /* VPAES_CAPABLE */ { #ifdef AES_CTR_ASM - GCM_HW_SET_KEY_CTR_FN(ks, AES_set_encrypt_key, AES_encrypt, - AES_ctr32_encrypt); + return aes_gcm_hw_initkey(ctx, key, keylen, AES_set_encrypt_key, + AES_encrypt, (ctr128_f)AES_ctr32_encrypt); #else - GCM_HW_SET_KEY_CTR_FN(ks, AES_set_encrypt_key, AES_encrypt, NULL); + return aes_gcm_hw_initkey(ctx, key, keylen, AES_set_encrypt_key, + AES_encrypt, NULL); #endif /* AES_CTR_ASM */ } - return 1; } int generic_aes_gcm_cipher_update(PROV_GCM_CTX *ctx, const unsigned char *in, diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw_aesni.c b/providers/implementations/ciphers/cipher_aes_gcm_hw_aesni.c index d6f1af75dc..2b8938f27b 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw_aesni.c +++ b/providers/implementations/ciphers/cipher_aes_gcm_hw_aesni.c @@ -19,11 +19,8 @@ static int aesni_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, size_t keylen) { - PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; - AES_KEY *ks = &actx->ks.ks; - GCM_HW_SET_KEY_CTR_FN(ks, aesni_set_encrypt_key, aesni_encrypt, - aesni_ctr32_encrypt_blocks); - return 1; + return aes_gcm_hw_initkey(ctx, key, keylen, aesni_set_encrypt_key, + aesni_encrypt, aesni_ctr32_encrypt_blocks); } static const PROV_GCM_HW aesni_gcm = { diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw_armv8.c b/providers/implementations/ciphers/cipher_aes_gcm_hw_armv8.c index 00df7b2c0e..7d50de2a2f 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw_armv8.c +++ b/providers/implementations/ciphers/cipher_aes_gcm_hw_armv8.c @@ -83,17 +83,15 @@ size_t armv8_aes_gcm_decrypt(const unsigned char *in, unsigned char *out, size_t static int armv8_aes_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, size_t keylen) { - PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; - AES_KEY *ks = &actx->ks.ks; - if (AES_UNROLL12_EOR3_CAPABLE) { - GCM_HW_SET_KEY_CTR_FN(ks, aes_v8_set_encrypt_key, aes_v8_encrypt, + return aes_gcm_hw_initkey(ctx, key, keylen, + aes_v8_set_encrypt_key, aes_v8_encrypt, aes_v8_ctr32_encrypt_blocks_unroll12_eor3); } else { - GCM_HW_SET_KEY_CTR_FN(ks, aes_v8_set_encrypt_key, aes_v8_encrypt, + return aes_gcm_hw_initkey(ctx, key, keylen, + aes_v8_set_encrypt_key, aes_v8_encrypt, aes_v8_ctr32_encrypt_blocks); } - return 1; } static const PROV_GCM_HW armv8_aes_gcm = { diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw_ppc.c b/providers/implementations/ciphers/cipher_aes_gcm_hw_ppc.c index 0b500d64b2..c605cb0072 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw_ppc.c +++ b/providers/implementations/ciphers/cipher_aes_gcm_hw_ppc.c @@ -19,12 +19,8 @@ static int aes_ppc_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, size_t keylen) { - PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; - AES_KEY *ks = &actx->ks.ks; - - GCM_HW_SET_KEY_CTR_FN(ks, aes_p8_set_encrypt_key, aes_p8_encrypt, - aes_p8_ctr32_encrypt_blocks); - return 1; + return aes_gcm_hw_initkey(ctx, key, keylen, aes_p8_set_encrypt_key, + aes_p8_encrypt, aes_p8_ctr32_encrypt_blocks); } static inline uint32_t UTO32(unsigned char *buf) diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw_rv32i.c b/providers/implementations/ciphers/cipher_aes_gcm_hw_rv32i.c index a5dee80453..708be6aef5 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw_rv32i.c +++ b/providers/implementations/ciphers/cipher_aes_gcm_hw_rv32i.c @@ -19,24 +19,16 @@ static int rv32i_zknd_zkne_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, size_t keylen) { - PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; - AES_KEY *ks = &actx->ks.ks; - - GCM_HW_SET_KEY_CTR_FN(ks, rv32i_zkne_set_encrypt_key, rv32i_zkne_encrypt, - NULL); - return 1; + return aes_gcm_hw_initkey(ctx, key, keylen, + rv32i_zkne_set_encrypt_key, rv32i_zkne_encrypt, NULL); } static int rv32i_zbkb_zknd_zkne_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, size_t keylen) { - PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; - AES_KEY *ks = &actx->ks.ks; - - GCM_HW_SET_KEY_CTR_FN(ks, rv32i_zbkb_zkne_set_encrypt_key, rv32i_zkne_encrypt, - NULL); - return 1; + return aes_gcm_hw_initkey(ctx, key, keylen, + rv32i_zbkb_zkne_set_encrypt_key, rv32i_zkne_encrypt, NULL); } static const PROV_GCM_HW rv32i_zknd_zkne_gcm = { diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw_rv64i.c b/providers/implementations/ciphers/cipher_aes_gcm_hw_rv64i.c index 74de38f175..7d88ff7e43 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw_rv64i.c +++ b/providers/implementations/ciphers/cipher_aes_gcm_hw_rv64i.c @@ -19,14 +19,11 @@ /*- * RISC-V 64 ZKND and ZKNE support for AES GCM. */ -static int rv64i_zknd_zkne_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, - size_t keylen) +static int rv64i_zknd_zkne_gcm_initkey(PROV_GCM_CTX *ctx, + const unsigned char *key, size_t keylen) { - PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; - AES_KEY *ks = &actx->ks.ks; - GCM_HW_SET_KEY_CTR_FN(ks, rv64i_zkne_set_encrypt_key, rv64i_zkne_encrypt, - NULL); - return 1; + return aes_gcm_hw_initkey(ctx, key, keylen, + rv64i_zkne_set_encrypt_key, rv64i_zkne_encrypt, NULL); } static const PROV_GCM_HW rv64i_zknd_zkne_gcm = { @@ -44,22 +41,17 @@ static const PROV_GCM_HW rv64i_zknd_zkne_gcm = { static int rv64i_zvkned_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, size_t keylen) { - PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; - AES_KEY *ks = &actx->ks.ks; - /* * Zvkned only supports 128 and 256 bit keys for key schedule generation. * For AES-192 case, we could fallback to `AES_set_encrypt_key`. */ if (keylen * 8 == 128 || keylen * 8 == 256) { - GCM_HW_SET_KEY_CTR_FN(ks, rv64i_zvkned_set_encrypt_key, - rv64i_zvkned_encrypt, NULL); + return aes_gcm_hw_initkey(ctx, key, keylen, + rv64i_zvkned_set_encrypt_key, rv64i_zvkned_encrypt, NULL); } else { - GCM_HW_SET_KEY_CTR_FN(ks, AES_set_encrypt_key, - rv64i_zvkned_encrypt, NULL); + return aes_gcm_hw_initkey(ctx, key, keylen, + AES_set_encrypt_key, rv64i_zvkned_encrypt, NULL); } - - return 1; } static const PROV_GCM_HW rv64i_zvkned_gcm = { @@ -78,24 +70,19 @@ static int rv64i_zvkb_zvkg_zvkned_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, size_t keylen) { - PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; - AES_KEY *ks = &actx->ks.ks; - /* * Zvkned only supports 128 and 256 bit keys for key schedule generation. * For AES-192 case, we could fallback to `AES_set_encrypt_key`. */ if (keylen * 8 == 128 || keylen * 8 == 256) { - GCM_HW_SET_KEY_CTR_FN(ks, rv64i_zvkned_set_encrypt_key, - rv64i_zvkned_encrypt, + return aes_gcm_hw_initkey(ctx, key, keylen, + rv64i_zvkned_set_encrypt_key, rv64i_zvkned_encrypt, rv64i_zvkb_zvkned_ctr32_encrypt_blocks); } else { - GCM_HW_SET_KEY_CTR_FN(ks, AES_set_encrypt_key, - rv64i_zvkned_encrypt, + return aes_gcm_hw_initkey(ctx, key, keylen, + AES_set_encrypt_key, rv64i_zvkned_encrypt, rv64i_zvkb_zvkned_ctr32_encrypt_blocks); } - - return 1; } static const PROV_GCM_HW rv64i_zvkb_zvkg_zvkned_gcm = { diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw_t4.c b/providers/implementations/ciphers/cipher_aes_gcm_hw_t4.c index 4625c66e4d..4b6968581d 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw_t4.c +++ b/providers/implementations/ciphers/cipher_aes_gcm_hw_t4.c @@ -19,26 +19,22 @@ static int t4_aes_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, size_t keylen) { - ctr128_f ctr; - PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; - AES_KEY *ks = &actx->ks.ks; - switch (keylen) { case 16: - ctr = (ctr128_f)aes128_t4_ctr32_encrypt; - break; + return aes_gcm_hw_initkey(ctx, key, keylen, + aes_t4_set_encrypt_key, aes_t4_encrypt, + (ctr128_f)aes128_t4_ctr32_encrypt); case 24: - ctr = (ctr128_f)aes192_t4_ctr32_encrypt; - break; + return aes_gcm_hw_initkey(ctx, key, keylen, + aes_t4_set_encrypt_key, aes_t4_encrypt, + (ctr128_f)aes192_t4_ctr32_encrypt); case 32: - ctr = (ctr128_f)aes256_t4_ctr32_encrypt; - break; + return aes_gcm_hw_initkey(ctx, key, keylen, + aes_t4_set_encrypt_key, aes_t4_encrypt, + (ctr128_f)aes256_t4_ctr32_encrypt); default: return 0; } - - GCM_HW_SET_KEY_CTR_FN(ks, aes_t4_set_encrypt_key, aes_t4_encrypt, ctr); - return 1; } static const PROV_GCM_HW t4_aes_gcm = { diff --git a/providers/implementations/ciphers/cipher_aria_gcm_hw.c b/providers/implementations/ciphers/cipher_aria_gcm_hw.c index f4c8646103..35f3257d89 100644 --- a/providers/implementations/ciphers/cipher_aria_gcm_hw.c +++ b/providers/implementations/ciphers/cipher_aria_gcm_hw.c @@ -19,7 +19,11 @@ static int aria_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, PROV_ARIA_GCM_CTX *actx = (PROV_ARIA_GCM_CTX *)ctx; ARIA_KEY *ks = &actx->ks.ks; - GCM_HW_SET_KEY_CTR_FN(ks, ossl_aria_set_encrypt_key, ossl_aria_encrypt, NULL); + ossl_aria_set_encrypt_key(key, (int)(keylen * 8), ks); + CRYPTO_gcm128_init(&ctx->gcm, ks, (block128_f)ossl_aria_encrypt); + ctx->ctr = NULL; + ctx->key_set = 1; + return 1; } diff --git a/providers/implementations/include/prov/ciphercommon_gcm.h b/providers/implementations/include/prov/ciphercommon_gcm.h index ba5d0b5f87..08865b7595 100644 --- a/providers/implementations/include/prov/ciphercommon_gcm.h +++ b/providers/implementations/include/prov/ciphercommon_gcm.h @@ -123,10 +123,4 @@ int ossl_gcm_one_shot(PROV_GCM_CTX *ctx, unsigned char *aad, size_t aad_len, int ossl_gcm_cipher_update(PROV_GCM_CTX *ctx, const unsigned char *in, size_t len, unsigned char *out); -#define GCM_HW_SET_KEY_CTR_FN(ks, fn_set_enc_key, fn_block, fn_ctr) \ - fn_set_enc_key(key, (int)(keylen * 8), ks); \ - CRYPTO_gcm128_init(&ctx->gcm, ks, (block128_f)fn_block); \ - ctx->ctr = (ctr128_f)fn_ctr; \ - ctx->key_set = 1; - #endif From 62663738b0a73ff453f9d0bb058f69328f0893eb Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Tue, 9 Jun 2026 18:46:10 -0400 Subject: [PATCH 088/349] Refactor AES hardware cipher implementations Replace architecture-specific `.inc` files with standalone `.c` files for AES ciphers and consolidate CFB mode logic into these new modules. This eliminates the direct inclusion of source files and compiles each hardware implementation as a separate translation unit. This refactoring simplifies code organization and remove macro complexity that makes it hard to understand what is implemented and where enhancing maintainability. Signed-off-by: Simo Sorce Reviewed-by: Dmitry Belyavskiy Reviewed-by: Norbert Pocs Reviewed-by: Shane Lontis MergeDate: Sat Jun 27 09:05:40 2026 (Merged from https://github.com/openssl/openssl/pull/31472) --- .clang-format | 1 + include/crypto/aes_platform.h | 6 + providers/fips.module.sources | 10 +- providers/implementations/ciphers/build.info | 5 +- .../implementations/ciphers/cipher_aes.c | 1 - .../implementations/ciphers/cipher_aes.h | 48 ++- .../implementations/ciphers/cipher_aes_cfb.h | 21 - .../ciphers/cipher_aes_cfb_hw.c | 98 ----- .../ciphers/cipher_aes_cfb_hw_aesni.inc | 101 ----- .../ciphers/cipher_aes_cfb_hw_s390x.inc | 122 ------ .../implementations/ciphers/cipher_aes_gcm.h | 6 - .../implementations/ciphers/cipher_aes_hw.c | 369 +++++++++++------- .../ciphers/cipher_aes_hw_aesni.c | 168 ++++++++ .../ciphers/cipher_aes_hw_aesni.inc | 81 ---- .../ciphers/cipher_aes_hw_armv8.c | 46 +++ .../ciphers/cipher_aes_hw_armv8.inc | 34 -- .../ciphers/cipher_aes_hw_rv32i.c | 113 ++++++ .../ciphers/cipher_aes_hw_rv32i.inc | 102 ----- .../ciphers/cipher_aes_hw_rv64i.c | 133 +++++++ .../ciphers/cipher_aes_hw_rv64i.inc | 135 ------- .../ciphers/cipher_aes_hw_s390x.c | 240 ++++++++++++ .../ciphers/cipher_aes_hw_s390x.inc | 113 ------ .../ciphers/cipher_aes_hw_t4.c | 146 +++++++ .../ciphers/cipher_aes_hw_t4.inc | 96 ----- 24 files changed, 1145 insertions(+), 1050 deletions(-) delete mode 100644 providers/implementations/ciphers/cipher_aes_cfb.h delete mode 100644 providers/implementations/ciphers/cipher_aes_cfb_hw.c delete mode 100644 providers/implementations/ciphers/cipher_aes_cfb_hw_aesni.inc delete mode 100644 providers/implementations/ciphers/cipher_aes_cfb_hw_s390x.inc create mode 100644 providers/implementations/ciphers/cipher_aes_hw_aesni.c delete mode 100644 providers/implementations/ciphers/cipher_aes_hw_aesni.inc create mode 100644 providers/implementations/ciphers/cipher_aes_hw_armv8.c delete mode 100644 providers/implementations/ciphers/cipher_aes_hw_armv8.inc create mode 100644 providers/implementations/ciphers/cipher_aes_hw_rv32i.c delete mode 100644 providers/implementations/ciphers/cipher_aes_hw_rv32i.inc create mode 100644 providers/implementations/ciphers/cipher_aes_hw_rv64i.c delete mode 100644 providers/implementations/ciphers/cipher_aes_hw_rv64i.inc create mode 100644 providers/implementations/ciphers/cipher_aes_hw_s390x.c delete mode 100644 providers/implementations/ciphers/cipher_aes_hw_s390x.inc create mode 100644 providers/implementations/ciphers/cipher_aes_hw_t4.c delete mode 100644 providers/implementations/ciphers/cipher_aes_hw_t4.inc diff --git a/.clang-format b/.clang-format index df41044fe1..b01602a2b9 100644 --- a/.clang-format +++ b/.clang-format @@ -1375,6 +1375,7 @@ StatementMacros: - "static_ASN1_SEQUENCE_END_cb" - "static_ASN1_SEQUENCE_END_name" - "static_ASN1_SEQUENCE_END_ref" + - "PROV_CIPHER_HW_aes_mode" - "PROV_CIPHER_HW_aria_mode" - "PROV_CIPHER_HW_camellia_mode" - "PROV_CIPHER_HW_des_mode" diff --git a/include/crypto/aes_platform.h b/include/crypto/aes_platform.h index 13a31c5708..36ba2665ee 100644 --- a/include/crypto/aes_platform.h +++ b/include/crypto/aes_platform.h @@ -13,6 +13,12 @@ #include +typedef int (*aes_set_encrypt_key_fn)(const unsigned char *key, + int bits, AES_KEY *ks); + +typedef void (*aes_block128_f)(const unsigned char in[16], + unsigned char out[16], const AES_KEY *key); + #ifdef VPAES_ASM int vpaes_set_encrypt_key(const unsigned char *userKey, int bits, AES_KEY *key); diff --git a/providers/fips.module.sources b/providers/fips.module.sources index c584493244..6a9058e66b 100644 --- a/providers/fips.module.sources +++ b/providers/fips.module.sources @@ -661,9 +661,6 @@ providers/implementations/ciphers/cipher_aes_ccm.c providers/implementations/ciphers/cipher_aes_ccm.h providers/implementations/ciphers/cipher_aes_ccm_hw.c providers/implementations/ciphers/cipher_aes_ccm_hw_aesni.inc -providers/implementations/ciphers/cipher_aes_cfb.h -providers/implementations/ciphers/cipher_aes_cfb_hw.c -providers/implementations/ciphers/cipher_aes_cfb_hw_aesni.inc providers/implementations/ciphers/cipher_aes_cts.inc providers/implementations/ciphers/cipher_aes_gcm.c providers/implementations/ciphers/cipher_aes_gcm.h @@ -675,8 +672,13 @@ providers/implementations/ciphers/cipher_aes_gcm_hw_ppc.c providers/implementations/ciphers/cipher_aes_gcm_hw_rv32i.c providers/implementations/ciphers/cipher_aes_gcm_hw_rv64i.c providers/implementations/ciphers/cipher_aes_gcm_hw_t4.c +providers/implementations/ciphers/cipher_aes_hw_aesni.c +providers/implementations/ciphers/cipher_aes_hw_armv8.c +providers/implementations/ciphers/cipher_aes_hw_rv32i.c +providers/implementations/ciphers/cipher_aes_hw_rv64i.c +providers/implementations/ciphers/cipher_aes_hw_s390x.c +providers/implementations/ciphers/cipher_aes_hw_t4.c providers/implementations/ciphers/cipher_aes_hw.c -providers/implementations/ciphers/cipher_aes_hw_aesni.inc providers/implementations/ciphers/cipher_aes_ocb.c providers/implementations/ciphers/cipher_aes_ocb.h providers/implementations/ciphers/cipher_aes_ocb.inc.in diff --git a/providers/implementations/ciphers/build.info b/providers/implementations/ciphers/build.info index b724c0baa9..6f12c208c3 100644 --- a/providers/implementations/ciphers/build.info +++ b/providers/implementations/ciphers/build.info @@ -103,9 +103,10 @@ SOURCE[$NULL_GOAL]=\ cipher_null.c SOURCE[$AES_GOAL]=\ - cipher_aes.c cipher_aes_hw.c \ + cipher_aes.c cipher_aes_hw.c cipher_aes_hw_aesni.c \ + cipher_aes_hw_armv8.c cipher_aes_hw_rv32i.c cipher_aes_hw_rv64i.c \ + cipher_aes_hw_s390x.c cipher_aes_hw_t4.c \ cipher_aes_xts.c cipher_aes_xts_hw.c \ - cipher_aes_cfb_hw.c \ cipher_aes_gcm.c cipher_aes_gcm_hw.c cipher_aes_gcm_hw_aesni.c \ cipher_aes_gcm_hw_armv8.c cipher_aes_gcm_hw_ppc.c \ cipher_aes_gcm_hw_rv32i.c cipher_aes_gcm_hw_rv64i.c \ diff --git a/providers/implementations/ciphers/cipher_aes.c b/providers/implementations/ciphers/cipher_aes.c index 3444888082..3638919aeb 100644 --- a/providers/implementations/ciphers/cipher_aes.c +++ b/providers/implementations/ciphers/cipher_aes.c @@ -19,7 +19,6 @@ #include "cipher_aes.h" #include "prov/implementations.h" #include "prov/providercommon.h" -#include "cipher_aes_cfb.h" static OSSL_FUNC_cipher_freectx_fn aes_freectx; static OSSL_FUNC_cipher_dupctx_fn aes_dupctx; diff --git a/providers/implementations/ciphers/cipher_aes.h b/providers/implementations/ciphers/cipher_aes.h index 48235f32d8..c5dfbc59e2 100644 --- a/providers/implementations/ciphers/cipher_aes.h +++ b/providers/implementations/ciphers/cipher_aes.h @@ -53,10 +53,56 @@ typedef struct prov_aes_ctx_st { } PROV_AES_CTX; -#define ossl_prov_cipher_hw_aes_ofb ossl_prov_cipher_hw_aes_ofb128 +/* Note that XTS, CCM and GCM modes are handled with separate abstractions + * so they are not listed here */ +enum aes_modes { + AES_MODE_ECB = 1, + AES_MODE_CBC, + AES_MODE_CFB128, + AES_MODE_CFB8, + AES_MODE_CFB1, + AES_MODE_OFB128, + AES_MODE_CTR, +}; + const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_ecb(size_t keybits); const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_cbc(size_t keybits); +#define ossl_prov_cipher_hw_aes_cfb ossl_prov_cipher_hw_aes_cfb128 +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_cfb128(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_cfb8(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_cfb1(size_t keybits); +#define ossl_prov_cipher_hw_aes_ofb ossl_prov_cipher_hw_aes_ofb128 const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_ofb128(size_t keybits); const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_ctr(size_t keybits); +int ossl_cipher_set_aes_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen, + aes_set_encrypt_key_fn fn_set_key, aes_block128_f fn_block, + ecb128_f fn_ecb, cbc128_f fn_cbc, ctr128_f fn_ctr); + +int ossl_cipher_hw_aes_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen); + +void ossl_cipher_aes_copyctx(PROV_CIPHER_CTX *dst, const PROV_CIPHER_CTX *src); + +#if defined(AESNI_CAPABLE) +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aesni(enum aes_modes mode, + size_t keybits); +#elif defined(ARMv8_HWAES_CAPABLE) +const PROV_CIPHER_HW *ossl_prov_cipher_hw_arm(enum aes_modes mode, + size_t keybits); +#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 +const PROV_CIPHER_HW *ossl_prov_cipher_hw_rv32i(enum aes_modes mode, + size_t keybits); +#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 +const PROV_CIPHER_HW *ossl_prov_cipher_hw_rv64i(enum aes_modes mode, + size_t keybits); +#elif defined(S390X_aes_128_CAPABLE) +const PROV_CIPHER_HW *ossl_prov_cipher_hw_s390x(enum aes_modes mode, + size_t keybits); +#elif defined(SPARC_AES_CAPABLE) +const PROV_CIPHER_HW *ossl_prov_cipher_hw_t4(enum aes_modes mode, + size_t keybits); +#endif + #endif /* !defined(OSSL_PROVIDERS_IMPLEMENTATIONS_CIPHERS_CIPHER_AES_H) */ diff --git a/providers/implementations/ciphers/cipher_aes_cfb.h b/providers/implementations/ciphers/cipher_aes_cfb.h deleted file mode 100644 index 416ba585fc..0000000000 --- a/providers/implementations/ciphers/cipher_aes_cfb.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -#if !defined(OSSL_PROVIDERS_IMPLEMENTATIONS_CIPHERS_CIPHER_AES_CFB_H) -#define OSSL_PROVIDERS_IMPLEMENTATIONS_CIPHERS_CIPHER_AES_CFB_H - -#include "prov/ciphercommon.h" - -#define ossl_prov_cipher_hw_aes_cfb ossl_prov_cipher_hw_aes_cfb128 - -const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_cfb128(size_t keybits); -const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_cfb1(size_t keybits); -const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_cfb8(size_t keybits); - -#endif /* !defined(OSSL_PROVIDERS_IMPLEMENTATIONS_CIPHERS_CIPHER_AES_CFB_H) */ diff --git a/providers/implementations/ciphers/cipher_aes_cfb_hw.c b/providers/implementations/ciphers/cipher_aes_cfb_hw.c deleted file mode 100644 index a0399bcdc2..0000000000 --- a/providers/implementations/ciphers/cipher_aes_cfb_hw.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/* - * This file uses the low level AES functions (which are deprecated for - * non-internal use) in order to implement provider AES ciphers. - */ -#include "internal/deprecated.h" - -#include -#include "cipher_aes.h" -#include "cipher_aes_cfb.h" - -static int cipher_hw_aes_initkey(PROV_CIPHER_CTX *dat, - const unsigned char *key, size_t keylen) -{ - int ret; - PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; - AES_KEY *ks = &adat->ks.ks; - - dat->ks = ks; - -#ifdef HWAES_CAPABLE - if (HWAES_CAPABLE) { - ret = HWAES_set_encrypt_key(key, (int)(keylen * 8), ks); - dat->block = (block128_f)HWAES_encrypt; - dat->stream.cbc = NULL; - } else { -#endif -#ifdef VPAES_CAPABLE - if (VPAES_CAPABLE) { - ret = vpaes_set_encrypt_key(key, (int)(keylen * 8), ks); - dat->block = (block128_f)vpaes_encrypt; - dat->stream.cbc = NULL; - } else { -#endif - { - ret = AES_set_encrypt_key(key, (int)(keylen * 8), ks); - dat->block = (block128_f)AES_encrypt; - dat->stream.cbc = NULL; - } -#ifdef VPAES_CAPABLE - } -#endif -#ifdef HWAES_CAPABLE - } -#endif - - if (ret < 0) { - ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED); - return 0; - } - - return 1; -} - -IMPLEMENT_CIPHER_HW_COPYCTX(cipher_hw_aes_copyctx, PROV_AES_CTX) - -#define PROV_CIPHER_HW_aes_mode(mode) \ - static const PROV_CIPHER_HW aes_##mode = { \ - cipher_hw_aes_initkey, \ - ossl_cipher_hw_generic_##mode, \ - cipher_hw_aes_copyctx \ - }; \ - PROV_CIPHER_HW_declare(mode) \ - const PROV_CIPHER_HW * \ - ossl_prov_cipher_hw_aes_##mode(size_t keybits) \ - { \ - PROV_CIPHER_HW_select(mode) return &aes_##mode; \ - } - -#if defined(AESNI_CAPABLE) -#include "cipher_aes_cfb_hw_aesni.inc" -#elif defined(SPARC_AES_CAPABLE) -#include "cipher_aes_hw_t4.inc" -#elif defined(S390X_aes_128_CAPABLE) -#include "cipher_aes_cfb_hw_s390x.inc" -#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 -#include "cipher_aes_hw_rv64i.inc" -#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 -#include "cipher_aes_hw_rv32i.inc" -#elif defined(ARMv8_HWAES_CAPABLE) -#include "cipher_aes_hw_armv8.inc" -#else -/* The generic case */ -#define PROV_CIPHER_HW_declare(mode) -#define PROV_CIPHER_HW_select(mode) -#endif - -PROV_CIPHER_HW_aes_mode(cfb128) - PROV_CIPHER_HW_aes_mode(cfb1) - PROV_CIPHER_HW_aes_mode(cfb8) diff --git a/providers/implementations/ciphers/cipher_aes_cfb_hw_aesni.inc b/providers/implementations/ciphers/cipher_aes_cfb_hw_aesni.inc deleted file mode 100644 index eb8e0164ac..0000000000 --- a/providers/implementations/ciphers/cipher_aes_cfb_hw_aesni.inc +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2001-2025 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/*- - * AES-NI and VAES support for AES CFB mode. - * This file is included by cipher_aes_cfb_hw.c - */ - -#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_X64)) - #define cipher_hw_vaes_cfb128 aes_cfb128_vaes_encdec_wrapper -#else - #define cipher_hw_vaes_cfb128 ossl_cipher_hw_generic_cfb128 - int ossl_aes_cfb128_vaes_eligible() { - return 0; - } -#endif -#define cipher_hw_vaes_cfb8 ossl_cipher_hw_generic_cfb8 -#define cipher_hw_vaes_cfb1 ossl_cipher_hw_generic_cfb1 - -#define cipher_hw_aesni_cfb128 ossl_cipher_hw_generic_cfb128 -#define cipher_hw_aesni_cfb8 ossl_cipher_hw_generic_cfb8 -#define cipher_hw_aesni_cfb1 ossl_cipher_hw_generic_cfb1 - -static int ossl_aes_cfb8_vaes_eligible(void) { return 0; } -static int ossl_aes_cfb1_vaes_eligible(void) { return 0; } - -#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_X64)) -/* active in 64-bit builds when AES-NI, AVX512F, and VAES are detected */ -static int aes_cfb128_vaes_encdec_wrapper( - PROV_CIPHER_CTX* dat, - unsigned char *out, - const unsigned char *in, - size_t len) -{ - ossl_ssize_t num; - - num = (ossl_ssize_t)dat->num; - - if (num < 0) { - /* behavior from CRYPTO_cfb128_encrypt */ - dat->num = -1; - return 1; - } - - if (dat->enc) - ossl_aes_cfb128_vaes_enc(in, out, len, dat->ks, dat->iv, &num); - else - ossl_aes_cfb128_vaes_dec(in, out, len, dat->ks, dat->iv, &num); - - dat->num = (int)num; - - return 1; -} -#endif - -/* generates AES round keys for AES-NI and VAES implementations */ -static int cipher_hw_aesni_initkey(PROV_CIPHER_CTX *dat, - const unsigned char *key, size_t keylen) -{ - int ret; - PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; - AES_KEY *ks = &adat->ks.ks; - - dat->ks = ks; - - ret = aesni_set_encrypt_key(key, (int)(keylen * 8), ks); - - dat->block = (block128_f) aesni_encrypt; - dat->stream.cbc = NULL; - - if (ret < 0) { - ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED); - return 0; - } - - return 1; -} - -#define PROV_CIPHER_HW_declare(mode) \ -static const PROV_CIPHER_HW aesni_##mode = { \ - cipher_hw_aesni_initkey, \ - cipher_hw_aesni_##mode, \ - cipher_hw_aes_copyctx \ -}; \ -static const PROV_CIPHER_HW vaes_##mode = { \ - cipher_hw_aesni_initkey, \ - cipher_hw_vaes_##mode, \ - cipher_hw_aes_copyctx \ -}; -#define PROV_CIPHER_HW_select(mode) \ -if (AESNI_CAPABLE) { \ - if (ossl_aes_##mode##_vaes_eligible()) \ - return &vaes_##mode; \ - return &aesni_##mode; \ -} diff --git a/providers/implementations/ciphers/cipher_aes_cfb_hw_s390x.inc b/providers/implementations/ciphers/cipher_aes_cfb_hw_s390x.inc deleted file mode 100644 index 113c1de6a9..0000000000 --- a/providers/implementations/ciphers/cipher_aes_cfb_hw_s390x.inc +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2001-2025 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/* - * IBM S390X support for AES mode cfb. - * This file is included by cipher_aes_cfb_hw.c - */ - -#include "arch/s390x_arch.h" - -#include - -#define s390x_aes_cfb1_initkey cipher_hw_aes_initkey -#define s390x_aes_cfb1_cipher_hw ossl_cipher_hw_generic_cfb1 - -#define S390X_aes_128_cfb128_CAPABLE S390X_aes_128_cfb_CAPABLE -#define S390X_aes_192_cfb128_CAPABLE S390X_aes_192_cfb_CAPABLE -#define S390X_aes_256_cfb128_CAPABLE S390X_aes_256_cfb_CAPABLE - -static int s390x_aes_cfb128_initkey(PROV_CIPHER_CTX *dat, - const unsigned char *key, size_t keylen) -{ - PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; - - adat->plat.s390x.fc = S390X_AES_FC(keylen); - adat->plat.s390x.fc |= 16 << 24; /* 16 bytes cipher feedback */ - memcpy(adat->plat.s390x.param.kmo_kmf.k, key, keylen); - return 1; -} - -static int s390x_aes_cfb128_cipher_hw(PROV_CIPHER_CTX *dat, unsigned char *out, - const unsigned char *in, size_t len) -{ - PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; - unsigned int modifier = adat->base.enc ? 0 : S390X_DECRYPT; - int n = dat->num; - int rem; - unsigned char tmp; - - memcpy(adat->plat.s390x.param.kmo_kmf.cv, dat->iv, dat->ivlen); - while (n && len) { - tmp = *in; - *out = adat->plat.s390x.param.kmo_kmf.cv[n] ^ tmp; - adat->plat.s390x.param.kmo_kmf.cv[n] = dat->enc ? *out : tmp; - n = (n + 1) & 0xf; - --len; - ++in; - ++out; - } - - rem = len & 0xf; - - len &= ~(size_t)0xf; - if (len) { - s390x_kmf(in, len, out, adat->plat.s390x.fc | modifier, - &adat->plat.s390x.param.kmo_kmf); - - out += len; - in += len; - } - - if (rem) { - s390x_km(adat->plat.s390x.param.kmo_kmf.cv, 16, - adat->plat.s390x.param.kmo_kmf.cv, - S390X_AES_FC(dat->keylen), - adat->plat.s390x.param.kmo_kmf.k); - - while (rem--) { - tmp = in[n]; - out[n] = adat->plat.s390x.param.kmo_kmf.cv[n] ^ tmp; - adat->plat.s390x.param.kmo_kmf.cv[n] = dat->enc ? out[n] : tmp; - ++n; - } - } - - memcpy(dat->iv, adat->plat.s390x.param.kmo_kmf.cv, dat->ivlen); - dat->num = n; - return 1; -} - -static int s390x_aes_cfb8_initkey(PROV_CIPHER_CTX *dat, - const unsigned char *key, size_t keylen) -{ - PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; - - adat->plat.s390x.fc = S390X_AES_FC(keylen); - adat->plat.s390x.fc |= 1 << 24; /* 1 byte cipher feedback */ - memcpy(adat->plat.s390x.param.kmo_kmf.k, key, keylen); - return 1; -} - -static int s390x_aes_cfb8_cipher_hw(PROV_CIPHER_CTX *dat, unsigned char *out, - const unsigned char *in, size_t len) -{ - PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; - unsigned int modifier = adat->base.enc ? 0 : S390X_DECRYPT; - - memcpy(adat->plat.s390x.param.kmo_kmf.cv, dat->iv, dat->ivlen); - s390x_kmf(in, len, out, adat->plat.s390x.fc | modifier, - &adat->plat.s390x.param.kmo_kmf); - memcpy(dat->iv, adat->plat.s390x.param.kmo_kmf.cv, dat->ivlen); - return 1; -} - -#define PROV_CIPHER_HW_declare(mode) \ -static const PROV_CIPHER_HW s390x_aes_##mode = { \ - s390x_aes_##mode##_initkey, \ - s390x_aes_##mode##_cipher_hw, \ - cipher_hw_aes_copyctx \ -}; -#define PROV_CIPHER_HW_select(mode) \ -if ((keybits == 128 && S390X_aes_128_##mode##_CAPABLE) \ - || (keybits == 192 && S390X_aes_192_##mode##_CAPABLE) \ - || (keybits == 256 && S390X_aes_256_##mode##_CAPABLE)) \ - return &s390x_aes_##mode; - diff --git a/providers/implementations/ciphers/cipher_aes_gcm.h b/providers/implementations/ciphers/cipher_aes_gcm.h index ef4170c255..d8ce0b4563 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm.h +++ b/providers/implementations/ciphers/cipher_aes_gcm.h @@ -45,12 +45,6 @@ typedef struct prov_aes_gcm_ctx_st { } plat; } PROV_AES_GCM_CTX; -typedef void (*aes_block128_f)(const unsigned char in[16], - unsigned char out[16], const AES_KEY *key); - -typedef int (*aes_set_encrypt_key_fn)(const unsigned char *key, - int bits, AES_KEY *ks); - int aes_gcm_hw_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, size_t keylen, aes_set_encrypt_key_fn fn_set_key, aes_block128_f fn_block, ctr128_f fn_ctr); diff --git a/providers/implementations/ciphers/cipher_aes_hw.c b/providers/implementations/ciphers/cipher_aes_hw.c index da9a6729d0..5ad1d81e94 100644 --- a/providers/implementations/ciphers/cipher_aes_hw.c +++ b/providers/implementations/ciphers/cipher_aes_hw.c @@ -1,5 +1,5 @@ /* - * Copyright 2001-2025 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2001-2026 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -16,149 +16,252 @@ #include #include "cipher_aes.h" -static int cipher_hw_aes_initkey(PROV_CIPHER_CTX *dat, - const unsigned char *key, size_t keylen) +int ossl_cipher_set_aes_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen, + aes_set_encrypt_key_fn fn_set_key, aes_block128_f fn_block, + ecb128_f fn_ecb, cbc128_f fn_cbc, ctr128_f fn_ctr) { - int ret; - PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; - AES_KEY *ks = &adat->ks.ks; - - dat->ks = ks; - - if ((dat->mode == EVP_CIPH_ECB_MODE || dat->mode == EVP_CIPH_CBC_MODE) - && !dat->enc) { -#ifdef HWAES_CAPABLE - if (HWAES_CAPABLE) { - ret = HWAES_set_decrypt_key(key, keylen * 8, ks); - dat->block = (block128_f)HWAES_decrypt; - dat->stream.cbc = NULL; -#ifdef HWAES_cbc_encrypt - if (dat->mode == EVP_CIPH_CBC_MODE) - dat->stream.cbc = (cbc128_f)HWAES_cbc_encrypt; -#endif -#ifdef HWAES_ecb_encrypt - if (dat->mode == EVP_CIPH_ECB_MODE) - dat->stream.ecb = (ecb128_f)HWAES_ecb_encrypt; -#endif - } else -#endif -#ifdef BSAES_CAPABLE - if (BSAES_CAPABLE && dat->mode == EVP_CIPH_CBC_MODE) { - ret = AES_set_decrypt_key(key, (int)(keylen * 8), ks); - dat->block = (block128_f)AES_decrypt; - dat->stream.cbc = (cbc128_f)ossl_bsaes_cbc_encrypt; - } else -#endif -#ifdef VPAES_CAPABLE - if (VPAES_CAPABLE) { - ret = vpaes_set_decrypt_key(key, (int)(keylen * 8), ks); - dat->block = (block128_f)vpaes_decrypt; - dat->stream.cbc = (dat->mode == EVP_CIPH_CBC_MODE) - ? (cbc128_f)vpaes_cbc_encrypt - : NULL; - } else -#endif - { - ret = AES_set_decrypt_key(key, (int)(keylen * 8), ks); - dat->block = (block128_f)AES_decrypt; - dat->stream.cbc = (dat->mode == EVP_CIPH_CBC_MODE) - ? (cbc128_f)AES_cbc_encrypt - : NULL; - } - } else -#ifdef HWAES_CAPABLE - if (HWAES_CAPABLE) { - ret = HWAES_set_encrypt_key(key, keylen * 8, ks); - dat->block = (block128_f)HWAES_encrypt; - dat->stream.cbc = NULL; -#ifdef HWAES_cbc_encrypt - if (dat->mode == EVP_CIPH_CBC_MODE) - dat->stream.cbc = (cbc128_f)HWAES_cbc_encrypt; - else -#endif -#ifdef HWAES_ecb_encrypt - if (dat->mode == EVP_CIPH_ECB_MODE) - dat->stream.ecb = (ecb128_f)HWAES_ecb_encrypt; - else -#endif -#ifdef HWAES_ctr32_encrypt_blocks - if (dat->mode == EVP_CIPH_CTR_MODE) - dat->stream.ctr = (ctr128_f)HWAES_ctr32_encrypt_blocks; - else -#endif - (void)0; /* terminate potentially open 'else' */ - } else -#endif -#ifdef BSAES_CAPABLE - if (BSAES_CAPABLE && dat->mode == EVP_CIPH_CTR_MODE) { - ret = AES_set_encrypt_key(key, (int)(keylen * 8), ks); - dat->block = (block128_f)AES_encrypt; - dat->stream.ctr = (ctr128_f)ossl_bsaes_ctr32_encrypt_blocks; - } else -#endif -#ifdef VPAES_CAPABLE - if (VPAES_CAPABLE) { - ret = vpaes_set_encrypt_key(key, (int)(keylen * 8), ks); - dat->block = (block128_f)vpaes_encrypt; - dat->stream.cbc = (dat->mode == EVP_CIPH_CBC_MODE) - ? (cbc128_f)vpaes_cbc_encrypt - : NULL; - } else -#endif - { - ret = AES_set_encrypt_key(key, (int)(keylen * 8), ks); - dat->block = (block128_f)AES_encrypt; - dat->stream.cbc = (dat->mode == EVP_CIPH_CBC_MODE) - ? (cbc128_f)AES_cbc_encrypt - : NULL; -#ifdef AES_CTR_ASM - if (dat->mode == EVP_CIPH_CTR_MODE) - dat->stream.ctr = (ctr128_f)AES_ctr32_encrypt; -#endif - } + PROV_AES_CTX *actx = (PROV_AES_CTX *)ctx; + AES_KEY *ks = &actx->ks.ks; + int ret = fn_set_key(key, (int)(keylen * 8), ks); if (ret < 0) { ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED); return 0; } + ctx->ks = ks; + + ctx->block = (block128_f)fn_block; + + switch (ctx->mode) { + case EVP_CIPH_ECB_MODE: + ctx->stream.ecb = fn_ecb; + break; + case EVP_CIPH_CBC_MODE: + ctx->stream.cbc = fn_cbc; + break; + case EVP_CIPH_CTR_MODE: + ctx->stream.ctr = fn_ctr; + break; + default: + memset(&ctx->stream, 0, sizeof(ctx->stream)); + break; + } return 1; } -IMPLEMENT_CIPHER_HW_COPYCTX(cipher_hw_aes_copyctx, PROV_AES_CTX) - -#define PROV_CIPHER_HW_aes_mode(mode) \ - static const PROV_CIPHER_HW aes_##mode = { \ - cipher_hw_aes_initkey, \ - ossl_cipher_hw_generic_##mode, \ - cipher_hw_aes_copyctx \ - }; \ - PROV_CIPHER_HW_declare(mode) \ - const PROV_CIPHER_HW * \ - ossl_prov_cipher_hw_aes_##mode(size_t keybits) \ - { \ - PROV_CIPHER_HW_select(mode) return &aes_##mode; \ +#ifdef HWAES_CAPABLE +static int hwaes_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + if (HWAES_CAPABLE) { + ecb128_f fn_ecb = NULL; + cbc128_f fn_cbc = NULL; + ctr128_f fn_ctr = NULL; +#ifdef HWAES_ecb_encrypt + fn_ecb = (ecb128_f)HWAES_ecb_encrypt; +#endif +#ifdef HWAES_cbc_encrypt + fn_cbc = (cbc128_f)HWAES_cbc_encrypt; +#endif +#ifdef HWAES_ctr32_encrypt_blocks + fn_ctr = (ctr128_f)HWAES_ctr32_encrypt_blocks; +#endif + if ((ctx->mode == EVP_CIPH_ECB_MODE || ctx->mode == EVP_CIPH_CBC_MODE) + && !ctx->enc) + return ossl_cipher_set_aes_initkey(ctx, key, keylen, + HWAES_set_decrypt_key, HWAES_decrypt, fn_ecb, fn_cbc, fn_ctr); + else + return ossl_cipher_set_aes_initkey(ctx, key, keylen, + HWAES_set_encrypt_key, HWAES_encrypt, fn_ecb, fn_cbc, fn_ctr); } + return -1; +} +#endif /* HWAES_CAPABLE */ -#if defined(AESNI_CAPABLE) -#include "cipher_aes_hw_aesni.inc" -#elif defined(SPARC_AES_CAPABLE) -#include "cipher_aes_hw_t4.inc" -#elif defined(S390X_aes_128_CAPABLE) -#include "cipher_aes_hw_s390x.inc" -#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 -#include "cipher_aes_hw_rv64i.inc" -#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 -#include "cipher_aes_hw_rv32i.inc" -#elif defined(ARMv8_HWAES_CAPABLE) -#include "cipher_aes_hw_armv8.inc" -#else -/* The generic case */ -#define PROV_CIPHER_HW_declare(mode) -#define PROV_CIPHER_HW_select(mode) +#ifdef BSAES_CAPABLE +static int bsaes_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + if (BSAES_CAPABLE) { + if (ctx->mode == EVP_CIPH_CBC_MODE && !ctx->enc) + return ossl_cipher_set_aes_initkey(ctx, key, keylen, + AES_set_decrypt_key, AES_decrypt, NULL, + (cbc128_f)ossl_bsaes_cbc_encrypt, NULL); + else if (ctx->mode == EVP_CIPH_CTR_MODE) + return ossl_cipher_set_aes_initkey(ctx, key, keylen, + AES_set_encrypt_key, AES_encrypt, NULL, NULL, + (ctr128_f)ossl_bsaes_ctr32_encrypt_blocks); + } + return -1; +} +#endif /* BSAES_CAPABLE */ + +#ifdef VPAES_CAPABLE +static int vpaes_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + if (VPAES_CAPABLE) { + if ((ctx->mode == EVP_CIPH_ECB_MODE || ctx->mode == EVP_CIPH_CBC_MODE) + && !ctx->enc) { + return ossl_cipher_set_aes_initkey(ctx, key, keylen, + vpaes_set_decrypt_key, vpaes_decrypt, NULL, + (cbc128_f)vpaes_cbc_encrypt, NULL); + } else { + return ossl_cipher_set_aes_initkey(ctx, key, keylen, + vpaes_set_encrypt_key, vpaes_encrypt, NULL, + (cbc128_f)vpaes_cbc_encrypt, NULL); + } + } + return -1; +} +#endif /* VPAES_CAPABLE */ + +int ossl_cipher_hw_aes_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + int ret = 0; + +#ifdef HWAES_CAPABLE + ret = hwaes_initkey(ctx, key, keylen); + if (ret >= 0) + return ret; #endif -PROV_CIPHER_HW_aes_mode(cbc) - PROV_CIPHER_HW_aes_mode(ecb) - PROV_CIPHER_HW_aes_mode(ofb128) - PROV_CIPHER_HW_aes_mode(ctr) +#ifdef BSAES_CAPABLE + ret = bsaes_initkey(ctx, key, keylen); + if (ret >= 0) + return ret; +#endif + +#ifdef VPAES_CAPABLE + ret = vpaes_initkey(ctx, key, keylen); + if (ret >= 0) + return ret; +#endif + + if ((ctx->mode == EVP_CIPH_ECB_MODE || ctx->mode == EVP_CIPH_CBC_MODE) + && !ctx->enc) { + ret = ossl_cipher_set_aes_initkey(ctx, key, keylen, + AES_set_decrypt_key, AES_decrypt, NULL, (cbc128_f)AES_cbc_encrypt, + NULL); + } else { + ctr128_f fn_ctr = NULL; +#ifdef AES_CTR_ASM + fn_ctr = (ctr128_f)AES_ctr32_encrypt; +#endif + ret = ossl_cipher_set_aes_initkey(ctx, key, keylen, + AES_set_encrypt_key, AES_encrypt, NULL, (cbc128_f)AES_cbc_encrypt, + fn_ctr); + } + + return ret; +} + +void ossl_cipher_aes_copyctx(PROV_CIPHER_CTX *dst, + const PROV_CIPHER_CTX *src) +{ + PROV_AES_CTX *sctx = (PROV_AES_CTX *)src; + PROV_AES_CTX *dctx = (PROV_AES_CTX *)dst; + + *dctx = *sctx; + dst->ks = &dctx->ks.ks; +} + +static const PROV_CIPHER_HW aes_ecb = { + ossl_cipher_hw_aes_initkey, + ossl_cipher_hw_generic_ecb, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW aes_cbc = { + ossl_cipher_hw_aes_initkey, + ossl_cipher_hw_generic_cbc, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW aes_cfb128 = { + ossl_cipher_hw_aes_initkey, + ossl_cipher_hw_generic_cfb128, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW aes_cfb8 = { + ossl_cipher_hw_aes_initkey, + ossl_cipher_hw_generic_cfb8, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW aes_cfb1 = { + ossl_cipher_hw_aes_initkey, + ossl_cipher_hw_generic_cfb1, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW aes_ofb128 = { + ossl_cipher_hw_aes_initkey, + ossl_cipher_hw_generic_ofb128, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW aes_ctr = { + ossl_cipher_hw_aes_initkey, + ossl_cipher_hw_generic_ctr, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_mode(enum aes_modes mode, + size_t keybits) +{ + const PROV_CIPHER_HW *aes_hw_mode = NULL; + +#if defined(AESNI_CAPABLE) + aes_hw_mode = ossl_prov_cipher_hw_aesni(mode, keybits); +#elif defined(ARMv8_HWAES_CAPABLE) + aes_hw_mode = ossl_prov_cipher_hw_arm(mode, keybits); +#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 + aes_hw_mode = ossl_prov_cipher_hw_rv32i(mode, keybits); +#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 + aes_hw_mode = ossl_prov_cipher_hw_rv64i(mode, keybits); +#elif defined(S390X_aes_128_CAPABLE) + aes_hw_mode = ossl_prov_cipher_hw_s390x(mode, keybits); +#elif defined(SPARC_AES_CAPABLE) + aes_hw_mode = ossl_prov_cipher_hw_t4(mode, keybits); +#endif + + if (aes_hw_mode == NULL) { + switch (mode) { + case AES_MODE_ECB: + return &aes_ecb; + case AES_MODE_CBC: + return &aes_cbc; + case AES_MODE_CFB128: + return &aes_cfb128; + case AES_MODE_CFB8: + return &aes_cfb8; + case AES_MODE_CFB1: + return &aes_cfb1; + case AES_MODE_OFB128: + return &aes_ofb128; + case AES_MODE_CTR: + return &aes_ctr; + } + } + + return aes_hw_mode; +} + +#define PROV_CIPHER_HW_aes_mode(ENUM_MODE, mode) \ + const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_##mode(size_t keybits) \ + { \ + return ossl_prov_cipher_hw_aes_mode(ENUM_MODE, keybits); \ + } + +PROV_CIPHER_HW_aes_mode(AES_MODE_ECB, ecb) +PROV_CIPHER_HW_aes_mode(AES_MODE_CBC, cbc) +PROV_CIPHER_HW_aes_mode(AES_MODE_CFB128, cfb128) +PROV_CIPHER_HW_aes_mode(AES_MODE_CFB8, cfb8) +PROV_CIPHER_HW_aes_mode(AES_MODE_CFB1, cfb1) +PROV_CIPHER_HW_aes_mode(AES_MODE_OFB128, ofb128) +PROV_CIPHER_HW_aes_mode(AES_MODE_CTR, ctr) diff --git a/providers/implementations/ciphers/cipher_aes_hw_aesni.c b/providers/implementations/ciphers/cipher_aes_hw_aesni.c new file mode 100644 index 0000000000..d434874a43 --- /dev/null +++ b/providers/implementations/ciphers/cipher_aes_hw_aesni.c @@ -0,0 +1,168 @@ +/* + * Copyright 2001-2025 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/*- + * AES-NI support for AES modes ecb, cbc, ofb, cfb, ctr. + * This file is used by cipher_aes_hw.c + */ + +#include "internal/deprecated.h" +#include "cipher_aes.h" + +#if defined(AESNI_CAPABLE) + +/* generates AES round keys for AES-NI and VAES implementations */ +static int cipher_hw_aesni_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + if ((ctx->mode == EVP_CIPH_ECB_MODE || ctx->mode == EVP_CIPH_CBC_MODE) + && !ctx->enc) { + return ossl_cipher_set_aes_initkey(ctx, key, keylen, + aesni_set_decrypt_key, aesni_decrypt, NULL, + (cbc128_f)aesni_cbc_encrypt, NULL); + } else { + return ossl_cipher_set_aes_initkey(ctx, key, keylen, + aesni_set_encrypt_key, aesni_encrypt, NULL, + (cbc128_f)aesni_cbc_encrypt, (ctr128_f)aesni_ctr32_encrypt_blocks); + } +} + +static int cipher_hw_aesni_ecb(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t len) +{ + if (len < ctx->blocksize) + return 1; + + aesni_ecb_encrypt(in, out, len, ctx->ks, ctx->enc); + + return 1; +} + +static const PROV_CIPHER_HW aesni_ecb = { + cipher_hw_aesni_initkey, + cipher_hw_aesni_ecb, + ossl_cipher_aes_copyctx +}; + +static int cipher_hw_aesni_cbc(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t len) +{ + aesni_cbc_encrypt(in, out, len, ctx->ks, ctx->iv, ctx->enc); + + return 1; +} + +static const PROV_CIPHER_HW aesni_cbc = { + cipher_hw_aesni_initkey, + cipher_hw_aesni_cbc, + ossl_cipher_aes_copyctx +}; + +#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_X64)) +/* active in 64-bit builds when AES-NI, AVX512F, and VAES are detected */ +#define VAES_CFB128_ELIGIBLE 1 +#else +#define VAES_CFB128_ELIGIBLE 0 +#endif + +#if VAES_CFB128_ELIGIBLE +static int aes_cfb128_vaes_encdec_wrapper( + PROV_CIPHER_CTX *ctx, + unsigned char *out, + const unsigned char *in, + size_t len) +{ + ossl_ssize_t num; + + num = (ossl_ssize_t)ctx->num; + + if (num < 0) { + /* behavior from CRYPTO_cfb128_encrypt */ + ctx->num = -1; + return 1; + } + + if (ctx->enc) + ossl_aes_cfb128_vaes_enc(in, out, len, ctx->ks, ctx->iv, &num); + else + ossl_aes_cfb128_vaes_dec(in, out, len, ctx->ks, ctx->iv, &num); + + ctx->num = (int)num; + + return 1; +} + +static const PROV_CIPHER_HW aesni_vaes_cfb128 = { + cipher_hw_aesni_initkey, + aes_cfb128_vaes_encdec_wrapper, + ossl_cipher_aes_copyctx +}; +#endif /* VAES_CFB128_ELIGIBLE */ + +static const PROV_CIPHER_HW aesni_cfb128 = { + cipher_hw_aesni_initkey, + ossl_cipher_hw_generic_cfb128, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW aesni_cfb8 = { + cipher_hw_aesni_initkey, + ossl_cipher_hw_generic_cfb8, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW aesni_cfb1 = { + cipher_hw_aesni_initkey, + ossl_cipher_hw_generic_cfb1, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW aesni_ofb128 = { + cipher_hw_aesni_initkey, + ossl_cipher_hw_generic_ofb128, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW aesni_ctr = { + cipher_hw_aesni_initkey, + ossl_cipher_hw_generic_ctr, + ossl_cipher_aes_copyctx +}; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aesni(enum aes_modes mode, + size_t keybits) +{ + if (AESNI_CAPABLE) { + switch (mode) { + case AES_MODE_ECB: + return &aesni_ecb; + case AES_MODE_CBC: + return &aesni_cbc; + case AES_MODE_CFB128: +#if VAES_CFB128_ELIGIBLE + if (ossl_aes_cfb128_vaes_eligible()) + return &aesni_vaes_cfb128; +#endif + return &aesni_cfb128; + case AES_MODE_CFB8: + return &aesni_cfb8; + case AES_MODE_CFB1: + return &aesni_cfb1; + case AES_MODE_OFB128: + return &aesni_ofb128; + case AES_MODE_CTR: + return &aesni_ctr; + default: + return NULL; + } + } + return NULL; +} + +#endif diff --git a/providers/implementations/ciphers/cipher_aes_hw_aesni.inc b/providers/implementations/ciphers/cipher_aes_hw_aesni.inc deleted file mode 100644 index 7b7084aea7..0000000000 --- a/providers/implementations/ciphers/cipher_aes_hw_aesni.inc +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2001-2025 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/*- - * AES-NI support for AES modes ecb, cbc, ofb, ctr. - * This file is included by cipher_aes_hw.c - */ - -#define cipher_hw_aesni_ofb128 ossl_cipher_hw_generic_ofb128 -#define cipher_hw_aesni_ctr ossl_cipher_hw_generic_ctr - -static int cipher_hw_aesni_initkey(PROV_CIPHER_CTX *dat, - const unsigned char *key, size_t keylen) -{ - int ret; - PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; - AES_KEY *ks = &adat->ks.ks; - - dat->ks = ks; - - if ((dat->mode == EVP_CIPH_ECB_MODE || dat->mode == EVP_CIPH_CBC_MODE) - && !dat->enc) { - ret = aesni_set_decrypt_key(key, (int)(keylen * 8), ks); - dat->block = (block128_f) aesni_decrypt; - dat->stream.cbc = dat->mode == EVP_CIPH_CBC_MODE ? - (cbc128_f) aesni_cbc_encrypt : NULL; - } else { - ret = aesni_set_encrypt_key(key, (int)(keylen * 8), ks); - dat->block = (block128_f) aesni_encrypt; - if (dat->mode == EVP_CIPH_CBC_MODE) - dat->stream.cbc = (cbc128_f) aesni_cbc_encrypt; - else if (dat->mode == EVP_CIPH_CTR_MODE) - dat->stream.ctr = (ctr128_f) aesni_ctr32_encrypt_blocks; - else - dat->stream.cbc = NULL; - } - - if (ret < 0) { - ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED); - return 0; - } - - return 1; -} - -static int cipher_hw_aesni_cbc(PROV_CIPHER_CTX *ctx, unsigned char *out, - const unsigned char *in, size_t len) -{ - const AES_KEY *ks = ctx->ks; - - aesni_cbc_encrypt(in, out, len, ks, ctx->iv, ctx->enc); - - return 1; -} - -static int cipher_hw_aesni_ecb(PROV_CIPHER_CTX *ctx, unsigned char *out, - const unsigned char *in, size_t len) -{ - if (len < ctx->blocksize) - return 1; - - aesni_ecb_encrypt(in, out, len, ctx->ks, ctx->enc); - - return 1; -} - -#define PROV_CIPHER_HW_declare(mode) \ -static const PROV_CIPHER_HW aesni_##mode = { \ - cipher_hw_aesni_initkey, \ - cipher_hw_aesni_##mode, \ - cipher_hw_aes_copyctx \ -}; -#define PROV_CIPHER_HW_select(mode) \ -if (AESNI_CAPABLE) \ - return &aesni_##mode; diff --git a/providers/implementations/ciphers/cipher_aes_hw_armv8.c b/providers/implementations/ciphers/cipher_aes_hw_armv8.c new file mode 100644 index 0000000000..773e40a411 --- /dev/null +++ b/providers/implementations/ciphers/cipher_aes_hw_armv8.c @@ -0,0 +1,46 @@ +/* + * Copyright 2023 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * Crypto extension support for AES modes ecb, cbc, ofb, cfb, ctr. + * This file is used by cipher_aes_hw.c + */ + +#include "internal/deprecated.h" +#include "cipher_aes.h" + +#if defined(ARMv8_HWAES_CAPABLE) + +static int cipher_hw_aes_arm_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + if (!ossl_cipher_hw_aes_initkey(ctx, key, keylen)) + return 0; + + if (AES_UNROLL12_EOR3_CAPABLE && ctx->mode == EVP_CIPH_CTR_MODE) + ctx->stream.ctr = (ctr128_f)HWAES_ctr32_encrypt_blocks_unroll12_eor3; + + return 1; +} + +static const PROV_CIPHER_HW arm_ctr = { + cipher_hw_aes_arm_initkey, + ossl_cipher_hw_generic_ctr, + ossl_cipher_aes_copyctx +}; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_arm(enum aes_modes mode, + size_t keybits) +{ + if (ARMv8_HWAES_CAPABLE && mode == AES_MODE_CTR) + return &arm_ctr; + return NULL; +} + +#endif diff --git a/providers/implementations/ciphers/cipher_aes_hw_armv8.inc b/providers/implementations/ciphers/cipher_aes_hw_armv8.inc deleted file mode 100644 index 3f73c79290..0000000000 --- a/providers/implementations/ciphers/cipher_aes_hw_armv8.inc +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2023 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/* - * Crypto extension support for AES modes ecb, cbc, ofb, cfb, ctr. - * This file is included by cipher_aes_hw.c - */ - -static int cipher_hw_aes_arm_initkey(PROV_CIPHER_CTX *dat, - const unsigned char *key, - size_t keylen) -{ - int ret = cipher_hw_aes_initkey(dat, key, keylen); - if (AES_UNROLL12_EOR3_CAPABLE && dat->mode == EVP_CIPH_CTR_MODE) - dat->stream.ctr = (ctr128_f)HWAES_ctr32_encrypt_blocks_unroll12_eor3; - - return ret; -} - -#define PROV_CIPHER_HW_declare(mode) \ -static const PROV_CIPHER_HW aes_arm_##mode = { \ - cipher_hw_aes_arm_initkey, \ - ossl_cipher_hw_generic_##mode, \ - cipher_hw_aes_copyctx \ -}; -#define PROV_CIPHER_HW_select(mode) \ -if (ARMv8_HWAES_CAPABLE) \ - return &aes_arm_##mode; diff --git a/providers/implementations/ciphers/cipher_aes_hw_rv32i.c b/providers/implementations/ciphers/cipher_aes_hw_rv32i.c new file mode 100644 index 0000000000..375f9dc946 --- /dev/null +++ b/providers/implementations/ciphers/cipher_aes_hw_rv32i.c @@ -0,0 +1,113 @@ +/* + * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/*- + * RISC-V 32 ZKND ZKNE support for AES modes ecb, cbc, ofb, cfb, ctr. + * This file is used by cipher_aes_hw.c + */ + +#include "internal/deprecated.h" +#include "cipher_aes.h" + +#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 + +static int cipher_hw_rv32i_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + if ((ctx->mode == EVP_CIPH_ECB_MODE || ctx->mode == EVP_CIPH_CBC_MODE) + && !ctx->enc) { + if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE()) + return ossl_cipher_set_aes_initkey(ctx, key, keylen, + rv32i_zbkb_zknd_zkne_set_decrypt_key, rv32i_zknd_decrypt, + NULL, NULL, NULL); + else + return ossl_cipher_set_aes_initkey(ctx, key, keylen, + rv32i_zknd_zkne_set_decrypt_key, rv32i_zknd_decrypt, + NULL, NULL, NULL); + } else { + if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE()) + return ossl_cipher_set_aes_initkey(ctx, key, keylen, + rv32i_zbkb_zkne_set_encrypt_key, rv32i_zkne_encrypt, + NULL, NULL, NULL); + else + return ossl_cipher_set_aes_initkey(ctx, key, keylen, + rv32i_zkne_set_encrypt_key, rv32i_zkne_encrypt, + NULL, NULL, NULL); + } +} + +static const PROV_CIPHER_HW rv32i_ecb = { + cipher_hw_rv32i_initkey, + ossl_cipher_hw_generic_ecb, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW rv32i_cbc = { + cipher_hw_rv32i_initkey, + ossl_cipher_hw_generic_cbc, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW rv32i_cfb128 = { + cipher_hw_rv32i_initkey, + ossl_cipher_hw_generic_cfb128, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW rv32i_cfb8 = { + cipher_hw_rv32i_initkey, + ossl_cipher_hw_generic_cfb8, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW rv32i_cfb1 = { + cipher_hw_rv32i_initkey, + ossl_cipher_hw_generic_cfb1, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW rv32i_ofb128 = { + cipher_hw_rv32i_initkey, + ossl_cipher_hw_generic_ofb128, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW rv32i_ctr = { + cipher_hw_rv32i_initkey, + ossl_cipher_hw_generic_ctr, + ossl_cipher_aes_copyctx +}; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_rv32i(enum aes_modes mode, + size_t keybits) +{ + if (RISCV_HAS_ZKND_AND_ZKNE()) { + switch (mode) { + case AES_MODE_ECB: + return &rv32i_ecb; + case AES_MODE_CBC: + return &rv32i_cbc; + case AES_MODE_CFB128: + return &rv32i_cfb128; + case AES_MODE_CFB8: + return &rv32i_cfb8; + case AES_MODE_CFB1: + return &rv32i_cfb1; + case AES_MODE_OFB128: + return &rv32i_ofb128; + case AES_MODE_CTR: + return &rv32i_ctr; + default: + return NULL; + } + } + return NULL; +} + +#endif diff --git a/providers/implementations/ciphers/cipher_aes_hw_rv32i.inc b/providers/implementations/ciphers/cipher_aes_hw_rv32i.inc deleted file mode 100644 index f6c652c32d..0000000000 --- a/providers/implementations/ciphers/cipher_aes_hw_rv32i.inc +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/*- - * RISC-V 32 ZKND ZKNE support for AES modes ecb, cbc, ofb, cfb, ctr. - * This file is included by cipher_aes_hw.c - */ - -#define cipher_hw_rv32i_zknd_zkne_cbc ossl_cipher_hw_generic_cbc -#define cipher_hw_rv32i_zknd_zkne_ecb ossl_cipher_hw_generic_ecb -#define cipher_hw_rv32i_zknd_zkne_ofb128 ossl_cipher_hw_generic_ofb128 -#define cipher_hw_rv32i_zknd_zkne_cfb128 ossl_cipher_hw_generic_cfb128 -#define cipher_hw_rv32i_zknd_zkne_cfb8 ossl_cipher_hw_generic_cfb8 -#define cipher_hw_rv32i_zknd_zkne_cfb1 ossl_cipher_hw_generic_cfb1 -#define cipher_hw_rv32i_zknd_zkne_ctr ossl_cipher_hw_generic_ctr - -#define cipher_hw_rv32i_zbkb_zknd_zkne_cbc ossl_cipher_hw_generic_cbc -#define cipher_hw_rv32i_zbkb_zknd_zkne_ecb ossl_cipher_hw_generic_ecb -#define cipher_hw_rv32i_zbkb_zknd_zkne_ofb128 ossl_cipher_hw_generic_ofb128 -#define cipher_hw_rv32i_zbkb_zknd_zkne_cfb128 ossl_cipher_hw_generic_cfb128 -#define cipher_hw_rv32i_zbkb_zknd_zkne_cfb8 ossl_cipher_hw_generic_cfb8 -#define cipher_hw_rv32i_zbkb_zknd_zkne_cfb1 ossl_cipher_hw_generic_cfb1 -#define cipher_hw_rv32i_zbkb_zknd_zkne_ctr ossl_cipher_hw_generic_ctr - -static int cipher_hw_rv32i_zknd_zkne_initkey(PROV_CIPHER_CTX *dat, - const unsigned char *key, size_t keylen) -{ - int ret; - PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; - AES_KEY *ks = &adat->ks.ks; - - dat->ks = ks; - - if ((dat->mode == EVP_CIPH_ECB_MODE || dat->mode == EVP_CIPH_CBC_MODE) - && !dat->enc) { - ret = rv32i_zknd_zkne_set_decrypt_key(key, keylen * 8, ks); - dat->block = (block128_f) rv32i_zknd_decrypt; - dat->stream.cbc = NULL; - } else { - ret = rv32i_zkne_set_encrypt_key(key, keylen * 8, ks); - dat->block = (block128_f) rv32i_zkne_encrypt; - dat->stream.cbc = NULL; - } - - if (ret < 0) { - ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED); - return 0; - } - - return 1; -} - -static int cipher_hw_rv32i_zbkb_zknd_zkne_initkey(PROV_CIPHER_CTX *dat, - const unsigned char *key, size_t keylen) -{ - int ret; - PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; - AES_KEY *ks = &adat->ks.ks; - - dat->ks = ks; - - if ((dat->mode == EVP_CIPH_ECB_MODE || dat->mode == EVP_CIPH_CBC_MODE) - && !dat->enc) { - ret = rv32i_zbkb_zknd_zkne_set_decrypt_key(key, keylen * 8, ks); - dat->block = (block128_f) rv32i_zknd_decrypt; - dat->stream.cbc = NULL; - } else { - ret = rv32i_zbkb_zkne_set_encrypt_key(key, keylen * 8, ks); - dat->block = (block128_f) rv32i_zkne_encrypt; - dat->stream.cbc = NULL; - } - - if (ret < 0) { - ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED); - return 0; - } - - return 1; -} - -#define PROV_CIPHER_HW_declare(mode) \ -static const PROV_CIPHER_HW rv32i_zknd_zkne_##mode = { \ - cipher_hw_rv32i_zknd_zkne_initkey, \ - cipher_hw_rv32i_zknd_zkne_##mode, \ - cipher_hw_aes_copyctx \ -}; \ -static const PROV_CIPHER_HW rv32i_zbkb_zknd_zkne_##mode = { \ - cipher_hw_rv32i_zbkb_zknd_zkne_initkey, \ - cipher_hw_rv32i_zbkb_zknd_zkne_##mode, \ - cipher_hw_aes_copyctx \ -}; -#define PROV_CIPHER_HW_select(mode) \ -if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE()) \ - return &rv32i_zbkb_zknd_zkne_##mode; \ -if (RISCV_HAS_ZKND_AND_ZKNE()) \ - return &rv32i_zknd_zkne_##mode; diff --git a/providers/implementations/ciphers/cipher_aes_hw_rv64i.c b/providers/implementations/ciphers/cipher_aes_hw_rv64i.c new file mode 100644 index 0000000000..e13c46813e --- /dev/null +++ b/providers/implementations/ciphers/cipher_aes_hw_rv64i.c @@ -0,0 +1,133 @@ +/* + * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/*- + * RISC-V 64 ZKND ZKNE / ZVKNED support for AES modes ecb, cbc, ofb, cfb, ctr. + * This file is used by cipher_aes_hw.c + */ + +#include "internal/deprecated.h" +#include "cipher_aes.h" + +#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 + +static int cipher_hw_rv64i_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + if (RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) { + /* + * Zvkned only supports 128 and 256 bit keys for key schedule + * generation. For the AES-192 case, we fallback to the generic + * `AES_set_encrypt_key`. All Zvkned-based implementations use the + * same `encrypt-key` scheduling for both encryption and decryption. + */ + aes_set_encrypt_key_fn fn_set_key = AES_set_encrypt_key; + aes_block128_f fn_block = NULL; + + if (keylen * 8 == 128 || keylen * 8 == 256) { + fn_set_key = rv64i_zvkned_set_encrypt_key; + } + ecb128_f fn_ecb = ctx->enc ? rv64i_zvkned_ecb_encrypt : rv64i_zvkned_ecb_decrypt; + cbc128_f fn_cbc = ctx->enc ? rv64i_zvkned_cbc_encrypt : rv64i_zvkned_cbc_decrypt; + ctr128_f fn_ctr = RISCV_HAS_ZVKB() ? (ctr128_f)rv64i_zvkb_zvkned_ctr32_encrypt_blocks : NULL; + + /* Zvkned supports aes-128/192/256 encryption and decryption. */ + if ((ctx->mode == EVP_CIPH_ECB_MODE || ctx->mode == EVP_CIPH_CBC_MODE) + && !ctx->enc) { + fn_block = rv64i_zvkned_decrypt; + } else { + fn_block = (block128_f)rv64i_zvkned_encrypt; + } + return ossl_cipher_set_aes_initkey(ctx, key, keylen, fn_set_key, + fn_block, fn_ecb, fn_cbc, fn_ctr); + } else if (RISCV_HAS_ZKND_AND_ZKNE()) { + if ((ctx->mode == EVP_CIPH_ECB_MODE || ctx->mode == EVP_CIPH_CBC_MODE) + && !ctx->enc) { + return ossl_cipher_set_aes_initkey(ctx, key, keylen, + rv64i_zknd_set_decrypt_key, rv64i_zknd_decrypt, + NULL, NULL, NULL); + } else { + return ossl_cipher_set_aes_initkey(ctx, key, keylen, + rv64i_zkne_set_encrypt_key, rv64i_zkne_encrypt, + NULL, NULL, NULL); + } + } + return 0; +} + +static const PROV_CIPHER_HW rv64i_ecb = { + cipher_hw_rv64i_initkey, + ossl_cipher_hw_generic_ecb, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW rv64i_cbc = { + cipher_hw_rv64i_initkey, + ossl_cipher_hw_generic_cbc, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW rv64i_cfb128 = { + cipher_hw_rv64i_initkey, + ossl_cipher_hw_generic_cfb128, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW rv64i_cfb8 = { + cipher_hw_rv64i_initkey, + ossl_cipher_hw_generic_cfb8, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW rv64i_cfb1 = { + cipher_hw_rv64i_initkey, + ossl_cipher_hw_generic_cfb1, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW rv64i_ofb128 = { + cipher_hw_rv64i_initkey, + ossl_cipher_hw_generic_ofb128, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW rv64i_ctr = { + cipher_hw_rv64i_initkey, + ossl_cipher_hw_generic_ctr, + ossl_cipher_aes_copyctx +}; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_rv64i(enum aes_modes mode, + size_t keybits) +{ + if ((RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) + || RISCV_HAS_ZKND_AND_ZKNE()) { + switch (mode) { + case AES_MODE_ECB: + return &rv64i_ecb; + case AES_MODE_CBC: + return &rv64i_cbc; + case AES_MODE_CFB128: + return &rv64i_cfb128; + case AES_MODE_CFB8: + return &rv64i_cfb8; + case AES_MODE_CFB1: + return &rv64i_cfb1; + case AES_MODE_OFB128: + return &rv64i_ofb128; + case AES_MODE_CTR: + return &rv64i_ctr; + default: + return NULL; + } + } + return NULL; +} + +#endif diff --git a/providers/implementations/ciphers/cipher_aes_hw_rv64i.inc b/providers/implementations/ciphers/cipher_aes_hw_rv64i.inc deleted file mode 100644 index 07d479303d..0000000000 --- a/providers/implementations/ciphers/cipher_aes_hw_rv64i.inc +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/*- - * RISC-V 64 ZKND ZKNE support for AES modes ecb, cbc, ofb, cfb, ctr. - * This file is included by cipher_aes_hw.c - */ - -#define cipher_hw_rv64i_zknd_zkne_cbc ossl_cipher_hw_generic_cbc -#define cipher_hw_rv64i_zknd_zkne_ecb ossl_cipher_hw_generic_ecb -#define cipher_hw_rv64i_zknd_zkne_ofb128 ossl_cipher_hw_generic_ofb128 -#define cipher_hw_rv64i_zknd_zkne_cfb128 ossl_cipher_hw_generic_cfb128 -#define cipher_hw_rv64i_zknd_zkne_cfb8 ossl_cipher_hw_generic_cfb8 -#define cipher_hw_rv64i_zknd_zkne_cfb1 ossl_cipher_hw_generic_cfb1 -#define cipher_hw_rv64i_zknd_zkne_ctr ossl_cipher_hw_generic_ctr - -static int cipher_hw_rv64i_zknd_zkne_initkey(PROV_CIPHER_CTX *dat, - const unsigned char *key, size_t keylen) -{ - int ret; - PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; - AES_KEY *ks = &adat->ks.ks; - - dat->ks = ks; - - if ((dat->mode == EVP_CIPH_ECB_MODE || dat->mode == EVP_CIPH_CBC_MODE) - && !dat->enc) { - ret = rv64i_zknd_set_decrypt_key(key, keylen * 8, ks); - dat->block = (block128_f) rv64i_zknd_decrypt; - dat->stream.cbc = NULL; - } else { - ret = rv64i_zkne_set_encrypt_key(key, keylen * 8, ks); - dat->block = (block128_f) rv64i_zkne_encrypt; - dat->stream.cbc = NULL; - } - - if (ret < 0) { - ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED); - return 0; - } - - return 1; -} - -/*- - * RISC-V RV64 ZVKNED support for AES modes ecb, cbc, ofb, cfb, ctr. - * This file is included by cipher_aes_hw.c - */ - -#define cipher_hw_rv64i_zvkned_cbc ossl_cipher_hw_generic_cbc -#define cipher_hw_rv64i_zvkned_ecb ossl_cipher_hw_generic_ecb -#define cipher_hw_rv64i_zvkned_ofb128 ossl_cipher_hw_generic_ofb128 -#define cipher_hw_rv64i_zvkned_cfb128 ossl_cipher_hw_generic_cfb128 -#define cipher_hw_rv64i_zvkned_cfb8 ossl_cipher_hw_generic_cfb8 -#define cipher_hw_rv64i_zvkned_cfb1 ossl_cipher_hw_generic_cfb1 -#define cipher_hw_rv64i_zvkned_ctr ossl_cipher_hw_generic_ctr - -static int cipher_hw_rv64i_zvkned_initkey(PROV_CIPHER_CTX *dat, - const unsigned char *key, - size_t keylen) -{ - int ret; - PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; - AES_KEY *ks = &adat->ks.ks; - - dat->ks = ks; - - /* - * Zvkned only supports 128 and 256 bit keys for key schedule generation. - * For AES-192 case, we could fallback to `AES_set_encrypt_key`. - * All Zvkned-based implementations use the same `encrypt-key` scheduling - * for both encryption and decryption. - */ - if (keylen * 8 == 128 || keylen * 8 == 256) { - ret = rv64i_zvkned_set_encrypt_key(key, keylen * 8, ks); - } else { - ret = AES_set_encrypt_key(key, keylen * 8, ks); - } - - if (dat->mode == EVP_CIPH_CBC_MODE) { - if (dat->enc) { - dat->stream.cbc = (cbc128_f) rv64i_zvkned_cbc_encrypt; - } else { - dat->stream.cbc = (cbc128_f) rv64i_zvkned_cbc_decrypt; - } - } else if (dat->mode == EVP_CIPH_CTR_MODE) { - if (RISCV_HAS_ZVKB()) { - dat->stream.ctr = (ctr128_f) rv64i_zvkb_zvkned_ctr32_encrypt_blocks; - } - } else if (dat->mode == EVP_CIPH_ECB_MODE) { - if (dat->enc) { - dat->stream.ecb = (ecb128_f) rv64i_zvkned_ecb_encrypt; - } else { - dat->stream.ecb = (ecb128_f) rv64i_zvkned_ecb_decrypt; - } - } - - /* Zvkned supports aes-128/192/256 encryption and decryption. */ - if ((dat->mode == EVP_CIPH_ECB_MODE || dat->mode == EVP_CIPH_CBC_MODE) && - !dat->enc) { - dat->block = (block128_f) rv64i_zvkned_decrypt; - } else { - dat->block = (block128_f) rv64i_zvkned_encrypt; - } - - if (ret < 0) { - ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED); - return 0; - } - - return 1; -} - -#define PROV_CIPHER_HW_declare(mode) \ -static const PROV_CIPHER_HW rv64i_zknd_zkne_##mode = { \ - cipher_hw_rv64i_zknd_zkne_initkey, \ - cipher_hw_rv64i_zknd_zkne_##mode, \ - cipher_hw_aes_copyctx \ -}; \ -static const PROV_CIPHER_HW rv64i_zvkned_##mode = { \ - cipher_hw_rv64i_zvkned_initkey, \ - cipher_hw_rv64i_zvkned_##mode, \ - cipher_hw_aes_copyctx \ -}; -#define PROV_CIPHER_HW_select(mode) \ -if (RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) \ - return &rv64i_zvkned_##mode; \ -else if (RISCV_HAS_ZKND_AND_ZKNE()) \ - return &rv64i_zknd_zkne_##mode; diff --git a/providers/implementations/ciphers/cipher_aes_hw_s390x.c b/providers/implementations/ciphers/cipher_aes_hw_s390x.c new file mode 100644 index 0000000000..30d30d5de7 --- /dev/null +++ b/providers/implementations/ciphers/cipher_aes_hw_s390x.c @@ -0,0 +1,240 @@ +/* + * Copyright 2001-2025 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * IBM S390X support for AES modes ecb, cbc, ofb, cfb, ctr. + * This file is used by cipher_aes_hw.c + */ + +#include "internal/deprecated.h" +#include "cipher_aes.h" +#include "arch/s390x_arch.h" +#include + +#if defined(S390X_aes_128_CAPABLE) + +static int s390x_aes_ecb_initkey(PROV_CIPHER_CTX *dat, + const unsigned char *key, size_t keylen) +{ + PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; + + adat->plat.s390x.fc = S390X_AES_FC(keylen); + memcpy(adat->plat.s390x.param.km.k, key, keylen); + return 1; +} + +static int s390x_aes_ecb_cipher_hw(PROV_CIPHER_CTX *dat, unsigned char *out, + const unsigned char *in, size_t len) +{ + PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; + unsigned int modifier = adat->base.enc ? 0 : S390X_DECRYPT; + + s390x_km(in, len, out, adat->plat.s390x.fc | modifier, + &adat->plat.s390x.param.km); + return 1; +} + +static const PROV_CIPHER_HW s390x_aes_ecb = { + s390x_aes_ecb_initkey, + s390x_aes_ecb_cipher_hw, + ossl_cipher_aes_copyctx +}; + +static int s390x_aes_ofb128_initkey(PROV_CIPHER_CTX *dat, + const unsigned char *key, size_t keylen) +{ + PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; + + memcpy(adat->plat.s390x.param.kmo_kmf.k, key, keylen); + adat->plat.s390x.fc = S390X_AES_FC(keylen); + return 1; +} + +static int s390x_aes_ofb128_cipher_hw(PROV_CIPHER_CTX *dat, unsigned char *out, + const unsigned char *in, size_t len) +{ + PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; + int n = dat->num; + int rem; + + memcpy(adat->plat.s390x.param.kmo_kmf.cv, dat->iv, dat->ivlen); + while (n && len) { + *out = *in ^ adat->plat.s390x.param.kmo_kmf.cv[n]; + n = (n + 1) & 0xf; + --len; + ++in; + ++out; + } + + rem = len & 0xf; + + len &= ~(size_t)0xf; + if (len) { + s390x_kmo(in, len, out, adat->plat.s390x.fc, + &adat->plat.s390x.param.kmo_kmf); + + out += len; + in += len; + } + + if (rem) { + s390x_km(adat->plat.s390x.param.kmo_kmf.cv, 16, + adat->plat.s390x.param.kmo_kmf.cv, + adat->plat.s390x.fc, + adat->plat.s390x.param.kmo_kmf.k); + + while (rem--) { + out[n] = in[n] ^ adat->plat.s390x.param.kmo_kmf.cv[n]; + ++n; + } + } + + memcpy(dat->iv, adat->plat.s390x.param.kmo_kmf.cv, dat->ivlen); + dat->num = n; + return 1; +} + +static const PROV_CIPHER_HW s390x_aes_ofb128 = { + s390x_aes_ofb128_initkey, + s390x_aes_ofb128_cipher_hw, + ossl_cipher_aes_copyctx +}; + +static int s390x_aes_cfb128_initkey(PROV_CIPHER_CTX *dat, + const unsigned char *key, size_t keylen) +{ + PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; + + adat->plat.s390x.fc = S390X_AES_FC(keylen); + adat->plat.s390x.fc |= 16 << 24; /* 16 bytes cipher feedback */ + memcpy(adat->plat.s390x.param.kmo_kmf.k, key, keylen); + return 1; +} + +static int s390x_aes_cfb128_cipher_hw(PROV_CIPHER_CTX *dat, unsigned char *out, + const unsigned char *in, size_t len) +{ + PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; + unsigned int modifier = adat->base.enc ? 0 : S390X_DECRYPT; + int n = dat->num; + int rem; + unsigned char tmp; + + memcpy(adat->plat.s390x.param.kmo_kmf.cv, dat->iv, dat->ivlen); + while (n && len) { + tmp = *in; + *out = adat->plat.s390x.param.kmo_kmf.cv[n] ^ tmp; + adat->plat.s390x.param.kmo_kmf.cv[n] = dat->enc ? *out : tmp; + n = (n + 1) & 0xf; + --len; + ++in; + ++out; + } + + rem = len & 0xf; + + len &= ~(size_t)0xf; + if (len) { + s390x_kmf(in, len, out, adat->plat.s390x.fc | modifier, + &adat->plat.s390x.param.kmo_kmf); + + out += len; + in += len; + } + + if (rem) { + s390x_km(adat->plat.s390x.param.kmo_kmf.cv, 16, + adat->plat.s390x.param.kmo_kmf.cv, + S390X_AES_FC(dat->keylen), + adat->plat.s390x.param.kmo_kmf.k); + + while (rem--) { + tmp = in[n]; + out[n] = adat->plat.s390x.param.kmo_kmf.cv[n] ^ tmp; + adat->plat.s390x.param.kmo_kmf.cv[n] = dat->enc ? out[n] : tmp; + ++n; + } + } + + memcpy(dat->iv, adat->plat.s390x.param.kmo_kmf.cv, dat->ivlen); + dat->num = n; + return 1; +} + +static const PROV_CIPHER_HW s390x_aes_cfb128 = { + s390x_aes_cfb128_initkey, + s390x_aes_cfb128_cipher_hw, + ossl_cipher_aes_copyctx +}; + +static int s390x_aes_cfb8_initkey(PROV_CIPHER_CTX *dat, + const unsigned char *key, size_t keylen) +{ + PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; + + adat->plat.s390x.fc = S390X_AES_FC(keylen); + adat->plat.s390x.fc |= 1 << 24; /* 1 byte cipher feedback */ + memcpy(adat->plat.s390x.param.kmo_kmf.k, key, keylen); + return 1; +} + +static int s390x_aes_cfb8_cipher_hw(PROV_CIPHER_CTX *dat, unsigned char *out, + const unsigned char *in, size_t len) +{ + PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; + unsigned int modifier = adat->base.enc ? 0 : S390X_DECRYPT; + + memcpy(adat->plat.s390x.param.kmo_kmf.cv, dat->iv, dat->ivlen); + s390x_kmf(in, len, out, adat->plat.s390x.fc | modifier, + &adat->plat.s390x.param.kmo_kmf); + memcpy(dat->iv, adat->plat.s390x.param.kmo_kmf.cv, dat->ivlen); + return 1; +} + +static const PROV_CIPHER_HW s390x_aes_cfb8 = { + s390x_aes_cfb8_initkey, + s390x_aes_cfb8_cipher_hw, + ossl_cipher_aes_copyctx +}; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_s390x(enum aes_modes mode, + size_t keybits) +{ + switch (mode) { + case AES_MODE_ECB: + if ((keybits == 128 && S390X_aes_128_ecb_CAPABLE) + || (keybits == 192 && S390X_aes_192_ecb_CAPABLE) + || (keybits == 256 && S390X_aes_256_ecb_CAPABLE)) + return &s390x_aes_ecb; + break; + case AES_MODE_CFB128: + if ((keybits == 128 && S390X_aes_128_cfb_CAPABLE) + || (keybits == 192 && S390X_aes_192_cfb_CAPABLE) + || (keybits == 256 && S390X_aes_256_cfb_CAPABLE)) + return &s390x_aes_cfb128; + break; + case AES_MODE_CFB8: + if ((keybits == 128 && S390X_aes_128_cfb8_CAPABLE) + || (keybits == 192 && S390X_aes_192_cfb8_CAPABLE) + || (keybits == 256 && S390X_aes_256_cfb8_CAPABLE)) + return &s390x_aes_cfb8; + break; + case AES_MODE_OFB128: + if ((keybits == 128 && S390X_aes_128_ofb_CAPABLE) + || (keybits == 192 && S390X_aes_192_ofb_CAPABLE) + || (keybits == 256 && S390X_aes_256_ofb_CAPABLE)) + return &s390x_aes_ofb128; + break; + default: + break; + } + return NULL; +} + +#endif diff --git a/providers/implementations/ciphers/cipher_aes_hw_s390x.inc b/providers/implementations/ciphers/cipher_aes_hw_s390x.inc deleted file mode 100644 index be82d3be9b..0000000000 --- a/providers/implementations/ciphers/cipher_aes_hw_s390x.inc +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2001-2025 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/* - * IBM S390X support for AES modes ecb, cbc, ofb, cfb, ctr. - * This file is included by cipher_aes_hw.c - */ - -#include "arch/s390x_arch.h" - -#include - -#define s390x_aes_cbc_initkey cipher_hw_aes_initkey -#define s390x_aes_ctr_initkey cipher_hw_aes_initkey -#define s390x_aes_cbc_cipher_hw ossl_cipher_hw_generic_cbc -#define s390x_aes_ctr_cipher_hw ossl_cipher_hw_generic_ctr - -#define S390X_aes_128_ofb128_CAPABLE S390X_aes_128_ofb_CAPABLE -#define S390X_aes_192_ofb128_CAPABLE S390X_aes_192_ofb_CAPABLE -#define S390X_aes_256_ofb128_CAPABLE S390X_aes_256_ofb_CAPABLE - -static int s390x_aes_ecb_initkey(PROV_CIPHER_CTX *dat, - const unsigned char *key, size_t keylen) -{ - PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; - - adat->plat.s390x.fc = S390X_AES_FC(keylen); - memcpy(adat->plat.s390x.param.km.k, key, keylen); - return 1; -} - -static int s390x_aes_ecb_cipher_hw(PROV_CIPHER_CTX *dat, unsigned char *out, - const unsigned char *in, size_t len) -{ - PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; - unsigned int modifier = adat->base.enc ? 0 : S390X_DECRYPT; - - s390x_km(in, len, out, adat->plat.s390x.fc | modifier, - &adat->plat.s390x.param.km); - return 1; -} - -static int s390x_aes_ofb128_initkey(PROV_CIPHER_CTX *dat, - const unsigned char *key, size_t keylen) -{ - PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; - - memcpy(adat->plat.s390x.param.kmo_kmf.k, key, keylen); - adat->plat.s390x.fc = S390X_AES_FC(keylen); - return 1; -} - -static int s390x_aes_ofb128_cipher_hw(PROV_CIPHER_CTX *dat, unsigned char *out, - const unsigned char *in, size_t len) -{ - PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; - int n = dat->num; - int rem; - - memcpy(adat->plat.s390x.param.kmo_kmf.cv, dat->iv, dat->ivlen); - while (n && len) { - *out = *in ^ adat->plat.s390x.param.kmo_kmf.cv[n]; - n = (n + 1) & 0xf; - --len; - ++in; - ++out; - } - - rem = len & 0xf; - - len &= ~(size_t)0xf; - if (len) { - s390x_kmo(in, len, out, adat->plat.s390x.fc, - &adat->plat.s390x.param.kmo_kmf); - - out += len; - in += len; - } - - if (rem) { - s390x_km(adat->plat.s390x.param.kmo_kmf.cv, 16, - adat->plat.s390x.param.kmo_kmf.cv, - adat->plat.s390x.fc, - adat->plat.s390x.param.kmo_kmf.k); - - while (rem--) { - out[n] = in[n] ^ adat->plat.s390x.param.kmo_kmf.cv[n]; - ++n; - } - } - - memcpy(dat->iv, adat->plat.s390x.param.kmo_kmf.cv, dat->ivlen); - dat->num = n; - return 1; -} - -#define PROV_CIPHER_HW_declare(mode) \ -static const PROV_CIPHER_HW s390x_aes_##mode = { \ - s390x_aes_##mode##_initkey, \ - s390x_aes_##mode##_cipher_hw, \ - cipher_hw_aes_copyctx \ -}; -#define PROV_CIPHER_HW_select(mode) \ -if ((keybits == 128 && S390X_aes_128_##mode##_CAPABLE) \ - || (keybits == 192 && S390X_aes_192_##mode##_CAPABLE) \ - || (keybits == 256 && S390X_aes_256_##mode##_CAPABLE)) \ - return &s390x_aes_##mode; diff --git a/providers/implementations/ciphers/cipher_aes_hw_t4.c b/providers/implementations/ciphers/cipher_aes_hw_t4.c new file mode 100644 index 0000000000..6d8e945de5 --- /dev/null +++ b/providers/implementations/ciphers/cipher_aes_hw_t4.c @@ -0,0 +1,146 @@ +/* + * Copyright 2001-2021 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/*- + * Sparc t4 support for AES modes ecb, cbc, ofb, cfb, ctr. + * This file is used by cipher_aes_hw.c + */ + +#include "internal/deprecated.h" +#include +#include "cipher_aes.h" + +#if defined(SPARC_AES_CAPABLE) + +static int t4_set_encrypt_key(const unsigned char *key, int bits, AES_KEY *ks) +{ + aes_t4_set_encrypt_key(key, bits, ks); + return 0; +} + +static int t4_set_decrypt_key(const unsigned char *key, int bits, AES_KEY *ks) +{ + aes_t4_set_decrypt_key(key, bits, ks); + return 0; +} + +static int cipher_hw_aes_t4_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + cbc128_f fn_cbc = NULL; + if ((ctx->mode == EVP_CIPH_ECB_MODE || ctx->mode == EVP_CIPH_CBC_MODE) + && !ctx->enc) { + switch (keylen) { + case 16: + fn_cbc = (cbc128_f)aes128_t4_cbc_decrypt; + break; + case 24: + fn_cbc = (cbc128_f)aes192_t4_cbc_decrypt; + break; + case 32: + fn_cbc = (cbc128_f)aes256_t4_cbc_decrypt; + break; + default: + ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED); + return 0; + } + return ossl_cipher_set_aes_initkey(ctx, key, keylen, t4_set_decrypt_key, + aes_t4_decrypt, NULL, fn_cbc, NULL); + } else { + ctr128_f fn_ctr = NULL; + switch (keylen) { + case 16: + fn_cbc = (cbc128_f)aes128_t4_cbc_encrypt; + fn_ctr = (ctr128_f)aes128_t4_ctr32_encrypt; + break; + case 24: + fn_cbc = (cbc128_f)aes192_t4_cbc_encrypt; + fn_ctr = (ctr128_f)aes192_t4_ctr32_encrypt; + break; + case 32: + fn_cbc = (cbc128_f)aes256_t4_cbc_encrypt; + fn_ctr = (ctr128_f)aes256_t4_ctr32_encrypt; + break; + default: + ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED); + return 0; + } + return ossl_cipher_set_aes_initkey(ctx, key, keylen, t4_set_encrypt_key, + aes_t4_encrypt, NULL, fn_cbc, fn_ctr); + } +} + +static const PROV_CIPHER_HW aes_t4_ecb = { + cipher_hw_aes_t4_initkey, + ossl_cipher_hw_generic_ecb, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW aes_t4_cbc = { + cipher_hw_aes_t4_initkey, + ossl_cipher_hw_generic_cbc, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW aes_t4_cfb128 = { + cipher_hw_aes_t4_initkey, + ossl_cipher_hw_generic_cfb128, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW aes_t4_cfb8 = { + cipher_hw_aes_t4_initkey, + ossl_cipher_hw_generic_cfb8, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW aes_t4_cfb1 = { + cipher_hw_aes_t4_initkey, + ossl_cipher_hw_generic_cfb1, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW aes_t4_ofb128 = { + cipher_hw_aes_t4_initkey, + ossl_cipher_hw_generic_ofb128, + ossl_cipher_aes_copyctx +}; + +static const PROV_CIPHER_HW aes_t4_ctr = { + cipher_hw_aes_t4_initkey, + ossl_cipher_hw_generic_ctr, + ossl_cipher_aes_copyctx +}; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_t4(enum aes_modes mode, + size_t keybits) +{ + if (SPARC_AES_CAPABLE) { + switch (mode) { + case AES_MODE_ECB: + return &aes_t4_ecb; + case AES_MODE_CBC: + return &aes_t4_cbc; + case AES_MODE_CFB128: + return &aes_t4_cfb128; + case AES_MODE_CFB8: + return &aes_t4_cfb8; + case AES_MODE_CFB1: + return &aes_t4_cfb1; + case AES_MODE_OFB128: + return &aes_t4_ofb128; + case AES_MODE_CTR: + return &aes_t4_ctr; + default: + return NULL; + } + } + return NULL; +} +#endif diff --git a/providers/implementations/ciphers/cipher_aes_hw_t4.inc b/providers/implementations/ciphers/cipher_aes_hw_t4.inc deleted file mode 100644 index 28454fc508..0000000000 --- a/providers/implementations/ciphers/cipher_aes_hw_t4.inc +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2001-2021 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/*- - * Sparc t4 support for AES modes ecb, cbc, ofb, cfb, ctr. - * This file is included by cipher_aes_hw.c - */ - -static int cipher_hw_aes_t4_initkey(PROV_CIPHER_CTX *dat, - const unsigned char *key, size_t keylen) -{ - int ret, bits; - PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; - AES_KEY *ks = &adat->ks.ks; - - dat->ks = (const void *)ks; /* used by cipher_hw_generic_XXX */ - - bits = keylen * 8; - if ((dat->mode == EVP_CIPH_ECB_MODE || dat->mode == EVP_CIPH_CBC_MODE) - && !dat->enc) { - ret = 0; - aes_t4_set_decrypt_key(key, bits, ks); - dat->block = (block128_f)aes_t4_decrypt; - switch (bits) { - case 128: - dat->stream.cbc = dat->mode == EVP_CIPH_CBC_MODE ? - (cbc128_f)aes128_t4_cbc_decrypt : NULL; - break; - case 192: - dat->stream.cbc = dat->mode == EVP_CIPH_CBC_MODE ? - (cbc128_f)aes192_t4_cbc_decrypt : NULL; - break; - case 256: - dat->stream.cbc = dat->mode == EVP_CIPH_CBC_MODE ? - (cbc128_f)aes256_t4_cbc_decrypt : NULL; - break; - default: - ret = -1; - } - } else { - ret = 0; - aes_t4_set_encrypt_key(key, bits, ks); - dat->block = (block128_f)aes_t4_encrypt; - switch (bits) { - case 128: - if (dat->mode == EVP_CIPH_CBC_MODE) - dat->stream.cbc = (cbc128_f)aes128_t4_cbc_encrypt; - else if (dat->mode == EVP_CIPH_CTR_MODE) - dat->stream.ctr = (ctr128_f)aes128_t4_ctr32_encrypt; - else - dat->stream.cbc = NULL; - break; - case 192: - if (dat->mode == EVP_CIPH_CBC_MODE) - dat->stream.cbc = (cbc128_f)aes192_t4_cbc_encrypt; - else if (dat->mode == EVP_CIPH_CTR_MODE) - dat->stream.ctr = (ctr128_f)aes192_t4_ctr32_encrypt; - else - dat->stream.cbc = NULL; - break; - case 256: - if (dat->mode == EVP_CIPH_CBC_MODE) - dat->stream.cbc = (cbc128_f)aes256_t4_cbc_encrypt; - else if (dat->mode == EVP_CIPH_CTR_MODE) - dat->stream.ctr = (ctr128_f)aes256_t4_ctr32_encrypt; - else - dat->stream.cbc = NULL; - break; - default: - ret = -1; - } - } - - if (ret < 0) { - ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED); - return 0; - } - - return 1; -} - -#define PROV_CIPHER_HW_declare(mode) \ -static const PROV_CIPHER_HW aes_t4_##mode = { \ - cipher_hw_aes_t4_initkey, \ - ossl_cipher_hw_generic_##mode, \ - cipher_hw_aes_copyctx \ -}; -#define PROV_CIPHER_HW_select(mode) \ - if (SPARC_AES_CAPABLE) \ - return &aes_t4_##mode; From e9cf01be98d7dc9bec575b5af0b668f53896f2c2 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Wed, 10 Jun 2026 17:55:57 -0400 Subject: [PATCH 089/349] Convert AES CCM .inc files to separate .c files The hardware-specific AES CCM implementations were previously included directly as `.inc` files into `cipher_aes_ccm_hw.c`. This commit renames them to standalone `.c` files and updates the build configuration, improving modularity by treating them as separate compilation units. Additionally, the `AES_HW_CCM_SET_KEY_FN` macro is replaced with a new `ossl_cipher_set_ccm_aes_initkey` helper function. This reduces code duplication, removes unsafe macro logic, and introduces proper error handling during provider key initialization. Signed-off-by: Simo Sorce Reviewed-by: Dmitry Belyavskiy Reviewed-by: Norbert Pocs Reviewed-by: Shane Lontis MergeDate: Sat Jun 27 09:05:42 2026 (Merged from https://github.com/openssl/openssl/pull/31472) --- providers/fips.module.sources | 1 - providers/implementations/ciphers/build.info | 4 +- .../implementations/ciphers/cipher_aes_ccm.h | 21 +++++ .../ciphers/cipher_aes_ccm_hw.c | 77 +++++++++++------- ...hw_aesni.inc => cipher_aes_ccm_hw_aesni.c} | 28 ++++--- ...hw_rv32i.inc => cipher_aes_ccm_hw_rv32i.c} | 33 ++++---- ...hw_rv64i.inc => cipher_aes_ccm_hw_rv64i.c} | 35 ++++---- ...hw_s390x.inc => cipher_aes_ccm_hw_s390x.c} | 81 ++++++++++--------- ...s_ccm_hw_t4.inc => cipher_aes_ccm_hw_t4.c} | 24 +++--- 9 files changed, 180 insertions(+), 124 deletions(-) rename providers/implementations/ciphers/{cipher_aes_ccm_hw_aesni.inc => cipher_aes_ccm_hw_aesni.c} (59%) rename providers/implementations/ciphers/{cipher_aes_ccm_hw_rv32i.inc => cipher_aes_ccm_hw_rv32i.c} (67%) rename providers/implementations/ciphers/{cipher_aes_ccm_hw_rv64i.inc => cipher_aes_ccm_hw_rv64i.c} (63%) rename providers/implementations/ciphers/{cipher_aes_ccm_hw_s390x.inc => cipher_aes_ccm_hw_s390x.c} (72%) rename providers/implementations/ciphers/{cipher_aes_ccm_hw_t4.inc => cipher_aes_ccm_hw_t4.c} (62%) diff --git a/providers/fips.module.sources b/providers/fips.module.sources index 6a9058e66b..7b404dbece 100644 --- a/providers/fips.module.sources +++ b/providers/fips.module.sources @@ -660,7 +660,6 @@ providers/implementations/ciphers/cipher_aes_cbc_hmac_sha_etm.h providers/implementations/ciphers/cipher_aes_ccm.c providers/implementations/ciphers/cipher_aes_ccm.h providers/implementations/ciphers/cipher_aes_ccm_hw.c -providers/implementations/ciphers/cipher_aes_ccm_hw_aesni.inc providers/implementations/ciphers/cipher_aes_cts.inc providers/implementations/ciphers/cipher_aes_gcm.c providers/implementations/ciphers/cipher_aes_gcm.h diff --git a/providers/implementations/ciphers/build.info b/providers/implementations/ciphers/build.info index 6f12c208c3..54e1a11469 100644 --- a/providers/implementations/ciphers/build.info +++ b/providers/implementations/ciphers/build.info @@ -111,7 +111,9 @@ SOURCE[$AES_GOAL]=\ cipher_aes_gcm_hw_armv8.c cipher_aes_gcm_hw_ppc.c \ cipher_aes_gcm_hw_rv32i.c cipher_aes_gcm_hw_rv64i.c \ cipher_aes_gcm_hw_s390x.c cipher_aes_gcm_hw_t4.c \ - cipher_aes_ccm.c cipher_aes_ccm_hw.c \ + cipher_aes_ccm.c cipher_aes_ccm_hw.c cipher_aes_ccm_hw_aesni.c \ + cipher_aes_ccm_hw_rv32i.c cipher_aes_ccm_hw_rv64i.c \ + cipher_aes_ccm_hw_s390x.c cipher_aes_ccm_hw_t4.c \ cipher_aes_wrp.c \ cipher_aes_cbc_hmac_sha.c \ cipher_aes_cbc_hmac_sha256_hw.c cipher_aes_cbc_hmac_sha1_hw.c \ diff --git a/providers/implementations/ciphers/cipher_aes_ccm.h b/providers/implementations/ciphers/cipher_aes_ccm.h index 6e4599fdbf..91495015a8 100644 --- a/providers/implementations/ciphers/cipher_aes_ccm.h +++ b/providers/implementations/ciphers/cipher_aes_ccm.h @@ -48,6 +48,27 @@ typedef struct prov_aes_ccm_ctx_st { } ccm; } PROV_AES_CCM_CTX; +int ossl_cipher_set_ccm_aes_initkey(PROV_CCM_CTX *ctx, + const unsigned char *key, size_t keylen, + aes_set_encrypt_key_fn fn_set_key, aes_block128_f fn_block, + ccm128_f fn_ccm_enc, ccm128_f fn_ccm_dec); + const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keylen); +#if defined(AESNI_CAPABLE) +const PROV_CCM_HW *ossl_prov_aes_hw_ccm_aesni(size_t keybits); +#endif +#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 +const PROV_CCM_HW *ossl_prov_aes_hw_ccm_rv32i(size_t keybits); +#endif +#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 +const PROV_CCM_HW *ossl_prov_aes_hw_ccm_rv64i(size_t keybits); +#endif +#if defined(S390X_aes_128_CAPABLE) +const PROV_CCM_HW *ossl_prov_aes_hw_ccm_s390x(size_t keybits); +#endif +#if defined(SPARC_AES_CAPABLE) +const PROV_CCM_HW *ossl_prov_aes_hw_ccm_t4(size_t keybits); +#endif + #endif /* !defined(OSSL_PROVIDERS_IMPLEMENTATIONS_CIPHERS_CIPHER_AES_CCM_H) */ diff --git a/providers/implementations/ciphers/cipher_aes_ccm_hw.c b/providers/implementations/ciphers/cipher_aes_ccm_hw.c index 8bc7586adc..8ee89a1bd6 100644 --- a/providers/implementations/ciphers/cipher_aes_ccm_hw.c +++ b/providers/implementations/ciphers/cipher_aes_ccm_hw.c @@ -14,37 +14,50 @@ * non-internal use) in order to implement provider AES ciphers. */ #include "internal/deprecated.h" - +#include #include "cipher_aes_ccm.h" -#define AES_HW_CCM_SET_KEY_FN(fn_set_enc_key, fn_blk, fn_ccm_enc, fn_ccm_dec) \ - fn_set_enc_key(key, (int)(keylen * 8), &actx->ccm.ks.ks); \ - CRYPTO_ccm128_init(&ctx->ccm_ctx, (unsigned int)ctx->m, \ - (unsigned int)ctx->l, &actx->ccm.ks.ks, \ - (block128_f)fn_blk); \ - ctx->str = ctx->enc ? (ccm128_f)fn_ccm_enc : (ccm128_f)fn_ccm_dec; \ +int ossl_cipher_set_ccm_aes_initkey(PROV_CCM_CTX *ctx, + const unsigned char *key, size_t keylen, + aes_set_encrypt_key_fn fn_set_key, aes_block128_f fn_block, + ccm128_f fn_ccm_enc, ccm128_f fn_ccm_dec) +{ + PROV_AES_CCM_CTX *actx = (PROV_AES_CCM_CTX *)ctx; + AES_KEY *ks = &actx->ccm.ks.ks; + + int ret = fn_set_key(key, (int)(keylen * 8), ks); + if (ret < 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED); + return 0; + } + CRYPTO_ccm128_init(&ctx->ccm_ctx, (unsigned int)ctx->m, + (unsigned int)ctx->l, ks, (block128_f)fn_block); + + ctx->str = ctx->enc ? fn_ccm_enc : fn_ccm_dec; ctx->key_set = 1; + return 1; +} + static int ccm_generic_aes_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, size_t keylen) { - PROV_AES_CCM_CTX *actx = (PROV_AES_CCM_CTX *)ctx; - #ifdef HWAES_CAPABLE if (HWAES_CAPABLE) { - AES_HW_CCM_SET_KEY_FN(HWAES_set_encrypt_key, HWAES_encrypt, NULL, NULL); - } else -#endif /* HWAES_CAPABLE */ + return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, + HWAES_set_encrypt_key, HWAES_encrypt, NULL, NULL); + } +#endif #ifdef VPAES_CAPABLE - if (VPAES_CAPABLE) { - AES_HW_CCM_SET_KEY_FN(vpaes_set_encrypt_key, vpaes_encrypt, NULL, NULL); - } else -#endif - { - AES_HW_CCM_SET_KEY_FN(AES_set_encrypt_key, AES_encrypt, NULL, NULL) + if (VPAES_CAPABLE) { + return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, + vpaes_set_encrypt_key, vpaes_encrypt, NULL, NULL); } - return 1; +#endif + + return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, + AES_set_encrypt_key, AES_encrypt, NULL, NULL); } static const PROV_CCM_HW aes_ccm = { @@ -56,19 +69,21 @@ static const PROV_CCM_HW aes_ccm = { ossl_ccm_generic_gettag }; -#if defined(S390X_aes_128_CAPABLE) -#include "cipher_aes_ccm_hw_s390x.inc" -#elif defined(AESNI_CAPABLE) -#include "cipher_aes_ccm_hw_aesni.inc" -#elif defined(SPARC_AES_CAPABLE) -#include "cipher_aes_ccm_hw_t4.inc" -#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 -#include "cipher_aes_ccm_hw_rv64i.inc" -#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 -#include "cipher_aes_ccm_hw_rv32i.inc" -#else const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keybits) { + const PROV_CCM_HW *aes_ccm_hw = NULL; +#if defined(AESNI_CAPABLE) + aes_ccm_hw = ossl_prov_aes_hw_ccm_aesni(keybits); +#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 + aes_ccm_hw = ossl_prov_aes_hw_ccm_rv32i(keybits); +#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 + aes_ccm_hw = ossl_prov_aes_hw_ccm_rv64i(keybits); +#elif defined(S390X_aes_128_CAPABLE) + aes_ccm_hw = ossl_prov_aes_hw_ccm_s390x(keybits); +#elif defined(SPARC_AES_CAPABLE) + aes_ccm_hw = ossl_prov_aes_hw_ccm_t4(keybits); +#endif + if (aes_ccm_hw != NULL) + return aes_ccm_hw; return &aes_ccm; } -#endif diff --git a/providers/implementations/ciphers/cipher_aes_ccm_hw_aesni.inc b/providers/implementations/ciphers/cipher_aes_ccm_hw_aesni.c similarity index 59% rename from providers/implementations/ciphers/cipher_aes_ccm_hw_aesni.inc rename to providers/implementations/ciphers/cipher_aes_ccm_hw_aesni.c index 579e5a3d4f..67644b324a 100644 --- a/providers/implementations/ciphers/cipher_aes_ccm_hw_aesni.inc +++ b/providers/implementations/ciphers/cipher_aes_ccm_hw_aesni.c @@ -9,18 +9,20 @@ /*- * AES-NI support for AES CCM. - * This file is included by cipher_aes_ccm_hw.c + * This file is used by cipher_aes_ccm_hw.c */ -static int ccm_aesni_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, - size_t keylen) -{ - PROV_AES_CCM_CTX *actx = (PROV_AES_CCM_CTX *)ctx; +#include "internal/deprecated.h" +#include "cipher_aes_ccm.h" - AES_HW_CCM_SET_KEY_FN(aesni_set_encrypt_key, aesni_encrypt, - aesni_ccm64_encrypt_blocks, - aesni_ccm64_decrypt_blocks); - return 1; +#if defined(AESNI_CAPABLE) + +static int ccm_aesni_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, + aesni_set_encrypt_key, aesni_encrypt, aesni_ccm64_encrypt_blocks, + aesni_ccm64_decrypt_blocks); } static const PROV_CCM_HW aesni_ccm = { @@ -32,7 +34,11 @@ static const PROV_CCM_HW aesni_ccm = { ossl_ccm_generic_gettag }; -const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keybits) +const PROV_CCM_HW *ossl_prov_aes_hw_ccm_aesni(size_t keybits) { - return AESNI_CAPABLE ? &aesni_ccm : &aes_ccm; + if (AESNI_CAPABLE) + return &aesni_ccm; + return NULL; } + +#endif diff --git a/providers/implementations/ciphers/cipher_aes_ccm_hw_rv32i.inc b/providers/implementations/ciphers/cipher_aes_ccm_hw_rv32i.c similarity index 67% rename from providers/implementations/ciphers/cipher_aes_ccm_hw_rv32i.inc rename to providers/implementations/ciphers/cipher_aes_ccm_hw_rv32i.c index 7cfe0fc4ce..b2dfee5650 100644 --- a/providers/implementations/ciphers/cipher_aes_ccm_hw_rv32i.inc +++ b/providers/implementations/ciphers/cipher_aes_ccm_hw_rv32i.c @@ -9,27 +9,26 @@ /*- * RISC-V 32 ZKND ZKNE support for AES CCM. - * This file is included by cipher_aes_ccm_hw.c + * This file is used by cipher_aes_ccm_hw.c */ -static int ccm_rv32i_zknd_zkne_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, - size_t keylen) -{ - PROV_AES_CCM_CTX *actx = (PROV_AES_CCM_CTX *)ctx; +#include "internal/deprecated.h" +#include "cipher_aes_ccm.h" - AES_HW_CCM_SET_KEY_FN(rv32i_zkne_set_encrypt_key, rv32i_zkne_encrypt, - NULL, NULL); - return 1; +#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 + +static int ccm_rv32i_zknd_zkne_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, + rv32i_zkne_set_encrypt_key, rv32i_zkne_encrypt, NULL, NULL); } static int ccm_rv32i_zbkb_zknd_zkne_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, - size_t keylen) + size_t keylen) { - PROV_AES_CCM_CTX *actx = (PROV_AES_CCM_CTX *)ctx; - - AES_HW_CCM_SET_KEY_FN(rv32i_zbkb_zkne_set_encrypt_key, rv32i_zkne_encrypt, - NULL, NULL); - return 1; + return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, + rv32i_zbkb_zkne_set_encrypt_key, rv32i_zkne_encrypt, NULL, NULL); } static const PROV_CCM_HW rv32i_zknd_zkne_ccm = { @@ -50,11 +49,13 @@ static const PROV_CCM_HW rv32i_zbkb_zknd_zkne_ccm = { ossl_ccm_generic_gettag }; -const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keybits) +const PROV_CCM_HW *ossl_prov_aes_hw_ccm_rv32i(size_t keybits) { if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE()) return &rv32i_zbkb_zknd_zkne_ccm; if (RISCV_HAS_ZKND_AND_ZKNE()) return &rv32i_zknd_zkne_ccm; - return &aes_ccm; + return NULL; } + +#endif diff --git a/providers/implementations/ciphers/cipher_aes_ccm_hw_rv64i.inc b/providers/implementations/ciphers/cipher_aes_ccm_hw_rv64i.c similarity index 63% rename from providers/implementations/ciphers/cipher_aes_ccm_hw_rv64i.inc rename to providers/implementations/ciphers/cipher_aes_ccm_hw_rv64i.c index f2353bb3b8..506e286e77 100644 --- a/providers/implementations/ciphers/cipher_aes_ccm_hw_rv64i.inc +++ b/providers/implementations/ciphers/cipher_aes_ccm_hw_rv64i.c @@ -9,17 +9,19 @@ /*- * RISC-V 64 ZKND ZKNE support for AES CCM. - * This file is included by cipher_aes_ccm_hw.c + * This file is used by cipher_aes_ccm_hw.c */ -static int ccm_rv64i_zknd_zkne_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, - size_t keylen) -{ - PROV_AES_CCM_CTX *actx = (PROV_AES_CCM_CTX *)ctx; +#include "internal/deprecated.h" +#include "cipher_aes_ccm.h" - AES_HW_CCM_SET_KEY_FN(rv64i_zkne_set_encrypt_key, rv64i_zkne_encrypt, - NULL, NULL); - return 1; +#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 + +static int ccm_rv64i_zknd_zkne_initkey(PROV_CCM_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, + rv64i_zkne_set_encrypt_key, rv64i_zkne_encrypt, NULL, NULL); } static const PROV_CCM_HW rv64i_zknd_zkne_ccm = { @@ -37,18 +39,16 @@ static const PROV_CCM_HW rv64i_zknd_zkne_ccm = { */ static int ccm_rv64i_zvkned_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, - size_t keylen) + size_t keylen) { - PROV_AES_CCM_CTX *actx = (PROV_AES_CCM_CTX *)ctx; - /* Zvkned only supports 128 and 256 bit keys for key schedule generation. */ if (keylen * 8 == 128 || keylen * 8 == 256) { - AES_HW_CCM_SET_KEY_FN(rv64i_zvkned_set_encrypt_key, rv64i_zvkned_encrypt, - NULL, NULL); + return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, + rv64i_zvkned_set_encrypt_key, rv64i_zvkned_encrypt, NULL, NULL); } else { - AES_HW_CCM_SET_KEY_FN(AES_set_encrypt_key, rv64i_zvkned_encrypt, NULL, NULL) + return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, + AES_set_encrypt_key, rv64i_zvkned_encrypt, NULL, NULL); } - return 1; } static const PROV_CCM_HW rv64i_zvkned_ccm = { @@ -60,12 +60,13 @@ static const PROV_CCM_HW rv64i_zvkned_ccm = { ossl_ccm_generic_gettag }; -const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keybits) +const PROV_CCM_HW *ossl_prov_aes_hw_ccm_rv64i(size_t keybits) { if (RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) return &rv64i_zvkned_ccm; else if (RISCV_HAS_ZKND_AND_ZKNE()) return &rv64i_zknd_zkne_ccm; else - return &aes_ccm; + return NULL; } +#endif diff --git a/providers/implementations/ciphers/cipher_aes_ccm_hw_s390x.inc b/providers/implementations/ciphers/cipher_aes_ccm_hw_s390x.c similarity index 72% rename from providers/implementations/ciphers/cipher_aes_ccm_hw_s390x.inc rename to providers/implementations/ciphers/cipher_aes_ccm_hw_s390x.c index 7253f03a7e..d79e2766bc 100644 --- a/providers/implementations/ciphers/cipher_aes_ccm_hw_s390x.inc +++ b/providers/implementations/ciphers/cipher_aes_ccm_hw_s390x.c @@ -9,13 +9,18 @@ /*- * S390X support for AES CCM. - * This file is included by cipher_aes_ccm_hw.c + * This file is used by cipher_aes_ccm_hw.c */ +#include "internal/deprecated.h" +#include "cipher_aes_ccm.h" + +#if defined(S390X_aes_128_CAPABLE) + #define S390X_CCM_AAD_FLAG 0x40 static int s390x_aes_ccm_initkey(PROV_CCM_CTX *ctx, - const unsigned char *key, size_t keylen) + const unsigned char *key, size_t keylen) { PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx; @@ -23,7 +28,7 @@ static int s390x_aes_ccm_initkey(PROV_CCM_CTX *ctx, memcpy(&sctx->ccm.s390x.kmac.k, key, keylen); /* Store encoded m and l. */ sctx->ccm.s390x.nonce.b[0] = ((ctx->l - 1) & 0x7) - | (((ctx->m - 2) >> 1) & 0x7) << 3; + | (((ctx->m - 2) >> 1) & 0x7) << 3; memset(sctx->ccm.s390x.nonce.b + 1, 0, sizeof(sctx->ccm.s390x.nonce.b)); sctx->ccm.s390x.blocks = 0; ctx->key_set = 1; @@ -31,8 +36,8 @@ static int s390x_aes_ccm_initkey(PROV_CCM_CTX *ctx, } static int s390x_aes_ccm_setiv(PROV_CCM_CTX *ctx, - const unsigned char *nonce, size_t noncelen, - size_t mlen) + const unsigned char *nonce, size_t noncelen, + size_t mlen) { PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx; @@ -46,7 +51,7 @@ static int s390x_aes_ccm_setiv(PROV_CCM_CTX *ctx, * Process additional authenticated data. Code is big-endian. */ static int s390x_aes_ccm_setaad(PROV_CCM_CTX *ctx, - const unsigned char *aad, size_t alen) + const unsigned char *aad, size_t alen) { PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx; unsigned char *ptr; @@ -64,7 +69,7 @@ static int s390x_aes_ccm_setaad(PROV_CCM_CTX *ctx, *(uint16_t *)ptr = alen; i = 2; } else if (sizeof(alen) == 8 - && alen >= (size_t)1 << (32 % (sizeof(alen) * 8))) { + && alen >= (size_t)1 << (32 % (sizeof(alen) * 8))) { *(uint16_t *)ptr = 0xffff; *(uint64_t *)(ptr + 2) = alen; i = 10; @@ -88,7 +93,7 @@ static int s390x_aes_ccm_setaad(PROV_CCM_CTX *ctx, sctx->ccm.s390x.kmac.icv.g[0] = 0; sctx->ccm.s390x.kmac.icv.g[1] = 0; s390x_kmac(sctx->ccm.s390x.nonce.b, 32, sctx->ccm.s390x.fc, - &sctx->ccm.s390x.kmac); + &sctx->ccm.s390x.kmac); sctx->ccm.s390x.blocks += 2; rem = alen & 0xf; @@ -103,8 +108,8 @@ static int s390x_aes_ccm_setaad(PROV_CCM_CTX *ctx, sctx->ccm.s390x.kmac.icv.b[i] ^= aad[i]; s390x_km(sctx->ccm.s390x.kmac.icv.b, 16, - sctx->ccm.s390x.kmac.icv.b, sctx->ccm.s390x.fc, - sctx->ccm.s390x.kmac.k); + sctx->ccm.s390x.kmac.icv.b, sctx->ccm.s390x.fc, + sctx->ccm.s390x.kmac.k); sctx->ccm.s390x.blocks++; } return 1; @@ -115,8 +120,8 @@ static int s390x_aes_ccm_setaad(PROV_CCM_CTX *ctx, * success. */ static int s390x_aes_ccm_auth_encdec(PROV_CCM_CTX *ctx, - const unsigned char *in, - unsigned char *out, size_t len, int enc) + const unsigned char *in, + unsigned char *out, size_t len, int enc) { PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx; size_t n, rem; @@ -126,7 +131,7 @@ static int s390x_aes_ccm_auth_encdec(PROV_CCM_CTX *ctx, flags = sctx->ccm.s390x.nonce.b[0]; if (!(flags & S390X_CCM_AAD_FLAG)) { s390x_km(sctx->ccm.s390x.nonce.b, 16, sctx->ccm.s390x.kmac.icv.b, - sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k); + sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k); sctx->ccm.s390x.blocks++; } l = flags & 0x7; @@ -146,13 +151,13 @@ static int s390x_aes_ccm_auth_encdec(PROV_CCM_CTX *ctx, sctx->ccm.s390x.nonce.b[15] = 1; if (n != len) - return 0; /* length mismatch */ + return 0; /* length mismatch */ if (enc) { /* Two operations per block plus one for tag encryption */ sctx->ccm.s390x.blocks += (((len + 15) >> 4) << 1) + 1; if (sctx->ccm.s390x.blocks > (1ULL << 61)) - return 0; /* too much data */ + return 0; /* too much data */ } num = 0; @@ -168,18 +173,18 @@ static int s390x_aes_ccm_auth_encdec(PROV_CCM_CTX *ctx, sctx->ccm.s390x.kmac.icv.b[i] ^= in[len + i]; s390x_km(sctx->ccm.s390x.kmac.icv.b, 16, - sctx->ccm.s390x.kmac.icv.b, - sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k); + sctx->ccm.s390x.kmac.icv.b, + sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k); } CRYPTO_ctr128_encrypt_ctr32(in, out, len + rem, &sctx->ccm.ks.ks, - sctx->ccm.s390x.nonce.b, sctx->ccm.s390x.buf.b, - &num, (ctr128_f)AES_ctr32_encrypt); + sctx->ccm.s390x.nonce.b, sctx->ccm.s390x.buf.b, + &num, (ctr128_f)AES_ctr32_encrypt); } else { /* decrypt-then-mac */ CRYPTO_ctr128_encrypt_ctr32(in, out, len + rem, &sctx->ccm.ks.ks, - sctx->ccm.s390x.nonce.b, sctx->ccm.s390x.buf.b, - &num, (ctr128_f)AES_ctr32_encrypt); + sctx->ccm.s390x.nonce.b, sctx->ccm.s390x.buf.b, + &num, (ctr128_f)AES_ctr32_encrypt); if (len) s390x_kmac(out, len, sctx->ccm.s390x.fc, &sctx->ccm.s390x.kmac); @@ -188,8 +193,8 @@ static int s390x_aes_ccm_auth_encdec(PROV_CCM_CTX *ctx, sctx->ccm.s390x.kmac.icv.b[i] ^= out[len + i]; s390x_km(sctx->ccm.s390x.kmac.icv.b, 16, - sctx->ccm.s390x.kmac.icv.b, - sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k); + sctx->ccm.s390x.kmac.icv.b, + sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k); } } /* encrypt tag */ @@ -197,17 +202,16 @@ static int s390x_aes_ccm_auth_encdec(PROV_CCM_CTX *ctx, sctx->ccm.s390x.nonce.b[i] = 0; s390x_km(sctx->ccm.s390x.nonce.b, 16, sctx->ccm.s390x.buf.b, - sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k); + sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k); sctx->ccm.s390x.kmac.icv.g[0] ^= sctx->ccm.s390x.buf.g[0]; sctx->ccm.s390x.kmac.icv.g[1] ^= sctx->ccm.s390x.buf.g[1]; - sctx->ccm.s390x.nonce.b[0] = flags; /* restore flags field */ + sctx->ccm.s390x.nonce.b[0] = flags; /* restore flags field */ return 1; } - static int s390x_aes_ccm_gettag(PROV_CCM_CTX *ctx, - unsigned char *tag, size_t tlen) + unsigned char *tag, size_t tlen) { PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx; @@ -218,9 +222,9 @@ static int s390x_aes_ccm_gettag(PROV_CCM_CTX *ctx, } static int s390x_aes_ccm_auth_encrypt(PROV_CCM_CTX *ctx, - const unsigned char *in, - unsigned char *out, size_t len, - unsigned char *tag, size_t taglen) + const unsigned char *in, + unsigned char *out, size_t len, + unsigned char *tag, size_t taglen) { int rv; @@ -231,10 +235,10 @@ static int s390x_aes_ccm_auth_encrypt(PROV_CCM_CTX *ctx, } static int s390x_aes_ccm_auth_decrypt(PROV_CCM_CTX *ctx, - const unsigned char *in, - unsigned char *out, size_t len, - unsigned char *expected_tag, - size_t taglen) + const unsigned char *in, + unsigned char *out, size_t len, + unsigned char *expected_tag, + size_t taglen) { int rv = 0; PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx; @@ -258,11 +262,12 @@ static const PROV_CCM_HW s390x_aes_ccm = { s390x_aes_ccm_gettag }; -const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keybits) +const PROV_CCM_HW *ossl_prov_aes_hw_ccm_s390x(size_t keybits) { if ((keybits == 128 && S390X_aes_128_ccm_CAPABLE) - || (keybits == 192 && S390X_aes_192_ccm_CAPABLE) - || (keybits == 256 && S390X_aes_256_ccm_CAPABLE)) + || (keybits == 192 && S390X_aes_192_ccm_CAPABLE) + || (keybits == 256 && S390X_aes_256_ccm_CAPABLE)) return &s390x_aes_ccm; - return &aes_ccm; + return NULL; } +#endif diff --git a/providers/implementations/ciphers/cipher_aes_ccm_hw_t4.inc b/providers/implementations/ciphers/cipher_aes_ccm_hw_t4.c similarity index 62% rename from providers/implementations/ciphers/cipher_aes_ccm_hw_t4.inc rename to providers/implementations/ciphers/cipher_aes_ccm_hw_t4.c index a676d411b5..981773760f 100644 --- a/providers/implementations/ciphers/cipher_aes_ccm_hw_t4.inc +++ b/providers/implementations/ciphers/cipher_aes_ccm_hw_t4.c @@ -9,16 +9,19 @@ /*- * Fujitsu SPARC64 X support for AES CCM. - * This file is included by cipher_aes_ccm_hw.c + * This file is used by cipher_aes_ccm_hw.c */ -static int ccm_t4_aes_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, - size_t keylen) -{ - PROV_AES_CCM_CTX *actx = (PROV_AES_CCM_CTX *)ctx; +#include "internal/deprecated.h" +#include "cipher_aes_ccm.h" - AES_HW_CCM_SET_KEY_FN(aes_t4_set_encrypt_key, aes_t4_encrypt, NULL, NULL); - return 1; +#if defined(SPARC_AES_CAPABLE) + +static int ccm_t4_aes_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, + aes_t4_set_encrypt_key, aes_t4_encrypt, NULL, NULL); } static const PROV_CCM_HW t4_aes_ccm = { @@ -30,7 +33,10 @@ static const PROV_CCM_HW t4_aes_ccm = { ossl_ccm_generic_gettag }; -const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keybits) +const PROV_CCM_HW *ossl_prov_aes_hw_ccm_t4(size_t keybits) { - return SPARC_AES_CAPABLE ? &t4_aes_ccm : &aes_ccm; + if (SPARC_AES_CAPABLE) + return &t4_aes_ccm; + return NULL; } +#endif From 5339c5219fc605b47db44041e835ff15a9444f50 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Thu, 11 Jun 2026 10:40:16 -0400 Subject: [PATCH 090/349] Refactor AES XTS hardware key initialization Replace complex AES XTS initialization and hardware selection macros (`XTS_SET_KEY_FN`, `PROV_CIPHER_HW_declare_xts`, and `PROV_CIPHER_HW_select_xts`) with standard C functions. Architecture-specific initializers, particularly for RISC-V, are consolidated to evaluate CPU capabilities within dedicated initialization functions rather than relying on macro expansion. This refactoring improves overall code readability, maintainability, and simplifies debugging by eliminating opaque multi-statement macros. Signed-off-by: Simo Sorce Reviewed-by: Dmitry Belyavskiy Reviewed-by: Norbert Pocs Reviewed-by: Shane Lontis MergeDate: Sat Jun 27 09:05:44 2026 (Merged from https://github.com/openssl/openssl/pull/31472) --- .../ciphers/cipher_aes_xts_hw.c | 336 ++++++++---------- 1 file changed, 155 insertions(+), 181 deletions(-) diff --git a/providers/implementations/ciphers/cipher_aes_xts_hw.c b/providers/implementations/ciphers/cipher_aes_xts_hw.c index 022fbc7ff0..6b8c7ab4b4 100644 --- a/providers/implementations/ciphers/cipher_aes_xts_hw.c +++ b/providers/implementations/ciphers/cipher_aes_xts_hw.c @@ -15,32 +15,37 @@ #include "cipher_aes_xts.h" -#define XTS_SET_KEY_FN(fn_set_enc_key, fn_set_dec_key, \ - fn_block_enc, fn_block_dec, \ - fn_stream_enc, fn_stream_dec) \ - { \ - size_t bytes = keylen / 2; \ - size_t bits = bytes * 8; \ - \ - if (ctx->enc) { \ - fn_set_enc_key(key, (int)bits, &xctx->ks1.ks); \ - xctx->xts.block1 = (block128_f)fn_block_enc; \ - } else { \ - fn_set_dec_key(key, (int)bits, &xctx->ks1.ks); \ - xctx->xts.block1 = (block128_f)fn_block_dec; \ - } \ - fn_set_enc_key(key + bytes, (int)bits, &xctx->ks2.ks); \ - xctx->xts.block2 = (block128_f)fn_block_enc; \ - xctx->xts.key1 = &xctx->ks1; \ - xctx->xts.key2 = &xctx->ks2; \ - xctx->stream = ctx->enc ? fn_stream_enc : fn_stream_dec; \ +static int cipher_set_aes_xts_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen, + aes_set_encrypt_key_fn fn_set_enc_key, + aes_set_encrypt_key_fn fn_set_dec_key, + aes_block128_f fn_block_enc, aes_block128_f fn_block_dec, + OSSL_xts_stream_fn fn_stream_enc, OSSL_xts_stream_fn fn_stream_dec) +{ + PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx; + size_t bytes = keylen / 2; + size_t bits = bytes * 8; + + if (ctx->enc) { + fn_set_enc_key(key, (int)bits, &xctx->ks1.ks); + xctx->xts.block1 = (block128_f)fn_block_enc; + } else { + fn_set_dec_key(key, (int)bits, &xctx->ks1.ks); + xctx->xts.block1 = (block128_f)fn_block_dec; } + fn_set_enc_key(key + bytes, (int)bits, &xctx->ks2.ks); + xctx->xts.block2 = (block128_f)fn_block_enc; + xctx->xts.key1 = &xctx->ks1; + xctx->xts.key2 = &xctx->ks2; + xctx->stream = ctx->enc ? fn_stream_enc : fn_stream_dec; + + return 1; +} static int cipher_hw_aes_xts_generic_initkey(PROV_CIPHER_CTX *ctx, const unsigned char *key, size_t keylen) { - PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx; OSSL_xts_stream_fn stream_enc = NULL; OSSL_xts_stream_fn stream_dec = NULL; @@ -57,34 +62,33 @@ static int cipher_hw_aes_xts_generic_initkey(PROV_CIPHER_CTX *ctx, #ifdef HWAES_xts_decrypt stream_dec = HWAES_xts_decrypt; #endif /* HWAES_xts_decrypt */ - XTS_SET_KEY_FN(HWAES_set_encrypt_key, HWAES_set_decrypt_key, - HWAES_encrypt, HWAES_decrypt, - stream_enc, stream_dec); - return 1; - } else + return cipher_set_aes_xts_initkey(ctx, key, keylen, + HWAES_set_encrypt_key, HWAES_set_decrypt_key, + HWAES_encrypt, HWAES_decrypt, stream_enc, stream_dec); + } #endif /* HWAES_CAPABLE */ #ifdef BSAES_CAPABLE - if (BSAES_CAPABLE) { + if (BSAES_CAPABLE) { stream_enc = ossl_bsaes_xts_encrypt; stream_dec = ossl_bsaes_xts_decrypt; - } else -#endif /* BSAES_CAPABLE */ -#ifdef VPAES_CAPABLE - if (VPAES_CAPABLE) { - XTS_SET_KEY_FN(vpaes_set_encrypt_key, vpaes_set_decrypt_key, - vpaes_encrypt, vpaes_decrypt, stream_enc, stream_dec); - return 1; - } else -#endif /* VPAES_CAPABLE */ - { - (void)0; - } - { - XTS_SET_KEY_FN(AES_set_encrypt_key, AES_set_decrypt_key, + return cipher_set_aes_xts_initkey(ctx, key, keylen, + AES_set_encrypt_key, AES_set_decrypt_key, AES_encrypt, AES_decrypt, stream_enc, stream_dec); } - return 1; +#endif /* BSAES_CAPABLE */ + +#ifdef VPAES_CAPABLE + if (VPAES_CAPABLE) { + return cipher_set_aes_xts_initkey(ctx, key, keylen, + vpaes_set_encrypt_key, vpaes_set_decrypt_key, + vpaes_encrypt, vpaes_decrypt, stream_enc, stream_dec); + } +#endif /* VPAES_CAPABLE */ + + return cipher_set_aes_xts_initkey(ctx, key, keylen, + AES_set_encrypt_key, AES_set_decrypt_key, + AES_encrypt, AES_decrypt, stream_enc, stream_dec); } static void cipher_hw_aes_xts_copyctx(PROV_CIPHER_CTX *dst, @@ -103,8 +107,6 @@ static void cipher_hw_aes_xts_copyctx(PROV_CIPHER_CTX *dst, static int cipher_hw_aesni_xts_initkey(PROV_CIPHER_CTX *ctx, const unsigned char *key, size_t keylen) { - PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx; - void (*aesni_xts_enc)(const unsigned char *in, unsigned char *out, size_t length, @@ -131,28 +133,29 @@ static int cipher_hw_aesni_xts_initkey(PROV_CIPHER_CTX *ctx, } #endif - XTS_SET_KEY_FN(aesni_set_encrypt_key, aesni_set_decrypt_key, - aesni_encrypt, aesni_decrypt, - aesni_xts_enc, aesni_xts_dec); - return 1; + return cipher_set_aes_xts_initkey(ctx, key, keylen, + aesni_set_encrypt_key, aesni_set_decrypt_key, + aesni_encrypt, aesni_decrypt, aesni_xts_enc, aesni_xts_dec); } -#define PROV_CIPHER_HW_declare_xts() \ - static const PROV_CIPHER_HW aesni_xts = { \ - cipher_hw_aesni_xts_initkey, \ - NULL, \ - cipher_hw_aes_xts_copyctx \ - }; -#define PROV_CIPHER_HW_select_xts() \ - if (AESNI_CAPABLE) \ +static const PROV_CIPHER_HW aesni_xts = { + cipher_hw_aesni_xts_initkey, + NULL, + cipher_hw_aes_xts_copyctx +}; + +static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_aesni() +{ + if (AESNI_CAPABLE) return &aesni_xts; + return NULL; +} #elif defined(SPARC_AES_CAPABLE) static int cipher_hw_aes_xts_t4_initkey(PROV_CIPHER_CTX *ctx, const unsigned char *key, size_t keylen) { - PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx; OSSL_xts_stream_fn stream_enc = NULL; OSSL_xts_stream_fn stream_dec = NULL; @@ -170,153 +173,109 @@ static int cipher_hw_aes_xts_t4_initkey(PROV_CIPHER_CTX *ctx, return 0; } - XTS_SET_KEY_FN(aes_t4_set_encrypt_key, aes_t4_set_decrypt_key, - aes_t4_encrypt, aes_t4_decrypt, - stream_enc, stream_dec); - return 1; + return cipher_set_aes_xts_initkey(ctx, key, keylen, + aes_t4_set_encrypt_key, aes_t4_set_decrypt_key, + aes_t4_encrypt, aes_t4_decrypt, stream_enc, stream_dec); } -#define PROV_CIPHER_HW_declare_xts() \ - static const PROV_CIPHER_HW aes_xts_t4 = { \ - cipher_hw_aes_xts_t4_initkey, \ - NULL, \ - cipher_hw_aes_xts_copyctx \ - }; -#define PROV_CIPHER_HW_select_xts() \ - if (SPARC_AES_CAPABLE) \ +static const PROV_CIPHER_HW aes_xts_t4 = { + cipher_hw_aes_xts_t4_initkey, + NULL, + cipher_hw_aes_xts_copyctx +}; + +static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_t4() +{ + if (SPARC_AES_CAPABLE) return &aes_xts_t4; + return NULL; +} #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 -static int cipher_hw_aes_xts_rv64i_zknd_zkne_initkey(PROV_CIPHER_CTX *ctx, - const unsigned char *key, - size_t keylen) +static int cipher_hw_aes_xts_rv64i_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) { - PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx; - OSSL_xts_stream_fn stream_enc = NULL; - OSSL_xts_stream_fn stream_dec = NULL; + if (RISCV_HAS_ZVBB() && RISCV_HAS_ZVKG() && RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) { + /* Zvkned only supports 128 and 256 bit keys. */ + if (keylen * 8 == 128 * 2 || keylen * 8 == 256 * 2) + return cipher_set_aes_xts_initkey(ctx, key, keylen, + rv64i_zvkned_set_encrypt_key, rv64i_zvkned_set_decrypt_key, + rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, + rv64i_zvbb_zvkg_zvkned_aes_xts_encrypt, + rv64i_zvbb_zvkg_zvkned_aes_xts_decrypt); - XTS_SET_KEY_FN(rv64i_zkne_set_encrypt_key, rv64i_zknd_set_decrypt_key, - rv64i_zkne_encrypt, rv64i_zknd_decrypt, - stream_enc, stream_dec); - return 1; -} - -static int cipher_hw_aes_xts_rv64i_zvbb_zvkg_zvkned_initkey( - PROV_CIPHER_CTX *ctx, const unsigned char *key, size_t keylen) -{ - PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx; - OSSL_xts_stream_fn stream_enc = NULL; - OSSL_xts_stream_fn stream_dec = NULL; - - /* Zvkned only supports 128 and 256 bit keys. */ - if (keylen * 8 == 128 * 2 || keylen * 8 == 256 * 2) { - XTS_SET_KEY_FN(rv64i_zvkned_set_encrypt_key, - rv64i_zvkned_set_decrypt_key, rv64i_zvkned_encrypt, - rv64i_zvkned_decrypt, - rv64i_zvbb_zvkg_zvkned_aes_xts_encrypt, - rv64i_zvbb_zvkg_zvkned_aes_xts_decrypt); - } else { - XTS_SET_KEY_FN(AES_set_encrypt_key, AES_set_encrypt_key, - rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, - stream_enc, stream_dec); + return cipher_set_aes_xts_initkey(ctx, key, keylen, + AES_set_encrypt_key, AES_set_encrypt_key, + rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, NULL, NULL); } - return 1; -} -static int cipher_hw_aes_xts_rv64i_zvkned_initkey(PROV_CIPHER_CTX *ctx, - const unsigned char *key, - size_t keylen) -{ - PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx; - OSSL_xts_stream_fn stream_enc = NULL; - OSSL_xts_stream_fn stream_dec = NULL; + if (RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) { + /* Zvkned only supports 128 and 256 bit keys. */ + if (keylen * 8 == 128 * 2 || keylen * 8 == 256 * 2) + return cipher_set_aes_xts_initkey(ctx, key, keylen, + rv64i_zvkned_set_encrypt_key, rv64i_zvkned_set_decrypt_key, + rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, NULL, NULL); - /* Zvkned only supports 128 and 256 bit keys. */ - if (keylen * 8 == 128 * 2 || keylen * 8 == 256 * 2) { - XTS_SET_KEY_FN(rv64i_zvkned_set_encrypt_key, - rv64i_zvkned_set_decrypt_key, - rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, - stream_enc, stream_dec); - } else { - XTS_SET_KEY_FN(AES_set_encrypt_key, AES_set_encrypt_key, - rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, - stream_enc, stream_dec); + return cipher_set_aes_xts_initkey(ctx, key, keylen, + AES_set_encrypt_key, AES_set_encrypt_key, + rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, NULL, NULL); } - return 1; + + if (RISCV_HAS_ZKND_AND_ZKNE()) + return cipher_set_aes_xts_initkey(ctx, key, keylen, + rv64i_zkne_set_encrypt_key, rv64i_zknd_set_decrypt_key, + rv64i_zkne_encrypt, rv64i_zknd_decrypt, NULL, NULL); + + return 0; } -#define PROV_CIPHER_HW_declare_xts() \ - static const PROV_CIPHER_HW aes_xts_rv64i_zknd_zkne = { \ - cipher_hw_aes_xts_rv64i_zknd_zkne_initkey, \ - NULL, \ - cipher_hw_aes_xts_copyctx \ - }; \ - static const PROV_CIPHER_HW aes_xts_rv64i_zvkned = { \ - cipher_hw_aes_xts_rv64i_zvkned_initkey, \ - NULL, \ - cipher_hw_aes_xts_copyctx \ - }; \ - static const PROV_CIPHER_HW aes_xts_rv64i_zvbb_zvkg_zvkned = { \ - cipher_hw_aes_xts_rv64i_zvbb_zvkg_zvkned_initkey, \ - NULL, \ - cipher_hw_aes_xts_copyctx \ - }; +static const PROV_CIPHER_HW aes_xts_rv64i = { + cipher_hw_aes_xts_rv64i_initkey, + NULL, + cipher_hw_aes_xts_copyctx +}; -#define PROV_CIPHER_HW_select_xts() \ - if (RISCV_HAS_ZVBB() && RISCV_HAS_ZVKG() && RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) \ - return &aes_xts_rv64i_zvbb_zvkg_zvkned; \ - if (RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) \ - return &aes_xts_rv64i_zvkned; \ - else if (RISCV_HAS_ZKND_AND_ZKNE()) \ - return &aes_xts_rv64i_zknd_zkne; +static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_rv64i() +{ + if ((RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) + || RISCV_HAS_ZKND_AND_ZKNE()) + return &aes_xts_rv64i; + return NULL; +} #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 -static int cipher_hw_aes_xts_rv32i_zknd_zkne_initkey(PROV_CIPHER_CTX *ctx, - const unsigned char *key, - size_t keylen) +static int cipher_hw_aes_xts_rv32i_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) { - PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx; + if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE()) + return cipher_set_aes_xts_initkey(ctx, key, keylen, + rv32i_zbkb_zkne_set_encrypt_key, + rv32i_zbkb_zknd_zkne_set_decrypt_key, + rv32i_zkne_encrypt, rv32i_zknd_decrypt, NULL, NULL); - XTS_SET_KEY_FN(rv32i_zkne_set_encrypt_key, rv32i_zknd_zkne_set_decrypt_key, - rv32i_zkne_encrypt, rv32i_zknd_decrypt, - NULL, NULL); - return 1; + if (RISCV_HAS_ZKND_AND_ZKNE()) + return cipher_set_aes_xts_initkey(ctx, key, keylen, + rv32i_zkne_set_encrypt_key, rv32i_zknd_zkne_set_decrypt_key, + rv32i_zkne_encrypt, rv32i_zknd_decrypt, NULL, NULL); + + return 0; } -static int cipher_hw_aes_xts_rv32i_zbkb_zknd_zkne_initkey(PROV_CIPHER_CTX *ctx, - const unsigned char *key, - size_t keylen) -{ - PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx; +static const PROV_CIPHER_HW aes_xts_rv32i = { + cipher_hw_aes_xts_rv32i_initkey, + NULL, + cipher_hw_aes_xts_copyctx +}; - XTS_SET_KEY_FN(rv32i_zbkb_zkne_set_encrypt_key, rv32i_zbkb_zknd_zkne_set_decrypt_key, - rv32i_zkne_encrypt, rv32i_zknd_decrypt, - NULL, NULL); - return 1; +static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_rv32i() +{ + if (RISCV_HAS_ZKND_AND_ZKNE()) + return &aes_xts_rv32i; + return NULL; } -#define PROV_CIPHER_HW_declare_xts() \ - static const PROV_CIPHER_HW aes_xts_rv32i_zknd_zkne = { \ - cipher_hw_aes_xts_rv32i_zknd_zkne_initkey, \ - NULL, \ - cipher_hw_aes_xts_copyctx \ - }; \ - static const PROV_CIPHER_HW aes_xts_rv32i_zbkb_zknd_zkne = { \ - cipher_hw_aes_xts_rv32i_zbkb_zknd_zkne_initkey, \ - NULL, \ - cipher_hw_aes_xts_copyctx \ - }; -#define PROV_CIPHER_HW_select_xts() \ - if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE()) \ - return &aes_xts_rv32i_zbkb_zknd_zkne; \ - if (RISCV_HAS_ZKND_AND_ZKNE()) \ - return &aes_xts_rv32i_zknd_zkne; -#else -/* The generic case */ -#define PROV_CIPHER_HW_declare_xts() -#define PROV_CIPHER_HW_select_xts() #endif static const PROV_CIPHER_HW aes_generic_xts = { @@ -324,8 +283,23 @@ static const PROV_CIPHER_HW aes_generic_xts = { NULL, cipher_hw_aes_xts_copyctx }; -PROV_CIPHER_HW_declare_xts() - const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts(size_t keybits) + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts(size_t keybits) { - PROV_CIPHER_HW_select_xts() return &aes_generic_xts; + const PROV_CIPHER_HW *aes_xts_hw = NULL; + +#if defined(AESNI_CAPABLE) + aes_xts_hw = ossl_prov_cipher_hw_aes_xts_aesni(); +#elif defined(SPARC_AES_CAPABLE) + aes_xts_hw = ossl_prov_cipher_hw_aes_xts_t4(); +#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 + aes_xts_hw = ossl_prov_cipher_hw_aes_xts_rv64i(); +#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 + aes_xts_hw = ossl_prov_cipher_hw_aes_xts_rv32i(); +#endif + + if (aes_xts_hw == NULL) + return &aes_generic_xts; + + return aes_xts_hw; } From 774525b38bd47b3e895249a81aa885239a7d2061 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Thu, 11 Jun 2026 13:38:05 -0400 Subject: [PATCH 091/349] Migrate s390x AES-XTS to standard HW interface Move the s390x-specific AES-XTS implementation from `cipher_aes_xts_s390x.inc` to `cipher_aes_xts_hw.c`, adapting it to use the standard `PROV_CIPHER_HW` dispatch structure. This refactoring removes standalone initialization wrappers and integrates the s390x hardware backend more cleanly with the generic AES-XTS provider code. It also reduces code duplication by relying on the generic layer for common validations (such as the maximum blocks per data unit limit) before invoking the hardware-specific stream cipher. Signed-off-by: Simo Sorce Reviewed-by: Dmitry Belyavskiy Reviewed-by: Norbert Pocs Reviewed-by: Shane Lontis MergeDate: Sat Jun 27 09:05:46 2026 (Merged from https://github.com/openssl/openssl/pull/31472) --- .../implementations/ciphers/cipher_aes_xts.c | 56 +++--- .../implementations/ciphers/cipher_aes_xts.h | 6 + .../ciphers/cipher_aes_xts_hw.c | 140 +++++++++++++++ .../ciphers/cipher_aes_xts_s390x.inc | 167 ------------------ 4 files changed, 175 insertions(+), 194 deletions(-) delete mode 100644 providers/implementations/ciphers/cipher_aes_xts_s390x.inc diff --git a/providers/implementations/ciphers/cipher_aes_xts.c b/providers/implementations/ciphers/cipher_aes_xts.c index a630b25eb3..10c99c400e 100644 --- a/providers/implementations/ciphers/cipher_aes_xts.c +++ b/providers/implementations/ciphers/cipher_aes_xts.c @@ -63,10 +63,6 @@ static int aes_xts_check_keys_differ(const unsigned char *key, size_t bytes, return 1; } -#ifdef AES_XTS_S390X -#include "cipher_aes_xts_s390x.inc" -#endif - /*- * Provider dispatch functions */ @@ -85,6 +81,17 @@ static int aes_xts_init(void *vctx, const unsigned char *key, size_t keylen, if (iv != NULL) { if (!ossl_cipher_generic_initiv(vctx, iv, ivlen)) return 0; +#ifdef AES_XTS_S390X + if (key == NULL) { + /* special handle iv-only update */ + if (ivlen > sizeof(xctx->plat.s390x.param.km.tweak)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); + return 0; + } + memcpy(xctx->plat.s390x.param.km.tweak, iv, ivlen); + xctx->plat.s390x.iv_set = 1; + } +#endif } if (key != NULL) { if (keylen != ctx->keylen) { @@ -103,10 +110,6 @@ static int aes_xts_einit(void *vctx, const unsigned char *key, size_t keylen, const unsigned char *iv, size_t ivlen, const OSSL_PARAM params[]) { -#ifdef AES_XTS_S390X - if (s390x_aes_xts_einit(vctx, key, keylen, iv, ivlen, params) == 1) - return 1; -#endif return aes_xts_init(vctx, key, keylen, iv, ivlen, params, 1); } @@ -114,10 +117,6 @@ static int aes_xts_dinit(void *vctx, const unsigned char *key, size_t keylen, const unsigned char *iv, size_t ivlen, const OSSL_PARAM params[]) { -#ifdef AES_XTS_S390X - if (s390x_aes_xts_dinit(vctx, key, keylen, iv, ivlen, params) == 1) - return 1; -#endif return aes_xts_init(vctx, key, keylen, iv, ivlen, params, 0); } @@ -152,11 +151,6 @@ static void *aes_xts_dupctx(void *vctx) if (!ossl_prov_is_running()) return NULL; -#ifdef AES_XTS_S390X - if (in->plat.s390x.fc) - return s390x_aes_xts_dupctx(vctx); -#endif - if (in->xts.key1 != NULL) { if (in->xts.key1 != &in->ks1) return NULL; @@ -177,20 +171,23 @@ static int aes_xts_cipher(void *vctx, unsigned char *out, size_t *outl, { PROV_AES_XTS_CTX *ctx = (PROV_AES_XTS_CTX *)vctx; -#ifdef AES_XTS_S390X - if (ctx->plat.s390x.fc) - return s390x_aes_xts_cipher(vctx, out, outl, outsize, in, inl); -#endif - if (!ossl_prov_is_running() - || ctx->xts.key1 == NULL - || ctx->xts.key2 == NULL - || !ctx->base.iv_set - || out == NULL + || inl < AES_BLOCK_SIZE || in == NULL - || inl < AES_BLOCK_SIZE) + || out == NULL) return 0; +#ifdef AES_XTS_S390X + if (ctx->plat.s390x.fc) { + if (!ctx->plat.s390x.iv_set || !ctx->plat.s390x.key_set) + return 0; + } else +#endif + { + if (ctx->xts.key1 == NULL || ctx->xts.key2 == NULL) + return 0; + } + /* * Impose a limit of 2^20 blocks per data unit as specified by * IEEE Std 1619-2018. The earlier and obsolete IEEE Std 1619-2007 @@ -202,6 +199,11 @@ static int aes_xts_cipher(void *vctx, unsigned char *out, size_t *outl, return 0; } +#ifdef AES_XTS_S390X + if (ctx->plat.s390x.fc) + return s390x_aes_xts_cipher_stream(ctx, out, outl, in, inl); +#endif + if (ctx->stream != NULL) (*ctx->stream)(in, out, inl, ctx->xts.key1, ctx->xts.key2, ctx->base.iv); else if (CRYPTO_xts128_encrypt(&ctx->xts, ctx->base.iv, in, out, inl, diff --git a/providers/implementations/ciphers/cipher_aes_xts.h b/providers/implementations/ciphers/cipher_aes_xts.h index 18dc295e47..a2421ae9ef 100644 --- a/providers/implementations/ciphers/cipher_aes_xts.h +++ b/providers/implementations/ciphers/cipher_aes_xts.h @@ -60,6 +60,12 @@ typedef struct prov_aes_xts_ctx_st { } plat; } PROV_AES_XTS_CTX; +#ifdef AES_XTS_S390X +int s390x_aes_xts_cipher_stream(PROV_AES_XTS_CTX *xctx, + unsigned char *out, size_t *outl, + const unsigned char *in, size_t inl); +#endif + const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts(size_t keybits); #endif /* !defined(OSSL_PROVIDERS_IMPLEMENTATIONS_CIPHERS_CIPHER_AES_XTS_H) */ diff --git a/providers/implementations/ciphers/cipher_aes_xts_hw.c b/providers/implementations/ciphers/cipher_aes_xts_hw.c index 6b8c7ab4b4..b9e21df4fd 100644 --- a/providers/implementations/ciphers/cipher_aes_xts_hw.c +++ b/providers/implementations/ciphers/cipher_aes_xts_hw.c @@ -276,6 +276,144 @@ static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_rv32i() return NULL; } +#elif defined(AES_XTS_S390X) + +int s390x_aes_xts_cipher_stream(PROV_AES_XTS_CTX *xctx, + unsigned char *out, size_t *outl, + const unsigned char *in, size_t inl) +{ + S390X_KM_XTS_PARAMS *km = &xctx->plat.s390x.param.km; + unsigned char *param = (unsigned char *)km + xctx->plat.s390x.offset; + unsigned int fc = xctx->plat.s390x.fc; + unsigned char tmp[2][AES_BLOCK_SIZE]; + unsigned char nap_n1[AES_BLOCK_SIZE]; + unsigned char drop[AES_BLOCK_SIZE]; + size_t len_incomplete, len_complete; + + len_incomplete = inl % AES_BLOCK_SIZE; + len_complete = (len_incomplete == 0) ? inl : (inl / AES_BLOCK_SIZE - 1) * AES_BLOCK_SIZE; + + if (len_complete > 0) + s390x_km(in, len_complete, out, fc, param); + if (len_incomplete == 0) + goto out; + + memcpy(tmp, in + len_complete, AES_BLOCK_SIZE + len_incomplete); + /* swap NAP for decrypt */ + if (fc & S390X_DECRYPT) { + memcpy(nap_n1, km->nap, AES_BLOCK_SIZE); + s390x_km(tmp[0], AES_BLOCK_SIZE, drop, fc, param); + } + s390x_km(tmp[0], AES_BLOCK_SIZE, tmp[0], fc, param); + if (fc & S390X_DECRYPT) + memcpy(km->nap, nap_n1, AES_BLOCK_SIZE); + + memcpy(tmp[1] + len_incomplete, tmp[0] + len_incomplete, + AES_BLOCK_SIZE - len_incomplete); + s390x_km(tmp[1], AES_BLOCK_SIZE, out + len_complete, fc, param); + memcpy(out + len_complete + AES_BLOCK_SIZE, tmp[0], len_incomplete); + + /* do not expose temporary data */ + OPENSSL_cleanse(tmp, sizeof(tmp)); +out: + memcpy(xctx->base.iv, km->tweak, AES_BLOCK_SIZE); + *outl = inl; + + return 1; +} + +static int cipher_hw_aes_xts_s390x_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx; + S390X_KM_XTS_PARAMS *km = &xctx->plat.s390x.param.km; + unsigned int fc, offs; + unsigned int dec = 0; + int supported = 0; + + switch (keylen) { + case 128 / 8 * 2: + fc = S390X_XTS_AES_128_MSA10; + offs = 32; + break; + case 256 / 8 * 2: + fc = S390X_XTS_AES_256_MSA10; + offs = 0; + break; + default: + fc = 0; + break; + } + + if (fc != 0) + supported = (OPENSSL_s390xcap_P.km[1] && S390X_CAPBIT(fc)); + if (!supported) { + xctx->plat.s390x.fc = 0; + xctx->plat.s390x.offset = 0; + return 0; + } + + if (xctx->base.iv_set) { + if (xctx->base.ivlen > sizeof(km->tweak)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); + return 0; + } + memcpy(km->tweak, xctx->base.iv, xctx->base.ivlen); + xctx->plat.s390x.iv_set = 1; + } + + if (key != NULL) { + memcpy(km->key + offs, key, keylen); + xctx->plat.s390x.key_set = 1; + } + + if (xctx->base.enc == 0) + dec = S390X_DECRYPT; + + xctx->plat.s390x.fc = fc | dec; + xctx->plat.s390x.offset = offs; + + memset(km->nap, 0, sizeof(km->nap)); + km->nap[0] = 0x1; + + return 1; +} + +static void cipher_hw_aes_xts_s390x_copyctx(PROV_CIPHER_CTX *dst, + const PROV_CIPHER_CTX *src) +{ + PROV_AES_XTS_CTX *sctx = (PROV_AES_XTS_CTX *)src; + PROV_AES_XTS_CTX *dctx = (PROV_AES_XTS_CTX *)dst; + + *dctx = *sctx; + dctx->xts.key1 = NULL; + dctx->xts.key2 = NULL; +} + +static const PROV_CIPHER_HW aes_xts_s390x = { + cipher_hw_aes_xts_s390x_initkey, + NULL, + cipher_hw_aes_xts_s390x_copyctx +}; + +static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_s390x(size_t keybits) +{ + switch (keybits) { + case (128 * 2): + if (OPENSSL_s390xcap_P.km[1] && S390X_CAPBIT(S390X_XTS_AES_128_MSA10)) + return &aes_xts_s390x; + break; + case (256 * 2): + if (OPENSSL_s390xcap_P.km[1] && S390X_CAPBIT(S390X_XTS_AES_256_MSA10)) + return &aes_xts_s390x; + break; + default: + break; + } + + return NULL; +} + #endif static const PROV_CIPHER_HW aes_generic_xts = { @@ -296,6 +434,8 @@ const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts(size_t keybits) aes_xts_hw = ossl_prov_cipher_hw_aes_xts_rv64i(); #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 aes_xts_hw = ossl_prov_cipher_hw_aes_xts_rv32i(); +#elif defined(AES_XTS_S390X) + aes_xts_hw = ossl_prov_cipher_hw_aes_xts_s390x(keybits); #endif if (aes_xts_hw == NULL) diff --git a/providers/implementations/ciphers/cipher_aes_xts_s390x.inc b/providers/implementations/ciphers/cipher_aes_xts_s390x.inc deleted file mode 100644 index b13d23581c..0000000000 --- a/providers/implementations/ciphers/cipher_aes_xts_s390x.inc +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -#include "arch/s390x_arch.h" - -static OSSL_FUNC_cipher_encrypt_init_fn s390x_aes_xts_einit; -static OSSL_FUNC_cipher_decrypt_init_fn s390x_aes_xts_dinit; -static OSSL_FUNC_cipher_cipher_fn s390x_aes_xts_cipher; -static OSSL_FUNC_cipher_dupctx_fn s390x_aes_xts_dupctx; - -static int s390x_aes_xts_init(void *vctx, const unsigned char *key, - size_t keylen, const unsigned char *iv, - size_t ivlen, const OSSL_PARAM params[], - unsigned int dec) -{ - PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)vctx; - S390X_KM_XTS_PARAMS *km = &xctx->plat.s390x.param.km; - unsigned int fc, offs; - - switch (xctx->base.keylen) { - case 128 / 8 * 2: - fc = S390X_XTS_AES_128_MSA10; - offs = 32; - break; - case 256 / 8 * 2: - fc = S390X_XTS_AES_256_MSA10; - offs = 0; - break; - default: - goto not_supported; - } - - if (!(OPENSSL_s390xcap_P.km[1] && S390X_CAPBIT(fc))) - goto not_supported; - - if (iv != NULL) { - if (ivlen != xctx->base.ivlen - || ivlen > sizeof(km->tweak)) { - ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); - return 0; - } - memcpy(km->tweak, iv, ivlen); - xctx->plat.s390x.iv_set = 1; - } - - if (key != NULL) { - if (keylen != xctx->base.keylen) { - ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); - return 0; - } - if (!aes_xts_check_keys_differ(key, keylen / 2, !dec)) - return 0; - - memcpy(km->key + offs, key, keylen); - xctx->plat.s390x.key_set = 1; - } - - xctx->plat.s390x.fc = fc | dec; - xctx->plat.s390x.offset = offs; - - memset(km->nap, 0, sizeof(km->nap)); - km->nap[0] = 0x1; - - return aes_xts_set_ctx_params(xctx, params); - -not_supported: - xctx->plat.s390x.fc = 0; - xctx->plat.s390x.offset = 0; - return 0; -} - -static int s390x_aes_xts_einit(void *vctx, const unsigned char *key, - size_t keylen, const unsigned char *iv, - size_t ivlen, const OSSL_PARAM params[]) -{ - return s390x_aes_xts_init(vctx, key, keylen, iv, ivlen, params, 0); -} - -static int s390x_aes_xts_dinit(void *vctx, const unsigned char *key, - size_t keylen, const unsigned char *iv, - size_t ivlen, const OSSL_PARAM params[]) -{ - return s390x_aes_xts_init(vctx, key, keylen, iv, ivlen, params, - S390X_DECRYPT); -} - -static void *s390x_aes_xts_dupctx(void *vctx) -{ - PROV_AES_XTS_CTX *in = (PROV_AES_XTS_CTX *)vctx; - PROV_AES_XTS_CTX *ret = OPENSSL_zalloc(sizeof(*in)); - - if (ret != NULL) - *ret = *in; - - return ret; -} - -static int s390x_aes_xts_cipher(void *vctx, unsigned char *out, size_t *outl, - size_t outsize, const unsigned char *in, - size_t inl) -{ - PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)vctx; - S390X_KM_XTS_PARAMS *km = &xctx->plat.s390x.param.km; - unsigned char *param = (unsigned char *)km + xctx->plat.s390x.offset; - unsigned int fc = xctx->plat.s390x.fc; - unsigned char tmp[2][AES_BLOCK_SIZE]; - unsigned char nap_n1[AES_BLOCK_SIZE]; - unsigned char drop[AES_BLOCK_SIZE]; - size_t len_incomplete, len_complete; - - if (!ossl_prov_is_running() - || inl < AES_BLOCK_SIZE - || in == NULL - || out == NULL - || !xctx->plat.s390x.iv_set - || !xctx->plat.s390x.key_set) - return 0; - - /* - * Impose a limit of 2^20 blocks per data unit as specified by - * IEEE Std 1619-2018. The earlier and obsolete IEEE Std 1619-2007 - * indicated that this was a SHOULD NOT rather than a MUST NOT. - * NIST SP 800-38E mandates the same limit. - */ - if (inl > XTS_MAX_BLOCKS_PER_DATA_UNIT * AES_BLOCK_SIZE) { - ERR_raise(ERR_LIB_PROV, PROV_R_XTS_DATA_UNIT_IS_TOO_LARGE); - return 0; - } - - len_incomplete = inl % AES_BLOCK_SIZE; - len_complete = (len_incomplete == 0) ? inl : - (inl / AES_BLOCK_SIZE - 1) * AES_BLOCK_SIZE; - - if (len_complete > 0) - s390x_km(in, len_complete, out, fc, param); - if (len_incomplete == 0) - goto out; - - memcpy(tmp, in + len_complete, AES_BLOCK_SIZE + len_incomplete); - /* swap NAP for decrypt */ - if (fc & S390X_DECRYPT) { - memcpy(nap_n1, km->nap, AES_BLOCK_SIZE); - s390x_km(tmp[0], AES_BLOCK_SIZE, drop, fc, param); - } - s390x_km(tmp[0], AES_BLOCK_SIZE, tmp[0], fc, param); - if (fc & S390X_DECRYPT) - memcpy(km->nap, nap_n1, AES_BLOCK_SIZE); - - memcpy(tmp[1] + len_incomplete, tmp[0] + len_incomplete, - AES_BLOCK_SIZE - len_incomplete); - s390x_km(tmp[1], AES_BLOCK_SIZE, out + len_complete, fc, param); - memcpy(out + len_complete + AES_BLOCK_SIZE, tmp[0], len_incomplete); - - /* do not expose temporary data */ - OPENSSL_cleanse(tmp, sizeof(tmp)); -out: - memcpy(xctx->base.iv, km->tweak, AES_BLOCK_SIZE); - *outl = inl; - - return 1; -} From 16234b9073dd20d59e00676e631a950c09443d5d Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Thu, 11 Jun 2026 16:44:12 -0400 Subject: [PATCH 092/349] Consolidate AESNI hardware implementations Move the AES-NI specific hardware implementations for GCM, CCM, and XTS modes from individual mode files into a single consolidated file (`cipher_aes_hw_aesni.c`). This groups architecture-specific optimizations together, improving code organization and maintainability. As part of this refactoring, the unused `keybits` parameter was removed from several AES-NI provider functions, and necessary XTS initialization and context copy functions were exported for shared use. Signed-off-by: Simo Sorce Reviewed-by: Dmitry Belyavskiy Reviewed-by: Norbert Pocs Reviewed-by: Shane Lontis MergeDate: Sat Jun 27 09:05:48 2026 (Merged from https://github.com/openssl/openssl/pull/31472) --- providers/implementations/ciphers/build.info | 4 +- .../implementations/ciphers/cipher_aes.h | 3 +- .../implementations/ciphers/cipher_aes_ccm.h | 2 +- .../ciphers/cipher_aes_ccm_hw.c | 2 +- .../ciphers/cipher_aes_ccm_hw_aesni.c | 44 --- .../implementations/ciphers/cipher_aes_gcm.h | 2 +- .../ciphers/cipher_aes_gcm_hw.c | 2 +- .../ciphers/cipher_aes_gcm_hw_aesni.c | 235 -------------- .../implementations/ciphers/cipher_aes_hw.c | 2 +- .../ciphers/cipher_aes_hw_aesni.c | 303 +++++++++++++++++- .../implementations/ciphers/cipher_aes_xts.h | 14 + .../ciphers/cipher_aes_xts_hw.c | 87 ++--- 12 files changed, 341 insertions(+), 359 deletions(-) delete mode 100644 providers/implementations/ciphers/cipher_aes_ccm_hw_aesni.c delete mode 100644 providers/implementations/ciphers/cipher_aes_gcm_hw_aesni.c diff --git a/providers/implementations/ciphers/build.info b/providers/implementations/ciphers/build.info index 54e1a11469..720874ba78 100644 --- a/providers/implementations/ciphers/build.info +++ b/providers/implementations/ciphers/build.info @@ -107,11 +107,11 @@ SOURCE[$AES_GOAL]=\ cipher_aes_hw_armv8.c cipher_aes_hw_rv32i.c cipher_aes_hw_rv64i.c \ cipher_aes_hw_s390x.c cipher_aes_hw_t4.c \ cipher_aes_xts.c cipher_aes_xts_hw.c \ - cipher_aes_gcm.c cipher_aes_gcm_hw.c cipher_aes_gcm_hw_aesni.c \ + cipher_aes_gcm.c cipher_aes_gcm_hw.c \ cipher_aes_gcm_hw_armv8.c cipher_aes_gcm_hw_ppc.c \ cipher_aes_gcm_hw_rv32i.c cipher_aes_gcm_hw_rv64i.c \ cipher_aes_gcm_hw_s390x.c cipher_aes_gcm_hw_t4.c \ - cipher_aes_ccm.c cipher_aes_ccm_hw.c cipher_aes_ccm_hw_aesni.c \ + cipher_aes_ccm.c cipher_aes_ccm_hw.c \ cipher_aes_ccm_hw_rv32i.c cipher_aes_ccm_hw_rv64i.c \ cipher_aes_ccm_hw_s390x.c cipher_aes_ccm_hw_t4.c \ cipher_aes_wrp.c \ diff --git a/providers/implementations/ciphers/cipher_aes.h b/providers/implementations/ciphers/cipher_aes.h index c5dfbc59e2..313c91ec9c 100644 --- a/providers/implementations/ciphers/cipher_aes.h +++ b/providers/implementations/ciphers/cipher_aes.h @@ -86,8 +86,7 @@ int ossl_cipher_hw_aes_initkey(PROV_CIPHER_CTX *ctx, void ossl_cipher_aes_copyctx(PROV_CIPHER_CTX *dst, const PROV_CIPHER_CTX *src); #if defined(AESNI_CAPABLE) -const PROV_CIPHER_HW *ossl_prov_cipher_hw_aesni(enum aes_modes mode, - size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aesni(enum aes_modes mode); #elif defined(ARMv8_HWAES_CAPABLE) const PROV_CIPHER_HW *ossl_prov_cipher_hw_arm(enum aes_modes mode, size_t keybits); diff --git a/providers/implementations/ciphers/cipher_aes_ccm.h b/providers/implementations/ciphers/cipher_aes_ccm.h index 91495015a8..be690dadb9 100644 --- a/providers/implementations/ciphers/cipher_aes_ccm.h +++ b/providers/implementations/ciphers/cipher_aes_ccm.h @@ -56,7 +56,7 @@ int ossl_cipher_set_ccm_aes_initkey(PROV_CCM_CTX *ctx, const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keylen); #if defined(AESNI_CAPABLE) -const PROV_CCM_HW *ossl_prov_aes_hw_ccm_aesni(size_t keybits); +const PROV_CCM_HW *ossl_prov_aes_hw_ccm_aesni(void); #endif #if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 const PROV_CCM_HW *ossl_prov_aes_hw_ccm_rv32i(size_t keybits); diff --git a/providers/implementations/ciphers/cipher_aes_ccm_hw.c b/providers/implementations/ciphers/cipher_aes_ccm_hw.c index 8ee89a1bd6..fa7318d460 100644 --- a/providers/implementations/ciphers/cipher_aes_ccm_hw.c +++ b/providers/implementations/ciphers/cipher_aes_ccm_hw.c @@ -73,7 +73,7 @@ const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keybits) { const PROV_CCM_HW *aes_ccm_hw = NULL; #if defined(AESNI_CAPABLE) - aes_ccm_hw = ossl_prov_aes_hw_ccm_aesni(keybits); + aes_ccm_hw = ossl_prov_aes_hw_ccm_aesni(); #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 aes_ccm_hw = ossl_prov_aes_hw_ccm_rv32i(keybits); #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 diff --git a/providers/implementations/ciphers/cipher_aes_ccm_hw_aesni.c b/providers/implementations/ciphers/cipher_aes_ccm_hw_aesni.c deleted file mode 100644 index 67644b324a..0000000000 --- a/providers/implementations/ciphers/cipher_aes_ccm_hw_aesni.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2001-2021 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/*- - * AES-NI support for AES CCM. - * This file is used by cipher_aes_ccm_hw.c - */ - -#include "internal/deprecated.h" -#include "cipher_aes_ccm.h" - -#if defined(AESNI_CAPABLE) - -static int ccm_aesni_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, - size_t keylen) -{ - return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, - aesni_set_encrypt_key, aesni_encrypt, aesni_ccm64_encrypt_blocks, - aesni_ccm64_decrypt_blocks); -} - -static const PROV_CCM_HW aesni_ccm = { - ccm_aesni_initkey, - ossl_ccm_generic_setiv, - ossl_ccm_generic_setaad, - ossl_ccm_generic_auth_encrypt, - ossl_ccm_generic_auth_decrypt, - ossl_ccm_generic_gettag -}; - -const PROV_CCM_HW *ossl_prov_aes_hw_ccm_aesni(size_t keybits) -{ - if (AESNI_CAPABLE) - return &aesni_ccm; - return NULL; -} - -#endif diff --git a/providers/implementations/ciphers/cipher_aes_gcm.h b/providers/implementations/ciphers/cipher_aes_gcm.h index d8ce0b4563..71ee4c97eb 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm.h +++ b/providers/implementations/ciphers/cipher_aes_gcm.h @@ -54,7 +54,7 @@ int generic_aes_gcm_cipher_update(PROV_GCM_CTX *ctx, const unsigned char *in, const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits); #if defined(AESNI_CAPABLE) -const PROV_GCM_HW *ossl_prov_aes_hw_gcm_aesni(size_t keybits); +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_aesni(void); #endif #if defined(AES_PMULL_CAPABLE) && defined(AES_GCM_ASM) const PROV_GCM_HW *ossl_prov_aes_hw_gcm_armv8(size_t keybits); diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw.c b/providers/implementations/ciphers/cipher_aes_gcm_hw.c index bcdb879400..41ff71cd21 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw.c +++ b/providers/implementations/ciphers/cipher_aes_gcm_hw.c @@ -156,7 +156,7 @@ const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits) const PROV_GCM_HW *aes_gcm_hw = NULL; #if defined(AESNI_CAPABLE) - aes_gcm_hw = ossl_prov_aes_hw_gcm_aesni(keybits); + aes_gcm_hw = ossl_prov_aes_hw_gcm_aesni(); #elif defined(AES_PMULL_CAPABLE) && defined(AES_GCM_ASM) aes_gcm_hw = ossl_prov_aes_hw_gcm_armv8(keybits); #elif defined(PPC_AES_GCM_CAPABLE) && defined(_ARCH_PPC64) diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw_aesni.c b/providers/implementations/ciphers/cipher_aes_gcm_hw_aesni.c deleted file mode 100644 index 2b8938f27b..0000000000 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw_aesni.c +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright 2001-2026 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/*- - * AES-NI support for AES GCM. - * This file is used by cipher_aes_gcm_hw.c - */ -#include "internal/deprecated.h" -#include "cipher_aes_gcm.h" - -#if defined(AESNI_CAPABLE) - -static int aesni_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, - size_t keylen) -{ - return aes_gcm_hw_initkey(ctx, key, keylen, aesni_set_encrypt_key, - aesni_encrypt, aesni_ctr32_encrypt_blocks); -} - -static const PROV_GCM_HW aesni_gcm = { - aesni_gcm_initkey, - ossl_gcm_setiv, - ossl_gcm_aad_update, - generic_aes_gcm_cipher_update, - ossl_gcm_cipher_final, - ossl_gcm_one_shot -}; - -/*- - * AVX512 VAES + VPCLMULDQD support for AES GCM. - */ - -#undef VAES_GCM_ENABLED -#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_X64)) -#define VAES_GCM_ENABLED - -/* Returns non-zero when AVX512F + VAES + VPCLMULDQD combination is available */ -int ossl_vaes_vpclmulqdq_capable(void); - -void ossl_aes_gcm_encrypt_avx512(const void *ks, void *gcm128ctx, - unsigned int *pblocklen, const unsigned char *in, size_t len, - unsigned char *out); -void ossl_aes_gcm_decrypt_avx512(const void *ks, void *gcm128ctx, - unsigned int *pblocklen, const unsigned char *in, size_t len, - unsigned char *out); - -void ossl_aes_gcm_init_avx512(const void *ks, void *gcm128ctx); -void ossl_aes_gcm_setiv_avx512(const void *ks, void *gcm128ctx, - const unsigned char *iv, size_t ivlen); -void ossl_aes_gcm_update_aad_avx512(void *gcm128ctx, const unsigned char *aad, - size_t aadlen); -void ossl_aes_gcm_finalize_avx512(void *gcm128ctx, unsigned int pblocklen); - -void ossl_gcm_gmult_avx512(uint64_t Xi[2], const void *gcm128ctx); - -static int vaes_gcm_setkey(PROV_GCM_CTX *ctx, const unsigned char *key, - size_t keylen) -{ - GCM128_CONTEXT *gcmctx = &ctx->gcm; - PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; - AES_KEY *ks = &actx->ks.ks; - - aesni_set_encrypt_key(key, (int)(keylen * 8), ks); - memset(gcmctx, 0, sizeof(*gcmctx)); - gcmctx->key = ks; - ctx->key_set = 1; - - ossl_aes_gcm_init_avx512(ks, gcmctx); - - return 1; -} - -static int vaes_gcm_setiv(PROV_GCM_CTX *ctx, const unsigned char *iv, - size_t ivlen) -{ - GCM128_CONTEXT *gcmctx = &ctx->gcm; - - gcmctx->Yi.u[0] = 0; /* Current counter */ - gcmctx->Yi.u[1] = 0; - gcmctx->Xi.u[0] = 0; /* AAD hash */ - gcmctx->Xi.u[1] = 0; - gcmctx->len.u[0] = 0; /* AAD length */ - gcmctx->len.u[1] = 0; /* Message length */ - gcmctx->ares = 0; - gcmctx->mres = 0; - - /* IV is limited by 2^64 bits, thus 2^61 bytes */ - if (ivlen > (U64(1) << 61)) - return 0; - - ossl_aes_gcm_setiv_avx512(gcmctx->key, gcmctx, iv, ivlen); - - return 1; -} - -static int vaes_gcm_aadupdate(PROV_GCM_CTX *ctx, - const unsigned char *aad, - size_t aad_len) -{ - GCM128_CONTEXT *gcmctx = &ctx->gcm; - uint64_t alen = gcmctx->len.u[0]; - unsigned int ares; - size_t i, lenBlks; - - /* Bad sequence: call of AAD update after message processing */ - if (gcmctx->len.u[1] > 0) - return 0; - - alen += aad_len; - /* AAD is limited by 2^64 bits, thus 2^61 bytes */ - if ((alen > (U64(1) << 61)) || (alen < aad_len)) - return 0; - - gcmctx->len.u[0] = alen; - - ares = gcmctx->ares; - /* Partial AAD block left from previous AAD update calls */ - if (ares > 0) { - /* - * Fill partial block buffer till full block - * (note, the hash is stored reflected) - */ - while (ares > 0 && aad_len > 0) { - gcmctx->Xi.c[15 - ares] ^= *(aad++); - --aad_len; - ares = (ares + 1) % AES_BLOCK_SIZE; - } - /* Full block gathered */ - if (ares == 0) { - ossl_gcm_gmult_avx512(gcmctx->Xi.u, gcmctx); - } else { /* no more AAD */ - gcmctx->ares = ares; - return 1; - } - } - - /* Bulk AAD processing */ - lenBlks = aad_len & ((size_t)(-AES_BLOCK_SIZE)); - if (lenBlks > 0) { - ossl_aes_gcm_update_aad_avx512(gcmctx, aad, lenBlks); - aad += lenBlks; - aad_len -= lenBlks; - } - - /* Add remaining AAD to the hash (note, the hash is stored reflected) */ - if (aad_len > 0) { - ares = (unsigned int)aad_len; - for (i = 0; i < aad_len; i++) - gcmctx->Xi.c[15 - i] ^= aad[i]; - } - - gcmctx->ares = ares; - - return 1; -} - -static int vaes_gcm_cipherupdate(PROV_GCM_CTX *ctx, const unsigned char *in, - size_t len, unsigned char *out) -{ - GCM128_CONTEXT *gcmctx = &ctx->gcm; - uint64_t mlen = gcmctx->len.u[1]; - - mlen += len; - if (mlen > ((U64(1) << 36) - 32) || (mlen < len)) - return 0; - - gcmctx->len.u[1] = mlen; - - /* Finalize GHASH(AAD) if AAD partial blocks left unprocessed */ - if (gcmctx->ares > 0) { - ossl_gcm_gmult_avx512(gcmctx->Xi.u, gcmctx); - gcmctx->ares = 0; - } - - if (ctx->enc) - ossl_aes_gcm_encrypt_avx512(gcmctx->key, gcmctx, &gcmctx->mres, in, len, out); - else - ossl_aes_gcm_decrypt_avx512(gcmctx->key, gcmctx, &gcmctx->mres, in, len, out); - - return 1; -} - -static int vaes_gcm_cipherfinal(PROV_GCM_CTX *ctx, unsigned char *tag) -{ - GCM128_CONTEXT *gcmctx = &ctx->gcm; - unsigned int *res = &gcmctx->mres; - - /* Finalize AAD processing */ - if (gcmctx->ares > 0) - res = &gcmctx->ares; - - ossl_aes_gcm_finalize_avx512(gcmctx, *res); - - if (ctx->enc) { - ctx->taglen = GCM_TAG_MAX_SIZE; - memcpy(tag, gcmctx->Xi.c, - ctx->taglen <= sizeof(gcmctx->Xi.c) ? ctx->taglen : sizeof(gcmctx->Xi.c)); - *res = 0; - } else { - return !CRYPTO_memcmp(gcmctx->Xi.c, tag, ctx->taglen); - } - - return 1; -} - -static const PROV_GCM_HW vaes_gcm = { - vaes_gcm_setkey, - vaes_gcm_setiv, - vaes_gcm_aadupdate, - vaes_gcm_cipherupdate, - vaes_gcm_cipherfinal, - ossl_gcm_one_shot -}; - -#endif - -const PROV_GCM_HW *ossl_prov_aes_hw_gcm_aesni(size_t keybits) -{ -#ifdef VAES_GCM_ENABLED - if (ossl_vaes_vpclmulqdq_capable()) - return &vaes_gcm; -#endif - if (AESNI_CAPABLE) - return &aesni_gcm; - - return NULL; -} - -#endif diff --git a/providers/implementations/ciphers/cipher_aes_hw.c b/providers/implementations/ciphers/cipher_aes_hw.c index 5ad1d81e94..60abfd1165 100644 --- a/providers/implementations/ciphers/cipher_aes_hw.c +++ b/providers/implementations/ciphers/cipher_aes_hw.c @@ -217,7 +217,7 @@ static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_mode(enum aes_modes mode, const PROV_CIPHER_HW *aes_hw_mode = NULL; #if defined(AESNI_CAPABLE) - aes_hw_mode = ossl_prov_cipher_hw_aesni(mode, keybits); + aes_hw_mode = ossl_prov_cipher_hw_aesni(mode); #elif defined(ARMv8_HWAES_CAPABLE) aes_hw_mode = ossl_prov_cipher_hw_arm(mode, keybits); #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 diff --git a/providers/implementations/ciphers/cipher_aes_hw_aesni.c b/providers/implementations/ciphers/cipher_aes_hw_aesni.c index d434874a43..6264c88426 100644 --- a/providers/implementations/ciphers/cipher_aes_hw_aesni.c +++ b/providers/implementations/ciphers/cipher_aes_hw_aesni.c @@ -1,5 +1,5 @@ /* - * Copyright 2001-2025 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2001-2026 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -14,9 +14,14 @@ #include "internal/deprecated.h" #include "cipher_aes.h" +#include "cipher_aes_gcm.h" +#include "cipher_aes_ccm.h" +#include "cipher_aes_xts.h" #if defined(AESNI_CAPABLE) +/* MODES: ecb, cbc, cfb, ofb, ctr */ + /* generates AES round keys for AES-NI and VAES implementations */ static int cipher_hw_aesni_initkey(PROV_CIPHER_CTX *ctx, const unsigned char *key, size_t keylen) @@ -135,8 +140,7 @@ static const PROV_CIPHER_HW aesni_ctr = { ossl_cipher_aes_copyctx }; -const PROV_CIPHER_HW *ossl_prov_cipher_hw_aesni(enum aes_modes mode, - size_t keybits) +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aesni(enum aes_modes mode) { if (AESNI_CAPABLE) { switch (mode) { @@ -165,4 +169,297 @@ const PROV_CIPHER_HW *ossl_prov_cipher_hw_aesni(enum aes_modes mode, return NULL; } +/* MODES: GCM */ + +static int aesni_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + return aes_gcm_hw_initkey(ctx, key, keylen, aesni_set_encrypt_key, + aesni_encrypt, aesni_ctr32_encrypt_blocks); +} + +static const PROV_GCM_HW aesni_gcm = { + aesni_gcm_initkey, + ossl_gcm_setiv, + ossl_gcm_aad_update, + generic_aes_gcm_cipher_update, + ossl_gcm_cipher_final, + ossl_gcm_one_shot +}; + +/*- + * AVX512 VAES + VPCLMULDQD support for AES GCM. + */ + +#undef VAES_GCM_ENABLED +#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_X64)) +#define VAES_GCM_ENABLED + +/* Returns non-zero when AVX512F + VAES + VPCLMULDQD combination is available */ +int ossl_vaes_vpclmulqdq_capable(void); + +void ossl_aes_gcm_encrypt_avx512(const void *ks, void *gcm128ctx, + unsigned int *pblocklen, const unsigned char *in, size_t len, + unsigned char *out); +void ossl_aes_gcm_decrypt_avx512(const void *ks, void *gcm128ctx, + unsigned int *pblocklen, const unsigned char *in, size_t len, + unsigned char *out); + +void ossl_aes_gcm_init_avx512(const void *ks, void *gcm128ctx); +void ossl_aes_gcm_setiv_avx512(const void *ks, void *gcm128ctx, + const unsigned char *iv, size_t ivlen); +void ossl_aes_gcm_update_aad_avx512(void *gcm128ctx, const unsigned char *aad, + size_t aadlen); +void ossl_aes_gcm_finalize_avx512(void *gcm128ctx, unsigned int pblocklen); + +void ossl_gcm_gmult_avx512(uint64_t Xi[2], const void *gcm128ctx); + +static int vaes_gcm_setkey(PROV_GCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + GCM128_CONTEXT *gcmctx = &ctx->gcm; + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + AES_KEY *ks = &actx->ks.ks; + + aesni_set_encrypt_key(key, (int)(keylen * 8), ks); + memset(gcmctx, 0, sizeof(*gcmctx)); + gcmctx->key = ks; + ctx->key_set = 1; + + ossl_aes_gcm_init_avx512(ks, gcmctx); + + return 1; +} + +static int vaes_gcm_setiv(PROV_GCM_CTX *ctx, const unsigned char *iv, + size_t ivlen) +{ + GCM128_CONTEXT *gcmctx = &ctx->gcm; + + gcmctx->Yi.u[0] = 0; /* Current counter */ + gcmctx->Yi.u[1] = 0; + gcmctx->Xi.u[0] = 0; /* AAD hash */ + gcmctx->Xi.u[1] = 0; + gcmctx->len.u[0] = 0; /* AAD length */ + gcmctx->len.u[1] = 0; /* Message length */ + gcmctx->ares = 0; + gcmctx->mres = 0; + + /* IV is limited by 2^64 bits, thus 2^61 bytes */ + if (ivlen > (U64(1) << 61)) + return 0; + + ossl_aes_gcm_setiv_avx512(gcmctx->key, gcmctx, iv, ivlen); + + return 1; +} + +static int vaes_gcm_aadupdate(PROV_GCM_CTX *ctx, + const unsigned char *aad, + size_t aad_len) +{ + GCM128_CONTEXT *gcmctx = &ctx->gcm; + uint64_t alen = gcmctx->len.u[0]; + unsigned int ares; + size_t i, lenBlks; + + /* Bad sequence: call of AAD update after message processing */ + if (gcmctx->len.u[1] > 0) + return 0; + + alen += aad_len; + /* AAD is limited by 2^64 bits, thus 2^61 bytes */ + if ((alen > (U64(1) << 61)) || (alen < aad_len)) + return 0; + + gcmctx->len.u[0] = alen; + + ares = gcmctx->ares; + /* Partial AAD block left from previous AAD update calls */ + if (ares > 0) { + /* + * Fill partial block buffer till full block + * (note, the hash is stored reflected) + */ + while (ares > 0 && aad_len > 0) { + gcmctx->Xi.c[15 - ares] ^= *(aad++); + --aad_len; + ares = (ares + 1) % AES_BLOCK_SIZE; + } + /* Full block gathered */ + if (ares == 0) { + ossl_gcm_gmult_avx512(gcmctx->Xi.u, gcmctx); + } else { /* no more AAD */ + gcmctx->ares = ares; + return 1; + } + } + + /* Bulk AAD processing */ + lenBlks = aad_len & ((size_t)(-AES_BLOCK_SIZE)); + if (lenBlks > 0) { + ossl_aes_gcm_update_aad_avx512(gcmctx, aad, lenBlks); + aad += lenBlks; + aad_len -= lenBlks; + } + + /* Add remaining AAD to the hash (note, the hash is stored reflected) */ + if (aad_len > 0) { + ares = (unsigned int)aad_len; + for (i = 0; i < aad_len; i++) + gcmctx->Xi.c[15 - i] ^= aad[i]; + } + + gcmctx->ares = ares; + + return 1; +} + +static int vaes_gcm_cipherupdate(PROV_GCM_CTX *ctx, const unsigned char *in, + size_t len, unsigned char *out) +{ + GCM128_CONTEXT *gcmctx = &ctx->gcm; + uint64_t mlen = gcmctx->len.u[1]; + + mlen += len; + if (mlen > ((U64(1) << 36) - 32) || (mlen < len)) + return 0; + + gcmctx->len.u[1] = mlen; + + /* Finalize GHASH(AAD) if AAD partial blocks left unprocessed */ + if (gcmctx->ares > 0) { + ossl_gcm_gmult_avx512(gcmctx->Xi.u, gcmctx); + gcmctx->ares = 0; + } + + if (ctx->enc) + ossl_aes_gcm_encrypt_avx512(gcmctx->key, gcmctx, &gcmctx->mres, in, len, out); + else + ossl_aes_gcm_decrypt_avx512(gcmctx->key, gcmctx, &gcmctx->mres, in, len, out); + + return 1; +} + +static int vaes_gcm_cipherfinal(PROV_GCM_CTX *ctx, unsigned char *tag) +{ + GCM128_CONTEXT *gcmctx = &ctx->gcm; + unsigned int *res = &gcmctx->mres; + + /* Finalize AAD processing */ + if (gcmctx->ares > 0) + res = &gcmctx->ares; + + ossl_aes_gcm_finalize_avx512(gcmctx, *res); + + if (ctx->enc) { + ctx->taglen = GCM_TAG_MAX_SIZE; + memcpy(tag, gcmctx->Xi.c, + ctx->taglen <= sizeof(gcmctx->Xi.c) ? ctx->taglen : sizeof(gcmctx->Xi.c)); + *res = 0; + } else { + return !CRYPTO_memcmp(gcmctx->Xi.c, tag, ctx->taglen); + } + + return 1; +} + +static const PROV_GCM_HW vaes_gcm = { + vaes_gcm_setkey, + vaes_gcm_setiv, + vaes_gcm_aadupdate, + vaes_gcm_cipherupdate, + vaes_gcm_cipherfinal, + ossl_gcm_one_shot +}; + +#endif + +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_aesni(void) +{ +#ifdef VAES_GCM_ENABLED + if (ossl_vaes_vpclmulqdq_capable()) + return &vaes_gcm; +#endif + if (AESNI_CAPABLE) + return &aesni_gcm; + + return NULL; +} + +/* MODES: CCM */ + +static int ccm_aesni_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, + aesni_set_encrypt_key, aesni_encrypt, aesni_ccm64_encrypt_blocks, + aesni_ccm64_decrypt_blocks); +} + +static const PROV_CCM_HW aesni_ccm = { + ccm_aesni_initkey, + ossl_ccm_generic_setiv, + ossl_ccm_generic_setaad, + ossl_ccm_generic_auth_encrypt, + ossl_ccm_generic_auth_decrypt, + ossl_ccm_generic_gettag +}; + +const PROV_CCM_HW *ossl_prov_aes_hw_ccm_aesni(void) +{ + if (AESNI_CAPABLE) + return &aesni_ccm; + return NULL; +} + +/* MODES: XTS */ + +static int cipher_hw_aesni_xts_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + void (*aesni_xts_enc)(const unsigned char *in, + unsigned char *out, + size_t length, + const AES_KEY *key1, const AES_KEY *key2, + const unsigned char iv[16]); + void (*aesni_xts_dec)(const unsigned char *in, + unsigned char *out, + size_t length, + const AES_KEY *key1, const AES_KEY *key2, + const unsigned char iv[16]); + + aesni_xts_enc = aesni_xts_encrypt; + aesni_xts_dec = aesni_xts_decrypt; + +#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_X64)) + if (aesni_xts_avx512_eligible()) { + if (keylen == 64) { + aesni_xts_enc = aesni_xts_256_encrypt_avx512; + aesni_xts_dec = aesni_xts_256_decrypt_avx512; + } else if (keylen == 32) { + aesni_xts_enc = aesni_xts_128_encrypt_avx512; + aesni_xts_dec = aesni_xts_128_decrypt_avx512; + } + } +#endif + + return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, + aesni_set_encrypt_key, aesni_set_decrypt_key, + aesni_encrypt, aesni_decrypt, aesni_xts_enc, aesni_xts_dec); +} + +static const PROV_CIPHER_HW aesni_xts = { + cipher_hw_aesni_xts_initkey, + NULL, + ossl_cipher_hw_aes_xts_copyctx +}; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_aesni(void) +{ + if (AESNI_CAPABLE) + return &aesni_xts; + return NULL; +} + #endif diff --git a/providers/implementations/ciphers/cipher_aes_xts.h b/providers/implementations/ciphers/cipher_aes_xts.h index a2421ae9ef..f103bad0a9 100644 --- a/providers/implementations/ciphers/cipher_aes_xts.h +++ b/providers/implementations/ciphers/cipher_aes_xts.h @@ -60,6 +60,20 @@ typedef struct prov_aes_xts_ctx_st { } plat; } PROV_AES_XTS_CTX; +int ossl_cipher_set_aes_xts_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen, + aes_set_encrypt_key_fn fn_set_enc_key, + aes_set_encrypt_key_fn fn_set_dec_key, + aes_block128_f fn_block_enc, aes_block128_f fn_block_dec, + OSSL_xts_stream_fn fn_stream_enc, OSSL_xts_stream_fn fn_stream_dec); + +void ossl_cipher_hw_aes_xts_copyctx(PROV_CIPHER_CTX *dst, + const PROV_CIPHER_CTX *src); + +#if defined(AESNI_CAPABLE) +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_aesni(void); +#endif + #ifdef AES_XTS_S390X int s390x_aes_xts_cipher_stream(PROV_AES_XTS_CTX *xctx, unsigned char *out, size_t *outl, diff --git a/providers/implementations/ciphers/cipher_aes_xts_hw.c b/providers/implementations/ciphers/cipher_aes_xts_hw.c index b9e21df4fd..215d151d69 100644 --- a/providers/implementations/ciphers/cipher_aes_xts_hw.c +++ b/providers/implementations/ciphers/cipher_aes_xts_hw.c @@ -15,7 +15,7 @@ #include "cipher_aes_xts.h" -static int cipher_set_aes_xts_initkey(PROV_CIPHER_CTX *ctx, +int ossl_cipher_set_aes_xts_initkey(PROV_CIPHER_CTX *ctx, const unsigned char *key, size_t keylen, aes_set_encrypt_key_fn fn_set_enc_key, aes_set_encrypt_key_fn fn_set_dec_key, @@ -62,7 +62,7 @@ static int cipher_hw_aes_xts_generic_initkey(PROV_CIPHER_CTX *ctx, #ifdef HWAES_xts_decrypt stream_dec = HWAES_xts_decrypt; #endif /* HWAES_xts_decrypt */ - return cipher_set_aes_xts_initkey(ctx, key, keylen, + return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, HWAES_set_encrypt_key, HWAES_set_decrypt_key, HWAES_encrypt, HWAES_decrypt, stream_enc, stream_dec); } @@ -72,7 +72,7 @@ static int cipher_hw_aes_xts_generic_initkey(PROV_CIPHER_CTX *ctx, if (BSAES_CAPABLE) { stream_enc = ossl_bsaes_xts_encrypt; stream_dec = ossl_bsaes_xts_decrypt; - return cipher_set_aes_xts_initkey(ctx, key, keylen, + return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, AES_set_encrypt_key, AES_set_decrypt_key, AES_encrypt, AES_decrypt, stream_enc, stream_dec); } @@ -80,18 +80,18 @@ static int cipher_hw_aes_xts_generic_initkey(PROV_CIPHER_CTX *ctx, #ifdef VPAES_CAPABLE if (VPAES_CAPABLE) { - return cipher_set_aes_xts_initkey(ctx, key, keylen, + return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, vpaes_set_encrypt_key, vpaes_set_decrypt_key, vpaes_encrypt, vpaes_decrypt, stream_enc, stream_dec); } #endif /* VPAES_CAPABLE */ - return cipher_set_aes_xts_initkey(ctx, key, keylen, + return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, AES_set_encrypt_key, AES_set_decrypt_key, AES_encrypt, AES_decrypt, stream_enc, stream_dec); } -static void cipher_hw_aes_xts_copyctx(PROV_CIPHER_CTX *dst, +void ossl_cipher_hw_aes_xts_copyctx(PROV_CIPHER_CTX *dst, const PROV_CIPHER_CTX *src) { PROV_AES_XTS_CTX *sctx = (PROV_AES_XTS_CTX *)src; @@ -102,56 +102,7 @@ static void cipher_hw_aes_xts_copyctx(PROV_CIPHER_CTX *dst, dctx->xts.key2 = &dctx->ks2.ks; } -#if defined(AESNI_CAPABLE) - -static int cipher_hw_aesni_xts_initkey(PROV_CIPHER_CTX *ctx, - const unsigned char *key, size_t keylen) -{ - void (*aesni_xts_enc)(const unsigned char *in, - unsigned char *out, - size_t length, - const AES_KEY *key1, const AES_KEY *key2, - const unsigned char iv[16]); - void (*aesni_xts_dec)(const unsigned char *in, - unsigned char *out, - size_t length, - const AES_KEY *key1, const AES_KEY *key2, - const unsigned char iv[16]); - - aesni_xts_enc = aesni_xts_encrypt; - aesni_xts_dec = aesni_xts_decrypt; - -#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_X64)) - if (aesni_xts_avx512_eligible()) { - if (keylen == 64) { - aesni_xts_enc = aesni_xts_256_encrypt_avx512; - aesni_xts_dec = aesni_xts_256_decrypt_avx512; - } else if (keylen == 32) { - aesni_xts_enc = aesni_xts_128_encrypt_avx512; - aesni_xts_dec = aesni_xts_128_decrypt_avx512; - } - } -#endif - - return cipher_set_aes_xts_initkey(ctx, key, keylen, - aesni_set_encrypt_key, aesni_set_decrypt_key, - aesni_encrypt, aesni_decrypt, aesni_xts_enc, aesni_xts_dec); -} - -static const PROV_CIPHER_HW aesni_xts = { - cipher_hw_aesni_xts_initkey, - NULL, - cipher_hw_aes_xts_copyctx -}; - -static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_aesni() -{ - if (AESNI_CAPABLE) - return &aesni_xts; - return NULL; -} - -#elif defined(SPARC_AES_CAPABLE) +#if defined(SPARC_AES_CAPABLE) static int cipher_hw_aes_xts_t4_initkey(PROV_CIPHER_CTX *ctx, const unsigned char *key, size_t keylen) @@ -173,7 +124,7 @@ static int cipher_hw_aes_xts_t4_initkey(PROV_CIPHER_CTX *ctx, return 0; } - return cipher_set_aes_xts_initkey(ctx, key, keylen, + return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, aes_t4_set_encrypt_key, aes_t4_set_decrypt_key, aes_t4_encrypt, aes_t4_decrypt, stream_enc, stream_dec); } @@ -181,7 +132,7 @@ static int cipher_hw_aes_xts_t4_initkey(PROV_CIPHER_CTX *ctx, static const PROV_CIPHER_HW aes_xts_t4 = { cipher_hw_aes_xts_t4_initkey, NULL, - cipher_hw_aes_xts_copyctx + ossl_cipher_hw_aes_xts_copyctx }; static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_t4() @@ -199,13 +150,13 @@ static int cipher_hw_aes_xts_rv64i_initkey(PROV_CIPHER_CTX *ctx, if (RISCV_HAS_ZVBB() && RISCV_HAS_ZVKG() && RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) { /* Zvkned only supports 128 and 256 bit keys. */ if (keylen * 8 == 128 * 2 || keylen * 8 == 256 * 2) - return cipher_set_aes_xts_initkey(ctx, key, keylen, + return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, rv64i_zvkned_set_encrypt_key, rv64i_zvkned_set_decrypt_key, rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, rv64i_zvbb_zvkg_zvkned_aes_xts_encrypt, rv64i_zvbb_zvkg_zvkned_aes_xts_decrypt); - return cipher_set_aes_xts_initkey(ctx, key, keylen, + return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, AES_set_encrypt_key, AES_set_encrypt_key, rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, NULL, NULL); } @@ -213,17 +164,17 @@ static int cipher_hw_aes_xts_rv64i_initkey(PROV_CIPHER_CTX *ctx, if (RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) { /* Zvkned only supports 128 and 256 bit keys. */ if (keylen * 8 == 128 * 2 || keylen * 8 == 256 * 2) - return cipher_set_aes_xts_initkey(ctx, key, keylen, + return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, rv64i_zvkned_set_encrypt_key, rv64i_zvkned_set_decrypt_key, rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, NULL, NULL); - return cipher_set_aes_xts_initkey(ctx, key, keylen, + return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, AES_set_encrypt_key, AES_set_encrypt_key, rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, NULL, NULL); } if (RISCV_HAS_ZKND_AND_ZKNE()) - return cipher_set_aes_xts_initkey(ctx, key, keylen, + return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, rv64i_zkne_set_encrypt_key, rv64i_zknd_set_decrypt_key, rv64i_zkne_encrypt, rv64i_zknd_decrypt, NULL, NULL); @@ -233,7 +184,7 @@ static int cipher_hw_aes_xts_rv64i_initkey(PROV_CIPHER_CTX *ctx, static const PROV_CIPHER_HW aes_xts_rv64i = { cipher_hw_aes_xts_rv64i_initkey, NULL, - cipher_hw_aes_xts_copyctx + ossl_cipher_hw_aes_xts_copyctx }; static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_rv64i() @@ -250,13 +201,13 @@ static int cipher_hw_aes_xts_rv32i_initkey(PROV_CIPHER_CTX *ctx, const unsigned char *key, size_t keylen) { if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE()) - return cipher_set_aes_xts_initkey(ctx, key, keylen, + return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, rv32i_zbkb_zkne_set_encrypt_key, rv32i_zbkb_zknd_zkne_set_decrypt_key, rv32i_zkne_encrypt, rv32i_zknd_decrypt, NULL, NULL); if (RISCV_HAS_ZKND_AND_ZKNE()) - return cipher_set_aes_xts_initkey(ctx, key, keylen, + return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, rv32i_zkne_set_encrypt_key, rv32i_zknd_zkne_set_decrypt_key, rv32i_zkne_encrypt, rv32i_zknd_decrypt, NULL, NULL); @@ -266,7 +217,7 @@ static int cipher_hw_aes_xts_rv32i_initkey(PROV_CIPHER_CTX *ctx, static const PROV_CIPHER_HW aes_xts_rv32i = { cipher_hw_aes_xts_rv32i_initkey, NULL, - cipher_hw_aes_xts_copyctx + ossl_cipher_hw_aes_xts_copyctx }; static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_rv32i() @@ -419,7 +370,7 @@ static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_s390x(size_t keybits) static const PROV_CIPHER_HW aes_generic_xts = { cipher_hw_aes_xts_generic_initkey, NULL, - cipher_hw_aes_xts_copyctx + ossl_cipher_hw_aes_xts_copyctx }; const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts(size_t keybits) From d1004c62ace7bc550b755d5bea356473e6d2df32 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Thu, 11 Jun 2026 17:22:17 -0400 Subject: [PATCH 093/349] Consolidate ARMv8 AES hardware implementations Move the ARMv8-specific hardware implementations for AES GCM into a single consolidated file (`cipher_aes_hw_armv8.c`). This groups architecture-specific optimizations together to improve code organization and maintainability. Additionally, remove the unused `keybits` parameter from the ARMv8 provider functions to simplify the function signatures. Signed-off-by: Simo Sorce Reviewed-by: Dmitry Belyavskiy Reviewed-by: Norbert Pocs Reviewed-by: Shane Lontis MergeDate: Sat Jun 27 09:05:50 2026 (Merged from https://github.com/openssl/openssl/pull/31472) --- providers/implementations/ciphers/build.info | 2 +- .../implementations/ciphers/cipher_aes.h | 3 +- .../implementations/ciphers/cipher_aes_gcm.h | 2 +- .../ciphers/cipher_aes_gcm_hw.c | 2 +- .../ciphers/cipher_aes_gcm_hw_armv8.c | 111 ------------------ .../implementations/ciphers/cipher_aes_hw.c | 2 +- .../ciphers/cipher_aes_hw_aesni.c | 3 +- .../ciphers/cipher_aes_hw_armv8.c | 109 ++++++++++++++++- 8 files changed, 110 insertions(+), 124 deletions(-) delete mode 100644 providers/implementations/ciphers/cipher_aes_gcm_hw_armv8.c diff --git a/providers/implementations/ciphers/build.info b/providers/implementations/ciphers/build.info index 720874ba78..9dcf738946 100644 --- a/providers/implementations/ciphers/build.info +++ b/providers/implementations/ciphers/build.info @@ -108,7 +108,7 @@ SOURCE[$AES_GOAL]=\ cipher_aes_hw_s390x.c cipher_aes_hw_t4.c \ cipher_aes_xts.c cipher_aes_xts_hw.c \ cipher_aes_gcm.c cipher_aes_gcm_hw.c \ - cipher_aes_gcm_hw_armv8.c cipher_aes_gcm_hw_ppc.c \ + cipher_aes_gcm_hw_ppc.c \ cipher_aes_gcm_hw_rv32i.c cipher_aes_gcm_hw_rv64i.c \ cipher_aes_gcm_hw_s390x.c cipher_aes_gcm_hw_t4.c \ cipher_aes_ccm.c cipher_aes_ccm_hw.c \ diff --git a/providers/implementations/ciphers/cipher_aes.h b/providers/implementations/ciphers/cipher_aes.h index 313c91ec9c..0f0aff4891 100644 --- a/providers/implementations/ciphers/cipher_aes.h +++ b/providers/implementations/ciphers/cipher_aes.h @@ -88,8 +88,7 @@ void ossl_cipher_aes_copyctx(PROV_CIPHER_CTX *dst, const PROV_CIPHER_CTX *src); #if defined(AESNI_CAPABLE) const PROV_CIPHER_HW *ossl_prov_cipher_hw_aesni(enum aes_modes mode); #elif defined(ARMv8_HWAES_CAPABLE) -const PROV_CIPHER_HW *ossl_prov_cipher_hw_arm(enum aes_modes mode, - size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_arm(enum aes_modes mode); #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 const PROV_CIPHER_HW *ossl_prov_cipher_hw_rv32i(enum aes_modes mode, size_t keybits); diff --git a/providers/implementations/ciphers/cipher_aes_gcm.h b/providers/implementations/ciphers/cipher_aes_gcm.h index 71ee4c97eb..0b8d23fcb9 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm.h +++ b/providers/implementations/ciphers/cipher_aes_gcm.h @@ -57,7 +57,7 @@ const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits); const PROV_GCM_HW *ossl_prov_aes_hw_gcm_aesni(void); #endif #if defined(AES_PMULL_CAPABLE) && defined(AES_GCM_ASM) -const PROV_GCM_HW *ossl_prov_aes_hw_gcm_armv8(size_t keybits); +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_armv8(void); #endif #if defined(PPC_AES_GCM_CAPABLE) && defined(_ARCH_PPC64) const PROV_GCM_HW *ossl_prov_aes_hw_gcm_ppc(size_t keybits); diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw.c b/providers/implementations/ciphers/cipher_aes_gcm_hw.c index 41ff71cd21..0d166b545a 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw.c +++ b/providers/implementations/ciphers/cipher_aes_gcm_hw.c @@ -158,7 +158,7 @@ const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits) #if defined(AESNI_CAPABLE) aes_gcm_hw = ossl_prov_aes_hw_gcm_aesni(); #elif defined(AES_PMULL_CAPABLE) && defined(AES_GCM_ASM) - aes_gcm_hw = ossl_prov_aes_hw_gcm_armv8(keybits); + aes_gcm_hw = ossl_prov_aes_hw_gcm_armv8(); #elif defined(PPC_AES_GCM_CAPABLE) && defined(_ARCH_PPC64) aes_gcm_hw = ossl_prov_aes_hw_gcm_ppc(keybits); #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw_armv8.c b/providers/implementations/ciphers/cipher_aes_gcm_hw_armv8.c deleted file mode 100644 index 7d50de2a2f..0000000000 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw_armv8.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/* - * Crypto extension support for AES GCM. - * This file is used by cipher_aes_gcm_hw.c - */ -#include "internal/deprecated.h" -#include "cipher_aes_gcm.h" - -#if defined(AES_PMULL_CAPABLE) && defined(AES_GCM_ASM) - -size_t armv8_aes_gcm_encrypt(const unsigned char *in, unsigned char *out, size_t len, - const void *key, unsigned char ivec[16], uint64_t *Xi) -{ - AES_KEY *aes_key = (AES_KEY *)key; - size_t align_bytes = len - len % 16; - - switch (aes_key->rounds) { - case 10: - if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { - unroll8_eor3_aes_gcm_enc_128_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); - } else { - aes_gcm_enc_128_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); - } - break; - case 12: - if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { - unroll8_eor3_aes_gcm_enc_192_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); - } else { - aes_gcm_enc_192_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); - } - break; - case 14: - if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { - unroll8_eor3_aes_gcm_enc_256_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); - } else { - aes_gcm_enc_256_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); - } - break; - } - return align_bytes; -} - -size_t armv8_aes_gcm_decrypt(const unsigned char *in, unsigned char *out, size_t len, - const void *key, unsigned char ivec[16], uint64_t *Xi) -{ - AES_KEY *aes_key = (AES_KEY *)key; - size_t align_bytes = len - len % 16; - - switch (aes_key->rounds) { - case 10: - if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { - unroll8_eor3_aes_gcm_dec_128_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); - } else { - aes_gcm_dec_128_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); - } - break; - case 12: - if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { - unroll8_eor3_aes_gcm_dec_192_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); - } else { - aes_gcm_dec_192_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); - } - break; - case 14: - if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { - unroll8_eor3_aes_gcm_dec_256_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); - } else { - aes_gcm_dec_256_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); - } - break; - } - return align_bytes; -} - -static int armv8_aes_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, - size_t keylen) -{ - if (AES_UNROLL12_EOR3_CAPABLE) { - return aes_gcm_hw_initkey(ctx, key, keylen, - aes_v8_set_encrypt_key, aes_v8_encrypt, - aes_v8_ctr32_encrypt_blocks_unroll12_eor3); - } else { - return aes_gcm_hw_initkey(ctx, key, keylen, - aes_v8_set_encrypt_key, aes_v8_encrypt, - aes_v8_ctr32_encrypt_blocks); - } -} - -static const PROV_GCM_HW armv8_aes_gcm = { - armv8_aes_gcm_initkey, - ossl_gcm_setiv, - ossl_gcm_aad_update, - generic_aes_gcm_cipher_update, - ossl_gcm_cipher_final, - ossl_gcm_one_shot -}; - -const PROV_GCM_HW *ossl_prov_aes_hw_gcm_armv8(size_t keybits) -{ - return AES_PMULL_CAPABLE ? &armv8_aes_gcm : NULL; -} - -#endif diff --git a/providers/implementations/ciphers/cipher_aes_hw.c b/providers/implementations/ciphers/cipher_aes_hw.c index 60abfd1165..e71a15cc00 100644 --- a/providers/implementations/ciphers/cipher_aes_hw.c +++ b/providers/implementations/ciphers/cipher_aes_hw.c @@ -219,7 +219,7 @@ static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_mode(enum aes_modes mode, #if defined(AESNI_CAPABLE) aes_hw_mode = ossl_prov_cipher_hw_aesni(mode); #elif defined(ARMv8_HWAES_CAPABLE) - aes_hw_mode = ossl_prov_cipher_hw_arm(mode, keybits); + aes_hw_mode = ossl_prov_cipher_hw_arm(mode); #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 aes_hw_mode = ossl_prov_cipher_hw_rv32i(mode, keybits); #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 diff --git a/providers/implementations/ciphers/cipher_aes_hw_aesni.c b/providers/implementations/ciphers/cipher_aes_hw_aesni.c index 6264c88426..15e2d71b3e 100644 --- a/providers/implementations/ciphers/cipher_aes_hw_aesni.c +++ b/providers/implementations/ciphers/cipher_aes_hw_aesni.c @@ -8,8 +8,7 @@ */ /*- - * AES-NI support for AES modes ecb, cbc, ofb, cfb, ctr. - * This file is used by cipher_aes_hw.c + * AES-NI support for all hardware accelerated AES modes. */ #include "internal/deprecated.h" diff --git a/providers/implementations/ciphers/cipher_aes_hw_armv8.c b/providers/implementations/ciphers/cipher_aes_hw_armv8.c index 773e40a411..da4f9fd0e3 100644 --- a/providers/implementations/ciphers/cipher_aes_hw_armv8.c +++ b/providers/implementations/ciphers/cipher_aes_hw_armv8.c @@ -1,5 +1,5 @@ /* - * Copyright 2023 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2019-2026 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -8,15 +8,17 @@ */ /* - * Crypto extension support for AES modes ecb, cbc, ofb, cfb, ctr. - * This file is used by cipher_aes_hw.c + * ARMv8 support for all hardware accelerated AES modes. */ #include "internal/deprecated.h" #include "cipher_aes.h" +#include "cipher_aes_gcm.h" #if defined(ARMv8_HWAES_CAPABLE) +/* MODES: ctr */ + static int cipher_hw_aes_arm_initkey(PROV_CIPHER_CTX *ctx, const unsigned char *key, size_t keylen) { @@ -35,8 +37,7 @@ static const PROV_CIPHER_HW arm_ctr = { ossl_cipher_aes_copyctx }; -const PROV_CIPHER_HW *ossl_prov_cipher_hw_arm(enum aes_modes mode, - size_t keybits) +const PROV_CIPHER_HW *ossl_prov_cipher_hw_arm(enum aes_modes mode) { if (ARMv8_HWAES_CAPABLE && mode == AES_MODE_CTR) return &arm_ctr; @@ -44,3 +45,101 @@ const PROV_CIPHER_HW *ossl_prov_cipher_hw_arm(enum aes_modes mode, } #endif + +/* MODES: GCM */ + +#if defined(AES_PMULL_CAPABLE) && defined(AES_GCM_ASM) + +size_t armv8_aes_gcm_encrypt(const unsigned char *in, unsigned char *out, size_t len, + const void *key, unsigned char ivec[16], uint64_t *Xi) +{ + AES_KEY *aes_key = (AES_KEY *)key; + size_t align_bytes = len - len % 16; + + switch (aes_key->rounds) { + case 10: + if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { + unroll8_eor3_aes_gcm_enc_128_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } else { + aes_gcm_enc_128_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } + break; + case 12: + if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { + unroll8_eor3_aes_gcm_enc_192_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } else { + aes_gcm_enc_192_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } + break; + case 14: + if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { + unroll8_eor3_aes_gcm_enc_256_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } else { + aes_gcm_enc_256_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } + break; + } + return align_bytes; +} + +size_t armv8_aes_gcm_decrypt(const unsigned char *in, unsigned char *out, size_t len, + const void *key, unsigned char ivec[16], uint64_t *Xi) +{ + AES_KEY *aes_key = (AES_KEY *)key; + size_t align_bytes = len - len % 16; + + switch (aes_key->rounds) { + case 10: + if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { + unroll8_eor3_aes_gcm_dec_128_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } else { + aes_gcm_dec_128_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } + break; + case 12: + if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { + unroll8_eor3_aes_gcm_dec_192_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } else { + aes_gcm_dec_192_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } + break; + case 14: + if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { + unroll8_eor3_aes_gcm_dec_256_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } else { + aes_gcm_dec_256_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } + break; + } + return align_bytes; +} + +static int armv8_aes_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + if (AES_UNROLL12_EOR3_CAPABLE) { + return aes_gcm_hw_initkey(ctx, key, keylen, + aes_v8_set_encrypt_key, aes_v8_encrypt, + aes_v8_ctr32_encrypt_blocks_unroll12_eor3); + } else { + return aes_gcm_hw_initkey(ctx, key, keylen, + aes_v8_set_encrypt_key, aes_v8_encrypt, + aes_v8_ctr32_encrypt_blocks); + } +} + +static const PROV_GCM_HW armv8_aes_gcm = { + armv8_aes_gcm_initkey, + ossl_gcm_setiv, + ossl_gcm_aad_update, + generic_aes_gcm_cipher_update, + ossl_gcm_cipher_final, + ossl_gcm_one_shot +}; + +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_armv8(void) +{ + return AES_PMULL_CAPABLE ? &armv8_aes_gcm : NULL; +} + +#endif From f537454de00015b4a81cbd62ebb221ebfc7bf393 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Thu, 11 Jun 2026 18:03:16 -0400 Subject: [PATCH 094/349] Consolidate RV32I AES hardware implementations Move the RISC-V 32-bit hardware-accelerated AES implementations for GCM, CCM, and XTS modes into a single file (`cipher_aes_hw_rv32i.c`). This removes the need for separate files (`cipher_aes_gcm_hw_rv32i.c` and `cipher_aes_ccm_hw_rv32i.c`) and simplifies the build process. Additionally, unused `keybits` parameters are removed from the provider hardware initialization functions to clean up the code. Signed-off-by: Simo Sorce Reviewed-by: Dmitry Belyavskiy Reviewed-by: Norbert Pocs Reviewed-by: Shane Lontis MergeDate: Sat Jun 27 09:05:52 2026 (Merged from https://github.com/openssl/openssl/pull/31472) --- providers/implementations/ciphers/build.info | 4 +- .../implementations/ciphers/cipher_aes.h | 3 +- .../implementations/ciphers/cipher_aes_ccm.h | 2 +- .../ciphers/cipher_aes_ccm_hw.c | 2 +- .../ciphers/cipher_aes_ccm_hw_rv32i.c | 61 ---------- .../implementations/ciphers/cipher_aes_gcm.h | 6 +- .../ciphers/cipher_aes_gcm_hw.c | 4 +- .../ciphers/cipher_aes_gcm_hw_rv32i.c | 61 ---------- .../implementations/ciphers/cipher_aes_hw.c | 2 +- .../ciphers/cipher_aes_hw_rv32i.c | 108 +++++++++++++++++- .../implementations/ciphers/cipher_aes_xts.h | 4 + .../ciphers/cipher_aes_xts_hw.c | 32 ------ 12 files changed, 118 insertions(+), 171 deletions(-) delete mode 100644 providers/implementations/ciphers/cipher_aes_ccm_hw_rv32i.c delete mode 100644 providers/implementations/ciphers/cipher_aes_gcm_hw_rv32i.c diff --git a/providers/implementations/ciphers/build.info b/providers/implementations/ciphers/build.info index 9dcf738946..671351743b 100644 --- a/providers/implementations/ciphers/build.info +++ b/providers/implementations/ciphers/build.info @@ -109,10 +109,10 @@ SOURCE[$AES_GOAL]=\ cipher_aes_xts.c cipher_aes_xts_hw.c \ cipher_aes_gcm.c cipher_aes_gcm_hw.c \ cipher_aes_gcm_hw_ppc.c \ - cipher_aes_gcm_hw_rv32i.c cipher_aes_gcm_hw_rv64i.c \ + cipher_aes_gcm_hw_rv64i.c \ cipher_aes_gcm_hw_s390x.c cipher_aes_gcm_hw_t4.c \ cipher_aes_ccm.c cipher_aes_ccm_hw.c \ - cipher_aes_ccm_hw_rv32i.c cipher_aes_ccm_hw_rv64i.c \ + cipher_aes_ccm_hw_rv64i.c \ cipher_aes_ccm_hw_s390x.c cipher_aes_ccm_hw_t4.c \ cipher_aes_wrp.c \ cipher_aes_cbc_hmac_sha.c \ diff --git a/providers/implementations/ciphers/cipher_aes.h b/providers/implementations/ciphers/cipher_aes.h index 0f0aff4891..05028053f2 100644 --- a/providers/implementations/ciphers/cipher_aes.h +++ b/providers/implementations/ciphers/cipher_aes.h @@ -90,8 +90,7 @@ const PROV_CIPHER_HW *ossl_prov_cipher_hw_aesni(enum aes_modes mode); #elif defined(ARMv8_HWAES_CAPABLE) const PROV_CIPHER_HW *ossl_prov_cipher_hw_arm(enum aes_modes mode); #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 -const PROV_CIPHER_HW *ossl_prov_cipher_hw_rv32i(enum aes_modes mode, - size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_rv32i(enum aes_modes mode); #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 const PROV_CIPHER_HW *ossl_prov_cipher_hw_rv64i(enum aes_modes mode, size_t keybits); diff --git a/providers/implementations/ciphers/cipher_aes_ccm.h b/providers/implementations/ciphers/cipher_aes_ccm.h index be690dadb9..2fa59dca75 100644 --- a/providers/implementations/ciphers/cipher_aes_ccm.h +++ b/providers/implementations/ciphers/cipher_aes_ccm.h @@ -59,7 +59,7 @@ const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keylen); const PROV_CCM_HW *ossl_prov_aes_hw_ccm_aesni(void); #endif #if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 -const PROV_CCM_HW *ossl_prov_aes_hw_ccm_rv32i(size_t keybits); +const PROV_CCM_HW *ossl_prov_aes_hw_ccm_rv32i(void); #endif #if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 const PROV_CCM_HW *ossl_prov_aes_hw_ccm_rv64i(size_t keybits); diff --git a/providers/implementations/ciphers/cipher_aes_ccm_hw.c b/providers/implementations/ciphers/cipher_aes_ccm_hw.c index fa7318d460..2a69a1427d 100644 --- a/providers/implementations/ciphers/cipher_aes_ccm_hw.c +++ b/providers/implementations/ciphers/cipher_aes_ccm_hw.c @@ -75,7 +75,7 @@ const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keybits) #if defined(AESNI_CAPABLE) aes_ccm_hw = ossl_prov_aes_hw_ccm_aesni(); #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 - aes_ccm_hw = ossl_prov_aes_hw_ccm_rv32i(keybits); + aes_ccm_hw = ossl_prov_aes_hw_ccm_rv32i(); #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 aes_ccm_hw = ossl_prov_aes_hw_ccm_rv64i(keybits); #elif defined(S390X_aes_128_CAPABLE) diff --git a/providers/implementations/ciphers/cipher_aes_ccm_hw_rv32i.c b/providers/implementations/ciphers/cipher_aes_ccm_hw_rv32i.c deleted file mode 100644 index b2dfee5650..0000000000 --- a/providers/implementations/ciphers/cipher_aes_ccm_hw_rv32i.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/*- - * RISC-V 32 ZKND ZKNE support for AES CCM. - * This file is used by cipher_aes_ccm_hw.c - */ - -#include "internal/deprecated.h" -#include "cipher_aes_ccm.h" - -#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 - -static int ccm_rv32i_zknd_zkne_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, - size_t keylen) -{ - return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, - rv32i_zkne_set_encrypt_key, rv32i_zkne_encrypt, NULL, NULL); -} - -static int ccm_rv32i_zbkb_zknd_zkne_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, - size_t keylen) -{ - return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, - rv32i_zbkb_zkne_set_encrypt_key, rv32i_zkne_encrypt, NULL, NULL); -} - -static const PROV_CCM_HW rv32i_zknd_zkne_ccm = { - ccm_rv32i_zknd_zkne_initkey, - ossl_ccm_generic_setiv, - ossl_ccm_generic_setaad, - ossl_ccm_generic_auth_encrypt, - ossl_ccm_generic_auth_decrypt, - ossl_ccm_generic_gettag -}; - -static const PROV_CCM_HW rv32i_zbkb_zknd_zkne_ccm = { - ccm_rv32i_zbkb_zknd_zkne_initkey, - ossl_ccm_generic_setiv, - ossl_ccm_generic_setaad, - ossl_ccm_generic_auth_encrypt, - ossl_ccm_generic_auth_decrypt, - ossl_ccm_generic_gettag -}; - -const PROV_CCM_HW *ossl_prov_aes_hw_ccm_rv32i(size_t keybits) -{ - if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE()) - return &rv32i_zbkb_zknd_zkne_ccm; - if (RISCV_HAS_ZKND_AND_ZKNE()) - return &rv32i_zknd_zkne_ccm; - return NULL; -} - -#endif diff --git a/providers/implementations/ciphers/cipher_aes_gcm.h b/providers/implementations/ciphers/cipher_aes_gcm.h index 0b8d23fcb9..88ad752e92 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm.h +++ b/providers/implementations/ciphers/cipher_aes_gcm.h @@ -62,12 +62,12 @@ const PROV_GCM_HW *ossl_prov_aes_hw_gcm_armv8(void); #if defined(PPC_AES_GCM_CAPABLE) && defined(_ARCH_PPC64) const PROV_GCM_HW *ossl_prov_aes_hw_gcm_ppc(size_t keybits); #endif +#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_rv32i(void); +#endif #if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 const PROV_GCM_HW *ossl_prov_aes_hw_gcm_rv64i(size_t keybits); #endif -#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 -const PROV_GCM_HW *ossl_prov_aes_hw_gcm_rv32i(size_t keybits); -#endif #if defined(S390X_aes_128_CAPABLE) const PROV_GCM_HW *ossl_prov_aes_hw_gcm_s390x(size_t keybits); #endif diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw.c b/providers/implementations/ciphers/cipher_aes_gcm_hw.c index 0d166b545a..87e46e71b3 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw.c +++ b/providers/implementations/ciphers/cipher_aes_gcm_hw.c @@ -161,10 +161,10 @@ const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits) aes_gcm_hw = ossl_prov_aes_hw_gcm_armv8(); #elif defined(PPC_AES_GCM_CAPABLE) && defined(_ARCH_PPC64) aes_gcm_hw = ossl_prov_aes_hw_gcm_ppc(keybits); +#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 + aes_gcm_hw = ossl_prov_aes_hw_gcm_rv32i(); #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 aes_gcm_hw = ossl_prov_aes_hw_gcm_rv64i(keybits); -#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 - aes_gcm_hw = ossl_prov_aes_hw_gcm_rv32i(keybits); #elif defined(S390X_aes_128_CAPABLE) aes_gcm_hw = ossl_prov_aes_hw_gcm_s390x(keybits); #elif defined(SPARC_AES_CAPABLE) diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw_rv32i.c b/providers/implementations/ciphers/cipher_aes_gcm_hw_rv32i.c deleted file mode 100644 index 708be6aef5..0000000000 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw_rv32i.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/*- - * RISC-V 32 ZKND ZKNE support for AES GCM. - * This file is used by cipher_aes_gcm_hw.c - */ -#include "internal/deprecated.h" -#include "cipher_aes_gcm.h" - -#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 - -static int rv32i_zknd_zkne_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, - size_t keylen) -{ - return aes_gcm_hw_initkey(ctx, key, keylen, - rv32i_zkne_set_encrypt_key, rv32i_zkne_encrypt, NULL); -} - -static int rv32i_zbkb_zknd_zkne_gcm_initkey(PROV_GCM_CTX *ctx, - const unsigned char *key, - size_t keylen) -{ - return aes_gcm_hw_initkey(ctx, key, keylen, - rv32i_zbkb_zkne_set_encrypt_key, rv32i_zkne_encrypt, NULL); -} - -static const PROV_GCM_HW rv32i_zknd_zkne_gcm = { - rv32i_zknd_zkne_gcm_initkey, - ossl_gcm_setiv, - ossl_gcm_aad_update, - generic_aes_gcm_cipher_update, - ossl_gcm_cipher_final, - ossl_gcm_one_shot -}; - -static const PROV_GCM_HW rv32i_zbkb_zknd_zkne_gcm = { - rv32i_zbkb_zknd_zkne_gcm_initkey, - ossl_gcm_setiv, - ossl_gcm_aad_update, - generic_aes_gcm_cipher_update, - ossl_gcm_cipher_final, - ossl_gcm_one_shot -}; - -const PROV_GCM_HW *ossl_prov_aes_hw_gcm_rv32i(size_t keybits) -{ - if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE()) - return &rv32i_zbkb_zknd_zkne_gcm; - if (RISCV_HAS_ZKND_AND_ZKNE()) - return &rv32i_zknd_zkne_gcm; - return NULL; -} - -#endif diff --git a/providers/implementations/ciphers/cipher_aes_hw.c b/providers/implementations/ciphers/cipher_aes_hw.c index e71a15cc00..34b28bb196 100644 --- a/providers/implementations/ciphers/cipher_aes_hw.c +++ b/providers/implementations/ciphers/cipher_aes_hw.c @@ -221,7 +221,7 @@ static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_mode(enum aes_modes mode, #elif defined(ARMv8_HWAES_CAPABLE) aes_hw_mode = ossl_prov_cipher_hw_arm(mode); #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 - aes_hw_mode = ossl_prov_cipher_hw_rv32i(mode, keybits); + aes_hw_mode = ossl_prov_cipher_hw_rv32i(mode); #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 aes_hw_mode = ossl_prov_cipher_hw_rv64i(mode, keybits); #elif defined(S390X_aes_128_CAPABLE) diff --git a/providers/implementations/ciphers/cipher_aes_hw_rv32i.c b/providers/implementations/ciphers/cipher_aes_hw_rv32i.c index 375f9dc946..237f6fc20e 100644 --- a/providers/implementations/ciphers/cipher_aes_hw_rv32i.c +++ b/providers/implementations/ciphers/cipher_aes_hw_rv32i.c @@ -1,5 +1,5 @@ /* - * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2022-2026 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -8,15 +8,19 @@ */ /*- - * RISC-V 32 ZKND ZKNE support for AES modes ecb, cbc, ofb, cfb, ctr. - * This file is used by cipher_aes_hw.c + * RISC-V 32 ZKND ZKNE support for all hardware accelerated AES modes. */ #include "internal/deprecated.h" #include "cipher_aes.h" +#include "cipher_aes_gcm.h" +#include "cipher_aes_ccm.h" +#include "cipher_aes_xts.h" #if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 +/* MODES: ecb, cbc, cfb, ofb, ctr */ + static int cipher_hw_rv32i_initkey(PROV_CIPHER_CTX *ctx, const unsigned char *key, size_t keylen) { @@ -84,8 +88,7 @@ static const PROV_CIPHER_HW rv32i_ctr = { ossl_cipher_aes_copyctx }; -const PROV_CIPHER_HW *ossl_prov_cipher_hw_rv32i(enum aes_modes mode, - size_t keybits) +const PROV_CIPHER_HW *ossl_prov_cipher_hw_rv32i(enum aes_modes mode) { if (RISCV_HAS_ZKND_AND_ZKNE()) { switch (mode) { @@ -110,4 +113,99 @@ const PROV_CIPHER_HW *ossl_prov_cipher_hw_rv32i(enum aes_modes mode, return NULL; } +/* MODES: GCM */ + +static int aes_gcm_rv32i_initkey(PROV_GCM_CTX *ctx, + const unsigned char *key, + size_t keylen) +{ + if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE()) { + return aes_gcm_hw_initkey(ctx, key, keylen, + rv32i_zbkb_zkne_set_encrypt_key, rv32i_zkne_encrypt, NULL); + } else if (RISCV_HAS_ZKND_AND_ZKNE()) { + return aes_gcm_hw_initkey(ctx, key, keylen, + rv32i_zkne_set_encrypt_key, rv32i_zkne_encrypt, NULL); + } + return 0; +} + +static const PROV_GCM_HW aes_gcm_rv32i = { + aes_gcm_rv32i_initkey, + ossl_gcm_setiv, + ossl_gcm_aad_update, + generic_aes_gcm_cipher_update, + ossl_gcm_cipher_final, + ossl_gcm_one_shot +}; + +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_rv32i(void) +{ + if (RISCV_HAS_ZKND_AND_ZKNE()) + return &aes_gcm_rv32i; + return NULL; +} + +/* MODES: CCM */ + +static int aes_ccm_rv32i_initkey(PROV_CCM_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE()) { + return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, + rv32i_zbkb_zkne_set_encrypt_key, rv32i_zkne_encrypt, NULL, NULL); + } else if (RISCV_HAS_ZKND_AND_ZKNE()) { + return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, + rv32i_zkne_set_encrypt_key, rv32i_zkne_encrypt, NULL, NULL); + } + return 0; +} + +static const PROV_CCM_HW aes_ccm_rv32i = { + aes_ccm_rv32i_initkey, + ossl_ccm_generic_setiv, + ossl_ccm_generic_setaad, + ossl_ccm_generic_auth_encrypt, + ossl_ccm_generic_auth_decrypt, + ossl_ccm_generic_gettag +}; + +const PROV_CCM_HW *ossl_prov_aes_hw_ccm_rv32i(void) +{ + if (RISCV_HAS_ZKND_AND_ZKNE()) + return &aes_ccm_rv32i; + return NULL; +} + +/* MODES: XTS */ + +static int cipher_hw_aes_xts_rv32i_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE()) + return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, + rv32i_zbkb_zkne_set_encrypt_key, + rv32i_zbkb_zknd_zkne_set_decrypt_key, + rv32i_zkne_encrypt, rv32i_zknd_decrypt, NULL, NULL); + + if (RISCV_HAS_ZKND_AND_ZKNE()) + return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, + rv32i_zkne_set_encrypt_key, rv32i_zknd_zkne_set_decrypt_key, + rv32i_zkne_encrypt, rv32i_zknd_decrypt, NULL, NULL); + + return 0; +} + +static const PROV_CIPHER_HW aes_xts_rv32i = { + cipher_hw_aes_xts_rv32i_initkey, + NULL, + ossl_cipher_hw_aes_xts_copyctx +}; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_rv32i(void) +{ + if (RISCV_HAS_ZKND_AND_ZKNE()) + return &aes_xts_rv32i; + return NULL; +} + #endif diff --git a/providers/implementations/ciphers/cipher_aes_xts.h b/providers/implementations/ciphers/cipher_aes_xts.h index f103bad0a9..908e6ab937 100644 --- a/providers/implementations/ciphers/cipher_aes_xts.h +++ b/providers/implementations/ciphers/cipher_aes_xts.h @@ -74,6 +74,10 @@ void ossl_cipher_hw_aes_xts_copyctx(PROV_CIPHER_CTX *dst, const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_aesni(void); #endif +#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_rv32i(void); +#endif + #ifdef AES_XTS_S390X int s390x_aes_xts_cipher_stream(PROV_AES_XTS_CTX *xctx, unsigned char *out, size_t *outl, diff --git a/providers/implementations/ciphers/cipher_aes_xts_hw.c b/providers/implementations/ciphers/cipher_aes_xts_hw.c index 215d151d69..4bf4462442 100644 --- a/providers/implementations/ciphers/cipher_aes_xts_hw.c +++ b/providers/implementations/ciphers/cipher_aes_xts_hw.c @@ -195,38 +195,6 @@ static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_rv64i() return NULL; } -#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 - -static int cipher_hw_aes_xts_rv32i_initkey(PROV_CIPHER_CTX *ctx, - const unsigned char *key, size_t keylen) -{ - if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE()) - return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, - rv32i_zbkb_zkne_set_encrypt_key, - rv32i_zbkb_zknd_zkne_set_decrypt_key, - rv32i_zkne_encrypt, rv32i_zknd_decrypt, NULL, NULL); - - if (RISCV_HAS_ZKND_AND_ZKNE()) - return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, - rv32i_zkne_set_encrypt_key, rv32i_zknd_zkne_set_decrypt_key, - rv32i_zkne_encrypt, rv32i_zknd_decrypt, NULL, NULL); - - return 0; -} - -static const PROV_CIPHER_HW aes_xts_rv32i = { - cipher_hw_aes_xts_rv32i_initkey, - NULL, - ossl_cipher_hw_aes_xts_copyctx -}; - -static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_rv32i() -{ - if (RISCV_HAS_ZKND_AND_ZKNE()) - return &aes_xts_rv32i; - return NULL; -} - #elif defined(AES_XTS_S390X) int s390x_aes_xts_cipher_stream(PROV_AES_XTS_CTX *xctx, From aa23e0cd7a5d9de2654ffe1eab943213c2bb109e Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Thu, 11 Jun 2026 18:32:37 -0400 Subject: [PATCH 095/349] Consolidate RISC-V 64 AES hardware code Merge the RISC-V 64 hardware-accelerated AES mode implementations (GCM, CCM, and XTS) into a single file (`cipher_aes_hw_rv64i.c`). This removes the need for separate files for GCM and CCM, and extracts RV64-specific XTS code from the generic XTS hardware file, improving code organization. Additionally, remove the unused `keybits` parameter from the RV64 AES hardware retrieval functions. Signed-off-by: Simo Sorce Reviewed-by: Dmitry Belyavskiy Reviewed-by: Norbert Pocs Reviewed-by: Shane Lontis MergeDate: Sat Jun 27 09:05:54 2026 (Merged from https://github.com/openssl/openssl/pull/31472) --- providers/implementations/ciphers/build.info | 2 - .../implementations/ciphers/cipher_aes.h | 3 +- .../implementations/ciphers/cipher_aes_ccm.h | 2 +- .../ciphers/cipher_aes_ccm_hw.c | 2 +- .../ciphers/cipher_aes_ccm_hw_rv64i.c | 72 ------ .../implementations/ciphers/cipher_aes_gcm.h | 2 +- .../ciphers/cipher_aes_gcm_hw.c | 2 +- .../ciphers/cipher_aes_gcm_hw_rv64i.c | 112 --------- .../implementations/ciphers/cipher_aes_hw.c | 2 +- .../ciphers/cipher_aes_hw_rv64i.c | 222 +++++++++++++++++- .../implementations/ciphers/cipher_aes_xts.h | 4 + .../ciphers/cipher_aes_xts_hw.c | 57 +---- 12 files changed, 226 insertions(+), 256 deletions(-) delete mode 100644 providers/implementations/ciphers/cipher_aes_ccm_hw_rv64i.c delete mode 100644 providers/implementations/ciphers/cipher_aes_gcm_hw_rv64i.c diff --git a/providers/implementations/ciphers/build.info b/providers/implementations/ciphers/build.info index 671351743b..70e41b3e34 100644 --- a/providers/implementations/ciphers/build.info +++ b/providers/implementations/ciphers/build.info @@ -109,10 +109,8 @@ SOURCE[$AES_GOAL]=\ cipher_aes_xts.c cipher_aes_xts_hw.c \ cipher_aes_gcm.c cipher_aes_gcm_hw.c \ cipher_aes_gcm_hw_ppc.c \ - cipher_aes_gcm_hw_rv64i.c \ cipher_aes_gcm_hw_s390x.c cipher_aes_gcm_hw_t4.c \ cipher_aes_ccm.c cipher_aes_ccm_hw.c \ - cipher_aes_ccm_hw_rv64i.c \ cipher_aes_ccm_hw_s390x.c cipher_aes_ccm_hw_t4.c \ cipher_aes_wrp.c \ cipher_aes_cbc_hmac_sha.c \ diff --git a/providers/implementations/ciphers/cipher_aes.h b/providers/implementations/ciphers/cipher_aes.h index 05028053f2..05e46d204a 100644 --- a/providers/implementations/ciphers/cipher_aes.h +++ b/providers/implementations/ciphers/cipher_aes.h @@ -92,8 +92,7 @@ const PROV_CIPHER_HW *ossl_prov_cipher_hw_arm(enum aes_modes mode); #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 const PROV_CIPHER_HW *ossl_prov_cipher_hw_rv32i(enum aes_modes mode); #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 -const PROV_CIPHER_HW *ossl_prov_cipher_hw_rv64i(enum aes_modes mode, - size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_rv64i(enum aes_modes mode); #elif defined(S390X_aes_128_CAPABLE) const PROV_CIPHER_HW *ossl_prov_cipher_hw_s390x(enum aes_modes mode, size_t keybits); diff --git a/providers/implementations/ciphers/cipher_aes_ccm.h b/providers/implementations/ciphers/cipher_aes_ccm.h index 2fa59dca75..636f5e687b 100644 --- a/providers/implementations/ciphers/cipher_aes_ccm.h +++ b/providers/implementations/ciphers/cipher_aes_ccm.h @@ -62,7 +62,7 @@ const PROV_CCM_HW *ossl_prov_aes_hw_ccm_aesni(void); const PROV_CCM_HW *ossl_prov_aes_hw_ccm_rv32i(void); #endif #if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 -const PROV_CCM_HW *ossl_prov_aes_hw_ccm_rv64i(size_t keybits); +const PROV_CCM_HW *ossl_prov_aes_hw_ccm_rv64i(void); #endif #if defined(S390X_aes_128_CAPABLE) const PROV_CCM_HW *ossl_prov_aes_hw_ccm_s390x(size_t keybits); diff --git a/providers/implementations/ciphers/cipher_aes_ccm_hw.c b/providers/implementations/ciphers/cipher_aes_ccm_hw.c index 2a69a1427d..a3ee92c542 100644 --- a/providers/implementations/ciphers/cipher_aes_ccm_hw.c +++ b/providers/implementations/ciphers/cipher_aes_ccm_hw.c @@ -77,7 +77,7 @@ const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keybits) #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 aes_ccm_hw = ossl_prov_aes_hw_ccm_rv32i(); #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 - aes_ccm_hw = ossl_prov_aes_hw_ccm_rv64i(keybits); + aes_ccm_hw = ossl_prov_aes_hw_ccm_rv64i(); #elif defined(S390X_aes_128_CAPABLE) aes_ccm_hw = ossl_prov_aes_hw_ccm_s390x(keybits); #elif defined(SPARC_AES_CAPABLE) diff --git a/providers/implementations/ciphers/cipher_aes_ccm_hw_rv64i.c b/providers/implementations/ciphers/cipher_aes_ccm_hw_rv64i.c deleted file mode 100644 index 506e286e77..0000000000 --- a/providers/implementations/ciphers/cipher_aes_ccm_hw_rv64i.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/*- - * RISC-V 64 ZKND ZKNE support for AES CCM. - * This file is used by cipher_aes_ccm_hw.c - */ - -#include "internal/deprecated.h" -#include "cipher_aes_ccm.h" - -#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 - -static int ccm_rv64i_zknd_zkne_initkey(PROV_CCM_CTX *ctx, - const unsigned char *key, size_t keylen) -{ - return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, - rv64i_zkne_set_encrypt_key, rv64i_zkne_encrypt, NULL, NULL); -} - -static const PROV_CCM_HW rv64i_zknd_zkne_ccm = { - ccm_rv64i_zknd_zkne_initkey, - ossl_ccm_generic_setiv, - ossl_ccm_generic_setaad, - ossl_ccm_generic_auth_encrypt, - ossl_ccm_generic_auth_decrypt, - ossl_ccm_generic_gettag -}; - -/*- - * RISC-V RV64 ZVKNED support for AES CCM. - * This file is included by cipher_aes_ccm_hw.c - */ - -static int ccm_rv64i_zvkned_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, - size_t keylen) -{ - /* Zvkned only supports 128 and 256 bit keys for key schedule generation. */ - if (keylen * 8 == 128 || keylen * 8 == 256) { - return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, - rv64i_zvkned_set_encrypt_key, rv64i_zvkned_encrypt, NULL, NULL); - } else { - return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, - AES_set_encrypt_key, rv64i_zvkned_encrypt, NULL, NULL); - } -} - -static const PROV_CCM_HW rv64i_zvkned_ccm = { - ccm_rv64i_zvkned_initkey, - ossl_ccm_generic_setiv, - ossl_ccm_generic_setaad, - ossl_ccm_generic_auth_encrypt, - ossl_ccm_generic_auth_decrypt, - ossl_ccm_generic_gettag -}; - -const PROV_CCM_HW *ossl_prov_aes_hw_ccm_rv64i(size_t keybits) -{ - if (RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) - return &rv64i_zvkned_ccm; - else if (RISCV_HAS_ZKND_AND_ZKNE()) - return &rv64i_zknd_zkne_ccm; - else - return NULL; -} -#endif diff --git a/providers/implementations/ciphers/cipher_aes_gcm.h b/providers/implementations/ciphers/cipher_aes_gcm.h index 88ad752e92..43cba79541 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm.h +++ b/providers/implementations/ciphers/cipher_aes_gcm.h @@ -66,7 +66,7 @@ const PROV_GCM_HW *ossl_prov_aes_hw_gcm_ppc(size_t keybits); const PROV_GCM_HW *ossl_prov_aes_hw_gcm_rv32i(void); #endif #if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 -const PROV_GCM_HW *ossl_prov_aes_hw_gcm_rv64i(size_t keybits); +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_rv64i(void); #endif #if defined(S390X_aes_128_CAPABLE) const PROV_GCM_HW *ossl_prov_aes_hw_gcm_s390x(size_t keybits); diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw.c b/providers/implementations/ciphers/cipher_aes_gcm_hw.c index 87e46e71b3..8493451688 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw.c +++ b/providers/implementations/ciphers/cipher_aes_gcm_hw.c @@ -164,7 +164,7 @@ const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits) #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 aes_gcm_hw = ossl_prov_aes_hw_gcm_rv32i(); #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 - aes_gcm_hw = ossl_prov_aes_hw_gcm_rv64i(keybits); + aes_gcm_hw = ossl_prov_aes_hw_gcm_rv64i(); #elif defined(S390X_aes_128_CAPABLE) aes_gcm_hw = ossl_prov_aes_hw_gcm_s390x(keybits); #elif defined(SPARC_AES_CAPABLE) diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw_rv64i.c b/providers/implementations/ciphers/cipher_aes_gcm_hw_rv64i.c deleted file mode 100644 index 7d88ff7e43..0000000000 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw_rv64i.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/*- - * RISC-V 64 support for AES GCM. - * This file is used by cipher_aes_gcm_hw.c - */ -#include "internal/deprecated.h" -#include "cipher_aes_gcm.h" - -#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 - -/*- - * RISC-V 64 ZKND and ZKNE support for AES GCM. - */ -static int rv64i_zknd_zkne_gcm_initkey(PROV_GCM_CTX *ctx, - const unsigned char *key, size_t keylen) -{ - return aes_gcm_hw_initkey(ctx, key, keylen, - rv64i_zkne_set_encrypt_key, rv64i_zkne_encrypt, NULL); -} - -static const PROV_GCM_HW rv64i_zknd_zkne_gcm = { - rv64i_zknd_zkne_gcm_initkey, - ossl_gcm_setiv, - ossl_gcm_aad_update, - generic_aes_gcm_cipher_update, - ossl_gcm_cipher_final, - ossl_gcm_one_shot -}; - -/*- - * RISC-V RV64 ZVKNED support for AES GCM. - */ -static int rv64i_zvkned_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, - size_t keylen) -{ - /* - * Zvkned only supports 128 and 256 bit keys for key schedule generation. - * For AES-192 case, we could fallback to `AES_set_encrypt_key`. - */ - if (keylen * 8 == 128 || keylen * 8 == 256) { - return aes_gcm_hw_initkey(ctx, key, keylen, - rv64i_zvkned_set_encrypt_key, rv64i_zvkned_encrypt, NULL); - } else { - return aes_gcm_hw_initkey(ctx, key, keylen, - AES_set_encrypt_key, rv64i_zvkned_encrypt, NULL); - } -} - -static const PROV_GCM_HW rv64i_zvkned_gcm = { - rv64i_zvkned_gcm_initkey, - ossl_gcm_setiv, - ossl_gcm_aad_update, - generic_aes_gcm_cipher_update, - ossl_gcm_cipher_final, - ossl_gcm_one_shot -}; - -/*- - * RISC-V RV64 ZVKB, ZVKG and ZVKNED support for AES GCM. - */ -static int rv64i_zvkb_zvkg_zvkned_gcm_initkey(PROV_GCM_CTX *ctx, - const unsigned char *key, - size_t keylen) -{ - /* - * Zvkned only supports 128 and 256 bit keys for key schedule generation. - * For AES-192 case, we could fallback to `AES_set_encrypt_key`. - */ - if (keylen * 8 == 128 || keylen * 8 == 256) { - return aes_gcm_hw_initkey(ctx, key, keylen, - rv64i_zvkned_set_encrypt_key, rv64i_zvkned_encrypt, - rv64i_zvkb_zvkned_ctr32_encrypt_blocks); - } else { - return aes_gcm_hw_initkey(ctx, key, keylen, - AES_set_encrypt_key, rv64i_zvkned_encrypt, - rv64i_zvkb_zvkned_ctr32_encrypt_blocks); - } -} - -static const PROV_GCM_HW rv64i_zvkb_zvkg_zvkned_gcm = { - rv64i_zvkb_zvkg_zvkned_gcm_initkey, - ossl_gcm_setiv, - ossl_gcm_aad_update, - generic_aes_gcm_cipher_update, - ossl_gcm_cipher_final, - ossl_gcm_one_shot -}; - -const PROV_GCM_HW *ossl_prov_aes_hw_gcm_rv64i(size_t keybits) -{ - if (RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) { - if (RISCV_HAS_ZVKB() && RISCV_HAS_ZVKG()) - return &rv64i_zvkb_zvkg_zvkned_gcm; - return &rv64i_zvkned_gcm; - } - - if (RISCV_HAS_ZKND_AND_ZKNE()) { - return &rv64i_zknd_zkne_gcm; - } - - return NULL; -} - -#endif diff --git a/providers/implementations/ciphers/cipher_aes_hw.c b/providers/implementations/ciphers/cipher_aes_hw.c index 34b28bb196..294b2dea7d 100644 --- a/providers/implementations/ciphers/cipher_aes_hw.c +++ b/providers/implementations/ciphers/cipher_aes_hw.c @@ -223,7 +223,7 @@ static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_mode(enum aes_modes mode, #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 aes_hw_mode = ossl_prov_cipher_hw_rv32i(mode); #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 - aes_hw_mode = ossl_prov_cipher_hw_rv64i(mode, keybits); + aes_hw_mode = ossl_prov_cipher_hw_rv64i(mode); #elif defined(S390X_aes_128_CAPABLE) aes_hw_mode = ossl_prov_cipher_hw_s390x(mode, keybits); #elif defined(SPARC_AES_CAPABLE) diff --git a/providers/implementations/ciphers/cipher_aes_hw_rv64i.c b/providers/implementations/ciphers/cipher_aes_hw_rv64i.c index e13c46813e..82e41096f5 100644 --- a/providers/implementations/ciphers/cipher_aes_hw_rv64i.c +++ b/providers/implementations/ciphers/cipher_aes_hw_rv64i.c @@ -1,5 +1,5 @@ /* - * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2022-2026 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -8,15 +8,19 @@ */ /*- - * RISC-V 64 ZKND ZKNE / ZVKNED support for AES modes ecb, cbc, ofb, cfb, ctr. - * This file is used by cipher_aes_hw.c + * RISC-V 64 ZKND ZKNE / ZVKNED support for all hardware accelerated AES modes. */ #include "internal/deprecated.h" #include "cipher_aes.h" +#include "cipher_aes_gcm.h" +#include "cipher_aes_ccm.h" +#include "cipher_aes_xts.h" #if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 +/* MODES: ecb, cbc, cfb, ofb, ctr */ + static int cipher_hw_rv64i_initkey(PROV_CIPHER_CTX *ctx, const unsigned char *key, size_t keylen) { @@ -33,8 +37,8 @@ static int cipher_hw_rv64i_initkey(PROV_CIPHER_CTX *ctx, if (keylen * 8 == 128 || keylen * 8 == 256) { fn_set_key = rv64i_zvkned_set_encrypt_key; } - ecb128_f fn_ecb = ctx->enc ? rv64i_zvkned_ecb_encrypt : rv64i_zvkned_ecb_decrypt; - cbc128_f fn_cbc = ctx->enc ? rv64i_zvkned_cbc_encrypt : rv64i_zvkned_cbc_decrypt; + ecb128_f fn_ecb = ctx->enc ? (ecb128_f)rv64i_zvkned_ecb_encrypt : (ecb128_f)rv64i_zvkned_ecb_decrypt; + cbc128_f fn_cbc = ctx->enc ? (cbc128_f)rv64i_zvkned_cbc_encrypt : (cbc128_f)rv64i_zvkned_cbc_decrypt; ctr128_f fn_ctr = RISCV_HAS_ZVKB() ? (ctr128_f)rv64i_zvkb_zvkned_ctr32_encrypt_blocks : NULL; /* Zvkned supports aes-128/192/256 encryption and decryption. */ @@ -42,7 +46,7 @@ static int cipher_hw_rv64i_initkey(PROV_CIPHER_CTX *ctx, && !ctx->enc) { fn_block = rv64i_zvkned_decrypt; } else { - fn_block = (block128_f)rv64i_zvkned_encrypt; + fn_block = rv64i_zvkned_encrypt; } return ossl_cipher_set_aes_initkey(ctx, key, keylen, fn_set_key, fn_block, fn_ecb, fn_cbc, fn_ctr); @@ -103,8 +107,7 @@ static const PROV_CIPHER_HW rv64i_ctr = { ossl_cipher_aes_copyctx }; -const PROV_CIPHER_HW *ossl_prov_cipher_hw_rv64i(enum aes_modes mode, - size_t keybits) +const PROV_CIPHER_HW *ossl_prov_cipher_hw_rv64i(enum aes_modes mode) { if ((RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) || RISCV_HAS_ZKND_AND_ZKNE()) { @@ -130,4 +133,207 @@ const PROV_CIPHER_HW *ossl_prov_cipher_hw_rv64i(enum aes_modes mode, return NULL; } +/* MODES: GCM */ + +/*- + * RISC-V 64 ZKND and ZKNE support for AES GCM. + */ +static int rv64i_zknd_zkne_gcm_initkey(PROV_GCM_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + return aes_gcm_hw_initkey(ctx, key, keylen, + rv64i_zkne_set_encrypt_key, rv64i_zkne_encrypt, NULL); +} + +static const PROV_GCM_HW rv64i_zknd_zkne_gcm = { + rv64i_zknd_zkne_gcm_initkey, + ossl_gcm_setiv, + ossl_gcm_aad_update, + generic_aes_gcm_cipher_update, + ossl_gcm_cipher_final, + ossl_gcm_one_shot +}; + +/*- + * RISC-V RV64 ZVKNED support for AES GCM. + */ +static int rv64i_zvkned_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + /* + * Zvkned only supports 128 and 256 bit keys for key schedule generation. + * For AES-192 case, we could fallback to `AES_set_encrypt_key`. + */ + if (keylen * 8 == 128 || keylen * 8 == 256) { + return aes_gcm_hw_initkey(ctx, key, keylen, + rv64i_zvkned_set_encrypt_key, rv64i_zvkned_encrypt, NULL); + } else { + return aes_gcm_hw_initkey(ctx, key, keylen, + AES_set_encrypt_key, rv64i_zvkned_encrypt, NULL); + } +} + +static const PROV_GCM_HW rv64i_zvkned_gcm = { + rv64i_zvkned_gcm_initkey, + ossl_gcm_setiv, + ossl_gcm_aad_update, + generic_aes_gcm_cipher_update, + ossl_gcm_cipher_final, + ossl_gcm_one_shot +}; + +/*- + * RISC-V RV64 ZVKB, ZVKG and ZVKNED support for AES GCM. + */ +static int rv64i_zvkb_zvkg_zvkned_gcm_initkey(PROV_GCM_CTX *ctx, + const unsigned char *key, + size_t keylen) +{ + /* + * Zvkned only supports 128 and 256 bit keys for key schedule generation. + * For AES-192 case, we could fallback to `AES_set_encrypt_key`. + */ + if (keylen * 8 == 128 || keylen * 8 == 256) { + return aes_gcm_hw_initkey(ctx, key, keylen, + rv64i_zvkned_set_encrypt_key, rv64i_zvkned_encrypt, + rv64i_zvkb_zvkned_ctr32_encrypt_blocks); + } else { + return aes_gcm_hw_initkey(ctx, key, keylen, + AES_set_encrypt_key, rv64i_zvkned_encrypt, + rv64i_zvkb_zvkned_ctr32_encrypt_blocks); + } +} + +static const PROV_GCM_HW rv64i_zvkb_zvkg_zvkned_gcm = { + rv64i_zvkb_zvkg_zvkned_gcm_initkey, + ossl_gcm_setiv, + ossl_gcm_aad_update, + generic_aes_gcm_cipher_update, + ossl_gcm_cipher_final, + ossl_gcm_one_shot +}; + +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_rv64i(void) +{ + if (RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) { + if (RISCV_HAS_ZVKB() && RISCV_HAS_ZVKG()) + return &rv64i_zvkb_zvkg_zvkned_gcm; + return &rv64i_zvkned_gcm; + } + + if (RISCV_HAS_ZKND_AND_ZKNE()) { + return &rv64i_zknd_zkne_gcm; + } + + return NULL; +} + +/* MODES: CCM */ + +static int ccm_rv64i_zknd_zkne_initkey(PROV_CCM_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, + rv64i_zkne_set_encrypt_key, rv64i_zkne_encrypt, NULL, NULL); +} + +static const PROV_CCM_HW rv64i_zknd_zkne_ccm = { + ccm_rv64i_zknd_zkne_initkey, + ossl_ccm_generic_setiv, + ossl_ccm_generic_setaad, + ossl_ccm_generic_auth_encrypt, + ossl_ccm_generic_auth_decrypt, + ossl_ccm_generic_gettag +}; + +/*- + * RISC-V RV64 ZVKNED support for AES CCM. + * This file is included by cipher_aes_ccm_hw.c + */ + +static int ccm_rv64i_zvkned_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + /* Zvkned only supports 128 and 256 bit keys for key schedule generation. */ + if (keylen * 8 == 128 || keylen * 8 == 256) { + return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, + rv64i_zvkned_set_encrypt_key, rv64i_zvkned_encrypt, NULL, NULL); + } else { + return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, + AES_set_encrypt_key, rv64i_zvkned_encrypt, NULL, NULL); + } +} + +static const PROV_CCM_HW rv64i_zvkned_ccm = { + ccm_rv64i_zvkned_initkey, + ossl_ccm_generic_setiv, + ossl_ccm_generic_setaad, + ossl_ccm_generic_auth_encrypt, + ossl_ccm_generic_auth_decrypt, + ossl_ccm_generic_gettag +}; + +const PROV_CCM_HW *ossl_prov_aes_hw_ccm_rv64i(void) +{ + if (RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) + return &rv64i_zvkned_ccm; + else if (RISCV_HAS_ZKND_AND_ZKNE()) + return &rv64i_zknd_zkne_ccm; + else + return NULL; +} + +/* MODES: XTS */ + +static int cipher_hw_aes_xts_rv64i_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + if (RISCV_HAS_ZVBB() && RISCV_HAS_ZVKG() && RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) { + /* Zvkned only supports 128 and 256 bit keys. */ + if (keylen * 8 == 128 * 2 || keylen * 8 == 256 * 2) + return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, + rv64i_zvkned_set_encrypt_key, rv64i_zvkned_set_decrypt_key, + rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, + rv64i_zvbb_zvkg_zvkned_aes_xts_encrypt, + rv64i_zvbb_zvkg_zvkned_aes_xts_decrypt); + + return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, + AES_set_encrypt_key, AES_set_encrypt_key, + rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, NULL, NULL); + } + + if (RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) { + /* Zvkned only supports 128 and 256 bit keys. */ + if (keylen * 8 == 128 * 2 || keylen * 8 == 256 * 2) + return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, + rv64i_zvkned_set_encrypt_key, rv64i_zvkned_set_decrypt_key, + rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, NULL, NULL); + + return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, + AES_set_encrypt_key, AES_set_encrypt_key, + rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, NULL, NULL); + } + + if (RISCV_HAS_ZKND_AND_ZKNE()) + return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, + rv64i_zkne_set_encrypt_key, rv64i_zknd_set_decrypt_key, + rv64i_zkne_encrypt, rv64i_zknd_decrypt, NULL, NULL); + + return 0; +} + +static const PROV_CIPHER_HW aes_xts_rv64i = { + cipher_hw_aes_xts_rv64i_initkey, + NULL, + ossl_cipher_hw_aes_xts_copyctx +}; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_rv64i(void) +{ + if ((RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) + || RISCV_HAS_ZKND_AND_ZKNE()) + return &aes_xts_rv64i; + return NULL; +} + #endif diff --git a/providers/implementations/ciphers/cipher_aes_xts.h b/providers/implementations/ciphers/cipher_aes_xts.h index 908e6ab937..5a626144e0 100644 --- a/providers/implementations/ciphers/cipher_aes_xts.h +++ b/providers/implementations/ciphers/cipher_aes_xts.h @@ -78,6 +78,10 @@ const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_aesni(void); const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_rv32i(void); #endif +#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_rv64i(void); +#endif + #ifdef AES_XTS_S390X int s390x_aes_xts_cipher_stream(PROV_AES_XTS_CTX *xctx, unsigned char *out, size_t *outl, diff --git a/providers/implementations/ciphers/cipher_aes_xts_hw.c b/providers/implementations/ciphers/cipher_aes_xts_hw.c index 4bf4462442..537786a7aa 100644 --- a/providers/implementations/ciphers/cipher_aes_xts_hw.c +++ b/providers/implementations/ciphers/cipher_aes_xts_hw.c @@ -142,59 +142,6 @@ static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_t4() return NULL; } -#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 - -static int cipher_hw_aes_xts_rv64i_initkey(PROV_CIPHER_CTX *ctx, - const unsigned char *key, size_t keylen) -{ - if (RISCV_HAS_ZVBB() && RISCV_HAS_ZVKG() && RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) { - /* Zvkned only supports 128 and 256 bit keys. */ - if (keylen * 8 == 128 * 2 || keylen * 8 == 256 * 2) - return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, - rv64i_zvkned_set_encrypt_key, rv64i_zvkned_set_decrypt_key, - rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, - rv64i_zvbb_zvkg_zvkned_aes_xts_encrypt, - rv64i_zvbb_zvkg_zvkned_aes_xts_decrypt); - - return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, - AES_set_encrypt_key, AES_set_encrypt_key, - rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, NULL, NULL); - } - - if (RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) { - /* Zvkned only supports 128 and 256 bit keys. */ - if (keylen * 8 == 128 * 2 || keylen * 8 == 256 * 2) - return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, - rv64i_zvkned_set_encrypt_key, rv64i_zvkned_set_decrypt_key, - rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, NULL, NULL); - - return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, - AES_set_encrypt_key, AES_set_encrypt_key, - rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, NULL, NULL); - } - - if (RISCV_HAS_ZKND_AND_ZKNE()) - return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, - rv64i_zkne_set_encrypt_key, rv64i_zknd_set_decrypt_key, - rv64i_zkne_encrypt, rv64i_zknd_decrypt, NULL, NULL); - - return 0; -} - -static const PROV_CIPHER_HW aes_xts_rv64i = { - cipher_hw_aes_xts_rv64i_initkey, - NULL, - ossl_cipher_hw_aes_xts_copyctx -}; - -static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_rv64i() -{ - if ((RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) - || RISCV_HAS_ZKND_AND_ZKNE()) - return &aes_xts_rv64i; - return NULL; -} - #elif defined(AES_XTS_S390X) int s390x_aes_xts_cipher_stream(PROV_AES_XTS_CTX *xctx, @@ -349,10 +296,10 @@ const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts(size_t keybits) aes_xts_hw = ossl_prov_cipher_hw_aes_xts_aesni(); #elif defined(SPARC_AES_CAPABLE) aes_xts_hw = ossl_prov_cipher_hw_aes_xts_t4(); -#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 - aes_xts_hw = ossl_prov_cipher_hw_aes_xts_rv64i(); #elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 aes_xts_hw = ossl_prov_cipher_hw_aes_xts_rv32i(); +#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 + aes_xts_hw = ossl_prov_cipher_hw_aes_xts_rv64i(); #elif defined(AES_XTS_S390X) aes_xts_hw = ossl_prov_cipher_hw_aes_xts_s390x(keybits); #endif From 10dcad1311dcc22f5d2b5116c989e80b53b66282 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Thu, 11 Jun 2026 18:51:14 -0400 Subject: [PATCH 096/349] Extract Zvkned key length check to helper A new helper function, zvkned_key_schedule_supported, is introduced to centralize the verification of 128-bit and 256-bit key lengths. This replaces multiple duplicated, hardcoded checks across the RISC-V AES implementations to improve code readability and maintainability. It also ensures consistent check handling for XTS mode by correctly evaluating half the key length. Signed-off-by: Simo Sorce Reviewed-by: Dmitry Belyavskiy Reviewed-by: Norbert Pocs Reviewed-by: Shane Lontis MergeDate: Sat Jun 27 09:05:56 2026 (Merged from https://github.com/openssl/openssl/pull/31472) --- .../ciphers/cipher_aes_hw_rv64i.c | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/providers/implementations/ciphers/cipher_aes_hw_rv64i.c b/providers/implementations/ciphers/cipher_aes_hw_rv64i.c index 82e41096f5..ba51447c42 100644 --- a/providers/implementations/ciphers/cipher_aes_hw_rv64i.c +++ b/providers/implementations/ciphers/cipher_aes_hw_rv64i.c @@ -19,6 +19,14 @@ #if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 +static int zvkned_key_schedule_supported(size_t keylen) +{ + if (keylen * 8 == 128 || keylen * 8 == 256) { + return 1; + } + return 0; +} + /* MODES: ecb, cbc, cfb, ofb, ctr */ static int cipher_hw_rv64i_initkey(PROV_CIPHER_CTX *ctx, @@ -34,7 +42,7 @@ static int cipher_hw_rv64i_initkey(PROV_CIPHER_CTX *ctx, aes_set_encrypt_key_fn fn_set_key = AES_set_encrypt_key; aes_block128_f fn_block = NULL; - if (keylen * 8 == 128 || keylen * 8 == 256) { + if (zvkned_key_schedule_supported(keylen)) { fn_set_key = rv64i_zvkned_set_encrypt_key; } ecb128_f fn_ecb = ctx->enc ? (ecb128_f)rv64i_zvkned_ecb_encrypt : (ecb128_f)rv64i_zvkned_ecb_decrypt; @@ -164,7 +172,7 @@ static int rv64i_zvkned_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, * Zvkned only supports 128 and 256 bit keys for key schedule generation. * For AES-192 case, we could fallback to `AES_set_encrypt_key`. */ - if (keylen * 8 == 128 || keylen * 8 == 256) { + if (zvkned_key_schedule_supported(keylen)) { return aes_gcm_hw_initkey(ctx, key, keylen, rv64i_zvkned_set_encrypt_key, rv64i_zvkned_encrypt, NULL); } else { @@ -193,7 +201,7 @@ static int rv64i_zvkb_zvkg_zvkned_gcm_initkey(PROV_GCM_CTX *ctx, * Zvkned only supports 128 and 256 bit keys for key schedule generation. * For AES-192 case, we could fallback to `AES_set_encrypt_key`. */ - if (keylen * 8 == 128 || keylen * 8 == 256) { + if (zvkned_key_schedule_supported(keylen)) { return aes_gcm_hw_initkey(ctx, key, keylen, rv64i_zvkned_set_encrypt_key, rv64i_zvkned_encrypt, rv64i_zvkb_zvkned_ctr32_encrypt_blocks); @@ -255,7 +263,7 @@ static int ccm_rv64i_zvkned_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, size_t keylen) { /* Zvkned only supports 128 and 256 bit keys for key schedule generation. */ - if (keylen * 8 == 128 || keylen * 8 == 256) { + if (zvkned_key_schedule_supported(keylen)) { return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, rv64i_zvkned_set_encrypt_key, rv64i_zvkned_encrypt, NULL, NULL); } else { @@ -290,13 +298,13 @@ static int cipher_hw_aes_xts_rv64i_initkey(PROV_CIPHER_CTX *ctx, { if (RISCV_HAS_ZVBB() && RISCV_HAS_ZVKG() && RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) { /* Zvkned only supports 128 and 256 bit keys. */ - if (keylen * 8 == 128 * 2 || keylen * 8 == 256 * 2) + if (zvkned_key_schedule_supported(keylen / 2)) { return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, rv64i_zvkned_set_encrypt_key, rv64i_zvkned_set_decrypt_key, rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, rv64i_zvbb_zvkg_zvkned_aes_xts_encrypt, rv64i_zvbb_zvkg_zvkned_aes_xts_decrypt); - + } return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, AES_set_encrypt_key, AES_set_encrypt_key, rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, NULL, NULL); @@ -304,20 +312,21 @@ static int cipher_hw_aes_xts_rv64i_initkey(PROV_CIPHER_CTX *ctx, if (RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) { /* Zvkned only supports 128 and 256 bit keys. */ - if (keylen * 8 == 128 * 2 || keylen * 8 == 256 * 2) + if (zvkned_key_schedule_supported(keylen / 2)) { return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, rv64i_zvkned_set_encrypt_key, rv64i_zvkned_set_decrypt_key, rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, NULL, NULL); - + } return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, AES_set_encrypt_key, AES_set_encrypt_key, rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, NULL, NULL); } - if (RISCV_HAS_ZKND_AND_ZKNE()) + if (RISCV_HAS_ZKND_AND_ZKNE()) { return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, rv64i_zkne_set_encrypt_key, rv64i_zknd_set_decrypt_key, rv64i_zkne_encrypt, rv64i_zknd_decrypt, NULL, NULL); + } return 0; } From b0bda138a64086485da93e1dc7fa90fd866e62bd Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Thu, 11 Jun 2026 19:02:27 -0400 Subject: [PATCH 097/349] Consolidate s390x AES hardware implementations Move the s390x hardware-accelerated AES mode implementations (GCM, CCM, and XTS) from their respective files into a single `cipher_aes_hw_s390x.c` file. Centralizing the platform-specific logic simplifies the code structure and improves maintainability. Signed-off-by: Simo Sorce Reviewed-by: Dmitry Belyavskiy Reviewed-by: Norbert Pocs Reviewed-by: Shane Lontis MergeDate: Sat Jun 27 09:05:58 2026 (Merged from https://github.com/openssl/openssl/pull/31472) --- providers/implementations/ciphers/build.info | 4 +- .../ciphers/cipher_aes_ccm_hw_s390x.c | 273 ------- .../ciphers/cipher_aes_gcm_hw_s390x.c | 317 -------- .../ciphers/cipher_aes_hw_s390x.c | 710 +++++++++++++++++- .../implementations/ciphers/cipher_aes_xts.h | 1 + .../ciphers/cipher_aes_xts_hw.c | 138 ---- 6 files changed, 709 insertions(+), 734 deletions(-) delete mode 100644 providers/implementations/ciphers/cipher_aes_ccm_hw_s390x.c delete mode 100644 providers/implementations/ciphers/cipher_aes_gcm_hw_s390x.c diff --git a/providers/implementations/ciphers/build.info b/providers/implementations/ciphers/build.info index 70e41b3e34..f82cae6762 100644 --- a/providers/implementations/ciphers/build.info +++ b/providers/implementations/ciphers/build.info @@ -109,9 +109,9 @@ SOURCE[$AES_GOAL]=\ cipher_aes_xts.c cipher_aes_xts_hw.c \ cipher_aes_gcm.c cipher_aes_gcm_hw.c \ cipher_aes_gcm_hw_ppc.c \ - cipher_aes_gcm_hw_s390x.c cipher_aes_gcm_hw_t4.c \ + cipher_aes_gcm_hw_t4.c \ cipher_aes_ccm.c cipher_aes_ccm_hw.c \ - cipher_aes_ccm_hw_s390x.c cipher_aes_ccm_hw_t4.c \ + cipher_aes_ccm_hw_t4.c \ cipher_aes_wrp.c \ cipher_aes_cbc_hmac_sha.c \ cipher_aes_cbc_hmac_sha256_hw.c cipher_aes_cbc_hmac_sha1_hw.c \ diff --git a/providers/implementations/ciphers/cipher_aes_ccm_hw_s390x.c b/providers/implementations/ciphers/cipher_aes_ccm_hw_s390x.c deleted file mode 100644 index d79e2766bc..0000000000 --- a/providers/implementations/ciphers/cipher_aes_ccm_hw_s390x.c +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright 2001-2020 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/*- - * S390X support for AES CCM. - * This file is used by cipher_aes_ccm_hw.c - */ - -#include "internal/deprecated.h" -#include "cipher_aes_ccm.h" - -#if defined(S390X_aes_128_CAPABLE) - -#define S390X_CCM_AAD_FLAG 0x40 - -static int s390x_aes_ccm_initkey(PROV_CCM_CTX *ctx, - const unsigned char *key, size_t keylen) -{ - PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx; - - sctx->ccm.s390x.fc = S390X_AES_FC(keylen); - memcpy(&sctx->ccm.s390x.kmac.k, key, keylen); - /* Store encoded m and l. */ - sctx->ccm.s390x.nonce.b[0] = ((ctx->l - 1) & 0x7) - | (((ctx->m - 2) >> 1) & 0x7) << 3; - memset(sctx->ccm.s390x.nonce.b + 1, 0, sizeof(sctx->ccm.s390x.nonce.b)); - sctx->ccm.s390x.blocks = 0; - ctx->key_set = 1; - return 1; -} - -static int s390x_aes_ccm_setiv(PROV_CCM_CTX *ctx, - const unsigned char *nonce, size_t noncelen, - size_t mlen) -{ - PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx; - - sctx->ccm.s390x.nonce.b[0] &= ~S390X_CCM_AAD_FLAG; - sctx->ccm.s390x.nonce.g[1] = mlen; - memcpy(sctx->ccm.s390x.nonce.b + 1, nonce, 15 - ctx->l); - return 1; -} - -/*- - * Process additional authenticated data. Code is big-endian. - */ -static int s390x_aes_ccm_setaad(PROV_CCM_CTX *ctx, - const unsigned char *aad, size_t alen) -{ - PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx; - unsigned char *ptr; - int i, rem; - - if (!alen) - return 1; - - sctx->ccm.s390x.nonce.b[0] |= S390X_CCM_AAD_FLAG; - - /* Suppress 'type-punned pointer dereference' warning. */ - ptr = sctx->ccm.s390x.buf.b; - - if (alen < ((1 << 16) - (1 << 8))) { - *(uint16_t *)ptr = alen; - i = 2; - } else if (sizeof(alen) == 8 - && alen >= (size_t)1 << (32 % (sizeof(alen) * 8))) { - *(uint16_t *)ptr = 0xffff; - *(uint64_t *)(ptr + 2) = alen; - i = 10; - } else { - *(uint16_t *)ptr = 0xfffe; - *(uint32_t *)(ptr + 2) = alen; - i = 6; - } - - while (i < 16 && alen) { - sctx->ccm.s390x.buf.b[i] = *aad; - ++aad; - --alen; - ++i; - } - while (i < 16) { - sctx->ccm.s390x.buf.b[i] = 0; - ++i; - } - - sctx->ccm.s390x.kmac.icv.g[0] = 0; - sctx->ccm.s390x.kmac.icv.g[1] = 0; - s390x_kmac(sctx->ccm.s390x.nonce.b, 32, sctx->ccm.s390x.fc, - &sctx->ccm.s390x.kmac); - sctx->ccm.s390x.blocks += 2; - - rem = alen & 0xf; - alen &= ~(size_t)0xf; - if (alen) { - s390x_kmac(aad, alen, sctx->ccm.s390x.fc, &sctx->ccm.s390x.kmac); - sctx->ccm.s390x.blocks += alen >> 4; - aad += alen; - } - if (rem) { - for (i = 0; i < rem; i++) - sctx->ccm.s390x.kmac.icv.b[i] ^= aad[i]; - - s390x_km(sctx->ccm.s390x.kmac.icv.b, 16, - sctx->ccm.s390x.kmac.icv.b, sctx->ccm.s390x.fc, - sctx->ccm.s390x.kmac.k); - sctx->ccm.s390x.blocks++; - } - return 1; -} - -/*- - * En/de-crypt plain/cipher-text. Compute tag from plaintext. Returns 1 for - * success. - */ -static int s390x_aes_ccm_auth_encdec(PROV_CCM_CTX *ctx, - const unsigned char *in, - unsigned char *out, size_t len, int enc) -{ - PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx; - size_t n, rem; - unsigned int i, l, num; - unsigned char flags; - - flags = sctx->ccm.s390x.nonce.b[0]; - if (!(flags & S390X_CCM_AAD_FLAG)) { - s390x_km(sctx->ccm.s390x.nonce.b, 16, sctx->ccm.s390x.kmac.icv.b, - sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k); - sctx->ccm.s390x.blocks++; - } - l = flags & 0x7; - sctx->ccm.s390x.nonce.b[0] = l; - - /*- - * Reconstruct length from encoded length field - * and initialize it with counter value. - */ - n = 0; - for (i = 15 - l; i < 15; i++) { - n |= sctx->ccm.s390x.nonce.b[i]; - sctx->ccm.s390x.nonce.b[i] = 0; - n <<= 8; - } - n |= sctx->ccm.s390x.nonce.b[15]; - sctx->ccm.s390x.nonce.b[15] = 1; - - if (n != len) - return 0; /* length mismatch */ - - if (enc) { - /* Two operations per block plus one for tag encryption */ - sctx->ccm.s390x.blocks += (((len + 15) >> 4) << 1) + 1; - if (sctx->ccm.s390x.blocks > (1ULL << 61)) - return 0; /* too much data */ - } - - num = 0; - rem = len & 0xf; - len &= ~(size_t)0xf; - - if (enc) { - /* mac-then-encrypt */ - if (len) - s390x_kmac(in, len, sctx->ccm.s390x.fc, &sctx->ccm.s390x.kmac); - if (rem) { - for (i = 0; i < rem; i++) - sctx->ccm.s390x.kmac.icv.b[i] ^= in[len + i]; - - s390x_km(sctx->ccm.s390x.kmac.icv.b, 16, - sctx->ccm.s390x.kmac.icv.b, - sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k); - } - - CRYPTO_ctr128_encrypt_ctr32(in, out, len + rem, &sctx->ccm.ks.ks, - sctx->ccm.s390x.nonce.b, sctx->ccm.s390x.buf.b, - &num, (ctr128_f)AES_ctr32_encrypt); - } else { - /* decrypt-then-mac */ - CRYPTO_ctr128_encrypt_ctr32(in, out, len + rem, &sctx->ccm.ks.ks, - sctx->ccm.s390x.nonce.b, sctx->ccm.s390x.buf.b, - &num, (ctr128_f)AES_ctr32_encrypt); - - if (len) - s390x_kmac(out, len, sctx->ccm.s390x.fc, &sctx->ccm.s390x.kmac); - if (rem) { - for (i = 0; i < rem; i++) - sctx->ccm.s390x.kmac.icv.b[i] ^= out[len + i]; - - s390x_km(sctx->ccm.s390x.kmac.icv.b, 16, - sctx->ccm.s390x.kmac.icv.b, - sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k); - } - } - /* encrypt tag */ - for (i = 15 - l; i < 16; i++) - sctx->ccm.s390x.nonce.b[i] = 0; - - s390x_km(sctx->ccm.s390x.nonce.b, 16, sctx->ccm.s390x.buf.b, - sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k); - sctx->ccm.s390x.kmac.icv.g[0] ^= sctx->ccm.s390x.buf.g[0]; - sctx->ccm.s390x.kmac.icv.g[1] ^= sctx->ccm.s390x.buf.g[1]; - - sctx->ccm.s390x.nonce.b[0] = flags; /* restore flags field */ - return 1; -} - -static int s390x_aes_ccm_gettag(PROV_CCM_CTX *ctx, - unsigned char *tag, size_t tlen) -{ - PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx; - - if (tlen > ctx->m) - return 0; - memcpy(tag, sctx->ccm.s390x.kmac.icv.b, tlen); - return 1; -} - -static int s390x_aes_ccm_auth_encrypt(PROV_CCM_CTX *ctx, - const unsigned char *in, - unsigned char *out, size_t len, - unsigned char *tag, size_t taglen) -{ - int rv; - - rv = s390x_aes_ccm_auth_encdec(ctx, in, out, len, 1); - if (rv && tag != NULL) - rv = s390x_aes_ccm_gettag(ctx, tag, taglen); - return rv; -} - -static int s390x_aes_ccm_auth_decrypt(PROV_CCM_CTX *ctx, - const unsigned char *in, - unsigned char *out, size_t len, - unsigned char *expected_tag, - size_t taglen) -{ - int rv = 0; - PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx; - - rv = s390x_aes_ccm_auth_encdec(ctx, in, out, len, 0); - if (rv) { - if (CRYPTO_memcmp(sctx->ccm.s390x.kmac.icv.b, expected_tag, ctx->m) != 0) - rv = 0; - } - if (rv == 0) - OPENSSL_cleanse(out, len); - return rv; -} - -static const PROV_CCM_HW s390x_aes_ccm = { - s390x_aes_ccm_initkey, - s390x_aes_ccm_setiv, - s390x_aes_ccm_setaad, - s390x_aes_ccm_auth_encrypt, - s390x_aes_ccm_auth_decrypt, - s390x_aes_ccm_gettag -}; - -const PROV_CCM_HW *ossl_prov_aes_hw_ccm_s390x(size_t keybits) -{ - if ((keybits == 128 && S390X_aes_128_ccm_CAPABLE) - || (keybits == 192 && S390X_aes_192_ccm_CAPABLE) - || (keybits == 256 && S390X_aes_256_ccm_CAPABLE)) - return &s390x_aes_ccm; - return NULL; -} -#endif diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw_s390x.c b/providers/implementations/ciphers/cipher_aes_gcm_hw_s390x.c deleted file mode 100644 index 4c154744e0..0000000000 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw_s390x.c +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Copyright 2001-2021 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/*- - * IBM S390X support for AES GCM. - * This file is used by cipher_aes_gcm_hw.c - */ -#include "internal/deprecated.h" -#include "cipher_aes_gcm.h" - -#if defined(S390X_aes_128_CAPABLE) - -/* iv + padding length for iv lengths != 12 */ -#define S390X_gcm_ivpadlen(i) ((((i) + 15) >> 4 << 4) + 16) - -/* Additional flag or'ed to fc for decryption */ -#define S390X_gcm_decrypt_flag(ctx) (((ctx)->enc) ? 0 : S390X_DECRYPT) - -#define S390X_gcm_fc(A, C) ((A)->plat.s390x.fc | (A)->plat.s390x.hsflag | S390X_gcm_decrypt_flag((C))) - -static int s390x_aes_gcm_initkey(PROV_GCM_CTX *ctx, - const unsigned char *key, size_t keylen) -{ - PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; - - ctx->key_set = 1; - memcpy(&actx->plat.s390x.param.kma.k, key, keylen); - actx->plat.s390x.fc = S390X_AES_FC(keylen); - return 1; -} - -static int s390x_aes_gcm_setiv(PROV_GCM_CTX *ctx, const unsigned char *iv, - size_t ivlen) -{ - PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; - S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma; - - kma->t.g[0] = 0; - kma->t.g[1] = 0; - kma->tpcl = 0; - kma->taadl = 0; - actx->plat.s390x.mreslen = 0; - actx->plat.s390x.areslen = 0; - actx->plat.s390x.kreslen = 0; - - if (ivlen == GCM_IV_DEFAULT_SIZE) { - memcpy(&kma->j0, iv, ivlen); - kma->j0.w[3] = 1; - kma->cv.w = 1; - actx->plat.s390x.hsflag = 0; - } else { - unsigned long long ivbits = ivlen << 3; - size_t len = S390X_gcm_ivpadlen(ivlen); - unsigned char iv_zero_pad[S390X_gcm_ivpadlen(GCM_IV_MAX_SIZE)]; - /* - * The IV length needs to be zero padded to be a multiple of 16 bytes - * followed by 8 bytes of zeros and 8 bytes for the IV length. - * The GHASH of this value can then be calculated. - */ - memcpy(iv_zero_pad, iv, ivlen); - memset(iv_zero_pad + ivlen, 0, len - ivlen); - memcpy(iv_zero_pad + len - sizeof(ivbits), &ivbits, sizeof(ivbits)); - /* - * Calculate the ghash of the iv - the result is stored into the tag - * param. - */ - s390x_kma(iv_zero_pad, len, NULL, 0, NULL, actx->plat.s390x.fc, kma); - actx->plat.s390x.hsflag = S390X_KMA_HS; /* The hash subkey is set */ - - /* Copy the 128 bit GHASH result into J0 and clear the tag */ - kma->j0.g[0] = kma->t.g[0]; - kma->j0.g[1] = kma->t.g[1]; - kma->t.g[0] = 0; - kma->t.g[1] = 0; - /* Set the 32 bit counter */ - kma->cv.w = kma->j0.w[3]; - } - return 1; -} - -static int s390x_aes_gcm_cipher_final(PROV_GCM_CTX *ctx, unsigned char *tag) -{ - PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; - S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma; - unsigned char out[AES_BLOCK_SIZE]; - unsigned int fc; - int rc; - - kma->taadl <<= 3; - kma->tpcl <<= 3; - fc = S390X_gcm_fc(actx, ctx) | S390X_KMA_LAAD | S390X_KMA_LPC; - s390x_kma(actx->plat.s390x.ares, actx->plat.s390x.areslen, - actx->plat.s390x.mres, actx->plat.s390x.mreslen, out, - fc, kma); - - /* gctx->mres already returned to the caller */ - OPENSSL_cleanse(out, actx->plat.s390x.mreslen); - - if (ctx->enc) { - ctx->taglen = GCM_TAG_MAX_SIZE; - memcpy(tag, kma->t.b, ctx->taglen); - rc = 1; - } else { - rc = (CRYPTO_memcmp(tag, kma->t.b, ctx->taglen) == 0); - } - return rc; -} - -static int s390x_aes_gcm_one_shot(PROV_GCM_CTX *ctx, - unsigned char *aad, size_t aad_len, - const unsigned char *in, size_t in_len, - unsigned char *out, - unsigned char *tag, size_t taglen) -{ - PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; - S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma; - unsigned int fc; - int rc; - - kma->taadl = aad_len << 3; - kma->tpcl = in_len << 3; - fc = S390X_gcm_fc(actx, ctx) | S390X_KMA_LAAD | S390X_KMA_LPC; - s390x_kma(aad, aad_len, in, in_len, out, fc, kma); - - if (ctx->enc) { - memcpy(tag, kma->t.b, taglen); - rc = 1; - } else { - rc = (CRYPTO_memcmp(tag, kma->t.b, taglen) == 0); - } - return rc; -} - -/* - * Process additional authenticated data. Returns 1 on success. Code is - * big-endian. - */ -static int s390x_aes_gcm_aad_update(PROV_GCM_CTX *ctx, - const unsigned char *aad, size_t len) -{ - PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; - S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma; - unsigned long long alen; - unsigned int fc; - int n, rem; - - /* If already processed pt/ct then error */ - if (kma->tpcl != 0) - return 0; - - /* update the total aad length */ - alen = kma->taadl + len; - if (alen > (U64(1) << 61) || (sizeof(len) == 8 && alen < len)) - return 0; - kma->taadl = alen; - - /* check if there is any existing aad data from a previous add */ - n = actx->plat.s390x.areslen; - if (n) { - /* add additional data to a buffer until it has 16 bytes */ - while (n && len) { - actx->plat.s390x.ares[n] = *aad; - ++aad; - --len; - n = (n + 1) & 0xf; - } - /* ctx->ares contains a complete block if offset has wrapped around */ - if (!n) { - fc = S390X_gcm_fc(actx, ctx); - s390x_kma(actx->plat.s390x.ares, 16, NULL, 0, NULL, fc, kma); - actx->plat.s390x.hsflag = S390X_KMA_HS; - } - actx->plat.s390x.areslen = n; - } - - /* If there are leftover bytes (< 128 bits) save them for next time */ - rem = len & 0xf; - /* Add any remaining 16 byte blocks (128 bit each) */ - len &= ~(size_t)0xf; - if (len) { - fc = S390X_gcm_fc(actx, ctx); - s390x_kma(aad, len, NULL, 0, NULL, fc, kma); - actx->plat.s390x.hsflag = S390X_KMA_HS; - aad += len; - } - - if (rem) { - actx->plat.s390x.areslen = rem; - - do { - --rem; - actx->plat.s390x.ares[rem] = aad[rem]; - } while (rem); - } - return 1; -} - -/*- - * En/de-crypt plain/cipher-text and authenticate ciphertext. Returns 1 for - * success. Code is big-endian. - */ -static int s390x_aes_gcm_cipher_update(PROV_GCM_CTX *ctx, - const unsigned char *in, size_t len, - unsigned char *out) -{ - PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; - S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma; - const unsigned char *inptr; - unsigned long long mlen; - unsigned int fc; - union { - unsigned int w[4]; - unsigned char b[16]; - } buf; - size_t inlen; - int n, rem, i; - - mlen = kma->tpcl + len; - if (mlen > ((U64(1) << 36) - 32) || (sizeof(len) == 8 && mlen < len)) - return 0; - kma->tpcl = mlen; - - fc = S390X_gcm_fc(actx, ctx) | S390X_KMA_LAAD; - n = actx->plat.s390x.mreslen; - if (n) { - inptr = in; - inlen = len; - while (n && inlen) { - actx->plat.s390x.mres[n] = *inptr; - n = (n + 1) & 0xf; - ++inptr; - --inlen; - } - /* ctx->mres contains a complete block if offset has wrapped around */ - if (!n) { - s390x_kma(actx->plat.s390x.ares, actx->plat.s390x.areslen, - actx->plat.s390x.mres, 16, buf.b, fc, kma); - actx->plat.s390x.hsflag = S390X_KMA_HS; - fc |= S390X_KMA_HS; - actx->plat.s390x.areslen = 0; - - /* previous call already encrypted/decrypted its remainder, - * see comment below */ - n = actx->plat.s390x.mreslen; - while (n) { - *out = buf.b[n]; - n = (n + 1) & 0xf; - ++out; - ++in; - --len; - } - actx->plat.s390x.mreslen = 0; - } - } - - rem = len & 0xf; - - len &= ~(size_t)0xf; - if (len) { - s390x_kma(actx->plat.s390x.ares, actx->plat.s390x.areslen, in, len, out, - fc, kma); - in += len; - out += len; - actx->plat.s390x.hsflag = S390X_KMA_HS; - actx->plat.s390x.areslen = 0; - } - - /*- - * If there is a remainder, it has to be saved such that it can be - * processed by kma later. However, we also have to do the for-now - * unauthenticated encryption/decryption part here and now... - */ - if (rem) { - if (!actx->plat.s390x.mreslen) { - buf.w[0] = kma->j0.w[0]; - buf.w[1] = kma->j0.w[1]; - buf.w[2] = kma->j0.w[2]; - buf.w[3] = kma->cv.w + 1; - s390x_km(buf.b, 16, actx->plat.s390x.kres, - fc & 0x1f, &kma->k); - } - - n = actx->plat.s390x.mreslen; - for (i = 0; i < rem; i++) { - actx->plat.s390x.mres[n + i] = in[i]; - out[i] = in[i] ^ actx->plat.s390x.kres[n + i]; - } - actx->plat.s390x.mreslen += rem; - } - return 1; -} - -static const PROV_GCM_HW s390x_aes_gcm = { - s390x_aes_gcm_initkey, - s390x_aes_gcm_setiv, - s390x_aes_gcm_aad_update, - s390x_aes_gcm_cipher_update, - s390x_aes_gcm_cipher_final, - s390x_aes_gcm_one_shot -}; - -const PROV_GCM_HW *ossl_prov_aes_hw_gcm_s390x(size_t keybits) -{ - if ((keybits == 128 && S390X_aes_128_gcm_CAPABLE) - || (keybits == 192 && S390X_aes_192_gcm_CAPABLE) - || (keybits == 256 && S390X_aes_256_gcm_CAPABLE)) - return &s390x_aes_gcm; - return NULL; -} - -#endif diff --git a/providers/implementations/ciphers/cipher_aes_hw_s390x.c b/providers/implementations/ciphers/cipher_aes_hw_s390x.c index 30d30d5de7..9c3b2e91aa 100644 --- a/providers/implementations/ciphers/cipher_aes_hw_s390x.c +++ b/providers/implementations/ciphers/cipher_aes_hw_s390x.c @@ -1,5 +1,5 @@ /* - * Copyright 2001-2025 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2001-2026 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -8,17 +8,21 @@ */ /* - * IBM S390X support for AES modes ecb, cbc, ofb, cfb, ctr. - * This file is used by cipher_aes_hw.c + * IBM S390X support for all hardware accelerated AES modes. */ #include "internal/deprecated.h" +#include #include "cipher_aes.h" -#include "arch/s390x_arch.h" +#include "cipher_aes_gcm.h" +#include "cipher_aes_ccm.h" +#include "cipher_aes_xts.h" #include #if defined(S390X_aes_128_CAPABLE) +/* MODES: ecb, cfb, ofb */ + static int s390x_aes_ecb_initkey(PROV_CIPHER_CTX *dat, const unsigned char *key, size_t keylen) { @@ -237,4 +241,702 @@ const PROV_CIPHER_HW *ossl_prov_cipher_hw_s390x(enum aes_modes mode, return NULL; } +/* MODES: GCM */ + +/* iv + padding length for iv lengths != 12 */ +#define S390X_gcm_ivpadlen(i) ((((i) + 15) >> 4 << 4) + 16) + +/* Additional flag or'ed to fc for decryption */ +#define S390X_gcm_decrypt_flag(ctx) (((ctx)->enc) ? 0 : S390X_DECRYPT) + +#define S390X_gcm_fc(A, C) ((A)->plat.s390x.fc | (A)->plat.s390x.hsflag | S390X_gcm_decrypt_flag((C))) + +static int s390x_aes_gcm_initkey(PROV_GCM_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + + ctx->key_set = 1; + memcpy(&actx->plat.s390x.param.kma.k, key, keylen); + actx->plat.s390x.fc = S390X_AES_FC(keylen); + return 1; +} + +static int s390x_aes_gcm_setiv(PROV_GCM_CTX *ctx, const unsigned char *iv, + size_t ivlen) +{ + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma; + + kma->t.g[0] = 0; + kma->t.g[1] = 0; + kma->tpcl = 0; + kma->taadl = 0; + actx->plat.s390x.mreslen = 0; + actx->plat.s390x.areslen = 0; + actx->plat.s390x.kreslen = 0; + + if (ivlen == GCM_IV_DEFAULT_SIZE) { + memcpy(&kma->j0, iv, ivlen); + kma->j0.w[3] = 1; + kma->cv.w = 1; + actx->plat.s390x.hsflag = 0; + } else { + unsigned long long ivbits = ivlen << 3; + size_t len = S390X_gcm_ivpadlen(ivlen); + unsigned char iv_zero_pad[S390X_gcm_ivpadlen(GCM_IV_MAX_SIZE)]; + /* + * The IV length needs to be zero padded to be a multiple of 16 bytes + * followed by 8 bytes of zeros and 8 bytes for the IV length. + * The GHASH of this value can then be calculated. + */ + memcpy(iv_zero_pad, iv, ivlen); + memset(iv_zero_pad + ivlen, 0, len - ivlen); + memcpy(iv_zero_pad + len - sizeof(ivbits), &ivbits, sizeof(ivbits)); + /* + * Calculate the ghash of the iv - the result is stored into the tag + * param. + */ + s390x_kma(iv_zero_pad, len, NULL, 0, NULL, actx->plat.s390x.fc, kma); + actx->plat.s390x.hsflag = S390X_KMA_HS; /* The hash subkey is set */ + + /* Copy the 128 bit GHASH result into J0 and clear the tag */ + kma->j0.g[0] = kma->t.g[0]; + kma->j0.g[1] = kma->t.g[1]; + kma->t.g[0] = 0; + kma->t.g[1] = 0; + /* Set the 32 bit counter */ + kma->cv.w = kma->j0.w[3]; + } + return 1; +} + +static int s390x_aes_gcm_cipher_final(PROV_GCM_CTX *ctx, unsigned char *tag) +{ + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma; + unsigned char out[AES_BLOCK_SIZE]; + unsigned int fc; + int rc; + + kma->taadl <<= 3; + kma->tpcl <<= 3; + fc = S390X_gcm_fc(actx, ctx) | S390X_KMA_LAAD | S390X_KMA_LPC; + s390x_kma(actx->plat.s390x.ares, actx->plat.s390x.areslen, + actx->plat.s390x.mres, actx->plat.s390x.mreslen, out, + fc, kma); + + /* gctx->mres already returned to the caller */ + OPENSSL_cleanse(out, actx->plat.s390x.mreslen); + + if (ctx->enc) { + ctx->taglen = GCM_TAG_MAX_SIZE; + memcpy(tag, kma->t.b, ctx->taglen); + rc = 1; + } else { + rc = (CRYPTO_memcmp(tag, kma->t.b, ctx->taglen) == 0); + } + return rc; +} + +static int s390x_aes_gcm_one_shot(PROV_GCM_CTX *ctx, + unsigned char *aad, size_t aad_len, + const unsigned char *in, size_t in_len, + unsigned char *out, + unsigned char *tag, size_t taglen) +{ + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma; + unsigned int fc; + int rc; + + kma->taadl = aad_len << 3; + kma->tpcl = in_len << 3; + fc = S390X_gcm_fc(actx, ctx) | S390X_KMA_LAAD | S390X_KMA_LPC; + s390x_kma(aad, aad_len, in, in_len, out, fc, kma); + + if (ctx->enc) { + memcpy(tag, kma->t.b, taglen); + rc = 1; + } else { + rc = (CRYPTO_memcmp(tag, kma->t.b, taglen) == 0); + } + return rc; +} + +/* + * Process additional authenticated data. Returns 1 on success. Code is + * big-endian. + */ +static int s390x_aes_gcm_aad_update(PROV_GCM_CTX *ctx, + const unsigned char *aad, size_t len) +{ + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma; + unsigned long long alen; + unsigned int fc; + int n, rem; + + /* If already processed pt/ct then error */ + if (kma->tpcl != 0) + return 0; + + /* update the total aad length */ + alen = kma->taadl + len; + if (alen > (U64(1) << 61) || (sizeof(len) == 8 && alen < len)) + return 0; + kma->taadl = alen; + + /* check if there is any existing aad data from a previous add */ + n = actx->plat.s390x.areslen; + if (n) { + /* add additional data to a buffer until it has 16 bytes */ + while (n && len) { + actx->plat.s390x.ares[n] = *aad; + ++aad; + --len; + n = (n + 1) & 0xf; + } + /* ctx->ares contains a complete block if offset has wrapped around */ + if (!n) { + fc = S390X_gcm_fc(actx, ctx); + s390x_kma(actx->plat.s390x.ares, 16, NULL, 0, NULL, fc, kma); + actx->plat.s390x.hsflag = S390X_KMA_HS; + } + actx->plat.s390x.areslen = n; + } + + /* If there are leftover bytes (< 128 bits) save them for next time */ + rem = len & 0xf; + /* Add any remaining 16 byte blocks (128 bit each) */ + len &= ~(size_t)0xf; + if (len) { + fc = S390X_gcm_fc(actx, ctx); + s390x_kma(aad, len, NULL, 0, NULL, fc, kma); + actx->plat.s390x.hsflag = S390X_KMA_HS; + aad += len; + } + + if (rem) { + actx->plat.s390x.areslen = rem; + + do { + --rem; + actx->plat.s390x.ares[rem] = aad[rem]; + } while (rem); + } + return 1; +} + +/*- + * En/de-crypt plain/cipher-text and authenticate ciphertext. Returns 1 for + * success. Code is big-endian. + */ +static int s390x_aes_gcm_cipher_update(PROV_GCM_CTX *ctx, + const unsigned char *in, size_t len, + unsigned char *out) +{ + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma; + const unsigned char *inptr; + unsigned long long mlen; + unsigned int fc; + union { + unsigned int w[4]; + unsigned char b[16]; + } buf; + size_t inlen; + int n, rem, i; + + mlen = kma->tpcl + len; + if (mlen > ((U64(1) << 36) - 32) || (sizeof(len) == 8 && mlen < len)) + return 0; + kma->tpcl = mlen; + + fc = S390X_gcm_fc(actx, ctx) | S390X_KMA_LAAD; + n = actx->plat.s390x.mreslen; + if (n) { + inptr = in; + inlen = len; + while (n && inlen) { + actx->plat.s390x.mres[n] = *inptr; + n = (n + 1) & 0xf; + ++inptr; + --inlen; + } + /* ctx->mres contains a complete block if offset has wrapped around */ + if (!n) { + s390x_kma(actx->plat.s390x.ares, actx->plat.s390x.areslen, + actx->plat.s390x.mres, 16, buf.b, fc, kma); + actx->plat.s390x.hsflag = S390X_KMA_HS; + fc |= S390X_KMA_HS; + actx->plat.s390x.areslen = 0; + + /* previous call already encrypted/decrypted its remainder, + * see comment below */ + n = actx->plat.s390x.mreslen; + while (n) { + *out = buf.b[n]; + n = (n + 1) & 0xf; + ++out; + ++in; + --len; + } + actx->plat.s390x.mreslen = 0; + } + } + + rem = len & 0xf; + + len &= ~(size_t)0xf; + if (len) { + s390x_kma(actx->plat.s390x.ares, actx->plat.s390x.areslen, in, len, out, + fc, kma); + in += len; + out += len; + actx->plat.s390x.hsflag = S390X_KMA_HS; + actx->plat.s390x.areslen = 0; + } + + /*- + * If there is a remainder, it has to be saved such that it can be + * processed by kma later. However, we also have to do the for-now + * unauthenticated encryption/decryption part here and now... + */ + if (rem) { + if (!actx->plat.s390x.mreslen) { + buf.w[0] = kma->j0.w[0]; + buf.w[1] = kma->j0.w[1]; + buf.w[2] = kma->j0.w[2]; + buf.w[3] = kma->cv.w + 1; + s390x_km(buf.b, 16, actx->plat.s390x.kres, + fc & 0x1f, &kma->k); + } + + n = actx->plat.s390x.mreslen; + for (i = 0; i < rem; i++) { + actx->plat.s390x.mres[n + i] = in[i]; + out[i] = in[i] ^ actx->plat.s390x.kres[n + i]; + } + actx->plat.s390x.mreslen += rem; + } + return 1; +} + +static const PROV_GCM_HW s390x_aes_gcm = { + s390x_aes_gcm_initkey, + s390x_aes_gcm_setiv, + s390x_aes_gcm_aad_update, + s390x_aes_gcm_cipher_update, + s390x_aes_gcm_cipher_final, + s390x_aes_gcm_one_shot +}; + +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_s390x(size_t keybits) +{ + if ((keybits == 128 && S390X_aes_128_gcm_CAPABLE) + || (keybits == 192 && S390X_aes_192_gcm_CAPABLE) + || (keybits == 256 && S390X_aes_256_gcm_CAPABLE)) + return &s390x_aes_gcm; + return NULL; +} + +/* MODES: CCM */ + +#define S390X_CCM_AAD_FLAG 0x40 + +static int s390x_aes_ccm_initkey(PROV_CCM_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx; + + sctx->ccm.s390x.fc = S390X_AES_FC(keylen); + memcpy(&sctx->ccm.s390x.kmac.k, key, keylen); + /* Store encoded m and l. */ + sctx->ccm.s390x.nonce.b[0] = ((ctx->l - 1) & 0x7) + | (((ctx->m - 2) >> 1) & 0x7) << 3; + memset(sctx->ccm.s390x.nonce.b + 1, 0, sizeof(sctx->ccm.s390x.nonce.b)); + sctx->ccm.s390x.blocks = 0; + ctx->key_set = 1; + return 1; +} + +static int s390x_aes_ccm_setiv(PROV_CCM_CTX *ctx, + const unsigned char *nonce, size_t noncelen, + size_t mlen) +{ + PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx; + + sctx->ccm.s390x.nonce.b[0] &= ~S390X_CCM_AAD_FLAG; + sctx->ccm.s390x.nonce.g[1] = mlen; + memcpy(sctx->ccm.s390x.nonce.b + 1, nonce, 15 - ctx->l); + return 1; +} + +/*- + * Process additional authenticated data. Code is big-endian. + */ +static int s390x_aes_ccm_setaad(PROV_CCM_CTX *ctx, + const unsigned char *aad, size_t alen) +{ + PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx; + unsigned char *ptr; + int i, rem; + + if (!alen) + return 1; + + sctx->ccm.s390x.nonce.b[0] |= S390X_CCM_AAD_FLAG; + + /* Suppress 'type-punned pointer dereference' warning. */ + ptr = sctx->ccm.s390x.buf.b; + + if (alen < ((1 << 16) - (1 << 8))) { + *(uint16_t *)ptr = alen; + i = 2; + } else if (sizeof(alen) == 8 + && alen >= (size_t)1 << (32 % (sizeof(alen) * 8))) { + *(uint16_t *)ptr = 0xffff; + *(uint64_t *)(ptr + 2) = alen; + i = 10; + } else { + *(uint16_t *)ptr = 0xfffe; + *(uint32_t *)(ptr + 2) = alen; + i = 6; + } + + while (i < 16 && alen) { + sctx->ccm.s390x.buf.b[i] = *aad; + ++aad; + --alen; + ++i; + } + while (i < 16) { + sctx->ccm.s390x.buf.b[i] = 0; + ++i; + } + + sctx->ccm.s390x.kmac.icv.g[0] = 0; + sctx->ccm.s390x.kmac.icv.g[1] = 0; + s390x_kmac(sctx->ccm.s390x.nonce.b, 32, sctx->ccm.s390x.fc, + &sctx->ccm.s390x.kmac); + sctx->ccm.s390x.blocks += 2; + + rem = alen & 0xf; + alen &= ~(size_t)0xf; + if (alen) { + s390x_kmac(aad, alen, sctx->ccm.s390x.fc, &sctx->ccm.s390x.kmac); + sctx->ccm.s390x.blocks += alen >> 4; + aad += alen; + } + if (rem) { + for (i = 0; i < rem; i++) + sctx->ccm.s390x.kmac.icv.b[i] ^= aad[i]; + + s390x_km(sctx->ccm.s390x.kmac.icv.b, 16, + sctx->ccm.s390x.kmac.icv.b, sctx->ccm.s390x.fc, + sctx->ccm.s390x.kmac.k); + sctx->ccm.s390x.blocks++; + } + return 1; +} + +/*- + * En/de-crypt plain/cipher-text. Compute tag from plaintext. Returns 1 for + * success. + */ +static int s390x_aes_ccm_auth_encdec(PROV_CCM_CTX *ctx, + const unsigned char *in, + unsigned char *out, size_t len, int enc) +{ + PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx; + size_t n, rem; + unsigned int i, l, num; + unsigned char flags; + + flags = sctx->ccm.s390x.nonce.b[0]; + if (!(flags & S390X_CCM_AAD_FLAG)) { + s390x_km(sctx->ccm.s390x.nonce.b, 16, sctx->ccm.s390x.kmac.icv.b, + sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k); + sctx->ccm.s390x.blocks++; + } + l = flags & 0x7; + sctx->ccm.s390x.nonce.b[0] = l; + + /*- + * Reconstruct length from encoded length field + * and initialize it with counter value. + */ + n = 0; + for (i = 15 - l; i < 15; i++) { + n |= sctx->ccm.s390x.nonce.b[i]; + sctx->ccm.s390x.nonce.b[i] = 0; + n <<= 8; + } + n |= sctx->ccm.s390x.nonce.b[15]; + sctx->ccm.s390x.nonce.b[15] = 1; + + if (n != len) + return 0; /* length mismatch */ + + if (enc) { + /* Two operations per block plus one for tag encryption */ + sctx->ccm.s390x.blocks += (((len + 15) >> 4) << 1) + 1; + if (sctx->ccm.s390x.blocks > (1ULL << 61)) + return 0; /* too much data */ + } + + num = 0; + rem = len & 0xf; + len &= ~(size_t)0xf; + + if (enc) { + /* mac-then-encrypt */ + if (len) + s390x_kmac(in, len, sctx->ccm.s390x.fc, &sctx->ccm.s390x.kmac); + if (rem) { + for (i = 0; i < rem; i++) + sctx->ccm.s390x.kmac.icv.b[i] ^= in[len + i]; + + s390x_km(sctx->ccm.s390x.kmac.icv.b, 16, + sctx->ccm.s390x.kmac.icv.b, + sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k); + } + + CRYPTO_ctr128_encrypt_ctr32(in, out, len + rem, &sctx->ccm.ks.ks, + sctx->ccm.s390x.nonce.b, sctx->ccm.s390x.buf.b, + &num, (ctr128_f)AES_ctr32_encrypt); + } else { + /* decrypt-then-mac */ + CRYPTO_ctr128_encrypt_ctr32(in, out, len + rem, &sctx->ccm.ks.ks, + sctx->ccm.s390x.nonce.b, sctx->ccm.s390x.buf.b, + &num, (ctr128_f)AES_ctr32_encrypt); + + if (len) + s390x_kmac(out, len, sctx->ccm.s390x.fc, &sctx->ccm.s390x.kmac); + if (rem) { + for (i = 0; i < rem; i++) + sctx->ccm.s390x.kmac.icv.b[i] ^= out[len + i]; + + s390x_km(sctx->ccm.s390x.kmac.icv.b, 16, + sctx->ccm.s390x.kmac.icv.b, + sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k); + } + } + /* encrypt tag */ + for (i = 15 - l; i < 16; i++) + sctx->ccm.s390x.nonce.b[i] = 0; + + s390x_km(sctx->ccm.s390x.nonce.b, 16, sctx->ccm.s390x.buf.b, + sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k); + sctx->ccm.s390x.kmac.icv.g[0] ^= sctx->ccm.s390x.buf.g[0]; + sctx->ccm.s390x.kmac.icv.g[1] ^= sctx->ccm.s390x.buf.g[1]; + + sctx->ccm.s390x.nonce.b[0] = flags; /* restore flags field */ + return 1; +} + +static int s390x_aes_ccm_gettag(PROV_CCM_CTX *ctx, + unsigned char *tag, size_t tlen) +{ + PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx; + + if (tlen > ctx->m) + return 0; + memcpy(tag, sctx->ccm.s390x.kmac.icv.b, tlen); + return 1; +} + +static int s390x_aes_ccm_auth_encrypt(PROV_CCM_CTX *ctx, + const unsigned char *in, + unsigned char *out, size_t len, + unsigned char *tag, size_t taglen) +{ + int rv; + + rv = s390x_aes_ccm_auth_encdec(ctx, in, out, len, 1); + if (rv && tag != NULL) + rv = s390x_aes_ccm_gettag(ctx, tag, taglen); + return rv; +} + +static int s390x_aes_ccm_auth_decrypt(PROV_CCM_CTX *ctx, + const unsigned char *in, + unsigned char *out, size_t len, + unsigned char *expected_tag, + size_t taglen) +{ + int rv = 0; + PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx; + + rv = s390x_aes_ccm_auth_encdec(ctx, in, out, len, 0); + if (rv) { + if (CRYPTO_memcmp(sctx->ccm.s390x.kmac.icv.b, expected_tag, ctx->m) != 0) + rv = 0; + } + if (rv == 0) + OPENSSL_cleanse(out, len); + return rv; +} + +static const PROV_CCM_HW s390x_aes_ccm = { + s390x_aes_ccm_initkey, + s390x_aes_ccm_setiv, + s390x_aes_ccm_setaad, + s390x_aes_ccm_auth_encrypt, + s390x_aes_ccm_auth_decrypt, + s390x_aes_ccm_gettag +}; + +const PROV_CCM_HW *ossl_prov_aes_hw_ccm_s390x(size_t keybits) +{ + if ((keybits == 128 && S390X_aes_128_ccm_CAPABLE) + || (keybits == 192 && S390X_aes_192_ccm_CAPABLE) + || (keybits == 256 && S390X_aes_256_ccm_CAPABLE)) + return &s390x_aes_ccm; + return NULL; +} + +#endif + +/* MODES: XTS */ + +#if defined(AES_XTS_S390X) + +int s390x_aes_xts_cipher_stream(PROV_AES_XTS_CTX *xctx, + unsigned char *out, size_t *outl, + const unsigned char *in, size_t inl) +{ + S390X_KM_XTS_PARAMS *km = &xctx->plat.s390x.param.km; + unsigned char *param = (unsigned char *)km + xctx->plat.s390x.offset; + unsigned int fc = xctx->plat.s390x.fc; + unsigned char tmp[2][AES_BLOCK_SIZE]; + unsigned char nap_n1[AES_BLOCK_SIZE]; + unsigned char drop[AES_BLOCK_SIZE]; + size_t len_incomplete, len_complete; + + len_incomplete = inl % AES_BLOCK_SIZE; + len_complete = (len_incomplete == 0) ? inl : (inl / AES_BLOCK_SIZE - 1) * AES_BLOCK_SIZE; + + if (len_complete > 0) + s390x_km(in, len_complete, out, fc, param); + if (len_incomplete == 0) + goto out; + + memcpy(tmp, in + len_complete, AES_BLOCK_SIZE + len_incomplete); + /* swap NAP for decrypt */ + if (fc & S390X_DECRYPT) { + memcpy(nap_n1, km->nap, AES_BLOCK_SIZE); + s390x_km(tmp[0], AES_BLOCK_SIZE, drop, fc, param); + } + s390x_km(tmp[0], AES_BLOCK_SIZE, tmp[0], fc, param); + if (fc & S390X_DECRYPT) + memcpy(km->nap, nap_n1, AES_BLOCK_SIZE); + + memcpy(tmp[1] + len_incomplete, tmp[0] + len_incomplete, + AES_BLOCK_SIZE - len_incomplete); + s390x_km(tmp[1], AES_BLOCK_SIZE, out + len_complete, fc, param); + memcpy(out + len_complete + AES_BLOCK_SIZE, tmp[0], len_incomplete); + + /* do not expose temporary data */ + OPENSSL_cleanse(tmp, sizeof(tmp)); +out: + memcpy(xctx->base.iv, km->tweak, AES_BLOCK_SIZE); + *outl = inl; + + return 1; +} + +static int cipher_hw_aes_xts_s390x_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx; + S390X_KM_XTS_PARAMS *km = &xctx->plat.s390x.param.km; + unsigned int fc, offs; + unsigned int dec = 0; + int supported = 0; + + switch (keylen) { + case 128 / 8 * 2: + fc = S390X_XTS_AES_128_MSA10; + offs = 32; + break; + case 256 / 8 * 2: + fc = S390X_XTS_AES_256_MSA10; + offs = 0; + break; + default: + fc = 0; + break; + } + + if (fc != 0) + supported = (OPENSSL_s390xcap_P.km[1] && S390X_CAPBIT(fc)); + if (!supported) { + xctx->plat.s390x.fc = 0; + xctx->plat.s390x.offset = 0; + return 0; + } + + if (xctx->base.iv_set) { + if (xctx->base.ivlen > sizeof(km->tweak)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); + return 0; + } + memcpy(km->tweak, xctx->base.iv, xctx->base.ivlen); + xctx->plat.s390x.iv_set = 1; + } + + if (key != NULL) { + memcpy(km->key + offs, key, keylen); + xctx->plat.s390x.key_set = 1; + } + + if (xctx->base.enc == 0) + dec = S390X_DECRYPT; + + xctx->plat.s390x.fc = fc | dec; + xctx->plat.s390x.offset = offs; + + memset(km->nap, 0, sizeof(km->nap)); + km->nap[0] = 0x1; + + return 1; +} + +static void cipher_hw_aes_xts_s390x_copyctx(PROV_CIPHER_CTX *dst, + const PROV_CIPHER_CTX *src) +{ + PROV_AES_XTS_CTX *sctx = (PROV_AES_XTS_CTX *)src; + PROV_AES_XTS_CTX *dctx = (PROV_AES_XTS_CTX *)dst; + + *dctx = *sctx; + dctx->xts.key1 = NULL; + dctx->xts.key2 = NULL; +} + +static const PROV_CIPHER_HW aes_xts_s390x = { + cipher_hw_aes_xts_s390x_initkey, + NULL, + cipher_hw_aes_xts_s390x_copyctx +}; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_s390x(size_t keybits) +{ + switch (keybits) { + case (128 * 2): + if (OPENSSL_s390xcap_P.km[1] && S390X_CAPBIT(S390X_XTS_AES_128_MSA10)) + return &aes_xts_s390x; + break; + case (256 * 2): + if (OPENSSL_s390xcap_P.km[1] && S390X_CAPBIT(S390X_XTS_AES_256_MSA10)) + return &aes_xts_s390x; + break; + default: + break; + } + + return NULL; +} + #endif diff --git a/providers/implementations/ciphers/cipher_aes_xts.h b/providers/implementations/ciphers/cipher_aes_xts.h index 5a626144e0..9fa0624dfc 100644 --- a/providers/implementations/ciphers/cipher_aes_xts.h +++ b/providers/implementations/ciphers/cipher_aes_xts.h @@ -86,6 +86,7 @@ const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_rv64i(void); int s390x_aes_xts_cipher_stream(PROV_AES_XTS_CTX *xctx, unsigned char *out, size_t *outl, const unsigned char *in, size_t inl); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_s390x(size_t keybits); #endif const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts(size_t keybits); diff --git a/providers/implementations/ciphers/cipher_aes_xts_hw.c b/providers/implementations/ciphers/cipher_aes_xts_hw.c index 537786a7aa..e584f12051 100644 --- a/providers/implementations/ciphers/cipher_aes_xts_hw.c +++ b/providers/implementations/ciphers/cipher_aes_xts_hw.c @@ -142,144 +142,6 @@ static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_t4() return NULL; } -#elif defined(AES_XTS_S390X) - -int s390x_aes_xts_cipher_stream(PROV_AES_XTS_CTX *xctx, - unsigned char *out, size_t *outl, - const unsigned char *in, size_t inl) -{ - S390X_KM_XTS_PARAMS *km = &xctx->plat.s390x.param.km; - unsigned char *param = (unsigned char *)km + xctx->plat.s390x.offset; - unsigned int fc = xctx->plat.s390x.fc; - unsigned char tmp[2][AES_BLOCK_SIZE]; - unsigned char nap_n1[AES_BLOCK_SIZE]; - unsigned char drop[AES_BLOCK_SIZE]; - size_t len_incomplete, len_complete; - - len_incomplete = inl % AES_BLOCK_SIZE; - len_complete = (len_incomplete == 0) ? inl : (inl / AES_BLOCK_SIZE - 1) * AES_BLOCK_SIZE; - - if (len_complete > 0) - s390x_km(in, len_complete, out, fc, param); - if (len_incomplete == 0) - goto out; - - memcpy(tmp, in + len_complete, AES_BLOCK_SIZE + len_incomplete); - /* swap NAP for decrypt */ - if (fc & S390X_DECRYPT) { - memcpy(nap_n1, km->nap, AES_BLOCK_SIZE); - s390x_km(tmp[0], AES_BLOCK_SIZE, drop, fc, param); - } - s390x_km(tmp[0], AES_BLOCK_SIZE, tmp[0], fc, param); - if (fc & S390X_DECRYPT) - memcpy(km->nap, nap_n1, AES_BLOCK_SIZE); - - memcpy(tmp[1] + len_incomplete, tmp[0] + len_incomplete, - AES_BLOCK_SIZE - len_incomplete); - s390x_km(tmp[1], AES_BLOCK_SIZE, out + len_complete, fc, param); - memcpy(out + len_complete + AES_BLOCK_SIZE, tmp[0], len_incomplete); - - /* do not expose temporary data */ - OPENSSL_cleanse(tmp, sizeof(tmp)); -out: - memcpy(xctx->base.iv, km->tweak, AES_BLOCK_SIZE); - *outl = inl; - - return 1; -} - -static int cipher_hw_aes_xts_s390x_initkey(PROV_CIPHER_CTX *ctx, - const unsigned char *key, size_t keylen) -{ - PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx; - S390X_KM_XTS_PARAMS *km = &xctx->plat.s390x.param.km; - unsigned int fc, offs; - unsigned int dec = 0; - int supported = 0; - - switch (keylen) { - case 128 / 8 * 2: - fc = S390X_XTS_AES_128_MSA10; - offs = 32; - break; - case 256 / 8 * 2: - fc = S390X_XTS_AES_256_MSA10; - offs = 0; - break; - default: - fc = 0; - break; - } - - if (fc != 0) - supported = (OPENSSL_s390xcap_P.km[1] && S390X_CAPBIT(fc)); - if (!supported) { - xctx->plat.s390x.fc = 0; - xctx->plat.s390x.offset = 0; - return 0; - } - - if (xctx->base.iv_set) { - if (xctx->base.ivlen > sizeof(km->tweak)) { - ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); - return 0; - } - memcpy(km->tweak, xctx->base.iv, xctx->base.ivlen); - xctx->plat.s390x.iv_set = 1; - } - - if (key != NULL) { - memcpy(km->key + offs, key, keylen); - xctx->plat.s390x.key_set = 1; - } - - if (xctx->base.enc == 0) - dec = S390X_DECRYPT; - - xctx->plat.s390x.fc = fc | dec; - xctx->plat.s390x.offset = offs; - - memset(km->nap, 0, sizeof(km->nap)); - km->nap[0] = 0x1; - - return 1; -} - -static void cipher_hw_aes_xts_s390x_copyctx(PROV_CIPHER_CTX *dst, - const PROV_CIPHER_CTX *src) -{ - PROV_AES_XTS_CTX *sctx = (PROV_AES_XTS_CTX *)src; - PROV_AES_XTS_CTX *dctx = (PROV_AES_XTS_CTX *)dst; - - *dctx = *sctx; - dctx->xts.key1 = NULL; - dctx->xts.key2 = NULL; -} - -static const PROV_CIPHER_HW aes_xts_s390x = { - cipher_hw_aes_xts_s390x_initkey, - NULL, - cipher_hw_aes_xts_s390x_copyctx -}; - -static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_s390x(size_t keybits) -{ - switch (keybits) { - case (128 * 2): - if (OPENSSL_s390xcap_P.km[1] && S390X_CAPBIT(S390X_XTS_AES_128_MSA10)) - return &aes_xts_s390x; - break; - case (256 * 2): - if (OPENSSL_s390xcap_P.km[1] && S390X_CAPBIT(S390X_XTS_AES_256_MSA10)) - return &aes_xts_s390x; - break; - default: - break; - } - - return NULL; -} - #endif static const PROV_CIPHER_HW aes_generic_xts = { From f7e4eb281dfcf01b19a6fb891d0432f5af898720 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Thu, 11 Jun 2026 19:19:19 -0400 Subject: [PATCH 098/349] Consolidate SPARC T4 AES implementations Move SPARC T4-specific AES hardware implementations (GCM, CCM, and XTS) from individual mode-specific files into a single unified file (cipher_aes_hw_t4.c). This consolidates the architecture-specific logic to improve code organization and maintainability. Additionally, this change removes the unused `keybits` parameter from several SPARC T4 hardware initialization routines to clean up the internal API. Signed-off-by: Simo Sorce Reviewed-by: Dmitry Belyavskiy Reviewed-by: Norbert Pocs Reviewed-by: Shane Lontis MergeDate: Sat Jun 27 09:06:01 2026 (Merged from https://github.com/openssl/openssl/pull/31472) --- providers/implementations/ciphers/build.info | 2 - .../implementations/ciphers/cipher_aes.h | 3 +- .../implementations/ciphers/cipher_aes_ccm.h | 2 +- .../ciphers/cipher_aes_ccm_hw.c | 2 +- .../ciphers/cipher_aes_ccm_hw_t4.c | 42 ------- .../implementations/ciphers/cipher_aes_gcm.h | 2 +- .../ciphers/cipher_aes_gcm_hw.c | 2 +- .../ciphers/cipher_aes_gcm_hw_t4.c | 54 -------- .../implementations/ciphers/cipher_aes_hw.c | 2 +- .../ciphers/cipher_aes_hw_t4.c | 116 +++++++++++++++++- .../implementations/ciphers/cipher_aes_xts.h | 4 + .../ciphers/cipher_aes_xts_hw.c | 42 ------- 12 files changed, 121 insertions(+), 152 deletions(-) delete mode 100644 providers/implementations/ciphers/cipher_aes_ccm_hw_t4.c delete mode 100644 providers/implementations/ciphers/cipher_aes_gcm_hw_t4.c diff --git a/providers/implementations/ciphers/build.info b/providers/implementations/ciphers/build.info index f82cae6762..2460b0183d 100644 --- a/providers/implementations/ciphers/build.info +++ b/providers/implementations/ciphers/build.info @@ -109,9 +109,7 @@ SOURCE[$AES_GOAL]=\ cipher_aes_xts.c cipher_aes_xts_hw.c \ cipher_aes_gcm.c cipher_aes_gcm_hw.c \ cipher_aes_gcm_hw_ppc.c \ - cipher_aes_gcm_hw_t4.c \ cipher_aes_ccm.c cipher_aes_ccm_hw.c \ - cipher_aes_ccm_hw_t4.c \ cipher_aes_wrp.c \ cipher_aes_cbc_hmac_sha.c \ cipher_aes_cbc_hmac_sha256_hw.c cipher_aes_cbc_hmac_sha1_hw.c \ diff --git a/providers/implementations/ciphers/cipher_aes.h b/providers/implementations/ciphers/cipher_aes.h index 05e46d204a..4e71678fd1 100644 --- a/providers/implementations/ciphers/cipher_aes.h +++ b/providers/implementations/ciphers/cipher_aes.h @@ -97,8 +97,7 @@ const PROV_CIPHER_HW *ossl_prov_cipher_hw_rv64i(enum aes_modes mode); const PROV_CIPHER_HW *ossl_prov_cipher_hw_s390x(enum aes_modes mode, size_t keybits); #elif defined(SPARC_AES_CAPABLE) -const PROV_CIPHER_HW *ossl_prov_cipher_hw_t4(enum aes_modes mode, - size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_t4(enum aes_modes mode); #endif #endif /* !defined(OSSL_PROVIDERS_IMPLEMENTATIONS_CIPHERS_CIPHER_AES_H) */ diff --git a/providers/implementations/ciphers/cipher_aes_ccm.h b/providers/implementations/ciphers/cipher_aes_ccm.h index 636f5e687b..9fe8ba2e07 100644 --- a/providers/implementations/ciphers/cipher_aes_ccm.h +++ b/providers/implementations/ciphers/cipher_aes_ccm.h @@ -68,7 +68,7 @@ const PROV_CCM_HW *ossl_prov_aes_hw_ccm_rv64i(void); const PROV_CCM_HW *ossl_prov_aes_hw_ccm_s390x(size_t keybits); #endif #if defined(SPARC_AES_CAPABLE) -const PROV_CCM_HW *ossl_prov_aes_hw_ccm_t4(size_t keybits); +const PROV_CCM_HW *ossl_prov_aes_hw_ccm_t4(void); #endif #endif /* !defined(OSSL_PROVIDERS_IMPLEMENTATIONS_CIPHERS_CIPHER_AES_CCM_H) */ diff --git a/providers/implementations/ciphers/cipher_aes_ccm_hw.c b/providers/implementations/ciphers/cipher_aes_ccm_hw.c index a3ee92c542..6387157740 100644 --- a/providers/implementations/ciphers/cipher_aes_ccm_hw.c +++ b/providers/implementations/ciphers/cipher_aes_ccm_hw.c @@ -81,7 +81,7 @@ const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keybits) #elif defined(S390X_aes_128_CAPABLE) aes_ccm_hw = ossl_prov_aes_hw_ccm_s390x(keybits); #elif defined(SPARC_AES_CAPABLE) - aes_ccm_hw = ossl_prov_aes_hw_ccm_t4(keybits); + aes_ccm_hw = ossl_prov_aes_hw_ccm_t4(); #endif if (aes_ccm_hw != NULL) return aes_ccm_hw; diff --git a/providers/implementations/ciphers/cipher_aes_ccm_hw_t4.c b/providers/implementations/ciphers/cipher_aes_ccm_hw_t4.c deleted file mode 100644 index 981773760f..0000000000 --- a/providers/implementations/ciphers/cipher_aes_ccm_hw_t4.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2001-2021 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/*- - * Fujitsu SPARC64 X support for AES CCM. - * This file is used by cipher_aes_ccm_hw.c - */ - -#include "internal/deprecated.h" -#include "cipher_aes_ccm.h" - -#if defined(SPARC_AES_CAPABLE) - -static int ccm_t4_aes_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, - size_t keylen) -{ - return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, - aes_t4_set_encrypt_key, aes_t4_encrypt, NULL, NULL); -} - -static const PROV_CCM_HW t4_aes_ccm = { - ccm_t4_aes_initkey, - ossl_ccm_generic_setiv, - ossl_ccm_generic_setaad, - ossl_ccm_generic_auth_encrypt, - ossl_ccm_generic_auth_decrypt, - ossl_ccm_generic_gettag -}; - -const PROV_CCM_HW *ossl_prov_aes_hw_ccm_t4(size_t keybits) -{ - if (SPARC_AES_CAPABLE) - return &t4_aes_ccm; - return NULL; -} -#endif diff --git a/providers/implementations/ciphers/cipher_aes_gcm.h b/providers/implementations/ciphers/cipher_aes_gcm.h index 43cba79541..210c1de97a 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm.h +++ b/providers/implementations/ciphers/cipher_aes_gcm.h @@ -72,7 +72,7 @@ const PROV_GCM_HW *ossl_prov_aes_hw_gcm_rv64i(void); const PROV_GCM_HW *ossl_prov_aes_hw_gcm_s390x(size_t keybits); #endif #if defined(SPARC_AES_CAPABLE) -const PROV_GCM_HW *ossl_prov_aes_hw_gcm_t4(size_t keybits); +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_t4(void); #endif #endif /* !defined(OSSL_PROVIDERS_IMPLEMENTATIONS_CIPHERS_CIPHER_AES_GCM_H) */ diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw.c b/providers/implementations/ciphers/cipher_aes_gcm_hw.c index 8493451688..2bd1cb9d34 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw.c +++ b/providers/implementations/ciphers/cipher_aes_gcm_hw.c @@ -168,7 +168,7 @@ const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits) #elif defined(S390X_aes_128_CAPABLE) aes_gcm_hw = ossl_prov_aes_hw_gcm_s390x(keybits); #elif defined(SPARC_AES_CAPABLE) - aes_gcm_hw = ossl_prov_aes_hw_gcm_t4(keybits); + aes_gcm_hw = ossl_prov_aes_hw_gcm_t4(); #endif if (aes_gcm_hw == NULL) diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw_t4.c b/providers/implementations/ciphers/cipher_aes_gcm_hw_t4.c deleted file mode 100644 index 4b6968581d..0000000000 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw_t4.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2001-2021 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/*- - * Fujitsu SPARC64 X support for AES GCM. - * This file is used by cipher_aes_gcm_hw.c - */ -#include "internal/deprecated.h" -#include "cipher_aes_gcm.h" - -#if defined(SPARC_AES_CAPABLE) - -static int t4_aes_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, - size_t keylen) -{ - switch (keylen) { - case 16: - return aes_gcm_hw_initkey(ctx, key, keylen, - aes_t4_set_encrypt_key, aes_t4_encrypt, - (ctr128_f)aes128_t4_ctr32_encrypt); - case 24: - return aes_gcm_hw_initkey(ctx, key, keylen, - aes_t4_set_encrypt_key, aes_t4_encrypt, - (ctr128_f)aes192_t4_ctr32_encrypt); - case 32: - return aes_gcm_hw_initkey(ctx, key, keylen, - aes_t4_set_encrypt_key, aes_t4_encrypt, - (ctr128_f)aes256_t4_ctr32_encrypt); - default: - return 0; - } -} - -static const PROV_GCM_HW t4_aes_gcm = { - t4_aes_gcm_initkey, - ossl_gcm_setiv, - ossl_gcm_aad_update, - generic_aes_gcm_cipher_update, - ossl_gcm_cipher_final, - ossl_gcm_one_shot -}; - -const PROV_GCM_HW *ossl_prov_aes_hw_gcm_t4(size_t keybits) -{ - return SPARC_AES_CAPABLE ? &t4_aes_gcm : NULL; -} - -#endif diff --git a/providers/implementations/ciphers/cipher_aes_hw.c b/providers/implementations/ciphers/cipher_aes_hw.c index 294b2dea7d..b282393d5b 100644 --- a/providers/implementations/ciphers/cipher_aes_hw.c +++ b/providers/implementations/ciphers/cipher_aes_hw.c @@ -227,7 +227,7 @@ static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_mode(enum aes_modes mode, #elif defined(S390X_aes_128_CAPABLE) aes_hw_mode = ossl_prov_cipher_hw_s390x(mode, keybits); #elif defined(SPARC_AES_CAPABLE) - aes_hw_mode = ossl_prov_cipher_hw_t4(mode, keybits); + aes_hw_mode = ossl_prov_cipher_hw_t4(mode); #endif if (aes_hw_mode == NULL) { diff --git a/providers/implementations/ciphers/cipher_aes_hw_t4.c b/providers/implementations/ciphers/cipher_aes_hw_t4.c index 6d8e945de5..c9549be90b 100644 --- a/providers/implementations/ciphers/cipher_aes_hw_t4.c +++ b/providers/implementations/ciphers/cipher_aes_hw_t4.c @@ -1,5 +1,5 @@ /* - * Copyright 2001-2021 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2001-2026 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -8,16 +8,20 @@ */ /*- - * Sparc t4 support for AES modes ecb, cbc, ofb, cfb, ctr. - * This file is used by cipher_aes_hw.c + * Sparc t4 support for all hardware accelerated AES modes. */ #include "internal/deprecated.h" #include #include "cipher_aes.h" +#include "cipher_aes_gcm.h" +#include "cipher_aes_ccm.h" +#include "cipher_aes_xts.h" #if defined(SPARC_AES_CAPABLE) +/* MODES: ecb, cbc, cfb, ofb, ctr */ + static int t4_set_encrypt_key(const unsigned char *key, int bits, AES_KEY *ks) { aes_t4_set_encrypt_key(key, bits, ks); @@ -118,8 +122,7 @@ static const PROV_CIPHER_HW aes_t4_ctr = { ossl_cipher_aes_copyctx }; -const PROV_CIPHER_HW *ossl_prov_cipher_hw_t4(enum aes_modes mode, - size_t keybits) +const PROV_CIPHER_HW *ossl_prov_cipher_hw_t4(enum aes_modes mode) { if (SPARC_AES_CAPABLE) { switch (mode) { @@ -143,4 +146,107 @@ const PROV_CIPHER_HW *ossl_prov_cipher_hw_t4(enum aes_modes mode, } return NULL; } + +/* MODES: GCM */ + +static int t4_aes_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + switch (keylen) { + case 16: + return aes_gcm_hw_initkey(ctx, key, keylen, + t4_set_encrypt_key, aes_t4_encrypt, + (ctr128_f)aes128_t4_ctr32_encrypt); + case 24: + return aes_gcm_hw_initkey(ctx, key, keylen, + t4_set_encrypt_key, aes_t4_encrypt, + (ctr128_f)aes192_t4_ctr32_encrypt); + case 32: + return aes_gcm_hw_initkey(ctx, key, keylen, + t4_set_encrypt_key, aes_t4_encrypt, + (ctr128_f)aes256_t4_ctr32_encrypt); + default: + return 0; + } +} + +static const PROV_GCM_HW t4_aes_gcm = { + t4_aes_gcm_initkey, + ossl_gcm_setiv, + ossl_gcm_aad_update, + generic_aes_gcm_cipher_update, + ossl_gcm_cipher_final, + ossl_gcm_one_shot +}; + +const PROV_GCM_HW *ossl_prov_aes_hw_gcm_t4(void) +{ + return SPARC_AES_CAPABLE ? &t4_aes_gcm : NULL; +} + +/* MODES: CCM */ + +static int ccm_t4_aes_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + return ossl_cipher_set_ccm_aes_initkey(ctx, key, keylen, + t4_set_encrypt_key, aes_t4_encrypt, NULL, NULL); +} + +static const PROV_CCM_HW t4_aes_ccm = { + ccm_t4_aes_initkey, + ossl_ccm_generic_setiv, + ossl_ccm_generic_setaad, + ossl_ccm_generic_auth_encrypt, + ossl_ccm_generic_auth_decrypt, + ossl_ccm_generic_gettag +}; + +const PROV_CCM_HW *ossl_prov_aes_hw_ccm_t4(void) +{ + if (SPARC_AES_CAPABLE) + return &t4_aes_ccm; + return NULL; +} + +/* MODES: XTS */ + +static int cipher_hw_aes_xts_t4_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + OSSL_xts_stream_fn stream_enc = NULL; + OSSL_xts_stream_fn stream_dec = NULL; + + /* Note: keylen is the size of 2 keys */ + switch (keylen) { + case 32: + stream_enc = aes128_t4_xts_encrypt; + stream_dec = aes128_t4_xts_decrypt; + break; + case 64: + stream_enc = aes256_t4_xts_encrypt; + stream_dec = aes256_t4_xts_decrypt; + break; + default: + return 0; + } + + return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, + t4_set_encrypt_key, t4_set_decrypt_key, + aes_t4_encrypt, aes_t4_decrypt, stream_enc, stream_dec); +} + +static const PROV_CIPHER_HW aes_xts_t4 = { + cipher_hw_aes_xts_t4_initkey, + NULL, + ossl_cipher_hw_aes_xts_copyctx +}; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_t4(void) +{ + if (SPARC_AES_CAPABLE) + return &aes_xts_t4; + return NULL; +} + #endif diff --git a/providers/implementations/ciphers/cipher_aes_xts.h b/providers/implementations/ciphers/cipher_aes_xts.h index 9fa0624dfc..49ee66ac88 100644 --- a/providers/implementations/ciphers/cipher_aes_xts.h +++ b/providers/implementations/ciphers/cipher_aes_xts.h @@ -89,6 +89,10 @@ int s390x_aes_xts_cipher_stream(PROV_AES_XTS_CTX *xctx, const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_s390x(size_t keybits); #endif +#if defined(SPARC_AES_CAPABLE) +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_t4(void); +#endif + const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts(size_t keybits); #endif /* !defined(OSSL_PROVIDERS_IMPLEMENTATIONS_CIPHERS_CIPHER_AES_XTS_H) */ diff --git a/providers/implementations/ciphers/cipher_aes_xts_hw.c b/providers/implementations/ciphers/cipher_aes_xts_hw.c index e584f12051..162f8e4593 100644 --- a/providers/implementations/ciphers/cipher_aes_xts_hw.c +++ b/providers/implementations/ciphers/cipher_aes_xts_hw.c @@ -102,48 +102,6 @@ void ossl_cipher_hw_aes_xts_copyctx(PROV_CIPHER_CTX *dst, dctx->xts.key2 = &dctx->ks2.ks; } -#if defined(SPARC_AES_CAPABLE) - -static int cipher_hw_aes_xts_t4_initkey(PROV_CIPHER_CTX *ctx, - const unsigned char *key, size_t keylen) -{ - OSSL_xts_stream_fn stream_enc = NULL; - OSSL_xts_stream_fn stream_dec = NULL; - - /* Note: keylen is the size of 2 keys */ - switch (keylen) { - case 32: - stream_enc = aes128_t4_xts_encrypt; - stream_dec = aes128_t4_xts_decrypt; - break; - case 64: - stream_enc = aes256_t4_xts_encrypt; - stream_dec = aes256_t4_xts_decrypt; - break; - default: - return 0; - } - - return ossl_cipher_set_aes_xts_initkey(ctx, key, keylen, - aes_t4_set_encrypt_key, aes_t4_set_decrypt_key, - aes_t4_encrypt, aes_t4_decrypt, stream_enc, stream_dec); -} - -static const PROV_CIPHER_HW aes_xts_t4 = { - cipher_hw_aes_xts_t4_initkey, - NULL, - ossl_cipher_hw_aes_xts_copyctx -}; - -static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts_t4() -{ - if (SPARC_AES_CAPABLE) - return &aes_xts_t4; - return NULL; -} - -#endif - static const PROV_CIPHER_HW aes_generic_xts = { cipher_hw_aes_xts_generic_initkey, NULL, From 48a32190509a5e38671091062fad8ed85d01bce6 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Mon, 15 Jun 2026 09:51:16 -0400 Subject: [PATCH 099/349] Prefix internal AES-GCM functions with ossl_ The `aes_gcm_hw_initkey` and `generic_aes_gcm_cipher_update` functions are shared across multiple hardware-specific provider implementations but lacked the internal `ossl_` prefix. They have been renamed to `ossl_aes_gcm_hw_initkey` and `ossl_generic_aes_gcm_cipher_update` respectively to follow OpenSSL naming conventions and prevent potential namespace collisions. Signed-off-by: Simo Sorce Reviewed-by: Dmitry Belyavskiy Reviewed-by: Norbert Pocs Reviewed-by: Shane Lontis MergeDate: Sat Jun 27 09:06:03 2026 (Merged from https://github.com/openssl/openssl/pull/31472) --- .../implementations/ciphers/cipher_aes_gcm.h | 6 +++--- .../ciphers/cipher_aes_gcm_hw.c | 20 +++++++++---------- .../ciphers/cipher_aes_gcm_hw_ppc.c | 2 +- .../ciphers/cipher_aes_hw_aesni.c | 4 ++-- .../ciphers/cipher_aes_hw_armv8.c | 6 +++--- .../ciphers/cipher_aes_hw_rv32i.c | 6 +++--- .../ciphers/cipher_aes_hw_rv64i.c | 16 +++++++-------- .../ciphers/cipher_aes_hw_t4.c | 8 ++++---- 8 files changed, 34 insertions(+), 34 deletions(-) diff --git a/providers/implementations/ciphers/cipher_aes_gcm.h b/providers/implementations/ciphers/cipher_aes_gcm.h index 210c1de97a..b041248b3c 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm.h +++ b/providers/implementations/ciphers/cipher_aes_gcm.h @@ -45,12 +45,12 @@ typedef struct prov_aes_gcm_ctx_st { } plat; } PROV_AES_GCM_CTX; -int aes_gcm_hw_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, +int ossl_aes_gcm_hw_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, size_t keylen, aes_set_encrypt_key_fn fn_set_key, aes_block128_f fn_block, ctr128_f fn_ctr); -int generic_aes_gcm_cipher_update(PROV_GCM_CTX *ctx, const unsigned char *in, - size_t len, unsigned char *out); +int ossl_generic_aes_gcm_cipher_update(PROV_GCM_CTX *ctx, + const unsigned char *in, size_t len, unsigned char *out); const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits); #if defined(AESNI_CAPABLE) diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw.c b/providers/implementations/ciphers/cipher_aes_gcm_hw.c index 2bd1cb9d34..bbb7e21c31 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw.c +++ b/providers/implementations/ciphers/cipher_aes_gcm_hw.c @@ -17,7 +17,7 @@ #include #include "cipher_aes_gcm.h" -int aes_gcm_hw_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, +int ossl_aes_gcm_hw_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, size_t keylen, aes_set_encrypt_key_fn fn_set_key, aes_block128_f fn_block, ctr128_f fn_ctr) { @@ -43,10 +43,10 @@ static int aes_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, #ifdef HWAES_CAPABLE if (HWAES_CAPABLE) { #ifdef HWAES_ctr32_encrypt_blocks - return aes_gcm_hw_initkey(ctx, key, keylen, HWAES_set_encrypt_key, + return ossl_aes_gcm_hw_initkey(ctx, key, keylen, HWAES_set_encrypt_key, HWAES_encrypt, HWAES_ctr32_encrypt_blocks); #else - return aes_gcm_hw_initkey(ctx, key, keylen, HWAES_set_encrypt_key, + return ossl_aes_gcm_hw_initkey(ctx, key, keylen, HWAES_set_encrypt_key, HWAES_encrypt, NULL); #endif /* HWAES_ctr32_encrypt_blocks */ } else @@ -54,31 +54,31 @@ static int aes_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, #ifdef BSAES_CAPABLE if (BSAES_CAPABLE) { - return aes_gcm_hw_initkey(ctx, key, keylen, AES_set_encrypt_key, + return ossl_aes_gcm_hw_initkey(ctx, key, keylen, AES_set_encrypt_key, AES_encrypt, (ctr128_f)ossl_bsaes_ctr32_encrypt_blocks); } else #endif /* BSAES_CAPABLE */ #ifdef VPAES_CAPABLE if (VPAES_CAPABLE) { - return aes_gcm_hw_initkey(ctx, key, keylen, vpaes_set_encrypt_key, + return ossl_aes_gcm_hw_initkey(ctx, key, keylen, vpaes_set_encrypt_key, vpaes_encrypt, NULL); } else #endif /* VPAES_CAPABLE */ { #ifdef AES_CTR_ASM - return aes_gcm_hw_initkey(ctx, key, keylen, AES_set_encrypt_key, + return ossl_aes_gcm_hw_initkey(ctx, key, keylen, AES_set_encrypt_key, AES_encrypt, (ctr128_f)AES_ctr32_encrypt); #else - return aes_gcm_hw_initkey(ctx, key, keylen, AES_set_encrypt_key, + return ossl_aes_gcm_hw_initkey(ctx, key, keylen, AES_set_encrypt_key, AES_encrypt, NULL); #endif /* AES_CTR_ASM */ } } -int generic_aes_gcm_cipher_update(PROV_GCM_CTX *ctx, const unsigned char *in, - size_t len, unsigned char *out) +int ossl_generic_aes_gcm_cipher_update(PROV_GCM_CTX *ctx, + const unsigned char *in, size_t len, unsigned char *out) { if (ctx->enc) { if (ctx->ctr != NULL) { @@ -146,7 +146,7 @@ static const PROV_GCM_HW aes_gcm = { aes_gcm_initkey, ossl_gcm_setiv, ossl_gcm_aad_update, - generic_aes_gcm_cipher_update, + ossl_generic_aes_gcm_cipher_update, ossl_gcm_cipher_final, ossl_gcm_one_shot }; diff --git a/providers/implementations/ciphers/cipher_aes_gcm_hw_ppc.c b/providers/implementations/ciphers/cipher_aes_gcm_hw_ppc.c index c605cb0072..c5d8d27c94 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm_hw_ppc.c +++ b/providers/implementations/ciphers/cipher_aes_gcm_hw_ppc.c @@ -19,7 +19,7 @@ static int aes_ppc_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, size_t keylen) { - return aes_gcm_hw_initkey(ctx, key, keylen, aes_p8_set_encrypt_key, + return ossl_aes_gcm_hw_initkey(ctx, key, keylen, aes_p8_set_encrypt_key, aes_p8_encrypt, aes_p8_ctr32_encrypt_blocks); } diff --git a/providers/implementations/ciphers/cipher_aes_hw_aesni.c b/providers/implementations/ciphers/cipher_aes_hw_aesni.c index 15e2d71b3e..f8ae45129b 100644 --- a/providers/implementations/ciphers/cipher_aes_hw_aesni.c +++ b/providers/implementations/ciphers/cipher_aes_hw_aesni.c @@ -173,7 +173,7 @@ const PROV_CIPHER_HW *ossl_prov_cipher_hw_aesni(enum aes_modes mode) static int aesni_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, size_t keylen) { - return aes_gcm_hw_initkey(ctx, key, keylen, aesni_set_encrypt_key, + return ossl_aes_gcm_hw_initkey(ctx, key, keylen, aesni_set_encrypt_key, aesni_encrypt, aesni_ctr32_encrypt_blocks); } @@ -181,7 +181,7 @@ static const PROV_GCM_HW aesni_gcm = { aesni_gcm_initkey, ossl_gcm_setiv, ossl_gcm_aad_update, - generic_aes_gcm_cipher_update, + ossl_generic_aes_gcm_cipher_update, ossl_gcm_cipher_final, ossl_gcm_one_shot }; diff --git a/providers/implementations/ciphers/cipher_aes_hw_armv8.c b/providers/implementations/ciphers/cipher_aes_hw_armv8.c index da4f9fd0e3..673206b6d2 100644 --- a/providers/implementations/ciphers/cipher_aes_hw_armv8.c +++ b/providers/implementations/ciphers/cipher_aes_hw_armv8.c @@ -118,11 +118,11 @@ static int armv8_aes_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, size_t keylen) { if (AES_UNROLL12_EOR3_CAPABLE) { - return aes_gcm_hw_initkey(ctx, key, keylen, + return ossl_aes_gcm_hw_initkey(ctx, key, keylen, aes_v8_set_encrypt_key, aes_v8_encrypt, aes_v8_ctr32_encrypt_blocks_unroll12_eor3); } else { - return aes_gcm_hw_initkey(ctx, key, keylen, + return ossl_aes_gcm_hw_initkey(ctx, key, keylen, aes_v8_set_encrypt_key, aes_v8_encrypt, aes_v8_ctr32_encrypt_blocks); } @@ -132,7 +132,7 @@ static const PROV_GCM_HW armv8_aes_gcm = { armv8_aes_gcm_initkey, ossl_gcm_setiv, ossl_gcm_aad_update, - generic_aes_gcm_cipher_update, + ossl_generic_aes_gcm_cipher_update, ossl_gcm_cipher_final, ossl_gcm_one_shot }; diff --git a/providers/implementations/ciphers/cipher_aes_hw_rv32i.c b/providers/implementations/ciphers/cipher_aes_hw_rv32i.c index 237f6fc20e..eeb26a8948 100644 --- a/providers/implementations/ciphers/cipher_aes_hw_rv32i.c +++ b/providers/implementations/ciphers/cipher_aes_hw_rv32i.c @@ -120,10 +120,10 @@ static int aes_gcm_rv32i_initkey(PROV_GCM_CTX *ctx, size_t keylen) { if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE()) { - return aes_gcm_hw_initkey(ctx, key, keylen, + return ossl_aes_gcm_hw_initkey(ctx, key, keylen, rv32i_zbkb_zkne_set_encrypt_key, rv32i_zkne_encrypt, NULL); } else if (RISCV_HAS_ZKND_AND_ZKNE()) { - return aes_gcm_hw_initkey(ctx, key, keylen, + return ossl_aes_gcm_hw_initkey(ctx, key, keylen, rv32i_zkne_set_encrypt_key, rv32i_zkne_encrypt, NULL); } return 0; @@ -133,7 +133,7 @@ static const PROV_GCM_HW aes_gcm_rv32i = { aes_gcm_rv32i_initkey, ossl_gcm_setiv, ossl_gcm_aad_update, - generic_aes_gcm_cipher_update, + ossl_generic_aes_gcm_cipher_update, ossl_gcm_cipher_final, ossl_gcm_one_shot }; diff --git a/providers/implementations/ciphers/cipher_aes_hw_rv64i.c b/providers/implementations/ciphers/cipher_aes_hw_rv64i.c index ba51447c42..fa1e3f2b2a 100644 --- a/providers/implementations/ciphers/cipher_aes_hw_rv64i.c +++ b/providers/implementations/ciphers/cipher_aes_hw_rv64i.c @@ -149,7 +149,7 @@ const PROV_CIPHER_HW *ossl_prov_cipher_hw_rv64i(enum aes_modes mode) static int rv64i_zknd_zkne_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, size_t keylen) { - return aes_gcm_hw_initkey(ctx, key, keylen, + return ossl_aes_gcm_hw_initkey(ctx, key, keylen, rv64i_zkne_set_encrypt_key, rv64i_zkne_encrypt, NULL); } @@ -157,7 +157,7 @@ static const PROV_GCM_HW rv64i_zknd_zkne_gcm = { rv64i_zknd_zkne_gcm_initkey, ossl_gcm_setiv, ossl_gcm_aad_update, - generic_aes_gcm_cipher_update, + ossl_generic_aes_gcm_cipher_update, ossl_gcm_cipher_final, ossl_gcm_one_shot }; @@ -173,10 +173,10 @@ static int rv64i_zvkned_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, * For AES-192 case, we could fallback to `AES_set_encrypt_key`. */ if (zvkned_key_schedule_supported(keylen)) { - return aes_gcm_hw_initkey(ctx, key, keylen, + return ossl_aes_gcm_hw_initkey(ctx, key, keylen, rv64i_zvkned_set_encrypt_key, rv64i_zvkned_encrypt, NULL); } else { - return aes_gcm_hw_initkey(ctx, key, keylen, + return ossl_aes_gcm_hw_initkey(ctx, key, keylen, AES_set_encrypt_key, rv64i_zvkned_encrypt, NULL); } } @@ -185,7 +185,7 @@ static const PROV_GCM_HW rv64i_zvkned_gcm = { rv64i_zvkned_gcm_initkey, ossl_gcm_setiv, ossl_gcm_aad_update, - generic_aes_gcm_cipher_update, + ossl_generic_aes_gcm_cipher_update, ossl_gcm_cipher_final, ossl_gcm_one_shot }; @@ -202,11 +202,11 @@ static int rv64i_zvkb_zvkg_zvkned_gcm_initkey(PROV_GCM_CTX *ctx, * For AES-192 case, we could fallback to `AES_set_encrypt_key`. */ if (zvkned_key_schedule_supported(keylen)) { - return aes_gcm_hw_initkey(ctx, key, keylen, + return ossl_aes_gcm_hw_initkey(ctx, key, keylen, rv64i_zvkned_set_encrypt_key, rv64i_zvkned_encrypt, rv64i_zvkb_zvkned_ctr32_encrypt_blocks); } else { - return aes_gcm_hw_initkey(ctx, key, keylen, + return ossl_aes_gcm_hw_initkey(ctx, key, keylen, AES_set_encrypt_key, rv64i_zvkned_encrypt, rv64i_zvkb_zvkned_ctr32_encrypt_blocks); } @@ -216,7 +216,7 @@ static const PROV_GCM_HW rv64i_zvkb_zvkg_zvkned_gcm = { rv64i_zvkb_zvkg_zvkned_gcm_initkey, ossl_gcm_setiv, ossl_gcm_aad_update, - generic_aes_gcm_cipher_update, + ossl_generic_aes_gcm_cipher_update, ossl_gcm_cipher_final, ossl_gcm_one_shot }; diff --git a/providers/implementations/ciphers/cipher_aes_hw_t4.c b/providers/implementations/ciphers/cipher_aes_hw_t4.c index c9549be90b..514a7a737f 100644 --- a/providers/implementations/ciphers/cipher_aes_hw_t4.c +++ b/providers/implementations/ciphers/cipher_aes_hw_t4.c @@ -154,15 +154,15 @@ static int t4_aes_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, { switch (keylen) { case 16: - return aes_gcm_hw_initkey(ctx, key, keylen, + return ossl_aes_gcm_hw_initkey(ctx, key, keylen, t4_set_encrypt_key, aes_t4_encrypt, (ctr128_f)aes128_t4_ctr32_encrypt); case 24: - return aes_gcm_hw_initkey(ctx, key, keylen, + return ossl_aes_gcm_hw_initkey(ctx, key, keylen, t4_set_encrypt_key, aes_t4_encrypt, (ctr128_f)aes192_t4_ctr32_encrypt); case 32: - return aes_gcm_hw_initkey(ctx, key, keylen, + return ossl_aes_gcm_hw_initkey(ctx, key, keylen, t4_set_encrypt_key, aes_t4_encrypt, (ctr128_f)aes256_t4_ctr32_encrypt); default: @@ -174,7 +174,7 @@ static const PROV_GCM_HW t4_aes_gcm = { t4_aes_gcm_initkey, ossl_gcm_setiv, ossl_gcm_aad_update, - generic_aes_gcm_cipher_update, + ossl_generic_aes_gcm_cipher_update, ossl_gcm_cipher_final, ossl_gcm_one_shot }; From e60d940b29504b7b1f15d1fe597ae9cdde701c8a Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Mon, 15 Jun 2026 09:59:47 -0400 Subject: [PATCH 100/349] Expand AES mode macro into explicit functions This removes the PROV_CIPHER_HW_aes_mode macro and replaces it with explicitly written function definitions for each AES mode (ECB, CBC, CFB128, CFB8, CFB1, OFB128, CTR). Expanding macro-generated functions improves overall code readability, allows code navigation tools to properly index the function signatures, and provides clearer stack traces during debugging. Signed-off-by: Simo Sorce Reviewed-by: Dmitry Belyavskiy Reviewed-by: Norbert Pocs Reviewed-by: Shane Lontis MergeDate: Sat Jun 27 09:06:05 2026 (Merged from https://github.com/openssl/openssl/pull/31472) --- .../implementations/ciphers/cipher_aes_hw.c | 45 ++++++++++++++----- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/providers/implementations/ciphers/cipher_aes_hw.c b/providers/implementations/ciphers/cipher_aes_hw.c index b282393d5b..03ddb46947 100644 --- a/providers/implementations/ciphers/cipher_aes_hw.c +++ b/providers/implementations/ciphers/cipher_aes_hw.c @@ -252,16 +252,37 @@ static const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_mode(enum aes_modes mode, return aes_hw_mode; } -#define PROV_CIPHER_HW_aes_mode(ENUM_MODE, mode) \ - const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_##mode(size_t keybits) \ - { \ - return ossl_prov_cipher_hw_aes_mode(ENUM_MODE, keybits); \ - } +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_ecb(size_t keybits) +{ + return ossl_prov_cipher_hw_aes_mode(AES_MODE_ECB, keybits); +} -PROV_CIPHER_HW_aes_mode(AES_MODE_ECB, ecb) -PROV_CIPHER_HW_aes_mode(AES_MODE_CBC, cbc) -PROV_CIPHER_HW_aes_mode(AES_MODE_CFB128, cfb128) -PROV_CIPHER_HW_aes_mode(AES_MODE_CFB8, cfb8) -PROV_CIPHER_HW_aes_mode(AES_MODE_CFB1, cfb1) -PROV_CIPHER_HW_aes_mode(AES_MODE_OFB128, ofb128) -PROV_CIPHER_HW_aes_mode(AES_MODE_CTR, ctr) +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_cbc(size_t keybits) +{ + return ossl_prov_cipher_hw_aes_mode(AES_MODE_CBC, keybits); +} + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_cfb128(size_t keybits) +{ + return ossl_prov_cipher_hw_aes_mode(AES_MODE_CFB128, keybits); +} + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_cfb8(size_t keybits) +{ + return ossl_prov_cipher_hw_aes_mode(AES_MODE_CFB8, keybits); +} + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_cfb1(size_t keybits) +{ + return ossl_prov_cipher_hw_aes_mode(AES_MODE_CFB1, keybits); +} + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_ofb128(size_t keybits) +{ + return ossl_prov_cipher_hw_aes_mode(AES_MODE_OFB128, keybits); +} + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_ctr(size_t keybits) +{ + return ossl_prov_cipher_hw_aes_mode(AES_MODE_CTR, keybits); +} From ab612a9baff64b776d5dc3bd58fdf37c109bf1cc Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Tue, 23 Jun 2026 12:00:49 +0200 Subject: [PATCH 101/349] test/asn1_string_test.c: allocate tmpstring properly in asn1_string_new_not_owned_test Since tmpstring's ownership is transferred to tmp in ASN1_STRING_set0(), it should be allocated using OPENSSL_strdup() and not strdup() (as it will be freed with OPENSSL_free() in ASN1_STRING_clear_free()). Also, don't try to free tmpstring on error, as at no point there is a jump to err when tmpstring is allocated and not owned by tmp. Reported by Coverity, issue 1695274. Resolves: https://scan5.scan.coverity.com/#/project-view/65248/10222?selectedIssue=1695274 Fixes: 68c0321e90d0 "Provide ASN1_STRING_new_not_owned()" Signed-off-by: Eugene Syromiatnikov Reviewed-by: Bob Beck Reviewed-by: Norbert Pocs Reviewed-by: Neil Horman MergeDate: Sun Jun 28 16:22:28 2026 (Merged from https://github.com/openssl/openssl/pull/31667) --- test/asn1_string_test.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/asn1_string_test.c b/test/asn1_string_test.c index 9460ece872..6edc7619f7 100644 --- a/test/asn1_string_test.c +++ b/test/asn1_string_test.c @@ -428,7 +428,7 @@ asn1_string_new_not_owned_test(void) if (!TEST_ptr(tmp = ASN1_STRING_new_not_owned(V_ASN1_OCTET_STRING, data, sizeof(data)))) goto err; - if (!TEST_ptr(tmpstring = strdup("puppet"))) + if (!TEST_ptr(tmpstring = OPENSSL_strdup("puppet"))) goto err; ASN1_STRING_set0(tmp, tmpstring, 4); @@ -450,8 +450,8 @@ asn1_string_new_not_owned_test(void) if (!TEST_mem_eq(tmpstring, strlen("puppet"), "zzzzet", strlen("puppet"))) goto err; - ASN1_STRING_clear_free(tmp); tmpstring = NULL; + ASN1_STRING_clear_free(tmp); tmp = NULL; if (!TEST_ptr_null(tmp = ASN1_STRING_new_not_owned(V_ASN1_BIT_STRING, data, sizeof(data)))) @@ -470,7 +470,6 @@ asn1_string_new_not_owned_test(void) err: ASN1_STRING_clear_free(tmp); - free(tmpstring); return success; } From fc736368881368886501d432e3c0f5a1d3fbabce Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 15 Jun 2026 18:24:28 +0200 Subject: [PATCH 102/349] test/recipes/15-test_pkey.t: extend tests for openssl pkey -text and -text_pub This slightly improves openssl pkey text coverage. Reviewed-by: Eugene Syromiatnikov Reviewed-by: Tomas Mraz MergeDate: Sun Jun 28 17:42:46 2026 (Merged from https://github.com/openssl/openssl/pull/31521) --- test/recipes/15-test_pkey.t | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/test/recipes/15-test_pkey.t b/test/recipes/15-test_pkey.t index c61b0548a3..7d36e0e83d 100644 --- a/test/recipes/15-test_pkey.t +++ b/test/recipes/15-test_pkey.t @@ -112,20 +112,32 @@ subtest "=== pkey handling of DER encoding ===" => sub { "Same file contents after converting to DER and back"); }; -subtest "=== pkey text output ===" => sub { - plan tests => 3; +subtest "=== pkey text and text_pub output ===" => sub { + plan tests => 6; ok((grep /BEGIN PRIVATE KEY/, run(app([@app, '-in', $in_key, '-text']), capture => 1)), "pkey text output contains PEM header"); ok(!(grep /BEGIN PRIVATE KEY/, - run(app([@app, '-in', $in_key, '-text', '-noout']), capture => 1)), - "pkey text output with -noout does not contain PEM header"); + run(app([@app, '-in', $in_key, '-text', '-noout']), capture => 1)), + "pkey text output with -noout does not contain PEM header"); ok((grep /Private-Key:/, run(app([@app, '-in', $in_key, '-text', '-noout']), capture => 1)), "pkey text output (even with -noout) contains \"Private-Key:\""); + + ok(!(grep /Private-Key:/, + run(app([@app, '-in', $in_key, '-text_pub', '-noout']), capture => 1)), + "-text_pub does not print private key components"); + + ok((grep /Public-Key:/, + run(app([@app, '-in', $in_key, '-text_pub', '-noout']), capture => 1)), + "-text_pub prints public key components"); + + ok(!run(app([@app, '-in', $in_key, '-text', '-outform', 'DER', + '-out', 'text_der.tmp'])), + "-text combined with DER output is rejected"); }; subtest "=== pkey EC point conversion form ===" => sub { From 57f4bd9ab801c9d362ef5e110b74362f41db26c3 Mon Sep 17 00:00:00 2001 From: Matt Van Horn Date: Thu, 30 Apr 2026 21:46:43 -0700 Subject: [PATCH 103/349] providers/defltprov.c: remove static globals from provider_init The default provider stored two function pointers from the core dispatch table (c_gettable_params, c_get_params) in file-scope statics, written by ossl_default_provider_init() without any synchronization. When OSSL_PROVIDER_load() is invoked from multiple threads concurrently, TSAN reports a data race on both writes[1]. c_gettable_params is never read anywhere in the file; it was dead storage. c_get_params is only consumed once, inside the same call to ossl_default_provider_init(), to seed the provider context via ossl_prov_ctx_set0_core_get_params(). It can therefore be a local variable rather than file-scope state. Drop the unused c_gettable_params static together with its dispatch case, and scope c_get_params inside the init function. The behavior of the default provider is unchanged for single-threaded callers; the concurrent-load race goes away because the shared mutable state is gone. [1] https://github.com/openssl/openssl/issues/28935 CLA: trivial Resolves: https://github.com/openssl/openssl/issues/28935 Reviewed-by: Norbert Pocs Reviewed-by: Neil Horman Reviewed-by: Eugene Syromiatnikov MergeDate: Sun Jun 28 17:51:52 2026 (Merged from https://github.com/openssl/openssl/pull/31508) --- providers/defltprov.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/providers/defltprov.c b/providers/defltprov.c index c471cf8e7e..1f39b34d6e 100644 --- a/providers/defltprov.c +++ b/providers/defltprov.c @@ -34,10 +34,6 @@ static OSSL_FUNC_provider_query_operation_fn deflt_query; #define ALGC(NAMES, FUNC, CHECK) { { NAMES, "provider=default", FUNC }, CHECK } #define ALG(NAMES, FUNC) ALGC(NAMES, FUNC, NULL) -/* Functions provided by the core */ -static OSSL_FUNC_core_gettable_params_fn *c_gettable_params = NULL; -static OSSL_FUNC_core_get_params_fn *c_get_params = NULL; - /* Parameters we provide to the core */ static const OSSL_PARAM deflt_param_types[] = { OSSL_PARAM_DEFN(OSSL_PROV_PARAM_NAME, OSSL_PARAM_UTF8_PTR, NULL, 0), @@ -799,6 +795,7 @@ int ossl_default_provider_init(const OSSL_CORE_HANDLE *handle, void **provctx) { OSSL_FUNC_core_get_libctx_fn *c_get_libctx = NULL; + OSSL_FUNC_core_get_params_fn *c_get_params = NULL; BIO_METHOD *corebiometh; if (!ossl_prov_bio_from_dispatch(in) @@ -806,9 +803,6 @@ int ossl_default_provider_init(const OSSL_CORE_HANDLE *handle, return 0; for (; in->function_id != 0; in++) { switch (in->function_id) { - case OSSL_FUNC_CORE_GETTABLE_PARAMS: - c_gettable_params = OSSL_FUNC_core_gettable_params(in); - break; case OSSL_FUNC_CORE_GET_PARAMS: c_get_params = OSSL_FUNC_core_get_params(in); break; From f3447c3bc217a4439470fb65609b1c3e77956116 Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Tue, 16 Jun 2026 08:11:32 +0200 Subject: [PATCH 104/349] providers/baseprov.c: remove static globals c_gettable_params is never read anywhere in the file; it was dead storage. c_get_params is only consumed once, inside the same call to ossl_default_provider_init(), to seed the provider context via ossl_prov_ctx_set0_core_get_params(). It can therefore be a local variable rather than file-scope state. Drop the unused c_gettable_params static together with its dispatch case, and scope c_get_params inside the init function. The behavior of the base provider is unchanged for single-threaded callers; the concurrent-load race goes away because the shared mutable state is gone. Signed-off-by: Nikola Pajkovsky Reviewed-by: Norbert Pocs Reviewed-by: Neil Horman Reviewed-by: Eugene Syromiatnikov MergeDate: Sun Jun 28 17:51:54 2026 (Merged from https://github.com/openssl/openssl/pull/31508) --- providers/baseprov.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/providers/baseprov.c b/providers/baseprov.c index 16d2f91bb1..f517e5ae81 100644 --- a/providers/baseprov.c +++ b/providers/baseprov.c @@ -29,10 +29,6 @@ static OSSL_FUNC_provider_gettable_params_fn base_gettable_params; static OSSL_FUNC_provider_get_params_fn base_get_params; static OSSL_FUNC_provider_query_operation_fn base_query; -/* Functions provided by the core */ -static OSSL_FUNC_core_gettable_params_fn *c_gettable_params = NULL; -static OSSL_FUNC_core_get_params_fn *c_get_params = NULL; - /* Parameters we provide to the core */ static const OSSL_PARAM base_param_types[] = { OSSL_PARAM_DEFN(OSSL_PROV_PARAM_NAME, OSSL_PARAM_UTF8_PTR, NULL, 0), @@ -139,15 +135,13 @@ int ossl_base_provider_init(const OSSL_CORE_HANDLE *handle, void **provctx) { OSSL_FUNC_core_get_libctx_fn *c_get_libctx = NULL; + OSSL_FUNC_core_get_params_fn *c_get_params = NULL; BIO_METHOD *corebiometh; if (!ossl_prov_bio_from_dispatch(in)) return 0; for (; in->function_id != 0; in++) { switch (in->function_id) { - case OSSL_FUNC_CORE_GETTABLE_PARAMS: - c_gettable_params = OSSL_FUNC_core_gettable_params(in); - break; case OSSL_FUNC_CORE_GET_PARAMS: c_get_params = OSSL_FUNC_core_get_params(in); break; From f32f8682eac94360684d9856b7f72de4948b8637 Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Tue, 16 Jun 2026 08:32:35 +0200 Subject: [PATCH 105/349] providers/fips/fipsprov.c, test/p_test.c: remove c_gettable_params static global c_gettable_params is never read anywhere in the files; it was dead storage. Remove it. Signed-off-by: Nikola Pajkovsky Reviewed-by: Norbert Pocs Reviewed-by: Neil Horman Reviewed-by: Eugene Syromiatnikov MergeDate: Sun Jun 28 17:51:55 2026 (Merged from https://github.com/openssl/openssl/pull/31508) --- providers/fips/fipsprov.c | 4 ---- test/p_test.c | 4 ---- 2 files changed, 8 deletions(-) diff --git a/providers/fips/fipsprov.c b/providers/fips/fipsprov.c index 3292dce334..5f331920ba 100644 --- a/providers/fips/fipsprov.c +++ b/providers/fips/fipsprov.c @@ -63,7 +63,6 @@ extern OSSL_FUNC_core_thread_start_fn *c_thread_start; */ /* Functions provided by the core */ -static OSSL_FUNC_core_gettable_params_fn *c_gettable_params; static OSSL_FUNC_core_get_params_fn *c_get_params; OSSL_FUNC_core_thread_start_fn *c_thread_start; static OSSL_FUNC_core_new_error_fn *c_new_error; @@ -840,9 +839,6 @@ int OSSL_provider_init_int(const OSSL_CORE_HANDLE *handle, case OSSL_FUNC_CORE_GET_LIBCTX: set_func(c_get_libctx, OSSL_FUNC_core_get_libctx(in)); break; - case OSSL_FUNC_CORE_GETTABLE_PARAMS: - set_func(c_gettable_params, OSSL_FUNC_core_gettable_params(in)); - break; case OSSL_FUNC_CORE_GET_PARAMS: set_func(c_get_params, OSSL_FUNC_core_get_params(in)); break; diff --git a/test/p_test.c b/test/p_test.c index 1e38bee717..655cb7ce6f 100644 --- a/test/p_test.c +++ b/test/p_test.c @@ -43,7 +43,6 @@ typedef struct p_test_ctx { OSSL_LIB_CTX *libctx; } P_TEST_CTX; -static OSSL_FUNC_core_gettable_params_fn *c_gettable_params = NULL; static OSSL_FUNC_core_get_params_fn *c_get_params = NULL; static OSSL_FUNC_core_new_error_fn *c_new_error; static OSSL_FUNC_core_set_error_debug_fn *c_set_error_debug; @@ -258,9 +257,6 @@ int OSSL_provider_init(const OSSL_CORE_HANDLE *handle, for (; in->function_id != 0; in++) { switch (in->function_id) { - case OSSL_FUNC_CORE_GETTABLE_PARAMS: - c_gettable_params = OSSL_FUNC_core_gettable_params(in); - break; case OSSL_FUNC_CORE_GET_PARAMS: c_get_params = OSSL_FUNC_core_get_params(in); break; From b00f7b6c30f82a1bb2610f97d4349ed5d932c7d4 Mon Sep 17 00:00:00 2001 From: Pauli Date: Tue, 23 Jun 2026 09:01:17 +1000 Subject: [PATCH 106/349] demo: add program that shows how to query the FIPS provider version Reviewed-by: Nikola Pajkovsky Reviewed-by: Simo Sorce (Merged from https://github.com/openssl/openssl/pull/31654) --- demos/Makefile | 1 + demos/README.txt | 3 ++ demos/build.info | 2 +- demos/info/Makefile | 29 +++++++++++++++++++ demos/info/build.info | 11 +++++++ demos/info/fips-version.c | 61 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 demos/info/Makefile create mode 100644 demos/info/build.info create mode 100644 demos/info/fips-version.c diff --git a/demos/Makefile b/demos/Makefile index 208249e0fd..3b411fe052 100644 --- a/demos/Makefile +++ b/demos/Makefile @@ -6,6 +6,7 @@ MODULES = bio \ encrypt \ guide \ http3 \ + info \ kdf \ keyexch \ mac \ diff --git a/demos/README.txt b/demos/README.txt index 1a7d4f447f..9ccb5f1c2f 100644 --- a/demos/README.txt +++ b/demos/README.txt @@ -42,6 +42,9 @@ tls-client-non-block.c: A simple non-blocking SSL/TLS client http3: Demonstration of how to use OpenSSL's QUIC capabilities for HTTP/3. +info: +fips-version.c Demonstration of how to query the FIPS provider version + kdf: hkdf.c Demonstration of HMAC based key derivation pbkdf2.c Demonstration of PBKDF2 password based key derivation diff --git a/demos/build.info b/demos/build.info index 3c74e8f331..49068f74ec 100644 --- a/demos/build.info +++ b/demos/build.info @@ -1,4 +1,4 @@ -SUBDIRS=bio cipher digest keyexch mac kdf pkey signature \ +SUBDIRS=bio cipher digest info keyexch mac kdf pkey signature \ encrypt encode sslecho IF[{- !$disabled{"h3demo"} -}] diff --git a/demos/info/Makefile b/demos/info/Makefile new file mode 100644 index 0000000000..ef93a25df6 --- /dev/null +++ b/demos/info/Makefile @@ -0,0 +1,29 @@ +# +# To run the demos when linked with a shared library (default) ensure +# that libcrypto is on the library path. For example: +# +# LD_LIBRARY_PATH=../.. ./info + +TESTS = fips-version + +CFLAGS = -I../../include -g -Wall +LDFLAGS = -L../.. +LDLIBS = -lcrypto + +all: $(TESTS) + +fips-version: fips-version.o + +$(TESTS): + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LDLIBS) + +clean: + $(RM) *.o $(TESTS) + +.PHONY: test +test: all + @echo "\nINFO tests:" + @set -e; for tst in $(TESTS); do \ + echo "\n"$$tst; \ + LD_LIBRARY_PATH=../.. ./$$tst; \ + done diff --git a/demos/info/build.info b/demos/info/build.info new file mode 100644 index 0000000000..b5339cec91 --- /dev/null +++ b/demos/info/build.info @@ -0,0 +1,11 @@ +# +# To run the demos when linked with a shared library (default) ensure +# that libcrypto is on the library path. For example: +# +# LD_LIBRARY_PATH=../.. ./info + +PROGRAMS{noinst} = fips-version + +INCLUDE[fips-version]=../../include +SOURCE[fips-version]=fips-version.c +DEPEND[fips-version]=../../libcrypto diff --git a/demos/info/fips-version.c b/demos/info/fips-version.c new file mode 100644 index 0000000000..6b1bb4bfab --- /dev/null +++ b/demos/info/fips-version.c @@ -0,0 +1,61 @@ +/* + * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + int ret = EXIT_FAILURE; + OSSL_LIB_CTX *libctx; + OSSL_PROVIDER *fips_provider = NULL; + OSSL_PARAM params[2]; + char *version; + + /* Replace this with your libctx if you are using a non-default one */ + libctx = NULL; + + /* Check if the FIPS provider is available in this libctx */ + if (!OSSL_PROVIDER_available(libctx, "fips")) { + puts("FIPS provider is not available"); + goto done; + } + + /* Load the FIPS provider */ + fips_provider = OSSL_PROVIDER_load(libctx, "fips"); + if (fips_provider == NULL) { + puts("Failed to load FIPS provider"); + goto done; + } + + /* Query the FIPS provider version */ + params[0] = OSSL_PARAM_construct_utf8_ptr(OSSL_PROV_PARAM_VERSION, + &version, 0); + params[1] = OSSL_PARAM_construct_end(); + OSSL_PARAM_set_all_unmodified(params); + if (!OSSL_PROVIDER_get_params(fips_provider, params)) { + puts("Failed to query FIPS provider version"); + goto done; + } + + /* Check if the FIPS provider returned a version to us */ + if (!OSSL_PARAM_modified(params)) { + puts("FIPS provider failed to set version"); + goto done; + } + + printf("FIPS provider version is %s\n", version); + ret = EXIT_SUCCESS; +done: + OSSL_PROVIDER_unload(fips_provider); + return ret; +} From ea655177e0d0dd7885640d1d9a921ef6d2c9bf8c Mon Sep 17 00:00:00 2001 From: Billy Brumley Date: Thu, 25 Jun 2026 07:20:25 -0400 Subject: [PATCH 107/349] [test] check tag abuse for AEAD ciphers With AEAD ciphers, a tag is an input for decryption (the value to verify) and an output of encryption (the generated value). Therefore: - supplying a tag value while encrypting must fail - reading a tag while decrypting must fail - error codes should be consistent across all AEADs Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Dmitry Belyavskiy Reviewed-by: Daniel Kubec MergeDate: Tue Jun 30 07:47:03 2026 (Merged from https://github.com/openssl/openssl/pull/31734) --- .../ciphers/cipher_aes_gcm_siv.c | 10 +- .../implementations/ciphers/cipher_aes_ocb.c | 8 +- .../ciphers/ciphercommon_gcm.c | 8 +- test/evp_extra_test.c | 118 ++++++++++++++++++ 4 files changed, 138 insertions(+), 6 deletions(-) diff --git a/providers/implementations/ciphers/cipher_aes_gcm_siv.c b/providers/implementations/ciphers/cipher_aes_gcm_siv.c index 1fd97fe3d1..2f4a86dc56 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm_siv.c +++ b/providers/implementations/ciphers/cipher_aes_gcm_siv.c @@ -182,8 +182,11 @@ static int ossl_aes_gcm_siv_get_ctx_params(void *vctx, OSSL_PARAM params[]) return 0; if (p.tag != NULL && p.tag->data_type == OSSL_PARAM_OCTET_STRING) { - if (!ctx->enc || !ctx->generated_tag - || p.tag->data_size != sizeof(ctx->tag) + if (!ctx->enc || !ctx->generated_tag) { + ERR_raise(ERR_LIB_PROV, PROV_R_TAG_NOT_SET); + return 0; + } + if (p.tag->data_size != sizeof(ctx->tag) || !OSSL_PARAM_set_octet_string(p.tag, ctx->tag, sizeof(ctx->tag))) { ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); @@ -227,6 +230,9 @@ static int ossl_aes_gcm_siv_set_ctx_params(void *vctx, const OSSL_PARAM params[] if (!ctx->enc) { memcpy(ctx->user_tag, p.tag->data, sizeof(ctx->tag)); ctx->have_user_tag = 1; + } else if (p.tag->data != NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_TAG_NOT_NEEDED); + return 0; } } diff --git a/providers/implementations/ciphers/cipher_aes_ocb.c b/providers/implementations/ciphers/cipher_aes_ocb.c index 329d4bfa3e..1bd5281cc2 100644 --- a/providers/implementations/ciphers/cipher_aes_ocb.c +++ b/providers/implementations/ciphers/cipher_aes_ocb.c @@ -376,7 +376,7 @@ static int aes_ocb_set_ctx_params(void *vctx, const OSSL_PARAM params[]) ctx->taglen = p.tag->data_size; } else { if (ctx->base.enc) { - ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); + ERR_raise(ERR_LIB_PROV, PROV_R_TAG_NOT_NEEDED); return 0; } if (p.tag->data_size != ctx->taglen) { @@ -476,7 +476,11 @@ static int aes_ocb_get_ctx_params(void *vctx, OSSL_PARAM params[]) ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); return 0; } - if (!ctx->base.enc || p.tag->data_size != ctx->taglen) { + if (!ctx->base.enc) { + ERR_raise(ERR_LIB_PROV, PROV_R_TAG_NOT_SET); + return 0; + } + if (p.tag->data_size != ctx->taglen) { ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_TAG_LENGTH); return 0; } diff --git a/providers/implementations/ciphers/ciphercommon_gcm.c b/providers/implementations/ciphers/ciphercommon_gcm.c index d9be50f513..c93b0767b0 100644 --- a/providers/implementations/ciphers/ciphercommon_gcm.c +++ b/providers/implementations/ciphers/ciphercommon_gcm.c @@ -215,7 +215,7 @@ int ossl_gcm_get_ctx_params(void *vctx, OSSL_PARAM params[]) if (p.tag != NULL) { sz = p.tag->data_size; if (!ctx->enc || ctx->taglen == UNINITIALISED_SIZET) { - ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_TAG); + ERR_raise(ERR_LIB_PROV, PROV_R_TAG_NOT_SET); return 0; } if (p.tag->data != NULL && (sz > EVP_GCM_TLS_TAG_LEN || sz == 0)) { @@ -263,7 +263,11 @@ int ossl_gcm_set_ctx_params(void *vctx, const OSSL_PARAM params[]) ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); return 0; } - if (sz == 0 || ctx->enc) { + if (ctx->enc) { + ERR_raise(ERR_LIB_PROV, PROV_R_TAG_NOT_NEEDED); + return 0; + } + if (sz == 0) { ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_TAG); return 0; } diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c index 236991f1f5..cb18ead4db 100644 --- a/test/evp_extra_test.c +++ b/test/evp_extra_test.c @@ -5929,6 +5929,123 @@ err: return testresult; } +/* + * With AEAD ciphers, a tag is an input for decryption (the value to verify) + * and an output of encryption (the generated value). Therefore: + * - supplying a tag value while encrypting must fail + * - reading a tag while decrypting must fail + * - error codes should be consistent across all AEADs + */ +static int test_evp_aead_tag_direction(int idx) +{ + const EVP_CIPHER_TEST_INFO *info = &cipher_list[idx]; + EVP_CIPHER_CTX *ctx_enc = NULL; /* set tag while encrypting: must fail */ + EVP_CIPHER_CTX *ctx_dec = NULL; /* get tag while decrypting: must fail */ + + OSSL_PARAM tagparams[2]; + + unsigned char key[EVP_MAX_KEY_LENGTH] = { 0 }; + unsigned char iv[EVP_MAX_IV_LENGTH] = { 0 }; + unsigned char tag[EVPTEST_TAG_LEN_MAX] = { 0 }; + + int i = 0, testresult = 0, expected = 0; + char *errmsg = NULL; + unsigned long err_code = 0; + + /* filter out various modes */ + if (info->taglen == 0 + /* skip TLS stitched MTE cipher */ + || EVP_CIPHER_is_a(info->ciph, "AES-128-CBC-HMAC-SHA1") + /* skip TLS stitched MTE cipher */ + || EVP_CIPHER_is_a(info->ciph, "AES-256-CBC-HMAC-SHA1") + /* skip TLS stitched MTE cipher */ + || EVP_CIPHER_is_a(info->ciph, "AES-128-CBC-HMAC-SHA256") + /* skip TLS stitched MTE cipher */ + || EVP_CIPHER_is_a(info->ciph, "AES-256-CBC-HMAC-SHA256")) + return 1; + + for (i = 0; i < info->keylen && i < (int)sizeof(key); i++) + key[i] = (unsigned char)(0xA0 + i); + for (i = 0; i < info->ivlen && i < (int)sizeof(iv); i++) + iv[i] = (unsigned char)(0xB0 + i); + + /* + * a tag value supplied while encrypting must be rejected. data is non-NULL + * so this is a value-set, not the data == NULL tag-length query. + */ + if (!TEST_ptr(ctx_enc = EVP_CIPHER_CTX_new())) { + errmsg = "ENC_ALLOC"; + goto err; + } + if (!TEST_true(EVP_EncryptInit_ex2(ctx_enc, info->ciph, key, iv, NULL))) { + errmsg = "ENC_INIT"; + goto err; + } + tagparams[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + tag, info->taglen); + tagparams[1] = OSSL_PARAM_construct_end(); + ERR_set_mark(); + if (!TEST_false(EVP_CIPHER_CTX_set_params(ctx_enc, tagparams))) { + ERR_clear_last_mark(); + errmsg = "ENC_SET_TAG_NOT_REJECTED"; + goto err; + } + err_code = ERR_peek_last_error(); + if (!TEST_int_eq(ERR_GET_LIB(err_code), ERR_LIB_PROV) + || !TEST_int_eq(ERR_GET_REASON(err_code), PROV_R_TAG_NOT_NEEDED)) { + ERR_clear_last_mark(); + expected = PROV_R_TAG_NOT_NEEDED; + errmsg = "ENC_SET_TAG_WRONG_REASON"; + goto err; + } + ERR_pop_to_mark(); + + /* a tag read while decrypting must be rejected */ + if (!TEST_ptr(ctx_dec = EVP_CIPHER_CTX_new())) { + errmsg = "DEC_ALLOC"; + goto err; + } + if (!TEST_true(EVP_DecryptInit_ex2(ctx_dec, info->ciph, key, iv, NULL))) { + errmsg = "DEC_INIT"; + goto err; + } + tagparams[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + tag, info->taglen); + tagparams[1] = OSSL_PARAM_construct_end(); + ERR_set_mark(); + if (!TEST_false(EVP_CIPHER_CTX_get_params(ctx_dec, tagparams))) { + ERR_clear_last_mark(); + errmsg = "DEC_GET_TAG_NOT_REJECTED"; + goto err; + } + err_code = ERR_peek_last_error(); + if (!TEST_int_eq(ERR_GET_LIB(err_code), ERR_LIB_PROV) + || !TEST_int_eq(ERR_GET_REASON(err_code), PROV_R_TAG_NOT_SET)) { + ERR_clear_last_mark(); + expected = PROV_R_TAG_NOT_SET; + errmsg = "DEC_GET_TAG_WRONG_REASON"; + goto err; + } + ERR_pop_to_mark(); + + testresult = 1; + +err: + if (errmsg != NULL) { + if (expected != 0) + TEST_info("test_evp_aead_tag_direction %d, %s: %s" + " (expected reason %d, got %d)", + idx, errmsg, info->name, + expected, ERR_GET_REASON(err_code)); + else + TEST_info("test_evp_aead_tag_direction %d, %s: %s", + idx, errmsg, info->name); + } + EVP_CIPHER_CTX_free(ctx_enc); + EVP_CIPHER_CTX_free(ctx_dec); + return testresult; +} + /* * Verify stale key is not being used after providing a new key in multiple steps. * This test performs a full round of encryption and then changes the @@ -8937,6 +9054,7 @@ int setup_tests(void) ADD_ALL_TESTS(test_evp_stale_key_reinit, cipher_list_n); ADD_ALL_TESTS(test_evp_decrypt_roundtrip_multistep, cipher_list_n); ADD_ALL_TESTS(test_evp_oneshot_aead_zerolen, cipher_list_n); + ADD_ALL_TESTS(test_evp_aead_tag_direction, cipher_list_n); ADD_ALL_TESTS(test_evp_init_seq, OSSL_NELEM(evp_init_tests)); ADD_ALL_TESTS(test_evp_reset, OSSL_NELEM(evp_reset_tests)); From 6bb3799400f75258e8bbf371ee594605b0896c4e Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Wed, 24 Jun 2026 22:59:43 +0700 Subject: [PATCH 108/349] Port script_5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also fixes a bug in the OP_STREAM_RESET macro Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Saša Nedvědický Reviewed-by: Norbert Pocs MergeDate: Tue Jun 30 07:54:46 2026 (Merged from https://github.com/openssl/openssl/pull/31707) --- test/quic_multistream_test.c | 32 +----------------------- test/radix/quic_ops.c | 2 +- test/radix/quic_tests.c | 47 +++++++++++++++++++++++++++++++++++- 3 files changed, 48 insertions(+), 33 deletions(-) diff --git a/test/quic_multistream_test.c b/test/quic_multistream_test.c index 4edd53e15f..38289e6892 100644 --- a/test/quic_multistream_test.c +++ b/test/quic_multistream_test.c @@ -373,18 +373,6 @@ static void s_unlock(struct helper *h, struct helper_local *hl); #define ACQUIRE_S() s_lock(h, hl) #define ACQUIRE_S_NOHL() s_lock(h, NULL) -static int check_stream_reset(struct helper *h, struct helper_local *hl) -{ - uint64_t stream_id = hl->check_op->arg2, aec = 0; - - if (!ossl_quic_tserver_stream_has_peer_reset_stream(ACQUIRE_S(), stream_id, &aec)) { - h->check_spin_again = 1; - return 0; - } - - return TEST_uint64_t_eq(aec, 42); -} - static int check_stream_stopped(struct helper *h, struct helper_local *hl) { uint64_t stream_id = hl->check_op->arg2; @@ -2063,25 +2051,7 @@ static const struct script_op script_4[] = { /* 5. Test stream reset functionality */ static const struct script_op script_5[] = { - OP_C_SET_ALPN("ossltest"), - OP_C_CONNECT_WAIT(), - - OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE), - OP_C_NEW_STREAM_BIDI(a, C_BIDI_ID(0)), - OP_C_NEW_STREAM_BIDI(b, C_BIDI_ID(1)), - - OP_C_WRITE(a, "apple", 5), - OP_C_STREAM_RESET(a, 42), - - OP_C_WRITE(b, "strawberry", 10), - - OP_S_BIND_STREAM_ID(a, C_BIDI_ID(0)), - OP_S_BIND_STREAM_ID(b, C_BIDI_ID(1)), - OP_S_READ_EXPECT(b, "strawberry", 10), - /* Reset disrupts read of already sent data */ - OP_S_READ_FAIL(a, 0), - OP_CHECK(check_stream_reset, C_BIDI_ID(0)), - + /* test moved to test/radix/quic_tests.c */ OP_END }; diff --git a/test/radix/quic_ops.c b/test/radix/quic_ops.c index 11cf93be30..27e5a3705d 100644 --- a/test/radix/quic_ops.c +++ b/test/radix/quic_ops.c @@ -1099,7 +1099,7 @@ err: #define OP_STREAM_RESET(name, error_code) \ (OP_SELECT_SSL(0, name), \ - OP_PUSH_U64(flags), \ + OP_PUSH_PZ(#name), \ OP_PUSH_U64(error_code), \ OP_FUNC(hf_stream_reset)) diff --git a/test/radix/quic_tests.c b/test/radix/quic_tests.c index d1d8c54949..2f778e7a26 100644 --- a/test/radix/quic_tests.c +++ b/test/radix/quic_tests.c @@ -765,6 +765,30 @@ DEF_SCRIPT(check_ctx_cbks, "Check new_pending and client_hello callbacks") OP_FUNC(check_pending); } +DEF_FUNC(check_stream_reset_5) +{ + int ok = 0; + SSL *ssl; + uint64_t aec = 0; + int state; + + REQUIRE_SSL(ssl); + + state = SSL_get_stream_read_state(ssl); + if (state != SSL_STREAM_STATE_RESET_REMOTE) + F_SPIN_AGAIN(); + + if (!TEST_true(SSL_get_stream_read_error_code(ssl, &aec))) + goto err; + + if (!TEST_uint64_t_eq(aec, 42)) + goto err; + + ok = 1; +err: + return ok; +} + /* * script_5 - script_106 are place holders for tests we * currently keep in test/quic_multistream_test.c. @@ -778,8 +802,29 @@ DEF_SCRIPT(check_ctx_cbks, "Check new_pending and client_hello callbacks") * The scaffolding here hopes to avoid conflicts in 'scripts' * array below when more PRs will be in flight. */ -DEF_SCRIPT(script_5, "place holder for multistram script_5") + +/* 5. Test stream reset functionality */ +DEF_SCRIPT(script_5, "Test stream reset functionality") { + OP_SIMPLE_PAIR_CONN_ND(); + + OP_NEW_STREAM(C, Ca, 0 /* bidirectional */); + OP_NEW_STREAM(C, Cb, 0 /* bidirectional */); + + OP_WRITE(Ca, "apple", 5); + OP_STREAM_RESET(Ca, 42); + + OP_WRITE(Cb, "strawberry", 10); + + OP_ACCEPT_CONN_WAIT_ND(L, S, 0); + OP_ACCEPT_STREAM_WAIT(S, Sa, 0); /* first stream = Ca */ + OP_ACCEPT_STREAM_WAIT(S, Sb, 0); /* second stream = Cb */ + + /* Reset disrupts read of already-sent data */ + OP_SELECT_SSL(0, Sa); + OP_FUNC(check_stream_reset_5); + + OP_READ_EXPECT(Sb, "strawberry", 10); } DEF_SCRIPT(script_6, "place holder for multistram script_6") From 1ca326f0e728ba1edd0c69635afd0f8ad72be7ab Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Wed, 24 Jun 2026 23:07:22 +0700 Subject: [PATCH 109/349] Port script_6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Saša Nedvědický Reviewed-by: Norbert Pocs MergeDate: Tue Jun 30 07:54:47 2026 (Merged from https://github.com/openssl/openssl/pull/31707) --- test/quic_multistream_test.c | 26 +------------------------- test/radix/quic_tests.c | 30 +++++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/test/quic_multistream_test.c b/test/quic_multistream_test.c index 38289e6892..0459c8f194 100644 --- a/test/quic_multistream_test.c +++ b/test/quic_multistream_test.c @@ -373,18 +373,6 @@ static void s_unlock(struct helper *h, struct helper_local *hl); #define ACQUIRE_S() s_lock(h, hl) #define ACQUIRE_S_NOHL() s_lock(h, NULL) -static int check_stream_stopped(struct helper *h, struct helper_local *hl) -{ - uint64_t stream_id = hl->check_op->arg2; - - if (!ossl_quic_tserver_stream_has_peer_stop_sending(ACQUIRE_S(), stream_id, NULL)) { - h->check_spin_again = 1; - return 0; - } - - return 1; -} - static int override_key_update(struct helper *h, struct helper_local *hl) { QUIC_CHANNEL *ch = ossl_quic_conn_get_channel(h->c_conn); @@ -2057,19 +2045,7 @@ static const struct script_op script_5[] = { /* 6. Test STOP_SENDING functionality */ static const struct script_op script_6[] = { - OP_C_SET_ALPN("ossltest"), - OP_C_CONNECT_WAIT(), - - OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE), - OP_S_NEW_STREAM_BIDI(a, S_BIDI_ID(0)), - OP_S_WRITE(a, "apple", 5), - - OP_C_ACCEPT_STREAM_WAIT(a), - OP_C_FREE_STREAM(a), - OP_C_ACCEPT_STREAM_NONE(), - - OP_CHECK(check_stream_stopped, S_BIDI_ID(0)), - + /* test moved to test/radix/quic_tests.c */ OP_END }; diff --git a/test/radix/quic_tests.c b/test/radix/quic_tests.c index 2f778e7a26..3fd6bcb741 100644 --- a/test/radix/quic_tests.c +++ b/test/radix/quic_tests.c @@ -827,8 +827,36 @@ DEF_SCRIPT(script_5, "Test stream reset functionality") OP_READ_EXPECT(Sb, "strawberry", 10); } -DEF_SCRIPT(script_6, "place holder for multistram script_6") +DEF_FUNC(check_stream_stopped_6) { + int ok = 0; + SSL *ssl; + + REQUIRE_SSL(ssl); + + if (SSL_get_stream_write_state(ssl) != SSL_STREAM_STATE_RESET_LOCAL) + F_SPIN_AGAIN(); + + ok = 1; +err: + return ok; +} + +/* 6. Test STOP_SENDING functionality */ +DEF_SCRIPT(script_6, "Test STOP_SENDING functionality") +{ + OP_SIMPLE_PAIR_CONN_ND(); + OP_ACCEPT_CONN_WAIT_ND(L, S, 0); + + OP_NEW_STREAM(S, Sa, 0 /* bidirectional */); + OP_WRITE(Sa, "apple", 5); + + OP_ACCEPT_STREAM_WAIT(C, Ca, 0); + OP_UNBIND(Ca); + OP_ACCEPT_STREAM_NONE(C, 0); + + OP_SELECT_SSL(0, Sa); + OP_FUNC(check_stream_stopped_6); } DEF_SCRIPT(script_7, "place holder for multistrem script_7") From 0f41f011f8f1d95578ba7cec143b6dfdea251f28 Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Wed, 24 Jun 2026 23:20:24 +0700 Subject: [PATCH 110/349] Port script_7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Saša Nedvědický Reviewed-by: Norbert Pocs MergeDate: Tue Jun 30 07:54:48 2026 (Merged from https://github.com/openssl/openssl/pull/31707) --- test/quic_multistream_test.c | 11 +---------- test/radix/quic_tests.c | 10 +++++++++- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/test/quic_multistream_test.c b/test/quic_multistream_test.c index 0459c8f194..5bb15f9213 100644 --- a/test/quic_multistream_test.c +++ b/test/quic_multistream_test.c @@ -2051,16 +2051,7 @@ static const struct script_op script_6[] = { /* 7. Unidirectional default stream mode test (client sends first) */ static const struct script_op script_7[] = { - OP_C_SET_ALPN("ossltest"), - OP_C_CONNECT_WAIT(), - - OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_AUTO_UNI), - OP_C_WRITE(DEFAULT, "apple", 5), - - OP_S_BIND_STREAM_ID(a, C_UNI_ID(0)), - OP_S_READ_EXPECT(a, "apple", 5), - OP_S_WRITE_FAIL(a), - + /* test moved to test/radix/quic_tests.c */ OP_END }; diff --git a/test/radix/quic_tests.c b/test/radix/quic_tests.c index 3fd6bcb741..dd46002a02 100644 --- a/test/radix/quic_tests.c +++ b/test/radix/quic_tests.c @@ -859,8 +859,16 @@ DEF_SCRIPT(script_6, "Test STOP_SENDING functionality") OP_FUNC(check_stream_stopped_6); } -DEF_SCRIPT(script_7, "place holder for multistrem script_7") +/* 7. Unidirectional default stream mode test (client sends first) */ +DEF_SCRIPT(script_7, "Unidirectional default stream mode (client sends first)") { + OP_SIMPLE_PAIR_CONN(); + OP_SET_DEFAULT_STREAM_MODE(C, SSL_DEFAULT_STREAM_MODE_AUTO_UNI); + OP_WRITE(C, "apple", 5); + + OP_ACCEPT_CONN_WAIT(L, S, 0); + OP_READ_EXPECT(S, "apple", 5); + OP_WRITE_FAIL(S); } DEF_SCRIPT(script_8, "place holder for multistrem script_8") From 783baebb177ec39bfdfa101f38995a0916468aec Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Wed, 24 Jun 2026 23:20:54 +0700 Subject: [PATCH 111/349] Port script_8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Saša Nedvědický Reviewed-by: Norbert Pocs MergeDate: Tue Jun 30 07:54:50 2026 (Merged from https://github.com/openssl/openssl/pull/31707) --- test/quic_multistream_test.c | 10 +--------- test/radix/quic_tests.c | 12 +++++++++++- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/test/quic_multistream_test.c b/test/quic_multistream_test.c index 5bb15f9213..2e7def766e 100644 --- a/test/quic_multistream_test.c +++ b/test/quic_multistream_test.c @@ -2057,15 +2057,7 @@ static const struct script_op script_7[] = { /* 8. Unidirectional default stream mode test (server sends first) */ static const struct script_op script_8[] = { - OP_C_SET_ALPN("ossltest"), - OP_C_CONNECT_WAIT(), - - OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_AUTO_UNI), - OP_S_NEW_STREAM_UNI(a, S_UNI_ID(0)), - OP_S_WRITE(a, "apple", 5), - OP_C_READ_EXPECT(DEFAULT, "apple", 5), - OP_C_WRITE_FAIL(DEFAULT), - + /* test moved to test/radix/quic_tests.c */ OP_END }; diff --git a/test/radix/quic_tests.c b/test/radix/quic_tests.c index dd46002a02..f18a52622b 100644 --- a/test/radix/quic_tests.c +++ b/test/radix/quic_tests.c @@ -871,8 +871,18 @@ DEF_SCRIPT(script_7, "Unidirectional default stream mode (client sends first)") OP_WRITE_FAIL(S); } -DEF_SCRIPT(script_8, "place holder for multistrem script_8") +/* 8. Unidirectional default stream mode test (server sends first) */ +DEF_SCRIPT(script_8, "Unidirectional default stream mode (server sends first)") { + OP_SIMPLE_PAIR_CONN(); + OP_SET_DEFAULT_STREAM_MODE(C, SSL_DEFAULT_STREAM_MODE_AUTO_UNI); + + OP_ACCEPT_CONN_WAIT(L, S, 0); + OP_NEW_STREAM(S, Sa, SSL_STREAM_FLAG_UNI); + OP_WRITE(Sa, "apple", 5); + + OP_READ_EXPECT(C, "apple", 5); + OP_WRITE_FAIL(C); } DEF_SCRIPT(script_9, "place holder for multistrem script_9") From 7f2003289a7f657ad442ef66f02993f015aadcf0 Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Wed, 24 Jun 2026 23:21:16 +0700 Subject: [PATCH 112/349] Port script_9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Saša Nedvědický Reviewed-by: Norbert Pocs MergeDate: Tue Jun 30 07:54:51 2026 (Merged from https://github.com/openssl/openssl/pull/31707) --- test/quic_multistream_test.c | 11 +---------- test/radix/quic_tests.c | 13 ++++++++++++- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/test/quic_multistream_test.c b/test/quic_multistream_test.c index 2e7def766e..ea77aac694 100644 --- a/test/quic_multistream_test.c +++ b/test/quic_multistream_test.c @@ -2063,16 +2063,7 @@ static const struct script_op script_8[] = { /* 9. Unidirectional default stream mode test (server sends first on bidi) */ static const struct script_op script_9[] = { - OP_C_SET_ALPN("ossltest"), - OP_C_CONNECT_WAIT(), - - OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_AUTO_UNI), - OP_S_NEW_STREAM_BIDI(a, S_BIDI_ID(0)), - OP_S_WRITE(a, "apple", 5), - OP_C_READ_EXPECT(DEFAULT, "apple", 5), - OP_C_WRITE(DEFAULT, "orange", 6), - OP_S_READ_EXPECT(a, "orange", 6), - + /* test moved to test/radix/quic_tests.c */ OP_END }; diff --git a/test/radix/quic_tests.c b/test/radix/quic_tests.c index f18a52622b..a770f9f018 100644 --- a/test/radix/quic_tests.c +++ b/test/radix/quic_tests.c @@ -885,8 +885,19 @@ DEF_SCRIPT(script_8, "Unidirectional default stream mode (server sends first)") OP_WRITE_FAIL(C); } -DEF_SCRIPT(script_9, "place holder for multistrem script_9") +/* 9. Unidirectional default stream mode test (server sends first on bidi) */ +DEF_SCRIPT(script_9, "Unidirectional default stream mode (server sends bidi first)") { + OP_SIMPLE_PAIR_CONN(); + OP_SET_DEFAULT_STREAM_MODE(C, SSL_DEFAULT_STREAM_MODE_AUTO_UNI); + + OP_ACCEPT_CONN_WAIT(L, S, 0); + OP_NEW_STREAM(S, Sa, 0 /* bidirectional */); + OP_WRITE(Sa, "apple", 5); + + OP_READ_EXPECT(C, "apple", 5); + OP_WRITE(C, "orange", 6); + OP_READ_EXPECT(Sa, "orange", 6); } DEF_SCRIPT(script_10, "place holder for multistrem script_10") From e6220f6e313d4036533ded475fb510928a62cfef Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Wed, 24 Jun 2026 23:29:08 +0700 Subject: [PATCH 113/349] Fix OP_READ_FAIL_WAIT macro definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Saša Nedvědický Reviewed-by: Norbert Pocs MergeDate: Tue Jun 30 07:54:52 2026 (Merged from https://github.com/openssl/openssl/pull/31707) --- test/radix/quic_ops.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/radix/quic_ops.c b/test/radix/quic_ops.c index 27e5a3705d..5d69d88de0 100644 --- a/test/radix/quic_ops.c +++ b/test/radix/quic_ops.c @@ -1079,9 +1079,9 @@ err: OP_FUNC(hf_read_fail)) #define OP_READ_FAIL_WAIT(name) \ - (OP_SELECT_SSL(0, name), \ - OP_PUSH_U64(1), \ - OP_FUNC(hf_read_fail) + (OP_SELECT_SSL(0, name), \ + OP_PUSH_U64(1), \ + OP_FUNC(hf_read_fail)) #define OP_POP_ERR() \ OP_FUNC(hf_pop_err) From b06d2b2c92eb3e65484e8e3c6f263e1ba4c72cc1 Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Sun, 14 Jun 2026 16:12:01 +0900 Subject: [PATCH 114/349] test: run RIO notifier smoke test everywhere The RIO notifier smoke test is currently limited to Windows targets in both the build metadata and the test recipe. The test exercises the notifier abstraction and can run on other platforms as well, so this removes the Windows-only guards. The test remains conditional on QUIC being enabled. Reviewed-by: Matt Caswell Reviewed-by: Eugene Syromiatnikov MergeDate: Tue Jun 30 08:49:54 2026 (Merged from https://github.com/openssl/openssl/pull/31494) --- test/build.info | 7 +------ test/recipes/70-test_rio_notifier.t | 3 --- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/test/build.info b/test/build.info index 43812e3989..20aec5eb3e 100644 --- a/test/build.info +++ b/test/build.info @@ -87,10 +87,7 @@ IF[{- !$disabled{tests} -}] IF[{- !$disabled{quic} -}] PROGRAMS{noinst}=priority_queue_test quicfaultstest quicapitest \ - quic_newcid_test quic_srt_gen_test - IF[{- $config{target} =~ /^(?:VC-|mingw|BC-)/ -}] - PROGRAMS{noinst}=rio_notifier_test - ENDIF + quic_newcid_test quic_srt_gen_test rio_notifier_test ENDIF IF[{- !$disabled{quic} && !$disabled{qlog} -}] @@ -392,11 +389,9 @@ IF[{- !$disabled{tests} -}] DEPEND[packettest]=../libcrypto libtestutil.a IF[{- !$disabled{'quic'} -}] - IF[{- $config{target} =~ /^(?:VC-|mingw|BC-)/ -}] SOURCE[rio_notifier_test]=rio_notifier_test.c INCLUDE[rio_notifier_test]=.. ../include ../apps/include DEPEND[rio_notifier_test]=../libcrypto.a ../libssl.a libtestutil.a - ENDIF SOURCE[quic_wire_test]=quic_wire_test.c INCLUDE[quic_wire_test]=../include ../apps/include diff --git a/test/recipes/70-test_rio_notifier.t b/test/recipes/70-test_rio_notifier.t index a0224f8a88..016cc01216 100644 --- a/test/recipes/70-test_rio_notifier.t +++ b/test/recipes/70-test_rio_notifier.t @@ -14,9 +14,6 @@ setup("test_rio_notifier"); plan skip_all => "RIO notifier tests require QUIC" if disabled("quic"); -plan skip_all => "RIO notifier WSA tests are only available on Windows" - if config("target") !~ /^(?:VC-|mingw|BC-)/i; - plan tests => 1; ok(run(test(["rio_notifier_test"]))); From f5a5e89044174e1b9509841baa2b334b368bdadc Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Wed, 24 Jun 2026 15:55:48 -0400 Subject: [PATCH 115/349] ensure writes are syncronized on windows in CRYPTO_THREAD_run_once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We've tried to fix this properly using InitOnceExecuteOnce, but it results in an ABI breakage, so we're doing it this way. on windows, CRYPTO_THREAD_run_once, on weakly memory ordered systems, may complete the write of the run once variable lock before some of the writes made by the init callback routine complete. The result is that on a heavily multithreaded application, other therads may see the data that was meant to be in an initalized state, as in some erroneous in-between state, leading to errors. Fix it by inserting a full memory barrier after we return from the init callback, and prior to setting the run once variable to ONCE_DONE. Reviewed-by: Saša Nedvědický Reviewed-by: Nikola Pajkovsky Reviewed-by: Norbert Pocs MergeDate: Tue Jun 30 08:51:16 2026 (Merged from https://github.com/openssl/openssl/pull/31713) --- crypto/threads_win.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crypto/threads_win.c b/crypto/threads_win.c index b52c8dd520..d777010cd8 100644 --- a/crypto/threads_win.c +++ b/crypto/threads_win.c @@ -527,6 +527,20 @@ int CRYPTO_THREAD_run_once(CRYPTO_ONCE *once, void (*init)(void)) result = InterlockedCompareExchange(lock, ONCE_ININIT, ONCE_UNINITED); if (result == ONCE_UNINITED) { init(); + /* + * On weakly ordered systems, it may happen that the write to *lock + * below completes prior to some writes in whatever the init() + * callback routine above may do. In this case, other threads + * entering here may see unsynchronized data in whatever the init + * routine initializes, leading to erroneous behavior. + * + * We should use InitOnceExecuteOnce here to implement this, but + * doing so requires that we modify the definition of the + * CRYPTO_ONCE type, which is an ABI breakage. So instead + * just insert a memory barrier here to ensure that any pending + * writes are flushed to memory prior to setting ONCE_DONE below + */ + MemoryBarrier(); *lock = ONCE_DONE; return 1; } From 7d4475bdf251bae7ed504e96230dcf12d7329f6d Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Fri, 19 Jun 2026 15:58:59 +0200 Subject: [PATCH 116/349] statem: fail handshake if there is memory failure in negotiate_dhe Previously this just resulted in the skip of the group but such failure should result in a proper error. This is not a big issue but it impacts mfail tests so it would be good to fail. Reviewed-by: Tomas Mraz Reviewed-by: Daniel Kubec MergeDate: Tue Jun 30 08:54:47 2026 (Merged from https://github.com/openssl/openssl/pull/31618) --- ssl/statem/extensions_clnt.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c index f9ece09df3..0b220965c1 100644 --- a/ssl/statem/extensions_clnt.c +++ b/ssl/statem/extensions_clnt.c @@ -201,6 +201,8 @@ EXT_RETURN tls_construct_ctos_srp(SSL_CONNECTION *s, WPACKET *pkt, * with the sole exception of psk-ke resumption, provided the client is sure * that the server will not want elect a full handshake. The check type then * indicates whether ECDHE or FFDHE negotiation should be performed. + * + * It returns 1 if negotiation is supported, 0 if it's not and -1 on error. */ static int negotiate_dhe(SSL_CONNECTION *s, dhe_check_t check_type, int min_version, int max_version) @@ -214,6 +216,8 @@ static int negotiate_dhe(SSL_CONNECTION *s, dhe_check_t check_type, /* See if we support any EC or FFDHE ciphersuites */ cipher_stack = SSL_get1_supported_ciphers(ssl); + if (cipher_stack == NULL) + return -1; end = sk_SSL_CIPHER_num(cipher_stack); for (i = 0; i < end; i++) { const SSL_CIPHER *c = sk_SSL_CIPHER_value(cipher_stack, i); @@ -263,15 +267,20 @@ EXT_RETURN tls_construct_ctos_ec_pt_formats(SSL_CONNECTION *s, WPACKET *pkt, { const unsigned char *pformats; size_t num_formats; - int reason, min_version, max_version; + int reason, min_version, max_version, dhe_result; reason = ssl_get_min_max_version(s, &min_version, &max_version, NULL); if (reason != 0) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, reason); return EXT_RETURN_FAIL; } - if (!negotiate_dhe(s, ptfmt_check, min_version, max_version)) + dhe_result = negotiate_dhe(s, ptfmt_check, min_version, max_version); + if (dhe_result == 0) return EXT_RETURN_NOT_SENT; + if (dhe_result < 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } tls1_get_formatlist(s, &pformats, &num_formats); if (num_formats == 0) @@ -314,7 +323,11 @@ EXT_RETURN tls_construct_ctos_supported_groups(SSL_CONNECTION *s, WPACKET *pkt, */ use_ecdhe = negotiate_dhe(s, ecdhe_check, min_version, max_version); use_ffdhe = negotiate_dhe(s, ffdhe_check, min_version, max_version); - if (!use_ecdhe && !use_ffdhe + if (use_ecdhe < 0 || use_ffdhe < 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + if (use_ecdhe == 0 && use_ffdhe == 0 && (dtls ? DTLS_VERSION_LE(max_version, DTLS1_2_VERSION) : (max_version <= TLS1_2_VERSION))) return EXT_RETURN_NOT_SENT; From c36a9b463e765001c2d558bb73f06b0bf8612158 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Fri, 19 Jun 2026 18:52:37 +0200 Subject: [PATCH 117/349] statem: add direct tests for tls_construct_client_hello Add a new test which calls tls_construct_client_hello() directly, creating an SSL_CONNECTION enough to invoke the construct function without making a full handshake, then verifies the produced ClientHello structurally and by round-tripping it through the server-side tls_process_client_hello(). The covered branches include TLS 1.3 (with and without middlebox compat), TLS 1.2, DTLS, session resumption, HelloRetryRequest, the DTLS cookie and client_random reuse paths, the deterministic error branches (WPACKET overflow and no usable ciphers), and the ECH wrapper path (happy path with server-side decryption, the TLS 1.2 version error, and OOM). Out-of-memory branches are exercised with the mfail tests. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Tomas Mraz Reviewed-by: Daniel Kubec MergeDate: Tue Jun 30 08:54:48 2026 (Merged from https://github.com/openssl/openssl/pull/31618) --- test/build.info | 5 + test/recipes/25-test_statem_clnt.t | 19 + test/statem_clnt_construct_test.c | 734 +++++++++++++++++++++++++++++ 3 files changed, 758 insertions(+) create mode 100644 test/recipes/25-test_statem_clnt.t create mode 100644 test/statem_clnt_construct_test.c diff --git a/test/build.info b/test/build.info index 20aec5eb3e..ac51116e56 100644 --- a/test/build.info +++ b/test/build.info @@ -1167,6 +1167,11 @@ IF[{- !$disabled{tests} -}] INCLUDE[ext_internal_test]=.. ../include ../apps/include DEPEND[ext_internal_test]=../libcrypto.a ../libssl.a libtestutil.a + PROGRAMS{noinst}=statem_clnt_construct_test + SOURCE[statem_clnt_construct_test]=statem_clnt_construct_test.c + INCLUDE[statem_clnt_construct_test]=.. ../include ../apps/include + DEPEND[statem_clnt_construct_test]=../libcrypto.a ../libssl.a libtestutil.a + PROGRAMS{noinst}=algorithmid_test SOURCE[algorithmid_test]=algorithmid_test.c INCLUDE[algorithmid_test]=../include ../apps/include diff --git a/test/recipes/25-test_statem_clnt.t b/test/recipes/25-test_statem_clnt.t new file mode 100644 index 0000000000..4352029eed --- /dev/null +++ b/test/recipes/25-test_statem_clnt.t @@ -0,0 +1,19 @@ +#! /usr/bin/env perl +# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +use OpenSSL::Test; +use OpenSSL::Test::Utils; + +setup("test_statem_clnt"); + +plan skip_all => "No TLS protocol enabled in this build" + if disabled("tls1_2") && disabled("tls1_3"); + +plan tests => 1; + +ok(run(test(["statem_clnt_construct_test"])), "statem_clnt construct functions"); diff --git a/test/statem_clnt_construct_test.c b/test/statem_clnt_construct_test.c new file mode 100644 index 0000000000..43dc330dec --- /dev/null +++ b/test/statem_clnt_construct_test.c @@ -0,0 +1,734 @@ +/* + * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * Direct tests for tls_construct_client_hello(): prime a client SSL_CONNECTION + * enough to call the construct function without a full handshake, then check + * the result structurally and by round-tripping it through the server parser. + * OOM branches are covered with mfail tests. + */ + +#include +#ifndef OPENSSL_NO_ECH +#include +#include +#endif + +#include "internal/ssl_unwrap.h" +#include "../ssl/ssl_local.h" +#include "../ssl/statem/statem_local.h" +#include "testutil.h" + +/* + * TLS 1.3 needs a key-share group, so it is unusable when both EC and DH are + * disabled even though the protocol itself is compiled in (e.g. no-bulk). + */ +#if defined(OPENSSL_NO_TLS1_3) \ + || (defined(OPENSSL_NO_EC) && defined(OPENSSL_NO_DH)) +#define OSSL_NO_USABLE_TLS1_3 +#endif + +/* ECH needs a curve (EC/ECX) for its default suite and usable TLS 1.3. */ +#if defined(OPENSSL_NO_ECH) || defined(OPENSSL_NO_EC) \ + || defined(OPENSSL_NO_ECX) || defined(OSSL_NO_USABLE_TLS1_3) +#define OSSL_NO_USABLE_ECH +#endif + +/* + * Helpers down to prime_ssl() are generic and reusable by tests for any + * statem_clnt construct function; the ClientHello-specific code follows. + */ + +/* Connection configuration shared by the construct tests. */ +typedef struct { + int is_dtls; + int min_ver; /* 0 for library default */ + int max_ver; /* 0 for library default */ + int clear_midbox; /* clear SSL_OP_ENABLE_MIDDLEBOX_COMPAT */ +} CH_CONFIG; + +static const SSL_METHOD *client_method(const CH_CONFIG *cfg) +{ + return cfg->is_dtls ? DTLS_client_method() : TLS_client_method(); +} + +/* Handshake message header length (msg precedes the ClientHello body). */ +static size_t hdr_len(const CH_CONFIG *cfg) +{ + return cfg->is_dtls ? DTLS1_HM_HEADER_LENGTH : SSL3_HM_HEADER_LENGTH; +} + +static const SSL_METHOD *server_method(const CH_CONFIG *cfg) +{ + return cfg->is_dtls ? DTLS_server_method() : TLS_server_method(); +} + +static SSL_CTX *new_ctx(const CH_CONFIG *cfg, const SSL_METHOD *meth) +{ + SSL_CTX *ctx = SSL_CTX_new(meth); + + if (ctx == NULL) + return NULL; + if ((cfg->min_ver != 0 + && !SSL_CTX_set_min_proto_version(ctx, cfg->min_ver)) + || (cfg->max_ver != 0 + && !SSL_CTX_set_max_proto_version(ctx, cfg->max_ver))) { + SSL_CTX_free(ctx); + return NULL; + } + return ctx; +} + +/* + * Set up the init_buf and handshake state the write state machine would have + * established before calling a construct function. For the client a WPACKET + * with the handshake header is emitted into init_buf. initbuf_len of 0 means + * full size; a small value exercises WPACKET failures. + */ +static int prime_ssl(SSL *ssl, int is_client, size_t initbuf_len, WPACKET *pkt) +{ + SSL_CONNECTION *s = SSL_CONNECTION_FROM_SSL(ssl); + + if (!TEST_ptr(s)) + return 0; + + if (is_client) + SSL_set_connect_state(ssl); + else + SSL_set_accept_state(ssl); + + if (initbuf_len == 0) + initbuf_len = SSL3_RT_MAX_PLAIN_LENGTH; + + if (!TEST_ptr(s->init_buf = BUF_MEM_new()) + || !TEST_true(BUF_MEM_grow(s->init_buf, initbuf_len))) + return 0; + + if (!TEST_true(tls_setup_handshake(s))) + return 0; + + if (pkt != NULL + && (!TEST_true(WPACKET_init(pkt, s->init_buf)) + || !TEST_true(ssl_set_handshake_header(s, pkt, + SSL3_MT_CLIENT_HELLO)))) + return 0; + + return 1; +} + +/* + * =========================================================================== + * tls_construct_client_hello + * =========================================================================== + */ + +/* Finalize the constructed message and return its bytes (header + body). */ +static int finish_ch(SSL *ssl, WPACKET *pkt, unsigned char **msg, + size_t *msglen) +{ + SSL_CONNECTION *s = SSL_CONNECTION_FROM_SSL(ssl); + + if (!TEST_true(ssl_close_construct_packet(s, pkt, SSL3_MT_CLIENT_HELLO)) + || !TEST_true(WPACKET_get_total_written(pkt, msglen)) + || !TEST_true(WPACKET_finish(pkt))) + return 0; + + *msg = (unsigned char *)s->init_buf->data; + return 1; +} + +/* Recover the session_id length, the main branching difference in construct. */ +static int get_ch_sessid_len(const CH_CONFIG *cfg, const unsigned char *msg, + size_t msglen, size_t *sidlen) +{ + PACKET pkt = { 0 }, sessid = { 0 }, cookie = { 0 }; + PACKET ciphers = { 0 }, comp = { 0 }; + unsigned int legacy_version; + size_t hl = hdr_len(cfg); + + if (!TEST_size_t_gt(msglen, hl) + || !TEST_true(PACKET_buf_init(&pkt, msg + hl, msglen - hl))) + return 0; + + if (!TEST_true(PACKET_get_net_2(&pkt, &legacy_version)) + || !TEST_true(PACKET_forward(&pkt, SSL3_RANDOM_SIZE)) + || !TEST_true(PACKET_get_length_prefixed_1(&pkt, &sessid))) + return 0; + + if (cfg->is_dtls + && !TEST_true(PACKET_get_length_prefixed_1(&pkt, &cookie))) + return 0; + + /* Sanity: cipher list non-empty, compression present and contains NULL. */ + if (!TEST_true(PACKET_get_length_prefixed_2(&pkt, &ciphers)) + || !TEST_size_t_gt(PACKET_remaining(&ciphers), 0) + || !TEST_true(PACKET_get_length_prefixed_1(&pkt, &comp)) + || !TEST_size_t_gt(PACKET_remaining(&comp), 0)) + return 0; + + *sidlen = PACKET_remaining(&sessid); + return 1; +} + +/* Run the produced ClientHello body through the server-side parser. */ +static int roundtrip_process_ch(const CH_CONFIG *cfg, const unsigned char *msg, + size_t msglen) +{ + SSL_CTX *sctx = NULL; + SSL *ssl = NULL; + SSL_CONNECTION *s; + PACKET pkt; + int ret = 0; + + if (!TEST_ptr(sctx = new_ctx(cfg, server_method(cfg))) + || !TEST_ptr(ssl = SSL_new(sctx))) + goto err; + + if (!prime_ssl(ssl, 0, 0, NULL)) + goto err; + s = SSL_CONNECTION_FROM_SSL(ssl); + + if (!TEST_true(PACKET_buf_init(&pkt, msg + hdr_len(cfg), + msglen - hdr_len(cfg)))) + goto err; + + if (!TEST_int_eq(tls_process_client_hello(s, &pkt), + MSG_PROCESS_CONTINUE_PROCESSING)) + goto err; + + ret = 1; +err: + SSL_free(ssl); + SSL_CTX_free(sctx); + return ret; +} + +/* + * Construct a ClientHello, assert its session_id length and optionally + * round-trip it. prep injects extra state just before construct; + * expect_random asserts the produced client random (to check reuse). + * Both may be NULL. + */ +static int do_construct_ch(const CH_CONFIG *cfg, + int (*prep)(SSL_CONNECTION *s), size_t expect_sidlen, int roundtrip, + const unsigned char *expect_random) +{ + SSL_CTX *cctx = NULL; + SSL *ssl = NULL; + SSL_CONNECTION *s; + WPACKET pkt; + unsigned char *msg = NULL; + size_t msglen = 0, sidlen = 0; + int ret = 0; + + if (!TEST_ptr(cctx = new_ctx(cfg, client_method(cfg))) + || !TEST_ptr(ssl = SSL_new(cctx))) + goto err; + if (cfg->clear_midbox) + SSL_clear_options(ssl, SSL_OP_ENABLE_MIDDLEBOX_COMPAT); + + if (!prime_ssl(ssl, 1, 0, &pkt)) + goto err; + s = SSL_CONNECTION_FROM_SSL(ssl); + + if (prep != NULL && !prep(s)) { + WPACKET_cleanup(&pkt); + goto err; + } + + if (!TEST_int_eq(tls_construct_client_hello(s, &pkt), CON_FUNC_SUCCESS)) { + WPACKET_cleanup(&pkt); + goto err; + } + if (!finish_ch(ssl, &pkt, &msg, &msglen)) + goto err; + + if (!get_ch_sessid_len(cfg, msg, msglen, &sidlen) + || !TEST_size_t_eq(sidlen, expect_sidlen)) + goto err; + + /* The client random follows the 2-byte legacy_version in the body. */ + if (expect_random != NULL + && !TEST_mem_eq(msg + hdr_len(cfg) + 2, SSL3_RANDOM_SIZE, + expect_random, SSL3_RANDOM_SIZE)) + goto err; + + if (roundtrip && !roundtrip_process_ch(cfg, msg, msglen)) + goto err; + + ret = 1; +err: + SSL_free(ssl); + SSL_CTX_free(cctx); + return ret; +} + +/* Expect tls_construct_client_hello() to fail (CON_FUNC_ERROR). */ +static int do_construct_ch_expect_fail(const CH_CONFIG *cfg, + int (*prep)(SSL_CONNECTION *s), + size_t initbuf_len, int empty_ciphers) +{ + SSL_CTX *cctx = NULL; + SSL *ssl = NULL; + SSL_CONNECTION *s; + WPACKET pkt; + int have_pkt = 0; + int ret = 0; + + if (!TEST_ptr(cctx = new_ctx(cfg, client_method(cfg))) + || !TEST_ptr(ssl = SSL_new(cctx))) + goto err; + + if (empty_ciphers) { + /* Leave no usable cipher: aNULL is disabled at default sec level. */ + int r1 = SSL_set_ciphersuites(ssl, ""); + int r2 = SSL_set_cipher_list(ssl, "aNULL"); + + (void)r1; + (void)r2; + } + + if (!prime_ssl(ssl, 1, initbuf_len, &pkt)) + goto err; + have_pkt = 1; + s = SSL_CONNECTION_FROM_SSL(ssl); + + if (prep != NULL && !prep(s)) + goto err; + + if (!TEST_int_eq(tls_construct_client_hello(s, &pkt), CON_FUNC_ERROR)) + goto err; + + ret = 1; +err: + if (have_pkt) + WPACKET_cleanup(&pkt); + SSL_free(ssl); + SSL_CTX_free(cctx); + return ret; +} + +/* TLS 1.2 happy-path / session tests */ + +#ifndef OPENSSL_NO_TLS1_2 +static int test_construct_ch_tls12(void) +{ + CH_CONFIG cfg = { 0, TLS1_2_VERSION, TLS1_2_VERSION, 0 }; + + /* Fresh (non-resumable) TLS 1.2 session: empty session id. */ + return do_construct_ch(&cfg, NULL, 0, 1, NULL); +} + +/* Resumable non-TLS1.3 session: construct reuses its session id. */ +static int prep_resume(SSL_CONNECTION *s) +{ + SSL *ssl = SSL_CONNECTION_GET_SSL(s); + SSL_SESSION *sess = SSL_SESSION_new(); + int ret = 0; + + if (!TEST_ptr(sess)) + goto err; + sess->ssl_version = TLS1_2_VERSION; + sess->session_id_length = sizeof(sess->session_id); + memset(sess->session_id, 0x5A, sess->session_id_length); + sess->cipher = sk_SSL_CIPHER_value(SSL_get_ciphers(ssl), 0); + if (!TEST_ptr(sess->cipher) || !TEST_true(SSL_set_session(ssl, sess))) + goto err; + ret = 1; +err: + SSL_SESSION_free(sess); + return ret; +} + +static int test_construct_ch_resume(void) +{ + CH_CONFIG cfg = { 0, TLS1_2_VERSION, TLS1_2_VERSION, 0 }; + + /* Resumed session: the pre-loaded 32-byte session id is sent. */ + return do_construct_ch(&cfg, prep_resume, SSL_MAX_SSL_SESSION_ID_LENGTH, 1, + NULL); +} +#endif /* OPENSSL_NO_TLS1_2 */ + +/* TLS 1.3 happy-path / HRR tests */ + +#ifndef OSSL_NO_USABLE_TLS1_3 +static int test_construct_ch_tls13(void) +{ + CH_CONFIG cfg = { 0, TLS1_3_VERSION, TLS1_3_VERSION, 0 }; + + /* Middlebox compat is on by default: a random 32-byte session id. */ + return do_construct_ch(&cfg, NULL, SSL_MAX_SSL_SESSION_ID_LENGTH, 1, NULL); +} + +static int test_construct_ch_tls13_no_middlebox(void) +{ + CH_CONFIG cfg = { 0, TLS1_3_VERSION, TLS1_3_VERSION, 1 }; + + /* No middlebox compat: empty session id. */ + return do_construct_ch(&cfg, NULL, 0, 1, NULL); +} + +static int prep_hrr(SSL_CONNECTION *s) +{ + SSL *ssl = SSL_CONNECTION_GET_SSL(s); + SSL_SESSION *sess = SSL_SESSION_new(); + int ret = 0; + + /* Under HRR construct skips ssl_get_new_session(), so a session must + * already exist (created for the first ClientHello). */ + if (!TEST_ptr(sess)) + goto err; + sess->ssl_version = TLS1_3_VERSION; + sess->cipher = sk_SSL_CIPHER_value(SSL_get_ciphers(ssl), 0); + if (!TEST_ptr(sess->cipher) || !TEST_true(SSL_set_session(ssl, sess))) + goto err; + s->hello_retry_request = SSL_HRR_COMPLETE; + ret = 1; +err: + SSL_SESSION_free(sess); + return ret; +} + +static int test_construct_ch_hrr(void) +{ + CH_CONFIG cfg = { 0, TLS1_3_VERSION, TLS1_3_VERSION, 0 }; + + /* TLS 1.3 + middlebox compat still emits a 32-byte session id under HRR. */ + return do_construct_ch(&cfg, prep_hrr, SSL_MAX_SSL_SESSION_ID_LENGTH, 1, + NULL); +} +#endif /* OSSL_NO_USABLE_TLS1_3 */ + +/* DTLS happy-path / cookie / random-reuse tests */ + +#ifndef OPENSSL_NO_DTLS +static int test_construct_ch_dtls(void) +{ + CH_CONFIG cfg = { 1, 0, 0, 0 }; + + /* DTLS uses a different server parser path; skip the roundtrip. */ + return do_construct_ch(&cfg, NULL, 0, 0, NULL); +} + +static int prep_dtls_cookie(SSL_CONNECTION *s) +{ + /* A HelloVerifyRequest cookie is echoed in the ClientHello. */ + static const unsigned char cookie[16] = { + 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x04, + 0xc0, 0x05, 0xc0, 0x06, 0xc0, 0x07, 0xc0, 0x08 + }; + + memcpy(s->d1->cookie, cookie, sizeof(cookie)); + s->d1->cookie_len = sizeof(cookie); + return 1; +} + +static int test_construct_ch_dtls_cookie(void) +{ + CH_CONFIG cfg = { 1, 0, 0, 0 }; + + return do_construct_ch(&cfg, prep_dtls_cookie, 0, 0, NULL); +} + +/* A recognizable, all-non-zero client random to detect reuse. */ +static const unsigned char reused_random[SSL3_RANDOM_SIZE] = { + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf +}; + +static int prep_dtls_client_random(SSL_CONNECTION *s) +{ + /* DTLS reuses an already-set client random (HelloVerifyRequest reply). */ + memcpy(s->s3.client_random, reused_random, sizeof(reused_random)); + return 1; +} + +static int test_construct_ch_dtls_client_random(void) +{ + CH_CONFIG cfg = { 1, 0, 0, 0 }; + + return do_construct_ch(&cfg, prep_dtls_client_random, 0, 0, reused_random); +} +#endif /* OPENSSL_NO_DTLS */ + +/* Deterministic error-branch tests */ + +static int test_construct_ch_small_buf(void) +{ + /* Default version: the overflow is version-independent. */ + CH_CONFIG cfg = { 0, 0, 0, 0 }; + SSL_CTX *cctx = NULL; + SSL *ssl = NULL; + SSL_CONNECTION *s; + WPACKET pkt; + /* + * Fixed, non-growable buffer that overflows part-way through the body (a + * BUF_MEM-backed WPACKET would just grow), hitting a WPACKET write failure. + */ + unsigned char buf[40]; + int have_pkt = 0; + int ret = 0; + + if (!TEST_ptr(cctx = new_ctx(&cfg, client_method(&cfg))) + || !TEST_ptr(ssl = SSL_new(cctx))) + goto err; + SSL_set_connect_state(ssl); + s = SSL_CONNECTION_FROM_SSL(ssl); + if (!TEST_ptr(s) + || !TEST_ptr(s->init_buf = BUF_MEM_new()) + || !TEST_true(BUF_MEM_grow(s->init_buf, SSL3_RT_MAX_PLAIN_LENGTH)) + || !TEST_true(tls_setup_handshake(s))) + goto err; + + if (!TEST_true(WPACKET_init_static_len(&pkt, buf, sizeof(buf), 0))) + goto err; + have_pkt = 1; + if (!TEST_true(ssl_set_handshake_header(s, &pkt, SSL3_MT_CLIENT_HELLO))) + goto err; + + if (!TEST_int_eq(tls_construct_client_hello(s, &pkt), CON_FUNC_ERROR)) + goto err; + + ret = 1; +err: + if (have_pkt) + WPACKET_cleanup(&pkt); + SSL_free(ssl); + SSL_CTX_free(cctx); + return ret; +} + +static int test_construct_ch_no_ciphers(void) +{ + CH_CONFIG cfg = { 0, 0, 0, 0 }; + + return do_construct_ch_expect_fail(&cfg, NULL, 0, 1); +} + +/* Allocation-failure (mfail) tests */ + +/* Compiled when any mfail caller below (TLS 1.2, TLS 1.3 or ECH) is. */ +#if !defined(OSSL_NO_USABLE_TLS1_3) || !defined(OPENSSL_NO_TLS1_2) +static int mfail_construct_ch_common(const CH_CONFIG *cfg, + int (*prep)(SSL_CONNECTION *s)) +{ + SSL_CTX *cctx = NULL; + SSL *ssl = NULL; + SSL_CONNECTION *s; + WPACKET pkt; + int ok = 0; + int ret = 0; + + if (!TEST_ptr(cctx = new_ctx(cfg, client_method(cfg))) + || !TEST_ptr(ssl = SSL_new(cctx))) + goto err; + if (cfg->clear_midbox) + SSL_clear_options(ssl, SSL_OP_ENABLE_MIDDLEBOX_COMPAT); + + if (!prime_ssl(ssl, 1, 0, &pkt)) + goto err; + s = SSL_CONNECTION_FROM_SSL(ssl); + + if (prep != NULL && !prep(s)) { + WPACKET_cleanup(&pkt); + goto err; + } + + MFAIL_start(); + ok = (tls_construct_client_hello(s, &pkt) == CON_FUNC_SUCCESS); + MFAIL_end(); + + WPACKET_cleanup(&pkt); + + /* 1 on clean success, 0 on an injected allocation failure. */ + ret = ok ? 1 : 0; +err: + SSL_free(ssl); + SSL_CTX_free(cctx); + return ret; +} +#endif + +#if !defined(OSSL_NO_USABLE_TLS1_3) && !defined(OPENSSL_NO_CACHED_FETCH) +static int mfail_construct_ch_tls13(void) +{ + CH_CONFIG cfg = { 0, TLS1_3_VERSION, TLS1_3_VERSION, 0 }; + + return mfail_construct_ch_common(&cfg, NULL); +} +#endif + +#ifndef OPENSSL_NO_TLS1_2 +static int mfail_construct_ch_tls12(void) +{ + CH_CONFIG cfg = { 0, TLS1_2_VERSION, TLS1_2_VERSION, 0 }; + + return mfail_construct_ch_common(&cfg, NULL); +} +#endif + +#ifndef OSSL_NO_USABLE_ECH +/* ECH path tests */ + +/* Attach an ECH config so construct takes the ECH wrapper path. */ +static int prep_ech(SSL_CONNECTION *s) +{ + SSL *ssl = SSL_CONNECTION_GET_SSL(s); + OSSL_ECHSTORE *es = NULL; + OSSL_HPKE_SUITE suite = OSSL_HPKE_SUITE_DEFAULT; + int ret = 0; + + if (!TEST_ptr(es = OSSL_ECHSTORE_new(NULL, NULL)) + || !TEST_true(OSSL_ECHSTORE_new_config(es, OSSL_ECH_CURRENT_VERSION, 0, + "example.com", suite)) + || !TEST_true(SSL_set1_echstore(ssl, es))) + goto err; + ret = 1; +err: + OSSL_ECHSTORE_free(es); + return ret; +} + +/* + * ECH happy path: the server reuses the client's store, which holds the private + * key needed to decrypt the inner ClientHello, so the round-trip can succeed. + */ +static int test_construct_ch_ech(void) +{ + CH_CONFIG cfg = { 0, TLS1_3_VERSION, TLS1_3_VERSION, 0 }; + SSL_CTX *cctx = NULL, *sctx = NULL; + SSL *cssl = NULL, *sssl = NULL; + SSL_CONNECTION *cs, *ss; + OSSL_ECHSTORE *es = NULL; + OSSL_HPKE_SUITE suite = OSSL_HPKE_SUITE_DEFAULT; + WPACKET pkt; + PACKET rpkt; + unsigned char *msg = NULL; + size_t msglen = 0, sidlen = 0; + int ret = 0; + + if (!TEST_ptr(es = OSSL_ECHSTORE_new(NULL, NULL)) + || !TEST_true(OSSL_ECHSTORE_new_config(es, OSSL_ECH_CURRENT_VERSION, 0, + "example.com", suite))) + goto err; + + /* Client: construct the outer ClientHello with ECH. */ + if (!TEST_ptr(cctx = new_ctx(&cfg, client_method(&cfg))) + || !TEST_ptr(cssl = SSL_new(cctx)) + || !TEST_true(SSL_set1_echstore(cssl, es)) + || !prime_ssl(cssl, 1, 0, &pkt)) + goto err; + cs = SSL_CONNECTION_FROM_SSL(cssl); + if (!TEST_int_eq(tls_construct_client_hello(cs, &pkt), CON_FUNC_SUCCESS)) { + WPACKET_cleanup(&pkt); + goto err; + } + if (!finish_ch(cssl, &pkt, &msg, &msglen) + || !get_ch_sessid_len(&cfg, msg, msglen, &sidlen) + || !TEST_size_t_eq(sidlen, SSL_MAX_SSL_SESSION_ID_LENGTH)) + goto err; + + /* Server: decrypt and process the outer using the same store. */ + if (!TEST_ptr(sctx = new_ctx(&cfg, server_method(&cfg))) + || !TEST_ptr(sssl = SSL_new(sctx)) + || !TEST_true(SSL_set1_echstore(sssl, es)) + || !prime_ssl(sssl, 0, 0, NULL)) + goto err; + ss = SSL_CONNECTION_FROM_SSL(sssl); + if (!TEST_true(PACKET_buf_init(&rpkt, msg + hdr_len(&cfg), + msglen - hdr_len(&cfg))) + || !TEST_int_eq(tls_process_client_hello(ss, &rpkt), + MSG_PROCESS_CONTINUE_PROCESSING)) + goto err; + + ret = 1; +err: + OSSL_ECHSTORE_free(es); + SSL_free(cssl); + SSL_free(sssl); + SSL_CTX_free(cctx); + SSL_CTX_free(sctx); + return ret; +} + +#ifndef OPENSSL_NO_TLS1_2 +static int test_construct_ch_ech_tls12(void) +{ + CH_CONFIG cfg = { 0, TLS1_2_VERSION, TLS1_2_VERSION, 0 }; + + /* ECH requires TLS 1.3: the inner construct fails the version check. */ + return do_construct_ch_expect_fail(&cfg, prep_ech, 0, 0); +} +#endif /* OPENSSL_NO_TLS1_2 */ + +#ifndef OPENSSL_NO_CACHED_FETCH +static int mfail_construct_ch_ech(void) +{ + CH_CONFIG cfg = { 0, TLS1_3_VERSION, TLS1_3_VERSION, 0 }; + + return mfail_construct_ch_common(&cfg, prep_ech); +} +#endif /* OPENSSL_NO_CACHED_FETCH */ +#endif /* OSSL_NO_USABLE_ECH */ + +int setup_tests(void) +{ + ADD_TEST(test_construct_ch_small_buf); + ADD_TEST(test_construct_ch_no_ciphers); + +#ifndef OPENSSL_NO_TLS1_2 + ADD_TEST(test_construct_ch_tls12); + ADD_TEST(test_construct_ch_resume); + ADD_MFAIL_TEST(mfail_construct_ch_tls12); +#endif + +#ifndef OSSL_NO_USABLE_TLS1_3 + ADD_TEST(test_construct_ch_tls13); + ADD_TEST(test_construct_ch_tls13_no_middlebox); + ADD_TEST(test_construct_ch_hrr); +#ifndef OPENSSL_NO_CACHED_FETCH + /* + * The non-cached mfail run takes too long and does not test too much extra + * so better to skip it. + */ +#if defined(OPENSSL_NO_ECX) + /* + * Without ECX the key_share falls back to EC keygen, which makes a + * best-effort param-cache allocation whose failure does not propagate; + * only crash/leak checking is meaningful then. + * + * No caching also needs no check. + */ + ADD_MFAIL_NO_CHECK_TEST(mfail_construct_ch_tls13); +#else + ADD_MFAIL_TEST(mfail_construct_ch_tls13); +#endif /* OPENSSL_NO_ECX */ +#endif /* OPENSSL_NO_CACHED_FETCH */ +#endif /* OSSL_NO_USABLE_TLS1_3 */ + +#ifndef OPENSSL_NO_DTLS + ADD_TEST(test_construct_ch_dtls); + ADD_TEST(test_construct_ch_dtls_cookie); + ADD_TEST(test_construct_ch_dtls_client_random); +#endif + +#ifndef OSSL_NO_USABLE_ECH + ADD_TEST(test_construct_ch_ech); +#ifndef OPENSSL_NO_TLS1_2 + ADD_TEST(test_construct_ch_ech_tls12); +#endif +#ifndef OPENSSL_NO_CACHED_FETCH + ADD_MFAIL_TEST(mfail_construct_ch_ech); +#endif /* OPENSSL_NO_CACHED_FETCH */ +#endif /* OSSL_NO_USABLE_ECH */ + return 1; +} From 1a80366668b654d089bdc12264597e66606ac271 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 25 Jun 2026 22:49:07 +0200 Subject: [PATCH 118/349] quic: add mfail test for multi-packet RXE Add a record layer test that executes qrx_process_pkt() under mfail on a freshly created qrx whose rx_free freelist is empty. This deterministically lands the qrx_ensure_free_rxe() call for the first packet of a multi-packet datagram among the enumerated injection points which is the precondition for triggering assert failure in qrx_validate_hdr_early(). The test reuses rx_script_5's coalesced Initial+Handshake+1-RTT datagram and only provides the Initial secret, since the bug fires before any decryption is attempted. Assisted-by: Claude:claude-opus-4-7 Reviewed-by: Tomas Mraz Reviewed-by: Nikola Pajkovsky MergeDate: Tue Jun 30 08:56:00 2026 (Merged from https://github.com/openssl/openssl/pull/31331) --- test/quic_record_test.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/test/quic_record_test.c b/test/quic_record_test.c index 85a97a9293..0c9fa3a098 100644 --- a/test/quic_record_test.c +++ b/test/quic_record_test.c @@ -3883,6 +3883,37 @@ static int test_tx_script(int idx) return tx_run_script(tx_scripts[idx]); } +static int test_qrx_multipkt_alloc_failure(void) +{ + int testresult = 0; + struct rx_state s = { 0 }; + OSSL_QRX_PKT *pkt = NULL; + + s.args.short_conn_id_len = 0; + + if (!TEST_true(rx_state_ensure(&s))) + goto err; + + s.rx_dcid = empty_conn_id; + + if (!TEST_true(ossl_quic_provide_initial_secret(NULL, NULL, + &rx_script_5_c2s_init_dcid, 0, s.qrx, NULL))) + goto err; + + if (!TEST_true(ossl_quic_demux_inject(s.demux, rx_script_5_in, + sizeof(rx_script_5_in), NULL, NULL))) + goto err; + + MFAIL_start(); + testresult = ossl_qrx_read_pkt(s.qrx, &pkt); + MFAIL_end(); + +err: + ossl_qrx_pkt_release(pkt); + rx_state_teardown(&s); + return testresult; +} + int setup_tests(void) { ADD_ALL_TESTS(test_rx_script, OSSL_NELEM(rx_scripts)); @@ -3897,5 +3928,6 @@ int setup_tests(void) */ ADD_ALL_TESTS(test_wire_pkt_hdr, NUM_WIRE_PKT_HDR_TESTS + 1); ADD_ALL_TESTS(test_tx_script, OSSL_NELEM(tx_scripts)); + ADD_MFAIL_NO_CHECK_TEST(test_qrx_multipkt_alloc_failure); return 1; } From 801c432b188c6b807684bac4c9b919b3adfd43f7 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 18 Jun 2026 20:45:06 +0200 Subject: [PATCH 119/349] quic: add mfail test for RCIDM This tests memory failures in remote connection ID manager code. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Tomas Mraz Reviewed-by: Nikola Pajkovsky MergeDate: Tue Jun 30 08:56:45 2026 (Merged from https://github.com/openssl/openssl/pull/31602) --- test/quic_rcidm_test.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/test/quic_rcidm_test.c b/test/quic_rcidm_test.c index 619c4c3efe..1b966c93d3 100644 --- a/test/quic_rcidm_test.c +++ b/test/quic_rcidm_test.c @@ -125,8 +125,41 @@ err: return testresult; } +static int test_rcidm_mfail(void) +{ + int testresult = 0; + QUIC_RCIDM *rcidm = NULL; + OSSL_QUIC_FRAME_NEW_CONN_ID ncid = { 0 }; + uint64_t i; + + MFAIL_start(); + + rcidm = ossl_quic_rcidm_new(&cid8_1); + if (rcidm == NULL) + goto err; + + if (!ossl_quic_rcidm_add_from_initial(rcidm, &cid8_2)) + goto err; + + /* Push enough NCIDs to force at least one priority-queue grow/realloc. */ + ncid.conn_id.id_len = 8; + for (i = 2; i < 20; ++i) { + ncid.seq_num = i; + ncid.conn_id.id[0] = (unsigned char)i; + if (!ossl_quic_rcidm_add_from_ncid(rcidm, &ncid)) + goto err; + } + + testresult = 1; +err: + MFAIL_end(); + ossl_quic_rcidm_free(rcidm); + return testresult; +} + int setup_tests(void) { ADD_ALL_TESTS(test_rcidm, 3); + ADD_MFAIL_TEST(test_rcidm_mfail); return 1; } From f9dff993617b7151f1a4282f0e01d39d2d62c942 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 23 Jun 2026 16:21:27 +0200 Subject: [PATCH 120/349] apps: cover the smime multiple -signer parsing path The signerfile != NULL block in smime_main(), reached when more than one -signer is given (including the case where a preceding -inkey leaves keyfile != NULL), was not exercised: the existing multi-signer tests run through the cms command, and the smime app was only ever run with a single signer. Add a two-signer test, with an explicit -inkey, to the pkcs7 test set so it runs through smime when signing. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Tomas Mraz Reviewed-by: Bob Beck MergeDate: Tue Jun 30 08:57:47 2026 (Merged from https://github.com/openssl/openssl/pull/31675) --- test/recipes/80-test_cms.t | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/recipes/80-test_cms.t b/test/recipes/80-test_cms.t index 34b5fc4171..078e50123d 100644 --- a/test/recipes/80-test_cms.t +++ b/test/recipes/80-test_cms.t @@ -121,6 +121,16 @@ my @smime_pkcs7_tests = ( \&final_compare ], + [ "signed content DER format, two RSA signers with explicit -inkey", + [ "{cmd1}", @prov, "-sign", "-in", $smcont, "-outform", "DER", "-nodetach", + "-signer", catfile($smdir, "smrsa3-cert.pem"), + "-inkey", catfile($smdir, "smrsa3-key.pem"), + "-signer", $smrsa1, "-out", "{output}.cms" ], + [ "{cmd2}", @prov, "-verify", "-in", "{output}.cms", "-inform", "DER", + "-CAfile", $smroot, "-out", "{output}.txt" ], + \&final_compare + ], + [ "signed content DER format, DSA key", [ "{cmd1}", @prov, "-sign", "-in", $smcont, "-outform", "DER", "-nodetach", "-signer", catfile($smdir, "smdsa1.pem"), "-out", "{output}.cms" ], From bca4e465685a2eaa170d6d62505d0778fbc35d88 Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Wed, 24 Jun 2026 11:29:05 -0600 Subject: [PATCH 121/349] Clarify the comment in asn1.h.in for ASN1_VALUE I've been down this road a few times and nhorman@ just hit it. While technically true, most of our opaque pointers have internal defninitions. Stop people going down the garden path by explaining what this is Reviewed-by: Neil Horman Reviewed-by: Eugene Syromiatnikov Reviewed-by: Norbert Pocs MergeDate: Tue Jun 30 09:51:37 2026 (Merged from https://github.com/openssl/openssl/pull/31708) --- include/openssl/asn1.h.in | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/openssl/asn1.h.in b/include/openssl/asn1.h.in index 21d9e772c6..a7291738ff 100644 --- a/include/openssl/asn1.h.in +++ b/include/openssl/asn1.h.in @@ -199,7 +199,10 @@ struct asn1_string_table_st { */ typedef struct ASN1_TEMPLATE_st ASN1_TEMPLATE; typedef struct ASN1_TLC_st ASN1_TLC; -/* This is just an opaque pointer */ +/* + * This is deliberately used as an opaque pointer to a structure that + * is never defined. In other words it will behave like void * + */ typedef struct ASN1_VALUE_st ASN1_VALUE; /* Declare ASN1 functions: the implement macro is in asn1t.h */ From c25e1c4b78a8c3186eb2df52232a1995fee7814e Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Fri, 22 May 2026 06:37:24 -0600 Subject: [PATCH 122/349] SSL_OP_SECOP is dead code Nothing set the "vfy" parameters in any of these functions to anything but 0. Remove the parameter and stage things related to SSL_OP_SECOP to go away in the future. Reviewed-by: Neil Horman Reviewed-by: Norbert Pocs MergeDate: Tue Jun 30 10:47:07 2026 (Merged from https://github.com/openssl/openssl/pull/31271) --- apps/lib/s_cb.c | 4 ---- include/openssl/ssl.h.in | 18 +++++++++++------- ssl/ssl_cert.c | 6 +++--- ssl/ssl_local.h | 5 ++--- ssl/ssl_rsa.c | 8 ++++---- ssl/statem/statem_lib.c | 4 ++-- ssl/t1_lib.c | 17 +++++++---------- 7 files changed, 29 insertions(+), 33 deletions(-) diff --git a/apps/lib/s_cb.c b/apps/lib/s_cb.c index c556e961fd..4f2503502b 100644 --- a/apps/lib/s_cb.c +++ b/apps/lib/s_cb.c @@ -1524,10 +1524,7 @@ static STRINT_PAIR callback_types[] = { { "Signature Algorithm mask", SSL_SECOP_SIGALG_MASK }, { "Certificate chain EE key", SSL_SECOP_EE_KEY }, { "Certificate chain CA key", SSL_SECOP_CA_KEY }, - { "Peer Chain EE key", SSL_SECOP_PEER_EE_KEY }, - { "Peer Chain CA key", SSL_SECOP_PEER_CA_KEY }, { "Certificate chain CA digest", SSL_SECOP_CA_MD }, - { "Peer chain CA digest", SSL_SECOP_PEER_CA_MD }, { "SSL compression", SSL_SECOP_COMPRESSION }, { "Session ticket", SSL_SECOP_TICKET }, { NULL } @@ -1561,7 +1558,6 @@ static int security_callback_debug(const SSL *s, const SSL_CTX *ctx, show_nm = 0; break; case SSL_SECOP_CA_MD: - case SSL_SECOP_PEER_CA_MD: cert_md = 1; break; case SSL_SECOP_SIGALG_SUPPORTED: diff --git a/include/openssl/ssl.h.in b/include/openssl/ssl.h.in index f92937e951..fb044db957 100644 --- a/include/openssl/ssl.h.in +++ b/include/openssl/ssl.h.in @@ -2695,8 +2695,18 @@ const CTLOG_STORE *SSL_CTX_get0_ctlog_store(const SSL_CTX *ctx); #define SSL_SECOP_OTHER_SIGALG (5 << 16) #define SSL_SECOP_OTHER_CERT (6 << 16) -/* Indicated operation refers to peer key or certificate */ +/* + * Unused values - these do nothing and are never set. + * They are retained because of API. They should + * be removed next major + */ #define SSL_SECOP_PEER 0x1000 +/* Peer EE key in certificate */ +#define SSL_SECOP_PEER_EE_KEY (SSL_SECOP_EE_KEY | SSL_SECOP_PEER) +/* Peer CA key in certificate */ +#define SSL_SECOP_PEER_CA_KEY (SSL_SECOP_CA_KEY | SSL_SECOP_PEER) +/* Peer CA digest algorithm in certificate */ +#define SSL_SECOP_PEER_CA_MD (SSL_SECOP_CA_MD | SSL_SECOP_PEER) /* Values for "op" parameter in security callback */ @@ -2735,12 +2745,6 @@ const CTLOG_STORE *SSL_CTX_get0_ctlog_store(const SSL_CTX *ctx); #define SSL_SECOP_CA_KEY (17 | SSL_SECOP_OTHER_CERT) /* CA digest algorithm in certificate */ #define SSL_SECOP_CA_MD (18 | SSL_SECOP_OTHER_CERT) -/* Peer EE key in certificate */ -#define SSL_SECOP_PEER_EE_KEY (SSL_SECOP_EE_KEY | SSL_SECOP_PEER) -/* Peer CA key in certificate */ -#define SSL_SECOP_PEER_CA_KEY (SSL_SECOP_CA_KEY | SSL_SECOP_PEER) -/* Peer CA digest algorithm in certificate */ -#define SSL_SECOP_PEER_CA_MD (SSL_SECOP_CA_MD | SSL_SECOP_PEER) void SSL_set_security_level(SSL *s, int level); __owur int SSL_get_security_level(const SSL *s); diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c index c3479b08aa..2a51ada2a1 100644 --- a/ssl/ssl_cert.c +++ b/ssl/ssl_cert.c @@ -306,7 +306,7 @@ int ssl_cert_set0_chain(SSL_CONNECTION *s, SSL_CTX *ctx, STACK_OF(X509) *chain) for (i = 0; i < sk_X509_num(chain); i++) { X509 *x = sk_X509_value(chain, i); - r = ssl_security_cert(s, ctx, x, 0, 0); + r = ssl_security_cert(s, ctx, x, 0); if (r != 1) { ERR_raise(ERR_LIB_SSL, r); return 0; @@ -340,7 +340,7 @@ int ssl_cert_add0_chain_cert(SSL_CONNECTION *s, SSL_CTX *ctx, X509 *x) if (!cpk) return 0; - r = ssl_security_cert(s, ctx, x, 0, 0); + r = ssl_security_cert(s, ctx, x, 0); if (r != 1) { ERR_raise(ERR_LIB_SSL, r); return 0; @@ -1175,7 +1175,7 @@ int ssl_build_cert_chain(SSL_CONNECTION *s, SSL_CTX *ctx, int flags) */ for (i = 0; i < sk_X509_num(chain); i++) { x = sk_X509_value(chain, i); - rv = ssl_security_cert(s, ctx, x, 0, 0); + rv = ssl_security_cert(s, ctx, x, 0); if (rv != 1) { ERR_raise(ERR_LIB_SSL, rv); OSSL_STACK_OF_X509_free(chain); diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h index 62b0017b9c..a8171c30fa 100644 --- a/ssl/ssl_local.h +++ b/ssl/ssl_local.h @@ -2821,10 +2821,9 @@ __owur int ssl_validate_ct(SSL_CONNECTION *s); __owur EVP_PKEY *ssl_get_auto_dh(SSL_CONNECTION *s); -__owur int ssl_security_cert(SSL_CONNECTION *s, SSL_CTX *ctx, X509 *x, int vfy, - int is_ee); +__owur int ssl_security_cert(SSL_CONNECTION *s, SSL_CTX *ctx, X509 *x, int is_ee); __owur int ssl_security_cert_chain(SSL_CONNECTION *s, STACK_OF(X509) *sk, - X509 *ex, int vfy); + X509 *ex); int tls_choose_sigalg(SSL_CONNECTION *s, int fatalerrs); diff --git a/ssl/ssl_rsa.c b/ssl/ssl_rsa.c index 0e2b1fe9bd..7cfd59d6d0 100644 --- a/ssl/ssl_rsa.c +++ b/ssl/ssl_rsa.c @@ -42,7 +42,7 @@ int SSL_use_certificate(SSL *ssl, X509 *x) return 0; } - rv = ssl_security_cert(sc, NULL, x, 0, 1); + rv = ssl_security_cert(sc, NULL, x, 1); if (rv != 1) { ERR_raise(ERR_LIB_SSL, rv); return 0; @@ -247,7 +247,7 @@ int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) return 0; } - rv = ssl_security_cert(NULL, ctx, x, 0, 1); + rv = ssl_security_cert(NULL, ctx, x, 1); if (rv != 1) { ERR_raise(ERR_LIB_SSL, rv); return 0; @@ -993,13 +993,13 @@ static int ssl_set_cert_and_key(SSL *ssl, SSL_CTX *ctx, X509 *x509, EVP_PKEY *pr c = sc != NULL ? sc->cert : ctx->cert; /* Do all security checks before anything else */ - rv = ssl_security_cert(sc, ctx, x509, 0, 1); + rv = ssl_security_cert(sc, ctx, x509, 1); if (rv != 1) { ERR_raise(ERR_LIB_SSL, rv); goto out; } for (j = 0; j < sk_X509_num(chain); j++) { - rv = ssl_security_cert(sc, ctx, sk_X509_value(chain, j), 0, 0); + rv = ssl_security_cert(sc, ctx, sk_X509_value(chain, j), 0); if (rv != 1) { ERR_raise(ERR_LIB_SSL, rv); goto out; diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c index c9d76fe8a7..465d2eff33 100644 --- a/ssl/statem/statem_lib.c +++ b/ssl/statem/statem_lib.c @@ -1056,7 +1056,7 @@ static int ssl_add_cert_chain(SSL_CONNECTION *s, WPACKET *pkt, CERT_PKEY *cpk, i /* Don't leave errors in the queue */ ERR_clear_error(); chain = X509_STORE_CTX_get0_chain(xs_ctx); - i = ssl_security_cert_chain(s, chain, NULL, 0); + i = ssl_security_cert_chain(s, chain, NULL); if (i != 1) { #if 0 /* Dummy error calls so mkerr generates them */ @@ -1081,7 +1081,7 @@ static int ssl_add_cert_chain(SSL_CONNECTION *s, WPACKET *pkt, CERT_PKEY *cpk, i } X509_STORE_CTX_free(xs_ctx); } else { - i = ssl_security_cert_chain(s, extra_certs, x, 0); + i = ssl_security_cert_chain(s, extra_certs, x); if (i != 1) { if (!for_comp) SSLfatal(s, SSL_AD_INTERNAL_ERROR, i); diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index b6e487a1ab..01fcdefe1e 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -4605,19 +4605,16 @@ static int ssl_security_cert_sig(SSL_CONNECTION *s, SSL_CTX *ctx, X509 *x, return ssl_ctx_security(ctx, op, secbits, nid, x); } -int ssl_security_cert(SSL_CONNECTION *s, SSL_CTX *ctx, X509 *x, int vfy, - int is_ee) +int ssl_security_cert(SSL_CONNECTION *s, SSL_CTX *ctx, X509 *x, int is_ee) { - if (vfy) - vfy = SSL_SECOP_PEER; if (is_ee) { - if (!ssl_security_cert_key(s, ctx, x, SSL_SECOP_EE_KEY | vfy)) + if (!ssl_security_cert_key(s, ctx, x, SSL_SECOP_EE_KEY)) return SSL_R_EE_KEY_TOO_SMALL; } else { - if (!ssl_security_cert_key(s, ctx, x, SSL_SECOP_CA_KEY | vfy)) + if (!ssl_security_cert_key(s, ctx, x, SSL_SECOP_CA_KEY)) return SSL_R_CA_KEY_TOO_SMALL; } - if (!ssl_security_cert_sig(s, ctx, x, SSL_SECOP_CA_MD | vfy)) + if (!ssl_security_cert_sig(s, ctx, x, SSL_SECOP_CA_MD)) return SSL_R_CA_MD_TOO_WEAK; return 1; } @@ -4629,7 +4626,7 @@ int ssl_security_cert(SSL_CONNECTION *s, SSL_CTX *ctx, X509 *x, int vfy, */ int ssl_security_cert_chain(SSL_CONNECTION *s, STACK_OF(X509) *sk, - X509 *x, int vfy) + X509 *x) { int rv, start_idx, i; @@ -4641,13 +4638,13 @@ int ssl_security_cert_chain(SSL_CONNECTION *s, STACK_OF(X509) *sk, } else start_idx = 0; - rv = ssl_security_cert(s, NULL, x, vfy, 1); + rv = ssl_security_cert(s, NULL, x, 1); if (rv != 1) return rv; for (i = start_idx; i < sk_X509_num(sk); i++) { x = sk_X509_value(sk, i); - rv = ssl_security_cert(s, NULL, x, vfy, 0); + rv = ssl_security_cert(s, NULL, x, 0); if (rv != 1) return rv; } From 5a55913f10e9fe8686c82778810aa6b7d5335e4d Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Fri, 22 May 2026 04:10:37 -0600 Subject: [PATCH 123/349] Don't attempt to check the security level on what signed our own certificate. What matters to us is that the key *we* are using matches our desired security level, as we may sign things with that key. As far as who signed us, this could be signed by something we don't recognize at all, and it is up to the peer to decide if the thing signing us matters to it (i.e. if it recognizes the algorithm, decides it's strong enough, or it even verifies the signature, as it might already trusts our key due to pinning, TOFU, Prayer and Clean Living, or whatever.) Obviously, we still check the security level on any signatures *we* recieve to verify *from* a peer. Fixes: https://github.com/openssl/openssl/issues/31195 Reviewed-by: Neil Horman Reviewed-by: Norbert Pocs MergeDate: Tue Jun 30 10:47:09 2026 (Merged from https://github.com/openssl/openssl/pull/31271) --- ssl/t1_lib.c | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index 01fcdefe1e..df3db34125 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -4585,26 +4585,6 @@ static int ssl_security_cert_key(SSL_CONNECTION *s, SSL_CTX *ctx, X509 *x, return ssl_ctx_security(ctx, op, secbits, 0, x); } -static int ssl_security_cert_sig(SSL_CONNECTION *s, SSL_CTX *ctx, X509 *x, - int op) -{ - /* Lookup signature algorithm digest */ - int secbits, nid, pknid; - - /* Don't check signature if self signed */ - if ((X509_get_extension_flags(x) & EXFLAG_SS) != 0) - return 1; - if (!X509_get_signature_info(x, &nid, &pknid, &secbits, NULL)) - secbits = -1; - /* If digest NID not defined use signature NID */ - if (nid == NID_undef) - nid = pknid; - if (s != NULL) - return ssl_security(s, op, secbits, nid, x); - else - return ssl_ctx_security(ctx, op, secbits, nid, x); -} - int ssl_security_cert(SSL_CONNECTION *s, SSL_CTX *ctx, X509 *x, int is_ee) { if (is_ee) { @@ -4614,15 +4594,16 @@ int ssl_security_cert(SSL_CONNECTION *s, SSL_CTX *ctx, X509 *x, int is_ee) if (!ssl_security_cert_key(s, ctx, x, SSL_SECOP_CA_KEY)) return SSL_R_CA_KEY_TOO_SMALL; } - if (!ssl_security_cert_sig(s, ctx, x, SSL_SECOP_CA_MD)) - return SSL_R_CA_MD_TOO_WEAK; return 1; } /* - * Check security of a chain, if |sk| includes the end entity certificate then - * |x| is NULL. If |vfy| is 1 then we are verifying a peer chain and not sending - * one to the peer. Return values: 1 if ok otherwise error code to use + * Call ssl_security_check() on all certificates in a stack. + * If |x| is non NULL it is checked first, before checking the + * certificates in the stack. + * + * Return values: 1 if ok otherwise the error code from the first + * failing ssl_security_check().; */ int ssl_security_cert_chain(SSL_CONNECTION *s, STACK_OF(X509) *sk, From a06203b3cb15b75161ab5a2f3463682299b1ac8d Mon Sep 17 00:00:00 2001 From: Daniel Kubec Date: Fri, 5 Jun 2026 13:44:47 +0200 Subject: [PATCH 124/349] TEST: Add DTLS 1.2 coverage for Session ID verification The DTLS 1.2 tests exercise the same verification logic and confirm that the behavior matches the TLS 1.2 implementation. The tests pass as expected and provide additional coverage for DTLS 1.2 without requiring any functional changes. Fixes #31250 Reviewed-by: Igor Ustinov Reviewed-by: Tomas Mraz MergeDate: Tue Jun 30 11:00:55 2026 (Merged from https://github.com/openssl/openssl/pull/31389) --- test/build.info | 7 + test/dtls12psk.c | 309 ++++++++++++++++++++++++++++++ test/recipes/90-test_dtls12_psk.t | 20 ++ 3 files changed, 336 insertions(+) create mode 100644 test/dtls12psk.c create mode 100644 test/recipes/90-test_dtls12_psk.t diff --git a/test/build.info b/test/build.info index ac51116e56..874786ee33 100644 --- a/test/build.info +++ b/test/build.info @@ -991,6 +991,13 @@ IF[{- !$disabled{tests} -}] DEPEND[tls12psk_test]=../libcrypto.a ../libssl.a libtestutil.a ENDIF + IF[{- !$disabled{psk} && !$disabled{dtls1_2} -}] + PROGRAMS{noinst}=dtls12psk_test + SOURCE[dtls12psk_test]=dtls12psk.c helpers/ssltestlib.c + INCLUDE[dtls12psk_test]=.. ../include ../apps/include + DEPEND[dtls12psk_test]=../libcrypto.a ../libssl.a libtestutil.a + ENDIF + SOURCE[tls13encryptiontest]=tls13encryptiontest.c INCLUDE[tls13encryptiontest]=.. ../include ../apps/include DEPEND[tls13encryptiontest]=../libcrypto.a ../libssl.a libtestutil.a diff --git a/test/dtls12psk.c b/test/dtls12psk.c new file mode 100644 index 0000000000..e784f0660c --- /dev/null +++ b/test/dtls12psk.c @@ -0,0 +1,309 @@ +/* + * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include + +#include "helpers/ssltestlib.h" +#include "testutil.h" + +static const char psk_secret[] = "shared-secret"; +static const char psk_identity[] = "identity"; + +static const unsigned char sid_req[] = { + 0xde, 0xad, 0xbe, 0xef, 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c +}; + +static const struct ciphersuites { + char *name; +} css[] = { + { "PSK-AES128-CBC-SHA256" }, + { "PSK-AES256-CBC-SHA384" }, + { "PSK-AES128-GCM-SHA256" }, + { "PSK-AES256-GCM-SHA384" } +}; + +#define HELLO_RANDOM_OFF 6 +#define HELLO_RANDOM_LEN 32 +#define HELLO_SID_LEN_OFF (HELLO_RANDOM_OFF + HELLO_RANDOM_LEN) +#define HELLO_SID_OFF (HELLO_SID_LEN_OFF + 1) +#define HELLO_MIN_LEN (HELLO_SID_LEN_OFF + 1) + +static void hello_session_id(const unsigned char *p, size_t len) +{ + char *str; + size_t sid_len; + + if (len < HELLO_MIN_LEN) + return; + + sid_len = p[HELLO_SID_LEN_OFF]; + if (sid_len == 0 || len < HELLO_SID_OFF + sid_len) + return; + + str = OPENSSL_buf2hexstr(p + HELLO_SID_OFF, (long)sid_len); + TEST_info("session_id(%u): <%s>", (unsigned int)sid_len, str); + OPENSSL_free(str); +} + +static void msg_cb(int write_p, int version, int content_type, + const void *buf, size_t len, SSL *ssl, void *arg) +{ + const unsigned char *p = buf; + + if (content_type != SSL3_RT_HANDSHAKE || len < 1) + return; + + switch (p[0]) { + case SSL3_MT_CLIENT_HELLO: + TEST_info("%p client_hello", (void *)ssl); + hello_session_id(p, len); + break; + + case SSL3_MT_SERVER_HELLO: + TEST_info("%p server_hello", (void *)ssl); + hello_session_id(p, len); + break; + } +} + +static void handshake_finished(const SSL *ssl) +{ + const char *endpoint = SSL_is_server(ssl) ? "server" : "client"; + unsigned int has_ticket = SSL_SESSION_has_ticket(SSL_get_session(ssl)); + + if (SSL_session_reused(ssl)) + TEST_info("%s: Abbreviated handshake finished", endpoint); + else + TEST_info("%s: Full handshake finished", endpoint); + + TEST_info("%s: has_ticket: %u", endpoint, has_ticket); +} + +static void info_cb(const SSL *ssl, int type, int val) +{ + const char *endpoint = SSL_is_server(ssl) ? "server" : "client"; + + if (type & SSL_CB_ALERT) { + const char *dir = (type & SSL_CB_READ) ? "read" : "write"; + + TEST_info("%s: alert %s: %s : %s", endpoint, dir, + SSL_alert_type_string_long(val), + SSL_alert_desc_string_long(val)); + } + if (type & SSL_CB_HANDSHAKE_DONE) + handshake_finished(ssl); +} + +static unsigned int server_psk_cb(SSL *ssl, const char *identity, + unsigned char *psk, unsigned int max) +{ + if (max < (sizeof(psk_secret) - 1)) + return 0; + memcpy(psk, psk_secret, (sizeof(psk_secret) - 1)); + return (unsigned int)(sizeof(psk_secret) - 1); +} + +static unsigned int client_psk_cb(SSL *ssl, const char *hint, + char *identity, unsigned int max_id, + unsigned char *psk, unsigned int max) +{ + if (max < (sizeof(psk_secret) - 1) || max_id < sizeof(psk_identity)) + return 0; + strncpy(identity, psk_identity, max_id); + memcpy(psk, psk_secret, (sizeof(psk_secret) - 1)); + return (unsigned int)(sizeof(psk_secret) - 1); +} + +static SSL_SESSION *sess_cache; +static SSL_SESSION *get_sess_cb(SSL *ssl, const unsigned char *id, int len, int *copy) +{ + *copy = 1; + + if (sess_cache != NULL) { + char *str; + const unsigned char *sid; + unsigned int sid_len; + + sid = SSL_SESSION_get_id(sess_cache, &sid_len); + str = OPENSSL_buf2hexstr(sid, sid_len); + TEST_info("(cached) session_id: <%s>", str); + OPENSSL_free(str); + } + return sess_cache; +} + +static int ctx_set_cache(SSL_CTX *s_ctx, SSL_CTX *c_ctx) +{ + SSL_CTX_set_psk_server_callback(s_ctx, server_psk_cb); + SSL_CTX_set_psk_client_callback(c_ctx, client_psk_cb); + SSL_CTX_set_session_cache_mode(s_ctx, SSL_SESS_CACHE_SERVER); + SSL_CTX_set_session_cache_mode(c_ctx, SSL_SESS_CACHE_CLIENT); + SSL_CTX_set_options(s_ctx, SSL_OP_NO_TICKET); + SSL_CTX_set_verify(c_ctx, SSL_VERIFY_NONE, NULL); + return 1; +} + +static int ctx_set_ticket(SSL_CTX *s_ctx, SSL_CTX *c_ctx) +{ + SSL_CTX_set_psk_server_callback(s_ctx, server_psk_cb); + SSL_CTX_set_psk_client_callback(c_ctx, client_psk_cb); + SSL_CTX_set_session_cache_mode(s_ctx, SSL_SESS_CACHE_SERVER); + SSL_CTX_set_session_cache_mode(c_ctx, SSL_SESS_CACHE_CLIENT); + SSL_CTX_set_verify(c_ctx, SSL_VERIFY_NONE, NULL); + return 1; +} + +static int set_shutdown(SSL *c, SSL *s) +{ + SSL_set_shutdown(c, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN); + SSL_set_shutdown(s, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN); + return 1; +} + +static int set_server_cache(SSL_CTX *s_ctx) +{ + unsigned int v = SSL_SESS_CACHE_SERVER | SSL_SESS_CACHE_NO_INTERNAL_STORE; + SSL_CTX_sess_set_get_cb(s_ctx, get_sess_cb); + SSL_CTX_set_session_cache_mode(s_ctx, v); + return 1; +} + +static int set_callbacks(SSL *c, SSL *s) +{ + SSL_set_msg_callback(c, msg_cb); + SSL_set_info_callback(c, info_cb); + SSL_set_msg_callback(s, msg_cb); + SSL_set_info_callback(s, info_cb); + return 1; +} + +static int sessid_matches(SSL *c, SSL *s) +{ + const unsigned char *c_sid, *s_sid; + unsigned int c_len, s_len; + int test; + + test = TEST_ptr(c_sid = SSL_SESSION_get_id(SSL_get0_session(c), &c_len)) + && TEST_ptr(s_sid = SSL_SESSION_get_id(SSL_get0_session(s), &s_len)) + && TEST_uint_eq(c_len, s_len) && TEST_mem_eq(c_sid, c_len, s_sid, s_len); + + return test; +} + +static int test_dtls12_psk_resume_sessid_mismatch(int idx) +{ + const struct ciphersuites *cs = &css[idx]; + SSL_CTX *s_ctx = NULL, *c_ctx = NULL; + SSL *s_ssl = NULL, *c_ssl = NULL, *s = NULL, *c = NULL; + SSL_SESSION *sess = NULL, *r_sess = NULL; + const unsigned char *sid; + unsigned int sid_len; + int test; + + sess_cache = NULL; + + test = TEST_true(create_ssl_ctx_pair(NULL, DTLS_server_method(), DTLS_client_method(), + DTLS1_2_VERSION, DTLS1_2_VERSION, &s_ctx, &c_ctx, NULL, NULL)) + && TEST_true(SSL_CTX_set_cipher_list(s_ctx, cs->name)) + && TEST_true(SSL_CTX_set_cipher_list(c_ctx, cs->name)) + && TEST_true(ctx_set_cache(s_ctx, c_ctx)) + && TEST_true(create_ssl_objects(s_ctx, c_ctx, &s, &c, NULL, NULL)) + && TEST_true(set_callbacks(c, s)) + && TEST_true(create_ssl_connection(s, c, SSL_ERROR_NONE)) + && TEST_ptr(sess = SSL_get1_session(c)) + && TEST_true(set_shutdown(c, s)) + && TEST_ptr(sid = SSL_SESSION_get_id(sess, &sid_len)) + && TEST_uint_eq(sid_len, 32) + && TEST_ptr(r_sess = SSL_SESSION_dup(sess)) + && TEST_true(SSL_SESSION_set1_id(r_sess, sid_req, sizeof(sid_req))) + && TEST_ptr(sess_cache = sess) + && TEST_true(set_server_cache(s_ctx)) + && TEST_true(create_ssl_objects(s_ctx, c_ctx, &s_ssl, &c_ssl, NULL, NULL)) + && TEST_true(set_callbacks(c_ssl, s_ssl)) + && TEST_true(SSL_set_session(c_ssl, r_sess)) + && TEST_true(create_ssl_connection(s_ssl, c_ssl, SSL_ERROR_NONE)) + && TEST_false(SSL_session_reused(s_ssl)); + + sess_cache = NULL; + SSL_free(s_ssl); + SSL_free(c_ssl); + SSL_SESSION_free(r_sess); + SSL_SESSION_free(sess); + SSL_CTX_free(s_ctx); + SSL_CTX_free(c_ctx); + SSL_free(s); + SSL_free(c); + return test; +} + +/* + * RFC 5077 3.4 requires the server to echo the session ID from ClientHello + * in the ServerHello when accepting a session ticket. Some clients rely on + * this echo to confirm that resumption succeeded. The ticket decryption path + * in tls_decrypt_ticket() guarantees the restored SSL_SESSION carries the + * correct session ID, so tls_construct_server_hello() will echo it correctly. + * If the session ID is empty, its length is set to zero as required by the + * RFC. + */ +static int test_dtls12_psk_resume_ticket_mismatch(int idx) +{ + const struct ciphersuites *cs = &css[idx]; + SSL_CTX *s_ctx = NULL, *c_ctx = NULL; + SSL *s_ssl = NULL, *c_ssl = NULL, *s = NULL, *c = NULL; + SSL_SESSION *c_sess = NULL, *r_sess = NULL; + int test; + + test = TEST_true(create_ssl_ctx_pair(NULL, DTLS_server_method(), DTLS_client_method(), + DTLS1_2_VERSION, DTLS1_2_VERSION, &s_ctx, &c_ctx, NULL, NULL)) + && TEST_true(SSL_CTX_set_cipher_list(s_ctx, cs->name)) + && TEST_true(SSL_CTX_set_cipher_list(c_ctx, cs->name)) + && TEST_true(ctx_set_ticket(s_ctx, c_ctx)) + && TEST_true(create_ssl_objects(s_ctx, c_ctx, &s, &c, NULL, NULL)) + && TEST_true(set_callbacks(c, s)) + && TEST_true(create_ssl_connection(s, c, SSL_ERROR_NONE)) + && TEST_ptr(c_sess = SSL_get1_session(c)) + && TEST_true(SSL_SESSION_has_ticket(c_sess)) + && TEST_int_eq(set_shutdown(c, s), 1) + && TEST_ptr(r_sess = SSL_SESSION_dup(c_sess)) + && TEST_true(SSL_SESSION_set1_id(r_sess, sid_req, sizeof(sid_req))) + && TEST_true(create_ssl_objects(s_ctx, c_ctx, &s_ssl, &c_ssl, NULL, NULL)) + && TEST_true(set_callbacks(c_ssl, s_ssl)) + && TEST_true(SSL_set_session(c_ssl, r_sess)) + && TEST_true(create_ssl_connection(s_ssl, c_ssl, SSL_ERROR_NONE)) + && TEST_true(SSL_session_reused(s_ssl)) + && TEST_true(sessid_matches(c_ssl, s_ssl)); + + SSL_free(s_ssl); + SSL_free(c_ssl); + SSL_SESSION_free(r_sess); + SSL_SESSION_free(c_sess); + SSL_CTX_free(s_ctx); + SSL_CTX_free(c_ctx); + SSL_free(s); + SSL_free(c); + return test; +} + +OPT_TEST_DECLARE_USAGE("\n") + +int setup_tests(void) +{ + if (!test_skip_common_options()) { + TEST_error("Error parsing test options\n"); + return 0; + } + + ADD_ALL_TESTS(test_dtls12_psk_resume_sessid_mismatch, OSSL_NELEM(css)); + ADD_ALL_TESTS(test_dtls12_psk_resume_ticket_mismatch, OSSL_NELEM(css)); + return 1; +} diff --git a/test/recipes/90-test_dtls12_psk.t b/test/recipes/90-test_dtls12_psk.t new file mode 100644 index 0000000000..c7ed9759fc --- /dev/null +++ b/test/recipes/90-test_dtls12_psk.t @@ -0,0 +1,20 @@ +#! /usr/bin/env perl +# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +use OpenSSL::Test; +use OpenSSL::Test::Utils; + +my $test_name = "test_dtls12psk"; +setup($test_name); + +plan skip_all => "$test_name is not supported in this build" + if disabled("dtls1_2") || disabled("psk"); + +plan tests => 1; + +ok(run(test(["dtls12psk_test"])), "running dtls12psk_test"); From c5ae170e855d055774000f7221f5d5afd5f4fa72 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 25 Jun 2026 17:57:37 +0200 Subject: [PATCH 125/349] property: add cache provider-order regression test When two providers cache the same nid and property query, the first one to do so must own the providerless cache entry, so that a NULL-provider lookup keeps resolving to that provider. This covers commit 9d476175d7. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Neil Horman Reviewed-by: Nikola Pajkovsky MergeDate: Tue Jun 30 11:01:57 2026 (Merged from https://github.com/openssl/openssl/pull/31738) --- test/property_test.c | 57 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/test/property_test.c b/test/property_test.c index 47422b4049..602651df25 100644 --- a/test/property_test.c +++ b/test/property_test.c @@ -691,6 +691,62 @@ err: return res; } +/* + * When two providers cache the same nid and property query, the first one to + * do so must own the providerless ("any provider will do") cache entry, so + * that a NULL-provider lookup keeps resolving to that provider regardless of + * how many other providers subsequently cache the same nid. This matches the + * provider ossl_method_store_fetch would pick by implementation order. + */ +static int test_query_cache_provider_order(void) +{ + OSSL_METHOD_STORE *store = NULL; + int res = 0; + int method1 = 0, method2 = 0; + void *result = NULL; + OSSL_PROVIDER prov1 = { + .flag_initialized = 1, + .flag_activated = 1, + .name = "first-provider" + }; + OSSL_PROVIDER prov2 = { + .flag_initialized = 1, + .flag_activated = 1, + .name = "second-provider" + }; + + if (!TEST_ptr(store = ossl_method_store_new(NULL))) + goto err; + + /* prov1 caches the nid first, so it owns the providerless entry. */ + if (!TEST_true(ossl_method_store_cache_set(store, &prov1, 1, "", &method1, + up_ref, down_ref)) + || !TEST_true(ossl_method_store_cache_set(store, &prov2, 1, "", + &method2, up_ref, down_ref))) + goto err; + + /* A NULL-provider ("any provider") lookup must resolve to prov1. */ + if (!TEST_true(ossl_method_store_cache_get(store, NULL, 1, "", &result)) + || !TEST_ptr_eq(result, &method1)) + goto err; + + /* Provider-specific lookups must still return each provider's method. */ + result = NULL; + if (!TEST_true(ossl_method_store_cache_get(store, &prov1, 1, "", &result)) + || !TEST_ptr_eq(result, &method1)) + goto err; + result = NULL; + if (!TEST_true(ossl_method_store_cache_get(store, &prov2, 1, "", &result)) + || !TEST_ptr_eq(result, &method2)) + goto err; + + res = 1; + +err: + ossl_method_store_free(store); + return res; +} + static int test_fips_mode(void) { int ret = 0; @@ -804,6 +860,7 @@ int setup_tests(void) ADD_TEST(test_property); ADD_TEST(test_query_cache_stochastic); ADD_TEST(test_query_cache_set_duplicate); + ADD_TEST(test_query_cache_provider_order); ADD_TEST(test_fips_mode); ADD_ALL_TESTS(test_property_list_to_string, OSSL_NELEM(to_string_tests)); ADD_TEST(test_property_list_to_string_bounds); From ed259464950a1f625cf2106f45c0cc8862b146d9 Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Mon, 22 Jun 2026 13:03:55 -0600 Subject: [PATCH 126/349] Clean up the cpu id stuff by hoisting the append into a helper Reviewed-by: Nikola Pajkovsky Reviewed-by: Norbert Pocs MergeDate: Tue Jun 30 11:02:52 2026 (Merged from https://github.com/openssl/openssl/pull/31646) --- crypto/info.c | 117 ++++++++++++++++++++++++-------------------------- 1 file changed, 57 insertions(+), 60 deletions(-) diff --git a/crypto/info.c b/crypto/info.c index 2038db5a52..4baece2246 100644 --- a/crypto/info.c +++ b/crypto/info.c @@ -7,6 +7,7 @@ * https://www.openssl.org/source/license.html */ +#include #include #include "crypto/rand.h" #include "crypto/dso_conf.h" @@ -49,70 +50,75 @@ char ossl_cpu_info_str[CPU_INFO_STR_LEN] = ""; static CRYPTO_ONCE init_info = CRYPTO_ONCE_STATIC_INIT; +/* + * Append a printf-formatted suffix to ossl_cpu_info_str, truncating to + * fit. The first call writes the base string (the buffer starts empty, + * so off == 0); subsequent calls extend it. + */ +static ossl_unused void cpu_info_append(const char *fmt, ...) +{ + size_t off = strlen(ossl_cpu_info_str); + va_list args; + + if (off >= sizeof(ossl_cpu_info_str)) + return; + va_start(args, fmt); + (void)vsnprintf(ossl_cpu_info_str + off, + sizeof(ossl_cpu_info_str) - off, fmt, args); + va_end(args); +} + DEFINE_RUN_ONCE_STATIC(init_info_strings) { #if defined(OPENSSL_CPUID_OBJ) #if defined(__i386) || defined(__i386__) || defined(_M_IX86) || defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_X64) const char *env; - BIO_snprintf(ossl_cpu_info_str, sizeof(ossl_cpu_info_str), - CPUINFO_PREFIX "OPENSSL_ia32cap=0x%.16llx:0x%.16llx:0x%.16llx:0x%.16llx:0x%.16llx", + cpu_info_append(CPUINFO_PREFIX + "OPENSSL_ia32cap=0x%.16llx:0x%.16llx:0x%.16llx:0x%.16llx:0x%.16llx", (unsigned long long)OPENSSL_ia32cap_P[0] | (unsigned long long)OPENSSL_ia32cap_P[1] << 32, (unsigned long long)OPENSSL_ia32cap_P[2] | (unsigned long long)OPENSSL_ia32cap_P[3] << 32, (unsigned long long)OPENSSL_ia32cap_P[4] | (unsigned long long)OPENSSL_ia32cap_P[5] << 32, (unsigned long long)OPENSSL_ia32cap_P[6] | (unsigned long long)OPENSSL_ia32cap_P[7] << 32, (unsigned long long)OPENSSL_ia32cap_P[8] | (unsigned long long)OPENSSL_ia32cap_P[9] << 32); - if ((env = getenv("OPENSSL_ia32cap")) != NULL) - BIO_snprintf(ossl_cpu_info_str + strlen(ossl_cpu_info_str), - sizeof(ossl_cpu_info_str) - strlen(ossl_cpu_info_str), - " env:%s", env); + cpu_info_append(" env:%s", env); #elif defined(__arm__) || defined(__arm) || defined(__aarch64__) const char *env; - BIO_snprintf(ossl_cpu_info_str, sizeof(ossl_cpu_info_str), - CPUINFO_PREFIX "OPENSSL_armcap=0x%x", OPENSSL_armcap_P); + cpu_info_append(CPUINFO_PREFIX "OPENSSL_armcap=0x%x", OPENSSL_armcap_P); if ((env = getenv("OPENSSL_armcap")) != NULL) - BIO_snprintf(ossl_cpu_info_str + strlen(ossl_cpu_info_str), - sizeof(ossl_cpu_info_str) - strlen(ossl_cpu_info_str), - " env:%s", env); + cpu_info_append(" env:%s", env); #elif defined(__powerpc__) || defined(__POWERPC__) || defined(_ARCH_PPC) const char *env; - BIO_snprintf(ossl_cpu_info_str, sizeof(ossl_cpu_info_str), - CPUINFO_PREFIX "OPENSSL_ppccap=0x%x", OPENSSL_ppccap_P); + cpu_info_append(CPUINFO_PREFIX "OPENSSL_ppccap=0x%x", OPENSSL_ppccap_P); if ((env = getenv("OPENSSL_ppccap")) != NULL) - BIO_snprintf(ossl_cpu_info_str + strlen(ossl_cpu_info_str), - sizeof(ossl_cpu_info_str) - strlen(ossl_cpu_info_str), - " env:%s", env); + cpu_info_append(" env:%s", env); #elif defined(__sparcv9) || defined(__sparcv9__) const char *env; - BIO_snprintf(ossl_cpu_info_str, sizeof(ossl_cpu_info_str), - CPUINFO_PREFIX "OPENSSL_sparcv9cap=0x%x:0x%x", + cpu_info_append(CPUINFO_PREFIX "OPENSSL_sparcv9cap=0x%x:0x%x", OPENSSL_sparcv9cap_P[0], OPENSSL_sparcv9cap_P[1]); if ((env = getenv("OPENSSL_sparcv9cap")) != NULL) - BIO_snprintf(ossl_cpu_info_str + strlen(ossl_cpu_info_str), - sizeof(ossl_cpu_info_str) - strlen(ossl_cpu_info_str), - " env:%s", env); + cpu_info_append(" env:%s", env); #elif defined(__s390__) || defined(__s390x__) const char *env; - BIO_snprintf(ossl_cpu_info_str, sizeof(ossl_cpu_info_str), - CPUINFO_PREFIX "OPENSSL_s390xcap=" - "stfle:0x%llx:0x%llx:0x%llx:0x%llx:" - "kimd:0x%llx:0x%llx:" - "klmd:0x%llx:0x%llx:" - "km:0x%llx:0x%llx:" - "kmc:0x%llx:0x%llx:" - "kmac:0x%llx:0x%llx:" - "kmctr:0x%llx:0x%llx:" - "kmo:0x%llx:0x%llx:" - "kmf:0x%llx:0x%llx:" - "prno:0x%llx:0x%llx:" - "kma:0x%llx:0x%llx:" - "pcc:0x%llx:0x%llx:" - "kdsa:0x%llx:0x%llx", + cpu_info_append(CPUINFO_PREFIX "OPENSSL_s390xcap=" + "stfle:0x%llx:0x%llx:0x%llx:0x%llx:" + "kimd:0x%llx:0x%llx:" + "klmd:0x%llx:0x%llx:" + "km:0x%llx:0x%llx:" + "kmc:0x%llx:0x%llx:" + "kmac:0x%llx:0x%llx:" + "kmctr:0x%llx:0x%llx:" + "kmo:0x%llx:0x%llx:" + "kmf:0x%llx:0x%llx:" + "prno:0x%llx:0x%llx:" + "kma:0x%llx:0x%llx:" + "pcc:0x%llx:0x%llx:" + "kdsa:0x%llx:0x%llx", OPENSSL_s390xcap_P.stfle[0], OPENSSL_s390xcap_P.stfle[1], OPENSSL_s390xcap_P.stfle[2], OPENSSL_s390xcap_P.stfle[3], OPENSSL_s390xcap_P.kimd[0], OPENSSL_s390xcap_P.kimd[1], @@ -128,63 +134,54 @@ DEFINE_RUN_ONCE_STATIC(init_info_strings) OPENSSL_s390xcap_P.pcc[0], OPENSSL_s390xcap_P.pcc[1], OPENSSL_s390xcap_P.kdsa[0], OPENSSL_s390xcap_P.kdsa[1]); if ((env = getenv("OPENSSL_s390xcap")) != NULL) - BIO_snprintf(ossl_cpu_info_str + strlen(ossl_cpu_info_str), - sizeof(ossl_cpu_info_str) - strlen(ossl_cpu_info_str), - " env:%s", env); + cpu_info_append(" env:%s", env); #elif defined(__riscv) const char *env; size_t i; - BIO_snprintf(ossl_cpu_info_str, sizeof(ossl_cpu_info_str), - CPUINFO_PREFIX "OPENSSL_riscvcap=RV" + cpu_info_append(CPUINFO_PREFIX "OPENSSL_riscvcap=RV" #if __riscv_xlen == 32 - "32" + "32" #elif __riscv_xlen == 64 - "64" + "64" #elif __riscv_xlen == 128 - "128" + "128" #endif #if defined(__riscv_i) && defined(__riscv_m) && defined(__riscv_a) \ && defined(__riscv_f) && defined(__riscv_d) \ && defined(__riscv_zicsr) && defined(__riscv_zifencei) - "G" /* shorthand for IMAFD_Zicsr_Zifencei */ + "G" /* shorthand for IMAFD_Zicsr_Zifencei */ #else #ifdef __riscv_i - "I" + "I" #endif #ifdef __riscv_m - "M" + "M" #endif #ifdef __riscv_a - "A" + "A" #endif #ifdef __riscv_f - "F" + "F" #endif #ifdef __riscv_d - "D" + "D" #endif #endif #ifdef __riscv_c - "C" + "C" #endif ); for (i = 0; i < kRISCVNumCaps; i++) { if (OPENSSL_riscvcap_P[RISCV_capabilities[i].index] & (1 << RISCV_capabilities[i].bit_offset)) /* Match, display the name */ - BIO_snprintf(ossl_cpu_info_str + strlen(ossl_cpu_info_str), - sizeof(ossl_cpu_info_str) - strlen(ossl_cpu_info_str), - "_%s", RISCV_capabilities[i].name); + cpu_info_append("_%s", RISCV_capabilities[i].name); } if (RISCV_HAS_V()) - BIO_snprintf(ossl_cpu_info_str + strlen(ossl_cpu_info_str), - sizeof(ossl_cpu_info_str) - strlen(ossl_cpu_info_str), - " vlen:%lu", riscv_vlen()); + cpu_info_append(" vlen:%lu", riscv_vlen()); if ((env = getenv("OPENSSL_riscvcap")) != NULL) - BIO_snprintf(ossl_cpu_info_str + strlen(ossl_cpu_info_str), - sizeof(ossl_cpu_info_str) - strlen(ossl_cpu_info_str), - " env:%s", env); + cpu_info_append(" env:%s", env); #endif #endif From 4402bc95faa16014ecff27096ca5fdaa5f47b487 Mon Sep 17 00:00:00 2001 From: Frederik Wedel-Heinen Date: Mon, 13 Apr 2026 16:58:04 +0200 Subject: [PATCH 127/349] Removes some stale SSLv3 support around the code and updated documentation to reflect removal. Reviewed-by: Matt Caswell Reviewed-by: Neil Horman Reviewed-by: Norbert Pocs MergeDate: Tue Jun 30 11:04:18 2026 (Merged from https://github.com/openssl/openssl/pull/30824) --- doc/designs/quic-design/quic-api-ssl-funcs.md | 3 - doc/man1/openssl-ciphers.pod.in | 8 +- doc/man3/SSL_CIPHER_get_name.pod | 6 +- doc/man3/SSL_COMP_add_compression_method.pod | 4 +- doc/man3/SSL_CONF_cmd.pod | 42 +++--- doc/man3/SSL_CTX_set_min_proto_version.pod | 2 +- doc/man3/SSL_CTX_set_msg_callback.pod | 4 +- doc/man3/SSL_CTX_set_options.pod | 2 +- doc/man3/SSL_CTX_set_security_level.pod | 2 +- doc/man3/SSL_CTX_set_split_send_fragment.pod | 2 +- doc/man3/SSL_CTX_set_tlsext_ticket_key_cb.pod | 3 +- doc/man3/SSL_get_version.pod | 4 - include/internal/ssl3_cbc.h | 9 +- include/openssl/ssl.h.in | 2 +- .../ciphers/cipher_aes_cbc_hmac_sha.c | 3 +- .../implementations/ciphers/ciphercommon.c | 12 +- .../ciphers/ciphercommon_block.c | 7 +- providers/implementations/macs/hmac_prov.c | 3 +- ssl/methods.c | 6 +- ssl/record/methods/recmethod_local.h | 6 +- ssl/record/methods/ssl3_cbc.c | 134 +++++------------- ssl/record/methods/tls1_meth.c | 2 - ssl/record/methods/tls_common.c | 2 - ssl/record/methods/tls_pad.c | 45 ------ ssl/ssl_ciph.c | 4 +- ssl/ssl_lib.c | 6 +- ssl/ssl_local.h | 21 ++- ssl/ssl_sess.c | 2 +- ssl/statem/statem_clnt.c | 2 +- ssl/statem/statem_srvr.c | 12 -- ssl/t1_lib.c | 14 +- test/ssl_old_test.c | 2 +- 32 files changed, 103 insertions(+), 273 deletions(-) diff --git a/doc/designs/quic-design/quic-api-ssl-funcs.md b/doc/designs/quic-design/quic-api-ssl-funcs.md index 6333bafab8..37229c26e6 100644 --- a/doc/designs/quic-design/quic-api-ssl-funcs.md +++ b/doc/designs/quic-design/quic-api-ssl-funcs.md @@ -108,9 +108,6 @@ Notes: | `SSL_test_functions` | Global | 🟩U | 🟦U | 🟩NC | 🟢Done | | `SSL_select_next_proto` | Global | 🟩U | 🟦U | 🟩NC | 🟢Done | | **⇒ Methods** | | | | | | -| `SSLv3_method` | Global | 🟩U | 🟦U | 🟩NC | 🟢Done | -| `SSLv3_client_method` | Global | 🟩U | 🟦U | 🟩NC | 🟢Done | -| `SSLv3_server_method` | Global | 🟩U | 🟦U | 🟩NC | 🟢Done | | `TLS_method` | Global | 🟩U | 🟦U | 🟩NC | 🟢Done | | `TLS_client_method` | Global | 🟩U | 🟦U | 🟩NC | 🟢Done | | `TLS_server_method` | Global | 🟩U | 🟦U | 🟩NC | 🟢Done | diff --git a/doc/man1/openssl-ciphers.pod.in b/doc/man1/openssl-ciphers.pod.in index c7e1291c83..db8b6e55e1 100644 --- a/doc/man1/openssl-ciphers.pod.in +++ b/doc/man1/openssl-ciphers.pod.in @@ -269,12 +269,12 @@ Cipher suites using DSS authentication, i.e. the certificates carry DSS keys. Cipher suites using ECDSA authentication, i.e. the certificates carry ECDSA keys. -=item B, B, B +=item B, B -Lists cipher suites introduced in TLS v1.2, TLS v1.0 or SSL v3.0 respectively. +Lists cipher suites introduced in TLS v1.2 or TLS v1.0 respectively. Note: there are no cipher suites specific to TLS v1.1. -Since this is only the minimum version, if, for example, TLSv1.0 is negotiated -then both TLSv1.0 and SSLv3.0 cipher suites are available. +Since this is only the minimum version, if, for example, TLSv1.2 is negotiated +then both TLSv1.2 and TLSv1.0 cipher suites are available. Note: these cipher strings B change the negotiated version of SSL or TLS, they only affect the list of available cipher suites. diff --git a/doc/man3/SSL_CIPHER_get_name.pod b/doc/man3/SSL_CIPHER_get_name.pod index 4a159a68b8..d0cee8adf1 100644 --- a/doc/man3/SSL_CIPHER_get_name.pod +++ b/doc/man3/SSL_CIPHER_get_name.pod @@ -46,13 +46,11 @@ B is NULL, it returns "(NONE)". SSL_CIPHER_standard_name() returns a pointer to the standard RFC name of B. If the B is NULL, it returns "(NONE)". If the B -has no standard name, it returns B. If B was defined in both -SSLv3 and TLS, it returns the TLS name. +has no standard name, it returns B. OPENSSL_cipher_name() returns a pointer to the OpenSSL name of B. If the B is NULL, or B has no corresponding OpenSSL name, -it returns "(NONE)". Where both exist, B should be the TLS name rather -than the SSLv3 name. +it returns "(NONE)". SSL_CIPHER_get_bits() returns the number of secret bits used for B. If B is NULL, 0 is returned. diff --git a/doc/man3/SSL_COMP_add_compression_method.pod b/doc/man3/SSL_COMP_add_compression_method.pod index 4b32023959..56f708ca88 100644 --- a/doc/man3/SSL_COMP_add_compression_method.pod +++ b/doc/man3/SSL_COMP_add_compression_method.pod @@ -40,7 +40,7 @@ maintain the internal table of compression methods. =head1 NOTES -The TLS standard (or SSLv3) allows the integration of compression methods +The TLS standard allows the integration of compression methods into the communication. The TLS RFC does however not specify compression methods or their corresponding identifiers, so there is currently no compatible way to integrate compression with unknown peers. It is therefore currently not @@ -48,7 +48,7 @@ recommended to integrate compression into applications. Applications for non-public use may agree on certain compression methods. Using different compression methods with the same identifier will lead to connection failure. -An OpenSSL client speaking a protocol that allows compression (SSLv3, TLSv1) +An OpenSSL client speaking a protocol that allows compression (TLSv1) will unconditionally send the list of all compression methods enabled with SSL_COMP_add_compression_method() to the server during the handshake. Unlike the mechanisms to set a cipher list, there is no method available to diff --git a/doc/man3/SSL_CONF_cmd.pod b/doc/man3/SSL_CONF_cmd.pod index ed15a784fa..15f969eb61 100644 --- a/doc/man3/SSL_CONF_cmd.pod +++ b/doc/man3/SSL_CONF_cmd.pod @@ -246,7 +246,7 @@ See L for more information. =item B<-min_protocol> I, B<-max_protocol> I Sets the minimum and maximum supported protocol. -Currently supported protocol values are B, B, B, +Currently supported protocol values are B, B, B, B for TLS; B, B for DTLS, and B for no limit. If either the lower or upper bound is not specified then only the other bound @@ -309,12 +309,15 @@ operations are permitted. =item B<-no_ssl3>, B<-no_tls1>, B<-no_tls1_1>, B<-no_tls1_2>, B<-no_tls1_3> -Disables protocol support for SSLv3, TLSv1.0, TLSv1.1, TLSv1.2 or TLSv1.3 by -setting the corresponding options B, B, +Disables protocol support for TLSv1.0, TLSv1.1, TLSv1.2 or TLSv1.3 by +setting the corresponding options B, B, B and B respectively. These options are deprecated, use B<-min_protocol> and B<-max_protocol> instead. +Note that B<-no_ssl3> is a no-op since support for SSLv3 was removed in OpenSSL +4.0. + =item B<-anti_replay>, B<-no_anti_replay> Switches replay protection, on or off respectively. With replay protection on, @@ -493,7 +496,7 @@ This is a synonym for the "Groups" command. This sets the minimum supported SSL, TLS or DTLS version. -Currently supported protocol values are B, B, B, +Currently supported protocol values are B, B, B, B, B and B. The SSL and TLS bounds apply only to TLS-based contexts, while the DTLS bounds apply only to DTLS-based contexts. @@ -505,7 +508,7 @@ The value B applies to both types of contexts and disables the limits. This sets the maximum supported SSL, TLS or DTLS version. -Currently supported protocol values are B, B, B, +Currently supported protocol values are B, B, B, B, B and B. The SSL and TLS bounds apply only to TLS-based contexts, while the DTLS bounds apply only to DTLS-based contexts. @@ -528,7 +531,7 @@ effect. Only enabling some protocol versions does not disable the other protocol versions. -Currently supported protocol values are B, B, B, +Currently supported protocol values are B, B, B, B, B and B. The special value B refers to all supported versions. @@ -560,7 +563,7 @@ B: SSL/TLS compression support, disabled by default. Inverse of B. B: use empty fragments as a countermeasure against a -SSL 3.0/TLS 1.0 protocol vulnerability affecting CBC ciphers. It +TLS 1.0 protocol vulnerability affecting CBC ciphers. It is set by default. Inverse of B. B: enable various bug workarounds. Same as B. @@ -728,16 +731,15 @@ argument. The order of operations is significant. This can be used to set either defaults or values which cannot be overridden. For example if an application calls: - SSL_CONF_cmd(ctx, "Protocol", "-SSLv3"); + SSL_CONF_cmd(ctx, "Protocol", "-TLSv1"); SSL_CONF_cmd(ctx, userparam, uservalue); -it will disable SSLv3 support by default but the user can override it. If +it will disable TLSv1 support by default but the user can override it. If however the call sequence is: SSL_CONF_cmd(ctx, userparam, uservalue); - SSL_CONF_cmd(ctx, "Protocol", "-SSLv3"); - -SSLv3 is B disabled and attempt to override this by the user are + SSL_CONF_cmd(ctx, "Protocol", "-TLSv1"); +TLSv1 is B disabled and attempt to override this by the user are ignored. By checking the return code of SSL_CONF_cmd() it is possible to query if a @@ -792,22 +794,22 @@ Set supported signature algorithms: There are various ways to select the supported protocols. -This set the minimum protocol version to TLSv1, and so disables SSLv3. +This sets the minimum protocol version to TLSv1.1, and so disables TLSv1. This is the recommended way to disable protocols. - SSL_CONF_cmd(ctx, "MinProtocol", "TLSv1"); + SSL_CONF_cmd(ctx, "MinProtocol", "TLSv1.1"); -The following also disables SSLv3: +The following also disables TLSv1: - SSL_CONF_cmd(ctx, "Protocol", "-SSLv3"); + SSL_CONF_cmd(ctx, "Protocol", "-TLSv1"); The following will first enable all protocols, and then disable -SSLv3. +TLSv1. If no protocol versions were disabled before this has the same effect as -"-SSLv3", but if some versions were disables this will re-enable them before -disabling SSLv3. +"-TLSv1", but if some versions were disables this will re-enable them before +disabling TLSv1. - SSL_CONF_cmd(ctx, "Protocol", "ALL,-SSLv3"); + SSL_CONF_cmd(ctx, "Protocol", "ALL,-TLSv1"); Only enable TLSv1.2: diff --git a/doc/man3/SSL_CTX_set_min_proto_version.pod b/doc/man3/SSL_CTX_set_min_proto_version.pod index d9b61dcce9..c1bf21ac42 100644 --- a/doc/man3/SSL_CTX_set_min_proto_version.pod +++ b/doc/man3/SSL_CTX_set_min_proto_version.pod @@ -39,7 +39,7 @@ controlled by system configuration. Getters return 0 in case B or B have been configured to automatically use the lowest or highest version supported by the library. -Currently supported versions are B, B, +Currently supported versions are B, B, B, B for TLS and B, B for DTLS. diff --git a/doc/man3/SSL_CTX_set_msg_callback.pod b/doc/man3/SSL_CTX_set_msg_callback.pod index c1748bbd8a..7acc796a24 100644 --- a/doc/man3/SSL_CTX_set_msg_callback.pod +++ b/doc/man3/SSL_CTX_set_msg_callback.pod @@ -108,8 +108,8 @@ processed. Due to automatic protocol version negotiation, I is not necessarily the protocol version used by the sender of the message: If -a TLS 1.0 ClientHello message is received by an SSL 3.0-only server, -I will be B. +a TLS 1.1 ClientHello message is received by an TLS 1-only server, +I will be B. Pseudo content type values may be sent at various points during the processing of data. The following pseudo content types are currently defined: diff --git a/doc/man3/SSL_CTX_set_options.pod b/doc/man3/SSL_CTX_set_options.pod index ae6f11cd54..400e492f3a 100644 --- a/doc/man3/SSL_CTX_set_options.pod +++ b/doc/man3/SSL_CTX_set_options.pod @@ -71,7 +71,7 @@ CSP 3.x. =item SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS -Disables a countermeasure against an SSL 3.0/TLS 1.0 protocol +Disables a countermeasure against an TLS 1.0 protocol vulnerability affecting CBC ciphers, which cannot be handled by some broken SSL implementations. This option has no effect for connections using other ciphers. diff --git a/doc/man3/SSL_CTX_set_security_level.pod b/doc/man3/SSL_CTX_set_security_level.pod index b490c74039..38bd87b5f0 100644 --- a/doc/man3/SSL_CTX_set_security_level.pod +++ b/doc/man3/SSL_CTX_set_security_level.pod @@ -78,7 +78,7 @@ DSA and DH keys shorter than 1024 bits and ECC keys shorter than 160 bits are prohibited. Any cipher suite using MD5 for the MAC is also prohibited. Any cipher suites using CCM with a 64 bit authentication tag are prohibited. Note that signatures using SHA1 and MD5 are also forbidden at this level as they -have less than 80 security bits. Additionally, SSLv3, TLS 1.0, TLS 1.1 and +have less than 80 security bits. Additionally, TLS 1.0, TLS 1.1 and DTLS 1.0 are all disabled at this level. =item B diff --git a/doc/man3/SSL_CTX_set_split_send_fragment.pod b/doc/man3/SSL_CTX_set_split_send_fragment.pod index 22433e5e37..d26b41fcfe 100644 --- a/doc/man3/SSL_CTX_set_split_send_fragment.pod +++ b/doc/man3/SSL_CTX_set_split_send_fragment.pod @@ -38,7 +38,7 @@ able to process multiple simultaneous crypto operations. This capability could be utilised to parallelise the processing of a single connection. For example a single write can be split into multiple records and each one encrypted independently and in parallel. Note: this would only work in -TLS1.1+. There was no support in SSLv3, TLSv1.0 or DTLS (any version). This +TLS1.1+. There was no support in TLSv1.0 or DTLS (any version). This capability is known as "pipelining" within OpenSSL. In order to benefit from the pipelining capability, you would need to have an diff --git a/doc/man3/SSL_CTX_set_tlsext_ticket_key_cb.pod b/doc/man3/SSL_CTX_set_tlsext_ticket_key_cb.pod index e4871590f7..4176bc467b 100644 --- a/doc/man3/SSL_CTX_set_tlsext_ticket_key_cb.pod +++ b/doc/man3/SSL_CTX_set_tlsext_ticket_key_cb.pod @@ -29,8 +29,7 @@ see L: SSL_CTX_set_tlsext_ticket_key_evp_cb() sets a callback function I for handling session tickets for the ssl context I. Session tickets, defined in RFC5077 provide an enhanced session resumption capability where the server -implementation is not required to maintain per session state. It only applies -to TLS and there is no SSLv3 implementation. +implementation is not required to maintain per session state. The callback function I will be called for every client instigated TLS session when session ticket extension is presented in the TLS hello diff --git a/doc/man3/SSL_get_version.pod b/doc/man3/SSL_get_version.pod index b7c2d8156b..2d9fd78cfc 100644 --- a/doc/man3/SSL_get_version.pod +++ b/doc/man3/SSL_get_version.pod @@ -100,10 +100,6 @@ of the following: =over 4 -=item SSL3_VERSION - -The connection uses the SSLv3 protocol. - =item TLS1_VERSION The connection uses the TLSv1.0 protocol. diff --git a/include/internal/ssl3_cbc.h b/include/internal/ssl3_cbc.h index 89a3efa25d..84c2ccb813 100644 --- a/include/internal/ssl3_cbc.h +++ b/include/internal/ssl3_cbc.h @@ -13,13 +13,6 @@ #include /* tls_pad.c */ -int ssl3_cbc_remove_padding_and_mac(size_t *reclen, - size_t origreclen, - unsigned char *recdata, - unsigned char **mac, - int *alloced, - size_t block_size, size_t mac_size, - OSSL_LIB_CTX *libctx); int tls1_cbc_remove_padding_and_mac(size_t *reclen, size_t origreclen, @@ -40,6 +33,6 @@ __owur int ssl3_cbc_digest_record(const EVP_MD *md, size_t data_size, size_t data_plus_mac_plus_padding_size, const unsigned char *mac_secret, - size_t mac_secret_length, char is_sslv3); + size_t mac_secret_length); #endif /* !defined(OSSL_INTERNAL_SSL3_CBC_H) */ diff --git a/include/openssl/ssl.h.in b/include/openssl/ssl.h.in index fb044db957..7fc254d75e 100644 --- a/include/openssl/ssl.h.in +++ b/include/openssl/ssl.h.in @@ -346,7 +346,7 @@ typedef int (*SSL_async_callback_fn)(SSL *s, void *arg); /* In TLSv1.3 allow a non-(ec)dhe based kex_mode */ #define SSL_OP_ALLOW_NO_DHE_KEX SSL_OP_BIT(10) /* - * Disable SSL 3.0/TLS 1.0 CBC vulnerability workaround that was added + * Disable TLS 1.0 CBC vulnerability workaround that was added * in OpenSSL 0.9.6d. Usually (depending on the application protocol) * the workaround is not needed. Unfortunately some broken SSL/TLS * implementations cannot handle it at all, which is why we include it diff --git a/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.c b/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.c index b1041b5550..747a30f287 100644 --- a/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.c +++ b/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.c @@ -176,8 +176,7 @@ static int aes_set_ctx_params(void *vctx, const OSSL_PARAM params[]) ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); return 0; } - if (ctx->base.tlsversion == SSL3_VERSION - || ctx->base.tlsversion == TLS1_VERSION) { + if (ctx->base.tlsversion == TLS1_VERSION) { if (!ossl_assert(ctx->base.removetlsfixed >= AES_BLOCK_SIZE)) { ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); return 0; diff --git a/providers/implementations/ciphers/ciphercommon.c b/providers/implementations/ciphers/ciphercommon.c index 36c3864c9a..a6320d2d81 100644 --- a/providers/implementations/ciphers/ciphercommon.c +++ b/providers/implementations/ciphers/ciphercommon.c @@ -276,15 +276,9 @@ int ossl_cipher_generic_block_update(void *vctx, unsigned char *out, return 0; } padval = (unsigned char)(padnum - 1); - if (ctx->tlsversion == SSL3_VERSION) { - if (padnum > 1) - memset(out + inl, 0, padnum - 1); - *(out + inl + padnum - 1) = padval; - } else { - /* we need to add 'padnum' padding bytes of value padval */ - for (loop = inl; loop < inl + padnum; loop++) - out[loop] = padval; - } + /* we need to add 'padnum' padding bytes of value padval */ + for (loop = inl; loop < inl + padnum; loop++) + out[loop] = padval; inl += padnum; } diff --git a/providers/implementations/ciphers/ciphercommon_block.c b/providers/implementations/ciphers/ciphercommon_block.c index 634bae4c1c..8ce491ddca 100644 --- a/providers/implementations/ciphers/ciphercommon_block.c +++ b/providers/implementations/ciphers/ciphercommon_block.c @@ -123,7 +123,7 @@ int ossl_cipher_unpadblock(unsigned char *buf, size_t *buflen, size_t blocksize) * time. * * libctx: Our library context - * tlsversion: The TLS version in use, e.g. SSL3_VERSION, TLS1_VERSION, etc + * tlsversion: The TLS version in use, e.g. TLS1_VERSION, etc * buf: The decrypted TLS record data * buflen: The length of the decrypted TLS record data. Updated with the new * length after the padding is removed @@ -147,11 +147,6 @@ int ossl_cipher_tlsunpadblock(OSSL_LIB_CTX *libctx, unsigned int tlsversion, int ret; switch (tlsversion) { - case SSL3_VERSION: - return ssl3_cbc_remove_padding_and_mac(buflen, *buflen, buf, mac, - alloced, blocksize, macsize, - libctx); - case TLS1_2_VERSION: case DTLS1_2_VERSION: case TLS1_1_VERSION: diff --git a/providers/implementations/macs/hmac_prov.c b/providers/implementations/macs/hmac_prov.c index f3dcdf962b..201d311bfe 100644 --- a/providers/implementations/macs/hmac_prov.c +++ b/providers/implementations/macs/hmac_prov.c @@ -239,8 +239,7 @@ static int hmac_update(void *vmacctx, const unsigned char *data, datalen, macctx->tls_data_size, macctx->key, - macctx->keylen, - 0); + macctx->keylen); } return HMAC_Update(macctx->ctx, data, datalen); diff --git a/ssl/methods.c b/ssl/methods.c index 37bd37f719..4c7d46efdb 100644 --- a/ssl/methods.c +++ b/ssl/methods.c @@ -13,7 +13,7 @@ #include "ssl_local.h" /*- - * TLS/SSLv3 methods + * TLS methods */ IMPLEMENT_tls_meth_func(TLS_ANY_VERSION, 0, 0, @@ -42,7 +42,7 @@ IMPLEMENT_tls_meth_func(TLS1_VERSION, SSL_METHOD_NO_SUITEB, SSL_OP_NO_TLSv1, ossl_statem_accept, ossl_statem_connect, TLSv1_enc_data) #endif /*- - * TLS/SSLv3 server methods + * TLS server methods */ IMPLEMENT_tls_meth_func(TLS_ANY_VERSION, 0, 0, TLS_server_method, @@ -71,7 +71,7 @@ IMPLEMENT_tls_meth_func(TLS1_VERSION, SSL_METHOD_NO_SUITEB, SSL_OP_NO_TLSv1, ssl_undefined_function, TLSv1_enc_data) #endif /*- - * TLS/SSLv3 client methods + * TLS client methods */ IMPLEMENT_tls_meth_func(TLS_ANY_VERSION, 0, 0, TLS_client_method, diff --git a/ssl/record/methods/recmethod_local.h b/ssl/record/methods/recmethod_local.h index d6db9850c0..c5cdfc6cfc 100644 --- a/ssl/record/methods/recmethod_local.h +++ b/ssl/record/methods/recmethod_local.h @@ -287,8 +287,7 @@ struct ossl_record_layer_st { /* * Do we need to send a prefix empty record before application data as a - * countermeasure against known-IV weakness (necessary for SSLv3 and - * TLSv1.0) + * countermeasure against known-IV weakness (necessary for TLSv1.0) */ int need_empty_fragments; @@ -326,9 +325,6 @@ struct ossl_record_layer_st { size_t block_padding; size_t hs_padding; - /* Only used by SSLv3 */ - unsigned char mac_secret[EVP_MAX_MD_SIZE]; - /* TLSv1.0/TLSv1.1/TLSv1.2 */ int use_etm; diff --git a/ssl/record/methods/ssl3_cbc.c b/ssl/record/methods/ssl3_cbc.c index 3702c03c52..133067ab15 100644 --- a/ssl/record/methods/ssl3_cbc.c +++ b/ssl/record/methods/ssl3_cbc.c @@ -105,8 +105,7 @@ static void tls1_sha512_final_raw(void *ctx, unsigned char *md_out) #define LARGEST_DIGEST_CTX SHA512_CTX /*- - * ssl3_cbc_digest_record computes the MAC of a decrypted, padded SSLv3/TLS - * record. + * ssl3_cbc_digest_record computes the MAC of a decrypted, padded TLS record. * * ctx: the EVP_MD_CTX from which we take the hash function. * ssl3_cbc_record_digest_supported must return true for this EVP_MD_CTX. @@ -118,7 +117,6 @@ static void tls1_sha512_final_raw(void *ctx, unsigned char *md_out) * has been removed. * data_plus_mac_plus_padding_size: the public length of the whole * record, including MAC and padding. - * is_sslv3: non-zero if we are to use SSLv3. Otherwise, TLS. * * On entry: we know that data is data_plus_mac_plus_padding_size in length * Returns 1 on success or 0 on error @@ -131,7 +129,7 @@ int ssl3_cbc_digest_record(const EVP_MD *md, size_t data_size, size_t data_plus_mac_plus_padding_size, const unsigned char *mac_secret, - size_t mac_secret_length, char is_sslv3) + size_t mac_secret_length) { union { OSSL_UNION_ALIGN; @@ -140,9 +138,8 @@ int ssl3_cbc_digest_record(const EVP_MD *md, void (*md_final_raw)(void *ctx, unsigned char *md_out); void (*md_transform)(void *ctx, const unsigned char *block); size_t md_size, md_block_size = 64; - size_t sslv3_pad_length = 40, header_length, variance_blocks, - len, max_mac_bytes, num_blocks, - num_starting_blocks, k, mac_end_offset, c, index_a, index_b; + size_t header_length, variance_blocks, len, max_mac_bytes, num_blocks, + num_starting_blocks, k, mac_end_offset, c, index_a, index_b; size_t bits; /* at most 18 bits */ unsigned char length_bytes[MAX_HASH_BIT_COUNT_BYTES]; /* hmac_pad is the masked HMAC key. */ @@ -176,7 +173,6 @@ int ssl3_cbc_digest_record(const EVP_MD *md, md_final_raw = tls1_md5_final_raw; md_transform = (void (*)(void *ctx, const unsigned char *block))MD5_Transform; md_size = 16; - sslv3_pad_length = 48; length_is_big_endian = 0; #endif } else if (EVP_MD_is_a(md, "SHA1")) { @@ -229,22 +225,11 @@ int ssl3_cbc_digest_record(const EVP_MD *md, return 0; header_length = 13; - if (is_sslv3) { - header_length = mac_secret_length - + sslv3_pad_length - + 8 /* sequence number */ - + 1 /* record type */ - + 2; /* record length */ - } /* * variance_blocks is the number of blocks of the hash that we have to * calculate in constant time because they could be altered by the - * padding value. In SSLv3, the padding must be minimal so the end of - * the plaintext varies by, at most, 15+20 = 35 bytes. (We conservatively - * assume that the MAC size varies from 0..20 bytes.) In case the 9 bytes - * of hash termination (0x80 + 64-bit length) don't fit in the final - * block, we say that the final two blocks can vary based on the padding. + * padding value. * TLSv1 has MACs up to 48 bytes long (SHA-384) and the padding is not * required to be minimal. Therefore we say that the final |variance_blocks| * blocks can @@ -252,14 +237,11 @@ int ssl3_cbc_digest_record(const EVP_MD *md, * short and there obviously cannot be this many blocks then * variance_blocks can be reduced. */ - variance_blocks = is_sslv3 ? 2 - : (((255 + 1 + md_size + md_block_size - 1) - / md_block_size) - + 1); + variance_blocks = ((255 + 1 + md_size + md_block_size - 1) / md_block_size) + 1; + /* * From now on we're dealing with the MAC, which conceptually has 13 - * bytes of `header' before the start of the data (TLS) or 71/75 bytes - * (SSLv3) + * bytes of `header' before the start of the data (TLS) */ len = data_plus_mac_plus_padding_size + header_length; /* @@ -302,37 +284,30 @@ int ssl3_cbc_digest_record(const EVP_MD *md, * in bits. */ index_b = (mac_end_offset + md_length_size) / md_block_size; - /* - * bits is the hash-length in bits. It includes the additional hash block - * for the masked HMAC key, or whole of |header| in the case of SSLv3. - */ - /* - * For SSLv3, if we're going to have any starting blocks then we need at - * least two because the header is larger than a single block. - */ - if (num_blocks > variance_blocks + (is_sslv3 ? 1 : 0)) { + if (num_blocks > variance_blocks) { num_starting_blocks = num_blocks - variance_blocks; k = md_block_size * num_starting_blocks; } + /* + * bits is the hash-length in bits. It includes the additional hash block + * for the masked HMAC key, or whole of |header| in the case of SSLv3. + */ bits = 8 * mac_end_offset; - if (!is_sslv3) { - /* - * Compute the initial HMAC block. For SSLv3, the padding and secret - * bytes are included in |header| because they take more than a - * single block. - */ - bits += 8 * md_block_size; - memset(hmac_pad, 0, md_block_size); - if (!ossl_assert(mac_secret_length <= sizeof(hmac_pad))) - return 0; - memcpy(hmac_pad, mac_secret, mac_secret_length); - for (i = 0; i < md_block_size; i++) - hmac_pad[i] ^= 0x36; - md_transform(md_state.c, hmac_pad); - } + /* + * Compute the initial HMAC block. + */ + bits += 8 * md_block_size; + memset(hmac_pad, 0, md_block_size); + if (!ossl_assert(mac_secret_length <= sizeof(hmac_pad))) + return 0; + memcpy(hmac_pad, mac_secret, mac_secret_length); + for (i = 0; i < md_block_size; i++) + hmac_pad[i] ^= 0x36; + + md_transform(md_state.c, hmac_pad); if (length_is_big_endian) { memset(length_bytes, 0, md_length_size - 4); @@ -349,37 +324,12 @@ int ssl3_cbc_digest_record(const EVP_MD *md, } if (k > 0) { - if (is_sslv3) { - size_t overhang; - - /* - * The SSLv3 header is larger than a single block. overhang is - * the number of bytes beyond a single block that the header - * consumes: either 7 bytes (SHA1) or 11 bytes (MD5). There are no - * ciphersuites in SSLv3 that are not SHA1 or MD5 based and - * therefore we can be confident that the header_length will be - * greater than |md_block_size|. However we add a sanity check just - * in case - */ - if (header_length <= md_block_size) { - /* Should never happen */ - return 0; - } - overhang = header_length - md_block_size; - md_transform(md_state.c, header); - memcpy(first_block, header + md_block_size, overhang); - memcpy(first_block + overhang, data, md_block_size - overhang); - md_transform(md_state.c, first_block); - for (i = 1; i < k / md_block_size - 1; i++) - md_transform(md_state.c, data + md_block_size * i - overhang); - } else { - /* k is a multiple of md_block_size. */ - memcpy(first_block, header, 13); - memcpy(first_block + 13, data, md_block_size - 13); - md_transform(md_state.c, first_block); - for (i = 1; i < k / md_block_size; i++) - md_transform(md_state.c, data + md_block_size * i - 13); - } + /* k is a multiple of md_block_size. */ + memcpy(first_block, header, 13); + memcpy(first_block + 13, data, md_block_size - 13); + md_transform(md_state.c, first_block); + for (i = 1; i < k / md_block_size; i++) + md_transform(md_state.c, data + md_block_size * i - 13); } memset(mac_out, 0, sizeof(mac_out)); @@ -449,23 +399,15 @@ int ssl3_cbc_digest_record(const EVP_MD *md, if (EVP_DigestInit_ex(md_ctx, md, NULL) <= 0) goto err; - if (is_sslv3) { - /* We repurpose |hmac_pad| to contain the SSLv3 pad2 block. */ - memset(hmac_pad, 0x5c, sslv3_pad_length); - if (EVP_DigestUpdate(md_ctx, mac_secret, mac_secret_length) <= 0 - || EVP_DigestUpdate(md_ctx, hmac_pad, sslv3_pad_length) <= 0 - || EVP_DigestUpdate(md_ctx, mac_out, md_size) <= 0) - goto err; - } else { - /* Complete the HMAC in the standard manner. */ - for (i = 0; i < md_block_size; i++) - hmac_pad[i] ^= 0x6a; + /* Complete the HMAC in the standard manner. */ + for (i = 0; i < md_block_size; i++) + hmac_pad[i] ^= 0x6a; + + if (EVP_DigestUpdate(md_ctx, hmac_pad, md_block_size) <= 0 + || EVP_DigestUpdate(md_ctx, mac_out, md_size) <= 0) + goto err; - if (EVP_DigestUpdate(md_ctx, hmac_pad, md_block_size) <= 0 - || EVP_DigestUpdate(md_ctx, mac_out, md_size) <= 0) - goto err; - } ret = EVP_DigestFinal(md_ctx, md_out, &md_out_size_u); if (ret && md_out_size) *md_out_size = md_out_size_u; diff --git a/ssl/record/methods/tls1_meth.c b/ssl/record/methods/tls1_meth.c index f89c365d7f..717e1ed3ff 100644 --- a/ssl/record/methods/tls1_meth.c +++ b/ssl/record/methods/tls1_meth.c @@ -530,7 +530,6 @@ end: #endif /* OPENSSL_NO_COMP */ #endif -/* This function is also used by the SSLv3 implementation */ int tls1_allocate_write_buffers(OSSL_RECORD_LAYER *rl, OSSL_RECORD_TEMPLATE *templates, size_t numtempl, size_t *prefix) @@ -552,7 +551,6 @@ int tls1_allocate_write_buffers(OSSL_RECORD_LAYER *rl, return 1; } -/* This function is also used by the SSLv3 implementation */ int tls1_initialise_write_packets(OSSL_RECORD_LAYER *rl, OSSL_RECORD_TEMPLATE *templates, size_t numtempl, diff --git a/ssl/record/methods/tls_common.c b/ssl/record/methods/tls_common.c index 190e61bb1c..0363bf23bf 100644 --- a/ssl/record/methods/tls_common.c +++ b/ssl/record/methods/tls_common.c @@ -1401,8 +1401,6 @@ static void tls_int_free(OSSL_RECORD_LAYER *rl) #endif OPENSSL_free(rl->iv); OPENSSL_free(rl->nonce); - if (rl->version == SSL3_VERSION) - OPENSSL_cleanse(rl->mac_secret, sizeof(rl->mac_secret)); TLS_RL_RECORD_release(rl->rrec, SSL_MAX_PIPELINES); diff --git a/ssl/record/methods/tls_pad.c b/ssl/record/methods/tls_pad.c index 7209506bc4..a017e9221e 100644 --- a/ssl/record/methods/tls_pad.c +++ b/ssl/record/methods/tls_pad.c @@ -33,51 +33,6 @@ static int ssl3_cbc_copy_mac(size_t *reclen, size_t good, OSSL_LIB_CTX *libctx); -/*- - * ssl3_cbc_remove_padding removes padding from the decrypted, SSLv3, CBC - * record in |recdata| by updating |reclen| in constant time. It also extracts - * the MAC from the underlying record and places a pointer to it in |mac|. The - * MAC data can either be newly allocated memory, or a pointer inside the - * |recdata| buffer. If allocated then |*alloced| is set to 1, otherwise it is - * set to 0. - * - * origreclen: the original record length before any changes were made - * block_size: the block size of the cipher used to encrypt the record. - * mac_size: the size of the MAC to be extracted - * aead: 1 if an AEAD cipher is in use, or 0 otherwise - * returns: - * 0: if the record is publicly invalid. - * 1: if the record is publicly valid. If the padding removal fails then the - * MAC returned is random. - */ -int ssl3_cbc_remove_padding_and_mac(size_t *reclen, - size_t origreclen, - unsigned char *recdata, - unsigned char **mac, - int *alloced, - size_t block_size, size_t mac_size, - OSSL_LIB_CTX *libctx) -{ - size_t padding_length; - size_t good; - const size_t overhead = 1 /* padding length byte */ + mac_size; - - /* - * These lengths are all public so we can test them in non-constant time. - */ - if (overhead > *reclen) - return 0; - - padding_length = recdata[*reclen - 1]; - good = constant_time_ge_s(*reclen, padding_length + overhead); - /* SSLv3 requires that the padding is minimal. */ - good &= constant_time_ge_s(block_size, padding_length + 1); - *reclen -= good & (padding_length + 1); - - return ssl3_cbc_copy_mac(reclen, origreclen, recdata, mac, alloced, - block_size, mac_size, good, libctx); -} - /*- * tls1_cbc_remove_padding_and_mac removes padding from the decrypted, TLS, CBC * record in |recdata| by updating |reclen| in constant time. It also extracts diff --git a/ssl/ssl_ciph.c b/ssl/ssl_ciph.c index 61e8c4abbe..47e3ff4df3 100644 --- a/ssl/ssl_ciph.c +++ b/ssl/ssl_ciph.c @@ -618,7 +618,7 @@ static void ssl_cipher_collect_ciphers(const SSL_METHOD *ssl_method, /* * We have num_of_ciphers descriptions compiled in, depending on the - * method selected (SSLv3, TLSv1 etc). + * method selected (TLSv1, etc.). * These will later be sorted in a linked list with at most num * entries. */ @@ -2281,7 +2281,7 @@ int ssl_cipher_list_to_bytes(SSL_CONNECTION *s, STACK_OF(SSL_CIPHER) *sk, c = sk_SSL_CIPHER_value(sk, i); /* Skip disabled ciphers */ - if (ssl_cipher_disabled(s, c, SSL_SECOP_CIPHER_SUPPORTED, 0)) + if (ssl_cipher_disabled(s, c, SSL_SECOP_CIPHER_SUPPORTED)) continue; if (!ssl->method->put_cipher_by_char(c, pkt, &len)) { diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index c89f3e4017..2902a5f5aa 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -510,10 +510,6 @@ static int ssl_check_allowed_versions(int min_version, int max_version) if (max_version == TLS1_1_VERSION) max_version = TLS1_VERSION; #endif -#ifdef OPENSSL_NO_TLS1 - if (max_version == TLS1_VERSION) - max_version = SSL3_VERSION; -#endif #ifdef OPENSSL_NO_TLS1 if (min_version == TLS1_VERSION) min_version = TLS1_1_VERSION; @@ -3469,7 +3465,7 @@ STACK_OF(SSL_CIPHER) *SSL_get1_supported_ciphers(SSL *s) return NULL; for (i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) { const SSL_CIPHER *c = sk_SSL_CIPHER_value(ciphers, i); - if (!ssl_cipher_disabled(sc, c, SSL_SECOP_CIPHER_SUPPORTED, 0)) { + if (!ssl_cipher_disabled(sc, c, SSL_SECOP_CIPHER_SUPPORTED)) { if (!sk) sk = sk_SSL_CIPHER_new_null(); if (!sk) diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h index a8171c30fa..5c0fa8f925 100644 --- a/ssl/ssl_local.h +++ b/ssl/ssl_local.h @@ -387,7 +387,7 @@ typedef enum { SSL_PHA_REQUESTED /* request received by client, or sent by server */ } SSL_PHA_STATE; -/* CipherSuite length. SSLv3 and all TLS versions. */ +/* CipherSuite value length. */ #define TLS_CIPHER_LEN 2 /* used to hold info on the particular ciphers used */ struct ssl_cipher_st { @@ -447,7 +447,7 @@ struct ssl_method_st { int (*num_ciphers)(void); const SSL_CIPHER *(*get_cipher)(unsigned ncipher); OSSL_TIME (*get_timeout)(void); - const struct ssl3_enc_method *ssl3_enc; /* Extra SSLv3/TLS stuff */ + const struct ssl3_enc_method *ssl3_enc; /* Extra TLS stuff */ int (*ssl_version)(void); long (*ssl_callback_ctrl)(SSL *s, int cb_id, void (*fp)(void)); long (*ssl_ctx_callback_ctrl)(SSL_CTX *s, int cb_id, void (*fp)(void)); @@ -927,11 +927,8 @@ struct ssl_ctx_st { CRYPTO_EX_DATA ex_data; - const EVP_MD *md5; /* For SSLv3/TLSv1 'ssl3-md5' */ - const EVP_MD *sha1; /* For SSLv3/TLSv1 'ssl3-sha1' */ - STACK_OF(X509) *extra_certs; - STACK_OF(SSL_COMP) *comp_methods; /* stack of SSL_COMP, SSLv3/TLSv1 */ + STACK_OF(SSL_COMP) *comp_methods; /* stack of SSL_COMP, TLSv1 */ /* Default values used when no per-SSL value is defined follow */ @@ -1279,8 +1276,7 @@ struct ssl_connection_st { SSL *user_ssl; /* - * protocol version (one of SSL2_VERSION, SSL3_VERSION, TLS1_VERSION, - * DTLS1_VERSION) + * protocol version (one of TLS1_VERSION, DTLS1_VERSION) */ int version; /* @@ -1375,7 +1371,7 @@ struct ssl_connection_st { int in_read_app_data; struct { - /* actually only need to be 16+20 for SSLv3 and 12 for TLS */ + /* actually only need to be 12 for TLS */ unsigned char finish_md[EVP_MAX_MD_SIZE * 2]; size_t finish_md_len; unsigned char peer_finish_md[EVP_MAX_MD_SIZE * 2]; @@ -1626,7 +1622,7 @@ struct ssl_connection_st { int first_packet; /* * What was passed in ClientHello.legacy_version. Used for RSA pre-master - * secret and SSLv3/TLS (<=1.2) rollback check + * secret and (D)TLS (<=1.2) rollback check */ int client_version; /* @@ -2183,8 +2179,7 @@ typedef struct cert_st { } CERT; /* - * This is for the SSLv3/TLSv1.0 differences in crypto/hash stuff It is a bit - * of a mess of functions, but hell, think of it as an opaque structure :-) + * This is for the TLSv1.0 differences in crypto/hash stuff. */ typedef struct ssl3_enc_method { int (*setup_key_block)(SSL_CONNECTION *); @@ -2842,7 +2837,7 @@ __owur int tls_check_sigalg_curve(const SSL_CONNECTION *s, int curve); __owur int tls12_check_peer_sigalg(SSL_CONNECTION *s, uint16_t, EVP_PKEY *pkey); __owur int ssl_set_client_disabled(SSL_CONNECTION *s); __owur int ssl_cipher_disabled(const SSL_CONNECTION *s, const SSL_CIPHER *c, - int op, int echde); + int op); __owur int ssl_handshake_hash(SSL_CONNECTION *s, unsigned char *out, size_t outlen, diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c index 1bd1110973..1ba4add39d 100644 --- a/ssl/ssl_sess.c +++ b/ssl/ssl_sess.c @@ -306,7 +306,7 @@ unsigned int SSL_SESSION_get_compress_id(const SSL_SESSION *s) } /* - * SSLv3/TLSv1 has 32 bytes (256 bits) of session ID space. As such, filling + * TLSv1 has 32 bytes (256 bits) of session ID space. As such, filling * the ID with random junk repeatedly until we have no conflict is going to * complete in one iteration pretty much "most" of the time (btw: * understatement). So, if it takes us 10 iterations and we still can't avoid diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c index 6c77296c31..15ed8ef8d5 100644 --- a/ssl/statem/statem_clnt.c +++ b/ssl/statem/statem_clnt.c @@ -1639,7 +1639,7 @@ static int set_client_ciphersuite(SSL_CONNECTION *s, * If it is a disabled cipher we either didn't send it in client hello, * or it's not allowed for the selected protocol. So we return an error. */ - if (ssl_cipher_disabled(s, c, SSL_SECOP_CIPHER_CHECK, 1)) { + if (ssl_cipher_disabled(s, c, SSL_SECOP_CIPHER_CHECK)) { SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_WRONG_CIPHER_RETURNED); return 0; } diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c index 46e418676e..a90d401679 100644 --- a/ssl/statem/statem_srvr.c +++ b/ssl/statem/statem_srvr.c @@ -2550,18 +2550,6 @@ WORK_STATE tls_post_process_client_hello(SSL_CONNECTION *s, WORK_STATE wst) s->s3.tmp.new_cipher = s->session->cipher; } - /*- - * we now have the following setup. - * client_random - * cipher_list - our preferred list of ciphers - * ciphers - the client's preferred list of ciphers - * compression - basically ignored right now - * ssl version is set - sslv3 - * s->session - The ssl session has been setup. - * s->hit - session reuse flag - * s->s3.tmp.new_cipher - the new cipher to use. - */ - /* * Call status_request callback if needed. Has to be done after the * certificate callbacks etc above. diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index df3db34125..b55b101324 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -3052,12 +3052,11 @@ int ssl_set_client_disabled(SSL_CONNECTION *s) * @s: SSL connection that you want to use the cipher on * @c: cipher to check * @op: Security check that you want to do - * @ecdhe: If set to 1 then TLSv1 ECDHE ciphers are also allowed in SSLv3 * * Returns 1 when it's disabled, 0 when enabled. */ int ssl_cipher_disabled(const SSL_CONNECTION *s, const SSL_CIPHER *c, - int op, int ecdhe) + int op) { int minversion = SSL_CONNECTION_IS_DTLS(s) ? c->min_dtls : c->min_tls; int maxversion = SSL_CONNECTION_IS_DTLS(s) ? c->max_dtls : c->max_tls; @@ -3079,15 +3078,6 @@ int ssl_cipher_disabled(const SSL_CONNECTION *s, const SSL_CIPHER *c, return 1; } - /* - * For historical reasons we will allow ECHDE to be selected by a server - * in SSLv3 if we are a client - */ - if (minversion == TLS1_VERSION - && ecdhe - && (c->algorithm_mkey & (SSL_kECDHE | SSL_kECDHEPSK)) != 0) - minversion = SSL3_VERSION; - if (ssl_version_cmp(s, minversion, s->s3.tmp.max_ver) > 0 || ssl_version_cmp(s, maxversion, s->s3.tmp.min_ver) < 0) return 1; @@ -3537,7 +3527,7 @@ static int tls12_sigalg_allowed(const SSL_CONNECTION *s, int op, c = sk_SSL_CIPHER_value(sk, i); /* Skip disabled ciphers */ - if (ssl_cipher_disabled(s, c, SSL_SECOP_CIPHER_SUPPORTED, 0)) + if (ssl_cipher_disabled(s, c, SSL_SECOP_CIPHER_SUPPORTED)) continue; if ((c->algorithm_mkey & (SSL_kGOST | SSL_kGOST18)) != 0) diff --git a/test/ssl_old_test.c b/test/ssl_old_test.c index 8a3c7b5fb0..40df6536e3 100644 --- a/test/ssl_old_test.c +++ b/test/ssl_old_test.c @@ -1284,7 +1284,7 @@ int main(int argc, char *argv[]) no_protocol = 0; /* - * Testing was requested for a compiled-out protocol (e.g. SSLv3). + * Testing was requested for a compiled-out protocol (e.g. TLSv1, etc.). * Ideally, we would error out, but the generic test wrapper can't know * when to expect failure. So we do nothing and return success. */ From f0ca416976ac5b6761bf45e28fa42f1981edbe2c Mon Sep 17 00:00:00 2001 From: Ingo Franzki Date: Thu, 25 Jun 2026 11:06:30 +0200 Subject: [PATCH 128/349] s390x: Fix return code handling in HMAC_Init_ex() When running on the s390x platform HMAC_Init_ex() calls s390x_HMAC_init() to optionally allow hardware acceleration of the HMAC operation. In case the hardware acceleration is not available, s390x_HMAC_init() returns -1 to indicate that. In this case the software path is continued. The problem is that rv was set to -1 by s390x_HMAC_init() and stays at this until the end of the function. In case the software path detects an error it goes to the 'err' label which just returns rv as is, and thus HMAC_Init_ex() now returns -1 instead of 0 (rv was initialized to 0 at declaration). The wrong return value might then be propagated through all layers, i.e. to EVP_MAC_init() which also returns -1 in this case. However, EVP_MAC_init() is defined as returning 1 on success, or 0 on error, i.e. a boolean kind of return value. Typically, callers will do something like 'if (!EVP_MAC_init(s....))' to check for errors. A return value of -1 is non-zero, and thus it is treated as successful return. Fix this by setting rv back to 0 when s390x_HMAC_init() returned -1. Fixes: 0499de5adda2 "s390x: Add hardware acceleration for HMAC" Resolves: https://github.com/openssl/openssl/issues/31706 Signed-off-by: Ingo Franzki Reviewed-by: Eugene Syromiatnikov Reviewed-by: Milan Broz MergeDate: Tue Jun 30 20:49:52 2026 (Merged from https://github.com/openssl/openssl/pull/31723) --- crypto/hmac/hmac.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crypto/hmac/hmac.c b/crypto/hmac/hmac.c index ab0f2b5ebd..f12b5bf0b3 100644 --- a/crypto/hmac/hmac.c +++ b/crypto/hmac/hmac.c @@ -53,9 +53,11 @@ int HMAC_Init_ex(HMAC_CTX *ctx, const void *key, int len, return 0; #ifdef OPENSSL_HMAC_S390X - rv = s390x_HMAC_init(ctx, key, len); - if (rv != -1) - return rv; + { + int ret = s390x_HMAC_init(ctx, key, len); + if (ret != -1) /* -1 means SW fallback */ + return ret; + } #endif if (key != NULL) { From 1be08a77911e039959c74be7ab4eecc63b5ae76d Mon Sep 17 00:00:00 2001 From: Pauli Date: Mon, 29 Jun 2026 11:07:42 +1000 Subject: [PATCH 129/349] demo: fix fips-version Makefile so it loads the FIPS provider correctly Fixes #31760 Reviewed-by: Eugene Syromiatnikov Reviewed-by: Nikola Pajkovsky (Merged from https://github.com/openssl/openssl/pull/31762) --- demos/info/Makefile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/demos/info/Makefile b/demos/info/Makefile index ef93a25df6..05f1707ba5 100644 --- a/demos/info/Makefile +++ b/demos/info/Makefile @@ -2,7 +2,6 @@ # To run the demos when linked with a shared library (default) ensure # that libcrypto is on the library path. For example: # -# LD_LIBRARY_PATH=../.. ./info TESTS = fips-version @@ -25,5 +24,9 @@ test: all @echo "\nINFO tests:" @set -e; for tst in $(TESTS); do \ echo "\n"$$tst; \ - LD_LIBRARY_PATH=../.. ./$$tst; \ + LD_LIBRARY_PATH=../.. \ + OPENSSL_CONF=../../test/fips-and-base.cnf \ + OPENSSL_MODULES=../../providers \ + OPENSSL_CONF_INCLUDE=../../providers \ + ./$$tst; \ done From e9351bcce65c642b130cdf875fd0e64e8637d737 Mon Sep 17 00:00:00 2001 From: Ingo Franzki Date: Mon, 29 Jun 2026 13:04:32 +0200 Subject: [PATCH 130/349] s390x: Fix AES-XTS hardware acceleration in IBM z17 For the re-init case where only the IV is specified, but no key, the 'nap' field must also be initialized. Instead of setting the s390 specific fields in a special case block, call ctx->hw->init() also in this case. It performs the necessary setup already (when the KM function code was once set already). Adjust the cipher_hw_aes_xts_s390x_initkey() function so that it can also be called with a NULL key. It then only performs the IV setup as well as setting up the 'nap'. Closes: https://github.com/openssl/openssl/issues/31766 Signed-off-by: Ingo Franzki Reviewed-by: Simo Sorce Reviewed-by: Milan Broz Reviewed-by: Shane Lontis MergeDate: Wed Jul 1 09:12:26 2026 (Merged from https://github.com/openssl/openssl/pull/31775) --- .../ciphers/cipher_aes_hw_s390x.c | 29 +++++++++++-------- .../implementations/ciphers/cipher_aes_xts.c | 18 +++++------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/providers/implementations/ciphers/cipher_aes_hw_s390x.c b/providers/implementations/ciphers/cipher_aes_hw_s390x.c index 9c3b2e91aa..e9e81f4746 100644 --- a/providers/implementations/ciphers/cipher_aes_hw_s390x.c +++ b/providers/implementations/ciphers/cipher_aes_hw_s390x.c @@ -856,18 +856,23 @@ static int cipher_hw_aes_xts_s390x_initkey(PROV_CIPHER_CTX *ctx, unsigned int dec = 0; int supported = 0; - switch (keylen) { - case 128 / 8 * 2: - fc = S390X_XTS_AES_128_MSA10; - offs = 32; - break; - case 256 / 8 * 2: - fc = S390X_XTS_AES_256_MSA10; - offs = 0; - break; - default: - fc = 0; - break; + if (key != NULL) { + switch (keylen) { + case 128 / 8 * 2: + fc = S390X_XTS_AES_128_MSA10; + offs = 32; + break; + case 256 / 8 * 2: + fc = S390X_XTS_AES_256_MSA10; + offs = 0; + break; + default: + fc = 0; + break; + } + } else { + fc = xctx->plat.s390x.fc & ~S390X_DECRYPT; + offs = xctx->plat.s390x.offset; } if (fc != 0) diff --git a/providers/implementations/ciphers/cipher_aes_xts.c b/providers/implementations/ciphers/cipher_aes_xts.c index 10c99c400e..9be87374d1 100644 --- a/providers/implementations/ciphers/cipher_aes_xts.c +++ b/providers/implementations/ciphers/cipher_aes_xts.c @@ -81,17 +81,6 @@ static int aes_xts_init(void *vctx, const unsigned char *key, size_t keylen, if (iv != NULL) { if (!ossl_cipher_generic_initiv(vctx, iv, ivlen)) return 0; -#ifdef AES_XTS_S390X - if (key == NULL) { - /* special handle iv-only update */ - if (ivlen > sizeof(xctx->plat.s390x.param.km.tweak)) { - ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); - return 0; - } - memcpy(xctx->plat.s390x.param.km.tweak, iv, ivlen); - xctx->plat.s390x.iv_set = 1; - } -#endif } if (key != NULL) { if (keylen != ctx->keylen) { @@ -103,6 +92,13 @@ static int aes_xts_init(void *vctx, const unsigned char *key, size_t keylen, if (!ctx->hw->init(ctx, key, keylen)) return 0; } +#ifdef AES_XTS_S390X + else if (xctx->plat.s390x.fc && ctx->iv_set) { + /* special handle iv-only update */ + if (!ctx->hw->init(ctx, NULL, 0)) + return 0; + } +#endif return aes_xts_set_ctx_params(ctx, params); } From 635ffe8bb9c0d14ce33c5368d90d5e87781480c3 Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Mon, 29 Jun 2026 15:30:52 +0200 Subject: [PATCH 131/349] Fix use of BCryptGenRandom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current path for Windows RNG (for Vista and above) should use BCryptGenRandom() function but the symbol is not included in platform_symbols. The issue was hidden before commit 1eaf29ef6c reordered Windows header inclusion. Moreover, a cast to ULONG is required for strict warnings build. Reviewed-by: Neil Horman Reviewed-by: Saša Nedvědický Reviewed-by: Norbert Pocs MergeDate: Wed Jul 1 09:14:17 2026 (Merged from https://github.com/openssl/openssl/pull/31774) --- providers/implementations/rands/seeding/rand_win.c | 2 +- util/platform_symbols/windows-symbols.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/providers/implementations/rands/seeding/rand_win.c b/providers/implementations/rands/seeding/rand_win.c index 9b889e38bc..acc35936f0 100644 --- a/providers/implementations/rands/seeding/rand_win.c +++ b/providers/implementations/rands/seeding/rand_win.c @@ -70,7 +70,7 @@ size_t ossl_pool_acquire_entropy(RAND_POOL *pool) buffer = ossl_rand_pool_add_begin(pool, bytes_needed); if (buffer != NULL) { size_t bytes = 0; - if (BCryptGenRandom(NULL, buffer, bytes_needed, + if (BCryptGenRandom(NULL, buffer, (ULONG)bytes_needed, BCRYPT_USE_SYSTEM_PREFERRED_RNG) == STATUS_SUCCESS) bytes = bytes_needed; diff --git a/util/platform_symbols/windows-symbols.txt b/util/platform_symbols/windows-symbols.txt index 0f6cc11450..7ae58a3f18 100644 --- a/util/platform_symbols/windows-symbols.txt +++ b/util/platform_symbols/windows-symbols.txt @@ -1,5 +1,6 @@ AcquireSRWLockExclusive AcquireSRWLockShared +BCryptGenRandom CertCloseStore CertFindCertificateInStore CertFreeCertificateContext From 8a57600f2348a2a097127110c6db4730091e7c17 Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Mon, 29 Jun 2026 13:59:16 +0200 Subject: [PATCH 132/349] Use more recent default for _WIN32_WINNT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After the windows.h include optimization introduced in commit 1eaf29ef6c, the _WIN32_WINNT default was changed, causing performance regressions. Currently, _WIN32_WINNT is defined as 0x0501, which means WinXP. This causes the code to be compiled with WinXP-compatible code, notably - without USE_RWLOCK - using legacy thread implementation - legacy RNG seeding (no BCryptGenRandom) This patch increases the requirement to 0x600 (Windows Vista). Note that code running on WinXP cannot currently be compiled with any default configuration, as supported compilers generate executables for Windows Vista and above. If we provide some way to support WinXP, it can be done by redefining _WIN32_WINNT. Resolves: https://github.com/openssl/project/issues/2010 Fixes: 1eaf29ef6c "Remove direct includes of windows.h where possible" Reviewed-by: Neil Horman Reviewed-by: Saša Nedvědický Reviewed-by: Norbert Pocs MergeDate: Wed Jul 1 09:14:18 2026 (Merged from https://github.com/openssl/openssl/pull/31774) --- include/internal/e_os.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/internal/e_os.h b/include/internal/e_os.h index b54f4d5a70..444f888674 100644 --- a/include/internal/e_os.h +++ b/include/internal/e_os.h @@ -109,7 +109,7 @@ * 0x0603 // Windows 8.1 * 0x0A00 // Windows 10 */ -#define _WIN32_WINNT 0x0501 +#define _WIN32_WINNT 0x0600 #endif #include #include From 10c0927bb398021f39adba04e371ee72cca1c724 Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Mon, 29 Jun 2026 16:46:29 +0900 Subject: [PATCH 133/349] Suppress MSVC C4996 in applink.c applink.c deliberately stores legacy CRT function pointers because ms/uplink.h expects the old fopen and _open signatures. Keep the table entries unchanged and suppress MSVC warning C4996 locally around OPENSSL_Applink() instead of switching to fopen_s or _sopen_s. Fixes #8241 Reviewed-by: Milan Broz Reviewed-by: Eugene Syromiatnikov MergeDate: Wed Jul 1 09:17:39 2026 (Merged from https://github.com/openssl/openssl/pull/31765) --- ms/applink.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ms/applink.c b/ms/applink.c index 4777d2e361..242b0bed74 100644 --- a/ms/applink.c +++ b/ms/applink.c @@ -103,6 +103,14 @@ static int app_fsetmod(FILE *fp, char mod) extern "C" { #endif +/* + * The AppLink table exposes the legacy CRT signatures used by ms/uplink.h. + */ +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) +#endif + __declspec(dllexport) void ** #if defined(__BORLANDC__) /* @@ -151,6 +159,10 @@ __declspec(dllexport) void ** return OPENSSL_ApplinkTable; } +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + #ifdef __GNUC__ #pragma GCC diagnostic pop #endif From 2229aad774f6c7b7863b68320abf9a9cd134ab3c Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Tue, 30 Jun 2026 19:12:18 +0900 Subject: [PATCH 134/349] apps/s_server.c: fix SSL object leak on rpk_enable() failure In www_body() and rev_body(), con = SSL_new(ctx) is called before rpk_enable(con), but ownership of con is transferred to ssl_bio only later by BIO_set_ssl(..., BIO_CLOSE). If rpk_enable() fails, the code jumps to err: before that transfer without freeing con, leaking the SSL object. Add SSL_free(con) before goto err in both rpk_enable() failure paths, matching the adjacent SSL_set_session_id_context() and BIO_new_socket() error paths. sv_body() is unaffected because its err: block already frees con. The global rpk_files is not leaked. Its lifetime is managed by s_server_main(), which frees it in the end cleanup block. Resolves: https://github.com/openssl/openssl/issues/31769 Reviewed-by: Matt Caswell Reviewed-by: Eugene Syromiatnikov MergeDate: Wed Jul 1 12:04:13 2026 (Merged from https://github.com/openssl/openssl/pull/31789) --- apps/s_server.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/s_server.c b/apps/s_server.c index 43b212df7a..e1c601589b 100644 --- a/apps/s_server.c +++ b/apps/s_server.c @@ -4020,6 +4020,7 @@ static int www_body(int s, int stype, int prot, unsigned char *context) if (rpk_files != NULL && !rpk_enable(con)) { BIO_puts(bio_err, "Error enabling client RPK verification\n"); + SSL_free(con); goto err; } @@ -4543,6 +4544,7 @@ static int rev_body(int s, int stype, int prot, unsigned char *context) if (rpk_files != NULL && !rpk_enable(con)) { BIO_puts(bio_err, "Error enabling client RPK verification\n"); ERR_print_errors(bio_err); + SSL_free(con); goto err; } From 5d61a36a9425c7615f9b8510967db330ee5ed6fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Trojnara?= Date: Sat, 27 Jun 2026 22:39:44 +0200 Subject: [PATCH 135/349] s_client: skip TCP shutdown drain for datagram protocols The shutdown-side drain uses a TCP half-close and a fixed 500 ms select() timeout to let peers consume buffered alerts before close. Running it for DTLS/QUIC datagram connections causes an unnecessary delay after the connection has otherwise completed. Limit this workaround to non-DTLS, non-QUIC connections. CLA: trivial Reviewed-by: Matt Caswell Reviewed-by: Dmitry Belyavskiy Reviewed-by: Eugene Syromiatnikov MergeDate: Wed Jul 1 13:35:53 2026 (Merged from https://github.com/openssl/openssl/pull/31558) --- apps/s_client.c | 49 ++++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/apps/s_client.c b/apps/s_client.c index 2d1f61a179..d247cd836d 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -3520,29 +3520,32 @@ shut: print_stuff(bio_c_out, con, full_log); do_ssl_shutdown(con); - /* - * If we ended with an alert being sent, but still with data in the - * network buffer to be read, then calling BIO_closesocket() will - * result in a TCP-RST being sent. On some platforms (notably - * Windows) then this will result in the peer immediately abandoning - * the connection including any buffered alert data before it has - * had a chance to be read. Shutting down the sending side first, - * and then closing the socket sends TCP-FIN first followed by - * TCP-RST. This seems to allow the peer to read the alert data. - */ - shutdown(SSL_get_fd(con), 1); /* SHUT_WR */ - /* - * We just said we have nothing else to say, but it doesn't mean that - * the other side has nothing. It's even recommended to consume incoming - * data. [In testing context this ensures that alerts are passed on...] - */ - timeout.tv_sec = 0; - timeout.tv_usec = 500000; /* some extreme round-trip */ - do { - FD_ZERO(&readfds); - openssl_fdset(sock, &readfds); - } while (select(sock + 1, &readfds, NULL, NULL, &timeout) > 0 - && BIO_read(sbio, sbuf, BUFSIZZ) > 0); + /* The following half-close/drain workaround is TCP-specific. */ + if (!isdtls && !isquic) { + /* + * If we ended with an alert being sent, but still with data in the + * network buffer to be read, then calling BIO_closesocket() will + * result in a TCP-RST being sent. On some platforms (notably + * Windows) then this will result in the peer immediately abandoning + * the connection including any buffered alert data before it has + * had a chance to be read. Shutting down the sending side first, + * and then closing the socket sends TCP-FIN first followed by + * TCP-RST. This seems to allow the peer to read the alert data. + */ + shutdown(SSL_get_fd(con), 1); /* SHUT_WR */ + /* + * We just said we have nothing else to say, but it doesn't mean that + * the other side has nothing. It's even recommended to consume incoming + * data. [In testing context this ensures that alerts are passed on...] + */ + timeout.tv_sec = 0; + timeout.tv_usec = 500000; /* some extreme round-trip */ + do { + FD_ZERO(&readfds); + openssl_fdset(sock, &readfds); + } while (select(sock + 1, &readfds, NULL, NULL, &timeout) > 0 + && BIO_read(sbio, sbuf, BUFSIZZ) > 0); + } BIO_closesocket(SSL_get_fd(con)); end: From 8225d82f04cf49412c5779ee19c3d5d812e1f265 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 26 Jun 2026 11:13:21 -0400 Subject: [PATCH 136/349] use evp_kem_free in evp_kem_from_algorithm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit evp_kem_from_algorithm, in its error path frees the allocated kem with EVP_KEM_free, but thats a no-op now, and we actually want to free it to avoid leaks, so we should use evp_kem_free (the internal function that acutally does free the alg) instead. Fixes https://scan5.scan.coverity.com/#/project-view/60762/10222?selectedIssue=1695456 Reviewed-by: Saša Nedvědický Reviewed-by: Eugene Syromiatnikov Reviewed-by: Norbert Pocs MergeDate: Wed Jul 1 15:32:16 2026 (Merged from https://github.com/openssl/openssl/pull/31748) --- crypto/evp/kem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/evp/kem.c b/crypto/evp/kem.c index 48de927ce4..ec83236ab4 100644 --- a/crypto/evp/kem.c +++ b/crypto/evp/kem.c @@ -442,7 +442,7 @@ static void *evp_kem_from_algorithm(int name_id, const OSSL_ALGORITHM *algodef, return kem; err: - EVP_KEM_free(kem); + evp_kem_free(kem); return NULL; } From e4551d81c3239c31fd2c60ddb5926cca56c73a6d Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 26 Jun 2026 11:17:09 -0400 Subject: [PATCH 137/349] use evp_keyexch_free in evp_keyexch_from_algorithm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit evp_keyexch_from_algorithm, in its error path frees the allocated kem with EVP_KEM_free, but thats a no-op now, and we actually want to free it to avoid leaks, so we should use evp_keyexch_free (the internal function that acutally does free the alg) instead. Fixes https://scan5.scan.coverity.com/#/project-view/60762/10222?selectedIssue=1695455 Reviewed-by: Saša Nedvědický Reviewed-by: Eugene Syromiatnikov Reviewed-by: Norbert Pocs MergeDate: Wed Jul 1 15:32:18 2026 (Merged from https://github.com/openssl/openssl/pull/31748) --- crypto/evp/exchange.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/evp/exchange.c b/crypto/evp/exchange.c index 1d05f8e857..535493c244 100644 --- a/crypto/evp/exchange.c +++ b/crypto/evp/exchange.c @@ -169,7 +169,7 @@ static void *evp_keyexch_from_algorithm(int name_id, return exchange; err: - EVP_KEYEXCH_free(exchange); + evp_keyexch_free(exchange); return NULL; } From 4c48ed73190fe282feb3858bdc48dd5fa8d03b96 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 26 Jun 2026 11:17:09 -0400 Subject: [PATCH 138/349] use evp_keymgmt_free in keymgmt_from_algorithm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit keymgmt_from_algorithm, in its error path frees the allocated keymgmt with EVP_KEYMGMT_free, but thats a no-op now, and we actually want to free it to avoid leaks, so we should use evp_keymgmt_free (the internal function that acutally does free the alg) instead. Fixes https://scan5.scan.coverity.com/#/project-view/60762/10222?selectedIssue=1695452 Reviewed-by: Saša Nedvědický Reviewed-by: Eugene Syromiatnikov Reviewed-by: Norbert Pocs MergeDate: Wed Jul 1 15:32:21 2026 (Merged from https://github.com/openssl/openssl/pull/31748) --- crypto/evp/keymgmt_meth.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crypto/evp/keymgmt_meth.c b/crypto/evp/keymgmt_meth.c index a84c634441..3a6a5d0644 100644 --- a/crypto/evp/keymgmt_meth.c +++ b/crypto/evp/keymgmt_meth.c @@ -50,7 +50,7 @@ static void *keymgmt_new(void) if ((keymgmt = OPENSSL_zalloc(sizeof(*keymgmt))) == NULL) return NULL; if (!CRYPTO_NEW_REF(&keymgmt->refcnt, 1)) { - EVP_KEYMGMT_free(keymgmt); + OPENSSL_free(keymgmt); return NULL; } return keymgmt; @@ -93,7 +93,7 @@ static void *keymgmt_from_algorithm(int name_id, keymgmt->name_id = name_id; if ((keymgmt->type_name = ossl_algorithm_get1_first_name(algodef)) == NULL) { - EVP_KEYMGMT_free(keymgmt); + evp_keymgmt_free(keymgmt); return NULL; } keymgmt->description = algodef->algorithm_description; @@ -269,13 +269,13 @@ static void *keymgmt_from_algorithm(int name_id, || (keymgmt->gen != NULL && (keymgmt->gen_init == NULL || keymgmt->gen_cleanup == NULL))) { - EVP_KEYMGMT_free(keymgmt); + evp_keymgmt_free(keymgmt); ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_PROVIDER_FUNCTIONS); return NULL; } keymgmt->prov = prov; if (prov != NULL && !ossl_provider_up_ref(prov)) { - EVP_KEYMGMT_free(keymgmt); + evp_keymgmt_free(keymgmt); ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR); return NULL; } From 4d50403e5125d7d8190e6adff3d86727ba05c912 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 26 Jun 2026 11:17:09 -0400 Subject: [PATCH 139/349] use evp_signature_free in evp_signature_from_algorithm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit evp_signature_from_algorithm, in its error path frees the allocated sig with EVP_SIGNATURE_free, but thats a no-op now, and we actually want to free it to avoid leaks, so we should use evp_signature_free (the internal function that acutally does free the alg) instead. Fixes https://scan5.scan.coverity.com/#/project-view/60762/10222?selectedIssue=1695450 Reviewed-by: Saša Nedvědický Reviewed-by: Eugene Syromiatnikov Reviewed-by: Norbert Pocs MergeDate: Wed Jul 1 15:32:23 2026 (Merged from https://github.com/openssl/openssl/pull/31748) --- crypto/evp/signature.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/evp/signature.c b/crypto/evp/signature.c index 15e4116400..cd8e00d77e 100644 --- a/crypto/evp/signature.c +++ b/crypto/evp/signature.c @@ -463,7 +463,7 @@ static void *evp_signature_from_algorithm(int name_id, return signature; err: - EVP_SIGNATURE_free(signature); + evp_signature_free(signature); return NULL; } From 8a072a3dabe4df116460f8910735569df321b97b Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 26 Jun 2026 11:17:09 -0400 Subject: [PATCH 140/349] use evp_asym_cipher_free in evp_asym_cipher_from_algorithm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit evp_asym_cipher_from_algorithm, in its error path frees the allocated cipher with EVP_ASM_CIPHER_free, but thats a no-op now, and we actually want to free it to avoid leaks, so we should use evp_asym_cipher_free (the internal function that acutally does free the alg) instead. Fixes https://scan5.scan.coverity.com/#/project-view/60762/10222?selectedIssue=1695449 Reviewed-by: Saša Nedvědický Reviewed-by: Eugene Syromiatnikov Reviewed-by: Norbert Pocs MergeDate: Wed Jul 1 15:32:25 2026 (Merged from https://github.com/openssl/openssl/pull/31748) --- crypto/evp/asymcipher.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/evp/asymcipher.c b/crypto/evp/asymcipher.c index 78dbb7e2f5..c449848ae0 100644 --- a/crypto/evp/asymcipher.c +++ b/crypto/evp/asymcipher.c @@ -453,7 +453,7 @@ static void *evp_asym_cipher_from_algorithm(int name_id, return cipher; err: - EVP_ASYM_CIPHER_free(cipher); + evp_asym_cipher_free(cipher); return NULL; } From 7520345190b4f0a9303c6a9352b3e29c71df6c04 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 22 Jun 2026 14:44:44 +0200 Subject: [PATCH 141/349] quic: add mfail test for a read with key update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds test for #31268 with using SSL_read for QUIC client and forcing key update. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Tomas Mraz Reviewed-by: Saša Nedvědický MergeDate: Thu Jul 2 07:21:14 2026 (Merged from https://github.com/openssl/openssl/pull/31272) --- test/quicapitest.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/test/quicapitest.c b/test/quicapitest.c index 5b7cdb6a3c..ff858fb170 100644 --- a/test/quicapitest.c +++ b/test/quicapitest.c @@ -21,6 +21,7 @@ #include "../ssl/ssl_local.h" #include "../ssl/quic/quic_channel_local.h" #include "internal/quic_error.h" +#include "internal/quic_ssl.h" static OSSL_LIB_CTX *libctx = NULL; static char *propq = NULL; @@ -39,6 +40,9 @@ static BIO_ADDR *create_addr(struct in_addr *ina, short int port); static int bio_addr_bind(BIO *bio, BIO_ADDR *addr); static SSL *ql_create(SSL_CTX *ssl_ctx, BIO *bio); static SSL_CTX *create_server_ctx(void); +static SSL_CTX *create_client_ctx(void); +static int create_quic_ssl_objects(SSL_CTX *sctx, SSL_CTX *cctx, + SSL **lssl, SSL **cssl); static int qc_init(SSL *qconn, BIO_ADDR *dst_addr); /* The ssltrace test assumes some options are switched on/off */ @@ -214,6 +218,92 @@ end: return ret; } +#ifndef OPENSSL_NO_CACHED_FETCH +static int test_ssl_read_key_update_mfail(void) +{ + SSL_CTX *cctx = NULL, *sctx = NULL; + SSL *clientssl = NULL, *serverssl = NULL, *qlistener = NULL; + QUIC_CHANNEL *sch = NULL; + int ret = 0, i; + const char *msg = "ping"; + size_t msglen = strlen(msg); + size_t numbytes = 0; + unsigned char buf[64]; + + if (!TEST_ptr(sctx = create_server_ctx()) + || !TEST_ptr(cctx = create_client_ctx())) + goto err; + + if (!create_quic_ssl_objects(sctx, cctx, &qlistener, &clientssl)) + goto err; + + if (!TEST_true(SSL_set_tlsext_host_name(clientssl, "localhost"))) + goto err; + + /* Send ClientHello and server retry. */ + for (i = 0; i < 2; i++) { + ret = SSL_connect(clientssl); + if (!TEST_int_le(ret, 0) + || !TEST_int_eq(SSL_get_error(clientssl, ret), SSL_ERROR_WANT_READ)) + goto err; + SSL_handle_events(qlistener); + } + + serverssl = SSL_accept_connection(qlistener, 0); + if (!TEST_ptr(serverssl) + || !TEST_true(create_bare_ssl_connection(serverssl, clientssl, + SSL_ERROR_NONE, 0, 0))) + goto err; + + if (!TEST_ptr(sch = ossl_quic_conn_get_channel(serverssl))) + goto err; + + /* Open the default stream so the server has something to write back on. */ + if (!TEST_true(SSL_write_ex(clientssl, msg, msglen, &numbytes)) + || !TEST_size_t_eq(numbytes, msglen)) + goto err; + + /* Route the datagram to the server connection and let it consume it. */ + SSL_handle_events(qlistener); + SSL_handle_events(serverssl); + if (!TEST_true(SSL_read_ex(serverssl, buf, sizeof(buf), &numbytes))) + goto err; + + /* + * Force the server's TX side to rotate keys. Its next outgoing packet + * will carry the flipped Key Phase bit. When the client decrypts that + * packet, qrx_key_update_initiated -> rxku_detected -> ch_trigger_txku + * fires on the client. + */ + if (!TEST_true(ossl_qtx_trigger_key_update(sch->qtx))) + goto err; + + if (!TEST_true(SSL_write_ex(serverssl, msg, msglen, &numbytes)) + || !TEST_size_t_eq(numbytes, msglen)) + goto err; + + /* + * Process the inbound packet (carrying the new Key Phase) under mfail. + * SSL_read_ex ticks the client, reads the datagram off its BIO and + * decrypts it, which is where the key update handling runs. + */ + MFAIL_start(); + ret = SSL_read_ex(clientssl, buf, sizeof(buf), &numbytes); + MFAIL_end(); + + ret = (ret > 0); + +err: + SSL_free(serverssl); + SSL_free(clientssl); + SSL_free(qlistener); + SSL_CTX_free(sctx); + SSL_CTX_free(cctx); + + return ret; +} +#endif + /* * Test that sending FIN with no data to a client blocking in SSL_read_ex() will * wake up the client. @@ -3655,6 +3745,9 @@ int setup_tests(void) goto err; ADD_ALL_TESTS(test_quic_write_read, 3); +#ifndef OPENSSL_NO_CACHED_FETCH + ADD_MFAIL_NO_CHECK_TEST(test_ssl_read_key_update_mfail); +#endif ADD_TEST(test_fin_only_blocking); ADD_TEST(test_ciphersuites); ADD_TEST(test_cipher_find); From b71c5cfedaea25351498c57aa2c583e36d6e386c Mon Sep 17 00:00:00 2001 From: Leon Timmermans Date: Sun, 19 Oct 2025 15:01:34 +0200 Subject: [PATCH 142/349] Add EVP_KDF_CTX_get0_kdf and EVP_KDF_CTX_get1_kdf, deprecate EVP_KDF_CTX_kdf Resolves: https://github.com/openssl/openssl/issues/28327 Reviewed-by: Bob Beck Reviewed-by: Nikola Pajkovsky MergeDate: Thu Jul 2 07:24:22 2026 (Merged from https://github.com/openssl/openssl/pull/28954) --- CHANGES.md | 5 +++++ crypto/evp/kdf_lib.c | 16 +++++++++++++++- doc/man3/EVP_KDF.pod | 23 +++++++++++++++++++---- doc/man7/ossl-guide-migration.pod | 6 ++++++ include/openssl/kdf.h | 8 +++++++- test/evp_test.c | 2 +- util/libcrypto.num | 4 +++- util/other.syms | 1 + 8 files changed, 57 insertions(+), 8 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index ea505a94e5..26ac778f55 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -199,6 +199,11 @@ OpenSSL Releases *Timo Keller* + * Added `EVP_KDF_CTX_get0_kdf()` and `EVP_KDF_CTX_get1_kdf()` functions + as a replacement for the now deprecated `EVP_KDF_CTX_kdf()`. + + *Leon Timmermans* + * Add `FIPS_mode()` as a convenience define to `EVP_default_properties_is_fips_enabled(NULL)`, which is shorthand to check whether the `fips=yes` property is currently enabled diff --git a/crypto/evp/kdf_lib.c b/crypto/evp/kdf_lib.c index 4c98942992..67351044fb 100644 --- a/crypto/evp/kdf_lib.c +++ b/crypto/evp/kdf_lib.c @@ -104,11 +104,25 @@ const OSSL_PROVIDER *EVP_KDF_get0_provider(const EVP_KDF *kdf) return kdf->prov; } -const EVP_KDF *EVP_KDF_CTX_kdf(EVP_KDF_CTX *ctx) +const EVP_KDF *EVP_KDF_CTX_get0_kdf(const EVP_KDF_CTX *ctx) { return ctx->meth; } +#if !defined(OPENSSL_NO_DEPRECATED_4_1) +const EVP_KDF *EVP_KDF_CTX_kdf(const EVP_KDF_CTX *ctx) +{ + return EVP_KDF_CTX_get0_kdf(ctx); +} +#endif /* !OPENSSL_NO_DEPRECATED_4_1 */ + +EVP_KDF *EVP_KDF_CTX_get1_kdf(const EVP_KDF_CTX *ctx) +{ + if (!EVP_KDF_up_ref(ctx->meth)) + return NULL; + return ctx->meth; +} + void EVP_KDF_CTX_reset(EVP_KDF_CTX *ctx) { if (ctx == NULL) diff --git a/doc/man3/EVP_KDF.pod b/doc/man3/EVP_KDF.pod index b9cc14eb79..6df44e8643 100644 --- a/doc/man3/EVP_KDF.pod +++ b/doc/man3/EVP_KDF.pod @@ -6,7 +6,7 @@ EVP_KDF, EVP_KDF_fetch, EVP_KDF_free, EVP_KDF_up_ref, EVP_KDF_CTX, EVP_KDF_CTX_new, EVP_KDF_CTX_free, EVP_KDF_CTX_dup, EVP_KDF_CTX_reset, EVP_KDF_derive, EVP_KDF_CTX_set_SKEY, EVP_KDF_derive_SKEY, -EVP_KDF_CTX_get_kdf_size, +EVP_KDF_CTX_get_kdf_size, EVP_KDF_CTX_get0_kdf, EVP_KDF_CTX_get1_kdf, EVP_KDF_get0_provider, EVP_KDF_CTX_kdf, EVP_KDF_is_a, EVP_KDF_get0_name, EVP_KDF_names_do_all, EVP_KDF_get0_description, EVP_KDF_CTX_get_params, EVP_KDF_CTX_set_params, EVP_KDF_do_all_provided, @@ -22,7 +22,8 @@ EVP_KDF_CTX_gettable_params, EVP_KDF_CTX_settable_params - EVP KDF routines typedef struct evp_kdf_ctx_st EVP_KDF_CTX; EVP_KDF_CTX *EVP_KDF_CTX_new(EVP_KDF *kdf); - const EVP_KDF *EVP_KDF_CTX_kdf(EVP_KDF_CTX *ctx); + const EVP_KDF *EVP_KDF_CTX_get0_kdf(const EVP_KDF_CTX *ctx); + EVP_KDF *EVP_KDF_CTX_get1_kdf(EVP_KDF_CTX *ctx); void EVP_KDF_CTX_free(EVP_KDF_CTX *ctx); EVP_KDF_CTX *EVP_KDF_CTX_dup(const EVP_KDF_CTX *src); void EVP_KDF_CTX_reset(EVP_KDF_CTX *ctx); @@ -57,6 +58,12 @@ EVP_KDF_CTX_gettable_params, EVP_KDF_CTX_settable_params - EVP KDF routines const OSSL_PARAM *EVP_KDF_CTX_settable_params(const EVP_KDF *kdf); const OSSL_PROVIDER *EVP_KDF_get0_provider(const EVP_KDF *kdf); +The following functions have been deprecated since OpenSSL 4.1, +and can be hidden entirely by defining B with a suitable +version value, see L: + + const EVP_KDF *EVP_KDF_CTX_kdf(const EVP_KDF_CTX *ctx); + =head1 DESCRIPTION The EVP KDF routines are a high-level interface to Key Derivation Function @@ -99,8 +106,10 @@ EVP_KDF_CTX_new() creates a new context for the KDF implementation I. EVP_KDF_CTX_free() frees up the context I. If I is NULL, nothing is done. -EVP_KDF_CTX_kdf() returns the B associated with the context -I. +EVP_KDF_CTX_get0_kdf() returns the B associated with the context +I. EVP_KDF_CTX_get1_kdf() is the same, except ownership is passed +to the caller. +EVP_KDF_CTX_kdf() is an alias for EVP_KDF_CTX_get0_kdf(). =head2 Computing functions @@ -324,6 +333,12 @@ This functionality was added in OpenSSL 3.0. EVP_KDF_derive_SKEY() and EVP_KDF_CTX_set_SKEY() functions were introduced in OpenSSL 3.6. +EVP_KDF_CTX_get0_kdf() and EVP_KDF_CTX_get1_kdf() functions were introduced +in OpenSSL 4.1. + +EVP_KDF_CTX_kdf() function was deprecated in favour of EVP_KDF_CTX_get0_kdf() +in OpenSSL 4.1. + =head1 COPYRIGHT Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved. diff --git a/doc/man7/ossl-guide-migration.pod b/doc/man7/ossl-guide-migration.pod index 2c690dc48e..bf5c57d617 100644 --- a/doc/man7/ossl-guide-migration.pod +++ b/doc/man7/ossl-guide-migration.pod @@ -38,6 +38,12 @@ ASN1_BIT_STRING_set1(). The new functions in addition to what ASN1_BIT_STRING_set() does, validates the function arguments and sets unused bits after setting the BIT STRING value. +=head3 Deprecation of EVP_KDF_CTX_kdf() + +This function is deprecated in favour of EVP_KDF_CTX_get0_ctx(), to align +with the naming of functions that provide similar functionality for other kinds +of EVP context oobjects. + =head1 OPENSSL 4.0 =head2 Main Changes from OpenSSL 3.6 diff --git a/include/openssl/kdf.h b/include/openssl/kdf.h index ab79e02e04..d49b22373e 100644 --- a/include/openssl/kdf.h +++ b/include/openssl/kdf.h @@ -37,7 +37,13 @@ const char *EVP_KDF_get0_description(const EVP_KDF *kdf); int EVP_KDF_is_a(const EVP_KDF *kdf, const char *name); const char *EVP_KDF_get0_name(const EVP_KDF *kdf); const OSSL_PROVIDER *EVP_KDF_get0_provider(const EVP_KDF *kdf); -const EVP_KDF *EVP_KDF_CTX_kdf(EVP_KDF_CTX *ctx); +const EVP_KDF *EVP_KDF_CTX_get0_kdf(const EVP_KDF_CTX *ctx); +EVP_KDF *EVP_KDF_CTX_get1_kdf(const EVP_KDF_CTX *ctx); + +#if !defined(OPENSSL_NO_DEPRECATED_4_1) +OSSL_DEPRECATEDIN_4_1_FOR("Use EVP_KDF_CTX_get0_kdf") +const EVP_KDF *EVP_KDF_CTX_kdf(const EVP_KDF_CTX *ctx); +#endif /* !OPENSSL_NO_DEPRECATED_4_1 */ void EVP_KDF_CTX_reset(EVP_KDF_CTX *ctx); size_t EVP_KDF_CTX_get_kdf_size(EVP_KDF_CTX *ctx); diff --git a/test/evp_test.c b/test/evp_test.c index ef359a72ef..77029f40db 100644 --- a/test/evp_test.c +++ b/test/evp_test.c @@ -4151,7 +4151,7 @@ static int kdf_test_ctrl(EVP_TEST *t, EVP_KDF_CTX *kctx, KDF_DATA *kdata = t->data; int rv; char *p, *name; - const OSSL_PARAM *defs = EVP_KDF_settable_ctx_params(EVP_KDF_CTX_kdf(kctx)); + const OSSL_PARAM *defs = EVP_KDF_settable_ctx_params(EVP_KDF_CTX_get0_kdf(kctx)); if (!TEST_ptr(name = OPENSSL_strdup(value))) return 0; diff --git a/util/libcrypto.num b/util/libcrypto.num index 777dfbf70e..a51b72cf93 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -1616,7 +1616,7 @@ EVP_KDF_get0_description 1614 4_0_0 EXIST::FUNCTION: EVP_KDF_is_a 1615 4_0_0 EXIST::FUNCTION: EVP_KDF_get0_name 1616 4_0_0 EXIST::FUNCTION: EVP_KDF_get0_provider 1617 4_0_0 EXIST::FUNCTION: -EVP_KDF_CTX_kdf 1618 4_0_0 EXIST::FUNCTION: +EVP_KDF_CTX_kdf 1618 4_0_0 EXIST::FUNCTION:DEPRECATEDIN_4_1 EVP_KDF_CTX_reset 1619 4_0_0 EXIST::FUNCTION: EVP_KDF_CTX_get_kdf_size 1620 4_0_0 EXIST::FUNCTION: EVP_KDF_derive 1621 4_0_0 EXIST::FUNCTION: @@ -5722,3 +5722,5 @@ CRYPTO_atomic_cmp_exch_ptr ? 4_1_0 EXIST::FUNCTION: EVP_EC_affine2oct ? 4_1_0 EXIST::FUNCTION: OPENSSL_sk_set_copy_thunks ? 4_1_0 EXIST::FUNCTION: ASN1_STRING_new_not_owned ? 4_1_0 EXIST::FUNCTION: +EVP_KDF_CTX_get0_kdf ? 4_1_0 EXIST::FUNCTION: +EVP_KDF_CTX_get1_kdf ? 4_1_0 EXIST::FUNCTION: diff --git a/util/other.syms b/util/other.syms index fa54186811..564704dd87 100644 --- a/util/other.syms +++ b/util/other.syms @@ -314,6 +314,7 @@ ERR_raise define ERR_raise_data define EVP_DigestSignUpdate define EVP_DigestVerifyUpdate define +EVP_KDF_CTX_kdf define EVP_MD_CTX_get_block_size define EVP_MD_CTX_get0_name define EVP_MD_CTX_get_size define From a5b0c2ef9a38b81541d8d0669d130f8ea75cc849 Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Wed, 17 Jun 2026 11:10:51 +0200 Subject: [PATCH 143/349] test/evp_kdf_test.c: add checks or EVP_KDF_CTX_{,get0_,get1_}kdf() Signed-off-by: Eugene Syromiatnikov Reviewed-by: Bob Beck Reviewed-by: Nikola Pajkovsky MergeDate: Thu Jul 2 07:24:24 2026 (Merged from https://github.com/openssl/openssl/pull/28954) --- test/evp_kdf_test.c | 71 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/test/evp_kdf_test.c b/test/evp_kdf_test.c index 73093119e3..6d4a55d11e 100644 --- a/test/evp_kdf_test.c +++ b/test/evp_kdf_test.c @@ -2097,6 +2097,73 @@ static int test_kdf_get_kdf(void) return ok; } +static int test_kdf_ctx_get_kdf(void) +{ + EVP_KDF *kdf = NULL; + const EVP_KDF *kdf_get0 = NULL; + EVP_KDF *kdf_get1 = NULL; + EVP_KDF_CTX *kctx = NULL; + int ok = 0; + + kdf = EVP_KDF_fetch(NULL, OSSL_KDF_NAME_PBKDF2, NULL); + if (!TEST_ptr(kdf)) + goto out; + + kctx = EVP_KDF_CTX_new(kdf); + if (!TEST_ptr(kdf)) + goto out; + + kdf_get0 = EVP_KDF_CTX_get0_kdf(kctx); + if (!TEST_ptr_eq(kdf, kdf_get0)) + goto out; + + kdf_get1 = EVP_KDF_CTX_get1_kdf(kctx); + if (!TEST_ptr(kdf_get1) + || !TEST_true(EVP_KDF_is_a(kdf_get1, EVP_KDF_get0_name(kdf)))) + goto out; + + ok = 1; + +out: + EVP_KDF_free(kdf_get1); + EVP_KDF_CTX_free(kctx); + EVP_KDF_free(kdf); + + return ok; +} + +#if !defined(OPENSSL_NO_DEPRECATED_4_1) +static int test_kdf_ctx_kdf(void) +{ + EVP_KDF *kdf = NULL; + const EVP_KDF *kdf_get = NULL; + EVP_KDF_CTX *kctx = NULL; + int ok = 0; + + kdf = EVP_KDF_fetch(NULL, OSSL_KDF_NAME_PBKDF2, NULL); + if (!TEST_ptr(kdf)) + goto out; + + kctx = EVP_KDF_CTX_new(kdf); + if (!TEST_ptr(kdf)) + goto out; + + OSSL_BEGIN_ALLOW_DEPRECATED + kdf_get = EVP_KDF_CTX_kdf(kctx); + OSSL_END_ALLOW_DEPRECATED + if (!TEST_ptr_eq(kdf, kdf_get)) + goto out; + + ok = 1; + +out: + EVP_KDF_CTX_free(kctx); + EVP_KDF_free(kdf); + + return ok; +} +#endif /* !OPENSSL_NO_DEPRECATED_4_1 */ + #if !defined(OPENSSL_NO_CMS) && !defined(OPENSSL_NO_DES) && !defined(OPENSSL_NO_X942KDF) static int test_kdf_x942_asn1(void) { @@ -2366,6 +2433,10 @@ int setup_tests(void) ADD_TEST(test_kdf_kbkdf_kmac); #endif /* OPENSSL_NO_KBKDF */ ADD_TEST(test_kdf_get_kdf); + ADD_TEST(test_kdf_ctx_get_kdf); +#if !defined(OPENSSL_NO_DEPRECATED_4_1) + ADD_TEST(test_kdf_ctx_kdf); +#endif ADD_TEST(test_kdf_tls1_prf); ADD_TEST(test_kdf_tls1_prf_set_skey); ADD_TEST(test_kdf_tls1_prf_derive_skey); From 862844ddf9f844863b309db47765615940b09cc8 Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Tue, 30 Jun 2026 15:03:25 +0200 Subject: [PATCH 144/349] fuzz/provider.c: check evp##_up_ref return value in collect_##evp Coverity has reported an unchecked result of an evp##_up_ref call that is checked elsewhere. Rewrite the collect routine to try to do the up_ref first, and then call free if push doesn't succeed. Resolves: https://scan5.scan.coverity.com/#/project-view/65248/10222?selectedIssue=1695451 Resolves: https://scan5.scan.coverity.com/#/project-view/65248/10222?selectedIssue=1695454 Fixes: f3b988dc2951 "Add provider fuzzer" Reviewed-by: Nikola Pajkovsky Reviewed-by: Paul Dale MergeDate: Thu Jul 2 07:26:22 2026 (Merged from https://github.com/openssl/openssl/pull/31792) --- fuzz/provider.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fuzz/provider.c b/fuzz/provider.c index 981be0690d..e3acf3e470 100644 --- a/fuzz/provider.c +++ b/fuzz/provider.c @@ -32,8 +32,11 @@ { \ STACK_OF(evp) *obj_stack = stack; \ \ - if (sk_##evp##_push(obj_stack, obj) > 0) \ - evp##_up_ref(obj); \ + if (!evp##_up_ref(obj)) \ + return; \ + \ + if (sk_##evp##_push(obj_stack, obj) <= 0) \ + evp##_free(obj); \ } \ static void init_##name(OSSL_LIB_CTX *libctx) \ { \ From 7c9b263005c5e886b4442e38ca846bbd2badd75b Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Tue, 30 Jun 2026 15:43:04 +0200 Subject: [PATCH 145/349] crypto/pem/pem_info.c: avoid switching on PEM_INFO_NONE Coverity complains that the switch statements in PEM_X509_INFO_read_bio_ex() switches on PEM_INFO_NONE, while the enclosing condition (itype != PEM_INFO_NONE) explicitly rules it out. Pacify it by changing the switch case to default (not removing it to avoid triggering -Werror=switch). Resolves: https://scan5.scan.coverity.com/#/project-view/65248/10222?selectedIssue=1695453 Complements: 0e8f2844ed3e "fix function pointer type mismatch in PEM_X509_INFO_read_bio_ex" Signed-off-by: Eugene Syromiatnikov Reviewed-by: Nikola Pajkovsky Reviewed-by: Paul Dale MergeDate: Thu Jul 2 07:26:24 2026 (Merged from https://github.com/openssl/openssl/pull/31792) --- crypto/pem/pem_info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/pem/pem_info.c b/crypto/pem/pem_info.c index d9bbb7256e..fa189f0c5d 100644 --- a/crypto/pem/pem_info.c +++ b/crypto/pem/pem_info.c @@ -192,7 +192,7 @@ STACK_OF(X509_INFO) *PEM_X509_INFO_read_bio_ex(BIO *bp, STACK_OF(X509_INFO) *sk, decoded = d2i_AutoPrivateKey_ex((EVP_PKEY **)pp, &p, len, libctx, propq); break; - case PEM_INFO_NONE: + default: break; } if (decoded == NULL) { From a3b7e09114922fb273a9079c9bd6e78daf751916 Mon Sep 17 00:00:00 2001 From: Evy Garden Date: Tue, 30 Jun 2026 17:48:45 +0200 Subject: [PATCH 146/349] Add -n option to openssl-rand to not output the trailing newline Reviewed-by: Matt Caswell Reviewed-by: Paul Dale Reviewed-by: Eugene Syromiatnikov MergeDate: Thu Jul 2 10:59:26 2026 (Merged from https://github.com/openssl/openssl/pull/31795) --- apps/rand.c | 9 +++++++-- doc/man1/openssl-rand.pod.in | 5 +++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/rand.c b/apps/rand.c index 7aec7b6e17..3b1647d9b1 100644 --- a/apps/rand.c +++ b/apps/rand.c @@ -23,6 +23,7 @@ typedef enum OPTION_choice { OPT_OUT, OPT_BASE64, OPT_HEX, + OPT_NO_NEWLINE, OPT_R_ENUM, OPT_PROV_ENUM } OPTION_CHOICE; @@ -37,6 +38,7 @@ const OPTIONS rand_options[] = { { "out", OPT_OUT, '>', "Output file" }, { "base64", OPT_BASE64, '-', "Base64 encode output" }, { "hex", OPT_HEX, '-', "Hex encode output" }, + { "n", OPT_NO_NEWLINE, '-', "Do not output the trailing newline" }, OPT_R_OPTIONS, OPT_PROV_OPTIONS, @@ -51,7 +53,7 @@ int rand_main(int argc, char **argv) BIO *out = NULL; char *outfile = NULL, *prog; OPTION_CHOICE o; - int format = FORMAT_BINARY, r, i, ret = 1; + int format = FORMAT_BINARY, r, i, ret = 1, newline = 1; size_t buflen = (1 << 16); /* max rand chunk size is 2^16 bytes */ long num = -1; uint64_t scaled_num = 0; @@ -82,6 +84,9 @@ int rand_main(int argc, char **argv) case OPT_HEX: format = FORMAT_TEXT; break; + case OPT_NO_NEWLINE: + newline = 0; + break; case OPT_PROV_CASES: if (!opt_provider(o)) goto end; @@ -208,7 +213,7 @@ int rand_main(int argc, char **argv) } scaled_num -= chunk; } - if (format == FORMAT_TEXT) + if (newline && format == FORMAT_TEXT) BIO_puts(out, "\n"); if (BIO_flush(out) <= 0) goto end; diff --git a/doc/man1/openssl-rand.pod.in b/doc/man1/openssl-rand.pod.in index d38961acc3..4d4cda2b4d 100644 --- a/doc/man1/openssl-rand.pod.in +++ b/doc/man1/openssl-rand.pod.in @@ -12,6 +12,7 @@ B [B<-out> I] [B<-base64>] [B<-hex>] +[B<-n>] {- $OpenSSL::safe::opt_r_synopsis -} {- $OpenSSL::safe::opt_provider_synopsis -} I[K|M|G|T] @@ -55,6 +56,10 @@ Perform base64 encoding on the output. Show the output as a hex string. +=item B<-n> + +Do not output the trailing newline. + {- $OpenSSL::safe::opt_r_item -} {- $OpenSSL::safe::opt_provider_item -} From 8c8a80b00ba1edb481838021568cdcd126386bff Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Mon, 29 Jun 2026 17:27:53 -0400 Subject: [PATCH 147/349] Extend refcounting on evp objects to provider requesed no-caching We recently removed reference counting for EVP objects, but kept the refcounting when we build with no-cached-fetch. There is a corner case in which providers in builds that do caching may still request non-caching by setting *no_cache = 1 in their query operations. OQS tripped over this here: https://github.com/open-quantum-safe/oqs-provider/pull/787 When a provider requests no caching, we need to treat those algorithms as though we are running in a no-cached-fetch build and still do ref counting on them. Teach our algorithms implementation to understand when a provider is requesting non-caching, mark them as such and ref count only those. Reviewed-by: Milan Broz Reviewed-by: Dmitry Belyavskiy MergeDate: Thu Jul 2 14:24:13 2026 (Merged from https://github.com/openssl/openssl/pull/31782) --- crypto/core_fetch.c | 2 +- crypto/encode_decode/decoder_meth.c | 11 +-- crypto/encode_decode/encoder_local.h | 1 + crypto/encode_decode/encoder_meth.c | 12 ++- crypto/evp/asymcipher.c | 8 +- crypto/evp/digest.c | 9 ++- crypto/evp/evp_enc.c | 10 ++- crypto/evp/evp_fetch.c | 78 +++++++++++++++++-- crypto/evp/evp_lib.c | 3 +- crypto/evp/evp_local.h | 12 ++- crypto/evp/evp_rand.c | 9 ++- crypto/evp/exchange.c | 8 +- crypto/evp/kdf_meth.c | 9 ++- crypto/evp/kem.c | 9 ++- crypto/evp/keymgmt_meth.c | 9 ++- crypto/evp/mac_meth.c | 8 +- crypto/evp/signature.c | 8 +- crypto/evp/skeymgmt_meth.c | 8 +- crypto/store/store_local.h | 1 + crypto/store/store_meth.c | 12 ++- include/crypto/decoder.h | 2 +- include/crypto/evp.h | 2 + include/internal/core.h | 2 +- include/openssl/evp.h | 4 + .../implementations/storemgmt/file_store.c | 2 +- .../storemgmt/winstore_store.c | 2 +- 26 files changed, 202 insertions(+), 39 deletions(-) diff --git a/crypto/core_fetch.c b/crypto/core_fetch.c index 3293f70372..3d09b384aa 100644 --- a/crypto/core_fetch.c +++ b/crypto/core_fetch.c @@ -107,7 +107,7 @@ static void ossl_method_construct_this(OSSL_PROVIDER *provider, struct construct_data_st *data = cbdata; void *method = NULL; - if ((method = data->mcm->construct(algo, provider, data->mcm_data)) + if ((method = data->mcm->construct(algo, provider, data->mcm_data, no_store)) == NULL) return; diff --git a/crypto/encode_decode/decoder_meth.c b/crypto/encode_decode/decoder_meth.c index 315c99d8c6..995cb675ba 100644 --- a/crypto/encode_decode/decoder_meth.c +++ b/crypto/encode_decode/decoder_meth.c @@ -79,7 +79,7 @@ int OSSL_DECODER_up_ref(OSSL_DECODER *decoder) * We can identify them based on the fact that they never have a registered nid (i.e. * its always zero) */ - if (decoder->base.id == 0) + if (decoder->base.id == 0 || decoder->base.no_store != 0) return ossl_decoder_up_ref(decoder); return 1; #endif @@ -90,7 +90,7 @@ void OSSL_DECODER_free(OSSL_DECODER *decoder) #ifdef OPENSSL_NO_CACHED_FETCH ossl_decoder_free(decoder); #else - if (decoder != NULL && decoder->base.id == 0) + if (decoder != NULL && (decoder->base.id == 0 || decoder->base.no_store != 0)) ossl_decoder_free(decoder); #endif } @@ -228,7 +228,7 @@ static int put_decoder_in_store(void *store, void *method, /* Create and populate a decoder method */ void *ossl_decoder_from_algorithm(int id, const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov) + OSSL_PROVIDER *prov, int no_store) { OSSL_DECODER *decoder = NULL; const OSSL_DISPATCH *fns = algodef->implementation; @@ -237,6 +237,7 @@ void *ossl_decoder_from_algorithm(int id, const OSSL_ALGORITHM *algodef, if ((decoder = ossl_decoder_new()) == NULL) return NULL; decoder->base.id = id; + decoder->base.no_store = no_store; if ((decoder->base.name = ossl_algorithm_get1_first_name(algodef)) == NULL) { ossl_decoder_free(decoder); return NULL; @@ -317,7 +318,7 @@ void *ossl_decoder_from_algorithm(int id, const OSSL_ALGORITHM *algodef, * then call ossl_decoder_from_algorithm() with that identity number. */ static void *construct_decoder(const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov, void *data) + OSSL_PROVIDER *prov, void *data, int no_store) { /* * This function is only called if get_decoder_from_store() returned @@ -333,7 +334,7 @@ static void *construct_decoder(const OSSL_ALGORITHM *algodef, void *method = NULL; if (id != 0) - method = ossl_decoder_from_algorithm(id, algodef, prov); + method = ossl_decoder_from_algorithm(id, algodef, prov, no_store); /* * Flag to indicate that there was actual construction errors. This diff --git a/crypto/encode_decode/encoder_local.h b/crypto/encode_decode/encoder_local.h index 789212746b..6ebbe1c513 100644 --- a/crypto/encode_decode/encoder_local.h +++ b/crypto/encode_decode/encoder_local.h @@ -24,6 +24,7 @@ struct ossl_endecode_base_st { OSSL_PROVIDER *prov; int id; + int no_store; char *name; const OSSL_ALGORITHM *algodef; OSSL_PROPERTY_LIST *parsed_propdef; diff --git a/crypto/encode_decode/encoder_meth.c b/crypto/encode_decode/encoder_meth.c index 611dc787d7..f329d0a307 100644 --- a/crypto/encode_decode/encoder_meth.c +++ b/crypto/encode_decode/encoder_meth.c @@ -72,6 +72,8 @@ int OSSL_ENCODER_up_ref(OSSL_ENCODER *encoder) #ifdef OPENSSL_NO_CACHED_FETCH return ossl_encoder_up_ref(encoder); #else + if (encoder->base.no_store != 0) + return ossl_encoder_up_ref(encoder); return 1; #endif } @@ -80,6 +82,9 @@ void OSSL_ENCODER_free(OSSL_ENCODER *encoder) { #ifdef OPENSSL_NO_CACHED_FETCH ossl_encoder_free(encoder); +#else + if (encoder != NULL && (encoder->base.no_store != 0)) + ossl_encoder_free(encoder); #endif } @@ -216,7 +221,7 @@ static int put_encoder_in_store(void *store, void *method, /* Create and populate a encoder method */ static void *encoder_from_algorithm(int id, const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov) + OSSL_PROVIDER *prov, int no_store) { OSSL_ENCODER *encoder = NULL; const OSSL_DISPATCH *fns = algodef->implementation; @@ -225,6 +230,7 @@ static void *encoder_from_algorithm(int id, const OSSL_ALGORITHM *algodef, if ((encoder = ossl_encoder_new()) == NULL) return NULL; encoder->base.id = id; + encoder->base.no_store = no_store; if ((encoder->base.name = ossl_algorithm_get1_first_name(algodef)) == NULL) { ossl_encoder_free(encoder); return NULL; @@ -311,7 +317,7 @@ static void *encoder_from_algorithm(int id, const OSSL_ALGORITHM *algodef, * then call encoder_from_algorithm() with that identity number. */ static void *construct_encoder(const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov, void *data) + OSSL_PROVIDER *prov, void *data, int no_store) { /* * This function is only called if get_encoder_from_store() returned @@ -327,7 +333,7 @@ static void *construct_encoder(const OSSL_ALGORITHM *algodef, void *method = NULL; if (id != 0) - method = encoder_from_algorithm(id, algodef, prov); + method = encoder_from_algorithm(id, algodef, prov, no_store); /* * Flag to indicate that there was actual construction errors. This diff --git a/crypto/evp/asymcipher.c b/crypto/evp/asymcipher.c index c449848ae0..1665efd3e2 100644 --- a/crypto/evp/asymcipher.c +++ b/crypto/evp/asymcipher.c @@ -342,7 +342,7 @@ static EVP_ASYM_CIPHER *evp_asym_cipher_new(OSSL_PROVIDER *prov) static void *evp_asym_cipher_from_algorithm(int name_id, const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov) + OSSL_PROVIDER *prov, int no_store) { const OSSL_DISPATCH *fns = algodef->implementation; EVP_ASYM_CIPHER *cipher = NULL; @@ -355,6 +355,7 @@ static void *evp_asym_cipher_from_algorithm(int name_id, } cipher->name_id = name_id; + cipher->no_store = no_store; if ((cipher->type_name = ossl_algorithm_get1_first_name(algodef)) == NULL) goto err; cipher->description = algodef->algorithm_description; @@ -461,6 +462,9 @@ void EVP_ASYM_CIPHER_free(EVP_ASYM_CIPHER *cipher) { #ifdef OPENSSL_NO_CACHED_FETCH evp_asym_cipher_free(cipher); +#else + if (cipher != NULL && (cipher->no_store != 0)) + evp_asym_cipher_free(cipher); #endif } @@ -469,6 +473,8 @@ int EVP_ASYM_CIPHER_up_ref(EVP_ASYM_CIPHER *cipher) #ifdef OPENSSL_NO_CACHED_FETCH return evp_asym_cipher_up_ref(cipher); #else + if (cipher->no_store != 0) + return evp_asym_cipher_up_ref(cipher); return 1; #endif } diff --git a/crypto/evp/digest.c b/crypto/evp/digest.c index d18b707380..2cb490b279 100644 --- a/crypto/evp/digest.c +++ b/crypto/evp/digest.c @@ -831,7 +831,7 @@ static int evp_md_cache_constants(EVP_MD *md) static void *evp_md_from_algorithm(int name_id, const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov) + OSSL_PROVIDER *prov, int no_store) { const OSSL_DISPATCH *fns = algodef->implementation; EVP_MD *md = NULL; @@ -843,6 +843,9 @@ static void *evp_md_from_algorithm(int name_id, return NULL; } + if (no_store != 0) + md->flags |= EVP_MD_FLAG_NO_STORE; + #ifndef FIPS_MODULE md->type = NID_undef; if (!evp_names_do_all(prov, name_id, set_legacy_nid, &md->type) @@ -1013,6 +1016,8 @@ int EVP_MD_up_ref(EVP_MD *md) #ifdef OPENSSL_NO_CACHED_FETCH return evp_md_up_ref(md); #else + if (md->flags & EVP_MD_FLAG_NO_STORE) + return evp_md_up_ref(md); return 1; #endif } @@ -1022,6 +1027,8 @@ void EVP_MD_free(EVP_MD *md) #ifdef OPENSSL_NO_CACHED_FETCH evp_md_free(md); #else + if (md != NULL && (md->flags & EVP_MD_FLAG_NO_STORE)) + evp_md_free(md); return; #endif } diff --git a/crypto/evp/evp_enc.c b/crypto/evp/evp_enc.c index 07efde94e9..830cfdb8d8 100644 --- a/crypto/evp/evp_enc.c +++ b/crypto/evp/evp_enc.c @@ -1366,7 +1366,7 @@ static void evp_cipher_free(void *c) static void *evp_cipher_from_algorithm(const int name_id, const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov) + OSSL_PROVIDER *prov, int no_store) { const OSSL_DISPATCH *fns = algodef->implementation; EVP_CIPHER *cipher = NULL; @@ -1377,6 +1377,9 @@ static void *evp_cipher_from_algorithm(const int name_id, return NULL; } + if (no_store != 0) + cipher->flags |= EVP_CIPH_FLAG_NO_STORE; + #ifndef FIPS_MODULE cipher->nid = NID_undef; if (!evp_names_do_all(prov, name_id, set_legacy_nid, &cipher->nid) @@ -1574,6 +1577,8 @@ int EVP_CIPHER_up_ref(EVP_CIPHER *cipher) #ifdef OPENSSL_NO_CACHED_FETCH return evp_cipher_up_ref(cipher); #else + if (cipher->flags & EVP_CIPH_FLAG_NO_STORE) + return evp_cipher_up_ref(cipher); return 1; #endif } @@ -1590,6 +1595,9 @@ void EVP_CIPHER_free(EVP_CIPHER *cipher) { #ifdef OPENSSL_NO_CACHED_FETCH evp_cipher_free(cipher); +#else + if (cipher != NULL && (cipher->flags & EVP_CIPH_FLAG_NO_STORE)) + evp_cipher_free(cipher); #endif } diff --git a/crypto/evp/evp_fetch.c b/crypto/evp/evp_fetch.c index 9fa049cdce..15204628db 100644 --- a/crypto/evp/evp_fetch.c +++ b/crypto/evp/evp_fetch.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "internal/cryptlib.h" #include "internal/thread_once.h" #include "internal/property.h" @@ -36,7 +37,7 @@ struct evp_method_data_st { unsigned int flag_construct_error_occurred : 1; void *(*method_from_algorithm)(int name_id, const OSSL_ALGORITHM *, - OSSL_PROVIDER *); + OSSL_PROVIDER *, int); int (*refcnt_up_method)(void *method); void (*destruct_method)(void *method); }; @@ -208,7 +209,7 @@ static int put_evp_method_in_store(void *store, void *method, * This function is responsible to getting an identity number for it. */ static void *construct_evp_method(const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov, void *data) + OSSL_PROVIDER *prov, void *data, int no_store) { /* * This function is only called if get_evp_method_from_store() returned @@ -227,7 +228,7 @@ static void *construct_evp_method(const OSSL_ALGORITHM *algodef, if (name_id == 0) return NULL; - method = methdata->method_from_algorithm(name_id, algodef, prov); + method = methdata->method_from_algorithm(name_id, algodef, prov, no_store); /* * Flag to indicate that there was actual construction errors. This @@ -253,7 +254,7 @@ inner_evp_generic_fetch(struct evp_method_data_st *methdata, const char *name, ossl_unused const char *properties, void *(*new_method)(int name_id, const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov), + OSSL_PROVIDER *prov, int no_store), int (*up_ref_method)(void *), void (*free_method)(void *)) { @@ -363,9 +364,70 @@ inner_evp_generic_fetch(struct evp_method_data_st *methdata, * cached end up in ->tmp_store when provider asks not * to cache the result (see ossl_method_construct_reserve_store()) */ - if (meth_id != 0 && methdata->tmp_store == NULL) + if (meth_id != 0 && methdata->tmp_store == NULL) { ossl_method_store_cache_set(store, prov, meth_id, propq, method, up_ref_method, free_method); + } else { +#ifndef OPENSSL_NO_CACHED_FETCH + /* + * There is a corner case we need to handle here. IF: + * 1) we are fetching an algorithm and plan to return it to the caller + * 2) The provider we fetched from requested no_cache + * Then we are in a situation in which this method that was constructed + * only lives in the tmp_store, and has a reference count of 1. + * On return from this function, that tmp_store is going to be deallocated, + * Which will drop the methods ref count to 0 and free it, after which the + * method will be returned to the called, as an already freed object. + * + * That's bad. We need to grab an extra ref count on the method before returning + * so that the requestor via EVP_*_fetch has ownership. + * + * BUT we only want to do this in the event that the algorithm is uncached. + * Unfortunately, we don't know that here, because it was the provider that + * made that request. However, each algorithm type does store that information + * so we have a path forward. Based on the operation id, call the appropriate + * up_ref method. That implementation knows how to query its algorithm type and + * decide if a reference needs to be taken here + */ + switch (operation_id) { + case OSSL_OP_DIGEST: + EVP_MD_up_ref((EVP_MD *)method); + break; + case OSSL_OP_CIPHER: + EVP_CIPHER_up_ref((EVP_CIPHER *)method); + break; + case OSSL_OP_MAC: + EVP_MAC_up_ref((EVP_MAC *)method); + break; + case OSSL_OP_KDF: + EVP_KDF_up_ref((EVP_KDF *)method); + break; + case OSSL_OP_RAND: + EVP_RAND_up_ref((EVP_RAND *)method); + break; + case OSSL_OP_KEYMGMT: + EVP_KEYMGMT_up_ref((EVP_KEYMGMT *)method); + break; + case OSSL_OP_KEYEXCH: + EVP_KEYEXCH_up_ref((EVP_KEYEXCH *)method); + break; + case OSSL_OP_SIGNATURE: + EVP_SIGNATURE_up_ref((EVP_SIGNATURE *)method); + break; + case OSSL_OP_ASYM_CIPHER: + EVP_ASYM_CIPHER_up_ref((EVP_ASYM_CIPHER *)method); + break; + case OSSL_OP_KEM: + EVP_KEM_up_ref((EVP_KEM *)method); + break; + case OSSL_OP_SKEYMGMT: + EVP_SKEYMGMT_up_ref((EVP_SKEYMGMT *)method); + break; + default: + break; + } +#endif + } } } @@ -400,7 +462,7 @@ void *evp_generic_fetch(OSSL_LIB_CTX *libctx, int operation_id, const char *name, const char *properties, void *(*new_method)(int name_id, const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov), + OSSL_PROVIDER *prov, int no_store), int (*up_ref_method)(void *), void (*free_method)(void *)) { @@ -426,7 +488,7 @@ void *evp_generic_fetch_from_prov(OSSL_PROVIDER *prov, int operation_id, const char *name, const char *properties, void *(*new_method)(int name_id, const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov), + OSSL_PROVIDER *prov, int no_store), int (*up_ref_method)(void *), void (*free_method)(void *)) { @@ -640,7 +702,7 @@ void evp_generic_do_all(OSSL_LIB_CTX *libctx, int operation_id, void *user_arg, void *(*new_method)(int name_id, const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov), + OSSL_PROVIDER *prov, int no_store), int (*up_ref_method)(void *), void (*free_method)(void *)) { diff --git a/crypto/evp/evp_lib.c b/crypto/evp/evp_lib.c index 076efb30d1..644772bf37 100644 --- a/crypto/evp/evp_lib.c +++ b/crypto/evp/evp_lib.c @@ -310,6 +310,7 @@ int evp_cipher_cache_constants(EVP_CIPHER *cipher) size_t blksz = 0; size_t keylen = 0; unsigned int mode = 0; + int no_store = cipher->flags & EVP_CIPH_FLAG_NO_STORE; OSSL_PARAM params[11]; params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_BLOCK_SIZE, &blksz); @@ -332,7 +333,7 @@ int evp_cipher_cache_constants(EVP_CIPHER *cipher) cipher->block_size = (int)blksz; cipher->iv_len = (int)ivlen; cipher->key_len = (int)keylen; - cipher->flags = mode; + cipher->flags = mode | no_store; if (aead) cipher->flags |= EVP_CIPH_FLAG_AEAD_CIPHER; if (custom_iv) diff --git a/crypto/evp/evp_local.h b/crypto/evp/evp_local.h index d4a72b7527..1f41cfe644 100644 --- a/crypto/evp/evp_local.h +++ b/crypto/evp/evp_local.h @@ -102,6 +102,7 @@ struct evp_keymgmt_st { int id; /* libcrypto internal */ int name_id; + int no_store; /* NID for the legacy alg if there is one */ int legacy_alg; char *type_name; @@ -148,6 +149,7 @@ struct evp_keymgmt_st { struct evp_keyexch_st { int name_id; + int no_store; char *type_name; const char *description; OSSL_PROVIDER *prov; @@ -168,6 +170,7 @@ struct evp_keyexch_st { struct evp_signature_st { int name_id; + int no_store; char *type_name; const char *description; OSSL_PROVIDER *prov; @@ -211,6 +214,7 @@ struct evp_signature_st { struct evp_skeymgmt_st { int name_id; + int no_store; char *type_name; const char *description; OSSL_PROVIDER *prov; @@ -234,6 +238,7 @@ struct evp_skeymgmt_st { struct evp_asym_cipher_st { int name_id; + int no_store; char *type_name; const char *description; OSSL_PROVIDER *prov; @@ -254,6 +259,7 @@ struct evp_asym_cipher_st { struct evp_kem_st { int name_id; + int no_store; char *type_name; const char *description; OSSL_PROVIDER *prov; @@ -305,14 +311,14 @@ void *evp_generic_fetch(OSSL_LIB_CTX *ctx, int operation_id, const char *name, const char *properties, void *(*new_method)(int name_id, const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov), + OSSL_PROVIDER *prov, int no_store), int (*up_ref_method)(void *), void (*free_method)(void *)); void *evp_generic_fetch_from_prov(OSSL_PROVIDER *prov, int operation_id, const char *name, const char *properties, void *(*new_method)(int name_id, const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov), + OSSL_PROVIDER *prov, int no_store), int (*up_ref_method)(void *), void (*free_method)(void *)); void evp_generic_do_all_prefetched(OSSL_LIB_CTX *libctx, int operation_id, @@ -323,7 +329,7 @@ void evp_generic_do_all(OSSL_LIB_CTX *libctx, int operation_id, void *user_arg, void *(*new_method)(int name_id, const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov), + OSSL_PROVIDER *prov, int no_store), int (*up_ref_method)(void *), void (*free_method)(void *)); diff --git a/crypto/evp/evp_rand.c b/crypto/evp/evp_rand.c index 857ffb3350..a0041719b5 100644 --- a/crypto/evp/evp_rand.c +++ b/crypto/evp/evp_rand.c @@ -24,6 +24,7 @@ struct evp_rand_st { OSSL_PROVIDER *prov; int name_id; + int no_store; char *type_name; const char *description; CRYPTO_REF_COUNT refcnt; @@ -116,7 +117,7 @@ static void evp_rand_unlock(EVP_RAND_CTX *rand) static void *evp_rand_from_algorithm(int name_id, const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov) + OSSL_PROVIDER *prov, int no_store) { const OSSL_DISPATCH *fns = algodef->implementation; EVP_RAND *rand = NULL; @@ -130,6 +131,7 @@ static void *evp_rand_from_algorithm(int name_id, return NULL; } rand->name_id = name_id; + rand->no_store = no_store; if ((rand->type_name = ossl_algorithm_get1_first_name(algodef)) == NULL) { evp_rand_free(rand); return NULL; @@ -292,6 +294,8 @@ int EVP_RAND_up_ref(EVP_RAND *rand) #ifdef OPENSSL_NO_CACHED_FETCH return evp_rand_up_ref(rand); #else + if (rand->no_store != 0) + return evp_rand_up_ref(rand); return 1; #endif } @@ -300,6 +304,9 @@ void EVP_RAND_free(EVP_RAND *rand) { #ifdef OPENSSL_NO_CACHED_FETCH evp_rand_free(rand); +#else + if (rand != NULL && (rand->no_store != 0)) + evp_rand_free(rand); #endif } diff --git a/crypto/evp/exchange.c b/crypto/evp/exchange.c index 535493c244..83fcbeb104 100644 --- a/crypto/evp/exchange.c +++ b/crypto/evp/exchange.c @@ -64,7 +64,7 @@ static EVP_KEYEXCH *evp_keyexch_new(OSSL_PROVIDER *prov) static void *evp_keyexch_from_algorithm(int name_id, const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov) + OSSL_PROVIDER *prov, int no_store) { const OSSL_DISPATCH *fns = algodef->implementation; EVP_KEYEXCH *exchange = NULL; @@ -76,6 +76,7 @@ static void *evp_keyexch_from_algorithm(int name_id, } exchange->name_id = name_id; + exchange->no_store = no_store; if ((exchange->type_name = ossl_algorithm_get1_first_name(algodef)) == NULL) goto err; exchange->description = algodef->algorithm_description; @@ -177,6 +178,9 @@ void EVP_KEYEXCH_free(EVP_KEYEXCH *exchange) { #ifdef OPENSSL_NO_CACHED_FETCH evp_keyexch_free(exchange); +#else + if (exchange != NULL && (exchange->no_store != 0)) + evp_keyexch_free(exchange); #endif } @@ -185,6 +189,8 @@ int EVP_KEYEXCH_up_ref(EVP_KEYEXCH *exchange) #ifdef OPENSSL_NO_CACHED_FETCH return evp_keyexch_up_ref(exchange); #else + if (exchange->no_store != 0) + return evp_keyexch_up_ref(exchange); return 1; #endif } diff --git a/crypto/evp/kdf_meth.c b/crypto/evp/kdf_meth.c index fba2f9fc75..e0741450e9 100644 --- a/crypto/evp/kdf_meth.c +++ b/crypto/evp/kdf_meth.c @@ -57,7 +57,7 @@ static void *evp_kdf_new(void) static void *evp_kdf_from_algorithm(int name_id, const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov) + OSSL_PROVIDER *prov, int no_store) { const OSSL_DISPATCH *fns = algodef->implementation; EVP_KDF *kdf = NULL; @@ -68,6 +68,8 @@ static void *evp_kdf_from_algorithm(int name_id, return NULL; } kdf->name_id = name_id; + kdf->no_store = no_store; + if ((kdf->type_name = ossl_algorithm_get1_first_name(algodef)) == NULL) goto err; @@ -179,6 +181,8 @@ int EVP_KDF_up_ref(EVP_KDF *kdf) #ifdef OPENSSL_NO_CACHED_FETCH return evp_kdf_up_ref(kdf); #else + if (kdf->no_store != 0) + return evp_kdf_up_ref(kdf); return 1; #endif } @@ -187,6 +191,9 @@ void EVP_KDF_free(EVP_KDF *kdf) { #ifdef OPENSSL_NO_CACHED_FETCH evp_kdf_free(kdf); +#else + if (kdf != NULL && (kdf->no_store != 0)) + evp_kdf_free(kdf); #endif } diff --git a/crypto/evp/kem.c b/crypto/evp/kem.c index ec83236ab4..4041390106 100644 --- a/crypto/evp/kem.c +++ b/crypto/evp/kem.c @@ -317,7 +317,7 @@ static EVP_KEM *evp_kem_new(OSSL_PROVIDER *prov) } static void *evp_kem_from_algorithm(int name_id, const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov) + OSSL_PROVIDER *prov, int no_store) { const OSSL_DISPATCH *fns = algodef->implementation; EVP_KEM *kem = NULL; @@ -330,6 +330,8 @@ static void *evp_kem_from_algorithm(int name_id, const OSSL_ALGORITHM *algodef, } kem->name_id = name_id; + kem->no_store = no_store; + if ((kem->type_name = ossl_algorithm_get1_first_name(algodef)) == NULL) goto err; kem->description = algodef->algorithm_description; @@ -450,6 +452,9 @@ void EVP_KEM_free(EVP_KEM *kem) { #ifdef OPENSSL_NO_CACHED_FETCH evp_kem_free(kem); +#else + if (kem != NULL && (kem->no_store != 0)) + evp_kem_free(kem); #endif } @@ -458,6 +463,8 @@ int EVP_KEM_up_ref(EVP_KEM *kem) #ifdef OPENSSL_NO_CACHED_FETCH return evp_kem_up_ref(kem); #else + if (kem->no_store != 0) + return evp_kem_up_ref(kem); return 1; #endif } diff --git a/crypto/evp/keymgmt_meth.c b/crypto/evp/keymgmt_meth.c index 3a6a5d0644..07ea8f8b9e 100644 --- a/crypto/evp/keymgmt_meth.c +++ b/crypto/evp/keymgmt_meth.c @@ -78,7 +78,7 @@ static int get_legacy_alg_type_from_keymgmt(const EVP_KEYMGMT *keymgmt) static void *keymgmt_from_algorithm(int name_id, const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov) + OSSL_PROVIDER *prov, int no_store) { const OSSL_DISPATCH *fns = algodef->implementation; EVP_KEYMGMT *keymgmt = NULL; @@ -92,6 +92,8 @@ static void *keymgmt_from_algorithm(int name_id, return NULL; keymgmt->name_id = name_id; + keymgmt->no_store = no_store; + if ((keymgmt->type_name = ossl_algorithm_get1_first_name(algodef)) == NULL) { evp_keymgmt_free(keymgmt); return NULL; @@ -312,6 +314,8 @@ int EVP_KEYMGMT_up_ref(EVP_KEYMGMT *keymgmt) #ifdef OPENSSL_NO_CACHED_FETCH return evp_keymgmt_up_ref(keymgmt); #else + if (keymgmt->no_store != 0) + return evp_keymgmt_up_ref(keymgmt); return 1; #endif } @@ -320,6 +324,9 @@ void EVP_KEYMGMT_free(EVP_KEYMGMT *keymgmt) { #ifdef OPENSSL_NO_CACHED_FETCH evp_keymgmt_free(keymgmt); +#else + if (keymgmt != NULL && (keymgmt->no_store != 0)) + evp_keymgmt_free(keymgmt); #endif } diff --git a/crypto/evp/mac_meth.c b/crypto/evp/mac_meth.c index 439a6fc2cf..62d94de45c 100644 --- a/crypto/evp/mac_meth.c +++ b/crypto/evp/mac_meth.c @@ -56,7 +56,7 @@ static void *evp_mac_new(void) static void *evp_mac_from_algorithm(int name_id, const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov) + OSSL_PROVIDER *prov, int no_store) { const OSSL_DISPATCH *fns = algodef->implementation; EVP_MAC *mac = NULL; @@ -67,6 +67,7 @@ static void *evp_mac_from_algorithm(int name_id, goto err; } mac->name_id = name_id; + mac->no_store = no_store; if ((mac->type_name = ossl_algorithm_get1_first_name(algodef)) == NULL) goto err; @@ -185,6 +186,8 @@ int EVP_MAC_up_ref(EVP_MAC *mac) #ifdef OPENSSL_NO_CACHED_FETCH return evp_mac_up_ref(mac); #else + if (mac->no_store != 0) + return evp_mac_up_ref(mac); return 1; #endif } @@ -193,6 +196,9 @@ void EVP_MAC_free(EVP_MAC *mac) { #ifdef OPENSSL_NO_CACHED_FETCH evp_mac_free(mac); +#else + if (mac != NULL && (mac->no_store != 0)) + evp_mac_free(mac); #endif } diff --git a/crypto/evp/signature.c b/crypto/evp/signature.c index cd8e00d77e..f2e405aaa3 100644 --- a/crypto/evp/signature.c +++ b/crypto/evp/signature.c @@ -66,7 +66,7 @@ static EVP_SIGNATURE *evp_signature_new(OSSL_PROVIDER *prov) static void *evp_signature_from_algorithm(int name_id, const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov) + OSSL_PROVIDER *prov, int no_store) { const OSSL_DISPATCH *fns = algodef->implementation; EVP_SIGNATURE *signature = NULL; @@ -85,6 +85,7 @@ static void *evp_signature_from_algorithm(int name_id, } signature->name_id = name_id; + signature->no_store = no_store; if ((signature->type_name = ossl_algorithm_get1_first_name(algodef)) == NULL) goto err; signature->description = algodef->algorithm_description; @@ -471,6 +472,9 @@ void EVP_SIGNATURE_free(EVP_SIGNATURE *signature) { #ifdef OPENSSL_NO_CACHED_FETCH evp_signature_free(signature); +#else + if (signature != NULL && (signature->no_store != 0)) + evp_signature_free(signature); #endif } @@ -479,6 +483,8 @@ int EVP_SIGNATURE_up_ref(EVP_SIGNATURE *signature) #ifdef OPENSSL_NO_CACHED_FETCH return evp_signature_up_ref(signature); #else + if (signature->no_store != 0) + return evp_signature_up_ref(signature); return 1; #endif } diff --git a/crypto/evp/skeymgmt_meth.c b/crypto/evp/skeymgmt_meth.c index 88f0a6a133..d1e5c9d446 100644 --- a/crypto/evp/skeymgmt_meth.c +++ b/crypto/evp/skeymgmt_meth.c @@ -62,7 +62,7 @@ static void *skeymgmt_new(void) static void *skeymgmt_from_algorithm(int name_id, const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov) + OSSL_PROVIDER *prov, int no_store) { const OSSL_DISPATCH *fns = algodef->implementation; EVP_SKEYMGMT *skeymgmt = NULL; @@ -71,6 +71,7 @@ static void *skeymgmt_from_algorithm(int name_id, return NULL; skeymgmt->name_id = name_id; + skeymgmt->no_store = no_store; if ((skeymgmt->type_name = ossl_algorithm_get1_first_name(algodef)) == NULL) { evp_skeymgmt_free(skeymgmt); return NULL; @@ -181,6 +182,8 @@ int EVP_SKEYMGMT_up_ref(EVP_SKEYMGMT *skeymgmt) #ifdef OPENSSL_NO_CACHED_FETCH return evp_skeymgmt_up_ref(skeymgmt); #else + if (skeymgmt->no_store != 0) + return evp_skeymgmt_up_ref(skeymgmt); return 1; #endif } @@ -189,6 +192,9 @@ void EVP_SKEYMGMT_free(EVP_SKEYMGMT *skeymgmt) { #ifdef OPENSSL_NO_CACHED_FETCH evp_skeymgmt_free(skeymgmt); +#else + if (skeymgmt != NULL && (skeymgmt->no_store != 0)) + evp_skeymgmt_free(skeymgmt); #endif } diff --git a/crypto/store/store_local.h b/crypto/store/store_local.h index f668d4bbbc..995024d5c9 100644 --- a/crypto/store/store_local.h +++ b/crypto/store/store_local.h @@ -104,6 +104,7 @@ struct ossl_store_loader_st { const char *propdef; const char *description; + int no_store; CRYPTO_REF_COUNT refcnt; OSSL_FUNC_store_open_fn *p_open; diff --git a/crypto/store/store_meth.c b/crypto/store/store_meth.c index 840ddc1a4a..96b092e5bb 100644 --- a/crypto/store/store_meth.c +++ b/crypto/store/store_meth.c @@ -47,6 +47,8 @@ int OSSL_STORE_LOADER_up_ref(OSSL_STORE_LOADER *loader) #ifdef OPENSSL_NO_CACHED_FETCH return up_ref_loader(loader); #else + if (loader->no_store != 0) + return up_ref_loader(loader); return 1; #endif } @@ -55,6 +57,9 @@ void OSSL_STORE_LOADER_free(OSSL_STORE_LOADER *loader) { #ifdef OPENSSL_NO_CACHED_FETCH free_loader(loader); +#else + if (loader != NULL && (loader->no_store != 0)) + free_loader(loader); #endif } @@ -185,7 +190,7 @@ static int put_loader_in_store(void *store, void *method, } static void *loader_from_algorithm(int scheme_id, const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov) + OSSL_PROVIDER *prov, int no_store) { OSSL_STORE_LOADER *loader = NULL; const OSSL_DISPATCH *fns = algodef->implementation; @@ -195,6 +200,7 @@ static void *loader_from_algorithm(int scheme_id, const OSSL_ALGORITHM *algodef, loader->scheme_id = scheme_id; loader->propdef = algodef->property_definition; loader->description = algodef->algorithm_description; + loader->no_store = no_store; for (; fns->function_id != 0; fns++) { switch (fns->function_id) { @@ -259,7 +265,7 @@ static void *loader_from_algorithm(int scheme_id, const OSSL_ALGORITHM *algodef, * then call loader_from_algorithm() with that identity number. */ static void *construct_loader(const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov, void *data) + OSSL_PROVIDER *prov, void *data, int no_store) { /* * This function is only called if get_loader_from_store() returned @@ -275,7 +281,7 @@ static void *construct_loader(const OSSL_ALGORITHM *algodef, void *method = NULL; if (id != 0) - method = loader_from_algorithm(id, algodef, prov); + method = loader_from_algorithm(id, algodef, prov, no_store); /* * Flag to indicate that there was actual construction errors. This diff --git a/include/crypto/decoder.h b/include/crypto/decoder.h index fcb914faeb..dafb143593 100644 --- a/include/crypto/decoder.h +++ b/include/crypto/decoder.h @@ -20,7 +20,7 @@ * (provider-object(7)). */ void *ossl_decoder_from_algorithm(int id, const OSSL_ALGORITHM *algodef, - OSSL_PROVIDER *prov); + OSSL_PROVIDER *prov, int no_store); OSSL_DECODER_INSTANCE * ossl_decoder_instance_new_forprov(OSSL_DECODER *decoder, void *provctx, diff --git a/include/crypto/evp.h b/include/crypto/evp.h index aec23fb013..eca29a7438 100644 --- a/include/crypto/evp.h +++ b/include/crypto/evp.h @@ -141,6 +141,7 @@ void evp_pkey_set_cb_translate(BN_GENCB *cb, EVP_PKEY_CTX *ctx); struct evp_mac_st { OSSL_PROVIDER *prov; int name_id; + int no_store; char *type_name; const char *description; @@ -164,6 +165,7 @@ struct evp_mac_st { struct evp_kdf_st { OSSL_PROVIDER *prov; int name_id; + int no_store; char *type_name; const char *description; CRYPTO_REF_COUNT refcnt; diff --git a/include/internal/core.h b/include/internal/core.h index 5c9ffc657b..33a16395e2 100644 --- a/include/internal/core.h +++ b/include/internal/core.h @@ -44,7 +44,7 @@ typedef struct ossl_method_construct_method_st { const char *name, const char *propdef, void *data); /* Construct a new method */ void *(*construct)(const OSSL_ALGORITHM *algodef, OSSL_PROVIDER *prov, - void *data); + void *data, int no_store); /* Destruct a method */ void (*destruct)(void *method, void *data); } OSSL_METHOD_CONSTRUCT_METHOD; diff --git a/include/openssl/evp.h b/include/openssl/evp.h index 6ab3f556cd..dcbc5b26d3 100644 --- a/include/openssl/evp.h +++ b/include/openssl/evp.h @@ -155,6 +155,8 @@ int EVP_default_properties_enable_fips(OSSL_LIB_CTX *libctx, int enable); /* Note if suitable for use in FIPS mode */ #define EVP_MD_FLAG_FIPS 0x0400 +#define EVP_MD_FLAG_NO_STORE 0x0800 + /* Digest ctrls */ #define EVP_MD_CTRL_DIGALGID 0x1 @@ -267,6 +269,8 @@ int EVP_default_properties_enable_fips(OSSL_LIB_CTX *libctx, int enable); #define EVP_CIPH_FLAG_GET_WRAP_CIPHER 0x4000000 #define EVP_CIPH_FLAG_INVERSE_CIPHER 0x8000000 #define EVP_CIPH_FLAG_ENC_THEN_MAC 0x10000000 +/* flag to indicate that this cipher isn't cached, and so should be refcounted*/ +#define EVP_CIPH_FLAG_NO_STORE 0x20000000 /* * Cipher context flag to indicate we can handle wrap mode: if allowed in diff --git a/providers/implementations/storemgmt/file_store.c b/providers/implementations/storemgmt/file_store.c index 2df5406699..894685ce8c 100644 --- a/providers/implementations/storemgmt/file_store.c +++ b/providers/implementations/storemgmt/file_store.c @@ -505,7 +505,7 @@ static int file_setup_decoders(struct file_ctx_st *ctx) * The decoder doesn't need any identification or to be * attached to any provider, since it's only used locally. */ - to_obj = ossl_decoder_from_algorithm(0, to_algo, NULL); + to_obj = ossl_decoder_from_algorithm(0, to_algo, NULL, 0); if (to_obj != NULL) to_obj_inst = ossl_decoder_instance_new_forprov(to_obj, ctx->provctx, input_structure); diff --git a/providers/implementations/storemgmt/winstore_store.c b/providers/implementations/storemgmt/winstore_store.c index c33763b8d9..59318e7547 100644 --- a/providers/implementations/storemgmt/winstore_store.c +++ b/providers/implementations/storemgmt/winstore_store.c @@ -209,7 +209,7 @@ static int setup_decoder(struct winstore_ctx_st *ctx) * The decoder doesn't need any identification or to be * attached to any provider, since it's only used locally. */ - to_obj = ossl_decoder_from_algorithm(0, to_algo, NULL); + to_obj = ossl_decoder_from_algorithm(0, to_algo, NULL, 0); if (to_obj != NULL) to_obj_inst = ossl_decoder_instance_new_forprov(to_obj, ctx->provctx, input_structure); From a66d74c8c18ed54ad9c95617959cc0b32e508398 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Tue, 30 Jun 2026 12:56:46 -0400 Subject: [PATCH 148/349] add support to p_ossltest for non-cacheable fetches Add the ability to direct p_ossltest to request no caching so that we can test the provider driver non-cache code path. Reviewed-by: Milan Broz Reviewed-by: Dmitry Belyavskiy MergeDate: Thu Jul 2 14:24:15 2026 (Merged from https://github.com/openssl/openssl/pull/31782) --- test/p_ossltest.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/p_ossltest.c b/test/p_ossltest.c index 6d775520ba..7864994485 100644 --- a/test/p_ossltest.c +++ b/test/p_ossltest.c @@ -1766,6 +1766,10 @@ static const OSSL_ALGORITHM *ossltest_query(void *provctx, int operation_id, int *no_cache) { *no_cache = 0; + + if (getenv("OSSL_TEST_PROVIDER_NO_CACHE") != NULL) + *no_cache = 1; + switch (operation_id) { case OSSL_OP_DIGEST: return ossltest_digests; From cf82f5f78f57f56de49a5f346aea6a4baf020c7c Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Tue, 30 Jun 2026 13:06:52 -0400 Subject: [PATCH 149/349] Add test to exercise non-caching code paths Use openssl list, which uses EVP_*_do_all_provided on non-caching algorithms. The do_all_provided path callbacks expect the algorithms that are looked up to be saveable via up_ref, so this, when run under asan, makes for a good test to ensure we don't trigger any use after free situations when not caching algs. Reviewed-by: Milan Broz Reviewed-by: Dmitry Belyavskiy MergeDate: Thu Jul 2 14:24:17 2026 (Merged from https://github.com/openssl/openssl/pull/31782) --- test/recipes/30-test_evp_list_noncache.t | 34 ++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 test/recipes/30-test_evp_list_noncache.t diff --git a/test/recipes/30-test_evp_list_noncache.t b/test/recipes/30-test_evp_list_noncache.t new file mode 100644 index 0000000000..511bcec628 --- /dev/null +++ b/test/recipes/30-test_evp_list_noncache.t @@ -0,0 +1,34 @@ +#! /usr/bin/env perl +# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +use strict; +use warnings; + + +use OpenSSL::Test qw/:DEFAULT srctop_file srctop_dir bldtop_dir/; +use OpenSSL::Test::Utils; + +setup("test_noncaching_evp_fetch"); + +plan skip_all => "This test requires provider module support" + if disabled("module"); + +plan tests => 1; + +# This tells the p_ossltest provider to request no caching of algs +$ENV{OSSL_TEST_PROVIDER_NO_CACHE} = "yes"; + +my $provdir = bldtop_dir("test"); + +# list all algorithms in p_ossltest, this exercises the EVP_*do_all_provided paths when algorithms +# are not being cached +# +ok(run(app(["openssl", "list", "-provider-path", $provdir, "-provider", "p_ossltest", "-all-algorithms"])), + "list provided algs when provider requests no caching"); + + From ff6b868ddde5d603749132bbe1b4a94be878be20 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Wed, 24 Jun 2026 16:12:37 +0200 Subject: [PATCH 150/349] ci: run full cross-compile tests on PRs with 'extended tests' label Previously the cross-compile workflow only ran the EVP tests on pull requests, with the full test suite done only for push events. Allow the full suite to run on a pull request when it has the 'extended tests' label which is already used for extended tests. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Eugene Syromiatnikov Reviewed-by: Milan Broz Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz MergeDate: Fri Jul 3 14:39:38 2026 (Merged from https://github.com/openssl/openssl/pull/31705) --- .github/workflows/cross-compiles.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cross-compiles.yml b/.github/workflows/cross-compiles.yml index efc498edf1..3e1fbc142d 100644 --- a/.github/workflows/cross-compiles.yml +++ b/.github/workflows/cross-compiles.yml @@ -14,6 +14,10 @@ permissions: jobs: cross-compilation: + # Run the full test suite on push, and on pull requests labelled with + # 'extended tests'. Other pull requests only run the EVP tests. + env: + EXTENDED: ${{ github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'extended tests') }} strategy: fail-fast: false matrix: @@ -211,19 +215,19 @@ jobs: cat /proc/cpuinfo QEMU_LD_PREFIX=/usr/${{ matrix.platform.arch }} ./util/opensslwrap.sh version -c - name: make all tests - if: github.event_name == 'push' && matrix.platform.tests == '' + if: env.EXTENDED == 'true' && matrix.platform.tests == '' run: | .github/workflows/make-test \ TESTS="-test_afalg" \ QEMU_LD_PREFIX=/usr/${{ matrix.platform.arch }} - name: make some tests - if: github.event_name == 'push' && matrix.platform.tests != 'none' && matrix.platform.tests != '' + if: env.EXTENDED == 'true' && matrix.platform.tests != 'none' && matrix.platform.tests != '' run: | .github/workflows/make-test \ TESTS="${{ matrix.platform.tests }} -test_afalg" \ QEMU_LD_PREFIX=/usr/${{ matrix.platform.arch }} - name: make evp tests - if: github.event_name == 'pull_request' && matrix.platform.tests != 'none' + if: env.EXTENDED != 'true' && matrix.platform.tests != 'none' run: | .github/workflows/make-test \ TESTS="test_evp*" \ From e59165a1e3df2f1cbe223406ea4fb40025e54d8a Mon Sep 17 00:00:00 2001 From: Niels Provos Date: Tue, 12 May 2026 09:44:12 -0700 Subject: [PATCH 151/349] crypto/x509: replace O(N^2) RFC 3779 canonicalisation merge with linear sweep ASIdentifierChoice_canonize and IPAddressOrRanges_canonize previously merged adjacent entries with an in-place loop that called sk_..._delete() after each merge, making the merge O(N^2) due to the per-merge stack shift. Replace the merge with a single linear sweep using a write index distinct from the read cursor: mergeable entries fold into the previous output's upper bound in O(1), non-mergeable entries are slid forward into the write slot, and the source slot is set to NULL so the ASN.1 free machinery cannot double-free on a subsequent abort. Canonicalisation is now O(N log N) overall, bounded by the existing sort. Mixed-state-on-error safety is provided by the caller's normal teardown path: OPENSSL_sk_pop unlinks without freeing, OPENSSL_sk_set replaces without freeing the displaced value, and ossl_asn1_item_embed_free no-ops on NULL slots, so returning early on an inner failure leaves the stack in a state that the choice's normal free path handles cleanly. New regression tests in test/v3ext.c at N=8192 cover the all-merge, no-merge, interleaved (slide-forward), range-merge, overlap-mid-sweep, and inverted-range-mid-sweep paths; the mixed-state teardown invariant is exercised under ASan + UBSan. Reviewed-by: Paul Dale Reviewed-by: Bob Beck MergeDate: Fri Jul 3 19:20:01 2026 (Merged from https://github.com/openssl/openssl/pull/31147) --- crypto/x509/v3_addr.c | 127 +++++---- crypto/x509/v3_asid.c | 212 ++++++++------- test/v3ext.c | 598 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 798 insertions(+), 139 deletions(-) diff --git a/crypto/x509/v3_addr.c b/crypto/x509/v3_addr.c index 1e0d94babf..bdaa74a0d1 100644 --- a/crypto/x509/v3_addr.c +++ b/crypto/x509/v3_addr.c @@ -760,6 +760,7 @@ int X509v3_addr_is_canonical(IPAddrBlocks *addr) aors = f->ipAddressChoice->u.addressesOrRanges; if (sk_IPAddressOrRange_num(aors) == 0) return 0; + for (j = 0; j < sk_IPAddressOrRange_num(aors) - 1; j++) { IPAddressOrRange *a = sk_IPAddressOrRange_value(aors, j); IPAddressOrRange *b = sk_IPAddressOrRange_value(aors, j + 1); @@ -814,78 +815,106 @@ int X509v3_addr_is_canonical(IPAddrBlocks *addr) /* * Whack an IPAddressOrRanges into canonical form. + * + * After the initial sort, the merge runs as a single linear sweep + * over the list using a write index. Adjacent entries are folded + * into the previous output by replacing it with a freshly built + * merged range; both old entries are then freed and the source slot + * is left NULL so the asn1 free machinery does not double-free on a + * subsequent abort. Total cost is O(N log N) sort + O(N) merge, + * with no stack deletes inside the loop. */ static int IPAddressOrRanges_canonize(IPAddressOrRanges *aors, const unsigned afi) { - int i, j, length = length_from_afi(afi); + int length = length_from_afi(afi); + int read, write = 0, n; - /* - * Sort the IPAddressOrRanges sequence. - */ sk_IPAddressOrRange_sort(aors); + n = sk_IPAddressOrRange_num(aors); /* - * Clean up representation issues, punt on duplicates or overlaps. + * Error paths below all `return 0` directly. Slots at + * [write..read-1] are NULL (from earlier iterations) and slots at + * [read..n-1] still hold their original entries; the caller's + * normal teardown walks the whole stack and frees each non-NULL + * slot safely, so leaving the stack in this mixed state is sound. */ - for (i = 0; i < sk_IPAddressOrRange_num(aors) - 1; i++) { - IPAddressOrRange *a = sk_IPAddressOrRange_value(aors, i); - IPAddressOrRange *b = sk_IPAddressOrRange_value(aors, i + 1); - unsigned char a_min[ADDR_RAW_BUF_LEN], a_max[ADDR_RAW_BUF_LEN]; - unsigned char b_min[ADDR_RAW_BUF_LEN], b_max[ADDR_RAW_BUF_LEN]; + for (read = 0; read < n; read++) { + IPAddressOrRange *cur = sk_IPAddressOrRange_value(aors, read); + unsigned char c_min[ADDR_RAW_BUF_LEN], c_max[ADDR_RAW_BUF_LEN]; - if (!extract_min_max(a, a_min, a_max, length) || !extract_min_max(b, b_min, b_max, length)) + if (!extract_min_max(cur, c_min, c_max, length)) return 0; /* - * Punt inverted ranges. + * Punt inverted range. */ - if (memcmp(a_min, a_max, length) > 0 || memcmp(b_min, b_max, length) > 0) + if (memcmp(c_min, c_max, length) > 0) return 0; - /* - * Punt overlaps. - */ - if (memcmp(a_max, b_min, length) >= 0) - return 0; + if (write > 0) { + IPAddressOrRange *prev = sk_IPAddressOrRange_value(aors, + write - 1); + unsigned char p_min[ADDR_RAW_BUF_LEN], p_max[ADDR_RAW_BUF_LEN]; + unsigned char c_min_minus_one[ADDR_RAW_BUF_LEN]; + int j; - /* - * Merge if a and b are adjacent. We check for - * adjacency by subtracting one from b_min first. - */ - for (j = length - 1; j >= 0 && b_min[j]-- == 0x00; j--) - ; - if (memcmp(a_max, b_min, length) == 0) { - IPAddressOrRange *merged; - - if (!make_addressRange(&merged, a_min, b_max, length)) + if (!extract_min_max(prev, p_min, p_max, length)) return 0; - (void)sk_IPAddressOrRange_set(aors, i, merged); - (void)sk_IPAddressOrRange_delete(aors, i + 1); - IPAddressOrRange_free(a); - IPAddressOrRange_free(b); - --i; - continue; + + /* + * Reject overlap with the previous accepted entry. + */ + if (memcmp(p_max, c_min, length) >= 0) + return 0; + + /* + * Adjacency test: does c_min - 1 equal p_max? Work on a + * scratch copy so the original c_min stays intact for use + * as the lower bound if we end up keeping cur. + */ + memcpy(c_min_minus_one, c_min, length); + for (j = length - 1; + j >= 0 && c_min_minus_one[j]-- == 0x00; + j--) + ; + if (memcmp(p_max, c_min_minus_one, length) == 0) { + IPAddressOrRange *merged; + + if (!make_addressRange(&merged, p_min, c_max, length)) + return 0; + /* + * Replace prev with merged, free the originals, and + * NULL the source slot so the stack does not retain a + * second reference to cur. + */ + (void)sk_IPAddressOrRange_set(aors, write - 1, merged); + IPAddressOrRange_free(prev); + IPAddressOrRange_free(cur); + (void)sk_IPAddressOrRange_set(aors, read, NULL); + continue; + } } + + /* + * Keep cur. Slide it forward into the write slot if we have + * fallen behind, and NULL the source slot to avoid duplicate + * ownership. + */ + if (write != read) { + (void)sk_IPAddressOrRange_set(aors, write, cur); + (void)sk_IPAddressOrRange_set(aors, read, NULL); + } + write++; } /* - * Check for inverted final range. + * Compaction succeeded: every slot at [write..n-1] is NULL, so + * popping the tail leaves the canonicalised list at [0..write-1]. */ - j = sk_IPAddressOrRange_num(aors) - 1; - { - IPAddressOrRange *a = sk_IPAddressOrRange_value(aors, j); - - if (a != NULL && a->type == IPAddressOrRange_addressRange) { - unsigned char a_min[ADDR_RAW_BUF_LEN], a_max[ADDR_RAW_BUF_LEN]; - - if (!extract_min_max(a, a_min, a_max, length)) - return 0; - if (memcmp(a_min, a_max, length) > 0) - return 0; - } - } - + while (sk_IPAddressOrRange_num(aors) > write) + (void)sk_IPAddressOrRange_pop(aors); return 1; } diff --git a/crypto/x509/v3_asid.c b/crypto/x509/v3_asid.c index 8470e0f134..c00ded15f9 100644 --- a/crypto/x509/v3_asid.c +++ b/crypto/x509/v3_asid.c @@ -347,13 +347,22 @@ int X509v3_asid_is_canonical(ASIdentifiers *asid) /* * Whack an ASIdentifierChoice into canonical form. + * + * After the initial sort, the merge runs as a single linear sweep + * over the list using a write index. Each entry is examined once; + * adjacent / mergeable entries extend the previous output's upper + * bound in O(1) and the source slot is left NULL so the asn1 free + * machinery does not double-free on a subsequent abort. Total cost + * is O(N log N) sort + O(N) merge, with no stack deletes inside the + * loop. */ static int ASIdentifierChoice_canonize(ASIdentifierChoice *choice) { ASN1_INTEGER *a_max_plus_one = NULL; ASN1_INTEGER *orig; BIGNUM *bn = NULL; - int i, ret = 0; + int read, write = 0, n; + int ret = 0; /* * Nothing to do for empty element or inheritance. @@ -370,112 +379,135 @@ static int ASIdentifierChoice_canonize(ASIdentifierChoice *choice) } /* - * We have a non-empty list. Sort it. + * Sort the list, then merge in a single sweep using a write index. */ sk_ASIdOrRange_sort(choice->u.asIdsOrRanges); + n = sk_ASIdOrRange_num(choice->u.asIdsOrRanges); - /* - * Now check for errors and suboptimal encoding, rejecting the - * former and fixing the latter. - */ - for (i = 0; i < sk_ASIdOrRange_num(choice->u.asIdsOrRanges) - 1; i++) { - ASIdOrRange *a = sk_ASIdOrRange_value(choice->u.asIdsOrRanges, i); - ASIdOrRange *b = sk_ASIdOrRange_value(choice->u.asIdsOrRanges, i + 1); - ASN1_INTEGER *a_min = NULL, *a_max = NULL, *b_min = NULL, *b_max = NULL; + for (read = 0; read < n; read++) { + ASIdOrRange *cur = sk_ASIdOrRange_value(choice->u.asIdsOrRanges, read); + ASN1_INTEGER *c_min = NULL, *c_max = NULL; - if (!extract_min_max(a, &a_min, &a_max) - || !extract_min_max(b, &b_min, &b_max)) + if (!extract_min_max(cur, &c_min, &c_max)) goto done; /* - * Make sure we're properly sorted (paranoia). + * Punt inverted range. */ - if (!ossl_assert(ASN1_INTEGER_cmp(a_min, b_min) <= 0)) + if (ASN1_INTEGER_cmp(c_min, c_max) > 0) goto done; - /* - * Punt inverted ranges. - */ - if (ASN1_INTEGER_cmp(a_min, a_max) > 0 || ASN1_INTEGER_cmp(b_min, b_max) > 0) - goto done; + if (write > 0) { + ASIdOrRange *prev = sk_ASIdOrRange_value(choice->u.asIdsOrRanges, + write - 1); + ASN1_INTEGER *p_min = NULL, *p_max = NULL; - /* - * Check for overlaps. - */ - if (ASN1_INTEGER_cmp(a_max, b_min) >= 0) { - ERR_raise(ERR_LIB_X509V3, X509V3_R_EXTENSION_VALUE_ERROR); - goto done; - } - - /* - * Calculate a_max + 1 to check for adjacency. - */ - if ((bn == NULL && (bn = BN_new()) == NULL) || ASN1_INTEGER_to_BN(a_max, bn) == NULL || !BN_add_word(bn, 1)) { - ERR_raise(ERR_LIB_X509V3, ERR_R_BN_LIB); - goto done; - } - - if ((a_max_plus_one = BN_to_ASN1_INTEGER(bn, orig = a_max_plus_one)) == NULL) { - a_max_plus_one = orig; - ERR_raise(ERR_LIB_X509V3, ERR_R_ASN1_LIB); - goto done; - } - - /* - * If a and b are adjacent, merge them. - */ - if (ASN1_INTEGER_cmp(a_max_plus_one, b_min) == 0) { - ASRange *r; - switch (a->type) { - case ASIdOrRange_id: - if ((r = OPENSSL_malloc(sizeof(*r))) == NULL) - goto done; - r->min = a_min; - r->max = b_max; - a->type = ASIdOrRange_range; - a->u.range = r; - break; - case ASIdOrRange_range: - ASN1_INTEGER_free(a->u.range->max); - a->u.range->max = b_max; - break; - } - switch (b->type) { - case ASIdOrRange_id: - b->u.id = NULL; - break; - case ASIdOrRange_range: - b->u.range->max = NULL; - break; - } - ASIdOrRange_free(b); - (void)sk_ASIdOrRange_delete(choice->u.asIdsOrRanges, i + 1); - i--; - continue; - } - } - - /* - * Check for final inverted range. - */ - i = sk_ASIdOrRange_num(choice->u.asIdsOrRanges) - 1; - { - ASIdOrRange *a = sk_ASIdOrRange_value(choice->u.asIdsOrRanges, i); - ASN1_INTEGER *a_min, *a_max; - if (a != NULL && a->type == ASIdOrRange_range) { - if (!extract_min_max(a, &a_min, &a_max) - || ASN1_INTEGER_cmp(a_min, a_max) > 0) + if (!extract_min_max(prev, &p_min, &p_max)) goto done; - } - } - /* Paranoia */ - if (!ossl_assert(ASIdentifierChoice_is_canonical(choice))) - goto done; + /* + * Make sure we're properly sorted (paranoia). + */ + if (!ossl_assert(ASN1_INTEGER_cmp(p_min, c_min) <= 0)) + goto done; + + /* + * Reject overlap with the previous accepted entry. + */ + if (ASN1_INTEGER_cmp(p_max, c_min) >= 0) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_EXTENSION_VALUE_ERROR); + goto done; + } + + /* + * Calculate p_max + 1 to check for adjacency. + */ + if ((bn == NULL && (bn = BN_new()) == NULL) + || ASN1_INTEGER_to_BN(p_max, bn) == NULL + || !BN_add_word(bn, 1)) { + ERR_raise(ERR_LIB_X509V3, ERR_R_BN_LIB); + goto done; + } + if ((a_max_plus_one = BN_to_ASN1_INTEGER(bn, + orig = a_max_plus_one)) + == NULL) { + a_max_plus_one = orig; + ERR_raise(ERR_LIB_X509V3, ERR_R_ASN1_LIB); + goto done; + } + + /* + * If prev and cur are adjacent, fold cur into prev. + */ + if (ASN1_INTEGER_cmp(a_max_plus_one, c_min) == 0) { + ASRange *r; + + switch (prev->type) { + case ASIdOrRange_id: + if ((r = OPENSSL_malloc(sizeof(*r))) == NULL) + goto done; + r->min = p_min; + r->max = c_max; + prev->type = ASIdOrRange_range; + prev->u.range = r; + break; + case ASIdOrRange_range: + ASN1_INTEGER_free(prev->u.range->max); + prev->u.range->max = c_max; + break; + } + /* + * Detach c_max from cur so freeing cur does not free + * the value we just transferred to prev. + */ + switch (cur->type) { + case ASIdOrRange_id: + cur->u.id = NULL; + break; + case ASIdOrRange_range: + cur->u.range->max = NULL; + break; + } + ASIdOrRange_free(cur); + /* + * NULL the source slot so any later teardown does not + * walk a freed pointer. We do not advance `write`. + */ + (void)sk_ASIdOrRange_set(choice->u.asIdsOrRanges, read, NULL); + continue; + } + } + + /* + * Keep cur. Slide it forward into the write slot if we have + * fallen behind, and NULL the source slot to avoid duplicate + * ownership. + */ + if (write != read) { + (void)sk_ASIdOrRange_set(choice->u.asIdsOrRanges, write, cur); + (void)sk_ASIdOrRange_set(choice->u.asIdsOrRanges, read, NULL); + } + write++; + } ret = 1; done: + /* + * On success every slot at [write..n-1] is NULL, so popping the + * tail leaves the canonicalised list at [0..write-1]. On error we + * leave the tail untouched; the slots are either NULL (from earlier + * iterations) or original entries the loop never reached, both of + * which the caller's ASIdentifierChoice_free path handles safely. + */ + if (ret) { + while (sk_ASIdOrRange_num(choice->u.asIdsOrRanges) > write) + (void)sk_ASIdOrRange_pop(choice->u.asIdsOrRanges); + /* Paranoia */ + if (!ossl_assert(ASIdentifierChoice_is_canonical(choice))) + ret = 0; + } + ASN1_INTEGER_free(a_max_plus_one); BN_free(bn); return ret; diff --git a/test/v3ext.c b/test/v3ext.c index 2e852804ee..e2b6441197 100644 --- a/test/v3ext.c +++ b/test/v3ext.c @@ -417,6 +417,595 @@ static int test_ext_syntax(void) return testresult; } +/* + * Number of entries the large-canonize regression tests construct. + * Chosen well above the legacy 4096 cap and large enough that the + * previous O(N^2) merge would be visibly slow under any sanitiser + * configuration, while still small enough to keep CI cost negligible + * with the linear merge. + */ +#define V3EXT_TEST_LARGE_N 8192 + +/* + * Build an ASIdentifiers extension containing V3EXT_TEST_LARGE_N + * adjacent single integers (1, 2, 3, ...), exercise canonize, and + * verify that the entire list collapses to one ASIdOrRange_range and + * that the post-canonize result reports canonical. Stresses the + * linear merge path that replaced the quadratic in-place delete. + */ +static int test_asid_large_canonize_merge(void) +{ + ASIdentifiers *asid = NULL; + ASN1_INTEGER *val = NULL; + int i; + int testresult = 0; + + if (!TEST_ptr(asid = ASIdentifiers_new())) + goto err; + + for (i = 0; i < V3EXT_TEST_LARGE_N; i++) { + if (!TEST_ptr(val = ASN1_INTEGER_new()) + || !TEST_true(ASN1_INTEGER_set_int64(val, (int64_t)(i + 1)))) + goto err; + if (!TEST_true(X509v3_asid_add_id_or_range(asid, V3_ASID_ASNUM, + val, NULL))) + goto err; + /* Ownership of val transferred on success. */ + val = NULL; + } + + if (!TEST_int_eq(sk_ASIdOrRange_num(asid->asnum->u.asIdsOrRanges), + V3EXT_TEST_LARGE_N)) + goto err; + + if (!TEST_true(X509v3_asid_canonize(asid))) + goto err; + + /* The whole list must collapse to a single merged range [1, N]. */ + if (!TEST_int_eq(sk_ASIdOrRange_num(asid->asnum->u.asIdsOrRanges), 1)) + goto err; + { + ASIdOrRange *aor = sk_ASIdOrRange_value(asid->asnum->u.asIdsOrRanges, + 0); + int64_t got_min = 0, got_max = 0; + + if (!TEST_ptr(aor) || !TEST_int_eq(aor->type, ASIdOrRange_range)) + goto err; + if (!TEST_true(ASN1_INTEGER_get_int64(&got_min, aor->u.range->min)) + || !TEST_int64_t_eq(got_min, 1) + || !TEST_true(ASN1_INTEGER_get_int64(&got_max, aor->u.range->max)) + || !TEST_int64_t_eq(got_max, (int64_t)V3EXT_TEST_LARGE_N)) + goto err; + } + + if (!TEST_int_eq(X509v3_asid_is_canonical(asid), 1)) + goto err; + + testresult = 1; +err: + ASN1_INTEGER_free(val); + ASIdentifiers_free(asid); + return testresult; +} + +/* + * Build an ASIdentifiers extension containing V3EXT_TEST_LARGE_N + * non-mergeable single integers (1, 3, 5, ...). After canonize the + * list must be unchanged in length, every entry must remain an id + * with its original value (none silently merged or transformed), and + * is_canonical must report canonical -- i.e. the large list is + * accepted on its merits with no arbitrary cap kicking in. + */ +static int test_asid_large_canonize_no_merge(void) +{ + ASIdentifiers *asid = NULL; + ASN1_INTEGER *val = NULL; + int i; + int testresult = 0; + + if (!TEST_ptr(asid = ASIdentifiers_new())) + goto err; + + for (i = 0; i < V3EXT_TEST_LARGE_N; i++) { + if (!TEST_ptr(val = ASN1_INTEGER_new()) + || !TEST_true(ASN1_INTEGER_set_int64(val, (int64_t)(2 * i + 1)))) + goto err; + if (!TEST_true(X509v3_asid_add_id_or_range(asid, V3_ASID_ASNUM, + val, NULL))) + goto err; + val = NULL; + } + + if (!TEST_true(X509v3_asid_canonize(asid))) + goto err; + + if (!TEST_int_eq(sk_ASIdOrRange_num(asid->asnum->u.asIdsOrRanges), + V3EXT_TEST_LARGE_N)) + goto err; + + /* + * Every entry must still be a single id with its original value; + * adjacent ids should NOT have been merged. + */ + for (i = 0; i < V3EXT_TEST_LARGE_N; i++) { + ASIdOrRange *aor = sk_ASIdOrRange_value(asid->asnum->u.asIdsOrRanges, + i); + int64_t got = 0; + + if (!TEST_ptr(aor) + || !TEST_int_eq(aor->type, ASIdOrRange_id) + || !TEST_true(ASN1_INTEGER_get_int64(&got, aor->u.id)) + || !TEST_int64_t_eq(got, (int64_t)(2 * i + 1))) + goto err; + } + + if (!TEST_int_eq(X509v3_asid_is_canonical(asid), 1)) + goto err; + + testresult = 1; +err: + ASN1_INTEGER_free(val); + ASIdentifiers_free(asid); + return testresult; +} + +/* + * Build an IPAddrBlocks with a single IPv4 family carrying + * V3EXT_TEST_LARGE_N adjacent /32 host prefixes starting at 1.0.0.1 + * (deliberately offset by one from the prefix-aligned 1.0.0.0 so the + * merged span [1.0.0.1, 1.0.32.0] cannot be expressed as a single + * prefix and stays an IPAddressOrRange_addressRange). After canonize + * the family must contain exactly one IPAddressOrRange of type + * addressRange covering the whole block, and is_canonical must + * accept it. Stresses the linear merge path in v3_addr.c. + */ +static int test_addr_large_canonize_merge(void) +{ + IPAddrBlocks *addr = NULL; + int i; + int testresult = 0; + + if (!TEST_ptr(addr = sk_IPAddressFamily_new_null())) + goto end; + + for (i = 0; i < V3EXT_TEST_LARGE_N; i++) { + unsigned char ip[4]; + unsigned int v = 0x01000001u + (unsigned int)i; /* 1.0.0.1 + i */ + + ip[0] = (unsigned char)((v >> 24) & 0xFF); + ip[1] = (unsigned char)((v >> 16) & 0xFF); + ip[2] = (unsigned char)((v >> 8) & 0xFF); + ip[3] = (unsigned char)(v & 0xFF); + if (!TEST_true(X509v3_addr_add_prefix(addr, IANA_AFI_IPV4, NULL, + ip, 32))) + goto end; + } + + if (!TEST_true(X509v3_addr_canonize(addr))) + goto end; + + if (!TEST_int_eq(sk_IPAddressFamily_num(addr), 1)) + goto end; + { + IPAddressFamily *f = sk_IPAddressFamily_value(addr, 0); + IPAddressOrRange *aor; + unsigned char got_min[4], got_max[4]; + unsigned int expected_max = 0x01000001u + V3EXT_TEST_LARGE_N - 1; + unsigned char want_min[4] = { 0x01, 0x00, 0x00, 0x01 }; + unsigned char want_max[4]; + + want_max[0] = (unsigned char)((expected_max >> 24) & 0xFF); + want_max[1] = (unsigned char)((expected_max >> 16) & 0xFF); + want_max[2] = (unsigned char)((expected_max >> 8) & 0xFF); + want_max[3] = (unsigned char)(expected_max & 0xFF); + + if (!TEST_ptr(f) + || !TEST_int_eq(f->ipAddressChoice->type, + IPAddressChoice_addressesOrRanges) + || !TEST_int_eq(sk_IPAddressOrRange_num( + f->ipAddressChoice->u.addressesOrRanges), + 1)) + goto end; + + aor = sk_IPAddressOrRange_value(f->ipAddressChoice->u.addressesOrRanges, + 0); + if (!TEST_ptr(aor) + || !TEST_int_eq(aor->type, IPAddressOrRange_addressRange)) + goto end; + if (!TEST_int_eq(X509v3_addr_get_range(aor, IANA_AFI_IPV4, + got_min, got_max, sizeof(got_min)), + 4) + || !TEST_mem_eq(got_min, 4, want_min, 4) + || !TEST_mem_eq(got_max, 4, want_max, 4)) + goto end; + } + + if (!TEST_int_eq(X509v3_addr_is_canonical(addr), 1)) + goto end; + + testresult = 1; +end: + sk_IPAddressFamily_pop_free(addr, IPAddressFamily_free); + return testresult; +} + +/* + * Interleaved merge / no-merge pattern, exercising the slide-forward + * arm of the linear-sweep compaction in ASIdentifierChoice_canonize. + * + * We build pairs of adjacent integers separated by gaps: (1, 2), (5, 6), + * (9, 10), ... Each pair merges to a single range, so the canonical + * output has exactly V3EXT_TEST_LARGE_N / 2 entries. Critically, after + * the second element of every pair merges into the first the merge + * loop's `write` index falls behind `read`; the *next* (non-mergeable) + * pair-start must then be slid forward into slot `write` and the source + * slot at `read` must be NULL'd. This is the path with no coverage in + * the all-merge or all-no-merge tests. + */ +static int test_asid_interleaved_canonize(void) +{ + ASIdentifiers *asid = NULL; + ASN1_INTEGER *val = NULL; + int i; + int expected = V3EXT_TEST_LARGE_N / 2; + int testresult = 0; + + if (!TEST_ptr(asid = ASIdentifiers_new())) + goto err; + + for (i = 0; i < V3EXT_TEST_LARGE_N; i++) { + /* Pair starts at 4*p, then 4*p+1; the next pair starts at 4*(p+1). */ + int pair = i / 2; + int within = i % 2; + int64_t v = 4 * (int64_t)pair + within + 1; + + if (!TEST_ptr(val = ASN1_INTEGER_new()) + || !TEST_true(ASN1_INTEGER_set_int64(val, v))) + goto err; + if (!TEST_true(X509v3_asid_add_id_or_range(asid, V3_ASID_ASNUM, + val, NULL))) + goto err; + val = NULL; + } + + if (!TEST_true(X509v3_asid_canonize(asid))) + goto err; + + if (!TEST_int_eq(sk_ASIdOrRange_num(asid->asnum->u.asIdsOrRanges), + expected)) + goto err; + + /* Every entry must now be a 2-wide range (the merge result of a pair). */ + for (i = 0; i < expected; i++) { + ASIdOrRange *aor = sk_ASIdOrRange_value(asid->asnum->u.asIdsOrRanges, i); + + if (!TEST_ptr(aor) || !TEST_int_eq(aor->type, ASIdOrRange_range)) + goto err; + } + + if (!TEST_int_eq(X509v3_asid_is_canonical(asid), 1)) + goto err; + + testresult = 1; +err: + ASN1_INTEGER_free(val); + ASIdentifiers_free(asid); + return testresult; +} + +/* + * IP-address counterpart to test_asid_interleaved_canonize: pairs of + * adjacent /32 prefixes separated by a gap of 2, so each pair merges + * but the next pair-start must be slid forward. + */ +static int test_addr_interleaved_canonize(void) +{ + IPAddrBlocks *addr = NULL; + int i; + int expected = V3EXT_TEST_LARGE_N / 2; + int testresult = 0; + + if (!TEST_ptr(addr = sk_IPAddressFamily_new_null())) + goto end; + + for (i = 0; i < V3EXT_TEST_LARGE_N; i++) { + unsigned char ip[4]; + unsigned int pair = (unsigned int)(i / 2); + unsigned int within = (unsigned int)(i % 2); + unsigned int v = 0x01000000u + 4u * pair + within; + + ip[0] = (unsigned char)((v >> 24) & 0xFF); + ip[1] = (unsigned char)((v >> 16) & 0xFF); + ip[2] = (unsigned char)((v >> 8) & 0xFF); + ip[3] = (unsigned char)(v & 0xFF); + if (!TEST_true(X509v3_addr_add_prefix(addr, IANA_AFI_IPV4, NULL, + ip, 32))) + goto end; + } + + if (!TEST_true(X509v3_addr_canonize(addr))) + goto end; + + if (!TEST_int_eq(sk_IPAddressFamily_num(addr), 1)) + goto end; + { + IPAddressFamily *f = sk_IPAddressFamily_value(addr, 0); + + if (!TEST_ptr(f) + || !TEST_int_eq(f->ipAddressChoice->type, + IPAddressChoice_addressesOrRanges) + || !TEST_int_eq(sk_IPAddressOrRange_num( + f->ipAddressChoice->u.addressesOrRanges), + expected)) + goto end; + } + + if (!TEST_int_eq(X509v3_addr_is_canonical(addr), 1)) + goto end; + + testresult = 1; +end: + sk_IPAddressFamily_pop_free(addr, IPAddressFamily_free); + return testresult; +} + +/* + * Trigger an overlap-detection error partway through the linear + * merge. The first V3EXT_TEST_LARGE_N / 2 entries are adjacent and + * mergeable; entry K is a duplicate of entry K-1 (overlap). The + * canonize call must return 0, and the caller's normal teardown of + * the choice must safely free the stack -- some slots hold merged + * results, some hold NULL (from earlier merges), and some hold + * originals that the loop never reached. ASan / UBSan-instrumented + * builds will catch any double-free or use-after-free in the + * teardown that the mixed-state-on-error invariant claims to avoid. + */ +static int test_asid_canonize_error_midsweep(void) +{ + ASIdentifiers *asid = NULL; + ASN1_INTEGER *val = NULL; + int i; + int n = V3EXT_TEST_LARGE_N; + int k = n / 2; + int testresult = 0; + + if (!TEST_ptr(asid = ASIdentifiers_new())) + goto err; + + for (i = 0; i < n; i++) { + /* + * Entries 1..k are 1,2,...,k (all adjacent). + * Entry k+1 duplicates entry k (overlap, triggers the error). + * Remaining entries are far away so they sort after the overlap. + */ + int64_t v; + + if (i < k) + v = (int64_t)i + 1; + else if (i == k) + v = (int64_t)k; /* duplicate, overlap */ + else + v = (int64_t)i + 1000000; /* far suffix, untouched */ + + if (!TEST_ptr(val = ASN1_INTEGER_new()) + || !TEST_true(ASN1_INTEGER_set_int64(val, v))) + goto err; + if (!TEST_true(X509v3_asid_add_id_or_range(asid, V3_ASID_ASNUM, + val, NULL))) + goto err; + val = NULL; + } + + /* canonize must reject overlap. */ + if (!TEST_int_eq(X509v3_asid_canonize(asid), 0)) + goto err; + + /* + * Successful return below relies on ASIdentifiers_free walking + * the partially-compacted stack without UAF or double-free. + * Under ASan / UBSan that walk is the actual test. + */ + testresult = 1; +err: + ASN1_INTEGER_free(val); + ASIdentifiers_free(asid); + return testresult; +} + +/* + * Trigger an overlap-detection error partway through the linear merge + * in IPAddressOrRanges_canonize. Construct a list whose first half is + * adjacent and mergeable, with a duplicate at position k that hits the + * overlap check after a series of merges has driven write < read. + * The canonize call must return 0, and the family's normal teardown + * (sk_IPAddressFamily_pop_free) must safely walk the partially + * compacted stack -- ASan / UBSan catches any double-free or UAF the + * mixed-state-on-error invariant would otherwise miss. Because the + * v3_addr.c canonize uses direct `return 0` rather than a `done:` + * cleanup label, the teardown invariant for this file is different + * from the asid path and warrants its own coverage. + */ +static int test_addr_canonize_error_midsweep(void) +{ + IPAddrBlocks *addr = NULL; + int i; + int n = V3EXT_TEST_LARGE_N; + int k = n / 2; + int testresult = 0; + + if (!TEST_ptr(addr = sk_IPAddressFamily_new_null())) + goto end; + + for (i = 0; i < n; i++) { + unsigned char ip[4]; + unsigned int v; + + if (i < k) + v = 0x01000000u + (unsigned int)i; /* adjacent /32 prefixes */ + else if (i == k) + v = 0x01000000u + (unsigned int)(k - 1); /* duplicate, overlap */ + else + v = 0x02000000u + (unsigned int)i; /* far suffix, untouched */ + + ip[0] = (unsigned char)((v >> 24) & 0xFF); + ip[1] = (unsigned char)((v >> 16) & 0xFF); + ip[2] = (unsigned char)((v >> 8) & 0xFF); + ip[3] = (unsigned char)(v & 0xFF); + if (!TEST_true(X509v3_addr_add_prefix(addr, IANA_AFI_IPV4, NULL, + ip, 32))) + goto end; + } + + /* canonize must reject overlap. */ + if (!TEST_int_eq(X509v3_addr_canonize(addr), 0)) + goto end; + + /* + * Successful return below relies on sk_IPAddressFamily_pop_free + * walking the partially-compacted aors stack without UAF or + * double-free. Under ASan / UBSan that walk is the actual test. + */ + testresult = 1; +end: + sk_IPAddressFamily_pop_free(addr, IPAddressFamily_free); + return testresult; +} + +/* + * Exercise the merge arm where `cur` is itself a range (rather than a + * single integer), hitting the `case ASIdOrRange_range` detach branch + * in ASIdentifierChoice_canonize. Build adjacent 2-wide ranges + * [1,2], [3,4], [5,6], ... which all fold into a single big range + * [1, 2 * V3EXT_TEST_LARGE_N]. + */ +static int test_asid_range_merge_canonize(void) +{ + ASIdentifiers *asid = NULL; + ASN1_INTEGER *minv = NULL, *maxv = NULL; + int i; + int testresult = 0; + + if (!TEST_ptr(asid = ASIdentifiers_new())) + goto err; + + for (i = 0; i < V3EXT_TEST_LARGE_N; i++) { + if (!TEST_ptr(minv = ASN1_INTEGER_new()) + || !TEST_true(ASN1_INTEGER_set_int64(minv, + (int64_t)(2 * i + 1))) + || !TEST_ptr(maxv = ASN1_INTEGER_new()) + || !TEST_true(ASN1_INTEGER_set_int64(maxv, + (int64_t)(2 * i + 2)))) + goto err; + if (!TEST_true(X509v3_asid_add_id_or_range(asid, V3_ASID_ASNUM, + minv, maxv))) + goto err; + minv = maxv = NULL; + } + + if (!TEST_true(X509v3_asid_canonize(asid))) + goto err; + + if (!TEST_int_eq(sk_ASIdOrRange_num(asid->asnum->u.asIdsOrRanges), 1)) + goto err; + { + ASIdOrRange *aor = sk_ASIdOrRange_value(asid->asnum->u.asIdsOrRanges, + 0); + int64_t got_min = 0, got_max = 0; + + if (!TEST_ptr(aor) || !TEST_int_eq(aor->type, ASIdOrRange_range)) + goto err; + /* + * The merged range must cover [1, 2 * V3EXT_TEST_LARGE_N]; + * incorrect max-propagation would still leave a single range + * but with a truncated upper bound. + */ + if (!TEST_true(ASN1_INTEGER_get_int64(&got_min, aor->u.range->min)) + || !TEST_int64_t_eq(got_min, 1) + || !TEST_true(ASN1_INTEGER_get_int64(&got_max, aor->u.range->max)) + || !TEST_int64_t_eq(got_max, (int64_t)(2 * V3EXT_TEST_LARGE_N))) + goto err; + } + + if (!TEST_int_eq(X509v3_asid_is_canonical(asid), 1)) + goto err; + + testresult = 1; +err: + ASN1_INTEGER_free(minv); + ASN1_INTEGER_free(maxv); + ASIdentifiers_free(asid); + return testresult; +} + +/* + * Trigger the inverted-range guard partway through the linear merge + * in ASIdentifierChoice_canonize. The first half of the list is + * well-formed adjacent integers; entry k is an explicitly inverted + * range (min = 1000, max = 100). X509v3_asid_add_id_or_range does + * not validate min <= max for ranges, so the bad entry is admitted + * into the list, and canonize must detect it on the sweep. The + * teardown under ASan / UBSan verifies that the early-exit path + * leaves the asIdsOrRanges stack in a freeable state. + * + * The addr-side counterpart of this branch (v3_addr.c:849) is not + * reachable through the public API: make_addressRange refuses to + * construct an inverted IPAddressOrRange in the first place. The + * guard remains as defence against a DER-decoded extension that + * carries inverted min/max bit strings; exercising it from C would + * require hand-building an IPAddressOrRange, which would bind the + * test to internal ASN.1 layout. + */ +static int test_asid_canonize_inverted_midsweep(void) +{ + ASIdentifiers *asid = NULL; + ASN1_INTEGER *val = NULL, *minv = NULL, *maxv = NULL; + int i; + int n = V3EXT_TEST_LARGE_N; + int k = n / 2; + int testresult = 0; + + if (!TEST_ptr(asid = ASIdentifiers_new())) + goto err; + + for (i = 0; i < n; i++) { + if (i == k) { + /* Inverted range: min=1000, max=100. */ + if (!TEST_ptr(minv = ASN1_INTEGER_new()) + || !TEST_true(ASN1_INTEGER_set_int64(minv, 1000)) + || !TEST_ptr(maxv = ASN1_INTEGER_new()) + || !TEST_true(ASN1_INTEGER_set_int64(maxv, 100))) + goto err; + if (!TEST_true(X509v3_asid_add_id_or_range(asid, V3_ASID_ASNUM, + minv, maxv))) + goto err; + minv = maxv = NULL; + } else { + int64_t v = (i < k) ? (int64_t)i + 1 + : (int64_t)i + 1000000; /* far suffix */ + + if (!TEST_ptr(val = ASN1_INTEGER_new()) + || !TEST_true(ASN1_INTEGER_set_int64(val, v))) + goto err; + if (!TEST_true(X509v3_asid_add_id_or_range(asid, V3_ASID_ASNUM, + val, NULL))) + goto err; + val = NULL; + } + } + + /* canonize must reject the inverted entry. */ + if (!TEST_int_eq(X509v3_asid_canonize(asid), 0)) + goto err; + + testresult = 1; +err: + ASN1_INTEGER_free(val); + ASN1_INTEGER_free(minv); + ASN1_INTEGER_free(maxv); + ASIdentifiers_free(asid); + return testresult; +} + static int test_addr_subset(void) { int i; @@ -480,6 +1069,15 @@ int setup_tests(void) ADD_TEST(test_ext_syntax); ADD_TEST(test_addr_fam_len); ADD_TEST(test_addr_subset); + ADD_TEST(test_asid_large_canonize_merge); + ADD_TEST(test_asid_large_canonize_no_merge); + ADD_TEST(test_addr_large_canonize_merge); + ADD_TEST(test_asid_interleaved_canonize); + ADD_TEST(test_addr_interleaved_canonize); + ADD_TEST(test_asid_canonize_error_midsweep); + ADD_TEST(test_addr_canonize_error_midsweep); + ADD_TEST(test_asid_range_merge_canonize); + ADD_TEST(test_asid_canonize_inverted_midsweep); #endif /* OPENSSL_NO_RFC3779 */ return 1; } From a8db32eed7a348bd22b136b3a7278cde8d462738 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 23 Jun 2026 11:44:51 +0200 Subject: [PATCH 152/349] apps: cover the CRL printing path in the pkcs7 test recipe The crls != NULL block of pkcs7 -print_certs was not exercised. Build a PKCS#7 structure containing a CRL with crl2pkcs7 and check the CRL is both printed and output in PEM form. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Daniel Kubec Reviewed-by: Matt Caswell MergeDate: Fri Jul 3 19:21:11 2026 (Merged from https://github.com/openssl/openssl/pull/31666) --- test/recipes/25-test_pkcs7.t | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/test/recipes/25-test_pkcs7.t b/test/recipes/25-test_pkcs7.t index 23f1c8a764..3801c4b106 100644 --- a/test/recipes/25-test_pkcs7.t +++ b/test/recipes/25-test_pkcs7.t @@ -15,7 +15,7 @@ use OpenSSL::Test qw/:DEFAULT srctop_file data_file/; setup("test_pkcs7"); -plan tests => 7; +plan tests => 10; require_ok(srctop_file('test','recipes','tconversion.pl')); @@ -43,3 +43,16 @@ is(cmp_text($out, data_file('grfc.out')), my $malformed = data_file('malformed.pkcs7'); ok(run(app(["openssl", "pkcs7", "-in", $malformed]))); + +# Test that -print_certs prints CRLs contained in a PKCS#7 structure +my $crlp7 = "testcrl.p7"; +ok(run(app(["openssl", "crl2pkcs7", + "-in", srctop_file("test", "testcrl.pem"), + "-out", $crlp7])), + "create a PKCS#7 structure containing a CRL"); +my @crlout = run(app(["openssl", "pkcs7", "-print_certs", "-in", $crlp7]), + capture => 1); +ok(grep(/Certificate Revocation List \(CRL\):/, @crlout) == 1, + "print_certs shows the CRL contents"); +ok(grep(/-----BEGIN X509 CRL-----/, @crlout) == 1, + "print_certs outputs the CRL in PEM form"); From 4b873a1d214974636db35f7224ff0ff434175e90 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Wed, 24 Jun 2026 23:13:02 +0200 Subject: [PATCH 153/349] x509: add delta CRL success test Exercise the previously uncovered X509_V_FLAG_USE_DELTAS path in get_delta_sk(): a current delta CRL revoking kLeaf must be honored. The base and delta CRLs are generated by `ossl-test-tools crltest delta`. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Daniel Kubec Reviewed-by: Eugene Syromiatnikov MergeDate: Fri Jul 3 19:22:29 2026 (Merged from https://github.com/openssl/openssl/pull/31714) --- test/crltest.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/test/crltest.c b/test/crltest.c index 3e06df10b7..15fdef3f3c 100644 --- a/test/crltest.c +++ b/test/crltest.c @@ -743,6 +743,43 @@ static const char *kCrlDeltaIndicatorString[] = { NULL }; +static const char *kCrlDeltaBase[] = { + "-----BEGIN X509 CRL-----\n", + "MIICQDCCASgCAQEwDQYJKoZIhvcNAQELBQAwgZAxCzAJBgNVBAYTAlVTMRMwEQYD\n", + "VQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRUwEwYDVQQK\n", + "DAxFeGFtcGxlIENvcnAxHjAcBgNVBAsMFUNlcnRpZmljYXRlIEF1dGhvcml0eTEd\n", + "MBsGA1UEAwwURXhhbXBsZSBDb3JwIFJvb3QgQ0EXDTI2MDMxMDA4MDAwMFoXDTI2\n", + "MDYwODA4MDAwMFqgYzBhMB8GA1UdIwQYMBaAFP4UDhMbCWfLSg1L2k/z75C1Q9sz\n", + "MAsGA1UdFAQEAgIQADAxBgNVHS4EKjAoMCagJKAihiBodHRwOi8vY3JsLmV4YW1w\n", + "bGUuY29tL2RlbHRhLmNybDANBgkqhkiG9w0BAQsFAAOCAQEAIxrY08mNQ1L8+nL9\n", + "H6Wn1ElntRzMLnk6FqgxosA0Tq3EDzRWKHj2Xbk1vGdRdZi7ttYH1+8+5UA8JPmN\n", + "tRyvrm3NieEqW2reDoyFxJYsWQlJCFHjDVeNpoi8fv/qrOYxtuMfyiwho9WjovVi\n", + "AS9/oa/kSbD39RN/wc0UVRBtQn/vBAzlYExehiwnmiXXwbQA+waNlnL58F/34gRh\n", + "sJs0C/HJn9VU4gvSVW1vbpA7Fxt4alUj2NlXSXHi44mXuei4qc3Pxlw2A2Pfca7y\n", + "vcd30ZZdoKFzMViOnLtcM4vLw59ZEENJmz3vIIU6jACBy8/FbdPsH/iJTTvc76Yv\n", + "CjQcgQ==\n", + "-----END X509 CRL-----\n", + NULL +}; + +static const char *kCrlDeltaValid[] = { + "-----BEGIN X509 CRL-----\n", + "MIICNDCCARwCAQEwDQYJKoZIhvcNAQELBQAwgZAxCzAJBgNVBAYTAlVTMRMwEQYD\n", + "VQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRUwEwYDVQQK\n", + "DAxFeGFtcGxlIENvcnAxHjAcBgNVBAsMFUNlcnRpZmljYXRlIEF1dGhvcml0eTEd\n", + "MBsGA1UEAwwURXhhbXBsZSBDb3JwIFJvb3QgQ0EXDTI2MDMxMDA4MDAwMFoXDTI2\n", + "MDYwODA4MDAwMFowFTATAgIQABcNMjYwMzIwMDAwMDAwWqBAMD4wHwYDVR0jBBgw\n", + "FoAU/hQOExsJZ8tKDUvaT/PvkLVD2zMwCwYDVR0UBAQCAhABMA4GA1UdGwEB/wQE\n", + "AgIQADANBgkqhkiG9w0BAQsFAAOCAQEAjDx5wqkXfcfTtEbMUN1UcKAHQC5Fx/Kq\n", + "wpoDulPh52zmugl9zhEWWuwA0hSJ/qNRo5tatSGvHbIOrwvZ0LKgChHwtdQfAcBY\n", + "xMl8KsVRqgGjJ4NahyAglsnsJ95VvImMJGFm+eS0DxQgGJgvsj/dh3dsJEGIW4Mo\n", + "baF6e6sAYaYjn9QW0uzoc5zqux25/DUR5DG99cbi6NOqCm7U1gvWkZsjx4HInx4r\n", + "CFazu5IQE7gk1qipnROwgfi/QQXZmAueW+XasEqQcQw0WVEmCHq6OBlrelTs165b\n", + "sK0XOqWDfa745ZN0EZwJY6GIVl+KEAC0XkoGZdqudOEQbbWog0OKkQ==\n", + "-----END X509 CRL-----\n", + NULL +}; + static const char *kCrlNumberString[] = { "-----BEGIN X509 CRL-----\n", "MIICJTCCAQ0CAQEwDQYJKoZIhvcNAQELBQAweTELMAkGA1UEBhMCVVMxEzARBgNV\n", @@ -1933,6 +1970,36 @@ end: return ret; } +/* + * Exercise the X509_V_FLAG_USE_DELTAS path. kCrlDeltaBase carries a Freshest + * CRL extension and revokes nothing; kCrlDeltaValid is a current delta that + * revokes kLeaf. The delta is in scope, so verification reports kLeaf revoked. + */ +static int test_crl_delta_valid(void) +{ + X509 *root = X509_from_strings(kRoot); + X509 *leaf = X509_from_strings(kLeaf); + X509_CRL *base = CRL_from_strings(kCrlDeltaBase); + X509_CRL *delta = CRL_from_strings(kCrlDeltaValid); + unsigned long flags = X509_V_FLAG_CRL_CHECK + | X509_V_FLAG_EXTENDED_CRL_SUPPORT | X509_V_FLAG_USE_DELTAS; + int test; + + test = TEST_ptr(root) + && TEST_ptr(leaf) + && TEST_ptr(base) + && TEST_ptr(delta) + && TEST_int_eq(verify(leaf, root, make_CRL_stack(base, delta), + flags, kVerify), + X509_V_ERR_CERT_REVOKED); + + X509_CRL_free(base); + X509_CRL_free(delta); + X509_free(leaf); + X509_free(root); + return test; +} + int setup_tests(void) { ADD_TEST(test_private_keys); @@ -1945,6 +2012,7 @@ int setup_tests(void) ADD_TEST(test_crl_date_invalid); ADD_TEST(test_crl_get_fn_score); ADD_TEST(test_crl_delta_indicator); + ADD_TEST(test_crl_delta_valid); ADD_TEST(test_crl_number); ADD_TEST(test_crl_idp_asn1_wrong_tag); ADD_TEST(test_crl_idp_asn1_wrong_tag2); From c30329c854c8e3ea97cf7b43dc476a1de13be35c Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 25 Jun 2026 12:40:59 +0200 Subject: [PATCH 154/349] apps: cover the req -set_serial option The OPT_SET_SERIAL case in req_main() was not covered. Add a test that generates a self-signed certificate with an explicit -set_serial value and checks it, plus the error path when -set_serial is given twice. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Matt Caswell Reviewed-by: Daniel Kubec Reviewed-by: Paul Dale MergeDate: Fri Jul 3 19:25:05 2026 (Merged from https://github.com/openssl/openssl/pull/31730) --- test/recipes/25-test_req.t | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/test/recipes/25-test_req.t b/test/recipes/25-test_req.t index 869ac4c3b7..b23e41ee2c 100644 --- a/test/recipes/25-test_req.t +++ b/test/recipes/25-test_req.t @@ -15,7 +15,7 @@ use OpenSSL::Test qw/:DEFAULT srctop_file/; setup("test_req"); -plan tests => 129; +plan tests => 130; require_ok(srctop_file('test', 'recipes', 'tconversion.pl')); @@ -501,6 +501,27 @@ subtest "generating certificate requests with SLH-DSA" => sub { } }; +subtest "generating certificate with -set_serial" => sub { + plan tests => 3; + + my $cert = "self-signed_set_serial.pem"; + ok(run(app(["openssl", "req", "-x509", "-new", "-days", "365", + "-config", srctop_file("test", "test.cnf"), + "-key", srctop_file("test", "testrsa.pem"), + "-set_serial", "12345", + "-out", $cert])), + "Generating self-signed cert with -set_serial"); + + cert_contains($cert, "Serial Number: 12345", 1); + + ok(!run(app(["openssl", "req", "-x509", "-new", "-days", "365", + "-config", srctop_file("test", "test.cnf"), + "-key", srctop_file("test", "testrsa.pem"), + "-set_serial", "12345", "-set_serial", "67890", + "-out", $cert])), + "Supplying -set_serial twice fails"); +}; + my @openssl_args = ("req", "-config", srctop_file("apps", "openssl.cnf")); run_conversion('req conversions', From dd943486d7a7ec1e9bbdb1b288c708d7005d0167 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 25 Jun 2026 14:44:06 +0200 Subject: [PATCH 155/349] apps: cover crl signature verification The CRL signature verification path was not exercised. Add a test that verifies a CRL signature against its issuer certificate supplied via -CAfile, -CAstore and -CApath. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Matt Caswell Reviewed-by: Daniel Kubec MergeDate: Fri Jul 3 19:26:15 2026 (Merged from https://github.com/openssl/openssl/pull/31736) --- test/recipes/25-test_crl.t | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/test/recipes/25-test_crl.t b/test/recipes/25-test_crl.t index 92101e8d94..6addde4b0b 100644 --- a/test/recipes/25-test_crl.t +++ b/test/recipes/25-test_crl.t @@ -11,11 +11,12 @@ use strict; use warnings; use File::Spec; +use File::Copy; use OpenSSL::Test qw/:DEFAULT srctop_file/; setup("test_crl"); -plan tests => 10; +plan tests => 11; require_ok(srctop_file('test','recipes','tconversion.pl')); @@ -51,6 +52,39 @@ ok(run(app(["openssl", "crl", "-text", "-in", $pem, "-inform", "PEM", is(cmp_text($out, srctop_file("test/certs", "cyrillic_crl.utf8")), 0, 'Comparing utf8 output'); +# Verify a CRL's signature against its issuer certificate, supplied via +# -CAfile, -CAstore and -CApath. +subtest 'crl signature verification' => sub { + plan tests => 4; + + my $crl = srctop_file("test/certs", "delta-crl-as-complete-delta.pem"); + my $cacert = srctop_file("test/certs", "delta-crl-as-complete-ca.pem"); + + ok(run(app(["openssl", "crl", "-noout", "-in", $crl, + "-CAfile", $cacert])), + "verify CRL signature with -CAfile"); + + ok(run(app(["openssl", "crl", "-noout", "-in", $crl, + "-CAstore", $cacert])), + "verify CRL signature with -CAstore"); + + # -CApath needs a rehashed directory, which relies on the rehash command + # (not available on platforms without symlink support, e.g. Windows). + SKIP: { + skip "rehash is not available on this platform", 2 + unless run(app(["openssl", "rehash", "-help"])); + + my $capath = "crl_capath"; + mkdir $capath; + copy($cacert, File::Spec->catfile($capath, "ca.pem")); + ok(run(app(["openssl", "rehash", $capath])), + "rehash the -CApath directory"); + ok(run(app(["openssl", "crl", "-noout", "-in", $crl, + "-CApath", $capath])), + "verify CRL signature with -CApath"); + } +}; + sub compare1stline { my ($cmdarray, $str) = @_; my @lines = run(app($cmdarray), capture => 1); From be1673815439d14abaed2ee1d7714587eb1fc085 Mon Sep 17 00:00:00 2001 From: Frederik Wedel-Heinen Date: Tue, 30 Jun 2026 17:22:11 +0200 Subject: [PATCH 156/349] Remove internal-only DRBG_STATUS enum Changed internal prov_drbg_st member variable from DRBG_STATUS to int to reflect how it was actually used. Reviewed-by: Paul Dale Reviewed-by: Nikola Pajkovsky MergeDate: Fri Jul 3 19:29:47 2026 (Merged from https://github.com/openssl/openssl/pull/31745) --- providers/implementations/include/prov/drbg.h | 13 +++++-------- test/drbgtest.c | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/providers/implementations/include/prov/drbg.h b/providers/implementations/include/prov/drbg.h index c93acba96e..84494a7e0a 100644 --- a/providers/implementations/include/prov/drbg.h +++ b/providers/implementations/include/prov/drbg.h @@ -46,13 +46,6 @@ typedef struct prov_drbg_st PROV_DRBG; -/* DRBG status values */ -typedef enum drbg_status_e { - DRBG_UNINITIALISED, - DRBG_READY, - DRBG_ERROR -} DRBG_STATUS; - /* * The state of all types of DRBGs. */ @@ -149,7 +142,11 @@ struct prov_drbg_st { unsigned int parent_reseed_counter; size_t seedlen; - DRBG_STATUS state; + /* + * state is one of: EVP_RAND_STATE_UNINITIALISED, EVP_RAND_STATE_ERROR, + * EVP_RAND_STATE_READY. + */ + int state; /* DRBG specific data */ void *data; diff --git a/test/drbgtest.c b/test/drbgtest.c index 91060cf60a..0828ebf7b5 100644 --- a/test/drbgtest.c +++ b/test/drbgtest.c @@ -182,7 +182,7 @@ static int test_drbg_reseed(int expect_success, time_t reseed_when) { time_t before_reseed, after_reseed; - int expected_state = (expect_success ? DRBG_READY : DRBG_ERROR); + int expected_state = (expect_success ? EVP_RAND_STATE_READY : EVP_RAND_STATE_ERROR); unsigned int primary_reseed, public_reseed, private_reseed; unsigned char dummy[RANDOM_SIZE]; From 3cd2f291245c523692b655561e70a1e3fd5dfab1 Mon Sep 17 00:00:00 2001 From: Frederik Wedel-Heinen Date: Tue, 30 Jun 2026 17:22:54 +0200 Subject: [PATCH 157/349] Removes two unused macros: MAX_RESEED_INTERVAL and MAX_RESEED_TIME_INTERVAL. Reviewed-by: Paul Dale Reviewed-by: Nikola Pajkovsky MergeDate: Fri Jul 3 19:29:48 2026 (Merged from https://github.com/openssl/openssl/pull/31745) --- providers/implementations/include/prov/drbg.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/providers/implementations/include/prov/drbg.h b/providers/implementations/include/prov/drbg.h index 84494a7e0a..42fd8212ac 100644 --- a/providers/implementations/include/prov/drbg.h +++ b/providers/implementations/include/prov/drbg.h @@ -23,10 +23,6 @@ /* How many times to read the TSC as a randomness source. */ #define TSC_READ_COUNT 4 -/* Maximum reseed intervals */ -#define MAX_RESEED_INTERVAL (1 << 24) -#define MAX_RESEED_TIME_INTERVAL (1 << 20) /* approx. 12 days */ - /* Default reseed intervals */ #define RESEED_INTERVAL (1 << 8) #define TIME_INTERVAL (60 * 60) /* 1 hour */ From 7e2879e035d703d8ddca8f43a3f73b9a22508cf0 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 30 Jun 2026 19:44:15 +0200 Subject: [PATCH 158/349] apps: test dsa app PVK output Cover the previously untested PVK code paths of the dsa app: round-trip the test key through the PVK encoding (mirroring the existing rsa PVK test, and skipped unless rc4, legacy and pvkkdf are enabled), and check that requesting PVK output for a public key input is rejected. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Matt Caswell Reviewed-by: Paul Dale MergeDate: Fri Jul 3 19:30:48 2026 (Merged from https://github.com/openssl/openssl/pull/31801) --- test/recipes/15-test_dsa.t | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/test/recipes/15-test_dsa.t b/test/recipes/15-test_dsa.t index b2747e3d46..602e94e6a0 100644 --- a/test/recipes/15-test_dsa.t +++ b/test/recipes/15-test_dsa.t @@ -17,7 +17,7 @@ use OpenSSL::Test::Utils; setup("test_dsa"); plan skip_all => 'DSA is not supported in this build' if disabled('dsa'); -plan tests => 7; +plan tests => 9; require_ok(srctop_file('test','recipes','tconversion.pl')); @@ -45,3 +45,28 @@ subtest "dsa conversions using 'openssl pkey' -- public key" => sub { -in => srctop_file("test","testdsapub.pem"), -args => ["pkey", "-pubin", "-pubout"] ); }; + +SKIP: { + skip "Skipping PVK conversion test", 1 + if disabled("rc4") || disabled("legacy") || disabled("pvkkdf"); + + subtest "dsa conversions using 'openssl dsa' -- PVK" => sub { + tconversion( -type => 'pvk', -prefix => 'dsa-pvk', + -in => srctop_file("test", "testdsa.pem"), + -args => ["dsa", "-passin", "pass:testpass", + "-passout", "pass:testpass", + "-provider", "default", + "-provider", "legacy"] ); + }; +} + +subtest "dsa PVK output is rejected for public key input" => sub { + plan tests => 1; + + # Note: -noout would short-circuit before the format check, so request + # an actual encoding to reach the PVK-with-public-key rejection. + ok(!run(app(['openssl', 'dsa', '-pubin', '-outform', 'PVK', + '-in', srctop_file("test", "testdsapub.pem"), + '-out', 'dsa-pubin.pvk'])), + "-outform PVK with -pubin is rejected"); +}; From f908737d5823d7f0c3714a819df7b5affb45952d Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 25 Jun 2026 12:51:49 +0200 Subject: [PATCH 159/349] apps: cover x509 DER key/cert input formats The -keyform, -CAform and -CAkeyform options were not covered. Add a test that self-signs a CSR with a DER-encoded key and signs a CSR with a DER-encoded CA cert and CA key. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Matt Caswell Reviewed-by: Daniel Kubec MergeDate: Fri Jul 3 19:40:27 2026 (Merged from https://github.com/openssl/openssl/pull/31733) --- test/recipes/25-test_x509.t | 41 ++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/test/recipes/25-test_x509.t b/test/recipes/25-test_x509.t index d438de0fe2..de467569f2 100644 --- a/test/recipes/25-test_x509.t +++ b/test/recipes/25-test_x509.t @@ -17,7 +17,7 @@ use File::Compare qw/compare_text/; setup("test_x509"); -plan tests => 151; +plan tests => 152; # Prevent MSys2 filename munging for arguments that look like file paths but # aren't @@ -709,3 +709,42 @@ ok(!run(app(["openssl", "x509", "-multi", "-checkend", # Bad parse still returns non-zero ok(!run(app(["openssl", "x509", "-checkend", "60", "-in", $c_key])), "Bad parse with -checkend returns non-zero"); + +# Signing using DER-encoded key and CA cert/key inputs, +# exercising -keyform, -CAform and -CAkeyform +subtest 'x509 signing with DER -keyform, -CAform and -CAkeyform' => sub { + plan tests => 6; + + my $csr = srctop_file(@certs, "x509-check.csr"); + my $signkey_der = "x509-check-key.der"; + my $cacert_der = "ca-cert.der"; + my $cakey_der = "ca-key.der"; + + # self-sign the CSR with a DER-encoded signing key + ok(run(app(["openssl", "pkey", + "-in", srctop_file(@certs, "x509-check-key.pem"), + "-outform", "DER", "-out", $signkey_der])), + "convert signing key to DER"); + ok(run(app(["openssl", "x509", "-req", "-in", $csr, + "-signkey", $signkey_der, "-keyform", "DER", + "-out", "x509-self-der.pem"])), + "self-sign CSR with -keyform DER"); + + # sign the CSR with a DER-encoded CA cert and CA key + ok(run(app(["openssl", "x509", + "-in", srctop_file(@certs, "ca-cert.pem"), + "-outform", "DER", "-out", $cacert_der])), + "convert CA cert to DER"); + ok(run(app(["openssl", "pkey", + "-in", srctop_file(@certs, "ca-key.pem"), + "-outform", "DER", "-out", $cakey_der])), + "convert CA key to DER"); + my $caout = "ca-issued-der.pem"; + ok(run(app(["openssl", "x509", "-req", "-in", $csr, + "-CA", $cacert_der, "-CAform", "DER", + "-CAkey", $cakey_der, "-CAkeyform", "DER", + "-CAcreateserial", "-text", "-out", $caout])), + "sign CSR with -CAform DER and -CAkeyform DER"); + ok(get_issuer($caout) =~ /CN=CA/, + "issuer of CA-signed cert matches DER CA cert"); +}; From 70fa179635fb3daf2e1b1e0d2888f1f8bf79ff16 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Thu, 25 Jun 2026 17:42:05 -0400 Subject: [PATCH 160/349] eliminate use of CRYPTO_GET_REF in sslapitest CRYPTO_GET_REF is almost by definition a TOCTOU race, and we shouldn't use it. As part of the effort to deprecate it, eliminate its use from sslapitest. Avoid the use-after-free possibility by getting a session with SSL_get1_session (which increments the refcount) and freeing it after we're done with it. Reviewed-by: Kurt Roeckx Reviewed-by: Paul Dale Reviewed-by: Norbert Pocs Reviewed-by: Nikola Pajkovsky Reviewed-by: Bob Beck MergeDate: Sat Jul 4 16:47:10 2026 (Merged from https://github.com/openssl/openssl/pull/31750) --- test/sslapitest.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/test/sslapitest.c b/test/sslapitest.c index 91a2181b61..c5517016c5 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -10601,7 +10601,8 @@ static int test_session_cache_overflow(int idx) SSL *serverssl = NULL, *clientssl = NULL; int testresult = 0; SSL_SESSION *sess = NULL; - int references; + + get_sess_val = NULL; #ifdef OSSL_NO_USABLE_TLS1_3 /* If no TLSv1.3 available then do nothing in this case */ @@ -10672,18 +10673,9 @@ static int test_session_cache_overflow(int idx) * The session we just negotiated may have been already removed from the * internal cache - but we will return it anyway from our external cache. */ - get_sess_val = SSL_get_session(serverssl); + get_sess_val = SSL_get1_session(serverssl); if (!TEST_ptr(get_sess_val)) goto end; - /* - * Normally the session is also stored in the cache, thus we have more than - * one reference, but due to an out-of-memory error it can happen that this - * is the only reference, and in that case the SSL_free(serverssl) below - * would free the get_sess_val, causing a use-after-free error. - */ - if (!TEST_true(CRYPTO_GET_REF(&get_sess_val->references, &references)) - || !TEST_int_ge(references, 2)) - goto end; sess = SSL_get1_session(clientssl); if (!TEST_ptr(sess)) goto end; @@ -10707,6 +10699,8 @@ static int test_session_cache_overflow(int idx) testresult = 1; end: + SSL_SESSION_free(get_sess_val); + get_sess_val = NULL; SSL_free(serverssl); SSL_free(clientssl); SSL_CTX_free(sctx); From 16be8273addbb7c667e9ff12fdb6bf892c43d161 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 26 Jun 2026 13:06:20 -0400 Subject: [PATCH 161/349] Replace use of CRYPTO_GET_REF in bio_lib BIO_free_all makes use of CRYPTO_GET_REF to determine if there is another user of a BIO chain at some artibrary point within the chain. But CRYPTO_GET_REF is begging for a TOCTOU error, and so we're deprecating it. replace the use of GET_REF with an internal version of BIO_free that returns the value of the resultant ref count, and use that instead, so we are TOCTOU free Reviewed-by: Kurt Roeckx Reviewed-by: Paul Dale Reviewed-by: Norbert Pocs Reviewed-by: Nikola Pajkovsky Reviewed-by: Bob Beck MergeDate: Sat Jul 4 16:47:13 2026 (Merged from https://github.com/openssl/openssl/pull/31750) --- crypto/bio/bio_lib.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/crypto/bio/bio_lib.c b/crypto/bio/bio_lib.c index b7d4bc549e..dbd55c8b2f 100644 --- a/crypto/bio/bio_lib.c +++ b/crypto/bio/bio_lib.c @@ -116,24 +116,22 @@ BIO *BIO_new(const BIO_METHOD *method) return BIO_new_ex(NULL, method); } -int BIO_free(BIO *a) +static int BIO_free_int(BIO *a, int *ret) { - int ret; if (a == NULL) return 0; - if (CRYPTO_DOWN_REF(&a->references, &ret) <= 0) + if (CRYPTO_DOWN_REF(&a->references, ret) <= 0) return 0; - REF_PRINT_COUNT("BIO", ret, a); - if (ret > 0) + REF_PRINT_COUNT("BIO", *ret, a); + if (*ret > 0) return 1; - REF_ASSERT_ISNT(ret < 0); + REF_ASSERT_ISNT(*ret < 0); if (HAS_CALLBACK(a)) { - ret = (int)bio_call_callback(a, BIO_CB_FREE, NULL, 0, 0, 0L, 1L, NULL); - if (ret <= 0) + if ((int)bio_call_callback(a, BIO_CB_FREE, NULL, 0, 0, 0L, 1L, NULL) <= 0) return 0; } @@ -149,6 +147,13 @@ int BIO_free(BIO *a) return 1; } +int BIO_free(BIO *b) +{ + int ref; + + return BIO_free_int(b, &ref); +} + void BIO_set_data(BIO *a, void *ptr) { a->ptr = ptr; @@ -874,11 +879,11 @@ void BIO_free_all(BIO *bio) while (bio != NULL) { b = bio; - CRYPTO_GET_REF(&b->references, &ref); bio = bio->next_bio; - BIO_free(b); - /* Since ref count > 1, don't free anyone else. */ - if (ref > 1) + ref = 0; + BIO_free_int(b, &ref); + /* Since ref count > 0, don't free anyone else. */ + if (ref > 0) break; } } From 562d2a137a53faac1f2d59e23e3eb458af2edac7 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 26 Jun 2026 13:18:49 -0400 Subject: [PATCH 162/349] Remove CRYPTO_GET_REF This function should never have existed. Its a TOCTOU waiting to happen. Now that we've eliminated all internal uses, and given that its an internal function, send it to a nice farm upstate, where it can run and play with all the other functions that shouldn't have been. Reviewed-by: Kurt Roeckx Reviewed-by: Paul Dale Reviewed-by: Norbert Pocs Reviewed-by: Nikola Pajkovsky Reviewed-by: Bob Beck MergeDate: Sat Jul 4 16:47:16 2026 (Merged from https://github.com/openssl/openssl/pull/31750) --- include/internal/refcount.h | 43 ------------------------------------- 1 file changed, 43 deletions(-) diff --git a/include/internal/refcount.h b/include/internal/refcount.h index 11f207f881..2e7dedf2c3 100644 --- a/include/internal/refcount.h +++ b/include/internal/refcount.h @@ -68,12 +68,6 @@ static inline int CRYPTO_DOWN_REF(CRYPTO_REF_COUNT *refcnt, int *ret) return 1; } -static inline int CRYPTO_GET_REF(CRYPTO_REF_COUNT *refcnt, int *ret) -{ - *ret = atomic_load_explicit(&refcnt->val, memory_order_acquire); - return 1; -} - #elif defined(__GNUC__) && defined(__ATOMIC_RELAXED) && __GCC_ATOMIC_INT_LOCK_FREE > 0 #define HAVE_ATOMICS 1 @@ -96,12 +90,6 @@ static __inline__ int CRYPTO_DOWN_REF(CRYPTO_REF_COUNT *refcnt, int *ret) return 1; } -static __inline__ int CRYPTO_GET_REF(CRYPTO_REF_COUNT *refcnt, int *ret) -{ - *ret = __atomic_load_n(&refcnt->val, __ATOMIC_RELAXED); - return 1; -} - #elif defined(__ICL) && defined(_WIN32) #define HAVE_ATOMICS 1 @@ -121,12 +109,6 @@ static __inline int CRYPTO_DOWN_REF(CRYPTO_REF_COUNT *refcnt, int *ret) return 1; } -static __inline int CRYPTO_GET_REF(CRYPTO_REF_COUNT *refcnt, int *ret) -{ - *ret = _InterlockedExchangeAdd((void *)&refcnt->val, 0); - return 1; -} - #elif defined(_MSC_VER) && _MSC_VER >= 1200 #define HAVE_ATOMICS 1 @@ -153,12 +135,6 @@ static __inline int CRYPTO_DOWN_REF(CRYPTO_REF_COUNT *refcnt, int *ret) return 1; } -static __inline int CRYPTO_GET_REF(CRYPTO_REF_COUNT *refcnt, int *ret) -{ - *ret = _InterlockedExchangeAdd_acq((void *)&refcnt->val, 0); - return 1; -} - #else #pragma intrinsic(_InterlockedExchangeAdd) @@ -174,12 +150,6 @@ static __inline int CRYPTO_DOWN_REF(CRYPTO_REF_COUNT *refcnt, int *ret) return 1; } -static __inline int CRYPTO_GET_REF(CRYPTO_REF_COUNT *refcnt, int *ret) -{ - *ret = _InterlockedExchangeAdd(&refcnt->val, 0); - return 1; -} - #endif #endif @@ -213,12 +183,6 @@ static ossl_unused ossl_inline int CRYPTO_DOWN_REF(CRYPTO_REF_COUNT *refcnt, return CRYPTO_atomic_add(&refcnt->val, -1, ret, refcnt->lock); } -static ossl_unused ossl_inline int CRYPTO_GET_REF(CRYPTO_REF_COUNT *refcnt, - int *ret) -{ - return CRYPTO_atomic_load_int(&refcnt->val, ret, refcnt->lock); -} - #define CRYPTO_NEW_FREE_DEFINED 1 static ossl_unused ossl_inline int CRYPTO_NEW_REF(CRYPTO_REF_COUNT *refcnt, int n) { @@ -255,13 +219,6 @@ static ossl_unused ossl_inline int CRYPTO_DOWN_REF(CRYPTO_REF_COUNT *refcnt, return 1; } -static ossl_unused ossl_inline int CRYPTO_GET_REF(CRYPTO_REF_COUNT *refcnt, - int *ret) -{ - *ret = refcnt->val; - return 1; -} - #endif /* OPENSSL_THREADS */ #endif From c65777dd3c67a7d5ae33ff63cdd3534d36417d9d Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Thu, 16 Apr 2026 11:07:25 +0900 Subject: [PATCH 163/349] Add stack test for thunked sorted lookup Reviewed-by: Milan Broz Reviewed-by: Paul Dale Reviewed-by: Frederik Wedel-Heinen Reviewed-by: Neil Horman MergeDate: Sat Jul 4 16:53:24 2026 (Merged from https://github.com/openssl/openssl/pull/30857) --- test/stack_test.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/stack_test.c b/test/stack_test.c index 16d72f809e..c2ed731f44 100644 --- a/test/stack_test.c +++ b/test/stack_test.c @@ -255,6 +255,15 @@ static int test_uchar_stack(int reserve) goto end; sk_uchar_sort(r); sk_uchar_sort(q); + for (i = 0; i < n; i++) { + int idx = sk_uchar_find(q, v + i); + + if (!TEST_int_ge(idx, 0) + || !TEST_uchar_eq(*sk_uchar_value(q, idx), v[i])) { + TEST_info("uchar sorted find %d", i); + goto end; + } + } /* pop */ for (i = 0; i < n; i++) { From f31510e95333b33a8765cbb81a147df7572c88b2 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 23 Jun 2026 16:32:12 +0200 Subject: [PATCH 164/349] quic: add mfail test for handshake multi-packet processing This tests handshake level phase using mfail covering SSL_do_handshake. It is a test for #31323. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Matt Caswell Reviewed-by: Tomas Mraz Reviewed-by: Neil Horman MergeDate: Sat Jul 4 16:57:38 2026 (Merged from https://github.com/openssl/openssl/pull/31324) --- test/quicapitest.c | 173 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 171 insertions(+), 2 deletions(-) diff --git a/test/quicapitest.c b/test/quicapitest.c index ff858fb170..271778f6b3 100644 --- a/test/quicapitest.c +++ b/test/quicapitest.c @@ -2798,8 +2798,16 @@ end: return ret; } -static int create_quic_ssl_objects(SSL_CTX *sctx, SSL_CTX *cctx, - SSL **lssl, SSL **cssl) +/* Fake clock for tests that advance QUIC time without consuming real time. */ +static OSSL_TIME fake_now; + +static OSSL_TIME fake_now_cb(void *arg) +{ + return fake_now; +} + +static int create_quic_ssl_objects_ex(SSL_CTX *sctx, SSL_CTX *cctx, + SSL **lssl, SSL **cssl, int use_fake_time) { BIO_ADDR *addr = NULL; struct in_addr ina; @@ -2840,6 +2848,18 @@ static int create_quic_ssl_objects(SSL_CTX *sctx, SSL_CTX *cctx, SSL_set_bio(*cssl, cbio, cbio); cbio = NULL; + if (use_fake_time) { + /* + * The base value does not matter but must be nonzero, as the ACK + * manager reads a zero packet timestamp as unset. Use real time to + * match the clock the engines were created with. + */ + fake_now = ossl_time_now(); + if (!TEST_true(ossl_quic_set_override_now_cb(*lssl, fake_now_cb, NULL)) + || !TEST_true(ossl_quic_set_override_now_cb(*cssl, fake_now_cb, NULL))) + goto err; + } + ret = 1; err: @@ -2855,6 +2875,12 @@ err: return ret; } +static int create_quic_ssl_objects(SSL_CTX *sctx, SSL_CTX *cctx, + SSL **lssl, SSL **cssl) +{ + return create_quic_ssl_objects_ex(sctx, cctx, lssl, cssl, 0); +} + static int test_ssl_client_as_ossl_quic_method(void) { SSL_CTX *cctx = NULL, *sctx = NULL; @@ -3494,6 +3520,146 @@ static int test_quic_peer_addr_v6(void) } #endif +#ifndef OPENSSL_NO_CACHED_FETCH +/* + * Advance the fake clock to the next QUIC timer event when both endpoints are + * idle, consuming no real time. + */ +static void quic_advance_time(SSL *clientssl, SSL *serverssl) +{ + struct timeval tv; + int inf = 0; + OSSL_TIME delay = ossl_time_infinite(), t; + + /* If there is data waiting to be processed, do not wait - tick instead. */ + if (BIO_pending(SSL_get_rbio(clientssl)) > 0) + return; + + if (SSL_get_event_timeout(clientssl, &tv, &inf) && !inf) { + t = ossl_time_from_timeval(tv); + if (ossl_time_compare(t, delay) < 0) + delay = t; + } + if (SSL_get_event_timeout(serverssl, &tv, &inf) && !inf) { + t = ossl_time_from_timeval(tv); + if (ossl_time_compare(t, delay) < 0) + delay = t; + } + + if (!ossl_time_is_infinite(delay)) + fake_now = ossl_time_add(fake_now, delay); +} + +static int test_quic_handshake_multipkt_mfail(void) +{ + SSL_CTX *cctx = NULL, *sctx = NULL; + SSL *clientssl = NULL, *serverssl = NULL, *qlistener = NULL; + QUIC_CHANNEL *sch = NULL, *cch = NULL; + int ret = 0, rc = 0, err, i; + + if (!TEST_ptr(sctx = create_server_ctx()) + || !TEST_ptr(cctx = create_client_ctx())) + goto err; + + if (!create_quic_ssl_objects_ex(sctx, cctx, &qlistener, &clientssl, 1)) + goto err; + + if (!TEST_true(SSL_set_tlsext_host_name(clientssl, "localhost"))) + goto err; + + /* Get the listener to bind a channel we can accept. */ + for (i = 0; i < 10; i++) { + rc = SSL_connect(clientssl); + if (rc <= 0) { + err = SSL_get_error(clientssl, rc); + if (!TEST_true(err == SSL_ERROR_WANT_READ + || err == SSL_ERROR_WANT_WRITE)) + goto err; + } + SSL_handle_events(qlistener); + + serverssl = SSL_accept_connection(qlistener, 0); + if (serverssl != NULL) + break; + } + if (!TEST_ptr(serverssl) + || !TEST_false(SSL_is_init_finished(serverssl))) + goto err; + + if (!TEST_ptr(sch = ossl_quic_conn_get_channel(serverssl))) + goto err; + + /* Do handshake until the server reaches the first flight. */ + for (i = 0; i < 10; i++) { + rc = SSL_do_handshake(clientssl); + if (rc <= 0) { + err = SSL_get_error(clientssl, rc); + if (!TEST_true(err == SSL_ERROR_WANT_READ + || err == SSL_ERROR_WANT_WRITE)) + goto err; + } + if (ossl_quic_channel_is_term_any(sch)) + goto err; + SSL_handle_events(serverssl); + if (sch->tx_enc_level >= QUIC_ENC_LEVEL_HANDSHAKE) + break; + quic_advance_time(clientssl, serverssl); + } + if (!TEST_int_lt(i, 10)) + goto err; + + /* Process the multi-packet datagram under mfail. */ + MFAIL_start(); + rc = SSL_do_handshake(clientssl); + MFAIL_end(); + + /* A fatal injected failure may terminate the connection - bail if so. */ + if (rc <= 0) { + err = SSL_get_error(clientssl, rc); + if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) + goto err; + } + + if (!TEST_ptr(cch = ossl_quic_conn_get_channel(clientssl))) + goto err; + + /* Connection still live so get the handshake to converge. */ + for (i = 0; i < 10; i++) { + rc = SSL_do_handshake(clientssl); + if (rc == 1) + break; + + err = SSL_get_error(clientssl, rc); + if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) { + ret = -1; + goto err; + } + + if (ossl_quic_channel_is_term_any(cch) + || ossl_quic_channel_is_term_any(sch)) + goto err; + + SSL_handle_events(serverssl); + quic_advance_time(clientssl, serverssl); + } + if (!TEST_int_lt(i, 10)) { + ret = -1; + goto err; + } + + ret = 1; + +err: + SSL_free(serverssl); + SSL_free(clientssl); + SSL_free(qlistener); + SSL_CTX_free(sctx); + SSL_CTX_free(cctx); + + return ret; +} +#endif + /* Test ECH with quic */ static int test_ech(void) { @@ -3789,6 +3955,9 @@ int setup_tests(void) ADD_TEST(test_quic_peer_addr_v6); #endif ADD_TEST(test_quic_peer_addr_v4); +#ifndef OPENSSL_NO_CACHED_FETCH + ADD_MFAIL_NO_CHECK_TEST(test_quic_handshake_multipkt_mfail); +#endif ADD_TEST(test_ech); ADD_TEST(test_quic_resize_txe); #ifdef OPENSSL_NO_CACHED_FETCH From a89400969d49d42eb8699c6afaf2f613f917fc5b Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 3 Jul 2026 16:19:57 -0400 Subject: [PATCH 165/349] Fix new statem_clnt_test when dtls is disabled The new statem client tests added in comimt c36a9b4 assume that if OPENSSL_NO_DTLS is not defined, that we have DTLS support, but we have this odd setup in which we can have DTLS enabled, but DTLS1_2 disabled, in which case the needed support isn't present, and the test fails. why we have it setup that way, I'm not sure, but we should only run the dtls tests if both DTLS and DTLS1_2 support is available. Fixes #31851 Reviewed-by: Milan Broz Reviewed-by: Matt Caswell MergeDate: Mon Jul 6 09:42:44 2026 (Merged from https://github.com/openssl/openssl/pull/31582) --- test/statem_clnt_construct_test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/statem_clnt_construct_test.c b/test/statem_clnt_construct_test.c index 43dc330dec..459c8ac6d4 100644 --- a/test/statem_clnt_construct_test.c +++ b/test/statem_clnt_construct_test.c @@ -408,7 +408,7 @@ static int test_construct_ch_hrr(void) /* DTLS happy-path / cookie / random-reuse tests */ -#ifndef OPENSSL_NO_DTLS +#if !defined(OPENSSL_NO_DTLS) && !defined(OPENSSL_NO_DTLS1_2) static int test_construct_ch_dtls(void) { CH_CONFIG cfg = { 1, 0, 0, 0 }; @@ -715,7 +715,7 @@ int setup_tests(void) #endif /* OPENSSL_NO_CACHED_FETCH */ #endif /* OSSL_NO_USABLE_TLS1_3 */ -#ifndef OPENSSL_NO_DTLS +#if !defined(OPENSSL_NO_DTLS) && !defined(OPENSSL_NO_DTLS1_2) ADD_TEST(test_construct_ch_dtls); ADD_TEST(test_construct_ch_dtls_cookie); ADD_TEST(test_construct_ch_dtls_client_random); From 64f09bd826e8fdefae67aea68ec1bfacefa1d5f4 Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Tue, 30 Jun 2026 08:55:09 +0200 Subject: [PATCH 166/349] crypto/x509/x509_lu.c: fix memory leak in obj_ht_foreach_object() when sk_X509_OBJECT_push() fails after x509_object_dup() has already allocated the duplicate, the dup is neither stored on the destination stack nor freed: the error path only pop_free()s the stack the dup was never pushed onto, so it is leaked. Set env ASAN_OPTIONS in test explicitly to detect_leaks=1 to force ASAN to fail the test. Otherwise, the test reports ok even with valid leak. Fixes: 08cecb4448e9 "Add X509_STORE_get1_objects" Fixes: https://github.com/openssl/openssl/issues/31771 Signed-off-by: Nikola Pajkovsky Reviewed-by: Norbert Pocs Reviewed-by: Bob Beck MergeDate: Tue Jul 7 07:42:17 2026 (Merged from https://github.com/openssl/openssl/pull/31784) --- crypto/x509/x509_lu.c | 1 + test/recipes/60-test_x509_load_cert_file.t | 2 ++ test/x509_load_cert_file_test.c | 33 ++++++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/crypto/x509/x509_lu.c b/crypto/x509/x509_lu.c index e9cc7145e3..01e1969d19 100644 --- a/crypto/x509/x509_lu.c +++ b/crypto/x509/x509_lu.c @@ -764,6 +764,7 @@ static int obj_ht_foreach_object(HT_VALUE *v, void *arg) return 1; err: + X509_OBJECT_free(dup); sk_X509_OBJECT_pop_free(*sk, X509_OBJECT_free); *sk = NULL; diff --git a/test/recipes/60-test_x509_load_cert_file.t b/test/recipes/60-test_x509_load_cert_file.t index e329d7675c..15e9908ce1 100644 --- a/test/recipes/60-test_x509_load_cert_file.t +++ b/test/recipes/60-test_x509_load_cert_file.t @@ -8,6 +8,8 @@ use OpenSSL::Test qw/:DEFAULT srctop_file/; +$ENV{ASAN_OPTIONS} = "detect_leaks=1"; + setup("test_load_cert_file"); plan tests => 1; diff --git a/test/x509_load_cert_file_test.c b/test/x509_load_cert_file_test.c index 0df654559a..f9656aaa31 100644 --- a/test/x509_load_cert_file_test.c +++ b/test/x509_load_cert_file_test.c @@ -198,6 +198,38 @@ err: return ret; } +static int test_x509_get1_objects_mfail(void) +{ + X509 *cert1 = NULL, *cert2 = NULL; + X509_STORE *store = NULL; + STACK_OF(X509_OBJECT) *objs = NULL; + int ret = 0; + + if (!TEST_ptr(cert1 = X509_from_strings(cn_cert1)) + || !TEST_ptr(cert2 = X509_from_strings(cn_cert2))) + goto err; + + store = X509_STORE_new(); + if (!TEST_ptr(store)) + goto err; + if (!TEST_true(X509_STORE_add_cert(store, cert1)) + || !TEST_true(X509_STORE_add_cert(store, cert2))) + goto err; + + MFAIL_start(); + objs = X509_STORE_get1_objects(store); + MFAIL_end(); + + ret = (objs != NULL); + +err: + sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free); + X509_STORE_free(store); + X509_free(cert1); + X509_free(cert2); + return ret; +} + OPT_TEST_DECLARE_USAGE("cert.pem [crl.pem]\n") int setup_tests(void) @@ -216,6 +248,7 @@ int setup_tests(void) ADD_TEST(test_load_cert_file); ADD_TEST(test_load_same_cn_certs); ADD_MFAIL_TEST(test_x509_store_add_mfail); + ADD_MFAIL_TEST(test_x509_get1_objects_mfail); return 1; } From 46040a298884aba27617f9697a70ff725d10fd52 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 6 Jul 2026 11:30:17 +0200 Subject: [PATCH 167/349] quic: do not hard fail mfail test for old fips providers This is because ML-KEM change from #31432 is not backported there. Reviewed-by: Tomas Mraz Reviewed-by: Paul Dale Reviewed-by: Neil Horman MergeDate: Tue Jul 7 16:36:07 2026 (Merged from https://github.com/openssl/openssl/pull/31870) --- test/quicapitest.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/quicapitest.c b/test/quicapitest.c index 271778f6b3..dd574b56fc 100644 --- a/test/quicapitest.c +++ b/test/quicapitest.c @@ -3643,7 +3643,9 @@ static int test_quic_handshake_multipkt_mfail(void) quic_advance_time(clientssl, serverssl); } if (!TEST_int_lt(i, 10)) { - ret = -1; + ret = is_fips && fips_provider_version_match(libctx, ">=3.5.0 <4.1.0") + ? 0 + : -1; goto err; } From a21fdfc89aa07c62ca642bc729ef0c42e7cb0f7d Mon Sep 17 00:00:00 2001 From: Ingo Franzki Date: Mon, 6 Jul 2026 08:24:42 +0200 Subject: [PATCH 168/349] Fix crash in EVP_MD_CTX_copy_ex on inconsistent context EVP_MD_CTX_copy_ex() might crash on an NULL pointer access when an inconsistent context is copied. This happens when a context is copied where digest is set but algctx is NULL, i.e. due to an incomplete initialization. The copyctx shortcut for cases where the in and out contexts use the exact same digest call the copyctx function attempting to copy the algctx, but it does not check if algctx is NULL on the in or out contexts. Fix this by only taking the copyctx shortcut if algctx is non-NULL on both, in and out. Otherwise use the full copy path which will only duplicate the algctx if it is non-NULL. Closes: https://github.com/openssl/openssl/issues/31831 Signed-off-by: Ingo Franzki Reviewed-by: Neil Horman Reviewed-by: Paul Dale (Merged from https://github.com/openssl/openssl/pull/31867) --- crypto/evp/digest.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crypto/evp/digest.c b/crypto/evp/digest.c index 2cb490b279..61e7dbc4ec 100644 --- a/crypto/evp/digest.c +++ b/crypto/evp/digest.c @@ -494,7 +494,8 @@ int EVP_MD_CTX_copy_ex(EVP_MD_CTX *out, const EVP_MD_CTX *in) return 0; } - if (out->digest == in->digest && in->digest->copyctx != NULL) { + if (out->digest == in->digest && in->digest->copyctx != NULL + && out->algctx != NULL && in->algctx != NULL) { in->digest->copyctx(out->algctx, in->algctx); From b5b34d32cb98341434dd8b88a13182b0131d9344 Mon Sep 17 00:00:00 2001 From: olszomal Date: Tue, 5 Aug 2025 12:00:03 +0200 Subject: [PATCH 169/349] BIO: avoid returning internal FILE * with UPLINK-enabled builds on Windows On Windows with UPLINK enabled, BIO_get_fp() may return a FILE * pointer incompatible with the C runtime. Ensure that it returns NULL instead, preventing undefined behavior in applications. Update the documentation to include the missing return type for BIO_[gs]et_fp() and remove the mention that BIO_get_fp() never returns 0, as it does so now when NULL fp is returned. Signed-off-by: olszomal Reviewed-by: Dmitry Belyavskiy Reviewed-by: Eugene Syromiatnikov MergeDate: Wed Jul 8 09:47:40 2026 (Merged from https://github.com/openssl/openssl/pull/28172) --- crypto/bio/bss_file.c | 8 +++++++- doc/man3/BIO_s_file.pod | 7 +++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/crypto/bio/bss_file.c b/crypto/bio/bss_file.c index 11e96a23d7..7aed585342 100644 --- a/crypto/bio/bss_file.c +++ b/crypto/bio/bss_file.c @@ -332,7 +332,13 @@ static long file_ctrl(BIO *b, int cmd, long num, void *ptr) /* the ptr parameter is actually a FILE ** in this case. */ if (ptr != NULL) { fpp = (FILE **)ptr; - *fpp = (FILE *)b->ptr; + if (BIO_FLAGS_UPLINK_INTERNAL == 0 + || b->flags & BIO_FLAGS_UPLINK_INTERNAL) { + *fpp = (FILE *)b->ptr; + } else { /* avoid returning internal FILE * to the app */ + *fpp = NULL; + ret = 0; + } } break; case BIO_CTRL_GET_CLOSE: diff --git a/doc/man3/BIO_s_file.pod b/doc/man3/BIO_s_file.pod index 5dcd4bbbca..6cd1da02a3 100644 --- a/doc/man3/BIO_s_file.pod +++ b/doc/man3/BIO_s_file.pod @@ -14,8 +14,8 @@ BIO_rw_filename - FILE bio BIO *BIO_new_file(const char *filename, const char *mode); BIO *BIO_new_fp(FILE *stream, int flags); - BIO_set_fp(BIO *b, FILE *fp, int flags); - BIO_get_fp(BIO *b, FILE **fpp); + long BIO_set_fp(BIO *b, FILE *fp, int flags); + long BIO_get_fp(BIO *b, FILE **fpp); int BIO_read_filename(BIO *b, char *name); int BIO_write_filename(BIO *b, char *name); @@ -87,8 +87,7 @@ BIO_s_file() returns the file BIO method. BIO_new_file() and BIO_new_fp() return a file BIO or NULL if an error occurred. -BIO_set_fp() and BIO_get_fp() return 1 for success or <=0 for failure -(although the current implementation never return 0). +BIO_set_fp() and BIO_get_fp() return 1 for success or <=0 for failure. BIO_seek() returns 0 for success or negative values for failure. From d2a9af2144ac2938619e44af3eaeaed3ea49cb05 Mon Sep 17 00:00:00 2001 From: olszomal Date: Fri, 8 Aug 2025 12:06:36 +0200 Subject: [PATCH 170/349] apps: adjust stat usage to account for uplink Call stat() instead of fstat() when the FILE pointer provided by BIO_get_fp() is unavailable (as it may be the case in case of UPLINK builds). Signed-off-by: olszomal Reviewed-by: Dmitry Belyavskiy Reviewed-by: Eugene Syromiatnikov MergeDate: Wed Jul 8 09:47:42 2026 (Merged from https://github.com/openssl/openssl/pull/28172) --- apps/lib/apps.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/lib/apps.c b/apps/lib/apps.c index 2c55c0af21..128c40d086 100644 --- a/apps/lib/apps.c +++ b/apps/lib/apps.c @@ -1849,11 +1849,18 @@ CA_DB *load_index(const char *dbfile, DB_ATTR *db_attr) goto err; #ifndef OPENSSL_NO_POSIX_IO - BIO_get_fp(in, &dbfp); - if (fstat(fileno(dbfp), &dbst) == -1) { - ERR_raise_data(ERR_LIB_SYS, errno, - "calling fstat(%s)", dbfile); - goto err; + if (BIO_get_fp(in, &dbfp) > 0 && dbfp != NULL) { + if (fstat(fileno(dbfp), &dbst) == -1) { + ERR_raise_data(ERR_LIB_SYS, errno, + "calling fstat(%s)", dbfile); + goto err; + } + } else { + if (stat(dbfile, &dbst) == -1) { + ERR_raise_data(ERR_LIB_SYS, errno, + "calling stat(%s)", dbfile); + goto err; + } } #endif From 8c7e3f217cf104fb3bbfc903e5619c17e79b8393 Mon Sep 17 00:00:00 2001 From: olszomal Date: Wed, 8 Jul 2026 11:42:37 +0200 Subject: [PATCH 171/349] apps/ocsp.c: guard index_changed() with OPENSSL_NO_POSIX_IO, not HTTP_DAEMON Replace #ifdef HTTP_DAEMON with #ifndef OPENSSL_NO_POSIX_IO for code that only requires POSIX {,f}stat(), improving portability. Retain syslog() call only when HTTP_DAEMON is defined, use BIO_printf(bio_err) otherwise. Reviewed-by: Dmitry Belyavskiy Reviewed-by: Eugene Syromiatnikov MergeDate: Wed Jul 8 09:47:43 2026 (Merged from https://github.com/openssl/openssl/pull/28172) --- apps/ocsp.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/ocsp.c b/apps/ocsp.c index 2293185daf..59d33e90f2 100644 --- a/apps/ocsp.c +++ b/apps/ocsp.c @@ -74,7 +74,7 @@ static int do_responder(OCSP_REQUEST **preq, BIO **pcbio, BIO *acbio, static int send_ocsp_response(BIO *cbio, const OCSP_RESPONSE *resp); static char *prog; -#ifdef HTTP_DAEMON +#ifndef OPENSSL_NO_POSIX_IO static int index_changed(CA_DB *); #endif @@ -680,7 +680,7 @@ int ocsp_main(int argc, char **argv) redo_accept: if (acbio != NULL) { -#ifdef HTTP_DAEMON +#ifndef OPENSSL_NO_POSIX_IO if (index_changed(rdb)) { CA_DB *newrdb = load_index(ridx_filename, NULL); @@ -926,7 +926,7 @@ end: return ret; } -#ifdef HTTP_DAEMON +#ifndef OPENSSL_NO_POSIX_IO static int index_changed(CA_DB *rdb) { @@ -937,7 +937,11 @@ static int index_changed(CA_DB *rdb) || rdb->dbst.st_ctime != sb.st_ctime || rdb->dbst.st_ino != sb.st_ino || rdb->dbst.st_dev != sb.st_dev) { +#ifdef HTTP_DAEMON syslog(LOG_INFO, "index file changed, reloading"); +#else + BIO_printf(bio_err, "%s: index file changed, reloading\n", prog); +#endif return 1; } } From df5f836f641bcf7a6820531ae586e92243f2ff2a Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 4 Jul 2026 09:36:17 +0200 Subject: [PATCH 172/349] Eliminate/fix repeated word usage Found by running the checkpatch.pl Linux script to enforce coding style. Reviewed-by: Kurt Roeckx Reviewed-by: Neil Horman Reviewed-by: Norbert Pocs Reviewed-by: Eugene Syromiatnikov MergeDate: Wed Jul 8 10:11:11 2026 (Merged from https://github.com/openssl/openssl/pull/31860) --- apps/speed.c | 2 +- crypto/ml_kem/ml_kem.c | 2 +- crypto/rand/rand_uniform.c | 4 ++-- crypto/rsa/rsa_backend.c | 2 +- crypto/slh_dsa/slh_fors.c | 2 +- crypto/x509/v3_akid.c | 2 +- crypto/x509/v3_bitst.c | 2 +- demos/http3/ossl-nghttp3.c | 2 +- providers/fips/self_test.c | 2 +- ssl/quic/quic_impl.c | 2 +- ssl/quic/quic_txp.c | 2 +- test/asn1_internal_test.c | 2 +- test/danetest.c | 2 +- 13 files changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/speed.c b/apps/speed.c index aa10d32bd5..c63add0d8d 100644 --- a/apps/speed.c +++ b/apps/speed.c @@ -2945,7 +2945,7 @@ int speed_main(int argc, char **argv) &outlen, loopargs[k].buf, lengths[testnum])) { BIO_puts(bio_err, - "\nFailed to to encrypt the data\n"); + "\nFailed to encrypt the data\n"); dofail(); exit(1); } diff --git a/crypto/ml_kem/ml_kem.c b/crypto/ml_kem/ml_kem.c index 2fc0e5a980..27fa15e246 100644 --- a/crypto/ml_kem/ml_kem.c +++ b/crypto/ml_kem/ml_kem.c @@ -1148,7 +1148,7 @@ static __owur int gencbd_vector_ntt(scalar *out, CBD_FUNC cbd, uint8_t *counter, * |A| (our key->m, with the public key holding an expanded (16-bit per scalar * coefficient) key->t vector). * - * Caller passes storage in |tmp| for for two temporary vectors. + * Caller passes storage in |tmp| for two temporary vectors. */ static __owur int encrypt_cpa(uint8_t out[ML_KEM_SHARED_SECRET_BYTES], const uint8_t message[DEGREE / 8], diff --git a/crypto/rand/rand_uniform.c b/crypto/rand/rand_uniform.c index 877150af4e..0b7f7a3c0a 100644 --- a/crypto/rand/rand_uniform.c +++ b/crypto/rand/rand_uniform.c @@ -47,7 +47,7 @@ uint32_t ossl_rand_uniform_uint32(OSSL_LIB_CTX *ctx, uint32_t upper, int *err) * We are generating a fixed point number on the interval [0, 1). * Multiplying this by the range gives us a number on [0, upper). * The high word of the multiplication result represents the integral - * part we want. The lower word is the fractional part. We can early exit if + * part we want. The lower word is the fractional part. We can early exit * if the fractional part is small enough that no carry from the next lower * word can cause an overflow and carry into the integer part. This * happens when the fractional part is bounded by 2^32 - upper which @@ -69,7 +69,7 @@ uint32_t ossl_rand_uniform_uint32(OSSL_LIB_CTX *ctx, uint32_t upper, int *err) * repeat the process with the next lower word. * * Each *bit* of randomness has a probability of one half of terminating - * this process, so each each word beyond the first has a probability + * this process, so each word beyond the first has a probability * of 2^-32 of not terminating the process. That is, we're extremely * likely to stop very rapidly. */ diff --git a/crypto/rsa/rsa_backend.c b/crypto/rsa/rsa_backend.c index efacdcf9fe..161d9a9c56 100644 --- a/crypto/rsa/rsa_backend.c +++ b/crypto/rsa/rsa_backend.c @@ -227,7 +227,7 @@ int ossl_rsa_fromdata(RSA *rsa, const OSSL_PARAM params[], int include_private) if (!ossl_rsa_check_factors(rsa)) { ERR_raise_data(ERR_LIB_RSA, RSA_R_INVALID_KEYPAIR, - "RSA factors/exponents are too big for for n-modulus\n"); + "RSA factors/exponents are too big for n-modulus\n"); goto err; } diff --git a/crypto/slh_dsa/slh_fors.c b/crypto/slh_dsa/slh_fors.c index 78587589db..10335cc5df 100644 --- a/crypto/slh_dsa/slh_fors.c +++ b/crypto/slh_dsa/slh_fors.c @@ -156,7 +156,7 @@ int ossl_slh_fors_sign(SLH_DSA_HASH_CTX *ctx, const uint8_t *md, /* * Give each of the k trees a unique range at each level. * e.g. If we have 4096 leaf nodes (2^a = 2^12) for each tree - * tree i will use indexes from 4096 * i + (0..4095) for its bottom level. + * i will use indexes from 4096 * i + (0..4095) for its bottom level. * For the next level up from the bottom there would be 2048 nodes * (so tree i uses indexes 2048 * i + (0...2047) for this level) */ diff --git a/crypto/x509/v3_akid.c b/crypto/x509/v3_akid.c index 9c93e88267..f500165d0d 100644 --- a/crypto/x509/v3_akid.c +++ b/crypto/x509/v3_akid.c @@ -179,7 +179,7 @@ static AUTHORITY_KEYID *v2i_AUTHORITY_KEYID(X509V3_EXT_METHOD *method, /* * The subject key identifier of the issuer cert is acceptable unless * the issuer cert is same as subject cert, but the subject will not - * not be self-signed (i.e. will be signed with a different key). + * be self-signed (i.e. will be signed with a different key). */ i = X509_get_ext_by_NID(issuer_cert, NID_subject_key_identifier, -1); if (i >= 0 && (ext = X509_get_ext(issuer_cert, i)) != NULL diff --git a/crypto/x509/v3_bitst.c b/crypto/x509/v3_bitst.c index 89c3deddd3..1b0204bf75 100644 --- a/crypto/x509/v3_bitst.c +++ b/crypto/x509/v3_bitst.c @@ -52,7 +52,7 @@ STACK_OF(CONF_VALUE) *i2v_ASN1_BIT_STRING(X509V3_EXT_METHOD *method, for (bnam = method->usr_data; bnam->lname; bnam++) { /* * If the bitnumber did not change from the last iteration, this entry - * is an an alias for the previous bit; treat the first result as + * is an alias for the previous bit; treat the first result as * canonical and ignore the rest. */ if (last_seen_bit == bnam->bitnum) diff --git a/demos/http3/ossl-nghttp3.c b/demos/http3/ossl-nghttp3.c index 2461df73c2..e75f226221 100644 --- a/demos/http3/ossl-nghttp3.c +++ b/demos/http3/ossl-nghttp3.c @@ -543,7 +543,7 @@ static void h3_conn_pump_stream(OSSL_DEMO_H3_STREAM *s, void *conn_) break; /* - * This function is confusingly named as it is is named from nghttp3's + * This function is confusingly named as it is named from nghttp3's * 'perspective'; it is used to pass data *into* the HTTP/3 stack which * has been received from the network. */ diff --git a/providers/fips/self_test.c b/providers/fips/self_test.c index 6024395aa0..364fd0ef65 100644 --- a/providers/fips/self_test.c +++ b/providers/fips/self_test.c @@ -344,7 +344,7 @@ int SELF_TEST_post(SELF_TEST_POST_PARAMS *st, void *fips_global, bio_module = (*st->bio_new_file_cb)(st->module_filename, "rb"); /* This section can be called on demand and that could race with deferred - * tests being executed in another thread, so we use use helpers to get + * tests being executed in another thread, so we use helpers to get * proper locking around this critical section */ if (SELF_TEST_lock_deferred(fips_global)) { diff --git a/ssl/quic/quic_impl.c b/ssl/quic/quic_impl.c index 1efc149119..67725bf454 100644 --- a/ssl/quic/quic_impl.c +++ b/ssl/quic/quic_impl.c @@ -4906,7 +4906,7 @@ SSL *ossl_quic_new_from_listener(SSL *ssl, uint64_t flags) ossl_quic_channel_set_msg_callback_arg(qc->ch, ql->obj.ssl.ctx->msg_callback_arg); /* - * We deliberately pass NULL for engine and port, because we don't want to + * We deliberately pass NULL for engine and port, because we don't want * to turn QCSO we create here into an event leader, nor port leader. * Both those roles are occupied already by listener (`ssl`) we use * to create a new QCSO here. diff --git a/ssl/quic/quic_txp.c b/ssl/quic/quic_txp.c index 425f76a003..bd026af3a4 100644 --- a/ssl/quic/quic_txp.c +++ b/ssl/quic/quic_txp.c @@ -1477,7 +1477,7 @@ static int txp_should_try_staging(OSSL_QUIC_TX_PACKETISER *txp, * This is not a major concern for clients, since if a client has a 1-RTT EL * provisioned the server is guaranteed to also have a 1-RTT EL provisioned. * - * TODO(QUIC FUTURE): Revisit this when when have reached a decision on how + * TODO(QUIC FUTURE): Revisit this when we have reached a decision on how * best to implement this */ if (*conn_close_enc_level > enc_level diff --git a/test/asn1_internal_test.c b/test/asn1_internal_test.c index 8edea7ca91..1f6785cda3 100644 --- a/test/asn1_internal_test.c +++ b/test/asn1_internal_test.c @@ -476,7 +476,7 @@ static int posix_time_test(void) /* * Frequently platform conversions can not deal with one second before the - * the Unix epoch, due to inheriting terrible API design and knocking this + * Unix epoch, due to inheriting terrible API design and knocking this * time value out as an error return. * * We should do better. diff --git a/test/danetest.c b/test/danetest.c index 1b0320b28d..f9e30388e1 100644 --- a/test/danetest.c +++ b/test/danetest.c @@ -370,7 +370,7 @@ static int test_tlsafile(SSL_CTX *ctx, const char *base_name, continue; } if (!TEST_int_eq(mdpth, want_depth)) { - TEST_info("In test test %d", testno); + TEST_info("In test %d", testno); ret = 0; } } From 518d66e44d6ffd8fea9344e0ec578c9d5fd8a094 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 4 Jul 2026 11:09:46 +0200 Subject: [PATCH 173/349] Remove parentheses around return arguments Since return is an operator and not a function, parentheses are not required. Found by running the checkpatch.pl Linux script to enforce coding style. Reviewed-by: Kurt Roeckx Reviewed-by: Neil Horman Reviewed-by: Norbert Pocs Reviewed-by: Eugene Syromiatnikov MergeDate: Wed Jul 8 10:11:13 2026 (Merged from https://github.com/openssl/openssl/pull/31860) --- apps/lib/vms_term_sock.c | 2 +- crypto/ec/ec_asn1.c | 2 +- crypto/evp/e_chacha20_poly1305.c | 2 +- crypto/x509/x509_ext.c | 2 +- demos/http3/ossl-nghttp3-demo-server.c | 2 +- test/http_test.c | 2 +- test/quic_client_test.c | 6 +++--- test/tls-provider.c | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/lib/vms_term_sock.c b/apps/lib/vms_term_sock.c index faceb05d01..e60d7f0a1b 100644 --- a/apps/lib/vms_term_sock.c +++ b/apps/lib/vms_term_sock.c @@ -495,7 +495,7 @@ static int CreateSocketPair(int SocketFamily, SocketPair[0] = SockDesc2; SocketPair[1] = socket_fd(TcpDeviceChan); - return (0); + return 0; } /*----------------------------------------------------------------------------*/ diff --git a/crypto/ec/ec_asn1.c b/crypto/ec/ec_asn1.c index b839e9d2a2..cdc1826631 100644 --- a/crypto/ec/ec_asn1.c +++ b/crypto/ec/ec_asn1.c @@ -1084,7 +1084,7 @@ err: OPENSSL_clear_free(priv, privlen); OPENSSL_free(pub); EC_PRIVATEKEY_free(priv_key); - return (ok ? ret : 0); + return ok ? ret : 0; } int i2d_ECParameters(const EC_KEY *a, unsigned char **out) diff --git a/crypto/evp/e_chacha20_poly1305.c b/crypto/evp/e_chacha20_poly1305.c index 628f89bbdc..78dbe45ce4 100644 --- a/crypto/evp/e_chacha20_poly1305.c +++ b/crypto/evp/e_chacha20_poly1305.c @@ -41,7 +41,7 @@ static const EVP_CIPHER chacha20_poly1305 = { const EVP_CIPHER *EVP_chacha20_poly1305(void) { - return (&chacha20_poly1305); + return &chacha20_poly1305; } #endif #else diff --git a/crypto/x509/x509_ext.c b/crypto/x509/x509_ext.c index 3cd4ac51fc..8c9c5d96bc 100644 --- a/crypto/x509/x509_ext.c +++ b/crypto/x509/x509_ext.c @@ -84,7 +84,7 @@ int X509_get_ext_by_OBJ(const X509 *x, const ASN1_OBJECT *obj, int lastpos) int X509_get_ext_by_critical(const X509 *x, int crit, int lastpos) { - return (X509v3_get_ext_by_critical(x->cert_info.extensions, crit, lastpos)); + return X509v3_get_ext_by_critical(x->cert_info.extensions, crit, lastpos); } const X509_EXTENSION *X509_get_ext(const X509 *x, int loc) diff --git a/demos/http3/ossl-nghttp3-demo-server.c b/demos/http3/ossl-nghttp3-demo-server.c index 4529e35268..227ac6e264 100644 --- a/demos/http3/ossl-nghttp3-demo-server.c +++ b/demos/http3/ossl-nghttp3-demo-server.c @@ -1035,7 +1035,7 @@ static int wait_for_activity(SSL *ssl) * "select" (with updated timeouts). */ - return (select(sock + 1, &read_fd, &write_fd, NULL, tvp)); + return select(sock + 1, &read_fd, &write_fd, NULL, tvp); } /* Main loop for server to accept QUIC connections. */ diff --git a/test/http_test.c b/test/http_test.c index 70f48a4607..d122a45426 100644 --- a/test/http_test.c +++ b/test/http_test.c @@ -635,7 +635,7 @@ static int test_hdr_resp_hdr_limit_none(void) static int test_hdr_resp_hdr_limit_short(void) { - return (test_http_resp_hdr_limit(1)); + return test_http_resp_hdr_limit(1); } static int test_hdr_resp_hdr_limit_256(void) diff --git a/test/quic_client_test.c b/test/quic_client_test.c index 9edf3c18d8..90e8498148 100644 --- a/test/quic_client_test.c +++ b/test/quic_client_test.c @@ -172,7 +172,7 @@ err: static int test_quic_client(void) { - return (test_quic_client_ex(INVALID_SOCKET)); + return test_quic_client_ex(INVALID_SOCKET); } static int test_quic_client_connect_first(void) @@ -202,12 +202,12 @@ static int test_quic_client_connect_first(void) close(c_fd); - return (rv); + return rv; err: if (c_fd != INVALID_SOCKET) close(c_fd); - return (0); + return 0; } OPT_TEST_DECLARE_USAGE("certfile privkeyfile\n") diff --git a/test/tls-provider.c b/test/tls-provider.c index 51db8b138e..6652034936 100644 --- a/test/tls-provider.c +++ b/test/tls-provider.c @@ -2598,7 +2598,7 @@ static int xor_get_aid(unsigned char **oidbuf, const char *tls_name) aidlen = i2d_X509_ALGOR(algor, oidbuf); X509_ALGOR_free(algor); - return (aidlen); + return aidlen; } /* From ae6de181711f01db4492987dafb0d778c3d1368e Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 4 Jul 2026 11:23:18 +0200 Subject: [PATCH 174/349] Eliminate repeated semicolons at the end of statements Found by running the checkpatch.pl Linux script to enforce coding style. Reviewed-by: Kurt Roeckx Reviewed-by: Neil Horman Reviewed-by: Norbert Pocs Reviewed-by: Eugene Syromiatnikov MergeDate: Wed Jul 8 10:11:15 2026 (Merged from https://github.com/openssl/openssl/pull/31860) --- ssl/quic/quic_impl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/ssl/quic/quic_impl.c b/ssl/quic/quic_impl.c index 67725bf454..087c0dd582 100644 --- a/ssl/quic/quic_impl.c +++ b/ssl/quic/quic_impl.c @@ -5588,7 +5588,6 @@ long ossl_quic_callback_ctrl(SSL *s, int cmd, void (*fp)(void)) &ctx.qc->obj.ssl); /* This callback also needs to be set on the internal SSL object */ return ssl3_callback_ctrl(ctx.qc->tls, cmd, fp); - ; default: /* Probably a TLS related ctrl. Defer to our internal SSL object */ From 74e781dfece2869191d15b255216d9addc7eac76 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 4 Jul 2026 11:54:56 +0200 Subject: [PATCH 175/349] Eliminate unnecessary type casts of int constants Found by running the checkpatch.pl Linux script to enforce coding style. Reviewed-by: Kurt Roeckx Reviewed-by: Neil Horman Reviewed-by: Norbert Pocs Reviewed-by: Eugene Syromiatnikov MergeDate: Wed Jul 8 10:11:16 2026 (Merged from https://github.com/openssl/openssl/pull/31860) --- test/bftest.c | 4 ++-- test/destest.c | 2 +- test/ideatest.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/bftest.c b/test/bftest.c index 08f974ec9b..3b6d2ee4b6 100644 --- a/test/bftest.c +++ b/test/bftest.c @@ -387,7 +387,7 @@ static int test_bf_cfb64(void) memset(cbc_out, 0, 40); memcpy(iv, cbc_iv, 8); n = 0; - BF_cfb64_encrypt((unsigned char *)cbc_data, cbc_out, (long)13, + BF_cfb64_encrypt((unsigned char *)cbc_data, cbc_out, 13, &key, iv, &n, BF_ENCRYPT); BF_cfb64_encrypt((unsigned char *)&(cbc_data[13]), &(cbc_out[13]), len - 13, &key, iv, &n, BF_ENCRYPT); @@ -419,7 +419,7 @@ static int test_bf_ofb64(void) memset(cbc_out, 0, 40); memcpy(iv, cbc_iv, 8); n = 0; - BF_ofb64_encrypt((unsigned char *)cbc_data, cbc_out, (long)13, &key, iv, + BF_ofb64_encrypt((unsigned char *)cbc_data, cbc_out, 13, &key, iv, &n); BF_ofb64_encrypt((unsigned char *)&(cbc_data[13]), &(cbc_out[13]), len - 13, &key, iv, &n); diff --git a/test/destest.c b/test/destest.c index 26462e3b65..a5fa3b51a5 100644 --- a/test/destest.c +++ b/test/destest.c @@ -604,7 +604,7 @@ static int test_des_ede_cfb64(void) return 0; memcpy(cfb_tmp, cfb_iv, sizeof(cfb_iv)); n = 0; - DES_ede3_cfb64_encrypt(cfb_buf1, cfb_buf2, (long)17, &ks, &ks, &ks, + DES_ede3_cfb64_encrypt(cfb_buf1, cfb_buf2, 17, &ks, &ks, &ks, &cfb_tmp, &n, DES_DECRYPT); DES_ede3_cfb64_encrypt(&cfb_buf1[17], &cfb_buf2[17], sizeof(plain) - 17, &ks, &ks, &ks, &cfb_tmp, &n, DES_DECRYPT); diff --git a/test/ideatest.c b/test/ideatest.c index b426429198..4ad7f67054 100644 --- a/test/ideatest.c +++ b/test/ideatest.c @@ -98,7 +98,7 @@ static int test_idea_cfb64(void) IDEA_set_decrypt_key(&eks, &dks); memcpy(cfb_tmp, cfb_iv, sizeof(cfb_tmp)); n = 0; - IDEA_cfb64_encrypt(plain, cfb_buf1, (long)12, &eks, + IDEA_cfb64_encrypt(plain, cfb_buf1, 12, &eks, cfb_tmp, &n, IDEA_ENCRYPT); IDEA_cfb64_encrypt(&plain[12], &cfb_buf1[12], (long)CFB_TEST_SIZE - 12, &eks, @@ -107,7 +107,7 @@ static int test_idea_cfb64(void) return 0; memcpy(cfb_tmp, cfb_iv, sizeof(cfb_tmp)); n = 0; - IDEA_cfb64_encrypt(cfb_buf1, cfb_buf2, (long)13, &eks, + IDEA_cfb64_encrypt(cfb_buf1, cfb_buf2, 13, &eks, cfb_tmp, &n, IDEA_DECRYPT); IDEA_cfb64_encrypt(&cfb_buf1[13], &cfb_buf2[13], (long)CFB_TEST_SIZE - 13, &eks, From 7812b20ad590c110b0d4118c03f201e925dd43a1 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 4 Jul 2026 11:59:35 +0200 Subject: [PATCH 176/349] Add parentheses around sizeof arguments Found by running the checkpatch.pl Linux script to enforce coding style. Reviewed-by: Kurt Roeckx Reviewed-by: Neil Horman Reviewed-by: Norbert Pocs Reviewed-by: Eugene Syromiatnikov MergeDate: Wed Jul 8 10:11:18 2026 (Merged from https://github.com/openssl/openssl/pull/31860) --- crypto/x509/x509_vpm.c | 4 ++-- test/asn1_internal_test.c | 4 ++-- test/pkcs7_test.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crypto/x509/x509_vpm.c b/crypto/x509/x509_vpm.c index f0858e357e..44b116923b 100644 --- a/crypto/x509/x509_vpm.c +++ b/crypto/x509/x509_vpm.c @@ -35,7 +35,7 @@ static X509_BUFFER *buffer_from_bytes(const uint8_t *bytes, size_t length) { X509_BUFFER *buf; - if ((buf = OPENSSL_zalloc(sizeof *buf)) != NULL + if ((buf = OPENSSL_zalloc(sizeof(*buf))) != NULL && (buf->data = OPENSSL_memdup(bytes, length)) != NULL) { buf->len = length; } else { @@ -56,7 +56,7 @@ static X509_BUFFER *buffer_from_string(const uint8_t *bytes, size_t length) X509_BUFFER *buf, *ret = NULL; uint8_t *data = NULL; - if ((buf = OPENSSL_zalloc(sizeof *buf)) == NULL) + if ((buf = OPENSSL_zalloc(sizeof(*buf))) == NULL) goto err; if ((data = (uint8_t *)OPENSSL_strndup((char *)bytes, length)) == NULL) diff --git a/test/asn1_internal_test.c b/test/asn1_internal_test.c index 1f6785cda3..469daafc4c 100644 --- a/test/asn1_internal_test.c +++ b/test/asn1_internal_test.c @@ -185,9 +185,9 @@ static int test_unicode_range(void) "\xff\xff\xff\xff"; int ok = 1; - if (!test_unicode(univ_ok, sizeof univ_ok - 1, V_ASN1_UTF8STRING)) + if (!test_unicode(univ_ok, sizeof(univ_ok) - 1, V_ASN1_UTF8STRING)) ok = 0; - if (!test_unicode(univ_bad, sizeof univ_bad - 1, -1)) + if (!test_unicode(univ_bad, sizeof(univ_bad) - 1, -1)) ok = 0; return ok; } diff --git a/test/pkcs7_test.c b/test/pkcs7_test.c index 3fe68f60aa..8514101121 100644 --- a/test/pkcs7_test.c +++ b/test/pkcs7_test.c @@ -385,10 +385,10 @@ static int pkcs7_inner_content_verify_test(void) 0x2D, 0x6F, 0x81 }; - if (!TEST_ptr(bio = BIO_new_mem_buf(sig_der, sizeof sig_der))) + if (!TEST_ptr(bio = BIO_new_mem_buf(sig_der, sizeof(sig_der)))) goto end; - ret = TEST_ptr(x509_bio = BIO_new_mem_buf(smroot_der, sizeof smroot_der)) + ret = TEST_ptr(x509_bio = BIO_new_mem_buf(smroot_der, sizeof(smroot_der))) && TEST_ptr(cert = d2i_X509_bio(x509_bio, NULL)) && TEST_int_eq(ERR_peek_error(), 0) && TEST_ptr(store = X509_STORE_new()) From cddeed0734c045e9b7bfa31e6e3a5a41e2bacb7b Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 4 Jul 2026 12:01:33 +0200 Subject: [PATCH 177/349] Move const qualifier after static Found by running the checkpatch.pl Linux script to enforce coding style. Reviewed-by: Kurt Roeckx Reviewed-by: Neil Horman Reviewed-by: Norbert Pocs Reviewed-by: Eugene Syromiatnikov MergeDate: Wed Jul 8 10:11:20 2026 (Merged from https://github.com/openssl/openssl/pull/31860) --- test/igetest.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/igetest.c b/test/igetest.c index d19c116d40..7e7ad0606f 100644 --- a/test/igetest.c +++ b/test/igetest.c @@ -43,7 +43,7 @@ struct ige_test { const int encrypt; }; -static struct ige_test const ige_test_vectors[] = { +static const struct ige_test ige_test_vectors[] = { { { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, /* key */ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, @@ -88,7 +88,7 @@ struct bi_ige_test { const int encrypt; }; -static struct bi_ige_test const bi_ige_test_vectors[] = { +static const struct bi_ige_test bi_ige_test_vectors[] = { { { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, /* key1 */ { 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, From 917ebfca5cd36c8428b8b495456983cf08c22aeb Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 4 Jul 2026 12:03:53 +0200 Subject: [PATCH 178/349] Eliminate unnecessary whitespace before a quoted newline Found by running the checkpatch.pl Linux script to enforce coding style. Reviewed-by: Kurt Roeckx Reviewed-by: Neil Horman Reviewed-by: Norbert Pocs Reviewed-by: Eugene Syromiatnikov MergeDate: Wed Jul 8 10:11:22 2026 (Merged from https://github.com/openssl/openssl/pull/31860) --- apps/s_server.c | 2 +- providers/implementations/kem/template_kem.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/s_server.c b/apps/s_server.c index e1c601589b..4d99e5442b 100644 --- a/apps/s_server.c +++ b/apps/s_server.c @@ -462,7 +462,7 @@ typedef struct tlsextctx_st { static unsigned int ech_print_cb(SSL *s, const char *str) { if (str != NULL) - BIO_printf(bio_s_out, "ECH Server callback printing: \n%s\n", str); + BIO_printf(bio_s_out, "ECH Server callback printing:\n%s\n", str); return 1; } diff --git a/providers/implementations/kem/template_kem.c b/providers/implementations/kem/template_kem.c index 509a31e129..448b1caf4e 100644 --- a/providers/implementations/kem/template_kem.c +++ b/providers/implementations/kem/template_kem.c @@ -166,7 +166,7 @@ static int template_decapsulate(void *vctx, unsigned char *out, size_t *outlen, if (out == NULL) { if (outlen != NULL) - debug_print("decaps outlen set to %zu \n", *outlen); + debug_print("decaps outlen set to %zu\n", *outlen); return 1; } From 51584371a27d2e939babcac3a8fd145c0a2f16ac Mon Sep 17 00:00:00 2001 From: Joe Orton Date: Wed, 1 Jul 2026 17:11:54 +0100 Subject: [PATCH 179/349] doc/man3/ASN1_INTEGER_get_int64.pod: fix a typo CLA: trivial Fixes: 6c5b6cb03566 "ASN1 INTEGER refactor." Reviewed-by: Neil Horman Reviewed-by: Frederik Wedel-Heinen Reviewed-by: Eugene Syromiatnikov MergeDate: Wed Jul 8 10:18:11 2026 (Merged from https://github.com/openssl/openssl/pull/31815) --- doc/man3/ASN1_INTEGER_get_int64.pod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/man3/ASN1_INTEGER_get_int64.pod b/doc/man3/ASN1_INTEGER_get_int64.pod index 4ba6c4c0d7..d25c87a5e2 100644 --- a/doc/man3/ASN1_INTEGER_get_int64.pod +++ b/doc/man3/ASN1_INTEGER_get_int64.pod @@ -108,7 +108,7 @@ B structure respectively or NULL if an error occurs. They will only fail due to a memory allocation error. ASN1_INTEGER_to_BN() and ASN1_ENUMERATED_to_BN() return a B structure -of NULL if an error occurs. They can fail if the passed type is incorrect +or NULL if an error occurs. They can fail if the passed type is incorrect (due to programming error) or due to a memory allocation failure. =head1 SEE ALSO From 1860496d861e9ea92c1ac41ac0543c3f49d2353f Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 4 Jul 2026 10:32:44 +0200 Subject: [PATCH 180/349] Fix typos Found by typos[1]. [1] https://github.com/crate-ci/typos CLA: trivial Reviewed-by: Shane Lontis Reviewed-by: Paul Dale Reviewed-by: Tim Hudson Reviewed-by: Norbert Pocs Reviewed-by: Eugene Syromiatnikov MergeDate: Wed Jul 8 10:46:01 2026 (Merged from https://github.com/openssl/openssl/pull/31858) --- CHANGES.md | 4 ++-- apps/speed.c | 2 +- crypto/provider_core.c | 2 +- crypto/rc4/asm/rc4-md5-x86_64.pl | 2 +- crypto/sha/asm/keccak1600-avx512.pl | 2 +- crypto/sha/keccak1600.c | 2 +- crypto/sm3/asm/sm3-armv8.pl | 2 +- .../passing-algorithmidentifier-parameters.md | 4 ++-- doc/designs/quic-design/quic-ackm.md | 2 +- doc/designs/quic-design/quic-requirements.md | 2 +- doc/internal/man7/VERSION.pod | 2 +- doc/man3/EVP_MAC.pod | 4 ++-- doc/man3/OSSL_CMP_CTX_new.pod | 2 +- doc/man7/ossl-guide-quic-client-non-block.pod | 2 +- doc/man7/ossl-guide-tls-client-non-block.pod | 2 +- doc/man7/ossl-guide-tls-server-block.pod | 2 +- include/internal/quic_lcidm.h | 2 +- include/internal/quic_vlint.h | 2 +- include/internal/quic_wire.h | 2 +- include/internal/zeroization.h | 2 +- include/openssl/x509v3.h.in | 2 +- providers/implementations/signature/eddsa_sig.c | 4 ++-- ssl/quic/quic_wire_pkt.c | 4 ++-- test/bntest.c | 4 ++-- test/evp_extra_test.c | 4 ++-- test/quic_multistream_test.c | 2 +- test/sslbuffertest.c | 2 +- test/threadstest.c | 12 ++++++------ util/perl/OpenSSL/ParseC.pm | 2 +- 29 files changed, 41 insertions(+), 41 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 26ac778f55..da1eeb519a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4103,7 +4103,7 @@ breaking changes, and mappings for the large list of deprecated functions. * Fixed a bug in the function `OCSP_basic_verify` that verifies the signer certificate on an OCSP response. The bug caused the function in the case - where the (non-default) flag OCSP_NOCHECKS is used to return a postivie + where the (non-default) flag OCSP_NOCHECKS is used to return a positive response (meaning a successful verification) even in the case where the response signing certificate fails to verify. @@ -19901,7 +19901,7 @@ s-cbc 3624.96k 5258.21k 5530.91k 5624.30k 5628.26k The new configuration file reading functions are: NCONF_new, NCONF_free, NCONF_load, NCONF_load_fp, NCONF_load_bio, - NCONF_get_section, NCONF_get_string, NCONF_get_numbre + NCONF_get_section, NCONF_get_string, NCONF_get_number NCONF_default, NCONF_WIN32 diff --git a/apps/speed.c b/apps/speed.c index c63add0d8d..b1732744d3 100644 --- a/apps/speed.c +++ b/apps/speed.c @@ -4633,7 +4633,7 @@ static int do_multi(int multi, int size_num) for (n = 0; n < multi; ++n) { while (wait(&status) == -1) if (errno != EINTR) { - BIO_printf(bio_err, "Waitng for child failed with 0x%x\n", + BIO_printf(bio_err, "Waiting for child failed with 0x%x\n", errno); return 1; } diff --git a/crypto/provider_core.c b/crypto/provider_core.c index 6094882947..b408e95e10 100644 --- a/crypto/provider_core.c +++ b/crypto/provider_core.c @@ -1134,7 +1134,7 @@ static int provider_init(OSSL_PROVIDER *prov) prov->error_strings[0].error = ERR_PACK(prov->error_lib, 0, 0); prov->error_strings[0].string = prov->name; /* - * Copy reasonstrings item 0..cnt-1 to prov->error_trings positions + * Copy reasonstrings item 0..cnt-1 to prov->error_strings positions * 1..cnt. */ for (cnt2 = 1; cnt2 <= cnt; cnt2++) { diff --git a/crypto/rc4/asm/rc4-md5-x86_64.pl b/crypto/rc4/asm/rc4-md5-x86_64.pl index f814d6f86f..c4440ab32c 100644 --- a/crypto/rc4/asm/rc4-md5-x86_64.pl +++ b/crypto/rc4/asm/rc4-md5-x86_64.pl @@ -26,7 +26,7 @@ # and Jim Guilford of Intel. MD5 is fresh implementation aiming to # minimize register usage, which was used as "main thread" with RC4 # weaved into it, one RC4 round per one MD5 round. In addition to the -# stiched subroutine the script can generate standalone replacement +# stitched subroutine the script can generate standalone replacement # ossl_md5_block_asm_data_order and RC4. Below are performance numbers in # cycles per processed byte, less is better, for these the standalone # subroutines, sum of them, and stitched one: diff --git a/crypto/sha/asm/keccak1600-avx512.pl b/crypto/sha/asm/keccak1600-avx512.pl index 2a295d1c85..1b40130a6e 100755 --- a/crypto/sha/asm/keccak1600-avx512.pl +++ b/crypto/sha/asm/keccak1600-avx512.pl @@ -22,7 +22,7 @@ # It's impossible to have one that is optimal for every step, hence # it's changing as algorithm progresses. Data is saved in linear order, # but in-register order morphs between rounds. Even rounds take in -# linear layout, and odd rounds - transposed, or "verticaly-shaped"... +# linear layout, and odd rounds - transposed, or "vertically-shaped"... # ######################################################################## # Numbers are cycles per processed byte out of large message. diff --git a/crypto/sha/keccak1600.c b/crypto/sha/keccak1600.c index 59e688ce43..26d75f1b8e 100644 --- a/crypto/sha/keccak1600.c +++ b/crypto/sha/keccak1600.c @@ -111,7 +111,7 @@ static const uint64_t iotas[] = { /* * This is straightforward or "maximum clarity" implementation aiming * to resemble section 3.2 of the FIPS PUB 202 "SHA-3 Standard: - * Permutation-Based Hash and Extendible-Output Functions" as much as + * Permutation-Based Hash and Extendable-Output Functions" as much as * possible. With one caveat. Because of the way C stores matrices, * references to A[x,y] in the specification are presented as A[y][x]. * Implementation unrolls inner x-loops so that modulo 5 operations are diff --git a/crypto/sm3/asm/sm3-armv8.pl b/crypto/sm3/asm/sm3-armv8.pl index e0c33ecb95..6c51df28f8 100644 --- a/crypto/sm3/asm/sm3-armv8.pl +++ b/crypto/sm3/asm/sm3-armv8.pl @@ -51,7 +51,7 @@ $code.=<<___; ___ } -# A round of compresson function +# A round of compression function # Input: # ab - choose instruction among sm3tt1a, sm3tt1b, sm3tt2a, sm3tt2b # vstate0 - vstate1, store digest status(A - H) diff --git a/doc/designs/passing-algorithmidentifier-parameters.md b/doc/designs/passing-algorithmidentifier-parameters.md index 9c5669e86b..0e6126b056 100644 --- a/doc/designs/passing-algorithmidentifier-parameters.md +++ b/doc/designs/passing-algorithmidentifier-parameters.md @@ -129,10 +129,10 @@ at all when such parameter data needs to be passed. Background / tl;dr ------------------ -### AlgorithmIdenfier parameter and how it's used +### AlgorithmIdentifier parameter and how it's used OpenSSL has historically done a few tricks to not have to pass -AlgorithmIdenfier parameter data to the backend implementations of +AlgorithmIdentifier parameter data to the backend implementations of cryptographic operations: - In some cases, they were passed as part of the lower level key structure diff --git a/doc/designs/quic-design/quic-ackm.md b/doc/designs/quic-design/quic-ackm.md index 488fded5e1..38d72aac3c 100644 --- a/doc/designs/quic-design/quic-ackm.md +++ b/doc/designs/quic-design/quic-ackm.md @@ -424,7 +424,7 @@ This should be called for a packet before attempting to process its contents. Failure to do so may may result in processing a duplicated packet in violation of the RFC. -The returrn value of this function transitions from 1 to 0 for a given PN once +The return value of this function transitions from 1 to 0 for a given PN once that PN is passed to ossl_ackm_on_rx_packet, thus this function must be used before calling `ossl_ackm_on_rx_packet`. diff --git a/doc/designs/quic-design/quic-requirements.md b/doc/designs/quic-design/quic-requirements.md index c8aeedc7b7..daeb50db66 100644 --- a/doc/designs/quic-design/quic-requirements.md +++ b/doc/designs/quic-design/quic-requirements.md @@ -60,7 +60,7 @@ and that were specific to QUIC * For the MVP a single interop target (i.e. the server implementation list): - 1. [Cloudfare](https://cloudflare-quic.com/) + 1. [Cloudflare](https://cloudflare-quic.com/) * Testing against other implementations is not a release requirement for the MVP. diff --git a/doc/internal/man7/VERSION.pod b/doc/internal/man7/VERSION.pod index 4bc8ba6b93..8ffd836c13 100644 --- a/doc/internal/man7/VERSION.pod +++ b/doc/internal/man7/VERSION.pod @@ -26,7 +26,7 @@ The keys that are recognised are: The three parts of OpenSSL's 3 numbered version number, MAJOR.MINOR.PATCH. These are used to compose the values for the C macros B, -B, B. +B, B. =item B diff --git a/doc/man3/EVP_MAC.pod b/doc/man3/EVP_MAC.pod index e5b05701f7..1ca411e5d7 100644 --- a/doc/man3/EVP_MAC.pod +++ b/doc/man3/EVP_MAC.pod @@ -147,7 +147,7 @@ as part of this call or separately using EVP_MAC_CTX_set_params(). Providing non-NULL I to this function is equivalent to calling EVP_MAC_CTX_set_params() with those I for the same I beforehand. Note: There are additional requirements for some MAC algorithms during -re-initalization (i.e. calling EVP_MAC_init() on an EVP_MAC after EVP_MAC_final() +re-initialization (i.e. calling EVP_MAC_init() on an EVP_MAC after EVP_MAC_final() has been called on the same object). See the NOTES section below. EVP_MAC_init() should be called before EVP_MAC_update() and EVP_MAC_final(). @@ -352,7 +352,7 @@ The usage of the parameter names "custom", "iv" and "salt" correspond to the names used in the standard where the algorithm was defined. Some MAC algorithms store internal state that cannot be extracted during -re-initalization. For example GMAC cannot extract an B from the +re-initialization. For example GMAC cannot extract an B from the underlying CIPHER context, and so calling EVP_MAC_init() on an EVP_MAC object after EVP_MAC_final() has been called cannot reset its cipher state to what it was when the B was initially generated. For such instances, an diff --git a/doc/man3/OSSL_CMP_CTX_new.pod b/doc/man3/OSSL_CMP_CTX_new.pod index ce8a0b7fc9..9e4cd0c22a 100644 --- a/doc/man3/OSSL_CMP_CTX_new.pod +++ b/doc/man3/OSSL_CMP_CTX_new.pod @@ -846,7 +846,7 @@ Perform a Certification Request transaction, making use of the new credentials: OSSL_CMP_CTX_set1_cert(cmp_ctx, initialCert); OSSL_CMP_CTX_set1_pkey(cmp_ctx, initialKey); - OSSL_CMP_CTX_set0_newPkey(cmp_ctx, 1, curentKey); + OSSL_CMP_CTX_set0_newPkey(cmp_ctx, 1, currentKey); currentCert = OSSL_CMP_exec_CR_ses(cmp_ctx); Perform a Key Update Request, signed using the cert (and key) to be updated: diff --git a/doc/man7/ossl-guide-quic-client-non-block.pod b/doc/man7/ossl-guide-quic-client-non-block.pod index 06a13e36b6..c7f66079d7 100644 --- a/doc/man7/ossl-guide-quic-client-non-block.pod +++ b/doc/man7/ossl-guide-quic-client-non-block.pod @@ -173,7 +173,7 @@ stream but no data has not yet arrived from the peer for that stream). L and L will return 0 to indicate an error and L and L will return 0 or a negative value to indicate -an error. L will return a negative value to incidate an error. +an error. L will return a negative value to indicate an error. In the event of an error an application should call L to find out what type of error has occurred. If the error is non-fatal and can be diff --git a/doc/man7/ossl-guide-tls-client-non-block.pod b/doc/man7/ossl-guide-tls-client-non-block.pod index ee03f6624b..a45086caf6 100644 --- a/doc/man7/ossl-guide-tls-client-non-block.pod +++ b/doc/man7/ossl-guide-tls-client-non-block.pod @@ -140,7 +140,7 @@ from the underlying socket but the data has not yet arrived from the peer). L and L will return 0 to indicate an error and L and L will return 0 or a negative value to indicate -an error. L will return a negative value to incidate an error. +an error. L will return a negative value to indicate an error. In the event of an error an application should call L to find out what type of error has occurred. If the error is non-fatal and can be diff --git a/doc/man7/ossl-guide-tls-server-block.pod b/doc/man7/ossl-guide-tls-server-block.pod index 44ca2d7d8b..f445492282 100644 --- a/doc/man7/ossl-guide-tls-server-block.pod +++ b/doc/man7/ossl-guide-tls-server-block.pod @@ -170,7 +170,7 @@ key agreement, but the certificate exchange is avoided. Most servers, including this one, do not solicit client certificates. We therefore do not need a "trust store" and allow the handshake to complete even when the client does not present a certificate. Note: Even if a client did -present a trusted ceritificate, for it to be useful, the server application +present a trusted certificate, for it to be useful, the server application would still need custom code to use the verified identity to grant nondefault access to that particular client. Some servers grant access to all clients with certificates from a private CA, this then requires processing of diff --git a/include/internal/quic_lcidm.h b/include/internal/quic_lcidm.h index 66421e8ac1..079dacd597 100644 --- a/include/internal/quic_lcidm.h +++ b/include/internal/quic_lcidm.h @@ -254,7 +254,7 @@ int ossl_quic_lcidm_debug_add(QUIC_LCIDM *lcidm, void *opaque, /* * Obtain a local connection id which is not used yet. - * Returns 1 on succes, 0 on failure. + * Returns 1 on success, 0 on failure. */ int ossl_quic_lcidm_get_unused_cid(QUIC_LCIDM *lcidm, QUIC_CONN_ID *cid); diff --git a/include/internal/quic_vlint.h b/include/internal/quic_vlint.h index ff015eb127..c8b056909d 100644 --- a/include/internal/quic_vlint.h +++ b/include/internal/quic_vlint.h @@ -54,7 +54,7 @@ static ossl_unused ossl_inline size_t ossl_quic_vlint_encode_len(uint64_t v) } /* - * This function writes a QUIC varable-length encoded integer to buf. + * This function writes a QUIC variable-length encoded integer to buf. * The smallest usable representation is used. * * It is the caller's responsibility to ensure that the buffer is big enough by diff --git a/include/internal/quic_wire.h b/include/internal/quic_wire.h index f2efabaeba..06ae9ca677 100644 --- a/include/internal/quic_wire.h +++ b/include/internal/quic_wire.h @@ -424,7 +424,7 @@ int ossl_quic_wire_encode_frame_conn_close(WPACKET *pkt, /* * Encodes a QUIC HANDSHAKE_DONE frame to the packet writer. This frame type - * takes no arguiments. + * takes no arguments. */ int ossl_quic_wire_encode_frame_handshake_done(WPACKET *pkt); diff --git a/include/internal/zeroization.h b/include/internal/zeroization.h index be8e18d638..d30baa869a 100644 --- a/include/internal/zeroization.h +++ b/include/internal/zeroization.h @@ -11,7 +11,7 @@ * Utility functions for handling OPENSSL_PEDANTIC_ZEROIZATION. * * ISO 19790:2012/Cor.1:2015 7.9 requires cryptographic module to provide - * methods to zeroise all unproctected security sensitive parameters + * methods to zeroise all unprotected security sensitive parameters * (which includes both Critical/Private and Public security parameters). * * To comply with these (arguably, unnecessarily onerous) requirements, diff --git a/include/openssl/x509v3.h.in b/include/openssl/x509v3.h.in index 8e50d31144..21def64b94 100644 --- a/include/openssl/x509v3.h.in +++ b/include/openssl/x509v3.h.in @@ -933,7 +933,7 @@ DECLARE_ASN1_FUNCTIONS(IPAddressChoice) DECLARE_ASN1_FUNCTIONS(IPAddressFamily) /* - * API tag for elements of the ASIdentifer SEQUENCE. + * API tag for elements of the ASIdentifier SEQUENCE. */ #define V3_ASID_ASNUM 0 #define V3_ASID_RDI 1 diff --git a/providers/implementations/signature/eddsa_sig.c b/providers/implementations/signature/eddsa_sig.c index 9c25866794..74e9fc11ee 100644 --- a/providers/implementations/signature/eddsa_sig.c +++ b/providers/implementations/signature/eddsa_sig.c @@ -551,7 +551,7 @@ static int ed448_sign(void *vpeddsactx, /* * s390x_ed448_digestsign() does not yet support context-strings or * pre-hashing. Fall back to non-accelerated sign if a context-string or - * pre-hasing is provided. + * pre-hashing is provided. */ if (S390X_CAN_SIGN(ED448) && peddsactx->context_string_len == 0 @@ -673,7 +673,7 @@ static int ed448_verify(void *vpeddsactx, /* * s390x_ed448_digestverify() does not yet support context-strings or * pre-hashing. Fall back to non-accelerated verify if a context-string or - * pre-hasing is provided. + * pre-hashing is provided. */ if (S390X_CAN_SIGN(ED448) && peddsactx->context_string_len == 0 diff --git a/ssl/quic/quic_wire_pkt.c b/ssl/quic/quic_wire_pkt.c index 8a65ac9869..368a585072 100644 --- a/ssl/quic/quic_wire_pkt.c +++ b/ssl/quic/quic_wire_pkt.c @@ -870,7 +870,7 @@ int ossl_quic_calculate_retry_integrity_tag(OSSL_LIB_CTX *libctx, EVP_CIPHER_CTX *cctx = NULL; int ok = 0, l = 0, l2 = 0, wpkt_valid = 0; WPACKET wpkt; - /* Worst case length of the Retry Psuedo-Packet header is 68 bytes. */ + /* Worst case length of the Retry Pseudo-Packet header is 68 bytes. */ unsigned char buf[128]; QUIC_PKT_HDR hdr2; size_t hdr_enc_len = 0; @@ -892,7 +892,7 @@ int ossl_quic_calculate_retry_integrity_tag(OSSL_LIB_CTX *libctx, hdr2 = *hdr; hdr2.len = 0; - /* Assemble retry psuedo-packet. */ + /* Assemble retry pseudo-packet. */ if (!WPACKET_init_static_len(&wpkt, buf, sizeof(buf), 0)) { ERR_raise(ERR_LIB_SSL, ERR_R_CRYPTO_LIB); goto err; diff --git a/test/bntest.c b/test/bntest.c index cb5b9278af..77f07bc630 100644 --- a/test/bntest.c +++ b/test/bntest.c @@ -1238,7 +1238,7 @@ static int file_sum(STANZA *s) /* * Test that the functions work when |r| and |a| point to the same BIGNUM, * or when |r| and |b| point to the same BIGNUM. - * There is no test for all of |r|, |a|, and |b| pointint to the same BIGNUM. + * There is no test for all of |r|, |a|, and |b| pointing to the same BIGNUM. */ if (!TEST_true(BN_copy(ret, a)) || !TEST_true(BN_add(ret, ret, b)) @@ -1277,7 +1277,7 @@ static int file_sum(STANZA *s) /* * Test that the functions work when |r| and |a| point to the same * BIGNUM, or when |r| and |b| point to the same BIGNUM. - * There is no test for all of |r|, |a|, and |b| pointint to the same + * There is no test for all of |r|, |a|, and |b| pointing to the same * BIGNUM. */ if (!TEST_true(BN_copy(ret, a)) diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c index cb18ead4db..20d9496116 100644 --- a/test/evp_extra_test.c +++ b/test/evp_extra_test.c @@ -2411,7 +2411,7 @@ static struct ec_der_pub_keys_st { * Tests the range of the decoded EC char2 public point. * See ec_GF2m_simple_oct2point(). */ -static int test_invalide_ec_char2_pub_range_decode(int id) +static int test_invalid_ec_char2_pub_range_decode(int id) { int ret = 0; EVP_PKEY *pkey; @@ -8994,7 +8994,7 @@ int setup_tests(void) #ifndef OPENSSL_NO_EC ADD_TEST(test_X509_PUBKEY_inplace); ADD_TEST(test_X509_PUBKEY_dup); - ADD_ALL_TESTS(test_invalide_ec_char2_pub_range_decode, + ADD_ALL_TESTS(test_invalid_ec_char2_pub_range_decode, OSSL_NELEM(ec_der_pub_keys)); #endif #ifndef OPENSSL_NO_DSA diff --git a/test/quic_multistream_test.c b/test/quic_multistream_test.c index ea77aac694..1a70b07cf6 100644 --- a/test/quic_multistream_test.c +++ b/test/quic_multistream_test.c @@ -4559,7 +4559,7 @@ static int script_68_inject_handshake(struct helper *h, unsigned char *msg, return 1; } -/* Send a CerticateRequest message post-handshake */ +/* Send a CertificateRequest message post-handshake */ static const struct script_op script_68[] = { OP_S_SET_INJECT_HANDSHAKE(script_68_inject_handshake), OP_C_SET_ALPN("ossltest"), diff --git a/test/sslbuffertest.c b/test/sslbuffertest.c index c7bcbe66e9..13b6f6b404 100644 --- a/test/sslbuffertest.c +++ b/test/sslbuffertest.c @@ -177,7 +177,7 @@ end: * Test 1: Attempt to free buffers after only a partial record header has been * received * Test 2: Attempt to free buffers after a full record header but no record body - * Test 3: Attempt to free buffers after a full record hedaer and partial record + * Test 3: Attempt to free buffers after a full record header and partial record * body */ static int test_free_buffers(int test) diff --git a/test/threadstest.c b/test/threadstest.c index 6915df3a3e..50a5d90340 100644 --- a/test/threadstest.c +++ b/test/threadstest.c @@ -760,7 +760,7 @@ static OSSL_PROVIDER *multi_provider[MAXIMUM_PROVIDERS + 1]; static size_t multi_num_threads; static thread_t multi_threads[MAXIMUM_THREADS]; -static void multi_intialise(void) +static void multi_initialise(void) { multi_success = 1; multi_libctx = NULL; @@ -789,7 +789,7 @@ static void thead_teardown_libctx(void) for (p = multi_provider; *p != NULL; p++) OSSL_PROVIDER_unload(*p); OSSL_LIB_CTX_free(multi_libctx); - multi_intialise(); + multi_initialise(); } static int thread_setup_libctx(int libctx, const char *providers[]) @@ -840,7 +840,7 @@ static int thread_run_test(void (*main_func)(void), { int testresult = 0; - multi_intialise(); + multi_initialise(); if (!thread_setup_libctx(libctx, providers) || !start_threads(num_threads, thread_func)) goto err; @@ -1019,7 +1019,7 @@ static int test_multi_shared_pkey_common(void (*worker)(void)) { int testresult = 0; - multi_intialise(); + multi_initialise(); if (!thread_setup_libctx(1, do_fips ? fips_and_default_providers : default_provider) || !TEST_ptr(shared_evp_pkey = load_pkey_pem(privkey, multi_libctx)) || !start_threads(1, &thread_shared_evp_pkey) @@ -1071,7 +1071,7 @@ static int test_multi_shared_pkey_release(void) int testresult = 0; size_t i = 1; - multi_intialise(); + multi_initialise(); shared_evp_pkey = NULL; if (!thread_setup_libctx(1, do_fips ? fips_and_default_providers : default_provider) || !TEST_ptr(shared_evp_pkey = load_pkey_pem(privkey, multi_libctx))) @@ -1104,7 +1104,7 @@ static int test_multi_load_unload_provider(void) OSSL_PROVIDER *prov = NULL; int testresult = 0; - multi_intialise(); + multi_initialise(); if (!thread_setup_libctx(1, NULL) || !TEST_ptr(prov = OSSL_PROVIDER_load(multi_libctx, "default")) || !TEST_ptr(sha256 = EVP_MD_fetch(multi_libctx, "SHA2-256", NULL)) diff --git a/util/perl/OpenSSL/ParseC.pm b/util/perl/OpenSSL/ParseC.pm index c2546280c8..6249704a23 100644 --- a/util/perl/OpenSSL/ParseC.pm +++ b/util/perl/OpenSSL/ParseC.pm @@ -62,7 +62,7 @@ my @opensslcpphandlers = ( ################################################################## # OpenSSL CPP specials # - # These are used to convert certain pre-precessor expressions into + # These are used to convert certain pre-processor expressions into # others that @cpphandlers have a better chance to understand. # This changes any OPENSSL_NO_DEPRECATED_x_y[_z] check to a check of From 69cafbc498afa872f4023af2e9d0978219abd04f Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Fri, 3 Jul 2026 14:11:30 +0300 Subject: [PATCH 181/349] util/mkinstallvars.pl: Suppress more debug logs Commit aa4b47483f41 "Fix util/mkinstallvars.pl to treat LIBDIR and libdir correctly" added more logs while bc44134c32b9 "Configure: Remove extensive debug output by default" was under review, so those were missed. Complements: bc44134c32b9 "Configure: Remove extensive debug output by default" Reviewed-by: Eugene Syromiatnikov Reviewed-by: Paul Dale MergeDate: Wed Jul 8 11:03:11 2026 (Merged from https://github.com/openssl/openssl/pull/31843) --- util/mkinstallvars.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/mkinstallvars.pl b/util/mkinstallvars.pl index 578d344f4e..09924901ce 100644 --- a/util/mkinstallvars.pl +++ b/util/mkinstallvars.pl @@ -54,9 +54,9 @@ foreach (@ARGV) { # the former doesn't have a value, we give it the latter's value, and rely # on mechanisms further down to do the rest of the processing. # If they're both empty, it's still fine. -print STDERR "DEBUG: LIBDIR = $values{LIBDIR}->[0], libdir = $values{libdir}->[0] => "; +print STDERR "DEBUG: LIBDIR = $values{LIBDIR}->[0], libdir = $values{libdir}->[0] => " if $debug; $values{LIBDIR}->[0] = $values{libdir}->[0] unless $values{LIBDIR}->[0]; -print STDERR "LIBDIR = $values{LIBDIR}->[0]\n"; +print STDERR "LIBDIR = $values{LIBDIR}->[0]\n" if $debug; # warn if there are missing values, and also if there are unexpected values foreach my $k (sort keys %all) { From f167d0d2e93b39186fcff1d003f7301f14ca0d36 Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Thu, 2 Jul 2026 20:58:32 +0200 Subject: [PATCH 182/349] README.md: update web links to man pages Add missing OpenSSL 4.0, remove EOL branches. Reviewed-by: Paul Dale Reviewed-by: Dmitry Belyavskiy Reviewed-by: Eugene Syromiatnikov MergeDate: Wed Jul 8 11:09:16 2026 (Merged from https://github.com/openssl/openssl/pull/31839) --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 28e2a5e51a..2701f9004b 100644 --- a/README.md +++ b/README.md @@ -150,11 +150,10 @@ The manual pages for the master branch and all current stable releases are available online. - [OpenSSL master](https://docs.openssl.org/master/) +- [OpenSSL 4.0](https://docs.openssl.org/4.0/) - [OpenSSL 3.6](https://docs.openssl.org/3.6/) - [OpenSSL 3.5](https://docs.openssl.org/3.5/) - [OpenSSL 3.4](https://docs.openssl.org/3.4/) -- [OpenSSL 3.3](https://docs.openssl.org/3.3/) -- [OpenSSL 3.2](https://docs.openssl.org/3.2/) - [OpenSSL 3.0](https://docs.openssl.org/3.0/) Demos From d9671f4526a01eec2ea221d3c5dea2b84423c95a Mon Sep 17 00:00:00 2001 From: Frederik Wedel-Heinen Date: Thu, 25 Jun 2026 14:58:29 +0200 Subject: [PATCH 183/349] Remove unused source files poly1305_ieee754.c and poly1305_base2_44.c Reviewed-by: Daniel Kubec Reviewed-by: Milan Broz Reviewed-by: Eugene Syromiatnikov MergeDate: Wed Jul 8 11:16:22 2026 (Merged from https://github.com/openssl/openssl/pull/31737) --- crypto/poly1305/poly1305_base2_44.c | 164 ---------- crypto/poly1305/poly1305_ieee754.c | 485 ---------------------------- 2 files changed, 649 deletions(-) delete mode 100644 crypto/poly1305/poly1305_base2_44.c delete mode 100644 crypto/poly1305/poly1305_ieee754.c diff --git a/crypto/poly1305/poly1305_base2_44.c b/crypto/poly1305/poly1305_base2_44.c deleted file mode 100644 index e64f5294d8..0000000000 --- a/crypto/poly1305/poly1305_base2_44.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright 2016-2021 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/* - * This module is meant to be used as template for base 2^44 assembly - * implementation[s]. On side note compiler-generated code is not - * slower than compiler-generated base 2^64 code on [high-end] x86_64, - * even though amount of multiplications is 50% higher. Go figure... - */ -#include -#include - -typedef uint128_t u128; - -typedef struct { - uint64_t h[3]; - uint64_t s[2]; - uint64_t r[3]; -} poly1305_internal; - -#define POLY1305_BLOCK_SIZE 16 - -/* pick 64-bit unsigned integer in little endian order */ -static uint64_t U8TOU64(const unsigned char *p) -{ - return (((uint64_t)(p[0] & 0xff)) | ((uint64_t)(p[1] & 0xff) << 8) | ((uint64_t)(p[2] & 0xff) << 16) | ((uint64_t)(p[3] & 0xff) << 24) | ((uint64_t)(p[4] & 0xff) << 32) | ((uint64_t)(p[5] & 0xff) << 40) | ((uint64_t)(p[6] & 0xff) << 48) | ((uint64_t)(p[7] & 0xff) << 56)); -} - -/* store a 64-bit unsigned integer in little endian */ -static void U64TO8(unsigned char *p, uint64_t v) -{ - p[0] = (unsigned char)((v) & 0xff); - p[1] = (unsigned char)((v >> 8) & 0xff); - p[2] = (unsigned char)((v >> 16) & 0xff); - p[3] = (unsigned char)((v >> 24) & 0xff); - p[4] = (unsigned char)((v >> 32) & 0xff); - p[5] = (unsigned char)((v >> 40) & 0xff); - p[6] = (unsigned char)((v >> 48) & 0xff); - p[7] = (unsigned char)((v >> 56) & 0xff); -} - -int poly1305_init(void *ctx, const unsigned char key[16]) -{ - poly1305_internal *st = (poly1305_internal *)ctx; - uint64_t r0, r1; - - /* h = 0 */ - st->h[0] = 0; - st->h[1] = 0; - st->h[2] = 0; - - r0 = U8TOU64(&key[0]) & 0x0ffffffc0fffffff; - r1 = U8TOU64(&key[8]) & 0x0ffffffc0ffffffc; - - /* break r1:r0 to three 44-bit digits, masks are 1<<44-1 */ - st->r[0] = r0 & 0x0fffffffffff; - st->r[1] = ((r0 >> 44) | (r1 << 20)) & 0x0fffffffffff; - st->r[2] = (r1 >> 24); - - st->s[0] = (st->r[1] + (st->r[1] << 2)) << 2; - st->s[1] = (st->r[2] + (st->r[2] << 2)) << 2; - - return 0; -} - -void poly1305_blocks(void *ctx, const unsigned char *inp, size_t len, - uint32_t padbit) -{ - poly1305_internal *st = (poly1305_internal *)ctx; - uint64_t r0, r1, r2; - uint64_t s1, s2; - uint64_t h0, h1, h2, c; - u128 d0, d1, d2; - uint64_t pad = (uint64_t)padbit << 40; - - r0 = st->r[0]; - r1 = st->r[1]; - r2 = st->r[2]; - - s1 = st->s[0]; - s2 = st->s[1]; - - h0 = st->h[0]; - h1 = st->h[1]; - h2 = st->h[2]; - - while (len >= POLY1305_BLOCK_SIZE) { - uint64_t m0, m1; - - m0 = U8TOU64(inp + 0); - m1 = U8TOU64(inp + 8); - - /* h += m[i], m[i] is broken to 44-bit digits */ - h0 += m0 & 0x0fffffffffff; - h1 += ((m0 >> 44) | (m1 << 20)) & 0x0fffffffffff; - h2 += (m1 >> 24) + pad; - - /* h *= r "%" p, where "%" stands for "partial remainder" */ - d0 = ((u128)h0 * r0) + ((u128)h1 * s2) + ((u128)h2 * s1); - d1 = ((u128)h0 * r1) + ((u128)h1 * r0) + ((u128)h2 * s2); - d2 = ((u128)h0 * r2) + ((u128)h1 * r1) + ((u128)h2 * r0); - - /* "lazy" reduction step */ - h0 = (uint64_t)d0 & 0x0fffffffffff; - h1 = (uint64_t)(d1 += (uint64_t)(d0 >> 44)) & 0x0fffffffffff; - h2 = (uint64_t)(d2 += (uint64_t)(d1 >> 44)) & 0x03ffffffffff; /* last 42 bits */ - - c = (d2 >> 42); - h0 += c + (c << 2); - - inp += POLY1305_BLOCK_SIZE; - len -= POLY1305_BLOCK_SIZE; - } - - st->h[0] = h0; - st->h[1] = h1; - st->h[2] = h2; -} - -void poly1305_emit(void *ctx, unsigned char mac[16], const uint32_t nonce[4]) -{ - poly1305_internal *st = (poly1305_internal *)ctx; - uint64_t h0, h1, h2; - uint64_t g0, g1, g2; - u128 t; - uint64_t mask; - - h0 = st->h[0]; - h1 = st->h[1]; - h2 = st->h[2]; - - /* after "lazy" reduction, convert 44+bit digits to 64-bit ones */ - h0 = (uint64_t)(t = (u128)h0 + (h1 << 44)); - h1 >>= 20; - h1 = (uint64_t)(t = (u128)h1 + (h2 << 24) + (t >> 64)); - h2 >>= 40; - h2 += (uint64_t)(t >> 64); - - /* compare to modulus by computing h + -p */ - g0 = (uint64_t)(t = (u128)h0 + 5); - g1 = (uint64_t)(t = (u128)h1 + (t >> 64)); - g2 = h2 + (uint64_t)(t >> 64); - - /* if there was carry into 131st bit, h1:h0 = g1:g0 */ - mask = 0 - (g2 >> 2); - g0 &= mask; - g1 &= mask; - mask = ~mask; - h0 = (h0 & mask) | g0; - h1 = (h1 & mask) | g1; - - /* mac = (h + nonce) % (2^128) */ - h0 = (uint64_t)(t = (u128)h0 + nonce[0] + ((uint64_t)nonce[1] << 32)); - h1 = (uint64_t)(t = (u128)h1 + nonce[2] + ((uint64_t)nonce[3] << 32) + (t >> 64)); - - U64TO8(mac + 0, h0); - U64TO8(mac + 8, h1); -} diff --git a/crypto/poly1305/poly1305_ieee754.c b/crypto/poly1305/poly1305_ieee754.c deleted file mode 100644 index 16d3d0f5a9..0000000000 --- a/crypto/poly1305/poly1305_ieee754.c +++ /dev/null @@ -1,485 +0,0 @@ -/* - * Copyright 2016-2024 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/* - * This module is meant to be used as template for non-x87 floating- - * point assembly modules. The template itself is x86_64-specific - * though, as it was debugged on x86_64. So that implementer would - * have to recognize platform-specific parts, UxTOy and inline asm, - * and act accordingly. - * - * Huh? x86_64-specific code as template for non-x87? Note seven, which - * is not a typo, but reference to 80-bit precision. This module on the - * other hand relies on 64-bit precision operations, which are default - * for x86_64 code. And since we are at it, just for sense of it, - * large-block performance in cycles per processed byte for *this* code - * is: - * gcc-4.8 icc-15.0 clang-3.4(*) - * - * Westmere 4.96 5.09 4.37 - * Sandy Bridge 4.95 4.90 4.17 - * Haswell 4.92 4.87 3.78 - * Bulldozer 4.67 4.49 4.68 - * VIA Nano 7.07 7.05 5.98 - * Silvermont 10.6 9.61 12.6 - * - * (*) clang managed to discover parallelism and deployed SIMD; - * - * And for range of other platforms with unspecified gcc versions: - * - * Freescale e300 12.5 - * PPC74x0 10.8 - * POWER6 4.92 - * POWER7 4.50 - * POWER8 4.10 - * - * z10 11.2 - * z196+ 7.30 - * - * UltraSPARC III 16.0 - * SPARC T4 16.1 - */ - -#if !(defined(__GNUC__) && __GNUC__ >= 2) -#error "this is gcc-specific template" -#endif - -#include -#include - -typedef union { - double d; - uint64_t u; -} elem64; - -#define TWO(p) ((double)(1ULL << (p))) -#define TWO0 TWO(0) -#define TWO32 TWO(32) -#define TWO64 (TWO32 * TWO(32)) -#define TWO96 (TWO64 * TWO(32)) -#define TWO130 (TWO96 * TWO(34)) - -#define EXP(p) ((1023ULL + (p)) << 52) - -#if defined(__x86_64__) || (defined(__PPC__) && defined(__LITTLE_ENDIAN__)) -#define U8TOU32(p) (*(const uint32_t *)(p)) -#define U32TO8(p, v) (*(uint32_t *)(p) = (v)) -#elif defined(__PPC__) || defined(__POWERPC__) -#define U8TOU32(p) ({uint32_t ret; asm ("lwbrx %0,0,%1":"=r"(ret):"b"(p)); ret; }) -#define U32TO8(p, v) asm("stwbrx %0,0,%1" ::"r"(v), "b"(p) : "memory") -#elif defined(__s390x__) -#define U8TOU32(p) ({uint32_t ret; asm ("lrv %0,%1":"=d"(ret):"m"(*(uint32_t *)(p))); ret; }) -#define U32TO8(p, v) asm("strv %1,%0" : "=m"(*(uint32_t *)(p)) : "d"(v)) -#endif - -#ifndef U8TOU32 -#define U8TOU32(p) ((uint32_t)(p)[0] | (uint32_t)(p)[1] << 8 | (uint32_t)(p)[2] << 16 | (uint32_t)(p)[3] << 24) -#endif -#ifndef U32TO8 -#define U32TO8(p, v) ((p)[0] = (uint8_t)(v), (p)[1] = (uint8_t)((v) >> 8), \ - (p)[2] = (uint8_t)((v) >> 16), (p)[3] = (uint8_t)((v) >> 24)) -#endif - -typedef struct { - elem64 h[4]; - double r[8]; - double s[6]; -} poly1305_internal; - -/* "round toward zero (truncate), mask all exceptions" */ -#if defined(__x86_64__) -static const uint32_t mxcsr = 0x7f80; -#elif defined(__PPC__) || defined(__POWERPC__) -static const uint64_t one = 1; -#elif defined(__s390x__) -static const uint32_t fpc = 1; -#elif defined(__sparc__) -static const uint64_t fsr = 1ULL << 30; -#elif defined(__mips__) -static const uint32_t fcsr = 1; -#else -#error "unrecognized platform" -#endif - -int poly1305_init(void *ctx, const unsigned char key[16]) -{ - poly1305_internal *st = (poly1305_internal *)ctx; - elem64 r0, r1, r2, r3; - - /* h = 0, biased */ -#if 0 - st->h[0].d = TWO(52)*TWO0; - st->h[1].d = TWO(52)*TWO32; - st->h[2].d = TWO(52)*TWO64; - st->h[3].d = TWO(52)*TWO96; -#else - st->h[0].u = EXP(52 + 0); - st->h[1].u = EXP(52 + 32); - st->h[2].u = EXP(52 + 64); - st->h[3].u = EXP(52 + 96); -#endif - - if (key) { - /* - * set "truncate" rounding mode - */ -#if defined(__x86_64__) - uint32_t mxcsr_orig; - - asm volatile("stmxcsr %0" : "=m"(mxcsr_orig)); - asm volatile("ldmxcsr %0" ::"m"(mxcsr)); -#elif defined(__PPC__) || defined(__POWERPC__) - double fpscr_orig, fpscr = *(double *)&one; - - asm volatile("mffs %0" : "=f"(fpscr_orig)); - asm volatile("mtfsf 255,%0" ::"f"(fpscr)); -#elif defined(__s390x__) - uint32_t fpc_orig; - - asm volatile("stfpc %0" : "=m"(fpc_orig)); - asm volatile("lfpc %0" ::"m"(fpc)); -#elif defined(__sparc__) - uint64_t fsr_orig; - - asm volatile("stx %%fsr,%0" : "=m"(fsr_orig)); - asm volatile("ldx %0,%%fsr" ::"m"(fsr)); -#elif defined(__mips__) - uint32_t fcsr_orig; - - asm volatile("cfc1 %0,$31" : "=r"(fcsr_orig)); - asm volatile("ctc1 %0,$31" ::"r"(fcsr)); -#endif - - /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */ - r0.u = EXP(52 + 0) | (U8TOU32(&key[0]) & 0x0fffffff); - r1.u = EXP(52 + 32) | (U8TOU32(&key[4]) & 0x0ffffffc); - r2.u = EXP(52 + 64) | (U8TOU32(&key[8]) & 0x0ffffffc); - r3.u = EXP(52 + 96) | (U8TOU32(&key[12]) & 0x0ffffffc); - - st->r[0] = r0.d - TWO(52) * TWO0; - st->r[2] = r1.d - TWO(52) * TWO32; - st->r[4] = r2.d - TWO(52) * TWO64; - st->r[6] = r3.d - TWO(52) * TWO96; - - st->s[0] = st->r[2] * (5.0 / TWO130); - st->s[2] = st->r[4] * (5.0 / TWO130); - st->s[4] = st->r[6] * (5.0 / TWO130); - - /* - * base 2^32 -> base 2^16 - */ - st->r[1] = (st->r[0] + TWO(52) * TWO(16) * TWO0) - TWO(52) * TWO(16) * TWO0; - st->r[0] -= st->r[1]; - - st->r[3] = (st->r[2] + TWO(52) * TWO(16) * TWO32) - TWO(52) * TWO(16) * TWO32; - st->r[2] -= st->r[3]; - - st->r[5] = (st->r[4] + TWO(52) * TWO(16) * TWO64) - TWO(52) * TWO(16) * TWO64; - st->r[4] -= st->r[5]; - - st->r[7] = (st->r[6] + TWO(52) * TWO(16) * TWO96) - TWO(52) * TWO(16) * TWO96; - st->r[6] -= st->r[7]; - - st->s[1] = (st->s[0] + TWO(52) * TWO(16) * TWO0 / TWO96) - TWO(52) * TWO(16) * TWO0 / TWO96; - st->s[0] -= st->s[1]; - - st->s[3] = (st->s[2] + TWO(52) * TWO(16) * TWO32 / TWO96) - TWO(52) * TWO(16) * TWO32 / TWO96; - st->s[2] -= st->s[3]; - - st->s[5] = (st->s[4] + TWO(52) * TWO(16) * TWO64 / TWO96) - TWO(52) * TWO(16) * TWO64 / TWO96; - st->s[4] -= st->s[5]; - - /* - * restore original FPU control register - */ -#if defined(__x86_64__) - asm volatile("ldmxcsr %0" ::"m"(mxcsr_orig)); -#elif defined(__PPC__) || defined(__POWERPC__) - asm volatile("mtfsf 255,%0" ::"f"(fpscr_orig)); -#elif defined(__s390x__) - asm volatile("lfpc %0" ::"m"(fpc_orig)); -#elif defined(__sparc__) - asm volatile("ldx %0,%%fsr" ::"m"(fsr_orig)); -#elif defined(__mips__) - asm volatile("ctc1 %0,$31" ::"r"(fcsr_orig)); -#endif - } - - return 0; -} - -void poly1305_blocks(void *ctx, const unsigned char *inp, size_t len, - int padbit) -{ - poly1305_internal *st = (poly1305_internal *)ctx; - elem64 in0, in1, in2, in3; - uint64_t pad = (uint64_t)padbit << 32; - - double x0, x1, x2, x3; - double h0lo, h0hi, h1lo, h1hi, h2lo, h2hi, h3lo, h3hi; - double c0lo, c0hi, c1lo, c1hi, c2lo, c2hi, c3lo, c3hi; - - const double r0lo = st->r[0]; - const double r0hi = st->r[1]; - const double r1lo = st->r[2]; - const double r1hi = st->r[3]; - const double r2lo = st->r[4]; - const double r2hi = st->r[5]; - const double r3lo = st->r[6]; - const double r3hi = st->r[7]; - - const double s1lo = st->s[0]; - const double s1hi = st->s[1]; - const double s2lo = st->s[2]; - const double s2hi = st->s[3]; - const double s3lo = st->s[4]; - const double s3hi = st->s[5]; - - /* - * set "truncate" rounding mode - */ -#if defined(__x86_64__) - uint32_t mxcsr_orig; - - asm volatile("stmxcsr %0" : "=m"(mxcsr_orig)); - asm volatile("ldmxcsr %0" ::"m"(mxcsr)); -#elif defined(__PPC__) || defined(__POWERPC__) - double fpscr_orig, fpscr = *(double *)&one; - - asm volatile("mffs %0" : "=f"(fpscr_orig)); - asm volatile("mtfsf 255,%0" ::"f"(fpscr)); -#elif defined(__s390x__) - uint32_t fpc_orig; - - asm volatile("stfpc %0" : "=m"(fpc_orig)); - asm volatile("lfpc %0" ::"m"(fpc)); -#elif defined(__sparc__) - uint64_t fsr_orig; - - asm volatile("stx %%fsr,%0" : "=m"(fsr_orig)); - asm volatile("ldx %0,%%fsr" ::"m"(fsr)); -#elif defined(__mips__) - uint32_t fcsr_orig; - - asm volatile("cfc1 %0,$31" : "=r"(fcsr_orig)); - asm volatile("ctc1 %0,$31" ::"r"(fcsr)); -#endif - - /* - * load base 2^32 and de-bias - */ - h0lo = st->h[0].d - TWO(52) * TWO0; - h1lo = st->h[1].d - TWO(52) * TWO32; - h2lo = st->h[2].d - TWO(52) * TWO64; - h3lo = st->h[3].d - TWO(52) * TWO96; - -#ifdef __clang__ - h0hi = 0; - h1hi = 0; - h2hi = 0; - h3hi = 0; -#else - in0.u = EXP(52 + 0) | U8TOU32(&inp[0]); - in1.u = EXP(52 + 32) | U8TOU32(&inp[4]); - in2.u = EXP(52 + 64) | U8TOU32(&inp[8]); - in3.u = EXP(52 + 96) | U8TOU32(&inp[12]) | pad; - - x0 = in0.d - TWO(52) * TWO0; - x1 = in1.d - TWO(52) * TWO32; - x2 = in2.d - TWO(52) * TWO64; - x3 = in3.d - TWO(52) * TWO96; - - x0 += h0lo; - x1 += h1lo; - x2 += h2lo; - x3 += h3lo; - - goto fast_entry; -#endif - - do { - in0.u = EXP(52 + 0) | U8TOU32(&inp[0]); - in1.u = EXP(52 + 32) | U8TOU32(&inp[4]); - in2.u = EXP(52 + 64) | U8TOU32(&inp[8]); - in3.u = EXP(52 + 96) | U8TOU32(&inp[12]) | pad; - - x0 = in0.d - TWO(52) * TWO0; - x1 = in1.d - TWO(52) * TWO32; - x2 = in2.d - TWO(52) * TWO64; - x3 = in3.d - TWO(52) * TWO96; - - /* - * note that there are multiple ways to accumulate input, e.g. - * one can as well accumulate to h0lo-h1lo-h1hi-h2hi... - */ - h0lo += x0; - h0hi += x1; - h2lo += x2; - h2hi += x3; - - /* - * carries that cross 32n-bit (and 130-bit) boundaries - */ - c0lo = (h0lo + TWO(52) * TWO32) - TWO(52) * TWO32; - c1lo = (h1lo + TWO(52) * TWO64) - TWO(52) * TWO64; - c2lo = (h2lo + TWO(52) * TWO96) - TWO(52) * TWO96; - c3lo = (h3lo + TWO(52) * TWO130) - TWO(52) * TWO130; - - c0hi = (h0hi + TWO(52) * TWO32) - TWO(52) * TWO32; - c1hi = (h1hi + TWO(52) * TWO64) - TWO(52) * TWO64; - c2hi = (h2hi + TWO(52) * TWO96) - TWO(52) * TWO96; - c3hi = (h3hi + TWO(52) * TWO130) - TWO(52) * TWO130; - - /* - * base 2^48 -> base 2^32 with last reduction step - */ - x1 = (h1lo - c1lo) + c0lo; - x2 = (h2lo - c2lo) + c1lo; - x3 = (h3lo - c3lo) + c2lo; - x0 = (h0lo - c0lo) + c3lo * (5.0 / TWO130); - - x1 += (h1hi - c1hi) + c0hi; - x2 += (h2hi - c2hi) + c1hi; - x3 += (h3hi - c3hi) + c2hi; - x0 += (h0hi - c0hi) + c3hi * (5.0 / TWO130); - -#ifndef __clang__ - fast_entry: -#endif - /* - * base 2^32 * base 2^16 = base 2^48 - */ - h0lo = s3lo * x1 + s2lo * x2 + s1lo * x3 + r0lo * x0; - h1lo = r0lo * x1 + s3lo * x2 + s2lo * x3 + r1lo * x0; - h2lo = r1lo * x1 + r0lo * x2 + s3lo * x3 + r2lo * x0; - h3lo = r2lo * x1 + r1lo * x2 + r0lo * x3 + r3lo * x0; - - h0hi = s3hi * x1 + s2hi * x2 + s1hi * x3 + r0hi * x0; - h1hi = r0hi * x1 + s3hi * x2 + s2hi * x3 + r1hi * x0; - h2hi = r1hi * x1 + r0hi * x2 + s3hi * x3 + r2hi * x0; - h3hi = r2hi * x1 + r1hi * x2 + r0hi * x3 + r3hi * x0; - - inp += 16; - len -= 16; - - } while (len >= 16); - - /* - * carries that cross 32n-bit (and 130-bit) boundaries - */ - c0lo = (h0lo + TWO(52) * TWO32) - TWO(52) * TWO32; - c1lo = (h1lo + TWO(52) * TWO64) - TWO(52) * TWO64; - c2lo = (h2lo + TWO(52) * TWO96) - TWO(52) * TWO96; - c3lo = (h3lo + TWO(52) * TWO130) - TWO(52) * TWO130; - - c0hi = (h0hi + TWO(52) * TWO32) - TWO(52) * TWO32; - c1hi = (h1hi + TWO(52) * TWO64) - TWO(52) * TWO64; - c2hi = (h2hi + TWO(52) * TWO96) - TWO(52) * TWO96; - c3hi = (h3hi + TWO(52) * TWO130) - TWO(52) * TWO130; - - /* - * base 2^48 -> base 2^32 with last reduction step - */ - x1 = (h1lo - c1lo) + c0lo; - x2 = (h2lo - c2lo) + c1lo; - x3 = (h3lo - c3lo) + c2lo; - x0 = (h0lo - c0lo) + c3lo * (5.0 / TWO130); - - x1 += (h1hi - c1hi) + c0hi; - x2 += (h2hi - c2hi) + c1hi; - x3 += (h3hi - c3hi) + c2hi; - x0 += (h0hi - c0hi) + c3hi * (5.0 / TWO130); - - /* - * store base 2^32, with bias - */ - st->h[1].d = x1 + TWO(52) * TWO32; - st->h[2].d = x2 + TWO(52) * TWO64; - st->h[3].d = x3 + TWO(52) * TWO96; - st->h[0].d = x0 + TWO(52) * TWO0; - - /* - * restore original FPU control register - */ -#if defined(__x86_64__) - asm volatile("ldmxcsr %0" ::"m"(mxcsr_orig)); -#elif defined(__PPC__) || defined(__POWERPC__) - asm volatile("mtfsf 255,%0" ::"f"(fpscr_orig)); -#elif defined(__s390x__) - asm volatile("lfpc %0" ::"m"(fpc_orig)); -#elif defined(__sparc__) - asm volatile("ldx %0,%%fsr" ::"m"(fsr_orig)); -#elif defined(__mips__) - asm volatile("ctc1 %0,$31" ::"r"(fcsr_orig)); -#endif -} - -void poly1305_emit(void *ctx, unsigned char mac[16], const uint32_t nonce[4]) -{ - poly1305_internal *st = (poly1305_internal *)ctx; - uint64_t h0, h1, h2, h3, h4; - uint32_t g0, g1, g2, g3, g4; - uint64_t t; - uint32_t mask; - - /* - * thanks to bias masking exponent gives integer result - */ - h0 = st->h[0].u & 0x000fffffffffffffULL; - h1 = st->h[1].u & 0x000fffffffffffffULL; - h2 = st->h[2].u & 0x000fffffffffffffULL; - h3 = st->h[3].u & 0x000fffffffffffffULL; - - /* - * can be partially reduced, so reduce... - */ - h4 = h3 >> 32; - h3 &= 0xffffffffU; - g4 = h4 & -4; - h4 &= 3; - g4 += g4 >> 2; - - h0 += g4; - h1 += h0 >> 32; - h0 &= 0xffffffffU; - h2 += h1 >> 32; - h1 &= 0xffffffffU; - h3 += h2 >> 32; - h2 &= 0xffffffffU; - - /* compute h + -p */ - g0 = (uint32_t)(t = h0 + 5); - g1 = (uint32_t)(t = h1 + (t >> 32)); - g2 = (uint32_t)(t = h2 + (t >> 32)); - g3 = (uint32_t)(t = h3 + (t >> 32)); - g4 = h4 + (uint32_t)(t >> 32); - - /* if there was carry, select g0-g3 */ - mask = 0 - (g4 >> 2); - g0 &= mask; - g1 &= mask; - g2 &= mask; - g3 &= mask; - mask = ~mask; - g0 |= (h0 & mask); - g1 |= (h1 & mask); - g2 |= (h2 & mask); - g3 |= (h3 & mask); - - /* mac = (h + nonce) % (2^128) */ - g0 = (uint32_t)(t = (uint64_t)g0 + nonce[0]); - g1 = (uint32_t)(t = (uint64_t)g1 + (t >> 32) + nonce[1]); - g2 = (uint32_t)(t = (uint64_t)g2 + (t >> 32) + nonce[2]); - g3 = (uint32_t)(t = (uint64_t)g3 + (t >> 32) + nonce[3]); - - U32TO8(mac + 0, g0); - U32TO8(mac + 4, g1); - U32TO8(mac + 8, g2); - U32TO8(mac + 12, g3); -} From f2924392d32968f550afc0051c1108b1d553d6e1 Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Fri, 3 Jul 2026 18:05:14 +0900 Subject: [PATCH 184/349] BIO_vprintf: fix off-by-one at 512-byte buffer boundary BIO_vprintf() first formats into a 512-byte stack buffer. Since vsnprintf() returns the required length excluding the NUL, a return value of 512 means truncation. The old strict greater-than check therefore wrote the truncated buffer for exactly 512-byte output. Use >= for the realloc path and add boundary coverage for 511-, 512- and 513-byte outputs. Fixes: a29d157fdb6d "Replace homebrewed implementation of *printf*() functions with libc" Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz Reviewed-by: Eugene Syromiatnikov MergeDate: Wed Jul 8 11:24:31 2026 (Merged from https://github.com/openssl/openssl/pull/31842) --- crypto/bio/bio_print.c | 2 +- test/bio_core_test.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/crypto/bio/bio_print.c b/crypto/bio/bio_print.c index 8b0c3481f0..5366587a2a 100644 --- a/crypto/bio/bio_print.c +++ b/crypto/bio/bio_print.c @@ -111,7 +111,7 @@ int BIO_vprintf(BIO *bio, const char *format, va_list args) */ sz = vsnprintf(buf, sizeof(buf), format, args); if (sz >= 0) { - if ((size_t)sz > sizeof(buf)) { + if ((size_t)sz >= sizeof(buf)) { sz += 1; abuf = (char *)OPENSSL_malloc(sz); if (abuf == NULL) { diff --git a/test/bio_core_test.c b/test/bio_core_test.c index 677bf922e5..26ff787eec 100644 --- a/test/bio_core_test.c +++ b/test/bio_core_test.c @@ -108,6 +108,38 @@ err: return testresult; } +static int test_bio_vprintf_boundary(void) +{ + BIO *bio = NULL; + char *data; + long len; + int w; + int testresult = 0; + + /* + * At width 512, vsnprintf() reports 512 bytes excluding the NUL, + * so BIO_vprintf() must use its realloc path. + */ + for (w = 511; w <= 513; w++) { + bio = BIO_new(BIO_s_mem()); + if (!TEST_ptr(bio)) + goto err; + if (!TEST_int_eq(BIO_printf(bio, "%*d", w, 0), w)) + goto err; + len = BIO_get_mem_data(bio, &data); + if (!TEST_long_eq(len, w) + || !TEST_char_eq(data[w - 1], '0') + || !TEST_char_eq(data[0], ' ')) + goto err; + BIO_free(bio); + bio = NULL; + } + testresult = 1; +err: + BIO_free(bio); + return testresult; +} + int setup_tests(void) { if (!test_skip_common_options()) { @@ -116,5 +148,6 @@ int setup_tests(void) } ADD_TEST(test_bio_core); + ADD_TEST(test_bio_vprintf_boundary); return 1; } From 801d7011965299e3674dc4cf2be377cebd4a8f96 Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Tue, 7 Jul 2026 13:00:56 +0200 Subject: [PATCH 185/349] coveralls.yml: Disable the allocfail-tests They are failing and thus the coveralls output is not produced. Reviewed-by: Matt Caswell Reviewed-by: Nikola Pajkovsky MergeDate: Wed Jul 8 12:20:03 2026 (Merged from https://github.com/openssl/openssl/pull/31878) --- .github/workflows/coveralls.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/coveralls.yml b/.github/workflows/coveralls.yml index 4c873babc8..2a70b2a1bd 100644 --- a/.github/workflows/coveralls.yml +++ b/.github/workflows/coveralls.yml @@ -47,10 +47,10 @@ jobs: MATRIX=$(cat << EOF [{ "branch": "master", - "extra_config": "enable-fips enable-tfo enable-lms enable-crypto-mdebug enable-allocfail-tests" + "extra_config": "enable-fips enable-tfo enable-lms enable-crypto-mdebug" }, { "branch": "openssl-4.0", - "extra_config": "enable-fips enable-tfo enable-lms enable-crypto-mdebug enable-allocfail-tests" + "extra_config": "enable-fips enable-tfo enable-lms enable-crypto-mdebug" },{ "branch": "openssl-3.6", "extra_config": "no-afalgeng enable-fips enable-tfo enable-lms" From 76e33ae38fdafce7a8a693063dcfe6d70aa5c790 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 7 Jul 2026 13:38:16 +0200 Subject: [PATCH 186/349] property: replace property_memfail with in-tree mfail tests The standalone property_memfail.c program is superseded by memory-failure tests added directly to property_test.c using the MFAIL harness. They cover the same property store API surface under allocation failure injection: ossl_method_store_new, ossl_method_store_add, ossl_method_store_cache_set and the providerless ossl_method_store_cache_get lookup, plus the method == NULL cache_set branch. Unlike the old NO_CHECK-only program, the new tests run as checked mfail tests, verifying both clean error propagation and the absence of reference leaks on every failure path. The old program also relied on a stale, pre-lockless STORED_ALGORITHMS layout to poke the cache directly, which no longer matches property.c. Drop property_memfail.c along with its wiring in test/build.info and the 90-test_memfail.t recipe. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Nikola Pajkovsky Reviewed-by: Tomas Mraz MergeDate: Wed Jul 8 13:05:16 2026 (Merged from https://github.com/openssl/openssl/pull/31880) --- test/build.info | 6 +- test/property_memfail.c | 196 --------------------------------- test/property_test.c | 121 ++++++++++++++++++++ test/recipes/90-test_memfail.t | 10 +- 4 files changed, 123 insertions(+), 210 deletions(-) delete mode 100644 test/property_memfail.c diff --git a/test/build.info b/test/build.info index 874786ee33..c40c14e8f1 100644 --- a/test/build.info +++ b/test/build.info @@ -82,7 +82,7 @@ IF[{- !$disabled{tests} -}] ENDIF IF[{- !$disabled{'allocfail-tests'} -}] - PROGRAMS{noinst}=handshake-memfail x509-memfail load_key_certs_crls_memfail property-memfail + PROGRAMS{noinst}=handshake-memfail x509-memfail load_key_certs_crls_memfail ENDIF IF[{- !$disabled{quic} -}] @@ -651,10 +651,6 @@ IF[{- !$disabled{tests} -}] INCLUDE[load_key_certs_crls_memfail]=.. ../include ../apps/include DEPEND[load_key_certs_crls_memfail]=libtestutil.a ../libcrypto.a ../libssl.a - SOURCE[property-memfail]=property_memfail.c - INCLUDE[property-memfail]=../include ../apps/include - DEPEND[property-memfail]=../libcrypto.a - SOURCE[ssl_handshake_rtt_test]=ssl_handshake_rtt_test.c helpers/ssltestlib.c INCLUDE[ssl_handshake_rtt_test]=../include ../apps/include .. DEPEND[ssl_handshake_rtt_test]=../libcrypto.a ../libssl.a libtestutil.a diff --git a/test/property_memfail.c b/test/property_memfail.c deleted file mode 100644 index caedc51c83..0000000000 --- a/test/property_memfail.c +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -#include -#include -#include - -#include -#include "internal/hashtable.h" -#include "internal/property.h" -#include "internal/refcount.h" - -#define TEST_NID 1024 - -/* - * TEST_NID maps to shard zero with the property cache's current power-of-two - * shard count, so this partial view is enough to reach the cache table. - */ -typedef struct { - void *algs; - HT *cache; -} TEST_STORED_ALGORITHMS; - -struct ossl_method_store_st { - OSSL_LIB_CTX *ctx; - TEST_STORED_ALGORITHMS *algs; - CRYPTO_RWLOCK *biglock; -}; - -typedef struct { - HT_KEY key_header; -} QUERY_KEY; - -/* - * We make our OSSL_PROVIDER for testing purposes. The property cache only - * uses the provider pointer as a key, except when tracing asks for its name. - */ -struct ossl_provider_st { - unsigned int flag_initialized : 1; - unsigned int flag_activated : 1; - CRYPTO_RWLOCK *flag_lock; - CRYPTO_REF_COUNT refcnt; - CRYPTO_RWLOCK *activatecnt_lock; - int activatecnt; - char *name; -}; - -static long alloc_count; -static long fail_at; -static int fail_enabled; -static int method_refs; - -static void *test_malloc(size_t num, const char *file, int line) -{ - (void)file; - (void)line; - - if (fail_enabled && ++alloc_count == fail_at) - return NULL; - - return malloc(num); -} - -static void *test_realloc(void *ptr, size_t num, const char *file, int line) -{ - (void)file; - (void)line; - - if (fail_enabled && ++alloc_count == fail_at) - return NULL; - - return realloc(ptr, num); -} - -static void test_free(void *ptr, const char *file, int line) -{ - (void)file; - (void)line; - - free(ptr); -} - -static int up_ref(void *p) -{ - (void)p; - - method_refs++; - return 1; -} - -static void down_ref(void *p) -{ - (void)p; - - method_refs--; -} - -static int delete_providerless_cache_entry(OSSL_METHOD_STORE *store) -{ - QUERY_KEY key; - uint8_t keybuf[sizeof(int)]; - size_t keylen = 0; - int nid = TEST_NID; - - memcpy(&keybuf[keylen], &nid, sizeof(nid)); - keylen += sizeof(nid); - HT_INIT_KEY_EXTERNAL(&key, keybuf, keylen); - - return ossl_ht_delete(store->algs->cache, TO_HT_KEY(&key)); -} - -static int property_cache_workload(int expect_success) -{ - static struct ossl_provider_st prov = { - .flag_initialized = 1, - .flag_activated = 1, - .name = "property-memfail" - }; - OSSL_METHOD_STORE *store = NULL; - int method = 1; - void *result = NULL; - int ret = 0; - - method_refs = 0; - - if ((store = ossl_method_store_new(NULL)) == NULL) - goto end; - if (!ossl_method_store_add(store, (OSSL_PROVIDER *)&prov, TEST_NID, "", - &method, up_ref, down_ref)) - goto end; - /* - * Restrict failure injection to the cache paths. Store setup exercises - * unrelated global initialization and platform lock allocation. - */ - alloc_count = 0; - fail_enabled = 1; - if (!ossl_method_store_cache_set(store, (OSSL_PROVIDER *)&prov, TEST_NID, - "", &method, up_ref, down_ref)) - goto end; - if (!delete_providerless_cache_entry(store)) - goto end; - if (!ossl_method_store_cache_get(store, NULL, TEST_NID, "", &result) - || result != &method) - goto end; - ret = 1; - -end: - fail_enabled = 0; - if (result != NULL) - down_ref(result); - ossl_method_store_free(store); - - if (method_refs != 0) { - fprintf(stderr, "method reference leak: %d\n", method_refs); - return 0; - } - - return expect_success ? ret : 1; -} - -int main(int argc, char **argv) -{ - int ret = EXIT_FAILURE; - - if (argc < 2) { - fprintf(stderr, "usage: %s count | run \n", argv[0]); - return EXIT_FAILURE; - } - - if (!CRYPTO_set_mem_functions(test_malloc, test_realloc, test_free)) { - fprintf(stderr, "failed to set memory functions\n"); - return EXIT_FAILURE; - } - - if (strcmp(argv[1], "count") == 0) { - if (property_cache_workload(1)) { - fprintf(stderr, "skip: 0 count %ld\n", alloc_count); - ret = EXIT_SUCCESS; - } - } else if (strcmp(argv[1], "run") == 0 && argc == 3) { - fail_at = strtol(argv[2], NULL, 10); - if (fail_at > 0 && property_cache_workload(0)) - ret = EXIT_SUCCESS; - } else { - fprintf(stderr, "usage: %s count | run \n", argv[0]); - } - - OPENSSL_cleanup(); - return ret; -} diff --git a/test/property_test.c b/test/property_test.c index 602651df25..ed868dbe8c 100644 --- a/test/property_test.c +++ b/test/property_test.c @@ -747,6 +747,123 @@ err: return res; } +/* Memory-failure coverage for store creation. */ +static int test_query_store_new_mfail(void) +{ + OSSL_METHOD_STORE *store; + int rc; + + MFAIL_start(); + store = ossl_method_store_new(NULL); + MFAIL_end(); + + rc = store != NULL ? 1 : 0; + ossl_method_store_free(store); + return rc; +} + +/* Memory-failure coverage for method registration. */ +static int test_query_store_add_mfail(void) +{ + static OSSL_PROVIDER prov = { + .flag_initialized = 1, + .flag_activated = 1, + .name = "add-mfail-provider" + }; + OSSL_METHOD_STORE *store = NULL; + int refs = 0; + int rc = -1; + + if (!TEST_ptr(store = ossl_method_store_new(NULL))) + goto end; + + MFAIL_start(); + rc = ossl_method_store_add(store, &prov, 1, "", &refs, + counted_up_ref, counted_down_ref) + ? 1 + : 0; + MFAIL_end(); + +end: + ossl_method_store_free(store); + if (rc >= 0 && !TEST_int_eq(refs, 0)) + rc = -1; + return rc; +} + +/* A NULL method archives the matching entry instead of caching a new one. */ +static int test_query_cache_set_null(void) +{ + static OSSL_PROVIDER prov = { + .flag_initialized = 1, + .flag_activated = 1, + .name = "null-set-provider" + }; + OSSL_METHOD_STORE *store = NULL; + int refs = 0; + void *result = NULL; + int res = 0; + + if (!TEST_ptr(store = ossl_method_store_new(NULL)) + || !TEST_true(ossl_method_store_add(store, &prov, 1, "", &refs, + counted_up_ref, counted_down_ref)) + || !TEST_true(ossl_method_store_cache_set(store, &prov, 1, "", &refs, + counted_up_ref, counted_down_ref)) + || !TEST_true(ossl_method_store_cache_set(store, &prov, 1, "", NULL, + counted_up_ref, counted_down_ref)) + || !TEST_false(ossl_method_store_cache_get(store, &prov, 1, "", + &result))) + goto err; + + res = 1; + +err: + ossl_method_store_free(store); + if (!TEST_int_eq(refs, 0)) + res = 0; + return res; +} + +/* Memory-failure coverage for the cache set and providerless lookup. */ +static int test_query_cache_set_mfail(void) +{ + static OSSL_PROVIDER prov = { + .flag_initialized = 1, + .flag_activated = 1, + .name = "mfail-provider" + }; + OSSL_METHOD_STORE *store = NULL; + int refs = 0; + void *result = NULL; + int rc = -1; + + if (!TEST_ptr(store = ossl_method_store_new(NULL)) + || !TEST_true(ossl_method_store_add(store, &prov, 1, "", &refs, + counted_up_ref, counted_down_ref))) + goto end; + + /* Cache the method, then resolve it via the "any provider" (NULL) lookup. */ + MFAIL_start(); + rc = ossl_method_store_cache_set(store, &prov, 1, "", &refs, + counted_up_ref, counted_down_ref) + && ossl_method_store_cache_get(store, NULL, 1, "", &result) + && result == &refs + ? 1 + : 0; + MFAIL_end(); + +#ifdef OPENSSL_NO_CACHED_FETCH + if (result != NULL) + counted_down_ref(result); +#endif + +end: + ossl_method_store_free(store); + if (rc >= 0 && !TEST_int_eq(refs, 0)) + rc = -1; + return rc; +} + static int test_fips_mode(void) { int ret = 0; @@ -861,6 +978,10 @@ int setup_tests(void) ADD_TEST(test_query_cache_stochastic); ADD_TEST(test_query_cache_set_duplicate); ADD_TEST(test_query_cache_provider_order); + ADD_TEST(test_query_cache_set_null); + ADD_MFAIL_TEST(test_query_store_new_mfail); + ADD_MFAIL_TEST(test_query_store_add_mfail); + ADD_MFAIL_TEST(test_query_cache_set_mfail); ADD_TEST(test_fips_mode); ADD_ALL_TESTS(test_property_list_to_string, OSSL_NELEM(to_string_tests)); ADD_TEST(test_property_list_to_string_bounds); diff --git a/test/recipes/90-test_memfail.t b/test/recipes/90-test_memfail.t index f89b1b9e1e..fefc2771b6 100644 --- a/test/recipes/90-test_memfail.t +++ b/test/recipes/90-test_memfail.t @@ -35,8 +35,6 @@ run(test(["x509-memfail", "count", srctop_file("test", "certs", "servercert.pem" run(test(["load_key_certs_crls_memfail", "count", srctop_file("test", "certs", "servercert.pem")], stderr => "$resultdir/load_key_certs_crls_countinfo.txt")); -run(test(["property-memfail", "count"], stderr => "$resultdir/propertycountinfo.txt")); - sub get_count_info { my ($infile) = @_; my ($skipcount, $malloccount) = (0, 0); @@ -63,10 +61,8 @@ my ($x509skipcount, $x509malloccount) = get_count_info("$resultdir/x509countinfo my ($load_key_certs_crls_skipcount, $load_key_certs_crls_malloccount) = get_count_info("$resultdir/load_key_certs_crls_countinfo.txt"); -my (undef, $propertymalloccount) = get_count_info("$resultdir/propertycountinfo.txt"); - my $total_malloccount = $hsmalloccount + $x509malloccount - + $load_key_certs_crls_malloccount + $propertymalloccount; + + $load_key_certs_crls_malloccount; plan skip_all => "could not get malloc counts (one or more count runs failed or output format changed)" if $total_malloccount == 0; @@ -101,7 +97,3 @@ run_memfail_test($hsskipcount, $hsmalloccount, ["handshake-memfail", "run", srct run_memfail_test($x509skipcount, $x509malloccount, ["x509-memfail", "run", srctop_file("test", "certs", "servercert.pem")]); run_memfail_test($load_key_certs_crls_skipcount, $load_key_certs_crls_malloccount, ["load_key_certs_crls_memfail", "run", srctop_file("test", "certs", "servercert.pem")]); - -for my $idx (1..$propertymalloccount) { - ok(run(test(["property-memfail", "run", $idx]))); -} From 11889aa905bd6e80f80638f6cd2e40224a1dd084 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Thu, 2 Jul 2026 13:28:16 -0400 Subject: [PATCH 187/349] Suppress function pointer type validation in clang ubsan We've been concerned about ubsan errors comming with more recent versions of clang. specifically versions of clang later than 17 generate hundreds of function pointer type validation errors, i.e. assigning a function of type void (*)(TYPE *) to a function pointer of type void (*)(void *). Fixing these requires the creation of lots of thunk function that get littered through the code base, and are generally unpleasant to carry. A better fix requires siginficant code refactoring, and potentially large changes to our ABI, which we can't support until the next major release. So, for now, just suppress those ubsan errors, so we can more properly deal with the issue when we are able. Reviewed-by: Milan Broz Reviewed-by: Bob Beck Reviewed-by: Nikola Pajkovsky MergeDate: Wed Jul 8 16:01:12 2026 (Merged from https://github.com/openssl/openssl/pull/31837) --- Configure | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Configure b/Configure index 918a7c7cc0..30671d0af5 100755 --- a/Configure +++ b/Configure @@ -1671,8 +1671,13 @@ unless ($disabled{asan} || defined $detected_sanitizers{asan}) { $config{target} =~ /^VC-/ ? "/fsanitize=address" : "-fsanitize=address"; } +my %predefined_C = compiler_predefined($config{CROSS_COMPILE}.$config{CC}); + unless ($disabled{ubsan} || defined $detected_sanitizers{ubsan}) { push @{$config{cflags}}, "-fsanitize=undefined", "-fno-sanitize-recover=all", "-DPEDANTIC"; + if ($predefined_C{__clang__}) { + push @{$config{cflags}}, "-fno-sanitize=function"; + } } unless ($disabled{msan} || defined $detected_sanitizers{msan}) { @@ -1752,7 +1757,6 @@ if ($target{sys_id} ne "") push @{$config{openssl_sys_defines}}, "OPENSSL_SYS_$target{sys_id}"; } -my %predefined_C = compiler_predefined($config{CROSS_COMPILE}.$config{CC}); my %predefined_CXX = $config{CXX} ? compiler_predefined($config{CROSS_COMPILE}.$config{CXX}) : (); From 4e2e6f4174bb4098727314afea8e8decb80c0d92 Mon Sep 17 00:00:00 2001 From: Steven WdV Date: Tue, 7 Jul 2026 14:52:30 +0200 Subject: [PATCH 188/349] Allow `getentropy` for Emscripten Usually Emscripten emulates `/dev/urandom`, but in some cases, like with `-sNODERAWFS`, it doesn't. This means that on non-Unix platforms, where `/dev/urandom` does not exist on the host, OpenSSL will fail to seed its PRNG. This fixes that by instead using the POSIX function it implements, like which was already done for WASI. See https://github.com/emscripten-core/emscripten/issues/9628#issuecomment-4892658766 for more context. CLA: trivial Reviewed-by: Kurt Roeckx Reviewed-by: Daniel Kubec Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz MergeDate: Wed Jul 8 17:50:39 2026 (Merged from https://github.com/openssl/openssl/pull/31882) (cherry picked from commit dc219a04088d08de7df5afdb14263b3e9a4c7915) --- providers/implementations/rands/seeding/rand_unix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/implementations/rands/seeding/rand_unix.c b/providers/implementations/rands/seeding/rand_unix.c index 1af996b25f..95742eb848 100644 --- a/providers/implementations/rands/seeding/rand_unix.c +++ b/providers/implementations/rands/seeding/rand_unix.c @@ -396,7 +396,7 @@ static ssize_t syscall_random(void *buf, size_t buflen) return getrandom(buf, buflen, 0); #elif (defined(__FreeBSD__) || defined(__NetBSD__)) && defined(KERN_ARND) return sysctl_random(buf, buflen); -#elif defined(__wasi__) +#elif defined(__wasi__) || defined(__EMSCRIPTEN__) if (getentropy(buf, buflen) == 0) return (ssize_t)buflen; return -1; From 4d32206641715811d31b830dfb376844737af42b Mon Sep 17 00:00:00 2001 From: Urval Date: Sat, 18 Apr 2026 14:51:50 +0530 Subject: [PATCH 189/349] test: add all-alias BIGNUM coverage in file_sum Reviewed-by: Neil Horman Reviewed-by: Paul Dale MergeDate: Wed Jul 8 17:56:40 2026 (Merged from https://github.com/openssl/openssl/pull/30893) --- test/bntest.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/test/bntest.c b/test/bntest.c index 77f07bc630..5c8f76e1bc 100644 --- a/test/bntest.c +++ b/test/bntest.c @@ -1215,6 +1215,71 @@ err: return st; } +typedef struct sum_all_alias_st { + const char *hex; + int negative; +} SUM_ALL_ALIAS; + +static int test_sum_all_alias_helper(const SUM_ALL_ALIAS *test) +{ + BIGNUM *alias = NULL, *orig = NULL, *expected = NULL; + int st = 0; + + if (!TEST_true(BN_hex2bn(&alias, test->hex))) + goto err; + if (test->negative && !BN_is_zero(alias)) + BN_set_negative(alias, 1); + if (!TEST_ptr(orig = BN_dup(alias)) + || !TEST_ptr(expected = BN_new())) + goto err; + + /* BN_add */ + if (!TEST_true(BN_add(expected, orig, orig)) + || !TEST_true(BN_add(alias, alias, alias)) + || !TEST_BN_eq(expected, alias) + || !TEST_ptr(BN_copy(alias, orig)) + /* BN_sub */ + || !TEST_true(BN_sub(expected, orig, orig)) + || !TEST_true(BN_sub(alias, alias, alias)) + || !TEST_BN_eq(expected, alias) + || !TEST_ptr(BN_copy(alias, orig)) + /* BN_uadd */ + || !TEST_true(BN_uadd(expected, orig, orig)) + || !TEST_true(BN_uadd(alias, alias, alias)) + || !TEST_BN_eq(expected, alias) + || !TEST_ptr(BN_copy(alias, orig)) + /* BN_usub */ + || !TEST_true(BN_usub(expected, orig, orig)) + || !TEST_true(BN_usub(alias, alias, alias)) + || !TEST_BN_eq(expected, alias)) + goto err; + + st = 1; +err: + BN_free(alias); + BN_free(orig); + BN_free(expected); + return st; +} + +static int test_sum_all_alias(void) +{ + static const SUM_ALL_ALIAS tests[] = { + { "2A", 0 }, + { "2A", 1 }, + { "0", 0 }, + { "FEDCBA98765432100123456789ABCDEFFEDCBA98765432100123456789ABCDEF", 0 }, + { "FEDCBA98765432100123456789ABCDEFFEDCBA98765432100123456789ABCDEF", 1 } + }; + size_t i; + + for (i = 0; i < OSSL_NELEM(tests); i++) { + if (!test_sum_all_alias_helper(&tests[i])) + return 0; + } + return 1; +} + static int file_sum(STANZA *s) { BIGNUM *a = NULL, *b = NULL, *sum = NULL, *ret = NULL; @@ -1238,7 +1303,6 @@ static int file_sum(STANZA *s) /* * Test that the functions work when |r| and |a| point to the same BIGNUM, * or when |r| and |b| point to the same BIGNUM. - * There is no test for all of |r|, |a|, and |b| pointing to the same BIGNUM. */ if (!TEST_true(BN_copy(ret, a)) || !TEST_true(BN_add(ret, ret, b)) @@ -1277,8 +1341,6 @@ static int file_sum(STANZA *s) /* * Test that the functions work when |r| and |a| point to the same * BIGNUM, or when |r| and |b| point to the same BIGNUM. - * There is no test for all of |r|, |a|, and |b| pointing to the same - * BIGNUM. */ if (!TEST_true(BN_copy(ret, a)) || !TEST_true(BN_uadd(ret, ret, b)) @@ -3420,6 +3482,7 @@ int setup_tests(void) ADD_ALL_TESTS(test_signed_mod_replace_ab, OSSL_NELEM(signed_mod_tests)); ADD_ALL_TESTS(test_signed_mod_replace_ba, OSSL_NELEM(signed_mod_tests)); ADD_TEST(test_mod); + ADD_TEST(test_sum_all_alias); ADD_TEST(test_mod_inverse); ADD_ALL_TESTS(test_mod_exp_alias, 2); ADD_TEST(test_modexp_mont5); From 4be956b9cdeec99bd34f968ac4b595989d73abf7 Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Tue, 16 Jun 2026 11:35:25 +0100 Subject: [PATCH 190/349] Fix remove_session_cb called while holding ctx->lock SSL_CTX_add_session() held ctx->lock while calling remove_session_lock() with lck = 0, which still fired the remove_session_cb callback. SSL_CTX_flush_sessions_ex() had the same problem: it called remove_session_cb for each expired session while holding the lock. Any callback that re-entered an OpenSSL API requiring the same lock would deadlock. Refactor remove_session_lock() into remove_session_locked() (caller holds the lock) which returns the removed SSL_SESSION * instead of calling the callback and freeing it internally. SSL_CTX_remove_session() manages its own locking and invokes the callback unconditionally after releasing the lock (preserving the existing behaviour where the callback fires even when the session is not in the internal cache, to allow external caches to be notified). SSL_CTX_add_session() collects evicted sessions in a temporary singly-linked list (via the now-NULL next pointer) and processes them after CRYPTO_THREAD_unlock(). SSL_CTX_flush_sessions_ex() already deferred SSL_SESSION_free() to after the lock via a STACK_OF(SSL_SESSION). The callback is now also deferred: sessions are collected on the stack under the lock, then the lock is released before iterating the stack to fire callbacks and free each session. Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Neil Horman Reviewed-by: Paul Dale MergeDate: Wed Jul 8 17:58:47 2026 (Merged from https://github.com/openssl/openssl/pull/31540) --- ssl/ssl_sess.c | 103 +++++++++++++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 38 deletions(-) diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c index 1ba4add39d..5604ca1354 100644 --- a/ssl/ssl_sess.c +++ b/ssl/ssl_sess.c @@ -22,7 +22,7 @@ static void SSL_SESSION_list_remove(SSL_CTX *ctx, SSL_SESSION *s); static void SSL_SESSION_list_add(SSL_CTX *ctx, SSL_SESSION *s); -static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *c, int lck); +static SSL_SESSION *remove_session_locked(SSL_CTX *ctx, SSL_SESSION *c); DEFINE_STACK_OF(SSL_SESSION) @@ -798,6 +798,14 @@ int SSL_CTX_add_session(SSL_CTX *ctx, SSL_SESSION *c) ssl_session_calculate_timeout(c); } + /* + * evicted_head is a singly-linked list (via the next pointer, which + * SSL_SESSION_list_remove zeroes out) of sessions evicted from the cache + * that need their remove_session_cb called and their reference dropped + * once the lock is released. + */ + SSL_SESSION *evicted_head = NULL; + if (s == NULL) { /* * new cache entry -- remove old ones if cache has become too large @@ -808,10 +816,13 @@ int SSL_CTX_add_session(SSL_CTX *ctx, SSL_SESSION *c) if (SSL_CTX_sess_get_cache_size(ctx) > 0) { while (SSL_CTX_sess_number(ctx) >= SSL_CTX_sess_get_cache_size(ctx)) { - if (!remove_session_lock(ctx, ctx->session_cache_tail, 0)) + SSL_SESSION *r = remove_session_locked(ctx, ctx->session_cache_tail); + + if (r == NULL) break; - else - ssl_tsan_counter(ctx, &ctx->stats.sess_cache_full); + ssl_tsan_counter(ctx, &ctx->stats.sess_cache_full); + r->next = evicted_head; + evicted_head = r; } } @@ -828,41 +839,59 @@ int SSL_CTX_add_session(SSL_CTX *ctx, SSL_SESSION *c) ret = 0; } CRYPTO_THREAD_unlock(ctx->lock); + + while (evicted_head != NULL) { + SSL_SESSION *next = evicted_head->next; + + evicted_head->next = NULL; + if (ctx->remove_session_cb != NULL) + ctx->remove_session_cb(ctx, evicted_head); + SSL_SESSION_free(evicted_head); + evicted_head = next; + } + return ret; } int SSL_CTX_remove_session(SSL_CTX *ctx, SSL_SESSION *c) { - return remove_session_lock(ctx, c, 1); + SSL_SESSION *r; + + if (c == NULL || c->session_id_length == 0) + return 0; + if (!CRYPTO_THREAD_write_lock(ctx->lock)) + return 0; + r = remove_session_locked(ctx, c); + CRYPTO_THREAD_unlock(ctx->lock); + + /* + * The callback is invoked even when the session is not in the internal + * cache so that external caches can be notified. + */ + if (ctx->remove_session_cb != NULL) + ctx->remove_session_cb(ctx, c); + SSL_SESSION_free(r); + return r != NULL; } -static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *c, int lck) +/* + * Removes c from the session cache. Caller must hold ctx->lock. + * Returns the removed session (caller must invoke remove_session_cb and + * SSL_SESSION_free), or NULL if not found. + */ +static SSL_SESSION *remove_session_locked(SSL_CTX *ctx, SSL_SESSION *c) { - SSL_SESSION *r; - int ret = 0; + SSL_SESSION *r = NULL; - if ((c != NULL) && (c->session_id_length != 0)) { - if (lck) { - if (!CRYPTO_THREAD_write_lock(ctx->lock)) - return 0; - } - if ((r = lh_SSL_SESSION_retrieve(ctx->sessions, c)) != NULL) { - ret = 1; + if (c != NULL && c->session_id_length != 0) { + r = lh_SSL_SESSION_retrieve(ctx->sessions, c); + if (r != NULL) { r = lh_SSL_SESSION_delete(ctx->sessions, r); SSL_SESSION_list_remove(ctx, r); } c->not_resumable = 1; - - if (lck) - CRYPTO_THREAD_unlock(ctx->lock); - - if (ctx->remove_session_cb != NULL) - ctx->remove_session_cb(ctx, c); - - if (ret) - SSL_SESSION_free(r); } - return ret; + return r; } void SSL_SESSION_free(SSL_SESSION *ss) @@ -1242,9 +1271,10 @@ void SSL_CTX_flush_sessions_ex(SSL_CTX *s, time_t t) /* * Iterate over the list from the back (oldest), and stop * when a session can no longer be removed. - * Add the session to a temporary list to be freed outside - * the SSL_CTX lock. - * But still do the remove_session_cb() within the lock. + * Collect removed sessions on a stack to be processed outside the lock, + * so that remove_session_cb is never invoked while holding ctx->lock. + * If the stack failed to create, or a push fails, free the session + * immediately (without invoking the callback). */ while (s->session_cache_tail != NULL) { current = s->session_cache_tail; @@ -1252,15 +1282,6 @@ void SSL_CTX_flush_sessions_ex(SSL_CTX *s, time_t t) lh_SSL_SESSION_delete(s->sessions, current); SSL_SESSION_list_remove(s, current); current->not_resumable = 1; - if (s->remove_session_cb != NULL) - s->remove_session_cb(s, current); - /* - * Throw the session on a stack, it's entirely plausible - * that while freeing outside the critical section, the - * session could be re-added, so avoid using the next/prev - * pointers. If the stack failed to create, or the session - * couldn't be put on the stack, just free it here - */ if (sk == NULL || !sk_SSL_SESSION_push(sk, current)) SSL_SESSION_free(current); } else { @@ -1271,7 +1292,13 @@ void SSL_CTX_flush_sessions_ex(SSL_CTX *s, time_t t) lh_SSL_SESSION_set_down_load(s->sessions, i); CRYPTO_THREAD_unlock(s->lock); - sk_SSL_SESSION_pop_free(sk, SSL_SESSION_free); + while (sk_SSL_SESSION_num(sk) > 0) { + current = sk_SSL_SESSION_pop(sk); + if (s->remove_session_cb != NULL) + s->remove_session_cb(s, current); + SSL_SESSION_free(current); + } + sk_SSL_SESSION_free(sk); } int ssl_clear_bad_session(SSL_CONNECTION *s) From b24c43f9b8806f9fa7af89816db500f4dba7051b Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Tue, 16 Jun 2026 11:35:33 +0100 Subject: [PATCH 191/349] Add regression test for remove_session_cb under lock Install a remove_session_cb that calls SSL_CTX_flush_sessions_ex(). If the callback is invoked while ctx->lock is held, the nested flush call deadlocks immediately. The test covers the SSL_CTX_add_session() eviction path (adding a second session to a size == 1 cache evicts the first, firing the callback) and the SSL_CTX_flush_sessions_ex() path (SSL_CTX_free() flushes the remaining session via flush_sessions_ex()). Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Neil Horman Reviewed-by: Paul Dale MergeDate: Wed Jul 8 17:58:48 2026 (Merged from https://github.com/openssl/openssl/pull/31540) --- test/sslapitest.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/test/sslapitest.c b/test/sslapitest.c index c5517016c5..1c106355c6 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -3030,6 +3030,58 @@ static int test_session_with_both_cache(void) #endif } +/* + * Test that remove_session_cb is not invoked while ctx->lock is held. + * The callback calls SSL_CTX_flush_sessions_ex(), which itself tries to + * acquire ctx->lock; if the lock is already held when the callback fires, + * the nested acquisition deadlocks immediately. t = 1 (Unix epoch + 1s) is + * used so that no current sessions are flushed and the callback is not + * re-entered. + */ +static void remove_session_lock_test_cb(SSL_CTX *ctx, SSL_SESSION *sess) +{ + SSL_CTX_flush_sessions_ex(ctx, 1); +} + +static int test_remove_session_cb_not_under_lock(void) +{ + SSL_CTX *ctx = NULL; + SSL_SESSION *sess1 = NULL, *sess2 = NULL; + static const unsigned char sid1[] = { 1 }; + static const unsigned char sid2[] = { 2 }; + int testresult = 0; + + if (!TEST_ptr(ctx = SSL_CTX_new_ex(libctx, NULL, TLS_server_method()))) + goto end; + + SSL_CTX_sess_set_cache_size(ctx, 1); + SSL_CTX_sess_set_remove_cb(ctx, remove_session_lock_test_cb); + + if (!TEST_ptr(sess1 = SSL_SESSION_new()) + || !TEST_true(SSL_SESSION_set1_id(sess1, sid1, sizeof(sid1))) + || !TEST_true(SSL_CTX_add_session(ctx, sess1))) + goto end; + + if (!TEST_ptr(sess2 = SSL_SESSION_new()) + || !TEST_true(SSL_SESSION_set1_id(sess2, sid2, sizeof(sid2)))) + goto end; + + /* + * Adding sess2 evicts sess1 (cache is full), firing remove_session_cb. + * If the callback is invoked while holding ctx->lock the flush call + * inside it will deadlock. + */ + if (!TEST_true(SSL_CTX_add_session(ctx, sess2))) + goto end; + + testresult = 1; +end: + SSL_SESSION_free(sess1); + SSL_SESSION_free(sess2); + SSL_CTX_free(ctx); + return testresult; +} + static int test_session_wo_ca_names(void) { #ifndef OSSL_NO_USABLE_TLS1_3 @@ -15174,6 +15226,7 @@ int setup_tests(void) ADD_TEST(test_session_with_only_int_cache); ADD_TEST(test_session_with_only_ext_cache); ADD_TEST(test_session_with_both_cache); + ADD_TEST(test_remove_session_cb_not_under_lock); ADD_TEST(test_session_wo_ca_names); #ifndef OSSL_NO_USABLE_TLS1_3 ADD_ALL_TESTS(test_stateful_tickets, 3); From ea902d80b36691861644d68cade280a18c19b6f7 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 23 Jun 2026 11:35:42 +0200 Subject: [PATCH 192/349] apps: cover the unencrypted key bag path in the pkcs12 test recipe The NID_keyBag branch of dump_certs_pkeys_bag() was not exercised. Export a file with -keypbe NONE and dump it, checking the key bag is reported and its private key is output. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Matt Caswell Reviewed-by: Tomas Mraz MergeDate: Wed Jul 8 18:02:22 2026 (Merged from https://github.com/openssl/openssl/pull/31665) --- test/recipes/80-test_pkcs12.t | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/test/recipes/80-test_pkcs12.t b/test/recipes/80-test_pkcs12.t index a1dc539311..5e9838d107 100644 --- a/test/recipes/80-test_pkcs12.t +++ b/test/recipes/80-test_pkcs12.t @@ -56,7 +56,7 @@ $ENV{OPENSSL_WIN32_UTF8}=1; my $no_fips = disabled('fips') || ($ENV{NO_FIPS} // 0); -plan tests => 61 + ($no_fips ? 0 : 5); +plan tests => 64 + ($no_fips ? 0 : 5); # Test different PKCS#12 formats ok(run(test(["pkcs12_format_test"])), "test pkcs12 formats"); @@ -176,6 +176,29 @@ ok(grep(/Trusted key usage (Oracle)/, @pkcs12info) == 0, ok(scalar @match > 0 ? 0 : 1, "test_export_pkcs12_outerr6_empty"); } +# Test dumping a PKCS#12 file whose private key is stored in an unencrypted +# keyBag (created with -keypbe NONE) rather than a shrouded keyBag. +{ + my $keybag = "keybag.p12"; + ok(run(app(["openssl", "pkcs12", "-export", "-keypbe", "NONE", + "-certpbe", "NONE", "-nomac", + "-inkey", srctop_file(@path, "cert-key-cert.pem"), + "-in", srctop_file(@path, "cert-key-cert.pem"), + "-passout", "pass:", "-out", $keybag])), + "export PKCS#12 with an unencrypted key bag"); + + # -nodes so the dumped key isn't re-encrypted (which would prompt). + my @info = run(app(["openssl", "pkcs12", "-in", $keybag, "-info", "-nodes", + "-passin", "pass:"], stderr => "keybag_info.txt"), + capture => 1); + open DATA, "keybag_info.txt"; + my @match = grep /Key bag/, ; + close DATA; + ok(scalar @match > 0 ? 1 : 0, "test unencrypted key bag is reported"); + ok(grep(/-----BEGIN PRIVATE KEY-----/, @info) == 1, + "test private key from key bag is output"); +} + my %pbmac1_tests = ( pbmac1_defaults => {args => [], lookup => "hmacWithSHA256"}, pbmac1_nondefaults => {args => ["-pbmac1_pbkdf2_md", "sha512", "-macalg", "sha384"], lookup => "hmacWithSHA512"}, From 0cb923d4ba8c910f9e5f09bd815c1ee8721ee624 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Tue, 7 Jul 2026 12:50:01 +0200 Subject: [PATCH 193/349] Fix doubled semicolons as statement terminations Reviewed-by: Matt Caswell Reviewed-by: Kurt Roeckx Reviewed-by: Paul Dale MergeDate: Wed Jul 8 18:03:29 2026 (Merged from https://github.com/openssl/openssl/pull/31877) --- apps/req.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/req.c b/apps/req.c index 695fd049dd..83ac38ef86 100644 --- a/apps/req.c +++ b/apps/req.c @@ -1254,25 +1254,21 @@ static int prompt_info(X509_REQ *req, if (!join(buf, sizeof(buf), type, "_value", "Name")) goto err; - ; value = app_conf_try_string(req_conf, attr_sect, buf); if (!join(buf, sizeof(buf), type, "_min", "Name")) goto err; - ; if (!app_conf_try_number(req_conf, attr_sect, buf, &n_min)) n_min = -1; if (!join(buf, sizeof(buf), type, "_max", "Name")) goto err; - ; if (!app_conf_try_number(req_conf, attr_sect, buf, &n_max)) n_max = -1; if (!add_attribute_object(req, v->value, def, value, nid, n_min, n_max, chtype)) goto err; - ; } } } else { From a17cd7691ab33192abbcfe6a69aafb079ec1d0eb Mon Sep 17 00:00:00 2001 From: Billy Brumley Date: Thu, 2 Jul 2026 04:30:13 -0400 Subject: [PATCH 194/349] [test] exercise AEAD tag read rejection when actually present during decryption Set a verify tag while decrypting (which must succeed) before attempting the read, so the test asserts that a tag cannot be read back while decrypting even when one is _actually_ present. This isolates direction logic from tag present logic. Follow-up to #31734 Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Daniel Kubec Reviewed-by: Paul Dale MergeDate: Wed Jul 8 18:06:40 2026 (Merged from https://github.com/openssl/openssl/pull/31826) --- test/evp_extra_test.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c index 20d9496116..e1b80256bd 100644 --- a/test/evp_extra_test.c +++ b/test/evp_extra_test.c @@ -6012,6 +6012,14 @@ static int test_evp_aead_tag_direction(int idx) tagparams[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, tag, info->taglen); tagparams[1] = OSSL_PARAM_construct_end(); + + /* set a tag so the get below is exercised with one present */ + if (!TEST_true(EVP_CIPHER_CTX_set_params(ctx_dec, tagparams))) { + errmsg = "DEC_SET_TAG_REJECTED"; + goto err; + } + + /* but a tag must never be readable back while decrypting */ ERR_set_mark(); if (!TEST_false(EVP_CIPHER_CTX_get_params(ctx_dec, tagparams))) { ERR_clear_last_mark(); From a4ee6965a09273425af35cabfefa08862c388fc3 Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Sun, 28 Jun 2026 07:50:14 +0900 Subject: [PATCH 195/349] Reject AES-XTS operations without an IV Commit 774525b38bd4 moved AES-XTS to an implementation-specific cipher function but did not carry over the generic iv_set guard. This allowed AES-XTS operations initialized with a NULL IV to proceed. Restore the missing iv_set check before processing input and add an evp_extra_test regression covering both a valid-IV control and the missing-IV failure case. Fixes #31755 Reviewed-by: Simo Sorce Reviewed-by: Paul Dale MergeDate: Wed Jul 8 18:16:55 2026 (Merged from https://github.com/openssl/openssl/pull/31756) --- .../implementations/ciphers/cipher_aes_xts.c | 3 +- test/evp_extra_test.c | 65 +++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/providers/implementations/ciphers/cipher_aes_xts.c b/providers/implementations/ciphers/cipher_aes_xts.c index 9be87374d1..54820469a9 100644 --- a/providers/implementations/ciphers/cipher_aes_xts.c +++ b/providers/implementations/ciphers/cipher_aes_xts.c @@ -180,7 +180,8 @@ static int aes_xts_cipher(void *vctx, unsigned char *out, size_t *outl, } else #endif { - if (ctx->xts.key1 == NULL || ctx->xts.key2 == NULL) + if (ctx->xts.key1 == NULL || ctx->xts.key2 == NULL + || !ctx->base.iv_set) return 0; } diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c index e1b80256bd..7c8059350a 100644 --- a/test/evp_extra_test.c +++ b/test/evp_extra_test.c @@ -7901,6 +7901,70 @@ end: return ret; } +static int test_aes_xts_rejects_missing_iv(void) +{ + static const unsigned char key[32] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f + }; + static const unsigned char in[32] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00 + }; + static const unsigned char iv[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 + }; + EVP_CIPHER_CTX *ctx = NULL; + EVP_CIPHER *cipher = NULL; + unsigned char out[sizeof(in)]; + int outl = 0; + int ret = 0; + + if ((cipher = EVP_CIPHER_fetch(testctx, "AES-128-XTS", testpropq)) == NULL) + return TEST_skip("AES-128-XTS cipher is not available"); + + /* Initialize with a valid IV as a positive control */ + if (!TEST_ptr(ctx = EVP_CIPHER_CTX_new()) + || !TEST_true(EVP_EncryptInit_ex2(ctx, cipher, key, iv, NULL)) + || !TEST_true(EVP_EncryptUpdate(ctx, out, &outl, in, sizeof(in))) + || !TEST_int_eq(outl, (int)sizeof(in))) + goto err; + + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + outl = 0; + + if (!TEST_ptr(ctx = EVP_CIPHER_CTX_new())) + goto err; + + /* Initialize with a NULL IV, which may fail immediately */ + ERR_set_mark(); + if (!EVP_EncryptInit_ex2(ctx, cipher, key, NULL, NULL)) { + ERR_pop_to_mark(); + ret = 1; + goto err; + } + + /* Test EVP_EncryptUpdate after NULL IV initialization, which should fail */ + if (!TEST_false(EVP_EncryptUpdate(ctx, out, &outl, in, sizeof(in)))) { + ERR_clear_last_mark(); + goto err; + } + ERR_pop_to_mark(); + + ret = 1; + +err: + EVP_CIPHER_free(cipher); + EVP_CIPHER_CTX_free(ctx); + return ret; +} + /* * Cross-driver round-trip test for AEAD one-shot vs streaming paths. * @@ -9098,6 +9162,7 @@ int setup_tests(void) ADD_TEST(test_invalid_ctx_for_digest); ADD_TEST(test_evp_cipher_negative_length); + ADD_TEST(test_aes_xts_rejects_missing_iv); ADD_TEST(test_evp_cipher_pipeline); From 38c7ace6c17267ca9a5dde25c7f025ba418a0c36 Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Tue, 30 Jun 2026 09:01:20 +0200 Subject: [PATCH 196/349] crypto/x509/x509_lu.c: check X509_OBJECT_up_ref_count() in x509_object_dup() the return value of X509_OBJECT_up_ref_count() was ignored. If the reference count increment fails, x509_object_dup() still returned a duplicate X509_OBJECT whose ->data aliases the source X509/X509_CRL without a reference actually having been taken. Freeing that duplicate later drops a reference it never held, leading to a premature free and use-after-free of the shared object. Signed-off-by: Nikola Pajkovsky Reviewed-by: Eugene Syromiatnikov Reviewed-by: Neil Horman Reviewed-by: Norbert Pocs MergeDate: Wed Jul 8 18:20:10 2026 (Merged from https://github.com/openssl/openssl/pull/31811) --- crypto/x509/x509_lu.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crypto/x509/x509_lu.c b/crypto/x509/x509_lu.c index 01e1969d19..d1f4c1eb40 100644 --- a/crypto/x509/x509_lu.c +++ b/crypto/x509/x509_lu.c @@ -741,7 +741,12 @@ static X509_OBJECT *x509_object_dup(const X509_OBJECT *obj) ret->type = obj->type; ret->data = obj->data; - X509_OBJECT_up_ref_count(ret); + + if (!X509_OBJECT_up_ref_count(ret)) { + OPENSSL_free(ret); + return NULL; + } + return ret; } From 6b6fc647116d61ea8c1250b4ad55b7696dad7b6a Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Fri, 10 Apr 2026 21:31:19 +0200 Subject: [PATCH 197/349] Add WRAP build.info variable This is used for adding per target --wrap ld flags for each listed function. Reviewed-by: Richard Levitte Reviewed-by: Tomas Mraz MergeDate: Thu Jul 9 17:39:12 2026 (Merged from https://github.com/openssl/openssl/pull/30788) --- Configurations/unix-Makefile.tmpl | 9 ++++++++- Configure | 13 +++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Configurations/unix-Makefile.tmpl b/Configurations/unix-Makefile.tmpl index f27517e565..19455e2b11 100644 --- a/Configurations/unix-Makefile.tmpl +++ b/Configurations/unix-Makefile.tmpl @@ -2021,6 +2021,13 @@ EOF push @linkdirs, $d unless grep { $d eq $_ } @linkdirs; } } + my $wrapflags = ''; + if (defined $unified_info{wraps}->{$args{bin}}) { + $wrapflags = join(' ', + map { "-Wl,--wrap=$_" } + @{$unified_info{wraps}->{$args{bin}}}); + $wrapflags = ' ' . $wrapflags; + } my $linkflags = join("", map { $_." " } @linkdirs); my $linklibs = join("", map { $_." " } @linklibs); my $cmd = '$(CC)'; @@ -2038,7 +2045,7 @@ EOF return <<"EOF"; $bin: $deps rm -f $bin - \$\${LDCMD:-$cmd} $cmdflags $linkflags\$(BIN_LDFLAGS) \\ + \$\${LDCMD:-$cmd} $cmdflags $linkflags\$(BIN_LDFLAGS)$wrapflags \\ -o $bin \\ $objs \\ $linklibs\$(BIN_EX_LIBS) diff --git a/Configure b/Configure index 30671d0af5..d3e6563c07 100755 --- a/Configure +++ b/Configure @@ -2141,6 +2141,7 @@ if ($builder eq "unified") { my %includes = (); my %defines = (); my %depends = (); + my %wraps = (); my %generate = (); my %imagedocs = (); my %htmldocs = (); @@ -2396,6 +2397,11 @@ if ($builder eq "unified") { \$attributes{depends}, $+{ATTRIBS}, tokenize($expand_variables->($+{VALUE}))) if !@skip || $skip[$#skip] > 0; }, + qr/^\s* WRAP ${index_re} \s* = \s* ${value_re} \s* $/x + => sub { $push_to->(\%wraps, $expand_variables->($+{INDEX}), + undef, undef, + tokenize($expand_variables->($+{VALUE}))) + if !@skip || $skip[$#skip] > 0; }, qr/^\s* GENERATE ${index_re} ${attribs_re} \s* = \s* ${value_re} \s* $/x => sub { $push_to->(\%generate, $expand_variables->($+{INDEX}), \$attributes{generate}, $+{ATTRIBS}, @@ -2682,6 +2688,13 @@ if ($builder eq "unified") { } } + foreach my $dest (keys %wraps) { + my $ddest = cleanfile($buildd, $dest, $blddir); + foreach my $fn (@{$wraps{$dest}}) { + push @{$unified_info{wraps}->{$ddest}}, $fn; + } + } + foreach my $section (keys %imagedocs) { foreach (@{$imagedocs{$section}}) { my $imagedocs = cleanfile($buildd, $_, $blddir); From bc79a23e6c68512d9929576995007009c9daa2d0 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sun, 12 Apr 2026 15:57:43 +0200 Subject: [PATCH 198/349] Deprecate unit-test configure option and SSL_test_functions The unit-test configure option exists only to expose the SSL_test_functions() API allowing to overwrite ssl_init_wbio_buffer. Instead of renaming it, deprecate the option and the SSL_test_functions() function so both can be removed in OpenSSL 5.0. Assisted-by: Claude:claude-fable-5 Reviewed-by: Richard Levitte Reviewed-by: Tomas Mraz MergeDate: Thu Jul 9 17:39:13 2026 (Merged from https://github.com/openssl/openssl/pull/30788) --- CHANGES.md | 5 +++++ INSTALL.md | 2 ++ include/openssl/ssl.h.in | 3 +++ ssl/ssl_utst.c | 3 +++ util/libssl.num | 2 +- 5 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index da1eeb519a..275dc6c7c8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -31,6 +31,11 @@ OpenSSL Releases ### Changes between 4.0 and 4.1 [xx XXX xxxx] + * Deprecated the `enable-unit-test` configure option and the + `SSL_test_functions()` function. Both will be removed in OpenSSL 5.0. + + *Jakub Zelenka* + * Added -testmode option for `s_time` app. *Jakub Zelenka* diff --git a/INSTALL.md b/INSTALL.md index f087ab93fd..f907a89556 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1098,6 +1098,8 @@ Enable additional unit test APIs. This should not typically be used in production deployments. +This option is deprecated and will be removed in OpenSSL 5.0. + ### no-uplink Don't build support for UPLINK interface. diff --git a/include/openssl/ssl.h.in b/include/openssl/ssl.h.in index 7fc254d75e..b010a6e677 100644 --- a/include/openssl/ssl.h.in +++ b/include/openssl/ssl.h.in @@ -2784,8 +2784,11 @@ __owur void *SSL_CTX_get0_security_ex_data(const SSL_CTX *ctx); int OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings); #ifndef OPENSSL_NO_UNIT_TEST +#ifndef OPENSSL_NO_DEPRECATED_4_1 +OSSL_DEPRECATEDIN_4_1 __owur const struct openssl_ssl_test_functions *SSL_test_functions(void); #endif +#endif __owur int SSL_free_buffers(SSL *ssl); __owur int SSL_alloc_buffers(SSL *ssl); diff --git a/ssl/ssl_utst.c b/ssl/ssl_utst.c index 91be7398ca..7ff8932e42 100644 --- a/ssl/ssl_utst.c +++ b/ssl/ssl_utst.c @@ -7,6 +7,9 @@ * https://www.openssl.org/source/license.html */ +/* SSL_test_functions() is deprecated but still needs to be implemented */ +#include "internal/deprecated.h" + #include "ssl_local.h" #ifndef OPENSSL_NO_UNIT_TEST diff --git a/util/libssl.num b/util/libssl.num index 9fe992bc39..9b24054a7c 100644 --- a/util/libssl.num +++ b/util/libssl.num @@ -558,7 +558,7 @@ SSL_CTX_get_security_callback 557 4_0_0 EXIST::FUNCTION: SSL_CTX_set0_security_ex_data 558 4_0_0 EXIST::FUNCTION: SSL_CTX_get0_security_ex_data 559 4_0_0 EXIST::FUNCTION: OPENSSL_init_ssl 560 4_0_0 EXIST::FUNCTION: -SSL_test_functions 561 4_0_0 EXIST::FUNCTION:UNIT_TEST +SSL_test_functions 561 4_0_0 EXIST::FUNCTION:DEPRECATEDIN_4_1,UNIT_TEST SSL_free_buffers 562 4_0_0 EXIST::FUNCTION: SSL_alloc_buffers 563 4_0_0 EXIST::FUNCTION: SSL_CTX_set_session_ticket_cb 564 4_0_0 EXIST::FUNCTION: From acd7679800cd766bd855c4995a3d3799be9e2516 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sat, 20 Jun 2026 14:06:09 +0200 Subject: [PATCH 199/349] Add unit testing cmocka based framework This adds the unit testing framework that extends the build so it can be enabled and tests are built. It is executed as part of the test using a recipe which executes all unit tests. The documentation is added with more info about writing the unit tests. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Richard Levitte Reviewed-by: Tomas Mraz MergeDate: Thu Jul 9 17:39:14 2026 (Merged from https://github.com/openssl/openssl/pull/30788) --- .github/workflows/ci.yml | 4 +- .github/workflows/coveralls.yml | 4 +- CHANGES.md | 5 + Configurations/unix-Makefile.tmpl | 6 +- Configure | 32 +- INSTALL.md | 24 +- test/build.info | 4 + test/recipes/02-test_unit.t | 45 +++ test/unit/.gitignore | 13 + test/unit/README.md | 558 ++++++++++++++++++++++++++++++ test/unit/build.info | 1 + 11 files changed, 690 insertions(+), 6 deletions(-) create mode 100644 test/recipes/02-test_unit.t create mode 100644 test/unit/.gitignore create mode 100644 test/unit/README.md create mode 100644 test/unit/build.info diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2de5c58009..b10ab496c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -88,12 +88,14 @@ jobs: run: git submodule update --init --depth 1 fuzz/corpora - name: localegen run: sudo locale-gen tr_TR.UTF-8 + - name: cmocka + run: sudo apt-get -y install libcmocka-dev - name: fipsvendor # Make one fips build use a customized FIPS vendor run: echo "FIPS_VENDOR=CI" >> VERSION.dat - name: config # enable-quic is on by default, but we leave it here to check we're testing the explicit enable somewhere - run: CC=gcc ./config --strict-warnings --banner=Configured enable-demos enable-h3demo enable-ec_explicit_curves enable-sslkeylog enable-fips enable-quic enable-lms && perl configdata.pm --dump + run: CC=gcc ./config --strict-warnings --banner=Configured enable-demos enable-h3demo enable-ec_explicit_curves enable-sslkeylog enable-fips enable-quic enable-lms enable-unit-tests && perl configdata.pm --dump - name: make run: make -s -j4 - name: get cpu info diff --git a/.github/workflows/coveralls.yml b/.github/workflows/coveralls.yml index 2a70b2a1bd..34075a3566 100644 --- a/.github/workflows/coveralls.yml +++ b/.github/workflows/coveralls.yml @@ -47,7 +47,7 @@ jobs: MATRIX=$(cat << EOF [{ "branch": "master", - "extra_config": "enable-fips enable-tfo enable-lms enable-crypto-mdebug" + "extra_config": "enable-fips enable-tfo enable-lms enable-crypto-mdebug enable-unit-tests" }, { "branch": "openssl-4.0", "extra_config": "enable-fips enable-tfo enable-lms enable-crypto-mdebug" @@ -93,7 +93,7 @@ jobs: run: | sudo apt-get update sudo apt-get -yq install lcov - sudo apt-get -yq install bison gettext keyutils ldap-utils libldap2-dev libkeyutils-dev python3 python3-paste python3-pyrad slapd tcsh python3-virtualenv virtualenv python3-kdcproxy + sudo apt-get -yq install bison gettext keyutils ldap-utils libcmocka-dev libldap2-dev libkeyutils-dev python3 python3-paste python3-pyrad slapd tcsh python3-virtualenv virtualenv python3-kdcproxy - name: install Test2::V0 for gost_engine testing uses: perl-actions/install-with-cpanm@10d60f00b4073f484fc29d45bfbe2f776397ab3d #v1.7 with: diff --git a/CHANGES.md b/CHANGES.md index 275dc6c7c8..0c9366b01e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -31,6 +31,11 @@ OpenSSL Releases ### Changes between 4.0 and 4.1 [xx XXX xxxx] + * Added unit tests setup activated via `enable-unit-tests` option. This works + only on platforms with ld `--wrap` support (Linux, BSD). + + *Jakub Zelenka* + * Deprecated the `enable-unit-test` configure option and the `SSL_test_functions()` function. Both will be removed in OpenSSL 5.0. diff --git a/Configurations/unix-Makefile.tmpl b/Configurations/unix-Makefile.tmpl index 19455e2b11..f8e44e73f7 100644 --- a/Configurations/unix-Makefile.tmpl +++ b/Configurations/unix-Makefile.tmpl @@ -504,6 +504,8 @@ BIN_LDFLAGS={- join(' ', $target{bin_lflags} || (), '$(CNF_LDFLAGS)', '$(LDFLAGS)') -} BIN_EX_LIBS=$(CNF_EX_LIBS) $(EX_LIBS) +CMOCKA_LIBS={- $config{cmocka_libs} // '' -} + # CPPFLAGS_Q is used for one thing only: to build up buildinf.h CPPFLAGS_Q={- $cppflags1 =~ s|([\\"])|\\$1|g; $cppflags2 =~ s|([\\"])|\\$1|g; @@ -2022,11 +2024,13 @@ EOF } } my $wrapflags = ''; + my $cmocka = ''; if (defined $unified_info{wraps}->{$args{bin}}) { $wrapflags = join(' ', map { "-Wl,--wrap=$_" } @{$unified_info{wraps}->{$args{bin}}}); $wrapflags = ' ' . $wrapflags; + $cmocka = ' $(CMOCKA_LIBS)'; } my $linkflags = join("", map { $_." " } @linkdirs); my $linklibs = join("", map { $_." " } @linklibs); @@ -2048,7 +2052,7 @@ $bin: $deps \$\${LDCMD:-$cmd} $cmdflags $linkflags\$(BIN_LDFLAGS)$wrapflags \\ -o $bin \\ $objs \\ - $linklibs\$(BIN_EX_LIBS) + $linklibs\$(BIN_EX_LIBS)$cmocka EOF } sub in2script { diff --git a/Configure b/Configure index d3e6563c07..10a218954e 100755 --- a/Configure +++ b/Configure @@ -577,6 +577,7 @@ my @disablables_features = ( "ubsan", "ui-console", "unit-test", + "unit-tests", "uplink", "weak-ssl-ciphers", "zlib-dynamic", @@ -654,6 +655,7 @@ our %disabled = ( # "what" => "comment" "trace" => "default", "ubsan" => "default", "unit-test" => "default", + "unit-tests" => "default", "weak-ssl-ciphers" => "default", "zlib" => "default", "zlib-dynamic" => "default", @@ -723,7 +725,7 @@ my @disable_cascades = ( "stdio" => [ "apps", "egd" ], "apps" => [ "tests" ], - "tests" => [ "external-tests" ], + "tests" => [ "external-tests", "unit-tests" ], "comp" => [ "zlib", "brotli", "zstd" ], "sm3" => [ "sm2" ], sub { !$disabled{"unit-test"} } => [ "heartbeats" ], @@ -1115,6 +1117,14 @@ while (@argvcopy) { $withargs{fuzzer_include}=$1; } + elsif (/^--with-cmocka-lib=(.*)$/) + { + $withargs{cmocka_lib}=$1; + } + elsif (/^--with-cmocka-include=(.*)$/) + { + $withargs{cmocka_include}=$1; + } elsif (/^--with-rand-seed=(.*)$/) { foreach my $x (split(m|,|, $1)) @@ -1917,6 +1927,18 @@ unless ($disabled{winstore}) { push @{$config{openssl_other_defines}}, "OPENSSL_NO_KTLS" if ($disabled{ktls}); +unless ($disabled{"unit-tests"}) { + if ($target =~ /^linux/ || $target =~ /^BSD/) { + $config{cmocka_includes} = + $withargs{cmocka_include} ? [$withargs{cmocka_include}] : []; + $config{cmocka_libs} = $withargs{cmocka_lib} + ? "-L$withargs{cmocka_lib} -lcmocka" + : "-lcmocka"; + } else { + disable('no-wrap-support', 'unit-tests'); + } +} + # Get the extra flags used when building shared libraries and modules. We # do this late because some of them depend on %disabled. @@ -3027,6 +3049,14 @@ EOF } } +# Add cmocka include path to all targets that use WRAP +if (!$disabled{"unit-tests"} && @{$config{cmocka_includes} // []}) { + foreach my $dest (keys %{$unified_info{wraps} // {}}) { + push @{$unified_info{includes}->{$dest}}, + @{$config{cmocka_includes}}; + } +} + # For the schemes that need it, we provide the old *_obj configs # from the *_asm_obj ones foreach (grep /_(asm|aux)_src$/, keys %target) { diff --git a/INSTALL.md b/INSTALL.md index f907a89556..20535f2c27 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -427,6 +427,22 @@ The names of the libraries are: * brotlidec.lib * brotlienc.lib +### with-cmocka-include + + --with-cmocka-include=DIR + +The directory for the location of the cmocka include file. This option is only +necessary if [enable-unit-tests](#enable-unit-tests) is used and the include +file is not already on the system include path. + +### with-cmocka-lib + + --with-cmocka-lib=DIR + +The directory containing the cmocka library. This option is only necessary if +[enable-unit-tests](#enable-unit-tests) is used and the library is not already +on the system library path. + ### with-zlib-include --with-zlib-include=DIR @@ -817,6 +833,12 @@ external test suites are currently supported: See the file [test/README-external.md](test/README-external.md) for further details. +### enable-unit-tests + +Enable building and running unit tests. + +This works only on platforms supporting ld `--wrap` option like Linux and BSD. + ### no-filenames Don't compile in filename and line number information (e.g. for errors and @@ -1094,7 +1116,7 @@ The User Interface console method enables text based console prompts. ### enable-unit-test -Enable additional unit test APIs. +Enable exposing SSL_test_functions for overwriting ssl_init_wbio_buffer. This should not typically be used in production deployments. diff --git a/test/build.info b/test/build.info index c40c14e8f1..86174be7c5 100644 --- a/test/build.info +++ b/test/build.info @@ -9,6 +9,10 @@ IF[{- !$disabled{hqinterop} -}] SUBDIRS=quic-openssl-docker ENDIF +IF[{- !$disabled{"unit-tests"} -}] + SUBDIRS=unit +ENDIF + # Auxiliary program source (copied from ../apps/build.info) IF[{- $config{target} =~ /^(?:VC-|mingw|BC-)/ -}] # It's called 'init', but doesn't have much 'init' in it... diff --git a/test/recipes/02-test_unit.t b/test/recipes/02-test_unit.t new file mode 100644 index 0000000000..0c43c28a36 --- /dev/null +++ b/test/recipes/02-test_unit.t @@ -0,0 +1,45 @@ +#! /usr/bin/env perl +# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +use strict; +use warnings; + +use File::Find; +use File::Spec::Functions qw(abs2rel); + +use OpenSSL::Test qw(:DEFAULT bldtop_dir); +use OpenSSL::Test::Utils; + +setup("test_unit"); + +my $unit_dir = bldtop_dir('test', 'unit'); + +my @tests = (); +if (-d $unit_dir) { + find({ + wanted => sub { + return unless -f $_ && -x $_; + return unless $_ =~ m|/test_[^/]*$|; + return if $_ =~ m|\.\w+$|; + push @tests, $_; + }, + no_chdir => 1, + }, $unit_dir); +} + +@tests = sort @tests; + +plan skip_all => "No unit tests built (enable-unit-tests not set?)" + unless @tests; + +plan tests => scalar @tests; + +foreach my $test_bin (@tests) { + my $name = abs2rel($test_bin, $unit_dir); + ok(run(cmd([$test_bin])), "unit: $name"); +} diff --git a/test/unit/.gitignore b/test/unit/.gitignore new file mode 100644 index 0000000000..2576bf0319 --- /dev/null +++ b/test/unit/.gitignore @@ -0,0 +1,13 @@ +* +!*/ +!*.c +!*.h +!*.inc +!*.pl +!*.t +!*.txt +!*.cnf +!*.pem +!build.info +!.gitignore +!README* diff --git a/test/unit/README.md b/test/unit/README.md new file mode 100644 index 0000000000..42fd1873d5 --- /dev/null +++ b/test/unit/README.md @@ -0,0 +1,558 @@ +OpenSSL Unit Tests +================== + +This directory holds the OpenSSL *unit* tests. Their purpose is to test a +single function, or a small group of related functions, in isolation by +replacing the other functions it calls with mocks. This makes it possible to +test logic that is otherwise hard to reach: error and failure paths of +dependencies, branches that depend on the exact arguments passed to a +collaborator, or code whose real dependencies would require network access, +specific hardware, or elaborate setup. Each test can then drive the function +under test through a precise, fully controlled sequence of calls and return +values, and assert on exactly how it interacts with its surroundings. + +Unit tests are not meant to replace the broader, integration-style testing that +makes up most of `test/`, and they are not the right tool for everything. They +are used for selected, self-contained pieces of the library where the mocking +boundary is clean and the payoff is high, the BIO layer being the main example. +Most code continues to be tested through the ordinary recipes (typically built +on `testutil`, and generally without mocking). + +Requirements +------------ + +The tests are built on [cmocka], a lightweight C unit-testing framework, and +rely on the GNU/BSD linker's `--wrap` option to intercept calls into the +function under test's dependencies. Because `--wrap` is required, unit tests +are only built on platforms that support it (Linux and BSD only at present), +and only when the build is configured with `enable-unit-tests`. + +[cmocka]: https://cmocka.org/ + +Tests must build and run against cmocka 1.1.5, as that is still the version +shipped by some currently supported enterprise and LTS distributions. Do not +rely on APIs introduced in cmocka 2.x, since a test that needs them cannot be +built on those systems; when in doubt, check against the 1.1.5 headers rather +than the latest online documentation. + +Building and Running +-------------------- + +Unit tests are disabled by default. To build them, configure with +`enable-unit-tests` and make sure the cmocka development files are installed +on the system: + +```console +$ sudo apt-get install libcmocka-dev # Debian/Ubuntu +$ ./config enable-unit-tests +$ make +``` + +If cmocka is installed in a non-standard location, point the build at it with: + +```console +$ ./config enable-unit-tests \ + --with-cmocka-include=/path/to/include \ + --with-cmocka-lib=/path/to/lib +``` + +On a platform without `--wrap` support, `enable-unit-tests` is silently turned +off during configuration; the rest of the build proceeds normally. + +The whole unit-test suite runs as part of the normal test target: + +```console +$ make test +``` + +It is gathered under a single recipe, so it can also be run on its own: + +```console +$ make test TESTS=test_unit +``` + +The recipe discovers every executable named `test_*` under the build's +`test/unit` tree and runs each one, so a newly added test binary is picked up +automatically once it builds. Each binary reports its results in TAP, which the +harness consumes directly. + +Because each test is an ordinary standalone executable, it can also be run +directly, which is convenient when debugging a single failure under a debugger: + +```console +$ gdb test/unit/crypto/foo/test_bar +``` + +Running the binary on its own prints its TAP output to the terminal and makes +it straightforward to set breakpoints in a specific test, mock, or in the +function under test. + +For debugging it is worth configuring the build with `--debug` as well, e.g. +`./config enable-unit-tests --debug`. A normal build is optimized, which makes +stepping through code and inspecting variables awkward; `--debug` lowers the +optimization level and adds debug information, giving a much more predictable +experience under gdb. + +Anatomy of a Unit Test +---------------------- + +A unit test is a single C source file laid out under `test/unit/` in a path +that mirrors the location of the code it exercises. For example, code that +lives in `crypto/foo/bar.c` is tested by `test/unit/crypto/foo/test_bar.c`. The +file is self-contained: it provides its own `main()`, registers a list of test +cases, and runs them as a cmocka group. + +The body of a test file is organised into a few clearly separated sections, +conventionally introduced by short comments, in this order: + + * the `__wrap_*` mock implementations (`/* wraps */`), + * thin `expect_*` helpers that program each mock (`/* expectations */`), + * any shared helpers (fake methods, accessors, reset routines), + * the `setup`/`teardown` fixtures, + * the test functions themselves, and + * `main()`, which builds the `CMUnitTest` array and runs it. + +Keeping these sections in this order and clearly labelled makes a test file +predictable to read and easy to extend. + +### main() and the test list + +`main()` declares a `struct CMUnitTest` array, selects TAP output, and runs the +group: + +```c +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_something_simple), + cmocka_unit_test_setup_teardown(test_with_fixture, setup, teardown), + }; + + cmocka_set_message_output(CM_OUTPUT_TAP); + + return cmocka_run_group_tests(tests, NULL, NULL); +} +``` + +Always select `CM_OUTPUT_TAP` so the harness can parse the results. Use +`cmocka_unit_test()` for tests that need no per-test fixture, and +`cmocka_unit_test_setup_teardown()` when a test needs a fresh object built +before it and cleaned up after. The last two arguments to +`cmocka_run_group_tests()` are an optional group-level setup and teardown, run +once before and after the whole group; pass `NULL` when they are not needed. + +It is common to wrap the registration macros in a short local macro when most +tests share the same fixture, e.g. + +```c +#define MY_TEST(name) \ + cmocka_unit_test_setup_teardown(name, setup, teardown) +``` + +### A test function + +Each test is a function with the signature `void (void **state)`. The `state` +argument carries whatever a `setup` fixture stored there; tests that do not use +it should cast it to `void` to silence warnings: + +```c +static void test_addr_family(void **state) +{ + BIO_ADDR ap; + + (void)state; + memset(&ap, 0, sizeof(ap)); + ap.sa.sa_family = AF_INET; + assert_int_equal(BIO_ADDR_family(&ap), AF_INET); +} +``` + +Assertions come from cmocka: `assert_int_equal`, `assert_ptr_equal`, +`assert_true`, `assert_false`, `assert_null`, `assert_non_null`, +`assert_string_equal`, `assert_memory_equal`, and friends. A failing assertion +aborts the current test and marks it failed without disturbing the others. + +How the Expectation Mechanism Works +----------------------------------- + +Before writing mocks it helps to understand what cmocka is actually doing, +because the model is simple once stated plainly and it makes the rest of the +API obvious. + +For each `(function, parameter)` pair cmocka keeps an internal queue. The +`expect_*()` macros, called from the test, push values onto these queues. The +`check_expected()` macro, called from inside the mock, pops the next value and +compares it against the argument the mock actually received; a mismatch fails +the test. Return values work the same way: `will_return()` pushes a value from +the test, and `mock_type()`/`mock_ptr_type()` pops it inside the mock to use as +the return value. Call accounting is analogous: `expect_function_call()` pushes +an expected call and `function_called()` consumes one. + +So a test programs, in order, the calls it expects the function under test to +make, and the mocks consume those programmed entries as the calls actually +happen. Entries are consumed in the order they were queued, which is why the +`expect_*`/`will_return` calls in a test must be written in the same order the +function under test will call its dependencies. At the end of the test cmocka +fails if any queued entry was never consumed, or if a mock is called with +nothing queued for it. This is what turns the expected interaction sequence +into a checked specification rather than a loose suggestion. + +Because each entry covers a single call, a function that is expected to be +called more than once is programmed by pushing the entries that many times, in +the order the calls will occur: + +```c +/* the SUT is expected to call BIO_socket twice */ +expect_BIO_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, INVALID_SOCKET); +expect_BIO_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, FAKE_SOCKET); +``` + +cmocka also offers `_count` variants (such as `will_return_count()`, +`expect_value_count()` and `expect_function_calls()`) that program one entry +for a given number of calls in a single statement. These are not just a +shorthand for repeating the macro: they carry a different ordering semantics. +Repeating `expect_function_call()` pins each call's position in the overall +sequence, so any other expected call programmed between two of them must +actually occur between them. `expect_function_calls(f, 2)`, by contrast, only +requires that `f` is called twice somewhere; other calls may fall before, +after, or in between without failing the test. Prefer repeating the plain +macros when the interleaving matters (which is the common case), and reach for +the count variants only when a function is genuinely called many times and its +position relative to the others is not what the test is checking. + +Two consequences are worth keeping in mind: + + * **cmocka has nothing to do with `--wrap`.** The expectation machinery is + just these queues plus the `check_expected`/`mock`/`function_called` + macros. It works in any function whose body calls them. `--wrap` is merely + the linker trick OpenSSL uses to *substitute* a dependency with a mock; the + two are independent. The same `expect_*` style is used below for purpose- + built fake objects that are never wrapped at all (see Fixtures). + + * Because matching is per parameter and in order, a mock must call + `check_expected()` for exactly the parameters the test programs with + `expect_*()`, and the `will_return`/`mock_type` counts must balance. + +Mocking Dependencies with --wrap +-------------------------------- + +The core technique is link-time function interception. When a binary is linked +with `-Wl,--wrap=foo`, every call to `foo` is redirected to a function named +`__wrap_foo`, and the original is still reachable as `__real_foo`. This lets a +test replace the function under test's dependencies with mocks that record how +they were called and return whatever the test dictates. + +### Declaring the wraps + +The set of wrapped symbols for a test binary is declared in the `build.info` +file (see below). For each wrapped symbol the test file provides a +`__wrap_` function with exactly the same signature as the real one. A +prototype is also needed to satisfy `-Wmissing-prototypes`: + +```c +int __wrap_BIO_socket(int domain, int socktype, int protocol, int options); + +int __wrap_BIO_socket(int domain, int socktype, int protocol, int options) +{ + function_called(); + check_expected(domain); + check_expected(socktype); + check_expected(protocol); + check_expected(options); + return mock_type(int); +} +``` + +A typical mock does three things: + + * `function_called()` records that the function was invoked, balanced against + the test's `expect_function_call()`. + * `check_expected(param)` (or `check_expected_ptr(param)` for pointers) + verifies the argument against the value the test queued. Use the `_ptr` + variant for pointer parameters. + * `mock_type(T)` (or `mock_ptr_type(T)` for pointers) returns the value the + test queued for this call. A `void` mock omits this. + +Mocks may also have deliberate side effects when the real function would +produce one that the code under test depends on. For example, a function that +fills a caller-supplied buffer should have its mock write into that buffer, and +a fatal-error reporter that the code expects to flip a state flag should do so: + +```c +int __wrap_ssl_fill_hello_random(SSL_CONNECTION *s, int server, + unsigned char *field, size_t len, DOWNGRADE dgrd) +{ + function_called(); + check_expected_ptr(s); + check_expected(server); + check_expected_ptr(field); + check_expected(len); + check_expected(dgrd); + + if (field != NULL) + memset(field, 0xAB, len); + + return mock_type(int); +} +``` + +Keep these side effects minimal and confined to what the function under test +genuinely observes; the goal is to reproduce the contract of the real function, +not to re-implement it. + +### Programming the mocks: expectations + +Rather than scatter `expect_function_call`/`expect_value`/`will_return` calls +through every test, wrap each mock in a small `expect_` helper that takes +the expected arguments and the return value. This keeps the tests readable and, +crucially, gives a single place to update when a function's signature or call +contract changes: + +```c +static void expect_BIO_socket(int domain, int socktype, int protocol, + int options, int rc) +{ + expect_function_call(__wrap_BIO_socket); + expect_value(__wrap_BIO_socket, domain, domain); + expect_value(__wrap_BIO_socket, socktype, socktype); + expect_value(__wrap_BIO_socket, protocol, protocol); + expect_value(__wrap_BIO_socket, options, options); + will_return(__wrap_BIO_socket, rc); +} +``` + +The most useful cmocka primitives here are: + + * `expect_function_call(f)`: expect one call to `f`. Pair every + `function_called()` in a mock with one of these. + * `expect_value(f, param, value)`: the argument must equal `value`. This is + consumed by `check_expected(param)`. + * `expect_any(f, param)`: the argument may be anything; still consumed by + `check_expected(param)`, so it must be present whenever the mock checks + that parameter. + * `will_return(f, value)`: queue a return value for the next call, + retrieved by `mock_type`/`mock_ptr_type`. Queue several in order if the + mock pulls more than one value (e.g. a return code followed by an + out-parameter payload). + +When a mock conditionally retrieves a second value, the matching expectation +must queue it under the same condition, so the queues stay aligned: + +```c +static void expect_BIO_lookup(BIO_ADDRINFO *res, int rc) +{ + expect_function_call(__wrap_BIO_lookup); + expect_any(__wrap_BIO_lookup, host); + expect_any(__wrap_BIO_lookup, service); + expect_value(__wrap_BIO_lookup, lookup_type, BIO_LOOKUP_SERVER); + expect_any(__wrap_BIO_lookup, family); + expect_any(__wrap_BIO_lookup, socktype); + will_return(__wrap_BIO_lookup, rc); + if (rc == 1) + will_return(__wrap_BIO_lookup, res); +} +``` + +### Writing a test against the mocks + +With the helpers in place, a test reads as: arrange the object, declare the +expected sequence of calls, invoke the function under test, and assert on the +result and any observable state. + +```c +static void test_socket_then_listen_fails(void **state) +{ + BIO *bio = *state; + + expect_BIO_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, FAKE_SOCKET); + expect_BIO_listen(FAKE_SOCKET, &expected_addr, 0, 0); + expect_BIO_closesocket(FAKE_SOCKET, 0); + + assert_true(BIO_do_accept(bio) <= 0); +} +``` + +If the function under test makes a call that was not programmed, or fails to +make one that was, or passes an argument that does not match, cmocka fails the +test and reports the mismatch. + +Fixtures and Real Fake Objects +------------------------------ + +A `setup` function allocates or initialises whatever object the test needs and +stores it through `*state`; the matching `teardown` releases it. Returning +non-zero from either aborts the test as an error. + +```c +static int setup(void **state) +{ + BIO *bio = BIO_new(BIO_s_accept()); + + assert_non_null(bio); + *state = bio; + return 0; +} + +static int teardown(void **state) +{ + if (*state != NULL) + BIO_free(*state); + return 0; +} +``` + +A group-level setup/teardown (the last two arguments to +`cmocka_run_group_tests`) is the place for one-time work shared by every test, +such as initialising static fixtures used across the file. + +Two practical cautions apply when fixtures interact with wrapped functions: + + * Teardown can itself trigger wrapped calls. If freeing the object under test + would invoke a wrapped function (for instance, closing a socket), reset the + relevant fields to safe sentinels before the free so no *unexpected* mock + call is made, or program the expectation for it. A common pattern is a + small `reset_for_teardown()` helper called at the end of any test that left + such state behind. + + * It is often cleaner to drive an object through its public interface with a + *real* minimal fake than to mock everything. For example, building a small + fake `BIO_METHOD` whose read/write callbacks are themselves cmocka mocks + (using the same `function_called`/`check_expected`/`mock_type` machinery, + even though nothing is wrapped) lets a test exercise the forwarding logic + of the object under test without wrapping low-level syscalls. Choose + whichever boundary keeps the test focused on the function actually under + examination. + +Conditional Compilation +------------------------ + +Mirror the `#ifdef`/`#ifndef` guards of the code under test. If a function only +exists under a build option, guard both the test function and its registration +in `main()` with the same condition, so the suite still builds in every +configuration: + +```c +#ifndef OPENSSL_NO_UNIX_SOCK +static void test_addr_make_unix(void **state) +{ + ... +} +#endif + +int main(void) +{ + const struct CMUnitTest tests[] = { +#ifndef OPENSSL_NO_UNIX_SOCK + cmocka_unit_test(test_addr_make_unix), +#endif + ... + }; + ... +} +``` + +When an entire test file only makes sense under some option, guard the whole +body and provide a trivial `main()` for the disabled case so the binary still +links and the recipe still finds something to run: + +```c +#ifndef OPENSSL_NO_SOCK + +/* ... the tests ... */ + +#else + +int main(void) +{ + return 0; +} + +#endif +``` + +Wiring a New Test into the Build +-------------------------------- + +Test binaries are declared in `test/unit/build.info`. A unit test that wraps no +symbols would not be linked against cmocka, so **every** unit test must declare +at least one `WRAP[]` entry: this is what causes both the `--wrap` link flags +and `-lcmocka` to be added for that binary. The directives needed per test are +`PROGRAMS`, `SOURCE`, `INCLUDE`, `DEPEND`, and `WRAP`: + +```text +PROGRAMS{noinst}=crypto/foo/test_bar +SOURCE[crypto/foo/test_bar]=crypto/foo/test_bar.c +INCLUDE[crypto/foo/test_bar]=../../include ../../include/internal \ + ../../crypto/foo +DEPEND[crypto/foo/test_bar]=../../libcrypto.a +WRAP[crypto/foo/test_bar]=BIO_socket BIO_listen BIO_closesocket +``` + +Notes: + + * `PROGRAMS{noinst}` marks the binary as not installed. + * `INCLUDE[]` lists the directories needed to reach the headers the test + uses, including any internal directory that declares the types or the + function under test. The cmocka include path is added automatically to + every target that has a `WRAP[]` entry, so it need not be listed. + * `DEPEND[]` links the appropriate static libraries: `../../libcrypto.a`, + and `../../libssl.a` as well for libssl code. + * `WRAP[]` is the whitespace-separated list of symbols to intercept; it may + be split over several lines with trailing backslashes. List exactly the + dependencies the test mocks. + +Because the recipe discovers binaries by name, no change to the Perl recipe is +needed; building the new `test_*` binary is enough for it to run under +`test_unit`. + +Generating Mock Stubs with mkwraps.pl +------------------------------------- + +Writing the `__wrap_*` and `expect_*` boilerplate by hand for a long `WRAP[]` +list is tedious and error-prone, so the helper script `util/mkwraps.pl` +generates a first draft from the `build.info` declaration. It reads the +`WRAP[]` list, searches the headers under the target's `INCLUDE[]` +directories for each function's prototype, and emits matching wrap functions +and expectation helpers. + +```console +$ ./util/mkwraps.pl --build-info test/unit/build.info \ + --target crypto/foo/test_bar +``` + +Useful options: + + * `--mode wraps|expects|both`: emit only the `__wrap_*` functions, only the + `expect_*` helpers, or both (the default). + * `--include DIR`: add an extra header search directory beyond those in + `INCLUDE[]`. Cumulative. + * `--output FILE`: write to a file instead of standard output. + * `--verbose`: report progress and where each prototype was found. + +The output is a *starting point*, not a finished test. The generated mocks call +`function_called()`, check every parameter, and return a `mock_type` value, but +any real behaviour still has to be added by hand: side effects on +out-parameters, variadic forwarding, conditional `will_return` payloads, and +the use of internal headers for opaque types. Generated `#include` lines and +parameter checks frequently need adjusting. Treat the script as a way to skip +the mechanical typing, then review and edit every generated function. + +Conventions +----------- + +Unit tests follow the usual OpenSSL C coding style (enforced via +clang-format), so it is not repeated here. A few conventions specific to unit +tests keep them consistent and maintainable: + + * Order the file as wraps, expectations, helpers, fixtures, tests, then + `main()`, with the short section-header comments shown above. + * Name test functions `test__` so the suite reads as a list + of behaviours, and add a brief comment on any test whose setup or expected + sequence is not obvious from the name. + * Give every mock a matching `expect_` helper and route all programming + of that mock through it rather than inlining `expect_value`/`will_return` + in the tests, so a change to a function's contract is fixed in one place. + * Keep each test focused on one behaviour, and prefer several small tests + over one test with many branches. + * Always emit TAP output, and always reset fixture state that would otherwise + cause an unexpected wrapped call during teardown. diff --git a/test/unit/build.info b/test/unit/build.info new file mode 100644 index 0000000000..b31f186dfa --- /dev/null +++ b/test/unit/build.info @@ -0,0 +1 @@ +# Make sure all tests have WRAP otherwise -lcmocka is not added. From ac858acc91e9ea6a923add569d340d178f637983 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 30 Apr 2026 21:27:21 +0200 Subject: [PATCH 200/349] Add mkwraps script for generting wraps and expectations This is a helper script that can be used to provide a boilerplate code. Assisted-by: Claude:claude-opus-4-7 Reviewed-by: Richard Levitte Reviewed-by: Tomas Mraz MergeDate: Thu Jul 9 17:39:15 2026 (Merged from https://github.com/openssl/openssl/pull/30788) --- util/mkwraps.pl | 488 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 488 insertions(+) create mode 100755 util/mkwraps.pl diff --git a/util/mkwraps.pl b/util/mkwraps.pl new file mode 100755 index 0000000000..d7e8bcb7db --- /dev/null +++ b/util/mkwraps.pl @@ -0,0 +1,488 @@ +#! /usr/bin/env perl +# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +use strict; +use warnings; + +use Getopt::Long; +use File::Spec::Functions qw(catdir catfile file_name_is_absolute rel2abs); +use File::Basename qw(dirname); + +my $build_info_file; +my $target; +my @extra_includes; +my $output_file; +my $mode = 'both'; +my $verbose = 0; +my $help = 0; + +GetOptions('build-info=s' => \$build_info_file, + 'target=s' => \$target, + 'include=s' => \@extra_includes, + 'output=s' => \$output_file, + 'mode=s' => \$mode, + 'verbose' => \$verbose, + 'help' => \$help) + or die "Error in command line arguments\n"; + +sub help +{ + print STDERR <<"EOF"; +mkwraps.pl [options] + +Options: + + --build-info FILE build.info file containing a WRAP[] entry. + + --target NAME Test program name used as the WRAP[]/INCLUDE[] key. + + --include DIR Extra include directory to search. Cumulative. + + --output FILE Output file. Defaults to stdout. + + --mode MODE What to emit: 'wraps', 'expects' or 'both'. + Defaults to 'both'. + + --verbose Print progress to stderr. + + --help Show this help text. + +For each function name listed in WRAP[], the script searches +the headers (*.h) under each directory in INCLUDE[], resolved +relative to the directory containing the build.info file, plus any +extra --include directories. The search recurses into subdirectories, +mirroring how the compiler resolves via -I flags. +The first header containing a matching declaration provides the +prototype. + +The output is meant as a stub for further editing. Custom logic +(out-parameters, variadic forwarding, side effects on globals) still +needs to be written manually, and the emitted #include directives may +need to be adjusted (e.g. to add internal headers for opaque types). +Long lines in the output are not wrapped. +EOF +} + +if ($help) { + &help(); + exit 0; +} + +unless (defined $build_info_file && defined $target) { + &help(); + exit 1; +} + +die "--mode must be 'wraps', 'expects' or 'both'\n" + unless $mode =~ /^(?:wraps|expects|both)$/; + +my @t = localtime(); +my $YEAR = $t[5] + 1900; + +# Parse the build.info file. We are only interested in two directives, +# WRAP[] and INCLUDE[], either of which may be split +# across several physical lines using the usual backslash continuation. + +my @wraps; +my @includes; + +open(IN, "<$build_info_file") || die "Can't open $build_info_file, $!,"; +my $content = do { local $/; }; +close IN; + +$content =~ s/\\\n\s*/ /g; + +foreach (split /\n/, $content) { + next if /^\s*#/ || /^\s*$/; + if (/^\s*WRAP\[\Q$target\E\]\s*=\s*(.+?)\s*$/) { + push @wraps, split(/\s+/, $1); + } elsif (/^\s*INCLUDE\[\Q$target\E\]\s*=\s*(.+?)\s*$/) { + push @includes, split(/\s+/, $1); + } +} + +die "No WRAP[$target] entry found in $build_info_file\n" unless @wraps; + +my $bi_dir = dirname($build_info_file); +my @search_dirs; +foreach my $inc (@includes, @extra_includes) { + push @search_dirs, + file_name_is_absolute($inc) ? $inc : rel2abs(catdir($bi_dir, $inc)); +} + +print STDERR "Wraps:\n ", join("\n ", @wraps), "\n" if $verbose; +print STDERR "Search dirs:\n ", join("\n ", @search_dirs), "\n" if $verbose; + +# Walk all search directories and get them in the order that level ones are +# first followed by subdirs so if there are nested includes, we get the +# shortest ones first searched. +sub find_headers +{ + my (@bases) = @_; + my @found; + my @queue = map { [$_, ''] } @bases; + + while (@queue) { + my @next; + my @level_files; + foreach my $entry (@queue) { + my ($dir, $rel) = @$entry; + next unless -d $dir; + opendir(my $dh, $dir) or next; + foreach my $name (readdir $dh) { + next if $name =~ /^\./; + my $full = catfile($dir, $name); + my $newrel = $rel eq '' ? $name : "$rel/$name"; + if (-d $full) { + push @next, [$full, $newrel]; + } elsif (-f $full && $name =~ /\.h$/) { + push @level_files, [$full, $newrel]; + } + } + closedir $dh; + } + # Stable sort within a level for deterministic ordering. + push @found, sort { $a->[1] cmp $b->[1] } @level_files; + @queue = sort { $a->[1] cmp $b->[1] } @next; + } + return @found; +} + +my @search_files = find_headers(@search_dirs); + +my %file_cache; + +sub strip_c_comments +{ + my $s = shift; + $s =~ s{/\*.*?\*/}{}sg; + $s =~ s{//[^\n]*}{}g; + return $s; +} + +# Drop attribute and qualifier macros that should not appear in the +# emitted return type. +sub strip_attribute_macros +{ + my $s = shift; + foreach my $kw (qw(__owur __pure __malloc__ ossl_inline static inline + extern OSSL_DEPRECATEDIN_0_9_8 + OSSL_DEPRECATEDIN_1_0_0 OSSL_DEPRECATEDIN_1_1_0 + OSSL_DEPRECATEDIN_3_0 OSSL_DEPRECATEDIN_3_1 + OSSL_DEPRECATEDIN_3_2 OSSL_DEPRECATEDIN_3_3 + OSSL_DEPRECATEDIN_3_4 OSSL_DEPRECATEDIN_3_5)) { + $s =~ s/\b\Q$kw\E\b//g; + } + $s =~ s/\b__attribute__\s*\(\([^)]*\)\)//g; + $s =~ s/\s+/ /g; + $s =~ s/^\s+|\s+$//g; + return $s; +} + +sub find_function_decl +{ + my ($funcname) = @_; + + foreach my $entry (@search_files) { + my ($file, $relpath) = @$entry; + unless (exists $file_cache{$file}) { + my $text = ''; + if (open(my $fh, '<', $file)) { + local $/; + $text = <$fh>; + close $fh; + } + $file_cache{$file} = strip_c_comments($text); + } + my $text = $file_cache{$file}; + + while ($text =~ /\b\Q$funcname\E\s*\(/g) { + my $name_start = $-[0]; + my $paren_start = pos($text); + + # Find matching closing paren, respecting nesting. + my $depth = 1; + my $cursor = $paren_start; + while ($cursor < length($text) && $depth > 0) { + my $c = substr($text, $cursor, 1); + $depth++ if $c eq '('; + $depth-- if $c eq ')'; + $cursor++; + } + next if $depth != 0; + my $params_str = + substr($text, $paren_start, $cursor - $paren_start - 1); + + # What follows must be ; for this to be a declaration. + my $after = substr($text, $cursor); + $after =~ s/^\s+//; + next unless $after =~ /^;/; + + # Anything since the previous statement terminator is the return + # type expression. + my $pre = substr($text, 0, $name_start); + $pre =~ s/\s+$//; + my $ret_start = 0; + $ret_start = $-[2] if $pre =~ /([;}\n])([^;}\n]*)$/s; + my $rettype = strip_attribute_macros(substr($pre, $ret_start)); + + # Normalise to forward slashes for use as an #include path. + my $include_path = $relpath; + $include_path =~ s|\\|/|g; + + return { name => $funcname, + rettype => $rettype, + params => $params_str, + file => $file, + include_path => $include_path }; + } + } + return undef; +} + +# Split a parameter list on top-level commas, respecting parentheses +sub split_params +{ + my $str = shift; + $str =~ s/^\s+|\s+$//g; + return () if $str eq '' || $str eq 'void'; + + my @parts; + my $current = ''; + my $depth = 0; + foreach my $c (split //, $str) { + if ($c eq '(') { + $depth++; + $current .= $c; + } elsif ($c eq ')') { + $depth--; + $current .= $c; + } elsif ($c eq ',' && $depth == 0) { + push @parts, $current; + $current = ''; + } else { + $current .= $c; + } + } + push @parts, $current if $current ne ''; + foreach my $p (@parts) { + $p =~ s/^\s+|\s+$//g; + } + return @parts; +} + +sub parse_param +{ + my $param = shift; + return { type => '', name => '', is_variadic => 1, is_ptr => 0 } + if $param eq '...'; + + # Reduce TYPE NAME[size] to TYPE * NAME for our purposes. + my $is_array = 0; + $is_array = 1 if $param =~ s/\[\s*[^\]]*\s*\]\s*$//; + + my ($type, $name); + if ($param =~ /^(.*?)([A-Za-z_]\w*)\s*$/) { + $type = $1; + $name = $2; + $type =~ s/\s+$//; + } else { + $type = $param; + $name = ''; + } + + return { type => $type, + name => $name, + is_ptr => (($type =~ /\*/) || $is_array) ? 1 : 0, + is_variadic => 0, + is_array => $is_array }; +} + +sub is_void_type +{ + my $t = shift; + $t =~ s/^\s+|\s+$//g; + $t =~ s/\s+/ /g; + return $t eq 'void'; +} + +sub is_ptr_type { return $_[0] =~ /\*/; } + +# Look up each WRAP entry's signature. Functions we cannot find are +# skipped with a warning rather than aborting. +my @signatures; +my @found_includes; +my %seen_include; +foreach my $func (@wraps) { + my $info = find_function_decl($func); + unless (defined $info) { + warn "WARNING: $func: declaration not found in any include dir\n"; + next; + } + + my @params = map { parse_param($_) } split_params($info->{params}); + + my $idx = 0; + foreach my $p (@params) { + next if $p->{is_variadic}; + $p->{name} = "arg$idx" if $p->{name} eq ''; + $idx++; + } + + push @signatures, { name => $func, + rettype => $info->{rettype}, + params => \@params }; + + unless ($seen_include{$info->{include_path}}) { + $seen_include{$info->{include_path}} = 1; + push @found_includes, $info->{include_path}; + } + print STDERR " found $func in $info->{file}\n" if $verbose; +} + +die "No declarations found, nothing to emit\n" unless @signatures; + +my $out_fh; +if (defined $output_file) { + open($out_fh, '>', $output_file) or die "$output_file: $!\n"; +} else { + $out_fh = \*STDOUT; +} + +print $out_fh <<"EOF"; +/* + * Copyright $YEAR The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +EOF + +print $out_fh "#include \n"; +print $out_fh "\n"; +if (@found_includes) { + my @system; + my @local; + foreach my $inc (sort @found_includes) { + if ($inc =~ m|^openssl/|) { + push @system, $inc; + } else { + push @local, $inc; + } + } + foreach my $inc (@system) { + print $out_fh "#include <$inc>\n"; + } + print $out_fh "\n" if @system && @local; + foreach my $inc (@local) { + print $out_fh "#include \"$inc\"\n"; + } + print $out_fh "\n"; +} + +if ($mode eq 'wraps' || $mode eq 'both') { + print $out_fh "/* wraps */\n\n"; + foreach my $f (@signatures) { + print $out_fh format_signature($f->{rettype}, + "__wrap_$f->{name}", + $f->{params}), ";\n"; + } + print $out_fh "\n"; + + foreach my $f (@signatures) { + emit_wrap($out_fh, $f); + print $out_fh "\n"; + } +} + +if ($mode eq 'expects' || $mode eq 'both') { + print $out_fh "/* expectations */\n\n"; + foreach my $f (@signatures) { + emit_expect($out_fh, $f); + print $out_fh "\n"; + } +} + +close $out_fh if defined $output_file; + +exit 0; + +sub format_decl +{ + my ($type, $name) = @_; + return $type =~ /\*$/ ? "$type$name" : "$type $name"; +} + +sub format_param +{ + my $p = shift; + return '...' if $p->{is_variadic}; + return format_decl($p->{type}, $p->{name}); +} + +sub format_signature +{ + my ($rettype, $name, $params) = @_; + my $param_str = @$params + ? join(', ', map { format_param($_) } @$params) + : 'void'; + return format_decl($rettype, $name) . "($param_str)"; +} + +sub emit_wrap +{ + my ($fh, $f) = @_; + print $fh format_signature($f->{rettype}, "__wrap_$f->{name}", + $f->{params}), "\n{\n"; + print $fh " function_called();\n"; + foreach my $p (@{$f->{params}}) { + next if $p->{is_variadic}; + if ($p->{is_ptr}) { + print $fh " check_expected_ptr($p->{name});\n"; + } else { + print $fh " check_expected($p->{name});\n"; + } + } + if ( ! is_void_type($f->{rettype})) { + if (is_ptr_type($f->{rettype})) { + print $fh "\n return mock_ptr_type($f->{rettype});\n"; + } else { + print $fh "\n return mock_type($f->{rettype});\n"; + } + } + print $fh "}\n"; +} + +sub emit_expect +{ + my ($fh, $f) = @_; + my $name = $f->{name}; + my @params = @{$f->{params}}; + + my @sig_parts; + foreach my $p (@params) { + next if $p->{is_variadic}; + push @sig_parts, format_param($p); + } + + my $needs_rc = !is_void_type($f->{rettype}); + push @sig_parts, format_decl($f->{rettype}, 'rc') if $needs_rc; + + my $param_str = @sig_parts ? join(', ', @sig_parts) : 'void'; + print $fh "static void expect_$name($param_str)\n{\n"; + print $fh " expect_function_call(__wrap_$name);\n"; + foreach my $p (@params) { + next if $p->{is_variadic}; + print $fh " expect_value(__wrap_$name, $p->{name}, $p->{name});\n"; + } + print $fh " will_return(__wrap_$name, rc);\n" if $needs_rc; + print $fh "}\n"; +} From 82ccc1747f0976e01665589117d7d57e9b92a095 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Fri, 12 Jun 2026 13:08:57 +0200 Subject: [PATCH 201/349] Add BIO bss_fd test Assisted-by: Claude:claude-opus-4-7 Reviewed-by: Richard Levitte Reviewed-by: Tomas Mraz MergeDate: Thu Jul 9 17:39:17 2026 (Merged from https://github.com/openssl/openssl/pull/30788) --- test/unit/build.info | 6 + test/unit/crypto/bio/test_bss_fd.c | 644 +++++++++++++++++++++++++++++ 2 files changed, 650 insertions(+) create mode 100644 test/unit/crypto/bio/test_bss_fd.c diff --git a/test/unit/build.info b/test/unit/build.info index b31f186dfa..1ada01a0a5 100644 --- a/test/unit/build.info +++ b/test/unit/build.info @@ -1 +1,7 @@ # Make sure all tests have WRAP otherwise -lcmocka is not added. + +PROGRAMS{noinst}=crypto/bio/test_bss_fd +SOURCE[crypto/bio/test_bss_fd]=crypto/bio/test_bss_fd.c +INCLUDE[crypto/bio/test_bss_fd]=../../include ../../crypto/bio +DEPEND[crypto/bio/test_bss_fd]=../../libcrypto.a +WRAP[crypto/bio/test_bss_fd]=read write lseek close diff --git a/test/unit/crypto/bio/test_bss_fd.c b/test/unit/crypto/bio/test_bss_fd.c new file mode 100644 index 0000000000..3bb6b7d714 --- /dev/null +++ b/test/unit/crypto/bio/test_bss_fd.c @@ -0,0 +1,644 @@ +/* + * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#ifdef OPENSSL_NO_POSIX_IO + +int main(void) +{ + return 0; +} + +#else + +#include +#include +#include +#include +#include + +#include "bio_local.h" + +#include + +#define FAKE_FD 42 + +/* wraps */ + +ssize_t __wrap_read(int fd, void *buf, size_t count); +ssize_t __wrap_write(int fd, const void *buf, size_t count); +off_t __wrap_lseek(int fd, off_t offset, int whence); +int __wrap_close(int fd); + +ssize_t __wrap_read(int fd, void *buf, size_t count) +{ + ssize_t rc; + + function_called(); + check_expected(fd); + check_expected_ptr(buf); + check_expected(count); + rc = mock_type(ssize_t); + if (rc <= 0) + errno = mock_type(int); + return rc; +} + +ssize_t __wrap_write(int fd, const void *buf, size_t count) +{ + ssize_t rc; + + function_called(); + check_expected(fd); + check_expected_ptr(buf); + check_expected(count); + rc = mock_type(ssize_t); + if (rc <= 0) + errno = mock_type(int); + return rc; +} + +off_t __wrap_lseek(int fd, off_t offset, int whence) +{ + function_called(); + check_expected(fd); + check_expected(offset); + check_expected(whence); + + return mock_type(off_t); +} + +int __wrap_close(int fd) +{ + function_called(); + check_expected(fd); + + return mock_type(int); +} + +/* expectations */ + +/* + * errnoval is consumed by __wrap_read only when rc <= 0; pass 0 for success + * calls where the value is irrelevant. + */ +static void expect_read(int fd, const void *buf, size_t count, ssize_t rc, + int errnoval) +{ + expect_function_call(__wrap_read); + expect_value(__wrap_read, fd, fd); + expect_value(__wrap_read, buf, buf); + expect_value(__wrap_read, count, count); + will_return(__wrap_read, rc); + if (rc <= 0) + will_return(__wrap_read, errnoval); +} + +static void expect_write(int fd, const void *buf, size_t count, ssize_t rc, + int errnoval) +{ + expect_function_call(__wrap_write); + expect_value(__wrap_write, fd, fd); + expect_value(__wrap_write, buf, buf); + expect_value(__wrap_write, count, count); + will_return(__wrap_write, rc); + if (rc <= 0) + will_return(__wrap_write, errnoval); +} + +static void expect_lseek(int fd, off_t offset, int whence, off_t rc) +{ + expect_function_call(__wrap_lseek); + expect_value(__wrap_lseek, fd, fd); + expect_value(__wrap_lseek, offset, offset); + expect_value(__wrap_lseek, whence, whence); + will_return(__wrap_lseek, rc); +} + +static void expect_close(int fd, int rc) +{ + expect_function_call(__wrap_close); + expect_value(__wrap_close, fd, fd); + will_return(__wrap_close, rc); +} + +/* setup / teardown */ + +static int setup(void **state) +{ + BIO *bio = BIO_new(BIO_s_fd()); + + assert_non_null(bio); + BIO_set_fd(bio, FAKE_FD, BIO_NOCLOSE); + *state = bio; + return 0; +} + +static int teardown(void **state) +{ + if (*state != NULL) + BIO_free(*state); + return 0; +} + +/* BIO_fd_non_fatal_error */ + +static void test_non_fatal_error_retryable(void **state) +{ + static const int errs[] = { +#ifdef EAGAIN + EAGAIN, +#endif +#ifdef EINTR + EINTR, +#endif +#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || EWOULDBLOCK != EAGAIN) + EWOULDBLOCK, +#endif +#ifdef EINPROGRESS + EINPROGRESS, +#endif +#ifdef EALREADY + EALREADY, +#endif +#ifdef ENOTCONN + ENOTCONN, +#endif +#ifdef EPROTO + EPROTO, +#endif + }; + size_t i; + + (void)state; + for (i = 0; i < sizeof(errs) / sizeof(errs[0]); i++) + assert_int_equal(BIO_fd_non_fatal_error(errs[i]), 1); +} + +static void test_non_fatal_error_fatal(void **state) +{ + (void)state; + assert_int_equal(BIO_fd_non_fatal_error(ENOENT), 0); + assert_int_equal(BIO_fd_non_fatal_error(EBADF), 0); + assert_int_equal(BIO_fd_non_fatal_error(0), 0); +} + +/* BIO_fd_should_retry */ + +static void test_should_retry_positive_i(void **state) +{ + /* i > 0: always returns 0 regardless of errno */ + (void)state; + errno = EAGAIN; + assert_int_equal(BIO_fd_should_retry(1), 0); + assert_int_equal(BIO_fd_should_retry(100), 0); +} + +static void test_should_retry_fatal_errno(void **state) +{ + (void)state; + errno = ENOENT; + assert_int_equal(BIO_fd_should_retry(-1), 0); + errno = ENOENT; + assert_int_equal(BIO_fd_should_retry(0), 0); +} + +static void test_should_retry_non_fatal_errno(void **state) +{ + (void)state; + errno = EAGAIN; + assert_int_equal(BIO_fd_should_retry(-1), 1); + errno = EINTR; + assert_int_equal(BIO_fd_should_retry(0), 1); +} + +/* fd_read (via BIO_read) */ + +static void test_fd_read_success(void **state) +{ + BIO *bio = *state; + char buf[8] = { 0 }; + + expect_read(FAKE_FD, buf, 8, 8, 0); + assert_int_equal(BIO_read(bio, buf, 8), 8); + assert_false(BIO_should_retry(bio)); + assert_false(BIO_eof(bio)); +} + +static void test_fd_read_eof(void **state) +{ + BIO *bio = *state; + char buf[8] = { 0 }; + + expect_read(FAKE_FD, buf, 8, 0, 0); + assert_true(BIO_read(bio, buf, 8) <= 0); + assert_true(BIO_eof(bio)); + assert_false(BIO_should_retry(bio)); +} + +static void test_fd_read_retry(void **state) +{ + BIO *bio = *state; + char buf[8] = { 0 }; + + expect_read(FAKE_FD, buf, 8, -1, EAGAIN); + assert_true(BIO_read(bio, buf, 8) <= 0); + assert_true(BIO_should_retry(bio)); + assert_true(BIO_should_read(bio)); + assert_false(BIO_eof(bio)); +} + +static void test_fd_read_error(void **state) +{ + BIO *bio = *state; + char buf[8] = { 0 }; + + expect_read(FAKE_FD, buf, 8, -1, ENOENT); + assert_true(BIO_read(bio, buf, 8) <= 0); + assert_false(BIO_should_retry(bio)); + assert_false(BIO_eof(bio)); +} + +static void test_fd_read_clears_eof(void **state) +{ + /* BIO_FLAGS_IN_EOF is cleared at the start of each new read attempt. */ + BIO *bio = *state; + char buf[1] = { 0 }; + + expect_read(FAKE_FD, buf, 1, 0, 0); + BIO_read(bio, buf, 1); + assert_true(BIO_eof(bio)); + + expect_read(FAKE_FD, buf, 1, 1, 0); + assert_int_equal(BIO_read(bio, buf, 1), 1); + assert_false(BIO_eof(bio)); +} + +/* fd_write (via BIO_write) */ + +static void test_fd_write_success(void **state) +{ + BIO *bio = *state; + const char buf[] = "hello"; + + expect_write(FAKE_FD, buf, 5, 5, 0); + assert_int_equal(BIO_write(bio, buf, 5), 5); + assert_false(BIO_should_retry(bio)); +} + +static void test_fd_write_retry(void **state) +{ + BIO *bio = *state; + const char buf[] = "hello"; + + expect_write(FAKE_FD, buf, 5, -1, EAGAIN); + assert_true(BIO_write(bio, buf, 5) <= 0); + assert_true(BIO_should_retry(bio)); + assert_true(BIO_should_write(bio)); +} + +static void test_fd_write_error(void **state) +{ + BIO *bio = *state; + const char buf[] = "hello"; + + expect_write(FAKE_FD, buf, 5, -1, ENOSPC); + assert_true(BIO_write(bio, buf, 5) <= 0); + assert_false(BIO_should_retry(bio)); +} + +/* fd_ctrl (via BIO_ctrl) */ + +static void test_fd_ctrl_reset(void **state) +{ + BIO *bio = *state; + + /* RESET sets num=0 then falls through to FILE_SEEK: lseek(fd, 0, 0) */ + expect_lseek(FAKE_FD, 0, 0, 0); + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_RESET, 0, NULL), 0); +} + +static void test_fd_ctrl_seek(void **state) +{ + BIO *bio = *state; + + expect_lseek(FAKE_FD, 512, 0, 512); + assert_int_equal(BIO_ctrl(bio, BIO_C_FILE_SEEK, 512, NULL), 512); +} + +static void test_fd_ctrl_tell(void **state) +{ + BIO *bio = *state; + + expect_lseek(FAKE_FD, 0, 1, 256); + assert_int_equal(BIO_ctrl(bio, BIO_C_FILE_TELL, 0, NULL), 256); +} + +static void test_fd_ctrl_info(void **state) +{ + BIO *bio = *state; + + /* BIO_CTRL_INFO shares the lseek(fd, 0, 1) branch with FILE_TELL */ + expect_lseek(FAKE_FD, 0, 1, 128); + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_INFO, 0, NULL), 128); +} + +static void test_fd_ctrl_set_fd(void **state) +{ + BIO *bio = *state; + int newfd = 99; + + /* fd_free is called first; existing shutdown=BIO_NOCLOSE so no close */ + BIO_ctrl(bio, BIO_C_SET_FD, BIO_NOCLOSE, &newfd); + assert_int_equal(bio->num, 99); + assert_int_equal(bio->shutdown, BIO_NOCLOSE); + assert_int_equal(bio->init, 1); +} + +static void test_fd_ctrl_get_fd_init(void **state) +{ + BIO *bio = *state; + int out = -1; + + assert_int_equal(BIO_ctrl(bio, BIO_C_GET_FD, 0, &out), FAKE_FD); + assert_int_equal(out, FAKE_FD); +} + +static void test_fd_ctrl_get_fd_uninit(void **state) +{ + BIO *bio = *state; + + /* teardown is safe: shutdown=BIO_NOCLOSE guards the close call */ + bio->init = 0; + assert_int_equal(BIO_ctrl(bio, BIO_C_GET_FD, 0, NULL), -1); +} + +static void test_fd_ctrl_get_close(void **state) +{ + BIO *bio = *state; + + bio->shutdown = BIO_NOCLOSE; + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_GET_CLOSE, 0, NULL), BIO_NOCLOSE); + bio->shutdown = BIO_CLOSE; + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_GET_CLOSE, 0, NULL), BIO_CLOSE); + bio->shutdown = BIO_NOCLOSE; +} + +static void test_fd_ctrl_set_close(void **state) +{ + BIO *bio = *state; + + BIO_ctrl(bio, BIO_CTRL_SET_CLOSE, BIO_CLOSE, NULL); + assert_int_equal(bio->shutdown, BIO_CLOSE); + /* Restore before teardown to avoid an unexpected close call. */ + BIO_ctrl(bio, BIO_CTRL_SET_CLOSE, BIO_NOCLOSE, NULL); +} + +static void test_fd_ctrl_pending(void **state) +{ + assert_int_equal(BIO_ctrl(*state, BIO_CTRL_PENDING, 0, NULL), 0); +} + +static void test_fd_ctrl_wpending(void **state) +{ + assert_int_equal(BIO_ctrl(*state, BIO_CTRL_WPENDING, 0, NULL), 0); +} + +static void test_fd_ctrl_dup(void **state) +{ + assert_int_equal(BIO_ctrl(*state, BIO_CTRL_DUP, 0, NULL), 1); +} + +static void test_fd_ctrl_flush(void **state) +{ + assert_int_equal(BIO_ctrl(*state, BIO_CTRL_FLUSH, 0, NULL), 1); +} + +static void test_fd_ctrl_eof_clear(void **state) +{ + BIO *bio = *state; + + bio->flags &= ~BIO_FLAGS_IN_EOF; + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_EOF, 0, NULL), 0); +} + +static void test_fd_ctrl_eof_set(void **state) +{ + BIO *bio = *state; + + bio->flags |= BIO_FLAGS_IN_EOF; + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_EOF, 0, NULL), 1); + bio->flags &= ~BIO_FLAGS_IN_EOF; +} + +static void test_fd_ctrl_default(void **state) +{ + assert_int_equal(BIO_ctrl(*state, 9999, 0, NULL), 0); +} + +/* fd_free (via BIO_free) */ + +static void test_fd_free_shutdown_with_init(void **state) +{ + /* shutdown=1 and init=1: close must be called */ + BIO *bio = BIO_new(BIO_s_fd()); + + assert_non_null(bio); + bio->num = FAKE_FD; + bio->shutdown = BIO_CLOSE; + bio->init = 1; + + expect_close(FAKE_FD, 0); + BIO_free(bio); + *state = NULL; +} + +static void test_fd_free_shutdown_no_init(void **state) +{ + /* shutdown=1 but init=0: close must NOT be called */ + BIO *bio = BIO_new(BIO_s_fd()); + + assert_non_null(bio); + bio->num = FAKE_FD; + bio->shutdown = BIO_CLOSE; + bio->init = 0; + + BIO_free(bio); + *state = NULL; +} + +static void test_fd_free_no_shutdown(void **state) +{ + /* shutdown=0: close must NOT be called regardless of init */ + BIO *bio = BIO_new(BIO_s_fd()); + + assert_non_null(bio); + bio->num = FAKE_FD; + bio->shutdown = BIO_NOCLOSE; + bio->init = 1; + + BIO_free(bio); + *state = NULL; +} + +/* fd_puts (via BIO_puts) */ + +static void test_fd_puts_success(void **state) +{ + BIO *bio = *state; + const char *str = "hello"; + + expect_write(FAKE_FD, str, 5, 5, 0); + assert_int_equal(BIO_puts(bio, str), 5); +} + +static void test_fd_puts_write_fails(void **state) +{ + BIO *bio = *state; + const char *str = "hello"; + + expect_write(FAKE_FD, str, 5, -1, ENOSPC); + assert_true(BIO_puts(bio, str) <= 0); +} + +/* + * fd_gets (via BIO_gets) + * + * fd_gets calls fd_read one byte at a time, which in turn calls read. + * The buffer is pre-filled with the data that each mocked read delivers, + * since __wrap_read returns the count without writing into the buffer. + */ + +static void test_fd_gets_size_one(void **state) +{ + /* end == buf when size=1, so the loop body never executes */ + BIO *bio = *state; + char buf[4] = { 0 }; + + assert_int_equal(BIO_gets(bio, buf, 1), 0); + assert_int_equal(buf[0], '\0'); +} + +static void test_fd_gets_newline_terminates(void **state) +{ + BIO *bio = *state; + char buf[8] = { 'h', 'i', '\n' }; + + expect_read(FAKE_FD, buf, 1, 1, 0); + expect_read(FAKE_FD, buf + 1, 1, 1, 0); + expect_read(FAKE_FD, buf + 2, 1, 1, 0); + + assert_int_equal(BIO_gets(bio, buf, sizeof(buf)), 3); + assert_memory_equal(buf, "hi\n", 4); +} + +static void test_fd_gets_fills_to_limit(void **state) +{ + /* size=4: reads at most 3 chars (buf+3 is the null slot) */ + BIO *bio = *state; + char buf[4] = { 'a', 'b', 'c' }; + + expect_read(FAKE_FD, buf, 1, 1, 0); + expect_read(FAKE_FD, buf + 1, 1, 1, 0); + expect_read(FAKE_FD, buf + 2, 1, 1, 0); + + assert_int_equal(BIO_gets(bio, buf, 4), 3); + assert_memory_equal(buf, "abc", 4); +} + +static void test_fd_gets_eof_mid_line(void **state) +{ + /* read returns 0 after the first byte: return what was read */ + BIO *bio = *state; + char buf[8] = { 'z' }; + + expect_read(FAKE_FD, buf, 1, 1, 0); + expect_read(FAKE_FD, buf + 1, 1, 0, 0); + + assert_int_equal(BIO_gets(bio, buf, sizeof(buf)), 1); + assert_memory_equal(buf, "z", 2); +} + +static void test_fd_gets_immediate_eof(void **state) +{ + /* First read returns 0: returns 0 and buf[0] is '\0' */ + BIO *bio = *state; + char buf[8] = { 0 }; + + expect_read(FAKE_FD, buf, 1, 0, 0); + + assert_int_equal(BIO_gets(bio, buf, sizeof(buf)), 0); + assert_int_equal(buf[0], '\0'); +} + +/* main */ + +#define FD_TEST(name) \ + cmocka_unit_test_setup_teardown(name, setup, teardown) + +#define FD_TEST_PLAIN(name) \ + cmocka_unit_test(name) + +int main(void) +{ + const struct CMUnitTest tests[] = { + /* BIO_fd_non_fatal_error */ + FD_TEST_PLAIN(test_non_fatal_error_retryable), + FD_TEST_PLAIN(test_non_fatal_error_fatal), + /* BIO_fd_should_retry */ + FD_TEST_PLAIN(test_should_retry_positive_i), + FD_TEST_PLAIN(test_should_retry_fatal_errno), + FD_TEST_PLAIN(test_should_retry_non_fatal_errno), + /* fd_read */ + FD_TEST(test_fd_read_success), + FD_TEST(test_fd_read_eof), + FD_TEST(test_fd_read_retry), + FD_TEST(test_fd_read_error), + FD_TEST(test_fd_read_clears_eof), + /* fd_write */ + FD_TEST(test_fd_write_success), + FD_TEST(test_fd_write_retry), + FD_TEST(test_fd_write_error), + /* fd_ctrl */ + FD_TEST(test_fd_ctrl_reset), + FD_TEST(test_fd_ctrl_seek), + FD_TEST(test_fd_ctrl_tell), + FD_TEST(test_fd_ctrl_info), + FD_TEST(test_fd_ctrl_set_fd), + FD_TEST(test_fd_ctrl_get_fd_init), + FD_TEST(test_fd_ctrl_get_fd_uninit), + FD_TEST(test_fd_ctrl_get_close), + FD_TEST(test_fd_ctrl_set_close), + FD_TEST(test_fd_ctrl_pending), + FD_TEST(test_fd_ctrl_wpending), + FD_TEST(test_fd_ctrl_dup), + FD_TEST(test_fd_ctrl_flush), + FD_TEST(test_fd_ctrl_eof_clear), + FD_TEST(test_fd_ctrl_eof_set), + FD_TEST(test_fd_ctrl_default), + /* fd_free */ + FD_TEST(test_fd_free_shutdown_with_init), + FD_TEST(test_fd_free_shutdown_no_init), + FD_TEST(test_fd_free_no_shutdown), + /* fd_puts */ + FD_TEST(test_fd_puts_success), + FD_TEST(test_fd_puts_write_fails), + /* fd_gets */ + FD_TEST(test_fd_gets_size_one), + FD_TEST(test_fd_gets_newline_terminates), + FD_TEST(test_fd_gets_fills_to_limit), + FD_TEST(test_fd_gets_eof_mid_line), + FD_TEST(test_fd_gets_immediate_eof), + }; + + cmocka_set_message_output(CM_OUTPUT_TAP); + + return cmocka_run_group_tests(tests, NULL, NULL); +} + +#endif /* OPENSSL_NO_POSIX_IO */ From bcbecdcaa5c0d7ae11ddf2de58b65ae1b97ff0c4 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 14 May 2026 18:39:18 +0200 Subject: [PATCH 202/349] Add BIO bio_addr test Assisted-by: Claude:claude-opus-4-7 Reviewed-by: Richard Levitte Reviewed-by: Tomas Mraz MergeDate: Thu Jul 9 17:39:18 2026 (Merged from https://github.com/openssl/openssl/pull/30788) --- test/unit/build.info | 6 + test/unit/crypto/bio/test_bio_addr.c | 1155 ++++++++++++++++++++++++++ 2 files changed, 1161 insertions(+) create mode 100644 test/unit/crypto/bio/test_bio_addr.c diff --git a/test/unit/build.info b/test/unit/build.info index 1ada01a0a5..befcc67279 100644 --- a/test/unit/build.info +++ b/test/unit/build.info @@ -1,5 +1,11 @@ # Make sure all tests have WRAP otherwise -lcmocka is not added. +PROGRAMS{noinst}=crypto/bio/test_bio_addr +SOURCE[crypto/bio/test_bio_addr]=crypto/bio/test_bio_addr.c +INCLUDE[crypto/bio/test_bio_addr]=../../include ../../crypto/bio +DEPEND[crypto/bio/test_bio_addr]=../../libcrypto.a +WRAP[crypto/bio/test_bio_addr]=BIO_sock_init getnameinfo freeaddrinfo + PROGRAMS{noinst}=crypto/bio/test_bss_fd SOURCE[crypto/bio/test_bss_fd]=crypto/bio/test_bss_fd.c INCLUDE[crypto/bio/test_bss_fd]=../../include ../../crypto/bio diff --git a/test/unit/crypto/bio/test_bio_addr.c b/test/unit/crypto/bio/test_bio_addr.c new file mode 100644 index 0000000000..de92d8a0f6 --- /dev/null +++ b/test/unit/crypto/bio/test_bio_addr.c @@ -0,0 +1,1155 @@ +/* + * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "internal/sockets.h" + +#ifdef OPENSSL_NO_SOCK + +int main(void) +{ + return 0; +} + +#else + +#include +#include +#include +#include +#include + +#include "bio_local.h" + +#include + +/* wraps */ + +int __wrap_BIO_sock_init(void); +#ifdef AI_PASSIVE +int __wrap_getnameinfo(const struct sockaddr *sa, socklen_t salen, + char *host, socklen_t hostlen, + char *serv, socklen_t servlen, int flags); +void __wrap_freeaddrinfo(struct addrinfo *res); +#endif + +int __wrap_BIO_sock_init(void) +{ + function_called(); + return mock_type(int); +} + +#ifdef AI_PASSIVE +int __wrap_getnameinfo(const struct sockaddr *sa, socklen_t salen, + char *host, socklen_t hostlen, + char *serv, socklen_t servlen, int flags) +{ + int rc; + + function_called(); + check_expected_ptr(sa); + check_expected(salen); + check_expected(flags); + rc = mock_type(int); + if (rc == 0) { + if (host != NULL) + strncpy(host, mock_ptr_type(const char *), hostlen - 1); + if (serv != NULL) + strncpy(serv, mock_ptr_type(const char *), servlen - 1); + } + return rc; +} + +void __wrap_freeaddrinfo(struct addrinfo *res) +{ + function_called(); + check_expected_ptr(res); +} +#endif /* AI_PASSIVE */ + +/* expectations */ + +static void expect_sock_init(int rc) +{ + expect_function_call(__wrap_BIO_sock_init); + will_return(__wrap_BIO_sock_init, rc); +} + +#ifdef AI_PASSIVE +static void expect_getnameinfo(const struct sockaddr *sa, socklen_t salen, + int flags, int rc, + const char *host_out, const char *serv_out) +{ + expect_function_call(__wrap_getnameinfo); + expect_value(__wrap_getnameinfo, sa, sa); + expect_value(__wrap_getnameinfo, salen, salen); + expect_value(__wrap_getnameinfo, flags, flags); + will_return(__wrap_getnameinfo, rc); + if (rc == 0) { + will_return(__wrap_getnameinfo, host_out); + will_return(__wrap_getnameinfo, serv_out); + } +} + +static void expect_freeaddrinfo(const struct addrinfo *res) +{ + expect_function_call(__wrap_freeaddrinfo); + expect_value(__wrap_freeaddrinfo, res, res); +} +#endif /* AI_PASSIVE */ + +/* BIO_ADDR_clear */ + +static void test_addr_clear(void **state) +{ + BIO_ADDR ap; + + (void)state; + memset(&ap, 0xFF, sizeof(ap)); + BIO_ADDR_clear(&ap); + assert_int_equal(ap.sa.sa_family, AF_UNSPEC); + assert_int_equal(ap.s_in.sin_port, 0); + assert_int_equal(ap.s_in.sin_addr.s_addr, 0); +} + +/* BIO_ADDR_make */ + +static void test_addr_make_ipv4(void **state) +{ + struct sockaddr_in sa4; + BIO_ADDR ap; + + (void)state; + memset(&sa4, 0, sizeof(sa4)); + sa4.sin_family = AF_INET; + sa4.sin_port = htons(443); + sa4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + assert_int_equal(BIO_ADDR_make(&ap, (const struct sockaddr *)&sa4), 1); + assert_int_equal(ap.s_in.sin_family, AF_INET); + assert_int_equal(ap.s_in.sin_port, htons(443)); + assert_int_equal(ap.s_in.sin_addr.s_addr, htonl(INADDR_LOOPBACK)); +} + +#if OPENSSL_USE_IPV6 +static void test_addr_make_ipv6(void **state) +{ + struct sockaddr_in6 sa6; + BIO_ADDR ap; + struct in6_addr loopback = IN6ADDR_LOOPBACK_INIT; + + (void)state; + memset(&sa6, 0, sizeof(sa6)); + sa6.sin6_family = AF_INET6; + sa6.sin6_port = htons(443); + sa6.sin6_addr = loopback; + + assert_int_equal(BIO_ADDR_make(&ap, (const struct sockaddr *)&sa6), 1); + assert_int_equal(ap.s_in6.sin6_family, AF_INET6); + assert_int_equal(ap.s_in6.sin6_port, htons(443)); + assert_memory_equal(&ap.s_in6.sin6_addr, &loopback, sizeof(loopback)); +} +#endif + +#ifndef OPENSSL_NO_UNIX_SOCK +static void test_addr_make_unix(void **state) +{ + struct sockaddr_un sau; + BIO_ADDR ap; + + (void)state; + memset(&sau, 0, sizeof(sau)); + sau.sun_family = AF_UNIX; + strncpy(sau.sun_path, "/tmp/test.sock", sizeof(sau.sun_path) - 1); + + assert_int_equal(BIO_ADDR_make(&ap, (const struct sockaddr *)&sau), 1); + assert_int_equal(ap.s_un.sun_family, AF_UNIX); + assert_string_equal(ap.s_un.sun_path, "/tmp/test.sock"); +} +#endif + +static void test_addr_make_unknown_family(void **state) +{ + struct sockaddr sa; + BIO_ADDR ap; + + (void)state; + memset(&sa, 0, sizeof(sa)); + sa.sa_family = AF_UNSPEC; + assert_int_equal(BIO_ADDR_make(&ap, &sa), 0); +} + +/* BIO_ADDR_rawmake */ + +static void test_rawmake_ipv4(void **state) +{ + struct in_addr addr4; + BIO_ADDR ap; + + (void)state; + addr4.s_addr = htonl(INADDR_LOOPBACK); + assert_int_equal( + BIO_ADDR_rawmake(&ap, AF_INET, &addr4, sizeof(addr4), htons(80)), 1); + assert_int_equal(ap.s_in.sin_family, AF_INET); + assert_int_equal(ap.s_in.sin_port, htons(80)); + assert_int_equal(ap.s_in.sin_addr.s_addr, htonl(INADDR_LOOPBACK)); +} + +static void test_rawmake_ipv4_wrong_len(void **state) +{ + struct in_addr addr4; + BIO_ADDR ap; + + (void)state; + addr4.s_addr = htonl(INADDR_LOOPBACK); + assert_int_equal( + BIO_ADDR_rawmake(&ap, AF_INET, &addr4, sizeof(addr4) - 1, 0), 0); +} + +#if OPENSSL_USE_IPV6 +static void test_rawmake_ipv6(void **state) +{ + struct in6_addr addr6 = IN6ADDR_LOOPBACK_INIT; + BIO_ADDR ap; + + (void)state; + assert_int_equal( + BIO_ADDR_rawmake(&ap, AF_INET6, &addr6, sizeof(addr6), htons(443)), 1); + assert_int_equal(ap.s_in6.sin6_family, AF_INET6); + assert_int_equal(ap.s_in6.sin6_port, htons(443)); + assert_memory_equal(&ap.s_in6.sin6_addr, &addr6, sizeof(addr6)); +} +#endif + +#ifndef OPENSSL_NO_UNIX_SOCK +static void test_rawmake_unix(void **state) +{ + const char *path = "/tmp/test.sock"; + BIO_ADDR ap; + + (void)state; + assert_int_equal( + BIO_ADDR_rawmake(&ap, AF_UNIX, path, strlen(path), 0), 1); + assert_int_equal(ap.s_un.sun_family, AF_UNIX); + assert_string_equal(ap.s_un.sun_path, path); +} + +static void test_rawmake_unix_too_long(void **state) +{ + /* path longer than sun_path must be rejected */ + char path[sizeof(((struct sockaddr_un *)0)->sun_path) + 2]; + BIO_ADDR ap; + + (void)state; + memset(path, 'x', sizeof(path) - 1); + path[sizeof(path) - 1] = '\0'; + assert_int_equal( + BIO_ADDR_rawmake(&ap, AF_UNIX, path, strlen(path), 0), 0); +} +#endif + +static void test_rawmake_unknown_family(void **state) +{ + char data[4] = { 0 }; + BIO_ADDR ap; + + (void)state; + assert_int_equal(BIO_ADDR_rawmake(&ap, AF_UNSPEC, data, 0, 0), 0); +} + +/* BIO_ADDR_family */ + +static void test_addr_family(void **state) +{ + BIO_ADDR ap; + + (void)state; + memset(&ap, 0, sizeof(ap)); + ap.sa.sa_family = AF_UNSPEC; + assert_int_equal(BIO_ADDR_family(&ap), AF_UNSPEC); + ap.sa.sa_family = AF_INET; + assert_int_equal(BIO_ADDR_family(&ap), AF_INET); +} + +/* BIO_ADDR_rawport */ + +static void test_rawport_ipv4(void **state) +{ + BIO_ADDR ap; + struct in_addr addr4; + + (void)state; + addr4.s_addr = htonl(INADDR_LOOPBACK); + BIO_ADDR_rawmake(&ap, AF_INET, &addr4, sizeof(addr4), htons(443)); + assert_int_equal(BIO_ADDR_rawport(&ap), htons(443)); +} + +#if OPENSSL_USE_IPV6 +static void test_rawport_ipv6(void **state) +{ + BIO_ADDR ap; + struct in6_addr addr6 = IN6ADDR_LOOPBACK_INIT; + + (void)state; + BIO_ADDR_rawmake(&ap, AF_INET6, &addr6, sizeof(addr6), htons(8080)); + assert_int_equal(BIO_ADDR_rawport(&ap), htons(8080)); +} +#endif + +static void test_rawport_no_port(void **state) +{ + BIO_ADDR ap; + + (void)state; + memset(&ap, 0, sizeof(ap)); + ap.sa.sa_family = AF_UNSPEC; + assert_int_equal(BIO_ADDR_rawport(&ap), 0); +} + +/* BIO_ADDR_sockaddr and BIO_ADDR_sockaddr_noconst */ + +static void test_sockaddr_pointers(void **state) +{ + BIO_ADDR ap = { 0 }; + + (void)state; + memset(&ap, 0, sizeof(ap)); + assert_ptr_equal(BIO_ADDR_sockaddr(&ap), &ap.sa); + assert_ptr_equal(BIO_ADDR_sockaddr_noconst(&ap), &ap.sa); +} + +/* BIO_ADDR_sockaddr_size */ + +static void test_sockaddr_size_ipv4(void **state) +{ + BIO_ADDR ap; + + (void)state; + memset(&ap, 0, sizeof(ap)); + ap.sa.sa_family = AF_INET; + assert_int_equal(BIO_ADDR_sockaddr_size(&ap), sizeof(struct sockaddr_in)); +} + +#if OPENSSL_USE_IPV6 +static void test_sockaddr_size_ipv6(void **state) +{ + BIO_ADDR ap; + + (void)state; + memset(&ap, 0, sizeof(ap)); + ap.sa.sa_family = AF_INET6; + assert_int_equal(BIO_ADDR_sockaddr_size(&ap), sizeof(struct sockaddr_in6)); +} +#endif + +#ifndef OPENSSL_NO_UNIX_SOCK +static void test_sockaddr_size_unix(void **state) +{ + BIO_ADDR ap; + + (void)state; + memset(&ap, 0, sizeof(ap)); + ap.sa.sa_family = AF_UNIX; + assert_int_equal(BIO_ADDR_sockaddr_size(&ap), sizeof(struct sockaddr_un)); +} +#endif + +static void test_sockaddr_size_default(void **state) +{ + BIO_ADDR ap; + + (void)state; + memset(&ap, 0, sizeof(ap)); + ap.sa.sa_family = AF_UNSPEC; + assert_int_equal(BIO_ADDR_sockaddr_size(&ap), sizeof(BIO_ADDR)); +} + +/* BIO_ADDR_rawaddress */ + +static void test_rawaddress_unset(void **state) +{ + BIO_ADDR ap; + struct in_addr out; + size_t len; + + (void)state; + memset(&ap, 0, sizeof(ap)); + ap.sa.sa_family = AF_UNSPEC; + assert_int_equal(BIO_ADDR_rawaddress(&ap, &out, &len), 0); +} + +static void test_rawaddress_ipv4(void **state) +{ + BIO_ADDR ap; + struct in_addr expected, out; + size_t len = 0; + + (void)state; + expected.s_addr = htonl(INADDR_LOOPBACK); + BIO_ADDR_rawmake(&ap, AF_INET, &expected, sizeof(expected), htons(80)); + + assert_int_equal(BIO_ADDR_rawaddress(&ap, &out, &len), 1); + assert_int_equal(len, sizeof(struct in_addr)); + assert_memory_equal(&out, &expected, sizeof(expected)); +} + +static void test_rawaddress_ipv4_len_only(void **state) +{ + BIO_ADDR ap; + struct in_addr addr4; + size_t len = 0; + + (void)state; + addr4.s_addr = htonl(INADDR_LOOPBACK); + BIO_ADDR_rawmake(&ap, AF_INET, &addr4, sizeof(addr4), 0); + + assert_int_equal(BIO_ADDR_rawaddress(&ap, NULL, &len), 1); + assert_int_equal(len, sizeof(struct in_addr)); +} + +static void test_rawaddress_ipv4_ptr_only(void **state) +{ + BIO_ADDR ap; + struct in_addr expected, out; + + (void)state; + expected.s_addr = htonl(INADDR_LOOPBACK); + BIO_ADDR_rawmake(&ap, AF_INET, &expected, sizeof(expected), 0); + + assert_int_equal(BIO_ADDR_rawaddress(&ap, &out, NULL), 1); + assert_memory_equal(&out, &expected, sizeof(expected)); +} + +#ifndef OPENSSL_NO_UNIX_SOCK +static void test_rawaddress_unix(void **state) +{ + BIO_ADDR ap; + const char *path = "/tmp/unit.sock"; + char out[64]; + size_t len = 0; + + (void)state; + BIO_ADDR_rawmake(&ap, AF_UNIX, path, strlen(path), 0); + + assert_int_equal(BIO_ADDR_rawaddress(&ap, out, &len), 1); + assert_int_equal(len, strlen(path)); + assert_memory_equal(out, path, strlen(path)); +} +#endif + +/* BIO_ADDR_copy */ + +static void test_addr_copy_null(void **state) +{ + BIO_ADDR ap; + + (void)state; + memset(&ap, 0, sizeof(ap)); + assert_int_equal(BIO_ADDR_copy(NULL, NULL), 0); + assert_int_equal(BIO_ADDR_copy(NULL, &ap), 0); + assert_int_equal(BIO_ADDR_copy(&ap, NULL), 0); +} + +static void test_addr_copy_unspec(void **state) +{ + BIO_ADDR src, dst; + + (void)state; + memset(&src, 0, sizeof(src)); + src.sa.sa_family = AF_UNSPEC; + memset(&dst, 0xFF, sizeof(dst)); + + assert_int_equal(BIO_ADDR_copy(&dst, &src), 1); + assert_int_equal(dst.sa.sa_family, AF_UNSPEC); +} + +static void test_addr_copy_ipv4(void **state) +{ + BIO_ADDR src, dst; + struct in_addr addr4; + + (void)state; + addr4.s_addr = htonl(INADDR_LOOPBACK); + BIO_ADDR_rawmake(&src, AF_INET, &addr4, sizeof(addr4), htons(443)); + memset(&dst, 0, sizeof(dst)); + + assert_int_equal(BIO_ADDR_copy(&dst, &src), 1); + assert_int_equal(dst.s_in.sin_family, AF_INET); + assert_int_equal(dst.s_in.sin_port, htons(443)); + assert_int_equal(dst.s_in.sin_addr.s_addr, htonl(INADDR_LOOPBACK)); +} + +/* BIO_ADDR_new and BIO_ADDR_free */ + +static void test_addr_new_sets_unspec(void **state) +{ + BIO_ADDR *ap; + + (void)state; + ap = BIO_ADDR_new(); + assert_non_null(ap); + assert_int_equal(BIO_ADDR_family(ap), AF_UNSPEC); + BIO_ADDR_free(ap); +} + +/* BIO_ADDR_dup */ + +static void test_addr_dup_null(void **state) +{ + (void)state; + assert_null(BIO_ADDR_dup(NULL)); +} + +static void test_addr_dup_ipv4(void **state) +{ + BIO_ADDR src, *dup; + struct in_addr addr4; + + (void)state; + addr4.s_addr = htonl(INADDR_LOOPBACK); + BIO_ADDR_rawmake(&src, AF_INET, &addr4, sizeof(addr4), htons(8443)); + + dup = BIO_ADDR_dup(&src); + assert_non_null(dup); + assert_int_equal(dup->s_in.sin_family, AF_INET); + assert_int_equal(dup->s_in.sin_port, htons(8443)); + assert_int_equal(dup->s_in.sin_addr.s_addr, htonl(INADDR_LOOPBACK)); + BIO_ADDR_free(dup); +} + +/* BIO_ADDR_path_string */ + +#ifndef OPENSSL_NO_UNIX_SOCK +static void test_path_string_unix(void **state) +{ + BIO_ADDR ap; + char *path; + + (void)state; + BIO_ADDR_rawmake(&ap, AF_UNIX, "/run/unit.sock", 14, 0); + + path = BIO_ADDR_path_string(&ap); + assert_non_null(path); + assert_string_equal(path, "/run/unit.sock"); + OPENSSL_free(path); +} +#endif + +static void test_path_string_non_unix(void **state) +{ + BIO_ADDR ap; + struct in_addr addr4; + + (void)state; + addr4.s_addr = htonl(INADDR_LOOPBACK); + BIO_ADDR_rawmake(&ap, AF_INET, &addr4, sizeof(addr4), htons(80)); + assert_null(BIO_ADDR_path_string(&ap)); +} + +/* BIO_ADDRINFO accessors */ + +static void test_addrinfo_next_null(void **state) +{ + (void)state; + assert_null(BIO_ADDRINFO_next(NULL)); +} + +static void test_addrinfo_next(void **state) +{ + BIO_ADDRINFO a, b; + + (void)state; + memset(&a, 0, sizeof(a)); + memset(&b, 0, sizeof(b)); + a.bai_next = &b; + b.bai_next = NULL; + assert_ptr_equal(BIO_ADDRINFO_next(&a), &b); + assert_null(BIO_ADDRINFO_next(&b)); +} + +static void test_addrinfo_family(void **state) +{ + BIO_ADDRINFO bai; + + (void)state; + assert_int_equal(BIO_ADDRINFO_family(NULL), 0); + memset(&bai, 0, sizeof(bai)); + bai.bai_family = AF_INET; + assert_int_equal(BIO_ADDRINFO_family(&bai), AF_INET); +} + +static void test_addrinfo_socktype(void **state) +{ + BIO_ADDRINFO bai; + + (void)state; + assert_int_equal(BIO_ADDRINFO_socktype(NULL), 0); + memset(&bai, 0, sizeof(bai)); + bai.bai_socktype = SOCK_STREAM; + assert_int_equal(BIO_ADDRINFO_socktype(&bai), SOCK_STREAM); +} + +/* BIO_ADDRINFO_protocol */ + +static void test_addrinfo_protocol_null(void **state) +{ + (void)state; + assert_int_equal(BIO_ADDRINFO_protocol(NULL), 0); +} + +static void test_addrinfo_protocol_explicit(void **state) +{ + BIO_ADDRINFO bai; + + (void)state; + memset(&bai, 0, sizeof(bai)); + bai.bai_protocol = IPPROTO_SCTP; + assert_int_equal(BIO_ADDRINFO_protocol(&bai), IPPROTO_SCTP); +} + +static void test_addrinfo_protocol_stream(void **state) +{ + BIO_ADDRINFO bai; + + (void)state; + memset(&bai, 0, sizeof(bai)); + bai.bai_family = AF_INET; + bai.bai_socktype = SOCK_STREAM; + assert_int_equal(BIO_ADDRINFO_protocol(&bai), IPPROTO_TCP); +} + +static void test_addrinfo_protocol_dgram(void **state) +{ + BIO_ADDRINFO bai; + + (void)state; + memset(&bai, 0, sizeof(bai)); + bai.bai_family = AF_INET; + bai.bai_socktype = SOCK_DGRAM; + assert_int_equal(BIO_ADDRINFO_protocol(&bai), IPPROTO_UDP); +} + +#ifndef OPENSSL_NO_UNIX_SOCK +static void test_addrinfo_protocol_unix(void **state) +{ + BIO_ADDRINFO bai; + + (void)state; + memset(&bai, 0, sizeof(bai)); + bai.bai_family = AF_UNIX; + bai.bai_socktype = SOCK_STREAM; + /* AF_UNIX always returns 0, regardless of socktype */ + assert_int_equal(BIO_ADDRINFO_protocol(&bai), 0); +} +#endif + +static void test_addrinfo_sockaddr_size(void **state) +{ + BIO_ADDRINFO bai; + + (void)state; + assert_int_equal(BIO_ADDRINFO_sockaddr_size(NULL), 0); + memset(&bai, 0, sizeof(bai)); + bai.bai_addrlen = 28; + assert_int_equal(BIO_ADDRINFO_sockaddr_size(&bai), 28); +} + +static void test_addrinfo_sockaddr(void **state) +{ + BIO_ADDRINFO bai; + struct sockaddr sa; + + (void)state; + assert_null(BIO_ADDRINFO_sockaddr(NULL)); + memset(&bai, 0, sizeof(bai)); + bai.bai_addr = &sa; + assert_ptr_equal(BIO_ADDRINFO_sockaddr(&bai), &sa); +} + +static void test_addrinfo_address(void **state) +{ + BIO_ADDRINFO bai; + struct sockaddr sa; + + (void)state; + assert_null(BIO_ADDRINFO_address(NULL)); + memset(&bai, 0, sizeof(bai)); + bai.bai_addr = &sa; + assert_ptr_equal(BIO_ADDRINFO_address(&bai), (BIO_ADDR *)&sa); +} + +/* BIO_ADDRINFO_free */ + +static void test_addrinfo_free_null(void **state) +{ + (void)state; + BIO_ADDRINFO_free(NULL); /* must not crash */ +} + +#ifdef AI_PASSIVE +static void test_addrinfo_free_ip(void **state) +{ + /* AF_INET with AI_PASSIVE: freeaddrinfo is called, not manual free */ + struct addrinfo bai; + + (void)state; + memset(&bai, 0, sizeof(bai)); + bai.ai_family = AF_INET; + + expect_freeaddrinfo(&bai); + BIO_ADDRINFO_free(&bai); +} +#endif + +/* BIO_parse_hostserv */ + +static void test_parse_host_and_service(void **state) +{ + char *host = NULL, *service = NULL; + + (void)state; + assert_int_equal( + BIO_parse_hostserv("host.example:443", &host, &service, + BIO_PARSE_PRIO_HOST), + 1); + assert_string_equal(host, "host.example"); + assert_string_equal(service, "443"); + OPENSSL_free(host); + OPENSSL_free(service); +} + +static void test_parse_empty_host(void **state) +{ + char *host = NULL, *service = NULL; + + (void)state; + assert_int_equal( + BIO_parse_hostserv(":80", &host, &service, BIO_PARSE_PRIO_HOST), 1); + assert_null(host); + assert_string_equal(service, "80"); + OPENSSL_free(service); +} + +static void test_parse_star_host(void **state) +{ + char *host = NULL, *service = NULL; + + (void)state; + assert_int_equal( + BIO_parse_hostserv("*:80", &host, &service, BIO_PARSE_PRIO_HOST), 1); + assert_null(host); + assert_string_equal(service, "80"); + OPENSSL_free(service); +} + +static void test_parse_empty_service(void **state) +{ + char *host = NULL, *service = NULL; + + (void)state; + assert_int_equal( + BIO_parse_hostserv("host:", &host, &service, BIO_PARSE_PRIO_HOST), 1); + assert_string_equal(host, "host"); + assert_null(service); + OPENSSL_free(host); +} + +static void test_parse_star_service(void **state) +{ + char *host = NULL, *service = NULL; + + (void)state; + assert_int_equal( + BIO_parse_hostserv("host:*", &host, &service, BIO_PARSE_PRIO_HOST), 1); + assert_string_equal(host, "host"); + assert_null(service); + OPENSSL_free(host); +} + +static void test_parse_no_colon_host_prio(void **state) +{ + /* no colon + HOST prio: host set, service untouched */ + char *host = NULL, *service = NULL; + + (void)state; + assert_int_equal( + BIO_parse_hostserv("myhost", &host, &service, BIO_PARSE_PRIO_HOST), 1); + assert_string_equal(host, "myhost"); + assert_null(service); + OPENSSL_free(host); +} + +static void test_parse_no_colon_serv_prio(void **state) +{ + /* no colon + SERV prio: service set, host untouched */ + char *host = NULL, *service = NULL; + + (void)state; + assert_int_equal( + BIO_parse_hostserv("https", &host, &service, BIO_PARSE_PRIO_SERV), 1); + assert_null(host); + assert_string_equal(service, "https"); + OPENSSL_free(service); +} + +static void test_parse_bracket_host_and_service(void **state) +{ + char *host = NULL, *service = NULL; + + (void)state; + assert_int_equal( + BIO_parse_hostserv("[::1]:443", &host, &service, BIO_PARSE_PRIO_HOST), 1); + assert_string_equal(host, "::1"); + assert_string_equal(service, "443"); + OPENSSL_free(host); + OPENSSL_free(service); +} + +static void test_parse_bracket_no_service(void **state) +{ + /* "[host]" with no trailing colon: service left untouched */ + char *host = NULL, *service = NULL; + + (void)state; + assert_int_equal( + BIO_parse_hostserv("[::1]", &host, &service, BIO_PARSE_PRIO_HOST), 1); + assert_string_equal(host, "::1"); + assert_null(service); + OPENSSL_free(host); +} + +static void test_parse_bracket_unclosed(void **state) +{ + char *host = NULL, *service = NULL; + + (void)state; + assert_int_equal( + BIO_parse_hostserv("[::1", &host, &service, BIO_PARSE_PRIO_HOST), 0); +} + +static void test_parse_bracket_bad_suffix(void **state) +{ + /* ']' not followed by ':' or '\0' */ + char *host = NULL, *service = NULL; + + (void)state; + assert_int_equal( + BIO_parse_hostserv("[::1]X", &host, &service, BIO_PARSE_PRIO_HOST), 0); +} + +static void test_parse_bracket_service_with_colon(void **state) +{ + /* service part contains a colon */ + char *host = NULL, *service = NULL; + + (void)state; + assert_int_equal( + BIO_parse_hostserv("[host]:a:b", &host, &service, BIO_PARSE_PRIO_HOST), 0); +} + +static void test_parse_ambiguous(void **state) +{ + /* multiple colons without brackets */ + char *host = NULL, *service = NULL; + + (void)state; + assert_int_equal( + BIO_parse_hostserv("host:port:extra", &host, &service, + BIO_PARSE_PRIO_HOST), + 0); +} + +static void test_parse_null_host_param(void **state) +{ + /* host output param NULL */ + char *service = NULL; + + (void)state; + assert_int_equal( + BIO_parse_hostserv("host:443", NULL, &service, BIO_PARSE_PRIO_HOST), 1); + assert_string_equal(service, "443"); + OPENSSL_free(service); +} + +static void test_parse_null_service_param(void **state) +{ + /* service output param NULL */ + char *host = NULL; + + (void)state; + assert_int_equal( + BIO_parse_hostserv("host:443", &host, NULL, BIO_PARSE_PRIO_HOST), 1); + assert_string_equal(host, "host"); + OPENSSL_free(host); +} + +/* BIO_ADDR_hostname_string and BIO_ADDR_service_string (addr_strings) */ + +static void make_ipv4_addr(BIO_ADDR *ap, unsigned short port) +{ + struct in_addr addr4; + + addr4.s_addr = htonl(INADDR_LOOPBACK); + BIO_ADDR_rawmake(ap, AF_INET, &addr4, sizeof(addr4), htons(port)); +} + +static void test_hostname_string_sock_init_fail(void **state) +{ + BIO_ADDR ap; + + (void)state; + make_ipv4_addr(&ap, 80); + + expect_sock_init(0); + assert_null(BIO_ADDR_hostname_string(&ap, 1)); +} + +static void test_service_string_sock_init_fail(void **state) +{ + BIO_ADDR ap; + + (void)state; + make_ipv4_addr(&ap, 443); + + expect_sock_init(0); + assert_null(BIO_ADDR_service_string(&ap, 1)); +} + +#ifdef AI_PASSIVE +static void test_hostname_string_getnameinfo_fail(void **state) +{ + BIO_ADDR ap; + + (void)state; + make_ipv4_addr(&ap, 80); + + expect_sock_init(1); + expect_getnameinfo(BIO_ADDR_sockaddr(&ap), + BIO_ADDR_sockaddr_size(&ap), + NI_NUMERICHOST | NI_NUMERICSERV, + EAI_AGAIN, NULL, NULL); + assert_null(BIO_ADDR_hostname_string(&ap, 1)); +} + +static void test_hostname_string_numeric(void **state) +{ + BIO_ADDR ap; + char *result; + + (void)state; + make_ipv4_addr(&ap, 80); + + expect_sock_init(1); + expect_getnameinfo(BIO_ADDR_sockaddr(&ap), + BIO_ADDR_sockaddr_size(&ap), + NI_NUMERICHOST | NI_NUMERICSERV, + 0, "127.0.0.1", "80"); + result = BIO_ADDR_hostname_string(&ap, 1); + assert_non_null(result); + assert_string_equal(result, "127.0.0.1"); + OPENSSL_free(result); +} + +static void test_hostname_string_non_numeric(void **state) +{ + BIO_ADDR ap; + char *result; + + (void)state; + make_ipv4_addr(&ap, 80); + + expect_sock_init(1); + expect_getnameinfo(BIO_ADDR_sockaddr(&ap), + BIO_ADDR_sockaddr_size(&ap), + 0, /* flags = 0 for non-numeric lookup */ + 0, "localhost", "http"); + result = BIO_ADDR_hostname_string(&ap, 0); + assert_non_null(result); + assert_string_equal(result, "localhost"); + OPENSSL_free(result); +} + +static void test_service_string_numeric(void **state) +{ + BIO_ADDR ap; + char *result; + + (void)state; + make_ipv4_addr(&ap, 443); + + expect_sock_init(1); + expect_getnameinfo(BIO_ADDR_sockaddr(&ap), + BIO_ADDR_sockaddr_size(&ap), + NI_NUMERICHOST | NI_NUMERICSERV, + 0, "127.0.0.1", "443"); + result = BIO_ADDR_service_string(&ap, 1); + assert_non_null(result); + assert_string_equal(result, "443"); + OPENSSL_free(result); +} +#else + +/* inet_ntoa path: no getnameinfo, result is deterministic from the address */ +static void test_hostname_string_fallback(void **state) +{ + BIO_ADDR ap; + char *result; + + (void)state; + make_ipv4_addr(&ap, 80); + + expect_sock_init(1); + result = BIO_ADDR_hostname_string(&ap, 1); + assert_non_null(result); + assert_string_equal(result, "127.0.0.1"); + OPENSSL_free(result); +} + +static void test_service_string_fallback(void **state) +{ + BIO_ADDR ap; + char *result; + + (void)state; + make_ipv4_addr(&ap, 443); + + expect_sock_init(1); + result = BIO_ADDR_service_string(&ap, 1); + assert_non_null(result); + assert_string_equal(result, "443"); + OPENSSL_free(result); +} + +#endif /* AI_PASSIVE */ + +/* main */ + +#define ADDR_TEST(name) cmocka_unit_test(name) + +int main(void) +{ + const struct CMUnitTest tests[] = { + /* BIO_ADDR_clear */ + ADDR_TEST(test_addr_clear), + /* BIO_ADDR_make */ + ADDR_TEST(test_addr_make_ipv4), +#if OPENSSL_USE_IPV6 + ADDR_TEST(test_addr_make_ipv6), +#endif +#ifndef OPENSSL_NO_UNIX_SOCK + ADDR_TEST(test_addr_make_unix), +#endif + ADDR_TEST(test_addr_make_unknown_family), + /* BIO_ADDR_rawmake */ + ADDR_TEST(test_rawmake_ipv4), + ADDR_TEST(test_rawmake_ipv4_wrong_len), +#if OPENSSL_USE_IPV6 + ADDR_TEST(test_rawmake_ipv6), +#endif +#ifndef OPENSSL_NO_UNIX_SOCK + ADDR_TEST(test_rawmake_unix), + ADDR_TEST(test_rawmake_unix_too_long), +#endif + ADDR_TEST(test_rawmake_unknown_family), + /* BIO_ADDR_family */ + ADDR_TEST(test_addr_family), + /* BIO_ADDR_rawport */ + ADDR_TEST(test_rawport_ipv4), +#if OPENSSL_USE_IPV6 + ADDR_TEST(test_rawport_ipv6), +#endif + ADDR_TEST(test_rawport_no_port), + /* BIO_ADDR_sockaddr / BIO_ADDR_sockaddr_noconst */ + ADDR_TEST(test_sockaddr_pointers), + /* BIO_ADDR_sockaddr_size */ + ADDR_TEST(test_sockaddr_size_ipv4), +#if OPENSSL_USE_IPV6 + ADDR_TEST(test_sockaddr_size_ipv6), +#endif +#ifndef OPENSSL_NO_UNIX_SOCK + ADDR_TEST(test_sockaddr_size_unix), +#endif + ADDR_TEST(test_sockaddr_size_default), + /* BIO_ADDR_rawaddress */ + ADDR_TEST(test_rawaddress_unset), + ADDR_TEST(test_rawaddress_ipv4), + ADDR_TEST(test_rawaddress_ipv4_len_only), + ADDR_TEST(test_rawaddress_ipv4_ptr_only), +#ifndef OPENSSL_NO_UNIX_SOCK + ADDR_TEST(test_rawaddress_unix), +#endif + /* BIO_ADDR_copy */ + ADDR_TEST(test_addr_copy_null), + ADDR_TEST(test_addr_copy_unspec), + ADDR_TEST(test_addr_copy_ipv4), + /* BIO_ADDR_new and BIO_ADDR_free */ + ADDR_TEST(test_addr_new_sets_unspec), + /* BIO_ADDR_dup */ + ADDR_TEST(test_addr_dup_null), + ADDR_TEST(test_addr_dup_ipv4), + /* BIO_ADDR_path_string */ +#ifndef OPENSSL_NO_UNIX_SOCK + ADDR_TEST(test_path_string_unix), +#endif + ADDR_TEST(test_path_string_non_unix), + /* BIO_ADDRINFO accessors */ + ADDR_TEST(test_addrinfo_next_null), + ADDR_TEST(test_addrinfo_next), + ADDR_TEST(test_addrinfo_family), + ADDR_TEST(test_addrinfo_socktype), + ADDR_TEST(test_addrinfo_protocol_null), + ADDR_TEST(test_addrinfo_protocol_explicit), + ADDR_TEST(test_addrinfo_protocol_stream), + ADDR_TEST(test_addrinfo_protocol_dgram), +#ifndef OPENSSL_NO_UNIX_SOCK + ADDR_TEST(test_addrinfo_protocol_unix), +#endif + ADDR_TEST(test_addrinfo_sockaddr_size), + ADDR_TEST(test_addrinfo_sockaddr), + ADDR_TEST(test_addrinfo_address), + /* BIO_ADDRINFO_free */ + ADDR_TEST(test_addrinfo_free_null), +#ifdef AI_PASSIVE + ADDR_TEST(test_addrinfo_free_ip), +#endif + /* BIO_parse_hostserv */ + ADDR_TEST(test_parse_host_and_service), + ADDR_TEST(test_parse_empty_host), + ADDR_TEST(test_parse_star_host), + ADDR_TEST(test_parse_empty_service), + ADDR_TEST(test_parse_star_service), + ADDR_TEST(test_parse_no_colon_host_prio), + ADDR_TEST(test_parse_no_colon_serv_prio), + ADDR_TEST(test_parse_bracket_host_and_service), + ADDR_TEST(test_parse_bracket_no_service), + ADDR_TEST(test_parse_bracket_unclosed), + ADDR_TEST(test_parse_bracket_bad_suffix), + ADDR_TEST(test_parse_bracket_service_with_colon), + ADDR_TEST(test_parse_ambiguous), + ADDR_TEST(test_parse_null_host_param), + ADDR_TEST(test_parse_null_service_param), + /* BIO_ADDR_hostname_string / BIO_ADDR_service_string */ + ADDR_TEST(test_hostname_string_sock_init_fail), + ADDR_TEST(test_service_string_sock_init_fail), +#ifdef AI_PASSIVE + ADDR_TEST(test_hostname_string_getnameinfo_fail), + ADDR_TEST(test_hostname_string_numeric), + ADDR_TEST(test_hostname_string_non_numeric), + ADDR_TEST(test_service_string_numeric), +#else + ADDR_TEST(test_hostname_string_fallback), + ADDR_TEST(test_service_string_fallback), +#endif + }; + + cmocka_set_message_output(CM_OUTPUT_TAP); + + return cmocka_run_group_tests(tests, NULL, NULL); +} + +#endif /* OPENSSL_NO_SOCK */ From 90afdb14ed35910f3311f08fa1f4301340164e4d Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Fri, 12 Jun 2026 13:52:16 +0200 Subject: [PATCH 203/349] Add BIO bss_conn test Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Richard Levitte Reviewed-by: Tomas Mraz MergeDate: Thu Jul 9 17:39:19 2026 (Merged from https://github.com/openssl/openssl/pull/30788) --- crypto/bio/bio_local.h | 40 + crypto/bio/bss_conn.c | 39 - test/unit/build.info | 8 + test/unit/crypto/bio/test_bss_conn.c | 1590 ++++++++++++++++++++++++++ 4 files changed, 1638 insertions(+), 39 deletions(-) create mode 100644 test/unit/crypto/bio/test_bss_conn.c diff --git a/crypto/bio/bio_local.h b/crypto/bio/bio_local.h index 36f72f5df1..d272eb1861 100644 --- a/crypto/bio/bio_local.h +++ b/crypto/bio/bio_local.h @@ -122,6 +122,46 @@ struct bio_st { }; #ifndef OPENSSL_NO_SOCK + +typedef struct bio_connect_st { + int state; + int connect_family; + int connect_sock_type; + char *param_hostname; + char *param_service; + int connect_mode; +#ifndef OPENSSL_NO_KTLS + unsigned char record_type; +#endif + int tfo_first; + + BIO_ADDRINFO *addr_first; + const BIO_ADDRINFO *addr_iter; + /* + * int socket; this will be kept in bio->num so that it is compatible + * with the bss_sock bio + */ + /* + * called when the connection is initially made callback(BIO,state,ret); + * The callback should return 'ret'. state is for compatibility with the + * ssl info_callback + */ + BIO_info_cb *info_callback; + /* + * Used when connect_sock_type is SOCK_DGRAM. Owned by us; we forward + * read/write(mmsg) calls to this if present. + */ + BIO *dgram_bio; +} BIO_CONNECT; + +#define BIO_CONN_S_BEFORE 1 +#define BIO_CONN_S_GET_ADDR 2 +#define BIO_CONN_S_CREATE_SOCKET 3 +#define BIO_CONN_S_CONNECT 4 +#define BIO_CONN_S_OK 5 +#define BIO_CONN_S_BLOCKED_CONNECT 6 +#define BIO_CONN_S_CONNECT_ERROR 7 + #ifdef OPENSSL_SYS_VMS typedef unsigned int socklen_t; #endif diff --git a/crypto/bio/bss_conn.c b/crypto/bio/bss_conn.c index 0e84e959d8..c4355392e4 100644 --- a/crypto/bio/bss_conn.c +++ b/crypto/bio/bss_conn.c @@ -16,37 +16,6 @@ #ifndef OPENSSL_NO_SOCK -typedef struct bio_connect_st { - int state; - int connect_family; - int connect_sock_type; - char *param_hostname; - char *param_service; - int connect_mode; -#ifndef OPENSSL_NO_KTLS - unsigned char record_type; -#endif - int tfo_first; - - BIO_ADDRINFO *addr_first; - const BIO_ADDRINFO *addr_iter; - /* - * int socket; this will be kept in bio->num so that it is compatible - * with the bss_sock bio - */ - /* - * called when the connection is initially made callback(BIO,state,ret); - * The callback should return 'ret'. state is for compatibility with the - * ssl info_callback - */ - BIO_info_cb *info_callback; - /* - * Used when connect_sock_type is SOCK_DGRAM. Owned by us; we forward - * read/write(mmsg) calls to this if present. - */ - BIO *dgram_bio; -} BIO_CONNECT; - static int conn_write(BIO *h, const char *buf, int num); static int conn_read(BIO *h, char *buf, int size); static int conn_puts(BIO *h, const char *str); @@ -65,14 +34,6 @@ static void conn_close_socket(BIO *data); static BIO_CONNECT *BIO_CONNECT_new(void); static void BIO_CONNECT_free(BIO_CONNECT *a); -#define BIO_CONN_S_BEFORE 1 -#define BIO_CONN_S_GET_ADDR 2 -#define BIO_CONN_S_CREATE_SOCKET 3 -#define BIO_CONN_S_CONNECT 4 -#define BIO_CONN_S_OK 5 -#define BIO_CONN_S_BLOCKED_CONNECT 6 -#define BIO_CONN_S_CONNECT_ERROR 7 - static const BIO_METHOD methods_connectp = { BIO_TYPE_CONNECT, "socket connect", diff --git a/test/unit/build.info b/test/unit/build.info index befcc67279..cf2a748418 100644 --- a/test/unit/build.info +++ b/test/unit/build.info @@ -6,6 +6,14 @@ INCLUDE[crypto/bio/test_bio_addr]=../../include ../../crypto/bio DEPEND[crypto/bio/test_bio_addr]=../../libcrypto.a WRAP[crypto/bio/test_bio_addr]=BIO_sock_init getnameinfo freeaddrinfo +PROGRAMS{noinst}=crypto/bio/test_bss_conn +SOURCE[crypto/bio/test_bss_conn]=crypto/bio/test_bss_conn.c +WRAP[crypto/bio/test_bss_conn]=BIO_lookup BIO_socket BIO_connect \ + BIO_sock_should_retry BIO_closesocket BIO_socket_wait BIO_sock_error \ + read write +INCLUDE[crypto/bio/test_bss_conn]=../../include ../../crypto/bio +DEPEND[crypto/bio/test_bss_conn]=../../libcrypto.a + PROGRAMS{noinst}=crypto/bio/test_bss_fd SOURCE[crypto/bio/test_bss_fd]=crypto/bio/test_bss_fd.c INCLUDE[crypto/bio/test_bss_fd]=../../include ../../crypto/bio diff --git a/test/unit/crypto/bio/test_bss_conn.c b/test/unit/crypto/bio/test_bss_conn.c new file mode 100644 index 0000000000..992a1b781a --- /dev/null +++ b/test/unit/crypto/bio/test_bss_conn.c @@ -0,0 +1,1590 @@ +/* + * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "internal/sockets.h" + +#ifndef OPENSSL_NO_SOCK + +#include +#include +#include +#include +#include +#include +#include +#include +#include "bio_local.h" + +#define FAKE_SOCKET 42 + +/* + * Fake addrinfo used across state machine tests. g_addrinfo1.bai_next is + * NULL by default. Tests needing a second address temporarily set it to + * &g_addrinfo2 and restore it afterwards. + */ +static struct sockaddr_in g_sin; +static BIO_ADDRINFO g_addrinfo1; +static BIO_ADDRINFO g_addrinfo2; + +/* prototypes for __wrap_* (required by -Wmissing-prototypes) */ +ssize_t __wrap_read(int fd, void *buf, size_t count); +ssize_t __wrap_write(int fd, const void *buf, size_t count); +int __wrap_BIO_lookup(const char *host, const char *service, + enum BIO_lookup_type lookup_type, + int family, int socktype, BIO_ADDRINFO **res); +int __wrap_BIO_socket(int domain, int socktype, int protocol, int options); +int __wrap_BIO_connect(int sock, const BIO_ADDR *addr, int options); +int __wrap_BIO_sock_should_retry(int i); +int __wrap_BIO_closesocket(int sock); +int __wrap_BIO_socket_wait(int fd, int for_write, time_t max_time); +int __wrap_BIO_sock_error(int sock); + +/* wraps */ + +ssize_t __wrap_read(int fd, void *buf, size_t count) +{ + function_called(); + check_expected(fd); + check_expected_ptr(buf); + check_expected(count); + return mock_type(ssize_t); +} + +ssize_t __wrap_write(int fd, const void *buf, size_t count) +{ + function_called(); + check_expected(fd); + check_expected_ptr(buf); + check_expected(count); + return mock_type(ssize_t); +} + +int __wrap_BIO_lookup(const char *host, const char *service, + enum BIO_lookup_type lookup_type, + int family, int socktype, BIO_ADDRINFO **res) +{ + int rc; + + function_called(); + check_expected(host); + check_expected(service); + check_expected(lookup_type); + check_expected(family); + check_expected(socktype); + rc = mock_type(int); + if (rc == 1) + *res = mock_ptr_type(BIO_ADDRINFO *); + return rc; +} + +int __wrap_BIO_socket(int domain, int socktype, int protocol, int options) +{ + function_called(); + check_expected(domain); + check_expected(socktype); + check_expected(protocol); + check_expected(options); + return mock_type(int); +} + +int __wrap_BIO_connect(int sock, const BIO_ADDR *addr, int options) +{ + function_called(); + check_expected(sock); + check_expected_ptr(addr); + check_expected(options); + return mock_type(int); +} + +int __wrap_BIO_sock_should_retry(int i) +{ + function_called(); + check_expected(i); + return mock_type(int); +} + +int __wrap_BIO_closesocket(int sock) +{ + function_called(); + check_expected(sock); + return mock_type(int); +} + +int __wrap_BIO_socket_wait(int fd, int for_write, time_t max_time) +{ + function_called(); + check_expected(fd); + check_expected(for_write); + (void)max_time; /* derived from time(NULL): not checked */ + return mock_type(int); +} + +int __wrap_BIO_sock_error(int sock) +{ + function_called(); + check_expected(sock); + return mock_type(int); +} + +/* + * A minimal fake dgram BIO. conn_read/conn_write/conn_sendmmsg/conn_recvmmsg + * delegate to the public BIO_* calls on data->dgram_bio, so the leaf needs + * read/write/sendmmsg/recvmmsg to dispatch. Everything the connect layer + * forwards (the buffer, message array, stride, count and flags) is verified. + */ + +static int fake_dgram_read(BIO *b, char *buf, size_t size, size_t *readbytes); +static int fake_dgram_write(BIO *b, const char *buf, size_t size, + size_t *written); +static long fake_dgram_ctrl(BIO *b, int cmd, long arg1, void *arg2); +static int fake_dgram_sendmmsg(BIO *b, BIO_MSG *m, size_t s, size_t n, + uint64_t f, size_t *mp); +static int fake_dgram_recvmmsg(BIO *b, BIO_MSG *m, size_t s, size_t n, + uint64_t f, size_t *mp); + +static int fake_dgram_read(BIO *b, char *buf, size_t size, size_t *readbytes) +{ + int ret; + + (void)b; + function_called(); + check_expected_ptr(buf); + check_expected(size); + ret = mock_type(int); + if (ret > 0) { + *readbytes = (size_t)ret; + return 1; + } + *readbytes = 0; + return ret; +} + +static int fake_dgram_write(BIO *b, const char *buf, size_t size, + size_t *written) +{ + int ret; + + (void)b; + function_called(); + check_expected_ptr(buf); + check_expected(size); + ret = mock_type(int); + if (ret > 0) { + *written = (size_t)ret; + return 1; + } + *written = 0; + return ret; +} + +static long fake_dgram_ctrl(BIO *b, int cmd, long arg1, void *arg2) +{ + (void)b; + (void)arg1; + (void)arg2; + if (cmd == BIO_CTRL_FLUSH) + return 1; + return 0; +} + +static int fake_dgram_sendmmsg(BIO *b, BIO_MSG *m, size_t s, size_t n, + uint64_t f, size_t *mp) +{ + (void)b; + function_called(); + check_expected_ptr(m); + check_expected(s); + check_expected(n); + check_expected(f); + *mp = mock_type(size_t); + return mock_type(int); +} + +static int fake_dgram_recvmmsg(BIO *b, BIO_MSG *m, size_t s, size_t n, + uint64_t f, size_t *mp) +{ + (void)b; + function_called(); + check_expected_ptr(m); + check_expected(s); + check_expected(n); + check_expected(f); + *mp = mock_type(size_t); + return mock_type(int); +} + +static BIO_METHOD *fake_dgram_method = NULL; + +static BIO_METHOD *make_fake_dgram_method(void) +{ + BIO_METHOD *m = BIO_meth_new(BIO_TYPE_DGRAM | 0xff, "fake dgram"); + + assert_non_null(m); + assert_true(BIO_meth_set_read_ex(m, fake_dgram_read)); + assert_true(BIO_meth_set_write_ex(m, fake_dgram_write)); + assert_true(BIO_meth_set_ctrl(m, fake_dgram_ctrl)); + assert_true(BIO_meth_set_sendmmsg(m, fake_dgram_sendmmsg)); + assert_true(BIO_meth_set_recvmmsg(m, fake_dgram_recvmmsg)); + return m; +} + +static BIO *make_fake_dgram(void) +{ + BIO *d = BIO_new(fake_dgram_method); + + assert_non_null(d); + BIO_set_init(d, 1); + return d; +} + +/* expectations */ + +static void expect_read(int fd, const void *buf, size_t count, ssize_t rc) +{ + expect_function_call(__wrap_read); + expect_value(__wrap_read, fd, fd); + expect_value(__wrap_read, buf, buf); + expect_value(__wrap_read, count, count); + will_return(__wrap_read, rc); +} + +static void expect_write(int fd, const void *buf, size_t count, ssize_t rc) +{ + expect_function_call(__wrap_write); + expect_value(__wrap_write, fd, fd); + expect_value(__wrap_write, buf, buf); + expect_value(__wrap_write, count, count); + will_return(__wrap_write, rc); +} + +static void expect_BIO_lookup(BIO_ADDRINFO *res, int rc) +{ + expect_function_call(__wrap_BIO_lookup); + expect_any(__wrap_BIO_lookup, host); + expect_any(__wrap_BIO_lookup, service); + expect_any(__wrap_BIO_lookup, lookup_type); + expect_any(__wrap_BIO_lookup, family); + expect_any(__wrap_BIO_lookup, socktype); + will_return(__wrap_BIO_lookup, rc); + if (rc == 1) + will_return(__wrap_BIO_lookup, res); +} + +/* options is the literal the state machine passes (always 0 here) */ +static void expect_BIO_socket(int domain, int socktype, int protocol, + int options, int rc) +{ + expect_function_call(__wrap_BIO_socket); + expect_value(__wrap_BIO_socket, domain, domain); + expect_value(__wrap_BIO_socket, socktype, socktype); + expect_value(__wrap_BIO_socket, protocol, protocol); + expect_value(__wrap_BIO_socket, options, options); + will_return(__wrap_BIO_socket, rc); +} + +static void expect_BIO_connect(int sock, const BIO_ADDR *addr, int options, + int rc) +{ + expect_function_call(__wrap_BIO_connect); + expect_value(__wrap_BIO_connect, sock, sock); + expect_value(__wrap_BIO_connect, addr, addr); + expect_value(__wrap_BIO_connect, options, options); + will_return(__wrap_BIO_connect, rc); +} + +static void expect_BIO_sock_should_retry(int i, int rc) +{ + expect_function_call(__wrap_BIO_sock_should_retry); + expect_value(__wrap_BIO_sock_should_retry, i, i); + will_return(__wrap_BIO_sock_should_retry, rc); +} + +static void expect_BIO_closesocket(int sock, int rc) +{ + expect_function_call(__wrap_BIO_closesocket); + expect_value(__wrap_BIO_closesocket, sock, sock); + will_return(__wrap_BIO_closesocket, rc); +} + +static void expect_BIO_socket_wait(int fd, int for_write, int rc) +{ + expect_function_call(__wrap_BIO_socket_wait); + expect_value(__wrap_BIO_socket_wait, fd, fd); + expect_value(__wrap_BIO_socket_wait, for_write, for_write); + will_return(__wrap_BIO_socket_wait, rc); +} + +static void expect_BIO_sock_error(int sock, int rc) +{ + expect_function_call(__wrap_BIO_sock_error); + expect_value(__wrap_BIO_sock_error, sock, sock); + will_return(__wrap_BIO_sock_error, rc); +} + +static void expect_fake_dgram_read(const void *buf, size_t size, int rc) +{ + expect_function_call(fake_dgram_read); + expect_value(fake_dgram_read, buf, buf); + expect_value(fake_dgram_read, size, size); + will_return(fake_dgram_read, rc); +} + +static void expect_fake_dgram_write(const void *buf, size_t size, int rc) +{ + expect_function_call(fake_dgram_write); + expect_value(fake_dgram_write, buf, buf); + expect_value(fake_dgram_write, size, size); + will_return(fake_dgram_write, rc); +} + +static void expect_fake_dgram_sendmmsg(const BIO_MSG *m, size_t s, size_t n, + uint64_t f, size_t processed, int rc) +{ + expect_function_call(fake_dgram_sendmmsg); + expect_value(fake_dgram_sendmmsg, m, m); + expect_value(fake_dgram_sendmmsg, s, s); + expect_value(fake_dgram_sendmmsg, n, n); + expect_value(fake_dgram_sendmmsg, f, f); + will_return(fake_dgram_sendmmsg, processed); + will_return(fake_dgram_sendmmsg, rc); +} + +static void expect_fake_dgram_recvmmsg(const BIO_MSG *m, size_t s, size_t n, + uint64_t f, size_t processed, int rc) +{ + expect_function_call(fake_dgram_recvmmsg); + expect_value(fake_dgram_recvmmsg, m, m); + expect_value(fake_dgram_recvmmsg, s, s); + expect_value(fake_dgram_recvmmsg, n, n); + expect_value(fake_dgram_recvmmsg, f, f); + will_return(fake_dgram_recvmmsg, processed); + will_return(fake_dgram_recvmmsg, rc); +} + +/* helpers */ + +static BIO_CONNECT *get_data(BIO *bio) +{ + return (BIO_CONNECT *)bio->ptr; +} + +/* + * Call at the end of any test that sets bio->num or addr_first to prevent + * unexpected BIO_closesocket or BIO_ADDRINFO_free invocations in teardown. + */ +static void reset_for_teardown(BIO *bio) +{ + BIO_CONNECT *data = get_data(bio); + + bio->num = (int)INVALID_SOCKET; + data->addr_first = NULL; + data->addr_iter = NULL; + data->state = BIO_CONN_S_BEFORE; +} + +/* setup / teardown */ + +static int setup(void **state) +{ + BIO *bio = BIO_new(BIO_s_connect()); + + assert_non_null(bio); + *state = bio; + return 0; +} + +static int teardown(void **state) +{ + if (*state != NULL) + BIO_free(*state); + return 0; +} + +/* I/O tests pre-establish state=OK so the state machine is not entered. */ +static int setup_io(void **state) +{ + BIO *bio; + BIO_CONNECT *data; + + if (setup(state) != 0) + return -1; + bio = *state; + data = get_data(bio); + data->state = BIO_CONN_S_OK; + bio->num = FAKE_SOCKET; + bio->init = 1; + return 0; +} + +static int teardown_io(void **state) +{ + if (*state != NULL) + reset_for_teardown(*state); + return teardown(state); +} + +static int group_setup(void **state) +{ + (void)state; + + fake_dgram_method = make_fake_dgram_method(); + + memset(&g_sin, 0, sizeof(g_sin)); + g_sin.sin_family = AF_INET; + g_sin.sin_port = htons(443); + g_sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + memset(&g_addrinfo1, 0, sizeof(g_addrinfo1)); + g_addrinfo1.bai_family = AF_INET; + g_addrinfo1.bai_socktype = SOCK_STREAM; + g_addrinfo1.bai_protocol = IPPROTO_TCP; + g_addrinfo1.bai_addrlen = sizeof(g_sin); + g_addrinfo1.bai_addr = (struct sockaddr *)&g_sin; + g_addrinfo1.bai_next = NULL; + + memcpy(&g_addrinfo2, &g_addrinfo1, sizeof(g_addrinfo1)); + g_addrinfo2.bai_next = NULL; + + return 0; +} + +static int group_teardown(void **state) +{ + (void)state; + BIO_meth_free(fake_dgram_method); + fake_dgram_method = NULL; + return 0; +} + +/* conn_new */ + +static void test_conn_new(void **state) +{ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + + assert_non_null(data); + assert_int_equal(data->state, BIO_CONN_S_BEFORE); + assert_int_equal(data->connect_family, BIO_FAMILY_IPANY); + assert_int_equal(data->connect_sock_type, SOCK_STREAM); + assert_null(data->param_hostname); + assert_null(data->param_service); + assert_null(data->addr_first); + assert_null(data->dgram_bio); + assert_int_equal(bio->num, (int)INVALID_SOCKET); + assert_int_equal(bio->init, 0); +} + +/* conn_free */ + +static void test_conn_free_no_shutdown(void **state) +{ + /* shutdown=0: conn_close_socket and BIO_CONNECT_free are both skipped */ + BIO *bio = BIO_new(BIO_s_connect()); + + assert_non_null(bio); + bio->shutdown = BIO_NOCLOSE; + BIO_free(bio); + *state = NULL; +} + +/* conn_close_socket (via BIO_CTRL_RESET) */ + +static void test_close_socket_none(void **state) +{ + /* bio->num == INVALID_SOCKET: no calls expected */ + assert_int_equal(BIO_ctrl(*state, BIO_CTRL_RESET, 0, NULL), 0); +} + +static void test_close_socket_non_ok_state(void **state) +{ + /* Socket open but state != OK: BIO_closesocket called, no shutdown */ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + + bio->num = FAKE_SOCKET; + data->state = BIO_CONN_S_BLOCKED_CONNECT; + + expect_BIO_closesocket(FAKE_SOCKET, 0); + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_RESET, 0, NULL), 0); + assert_int_equal(bio->num, (int)INVALID_SOCKET); + assert_int_equal(data->state, BIO_CONN_S_BEFORE); +} + +static void test_close_socket_ok_state(void **state) +{ + /* Socket open and state=OK: shutdown first, then BIO_closesocket */ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + + bio->num = FAKE_SOCKET; + data->state = BIO_CONN_S_OK; + + expect_BIO_closesocket(FAKE_SOCKET, 0); + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_RESET, 0, NULL), 0); + assert_int_equal(bio->num, (int)INVALID_SOCKET); + assert_int_equal(data->state, BIO_CONN_S_BEFORE); +} + +/* conn_state (via BIO_C_DO_STATE_MACHINE) */ + +static void test_conn_state_no_hostname(void **state) +{ + /* BEFORE with no hostname and no service */ + assert_true(BIO_ctrl(*state, BIO_C_DO_STATE_MACHINE, 0, NULL) <= 0); +} + +static void test_conn_state_unsupported_family(void **state) +{ + /* BEFORE -> GET_ADDR -> unrecognised connect_family -> error */ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + + data->param_hostname = OPENSSL_strdup("host"); + data->param_service = OPENSSL_strdup("443"); + data->connect_family = 9999; + + assert_true(BIO_ctrl(bio, BIO_C_DO_STATE_MACHINE, 0, NULL) <= 0); +} + +static void test_conn_state_lookup_fails(void **state) +{ + /* BEFORE -> GET_ADDR -> BIO_lookup returns 0 */ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + + data->param_hostname = OPENSSL_strdup("host"); + data->param_service = OPENSSL_strdup("443"); + + expect_BIO_lookup(NULL, 0); + assert_true(BIO_ctrl(bio, BIO_C_DO_STATE_MACHINE, 0, NULL) <= 0); +} + +static void test_conn_state_socket_fails(void **state) +{ + /* Pre-set CREATE_SOCKET: BIO_socket returns INVALID_SOCKET */ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + + data->state = BIO_CONN_S_CREATE_SOCKET; + data->addr_iter = &g_addrinfo1; + + expect_BIO_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, + (int)INVALID_SOCKET); + assert_true(BIO_ctrl(bio, BIO_C_DO_STATE_MACHINE, 0, NULL) <= 0); + + data->addr_iter = NULL; + data->state = BIO_CONN_S_BEFORE; +} + +static void test_conn_state_connect_succeeds(void **state) +{ + /* Full happy path: BEFORE -> GET_ADDR -> CREATE_SOCKET -> CONNECT -> OK */ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + + data->param_hostname = OPENSSL_strdup("host"); + data->param_service = OPENSSL_strdup("443"); + + expect_BIO_lookup(&g_addrinfo1, 1); + expect_BIO_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, FAKE_SOCKET); + /* STREAM addrinfo adds KEEPALIVE to the default (zero) connect_mode */ + expect_BIO_connect(FAKE_SOCKET, (BIO_ADDR *)&g_sin, BIO_SOCK_KEEPALIVE, 1); + + assert_int_equal(BIO_ctrl(bio, BIO_C_DO_STATE_MACHINE, 0, NULL), 1); + assert_int_equal(data->state, BIO_CONN_S_OK); + + reset_for_teardown(bio); +} + +static void test_conn_state_already_ok(void **state) +{ + /* State already OK: returns 1 with no external calls */ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + + data->state = BIO_CONN_S_OK; + assert_int_equal(BIO_ctrl(bio, BIO_C_DO_STATE_MACHINE, 0, NULL), 1); + data->state = BIO_CONN_S_BEFORE; +} + +static void test_conn_state_connect_retry(void **state) +{ + /* Pre-set CONNECT: BIO_connect fails with retry -> BLOCKED_CONNECT */ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + + data->state = BIO_CONN_S_CONNECT; + data->addr_iter = &g_addrinfo1; + bio->num = FAKE_SOCKET; + + expect_BIO_connect(FAKE_SOCKET, (BIO_ADDR *)&g_sin, BIO_SOCK_KEEPALIVE, 0); + expect_BIO_sock_should_retry(0, 1); + + assert_int_equal(BIO_ctrl(bio, BIO_C_DO_STATE_MACHINE, 0, NULL), 0); + assert_int_equal(data->state, BIO_CONN_S_BLOCKED_CONNECT); + assert_int_equal(bio->retry_reason, BIO_RR_CONNECT); + + reset_for_teardown(bio); +} + +static void test_conn_state_connect_error(void **state) +{ + /* Pre-set CONNECT: fails, no retry, no more addresses -> CONNECT_ERROR */ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + + data->state = BIO_CONN_S_CONNECT; + data->addr_iter = &g_addrinfo1; /* bai_next == NULL */ + bio->num = FAKE_SOCKET; + + expect_BIO_connect(FAKE_SOCKET, (BIO_ADDR *)&g_sin, BIO_SOCK_KEEPALIVE, 0); + expect_BIO_sock_should_retry(0, 0); + /* loop continues to CONNECT_ERROR which exits immediately */ + + assert_int_equal(BIO_ctrl(bio, BIO_C_DO_STATE_MACHINE, 0, NULL), 0); + + reset_for_teardown(bio); +} + +static void test_conn_state_connect_next_addr(void **state) +{ + /* + * Pre-set CONNECT with two addresses: first connect fails, iterator + * advances, second CREATE_SOCKET fails so we get a clean exit. + */ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + + g_addrinfo1.bai_next = &g_addrinfo2; + data->state = BIO_CONN_S_CONNECT; + data->addr_iter = &g_addrinfo1; + bio->num = FAKE_SOCKET; + + expect_BIO_connect(FAKE_SOCKET, (BIO_ADDR *)&g_sin, BIO_SOCK_KEEPALIVE, 0); + expect_BIO_sock_should_retry(0, 0); + expect_BIO_closesocket(FAKE_SOCKET, 0); + /* CREATE_SOCKET for g_addrinfo2 */ + expect_BIO_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, + (int)INVALID_SOCKET); + + assert_true(BIO_ctrl(bio, BIO_C_DO_STATE_MACHINE, 0, NULL) <= 0); + + g_addrinfo1.bai_next = NULL; + reset_for_teardown(bio); +} + +static void test_conn_state_blocked_ok(void **state) +{ + /* Pre-set BLOCKED_CONNECT: socket becomes writable, no error -> OK */ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + + data->state = BIO_CONN_S_BLOCKED_CONNECT; + data->addr_iter = &g_addrinfo1; + bio->num = FAKE_SOCKET; + + expect_BIO_socket_wait(FAKE_SOCKET, 0, 1); + expect_BIO_sock_error(FAKE_SOCKET, 0); + + assert_int_equal(BIO_ctrl(bio, BIO_C_DO_STATE_MACHINE, 0, NULL), 1); + assert_int_equal(data->state, BIO_CONN_S_OK); + + reset_for_teardown(bio); +} + +static void test_conn_state_blocked_error(void **state) +{ + /* Pre-set BLOCKED_CONNECT: socket error, no more addresses -> error */ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + + data->state = BIO_CONN_S_BLOCKED_CONNECT; + data->addr_iter = &g_addrinfo1; /* bai_next == NULL */ + bio->num = FAKE_SOCKET; + + expect_BIO_socket_wait(FAKE_SOCKET, 0, 1); + expect_BIO_sock_error(FAKE_SOCKET, ECONNREFUSED); + + assert_int_equal(BIO_ctrl(bio, BIO_C_DO_STATE_MACHINE, 0, NULL), 0); + + reset_for_teardown(bio); +} + +#ifndef OPENSSL_NO_DGRAM +static void test_conn_state_connect_dgram_ok(void **state) +{ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + + data->connect_sock_type = SOCK_DGRAM; + data->state = BIO_CONN_S_CONNECT; + data->addr_iter = &g_addrinfo1; + bio->num = FAKE_SOCKET; + + /* DGRAM addrinfo means opts stays at the default connect_mode (0) */ + g_addrinfo1.bai_socktype = SOCK_DGRAM; + + expect_BIO_connect(FAKE_SOCKET, (BIO_ADDR *)&g_sin, 0, 1); + + assert_int_equal(BIO_ctrl(bio, BIO_C_DO_STATE_MACHINE, 0, NULL), 1); + assert_int_equal(data->state, BIO_CONN_S_OK); + assert_non_null(data->dgram_bio); + + g_addrinfo1.bai_socktype = SOCK_STREAM; + + assert_int_equal(BIO_set_close(data->dgram_bio, BIO_NOCLOSE), 1); + BIO_free(data->dgram_bio); + data->dgram_bio = NULL; + reset_for_teardown(bio); +} +#endif + +/* conn_read */ + +static void test_conn_read_success(void **state) +{ + BIO *bio = *state; + char buf[8] = { 0 }; + + expect_read(FAKE_SOCKET, buf, 8, 8); + assert_int_equal(BIO_read(bio, buf, 8), 8); + assert_false(BIO_should_retry(bio)); + assert_false(BIO_eof(bio)); +} + +static void test_conn_read_eof(void **state) +{ + BIO *bio = *state; + char buf[8] = { 0 }; + + expect_read(FAKE_SOCKET, buf, 8, 0); + expect_BIO_sock_should_retry(0, 0); + assert_true(BIO_read(bio, buf, 8) <= 0); + assert_true(BIO_eof(bio)); +} + +static void test_conn_read_retry(void **state) +{ + BIO *bio = *state; + char buf[8] = { 0 }; + + expect_read(FAKE_SOCKET, buf, 8, -1); + expect_BIO_sock_should_retry(-1, 1); + assert_true(BIO_read(bio, buf, 8) <= 0); + assert_true(BIO_should_read(bio)); +} + +static void test_conn_read_error(void **state) +{ + BIO *bio = *state; + char buf[8] = { 0 }; + + expect_read(FAKE_SOCKET, buf, 8, -1); + expect_BIO_sock_should_retry(-1, 0); + assert_true(BIO_read(bio, buf, 8) <= 0); + assert_false(BIO_should_retry(bio)); + assert_false(BIO_eof(bio)); +} + +static void test_conn_read_enters_state_machine(void **state) +{ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + char buf[8] = { 0 }; + + data->param_hostname = OPENSSL_strdup("host"); + data->param_service = OPENSSL_strdup("443"); + bio->init = 1; + + expect_BIO_lookup(&g_addrinfo1, 1); + expect_BIO_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, FAKE_SOCKET); + expect_BIO_connect(FAKE_SOCKET, (BIO_ADDR *)&g_sin, BIO_SOCK_KEEPALIVE, 1); + expect_read(FAKE_SOCKET, buf, 8, 8); + + assert_int_equal(BIO_read(bio, buf, 8), 8); + assert_int_equal(data->state, BIO_CONN_S_OK); + + reset_for_teardown(bio); +} + +static void test_conn_read_state_machine_fails(void **state) +{ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + char buf[8] = { 0 }; + + data->param_hostname = OPENSSL_strdup("host"); + data->param_service = OPENSSL_strdup("443"); + bio->init = 1; + + expect_BIO_lookup(NULL, 0); /* conn_state <= 0, no socket touched */ + + assert_true(BIO_read(bio, buf, 8) <= 0); + + reset_for_teardown(bio); +} + +static void test_conn_read_dgram_delegates(void **state) +{ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + BIO *dg = make_fake_dgram(); + char buf[8] = { 0 }; + + data->dgram_bio = dg; + + expect_fake_dgram_read(buf, 8, 4); + assert_int_equal(BIO_read(bio, buf, 8), 4); + + data->dgram_bio = NULL; + BIO_free(dg); +} + +/* conn_write */ + +static void test_conn_write_success(void **state) +{ + BIO *bio = *state; + const char buf[] = "hello"; + + expect_write(FAKE_SOCKET, buf, 5, 5); + assert_int_equal(BIO_write(bio, buf, 5), 5); + assert_false(BIO_should_retry(bio)); +} + +static void test_conn_write_retry(void **state) +{ + BIO *bio = *state; + const char buf[] = "hello"; + + expect_write(FAKE_SOCKET, buf, 5, -1); + expect_BIO_sock_should_retry(-1, 1); + assert_true(BIO_write(bio, buf, 5) <= 0); + assert_true(BIO_should_write(bio)); +} + +static void test_conn_write_error(void **state) +{ + BIO *bio = *state; + const char buf[] = "hello"; + + expect_write(FAKE_SOCKET, buf, 5, -1); + expect_BIO_sock_should_retry(-1, 0); + assert_true(BIO_write(bio, buf, 5) <= 0); + assert_false(BIO_should_retry(bio)); +} + +static void test_conn_write_dgram_delegates(void **state) +{ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + BIO *dg = make_fake_dgram(); + const char buf[] = "hello"; + + data->dgram_bio = dg; + + expect_fake_dgram_write(buf, 5, 5); + assert_int_equal(BIO_write(bio, buf, 5), 5); + + data->dgram_bio = NULL; + BIO_free(dg); +} + +/* conn_gets + * + * conn_gets is non-static (unlike the other method functions) so it is a + * public symbol, but we exercise it via BIO_gets to stay in-interface. + */ + +static void test_conn_gets_null_buf(void **state) +{ + assert_true(BIO_gets(*state, NULL, 8) <= 0); +} + +static void test_conn_gets_zero_size(void **state) +{ + char buf[8] = { 0 }; + + assert_true(BIO_gets(*state, buf, 0) <= 0); +} + +static void test_conn_gets_null_ptr(void **state) +{ + /* bio->ptr == NULL is caught before any field access */ + BIO *bio = *state; + char buf[8] = { 0 }; + void *saved = bio->ptr; + + bio->ptr = NULL; + assert_true(BIO_gets(bio, buf, sizeof(buf)) <= 0); + bio->ptr = saved; +} + +static void test_conn_gets_dgram_bio_set(void **state) +{ + /* dgram_bio present is an error for gets */ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + char buf[8] = { 0 }; + BIO fake_dgram; + + data->dgram_bio = &fake_dgram; + assert_int_equal(BIO_gets(bio, buf, sizeof(buf)), -1); + data->dgram_bio = NULL; +} + +static void test_conn_gets_newline(void **state) +{ + BIO *bio = *state; + char buf[8] = { 'h', 'i', '\n' }; + + expect_read(FAKE_SOCKET, buf, 1, 1); + expect_read(FAKE_SOCKET, buf + 1, 1, 1); + expect_read(FAKE_SOCKET, buf + 2, 1, 1); + + assert_int_equal(BIO_gets(bio, buf, sizeof(buf)), 3); +} + +static void test_conn_gets_fills_buffer(void **state) +{ + /* size=4 allows at most 3 chars before the terminating NUL */ + BIO *bio = *state; + char buf[4] = { 'a', 'b', 'c' }; + + expect_read(FAKE_SOCKET, buf, 1, 1); + expect_read(FAKE_SOCKET, buf + 1, 1, 1); + expect_read(FAKE_SOCKET, buf + 2, 1, 1); + + assert_int_equal(BIO_gets(bio, buf, 4), 3); +} + +static void test_conn_gets_eof_mid(void **state) +{ + /* One char read, then EOF: returns the char count */ + BIO *bio = *state; + char buf[8] = { 'z' }; + + expect_read(FAKE_SOCKET, buf, 1, 1); + expect_read(FAKE_SOCKET, buf + 1, 1, 0); + expect_BIO_sock_should_retry(0, 0); + + assert_int_equal(BIO_gets(bio, buf, sizeof(buf)), 1); +} + +static void test_conn_gets_immediate_eof(void **state) +{ + /* First read returns 0: EOF flag set, returns 0 */ + BIO *bio = *state; + char buf[8] = { 0 }; + + expect_read(FAKE_SOCKET, buf, 1, 0); + expect_BIO_sock_should_retry(0, 0); + + assert_int_equal(BIO_gets(bio, buf, sizeof(buf)), 0); + assert_int_equal(buf[0], '\0'); +} + +static void test_conn_gets_retry(void **state) +{ + BIO *bio = *state; + char buf[8] = { 0 }; + + expect_read(FAKE_SOCKET, buf, 1, -1); + expect_BIO_sock_should_retry(-1, 1); + + assert_int_equal(BIO_gets(bio, buf, sizeof(buf)), -1); + assert_true(BIO_should_retry(bio)); +} + +/* conn_puts */ + +static void test_conn_puts_success(void **state) +{ + BIO *bio = *state; + const char *str = "hello"; + + expect_write(FAKE_SOCKET, str, 5, 5); + assert_int_equal(BIO_puts(bio, str), 5); +} + +static void test_conn_puts_write_fails(void **state) +{ + BIO *bio = *state; + const char *str = "hello"; + + expect_write(FAKE_SOCKET, str, 5, -1); + expect_BIO_sock_should_retry(-1, 0); + assert_true(BIO_puts(bio, str) <= 0); +} + +/* conn_ctrl */ + +static void test_conn_ctrl_reset_no_socket(void **state) +{ + /* INVALID_SOCKET -> conn_close_socket is a no-op */ + assert_int_equal(BIO_ctrl(*state, BIO_CTRL_RESET, 0, NULL), 0); + assert_int_equal(get_data(*state)->state, BIO_CONN_S_BEFORE); +} + +static void test_conn_ctrl_reset_clears_addrs(void **state) +{ + /* RESET also nulls the iterators and clears flags */ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + + data->addr_first = NULL; /* keep BIO_ADDRINFO_free a no-op */ + data->addr_iter = &g_addrinfo1; + bio->flags = BIO_FLAGS_IN_EOF; + bio->num = (int)INVALID_SOCKET; + + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_RESET, 0, NULL), 0); + assert_null(data->addr_first); + assert_null(data->addr_iter); + assert_int_equal(bio->flags, 0); + assert_int_equal(data->state, BIO_CONN_S_BEFORE); +} + +static void test_conn_ctrl_get_connect(void **state) +{ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + const char *out = NULL; + + /* NULL ptr always returns 0 regardless of num */ + assert_int_equal(BIO_ctrl(bio, BIO_C_GET_CONNECT, 0, NULL), 0); + + data->param_hostname = OPENSSL_strdup("host.example"); + data->param_service = OPENSSL_strdup("443"); + data->connect_mode = BIO_SOCK_KEEPALIVE; + + assert_int_equal(BIO_ctrl(bio, BIO_C_GET_CONNECT, 0, &out), 1); + assert_string_equal(out, "host.example"); + + assert_int_equal(BIO_ctrl(bio, BIO_C_GET_CONNECT, 1, &out), 1); + assert_string_equal(out, "443"); + + /* num==4: connect_mode; ptr just needs to be non-NULL */ + assert_int_equal(BIO_ctrl(bio, BIO_C_GET_CONNECT, 4, &out), + BIO_SOCK_KEEPALIVE); + + /* unknown num with non-NULL ptr -> 0 */ + assert_int_equal(BIO_ctrl(bio, BIO_C_GET_CONNECT, 99, &out), 0); +} + +static void test_conn_ctrl_get_connect_address(void **state) +{ + /* num==2: address pointer from addr_iter */ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + const char *out = NULL; + + data->addr_iter = &g_addrinfo1; + assert_int_equal(BIO_ctrl(bio, BIO_C_GET_CONNECT, 2, &out), 1); + assert_non_null(out); + data->addr_iter = NULL; +} + +static void test_conn_ctrl_get_connect_family(void **state) +{ + /* num==3: AF_INET addr_iter maps to BIO_FAMILY_IPV4 */ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + const char *out = NULL; + + data->addr_iter = &g_addrinfo1; + assert_int_equal(BIO_ctrl(bio, BIO_C_GET_CONNECT, 3, &out), + BIO_FAMILY_IPV4); + data->addr_iter = NULL; +} + +static void test_conn_ctrl_set_connect_null_ptr(void **state) +{ + /* ptr == NULL: no-op, init left untouched */ + BIO *bio = *state; + + assert_int_equal(bio->init, 0); + assert_int_equal(BIO_ctrl(bio, BIO_C_SET_CONNECT, 0, NULL), 1); + assert_int_equal(bio->init, 0); +} + +static void test_conn_ctrl_set_connect_hostname(void **state) +{ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + + assert_int_equal(BIO_ctrl(bio, BIO_C_SET_CONNECT, 0, "host.example:443"), + 1); + assert_int_equal(bio->init, 1); + assert_string_equal(data->param_hostname, "host.example"); + assert_string_equal(data->param_service, "443"); +} + +static void test_conn_ctrl_set_connect_port(void **state) +{ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + + assert_int_equal(BIO_ctrl(bio, BIO_C_SET_CONNECT, 1, "8443"), 1); + assert_string_equal(data->param_service, "8443"); +} + +static void test_conn_ctrl_set_connect_address(void **state) +{ + /* num==2: derive host/service from a BIO_ADDR */ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + BIO_ADDR *addr = BIO_ADDR_new(); + + assert_non_null(addr); + assert_true(BIO_ADDR_rawmake(addr, AF_INET, &g_sin.sin_addr, + sizeof(g_sin.sin_addr), g_sin.sin_port)); + + assert_int_equal(BIO_ctrl(bio, BIO_C_SET_CONNECT, 2, addr), 1); + assert_non_null(data->param_hostname); + assert_non_null(data->param_service); + assert_null(data->addr_first); + + BIO_ADDR_free(addr); +} + +static void test_conn_ctrl_set_connect_family(void **state) +{ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + int family = BIO_FAMILY_IPV4; + + assert_int_equal(BIO_ctrl(bio, BIO_C_SET_CONNECT, 3, &family), 1); + assert_int_equal(data->connect_family, BIO_FAMILY_IPV4); +} + +static void test_conn_ctrl_set_sock_type(void **state) +{ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + + assert_int_equal(BIO_ctrl(bio, BIO_C_SET_SOCK_TYPE, SOCK_DGRAM, NULL), 1); + assert_int_equal(data->connect_sock_type, SOCK_DGRAM); + + assert_int_equal(BIO_ctrl(bio, BIO_C_SET_SOCK_TYPE, SOCK_STREAM, NULL), 1); + assert_int_equal(data->connect_sock_type, SOCK_STREAM); + + /* Invalid socktype */ + assert_int_equal(BIO_ctrl(bio, BIO_C_SET_SOCK_TYPE, 9999, NULL), 0); + + /* Too late once past BEFORE */ + data->state = BIO_CONN_S_GET_ADDR; + assert_int_equal(BIO_ctrl(bio, BIO_C_SET_SOCK_TYPE, SOCK_DGRAM, NULL), 0); + data->state = BIO_CONN_S_BEFORE; +} + +static void test_conn_ctrl_get_sock_type(void **state) +{ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + + data->connect_sock_type = SOCK_DGRAM; + assert_int_equal(BIO_ctrl(bio, BIO_C_GET_SOCK_TYPE, 0, NULL), SOCK_DGRAM); + data->connect_sock_type = SOCK_STREAM; +} + +static void test_conn_ctrl_get_dgram_bio(void **state) +{ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + BIO *out = NULL; + BIO fake_dgram; + + /* dgram_bio NULL -> 0 */ + assert_int_equal(BIO_ctrl(bio, BIO_C_GET_DGRAM_BIO, 0, &out), 0); + + data->dgram_bio = &fake_dgram; + assert_int_equal(BIO_ctrl(bio, BIO_C_GET_DGRAM_BIO, 0, &out), 1); + assert_ptr_equal(out, &fake_dgram); + data->dgram_bio = NULL; +} + +static void test_conn_ctrl_nbio(void **state) +{ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + + BIO_ctrl(bio, BIO_C_SET_NBIO, 1, NULL); + assert_true(data->connect_mode & BIO_SOCK_NONBLOCK); + + BIO_ctrl(bio, BIO_C_SET_NBIO, 0, NULL); + assert_false(data->connect_mode & BIO_SOCK_NONBLOCK); +} + +static void test_conn_ctrl_nbio_dgram(void **state) +{ + /* with a dgram_bio attached the mode flips and the call delegates */ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + BIO *dg = make_fake_dgram(); + + data->dgram_bio = dg; + BIO_ctrl(bio, BIO_C_SET_NBIO, 1, NULL); + assert_true(data->connect_mode & BIO_SOCK_NONBLOCK); + + data->dgram_bio = NULL; + BIO_free(dg); +} + +static void test_conn_ctrl_connect_mode(void **state) +{ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + + BIO_ctrl(bio, BIO_C_SET_CONNECT_MODE, BIO_SOCK_KEEPALIVE, NULL); + assert_int_equal(data->connect_mode, BIO_SOCK_KEEPALIVE); + assert_int_equal(data->tfo_first, 0); +} + +static void test_conn_ctrl_get_fd(void **state) +{ + BIO *bio = *state; + int fd = -1; + + /* init==0 -> -1 */ + assert_int_equal(BIO_ctrl(bio, BIO_C_GET_FD, 0, &fd), -1); + + bio->init = 1; + bio->num = FAKE_SOCKET; + assert_int_equal(BIO_ctrl(bio, BIO_C_GET_FD, 0, &fd), FAKE_SOCKET); + assert_int_equal(fd, FAKE_SOCKET); + + bio->init = 0; + bio->num = (int)INVALID_SOCKET; +} + +static void test_conn_ctrl_get_set_close(void **state) +{ + BIO *bio = *state; + + bio->shutdown = BIO_NOCLOSE; + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_GET_CLOSE, 0, NULL), BIO_NOCLOSE); + + BIO_ctrl(bio, BIO_CTRL_SET_CLOSE, BIO_CLOSE, NULL); + assert_int_equal(bio->shutdown, BIO_CLOSE); +} + +static void test_conn_ctrl_pending_flush(void **state) +{ + BIO *bio = *state; + + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_PENDING, 0, NULL), 0); + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_WPENDING, 0, NULL), 0); + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_FLUSH, 0, NULL), 1); +} + +static void test_conn_ctrl_eof(void **state) +{ + BIO *bio = *state; + + bio->flags &= ~BIO_FLAGS_IN_EOF; + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_EOF, 0, NULL), 0); + + bio->flags |= BIO_FLAGS_IN_EOF; + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_EOF, 0, NULL), 1); + bio->flags &= ~BIO_FLAGS_IN_EOF; +} + +static void test_conn_ctrl_set_callback_defers(void **state) +{ + /* BIO_CTRL_SET_CALLBACK via conn_ctrl returns 0 (use callback ctrl) */ + BIO *bio = *state; + + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_SET_CALLBACK, 0, NULL), 0); +} + +static void test_conn_ctrl_default(void **state) +{ + assert_int_equal(BIO_ctrl(*state, 9999, 0, NULL), 0); +} + +/* conn_callback_ctrl */ + +static int dummy_cb(BIO *b, int s, int res) +{ + (void)b; + (void)s; + return res; +} + +static void test_conn_ctrl_get_callback(void **state) +{ + /* BIO_CTRL_GET_CALLBACK returns the stored info_callback */ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + BIO_info_cb *fp = NULL; + + data->info_callback = dummy_cb; + BIO_ctrl(bio, BIO_CTRL_GET_CALLBACK, 0, &fp); + assert_ptr_equal(fp, dummy_cb); + data->info_callback = NULL; +} + +static void test_conn_callback_ctrl_set(void **state) +{ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + BIO_info_cb *retrieved; + + assert_int_equal(BIO_set_info_callback(bio, dummy_cb), 1); + assert_ptr_equal(data->info_callback, dummy_cb); + + assert_int_equal(BIO_get_info_callback(bio, &retrieved), 1); + assert_ptr_equal(retrieved, dummy_cb); + + assert_int_equal(BIO_set_info_callback(bio, NULL), 1); +} + +static void test_conn_callback_ctrl_default(void **state) +{ + assert_int_equal(BIO_callback_ctrl(*state, BIO_CTRL_SET_CALLBACK, dummy_cb), 1); +} + +static void test_conn_callback_ctrl_invalid(void **state) +{ + /* If cmd different than BIO_CTRL_SET_CALLBACK, return -2. */ + assert_int_equal(BIO_callback_ctrl(*state, 9999, dummy_cb), -2); +} + +/* conn_sendmmsg / conn_recvmmsg */ + +static void test_conn_sendmmsg_no_dgram(void **state) +{ + /* State OK, dgram_bio NULL -> error */ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + BIO_MSG msg = { 0 }; + size_t processed = 1; + + data->state = BIO_CONN_S_OK; + bio->num = FAKE_SOCKET; + + assert_int_equal( + BIO_sendmmsg(bio, &msg, sizeof(msg), 1, 0, &processed), 0); + assert_int_equal(processed, 0); + + reset_for_teardown(bio); +} + +static void test_conn_sendmmsg_state_fails(void **state) +{ + /* state != OK and conn_state fails -> 0, processed zeroed */ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + BIO_MSG msg = { 0 }; + size_t processed = 5; + + data->param_hostname = OPENSSL_strdup("host"); + data->param_service = OPENSSL_strdup("443"); + bio->init = 1; /* BIO_sendmmsg rejects !init before dispatch */ + + expect_BIO_lookup(NULL, 0); + + assert_int_equal( + BIO_sendmmsg(bio, &msg, sizeof(msg), 1, 0, &processed), 0); + assert_int_equal(processed, 0); + + reset_for_teardown(bio); +} + +static void test_conn_sendmmsg_dgram_delegates(void **state) +{ + /* state OK with dgram_bio -> delegates to BIO_sendmmsg on it */ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + BIO *dg = make_fake_dgram(); + BIO_MSG msg = { 0 }; + size_t processed = 0; + + data->dgram_bio = dg; + + expect_fake_dgram_sendmmsg(&msg, sizeof(msg), 1, 0, 1, 1); + + assert_int_equal( + BIO_sendmmsg(bio, &msg, sizeof(msg), 1, 0, &processed), 1); + assert_int_equal(processed, 1); + + data->dgram_bio = NULL; + BIO_free(dg); + reset_for_teardown(bio); +} + +static void test_conn_recvmmsg_no_dgram(void **state) +{ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + BIO_MSG msg = { 0 }; + size_t processed = 1; + + data->state = BIO_CONN_S_OK; + bio->num = FAKE_SOCKET; + + assert_int_equal( + BIO_recvmmsg(bio, &msg, sizeof(msg), 1, 0, &processed), 0); + assert_int_equal(processed, 0); + + reset_for_teardown(bio); +} + +static void test_conn_recvmmsg_state_fails(void **state) +{ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + BIO_MSG msg = { 0 }; + size_t processed = 5; + + data->param_hostname = OPENSSL_strdup("host"); + data->param_service = OPENSSL_strdup("443"); + bio->init = 1; + + expect_BIO_lookup(NULL, 0); + + assert_int_equal( + BIO_recvmmsg(bio, &msg, sizeof(msg), 1, 0, &processed), 0); + assert_int_equal(processed, 0); + + reset_for_teardown(bio); +} + +static void test_conn_recvmmsg_dgram_delegates(void **state) +{ + BIO *bio = *state; + BIO_CONNECT *data = get_data(bio); + BIO *dg = make_fake_dgram(); + BIO_MSG msg = { 0 }; + size_t processed = 0; + + data->dgram_bio = dg; + + expect_fake_dgram_recvmmsg(&msg, sizeof(msg), 1, 0, 1, 1); + + assert_int_equal( + BIO_recvmmsg(bio, &msg, sizeof(msg), 1, 0, &processed), 1); + assert_int_equal(processed, 1); + + data->dgram_bio = NULL; + BIO_free(dg); + reset_for_teardown(bio); +} + +/* main */ + +#define CONN_TEST(name) \ + cmocka_unit_test_setup_teardown(name, setup, teardown) + +#define CONN_TEST_IO(name) \ + cmocka_unit_test_setup_teardown(name, setup_io, teardown_io) + +int main(void) +{ + const struct CMUnitTest tests[] = { + /* conn_new */ + CONN_TEST(test_conn_new), + /* conn_free */ + CONN_TEST(test_conn_free_no_shutdown), + /* conn_close_socket */ + CONN_TEST(test_close_socket_none), + CONN_TEST(test_close_socket_non_ok_state), + CONN_TEST(test_close_socket_ok_state), + /* conn_state */ + CONN_TEST(test_conn_state_no_hostname), + CONN_TEST(test_conn_state_unsupported_family), + CONN_TEST(test_conn_state_lookup_fails), + CONN_TEST(test_conn_state_socket_fails), + CONN_TEST(test_conn_state_connect_succeeds), + CONN_TEST(test_conn_state_already_ok), + CONN_TEST(test_conn_state_connect_retry), + CONN_TEST(test_conn_state_connect_error), + CONN_TEST(test_conn_state_connect_next_addr), + CONN_TEST(test_conn_state_blocked_ok), + CONN_TEST(test_conn_state_blocked_error), +#ifndef OPENSSL_NO_DGRAM + CONN_TEST(test_conn_state_connect_dgram_ok), +#endif + /* conn_read */ + CONN_TEST_IO(test_conn_read_success), + CONN_TEST_IO(test_conn_read_eof), + CONN_TEST_IO(test_conn_read_retry), + CONN_TEST_IO(test_conn_read_error), + CONN_TEST(test_conn_read_enters_state_machine), + CONN_TEST(test_conn_read_state_machine_fails), + CONN_TEST_IO(test_conn_read_dgram_delegates), + /* conn_write */ + CONN_TEST_IO(test_conn_write_success), + CONN_TEST_IO(test_conn_write_retry), + CONN_TEST_IO(test_conn_write_error), + CONN_TEST_IO(test_conn_write_dgram_delegates), + /* conn_gets */ + CONN_TEST(test_conn_gets_null_buf), + CONN_TEST(test_conn_gets_zero_size), + CONN_TEST(test_conn_gets_null_ptr), + CONN_TEST_IO(test_conn_gets_dgram_bio_set), + CONN_TEST_IO(test_conn_gets_newline), + CONN_TEST_IO(test_conn_gets_fills_buffer), + CONN_TEST_IO(test_conn_gets_eof_mid), + CONN_TEST_IO(test_conn_gets_immediate_eof), + CONN_TEST_IO(test_conn_gets_retry), + /* conn_puts */ + CONN_TEST_IO(test_conn_puts_success), + CONN_TEST_IO(test_conn_puts_write_fails), + /* conn_ctrl */ + CONN_TEST(test_conn_ctrl_reset_no_socket), + CONN_TEST(test_conn_ctrl_reset_clears_addrs), + CONN_TEST(test_conn_ctrl_get_connect), + CONN_TEST(test_conn_ctrl_get_connect_address), + CONN_TEST(test_conn_ctrl_get_connect_family), + CONN_TEST(test_conn_ctrl_set_connect_null_ptr), + CONN_TEST(test_conn_ctrl_set_connect_hostname), + CONN_TEST(test_conn_ctrl_set_connect_port), + CONN_TEST(test_conn_ctrl_set_connect_address), + CONN_TEST(test_conn_ctrl_set_connect_family), + CONN_TEST(test_conn_ctrl_set_sock_type), + CONN_TEST(test_conn_ctrl_get_sock_type), + CONN_TEST(test_conn_ctrl_get_dgram_bio), + CONN_TEST(test_conn_ctrl_nbio), + CONN_TEST_IO(test_conn_ctrl_nbio_dgram), + CONN_TEST(test_conn_ctrl_connect_mode), + CONN_TEST(test_conn_ctrl_get_fd), + CONN_TEST(test_conn_ctrl_get_set_close), + CONN_TEST(test_conn_ctrl_pending_flush), + CONN_TEST(test_conn_ctrl_eof), + CONN_TEST(test_conn_ctrl_set_callback_defers), + CONN_TEST(test_conn_ctrl_default), + /* conn_callback_ctrl */ + CONN_TEST(test_conn_ctrl_get_callback), + CONN_TEST(test_conn_callback_ctrl_set), + CONN_TEST(test_conn_callback_ctrl_default), + CONN_TEST(test_conn_callback_ctrl_invalid), + /* conn_sendmmsg / conn_recvmmsg */ + CONN_TEST(test_conn_sendmmsg_no_dgram), + CONN_TEST(test_conn_sendmmsg_state_fails), + CONN_TEST_IO(test_conn_sendmmsg_dgram_delegates), + CONN_TEST(test_conn_recvmmsg_no_dgram), + CONN_TEST(test_conn_recvmmsg_state_fails), + CONN_TEST_IO(test_conn_recvmmsg_dgram_delegates), + }; + + cmocka_set_message_output(CM_OUTPUT_TAP); + + return cmocka_run_group_tests(tests, group_setup, group_teardown); +} + +#else + +int main(void) +{ + return 0; +} + +#endif /* OPENSSL_NO_SOCK */ From b69282a759b0ae83385c99a540be5ab2af069586 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Fri, 12 Jun 2026 13:53:26 +0200 Subject: [PATCH 204/349] Add BIO bss_acpt test Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Richard Levitte Reviewed-by: Tomas Mraz MergeDate: Thu Jul 9 17:39:20 2026 (Merged from https://github.com/openssl/openssl/pull/30788) --- crypto/bio/bio_local.h | 27 + crypto/bio/bss_acpt.c | 59 +- test/unit/build.info | 8 + test/unit/crypto/bio/test_bss_acpt.c | 1054 ++++++++++++++++++++++++++ 4 files changed, 1105 insertions(+), 43 deletions(-) create mode 100644 test/unit/crypto/bio/test_bss_acpt.c diff --git a/crypto/bio/bio_local.h b/crypto/bio/bio_local.h index d272eb1861..4e2e974039 100644 --- a/crypto/bio/bio_local.h +++ b/crypto/bio/bio_local.h @@ -154,6 +154,26 @@ typedef struct bio_connect_st { BIO *dgram_bio; } BIO_CONNECT; +typedef struct bio_accept_st { + int state; + int accept_family; + int bind_mode; /* Socket mode for BIO_listen */ + int accepted_mode; /* Socket mode for BIO_accept (set on accepted sock) */ + char *param_addr; + char *param_serv; + + int accept_sock; + + BIO_ADDRINFO *addr_first; + const BIO_ADDRINFO *addr_iter; + BIO_ADDR cache_accepting_addr; /* Useful if we asked for port 0 */ + char *cache_accepting_name, *cache_accepting_serv; + BIO_ADDR cache_peer_addr; + char *cache_peer_name, *cache_peer_serv; + + BIO *bio_chain; +} BIO_ACCEPT; + #define BIO_CONN_S_BEFORE 1 #define BIO_CONN_S_GET_ADDR 2 #define BIO_CONN_S_CREATE_SOCKET 3 @@ -162,6 +182,13 @@ typedef struct bio_connect_st { #define BIO_CONN_S_BLOCKED_CONNECT 6 #define BIO_CONN_S_CONNECT_ERROR 7 +#define BIO_ACPT_S_BEFORE 1 +#define BIO_ACPT_S_GET_ADDR 2 +#define BIO_ACPT_S_CREATE_SOCKET 3 +#define BIO_ACPT_S_LISTEN 4 +#define BIO_ACPT_S_ACCEPT 5 +#define BIO_ACPT_S_OK 6 + #ifdef OPENSSL_SYS_VMS typedef unsigned int socklen_t; #endif diff --git a/crypto/bio/bss_acpt.c b/crypto/bio/bss_acpt.c index 1ba91c6592..c9cdba041a 100644 --- a/crypto/bio/bss_acpt.c +++ b/crypto/bio/bss_acpt.c @@ -15,26 +15,6 @@ #ifndef OPENSSL_NO_SOCK -typedef struct bio_accept_st { - int state; - int accept_family; - int bind_mode; /* Socket mode for BIO_listen */ - int accepted_mode; /* Socket mode for BIO_accept (set on accepted sock) */ - char *param_addr; - char *param_serv; - - int accept_sock; - - BIO_ADDRINFO *addr_first; - const BIO_ADDRINFO *addr_iter; - BIO_ADDR cache_accepting_addr; /* Useful if we asked for port 0 */ - char *cache_accepting_name, *cache_accepting_serv; - BIO_ADDR cache_peer_addr; - char *cache_peer_name, *cache_peer_serv; - - BIO *bio_chain; -} BIO_ACCEPT; - static int acpt_write(BIO *h, const char *buf, int num); static int acpt_read(BIO *h, char *buf, int size); static int acpt_puts(BIO *h, const char *str); @@ -46,13 +26,6 @@ static void acpt_close_socket(BIO *data); static BIO_ACCEPT *BIO_ACCEPT_new(void); static void BIO_ACCEPT_free(BIO_ACCEPT *a); -#define ACPT_S_BEFORE 1 -#define ACPT_S_GET_ADDR 2 -#define ACPT_S_CREATE_SOCKET 3 -#define ACPT_S_LISTEN 4 -#define ACPT_S_ACCEPT 5 -#define ACPT_S_OK 6 - static const BIO_METHOD methods_acceptp = { BIO_TYPE_ACCEPT, "socket accept", @@ -83,7 +56,7 @@ static int acpt_new(BIO *bi) if ((ba = BIO_ACCEPT_new()) == NULL) return 0; bi->ptr = (char *)ba; - ba->state = ACPT_S_BEFORE; + ba->state = BIO_ACPT_S_BEFORE; bi->shutdown = 1; return 1; } @@ -152,7 +125,7 @@ static int acpt_state(BIO *b, BIO_ACCEPT *c) for (;;) { switch (c->state) { - case ACPT_S_BEFORE: + case BIO_ACPT_S_BEFORE: if (c->param_addr == NULL && c->param_serv == NULL) { ERR_raise_data(ERR_LIB_BIO, BIO_R_NO_ACCEPT_ADDR_OR_SERVICE_SPECIFIED, @@ -174,10 +147,10 @@ static int acpt_state(BIO *b, BIO_ACCEPT *c) OPENSSL_free(c->cache_peer_serv); c->cache_peer_serv = NULL; - c->state = ACPT_S_GET_ADDR; + c->state = BIO_ACPT_S_GET_ADDR; break; - case ACPT_S_GET_ADDR: { + case BIO_ACPT_S_GET_ADDR: { int family = AF_UNSPEC; switch (c->accept_family) { case BIO_FAMILY_IPV6: @@ -213,10 +186,10 @@ static int acpt_state(BIO *b, BIO_ACCEPT *c) goto exit_loop; } c->addr_iter = c->addr_first; - c->state = ACPT_S_CREATE_SOCKET; + c->state = BIO_ACPT_S_CREATE_SOCKET; break; - case ACPT_S_CREATE_SOCKET: + case BIO_ACPT_S_CREATE_SOCKET: ERR_set_mark(); s = BIO_socket(BIO_ADDRINFO_family(c->addr_iter), BIO_ADDRINFO_socktype(c->addr_iter), @@ -238,11 +211,11 @@ static int acpt_state(BIO *b, BIO_ACCEPT *c) } c->accept_sock = s; b->num = s; - c->state = ACPT_S_LISTEN; + c->state = BIO_ACPT_S_LISTEN; s = -1; break; - case ACPT_S_LISTEN: { + case BIO_ACPT_S_LISTEN: { if (!BIO_listen(c->accept_sock, BIO_ADDRINFO_address(c->addr_iter), c->bind_mode)) { @@ -271,14 +244,14 @@ static int acpt_state(BIO *b, BIO_ACCEPT *c) OPENSSL_free(c->cache_accepting_serv); c->cache_accepting_name = BIO_ADDR_hostname_string(&c->cache_accepting_addr, 1); c->cache_accepting_serv = BIO_ADDR_service_string(&c->cache_accepting_addr, 1); - c->state = ACPT_S_ACCEPT; + c->state = BIO_ACPT_S_ACCEPT; s = -1; ret = 1; goto end; - case ACPT_S_ACCEPT: + case BIO_ACPT_S_ACCEPT: if (b->next_bio != NULL) { - c->state = ACPT_S_OK; + c->state = BIO_ACPT_S_OK; break; } BIO_clear_retry_flags(b); @@ -334,14 +307,14 @@ static int acpt_state(BIO *b, BIO_ACCEPT *c) c->cache_peer_name = BIO_ADDR_hostname_string(&c->cache_peer_addr, 1); c->cache_peer_serv = BIO_ADDR_service_string(&c->cache_peer_addr, 1); - c->state = ACPT_S_OK; + c->state = BIO_ACPT_S_OK; bio = NULL; ret = 1; goto end; - case ACPT_S_OK: + case BIO_ACPT_S_OK: if (b->next_bio == NULL) { - c->state = ACPT_S_ACCEPT; + c->state = BIO_ACPT_S_ACCEPT; break; } ret = 1; @@ -412,7 +385,7 @@ static long acpt_ctrl(BIO *b, int cmd, long num, void *ptr) switch (cmd) { case BIO_CTRL_RESET: ret = 0; - data->state = ACPT_S_BEFORE; + data->state = BIO_ACPT_S_BEFORE; acpt_close_socket(b); BIO_ADDRINFO_free(data->addr_first); data->addr_first = NULL; @@ -474,7 +447,7 @@ static long acpt_ctrl(BIO *b, int cmd, long num, void *ptr) case BIO_C_SET_FD: b->num = *((int *)ptr); data->accept_sock = b->num; - data->state = ACPT_S_ACCEPT; + data->state = BIO_ACPT_S_ACCEPT; b->shutdown = (int)num; b->init = 1; break; diff --git a/test/unit/build.info b/test/unit/build.info index cf2a748418..ae53ad8d61 100644 --- a/test/unit/build.info +++ b/test/unit/build.info @@ -6,6 +6,14 @@ INCLUDE[crypto/bio/test_bio_addr]=../../include ../../crypto/bio DEPEND[crypto/bio/test_bio_addr]=../../libcrypto.a WRAP[crypto/bio/test_bio_addr]=BIO_sock_init getnameinfo freeaddrinfo +PROGRAMS{noinst}=crypto/bio/test_bss_acpt +SOURCE[crypto/bio/test_bss_acpt]=crypto/bio/test_bss_acpt.c +WRAP[crypto/bio/test_bss_acpt]=BIO_lookup BIO_socket BIO_listen \ + BIO_accept_ex BIO_sock_info BIO_sock_should_retry BIO_closesocket \ + BIO_ADDR_hostname_string BIO_ADDR_service_string +INCLUDE[crypto/bio/test_bss_acpt]=../../include ../../crypto/bio +DEPEND[crypto/bio/test_bss_acpt]=../../libcrypto.a + PROGRAMS{noinst}=crypto/bio/test_bss_conn SOURCE[crypto/bio/test_bss_conn]=crypto/bio/test_bss_conn.c WRAP[crypto/bio/test_bss_conn]=BIO_lookup BIO_socket BIO_connect \ diff --git a/test/unit/crypto/bio/test_bss_acpt.c b/test/unit/crypto/bio/test_bss_acpt.c new file mode 100644 index 0000000000..4a51c27373 --- /dev/null +++ b/test/unit/crypto/bio/test_bss_acpt.c @@ -0,0 +1,1054 @@ +/* + * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "internal/sockets.h" + +#ifndef OPENSSL_NO_SOCK + +#include +#include +#include +#include +#include +#include +#include +#include +#include "bio_local.h" + +#define FAKE_SOCKET 42 + +/* + * Fake addrinfo used across state machine tests. g_addrinfo1.bai_next is + * NULL by default. Tests needing a second address temporarily set it to + * &g_addrinfo2 and restore it afterwards. + */ +static struct sockaddr_in g_sin; +static BIO_ADDRINFO g_addrinfo1; +static BIO_ADDRINFO g_addrinfo2; + +/* prototypes for __wrap_* (required by -Wmissing-prototypes) */ +int __wrap_BIO_lookup(const char *host, const char *service, + enum BIO_lookup_type lookup_type, + int family, int socktype, BIO_ADDRINFO **res); +int __wrap_BIO_socket(int domain, int socktype, int protocol, int options); +int __wrap_BIO_listen(int sock, const BIO_ADDR *addr, int options); +int __wrap_BIO_accept_ex(int accept_sock, BIO_ADDR *addr, int options); +int __wrap_BIO_sock_info(int sock, enum BIO_sock_info_type type, + union BIO_sock_info_u *info); +int __wrap_BIO_sock_should_retry(int i); +int __wrap_BIO_closesocket(int sock); +char *__wrap_BIO_ADDR_hostname_string(const BIO_ADDR *ap, int numeric); +char *__wrap_BIO_ADDR_service_string(const BIO_ADDR *ap, int numeric); + +/* wraps */ + +int __wrap_BIO_lookup(const char *host, const char *service, + enum BIO_lookup_type lookup_type, + int family, int socktype, BIO_ADDRINFO **res) +{ + int rc; + + function_called(); + check_expected(host); + check_expected(service); + check_expected(lookup_type); + check_expected(family); + check_expected(socktype); + rc = mock_type(int); + if (rc == 1) + *res = mock_ptr_type(BIO_ADDRINFO *); + return rc; +} + +int __wrap_BIO_socket(int domain, int socktype, int protocol, int options) +{ + function_called(); + check_expected(domain); + check_expected(socktype); + check_expected(protocol); + check_expected(options); + return mock_type(int); +} + +int __wrap_BIO_listen(int sock, const BIO_ADDR *addr, int options) +{ + function_called(); + check_expected(sock); + check_expected_ptr(addr); + check_expected(options); + return mock_type(int); +} + +int __wrap_BIO_accept_ex(int accept_sock, BIO_ADDR *addr, int options) +{ + function_called(); + check_expected(accept_sock); + check_expected_ptr(addr); + check_expected(options); + return mock_type(int); +} + +int __wrap_BIO_sock_info(int sock, enum BIO_sock_info_type type, + union BIO_sock_info_u *info) +{ + function_called(); + check_expected(sock); + check_expected(type); + (void)info; /* addr field left untouched; cache addr is zeroed already */ + return mock_type(int); +} + +int __wrap_BIO_sock_should_retry(int i) +{ + function_called(); + check_expected(i); + return mock_type(int); +} + +int __wrap_BIO_closesocket(int sock) +{ + function_called(); + check_expected(sock); + return mock_type(int); +} + +/* + * The cache_*_name / cache_*_serv pointers get OPENSSL_free()d in + * BIO_ACCEPT_free and when a new bind starts, so these wraps must return + * heap pointers (or NULL). + */ +char *__wrap_BIO_ADDR_hostname_string(const BIO_ADDR *ap, int numeric) +{ + function_called(); + (void)ap; + (void)numeric; + return mock_ptr_type(char *); +} + +char *__wrap_BIO_ADDR_service_string(const BIO_ADDR *ap, int numeric) +{ + function_called(); + (void)ap; + (void)numeric; + return mock_ptr_type(char *); +} + +/* + * A minimal fake sink BIO. The accept BIO forwards reads/writes to its + * next_bio once a connection is established, so I/O tests push one of these + * instead of a real socket BIO. This keeps acpt_read/acpt_write under test + * in isolation: no dependency on sock_read/sock_write, on the libc read/write + * syscalls, or on BIO_sock_should_retry. + * + * The fake read/write return mock_type and, on a non-positive result, set + * their own retry flag when asked, so acpt_read/acpt_write's + * BIO_copy_next_retry has a real source-of-truth to propagate upward. + */ + +static int fake_sink_read(BIO *b, char *buf, size_t size, size_t *readbytes); +static int fake_sink_write(BIO *b, const char *buf, size_t size, + size_t *written); +static long fake_sink_ctrl(BIO *b, int cmd, long arg1, void *arg2); + +static int fake_sink_read(BIO *b, char *buf, size_t size, size_t *readbytes) +{ + int ret; + + function_called(); + check_expected_ptr(buf); + check_expected(size); + BIO_clear_retry_flags(b); + ret = mock_type(int); + if (ret > 0) { + *readbytes = (size_t)ret; + return 1; + } + if (mock_type(int)) + BIO_set_retry_read(b); + *readbytes = 0; + return ret; +} + +static int fake_sink_write(BIO *b, const char *buf, size_t size, + size_t *written) +{ + int ret; + + function_called(); + check_expected_ptr(buf); + check_expected(size); + BIO_clear_retry_flags(b); + ret = mock_type(int); + if (ret > 0) { + *written = (size_t)ret; + return 1; + } + if (mock_type(int)) + BIO_set_retry_write(b); + *written = 0; + return ret; +} + +static long fake_sink_ctrl(BIO *b, int cmd, long arg1, void *arg2) +{ + (void)b; + (void)arg1; + (void)arg2; + if (cmd == BIO_CTRL_FLUSH) + return 1; + return 0; +} + +static BIO_METHOD *fake_sink_method = NULL; + +static BIO_METHOD *make_fake_sink_method(void) +{ + BIO_METHOD *m = BIO_meth_new(BIO_TYPE_SOURCE_SINK | 0xff, "fake sink"); + + assert_non_null(m); + assert_true(BIO_meth_set_read_ex(m, fake_sink_read)); + assert_true(BIO_meth_set_write_ex(m, fake_sink_write)); + assert_true(BIO_meth_set_ctrl(m, fake_sink_ctrl)); + return m; +} + +static BIO *make_fake_sink(void) +{ + BIO *b = BIO_new(fake_sink_method); + + assert_non_null(b); + BIO_set_init(b, 1); + return b; +} + +/* expectations */ + +static void expect_BIO_lookup(BIO_ADDRINFO *res, int rc) +{ + expect_function_call(__wrap_BIO_lookup); + expect_any(__wrap_BIO_lookup, host); + expect_any(__wrap_BIO_lookup, service); + expect_value(__wrap_BIO_lookup, lookup_type, BIO_LOOKUP_SERVER); + expect_any(__wrap_BIO_lookup, family); + expect_any(__wrap_BIO_lookup, socktype); + will_return(__wrap_BIO_lookup, rc); + if (rc == 1) + will_return(__wrap_BIO_lookup, res); +} + +/* options is the literal the state machine passes (always 0 here) */ +static void expect_BIO_socket(int domain, int socktype, int protocol, + int options, int rc) +{ + expect_function_call(__wrap_BIO_socket); + expect_value(__wrap_BIO_socket, domain, domain); + expect_value(__wrap_BIO_socket, socktype, socktype); + expect_value(__wrap_BIO_socket, protocol, protocol); + expect_value(__wrap_BIO_socket, options, options); + will_return(__wrap_BIO_socket, rc); +} + +static void expect_BIO_listen(int sock, const BIO_ADDR *addr, int options, + int rc) +{ + expect_function_call(__wrap_BIO_listen); + expect_value(__wrap_BIO_listen, sock, sock); + expect_value(__wrap_BIO_listen, addr, addr); + expect_value(__wrap_BIO_listen, options, options); + will_return(__wrap_BIO_listen, rc); +} + +static void expect_BIO_accept_ex(int sock, int options, int rc) +{ + expect_function_call(__wrap_BIO_accept_ex); + expect_value(__wrap_BIO_accept_ex, accept_sock, sock); + expect_any(__wrap_BIO_accept_ex, addr); + expect_value(__wrap_BIO_accept_ex, options, options); + will_return(__wrap_BIO_accept_ex, rc); +} + +static void expect_BIO_sock_info(int sock, int rc) +{ + expect_function_call(__wrap_BIO_sock_info); + expect_value(__wrap_BIO_sock_info, sock, sock); + expect_value(__wrap_BIO_sock_info, type, BIO_SOCK_INFO_ADDRESS); + will_return(__wrap_BIO_sock_info, rc); +} + +static void expect_BIO_sock_should_retry(int i, int rc) +{ + expect_function_call(__wrap_BIO_sock_should_retry); + expect_value(__wrap_BIO_sock_should_retry, i, i); + will_return(__wrap_BIO_sock_should_retry, rc); +} + +static void expect_BIO_closesocket(int sock, int rc) +{ + expect_function_call(__wrap_BIO_closesocket); + expect_value(__wrap_BIO_closesocket, sock, sock); + will_return(__wrap_BIO_closesocket, rc); +} + +/* The returned pointer is heap allocated; ownership passes to the BIO. */ +static void expect_BIO_ADDR_hostname_string(const char *val) +{ + expect_function_call(__wrap_BIO_ADDR_hostname_string); + will_return(__wrap_BIO_ADDR_hostname_string, + val == NULL ? NULL : OPENSSL_strdup(val)); +} + +static void expect_BIO_ADDR_service_string(const char *val) +{ + expect_function_call(__wrap_BIO_ADDR_service_string); + will_return(__wrap_BIO_ADDR_service_string, + val == NULL ? NULL : OPENSSL_strdup(val)); +} + +/* + * rc is the byte count (>0) or the non-positive result; retry tells the sink + * whether to set its own retry flag when rc <= 0. + */ +static void expect_fake_sink_read(const void *buf, size_t size, int rc, + int retry) +{ + expect_function_call(fake_sink_read); + expect_value(fake_sink_read, buf, buf); + expect_value(fake_sink_read, size, size); + will_return(fake_sink_read, rc); + if (rc <= 0) + will_return(fake_sink_read, retry); +} + +static void expect_fake_sink_write(const void *buf, size_t size, int rc, + int retry) +{ + expect_function_call(fake_sink_write); + expect_value(fake_sink_write, buf, buf); + expect_value(fake_sink_write, size, size); + will_return(fake_sink_write, rc); + if (rc <= 0) + will_return(fake_sink_write, retry); +} + +/* + * The LISTEN state always emits, in order: BIO_listen, BIO_sock_info, then a + * hostname_string + service_string pair for the accepting address cache. + */ +static void expect_listen_sequence(int sock) +{ + expect_BIO_listen(sock, (const BIO_ADDR *)&g_sin, 0, 1); + expect_BIO_sock_info(sock, 1); + expect_BIO_ADDR_hostname_string("127.0.0.1"); + expect_BIO_ADDR_service_string("443"); +} + +/* helpers */ + +static BIO_ACCEPT *get_data(BIO *bio) +{ + return (BIO_ACCEPT *)bio->ptr; +} + +/* + * Reset socket bookkeeping so acpt_close_socket is a no-op during free, and + * clear the address iterators that point at static storage. + */ +static void reset_for_teardown(BIO *bio) +{ + BIO_ACCEPT *data = get_data(bio); + + bio->num = (int)INVALID_SOCKET; + data->accept_sock = (int)INVALID_SOCKET; + data->addr_first = NULL; + data->addr_iter = NULL; + data->state = BIO_ACPT_S_BEFORE; +} + +/* setup / teardown */ + +static int setup(void **state) +{ + BIO *bio = BIO_new(BIO_s_accept()); + + assert_non_null(bio); + *state = bio; + return 0; +} + +static int teardown(void **state) +{ + if (*state != NULL) + BIO_free(*state); + return 0; +} + +/* + * I/O tests pre-establish state=OK and push a fake sink BIO so the state + * machine exits immediately and reads/writes are forwarded to next_bio. + */ +static int setup_io(void **state) +{ + BIO *bio, *sink; + BIO_ACCEPT *data; + + if (setup(state) != 0) + return -1; + bio = *state; + data = get_data(bio); + data->state = BIO_ACPT_S_OK; + data->accept_sock = FAKE_SOCKET; + bio->num = FAKE_SOCKET; + bio->init = 1; + + sink = make_fake_sink(); + assert_non_null(BIO_push(bio, sink)); + return 0; +} + +static int teardown_io(void **state) +{ + if (*state != NULL) { + reset_for_teardown(*state); + /* BIO_free_all to also release the pushed fake sink BIO. */ + BIO_free_all(*state); + *state = NULL; + } + return 0; +} + +static int group_setup(void **state) +{ + (void)state; + + fake_sink_method = make_fake_sink_method(); + + memset(&g_sin, 0, sizeof(g_sin)); + g_sin.sin_family = AF_INET; + g_sin.sin_port = htons(443); + g_sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + memset(&g_addrinfo1, 0, sizeof(g_addrinfo1)); + g_addrinfo1.bai_family = AF_INET; + g_addrinfo1.bai_socktype = SOCK_STREAM; + g_addrinfo1.bai_protocol = IPPROTO_TCP; + g_addrinfo1.bai_addrlen = sizeof(g_sin); + g_addrinfo1.bai_addr = (struct sockaddr *)&g_sin; + g_addrinfo1.bai_next = NULL; + + memcpy(&g_addrinfo2, &g_addrinfo1, sizeof(g_addrinfo1)); + g_addrinfo2.bai_next = NULL; + + return 0; +} + +static int group_teardown(void **state) +{ + (void)state; + BIO_meth_free(fake_sink_method); + fake_sink_method = NULL; + return 0; +} + +/* acpt_new */ + +static void test_acpt_new(void **state) +{ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + + assert_non_null(data); + assert_int_equal(data->state, BIO_ACPT_S_BEFORE); + assert_int_equal(data->accept_family, BIO_FAMILY_IPANY); + assert_int_equal(data->accept_sock, (int)INVALID_SOCKET); + assert_null(data->param_addr); + assert_null(data->param_serv); + assert_null(data->addr_first); + assert_null(data->bio_chain); + assert_int_equal(bio->num, (int)INVALID_SOCKET); + assert_int_equal(bio->init, 0); + assert_int_equal(bio->shutdown, 1); +} + +/* acpt_free */ + +static void test_acpt_free_no_shutdown(void **state) +{ + /* shutdown=0: acpt_close_socket and BIO_ACCEPT_free are both skipped */ + BIO *bio = BIO_new(BIO_s_accept()); + + assert_non_null(bio); + bio->shutdown = BIO_NOCLOSE; + BIO_free(bio); + *state = NULL; +} + +/* acpt_close_socket (via BIO_CTRL_RESET) */ + +static void test_close_socket_none(void **state) +{ + /* accept_sock == INVALID_SOCKET: no closesocket expected */ + assert_int_equal(BIO_ctrl(*state, BIO_CTRL_RESET, 0, NULL), 0); + assert_int_equal(get_data(*state)->state, BIO_ACPT_S_BEFORE); +} + +/* acpt_state via BIO_C_DO_STATE_MACHINE (BIO_do_accept) */ + +static void test_acpt_state_no_addr(void **state) +{ + /* BEFORE with no addr and no serv -> error */ + assert_true(BIO_do_accept(*state) <= 0); +} + +static void test_acpt_state_unsupported_family(void **state) +{ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + + data->param_serv = OPENSSL_strdup("443"); + data->accept_family = 9999; + + assert_true(BIO_do_accept(bio) <= 0); +} + +static void test_acpt_state_lookup_fails(void **state) +{ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + + data->param_serv = OPENSSL_strdup("443"); + + expect_BIO_lookup(NULL, 0); + assert_true(BIO_do_accept(bio) <= 0); +} + +static void test_acpt_state_lookup_empty(void **state) +{ + /* BIO_lookup succeeds but returns no addresses */ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + + data->param_serv = OPENSSL_strdup("443"); + + expect_BIO_lookup(NULL, 1); /* rc==1 but res stays NULL */ + assert_true(BIO_do_accept(bio) <= 0); +} + +static void test_acpt_state_socket_fails(void **state) +{ + /* Pre-set CREATE_SOCKET, single address: BIO_socket fails -> error */ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + + data->state = BIO_ACPT_S_CREATE_SOCKET; + data->addr_iter = &g_addrinfo1; /* bai_next == NULL */ + + expect_BIO_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, + (int)INVALID_SOCKET); + assert_true(BIO_do_accept(bio) <= 0); + + reset_for_teardown(bio); +} + +static void test_acpt_state_socket_next_addr(void **state) +{ + /* + * Two addresses: first BIO_socket fails, iterator advances, second + * BIO_socket also fails -> clean error exit. + */ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + + g_addrinfo1.bai_next = &g_addrinfo2; + data->state = BIO_ACPT_S_CREATE_SOCKET; + data->addr_iter = &g_addrinfo1; + + expect_BIO_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, + (int)INVALID_SOCKET); + expect_BIO_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, + (int)INVALID_SOCKET); + assert_true(BIO_do_accept(bio) <= 0); + + g_addrinfo1.bai_next = NULL; + reset_for_teardown(bio); +} + +static void test_acpt_state_listen_fails(void **state) +{ + /* CREATE_SOCKET succeeds, BIO_listen fails -> closesocket, error */ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + + data->state = BIO_ACPT_S_CREATE_SOCKET; + data->addr_iter = &g_addrinfo1; + + expect_BIO_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, FAKE_SOCKET); + expect_BIO_listen(FAKE_SOCKET, (const BIO_ADDR *)&g_sin, 0, 0); + expect_BIO_closesocket(FAKE_SOCKET, 0); + + assert_true(BIO_do_accept(bio) <= 0); + assert_int_equal(data->accept_sock, (int)INVALID_SOCKET); + + reset_for_teardown(bio); +} + +static void test_acpt_state_sock_info_fails(void **state) +{ + /* listen ok, BIO_sock_info fails -> closesocket, error */ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + + data->state = BIO_ACPT_S_CREATE_SOCKET; + data->addr_iter = &g_addrinfo1; + + expect_BIO_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, FAKE_SOCKET); + expect_BIO_listen(FAKE_SOCKET, (const BIO_ADDR *)&g_sin, 0, 1); + expect_BIO_sock_info(FAKE_SOCKET, 0); + expect_BIO_closesocket(FAKE_SOCKET, 0); + + assert_true(BIO_do_accept(bio) <= 0); + assert_int_equal(data->accept_sock, (int)INVALID_SOCKET); + + reset_for_teardown(bio); +} + +static void test_acpt_state_bind_ok(void **state) +{ + /* + * Full bind path: BEFORE -> GET_ADDR -> CREATE_SOCKET -> LISTEN returns 1 + * and the machine stops at BIO_ACPT_S_ACCEPT (next_bio is NULL). + */ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + + data->param_serv = OPENSSL_strdup("443"); + + expect_BIO_lookup(&g_addrinfo1, 1); + expect_BIO_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, FAKE_SOCKET); + expect_listen_sequence(FAKE_SOCKET); + + assert_int_equal(BIO_do_accept(bio), 1); + assert_int_equal(data->state, BIO_ACPT_S_ACCEPT); + assert_int_equal(data->accept_sock, FAKE_SOCKET); + assert_string_equal(data->cache_accepting_name, "127.0.0.1"); + assert_string_equal(data->cache_accepting_serv, "443"); + + reset_for_teardown(bio); +} + +static void test_acpt_state_accept_retry(void **state) +{ + /* Pre-set ACCEPT, no next_bio: BIO_accept_ex retryable -> special flag */ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + + data->state = BIO_ACPT_S_ACCEPT; + data->accept_sock = FAKE_SOCKET; + bio->num = FAKE_SOCKET; + + expect_BIO_accept_ex(FAKE_SOCKET, 0, -1); + expect_BIO_sock_should_retry(-1, 1); + + assert_true(BIO_do_accept(bio) <= 0); + assert_true(BIO_should_io_special(bio)); + assert_int_equal(bio->retry_reason, BIO_RR_ACCEPT); + + reset_for_teardown(bio); +} + +static void test_acpt_state_accept_error(void **state) +{ + /* Pre-set ACCEPT: BIO_accept_ex fails, not retryable -> error */ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + + data->state = BIO_ACPT_S_ACCEPT; + data->accept_sock = FAKE_SOCKET; + bio->num = FAKE_SOCKET; + + expect_BIO_accept_ex(FAKE_SOCKET, 0, -1); + expect_BIO_sock_should_retry(-1, 0); + + assert_true(BIO_do_accept(bio) <= 0); + + reset_for_teardown(bio); +} + +static void test_acpt_state_already_ok_no_next(void **state) +{ + /* + * State OK but next_bio NULL: machine drops back to ACCEPT and tries to + * accept. We make that accept fail non-retryably for a clean exit. + */ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + + data->state = BIO_ACPT_S_OK; + data->accept_sock = FAKE_SOCKET; + bio->num = FAKE_SOCKET; + + expect_BIO_accept_ex(FAKE_SOCKET, 0, -1); + expect_BIO_sock_should_retry(-1, 0); + + assert_true(BIO_do_accept(bio) <= 0); + + reset_for_teardown(bio); +} + +/* acpt_read / acpt_write (state already OK with a pushed fake sink BIO) */ + +static void test_acpt_read_forwards(void **state) +{ + BIO *bio = *state; + char buf[8] = { 0 }; + + expect_fake_sink_read(buf, 8, 8, 0); + assert_int_equal(BIO_read(bio, buf, 8), 8); + assert_false(BIO_should_retry(bio)); +} + +static void test_acpt_read_forwards_retry(void **state) +{ + /* + * The fake sink returns a retryable read and sets its own retry-read + * flag; acpt_read must propagate it onto the accept BIO via + * BIO_copy_next_retry. + */ + BIO *bio = *state; + char buf[8] = { 0 }; + + expect_fake_sink_read(buf, 8, -1, 1); + assert_true(BIO_read(bio, buf, 8) <= 0); + assert_true(BIO_should_read(bio)); + assert_true(BIO_should_retry(bio)); +} + +static void test_acpt_write_forwards(void **state) +{ + BIO *bio = *state; + const char buf[] = "hello"; + + expect_fake_sink_write(buf, 5, 5, 0); + assert_int_equal(BIO_write(bio, buf, 5), 5); + assert_false(BIO_should_retry(bio)); +} + +static void test_acpt_write_forwards_retry(void **state) +{ + BIO *bio = *state; + const char buf[] = "hello"; + + expect_fake_sink_write(buf, 5, -1, 1); + assert_true(BIO_write(bio, buf, 5) <= 0); + assert_true(BIO_should_write(bio)); + assert_true(BIO_should_retry(bio)); +} + +static void test_acpt_puts_forwards(void **state) +{ + BIO *bio = *state; + const char *str = "hello"; + + expect_fake_sink_write(str, 5, 5, 0); + assert_int_equal(BIO_puts(bio, str), 5); +} + +/* acpt_ctrl */ + +static void test_acpt_ctrl_reset_clears_addrs(void **state) +{ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + + data->addr_first = NULL; /* keep BIO_ADDRINFO_free a no-op */ + data->addr_iter = &g_addrinfo1; + bio->flags = BIO_FLAGS_SHOULD_RETRY; + + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_RESET, 0, NULL), 0); + assert_null(data->addr_first); + assert_null(data->addr_iter); + assert_int_equal(bio->flags, 0); + assert_int_equal(data->state, BIO_ACPT_S_BEFORE); +} + +static void test_acpt_ctrl_set_accept_name(void **state) +{ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + + assert_true(BIO_set_accept_name(bio, "host.example:443") > 0); + assert_int_equal(bio->init, 1); + assert_string_equal(data->param_addr, "host.example"); + assert_string_equal(data->param_serv, "443"); +} + +static void test_acpt_ctrl_set_accept_port(void **state) +{ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + + assert_true(BIO_set_accept_port(bio, "8443") > 0); + assert_int_equal(bio->init, 1); + assert_string_equal(data->param_serv, "8443"); +} + +static void test_acpt_ctrl_set_nbio_accept(void **state) +{ + /* toggles BIO_SOCK_NONBLOCK in bind_mode */ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + + BIO_set_nbio_accept(bio, 1); + assert_true(data->bind_mode & BIO_SOCK_NONBLOCK); + + BIO_set_nbio_accept(bio, 0); + assert_false(data->bind_mode & BIO_SOCK_NONBLOCK); +} + +static void test_acpt_ctrl_set_accept_bios(void **state) +{ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + BIO *chain = BIO_new(BIO_s_mem()); + + assert_non_null(chain); + assert_true(BIO_set_accept_bios(bio, chain) > 0); + assert_ptr_equal(data->bio_chain, chain); + /* freed by BIO_ACCEPT_free in teardown */ +} + +static void test_acpt_ctrl_set_accept_ip_family(void **state) +{ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + + assert_true(BIO_set_accept_ip_family(bio, BIO_FAMILY_IPV4) > 0); + assert_int_equal(data->accept_family, BIO_FAMILY_IPV4); +} + +static void test_acpt_ctrl_set_tfo_accept(void **state) +{ + /* toggles BIO_SOCK_TFO in bind_mode */ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + + BIO_set_tfo_accept(bio, 1); + assert_true(data->bind_mode & BIO_SOCK_TFO); + + BIO_set_tfo_accept(bio, 0); + assert_false(data->bind_mode & BIO_SOCK_TFO); +} + +static void test_acpt_ctrl_set_nbio(void **state) +{ + /* BIO_C_SET_NBIO: toggles BIO_SOCK_NONBLOCK in accepted_mode */ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + + BIO_set_nbio(bio, 1); + assert_true(data->accepted_mode & BIO_SOCK_NONBLOCK); + + BIO_set_nbio(bio, 0); + assert_false(data->accepted_mode & BIO_SOCK_NONBLOCK); +} + +static void test_acpt_ctrl_set_fd(void **state) +{ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + + BIO_set_fd(bio, FAKE_SOCKET, BIO_NOCLOSE); + assert_int_equal(bio->num, FAKE_SOCKET); + assert_int_equal(data->accept_sock, FAKE_SOCKET); + assert_int_equal(data->state, BIO_ACPT_S_ACCEPT); + assert_int_equal(bio->init, 1); + assert_int_equal(bio->shutdown, BIO_NOCLOSE); + + reset_for_teardown(bio); +} + +static void test_acpt_ctrl_get_fd(void **state) +{ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + int fd = -1; + + /* init==0 -> -1 */ + assert_int_equal(BIO_get_fd(bio, &fd), -1); + + bio->init = 1; + data->accept_sock = FAKE_SOCKET; + assert_int_equal(BIO_get_fd(bio, &fd), FAKE_SOCKET); + assert_int_equal(fd, FAKE_SOCKET); + + bio->init = 0; + data->accept_sock = (int)INVALID_SOCKET; +} + +static void test_acpt_ctrl_get_accept_names(void **state) +{ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + const char *out; + + assert_int_equal(BIO_ctrl(bio, BIO_C_GET_ACCEPT, 0, &out), -1); + + bio->init = 1; + data->cache_accepting_name = OPENSSL_strdup("accept.host"); + data->cache_accepting_serv = OPENSSL_strdup("443"); + data->cache_peer_name = OPENSSL_strdup("peer.host"); + data->cache_peer_serv = OPENSSL_strdup("55000"); + + assert_string_equal(BIO_get_accept_name(bio), "accept.host"); + assert_string_equal(BIO_get_accept_port(bio), "443"); + assert_string_equal(BIO_get_peer_name(bio), "peer.host"); + assert_string_equal(BIO_get_peer_port(bio), "55000"); + + bio->init = 0; +} + +static void test_acpt_ctrl_get_accept_family(void **state) +{ + /* AF_INET addr_iter maps to BIO_FAMILY_IPV4 */ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + + bio->init = 1; + data->addr_iter = &g_addrinfo1; + assert_int_equal(BIO_get_accept_ip_family(bio), BIO_FAMILY_IPV4); + data->addr_iter = NULL; + bio->init = 0; +} + +static void test_acpt_ctrl_get_set_close(void **state) +{ + BIO *bio = *state; + + bio->shutdown = BIO_NOCLOSE; + assert_int_equal(BIO_get_close(bio), BIO_NOCLOSE); + + assert_int_equal(BIO_set_close(bio, BIO_CLOSE), 1); + assert_int_equal(bio->shutdown, BIO_CLOSE); +} + +static void test_acpt_ctrl_bind_mode(void **state) +{ + BIO *bio = *state; + BIO_ACCEPT *data = get_data(bio); + + BIO_set_bind_mode(bio, BIO_SOCK_REUSEADDR); + assert_int_equal(data->bind_mode, BIO_SOCK_REUSEADDR); + assert_int_equal(BIO_get_bind_mode(bio), BIO_SOCK_REUSEADDR); +} + +static void test_acpt_ctrl_pending_flush(void **state) +{ + BIO *bio = *state; + + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_PENDING, 0, NULL), 0); + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_WPENDING, 0, NULL), 0); + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_FLUSH, 0, NULL), 1); +} + +static void test_acpt_ctrl_eof_no_next(void **state) +{ + /* next_bio == NULL -> 0 */ + assert_int_equal(BIO_ctrl(*state, BIO_CTRL_EOF, 0, NULL), 0); +} + +static void test_acpt_ctrl_default(void **state) +{ + assert_int_equal(BIO_ctrl(*state, 9999, 0, NULL), 0); +} + +/* BIO_new_accept convenience constructor */ + +static void test_bio_new_accept(void **state) +{ + BIO *bio = BIO_new_accept("localhost:443"); + BIO_ACCEPT *data; + + assert_non_null(bio); + data = (BIO_ACCEPT *)bio->ptr; + assert_string_equal(data->param_addr, "localhost"); + assert_string_equal(data->param_serv, "443"); + BIO_free(bio); + (void)state; +} + +/* main */ + +#define ACPT_TEST(name) \ + cmocka_unit_test_setup_teardown(name, setup, teardown) + +#define ACPT_TEST_IO(name) \ + cmocka_unit_test_setup_teardown(name, setup_io, teardown_io) + +int main(void) +{ + const struct CMUnitTest tests[] = { + /* acpt_new */ + ACPT_TEST(test_acpt_new), + /* acpt_free */ + ACPT_TEST(test_acpt_free_no_shutdown), + /* acpt_close_socket */ + ACPT_TEST(test_close_socket_none), + /* acpt_state */ + ACPT_TEST(test_acpt_state_no_addr), + ACPT_TEST(test_acpt_state_unsupported_family), + ACPT_TEST(test_acpt_state_lookup_fails), + ACPT_TEST(test_acpt_state_lookup_empty), + ACPT_TEST(test_acpt_state_socket_fails), + ACPT_TEST(test_acpt_state_socket_next_addr), + ACPT_TEST(test_acpt_state_listen_fails), + ACPT_TEST(test_acpt_state_sock_info_fails), + ACPT_TEST(test_acpt_state_bind_ok), + ACPT_TEST(test_acpt_state_accept_retry), + ACPT_TEST(test_acpt_state_accept_error), + ACPT_TEST(test_acpt_state_already_ok_no_next), + /* acpt_read / acpt_write / acpt_puts */ + ACPT_TEST_IO(test_acpt_read_forwards), + ACPT_TEST_IO(test_acpt_read_forwards_retry), + ACPT_TEST_IO(test_acpt_write_forwards), + ACPT_TEST_IO(test_acpt_write_forwards_retry), + ACPT_TEST_IO(test_acpt_puts_forwards), + /* acpt_ctrl */ + ACPT_TEST(test_acpt_ctrl_reset_clears_addrs), + ACPT_TEST(test_acpt_ctrl_set_accept_name), + ACPT_TEST(test_acpt_ctrl_set_accept_port), + ACPT_TEST(test_acpt_ctrl_set_nbio_accept), + ACPT_TEST(test_acpt_ctrl_set_accept_bios), + ACPT_TEST(test_acpt_ctrl_set_accept_ip_family), + ACPT_TEST(test_acpt_ctrl_set_tfo_accept), + ACPT_TEST(test_acpt_ctrl_set_nbio), + ACPT_TEST(test_acpt_ctrl_set_fd), + ACPT_TEST(test_acpt_ctrl_get_fd), + ACPT_TEST(test_acpt_ctrl_get_accept_names), + ACPT_TEST(test_acpt_ctrl_get_accept_family), + ACPT_TEST(test_acpt_ctrl_get_set_close), + ACPT_TEST(test_acpt_ctrl_bind_mode), + ACPT_TEST(test_acpt_ctrl_pending_flush), + ACPT_TEST(test_acpt_ctrl_eof_no_next), + ACPT_TEST(test_acpt_ctrl_default), + /* BIO_new_accept */ + ACPT_TEST(test_bio_new_accept), + }; + + cmocka_set_message_output(CM_OUTPUT_TAP); + + return cmocka_run_group_tests(tests, group_setup, group_teardown); +} + +#else + +int main(void) +{ + return 0; +} + +#endif /* OPENSSL_NO_SOCK */ From b05333b7e9a47aec2fe4a28915be0497a7480c44 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Wed, 10 Jun 2026 13:04:07 +0200 Subject: [PATCH 205/349] Add BIO bss_dgram test Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Richard Levitte Reviewed-by: Tomas Mraz MergeDate: Thu Jul 9 17:39:21 2026 (Merged from https://github.com/openssl/openssl/pull/30788) --- crypto/bio/bio_local.h | 15 + crypto/bio/bss_dgram.c | 15 - test/unit/build.info | 7 + test/unit/crypto/bio/test_bss_dgram.c | 772 ++++++++++++++++++++++++++ 4 files changed, 794 insertions(+), 15 deletions(-) create mode 100644 test/unit/crypto/bio/test_bss_dgram.c diff --git a/crypto/bio/bio_local.h b/crypto/bio/bio_local.h index 4e2e974039..87227e4f8f 100644 --- a/crypto/bio/bio_local.h +++ b/crypto/bio/bio_local.h @@ -74,6 +74,7 @@ struct bio_addrinfo_st { #include "internal/cryptlib.h" #include "internal/bio.h" #include "internal/refcount.h" +#include "internal/time.h" typedef struct bio_f_buffer_ctx_struct { /*- @@ -174,6 +175,20 @@ typedef struct bio_accept_st { BIO *bio_chain; } BIO_ACCEPT; +#ifndef OPENSSL_NO_DGRAM +typedef struct bio_dgram_data_st { + BIO_ADDR peer; + BIO_ADDR local_addr; + unsigned int connected; + unsigned int _errno; + unsigned int mtu; + OSSL_TIME next_timeout; + OSSL_TIME socket_timeout; + unsigned int peekmode; + char local_addr_enabled; +} bio_dgram_data; +#endif + #define BIO_CONN_S_BEFORE 1 #define BIO_CONN_S_GET_ADDR 2 #define BIO_CONN_S_CREATE_SOCKET 3 diff --git a/crypto/bio/bss_dgram.c b/crypto/bio/bss_dgram.c index 0a1479e834..1d98239511 100644 --- a/crypto/bio/bss_dgram.c +++ b/crypto/bio/bss_dgram.c @@ -14,7 +14,6 @@ #include #include -#include "internal/time.h" #include "bio_local.h" #ifndef OPENSSL_NO_DGRAM @@ -213,21 +212,7 @@ static const BIO_METHOD methods_dgramp_sctp = { NULL, /* sendmmsg */ NULL, /* recvmmsg */ }; -#endif -typedef struct bio_dgram_data_st { - BIO_ADDR peer; - BIO_ADDR local_addr; - unsigned int connected; - unsigned int _errno; - unsigned int mtu; - OSSL_TIME next_timeout; - OSSL_TIME socket_timeout; - unsigned int peekmode; - char local_addr_enabled; -} bio_dgram_data; - -#ifndef OPENSSL_NO_SCTP typedef struct bio_dgram_sctp_save_message_st { BIO *bio; char *data; diff --git a/test/unit/build.info b/test/unit/build.info index ae53ad8d61..6e300d992c 100644 --- a/test/unit/build.info +++ b/test/unit/build.info @@ -22,6 +22,13 @@ WRAP[crypto/bio/test_bss_conn]=BIO_lookup BIO_socket BIO_connect \ INCLUDE[crypto/bio/test_bss_conn]=../../include ../../crypto/bio DEPEND[crypto/bio/test_bss_conn]=../../libcrypto.a +PROGRAMS{noinst}=crypto/bio/test_bss_dgram +SOURCE[crypto/bio/test_bss_dgram]=crypto/bio/test_bss_dgram.c +WRAP[crypto/bio/test_bss_dgram]=recvfrom sendto write getsockname \ + getpeername BIO_closesocket BIO_socket_nbio +INCLUDE[crypto/bio/test_bss_dgram]=../../include ../../crypto/bio +DEPEND[crypto/bio/test_bss_dgram]=../../libcrypto.a + PROGRAMS{noinst}=crypto/bio/test_bss_fd SOURCE[crypto/bio/test_bss_fd]=crypto/bio/test_bss_fd.c INCLUDE[crypto/bio/test_bss_fd]=../../include ../../crypto/bio diff --git a/test/unit/crypto/bio/test_bss_dgram.c b/test/unit/crypto/bio/test_bss_dgram.c new file mode 100644 index 0000000000..4a39ebba5d --- /dev/null +++ b/test/unit/crypto/bio/test_bss_dgram.c @@ -0,0 +1,772 @@ +/* + * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#if defined(OPENSSL_NO_SOCK) || defined(OPENSSL_NO_DGRAM) + +int main(void) +{ + return 0; +} + +#else + +#include +#include +#include +#include +#include +#include +#include +#include "bio_local.h" +#include + +#define FAKE_SOCKET 42 + +/* prototypes for __wrap_* (required by -Wmissing-prototypes) */ +ssize_t __wrap_recvfrom(int fd, void *buf, size_t len, int flags, + struct sockaddr *src, socklen_t *slen); +ssize_t __wrap_sendto(int fd, const void *buf, size_t len, int flags, + const struct sockaddr *dst, socklen_t slen); +ssize_t __wrap_write(int fd, const void *buf, size_t count); +int __wrap_getsockname(int fd, struct sockaddr *addr, socklen_t *slen); +int __wrap_getpeername(int fd, struct sockaddr *addr, socklen_t *slen); +int __wrap_BIO_closesocket(int fd); +int __wrap_BIO_socket_nbio(int fd, int mode); + +/* + * Shared sockaddrs handed back by the address-returning mocks. group_setup + * fills g_sin (AF_INET loopback); g_sin6 is an AF_INET6 loopback and + * g_sin6_v4m an IPv4-mapped IPv6 address (::ffff:127.0.0.1). + */ +static struct sockaddr_in g_sin; +static struct sockaddr_in6 g_sin6; +static struct sockaddr_in6 g_sin6_v4m; + +/* wraps */ + +ssize_t __wrap_recvfrom(int fd, void *buf, size_t len, int flags, + struct sockaddr *src, socklen_t *slen) +{ + ssize_t rc; + + function_called(); + check_expected(fd); + check_expected_ptr(buf); + check_expected(len); + check_expected(flags); + rc = mock_type(ssize_t); + if (rc >= 0 && src != NULL && slen != NULL) { + const struct sockaddr *sa = mock_ptr_type(const struct sockaddr *); + + if (sa != NULL && *slen >= sizeof(g_sin)) { + memcpy(src, sa, sizeof(g_sin)); + *slen = sizeof(g_sin); + } + } + if (rc < 0) + errno = mock_type(int); + return rc; +} + +ssize_t __wrap_sendto(int fd, const void *buf, size_t len, int flags, + const struct sockaddr *dst, socklen_t slen) +{ + ssize_t rc; + + function_called(); + check_expected(fd); + check_expected_ptr(buf); + check_expected(len); + check_expected(flags); + check_expected(slen); + (void)dst; + rc = mock_type(ssize_t); + if (rc < 0) + errno = mock_type(int); + return rc; +} + +ssize_t __wrap_write(int fd, const void *buf, size_t count) +{ + function_called(); + check_expected(fd); + check_expected_ptr(buf); + check_expected(count); + return mock_type(ssize_t); +} + +int __wrap_getsockname(int fd, struct sockaddr *addr, socklen_t *slen) +{ + int rc; + + function_called(); + check_expected(fd); + rc = mock_type(int); + if (rc == 0 && addr != NULL && slen != NULL) { + const struct sockaddr *sa = mock_ptr_type(const struct sockaddr *); + socklen_t sl = (socklen_t)mock_type(int); + + if (sa != NULL && *slen >= sl) { + memcpy(addr, sa, sl); + *slen = sl; + } + } + return rc; +} + +int __wrap_getpeername(int fd, struct sockaddr *addr, socklen_t *slen) +{ + int rc; + + function_called(); + check_expected(fd); + rc = mock_type(int); + if (rc == 0 && addr != NULL && slen != NULL && *slen >= sizeof(g_sin)) { + memcpy(addr, &g_sin, sizeof(g_sin)); + *slen = sizeof(g_sin); + } + return rc; +} + +int __wrap_BIO_closesocket(int fd) +{ + function_called(); + check_expected(fd); + return mock_type(int); +} + +int __wrap_BIO_socket_nbio(int fd, int mode) +{ + function_called(); + check_expected(fd); + check_expected(mode); + return mock_type(int); +} + +/* expectations */ + +static void expect_recvfrom(int fd, const void *buf, size_t len, int flags, + ssize_t rc, const struct sockaddr *src, int errnoval) +{ + expect_function_call(__wrap_recvfrom); + expect_value(__wrap_recvfrom, fd, fd); + expect_value(__wrap_recvfrom, buf, buf); + expect_value(__wrap_recvfrom, len, len); + expect_value(__wrap_recvfrom, flags, flags); + will_return(__wrap_recvfrom, rc); + if (rc >= 0) + will_return(__wrap_recvfrom, src); + else + will_return(__wrap_recvfrom, errnoval); +} + +static void expect_sendto(int fd, const void *buf, size_t len, int flags, + socklen_t slen, ssize_t rc, int errnoval) +{ + expect_function_call(__wrap_sendto); + expect_value(__wrap_sendto, fd, fd); + expect_value(__wrap_sendto, buf, buf); + expect_value(__wrap_sendto, len, len); + expect_value(__wrap_sendto, flags, flags); + expect_value(__wrap_sendto, slen, slen); + will_return(__wrap_sendto, rc); + if (rc < 0) + will_return(__wrap_sendto, errnoval); +} + +static void expect_write(int fd, const void *buf, size_t count, ssize_t rc) +{ + expect_function_call(__wrap_write); + expect_value(__wrap_write, fd, fd); + expect_value(__wrap_write, buf, buf); + expect_value(__wrap_write, count, count); + will_return(__wrap_write, rc); +} + +static void expect_getsockname(int fd, int rc) +{ + expect_function_call(__wrap_getsockname); + expect_value(__wrap_getsockname, fd, fd); + will_return(__wrap_getsockname, rc); + if (rc == 0) { + will_return(__wrap_getsockname, (const struct sockaddr *)&g_sin); + will_return(__wrap_getsockname, (int)sizeof(g_sin)); + } +} + +static void expect_getpeername(int fd, int rc) +{ + expect_function_call(__wrap_getpeername); + expect_value(__wrap_getpeername, fd, fd); + will_return(__wrap_getpeername, rc); +} + +static void expect_BIO_closesocket(int fd, int rc) +{ + expect_function_call(__wrap_BIO_closesocket); + expect_value(__wrap_BIO_closesocket, fd, fd); + will_return(__wrap_BIO_closesocket, rc); +} + +static void expect_BIO_socket_nbio(int fd, int mode, int rc) +{ + expect_function_call(__wrap_BIO_socket_nbio); + expect_value(__wrap_BIO_socket_nbio, fd, fd); + expect_value(__wrap_BIO_socket_nbio, mode, mode); + will_return(__wrap_BIO_socket_nbio, rc); +} + +/* helpers */ + +static bio_dgram_data *get_data(BIO *bio) +{ + return (bio_dgram_data *)bio->ptr; +} + +static void make_peer(BIO *bio, unsigned short port) +{ + struct sockaddr_in sa; + + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons(port); + sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_PEER, 0, &sa); +} + +/* + * Detach the fake socket before teardown so BIO_free does not call a real + * close: BIO_NOCLOSE already guards it, but resetting num keeps any stray + * dgram_clear path inert. + */ +static void reset_for_teardown(BIO *bio) +{ + bio->num = (int)INVALID_SOCKET; + bio->shutdown = BIO_NOCLOSE; +} + +/* setup / teardown */ + +static int setup(void **state) +{ + BIO *bio = BIO_new(BIO_s_datagram()); + + assert_non_null(bio); + *state = bio; + return 0; +} + +static int teardown(void **state) +{ + if (*state != NULL) + BIO_free(*state); + return 0; +} + +/* I/O tests attach the fake socket up front so the BIO is init'ed. */ +static int setup_io(void **state) +{ + BIO *bio; + + if (setup(state) != 0) + return -1; + bio = *state; + expect_getsockname(FAKE_SOCKET, 0); + expect_getpeername(FAKE_SOCKET, -1); + BIO_set_fd(bio, FAKE_SOCKET, BIO_NOCLOSE); + return 0; +} + +static int teardown_io(void **state) +{ + if (*state != NULL) + reset_for_teardown(*state); + return teardown(state); +} + +static int group_setup(void **state) +{ + struct in6_addr loop6 = IN6ADDR_LOOPBACK_INIT; + + (void)state; + memset(&g_sin, 0, sizeof(g_sin)); + g_sin.sin_family = AF_INET; + g_sin.sin_port = htons(443); + g_sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + memset(&g_sin6, 0, sizeof(g_sin6)); + g_sin6.sin6_family = AF_INET6; + g_sin6.sin6_port = htons(443); + g_sin6.sin6_addr = loop6; + + /* ::ffff:127.0.0.1 -> IPv4-mapped, exercises the v4mapped MTU branch */ + memset(&g_sin6_v4m, 0, sizeof(g_sin6_v4m)); + g_sin6_v4m.sin6_family = AF_INET6; + g_sin6_v4m.sin6_port = htons(443); + g_sin6_v4m.sin6_addr.s6_addr[10] = 0xff; + g_sin6_v4m.sin6_addr.s6_addr[11] = 0xff; + g_sin6_v4m.sin6_addr.s6_addr[12] = 127; + g_sin6_v4m.sin6_addr.s6_addr[15] = 1; + return 0; +} + +/* BIO_new_dgram */ +static void test_new_dgram(void **state) +{ + int out = -1; + BIO *bio; + + (void)state; + expect_getsockname(FAKE_SOCKET, 0); + expect_getpeername(FAKE_SOCKET, -1); + bio = BIO_new_dgram(FAKE_SOCKET, BIO_NOCLOSE); + assert_non_null(bio); + assert_int_equal(BIO_get_fd(bio, &out), FAKE_SOCKET); + assert_int_equal(out, FAKE_SOCKET); + reset_for_teardown(bio); + BIO_free(bio); +} + +static void test_dgram_new_defaults(void **state) +{ + BIO *bio = *state; + + assert_non_null(get_data(bio)); + assert_int_equal(bio->num, 0); + assert_int_equal(bio->init, 0); +} + +/* BIO_C_SET_FD / BIO_C_GET_FD */ + +static void test_set_fd_unconnected(void **state) +{ + BIO *bio = *state; + bio_dgram_data *data; + int out = -1; + + expect_getsockname(FAKE_SOCKET, 0); + expect_getpeername(FAKE_SOCKET, -1); + BIO_set_fd(bio, FAKE_SOCKET, BIO_NOCLOSE); + + data = get_data(bio); + assert_int_equal(data->connected, 0); + assert_int_equal(bio->init, 1); + assert_int_equal(BIO_get_fd(bio, &out), FAKE_SOCKET); + assert_int_equal(out, FAKE_SOCKET); + reset_for_teardown(bio); +} + +static void test_set_fd_connected(void **state) +{ + /* getpeername succeeds: peer is recorded and connected is set */ + BIO *bio = *state; + bio_dgram_data *data; + + expect_getsockname(FAKE_SOCKET, 0); + expect_getpeername(FAKE_SOCKET, 0); + BIO_set_fd(bio, FAKE_SOCKET, BIO_NOCLOSE); + + data = get_data(bio); + assert_int_equal(data->connected, 1); + assert_int_equal(BIO_ADDR_family(&data->peer), AF_INET); + reset_for_teardown(bio); +} + +static void test_get_fd_uninit(void **state) +{ + assert_int_equal(BIO_ctrl(*state, BIO_C_GET_FD, 0, NULL), -1); +} + +/* dgram_read */ + +static void test_dgram_read_noop(void **state) +{ + /* outl == 0: recvfrom is never reached */ + BIO *bio = *state; + char buf[1]; + + assert_int_equal(BIO_read(bio, buf, 0), 0); +} + +static void test_dgram_read_success(void **state) +{ + BIO *bio = *state; + char buf[16] = { 0 }; + + expect_recvfrom(FAKE_SOCKET, buf, sizeof(buf), 0, 4, NULL, 0); + assert_int_equal(BIO_read(bio, buf, sizeof(buf)), 4); + assert_false(BIO_should_retry(bio)); +} + +static void test_dgram_read_sets_peer(void **state) +{ + /* unconnected receive records the source address as the peer */ + BIO *bio = *state; + bio_dgram_data *data = get_data(bio); + char buf[16] = { 0 }; + + expect_recvfrom(FAKE_SOCKET, buf, sizeof(buf), 0, 4, + (const struct sockaddr *)&g_sin, 0); + assert_int_equal(BIO_read(bio, buf, sizeof(buf)), 4); + assert_int_equal(BIO_ADDR_family(&data->peer), AF_INET); +} + +static void test_dgram_read_peek(void **state) +{ + /* peekmode passes MSG_PEEK to recvfrom */ + BIO *bio = *state; + char buf[16] = { 0 }; + + get_data(bio)->peekmode = 1; + expect_recvfrom(FAKE_SOCKET, buf, sizeof(buf), MSG_PEEK, 4, NULL, 0); + assert_int_equal(BIO_read(bio, buf, sizeof(buf)), 4); +} + +static void test_dgram_read_retry(void **state) +{ + BIO *bio = *state; + char buf[16] = { 0 }; + + expect_recvfrom(FAKE_SOCKET, buf, sizeof(buf), 0, -1, NULL, EAGAIN); + assert_true(BIO_read(bio, buf, sizeof(buf)) <= 0); + assert_true(BIO_should_read(bio)); +} + +static void test_dgram_read_error(void **state) +{ + BIO *bio = *state; + char buf[16] = { 0 }; + + expect_recvfrom(FAKE_SOCKET, buf, sizeof(buf), 0, -1, NULL, ECONNREFUSED); + assert_true(BIO_read(bio, buf, sizeof(buf)) <= 0); + assert_false(BIO_should_retry(bio)); +} + +/* dgram_write */ + +static void test_dgram_write_unconnected(void **state) +{ + BIO *bio = *state; + const char buf[] = "hello"; + + make_peer(bio, 4433); + expect_sendto(FAKE_SOCKET, buf, 5, 0, + (socklen_t)sizeof(struct sockaddr_in), 5, 0); + assert_int_equal(BIO_write(bio, buf, 5), 5); + assert_false(BIO_should_retry(bio)); +} + +static void test_dgram_write_connected(void **state) +{ + BIO *bio = *state; + bio_dgram_data *data = get_data(bio); + const char buf[] = "hello"; + + data->connected = 1; + expect_write(FAKE_SOCKET, buf, 5, 5); + assert_int_equal(BIO_write(bio, buf, 5), 5); + data->connected = 0; +} + +static void test_dgram_write_retry(void **state) +{ + BIO *bio = *state; + const char buf[] = "hello"; + + make_peer(bio, 4433); + expect_sendto(FAKE_SOCKET, buf, 5, 0, + (socklen_t)sizeof(struct sockaddr_in), -1, EAGAIN); + assert_true(BIO_write(bio, buf, 5) <= 0); + assert_true(BIO_should_write(bio)); +} + +static void test_dgram_write_error(void **state) +{ + BIO *bio = *state; + const char buf[] = "hello"; + + make_peer(bio, 4433); + expect_sendto(FAKE_SOCKET, buf, 5, 0, + (socklen_t)sizeof(struct sockaddr_in), -1, ECONNREFUSED); + assert_true(BIO_write(bio, buf, 5) <= 0); + assert_false(BIO_should_retry(bio)); +} + +/* dgram_ctrl */ + +static void test_ctrl_reset(void **state) +{ + assert_int_equal(BIO_ctrl(*state, BIO_CTRL_RESET, 0, NULL), 0); +} + +static void test_ctrl_info(void **state) +{ + assert_int_equal(BIO_ctrl(*state, BIO_CTRL_INFO, 0, NULL), 0); +} + +static void test_ctrl_pending_wpending(void **state) +{ + assert_int_equal(BIO_ctrl(*state, BIO_CTRL_PENDING, 0, NULL), 0); + assert_int_equal(BIO_ctrl(*state, BIO_CTRL_WPENDING, 0, NULL), 0); +} + +static void test_ctrl_dup_flush(void **state) +{ + assert_int_equal(BIO_ctrl(*state, BIO_CTRL_DUP, 0, NULL), 1); + assert_int_equal(BIO_ctrl(*state, BIO_CTRL_FLUSH, 0, NULL), 1); +} + +static void test_ctrl_get_set_close(void **state) +{ + BIO *bio = *state; + + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_GET_CLOSE, 0, NULL), BIO_NOCLOSE); + BIO_ctrl(bio, BIO_CTRL_SET_CLOSE, BIO_CLOSE, NULL); + assert_int_equal(bio->shutdown, BIO_CLOSE); + bio->shutdown = BIO_NOCLOSE; +} + +static void test_ctrl_connect(void **state) +{ + BIO *bio = *state; + bio_dgram_data *data = get_data(bio); + struct sockaddr_in sa; + + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons(4433); + sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + BIO_ctrl(bio, BIO_CTRL_DGRAM_CONNECT, 0, &sa); + assert_int_equal(BIO_ADDR_family(&data->peer), AF_INET); + assert_int_equal(data->peer.s_in.sin_port, htons(4433)); +} + +static void test_ctrl_set_get_peer(void **state) +{ + BIO *bio = *state; + BIO_ADDR got; + + make_peer(bio, 4433); + memset(&got, 0, sizeof(got)); + BIO_ctrl(bio, BIO_CTRL_DGRAM_GET_PEER, sizeof(got), &got); + assert_int_equal(got.s_in.sin_family, AF_INET); + assert_int_equal(got.s_in.sin_port, htons(4433)); +} + +static void test_ctrl_set_connected(void **state) +{ + BIO *bio = *state; + bio_dgram_data *data = get_data(bio); + struct sockaddr_in sa; + + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons(4433); + sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_CONNECTED, 0, &sa); + assert_int_equal(data->connected, 1); + assert_int_equal(BIO_ADDR_family(&data->peer), AF_INET); + + BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_CONNECTED, 0, NULL); + assert_int_equal(data->connected, 0); + assert_int_equal(BIO_ADDR_family(&data->peer), AF_UNSPEC); +} + +static void test_ctrl_detect_peer_addr_from_data(void **state) +{ + /* peer already known: returned without touching getpeername */ + BIO *bio = *state; + BIO_ADDR got; + + make_peer(bio, 4433); + memset(&got, 0, sizeof(got)); + assert_true( + BIO_ctrl(bio, BIO_CTRL_DGRAM_DETECT_PEER_ADDR, sizeof(got), &got) > 0); + assert_int_equal(got.s_in.sin_family, AF_INET); +} + +static void test_ctrl_detect_peer_addr_via_getpeername(void **state) +{ + /* peer unset: dgram_ctrl falls back to getpeername */ + BIO *bio = *state; + BIO_ADDR got; + + memset(&got, 0, sizeof(got)); + expect_getpeername(FAKE_SOCKET, 0); + assert_true( + BIO_ctrl(bio, BIO_CTRL_DGRAM_DETECT_PEER_ADDR, sizeof(got), &got) > 0); + assert_int_equal(got.s_in.sin_family, AF_INET); +} + +static void test_ctrl_set_get_mtu(void **state) +{ + BIO *bio = *state; + + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_MTU, 1400, NULL), 1400); + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_DGRAM_GET_MTU, 0, NULL), 1400); +} + +static void test_ctrl_fallback_mtu_ipv4(void **state) +{ + /* AF_INET peer: 576 payload minus 28 bytes IP+UDP overhead */ + BIO *bio = *state; + + make_peer(bio, 4433); + assert_int_equal( + BIO_ctrl(bio, BIO_CTRL_DGRAM_GET_FALLBACK_MTU, 0, NULL), 576 - 28); +} + +#if OPENSSL_USE_IPV6 +static void test_ctrl_fallback_mtu_ipv6(void **state) +{ + /* AF_INET6 non-mapped peer: 1280 minus 48 bytes overhead */ + BIO *bio = *state; + + BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_PEER, 0, &g_sin6); + assert_int_equal( + BIO_ctrl(bio, BIO_CTRL_DGRAM_GET_FALLBACK_MTU, 0, NULL), 1280 - 48); +} + +#ifdef IN6_IS_ADDR_V4MAPPED +static void test_ctrl_fallback_mtu_ipv6_v4mapped(void **state) +{ + /* v4-mapped AF_INET6 peer: treated as IPv4, 576 minus 28 overhead */ + BIO *bio = *state; + + BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_PEER, 0, &g_sin6_v4m); + assert_int_equal( + BIO_ctrl(bio, BIO_CTRL_DGRAM_GET_FALLBACK_MTU, 0, NULL), 576 - 28); +} +#endif +#endif + +#if defined(OPENSSL_SYS_LINUX) && defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO) +static void test_ctrl_mtu_discover_getsockname_fails(void **state) +{ + /* getsockname fails before any setsockopt: ret 0 */ + BIO *bio = *state; + + expect_getsockname(FAKE_SOCKET, -1); + assert_int_equal( + BIO_ctrl(bio, BIO_CTRL_DGRAM_MTU_DISCOVER, 0, NULL), 0); +} +#endif + +static void test_ctrl_nbio(void **state) +{ + BIO *bio = *state; + + expect_BIO_socket_nbio(FAKE_SOCKET, 1, 1); + assert_int_equal(BIO_ctrl(bio, BIO_C_SET_NBIO, 1, NULL), 1); +} + +static void test_ctrl_set_next_timeout(void **state) +{ + BIO *bio = *state; + struct timeval tv = { 1, 0 }; + + assert_int_equal( + BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0, &tv), 1); +} + +/* dgram_clear / dgram_free */ + +static void test_free_closes_when_shutdown(void **state) +{ + BIO *bio = BIO_new(BIO_s_datagram()); + + (void)state; + assert_non_null(bio); + expect_getsockname(FAKE_SOCKET, 0); + expect_getpeername(FAKE_SOCKET, -1); + BIO_set_fd(bio, FAKE_SOCKET, BIO_CLOSE); + + expect_BIO_closesocket(FAKE_SOCKET, 0); + BIO_free(bio); +} + +static void test_free_no_close_when_noclose(void **state) +{ + BIO *bio = BIO_new(BIO_s_datagram()); + + (void)state; + assert_non_null(bio); + expect_getsockname(FAKE_SOCKET, 0); + expect_getpeername(FAKE_SOCKET, -1); + BIO_set_fd(bio, FAKE_SOCKET, BIO_NOCLOSE); + BIO_free(bio); +} + +/* main */ + +#define DG_TEST(name) \ + cmocka_unit_test_setup_teardown(name, setup, teardown) + +#define DG_TEST_IO(name) \ + cmocka_unit_test_setup_teardown(name, setup_io, teardown_io) + +int main(void) +{ + const struct CMUnitTest tests[] = { + /* BIO_new_dgram */ + DG_TEST(test_new_dgram), + DG_TEST(test_dgram_new_defaults), + /* SET_FD / GET_FD */ + DG_TEST(test_set_fd_unconnected), + DG_TEST(test_set_fd_connected), + DG_TEST(test_get_fd_uninit), + /* dgram_read */ + DG_TEST_IO(test_dgram_read_noop), + DG_TEST_IO(test_dgram_read_success), + DG_TEST_IO(test_dgram_read_sets_peer), + DG_TEST_IO(test_dgram_read_peek), + DG_TEST_IO(test_dgram_read_retry), + DG_TEST_IO(test_dgram_read_error), + /* dgram_write */ + DG_TEST_IO(test_dgram_write_unconnected), + DG_TEST_IO(test_dgram_write_connected), + DG_TEST_IO(test_dgram_write_retry), + DG_TEST_IO(test_dgram_write_error), + /* dgram_ctrl */ + DG_TEST_IO(test_ctrl_reset), + DG_TEST_IO(test_ctrl_info), + DG_TEST_IO(test_ctrl_pending_wpending), + DG_TEST_IO(test_ctrl_dup_flush), + DG_TEST_IO(test_ctrl_get_set_close), + DG_TEST_IO(test_ctrl_connect), + DG_TEST_IO(test_ctrl_set_get_peer), + DG_TEST_IO(test_ctrl_set_connected), + DG_TEST_IO(test_ctrl_detect_peer_addr_from_data), + DG_TEST_IO(test_ctrl_detect_peer_addr_via_getpeername), + DG_TEST_IO(test_ctrl_set_get_mtu), + DG_TEST_IO(test_ctrl_fallback_mtu_ipv4), +#if OPENSSL_USE_IPV6 + DG_TEST_IO(test_ctrl_fallback_mtu_ipv6), +#ifdef IN6_IS_ADDR_V4MAPPED + DG_TEST_IO(test_ctrl_fallback_mtu_ipv6_v4mapped), +#endif +#endif +#if defined(OPENSSL_SYS_LINUX) && defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO) + DG_TEST_IO(test_ctrl_mtu_discover_getsockname_fails), +#endif + DG_TEST_IO(test_ctrl_nbio), + DG_TEST_IO(test_ctrl_set_next_timeout), + /* dgram_clear / dgram_free */ + DG_TEST(test_free_closes_when_shutdown), + DG_TEST(test_free_no_close_when_noclose), + }; + + cmocka_set_message_output(CM_OUTPUT_TAP); + + return cmocka_run_group_tests(tests, group_setup, NULL); +} + +#endif /* OPENSSL_NO_SOCK || OPENSSL_NO_DGRAM */ From bd07dda47d150d7ce758c932ab50097ce0c65962 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Wed, 10 Jun 2026 19:55:55 +0200 Subject: [PATCH 206/349] Add BIO bss_socket test Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Richard Levitte Reviewed-by: Tomas Mraz MergeDate: Thu Jul 9 17:39:22 2026 (Merged from https://github.com/openssl/openssl/pull/30788) --- test/unit/build.info | 6 + test/unit/crypto/bio/test_bss_sock.c | 530 +++++++++++++++++++++++++++ 2 files changed, 536 insertions(+) create mode 100644 test/unit/crypto/bio/test_bss_sock.c diff --git a/test/unit/build.info b/test/unit/build.info index 6e300d992c..c65bb5016a 100644 --- a/test/unit/build.info +++ b/test/unit/build.info @@ -34,3 +34,9 @@ SOURCE[crypto/bio/test_bss_fd]=crypto/bio/test_bss_fd.c INCLUDE[crypto/bio/test_bss_fd]=../../include ../../crypto/bio DEPEND[crypto/bio/test_bss_fd]=../../libcrypto.a WRAP[crypto/bio/test_bss_fd]=read write lseek close + +PROGRAMS{noinst}=crypto/bio/test_bss_sock +SOURCE[crypto/bio/test_bss_sock]=crypto/bio/test_bss_sock.c +WRAP[crypto/bio/test_bss_sock]=read write BIO_closesocket +INCLUDE[crypto/bio/test_bss_sock]=../../include ../../crypto/bio +DEPEND[crypto/bio/test_bss_sock]=../../libcrypto.a diff --git a/test/unit/crypto/bio/test_bss_sock.c b/test/unit/crypto/bio/test_bss_sock.c new file mode 100644 index 0000000000..8d5aa547f0 --- /dev/null +++ b/test/unit/crypto/bio/test_bss_sock.c @@ -0,0 +1,530 @@ +/* + * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "internal/sockets.h" + +#ifndef OPENSSL_NO_SOCK + +#include +#include +#include +#include +#include +#include +#include +#include "bio_local.h" +#include "internal/bio_tfo.h" +#include + +#define FAKE_SOCKET 42 + +/* prototypes for __wrap_* (required by -Wmissing-prototypes) */ +ssize_t __wrap_read(int fd, void *buf, size_t count); +ssize_t __wrap_write(int fd, const void *buf, size_t count); +int __wrap_BIO_closesocket(int fd); + +/* wraps */ + +ssize_t __wrap_read(int fd, void *buf, size_t count) +{ + ssize_t rc; + + function_called(); + check_expected(fd); + check_expected_ptr(buf); + check_expected(count); + rc = mock_type(ssize_t); + if (rc < 0) + errno = mock_type(int); + return rc; +} + +ssize_t __wrap_write(int fd, const void *buf, size_t count) +{ + ssize_t rc; + + function_called(); + check_expected(fd); + check_expected_ptr(buf); + check_expected(count); + rc = mock_type(ssize_t); + if (rc < 0) + errno = mock_type(int); + return rc; +} + +int __wrap_BIO_closesocket(int fd) +{ + function_called(); + check_expected(fd); + return mock_type(int); +} + +/* expectations */ + +static void expect_read(int fd, const void *buf, size_t count, ssize_t rc, + int errnoval) +{ + expect_function_call(__wrap_read); + expect_value(__wrap_read, fd, fd); + expect_value(__wrap_read, buf, buf); + expect_value(__wrap_read, count, count); + will_return(__wrap_read, rc); + if (rc < 0) + will_return(__wrap_read, errnoval); +} + +static void expect_write(int fd, const void *buf, size_t count, ssize_t rc, + int errnoval) +{ + expect_function_call(__wrap_write); + expect_value(__wrap_write, fd, fd); + expect_value(__wrap_write, buf, buf); + expect_value(__wrap_write, count, count); + will_return(__wrap_write, rc); + if (rc < 0) + will_return(__wrap_write, errnoval); +} + +static void expect_BIO_closesocket(int fd, int rc) +{ + expect_function_call(__wrap_BIO_closesocket); + expect_value(__wrap_BIO_closesocket, fd, fd); + will_return(__wrap_BIO_closesocket, rc); +} + +/* setup / teardown */ + +static int setup(void **state) +{ + BIO *bio = BIO_new(BIO_s_socket()); + + assert_non_null(bio); + BIO_set_fd(bio, FAKE_SOCKET, BIO_NOCLOSE); + *state = bio; + return 0; +} + +static int teardown(void **state) +{ + if (*state != NULL) + BIO_free(*state); + return 0; +} + +/* sock_new defaults */ + +static void test_sock_new_defaults(void **state) +{ + /* fresh BIO before BIO_set_fd: init 0, num 0, ptr allocated */ + BIO *bio = BIO_new(BIO_s_socket()); + + (void)state; + assert_non_null(bio); + assert_int_equal(bio->init, 0); + assert_int_equal(bio->num, 0); + assert_non_null(bio->ptr); + BIO_free(bio); +} + +/* BIO_new_socket */ + +static void test_new_socket(void **state) +{ + int out = -1; + BIO *bio = BIO_new_socket(FAKE_SOCKET, BIO_NOCLOSE); + + (void)state; + assert_non_null(bio); + assert_int_equal(bio->init, 1); + assert_int_equal(BIO_get_fd(bio, &out), FAKE_SOCKET); + assert_int_equal(out, FAKE_SOCKET); + BIO_free(bio); +} + +/* sock_read */ + +static void test_sock_read_noop(void **state) +{ + /* outl == 0: readsocket is never reached */ + BIO *bio = *state; + char buf[1]; + + assert_int_equal(BIO_read(bio, buf, 0), 0); +} + +static void test_sock_read_success(void **state) +{ + BIO *bio = *state; + char buf[16] = { 0 }; + + expect_read(FAKE_SOCKET, buf, 16, 4, 0); + assert_int_equal(BIO_read(bio, buf, sizeof(buf)), 4); + assert_false(BIO_should_retry(bio)); + assert_false(BIO_eof(bio)); +} + +static void test_sock_read_eof(void **state) +{ + BIO *bio = *state; + char buf[16] = { 0 }; + + expect_read(FAKE_SOCKET, buf, 16, 0, 0); + assert_true(BIO_read(bio, buf, sizeof(buf)) <= 0); + assert_true(BIO_eof(bio)); + assert_false(BIO_should_retry(bio)); +} + +static void test_sock_read_retry(void **state) +{ + BIO *bio = *state; + char buf[16] = { 0 }; + + expect_read(FAKE_SOCKET, buf, 16, -1, EAGAIN); + assert_true(BIO_read(bio, buf, sizeof(buf)) <= 0); + assert_true(BIO_should_read(bio)); + assert_false(BIO_eof(bio)); +} + +static void test_sock_read_error(void **state) +{ + BIO *bio = *state; + char buf[16] = { 0 }; + + expect_read(FAKE_SOCKET, buf, 16, -1, ECONNREFUSED); + assert_true(BIO_read(bio, buf, sizeof(buf)) <= 0); + assert_false(BIO_should_retry(bio)); + assert_false(BIO_eof(bio)); +} + +static void test_sock_read_clears_eof(void **state) +{ + /* BIO_FLAGS_IN_EOF is cleared at the start of each new read attempt */ + BIO *bio = *state; + char buf[1] = { 0 }; + + expect_read(FAKE_SOCKET, buf, 1, 0, 0); + BIO_read(bio, buf, 1); + assert_true(BIO_eof(bio)); + + expect_read(FAKE_SOCKET, buf, 1, 1, 0); + assert_int_equal(BIO_read(bio, buf, 1), 1); + assert_false(BIO_eof(bio)); +} + +/* sock_write */ + +static void test_sock_write_success(void **state) +{ + BIO *bio = *state; + const char buf[] = "hello"; + + expect_write(FAKE_SOCKET, buf, 5, 5, 0); + assert_int_equal(BIO_write(bio, buf, 5), 5); + assert_false(BIO_should_retry(bio)); +} + +static void test_sock_write_retry(void **state) +{ + BIO *bio = *state; + const char buf[] = "hello"; + + expect_write(FAKE_SOCKET, buf, 5, -1, EAGAIN); + assert_true(BIO_write(bio, buf, 5) <= 0); + assert_true(BIO_should_write(bio)); +} + +static void test_sock_write_error(void **state) +{ + BIO *bio = *state; + const char buf[] = "hello"; + + expect_write(FAKE_SOCKET, buf, 5, -1, ECONNREFUSED); + assert_true(BIO_write(bio, buf, 5) <= 0); + assert_false(BIO_should_retry(bio)); +} + +/* sock_ctrl */ + +static void test_ctrl_get_fd(void **state) +{ + BIO *bio = *state; + int out = -1; + + assert_int_equal(BIO_ctrl(bio, BIO_C_GET_FD, 0, &out), FAKE_SOCKET); + assert_int_equal(out, FAKE_SOCKET); +} + +static void test_ctrl_get_fd_uninit(void **state) +{ + BIO *bio = *state; + + bio->init = 0; + assert_int_equal(BIO_ctrl(bio, BIO_C_GET_FD, 0, NULL), -1); + bio->init = 1; +} + +static void test_ctrl_set_fd(void **state) +{ + BIO *bio = *state; + int newfd = 99; + + /* existing shutdown=BIO_NOCLOSE so the old fd is not closed */ + BIO_ctrl(bio, BIO_C_SET_FD, BIO_NOCLOSE, &newfd); + assert_int_equal(bio->num, 99); + assert_int_equal(bio->shutdown, BIO_NOCLOSE); + assert_int_equal(bio->init, 1); +} + +static void test_ctrl_get_set_close(void **state) +{ + BIO *bio = *state; + + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_GET_CLOSE, 0, NULL), BIO_NOCLOSE); + BIO_ctrl(bio, BIO_CTRL_SET_CLOSE, BIO_CLOSE, NULL); + assert_int_equal(bio->shutdown, BIO_CLOSE); + bio->shutdown = BIO_NOCLOSE; +} + +static void test_ctrl_dup_flush(void **state) +{ + BIO *bio = *state; + + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_DUP, 0, NULL), 1); + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_FLUSH, 0, NULL), 1); +} + +static void test_ctrl_rpoll_descriptor(void **state) +{ + BIO *bio = *state; + BIO_POLL_DESCRIPTOR pd; + + memset(&pd, 0, sizeof(pd)); + assert_int_equal( + BIO_ctrl(bio, BIO_CTRL_GET_RPOLL_DESCRIPTOR, 0, &pd), 1); + assert_int_equal(pd.type, BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD); + assert_int_equal(pd.value.fd, FAKE_SOCKET); +} + +static void test_ctrl_wpoll_descriptor(void **state) +{ + BIO *bio = *state; + BIO_POLL_DESCRIPTOR pd; + + memset(&pd, 0, sizeof(pd)); + assert_int_equal( + BIO_ctrl(bio, BIO_CTRL_GET_WPOLL_DESCRIPTOR, 0, &pd), 1); + assert_int_equal(pd.type, BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD); + assert_int_equal(pd.value.fd, FAKE_SOCKET); +} + +static void test_ctrl_poll_descriptor_uninit(void **state) +{ + BIO *bio = *state; + BIO_POLL_DESCRIPTOR pd; + + memset(&pd, 0, sizeof(pd)); + bio->init = 0; + assert_int_equal( + BIO_ctrl(bio, BIO_CTRL_GET_RPOLL_DESCRIPTOR, 0, &pd), 0); + bio->init = 1; +} + +static void test_ctrl_eof_clear(void **state) +{ + BIO *bio = *state; + + bio->flags &= ~BIO_FLAGS_IN_EOF; + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_EOF, 0, NULL), 0); +} + +static void test_ctrl_eof_set(void **state) +{ + BIO *bio = *state; + + bio->flags |= BIO_FLAGS_IN_EOF; + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_EOF, 0, NULL), 1); + bio->flags &= ~BIO_FLAGS_IN_EOF; +} + +static void test_ctrl_get_connect(void **state) +{ + /* num==2: returns a pointer to the stored tfo_peer */ + BIO *bio = *state; + const char *ptr = NULL; + + assert_int_equal(BIO_ctrl(bio, BIO_C_GET_CONNECT, 2, &ptr), 1); + assert_non_null(ptr); +} + +static void test_ctrl_get_connect_bad_num(void **state) +{ + BIO *bio = *state; + const char *ptr = NULL; + + assert_int_equal(BIO_ctrl(bio, BIO_C_GET_CONNECT, 0, &ptr), 0); +} + +static void test_ctrl_set_connect(void **state) +{ + BIO *bio = *state; + struct sockaddr_in sa; + BIO_ADDR addr; + + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons(4433); + sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + assert_true(BIO_ADDR_make(&addr, (const struct sockaddr *)&sa)); + + assert_int_equal(BIO_ctrl(bio, BIO_C_SET_CONNECT, 2, &addr), 1); +} + +static void test_ctrl_set_connect_bad_num(void **state) +{ + BIO *bio = *state; + BIO_ADDR addr; + + memset(&addr, 0, sizeof(addr)); + assert_int_equal(BIO_ctrl(bio, BIO_C_SET_CONNECT, 0, &addr), 0); +} + +static void test_ctrl_set_send_flags(void **state) +{ + BIO *bio = *state; + + assert_int_equal(BIO_ctrl(bio, BIO_C_SET_SEND_FLAGS, 0, NULL), 1); +} + +static void test_ctrl_default(void **state) +{ + assert_int_equal(BIO_ctrl(*state, 9999, 0, NULL), 0); +} + +/* sock_puts */ + +static void test_sock_puts_success(void **state) +{ + BIO *bio = *state; + const char *str = "hello"; + + expect_write(FAKE_SOCKET, str, 5, 5, 0); + assert_int_equal(BIO_puts(bio, str), 5); +} + +static void test_sock_puts_write_fails(void **state) +{ + BIO *bio = *state; + const char *str = "hello"; + + expect_write(FAKE_SOCKET, str, 5, -1, ECONNREFUSED); + assert_true(BIO_puts(bio, str) <= 0); +} + +/* sock_free (via BIO_free) - mostly just coverage for ASAN */ + +static void test_free_closes_when_shutdown(void **state) +{ + BIO *bio = BIO_new(BIO_s_socket()); + + (void)state; + assert_non_null(bio); + BIO_set_fd(bio, FAKE_SOCKET, BIO_CLOSE); + + expect_BIO_closesocket(FAKE_SOCKET, 0); + BIO_free(bio); +} + +static void test_free_no_close_when_noclose(void **state) +{ + BIO *bio = BIO_new(BIO_s_socket()); + + (void)state; + assert_non_null(bio); + BIO_set_fd(bio, FAKE_SOCKET, BIO_NOCLOSE); + BIO_free(bio); +} + +static void test_free_no_close_when_uninit(void **state) +{ + /* shutdown set but init==0: closesocket must NOT be called */ + BIO *bio = BIO_new(BIO_s_socket()); + + (void)state; + assert_non_null(bio); + bio->num = FAKE_SOCKET; + bio->shutdown = BIO_CLOSE; + bio->init = 0; + BIO_free(bio); +} + +/* main */ + +#define SOCK_TEST(name) \ + cmocka_unit_test_setup_teardown(name, setup, teardown) + +#define SOCK_TEST_PLAIN(name) \ + cmocka_unit_test(name) + +int main(void) +{ + const struct CMUnitTest tests[] = { + /* sock_new / BIO_new_socket */ + SOCK_TEST_PLAIN(test_sock_new_defaults), + SOCK_TEST_PLAIN(test_new_socket), + /* sock_read */ + SOCK_TEST(test_sock_read_noop), + SOCK_TEST(test_sock_read_success), + SOCK_TEST(test_sock_read_eof), + SOCK_TEST(test_sock_read_retry), + SOCK_TEST(test_sock_read_error), + SOCK_TEST(test_sock_read_clears_eof), + /* sock_write */ + SOCK_TEST(test_sock_write_success), + SOCK_TEST(test_sock_write_retry), + SOCK_TEST(test_sock_write_error), + /* sock_ctrl */ + SOCK_TEST(test_ctrl_get_fd), + SOCK_TEST(test_ctrl_get_fd_uninit), + SOCK_TEST(test_ctrl_set_fd), + SOCK_TEST(test_ctrl_get_set_close), + SOCK_TEST(test_ctrl_dup_flush), + SOCK_TEST(test_ctrl_rpoll_descriptor), + SOCK_TEST(test_ctrl_wpoll_descriptor), + SOCK_TEST(test_ctrl_poll_descriptor_uninit), + SOCK_TEST(test_ctrl_eof_clear), + SOCK_TEST(test_ctrl_eof_set), + SOCK_TEST(test_ctrl_get_connect), + SOCK_TEST(test_ctrl_get_connect_bad_num), + SOCK_TEST(test_ctrl_set_connect), + SOCK_TEST(test_ctrl_set_connect_bad_num), + SOCK_TEST(test_ctrl_set_send_flags), + SOCK_TEST(test_ctrl_default), + /* sock_puts */ + SOCK_TEST(test_sock_puts_success), + SOCK_TEST(test_sock_puts_write_fails), + /* sock_free */ + SOCK_TEST(test_free_closes_when_shutdown), + SOCK_TEST(test_free_no_close_when_noclose), + SOCK_TEST(test_free_no_close_when_uninit), + }; + + cmocka_set_message_output(CM_OUTPUT_TAP); + + return cmocka_run_group_tests(tests, NULL, NULL); +} + +#else + +int main(void) +{ + return 0; +} + +#endif /* OPENSSL_NO_SOCK */ From 207c40c97a1f502a39df260c8daf919de2fd9cb2 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Fri, 12 Jun 2026 11:53:56 +0200 Subject: [PATCH 207/349] Add BIO bio_sock test Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Richard Levitte Reviewed-by: Tomas Mraz MergeDate: Thu Jul 9 17:39:24 2026 (Merged from https://github.com/openssl/openssl/pull/30788) --- test/unit/build.info | 9 + test/unit/crypto/bio/test_bio_sock.c | 536 +++++++++++++++++++++++++++ 2 files changed, 545 insertions(+) create mode 100644 test/unit/crypto/bio/test_bio_sock.c diff --git a/test/unit/build.info b/test/unit/build.info index c65bb5016a..f215b70ec0 100644 --- a/test/unit/build.info +++ b/test/unit/build.info @@ -6,6 +6,15 @@ INCLUDE[crypto/bio/test_bio_addr]=../../include ../../crypto/bio DEPEND[crypto/bio/test_bio_addr]=../../libcrypto.a WRAP[crypto/bio/test_bio_addr]=BIO_sock_init getnameinfo freeaddrinfo +PROGRAMS{noinst}=crypto/bio/test_bio_sock +SOURCE[crypto/bio/test_bio_sock]=crypto/bio/test_bio_sock.c +INCLUDE[crypto/bio/test_bio_sock]=../../include ../../crypto/bio +DEPEND[crypto/bio/test_bio_sock]=../../libcrypto.a +WRAP[crypto/bio/test_bio_sock]=getsockopt setsockopt getsockname ioctl poll \ + gethostbyname BIO_lookup BIO_socket BIO_listen BIO_closesocket \ + BIO_ADDRINFO_free BIO_accept_ex BIO_sock_should_retry \ + BIO_ADDR_hostname_string BIO_ADDR_service_string + PROGRAMS{noinst}=crypto/bio/test_bss_acpt SOURCE[crypto/bio/test_bss_acpt]=crypto/bio/test_bss_acpt.c WRAP[crypto/bio/test_bss_acpt]=BIO_lookup BIO_socket BIO_listen \ diff --git a/test/unit/crypto/bio/test_bio_sock.c b/test/unit/crypto/bio/test_bio_sock.c new file mode 100644 index 0000000000..b41f9ed011 --- /dev/null +++ b/test/unit/crypto/bio/test_bio_sock.c @@ -0,0 +1,536 @@ +/* + * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "internal/sockets.h" + +#ifndef OPENSSL_NO_SOCK + +#include +#include +#include +#include + +#include "bio_local.h" +#include + +#define FAKE_SOCKET 42 + +#if defined(TCP_NODELAY) && (defined(IPPROTO_TCP) || defined(SOL_TCP)) +#ifdef SOL_TCP +#define TEST_TCP_LEVEL SOL_TCP +#else +#define TEST_TCP_LEVEL IPPROTO_TCP +#endif +#endif + +/* prototypes for __wrap_* (required by -Wmissing-prototypes) */ +int __wrap_getsockopt(int fd, int level, int optname, void *optval, + socklen_t *optlen); +int __wrap_setsockopt(int fd, int level, int optname, const void *optval, + socklen_t optlen); +int __wrap_getsockname(int fd, struct sockaddr *addr, socklen_t *slen); +int __wrap_ioctl(int fd, unsigned long request, void *arg); +int __wrap_poll(struct pollfd *fds, nfds_t nfds, int timeout); +struct hostent *__wrap_gethostbyname(const char *name); +int __wrap_BIO_lookup(const char *host, const char *service, + enum BIO_lookup_type lookup_type, int family, int socktype, + BIO_ADDRINFO **res); +int __wrap_BIO_socket(int domain, int socktype, int protocol, int options); +int __wrap_BIO_listen(int sock, const BIO_ADDR *ba, int options); +int __wrap_BIO_closesocket(int sock); +void __wrap_BIO_ADDRINFO_free(BIO_ADDRINFO *bai); +int __wrap_BIO_accept_ex(int accept_sock, BIO_ADDR *addr, int options); +int __wrap_BIO_sock_should_retry(int i); +char *__wrap_BIO_ADDR_hostname_string(const BIO_ADDR *ap, int numeric); +char *__wrap_BIO_ADDR_service_string(const BIO_ADDR *ap, int numeric); + +/* wraps */ + +int __wrap_getsockopt(int fd, int level, int optname, void *optval, + socklen_t *optlen) +{ + int rc; + + function_called(); + check_expected(fd); + check_expected(level); + check_expected(optname); + (void)optlen; + rc = mock_type(int); + if (rc == 0) { + if (optval != NULL) + *(int *)optval = mock_type(int); + } else { + errno = mock_type(int); + } + return rc; +} + +int __wrap_setsockopt(int fd, int level, int optname, const void *optval, + socklen_t optlen) +{ + int on = (optval != NULL) ? *(const int *)optval : 0; + + function_called(); + check_expected(fd); + check_expected(level); + check_expected(optname); + check_expected(on); + (void)optlen; + return mock_type(int); +} + +int __wrap_getsockname(int fd, struct sockaddr *addr, socklen_t *slen) +{ + int rc; + + function_called(); + check_expected(fd); + (void)addr; + rc = mock_type(int); + if (rc == 0 && slen != NULL) + *slen = (socklen_t)mock_type(int); + return rc; +} + +int __wrap_ioctl(int fd, unsigned long request, void *arg) +{ + function_called(); + check_expected(fd); + check_expected(request); + (void)arg; + return mock_type(int); +} + +int __wrap_poll(struct pollfd *fds, nfds_t nfds, int timeout) +{ + int pfd = fds[0].fd; + int events = fds[0].events; + + function_called(); + check_expected(pfd); + check_expected(events); + (void)nfds; + (void)timeout; + return mock_type(int); +} + +struct hostent *__wrap_gethostbyname(const char *name) +{ + function_called(); + check_expected_ptr(name); + return mock_ptr_type(struct hostent *); +} + +int __wrap_BIO_lookup(const char *host, const char *service, + enum BIO_lookup_type lookup_type, int family, int socktype, + BIO_ADDRINFO **res) +{ + int rc; + + function_called(); + check_expected(family); + check_expected(socktype); + check_expected(lookup_type); + (void)host; + (void)service; + rc = mock_type(int); + if (rc == 1) { + BIO_ADDRINFO *r = mock_ptr_type(BIO_ADDRINFO *); + + if (res != NULL) + *res = r; + } + return rc; +} + +int __wrap_BIO_socket(int domain, int socktype, int protocol, int options) +{ + function_called(); + check_expected(domain); + check_expected(socktype); + check_expected(protocol); + (void)options; + return mock_type(int); +} + +int __wrap_BIO_listen(int sock, const BIO_ADDR *ba, int options) +{ + function_called(); + check_expected(sock); + check_expected(options); + (void)ba; + return mock_type(int); +} + +int __wrap_BIO_closesocket(int sock) +{ + function_called(); + check_expected(sock); + return mock_type(int); +} + +/* the fake addrinfo handed back by BIO_lookup is static; nothing to release */ +void __wrap_BIO_ADDRINFO_free(BIO_ADDRINFO *bai) +{ + (void)bai; +} + +int __wrap_BIO_accept_ex(int accept_sock, BIO_ADDR *addr, int options) +{ + function_called(); + check_expected(accept_sock); + check_expected(options); + (void)addr; + return mock_type(int); +} + +int __wrap_BIO_sock_should_retry(int i) +{ + function_called(); + check_expected(i); + return mock_type(int); +} + +char *__wrap_BIO_ADDR_hostname_string(const BIO_ADDR *ap, int numeric) +{ + function_called(); + check_expected(numeric); + (void)ap; + return mock_ptr_type(char *); +} + +char *__wrap_BIO_ADDR_service_string(const BIO_ADDR *ap, int numeric) +{ + function_called(); + check_expected(numeric); + (void)ap; + return mock_ptr_type(char *); +} + +/* expectations */ + +static void expect_getsockopt(int fd, int rc, int value) +{ + expect_function_call(__wrap_getsockopt); + expect_value(__wrap_getsockopt, fd, fd); + expect_value(__wrap_getsockopt, level, SOL_SOCKET); + expect_value(__wrap_getsockopt, optname, SO_ERROR); + will_return(__wrap_getsockopt, rc); + will_return(__wrap_getsockopt, value); +} + +#if defined(TCP_NODELAY) && (defined(IPPROTO_TCP) || defined(SOL_TCP)) +static void expect_setsockopt(int fd, int on, int rc) +{ + expect_function_call(__wrap_setsockopt); + expect_value(__wrap_setsockopt, fd, fd); + expect_value(__wrap_setsockopt, level, TEST_TCP_LEVEL); + expect_value(__wrap_setsockopt, optname, TCP_NODELAY); + expect_value(__wrap_setsockopt, on, on); + will_return(__wrap_setsockopt, rc); +} +#endif + +static void expect_getsockname(int fd, int rc, socklen_t outlen) +{ + expect_function_call(__wrap_getsockname); + expect_value(__wrap_getsockname, fd, fd); + will_return(__wrap_getsockname, rc); + if (rc == 0) + will_return(__wrap_getsockname, (int)outlen); +} + +#ifdef FIONBIO +static void expect_ioctl(int fd, unsigned long request, int rc) +{ + expect_function_call(__wrap_ioctl); + expect_value(__wrap_ioctl, fd, fd); + expect_value(__wrap_ioctl, request, request); + will_return(__wrap_ioctl, rc); +} +#endif + +static void expect_poll(int fd, int events, int rc) +{ + expect_function_call(__wrap_poll); + expect_value(__wrap_poll, pfd, fd); + expect_value(__wrap_poll, events, events); + will_return(__wrap_poll, rc); +} + +/* setup */ + +/* BIO_sock_init / bio_sock_cleanup_int */ + +static void test_sock_init(void **state) +{ + (void)state; + assert_int_equal(BIO_sock_init(), 1); +} + +static void test_sock_cleanup(void **state) +{ + (void)state; + /* no-op on non-Windows; exercised for coverage */ + bio_sock_cleanup_int(); +} + +/* BIO_sock_error */ + +static void test_sock_error_value(void **state) +{ + /* getsockopt succeeds: SO_ERROR value is returned verbatim */ + (void)state; + expect_getsockopt(FAKE_SOCKET, 0, ECONNREFUSED); + assert_int_equal(BIO_sock_error(FAKE_SOCKET), ECONNREFUSED); +} + +static void test_sock_error_getsockopt_fails(void **state) +{ + /* getsockopt fails: the last socket error (errno) is returned */ + (void)state; + expect_getsockopt(FAKE_SOCKET, -1, EBADF); + assert_int_equal(BIO_sock_error(FAKE_SOCKET), EBADF); +} + +/* BIO_socket_ioctl */ + +#ifdef FIONBIO +static void test_socket_ioctl_success(void **state) +{ + int arg = 1; + + (void)state; + expect_ioctl(FAKE_SOCKET, FIONBIO, 0); + assert_int_equal(BIO_socket_ioctl(FAKE_SOCKET, FIONBIO, &arg), 0); +} + +static void test_socket_ioctl_error(void **state) +{ + int arg = 1; + + (void)state; + expect_ioctl(FAKE_SOCKET, FIONBIO, -1); + assert_int_equal(BIO_socket_ioctl(FAKE_SOCKET, FIONBIO, &arg), -1); +} +#endif + +/* BIO_set_tcp_ndelay */ + +#if defined(TCP_NODELAY) && (defined(IPPROTO_TCP) || defined(SOL_TCP)) +static void test_set_tcp_ndelay_on(void **state) +{ + (void)state; + expect_setsockopt(FAKE_SOCKET, 1, 0); + assert_int_equal(BIO_set_tcp_ndelay(FAKE_SOCKET, 1), 1); +} + +static void test_set_tcp_ndelay_off(void **state) +{ + (void)state; + expect_setsockopt(FAKE_SOCKET, 0, 0); + assert_int_equal(BIO_set_tcp_ndelay(FAKE_SOCKET, 0), 1); +} + +static void test_set_tcp_ndelay_fails(void **state) +{ + (void)state; + expect_setsockopt(FAKE_SOCKET, 1, -1); + assert_int_equal(BIO_set_tcp_ndelay(FAKE_SOCKET, 1), 0); +} +#endif + +/* BIO_socket_nbio */ + +#ifdef FIONBIO +static void test_socket_nbio_enable(void **state) +{ + (void)state; + expect_ioctl(FAKE_SOCKET, FIONBIO, 0); + assert_int_equal(BIO_socket_nbio(FAKE_SOCKET, 1), 1); +} + +static void test_socket_nbio_disable(void **state) +{ + (void)state; + expect_ioctl(FAKE_SOCKET, FIONBIO, 0); + assert_int_equal(BIO_socket_nbio(FAKE_SOCKET, 0), 1); +} + +static void test_socket_nbio_fails(void **state) +{ + (void)state; + expect_ioctl(FAKE_SOCKET, FIONBIO, -1); + assert_int_equal(BIO_socket_nbio(FAKE_SOCKET, 1), 0); +} +#endif + +/* BIO_sock_info */ + +static void test_sock_info_success(void **state) +{ + union BIO_sock_info_u info; + BIO_ADDR addr; + + (void)state; + memset(&addr, 0, sizeof(addr)); + info.addr = &addr; + expect_getsockname(FAKE_SOCKET, 0, (socklen_t)sizeof(struct sockaddr_in)); + assert_int_equal( + BIO_sock_info(FAKE_SOCKET, BIO_SOCK_INFO_ADDRESS, &info), 1); +} + +static void test_sock_info_getsockname_fails(void **state) +{ + union BIO_sock_info_u info; + BIO_ADDR addr; + + (void)state; + memset(&addr, 0, sizeof(addr)); + info.addr = &addr; + expect_getsockname(FAKE_SOCKET, -1, 0); + assert_int_equal( + BIO_sock_info(FAKE_SOCKET, BIO_SOCK_INFO_ADDRESS, &info), 0); +} + +static void test_sock_info_truncated(void **state) +{ + /* getsockname reports an address larger than the BIO_ADDR storage */ + union BIO_sock_info_u info; + BIO_ADDR addr; + + (void)state; + memset(&addr, 0, sizeof(addr)); + info.addr = &addr; + expect_getsockname(FAKE_SOCKET, 0, (socklen_t)(sizeof(BIO_ADDR) + 1)); + assert_int_equal( + BIO_sock_info(FAKE_SOCKET, BIO_SOCK_INFO_ADDRESS, &info), 0); +} + +static void test_sock_info_unknown_type(void **state) +{ + union BIO_sock_info_u info; + BIO_ADDR addr; + + (void)state; + memset(&addr, 0, sizeof(addr)); + info.addr = &addr; + assert_int_equal( + BIO_sock_info(FAKE_SOCKET, (enum BIO_sock_info_type)999, &info), 0); +} + +/* BIO_socket_wait */ + +static void test_socket_wait_immediate(void **state) +{ + /* max_time == 0 returns immediately without polling */ + (void)state; + assert_int_equal(BIO_socket_wait(FAKE_SOCKET, 1, 0), 1); +} + +static void test_socket_wait_bad_fd(void **state) +{ + (void)state; + assert_int_equal(BIO_socket_wait(-1, 1, time(NULL) + 100), -1); +} + +static void test_socket_wait_past(void **state) +{ + /* deadline already elapsed: timeout without polling */ + (void)state; + assert_int_equal(BIO_socket_wait(FAKE_SOCKET, 1, (time_t)1), 0); +} + +static void test_socket_wait_read_ready(void **state) +{ + (void)state; + expect_poll(FAKE_SOCKET, POLLIN, 1); + assert_int_equal(BIO_socket_wait(FAKE_SOCKET, 1, time(NULL) + 100), 1); +} + +static void test_socket_wait_write_ready(void **state) +{ + (void)state; + expect_poll(FAKE_SOCKET, POLLOUT, 1); + assert_int_equal(BIO_socket_wait(FAKE_SOCKET, 0, time(NULL) + 100), 1); +} + +static void test_socket_wait_timeout(void **state) +{ + (void)state; + expect_poll(FAKE_SOCKET, POLLIN, 0); + assert_int_equal(BIO_socket_wait(FAKE_SOCKET, 1, time(NULL) + 100), 0); +} + +static void test_socket_wait_error(void **state) +{ + (void)state; + expect_poll(FAKE_SOCKET, POLLIN, -1); + assert_int_equal(BIO_socket_wait(FAKE_SOCKET, 1, time(NULL) + 100), -1); +} + +/* main */ + +#define SOCK_TEST(name) cmocka_unit_test(name) + +int main(void) +{ + const struct CMUnitTest tests[] = { + /* BIO_sock_init / cleanup */ + SOCK_TEST(test_sock_init), + SOCK_TEST(test_sock_cleanup), + /* BIO_sock_error */ + SOCK_TEST(test_sock_error_value), + SOCK_TEST(test_sock_error_getsockopt_fails), +#ifdef FIONBIO + /* BIO_socket_ioctl */ + SOCK_TEST(test_socket_ioctl_success), + SOCK_TEST(test_socket_ioctl_error), +#endif + /* BIO_set_tcp_ndelay */ +#if defined(TCP_NODELAY) && (defined(IPPROTO_TCP) || defined(SOL_TCP)) + SOCK_TEST(test_set_tcp_ndelay_on), + SOCK_TEST(test_set_tcp_ndelay_off), + SOCK_TEST(test_set_tcp_ndelay_fails), +#endif + /* BIO_socket_nbio */ +#ifdef FIONBIO + SOCK_TEST(test_socket_nbio_enable), + SOCK_TEST(test_socket_nbio_disable), + SOCK_TEST(test_socket_nbio_fails), +#endif + /* BIO_sock_info */ + SOCK_TEST(test_sock_info_success), + SOCK_TEST(test_sock_info_getsockname_fails), + SOCK_TEST(test_sock_info_truncated), + SOCK_TEST(test_sock_info_unknown_type), + /* BIO_socket_wait */ + SOCK_TEST(test_socket_wait_immediate), + SOCK_TEST(test_socket_wait_bad_fd), + SOCK_TEST(test_socket_wait_past), + SOCK_TEST(test_socket_wait_read_ready), + SOCK_TEST(test_socket_wait_write_ready), + SOCK_TEST(test_socket_wait_timeout), + SOCK_TEST(test_socket_wait_error), + }; + + cmocka_set_message_output(CM_OUTPUT_TAP); + + return cmocka_run_group_tests(tests, NULL, NULL); +} + +#else + +int main(void) +{ + return 0; +} + +#endif /* OPENSSL_NO_SOCK */ From 234845aaabbcfef5f58a00835f0383ceabe469b5 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Fri, 12 Jun 2026 13:45:25 +0200 Subject: [PATCH 208/349] Add BIO bio_sock2 test Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Richard Levitte Reviewed-by: Tomas Mraz MergeDate: Thu Jul 9 17:39:25 2026 (Merged from https://github.com/openssl/openssl/pull/30788) --- test/unit/build.info | 7 + test/unit/crypto/bio/test_bio_sock2.c | 786 ++++++++++++++++++++++++++ 2 files changed, 793 insertions(+) create mode 100644 test/unit/crypto/bio/test_bio_sock2.c diff --git a/test/unit/build.info b/test/unit/build.info index f215b70ec0..bc0465dc13 100644 --- a/test/unit/build.info +++ b/test/unit/build.info @@ -15,6 +15,13 @@ WRAP[crypto/bio/test_bio_sock]=getsockopt setsockopt getsockname ioctl poll \ BIO_ADDRINFO_free BIO_accept_ex BIO_sock_should_retry \ BIO_ADDR_hostname_string BIO_ADDR_service_string +PROGRAMS{noinst}=crypto/bio/test_bio_sock2 +SOURCE[crypto/bio/test_bio_sock2]=crypto/bio/test_bio_sock2.c +INCLUDE[crypto/bio/test_bio_sock2]=../../include ../../crypto/bio +DEPEND[crypto/bio/test_bio_sock2]=../../libcrypto.a +WRAP[crypto/bio/test_bio_sock2]=socket connect bind listen accept close \ + getsockopt setsockopt BIO_socket_nbio BIO_sock_should_retry + PROGRAMS{noinst}=crypto/bio/test_bss_acpt SOURCE[crypto/bio/test_bss_acpt]=crypto/bio/test_bss_acpt.c WRAP[crypto/bio/test_bss_acpt]=BIO_lookup BIO_socket BIO_listen \ diff --git a/test/unit/crypto/bio/test_bio_sock2.c b/test/unit/crypto/bio/test_bio_sock2.c new file mode 100644 index 0000000000..96f63e59cf --- /dev/null +++ b/test/unit/crypto/bio/test_bio_sock2.c @@ -0,0 +1,786 @@ +/* + * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "internal/sockets.h" + +#ifndef OPENSSL_NO_SOCK + +#include +#include +#include +#include + +#include "bio_local.h" +#include "internal/bio_tfo.h" +#include + +#define FAKE_SOCKET 42 +#define ACCEPTED_SOCKET 7 + +/* prototypes for __wrap_* (required by -Wmissing-prototypes) */ +int __wrap_socket(int domain, int type, int protocol); +int __wrap_connect(int fd, const struct sockaddr *addr, socklen_t addrlen); +int __wrap_bind(int fd, const struct sockaddr *addr, socklen_t addrlen); +int __wrap_listen(int fd, int backlog); +int __wrap_accept(int fd, struct sockaddr *addr, socklen_t *addrlen); +int __wrap_close(int fd); +int __wrap_getsockopt(int fd, int level, int optname, void *optval, + socklen_t *optlen); +int __wrap_setsockopt(int fd, int level, int optname, const void *optval, + socklen_t optlen); +int __wrap_BIO_socket_nbio(int fd, int mode); +int __wrap_BIO_sock_should_retry(int i); + +/* wraps */ + +int __wrap_socket(int domain, int type, int protocol) +{ + function_called(); + check_expected(domain); + check_expected(type); + check_expected(protocol); + return mock_type(int); +} + +int __wrap_connect(int fd, const struct sockaddr *addr, socklen_t addrlen) +{ + function_called(); + check_expected(fd); + (void)addr; + (void)addrlen; + return mock_type(int); +} + +int __wrap_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) +{ + function_called(); + check_expected(fd); + (void)addr; + (void)addrlen; + return mock_type(int); +} + +int __wrap_listen(int fd, int backlog) +{ + function_called(); + check_expected(fd); + (void)backlog; + return mock_type(int); +} + +int __wrap_accept(int fd, struct sockaddr *addr, socklen_t *addrlen) +{ + function_called(); + check_expected(fd); + (void)addr; + (void)addrlen; + return mock_type(int); +} + +int __wrap_close(int fd) +{ + function_called(); + check_expected(fd); + return mock_type(int); +} + +int __wrap_getsockopt(int fd, int level, int optname, void *optval, + socklen_t *optlen) +{ + int rc; + + function_called(); + check_expected(fd); + check_expected(level); + check_expected(optname); + rc = mock_type(int); + if (rc == 0) { + if (optval != NULL) + *(int *)optval = mock_type(int); + if (optlen != NULL) + *optlen = (socklen_t)mock_type(int); + } + return rc; +} + +int __wrap_setsockopt(int fd, int level, int optname, const void *optval, + socklen_t optlen) +{ + int on = (optval != NULL) ? *(const int *)optval : 0; + + function_called(); + check_expected(fd); + check_expected(level); + check_expected(optname); + check_expected(on); + (void)optlen; + return mock_type(int); +} + +int __wrap_BIO_socket_nbio(int fd, int mode) +{ + function_called(); + check_expected(fd); + check_expected(mode); + return mock_type(int); +} + +int __wrap_BIO_sock_should_retry(int i) +{ + function_called(); + check_expected(i); + return mock_type(int); +} + +/* expectations */ + +static void expect_socket(int domain, int type, int protocol, int rc) +{ + expect_function_call(__wrap_socket); + expect_value(__wrap_socket, domain, domain); + expect_value(__wrap_socket, type, type); + expect_value(__wrap_socket, protocol, protocol); + will_return(__wrap_socket, rc); +} + +static void expect_connect(int fd, int rc) +{ + expect_function_call(__wrap_connect); + expect_value(__wrap_connect, fd, fd); + will_return(__wrap_connect, rc); +} + +static void expect_bind(int fd, int rc) +{ + expect_function_call(__wrap_bind); + expect_value(__wrap_bind, fd, fd); + will_return(__wrap_bind, rc); +} + +static void expect_listen(int fd, int rc) +{ + expect_function_call(__wrap_listen); + expect_value(__wrap_listen, fd, fd); + will_return(__wrap_listen, rc); +} + +static void expect_accept(int fd, int rc) +{ + expect_function_call(__wrap_accept); + expect_value(__wrap_accept, fd, fd); + will_return(__wrap_accept, rc); +} + +static void expect_close(int fd, int rc) +{ + expect_function_call(__wrap_close); + expect_value(__wrap_close, fd, fd); + will_return(__wrap_close, rc); +} + +static void expect_getsockopt(int fd, int level, int optname, int rc, + int value, socklen_t outlen) +{ + expect_function_call(__wrap_getsockopt); + expect_value(__wrap_getsockopt, fd, fd); + expect_value(__wrap_getsockopt, level, level); + expect_value(__wrap_getsockopt, optname, optname); + will_return(__wrap_getsockopt, rc); + if (rc == 0) { + will_return(__wrap_getsockopt, value); + will_return(__wrap_getsockopt, (int)outlen); + } +} + +static void expect_setsockopt(int fd, int level, int optname, int on, int rc) +{ + expect_function_call(__wrap_setsockopt); + expect_value(__wrap_setsockopt, fd, fd); + expect_value(__wrap_setsockopt, level, level); + expect_value(__wrap_setsockopt, optname, optname); + expect_value(__wrap_setsockopt, on, on); + will_return(__wrap_setsockopt, rc); +} + +static void expect_nbio(int fd, int mode, int rc) +{ + expect_function_call(__wrap_BIO_socket_nbio); + expect_value(__wrap_BIO_socket_nbio, fd, fd); + expect_value(__wrap_BIO_socket_nbio, mode, mode); + will_return(__wrap_BIO_socket_nbio, rc); +} + +static void expect_should_retry(int i, int rc) +{ + expect_function_call(__wrap_BIO_sock_should_retry); + expect_value(__wrap_BIO_sock_should_retry, i, i); + will_return(__wrap_BIO_sock_should_retry, rc); +} + +/* helpers */ + +static void make_addr(BIO_ADDR *a, int family) +{ + memset(a, 0, sizeof(*a)); + a->sa.sa_family = family; +} + +/* BIO_socket */ + +static void test_socket_success(void **state) +{ + (void)state; + expect_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP, FAKE_SOCKET); + assert_int_equal( + BIO_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0), FAKE_SOCKET); +} + +static void test_socket_fails(void **state) +{ + (void)state; + expect_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP, -1); + assert_int_equal( + BIO_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0), (int)INVALID_SOCKET); +} + +/* BIO_connect */ + +static void test_connect_invalid_sock(void **state) +{ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + assert_int_equal(BIO_connect(-1, &a, 0), 0); +} + +static void test_connect_nbio_fails(void **state) +{ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + expect_nbio(FAKE_SOCKET, 0, 0); + assert_int_equal(BIO_connect(FAKE_SOCKET, &a, 0), 0); +} + +static void test_connect_success(void **state) +{ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + expect_nbio(FAKE_SOCKET, 0, 1); + expect_connect(FAKE_SOCKET, 0); + assert_int_equal(BIO_connect(FAKE_SOCKET, &a, 0), 1); +} + +static void test_connect_nonblock(void **state) +{ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + expect_nbio(FAKE_SOCKET, 1, 1); + expect_connect(FAKE_SOCKET, 0); + assert_int_equal(BIO_connect(FAKE_SOCKET, &a, BIO_SOCK_NONBLOCK), 1); +} + +static void test_connect_keepalive(void **state) +{ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + expect_nbio(FAKE_SOCKET, 0, 1); + expect_setsockopt(FAKE_SOCKET, SOL_SOCKET, SO_KEEPALIVE, 1, 0); + expect_connect(FAKE_SOCKET, 0); + assert_int_equal(BIO_connect(FAKE_SOCKET, &a, BIO_SOCK_KEEPALIVE), 1); +} + +static void test_connect_keepalive_fails(void **state) +{ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + expect_nbio(FAKE_SOCKET, 0, 1); + expect_setsockopt(FAKE_SOCKET, SOL_SOCKET, SO_KEEPALIVE, 1, -1); + assert_int_equal(BIO_connect(FAKE_SOCKET, &a, BIO_SOCK_KEEPALIVE), 0); +} + +static void test_connect_nodelay(void **state) +{ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + expect_nbio(FAKE_SOCKET, 0, 1); + expect_setsockopt(FAKE_SOCKET, IPPROTO_TCP, TCP_NODELAY, 1, 0); + expect_connect(FAKE_SOCKET, 0); + assert_int_equal(BIO_connect(FAKE_SOCKET, &a, BIO_SOCK_NODELAY), 1); +} + +static void test_connect_nodelay_fails(void **state) +{ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + expect_nbio(FAKE_SOCKET, 0, 1); + expect_setsockopt(FAKE_SOCKET, IPPROTO_TCP, TCP_NODELAY, 1, -1); + assert_int_equal(BIO_connect(FAKE_SOCKET, &a, BIO_SOCK_NODELAY), 0); +} + +static void test_connect_fails_retry(void **state) +{ + /* connect() failing retryably returns 0 without raising a fatal error */ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + expect_nbio(FAKE_SOCKET, 0, 1); + expect_connect(FAKE_SOCKET, -1); + expect_should_retry(-1, 1); + assert_int_equal(BIO_connect(FAKE_SOCKET, &a, 0), 0); +} + +static void test_connect_fails_error(void **state) +{ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + expect_nbio(FAKE_SOCKET, 0, 1); + expect_connect(FAKE_SOCKET, -1); + expect_should_retry(-1, 0); + assert_int_equal(BIO_connect(FAKE_SOCKET, &a, 0), 0); +} + +/* BIO_bind */ + +static void test_bind_invalid_sock(void **state) +{ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + assert_int_equal(BIO_bind(-1, &a, 0), 0); +} + +static void test_bind_success(void **state) +{ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + expect_bind(FAKE_SOCKET, 0); + assert_int_equal(BIO_bind(FAKE_SOCKET, &a, 0), 1); +} + +static void test_bind_reuseaddr(void **state) +{ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + expect_setsockopt(FAKE_SOCKET, SOL_SOCKET, SO_REUSEADDR, 1, 0); + expect_bind(FAKE_SOCKET, 0); + assert_int_equal(BIO_bind(FAKE_SOCKET, &a, BIO_SOCK_REUSEADDR), 1); +} + +static void test_bind_reuseaddr_fails(void **state) +{ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + expect_setsockopt(FAKE_SOCKET, SOL_SOCKET, SO_REUSEADDR, 1, -1); + assert_int_equal(BIO_bind(FAKE_SOCKET, &a, BIO_SOCK_REUSEADDR), 0); +} + +static void test_bind_bind_fails(void **state) +{ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + expect_bind(FAKE_SOCKET, -1); + assert_int_equal(BIO_bind(FAKE_SOCKET, &a, 0), 0); +} + +/* BIO_listen */ + +static void test_listen_success(void **state) +{ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_STREAM, + sizeof(int)); + expect_nbio(FAKE_SOCKET, 0, 1); + expect_bind(FAKE_SOCKET, 0); + expect_listen(FAKE_SOCKET, 0); + assert_int_equal(BIO_listen(FAKE_SOCKET, &a, 0), 1); +} + +static void test_listen_invalid_sock(void **state) +{ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + assert_int_equal(BIO_listen(-1, &a, 0), 0); +} + +static void test_listen_getsockopt_fails(void **state) +{ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, -1, 0, 0); + assert_int_equal(BIO_listen(FAKE_SOCKET, &a, 0), 0); +} + +static void test_listen_socktype_len_mismatch(void **state) +{ + /* a short socktype_len is rejected even when getsockopt succeeds */ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_STREAM, + (socklen_t)(sizeof(int) - 1)); + assert_int_equal(BIO_listen(FAKE_SOCKET, &a, 0), 0); +} + +static void test_listen_nbio_fails(void **state) +{ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_STREAM, + sizeof(int)); + expect_nbio(FAKE_SOCKET, 0, 0); + assert_int_equal(BIO_listen(FAKE_SOCKET, &a, 0), 0); +} + +static void test_listen_keepalive(void **state) +{ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_STREAM, + sizeof(int)); + expect_nbio(FAKE_SOCKET, 0, 1); + expect_setsockopt(FAKE_SOCKET, SOL_SOCKET, SO_KEEPALIVE, 1, 0); + expect_bind(FAKE_SOCKET, 0); + expect_listen(FAKE_SOCKET, 0); + assert_int_equal(BIO_listen(FAKE_SOCKET, &a, BIO_SOCK_KEEPALIVE), 1); +} + +static void test_listen_keepalive_fails(void **state) +{ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_STREAM, + sizeof(int)); + expect_nbio(FAKE_SOCKET, 0, 1); + expect_setsockopt(FAKE_SOCKET, SOL_SOCKET, SO_KEEPALIVE, 1, -1); + assert_int_equal(BIO_listen(FAKE_SOCKET, &a, BIO_SOCK_KEEPALIVE), 0); +} + +static void test_listen_nodelay(void **state) +{ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_STREAM, + sizeof(int)); + expect_nbio(FAKE_SOCKET, 0, 1); + expect_setsockopt(FAKE_SOCKET, IPPROTO_TCP, TCP_NODELAY, 1, 0); + expect_bind(FAKE_SOCKET, 0); + expect_listen(FAKE_SOCKET, 0); + assert_int_equal(BIO_listen(FAKE_SOCKET, &a, BIO_SOCK_NODELAY), 1); +} + +static void test_listen_reuseaddr(void **state) +{ + /* REUSEADDR is honoured inside the real (unwrappable) BIO_bind */ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_STREAM, + sizeof(int)); + expect_nbio(FAKE_SOCKET, 0, 1); + expect_setsockopt(FAKE_SOCKET, SOL_SOCKET, SO_REUSEADDR, 1, 0); + expect_bind(FAKE_SOCKET, 0); + expect_listen(FAKE_SOCKET, 0); + assert_int_equal(BIO_listen(FAKE_SOCKET, &a, BIO_SOCK_REUSEADDR), 1); +} + +static void test_listen_bind_fails(void **state) +{ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_STREAM, + sizeof(int)); + expect_nbio(FAKE_SOCKET, 0, 1); + expect_bind(FAKE_SOCKET, -1); + assert_int_equal(BIO_listen(FAKE_SOCKET, &a, 0), 0); +} + +static void test_listen_dgram(void **state) +{ + /* datagram sockets are bound but never put into listen() */ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_DGRAM, + sizeof(int)); + expect_nbio(FAKE_SOCKET, 0, 1); + expect_bind(FAKE_SOCKET, 0); + assert_int_equal(BIO_listen(FAKE_SOCKET, &a, 0), 1); +} + +static void test_listen_listen_fails(void **state) +{ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET); + expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_STREAM, + sizeof(int)); + expect_nbio(FAKE_SOCKET, 0, 1); + expect_bind(FAKE_SOCKET, 0); + expect_listen(FAKE_SOCKET, -1); + assert_int_equal(BIO_listen(FAKE_SOCKET, &a, 0), 0); +} + +#if defined(IPV6_V6ONLY) && !defined(__OpenBSD__) +static void test_listen_v6only(void **state) +{ + /* an AF_INET6 socket always gets IPV6_V6ONLY set, here to 1 */ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET6); + expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_STREAM, + sizeof(int)); + expect_nbio(FAKE_SOCKET, 0, 1); + expect_setsockopt(FAKE_SOCKET, IPPROTO_IPV6, IPV6_V6ONLY, 1, 0); + expect_bind(FAKE_SOCKET, 0); + expect_listen(FAKE_SOCKET, 0); + assert_int_equal(BIO_listen(FAKE_SOCKET, &a, BIO_SOCK_V6_ONLY), 1); +} + +static void test_listen_v6only_off(void **state) +{ + /* without BIO_SOCK_V6_ONLY the option is still set, to 0 */ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET6); + expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_STREAM, + sizeof(int)); + expect_nbio(FAKE_SOCKET, 0, 1); + expect_setsockopt(FAKE_SOCKET, IPPROTO_IPV6, IPV6_V6ONLY, 0, 0); + expect_bind(FAKE_SOCKET, 0); + expect_listen(FAKE_SOCKET, 0); + assert_int_equal(BIO_listen(FAKE_SOCKET, &a, 0), 1); +} + +static void test_listen_v6only_fails(void **state) +{ + BIO_ADDR a; + + (void)state; + make_addr(&a, AF_INET6); + expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_STREAM, + sizeof(int)); + expect_nbio(FAKE_SOCKET, 0, 1); + expect_setsockopt(FAKE_SOCKET, IPPROTO_IPV6, IPV6_V6ONLY, 1, -1); + assert_int_equal(BIO_listen(FAKE_SOCKET, &a, BIO_SOCK_V6_ONLY), 0); +} +#endif + +/* BIO_accept_ex */ + +static void test_accept_ex_success(void **state) +{ + BIO_ADDR a; + + (void)state; + memset(&a, 0, sizeof(a)); + expect_accept(FAKE_SOCKET, ACCEPTED_SOCKET); + expect_nbio(ACCEPTED_SOCKET, 0, 1); + assert_int_equal(BIO_accept_ex(FAKE_SOCKET, &a, 0), ACCEPTED_SOCKET); +} + +static void test_accept_ex_null_addr(void **state) +{ + /* a NULL addr is accepted into an internal local BIO_ADDR */ + (void)state; + expect_accept(FAKE_SOCKET, ACCEPTED_SOCKET); + expect_nbio(ACCEPTED_SOCKET, 0, 1); + assert_int_equal(BIO_accept_ex(FAKE_SOCKET, NULL, 0), ACCEPTED_SOCKET); +} + +static void test_accept_ex_nonblock(void **state) +{ + BIO_ADDR a; + + (void)state; + memset(&a, 0, sizeof(a)); + expect_accept(FAKE_SOCKET, ACCEPTED_SOCKET); + expect_nbio(ACCEPTED_SOCKET, 1, 1); + assert_int_equal( + BIO_accept_ex(FAKE_SOCKET, &a, BIO_SOCK_NONBLOCK), ACCEPTED_SOCKET); +} + +static void test_accept_ex_retry(void **state) +{ + BIO_ADDR a; + + (void)state; + memset(&a, 0, sizeof(a)); + expect_accept(FAKE_SOCKET, -1); + expect_should_retry(-1, 1); + assert_int_equal(BIO_accept_ex(FAKE_SOCKET, &a, 0), (int)INVALID_SOCKET); +} + +static void test_accept_ex_error(void **state) +{ + BIO_ADDR a; + + (void)state; + memset(&a, 0, sizeof(a)); + expect_accept(FAKE_SOCKET, -1); + expect_should_retry(-1, 0); + assert_int_equal(BIO_accept_ex(FAKE_SOCKET, &a, 0), (int)INVALID_SOCKET); +} + +static void test_accept_ex_nbio_fails(void **state) +{ + /* a non-blocking failure on the accepted socket closes it again */ + BIO_ADDR a; + + (void)state; + memset(&a, 0, sizeof(a)); + expect_accept(FAKE_SOCKET, ACCEPTED_SOCKET); + expect_nbio(ACCEPTED_SOCKET, 0, 0); + expect_close(ACCEPTED_SOCKET, 0); + assert_int_equal(BIO_accept_ex(FAKE_SOCKET, &a, 0), (int)INVALID_SOCKET); +} + +/* BIO_closesocket */ + +static void test_closesocket_success(void **state) +{ + (void)state; + expect_close(FAKE_SOCKET, 0); + assert_int_equal(BIO_closesocket(FAKE_SOCKET), 1); +} + +static void test_closesocket_negative(void **state) +{ + /* a negative fd short-circuits before any close() */ + (void)state; + assert_int_equal(BIO_closesocket(-1), 0); +} + +static void test_closesocket_fails(void **state) +{ + (void)state; + expect_close(FAKE_SOCKET, -1); + assert_int_equal(BIO_closesocket(FAKE_SOCKET), 0); +} + +/* main */ + +#define SOCK_TEST(name) cmocka_unit_test(name) + +int main(void) +{ + const struct CMUnitTest tests[] = { + /* BIO_socket */ + SOCK_TEST(test_socket_success), + SOCK_TEST(test_socket_fails), + /* BIO_connect */ + SOCK_TEST(test_connect_invalid_sock), + SOCK_TEST(test_connect_nbio_fails), + SOCK_TEST(test_connect_success), + SOCK_TEST(test_connect_nonblock), + SOCK_TEST(test_connect_keepalive), + SOCK_TEST(test_connect_keepalive_fails), + SOCK_TEST(test_connect_nodelay), + SOCK_TEST(test_connect_nodelay_fails), + SOCK_TEST(test_connect_fails_retry), + SOCK_TEST(test_connect_fails_error), + /* BIO_bind */ + SOCK_TEST(test_bind_invalid_sock), + SOCK_TEST(test_bind_success), + SOCK_TEST(test_bind_reuseaddr), + SOCK_TEST(test_bind_reuseaddr_fails), + SOCK_TEST(test_bind_bind_fails), + /* BIO_listen */ + SOCK_TEST(test_listen_success), + SOCK_TEST(test_listen_invalid_sock), + SOCK_TEST(test_listen_getsockopt_fails), + SOCK_TEST(test_listen_socktype_len_mismatch), + SOCK_TEST(test_listen_nbio_fails), + SOCK_TEST(test_listen_keepalive), + SOCK_TEST(test_listen_keepalive_fails), + SOCK_TEST(test_listen_nodelay), + SOCK_TEST(test_listen_reuseaddr), + SOCK_TEST(test_listen_bind_fails), + SOCK_TEST(test_listen_dgram), + SOCK_TEST(test_listen_listen_fails), +#if defined(IPV6_V6ONLY) && !defined(__OpenBSD__) + SOCK_TEST(test_listen_v6only), + SOCK_TEST(test_listen_v6only_off), + SOCK_TEST(test_listen_v6only_fails), +#endif + /* BIO_accept_ex */ + SOCK_TEST(test_accept_ex_success), + SOCK_TEST(test_accept_ex_null_addr), + SOCK_TEST(test_accept_ex_nonblock), + SOCK_TEST(test_accept_ex_retry), + SOCK_TEST(test_accept_ex_error), + SOCK_TEST(test_accept_ex_nbio_fails), + /* BIO_closesocket */ + SOCK_TEST(test_closesocket_success), + SOCK_TEST(test_closesocket_negative), + SOCK_TEST(test_closesocket_fails), + }; + + cmocka_set_message_output(CM_OUTPUT_TAP); + + return cmocka_run_group_tests(tests, NULL, NULL); +} + +#else + +int main(void) +{ + return 0; +} + +#endif /* OPENSSL_NO_SOCK */ From d172868e5337563ecc0431e52ccb612951a6c576 Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Sun, 26 Apr 2026 04:05:02 +1000 Subject: [PATCH 209/349] EC: make the group the single source of the point conversion form The point conversion form (compressed, uncompressed, or hybrid) was kept both on the key and on the group, and the two could disagree -- a key imported as compressed could re-encode as uncompressed. The group is now the single source of truth: encoding, parameter output, and the legacy lookup all read it from the group, and decoding (PEM, DER, or raw parameters) records it there, so the form round-trips faithfully. Generated keys are always uncompressed; the point-format option at key generation is now a documented no-op (it had had no effect for several releases), and the unused form field on the keygen context is dropped. Imported keys still keep their form, and the deprecated EC_KEY_get_conv_form()/EC_KEY_set_conv_form() still work. EVP_PKEY_fromdata() and openssl pkey -text now report the form a loaded EC key actually has, and re-encoding via PEM or DER preserves it. Docs drop a stale note about a compile-time macro for compressed points on binary curves, and the EC tests now exercise both the affine and the compressed/hybrid binary formats unconditionally (fixing a latent bug in the compressed/hybrid form tests that were never exercised by CI). Reviewed-by: Neil Horman Reviewed-by: Matt Caswell MergeDate: Fri Jul 10 09:21:29 2026 (Merged from https://github.com/openssl/openssl/pull/30940) --- apps/ec.c | 2 +- apps/ecparam.c | 2 +- apps/pkey.c | 2 +- crypto/ec/ec_asn1.c | 6 +- crypto/ec/ec_backend.c | 1 - crypto/ec/ec_key.c | 12 ++-- crypto/ec/ec_kmeth.c | 1 - crypto/ec/ec_local.h | 1 - crypto/evp/p_lib.c | 9 +-- doc/man1/openssl-ec.pod.in | 3 - doc/man1/openssl-ecparam.pod.in | 3 - doc/man1/openssl-pkey.pod.in | 7 +-- doc/man7/EVP_PKEY-EC.pod | 12 ++++ providers/implementations/keymgmt/ec_kmgmt.c | 62 +++++++++++++------- test/ectest.c | 57 +++++------------- 15 files changed, 86 insertions(+), 94 deletions(-) diff --git a/apps/ec.c b/apps/ec.c index 588b488ac1..8ed452a153 100644 --- a/apps/ec.c +++ b/apps/ec.c @@ -54,7 +54,7 @@ const OPTIONS ec_options[] = { { "check", OPT_CHECK, '-', "check key consistency" }, { "", OPT_CIPHER, '-', "Any supported cipher" }, { "param_enc", OPT_PARAM_ENC, 's', - "Specifies the way the ec parameters are encoded" }, + "Selects between named_curve and explicit EC parameter encoding" }, { "conv_form", OPT_CONV_FORM, 's', "Specifies the point conversion form " }, OPT_SECTION("Output"), diff --git a/apps/ecparam.c b/apps/ecparam.c index cbb2ec8d50..aece2cb81d 100644 --- a/apps/ecparam.c +++ b/apps/ecparam.c @@ -57,7 +57,7 @@ const OPTIONS ecparam_options[] = { { "text", OPT_TEXT, '-', "Print the ec parameters in text form" }, { "noout", OPT_NOOUT, '-', "Do not print the ec parameter" }, { "param_enc", OPT_PARAM_ENC, 's', - "Specifies the way the ec parameters are encoded" }, + "Selects between named_curve and explicit EC parameter encoding" }, OPT_SECTION("Parameter"), { "check", OPT_CHECK, '-', "Validate the ec parameters" }, diff --git a/apps/pkey.c b/apps/pkey.c index 48b091c86d..868e411820 100644 --- a/apps/pkey.c +++ b/apps/pkey.c @@ -72,7 +72,7 @@ const OPTIONS pkey_options[] = { { "ec_conv_form", OPT_EC_CONV_FORM, 's', "Specifies the EC point conversion form in the encoding" }, { "ec_param_enc", OPT_EC_PARAM_ENC, 's', - "Specifies the way the EC parameters are encoded" }, + "Selects between named_curve and explicit EC parameter encoding" }, { NULL } }; diff --git a/crypto/ec/ec_asn1.c b/crypto/ec/ec_asn1.c index cdc1826631..fdc3ce94c7 100644 --- a/crypto/ec/ec_asn1.c +++ b/crypto/ec/ec_asn1.c @@ -1063,7 +1063,7 @@ int i2d_ECPrivateKey(const EC_KEY *a, unsigned char **out) goto err; } - publen = EC_KEY_key2buf(a, a->conv_form, &pub, NULL); + publen = EC_KEY_key2buf(a, EC_KEY_get_conv_form(a), &pub, NULL); if (publen == 0 || publen > INT_MAX) { ERR_raise(ERR_LIB_EC, ERR_R_EC_LIB); @@ -1164,7 +1164,7 @@ int i2o_ECPublicKey(const EC_KEY *a, unsigned char **out) } buf_len = EC_POINT_point2oct(a->group, a->pub_key, - a->conv_form, NULL, 0, NULL); + EC_KEY_get_conv_form(a), NULL, 0, NULL); if (buf_len > INT_MAX) { ERR_raise(ERR_LIB_EC, ERR_R_PASSED_INVALID_ARGUMENT); @@ -1179,7 +1179,7 @@ int i2o_ECPublicKey(const EC_KEY *a, unsigned char **out) return 0; new_buffer = 1; } - if (!EC_POINT_point2oct(a->group, a->pub_key, a->conv_form, + if (!EC_POINT_point2oct(a->group, a->pub_key, EC_KEY_get_conv_form(a), *out, buf_len, NULL)) { ERR_raise(ERR_LIB_EC, ERR_R_EC_LIB); if (new_buffer) { diff --git a/crypto/ec/ec_backend.c b/crypto/ec/ec_backend.c index 0d1de52dbb..764ab7558b 100644 --- a/crypto/ec/ec_backend.c +++ b/crypto/ec/ec_backend.c @@ -655,7 +655,6 @@ EC_KEY *ossl_ec_key_dup(const EC_KEY *src, int selection) /* copy the rest */ if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0) { ret->enc_flag = src->enc_flag; - ret->conv_form = src->conv_form; } ret->version = src->version; diff --git a/crypto/ec/ec_key.c b/crypto/ec/ec_key.c index 8deaa3b930..f5175653fa 100644 --- a/crypto/ec/ec_key.c +++ b/crypto/ec/ec_key.c @@ -146,7 +146,6 @@ EC_KEY *EC_KEY_copy(EC_KEY *dest, const EC_KEY *src) /* copy the rest */ dest->enc_flag = src->enc_flag; - dest->conv_form = src->conv_form; dest->version = src->version; dest->flags = src->flags; #ifndef FIPS_MODULE @@ -883,12 +882,13 @@ void EC_KEY_set_enc_flags(EC_KEY *key, unsigned int flags) point_conversion_form_t EC_KEY_get_conv_form(const EC_KEY *key) { - return key->conv_form; + return key->group != NULL + ? EC_GROUP_get_point_conversion_form(key->group) + : POINT_CONVERSION_UNCOMPRESSED; } void EC_KEY_set_conv_form(EC_KEY *key, point_conversion_form_t cform) { - key->conv_form = cform; if (key->group != NULL) EC_GROUP_set_point_conversion_form(key->group, cform); } @@ -959,8 +959,10 @@ int EC_KEY_oct2key(EC_KEY *key, const unsigned char *buf, size_t len, * EC_POINT_oct2point() has already performed sanity checking of * the buffer so we know it is valid. */ - if ((key->group->meth->flags & EC_FLAGS_CUSTOM_CURVE) == 0) - key->conv_form = (point_conversion_form_t)(buf[0] & ~0x01); + if ((key->group->meth->flags & EC_FLAGS_CUSTOM_CURVE) == 0) { + EC_GROUP_set_point_conversion_form(key->group, + (point_conversion_form_t)(buf[0] & ~0x01)); + } return 1; } diff --git a/crypto/ec/ec_kmeth.c b/crypto/ec/ec_kmeth.c index b98bdd578e..b27a40519d 100644 --- a/crypto/ec/ec_kmeth.c +++ b/crypto/ec/ec_kmeth.c @@ -90,7 +90,6 @@ EC_KEY *ossl_ec_key_new_method_int(OSSL_LIB_CTX *libctx, const char *propq) ret->meth = EC_KEY_get_default_method(); ret->version = 1; - ret->conv_form = POINT_CONVERSION_UNCOMPRESSED; /* No ex_data inside the FIPS provider */ #ifndef FIPS_MODULE diff --git a/crypto/ec/ec_local.h b/crypto/ec/ec_local.h index 16adfbb92f..0be3c5529e 100644 --- a/crypto/ec/ec_local.h +++ b/crypto/ec/ec_local.h @@ -301,7 +301,6 @@ struct ec_key_st { EC_POINT *pub_key; BIGNUM *priv_key; unsigned int enc_flag; - point_conversion_form_t conv_form; CRYPTO_REF_COUNT references; int flags; #ifndef FIPS_MODULE diff --git a/crypto/evp/p_lib.c b/crypto/evp/p_lib.c index fcf64ed004..2895db09a7 100644 --- a/crypto/evp/p_lib.c +++ b/crypto/evp/p_lib.c @@ -2405,13 +2405,10 @@ int EVP_PKEY_get_ec_point_conv_form(const EVP_PKEY *pkey) /* Might work through the legacy route */ const EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey); - if (ec == NULL) - return 0; - - return EC_KEY_get_conv_form(ec); -#else - return 0; + if (ec != NULL) + return EC_KEY_get_conv_form(ec); #endif + return 0; } if (!EVP_PKEY_get_utf8_string_param(pkey, diff --git a/doc/man1/openssl-ec.pod.in b/doc/man1/openssl-ec.pod.in index a60b796c00..2fd397da00 100644 --- a/doc/man1/openssl-ec.pod.in +++ b/doc/man1/openssl-ec.pod.in @@ -122,9 +122,6 @@ This specifies how the points on the elliptic curve are converted into octet strings. Possible values are: B, B (the default value) and B. For more information regarding the point conversion forms please read the X9.62 standard. -B Due to patent issues the B option is disabled -by default for binary curves and can be enabled by defining -the preprocessor macro B at compile time. =item B<-param_enc> I diff --git a/doc/man1/openssl-ecparam.pod.in b/doc/man1/openssl-ecparam.pod.in index ca4e002762..d48f19d789 100644 --- a/doc/man1/openssl-ecparam.pod.in +++ b/doc/man1/openssl-ecparam.pod.in @@ -98,9 +98,6 @@ This specifies how the points on the elliptic curve are converted into octet strings. Possible values are: B, B (the default value) and B. For more information regarding the point conversion forms please read the X9.62 standard. -B Due to patent issues the B option is disabled -by default for binary curves and can be enabled by defining -the preprocessor macro B at compile time. =item B<-param_enc> I diff --git a/doc/man1/openssl-pkey.pod.in b/doc/man1/openssl-pkey.pod.in index 52cc5712b2..bb6fcde863 100644 --- a/doc/man1/openssl-pkey.pod.in +++ b/doc/man1/openssl-pkey.pod.in @@ -179,12 +179,9 @@ This cannot be combined with encoded output in DER format. This option only applies to elliptic-curve based keys. This specifies how the points on the elliptic curve are converted -into octet strings. Possible values are: B (the default -value), B and B. For more information regarding +into octet strings. Possible values are: B (the default +value), B and B. For more information regarding the point conversion forms please read the X9.62 standard. -B Due to patent issues the B option is disabled -by default for binary curves and can be enabled by defining -the preprocessor macro B at compile time. =item B<-ec_param_enc> I diff --git a/doc/man7/EVP_PKEY-EC.pod b/doc/man7/EVP_PKEY-EC.pod index 78ac13f332..714b5c3b60 100644 --- a/doc/man7/EVP_PKEY-EC.pod +++ b/doc/man7/EVP_PKEY-EC.pod @@ -94,6 +94,18 @@ Sets or gets the point_conversion_form for the I. For a description of point_conversion_forms please see L. Valid values are "uncompressed" or "compressed". The default value is "uncompressed". +At key generation time the public point is emitted in uncompressed +form. Any B supplied via +B(3) or B<-pkeyopt point-format:EformE> +is validated -- an invalid value is rejected -- but is otherwise +ignored on the generated key. + +On EC parameter generation the B setting selects the +encoding of the group's generator. On an existing key the setting +is honoured normally, so imported keys retain their original form +through B(3), B(3), +B(3) and PEM/DER encoding and decoding. + =item "group-check" (B) Sets or Gets the type of group check done when EVP_PKEY_param_check() is called. diff --git a/providers/implementations/keymgmt/ec_kmgmt.c b/providers/implementations/keymgmt/ec_kmgmt.c index 03c7ea3529..57ff812793 100644 --- a/providers/implementations/keymgmt/ec_kmgmt.c +++ b/providers/implementations/keymgmt/ec_kmgmt.c @@ -250,19 +250,10 @@ static ossl_inline int otherparams_to_params(const EC_KEY *ec, OSSL_PARAM_BLD *t { int ecdh_cofactor_mode = 0, group_check = 0; const char *name = NULL; - point_conversion_form_t format; if (ec == NULL) return 0; - format = EC_KEY_get_conv_form(ec); - name = ossl_ec_pt_format_id2name((int)format); - if (name != NULL - && !ossl_param_build_set_utf8_string(tmpl, params, - OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, - name)) - return 0; - group_check = EC_KEY_get_flags(ec) & EC_FLAG_CHECK_NAMED_GROUP_MASK; name = ossl_ec_check_group_type_id2name(group_check); if (name != NULL @@ -512,19 +503,21 @@ static int ec_export(void *keydata, int selection, OSSL_CALLBACK *param_cb, && (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) == 0) return 0; - tmpl = OSSL_PARAM_BLD_new(); - if (tmpl == NULL) + if ((bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(ec))) == NULL) return 0; + BN_CTX_start(bnctx); - if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) { - bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(ec)); - if (bnctx == NULL) { - ok = 0; - goto end; - } - BN_CTX_start(bnctx); - ok = ok && ossl_ec_group_todata(EC_KEY_get0_group(ec), tmpl, NULL, ossl_ec_key_get_libctx(ec), ossl_ec_key_get0_propq(ec), bnctx, &genbuf); + if ((tmpl = OSSL_PARAM_BLD_new()) == NULL) { + ok = 0; + goto end; } + /* + * OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT is added based on the group's + * asn1_form by the call below. + */ + ok = ossl_ec_group_todata(EC_KEY_get0_group(ec), tmpl, NULL, + ossl_ec_key_get_libctx(ec), ossl_ec_key_get0_propq(ec), + bnctx, &genbuf); if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { int include_private = selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0; @@ -748,6 +741,10 @@ static int common_get_params(void *key, OSSL_PARAM params[], int sm2) goto err; } + /* + * OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT is added based on the group's + * asn1_form by ossl_ec_group_todata() below. + */ ret = ec_get_ecm_params(ecg, params) && ossl_ec_group_todata(ecg, NULL, params, libctx, propq, bnctx, &genbuf) @@ -1303,6 +1300,18 @@ static void *ec_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg) /* Whether you want it or not, you get a keypair, not just one half */ if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + /* + * A generated key's public point has been serialised + * in uncompressed form for many releases, regardless + * of anything requested on the ctx or inherited from + * the template -- callers who consume the SPKI, EC + * PKCS#8 or other encodings rely on that. With group + * and key form now consolidated on a single field on + * the group, force it to uncompressed here rather + * than start emitting compressed public points where + * no caller was expecting them. + */ + EC_KEY_set_conv_form(ec, POINT_CONVERSION_UNCOMPRESSED); #ifndef FIPS_MODULE if (gctx->dhkem_ikm != NULL && gctx->dhkem_ikmlen != 0) ret = ret && ossl_ec_generate_key_dhkem(ec, gctx->dhkem_ikm, gctx->dhkem_ikmlen); @@ -1376,8 +1385,21 @@ static void *sm2_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg) ret = ec_gen_assign_group(ec, gctx->gen_group); /* Whether you want it or not, you get a keypair, not just one half */ - if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) + if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + /* + * A generated key's public point has been serialised + * in uncompressed form for many releases, regardless + * of anything requested on the ctx or inherited from + * the template -- callers who consume the SPKI, EC + * PKCS#8 or other encodings rely on that. With group + * and key form now consolidated on a single field on + * the group, force it to uncompressed here rather + * than start emitting compressed public points where + * no caller was expecting them. + */ + EC_KEY_set_conv_form(ec, POINT_CONVERSION_UNCOMPRESSED); ret = ret && EC_KEY_generate_key(ec); + } if (ret) return ec; diff --git a/test/ectest.c b/test/ectest.c index 4665882e1c..36fcb50089 100644 --- a/test/ectest.c +++ b/test/ectest.c @@ -802,37 +802,22 @@ static int char2_curve_test(int n) || !TEST_true(BN_add(yplusone, y, BN_value_one()))) goto err; -/* Change test based on whether binary point compression is enabled or not. */ -#ifdef OPENSSL_EC_BIN_PT_COMP - /* - * When (x, y) is on the curve, (x, y + 1) is, as it happens, not, - * and therefore setting the coordinates should fail. - */ - if (!TEST_false(EC_POINT_set_affine_coordinates(group, P, x, yplusone, ctx)) - || !TEST_true(EC_POINT_set_compressed_coordinates(group, P, x, - test->y_bit, - ctx)) - || !TEST_int_gt(EC_POINT_is_on_curve(group, P, ctx), 0) - || !TEST_true(BN_hex2bn(&z, test->order)) - || !TEST_true(BN_hex2bn(&cof, test->cof)) - || !TEST_true(EC_GROUP_set_generator(group, P, z, cof)) - || !TEST_true(EC_POINT_get_affine_coordinates(group, P, x, y, ctx))) - goto err; - TEST_info("%s -- Generator", test->name); - test_output_bignum("x", x); - test_output_bignum("y", y); - /* G_y value taken from the standard: */ - if (!TEST_true(BN_hex2bn(&z, test->y)) - || !TEST_BN_eq(y, z)) - goto err; -#else /* * When (x, y) is on the curve, (x, y + 1) is, as it happens, not, * and therefore setting the coordinates should fail. + * + * Set the generator point P from its affine coordinates, and + * independently recover the same point Q from x and the y-bit via + * compressed coordinates. The two must agree, which exercises both + * the affine and compressed binary point formats in a single pass. */ if (!TEST_false(EC_POINT_set_affine_coordinates(group, P, x, yplusone, ctx)) || !TEST_true(EC_POINT_set_affine_coordinates(group, P, x, y, ctx)) || !TEST_int_gt(EC_POINT_is_on_curve(group, P, ctx), 0) + || !TEST_true(EC_POINT_set_compressed_coordinates(group, Q, x, + test->ybit, ctx)) + || !TEST_int_gt(EC_POINT_is_on_curve(group, Q, ctx), 0) + || !TEST_int_eq(0, EC_POINT_cmp(group, P, Q, ctx)) || !TEST_true(BN_hex2bn(&z, test->order)) || !TEST_true(BN_hex2bn(&cof, test->cof)) || !TEST_true(EC_GROUP_set_generator(group, P, z, cof))) @@ -840,7 +825,6 @@ static int char2_curve_test(int n) TEST_info("%s -- Generator:", test->name); test_output_bignum("x", x); test_output_bignum("y", y); -#endif if (!TEST_int_eq(EC_GROUP_get_degree(group), test->degree) || !TEST_int_eq(EC_GROUP_security_bits(group), test->security) @@ -971,26 +955,19 @@ static int char2_field_tests(void) || !TEST_ptr(cof = BN_new()) || !TEST_ptr(yplusone = BN_new()) || !TEST_true(BN_hex2bn(&x, "6")) -/* Change test based on whether binary point compression is enabled or not. */ -#ifdef OPENSSL_EC_BIN_PT_COMP - || !TEST_true(EC_POINT_set_compressed_coordinates(group, Q, x, 1, ctx)) -#else || !TEST_true(BN_hex2bn(&y, "8")) - || !TEST_true(EC_POINT_set_affine_coordinates(group, Q, x, y, ctx)) -#endif - ) + || !TEST_true(EC_POINT_set_affine_coordinates(group, Q, x, y, ctx))) goto err; if (!TEST_int_gt(EC_POINT_is_on_curve(group, Q, ctx), 0)) { -/* Change test based on whether binary point compression is enabled or not. */ -#ifdef OPENSSL_EC_BIN_PT_COMP - if (!TEST_true(EC_POINT_get_affine_coordinates(group, Q, x, y, ctx))) - goto err; -#endif TEST_info("Point is not on curve"); test_output_bignum("x", x); test_output_bignum("y", y); goto err; } + /* The same point recovered from compressed coordinates must agree. */ + if (!TEST_true(EC_POINT_set_compressed_coordinates(group, R, x, 1, ctx)) + || !TEST_int_eq(0, EC_POINT_cmp(group, R, Q, ctx))) + goto err; TEST_note("A cyclic subgroup:"); k = 100; @@ -1018,8 +995,6 @@ static int char2_field_tests(void) || !TEST_true(EC_POINT_is_at_infinity(group, P))) goto err; -/* Change test based on whether binary point compression is enabled or not. */ -#ifdef OPENSSL_EC_BIN_PT_COMP len = EC_POINT_point2oct(group, Q, POINT_CONVERSION_COMPRESSED, buf, sizeof(buf), ctx); if (!TEST_size_t_ne(len, 0) @@ -1028,7 +1003,6 @@ static int char2_field_tests(void) goto err; test_output_memory("Generator as octet string, compressed form:", buf, len); -#endif len = EC_POINT_point2oct(group, Q, POINT_CONVERSION_UNCOMPRESSED, buf, sizeof(buf), ctx); @@ -1039,8 +1013,6 @@ static int char2_field_tests(void) test_output_memory("Generator as octet string, uncompressed form:", buf, len); -/* Change test based on whether binary point compression is enabled or not. */ -#ifdef OPENSSL_EC_BIN_PT_COMP len = EC_POINT_point2oct(group, Q, POINT_CONVERSION_HYBRID, buf, sizeof(buf), ctx); if (!TEST_size_t_ne(len, 0) @@ -1049,7 +1021,6 @@ static int char2_field_tests(void) goto err; test_output_memory("Generator as octet string, hybrid form:", buf, len); -#endif if (!TEST_true(EC_POINT_invert(group, P, ctx)) || !TEST_int_eq(0, EC_POINT_cmp(group, P, R, ctx))) From 9d59af025a025b0aaa18148868ba0c773ce0ce97 Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Sat, 25 Apr 2026 22:12:59 +1000 Subject: [PATCH 210/349] Improve TLS handling of EC point formats Decouple the ec_point_formats extension from TLS 1.2 X.509 selection and acceptance. Remove tls1_check_pkey_comp() and its callers in tls1_check_cert_param() and tls1_check_chain(): TLS 1.3 disregards the extension already, and we can decode any point form a peer might send, so refusing a compressed peer cert in TLS 1.2 because we didn't advertise compressed buys nothing. The RFC 4492/8422 section 5.1.2 requirement that the peer's list contain "uncompressed" used to be enforced in a final hook on the client side only. Move it to the two points where the negotiated ciphersuite is known: the client's ServerHello parse hook, and the server's ServerHello construct hook. Both sites fire the alert only when an ECC TLS 1.2 ciphersuite has been negotiated, so a missing "uncompressed" is ignored under TLS 1.3 or a non-ECC cipher. The client- and server-side parse hooks now share one function. Drop the always-NULL ext.ecpointformats fields on SSL_CTX and SSL; our own list is built directly inside the constructors. The peer's list continues to be stored verbatim, and is also exposed through the SSL_get0_ec_point_formats() accessor (now documented). New tests verify the four corners (ECC vs non-ECC ciphersuite, TLS 1.2 vs 1.3) plus that a compressed point form EC cert is usable on both sides without any opt-in. Reviewed-by: Neil Horman Reviewed-by: Matt Caswell MergeDate: Fri Jul 10 09:21:30 2026 (Merged from https://github.com/openssl/openssl/pull/30940) --- CHANGES.md | 30 +++- doc/man3/SSL_CTX_set1_curves.pod | 21 ++- ssl/ssl_lib.c | 11 -- ssl/ssl_local.h | 51 ++++--- ssl/statem/extensions.c | 97 ++++++------ ssl/statem/extensions_clnt.c | 40 ----- ssl/statem/extensions_srvr.c | 45 +++--- ssl/statem/statem_local.h | 6 - ssl/t1_lib.c | 74 +-------- test/certs/server-ec-compressed-cert.pem | 12 ++ test/certs/server-ec-compressed-key.pem | 5 + test/recipes/70-test_ec_point_formats.t | 183 +++++++++++++++++++++++ test/recipes/80-test_ssl_new.t | 4 +- test/ssl-tests/33-compressed-spki.cnf | 76 ++++++++++ test/ssl-tests/33-compressed-spki.cnf.in | 81 ++++++++++ test/sslapitest.c | 2 +- util/missingmacro.txt | 1 - util/other.syms | 1 + 18 files changed, 519 insertions(+), 221 deletions(-) create mode 100644 test/certs/server-ec-compressed-cert.pem create mode 100644 test/certs/server-ec-compressed-key.pem create mode 100644 test/recipes/70-test_ec_point_formats.t create mode 100644 test/ssl-tests/33-compressed-spki.cnf create mode 100644 test/ssl-tests/33-compressed-spki.cnf.in diff --git a/CHANGES.md b/CHANGES.md index 0c9366b01e..70ed350c79 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -31,6 +31,34 @@ OpenSSL Releases ### Changes between 4.0 and 4.1 [xx XXX xxxx] + * EC key point format simplification. + + The point conversion form (compressed, uncompressed, or hybrid) + is now a single value on the `EC_GROUP` and round-trips + unchanged through import and export of `EC_KEY` objects. + + Freshly generated keys have their public point encoded in + uncompressed form. A `point-format` supplied at key generation + time via `OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT` is + validated (an invalid value is rejected) but otherwise ignored + on the generated key. EC parameter generation continues to + honour the requested form on the group's generator; imported + keys keep their form. + + The `ec_point_formats` extension no longer affects TLS 1.2 + X.509 certificate selection or acceptance. OpenSSL now + accepts an EC certificate in any point form it can decode, + and sends any EC certificate it has regardless of point form. + TLS 1.3 disregards the extension entirely. + + The RFC 4492/8422 section 5.1.2 requirement that the peer's + point-format list contain "uncompressed" is now enforced on + both sides (previously client-only), and only when an ECC + TLS 1.2 ciphersuite is negotiated -- a missing "uncompressed" + is ignored under TLS 1.3 or with a non-ECC cipher. + + *Viktor Dukhovni* + * Added unit tests setup activated via `enable-unit-tests` option. This works only on platforms with ld `--wrap` support (Linux, BSD). @@ -51,7 +79,7 @@ OpenSSL Releases *Adriano Sela Aviles* * SubjectPublicKeyInfo blobs whose AlgorithmIdentifier uses id-RSAES-OAEP - (NID_rsaesOaep, 1.2.840.113549.1.1.7) with a plain RSAPublicKey body + (`NID_rsaesOaep`, 1.2.840.113549.1.1.7) with a plain RSAPublicKey body are now decoded as RSA keys. This is required for interoperability with TPM 1.2 Endorsement Key certificates per TCG Credential Profiles V1.2 section 3.2.7. The OAEP AlgorithmIdentifier parameters are not diff --git a/doc/man3/SSL_CTX_set1_curves.pod b/doc/man3/SSL_CTX_set1_curves.pod index 64321df5f4..f8f9a149f7 100755 --- a/doc/man3/SSL_CTX_set1_curves.pod +++ b/doc/man3/SSL_CTX_set1_curves.pod @@ -4,7 +4,8 @@ SSL_CTX_set1_groups, SSL_CTX_set1_groups_list, SSL_set1_groups, SSL_set1_groups_list, SSL_get1_groups, SSL_get0_iana_groups, -SSL_get_shared_group, SSL_get_negotiated_group, SSL_CTX_set1_curves, +SSL_get0_ec_point_formats, SSL_get_shared_group, SSL_get_negotiated_group, +SSL_CTX_set1_curves, SSL_CTX_set1_curves_list, SSL_set1_curves, SSL_set1_curves_list, SSL_get1_curves, SSL_get_shared_curve, SSL_CTX_get0_implemented_groups - EC supported curve functions @@ -21,6 +22,7 @@ SSL_get1_curves, SSL_get_shared_curve, SSL_CTX_get0_implemented_groups int SSL_get1_groups(SSL *ssl, int *groups); int SSL_get0_iana_groups(SSL *ssl, uint16_t **out); + int SSL_get0_ec_point_formats(SSL *ssl, const unsigned char **plst); int SSL_get_shared_group(SSL *s, int n); int SSL_get_negotiated_group(SSL *s); @@ -246,6 +248,19 @@ identifiers, as assigned by IANA. The group list is returned in the same order that was received in the ClientHello. The return value is the number of groups, not the number of bytes written. +SSL_get0_ec_point_formats() retrieves the peer's B +extension as a byte array of B values in the +order received. If the peer sent the extension, B<*plst> is set to an +internal buffer holding the list and its length is returned; otherwise +the return value is 0 and B<*plst> is left unchanged. The returned +pointer is owned by the B connection and must not be freed. The +point format no longer affects certificate selection or acceptance. + +The B extension applies only to TLS 1.2 and below, but +the peer's list is recorded even when TLS 1.3 is ultimately negotiated, +since a TLS 1.3 B may still carry the extension for +backward compatibility. + SSL_get_shared_group() returns the NID of the shared group B for a server-side SSL B. If B is -1 then the total number of shared groups is returned, which may be zero. Other than for diagnostic purposes, @@ -304,6 +319,10 @@ SSL_get1_groups() returns the number of groups, which may be zero. SSL_get0_iana_groups() returns the number of (uint16_t) groups, which may be zero. +SSL_get0_ec_point_formats() returns the number of point formats sent by +the peer in its B extension, or 0 if no such extension +was received. + SSL_get_shared_group() returns the NID of shared group B or NID_undef if there is no shared group B; or the total number of shared groups if B is -1. diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 2902a5f5aa..a900ad557a 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -819,15 +819,6 @@ SSL *ossl_ssl_connection_new_int(SSL_CTX *ctx, SSL *user_ssl, goto err; s->session_ctx = ctx; - if (ctx->ext.ecpointformats != NULL) { - s->ext.ecpointformats = OPENSSL_memdup(ctx->ext.ecpointformats, - ctx->ext.ecpointformats_len); - if (s->ext.ecpointformats == NULL) { - s->ext.ecpointformats_len = 0; - goto err; - } - s->ext.ecpointformats_len = ctx->ext.ecpointformats_len; - } if (ctx->ext.supportedgroups != NULL) { size_t add = 0; @@ -1527,7 +1518,6 @@ void ossl_ssl_connection_free(SSL *ssl) OPENSSL_free(s->ext.hostname); SSL_CTX_free(s->session_ctx); - OPENSSL_free(s->ext.ecpointformats); OPENSSL_free(s->ext.peer_ecpointformats); OPENSSL_free(s->ext.supportedgroups); OPENSSL_free(s->ext.keyshares); @@ -4616,7 +4606,6 @@ void SSL_CTX_free(SSL_CTX *a) ssl_ctx_srp_ctx_free_intern(a); #endif - OPENSSL_free(a->ext.ecpointformats); OPENSSL_free(a->ext.supportedgroups); OPENSSL_free(a->ext.keyshares); OPENSSL_free(a->ext.tuples); diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h index 5c0fa8f925..978f627290 100644 --- a/ssl/ssl_local.h +++ b/ssl/ssl_local.h @@ -12,6 +12,7 @@ #ifndef OSSL_SSL_LOCAL_H #define OSSL_SSL_LOCAL_H +#include #include #include #include @@ -1039,10 +1040,6 @@ struct ssl_ctx_st { /* RFC 4366 Maximum Fragment Length Negotiation */ uint8_t max_fragment_len_mode; - /* EC extension values inherited by SSL structure */ - size_t ecpointformats_len; - unsigned char *ecpointformats; - size_t supportedgroups_len; uint16_t *supportedgroups; @@ -1653,8 +1650,6 @@ struct ssl_connection_st { unsigned char *scts; /* Length of raw extension data, if seen */ uint16_t scts_len; - /* Expect OCSP CertificateStatus message */ - int status_expected; struct { /* OCSP status request only */ @@ -1666,15 +1661,16 @@ struct ssl_connection_st { STACK_OF(OCSP_RESPONSE) *resp_ex; } ocsp; - /* RFC4507 session ticket expected to be received or sent */ - int ticket_expected; /* TLS 1.3 tickets requested by the application. */ int extra_tickets_expected; - /* our list */ - size_t ecpointformats_len; - unsigned char *ecpointformats; - /* peer's list */ + /* + * Peer's advertised ec_point_formats list (TLS 1.2 and below), + * retained as received so SSL_get0_ec_point_formats() can return + * it verbatim. Point format no longer influences cert selection + * or acceptance; the parse hook validates RFC 4492/8422 section + * 5.1.2 ("uncompressed" must be present) inline. + */ size_t peer_ecpointformats_len; unsigned char *peer_ecpointformats; @@ -1719,19 +1715,12 @@ struct ssl_connection_st { /* The available PSK key exchange modes */ int psk_kex_mode; - /* Set to one if we have negotiated ETM */ - int use_etm; - /* Are we expecting to receive early data? */ int early_data; - /* Is the session suitable for early data? */ - int early_data_ok; /* May be sent by a server in HRR. Must be echoed back in ClientHello */ unsigned char *tls13_cookie; size_t tls13_cookie_len; - /* Have we received a cookie from the client? */ - int cookieok; /* * Maximum Fragment Length as per RFC 4366. @@ -1753,8 +1742,6 @@ struct ssl_connection_st { /* This is the list of algorithms the peer supports that we also support */ int compress_certificate_from_peer[TLSEXT_comp_cert_limit]; - /* indicate that we sent the extension, so we'll accept it */ - int compress_certificate_sent; uint8_t client_cert_type; uint8_t client_cert_type_ctos; @@ -1767,7 +1754,27 @@ struct ssl_connection_st { /* RFC 8701 GREASE */ uint8_t grease_seed[OSSL_GREASE_LAST_INDEX + 1]; - int grease_seeded; + + /* "bool" fields go last, for slightly better packing */ + bool grease_seeded; + + /* Expect OCSP CertificateStatus message */ + bool status_expected; + + /* RFC4507 session ticket expected to be received or sent */ + bool ticket_expected; + + /* Set to one if we have negotiated ETM */ + bool use_etm; + + /* Is the session suitable for early data? */ + bool early_data_ok; + + /* Have we received a cookie from the client? */ + bool cookieok; + + /* indicate that we sent the extension, so we'll accept it */ + bool compress_certificate_sent; } ext; /* diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c index 2f97850972..017d1a5fb5 100644 --- a/ssl/statem/extensions.c +++ b/ssl/statem/extensions.c @@ -59,8 +59,6 @@ static int final_ech(SSL_CONNECTION *s, unsigned int context, int sent); static int final_renegotiate(SSL_CONNECTION *s, unsigned int context, int sent); static int init_server_name(SSL_CONNECTION *s, unsigned int context); static int final_server_name(SSL_CONNECTION *s, unsigned int context, int sent); -static int final_ec_pt_formats(SSL_CONNECTION *s, unsigned int context, - int sent); static int init_session_ticket(SSL_CONNECTION *s, unsigned int context); #ifndef OPENSSL_NO_OCSP static int init_status_request(SSL_CONNECTION *s, unsigned int context); @@ -111,6 +109,9 @@ static EXT_RETURN tls_construct_compress_certificate(SSL_CONNECTION *sc, WPACKET static int tls_parse_compress_certificate(SSL_CONNECTION *sc, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx); +static int tls_parse_ec_pt_formats(SSL_CONNECTION *s, PACKET *pkt, + unsigned int context, + X509 *x, size_t chainidx); /* Structure to define a built-in extension */ typedef struct extensions_definition_st { @@ -217,9 +218,9 @@ static const EXTENSION_DEFINITION ext_defs[] = { SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO | SSL_EXT_TLS1_2_AND_BELOW_ONLY, OSSL_ECH_HANDLING_COMPRESS, - init_ec_point_formats, tls_parse_ctos_ec_pt_formats, tls_parse_stoc_ec_pt_formats, + init_ec_point_formats, tls_parse_ec_pt_formats, tls_parse_ec_pt_formats, tls_construct_stoc_ec_pt_formats, tls_construct_ctos_ec_pt_formats, - final_ec_pt_formats }, + NULL }, { /* * "supported_groups" is spread across several specifications. * It was originally specified as "elliptic_curves" in RFC 4492, @@ -748,6 +749,55 @@ static int verify_extension(SSL_CONNECTION *s, unsigned int context, return 1; } +/* + * Parse an ec_point_formats extension off the wire (one function for + * both sides). The peer's list is retained verbatim for + * SSL_get0_ec_point_formats() and for the RFC 4492/8422 section 5.1.2 + * 'uncompressed must be present' check. + * + * The check is gated on the negotiated ciphersuite -- a TLS 1.3 + * handshake or a non-ECC TLS 1.2 ciphersuite makes the extension moot + * and any missing 'uncompressed' codepoint is ignored. On the client + * the chosen ciphersuite is already locked in by the time we parse + * ServerHello, so the check happens inline here. On the server it's + * deferred to tls_construct_stoc_ec_pt_formats(), the first point at + * which s->s3.tmp.new_cipher is set for TLS 1.2. + */ +static int tls_parse_ec_pt_formats(SSL_CONNECTION *s, PACKET *pkt, + unsigned int context, X509 *x, size_t chainidx) +{ + PACKET list; + + if (!PACKET_as_length_prefixed_1(pkt, &list) + || PACKET_remaining(&list) == 0) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION); + return 0; + } + if (!s->hit + && !PACKET_memdup(&list, &s->ext.peer_ecpointformats, + &s->ext.peer_ecpointformats_len)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + + if (!s->server) { + unsigned long alg_k = s->s3.tmp.new_cipher->algorithm_mkey; + unsigned long alg_a = s->s3.tmp.new_cipher->algorithm_auth; + + if (((alg_k & SSL_kECDHE) || (alg_a & SSL_aECDSA)) + && memchr(PACKET_data(&list), + TLSEXT_ECPOINTFORMAT_uncompressed, + PACKET_remaining(&list)) + == NULL) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, + SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST); + return 0; + } + } + + return 1; +} + /* * Check whether the context defined for an extension |extctx| means whether * the extension is relevant for the current context |thisctx| or not. Returns @@ -1360,45 +1410,6 @@ static int final_server_name(SSL_CONNECTION *s, unsigned int context, int sent) } } -static int final_ec_pt_formats(SSL_CONNECTION *s, unsigned int context, - int sent) -{ - unsigned long alg_k, alg_a; - - if (s->server) - return 1; - - alg_k = s->s3.tmp.new_cipher->algorithm_mkey; - alg_a = s->s3.tmp.new_cipher->algorithm_auth; - - /* - * If we are client and using an elliptic curve cryptography cipher - * suite, then if server returns an EC point formats lists extension it - * must contain uncompressed. - */ - if (s->ext.ecpointformats != NULL - && s->ext.ecpointformats_len > 0 - && s->ext.peer_ecpointformats != NULL - && s->ext.peer_ecpointformats_len > 0 - && ((alg_k & SSL_kECDHE) || (alg_a & SSL_aECDSA))) { - /* we are using an ECC cipher */ - size_t i; - unsigned char *list = s->ext.peer_ecpointformats; - - for (i = 0; i < s->ext.peer_ecpointformats_len; i++) { - if (*list++ == TLSEXT_ECPOINTFORMAT_uncompressed) - break; - } - if (i == s->ext.peer_ecpointformats_len) { - SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, - SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST); - return 0; - } - } - - return 1; -} - static int init_session_ticket(SSL_CONNECTION *s, unsigned int context) { if (!s->server) diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c index 0b220965c1..e3e713f5f6 100644 --- a/ssl/statem/extensions_clnt.c +++ b/ssl/statem/extensions_clnt.c @@ -1763,46 +1763,6 @@ int tls_parse_stoc_server_name(SSL_CONNECTION *s, PACKET *pkt, return 1; } -int tls_parse_stoc_ec_pt_formats(SSL_CONNECTION *s, PACKET *pkt, - unsigned int context, - X509 *x, size_t chainidx) -{ - size_t ecpointformats_len; - PACKET ecptformatlist; - - if (!PACKET_as_length_prefixed_1(pkt, &ecptformatlist)) { - SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION); - return 0; - } - if (!s->hit) { - ecpointformats_len = PACKET_remaining(&ecptformatlist); - if (ecpointformats_len == 0) { - SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_LENGTH); - return 0; - } - - s->ext.peer_ecpointformats_len = 0; - OPENSSL_free(s->ext.peer_ecpointformats); - s->ext.peer_ecpointformats = OPENSSL_malloc(ecpointformats_len); - if (s->ext.peer_ecpointformats == NULL) { - s->ext.peer_ecpointformats_len = 0; - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - return 0; - } - - s->ext.peer_ecpointformats_len = ecpointformats_len; - - if (!PACKET_copy_bytes(&ecptformatlist, - s->ext.peer_ecpointformats, - ecpointformats_len)) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - return 0; - } - } - - return 1; -} - int tls_parse_stoc_session_ticket(SSL_CONNECTION *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx) diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c index 0460cd79e7..12c06eef9b 100644 --- a/ssl/statem/extensions_srvr.c +++ b/ssl/statem/extensions_srvr.c @@ -245,30 +245,6 @@ int tls_parse_ctos_srp(SSL_CONNECTION *s, PACKET *pkt, unsigned int context, } #endif -int tls_parse_ctos_ec_pt_formats(SSL_CONNECTION *s, PACKET *pkt, - unsigned int context, - X509 *x, size_t chainidx) -{ - PACKET ec_point_format_list; - - if (!PACKET_as_length_prefixed_1(pkt, &ec_point_format_list) - || PACKET_remaining(&ec_point_format_list) == 0) { - SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION); - return 0; - } - - if (!s->hit) { - if (!PACKET_memdup(&ec_point_format_list, - &s->ext.peer_ecpointformats, - &s->ext.peer_ecpointformats_len)) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - return 0; - } - } - - return 1; -} - int tls_parse_ctos_session_ticket(SSL_CONNECTION *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx) @@ -1685,14 +1661,29 @@ EXT_RETURN tls_construct_stoc_ec_pt_formats(SSL_CONNECTION *s, WPACKET *pkt, { unsigned long alg_k = s->s3.tmp.new_cipher->algorithm_mkey; unsigned long alg_a = s->s3.tmp.new_cipher->algorithm_auth; - int using_ecc = ((alg_k & SSL_kECDHE) || (alg_a & SSL_aECDSA)) - && (s->ext.peer_ecpointformats != NULL); + int using_ecc = (alg_k & SSL_kECDHE) || (alg_a & SSL_aECDSA); const unsigned char *plist; size_t plistlen; - if (!using_ecc) + /* + * The extension is irrelevant unless we're negotiating an ECC + * ciphersuite at TLS 1.2 or below, and the peer sent a list. This + * is the first point at which the chosen ciphersuite is known, so + * the RFC 4492/8422 section 5.1.2 check for the required + * 'uncompressed' codepoint also happens here. + */ + if (!using_ecc || s->ext.peer_ecpointformats == NULL) return EXT_RETURN_NOT_SENT; + if (memchr(s->ext.peer_ecpointformats, + TLSEXT_ECPOINTFORMAT_uncompressed, + s->ext.peer_ecpointformats_len) + == NULL) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, + SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST); + return EXT_RETURN_FAIL; + } + tls1_get_formatlist(s, &plist, &plistlen); if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_ec_point_formats) || !WPACKET_start_sub_packet_u16(pkt) diff --git a/ssl/statem/statem_local.h b/ssl/statem/statem_local.h index 68ac803fa9..b73c1e100f 100644 --- a/ssl/statem/statem_local.h +++ b/ssl/statem/statem_local.h @@ -306,9 +306,6 @@ int tls_parse_ctos_srp(SSL_CONNECTION *s, PACKET *pkt, unsigned int context, int tls_parse_ctos_early_data(SSL_CONNECTION *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx); -int tls_parse_ctos_ec_pt_formats(SSL_CONNECTION *s, PACKET *pkt, - unsigned int context, - X509 *x, size_t chainidx); int tls_parse_ctos_supported_groups(SSL_CONNECTION *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidxl); @@ -518,9 +515,6 @@ int tls_parse_stoc_early_data(SSL_CONNECTION *s, PACKET *pkt, int tls_parse_stoc_maxfragmentlen(SSL_CONNECTION *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx); -int tls_parse_stoc_ec_pt_formats(SSL_CONNECTION *s, PACKET *pkt, - unsigned int context, - X509 *x, size_t chainidx); int tls_parse_stoc_session_ticket(SSL_CONNECTION *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx); diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index b55b101324..5d0276a5a1 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -1886,13 +1886,7 @@ int tls1_check_group_id(SSL_CONNECTION *s, uint16_t group_id, void tls1_get_formatlist(SSL_CONNECTION *s, const unsigned char **pformats, size_t *num_formats) { - /* - * If we have a custom point format list use it otherwise use default - */ - if (s->ext.ecpointformats) { - *pformats = s->ext.ecpointformats; - *num_formats = s->ext.ecpointformats_len; - } else if ((s->options & SSL_OP_LEGACY_EC_POINT_FORMATS) != 0) { + if ((s->options & SSL_OP_LEGACY_EC_POINT_FORMATS) != 0) { *pformats = ecformats_all; /* For Suite B we don't support char2 fields */ if (tls1_suiteb(s)) @@ -1905,53 +1899,6 @@ void tls1_get_formatlist(SSL_CONNECTION *s, const unsigned char **pformats, } } -/* Check a key is compatible with compression extension */ -static int tls1_check_pkey_comp(SSL_CONNECTION *s, EVP_PKEY *pkey) -{ - unsigned char comp_id; - size_t i; - int point_conv; - - /* If not an EC key nothing to check */ - if (!EVP_PKEY_is_a(pkey, "EC")) - return 1; - - /* Get required compression id */ - point_conv = EVP_PKEY_get_ec_point_conv_form(pkey); - if (point_conv == 0) - return 0; - if (point_conv == POINT_CONVERSION_UNCOMPRESSED) { - comp_id = TLSEXT_ECPOINTFORMAT_uncompressed; - } else if (SSL_CONNECTION_IS_TLS13(s)) { - /* - * ec_point_formats extension is not used in TLSv1.3 so we ignore - * this check. - */ - return 1; - } else { - int field_type = EVP_PKEY_get_field_type(pkey); - - if (field_type == NID_X9_62_prime_field) - comp_id = TLSEXT_ECPOINTFORMAT_ansiX962_compressed_prime; - else if (field_type == NID_X9_62_characteristic_two_field) - comp_id = TLSEXT_ECPOINTFORMAT_ansiX962_compressed_char2; - else - return 0; - } - /* - * If point formats extension present check it, otherwise everything is - * supported (see RFC4492). - */ - if (s->ext.peer_ecpointformats == NULL) - return 1; - - for (i = 0; i < s->ext.peer_ecpointformats_len; i++) { - if (s->ext.peer_ecpointformats[i] == comp_id) - return 1; - } - return 0; -} - /* Return group id of a key */ static uint16_t tls1_get_group_id(EVP_PKEY *pkey) { @@ -1964,7 +1911,7 @@ static uint16_t tls1_get_group_id(EVP_PKEY *pkey) /* * Check cert parameters compatible with extensions: currently just checks EC - * certificates have compatible curves and compression. + * certificates have compatible curves. */ static int tls1_check_cert_param(SSL_CONNECTION *s, X509 *x, int check_ee_md) { @@ -1976,9 +1923,6 @@ static int tls1_check_cert_param(SSL_CONNECTION *s, X509 *x, int check_ee_md) /* If not EC nothing to do */ if (!EVP_PKEY_is_a(pkey, "EC")) return 1; - /* Check compression */ - if (!tls1_check_pkey_comp(s, pkey)) - return 0; group_id = tls1_get_group_id(pkey); /* * For a server we allow the certificate to not be in our list of supported @@ -2917,13 +2861,11 @@ int tls12_check_peer_sigalg(SSL_CONNECTION *s, uint16_t sig, EVP_PKEY *pkey) } if (pkeyid == EVP_PKEY_EC) { - - /* Check point compression is permitted */ - if (!tls1_check_pkey_comp(s, pkey)) { - SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, - SSL_R_ILLEGAL_POINT_COMPRESSION); - return 0; - } + /* + * No point-format check on either the peer's or own cert. + * We accept any form we can decode, and send the cert we + * have. + */ /* For TLS 1.3 or Suite B check curve matches signature algorithm */ if (SSL_CONNECTION_IS_TLS13(s) || tls1_suiteb(s)) { @@ -4231,8 +4173,6 @@ int tls1_check_chain(SSL_CONNECTION *s, X509 *x, EVP_PKEY *pk, chain = cpk->chain; strict_mode = c->cert_flags & SSL_CERT_FLAGS_CHECK_TLS_STRICT; if (tls12_rpk_and_privkey(s, idx)) { - if (EVP_PKEY_is_a(pk, "EC") && !tls1_check_pkey_comp(s, pk)) - return 0; *pvalid = rv = CERT_PKEY_RPK; return rv; } diff --git a/test/certs/server-ec-compressed-cert.pem b/test/certs/server-ec-compressed-cert.pem new file mode 100644 index 0000000000..4e97f49af2 --- /dev/null +++ b/test/certs/server-ec-compressed-cert.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBrzCCATSgAwIBAgIBAjAKBggqhkjOPQQDAjAbMRkwFwYDVQQDDBBFQ0RTQSBQ +LTM4NCByb290MCAXDTI2MDYxOTE4MDA1OFoYDzIxMjYwNTI2MTgwMDU4WjAZMRcw +FQYDVQQDDA5zZXJ2ZXIuZXhhbXBsZTA5MBMGByqGSM49AgEGCCqGSM49AwEHAyIA +A4Mt9T6fKt3APp8/Frw65PDi2eMYdZK98nhBW9pA1Ccho4GIMIGFMB0GA1UdDgQW +BBTozN8kakexZEnc26PUo5K2W9ptbTAfBgNVHSMEGDAWgBQm0I8de1/cHn9BgH1j +yhx1gdaFaTAJBgNVHRMEAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD +AjAZBgNVHREEEjAQgg5zZXJ2ZXIuZXhhbXBsZTAKBggqhkjOPQQDAgNpADBmAjEA +hvcNLTyL7vamQEJet5uvOXH7NKPHlG8sbfPvGS/AQ0yk6ARdc1Y1gV6FAnmnQUhX +AjEA2HMTkh+h2ZDm68uRnhsNXIxI4dYF/nr5bdxw8XKN4P84Mg4gs37/IPoVN9jG +Y4yh +-----END CERTIFICATE----- diff --git a/test/certs/server-ec-compressed-key.pem b/test/certs/server-ec-compressed-key.pem new file mode 100644 index 0000000000..98218fcb3c --- /dev/null +++ b/test/certs/server-ec-compressed-key.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MGcCAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcETTBLAgEBBCAb1VgxSUhJyh43soLb +FMsebjWSp/Hma3kSyw6lT4txDaEkAyIAA4Mt9T6fKt3APp8/Frw65PDi2eMYdZK9 +8nhBW9pA1Cch +-----END PRIVATE KEY----- diff --git a/test/recipes/70-test_ec_point_formats.t b/test/recipes/70-test_ec_point_formats.t new file mode 100644 index 0000000000..4c86b66c9a --- /dev/null +++ b/test/recipes/70-test_ec_point_formats.t @@ -0,0 +1,183 @@ +#! /usr/bin/env perl +# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +use strict; + +use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file bldtop_dir/; +use OpenSSL::Test::Utils; +use TLSProxy::Proxy; +use Cwd qw(abs_path); + +my $test_name = "test_ec_point_formats"; +setup($test_name); + +$ENV{OPENSSL_MODULES} = abs_path(bldtop_dir("test")); + +plan skip_all => "TLSProxy isn't usable on $^O" + if $^O =~ /^(VMS)$/; + +plan skip_all => "$test_name needs the module feature enabled" + if disabled("module"); + +plan skip_all => "$test_name needs the sock feature enabled" + if disabled("sock"); + +plan skip_all => "$test_name needs TLS 1.2 enabled" + if disabled("tls1_2"); + +plan skip_all => "$test_name needs EC enabled" + if disabled("ec"); + +# RFC 4492 section 5.1.2 requires the peer's ec_point_formats list to +# contain "uncompressed", but only when an EC cipher suite is actually +# negotiated at TLS 1.2 or below. The peer's list is irrelevant at TLS +# 1.3 (the extension itself is) and at TLS 1.2 with a non-ECC cipher +# suite. We mangle the list to contain only "compressed" (0x01) and +# verify the four corner cases: both sides reject in an ECC TLS 1.2 +# handshake; both sides tolerate the missing "uncompressed" when TLS 1.3 +# or a non-ECC cipher suite is in play. + +# Wire format for ec_point_formats: 1-byte length prefix, then the +# ECPointFormat bytes. "\x01\x01" advertises a list of one format, +# the value 1 = ansiX962_compressed_prime; 0 (uncompressed) is absent. +my $only_compressed = "\x01\x01"; + +my $fatal_alert = 0; + +my $proxy = TLSProxy::Proxy->new( + \&mangle_ec_point_formats_clienthello, + cmdstr(app(["openssl"]), display => 1), + srctop_file("apps", "server.pem"), + (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE}), +); + +# Test 1: a non-conforming list in the ClientHello -- the server +# must abort with illegal_parameter. ECDHE-RSA suffices to +# trigger the check (client supports at least one ECDHE group and +# cipher, so sends the extension), no EC cert required from either +# side. +$proxy->clientflags("-tls1_2"); +$proxy->serverflags("-tls1_2"); +$proxy->cipherc("ECDHE-RSA-AES128-SHA256"); +$proxy->ciphers("ECDHE-RSA-AES128-SHA256"); +$proxy->start() or plan skip_all => "Unable to start up proxy for tests"; +plan tests => 4; +ok($fatal_alert, + "Server rejects ClientHello whose ec_point_formats omits uncompressed"); + +# Test 2: a non-conforming list in the ServerHello -- the client must +# abort with the same error. +$fatal_alert = 0; +$proxy->clear(); +$proxy->filter(\&mangle_ec_point_formats_serverhello); +$proxy->clientflags("-tls1_2"); +$proxy->serverflags("-tls1_2"); +$proxy->cipherc("ECDHE-RSA-AES128-SHA256"); +$proxy->ciphers("ECDHE-RSA-AES128-SHA256"); +$proxy->start(); +ok($fatal_alert, + "Client rejects ServerHello whose ec_point_formats omits uncompressed"); + +# Mutating a ClientHello on the wire breaks the handshake transcript +# (the client and server compute different transcript hashes), so a +# tolerance test can't wait for the handshake to complete -- the +# Finished MAC will fail later regardless of whether the construct +# hook rejected. Instead, the proxy is allowed to run to whatever +# end it finds and we look at $proxy->message_list afterwards: if the +# server sent a ServerHello in response to the mangled ClientHello, +# it accepted the extension; if it rejected, the only server-side +# message in the list will be the fatal alert. +sub server_sent_hello { + my $proxy = shift; + foreach my $msg (@{$proxy->message_list}) { + return 1 if $msg->mt == TLSProxy::Message::MT_SERVER_HELLO; + } + return 0; +} + +# Test 3: TLS 1.3 ignores the ec_point_formats extension entirely. The +# server doesn't emit one in its TLS 1.3 ServerHello, and the +# construct-hook 5.1.2 check requires an ECC TLS <= 1.2 ciphersuite +# to fire. A mangled ClientHello list must therefore leave the server +# willing to send its ServerHello. +SKIP: { + skip "TLS 1.3 disabled", 1 if disabled("tls1_3"); + + $fatal_alert = 0; + $proxy->clear(); + $proxy->filter(\&mangle_ec_point_formats_clienthello); + $proxy->clientflags("-tls1_3"); + $proxy->serverflags("-tls1_3"); + $proxy->start(); + ok(server_sent_hello($proxy), + "TLS 1.3 server tolerates ec_point_formats missing uncompressed"); +} + +# Test 4: At TLS 1.2 with a non-ECC cipher suite, the construct hook +# returns NOT_SENT without inspecting the peer's list. Offer ECDHE-RSA +# alongside DHE-RSA on the client -- the ECDHE entry is enough for the +# client to advertise ec_point_formats -- pin the server to DHE-RSA, +# and confirm a mangled list is tolerated. +SKIP: { + skip "DH disabled", 1 if disabled("dh"); + + $fatal_alert = 0; + $proxy->clear(); + $proxy->filter(\&mangle_ec_point_formats_clienthello); + $proxy->clientflags("-tls1_2"); + $proxy->serverflags("-tls1_2"); + $proxy->cipherc("ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-GCM-SHA256"); + $proxy->ciphers("DHE-RSA-AES128-GCM-SHA256"); + $proxy->start(); + ok(server_sent_hello($proxy), + "TLS 1.2 non-ECC server tolerates ec_point_formats missing uncompressed"); +} + +sub mangle_ec_point_formats_clienthello +{ + my $proxy = shift; + + if ($proxy->flight == 0) { + foreach my $message (@{$proxy->message_list}) { + if ($message->mt == TLSProxy::Message::MT_CLIENT_HELLO) { + $message->set_extension( + TLSProxy::Message::EXT_EC_POINT_FORMATS, + $only_compressed); + $message->repack(); + } + } + return; + } + + my $last_record = @{$proxy->{record_list}}[-1]; + $fatal_alert = 1 + if defined $last_record && $last_record->is_fatal_alert(1); +} + +sub mangle_ec_point_formats_serverhello +{ + my $proxy = shift; + + if ($proxy->flight == 0) { + return; + } elsif ($proxy->flight == 1) { + foreach my $message (@{$proxy->message_list}) { + if ($message->mt == TLSProxy::Message::MT_SERVER_HELLO) { + $message->set_extension( + TLSProxy::Message::EXT_EC_POINT_FORMATS, + $only_compressed); + $message->repack(); + } + } + return; + } + + my $last_record = @{$proxy->{record_list}}[-1]; + $fatal_alert = 1 + if defined $last_record && $last_record->is_fatal_alert(0); +} diff --git a/test/recipes/80-test_ssl_new.t b/test/recipes/80-test_ssl_new.t index a2d69e3c47..8516652f97 100644 --- a/test/recipes/80-test_ssl_new.t +++ b/test/recipes/80-test_ssl_new.t @@ -42,7 +42,7 @@ if (defined $ENV{SSL_TESTS}) { @conf_srcs = glob(srctop_file("test", "ssl-tests", "*.cnf.in")); # We hard-code the number of tests to double-check that the globbing above # finds all files as expected. - plan tests => 31; + plan tests => 32; } map { s/;.*// } @conf_srcs if $^O eq "VMS"; my @conf_files = map { basename($_, ".in") } @conf_srcs; @@ -100,6 +100,7 @@ my %conf_dependent_tests = ( "28-seclevel.cnf" => disabled("tls1_2") || $no_ecx, "30-extended-master-secret.cnf" => disabled("tls1_2"), "32-compressed-certificate.cnf" => disabled("comp") || disabled("tls1_3"), + "33-compressed-spki.cnf" => disabled("tls1_2") || disabled("tls1_3") || $no_ec, ); # Add your test here if it should be skipped for some compile-time @@ -135,6 +136,7 @@ my %skip = ( "26-tls13_client_auth.cnf" => disabled("tls1_3") || ($no_ec && $no_dh), "29-dtls-sctp-label-bug.cnf" => disabled("sctp") || disabled("sock"), "32-compressed-certificate.cnf" => disabled("comp") || disabled("tls1_3"), + "33-compressed-spki.cnf" => disabled("tls1_2") || disabled("tls1_3") || $no_ec, ); foreach my $conf (@conf_files) { diff --git a/test/ssl-tests/33-compressed-spki.cnf b/test/ssl-tests/33-compressed-spki.cnf new file mode 100644 index 0000000000..fd9816a27e --- /dev/null +++ b/test/ssl-tests/33-compressed-spki.cnf @@ -0,0 +1,76 @@ +# Generated with generate_ssl_tests.pl + +num_tests = 2 + +test-0 = 0-tls12-compressed-spki +test-1 = 1-tls13-compressed-spki +# =========================================================== + +[0-tls12-compressed-spki] +ssl_conf = 0-tls12-compressed-spki-ssl + +[0-tls12-compressed-spki-ssl] +server = 0-tls12-compressed-spki-server +client = 0-tls12-compressed-spki-client + +[0-tls12-compressed-spki-server] +Certificate = ${ENV::TEST_CERTS_DIR}/server-ec-compressed-cert.pem +CipherString = DEFAULT +ClientCAFile = ${ENV::TEST_CERTS_DIR}/p384-root.pem +MaxProtocol = TLSv1.2 +MinProtocol = TLSv1.2 +PrivateKey = ${ENV::TEST_CERTS_DIR}/server-ec-compressed-key.pem +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/p384-root.pem +VerifyMode = Require + +[0-tls12-compressed-spki-client] +Certificate = ${ENV::TEST_CERTS_DIR}/server-ec-compressed-cert.pem +CipherString = ECDHE-ECDSA-AES128-GCM-SHA256 +MaxProtocol = TLSv1.2 +MinProtocol = TLSv1.2 +PrivateKey = ${ENV::TEST_CERTS_DIR}/server-ec-compressed-key.pem +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/p384-root.pem +VerifyMode = Peer + +[test-0] +ExpectedClientCertType = P-256 +ExpectedProtocol = TLSv1.2 +ExpectedResult = Success +ExpectedServerCertType = P-256 + + +# =========================================================== + +[1-tls13-compressed-spki] +ssl_conf = 1-tls13-compressed-spki-ssl + +[1-tls13-compressed-spki-ssl] +server = 1-tls13-compressed-spki-server +client = 1-tls13-compressed-spki-client + +[1-tls13-compressed-spki-server] +Certificate = ${ENV::TEST_CERTS_DIR}/server-ec-compressed-cert.pem +CipherString = DEFAULT +ClientCAFile = ${ENV::TEST_CERTS_DIR}/p384-root.pem +MaxProtocol = TLSv1.3 +MinProtocol = TLSv1.3 +PrivateKey = ${ENV::TEST_CERTS_DIR}/server-ec-compressed-key.pem +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/p384-root.pem +VerifyMode = Require + +[1-tls13-compressed-spki-client] +Certificate = ${ENV::TEST_CERTS_DIR}/server-ec-compressed-cert.pem +CipherString = DEFAULT +MaxProtocol = TLSv1.3 +MinProtocol = TLSv1.3 +PrivateKey = ${ENV::TEST_CERTS_DIR}/server-ec-compressed-key.pem +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/p384-root.pem +VerifyMode = Peer + +[test-1] +ExpectedClientCertType = P-256 +ExpectedProtocol = TLSv1.3 +ExpectedResult = Success +ExpectedServerCertType = P-256 + + diff --git a/test/ssl-tests/33-compressed-spki.cnf.in b/test/ssl-tests/33-compressed-spki.cnf.in new file mode 100644 index 0000000000..06302a46a9 --- /dev/null +++ b/test/ssl-tests/33-compressed-spki.cnf.in @@ -0,0 +1,81 @@ +# -*- mode: perl; -*- +# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + + +## End-to-end check that compressed-form EC leaf certificates +## (server-ec-compressed-cert.pem, P-256 named curve, SPKI bit-string +## leading byte 0x02 or 0x03, anchored to the P-384 EC root) work at +## both TLS 1.2 and TLS 1.3 in both directions: the client presents +## the same compressed leaf to a server that requires client +## authentication, exercising X.509 acceptance of compressed point +## form on both sides simultaneously. The ec_point_formats extension +## no longer affects X.509 cert selection or acceptance, so neither +## peer opts into LegacyECPointFormats; the default ec_point_formats +## lists ('uncompressed' only) and the compressed leaves coexist. + +package ssltests; +use OpenSSL::Test::Utils; + +our @tests = (); + +unless (disabled("ec") || disabled("tls1_2") || disabled("tls1_3")) { +@tests = ( + { + name => "tls12-compressed-spki", + server => { + "Certificate" => test_pem("server-ec-compressed-cert.pem"), + "PrivateKey" => test_pem("server-ec-compressed-key.pem"), + "VerifyCAFile" => test_pem("p384-root.pem"), + "ClientCAFile" => test_pem("p384-root.pem"), + "VerifyMode" => "Require", + "MinProtocol" => "TLSv1.2", + "MaxProtocol" => "TLSv1.2", + }, + client => { + "Certificate" => test_pem("server-ec-compressed-cert.pem"), + "PrivateKey" => test_pem("server-ec-compressed-key.pem"), + "VerifyCAFile" => test_pem("p384-root.pem"), + "MinProtocol" => "TLSv1.2", + "MaxProtocol" => "TLSv1.2", + "CipherString" => "ECDHE-ECDSA-AES128-GCM-SHA256", + }, + test => { + "ExpectedResult" => "Success", + "ExpectedProtocol" => "TLSv1.2", + "ExpectedServerCertType" => "P-256", + "ExpectedClientCertType" => "P-256", + }, + }, + + { + name => "tls13-compressed-spki", + server => { + "Certificate" => test_pem("server-ec-compressed-cert.pem"), + "PrivateKey" => test_pem("server-ec-compressed-key.pem"), + "VerifyCAFile" => test_pem("p384-root.pem"), + "ClientCAFile" => test_pem("p384-root.pem"), + "VerifyMode" => "Require", + "MinProtocol" => "TLSv1.3", + "MaxProtocol" => "TLSv1.3", + }, + client => { + "Certificate" => test_pem("server-ec-compressed-cert.pem"), + "PrivateKey" => test_pem("server-ec-compressed-key.pem"), + "VerifyCAFile" => test_pem("p384-root.pem"), + "MinProtocol" => "TLSv1.3", + "MaxProtocol" => "TLSv1.3", + }, + test => { + "ExpectedResult" => "Success", + "ExpectedProtocol" => "TLSv1.3", + "ExpectedServerCertType" => "P-256", + "ExpectedClientCertType" => "P-256", + }, + }, +); +} diff --git a/test/sslapitest.c b/test/sslapitest.c index 1c106355c6..8baf38b93d 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -12439,7 +12439,7 @@ static int test_legacy_ec_point_formats(void) { SSL_CTX *cctx = NULL, *sctx = NULL; SSL *clientssl = NULL, *serverssl = NULL; - const char *pformats = NULL; + const unsigned char *pformats = NULL; int nformats; int testresult = 0; diff --git a/util/missingmacro.txt b/util/missingmacro.txt index 50b4b17b15..20e6077c6b 100644 --- a/util/missingmacro.txt +++ b/util/missingmacro.txt @@ -137,7 +137,6 @@ SSL_get0_certificate_types(3) SSL_CTX_set1_client_certificate_types(3) SSL_set1_client_certificate_types(3) SSL_get0_raw_cipherlist(3) -SSL_get0_ec_point_formats(3) SSL_CTX_need_tmp_RSA(3) SSL_CTX_set_tmp_rsa(3) SSL_need_tmp_RSA(3) diff --git a/util/other.syms b/util/other.syms index 564704dd87..114e25a348 100644 --- a/util/other.syms +++ b/util/other.syms @@ -647,6 +647,7 @@ SSL_clear_mode define SSL_disable_ct define SSL_get0_chain_certs define SSL_get0_iana_groups define +SSL_get0_ec_point_formats define SSL_get0_session define SSL_get0_chain_cert_store define SSL_get0_verify_cert_store define From e06fe987699b23984bee7e738a3f393b05359af0 Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Wed, 17 Jun 2026 16:58:11 -0600 Subject: [PATCH 211/349] Drop Watcom C compiler support. The only Watcom-specific code in the tree was a _vsntprintf mapping in crypto/cryptlib.c. There is no Watcom entry in Configurations/, no CI job builds with Watcom, and the tree has had no other Watcom-aware code in many years. The _vsntprintf symbol is supplied by on every supported Windows toolchain (MSVC, MinGW), so the conditional fallback is dead. Reviewed-by: Milan Broz Reviewed-by: Tomas Mraz Reviewed-by: Neil Horman MergeDate: Fri Jul 10 11:47:15 2026 (Merged from https://github.com/openssl/openssl/pull/31677) --- crypto/cryptlib.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/crypto/cryptlib.c b/crypto/cryptlib.c index d62ebfae0b..07b244cb35 100644 --- a/crypto/cryptlib.c +++ b/crypto/cryptlib.c @@ -15,13 +15,6 @@ #if defined(_WIN32) && !defined(OPENSSL_SYS_UEFI) #include #include -#ifdef __WATCOMC__ -#if defined(_UNICODE) || defined(__UNICODE__) -#define _vsntprintf _vsnwprintf -#else -#define _vsntprintf _vsnprintf -#endif -#endif #ifdef _MSC_VER #define alloca _alloca #endif From a7241e862e7a3faa3bd0f254ab6f968cbf98f6f4 Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Wed, 17 Jun 2026 17:05:05 -0600 Subject: [PATCH 212/349] We no longer need to test for GCC versions this old The minimum gcc to compile our code appears to be version 9, and we are now C99. All these older checks become a tautology on anything that will build a modern OpenSSL tree, so clean this up. Reviewed-by: Milan Broz Reviewed-by: Tomas Mraz Reviewed-by: Neil Horman MergeDate: Fri Jul 10 11:47:17 2026 (Merged from https://github.com/openssl/openssl/pull/31677) --- Configurations/10-main.conf | 1 - crypto/armcap.c | 2 +- crypto/bn/asm/x86_64-gcc.c | 2 +- crypto/bn/bn_div.c | 2 +- crypto/bn/bn_local.h | 10 +++++----- crypto/chacha/chacha_enc.c | 2 +- crypto/des/des_local.h | 2 +- crypto/ec/curve448/curve448.c | 2 +- crypto/ppccap.c | 2 +- crypto/rc5/rc5_local.h | 2 +- crypto/riscvcap.c | 2 +- crypto/sha/sha256.c | 2 +- crypto/sha/sha512.c | 2 +- crypto/sm3/sm3_local.h | 2 +- crypto/sparcv9cap.c | 2 +- crypto/whrlpool/wp_block.c | 2 +- include/crypto/modes.h | 2 +- include/openssl/e_os2.h | 4 ++-- include/openssl/macros.h | 12 +----------- providers/fips/self_test_kats.c | 2 +- providers/implementations/rands/seeding/rand_unix.c | 2 +- 21 files changed, 25 insertions(+), 36 deletions(-) diff --git a/Configurations/10-main.conf b/Configurations/10-main.conf index 6b2d1cffe4..4dd4250d1b 100644 --- a/Configurations/10-main.conf +++ b/Configurations/10-main.conf @@ -906,7 +906,6 @@ my %targets = ( perlasm_scheme => 'void', }, "linux64-sparcv9" => { - # GCC 3.1 is a requirement inherit_from => [ "linux-generic64" ], cflags => add("-m64 -mcpu=ultrasparc"), cxxflags => add("-m64 -mcpu=ultrasparc"), diff --git a/crypto/armcap.c b/crypto/armcap.c index 1b8ebb5025..31b17f06a5 100644 --- a/crypto/armcap.c +++ b/crypto/armcap.c @@ -69,7 +69,7 @@ uint32_t OPENSSL_rdtsc(void) /* First determine if getauxval() is available (OSSL_IMPLEMENT_GETAUXVAL) */ -#if defined(__GNUC__) && __GNUC__ >= 2 +#if defined(__GNUC__) void OPENSSL_cpuid_setup(void) __attribute__((constructor)); #endif diff --git a/crypto/bn/asm/x86_64-gcc.c b/crypto/bn/asm/x86_64-gcc.c index 7edb77806e..03299a7b72 100644 --- a/crypto/bn/asm/x86_64-gcc.c +++ b/crypto/bn/asm/x86_64-gcc.c @@ -8,7 +8,7 @@ */ #include "../bn_local.h" -#if !(defined(__GNUC__) && __GNUC__ >= 2) +#if !defined(__GNUC__) /* clang-format off */ # include "../bn_asm.c" /* kind of dirty hack for Sun Studio */ /* clang-format on */ diff --git a/crypto/bn/bn_div.c b/crypto/bn/bn_div.c index 2b2ace05e3..a731b2d37d 100644 --- a/crypto/bn/bn_div.c +++ b/crypto/bn/bn_div.c @@ -160,7 +160,7 @@ static int bn_left_align(BIGNUM *num) #if !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) \ && !defined(PEDANTIC) && !defined(BN_DIV3W) -#if defined(__GNUC__) && __GNUC__ >= 2 +#if defined(__GNUC__) #if defined(__i386) || defined(__i386__) /*- * There were two reasons for implementing this template: diff --git a/crypto/bn/bn_local.h b/crypto/bn/bn_local.h index db02711132..4602cdcaba 100644 --- a/crypto/bn/bn_local.h +++ b/crypto/bn/bn_local.h @@ -380,7 +380,7 @@ struct bn_gencb_st { #if defined(__DECC) #include #define BN_UMULT_HIGH(a, b) (BN_ULONG)asm("umulh %a0,%a1,%v0", (a), (b)) -#elif defined(__GNUC__) && __GNUC__ >= 2 +#elif defined(__GNUC__) #define BN_UMULT_HIGH(a, b) ({ \ register BN_ULONG ret; \ asm ("umulh %1,%2,%0" \ @@ -389,7 +389,7 @@ struct bn_gencb_st { ret; }) #endif /* compiler */ #elif defined(_ARCH_PPC64) && defined(SIXTY_FOUR_BIT_LONG) -#if defined(__GNUC__) && __GNUC__ >= 2 +#if defined(__GNUC__) #define BN_UMULT_HIGH(a, b) ({ \ register BN_ULONG ret; \ asm ("mulhdu %0,%1,%2" \ @@ -398,7 +398,7 @@ struct bn_gencb_st { ret; }) #endif /* compiler */ #elif (defined(__x86_64) || defined(__x86_64__)) && (defined(SIXTY_FOUR_BIT_LONG) || defined(SIXTY_FOUR_BIT)) -#if defined(__GNUC__) && __GNUC__ >= 2 +#if defined(__GNUC__) #define BN_UMULT_HIGH(a, b) ({ \ register BN_ULONG ret,discard; \ asm ("mulq %3" \ @@ -422,7 +422,7 @@ unsigned __int64 _umul128(unsigned __int64 a, unsigned __int64 b, #define BN_UMULT_LOHI(low, high, a, b) ((low) = _umul128((a), (b), &(high))) #endif #elif defined(__mips) && (defined(SIXTY_FOUR_BIT) || defined(SIXTY_FOUR_BIT_LONG)) -#if defined(__GNUC__) && __GNUC__ >= 2 +#if defined(__GNUC__) #define BN_UMULT_HIGH(a, b) ({ \ register BN_ULONG ret; \ asm ("dmultu %1,%2" \ @@ -435,7 +435,7 @@ unsigned __int64 _umul128(unsigned __int64 a, unsigned __int64 b, : "r"(a), "r"(b)); #endif #elif defined(__aarch64__) && defined(SIXTY_FOUR_BIT_LONG) -#if defined(__GNUC__) && __GNUC__ >= 2 +#if defined(__GNUC__) #define BN_UMULT_HIGH(a, b) ({ \ register BN_ULONG ret; \ asm ("umulh %0,%1,%2" \ diff --git a/crypto/chacha/chacha_enc.c b/crypto/chacha/chacha_enc.c index e9a4d3263a..c7c36b9c54 100644 --- a/crypto/chacha/chacha_enc.c +++ b/crypto/chacha/chacha_enc.c @@ -24,7 +24,7 @@ typedef union { #define ROTATE(v, n) (((v) << (n)) | ((v) >> (32 - (n)))) #ifndef PEDANTIC -#if defined(__GNUC__) && __GNUC__ >= 2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) +#if defined(__GNUC__) && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) #if defined(__riscv_zbb) || defined(__riscv_zbkb) #if __riscv_xlen == 64 #undef ROTATE diff --git a/crypto/des/des_local.h b/crypto/des/des_local.h index 0fee059ec4..fa368c359c 100644 --- a/crypto/des/des_local.h +++ b/crypto/des/des_local.h @@ -32,7 +32,7 @@ #define ROTATE(a, n) (_lrotr(a, n)) #elif defined(__ICC) #define ROTATE(a, n) (_rotr(a, n)) -#elif defined(__GNUC__) && __GNUC__ >= 2 && !defined(__STRICT_ANSI__) && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) && !defined(PEDANTIC) +#elif defined(__GNUC__) && !defined(__STRICT_ANSI__) && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) && !defined(PEDANTIC) #if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__) #define ROTATE(a, n) ({ \ register unsigned int ret; \ diff --git a/crypto/ec/curve448/curve448.c b/crypto/ec/curve448/curve448.c index 1a31f86355..29edb317f1 100644 --- a/crypto/ec/curve448/curve448.c +++ b/crypto/ec/curve448/curve448.c @@ -502,7 +502,7 @@ struct smvt_control { int power, addend; }; -#if defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)) +#if defined(__GNUC__) #define NUMTRAILINGZEROS __builtin_ctz #else #define NUMTRAILINGZEROS numtrailingzeros diff --git a/crypto/ppccap.c b/crypto/ppccap.c index a2acf6b6ed..e029eb3051 100644 --- a/crypto/ppccap.c +++ b/crypto/ppccap.c @@ -134,7 +134,7 @@ static unsigned long getauxval(unsigned long key) #define HWCAP_ARCH_3_00 (1U << 23) #define HWCAP_ARCH_3_1 (1U << 18) -#if defined(__GNUC__) && __GNUC__ >= 2 +#if defined(__GNUC__) __attribute__((constructor)) #endif void OPENSSL_cpuid_setup(void) diff --git a/crypto/rc5/rc5_local.h b/crypto/rc5/rc5_local.h index a1fbe61fec..7b5f8c847b 100644 --- a/crypto/rc5/rc5_local.h +++ b/crypto/rc5/rc5_local.h @@ -19,7 +19,7 @@ #elif defined(__ICC) #define ROTATE_l32(a, n) _rotl(a, n) #define ROTATE_r32(a, n) _rotr(a, n) -#elif defined(__GNUC__) && __GNUC__ >= 2 && !defined(__STRICT_ANSI__) && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) && !defined(PEDANTIC) +#elif defined(__GNUC__) && !defined(__STRICT_ANSI__) && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) && !defined(PEDANTIC) #if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__) #define ROTATE_l32(a, n) ({ \ register unsigned int ret; \ diff --git a/crypto/riscvcap.c b/crypto/riscvcap.c index cdec9a0216..760fc5b0dd 100644 --- a/crypto/riscvcap.c +++ b/crypto/riscvcap.c @@ -129,7 +129,7 @@ size_t riscv_vlen(void) return vlen; } -#if defined(__GNUC__) && __GNUC__ >= 2 +#if defined(__GNUC__) __attribute__((constructor)) #endif void OPENSSL_cpuid_setup(void) diff --git a/crypto/sha/sha256.c b/crypto/sha/sha256.c index eab9487622..8cbbfdbb30 100644 --- a/crypto/sha/sha256.c +++ b/crypto/sha/sha256.c @@ -164,7 +164,7 @@ static const SHA_LONG K256[64] = { }; #ifndef PEDANTIC -#if defined(__GNUC__) && __GNUC__ >= 2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) +#if defined(__GNUC__) && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) #if defined(__riscv_zknh) #define Sigma0(x) ({ MD32_REG_T ret; \ asm ("sha256sum0 %0, %1" \ diff --git a/crypto/sha/sha512.c b/crypto/sha/sha512.c index 6ccc0070e0..9ba32f2926 100644 --- a/crypto/sha/sha512.c +++ b/crypto/sha/sha512.c @@ -343,7 +343,7 @@ static const SHA_LONG64 K512[80] = { }; #ifndef PEDANTIC -#if defined(__GNUC__) && __GNUC__ >= 2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) +#if defined(__GNUC__) && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) #if defined(__x86_64) || defined(__x86_64__) #define ROTR(a, n) ({ SHA_LONG64 ret; \ asm ("rorq %1,%0" \ diff --git a/crypto/sm3/sm3_local.h b/crypto/sm3/sm3_local.h index 6cb8dca61b..41639b3c95 100644 --- a/crypto/sm3/sm3_local.h +++ b/crypto/sm3/sm3_local.h @@ -77,7 +77,7 @@ void ossl_sm3_transform(SM3_CTX *c, const unsigned char *data); /* clang-format on */ #ifndef PEDANTIC -#if defined(__GNUC__) && __GNUC__ >= 2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) +#if defined(__GNUC__) && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) #if defined(__riscv_zksh) #define P0(x) ({ MD32_REG_T ret; \ asm ("sm3p0 %0, %1" \ diff --git a/crypto/sparcv9cap.c b/crypto/sparcv9cap.c index c9cc2b9575..cea44ada9b 100644 --- a/crypto/sparcv9cap.c +++ b/crypto/sparcv9cap.c @@ -71,7 +71,7 @@ static void common_handler(int sig) } #if defined(__sun) && defined(__SVR4) -#if defined(__GNUC__) && __GNUC__ >= 2 +#if defined(__GNUC__) extern unsigned int getisax(unsigned int vec[], unsigned int sz) __attribute__((weak)); #elif defined(__SUNPRO_C) #pragma weak getisax diff --git a/crypto/whrlpool/wp_block.c b/crypto/whrlpool/wp_block.c index 13b3f7b37f..62fa849dad 100644 --- a/crypto/whrlpool/wp_block.c +++ b/crypto/whrlpool/wp_block.c @@ -103,7 +103,7 @@ typedef uint64_t u64_aX; #pragma intrinsic(_rotl64) #define ROTATE(a, n) _rotl64((a), n) #endif -#elif defined(__GNUC__) && __GNUC__ >= 2 +#elif defined(__GNUC__) #if defined(__x86_64) || defined(__x86_64__) #if defined(L_ENDIAN) #define ROTATE(a, n) ({ uint64_t ret; asm ("rolq %1,%0" \ diff --git a/include/crypto/modes.h b/include/crypto/modes.h index d749eed77d..baa0f3a6c1 100644 --- a/include/crypto/modes.h +++ b/include/crypto/modes.h @@ -31,7 +31,7 @@ #endif #if !defined(PEDANTIC) && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) -#if defined(__GNUC__) && __GNUC__ >= 2 +#if defined(__GNUC__) #if defined(__x86_64) || defined(__x86_64__) #define BSWAP8(x) ({ uint64_t ret_=(x); \ asm ("bswapq %0" \ diff --git a/include/openssl/e_os2.h b/include/openssl/e_os2.h index 5f390037d7..f3cda73da1 100644 --- a/include/openssl/e_os2.h +++ b/include/openssl/e_os2.h @@ -253,7 +253,7 @@ typedef uint64_t ossl_uintmax_t; #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* just use inline */ #define ossl_inline inline -#elif defined(__GNUC__) && __GNUC__ >= 2 +#elif defined(__GNUC__) #define ossl_inline __inline__ #elif defined(_MSC_VER) /* @@ -271,7 +271,7 @@ typedef uint64_t ossl_uintmax_t; #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__cplusplus) #define ossl_noreturn _Noreturn -#elif defined(__GNUC__) && __GNUC__ >= 2 +#elif defined(__GNUC__) #define ossl_noreturn __attribute__((noreturn)) #else #define ossl_noreturn diff --git a/include/openssl/macros.h b/include/openssl/macros.h index 40f067c8ab..7e820875e2 100644 --- a/include/openssl/macros.h +++ b/include/openssl/macros.h @@ -53,21 +53,11 @@ __pragma(warning(push)) __pragma(warning(disable : 4996)) #define OSSL_END_ALLOW_DEPRECATED __pragma(warning(pop)) #elif defined(__GNUC__) -/* - * According to GCC documentation, deprecations with message appeared in - * GCC 4.5.0 - */ -#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) #define OSSL_DEPRECATED(since) \ __attribute__((deprecated("Since OpenSSL " #since))) #define OSSL_DEPRECATED_FOR(since, message) \ __attribute__((deprecated("Since OpenSSL " #since ";" message))) #define OSSL_DEPRECATED_MESSAGE(message) __attribute__((deprecated(message))) -#elif __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 0) -#define OSSL_DEPRECATED(since) __attribute__((deprecated)) -#define OSSL_DEPRECATED_FOR(since, message) __attribute__((deprecated)) -#define OSSL_DEPRECATED_MESSAGE(message) __attribute__((deprecated)) -#endif #define OSSL_BEGIN_ALLOW_DEPRECATED \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") @@ -376,7 +366,7 @@ #if defined(__STDC_VERSION__) #if __STDC_VERSION__ >= 199901L #define OPENSSL_FUNC __func__ -#elif defined(__GNUC__) && __GNUC__ >= 2 +#elif defined(__GNUC__) #define OPENSSL_FUNC __FUNCTION__ #endif #elif defined(_MSC_VER) diff --git a/providers/fips/self_test_kats.c b/providers/fips/self_test_kats.c index 17f79b9d0c..f2d5c439d9 100644 --- a/providers/fips/self_test_kats.c +++ b/providers/fips/self_test_kats.c @@ -213,7 +213,7 @@ err: return ret; } -#if defined(__GNUC__) && __GNUC__ >= 4 +#if defined(__GNUC__) #define SENTINEL __attribute__((sentinel)) #endif diff --git a/providers/implementations/rands/seeding/rand_unix.c b/providers/implementations/rands/seeding/rand_unix.c index 95742eb848..67b38cb719 100644 --- a/providers/implementations/rands/seeding/rand_unix.c +++ b/providers/implementations/rands/seeding/rand_unix.c @@ -354,7 +354,7 @@ static ssize_t syscall_random(void *buf, size_t buflen) * internally. So we need to check errno for ENOSYS */ #if !defined(__DragonFly__) && !defined(__NetBSD__) && !defined(__FreeBSD__) -#if defined(__GNUC__) && __GNUC__ >= 2 && defined(__ELF__) && !defined(__hpux) +#if defined(__GNUC__) && defined(__ELF__) && !defined(__hpux) extern int getentropy(void *buffer, size_t length) __attribute__((weak)); if (getentropy != NULL) { From aa2b1432b3245b81a08852380b237b5795b46a96 Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Thu, 18 Jun 2026 11:24:17 -0600 Subject: [PATCH 213/349] Simplify printf-attribute guards in bio.h.in and test/testutil/output.h. Now that C99 is the base level, and we are no longer supporting old GCC's, The ossl_bio__printf__ / ossl_test__printf__ indirection only existed to pick the gnu_printf attribute over the printf attribute for MinGW's MS-CRT printf, but MinGW is already excluded by the outer guard, so we can use the modern attribute everywhere this code runs. Spotted by idrassi on review, thanks!. Reviewed-by: Milan Broz Reviewed-by: Tomas Mraz Reviewed-by: Neil Horman MergeDate: Fri Jul 10 11:47:19 2026 (Merged from https://github.com/openssl/openssl/pull/31677) --- include/openssl/bio.h.in | 23 +++++------------------ test/testutil.h | 9 +-------- test/testutil/output.h | 31 +++++++++---------------------- 3 files changed, 15 insertions(+), 48 deletions(-) diff --git a/include/openssl/bio.h.in b/include/openssl/bio.h.in index 7912dcaa82..c2990efde4 100644 --- a/include/openssl/bio.h.in +++ b/include/openssl/bio.h.in @@ -932,33 +932,20 @@ void BIO_copy_next_retry(BIO *b); */ #define ossl_bio__attr__(x) -#if defined(__GNUC__) && defined(__STDC_VERSION__) \ - && !defined(__MINGW32__) && !defined(__MINGW64__) \ +#if defined(__GNUC__) && !defined(__MINGW32__) && !defined(__MINGW64__) \ && !defined(__APPLE__) -/* - * Because we support the 'z' modifier, which made its appearance in C99, - * we can't use __attribute__ with pre C99 dialects. - */ -#if __STDC_VERSION__ >= 199901L #undef ossl_bio__attr__ #define ossl_bio__attr__ __attribute__ -#if __GNUC__ * 10 + __GNUC_MINOR__ >= 44 -#define ossl_bio__printf__ __gnu_printf__ -#else -#define ossl_bio__printf__ __printf__ -#endif -#endif #endif int BIO_printf(BIO *bio, const char *format, ...) - ossl_bio__attr__((__format__(ossl_bio__printf__, 2, 3))); + ossl_bio__attr__((__format__(__printf__, 2, 3))); int BIO_vprintf(BIO *bio, const char *format, va_list args) - ossl_bio__attr__((__format__(ossl_bio__printf__, 2, 0))); + ossl_bio__attr__((__format__(__printf__, 2, 0))); int BIO_snprintf(char *buf, size_t n, const char *format, ...) - ossl_bio__attr__((__format__(ossl_bio__printf__, 3, 4))); + ossl_bio__attr__((__format__(__printf__, 3, 4))); int BIO_vsnprintf(char *buf, size_t n, const char *format, va_list args) - ossl_bio__attr__((__format__(ossl_bio__printf__, 3, 0))); + ossl_bio__attr__((__format__(__printf__, 3, 0))); #undef ossl_bio__attr__ -#undef ossl_bio__printf__ BIO_METHOD *BIO_meth_new(int type, const char *name); void BIO_meth_free(BIO_METHOD *biom); diff --git a/test/testutil.h b/test/testutil.h index b38791b287..f606ff1f75 100644 --- a/test/testutil.h +++ b/test/testutil.h @@ -322,18 +322,11 @@ const OPTIONS *test_get_options(void); */ #define PRINTF_FORMAT(a, b) -#if defined(__GNUC__) && defined(__STDC_VERSION__) \ - && !defined(__MINGW32__) && !defined(__MINGW64__) \ +#if defined(__GNUC__) && !defined(__MINGW32__) && !defined(__MINGW64__) \ && !defined(__APPLE__) -/* - * Because we support the 'z' modifier, which made its appearance in C99, - * we can't use __attribute__ with pre C99 dialects. - */ -#if __STDC_VERSION__ >= 199901L #undef PRINTF_FORMAT #define PRINTF_FORMAT(a, b) __attribute__((format(printf, a, b))) #endif -#endif #define DECLARE_COMPARISON(type, name, opname) \ int test_##name##_##opname(const char *, int, \ diff --git a/test/testutil/output.h b/test/testutil/output.h index cee3026b11..e4f6058ac7 100644 --- a/test/testutil/output.h +++ b/test/testutil/output.h @@ -13,22 +13,10 @@ #include #define ossl_test__attr__(x) -#if defined(__GNUC__) && defined(__STDC_VERSION__) \ - && !defined(__MINGW32__) && !defined(__MINGW64__) \ +#if defined(__GNUC__) && !defined(__MINGW32__) && !defined(__MINGW64__) \ && !defined(__APPLE__) -/* - * Because we support the 'z' modifier, which made its appearance in C99, - * we can't use __attribute__ with pre C99 dialects. - */ -#if __STDC_VERSION__ >= 199901L #undef ossl_test__attr__ #define ossl_test__attr__ __attribute__ -#if __GNUC__ * 10 + __GNUC_MINOR__ >= 44 -#define ossl_test__printf__ __gnu_printf__ -#else -#define ossl_test__printf__ __printf__ -#endif -#endif #endif /* * The basic I/O functions used internally by the test framework. These @@ -39,13 +27,13 @@ void test_close_streams(void); void test_adjust_streams_tap_level(int level); /* The following ALL return the number of characters written */ int test_vprintf_stdout(const char *fmt, va_list ap) - ossl_test__attr__((__format__(ossl_test__printf__, 1, 0))); + ossl_test__attr__((__format__(__printf__, 1, 0))); int test_vprintf_tapout(const char *fmt, va_list ap) - ossl_test__attr__((__format__(ossl_test__printf__, 1, 0))); + ossl_test__attr__((__format__(__printf__, 1, 0))); int test_vprintf_stderr(const char *fmt, va_list ap) - ossl_test__attr__((__format__(ossl_test__printf__, 1, 0))); + ossl_test__attr__((__format__(__printf__, 1, 0))); int test_vprintf_taperr(const char *fmt, va_list ap) - ossl_test__attr__((__format__(ossl_test__printf__, 1, 0))); + ossl_test__attr__((__format__(__printf__, 1, 0))); /* These return failure or success */ int test_flush_stdout(void); int test_flush_tapout(void); @@ -54,15 +42,14 @@ int test_flush_taperr(void); /* Commodity functions. There's no need to override these */ int test_printf_stdout(const char *fmt, ...) - ossl_test__attr__((__format__(ossl_test__printf__, 1, 2))); + ossl_test__attr__((__format__(__printf__, 1, 2))); int test_printf_tapout(const char *fmt, ...) - ossl_test__attr__((__format__(ossl_test__printf__, 1, 2))); + ossl_test__attr__((__format__(__printf__, 1, 2))); int test_printf_stderr(const char *fmt, ...) - ossl_test__attr__((__format__(ossl_test__printf__, 1, 2))); + ossl_test__attr__((__format__(__printf__, 1, 2))); int test_printf_taperr(const char *fmt, ...) - ossl_test__attr__((__format__(ossl_test__printf__, 1, 2))); + ossl_test__attr__((__format__(__printf__, 1, 2))); -#undef ossl_test__printf__ #undef ossl_test__attr__ #endif /* OSSL_TESTUTIL_OUTPUT_H */ From b72ee09a45109faec2e46865d9ef648525ccec78 Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Thu, 18 Jun 2026 11:28:28 -0600 Subject: [PATCH 214/349] Drop missed unneded GNUC>=2 check Spotted by idrassi on review. thanks! Reviewed-by: Milan Broz Reviewed-by: Tomas Mraz Reviewed-by: Neil Horman MergeDate: Fri Jul 10 11:47:21 2026 (Merged from https://github.com/openssl/openssl/pull/31677) --- include/crypto/md32_common.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/crypto/md32_common.inc b/include/crypto/md32_common.inc index 06293681b4..3dea481c56 100644 --- a/include/crypto/md32_common.inc +++ b/include/crypto/md32_common.inc @@ -104,7 +104,7 @@ #define ROTATE(a, n) (((a) << (n)) | (((a) & 0xffffffff) >> (32 - (n)))) #ifndef PEDANTIC -#if defined(__GNUC__) && __GNUC__ >= 2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) +#if defined(__GNUC__) && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) #if defined(__riscv_zbb) || defined(__riscv_zbkb) #if __riscv_xlen == 64 #undef ROTATE From 09d12ef1d740f0330ac2a04f281fb2838df0db7b Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Sun, 17 Jan 2021 02:04:08 +0100 Subject: [PATCH 215/349] Allow MinGW-w64 builds to use BCryptGenRandom MinGW-w64 has provided bcrypt headers and import libraries since version 2.0, but OpenSSL only enabled the BCryptGenRandom seeding path for supported MSVC builds. Enable the existing direct BCryptGenRandom flow for MinGW-w64 when targeting Windows Vista or newer, and link MinGW builds with bcrypt alongside the other Windows import libraries. Use __MINGW64_VERSION_MAJOR to detect MinGW-w64 because it is defined by both the 32-bit and 64-bit MinGW-w64 toolchains. Builds targeting older Windows versions keep the CryptoAPI fallback because USE_BCRYPTGENRANDOM remains disabled when _WIN32_WINNT is below 0x0600. Fixes #13878 Reviewed-by: Milan Broz Reviewed-by: Nikola Pajkovsky Reviewed-by: Neil Horman MergeDate: Fri Jul 10 12:05:09 2026 (Merged from https://github.com/openssl/openssl/pull/13882) --- Configurations/10-main.conf | 2 +- providers/implementations/rands/seeding/rand_win.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Configurations/10-main.conf b/Configurations/10-main.conf index 4dd4250d1b..390aac5011 100644 --- a/Configurations/10-main.conf +++ b/Configurations/10-main.conf @@ -1591,7 +1591,7 @@ my %targets = ( cppflags => combine("-DUNICODE -D_UNICODE -DWIN32_LEAN_AND_MEAN", threads("-D_MT")), lib_cppflags => "-DL_ENDIAN", - ex_libs => add("-lws2_32 -lgdi32 -lcrypt32"), + ex_libs => add("-lws2_32 -lgdi32 -lcrypt32 -lbcrypt"), thread_scheme => "winthreads", dso_scheme => "win32", shared_target => "mingw-shared", diff --git a/providers/implementations/rands/seeding/rand_win.c b/providers/implementations/rands/seeding/rand_win.c index acc35936f0..e1350f7d80 100644 --- a/providers/implementations/rands/seeding/rand_win.c +++ b/providers/implementations/rands/seeding/rand_win.c @@ -21,8 +21,9 @@ #endif /* On Windows Vista or higher use BCrypt instead of the legacy CryptoAPI */ -#if defined(_MSC_VER) && _MSC_VER > 1500 /* 1500 = Visual Studio 2008 */ \ - && defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 +#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 \ + && ((defined(_MSC_VER) && _MSC_VER > 1500) \ + || (defined(__MINGW64_VERSION_MAJOR) && __MINGW64_VERSION_MAJOR >= 2)) #define USE_BCRYPTGENRANDOM #endif From 68c08610751e4c8495725d1d235cf75c6eef1b9c Mon Sep 17 00:00:00 2001 From: Igor Ustinov Date: Thu, 25 Jun 2026 09:09:19 +0200 Subject: [PATCH 216/349] Fix a bug in BN_ucmp() when comparing constant-time BIGNUMs of different lengths Reviewed-by: Tomas Mraz Reviewed-by: Tom Cosgrove Reviewed-by: Neil Horman MergeDate: Fri Jul 10 12:08:43 2026 (Merged from https://github.com/openssl/openssl/pull/31717) --- crypto/bn/bn_lib.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/crypto/bn/bn_lib.c b/crypto/bn/bn_lib.c index 161f4e10a7..a63e2b9154 100644 --- a/crypto/bn/bn_lib.c +++ b/crypto/bn/bn_lib.c @@ -1,5 +1,5 @@ /* - * Copyright 1995-2025 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2026 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -696,19 +696,37 @@ int BN_ucmp(const BIGNUM *a, const BIGNUM *b) int i; BN_ULONG t1, t2, *ap, *bp; + /* + * As it is a public API function, we should handle NULL parameters in + * some way. The function can’t return an error, so let’s define that NULL + * is less than any BIGNUM. + */ + if (!ossl_assert(a != NULL && b != NULL)) + return (b == NULL) - (a == NULL); + ap = a->d; bp = b->d; if (BN_get_flags(a, BN_FLG_CONSTTIME) - && a->top == b->top) { + || BN_get_flags(b, BN_FLG_CONSTTIME)) { int res = 0; + int min_top = a->top < b->top ? a->top : b->top; - for (i = 0; i < b->top; i++) { + for (i = 0; i < min_top; i++) { res = constant_time_select_int((int)constant_time_lt_bn(ap[i], bp[i]), -1, res); res = constant_time_select_int((int)constant_time_lt_bn(bp[i], ap[i]), 1, res); } + + for (i = min_top; i < a->top; ++i) + res = constant_time_select_int((int)constant_time_is_zero_bn(ap[i]), + res, 1); + + for (i = min_top; i < b->top; ++i) + res = constant_time_select_int((int)constant_time_is_zero_bn(bp[i]), + res, -1); + return res; } From 1768a5a1be8895f3cf54d418093c337ea1f3238d Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Fri, 10 Jul 2026 11:09:09 +0200 Subject: [PATCH 217/349] Avoid undefined behavior adding or subtracting two BN_zero() values Reviewed-by: Dmitry Belyavskiy Reviewed-by: Nikola Pajkovsky MergeDate: Fri Jul 10 14:54:48 2026 (Merged from https://github.com/openssl/openssl/pull/31916) --- crypto/bn/bn_add.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crypto/bn/bn_add.c b/crypto/bn/bn_add.c index 88d77c534f..24151d0e13 100644 --- a/crypto/bn/bn_add.c +++ b/crypto/bn/bn_add.c @@ -97,6 +97,8 @@ int BN_uadd(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) return 0; r->top = max; + if (max == 0) + goto end; ap = a->d; bp = b->d; @@ -116,6 +118,7 @@ int BN_uadd(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) *rp = carry; r->top += (int)carry; +end: r->neg = 0; bn_check_top(r); return 1; @@ -143,6 +146,9 @@ int BN_usub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) if (bn_wexpand(r, max) == NULL) return 0; + if (max == 0) + goto end; + ap = a->d; bp = b->d; rp = r->d; @@ -162,6 +168,7 @@ int BN_usub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) while (max && *--rp == 0) max--; +end: r->top = max; r->neg = 0; bn_pollute(r); From 9b182491fc47042fa3fa5c246b5b24482cd49efd Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Fri, 26 Jun 2026 15:04:27 +0200 Subject: [PATCH 218/349] quic: fix intermittent idle-test failure in tserver test The thread-assisted idle test advances fake time in 10ms steps while the connection is kept alive solely by the background assist thread sending keepalive PINGs. The test stepped fake time without checking that a due keepalive had actually been sent, so whether it went out before the server's idle deadline lapsed depended on thread scheduling - hence the intermittent failure. Now, before each step, check the event timeout (next_deadline minus fake-now): while a keepalive is still due to be sent it stays at zero, so we wake the assist thread and re-check without advancing until it goes positive (or the existing real-time watchdog fires). Only then do we step fake time. The negotiated 30s idle timeout and 60s idle duration are unchanged, so the keepalive is still required and still tested; only the race is removed. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Matt Caswell Reviewed-by: Tomas Mraz MergeDate: Fri Jul 10 15:33:45 2026 (Merged from https://github.com/openssl/openssl/pull/31746) --- test/quic_tserver_test.c | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/test/quic_tserver_test.c b/test/quic_tserver_test.c index 0ef79035d2..b4e81f427f 100644 --- a/test/quic_tserver_test.c +++ b/test/quic_tserver_test.c @@ -331,16 +331,38 @@ static int do_test(int use_thread_assist, int use_fake_time, int use_inject) CRYPTO_THREAD_unlock(fake_time_lock); ++idle_units_done; - ossl_quic_conn_force_assist_thread_wake(c_ssl); /* - * If the event timeout has expired then give the assistance - * thread a chance to catch up + * The assist thread alone keeps the idle connection alive. It + * waits on real time internally, so advancing fake time can + * outrun it. Rather than race it, wait until it has caught up: + * the event timeout is computed against fake time, so once the + * next deadline is back in the future all events due up to now + * - including any keepalive - have been serviced. */ - if (!TEST_true(SSL_get_event_timeout(c_ssl, &tv, &isinf))) - goto err; - if (!isinf && ossl_time_compare(ossl_time_zero(), ossl_time_from_timeval(tv)) >= 0) - OSSL_sleep(10); /* Ensure CPU scheduling for test purposes */ + for (;;) { + ossl_quic_conn_force_assist_thread_wake(c_ssl); + + if (!TEST_true(SSL_get_event_timeout(c_ssl, &tv, &isinf))) + goto err; + + if (isinf + || ossl_time_compare(ossl_time_from_timeval(tv), + ossl_time_zero()) + > 0) + break; + + if (ossl_time_compare(ossl_time_subtract(real_now(NULL), + start_time), + ossl_ms2time(limit_ms)) + >= 0) { + TEST_error("timeout waiting for assist thread to send " + "keepalive during idle test"); + goto err; + } + + OSSL_sleep(1); /* Yield so the assist thread can run. */ + } } else { c_done_idle_test = 1; } From d8e49b85d003d4f3c4636b702f5a088bc530eaa8 Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Mon, 1 Dec 2025 15:10:48 -0700 Subject: [PATCH 219/349] Add a STYLE.md file and link it from CONTRIBUTING.md This is effectively, the current coding style policy web page changed to accomodate clang-format. This is more or less the same file I have had in the various clang-format sample PR's since September. It does include an additional sections on Integers and on Return Values that are not in the original coding style policy It is changed from the September version in that it does not have mention of keeping include files self contained. I believe that is achievable and desirable, but I think should be done as a separate change from this. There were a number of issues brought up in discussion of this file in the clang-format PR's. I recorded those in 818, 819, 820, 821, 822, 823, 824, 825, and 826, which we can link in there to the appropriate section of the document. Reviewed-by: Neil Horman Reviewed-by: Milan Broz MergeDate: Fri Jul 10 15:37:13 2026 (Merged from https://github.com/openssl/openssl/pull/29295) --- CONTRIBUTING.md | 9 +- STYLE.md | 689 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 694 insertions(+), 4 deletions(-) create mode 100644 STYLE.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 63eaf4397d..213125c255 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -127,8 +127,9 @@ guidelines: often. We do not accept merge commits, you will have to remove them (usually by rebasing) before it will be acceptable. - 5. Code provided should follow our [coding style] and [documentation policy] + 5. Code provided should follow our [coding style](STYLE.md) and [documentation policy] and compile without warnings. + Consistent formatting is enforced by using `clang-format` with configuration stored in [.clang-format](.clang-format). OpenSSL uses `WebKit` style. You can configure git pre-commit to automatically reformat your code with @@ -142,7 +143,6 @@ guidelines: Clean builds via GitHub Actions are required. They are started automatically whenever a PR is created or updated by committers. - [coding style]: https://openssl-library.org/policies/technical/coding-style/ [documentation policy]: https://openssl-library.org/policies/technical/documentation-policy/ 6. When at all possible, code contributions should include tests. These can @@ -195,7 +195,8 @@ guidelines: the commit should include the line: ``` - Fixes #XXXXX + Fixes: LINK ``` - where XXXXX is the issue number. + where LINK is the https link to the issue in github. + diff --git a/STYLE.md b/STYLE.md new file mode 100644 index 0000000000..f102cb9128 --- /dev/null +++ b/STYLE.md @@ -0,0 +1,689 @@ +# OpenSSL Style Guide + +## How to use this guide. + +A brief metaphor to serve as a guide both for reviewers and +contributors. + +This guide is intended to be applied like Electrical Wiring rules for +a house. + +OpenSSL is a large codebase with a long history. Just as a house may +be older than the latest technology and standards for residential +electrical installations, OpenSSL is older than many current +language standards and best practices. + +Electrical Wiring rules typically do not require that you rip open +your house with every revision of the rules, and change everything in +your house to the most modern standard of compliance. They do require +that changes need to be made reasonably, to ensure safety and improved +compliance, which can include bringing an area up to date if +significant changes are made to it and it could impact other +areas. Similarly, that is that the intention of the application of +the rules in this guide. + +All code in OpenSSL does not conform to this document. This document +is meant to establish the standards required for new code, and for +significant refactors of existing code. For example, if your household +kitchen has old knob and tube wiring, that is correctly installed and +safe and working, you are not required to open up your walls and +rewire your entire kitchen to fix one broken electrical outlet. + +However, if you did choose to replace your kitchen wiring for a +renovation, but then hack together something to feed the knob and tube +wiring that contines to the bedroom, You will be asked by the +electrical inspector to bring the bedroom up to date as well. + +It is the desire that new contributions meet this standard, and +contributions that significantly change existing areas should bring +them up to this standard where possible and reasonable to do so. + +## Webkit style + +OpenSSL follows the +[Webkit coding style for C code](https://webkit.org/code-style-guidelines/) +The rest of this document describes differences and clarifications on +top of the base guide. + +## Whitespace Indenting and Formatting + +The basic style for indenting and whitespace is as per the WebKit +C coding style. This style is enforced by clang-format using the +.clang-format file in this directory. Your changes should be formatted +with clang-format and whatever indentation and line wrapping +clang-format does to them should be deemed correct. + +In rare situations it may be necessary to disable clang-format +on a piece of code. This may be done by the comments: + +``` +/* clang-format off */ +I am doing something nasty here. +Reviewers should be triggered. +/* clang-format on */ +``` +This should be used sparingly, and should not be used if +there is any other way to do what you are doing. + +The use of clang-format is intended to ensure basic consistency +and to ease review. Nevertheless, clang-format can not enforce +other necessary aspects of style, and these are documented here. + +## Language + +The majority of the project is in C, so C++ specific rules in the +aforementioned coding standards do not apply. + +To maximise portability the version of C defined in ISO/IEC 9899:1999 +should be used. This is more commonly referred to as C99. More modern +version of the C language are not yet supported on some platforms that +OpenSSL is used on and therefore should be avoided. + +## Naming + +C is a Spartan language, and so should your naming be. + +Local variable names should be short, and to the point. If you have +some random integer loop counter, it should probably be called i or j. + +Avoid single-letter names when they can be visually confusing, such as +I and O. Avoid other single-letter names unless they are telling in +the given context. For instance, m for modulus and s for SSL pointers +are fine. + +Use simple variable names like tmp and name as long as they are +non-ambiguous in the given context. + +If you are afraid that someone might mix up your local variable names, +perhaps the function is too long; see the chapter on functions. + +Global variables (to be used only if you REALLY need them) need to +have descriptive names, as do global functions. If you have a function +that counts the number of active users, you should call that +count_active_users() or similar, you should NOT call it cntusr(). + +For getter functions returning a pointer and functions setting a +pointer given as a parameter, use names containing get0_ or get1_ +(rather than get_) or set0_ or set1_ (rather than set_) or push0_ or +push1_ (rather than push_) to indicate whether the structure referred +to by the pointer remains as it is or it is duplicated/up-ref’ed such +that an additional free() will be needed. + +Use lowercase prefix like ossl_ for internal symbols unless they are +static (i.e., local to the source file). + +Use uppercase prefix like EVP_ or OSSL_CMP_ for public (API) symbols. + +Do not encode the type into a name (so-called Hungarian notation, +e.g., int iAge). + +Align names to terms and wording used in standards and RFCs. + +Avoid mixed-case unless needed by other rules. Especially never use +FirstCharacterUpperCase. For instance, use EVP_PKEY_do_something +rather than EVP_DigestDoSomething. + +Make sure that names do not contain spelling errors. + +## Comments + +Use the classic /* ... */ comment markers. Don’t use // ... markers. +Place comments above or to the right of the code they refer to. +Comments are good, but there is also a danger of over-commenting. NEVER try to +explain HOW your code works in a comment. It is much better to write the code +so that it is obvious, and it’s a waste of time to explain badly written code. +You want your comments to tell WHAT your code does, not HOW. +The preferred style for long (multi-line) comments is: + +``` +/*- + * This is the preferred style for multi-line + * comments in the OpenSSL source code. + * Please use it consistently. + * + * Description: A column of asterisks on the left side, + * with beginning and ending almost-blank lines. + */ +``` + +Note the initial hyphen to prevent indent and clang-format from +modifying the comment block. Use this if the comment has particular +formatting that must be preserved. + +It’s also important to comment data, whether they are basic types or derived +types. To this end, use just one data declaration per line (no commas for +multiple data declarations). This leaves you room for a small comment on each +item, explaining its use. + +In an effort to better translate our source code into documentation that is +more easily understandable to future developers, please also consider adding +Doxygen style comments to any function/data structures/macros/etc that you +alter or create in the development of patches for OpenSSL. The intent is to +provide a more robust set of documentation for our entire code base (with +particular focus on our internal functions and data structures). Please use the +following sample code as a guideline: + +``` +/* + * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/** + * @file doxysample.c + * This is a brief file description that you may add + * Subsequent lines contain more detailed information about what you will + * find defined in this file. It is not currently required that you add a file + * description, but it's available if you like. + */ + + /** + * @def MAX(x, y) + * document a macro that returns the maximum of two inputs. + * @param x integer input value + * @param y integer input value + * @returns the maximum of x and y + */ + #define MAX(x, y) (x > y ? x : y) + +/** + * @struct foo_st + * @brief description of the foo_st struct. + * Optional more detailed description here. + */ +typedef foo_st { + int a; /**< Describe the a field here */ + char b; /**< Describe the b field here */ +} FOO; + + +/** + * \brief Describe the function add briefly. + * Add a more detailed description here, like sums two inputs and returns the + * results. + * \param a - input integer to add + * \param b - input integer to add + * \returns the sum of a and b + */ +int add(int a, int b) +{ + return a + b; +} +``` + +## Typedefs + +OpenSSL uses typedef’s extensively. For structures, they are all +uppercase and are usually declared like this: + +typedef struct name_st NAME; + +For examples, look in , but note that there are many +exceptions such as BN_CTX. Typedef’d enum is used much less often and +there is no convention, so consider not using a typedef. When doing +that, the enum name should be lowercase and the values (mostly) +uppercase. Note that enum arguments to public functions are not +permitted. + +The ASN.1 structures are an exception to this. The rationale is that +if a structure (and its fields) is already defined in a standard it’s +more convenient to use a similar name. For example, in the CMS code, a +CMS_ prefix is used so ContentInfo becomes CMS_ContentInfo, +RecipientInfo becomes CMS_RecipientInfo etc. Some older code uses an +all uppercase name instead. For example, RecipientInfo for the PKCS#7 +code uses PKCS7_RECIP_INFO. + +Be careful about common names which might cause conflicts. For +example, Windows headers use X509 and X590_NAME. Consider using a +prefix, as with CMS_ContentInfo, if the name is common or generic. Of +course, you often don’t find out until the code is ported to other +platforms. + +A final word on struct’s. OpenSSL has historically made all struct +definitions public; this has caused problems with maintaining binary +compatibility and adding features. Our stated direction is to have +struct’s be opaque and only expose pointers in the API. The actual +struct definition should be defined in a local header file that is not +exported. + +## Integers + +*When not constrained by legacy code*: + +Prefer using explicitly-sized integers where appropriate rather than +generic C ones. For instance, to represent a byte, use `uint8_t`, not +`unsigned char`. Likewise, represent a two-byte field as `uint16_t`, not +`unsigned short`. + +Sizes should be represented as `size_t`. When converting to/from an +`int` for legacy purposes ensure to account for overflow/underflow +conditions. + +Within structs that are retained across the lifetime of a connection, +for new integer values whose size are known and it's easy to do, use a +smaller integer type like `uint8_t`. This is a "free" connection +footprint optimization for servers. Don't make code significantly more +complex for it, and do still check the bounds when passing in and out +of the struct. This narrowing should not propagate to local variables +and function parameters which should use more conventional integer +types in order to not add complexity for the users of such +functions. Do not retroactively apply this rule to existing integer +values in structures as this could cause ABI breakage. + +When doing arithmetic, account for overflow conditions. + +Except in platform specific code, do not use `ssize_t`. MSVC lacks it, +and prefer out-of-band error signaling for `size_t` (see Return +Values). + +## Preprocessor Directives + +Prefer #if defined(FOO) and #if !defined(FOO) to #ifdef and +#ifndef. This allows you to use logical operations when conditional +compilation is dependant on more than one variable instead of nesting +multiple blocks. + +All #endif blocks must have a comment matching their #if. + +``` +#if defined(OPENSSL_LINUX) && (!defined(OPENSSL_NO_HOOBLA) || !defined(OPENSSL_BULA) +... +... +#endif /* defined(OPENSSL_LINUX) && (!defined(OPENSSL_NO_HOOBLA) || !defined(OPENSSL_BULA) */ +``` + +Always prefer to isolate conditional compilation in one place, or use +separate files. If you need to do "stuff" in many places that can be +done many different ways. It is very undesirable to have: + +``` +#if defined(OPENSSL_OS_FOO) || defined (OPENSSL_OS_BAR) +stuff the way foo or bar does it; +#elif defined(OPENSSL_OS_BLAH) || defined (OPENSSL_WOOF) +stuff the way blah or woof does it; +#elif +... +... +... +#endif /* defined(OPENSSL_OS_BLAH) || defined (OPENSSL_WOOF) */ +#endif /* defined(OPENSSL_OS_FOO) || defined (OPENSSL_OS_BAR) */ +``` + +in many places. + +If you need this you should consider making one function that contains +the os dependent "stuff" with the conditional compilation directives +only in that function, or if this is large, make separate files +(stuff_foo.c stuff_blah.c) which implement the same function and +choose the implementation to include in the build process. The latter +method is friendlier to non mainstream platforms as os dependent +implementations can be maintained in separate files rather than +intrusive code patches. + +## Macros and Enums +OpenSSL has historically made extensive use of macros in C, and the C +preprocessor. This was normal practice in older C codebases, when C +compilers were simple things and unable to avoid significant function +call overhead. This is no longer the case, so it is desirable to +reduce this and avoid it in new code. + +Macros and labels in enums should be named in +ALL_CAPS_WITH_UNDERSCORES. This convention helps distinguish macros +from functions and variables. + +``` +#define OPENSSL_MAGIC_FOO 0x12345 +``` + +Enums are preferred when defining several related constants. Note, +however, that enum arguments to public functions are not permitted. + +### Avoid Complex Macros + +Generally, avoid overly complex or "clever" macros that are difficult +to read, debug, or maintain. Nesting of macros calling other macros +should be avoided. Prioritize clarity and simplicity. + +### Avoid Function-like Macros + +Functions should be preferred to function-like macros. New code or +refactors should be with functions. Do not 'pre-optimize' for the +overhead of a function call without first implementing with a function +and obtaining measurements to indicate function call overhead is a +significant problem. Only at this point you can consider inlining the +function in preference to a function-like macro, if you can then +measure a significant performance from inlining that does not generate +an undesirable code size increase. + +### Macro Parenthesizetion + +Always parenthesize arguments in function-like macros to prevent +operator precedence issues during expansion. + +Enclose the entire macro definition in parentheses if it expands to an +expression to ensure correct evaluation when used within larger +expressions. For example: + +``` +#define BOB(blah) ((blah) + 42 - 23 / (blah)) +``` + +### Multi Statement Macros + +Enclose multi statement macros in a do {} while(0) loop. Do not +include a semicolon at the end of macros. Do not enclose macros in +braces. This ensures they can be used without problems. For example: + +``` +/* This is bad */ +#define KERMIT(x) muppet((x)); frog((x)); green((x)) +if (soemthing) + KERMIT(bob); +else /* This now breaks */ + +/* This is also bad, because now you have to omit the semicolon. */ +#define KERMIT(x) { muppet((x)); frog((x)); green((x)) } +if (something) + KERMIT(bob) /* No semicolon */ +else + +/* This does not break */ +#define KERMIT(x) do { muppet((x)); frog((x)); green((x)) } while (0) +/* Now this works sensibly */ +if (something) + KERMIT(bob); +else + +/* + * But just use a function - Note we now know that x is an + * integer that has something to do with froggieness and we + * gain some type safety. + */ +static void kermit(int frogginess) +{ + muppet(froggieness); + frog(froggieness); + green(froggieness); +} +if (something) + kermit(bob); +else +``` + +### Do not include files as multi-line macros + +Do not put code in a file and include it inline + +``` + ... + printf "Yolo\n"; +#include "./abagfullofcode.inc" + printf "That was fun\n"; + ... +``` + +Either make a function out of the code and call it or just put the +code in place. + +### Avoid Macros With Side Effects: + +Be extremely cautious with arguments that may have side effects. as they might be +evaluated multiple times in the macro expansion, leading to unexpected +behavior. Do not make macros that depend upon modifiying a particular +magic name: + +``` +#define FOO(val) bar(index, (val)) +``` + +It is confusing to the reader and is prone to breakage from seemingly innocent +changes. + +Do not write macros that are l-values: + +``` +FOO(x) = y +``` + +## Functions + +Ideally, functions should be short and sweet, and do just +one thing. A rule of thumb is that they should fit on one or two +screenfuls of text (25 lines as we all know), and do one thing and do +that well. + +The maximum length of a function is often inversely proportional to +the complexity and indentation level of that function. So, if you have +a conceptually simple function that is just one long (but simple) +switch statement, where you have to do lots of small things for a lot +of different cases, it’s okay to have a longer function. + +If you have a complex function, however, consider using helper +functions with descriptive names. You can ask the compiler to in-line +them if you think it’s performance-critical, and it will probably do a +better job of it than you would have done. + +Another measure of complexity is the number of local variables. If +there are more than five to 10, consider splitting it into smaller +pieces. A human brain can generally easily keep track of about seven +different things; anything more and it gets confused. Often things +which are simple and clear now are much less obvious two weeks from +now, or to someone else. An exception to this is the command-line +applications which support many options. + +In source files, separate functions with one blank line. In function +prototypes, include parameter names with their data types. Although +this is not required by the C language, it is preferred in OpenSSL +because it is a simple way to add valuable information for the +reader. The name in the prototype declaration should match the name in +the function definition. + +### Checking function arguments + +A public function should verify that its arguments are sensible. This +includes, but is not limited to, verifying that: + +* Non-optional pointer arguments are not NULL and, +* numeric arguments are within expected ranges. + +Where an argument is not sensible, an error should be returned. + +### Extending existing functions + +From time to time it is necessary to extend an existing function. Typically +this will mean adding additional arguments, but it may also include removal of +some. + +Where an extended function should be added the original function should be kept +and a new version created with the same name and an _ex suffix. For example, +the RAND_bytes function has an extended form called RAND_bytes_ex. + +Where an extended version of a function already exists and a second extended +version needs to be created then it should have an _ex2 suffix, and so on for +further extensions. + +When an extended version of a function is created the order of existing +parameters from the original function should be retained. However new +parameters may be inserted at any point (they do not have to be at the end), +and no longer required parameters may be removed. + +### Centralized exiting of functions + +The goto statement comes in handy when a function exits from multiple locations +and some common work such as cleanup has to be done. If there is no cleanup +needed then just return directly. The rationale for this is as follows: + +* Unconditional statements are easier to understand and follow +* It can reduce excessive control structures and nesting +* It avoids errors caused by failing to update multiple exit points when + the code is modified +* It saves the compiler work to optimize redundant code away ;) + +For example: +``` +int fun(int a) +{ + int result = 0; + char *buffer = OPENSSL_malloc(SIZE); + + if (buffer == NULL) + return -1; + + if (condition1) { + while (loop1) { + ... + } + result = 1; + goto out; + } + ... +out: + OPENSSL_free(buffer); + return result; +} +``` +## Return Values + +### Return values in legacy code +Historically, functions in OpenSSL can return values of many different +kinds, and one of the most common is a value indicating whether the +function succeeded or failed. Usually this is: + +* 1: success +* 0: failure + +Sometimes an additional value is used: + +* -1: something bad (e.g., internal error or memory allocation failure) + +Other APIs use the following pattern: + +* \>= 1: success, with value returning additional information +* <= 0: failure with return value indicating why things failed + +Sometimes a return value of -1 can mean “should retry” (e.g., BIO, +SSL, et al). Functions whose return value is the actual result of a +computation, rather than an indication of whether the computation +succeeded, are not subject to these rules. + +When constrained by legacy code, you should follow the existing API's +convention for return values. Be certain you are aware of what this is +when modifying such code. + +### Return values in new code + +For new code, functions should return `int` with one on +success and zero on error. Do not overload the return value to both +signal success/failure and output an integer. For example: + +``` + /** + * \brief ossl_snuffle_thingamabob snuffles a thingamabob from bytes of input. + * If a valid thingamabob is snuffled, the result is stored in + * |*out_thingamabob|. On failure a snuffling error code is stored + * in |*out_err|. + * \param input - pointer to the bytes to snuffle + * \param input_len - the number of bytes available to snuffle from |input|. + * \param out_err - pointer to a integer to store an error code. + * \param out_thingamabob - pointer to a thingamabob to store the output. + * \returns 1 if a thingamabob was snuffled and stored, 0 otherwise. + */ + int ossl_snuffle_thingamabob(uint8_t *input, size_t input_len, + int *out_err, thingamabob *out_thingamabob); +``` + +If a function outputs a pointer to an object on success and there are *no +other outputs*, and you are certain there never would be other outputs, +return the pointer directly and `NULL` on error. + +## Allocating memory +OpenSSL provides many general purpose memory utilities, including, but not +limited to: OPENSSL_malloc(), OPENSSL_zalloc(), OPENSSL_realloc(), OPENSSL_ +memdup(), OPENSSL_strdup() and OPENSSL_free(). Please refer to the API +documentation for further information about them. + +OpenSSL provides special purpose allocators for arrays, including +OPENSSL_malloc_array() and OPENSSL_realloc_array() which take +arguments of the element size and number of elements desired. Always +use these for array allocation, as these functions check for integer +overflow conditions safely when computing the size of the allocation. + +If you design an API that requires internal memory allocations before use, +ensure you provide both an initialization API to do the allocation before +using it, and a completion API to de-allocate the memory that consumers +of your API can call when finished. + +## Processor-specific code +In OpenSSL’s case the only reason to resort to processor-specific code is for +performance. As it still exists in a general platform-independent algorithm +context, it always has to be backed up by a neutral pure C one. This implies +certain limitations. + +The most common way to resolve this conflict is to opt for +short inline assembly function-like snippets, customarily implemented as +macros, so that they can be easily interchanged with other platform-specific or +neutral code. As with any macro, try to implement it as single expression. +You may need to mark your asm statement as volatile, to prevent GCC from +removing it if GCC doesn’t notice any side effects. You don’t always need to do +so, though, and doing so unnecessarily can limit optimization. + +When writing a single inline assembly statement containing multiple +instructions, put each instruction on a separate line in a separate quoted +string, and end each string except the last with \n\t to properly indent the +next instruction in the assembly output: + +``` +asm ("magic %reg1, #42\n\t" + "more_magic %reg2, %reg3" + : /* outputs */ : /* inputs */ : /* clobbers */); +``` + +Large, non-trivial assembly functions go in pure assembly modules, with +corresponding C prototypes defined in C. The preferred way to implement this is +so-called “perlasm”: instead of writing real .s file, you write a perl script +that generates one. This allows use symbolic names for variables (register as +well as locals allocated on stack) that are independent on specific assembler. +It simplifies implementation of recurring instruction sequences with regular +permutation of inputs. By adhering to specific coding rules, perlasm is also +used to support multiple ABIs and assemblers, see crypto/perlasm/x86_64- +xlate.pl for an example. + +Another option for processor-specific (primarily SIMD) capabilities is called +compiler intrinsics. We avoid this, because it’s not very much less complicated +than coding pure assembly, and it doesn’t provide the same performance +guarantee across different micro-architecture. Nor is it portable enough to +meet our multi-platform support goals. + +## Asserts +We have 3 kind of asserts. The behaviour depends on being a debug or release +build: +``` +Function failure release failure debug success release success debug +assert not evaluated abort not evaluated nothing +ossl_assert returns 0 abort returns 1 returns 1 +OPENSSL_assert abort abort nothing nothing +``` + +Use OPENSSL_assert() only in the following cases: + +* In the libraries when the global state of the software is corrupted and + there is no way to recover it +* In applications, test programs and fuzzers + +Use ossl_assert() in the libraries when the state can be recovered and an error +can be returned. Example code: + +``` +if (!ossl_assert(!should_not_happen)) { + /* push internal error onto error stack */ + return BAD; +} +``` + +Use assert() in libraries when no error can be returned, and what you are checking +is not a run-time dependent condition (such as failure to allocate resources or open +a file) but a programmer error. From b3020646af3c8c0c01a7ba8b64f1c63931b17c3f Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Thu, 11 Jun 2026 21:55:48 -0600 Subject: [PATCH 220/349] Modernize and update STYLE.MD, add DOCUMENTATION.MD So this repatriates DOCUMENTATION.MD from the web page to the code base, and links both STYLE.MD and DOCUMENTATION.MD from CONTRIBUTING.MD It does a large rototilling of STYLE.MD to address many of the outstanding concerns noted when I started this before clang-format last year, and brings us roughly in line with the things that are addressed in similar style guides for other projects. Most of the changed or updated reccomendations reflect what we currently have been doing, or have expressed as a desire to move to in the future. Most larger "OpenSSL-isms" I've tried to explicitly call out to make this a more cohesive and useful guide for a new contributor Reviewed-by: Neil Horman Reviewed-by: Milan Broz MergeDate: Fri Jul 10 15:37:16 2026 (Merged from https://github.com/openssl/openssl/pull/29295) --- CONTRIBUTING.md | 8 +- DOCUMENTATION.md | 185 ++++++ STYLE.md | 1446 ++++++++++++++++++++++++++++++---------------- 3 files changed, 1125 insertions(+), 514 deletions(-) create mode 100644 DOCUMENTATION.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 213125c255..8101e47114 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -127,8 +127,9 @@ guidelines: often. We do not accept merge commits, you will have to remove them (usually by rebasing) before it will be acceptable. - 5. Code provided should follow our [coding style](STYLE.md) and [documentation policy] - and compile without warnings. + 5. Code provided should follow our [coding style](STYLE.md) and + [documentation policy](DOCUMENTATION.md) and compile without warnings when + using a --strict-warnings configuration. Consistent formatting is enforced by using `clang-format` with configuration stored in [.clang-format](.clang-format). OpenSSL uses `WebKit` style. @@ -143,8 +144,6 @@ guidelines: Clean builds via GitHub Actions are required. They are started automatically whenever a PR is created or updated by committers. - [documentation policy]: https://openssl-library.org/policies/technical/documentation-policy/ - 6. When at all possible, code contributions should include tests. These can either be added to an existing test, or completely new. Please see [test/README.md](test/README.md) for information on the test framework. @@ -199,4 +198,3 @@ guidelines: ``` where LINK is the https link to the issue in github. - diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md new file mode 100644 index 0000000000..6fb2b70fa5 --- /dev/null +++ b/DOCUMENTATION.md @@ -0,0 +1,185 @@ +OpenSSL Documentation Policy +============================ + +This document describes the code documentation and commenting requirements +for the OpenSSL project. + +The project's documentation is about making the libraries and tools more +accessible to our users and making the code more maintainable. This policy +applies to new submissions; existing code does not uniformly conform to it +and will be brought up to standard gradually. + +Any non-trivial change to existing code must bring the affected code into +conformance with this policy as part of the same change. In particular, +renaming or relocating functions, changes to public APIs, and any change +that would render an existing POD page or in-source comment inaccurate +require the corresponding documentation to be updated. This includes +adding documentation that was previously absent where the change brings +the affected code within the scope of this policy. + +The form and style of code comments themselves -- comment markers, layout, +the use of `/**` and `/*-` blocks, doxygen markup, the structure of the +sample multi-line comment, and similar -- are described in +[STYLE.md](STYLE.md). This file describes what *must* be documented and +where; [STYLE.md](STYLE.md) describes how code comments look. + +Command line commands and arguments +----------------------------------- + +All new commands, as well as new or modified arguments to existing +commands, must be documented in the `doc/man1` directory. This +documentation is in POD format. + +Public symbols in the libraries +------------------------------- + +All new public symbols must be documented in a POD manual page in the +`doc/man3` directory. This includes types, macros, and functions. + +The allowed exceptions are: + +- guard macros preventing a header file being included twice +- new symbols generated automatically via `make update` (errors, objects, etc.) + +Each public function's declaration in its public header must carry a +doxygen comment block. The block's `@see` must include the function's +own manual page (`name(3)`) and may include additional manual pages +that a caller needs to use the function correctly. The doxygen block +is a navigation aid pointing to the canonical reference documentation +in the corresponding POD file; see [STYLE.md](STYLE.md) for the +doxygen form. + +Overviews, conventions, et al +----------------------------- + +Where additional user-facing information is required, it should be +included in the `doc/man7` section. This includes, but is not limited to: + +- algorithm descriptions and parameters +- architectural and subsystem overviews +- user guides and tutorials +- conventions and reference material (environment variables, glossary, + threading rules, file format conventions) + +Internal functions, structures, globals and macros +-------------------------------------------------- + +Internal functions and macros are those defined in the `include/crypto` +and `include/internal` directories. + +These should all be documented at the point of implementation, using a +doxygen-style comment block. The comment should describe the purpose +and, for functions, the input and output arguments and the return +value. See [STYLE.md](STYLE.md) for the doxygen conventions used by +OpenSSL. + +For *trivial* items, where their operation is obvious from their +implementation, the documentation requirement is not mandated. The +following are generally representative of trivial items, however it is +quite possible for any of these to be non-trivial in specific instances +and therefore require documentation: + +- `OSSL_DISPATCH` tables +- upref functions +- free functions +- simple getter/setter functions +- wrappers for other functions (a function that calls a more recent + `_ex` variant or a group of functions that call a common internal + routine) + +For structures, each of the fields should be commented stating its +purpose. Again, a *trivial* exception applies where the purpose is +obvious. Some representative examples: + +- `OSSL_LIB_CTX *ctx;` where there is only one library context referenced + in the structure. +- `struct *next;` in a linked list implementation. +- `CRYPTO_REF_COUNT refcnt;` + +Static functions and globals and local structures and macros +------------------------------------------------------------ + +These are functions, structures, globals and macros local to a specific +C file or defined for a single directory as part of a local `.h` file. + +These should all be documented at the point of implementation. Follow +the same rules and exceptions as for internal functions and structures +above. In some cases slightly more leniency with respect to *trivial* +can be tolerated. + +Code comments +------------- + +The form, style, and content guidance for code comments are described in +[STYLE.md](STYLE.md). Comments are required at the points described in +the internal and static sections above, subject to the *trivial* +exception, and at the additional points described in +[STYLE.md](STYLE.md). + +Assembly code +------------- + +Assembly code should include a good description of the algorithm and +approach being used. This should be followed by a performance comparison +and then the assembly code itself. The assembly code should be well +commented, but it is not necessary to comment every line. A comment +describing each block of code suffices. + +For pure-assembly modules (`.s` files and the perlasm scripts that +generate them), comments use the native syntax of the assembler or +generator (typically `#`). Doxygen-style markup does not apply here; +the algorithm description, performance comparison, and per-block +comments described above are still required. + +For assembly that appears inline inside a C file (within an `asm()` +statement, for example), the surrounding C function is documented +with doxygen-style C comments as for any other C code; see +[STYLE.md](STYLE.md). Comments inside the `asm()` body itself use +plain C `/* */` comments. + +There are no *trivial* exceptions for assembly code. + +Configure options +----------------- + +New options added to the configuration scripts must be documented in the +[INSTALL.md](INSTALL.md) file. + +Changes and news +---------------- + +Significant modifications should be documented in the +[CHANGES.md](CHANGES.md) file. + +Very significant features and changes should be documented in the +[NEWS.md](NEWS.md) file. + +In both cases, the added note should be short and to the point, and +should be written for users of the library, focusing on impact rather +than implementation details. + +Automated sanity checking +------------------------- + +The `make doc-nits` command should be run before submitting a pull +request and any problems it locates must be addressed. + +Language +-------- + +The language used for documentation shall be *British English*. + +In general the language, abbreviations, layout and formatting should also +correspond to the +[LDP](https://openssl-library.org/policies/general/glossary/#ldp) +guidelines. + +Common sense +------------ + +Comments and documentation are to improve readability and comprehension. +Where the code is obvious, there is no need to include a comment. +However, common sense applies: always err in favour of including more +comments than less or none. Code that you have just written that is +*obvious* will not necessarily be to someone else two years later. See +[STYLE.md](STYLE.md) for the form and content of code comments. diff --git a/STYLE.md b/STYLE.md index f102cb9128..a5df9546f0 100644 --- a/STYLE.md +++ b/STYLE.md @@ -1,141 +1,235 @@ -# OpenSSL Style Guide +OpenSSL Style Guide +=================== -## How to use this guide. +Applicability +------------- -A brief metaphor to serve as a guide both for reviewers and -contributors. +New code in OpenSSL is expected to follow the conventions in this +guide. Existing code does not uniformly comply and is being brought +up to standard gradually; non-trivial changes to existing code +should bring the affected area into compliance. -This guide is intended to be applied like Electrical Wiring rules for -a house. +When bringing an area into compliance as part of a larger change, +do so in a separate commit -- typically one that lands first, so +that the substantive change then operates on already-compliant +code. Combining a compliance sweep with a behaviour change in one +commit makes the diff hard to review and hard to revert. -OpenSSL is a large codebase with a long history. Just as a house may -be older than the latest technology and standards for residential -electrical installations, OpenSSL is older than many current -language standards and best practices. +Do not bring code into compliance as part of a bug fix. Make the +minimal change that fixes the bug. This holds for any bug fix, and +especially for one that may be backported to a stable release +branch -- and at the time of the fix you often cannot know whether +it will be. Mixing compliance changes into a fix complicates +backporting and makes the change larger than it needs to be. Leave +any compliance work for a separate change. -Electrical Wiring rules typically do not require that you rip open -your house with every revision of the rules, and change everything in -your house to the most modern standard of compliance. They do require -that changes need to be made reasonably, to ensure safety and improved -compliance, which can include bringing an area up to date if -significant changes are made to it and it could impact other -areas. Similarly, that is that the intention of the application of -the rules in this guide. +The language is C99 (ISO/IEC 9899:1999). More modern C versions +are not yet supported on every platform OpenSSL targets and +should be avoided. -All code in OpenSSL does not conform to this document. This document -is meant to establish the standards required for new code, and for -significant refactors of existing code. For example, if your household -kitchen has old knob and tube wiring, that is correctly installed and -safe and working, you are not required to open up your walls and -rewire your entire kitchen to fix one broken electrical outlet. - -However, if you did choose to replace your kitchen wiring for a -renovation, but then hack together something to feed the knob and tube -wiring that contines to the bedroom, You will be asked by the -electrical inspector to bring the bedroom up to date as well. - -It is the desire that new contributions meet this standard, and -contributions that significantly change existing areas should bring -them up to this standard where possible and reasonable to do so. - -## Webkit style +Formatting +---------- OpenSSL follows the -[Webkit coding style for C code](https://webkit.org/code-style-guidelines/) -The rest of this document describes differences and clarifications on -top of the base guide. +[WebKit coding style for C code](https://webkit.org/code-style-guidelines/). +In cases where the WebKit guide gives different rules for C and C++, +OpenSSL uses the C variant. -## Whitespace Indenting and Formatting +Whitespace, indentation, brace placement, line wrapping, alignment and +the other mechanical aspects of formatting are enforced by `clang-format` +using the [`.clang-format`](.clang-format) file at the top of this +repository. The configuration is the WebKit C style with a small set +of OpenSSL-specific customisations (notably the list of project +typedefs, the `STACK_OF` / `LHASH_OF` type macros, and the list of +statement-shaped macros). -The basic style for indenting and whitespace is as per the WebKit -C coding style. This style is enforced by clang-format using the -.clang-format file in this directory. Your changes should be formatted -with clang-format and whatever indentation and line wrapping -clang-format does to them should be deemed correct. +Run `clang-format` on your changes before submitting; the output of +`clang-format` is deemed correct. See +[CONTRIBUTING.md](CONTRIBUTING.md) for the tooling (`.pre-commit-config.yaml`, +the `util/reformat-patches.sh` helper, and editor integrations). -In rare situations it may be necessary to disable clang-format -on a piece of code. This may be done by the comments: +In rare situations it may be necessary to disable `clang-format` on a +piece of code. This may be done with paired comments: -``` +```c /* clang-format off */ I am doing something nasty here. Reviewers should be triggered. /* clang-format on */ ``` -This should be used sparingly, and should not be used if -there is any other way to do what you are doing. -The use of clang-format is intended to ensure basic consistency -and to ease review. Nevertheless, clang-format can not enforce -other necessary aspects of style, and these are documented here. +This should be used sparingly, and should not be used if there is any +other way to do what you are doing. -## Language +Multi-line comment blocks have an additional clang-format opt-out +via the `/**` and `/*-` markers; see [Comments](#comments). -The majority of the project is in C, so C++ specific rules in the -aforementioned coding standards do not apply. +Naming +------ -To maximise portability the version of C defined in ISO/IEC 9899:1999 -should be used. This is more commonly referred to as C99. More modern -version of the C language are not yet supported on some platforms that -OpenSSL is used on and therefore should be avoided. +### Functions and variables -## Naming +A name describes what the identifier holds or what it does. +Match the name to its role: a variable holding an `X509 *` is +typically `cert`; one holding an `X509_STORE_CTX *` is typically +`ctx`; a function that counts the number of active users is +called `count_active_users()`, not `cntusr()`. Use whole words +when there is no established short form, and reuse the same +name across the codebase for the same concept rather than +inventing synonyms. -C is a Spartan language, and so should your naming be. +Names use lowercase with underscores (snake_case). For public +functions, snake_case applies to the portion of the name after +the uppercase subsystem prefix (see below). Do not begin a +name with an underscore; identifiers starting with an +underscore are reserved by the C standard in various contexts +and can collide with toolchain or system identifiers. -Local variable names should be short, and to the point. If you have -some random integer loop counter, it should probably be called i or j. +For variables, OpenSSL has well-established short forms that +are fine to use without further qualification: `ctx`, `ptr`, +`len`, `buf`, `cert`, `key`, `pkey`, `ret`, `tmp`, and similar. +Use these in preference to longer forms; do not coin a new +variant when one of these already covers the meaning. Use the +suffix `_count` for a number of items, `_len` for a byte length, +and `_size` for a size in bytes; do not invent variants like +`num_X`, `X_length`, or `X_bytes` when one of these already +applies. -Avoid single-letter names when they can be visually confusing, such as -I and O. Avoid other single-letter names unless they are telling in -the given context. For instance, m for modulus and s for SSL pointers -are fine. +A variable that mirrors notation from a standard, RFC, paper, +or other authoritative specification being implemented may use +whatever name the spec uses (for example, `n`, `e`, `d` for RSA +parameters, or `salt` and `info` for HKDF). Document the spec +citation and which variables come from it in the function or +file doxygen comment; see [Doxygen comments](#doxygen-comments) +for the form. -Use simple variable names like tmp and name as long as they are -non-ambiguous in the given context. +Outside spec-mirroring, single letters are appropriate only as +loop counters (`i`, `j`, `k`). -If you are afraid that someone might mix up your local variable names, -perhaps the function is too long; see the chapter on functions. +For functions, OpenSSL names follow a `PREFIX_[OBJECT_]action()` +shape: an uppercase subsystem prefix; then, where the function +operates on a particular object or context, that object -- usually +the uppercase or mixed-case type name; then the action, in +lowercase with underscores. Where the prefix already identifies +the object, or the function is a general subsystem utility, there +is no separate object element. +Examples: `EVP_KDF_CTX_get0_kdf` (prefix `EVP`, object `KDF_CTX`, +action `get0_kdf`), `EVP_PKEY_sign`, `OSSL_CMP_validate_msg`, +`SSL_CTX_set_verify`; and, with no object element, `BIO_eof` and +`CRYPTO_malloc`. -Global variables (to be used only if you REALLY need them) need to -have descriptive names, as do global functions. If you have a function -that counts the number of active users, you should call that -count_active_users() or similar, you should NOT call it cntusr(). +This shape is aspirational and describes the direction for new +code. Much of the existing API predates it and carries years of +naming baggage, so it does not uniformly conform. Do not rename +existing public functions to fit it -- that breaks the API. -For getter functions returning a pointer and functions setting a -pointer given as a parameter, use names containing get0_ or get1_ -(rather than get_) or set0_ or set1_ (rather than set_) or push0_ or -push1_ (rather than push_) to indicate whether the structure referred -to by the pointer remains as it is or it is duplicated/up-ref’ed such -that an additional free() will be needed. +Public (API) functions use the uppercase subsystem prefix. +Internal functions use the lowercase `ossl_` prefix unless they +are static (i.e., local to the source file); static functions +need no prefix. -Use lowercase prefix like ossl_ for internal symbols unless they are -static (i.e., local to the source file). +Functions that return a pointer disclose ownership of the +returned value via a `0` or `1` suffix on the name: -Use uppercase prefix like EVP_ or OSSL_CMP_ for public (API) symbols. +- `get0_X()` returns a non-owning pointer. +- `get1_X()` returns an owning pointer; the caller is the new + owner, of either a fresh allocation or an up-ref. -Do not encode the type into a name (so-called Hungarian notation, -e.g., int iAge). +The same convention applies in reverse for setters and +pushers that take a pointer: -Align names to terms and wording used in standards and RFCs. +- `set0_X(obj, p)` and `push0_X(coll, p)` transfer ownership + of `p` to `obj` or `coll`. +- `set1_X(obj, p)` and `push1_X(coll, p)` leave ownership + with the caller; the callee stores a copy or up-ref. -Avoid mixed-case unless needed by other rules. Especially never use -FirstCharacterUpperCase. For instance, use EVP_PKEY_do_something -rather than EVP_DigestDoSomething. +Use these forms rather than a bare `get_` / `set_` / `push_` +whenever a pointer crosses the API boundary. -Make sure that names do not contain spelling errors. +A function extended from an existing form takes an `_ex` +suffix (`_ex2` for a second extension, `_ex3` for a third, +and so on). See [Extending existing functions](#extending-existing-functions) +for when to add an extended form and how to handle the +parameter list. -## Comments +### Typedefs + +OpenSSL uses typedefs extensively. Struct typedefs are named in +`ALL_CAPS_WITH_UNDERSCORES`, with a subsystem prefix, and the +underlying struct tag is the lowercase form of the typedef name +suffixed `_st`: + +```c +typedef struct evp_pkey_st EVP_PKEY; +``` + +For more examples, look in ``. + +When a typedef'd enum is used (see [Structs and typedefs](#structs-and-typedefs) +below for the policy on enums), the enum type name is lowercase +and the values are uppercase. + +Function-pointer and callback typedefs use one of two +suffixes: + +- `_cb` for typedefs that are user-supplied callbacks + (`X509_STORE_CTX_verify_cb`, `pem_password_cb`). +- `_fn` for function pointers in an internal interface or + dispatch table (`OSSL_provider_init_fn`, + `X509_STORE_CTX_verify_fn`). + +When introducing a new type, consider that a bare or generic +name may collide with system or third-party headers; OpenSSL +has historically used unprefixed names like `X509` and these +now collide with Windows headers in places. Prefix new type +names (for example `EVP_PKEY`, `OSSL_PARAM`) to avoid this. + +### Macros and enum labels + +Macros and labels in enums should be named in +`ALL_CAPS_WITH_UNDERSCORES`. This convention helps distinguish +macros from functions and variables. + +```c +#define OPENSSL_MAGIC_FOO 0x12345 +``` + +Error reason codes follow a `SUBSYSTEM_R_REASON` pattern, +where `_R_` is the infix marking the macro as an error reason: +`X509_R_INVALID_TRUST`, `SSL_R_NO_SHARED_CIPHER`, +`ERR_R_MALLOC_FAILURE`. + +Feature-disable macros follow `OPENSSL_NO_` -- for +example, `OPENSSL_NO_SOCK` (no socket support), +`OPENSSL_NO_RSA` (no RSA), `OPENSSL_NO_DEPRECATED__` +(no APIs deprecated as of that version). When defined, the +corresponding feature's headers and implementations are +conditionally compiled out. + +Comments +-------- + +This section describes the form and style of code comments. +[DOCUMENTATION.md](DOCUMENTATION.md) is the companion document that +describes the policy: when a comment is required, the *trivial* +exception, and the per-field commenting requirement on structures. + +Use the classic `/* ... */` comment markers. Do not use `// ...` +markers. + +Comments should describe *what* the code does and *why*. Do not +parrot the effect of each statement; well-written code is its own +description of *how*. As the complexity of the code increases, the +size and detail of comments should also increase. Err in favour of +more comments rather than fewer: code that is *obvious* to you +today will not necessarily be obvious to someone else two years +later. + +### Multi-line comment blocks -Use the classic /* ... */ comment markers. Don’t use // ... markers. -Place comments above or to the right of the code they refer to. -Comments are good, but there is also a danger of over-commenting. NEVER try to -explain HOW your code works in a comment. It is much better to write the code -so that it is obvious, and it’s a waste of time to explain badly written code. -You want your comments to tell WHAT your code does, not HOW. The preferred style for long (multi-line) comments is: -``` +```c /*- * This is the preferred style for multi-line * comments in the OpenSSL source code. @@ -146,68 +240,79 @@ The preferred style for long (multi-line) comments is: */ ``` -Note the initial hyphen to prevent indent and clang-format from -modifying the comment block. Use this if the comment has particular -formatting that must be preserved. +Both `/*-` and `/**` are recognised by the `CommentPragmas` setting +in [`.clang-format`](.clang-format) and cause the block to be left +exactly as written. Use `/*-` for plain prose comments whose layout +you want to preserve, and `/**` for doxygen blocks (see below). -It’s also important to comment data, whether they are basic types or derived -types. To this end, use just one data declaration per line (no commas for -multiple data declarations). This leaves you room for a small comment on each -item, explaining its use. +### TODO and FIXME markers -In an effort to better translate our source code into documentation that is -more easily understandable to future developers, please also consider adding -Doxygen style comments to any function/data structures/macros/etc that you -alter or create in the development of patches for OpenSSL. The intent is to -provide a more robust set of documentation for our entire code base (with -particular focus on our internal functions and data structures). Please use the -following sample code as a guideline: +Use `/* TODO: */` to mark work that should be +done later. Use `/* FIXME: */` to mark a known +incorrectness, hack, or workaround that needs to be addressed. If +a marker is worth adding, the underlying work is worth tracking: +ensure a GitHub issue is opened for it and include the issue's +full URL in the marker (e.g., `/* TODO: +(https://github.com/openssl/openssl/issues/1234) */`). Use the URL +form because OpenSSL has issue trackers in multiple repositories. -``` -/* - * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html +### Doxygen comments + +OpenSSL code uses doxygen-style comments on functions, data +structures, and macros to make the source easier to navigate and to +translate into reference documentation. The internal-function, +struct-field, and other in-source documentation requirements set out +in [DOCUMENTATION.md](DOCUMENTATION.md) must be satisfied with +doxygen-style comments using the conventions described below. + +Use the `@` form of doxygen markers (`@brief`, `@param`, `@returns`, +`@file`, `@def`, `@struct`, and so on). Do not use the `\` form +(`\brief`, `\param`, etc.). + +For the full set of recognised tags and their semantics, see the +Doxygen manual: the [commands list](https://www.doxygen.nl/manual/commands.html) +is the practical reference for what you can write inside a doxygen +block; the chapter on +[documenting the code](https://www.doxygen.nl/manual/docblocks.html) +explains the block forms and where comments attach. + +The following sample illustrates the convention: + +```c +/** + * @file doxysample.c + * This is a brief file description that you may add. + * Subsequent lines contain more detailed information about what you + * will find defined in this file. It is not currently required that + * you add a file description, but it is available if you like. */ /** - * @file doxysample.c - * This is a brief file description that you may add - * Subsequent lines contain more detailed information about what you will - * find defined in this file. It is not currently required that you add a file - * description, but it's available if you like. + * @def MAX(x, y) + * Document a macro that returns the maximum of two inputs. + * @param x integer input value + * @param y integer input value + * @returns the maximum of x and y */ - - /** - * @def MAX(x, y) - * document a macro that returns the maximum of two inputs. - * @param x integer input value - * @param y integer input value - * @returns the maximum of x and y - */ - #define MAX(x, y) (x > y ? x : y) +#define MAX(x, y) (x > y ? x : y) /** * @struct foo_st - * @brief description of the foo_st struct. + * @brief Description of the foo_st struct. * Optional more detailed description here. */ -typedef foo_st { +typedef struct foo_st { int a; /**< Describe the a field here */ char b; /**< Describe the b field here */ } FOO; - /** - * \brief Describe the function add briefly. - * Add a more detailed description here, like sums two inputs and returns the - * results. - * \param a - input integer to add - * \param b - input integer to add - * \returns the sum of a and b + * @brief Describe the function add briefly. + * Add a more detailed description here, like sums two inputs and + * returns the result. + * @param a input integer to add + * @param b input integer to add + * @returns the sum of a and b */ int add(int a, int b) { @@ -215,475 +320,798 @@ int add(int a, int b) } ``` -## Typedefs +#### Spec-mirroring variables -OpenSSL uses typedef’s extensively. For structures, they are all -uppercase and are usually declared like this: +When a function uses variable names taken from a specification +(see [Functions and variables](#functions-and-variables) in the +Naming section), the doxygen block cites the spec and identifies +each spec-derived variable: -typedef struct name_st NAME; +```c +/** + * @brief Transmogrify Calvin into Hobbes per RFC 31337 section 1.2.3. + * + * Variable naming follows the spec: + * - Calvin: input to be transmogrified + * - Hobbes: transmogrified output (caller-allocated) + * + * @see https://www.example.org/rfc/rfc31337.html#section-1.2.3 + */ +int transmogrify(const uint8_t *Calvin, size_t Calvin_len, + uint8_t *Hobbes, size_t Hobbes_len); +``` -For examples, look in , but note that there are many -exceptions such as BN_CTX. Typedef’d enum is used much less often and -there is no convention, so consider not using a typedef. When doing -that, the enum name should be lowercase and the values (mostly) -uppercase. Note that enum arguments to public functions are not -permitted. +#### Public functions: link the manual page -The ASN.1 structures are an exception to this. The rationale is that -if a structure (and its fields) is already defined in a standard it’s -more convenient to use a similar name. For example, in the CMS code, a -CMS_ prefix is used so ContentInfo becomes CMS_ContentInfo, -RecipientInfo becomes CMS_RecipientInfo etc. Some older code uses an -all uppercase name instead. For example, RecipientInfo for the PKCS#7 -code uses PKCS7_RECIP_INFO. +Every public function declaration in a public header must carry a +doxygen block that includes an `@see` referencing the function's +manual page in the standard `name(3)` form. This in-source comment +is a navigation aid; the canonical reference documentation lives in +the POD file under `doc/man3/` (see [DOCUMENTATION.md](DOCUMENTATION.md)). -Be careful about common names which might cause conflicts. For -example, Windows headers use X509 and X590_NAME. Consider using a -prefix, as with CMS_ContentInfo, if the name is common or generic. Of -course, you often don’t find out until the code is ported to other -platforms. +The cross-reference is to the function name, not the POD file +name; the build emits a man-page entry per function name, so +`man X509_verify_cert` resolves regardless of which POD file +currently documents it. -A final word on struct’s. OpenSSL has historically made all struct -definitions public; this has caused problems with maintaining binary -compatibility and adding features. Our stated direction is to have -struct’s be opaque and only expose pointers in the API. The actual -struct definition should be defined in a local header file that is not -exported. +```c +/** + * @brief One-line summary of what the function does. + * @see X509_verify_cert(3) + */ +int X509_verify_cert(X509_STORE_CTX *ctx); +``` -## Integers +Additional `@see` entries may be added for any manual page a caller +needs in order to use the function correctly, such as pages +documenting argument types, the flag families that affect the +function's behaviour, or closely related functions. List them +comma-separated on a single `@see`, matching the form used in POD's +`SEE ALSO` section: -*When not constrained by legacy code*: +```c +/** + * @brief One-line summary of what the function does. + * @see X509_verify_cert(3), X509_STORE_CTX_new(3), + * X509_VERIFY_PARAM_set_flags(3) + */ +int X509_verify_cert(X509_STORE_CTX *ctx); +``` -Prefer using explicitly-sized integers where appropriate rather than -generic C ones. For instance, to represent a byte, use `uint8_t`, not -`unsigned char`. Likewise, represent a two-byte field as `uint16_t`, not +The doxygen comment should not duplicate the POD content. Two +copies of "what this function does" inevitably diverge; the POD is +the source of truth. Keep the doxygen block to a short summary and +the `@see` references. + +Structs and typedefs +-------------------- + +See [Typedefs](#typedefs) under Naming for naming conventions. + +Typedef'd enums are used much less often than struct typedefs; +consider not using a typedef for an enum at all. A typedef'd +enum hides the integer-ness of the type from the caller, which +makes the implementation-defined underlying type easier to +forget. + +Enum arguments to public functions are not permitted. C's `enum` +underlying type is implementation-defined, and adding values to +an enum can change its ABI; use `int` and document the allowed +values instead. + +OpenSSL has historically made all struct definitions public, which +caused problems with maintaining binary compatibility and adding +features. New structs are opaque and expose only pointers in the +API; the struct definition is placed in a local header file that +is not exported. Legacy structs that are still part of the public +ABI are exempt; do not add new public struct definitions. + +In practice, the opaque pattern is to forward-declare the typedef +in the public header (`typedef struct foo_st FOO;`, with no struct +body) and place the `struct foo_st { ... };` definition in a local +header that is not exported. Callers see only the pointer type. + +Bitfield layout is implementation-defined and varies across +compilers and ABIs. Where that layout is observable -- in structs +that are part of the public ABI or that mirror a wire or file +format -- avoid bitfields and use explicit shifts and masks on a +regular integer instead. + +Flexible array members (C99 trailing `[]`) are permitted and +preferred over the older `[1]` "struct hack" for variable-length +trailing data. Remember that `sizeof(struct)` does not include the +flexible member; allocate the trailing data explicitly when the +struct is created. + +C99 designated initializers (`{ .field = value }`) are encouraged +for struct initialisation, particularly where they make the field +assignments self-documenting. + +A trailing comma in an initializer list is a layout hint to +`clang-format`: with it the list is kept one element per line; +without it the formatter may pack the list onto fewer lines. +Most of the time you do not want a trailing comma; omit it +unless you specifically want to lock the one-per-line layout +(for example, in a multi-row table of values). + +Integers +-------- + +Prefer explicitly-sized integers over generic C ones where the +size matters. To represent a byte use `uint8_t`, not +`unsigned char`; for a two-byte field, `uint16_t` rather than `unsigned short`. -Sizes should be represented as `size_t`. When converting to/from an -`int` for legacy purposes ensure to account for overflow/underflow -conditions. +Avoid `long` and `long long` specifically. `long` is 32 bits on +64-bit Windows and 64 bits on 64-bit Linux; using it for "at +least 32 bits" produces code that works inconsistently across +platforms. Use `int32_t`, `int64_t`, `size_t`, or another +`` type as appropriate. -Within structs that are retained across the lifetime of a connection, -for new integer values whose size are known and it's easy to do, use a -smaller integer type like `uint8_t`. This is a "free" connection -footprint optimization for servers. Don't make code significantly more -complex for it, and do still check the bounds when passing in and out -of the struct. This narrowing should not propagate to local variables -and function parameters which should use more conventional integer -types in order to not add complexity for the users of such -functions. Do not retroactively apply this rule to existing integer -values in structures as this could cause ABI breakage. +Sizes are `size_t`. When converting to or from `int` for legacy +reasons, check for overflow and underflow. -When doing arithmetic, account for overflow conditions. +Add an integer literal suffix when the literal participates in a +shift or appears in an expression involving a wider type -- +without a suffix the literal is `int`. Use `U` for unsigned +semantics (`1U << 31`) and the `UINT8_C` through `UINT64_C` +macros from `` for explicit widths (`UINT32_C(1) << 31`, +`UINT64_C(1) << 63`). Avoid `UL` and `ULL`, for the same reason +as `long` / `long long`: their widths vary by platform. -Except in platform specific code, do not use `ssize_t`. MSVC lacks it, -and prefer out-of-band error signaling for `size_t` (see Return -Values). +Bit shifts should be performed on unsigned operands. +Left-shifting a signed value is undefined behaviour when the +operand is negative or when the result reaches the sign bit; +right-shifting a signed negative value is implementation-defined. +Combined with the literal-suffix rule above, shifts of constants +typically take the form `UINT32_C(1) << n` or `(uint32_t)x << n`. -## Preprocessor Directives +In structs that are retained across the lifetime of a connection, +new integer fields whose value range is known should use a smaller +integer type (`uint8_t`, `uint16_t`) where doing so is +straightforward. This reduces per-connection memory in server +processes. Do not make code significantly more complex to achieve +it, and continue to bounds-check at the struct boundary. -Prefer #if defined(FOO) and #if !defined(FOO) to #ifdef and -#ifndef. This allows you to use logical operations when conditional -compilation is dependant on more than one variable instead of nesting -multiple blocks. +This narrowing should not propagate to local variables or function +parameters; those use the conventional integer types so callers +are not forced to deal with narrow types. -All #endif blocks must have a comment matching their #if. +Do not retroactively narrow existing integer fields in legacy +structs; this risks ABI breakage. +When doing arithmetic, account for overflow. + +Use `int` with `0` / `1` for boolean values, both in public API +and internal code. Do not introduce `` for new code; +the public API convention is `int`, and using `bool` internally +just to convert to `int` at the API boundary adds friction +without enough benefit. + +Except in platform-specific code, do not use `ssize_t`; MSVC lacks +it. Use `size_t` and signal errors out-of-band (see +[Return values in new code](#return-values-in-new-code)). + +Preprocessor directives +----------------------- + +Headers use traditional include guards in the `#if defined()` +form rather than `#pragma once`, which is non-standard: + +```c +#if !defined(OPENSSL_FOO_H) +# define OPENSSL_FOO_H + +/* ... header contents ... */ + +#endif /* defined(OPENSSL_FOO_H) */ ``` -#if defined(OPENSSL_LINUX) && (!defined(OPENSSL_NO_HOOBLA) || !defined(OPENSSL_BULA) + +Prefer `#if defined(FOO)` and `#if !defined(FOO)` to `#ifdef` and +`#ifndef`. This allows logical operations when conditional +compilation is dependent on more than one variable, without +nesting multiple blocks. + +All `#endif` blocks must have a comment matching their `#if`: + +```c +#if defined(OPENSSL_LINUX) && (!defined(OPENSSL_NO_HOOBLA) || !defined(OPENSSL_BULA)) ... -... -#endif /* defined(OPENSSL_LINUX) && (!defined(OPENSSL_NO_HOOBLA) || !defined(OPENSSL_BULA) */ +#endif /* defined(OPENSSL_LINUX) && (!defined(OPENSSL_NO_HOOBLA) || !defined(OPENSSL_BULA)) */ ``` -Always prefer to isolate conditional compilation in one place, or use -separate files. If you need to do "stuff" in many places that can be -done many different ways. It is very undesirable to have: +Minimise the footprint of conditional compilation in source +code: the more conditional code is concentrated and confined, +the easier the unconditional flow is to read. -``` -#if defined(OPENSSL_OS_FOO) || defined (OPENSSL_OS_BAR) -stuff the way foo or bar does it; -#elif defined(OPENSSL_OS_BLAH) || defined (OPENSSL_WOOF) -stuff the way blah or woof does it; -#elif -... -... -... -#endif /* defined(OPENSSL_OS_BLAH) || defined (OPENSSL_WOOF) */ -#endif /* defined(OPENSSL_OS_FOO) || defined (OPENSSL_OS_BAR) */ +Concentrate conditional compilation rather than dispersing it. +Do not duplicate the same OS-dispatch ladder across the +codebase: + +```c +#if defined(OPENSSL_OS_FOO) || defined(OPENSSL_OS_BAR) + stuff the way foo or bar does it; +#elif defined(OPENSSL_OS_BLAH) || defined(OPENSSL_OS_WOOF) + stuff the way blah or woof does it; +#endif /* defined(OPENSSL_OS_FOO) || defined(OPENSSL_OS_BAR) */ ``` -in many places. +For OS-dependent code in particular, put the directives inside +a single function that wraps the OS-dependent work, so callers +see a clean interface. When the OS-dependent implementations +are large, put them in separate files (`stuff_foo.c`, +`stuff_blah.c`) implementing a common function and select the +appropriate file via the build process; this lets non-mainstream +platforms add an implementation file without patching shared +code. -If you need this you should consider making one function that contains -the os dependent "stuff" with the conditional compilation directives -only in that function, or if this is large, make separate files -(stuff_foo.c stuff_blah.c) which implement the same function and -choose the implementation to include in the build process. The latter -method is friendlier to non mainstream platforms as os dependent -implementations can be maintained in separate files rather than -intrusive code patches. +When a feature can be compiled out, prefer to provide a no-op +stub implementation of its functions in the disabled case +rather than wrapping every call site in `#if`. Callers then +invoke the functions unconditionally and the compiler discards +the stubs: -## Macros and Enums -OpenSSL has historically made extensive use of macros in C, and the C -preprocessor. This was normal practice in older C codebases, when C -compilers were simple things and unable to avoid significant function -call overhead. This is no longer the case, so it is desirable to -reduce this and avoid it in new code. - -Macros and labels in enums should be named in -ALL_CAPS_WITH_UNDERSCORES. This convention helps distinguish macros -from functions and variables. - -``` -#define OPENSSL_MAGIC_FOO 0x12345 +```c +#if defined(OPENSSL_NO_FOO) +static ossl_inline int foo_init(void) { return 1; } +static ossl_inline void foo_cleanup(void) {} +#else +int foo_init(void); +void foo_cleanup(void); +#endif /* defined(OPENSSL_NO_FOO) */ ``` -Enums are preferred when defining several related constants. Note, -however, that enum arguments to public functions are not permitted. +Macros and enums +---------------- -### Avoid Complex Macros +**Just use a function, not a macro.** OpenSSL has historically +used macros heavily to avoid function-call overhead, but modern +compilers inline well; the trade-offs that justified that pattern +no longer apply. Where a macro is genuinely unavoidable, the +rules below apply. -Generally, avoid overly complex or "clever" macros that are difficult -to read, debug, or maintain. Nesting of macros calling other macros -should be avoided. Prioritize clarity and simplicity. +For the naming convention used for macros and enum labels, see the +[Macros and enum labels](#macros-and-enum-labels) subsection of +Naming above. -### Avoid Function-like Macros +Enums are preferred when defining several related constants. +Enum arguments to public functions are not permitted, because +C's `enum` underlying type is implementation-defined and adding +values can change ABI; see +[Structs and typedefs](#structs-and-typedefs) for the rule and +the canonical alternative (use `int` and document the allowed +values). -Functions should be preferred to function-like macros. New code or -refactors should be with functions. Do not 'pre-optimize' for the -overhead of a function call without first implementing with a function -and obtaining measurements to indicate function call overhead is a -significant problem. Only at this point you can consider inlining the -function in preference to a function-like macro, if you can then -measure a significant performance from inlining that does not generate -an undesirable code size increase. +Where the constants need a fixed underlying width (for ABI or +wire-format reasons), use `#define` or `static const` with an +explicit-width type from `` instead, since enum width +is implementation-defined. -### Macro Parenthesizetion +### Avoid complex macros -Always parenthesize arguments in function-like macros to prevent -operator precedence issues during expansion. +Avoid complex or clever macros: they are hard to read, debug, and +maintain. Do not nest macros calling other macros. -Enclose the entire macro definition in parentheses if it expands to an -expression to ensure correct evaluation when used within larger -expressions. For example: +### Avoid function-like macros -``` +Prefer functions over function-like macros. Do not optimise for +function-call overhead without first measuring with a function +implementation; if the function is hot enough to need inlining, +mark it `ossl_inline` rather than converting it to a macro. + +### Macro parenthesisation + +Always parenthesise arguments in function-like macros to prevent +operator-precedence issues during expansion. Enclose the entire +macro definition in parentheses if it expands to an expression, so +the expansion evaluates correctly inside larger expressions. For +example: + +```c #define BOB(blah) ((blah) + 42 - 23 / (blah)) ``` -### Multi Statement Macros +### Multi-statement macros -Enclose multi statement macros in a do {} while(0) loop. Do not -include a semicolon at the end of macros. Do not enclose macros in -braces. This ensures they can be used without problems. For example: +Enclose multi-statement macros in a `do { } while (0)` block. Do +not include a semicolon at the end, and do not use bare braces +(which fail when followed by `else`). For example: -``` -/* This is bad */ +```c +/* This is bad. */ #define KERMIT(x) muppet((x)); frog((x)); green((x)) -if (soemthing) - KERMIT(bob); -else /* This now breaks */ +if (something) + KERMIT(bob); +else /* This now breaks. */ /* This is also bad, because now you have to omit the semicolon. */ #define KERMIT(x) { muppet((x)); frog((x)); green((x)) } if (something) - KERMIT(bob) /* No semicolon */ + KERMIT(bob) /* No semicolon. */ else -/* This does not break */ +/* This works. */ #define KERMIT(x) do { muppet((x)); frog((x)); green((x)) } while (0) -/* Now this works sensibly */ if (something) KERMIT(bob); else /* - * But just use a function - Note we now know that x is an - * integer that has something to do with froggieness and we - * gain some type safety. + * But just use a function -- now we know that x is an integer that + * has something to do with frogginess and we gain some type safety. */ static void kermit(int frogginess) { - muppet(froggieness); - frog(froggieness); - green(froggieness); + muppet(frogginess); + frog(frogginess); + green(frogginess); } if (something) - kermit(bob); + kermit(bob); else ``` ### Do not include files as multi-line macros -Do not put code in a file and include it inline +Do not put code in a file and include it inline: -``` - ... - printf "Yolo\n"; -#include "./abagfullofcode.inc" - printf "That was fun\n"; - ... -``` - -Either make a function out of the code and call it or just put the -code in place. - -### Avoid Macros With Side Effects: - -Be extremely cautious with arguments that may have side effects. as they might be -evaluated multiple times in the macro expansion, leading to unexpected -behavior. Do not make macros that depend upon modifiying a particular -magic name: - -``` -#define FOO(val) bar(index, (val)) -``` - -It is confusing to the reader and is prone to breakage from seemingly innocent -changes. - -Do not write macros that are l-values: - -``` -FOO(x) = y -``` - -## Functions - -Ideally, functions should be short and sweet, and do just -one thing. A rule of thumb is that they should fit on one or two -screenfuls of text (25 lines as we all know), and do one thing and do -that well. - -The maximum length of a function is often inversely proportional to -the complexity and indentation level of that function. So, if you have -a conceptually simple function that is just one long (but simple) -switch statement, where you have to do lots of small things for a lot -of different cases, it’s okay to have a longer function. - -If you have a complex function, however, consider using helper -functions with descriptive names. You can ask the compiler to in-line -them if you think it’s performance-critical, and it will probably do a -better job of it than you would have done. - -Another measure of complexity is the number of local variables. If -there are more than five to 10, consider splitting it into smaller -pieces. A human brain can generally easily keep track of about seven -different things; anything more and it gets confused. Often things -which are simple and clear now are much less obvious two weeks from -now, or to someone else. An exception to this is the command-line -applications which support many options. - -In source files, separate functions with one blank line. In function -prototypes, include parameter names with their data types. Although -this is not required by the C language, it is preferred in OpenSSL -because it is a simple way to add valuable information for the -reader. The name in the prototype declaration should match the name in -the function definition. - -### Checking function arguments - -A public function should verify that its arguments are sensible. This -includes, but is not limited to, verifying that: - -* Non-optional pointer arguments are not NULL and, -* numeric arguments are within expected ranges. - -Where an argument is not sensible, an error should be returned. - -### Extending existing functions - -From time to time it is necessary to extend an existing function. Typically -this will mean adding additional arguments, but it may also include removal of -some. - -Where an extended function should be added the original function should be kept -and a new version created with the same name and an _ex suffix. For example, -the RAND_bytes function has an extended form called RAND_bytes_ex. - -Where an extended version of a function already exists and a second extended -version needs to be created then it should have an _ex2 suffix, and so on for -further extensions. - -When an extended version of a function is created the order of existing -parameters from the original function should be retained. However new -parameters may be inserted at any point (they do not have to be at the end), -and no longer required parameters may be removed. - -### Centralized exiting of functions - -The goto statement comes in handy when a function exits from multiple locations -and some common work such as cleanup has to be done. If there is no cleanup -needed then just return directly. The rationale for this is as follows: - -* Unconditional statements are easier to understand and follow -* It can reduce excessive control structures and nesting -* It avoids errors caused by failing to update multiple exit points when - the code is modified -* It saves the compiler work to optimize redundant code away ;) - -For example: -``` -int fun(int a) -{ - int result = 0; - char *buffer = OPENSSL_malloc(SIZE); - - if (buffer == NULL) - return -1; - - if (condition1) { - while (loop1) { - ... - } - result = 1; - goto out; - } +```c ... -out: - OPENSSL_free(buffer); - return result; + printf("Yolo\n"); +#include "./abagfullofcode.inc" + printf("That was fun\n"); + ... +``` + +Either make a function out of the code and call it, or put the code +in place. + +### Avoid macros that evaluate arguments multiple times + +Be cautious with arguments that may have side effects, since they +may be evaluated more than once during macro expansion, with +unexpected results: + +```c +#define SQUARE(x) ((x) * (x)) + +int n = 1; +int result = SQUARE(n++); /* expands to ((n++) * (n++)) -- evaluates twice */ +``` + +### Avoid macros that depend on magic names + +Do not write macros that rely on a particular variable name being +in scope at the call site: + +```c +#define FOO(val) bar(index, (val)) /* requires `index' to exist */ +``` + +This is confusing to the reader and prone to breakage from +seemingly innocent changes. + +### Avoid macros that expand to l-values + +Do not write a macro that expands to something assignable: + +```c +#define FIELD(p) (((struct foo *)(p))->field) + +FIELD(x) = y; /* legal C, but the macro hides the assignment */ +``` + +Use an accessor function or expose the field directly through a +typed pointer. + +### Avoid macros that affect control flow + +Do not write macros that `return`, `goto`, `break`, or `continue` +out of their expansion. Such macros hide control flow from a +reader at the call site, who sees what looks like a function +call but which may exit the surrounding function or jump out of +a loop: + +```c +#define RETURN_IF_NULL(p) do { if ((p) == NULL) return -1; } while (0) + +int f(void *p) +{ + RETURN_IF_NULL(p); /* may return from f() -- not visible at the call site */ + /* ... */ } ``` -## Return Values + +### Avoid `#` and `##` in new code + +The stringification (`#`) and token-pasting (`##`) operators are +forbidden in new code. Existing macros that use them (notably the +`DECLARE_*` and `IMPLEMENT_*` macro families) are not retroactively +changed; new code should achieve the same effect through +functions. + +### Use variadic macros sparingly + +Variadic macros (`__VA_ARGS__`) are permitted but should be used +sparingly: prefer a function or a small set of helper functions +where possible. They are harder to reason about and debug than +functions, and the rules around zero variadic arguments and +`__VA_ARGS__` forwarding are subtle. + +Functions +--------- + +A function should do one thing and be short enough that a +reader can hold its behaviour in their head while reading it. +Length follows from complexity, not the other way around: a +long but flat function (for example, a single switch dispatching +to many cases) is fine; a short function with three levels of +nested control flow is not. + +When complexity grows, factor out helpers with descriptive +names. A large number of local variables is a signal that this +factoring is overdue; consider splitting before reaching for a +comment to explain the variables. Performance-critical helpers +can be marked `inline`; see +[Avoid function-like macros](#avoid-function-like-macros) for +why this is preferable to a macro. + +In function prototypes, include parameter names alongside their +types. C does not require this, but it carries useful information +for the reader; the name in the prototype should match the name +in the definition. + +### Functions with no arguments + +A function that takes no arguments must declare so explicitly +with `void` in its parameter list: `int f(void);`, not +`int f();`. The latter declares the parameter list as +unspecified and prevents the compiler from checking calls. + +### Internal linkage + +Functions that are local to a single source file are declared +`static`. Static functions need no `ossl_` prefix (see +[Naming](#functions-and-variables) above) and do not appear in +the symbol table of the resulting object file. + +### Parameter ordering + +In OpenSSL's API style, a context parameter (an `SSL_CTX *`, +`EVP_PKEY_CTX *`, `OSSL_LIB_CTX *`, or similar) is the first +parameter. The order of the remaining parameters is at the +function's discretion but should be consistent with similar +functions in the same subsystem. + +### `const`-correctness + +Pointer parameters that are not modified by the function should +be declared `const`; likewise, pointer return values that the +caller must not modify should be declared `const`. The +return-side rule pairs with the `get0_X()` ownership convention: +a non-owning pointer is typically a read-only view, while an +owning pointer returned by `get1_X()` is non-`const` because the +caller controls it. The `const` qualifier documents the contract, +allows callers to pass or receive `const`-qualified data without +casts, and lets the compiler catch accidental modification. ### Return values in legacy code + Historically, functions in OpenSSL can return values of many different kinds, and one of the most common is a value indicating whether the function succeeded or failed. Usually this is: -* 1: success -* 0: failure +- `1`: success +- `0`: failure -Sometimes an additional value is used: +Other patterns appear in legacy code: -* -1: something bad (e.g., internal error or memory allocation failure) +- `-1` indicates a serious error (internal error or memory + allocation failure), and in some subsystems (BIO, SSL, etc.) + means "should retry" +- `>= 1` indicates success with the value carrying additional + information; `<= 0` indicates failure with the value indicating + the reason -Other APIs use the following pattern: +Functions that return a computed value (not a success/failure +indicator) are exempt. -* \>= 1: success, with value returning additional information -* <= 0: failure with return value indicating why things failed - -Sometimes a return value of -1 can mean “should retry” (e.g., BIO, -SSL, et al). Functions whose return value is the actual result of a -computation, rather than an indication of whether the computation -succeeded, are not subject to these rules. - -When constrained by legacy code, you should follow the existing API's -convention for return values. Be certain you are aware of what this is -when modifying such code. +**Read the existing return-value contract carefully before +modifying legacy code.** OpenSSL's legacy return-value +conventions are not uniform -- a function may use values, +overloadings, or semantics outside the patterns above -- and +bugs have been introduced into OpenSSL when contributors +assumed a function followed a familiar pattern when it did not. +The contract is part of the API, not just a stylistic choice. ### Return values in new code -For new code, functions should return `int` with one on -success and zero on error. Do not overload the return value to both +For new code, functions should return `int` with `1` on success +and `0` on error. Do not overload the return value to both signal success/failure and output an integer. For example: -``` - /** - * \brief ossl_snuffle_thingamabob snuffles a thingamabob from bytes of input. - * If a valid thingamabob is snuffled, the result is stored in - * |*out_thingamabob|. On failure a snuffling error code is stored - * in |*out_err|. - * \param input - pointer to the bytes to snuffle - * \param input_len - the number of bytes available to snuffle from |input|. - * \param out_err - pointer to a integer to store an error code. - * \param out_thingamabob - pointer to a thingamabob to store the output. - * \returns 1 if a thingamabob was snuffled and stored, 0 otherwise. - */ - int ossl_snuffle_thingamabob(uint8_t *input, size_t input_len, - int *out_err, thingamabob *out_thingamabob); +```c +/** + * @brief ossl_snuffle_thingamabob snuffles a thingamabob from bytes of input. + * If a valid thingamabob is snuffled, the result is stored in + * *out_thingamabob. On failure a snuffling error code is stored + * in *out_err. + * @param input pointer to the bytes to snuffle + * @param input_len the number of bytes available to snuffle from input + * @param out_err pointer to an integer to store an error code + * @param out_thingamabob pointer to a thingamabob to store the output + * @returns 1 if a thingamabob was snuffled and stored, 0 otherwise. + */ +int ossl_snuffle_thingamabob(uint8_t *input, size_t input_len, + int *out_err, thingamabob *out_thingamabob); ``` -If a function outputs a pointer to an object on success and there are *no -other outputs*, and you are certain there never would be other outputs, -return the pointer directly and `NULL` on error. +If a function outputs a single pointer and no other values, +return the pointer directly, with `NULL` on error. -## Allocating memory -OpenSSL provides many general purpose memory utilities, including, but not -limited to: OPENSSL_malloc(), OPENSSL_zalloc(), OPENSSL_realloc(), OPENSSL_ -memdup(), OPENSSL_strdup() and OPENSSL_free(). Please refer to the API -documentation for further information about them. +### Checking function arguments -OpenSSL provides special purpose allocators for arrays, including -OPENSSL_malloc_array() and OPENSSL_realloc_array() which take -arguments of the element size and number of elements desired. Always -use these for array allocation, as these functions check for integer -overflow conditions safely when computing the size of the allocation. +A public function must verify that its arguments are sensible +and return its documented failure value if they are not. +Typical checks include: -If you design an API that requires internal memory allocations before use, -ensure you provide both an initialization API to do the allocation before -using it, and a completion API to de-allocate the memory that consumers -of your API can call when finished. +- non-optional pointer arguments are not NULL; +- numeric arguments are within their expected ranges. -## Processor-specific code -In OpenSSL’s case the only reason to resort to processor-specific code is for -performance. As it still exists in a general platform-independent algorithm -context, it always has to be backed up by a neutral pure C one. This implies -certain limitations. +Public-API callers are outside the OpenSSL development envelope. +The contract cannot be enforced through code review, so a NULL +non-optional pointer or an out-of-range integer is a possibility +that must be handled defensively at the boundary. Failing with a +documented error code on the error stack is preferable to a +SIGSEGV in the calling application's process. -The most common way to resolve this conflict is to opt for -short inline assembly function-like snippets, customarily implemented as -macros, so that they can be easily interchanged with other platform-specific or -neutral code. As with any macro, try to implement it as single expression. -You may need to mark your asm statement as volatile, to prevent GCC from -removing it if GCC doesn’t notice any side effects. You don’t always need to do -so, though, and doing so unnecessarily can limit optimization. - -When writing a single inline assembly statement containing multiple -instructions, put each instruction on a separate line in a separate quoted -string, and end each string except the last with \n\t to properly indent the -next instruction in the assembly output: +For NULL pointer arguments, the canonical pattern is: +```c +if (arg == NULL) { + ERR_raise(ERR_LIB_, ERR_R_PASSED_NULL_PARAMETER); + return 0; +} ``` + +Use the function's documented failure value in place of `0` +where it differs (`NULL` for pointer-returning functions, `-1` +for functions that may return `-1`, and so on). + +Internal functions must not repeat these checks. Their callers +are us; the contract is enforceable in code review, and a NULL +or out-of-range argument is a programmer error of the same +character as the impossibilities discussed under +[Assertions](#assertions). A runtime check at an internal call +site is dead on any correct execution, and the untested branch +is itself attack surface. Use `assert()` instead where you want +to document an internal invariant. + +### Extending existing functions + +When an existing public function needs additional parameters, +keep the original and add a new function with the same name plus +an `_ex` suffix (`RAND_bytes_ex` extends `RAND_bytes`). Further +extensions use `_ex2`, `_ex3`, and so on. + +The extended function preserves the existing parameters in their +existing order. New parameters may be inserted at any position +(they do not have to be at the end); parameters that are no +longer needed may be removed. + +### Centralised exiting of functions + +When a function exits from multiple locations and some common +work (such as cleanup) has to be done at every exit, use `goto` +to a single exit label. Return directly when there is no cleanup +to do. The rationale: + +- a single exit point is easier to read and follow; +- it reduces excessive control structures and nesting; +- it avoids errors caused by failing to update multiple exit + points when the code changes; +- it lets the compiler avoid emitting redundant cleanup code. + +For example: + +```c +int do_thing(const uint8_t *in, size_t in_len) +{ + int ret = 0; + uint8_t *buf = OPENSSL_malloc(in_len); + + if (buf == NULL) + return 0; + + if (!step1(in, in_len, buf)) + goto out; + if (!step2(buf, in_len)) + goto out; + + ret = 1; +out: + OPENSSL_free(buf); + return ret; +} +``` + +Error reporting +--------------- + +OpenSSL surfaces errors through a per-thread error stack; see +`ERR_raise(3)` for the calls and `include/openssl/err.h` for the +available reason codes. This section describes the conventions +for using them. + +Raise at the leaf. The function that detects the failure pushes +the error; intermediate wrappers that propagate the failure +value must not re-raise. Re-raising on each frame floods the +stack with duplicates and obscures the originating condition. + +Use the `ERR_LIB_` corresponding to the function's +home directory (`ERR_LIB_X509` in code under `crypto/x509/`, and +so on). Use a cross-library reason (`ERR_R_PASSED_NULL_PARAMETER`, +`ERR_R_MALLOC_FAILURE`, `ERR_R_INTERNAL_ERROR`, and others) for +portable failure modes; use a `SUBSYSTEM_R_REASON` +(`X509_R_INVALID_TRUST`, etc.) for domain-specific ones. + +Do not call `ERR_clear_error` at function entry; the error stack +belongs to the caller, who may have pushed errors before +invoking you that they intend to inspect. + +Use `ERR_set_mark` / `ERR_pop_to_mark` to suppress error-stack +pollution from operations expected to fail sometimes (a +speculative parse, a capability probe), leaving earlier errors +intact. + +Allocating memory +----------------- + +Use the `OPENSSL_malloc` family for general allocation; see +`OPENSSL_malloc(3)` for the full set of calls. Do not mix these +with the C standard library's `malloc()` / `free()` family; +allocations made with one set must be released with the matching +set, and OpenSSL can be built with custom allocator hooks that +the C library does not know about. + +For arrays, use `OPENSSL_malloc_array()` and +`OPENSSL_realloc_array()`, which take the element size and +element count separately and check for integer overflow. + +Memory holding sensitive material (key bytes, plaintext, +internal state of cryptographic primitives) must be cleansed +before release. `OPENSSL_clear_free()` combines cleansing and +freeing; `OPENSSL_cleanse()` wipes without freeing. For +long-lived sensitive data, use the `OPENSSL_secure_malloc()` +family (`OPENSSL_secure_malloc(3)`), which allocates from a +separate non-pageable secure heap, and release with +`OPENSSL_secure_clear_free()`. + +An API that owns internal state requires both an initialisation +function to set it up and a completion function to release it. +This is the standard constructor/destructor pair for opaque +types; see [Structs and typedefs](#structs-and-typedefs). + +Processor-specific code +----------------------- + +The only reason for processor-specific code in OpenSSL is +performance. Every processor-specific path must have a +platform-neutral pure-C implementation as a fallback, because +not every target architecture or build configuration enables +the processor-specific path. OpenSSL selects between +implementations at runtime via the CPU-capability detection in +`OPENSSL_cpuid_setup` and the `OPENSSL_*` capability flags; +processor-specific code must integrate with this dispatch. + +Cryptographic primitives operating on secret data must execute +in time independent of those secrets. Avoid secret-dependent +branches, secret-indexed memory accesses, and variable-time +arithmetic (such as variable-time multiplication or division) +on words derived from secrets. Hand-coded asm is sometimes used +specifically to force a particular sequence of constant-time +operations that a compiler might otherwise rewrite. + +Short processor-specific operations are typically written as +inline assembly. Use a `static inline` function when the asm +constraints permit it. When the asm requires a compile-time +constant operand (an `i` constraint), use a statement-expression +macro instead, because a function parameter does not satisfy the +immediate-constant constraint. When `asm()` has side effects the +compiler cannot see, mark it `volatile`; do not mark `volatile` +unnecessarily as that limits optimisation. + +When writing a single inline assembly statement containing +multiple instructions, put each instruction on a separate line +in a separate quoted string, and end each string except the +last with `\n\t` to properly indent the next instruction in the +assembly output: + +```c asm ("magic %reg1, #42\n\t" "more_magic %reg2, %reg3" : /* outputs */ : /* inputs */ : /* clobbers */); ``` -Large, non-trivial assembly functions go in pure assembly modules, with -corresponding C prototypes defined in C. The preferred way to implement this is -so-called “perlasm”: instead of writing real .s file, you write a perl script -that generates one. This allows use symbolic names for variables (register as -well as locals allocated on stack) that are independent on specific assembler. -It simplifies implementation of recurring instruction sequences with regular -permutation of inputs. By adhering to specific coding rules, perlasm is also -used to support multiple ABIs and assemblers, see crypto/perlasm/x86_64- -xlate.pl for an example. +Large, non-trivial assembly functions go in pure assembly +modules, with corresponding C prototypes. The preferred way to +generate these is *perlasm*: a Perl script that generates a +`.s` file. Perlasm allows symbolic names for variables +(registers and stack-allocated locals) that are independent of +the specific assembler, and supports multiple ABIs and +assemblers from a single source by adhering to its coding +rules. See `crypto/perlasm/x86_64-xlate.pl` for an example. -Another option for processor-specific (primarily SIMD) capabilities is called -compiler intrinsics. We avoid this, because it’s not very much less complicated -than coding pure assembly, and it doesn’t provide the same performance -guarantee across different micro-architecture. Nor is it portable enough to -meet our multi-platform support goals. +Compiler intrinsics are permitted but used sparingly. They are +appropriate for self-contained SIMD acceleration where the +intrinsic vocabulary is well-supported across our target +compilers and the code does not need to span multiple ABIs -- +`crypto/evp/enc_b64_avx2.c` (AVX2 base64) is an example. +Intrinsics are not appropriate for cryptographic primitives +where constant-time execution is required (the compiler may +reorder, branch, or otherwise alter the timing), or where an +existing perlasm implementation already covers the multi-ABI +case. -## Asserts -We have 3 kind of asserts. The behaviour depends on being a debug or release -build: -``` -Function failure release failure debug success release success debug -assert not evaluated abort not evaluated nothing -ossl_assert returns 0 abort returns 1 returns 1 -OPENSSL_assert abort abort nothing nothing -``` +Assertions +---------- -Use OPENSSL_assert() only in the following cases: +Assertions check programmer errors -- invariants, preconditions, and +postconditions that must hold in any correctly-functioning build. +They are not for runtime conditions such as allocation failure, I/O +errors, or malformed input from callers; those are errors the +surrounding code must handle and propagate. -* In the libraries when the global state of the software is corrupted and - there is no way to recover it -* In applications, test programs and fuzzers +OpenSSL provides three assertion forms, which differ in their +behaviour depending on whether `NDEBUG` is defined (release) or not +(debug): -Use ossl_assert() in the libraries when the state can be recovered and an error -can be returned. Example code: +| Form | Failure (debug) | Failure (release) | Success | +|---|---|---|---| +| `assert(e)` | abort | `e` not evaluated | no effect | +| `ossl_assert(e)` | abort | returns 0 | returns 1 | +| `OPENSSL_assert(e)` | abort | abort | no effect | -``` -if (!ossl_assert(!should_not_happen)) { - /* push internal error onto error stack */ - return BAD; +Choosing between these forms is a trade-off, not a default. Each +form pays a cost somewhere: + +- `OPENSSL_assert()` terminates the host process when an invariant + fails, which is hostile to applications that link against + OpenSSL. +- `ossl_assert()` returns failure in release builds so the host + process survives, but the caller must then handle a failure for + a condition that, by definition, cannot occur in correct code. + That handling code is dead on any correct execution and cannot + be exercised by ordinary tests; untested branches accumulate + their own bugs and become part of the attack surface. +- `assert()` is silently dropped in release builds, so an invariant + violation in production passes through to downstream code that + may then operate on inconsistent state. + +Use `ossl_assert()` when the surrounding function already returns +success/failure and the recovery path collapses naturally into the +function's existing error path: push an internal error and return +the function's failure value. The recovery code is then colocated +with tested error handling and is not a structurally new branch: + +```c +if (!ossl_assert(invariant_holds)) { + ERR_raise(ERR_LIB_..., ERR_R_INTERNAL_ERROR); + return 0; } ``` -Use assert() in libraries when no error can be returned, and what you are checking -is not a run-time dependent condition (such as failure to allocate resources or open -a file) but a programmer error. +Use `assert()` for impossible cases in internal code -- typically +`switch` defaults, unreachable branches in helpers, and invariants +local to a function whose contract makes the violation strictly +impossible. The release-build behaviour ("do nothing") is the +right choice here, because the alternative is untested recovery +code for a case that cannot occur, and that code is itself a +hazard. The assertion expression must be free of side effects, +because `assert()` does not evaluate it in release builds. + +Use `OPENSSL_assert()` only when continued execution would be more +dangerous than termination -- typically when global library state +is irrecoverably corrupted -- or in applications, test programs, +and fuzzers where termination on a failed check is desired. +`OPENSSL_assert()` aborts in all builds, including production. From c17828595f60ab0a91c54a8da7c3461387ea77a7 Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Fri, 12 Jun 2026 09:55:42 -0600 Subject: [PATCH 221/349] Change the Doxygen doc requirement for non public functions To be at the prototype site in the internal header file. The major reason *Why* we would like to have Doxygen style comments describing what internals do is so that they work with modern IDE's since most common ones support them. It's wonderful to be looking at an internal function, thinking "wtf is this", and be able to hover over it and - boink - up comes the docs. This typically only works (or works better) when the Doxygen comment is at the prototype site, not if it is at the implementation site. This also reinforces the requirement that "yes you do this for shared functions but you don't need to for statics". Reviewed-by: Neil Horman Reviewed-by: Milan Broz MergeDate: Fri Jul 10 15:37:18 2026 (Merged from https://github.com/openssl/openssl/pull/29295) --- DOCUMENTATION.md | 39 ++++++++++++++++++++++++--------------- STYLE.md | 5 +---- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 6fb2b70fa5..05bbb8564c 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -64,14 +64,22 @@ included in the `doc/man7` section. This includes, but is not limited to: Internal functions, structures, globals and macros -------------------------------------------------- -Internal functions and macros are those defined in the `include/crypto` -and `include/internal` directories. +Internal functions, structures, globals and macros are non-public +items declared in any header that is not part of the public API. +These include items declared in: -These should all be documented at the point of implementation, using a -doxygen-style comment block. The comment should describe the purpose -and, for functions, the input and output arguments and the return -value. See [STYLE.md](STYLE.md) for the doxygen conventions used by -OpenSSL. +- `include/internal/` (shared across subsystems); +- `include/crypto/` (cryptographic internals); +- per-directory local headers (for example, `crypto/asn1/asn1_local.h`) + shared between source files in a single subdirectory. + +These should all be documented at the declaration site -- that is, +in the header that declares them -- using a doxygen-style comment +block. For functions, this places the comment at the prototype, +where editor tooling (clangd and similar) can surface it to readers +at every call site. The comment should describe the purpose and, +for functions, the input and output arguments and the return value. +See [STYLE.md](STYLE.md) for the doxygen conventions used by OpenSSL. For *trivial* items, where their operation is obvious from their implementation, the documentation requirement is not mandated. The @@ -96,16 +104,17 @@ obvious. Some representative examples: - `struct *next;` in a linked list implementation. - `CRYPTO_REF_COUNT refcnt;` -Static functions and globals and local structures and macros ------------------------------------------------------------- +File-local items +---------------- -These are functions, structures, globals and macros local to a specific -C file or defined for a single directory as part of a local `.h` file. +These are functions, structures, globals, and macros that are local +to a single C file: `static` functions, file-scope variables, +structures, and macros defined inside a `.c` file with no declaration +in any header. -These should all be documented at the point of implementation. Follow -the same rules and exceptions as for internal functions and structures -above. In some cases slightly more leniency with respect to *trivial* -can be tolerated. +These should all be documented at the point of definition. Follow the +same rules and exceptions as for internal items above. In some cases +slightly more leniency with respect to *trivial* can be tolerated. Code comments ------------- diff --git a/STYLE.md b/STYLE.md index a5df9546f0..35d08983e5 100644 --- a/STYLE.md +++ b/STYLE.md @@ -314,10 +314,7 @@ typedef struct foo_st { * @param b input integer to add * @returns the sum of a and b */ -int add(int a, int b) -{ - return a + b; -} +int add(int a, int b); ``` #### Spec-mirroring variables From a40f6f8bd67d209bb2d90baa6fad8b06a7f18a16 Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Wed, 8 Jul 2026 10:43:00 -0600 Subject: [PATCH 222/349] Make the examples in SYTLE and DOCUMENTATON compliant. Specifically bring them closer to the suggested naming conventions and make them clang-format compliant. Reviewed-by: Neil Horman Reviewed-by: Milan Broz MergeDate: Fri Jul 10 15:37:20 2026 (Merged from https://github.com/openssl/openssl/pull/29295) --- STYLE.md | 71 +++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 26 deletions(-) diff --git a/STYLE.md b/STYLE.md index 35d08983e5..2bedc42a5a 100644 --- a/STYLE.md +++ b/STYLE.md @@ -294,7 +294,7 @@ The following sample illustrates the convention: * @param y integer input value * @returns the maximum of x and y */ -#define MAX(x, y) (x > y ? x : y) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) /** * @struct foo_st @@ -307,14 +307,14 @@ typedef struct foo_st { } FOO; /** - * @brief Describe the function add briefly. + * @brief Describe the function ossl_add briefly. * Add a more detailed description here, like sums two inputs and * returns the result. * @param a input integer to add * @param b input integer to add * @returns the sum of a and b */ -int add(int a, int b); +int ossl_add(int a, int b); ``` #### Spec-mirroring variables @@ -332,10 +332,16 @@ each spec-derived variable: * - Calvin: input to be transmogrified * - Hobbes: transmogrified output (caller-allocated) * + * @param Calvin pointer to the input bytes to transmogrify + * @param Calvin_len the number of bytes available at Calvin + * @param Hobbes pointer to the caller-allocated output buffer + * @param Hobbes_len the number of bytes available at Hobbes + * @returns 1 on success, 0 on failure * @see https://www.example.org/rfc/rfc31337.html#section-1.2.3 + * @see https://calvinandhobbes.fandom.com/wiki/Transmogrifier */ -int transmogrify(const uint8_t *Calvin, size_t Calvin_len, - uint8_t *Hobbes, size_t Hobbes_len); +int ossl_transmogrify(const uint8_t *Calvin, size_t Calvin_len, + uint8_t *Hobbes, size_t Hobbes_len); ``` #### Public functions: link the manual page @@ -497,7 +503,7 @@ form rather than `#pragma once`, which is non-standard: ```c #if !defined(OPENSSL_FOO_H) -# define OPENSSL_FOO_H +#define OPENSSL_FOO_H /* ... header contents ... */ @@ -550,11 +556,11 @@ the stubs: ```c #if defined(OPENSSL_NO_FOO) -static ossl_inline int foo_init(void) { return 1; } -static ossl_inline void foo_cleanup(void) {} +static ossl_inline int ossl_foo_init(void) { return 1; } +static ossl_inline void ossl_foo_cleanup(void) { } #else -int foo_init(void); -void foo_cleanup(void); +int ossl_foo_init(void); +void ossl_foo_cleanup(void); #endif /* defined(OPENSSL_NO_FOO) */ ``` @@ -605,7 +611,7 @@ the expansion evaluates correctly inside larger expressions. For example: ```c -#define BOB(blah) ((blah) + 42 - 23 / (blah)) +#define BOB(blah) ((blah) + 42 - 23) ``` ### Multi-statement macros @@ -663,26 +669,39 @@ Do not put code in a file and include it inline: Either make a function out of the code and call it, or put the code in place. -### Avoid macros that evaluate arguments multiple times +### Be careful with macro arguments that have side effects -Be cautious with arguments that may have side effects, since they -may be evaluated more than once during macro expansion, with +Be careful when writing a function-like macro that could be called +with arguments that have side effects. Because a macro may expand an +argument more than once, a side-effecting argument (`n++`, a function +call, a volatile access) can then be evaluated more than once, with unexpected results: ```c #define SQUARE(x) ((x) * (x)) int n = 1; -int result = SQUARE(n++); /* expands to ((n++) * (n++)) -- evaluates twice */ +int result = SQUARE(n++); /* expands to ((n++) * (n++)) -- evaluates twice */ ``` +Where it can reasonably be avoided, prefer a form that expands each +argument exactly once -- a function, or an `ossl_inline` function +for a fixed type. If there is any doubt that your function-like +macro could be called with arguments that have side effects, treat +that as a sign to follow the advice in +[Avoid function-like macros](#avoid-function-like-macros) and make +it a real function. Some macros cannot avoid it: a type-generic macro +such as `MAX` must name each operand and so evaluates it more than +once. When that is unavoidable, say so at the definition and avoid +passing side-effecting expressions at the call site. + ### Avoid macros that depend on magic names Do not write macros that rely on a particular variable name being in scope at the call site: ```c -#define FOO(val) bar(index, (val)) /* requires `index' to exist */ +#define FOO(val) bar(index, (val)) /* requires `index' to exist */ ``` This is confusing to the reader and prone to breakage from @@ -695,7 +714,7 @@ Do not write a macro that expands to something assignable: ```c #define FIELD(p) (((struct foo *)(p))->field) -FIELD(x) = y; /* legal C, but the macro hides the assignment */ +FIELD(x) = y; /* legal C, but the macro hides the assignment */ ``` Use an accessor function or expose the field directly through a @@ -712,9 +731,9 @@ a loop: ```c #define RETURN_IF_NULL(p) do { if ((p) == NULL) return -1; } while (0) -int f(void *p) +int ossl_frobnicate(void *p) { - RETURN_IF_NULL(p); /* may return from f() -- not visible at the call site */ + RETURN_IF_NULL(p); /* may return from ossl_frobnicate() -- not visible at the call site */ /* ... */ } ``` @@ -839,7 +858,7 @@ signal success/failure and output an integer. For example: * @param out_thingamabob pointer to a thingamabob to store the output * @returns 1 if a thingamabob was snuffled and stored, 0 otherwise. */ -int ossl_snuffle_thingamabob(uint8_t *input, size_t input_len, +int ossl_snuffle_thingamabob(const uint8_t *input, size_t input_len, int *out_err, thingamabob *out_thingamabob); ``` @@ -912,7 +931,7 @@ to do. The rationale: For example: ```c -int do_thing(const uint8_t *in, size_t in_len) +int ossl_do_thing(const uint8_t *in, size_t in_len) { int ret = 0; uint8_t *buf = OPENSSL_malloc(in_len); @@ -920,9 +939,9 @@ int do_thing(const uint8_t *in, size_t in_len) if (buf == NULL) return 0; - if (!step1(in, in_len, buf)) + if (!ossl_step1(in, in_len, buf)) goto out; - if (!step2(buf, in_len)) + if (!ossl_step2(buf, in_len)) goto out; ret = 1; @@ -1025,9 +1044,9 @@ last with `\n\t` to properly indent the next instruction in the assembly output: ```c -asm ("magic %reg1, #42\n\t" - "more_magic %reg2, %reg3" - : /* outputs */ : /* inputs */ : /* clobbers */); +asm("magic %reg1, #42\n\t" + "more_magic %reg2, %reg3" + : /* outputs */ : /* inputs */ : /* clobbers */); ``` Large, non-trivial assembly functions go in pure assembly From 4f17865ba44b904c99d8e10ac1e460b811b7842e Mon Sep 17 00:00:00 2001 From: Daniel Kubec Date: Tue, 7 Jul 2026 09:16:19 +0000 Subject: [PATCH 223/349] NULL-pointer subtraction UB in tls_collect_extensions() Fixed invalid-pointer-pair in the existing branch by ensuring thisex != NULL before subtraction. Fixes #31689 Reviewed-by: Tom Cosgrove Reviewed-by: Tomas Mraz MergeDate: Fri Jul 10 15:40:21 2026 (Merged from https://github.com/openssl/openssl/pull/31875) --- ssl/statem/extensions.c | 25 ++++++++++++---------- test/sslapitest.c | 46 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 11 deletions(-) diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c index 017d1a5fb5..c3ef683d75 100644 --- a/ssl/statem/extensions.c +++ b/ssl/statem/extensions.c @@ -907,6 +907,11 @@ int tls_collect_extensions(SSL_CONNECTION *s, PACKET *packet, SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_EXTENSION); goto err; } + + /* The server must tolerate the unknown extension and complete. */ + if (thisex == NULL) + continue; + idx = (unsigned int)(thisex - raw_extensions); /*- * Check that we requested this extension (if appropriate). Requests can @@ -937,17 +942,15 @@ int tls_collect_extensions(SSL_CONNECTION *s, PACKET *packet, SSL_R_UNSOLICITED_EXTENSION); goto err; } - if (thisex != NULL) { - thisex->data = extension; - thisex->present = 1; - thisex->type = type; - thisex->received_order = i++; - if (s->ext.debug_cb) - s->ext.debug_cb(SSL_CONNECTION_GET_USER_SSL(s), !s->server, - thisex->type, PACKET_data(&thisex->data), - (int)PACKET_remaining(&thisex->data), - s->ext.debug_arg); - } + thisex->data = extension; + thisex->present = 1; + thisex->type = type; + thisex->received_order = i++; + if (s->ext.debug_cb) + s->ext.debug_cb(SSL_CONNECTION_GET_USER_SSL(s), !s->server, + thisex->type, PACKET_data(&thisex->data), + (int)PACKET_remaining(&thisex->data), + s->ext.debug_arg); } if (init) { diff --git a/test/sslapitest.c b/test/sslapitest.c index 8baf38b93d..d751385fd2 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -12920,6 +12920,51 @@ end: SSL_CTX_free(cctx); return testresult; } + +static int un_ext_add_cb(SSL *s, unsigned int ext_type, + unsigned int context, const unsigned char **out, size_t *outlen, X509 *x, + size_t chainidx, int *al, void *add_arg) +{ + static const unsigned char data[] = { 0xaa }; + *out = data; + *outlen = sizeof(data); + return 1; +} + +static int un_ext_parse_cb(SSL *s, unsigned int ext_type, + unsigned int context, const unsigned char *in, size_t inlen, X509 *x, + size_t chainidx, int *al, void *parse_arg) +{ + return 1; +} + +/* + * Test that a handshake succeeds when the peer sends an extension type we do + * not recognise. The client registers a custom extension in its ClientHello + * that the server knows nothing about, so on the server tls_collect_extensions() + * takes the "unknown extension" branch. + */ +static int test_tls13_unknown_extension(void) +{ + SSL_CTX *s = NULL, *c = NULL; + SSL *s_ssl = NULL, *c_ssl = NULL; + int test; + + test = TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(), + TLS_client_method(), TLS1_3_VERSION, TLS1_3_VERSION, &s, &c, cert, privkey)) + && TEST_true(SSL_CTX_add_custom_ext(c, 0xfefe, SSL_EXT_CLIENT_HELLO, + un_ext_add_cb, NULL, NULL, un_ext_parse_cb, NULL)) + && TEST_true(create_ssl_objects(s, c, &s_ssl, &c_ssl, NULL, NULL)) + /* The server must tolerate the unknown extension and complete. */ + && TEST_true(create_ssl_connection(s_ssl, c_ssl, SSL_ERROR_NONE)); + + SSL_free(s_ssl); + SSL_free(c_ssl); + SSL_CTX_free(s); + SSL_CTX_free(c); + return test; +} + #endif /* OSSL_NO_USABLE_TLS1_3 */ static int check_version_string(SSL *s, int version) @@ -15380,6 +15425,7 @@ int setup_tests(void) #ifndef OSSL_NO_USABLE_TLS1_3 ADD_TEST(test_read_ahead_key_change); ADD_ALL_TESTS(test_tls13_record_padding, 6); + ADD_TEST(test_tls13_unknown_extension); #endif #if !defined(OPENSSL_NO_TLS1_2) && !defined(OSSL_NO_USABLE_TLS1_3) ADD_ALL_TESTS(test_serverinfo_custom, 4); From 3bf2aba5f5e2e602b7b72b71d91e3befb08e9e94 Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Tue, 7 Jul 2026 12:32:13 +0200 Subject: [PATCH 224/349] Document the effect of SSL_VERIFY_FAIL_IF_NO_PEER_CERT on post-handshake auth Reviewed-by: Matt Caswell Reviewed-by: Paul Dale MergeDate: Fri Jul 10 15:45:39 2026 (Merged from https://github.com/openssl/openssl/pull/31876) --- doc/man3/SSL_CTX_set_verify.pod | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/man3/SSL_CTX_set_verify.pod b/doc/man3/SSL_CTX_set_verify.pod index 58774796cd..1a9ef7d83e 100644 --- a/doc/man3/SSL_CTX_set_verify.pod +++ b/doc/man3/SSL_CTX_set_verify.pod @@ -74,7 +74,9 @@ SSL_CTX_set_client_cert_cb() if no certificate is provided at initialization. SSL_verify_client_post_handshake() causes a CertificateRequest message to be sent by a server on the given B connection. The SSL_VERIFY_PEER flag must -be set; the SSL_VERIFY_POST_HANDSHAKE flag is optional. +be set; the SSL_VERIFY_POST_HANDSHAKE flag is optional. The +SSL_VERIFY_FAIL_IF_NO_PEER_CERT flag is also applicable and has the same +effect as with the client authentication during the handshake. =head1 NOTES From 24d208262acba826724860c568160cb04367068f Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 7 Jul 2026 16:33:12 +0200 Subject: [PATCH 225/349] test-rng: handle nonce length query in generate mode The DRBG instantiation probes the parent nonce callback with a NULL output buffer to obtain the nonce length before requesting the actual nonce. The generate mode branch of test_rng_nonce() wrote the bytes without checking the output pointer, crashing when TEST-RAND with generate=1 is used as a DRBG parent. The entropy-buffer branch already handles a NULL output correctly. Assisted-by: Claude:claude-fable-5 Reviewed-by: Paul Dale Reviewed-by: Nikola Pajkovsky MergeDate: Fri Jul 10 15:47:15 2026 (Merged from https://github.com/openssl/openssl/pull/31885) --- providers/implementations/rands/test_rng.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/providers/implementations/rands/test_rng.c b/providers/implementations/rands/test_rng.c index 78fef9549e..c073b3e5bf 100644 --- a/providers/implementations/rands/test_rng.c +++ b/providers/implementations/rands/test_rng.c @@ -170,8 +170,9 @@ static size_t test_rng_nonce(void *vtest, unsigned char *out, return 0; if (t->generate) { - for (i = 0; i < min_noncelen; i++) - out[i] = gen_byte(t); + if (out != NULL) + for (i = 0; i < min_noncelen; i++) + out[i] = gen_byte(t); return min_noncelen; } From 171e4e7eb64f243f78a1040f725ed80a31df7702 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 7 Jul 2026 16:37:49 +0200 Subject: [PATCH 226/349] rand: add mfail tests for generation and seeding Add memory-failure injection coverage to rand_test for the full RAND_bytes_ex/RAND_priv_bytes_ex stack on a fresh library context, the SEED-SRC entropy acquisition and the CTR-DRBG operations with a TEST-RAND parent. The cipher fetches used by the DRBG setup are warmed up outside the injection window so that the injection targets the RAND machinery itself. Assisted-by: Claude:claude-fable-5 Reviewed-by: Paul Dale Reviewed-by: Nikola Pajkovsky MergeDate: Fri Jul 10 15:47:16 2026 (Merged from https://github.com/openssl/openssl/pull/31885) --- test/rand_test.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/test/rand_test.c b/test/rand_test.c index 0fbd89542f..e5c889a9e9 100644 --- a/test/rand_test.c +++ b/test/rand_test.c @@ -275,6 +275,142 @@ err: return res; } +/* Warm up the DRBG cipher fetch caches outside the mfail injection window */ +static int rand_drbg_fetch_warmup(EVP_RAND *drbg_alg) +{ + EVP_RAND_CTX *warm; + OSSL_PARAM params[3]; + int ret; + + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_DRBG_PARAM_CIPHER, + (char *)"AES-256-CTR", 0); + params[1] = OSSL_PARAM_construct_utf8_string(OSSL_PROV_PARAM_CORE_PROV_NAME, + (char *)"default", 0); + params[2] = OSSL_PARAM_construct_end(); + + if (!TEST_ptr(warm = EVP_RAND_CTX_new(drbg_alg, NULL))) + return 0; + ret = TEST_true(EVP_RAND_CTX_set_params(warm, params)); + EVP_RAND_CTX_free(warm); + return ret; +} + +/* + * Memory-failure coverage for the whole random generation stack on a fresh + * library context: the seed source and DRBG chain creation and seeding. + */ +static int test_rand_bytes_mfail(int idx) +{ + OSSL_LIB_CTX *ctx = NULL; + EVP_RAND *drbg = NULL, *seed = NULL; + unsigned char buf[16]; + int rc = -1; + + if (!TEST_ptr(ctx = OSSL_LIB_CTX_new()) + || !TEST_ptr(drbg = EVP_RAND_fetch(ctx, "CTR-DRBG", NULL)) + || !rand_drbg_fetch_warmup(drbg)) + goto end; + /* The default seed source may be unavailable in some configurations */ + ERR_set_mark(); + seed = EVP_RAND_fetch(ctx, "SEED-SRC", NULL); + ERR_pop_to_mark(); + + MFAIL_start(); + rc = (idx == 0 ? RAND_bytes_ex(ctx, buf, sizeof(buf), 0) + : RAND_priv_bytes_ex(ctx, buf, sizeof(buf), 0)) + > 0; + MFAIL_end(); + +end: + EVP_RAND_free(seed); + EVP_RAND_free(drbg); + OSSL_LIB_CTX_free(ctx); + return rc; +} + +/* Memory-failure coverage for the seed source entropy acquisition. */ +static int test_rand_seed_src_mfail(void) +{ + OSSL_LIB_CTX *ctx = NULL; + EVP_RAND *rand = NULL; + EVP_RAND_CTX *seed = NULL; + unsigned char buf[64]; + int rc = -1; + + if (!TEST_ptr(ctx = OSSL_LIB_CTX_new()) + || !TEST_ptr(rand = EVP_RAND_fetch(ctx, "SEED-SRC", NULL))) + goto end; + + MFAIL_start(); + rc = (seed = EVP_RAND_CTX_new(rand, NULL)) != NULL + && EVP_RAND_instantiate(seed, 0, 0, NULL, 0, NULL) + && EVP_RAND_generate(seed, buf, sizeof(buf), 0, 0, NULL, 0); + MFAIL_end(); + +end: + EVP_RAND_CTX_free(seed); + EVP_RAND_free(rand); + OSSL_LIB_CTX_free(ctx); + return rc; +} + +/* Memory-failure coverage for the DRBG operations with a TEST-RAND parent */ +static int test_rand_drbg_mfail(void) +{ + OSSL_LIB_CTX *ctx = NULL; + EVP_RAND *parent_alg = NULL, *drbg_alg = NULL; + EVP_RAND_CTX *parent = NULL, *drbg = NULL; + unsigned int strength = 256, generate = 1; + unsigned char entropy[128]; + unsigned char buf[32]; + OSSL_PARAM parent_params[4], drbg_params[3]; + size_t i; + int rc = -1; + + for (i = 0; i < sizeof(entropy); i++) + entropy[i] = 0xff & i; + parent_params[0] = OSSL_PARAM_construct_uint(OSSL_RAND_PARAM_STRENGTH, + &strength); + parent_params[1] = OSSL_PARAM_construct_uint(OSSL_RAND_PARAM_GENERATE, + &generate); + parent_params[2] = OSSL_PARAM_construct_octet_string( + OSSL_RAND_PARAM_TEST_ENTROPY, entropy, sizeof(entropy)); + parent_params[3] = OSSL_PARAM_construct_end(); + + drbg_params[0] = OSSL_PARAM_construct_utf8_string(OSSL_DRBG_PARAM_CIPHER, + (char *)"AES-256-CTR", 0); + drbg_params[1] = OSSL_PARAM_construct_utf8_string( + OSSL_PROV_PARAM_CORE_PROV_NAME, (char *)"default", 0); + drbg_params[2] = OSSL_PARAM_construct_end(); + + if (!TEST_ptr(ctx = OSSL_LIB_CTX_new()) + || !TEST_ptr(parent_alg = EVP_RAND_fetch(ctx, "TEST-RAND", NULL)) + || !TEST_ptr(drbg_alg = EVP_RAND_fetch(ctx, "CTR-DRBG", NULL)) + || !rand_drbg_fetch_warmup(drbg_alg) + || !TEST_ptr(parent = EVP_RAND_CTX_new(parent_alg, NULL)) + || !TEST_true(EVP_RAND_instantiate(parent, 0, 0, NULL, 0, + parent_params))) + goto end; + + MFAIL_start(); + rc = (drbg = EVP_RAND_CTX_new(drbg_alg, parent)) != NULL + && EVP_RAND_instantiate(drbg, 0, 0, (unsigned char *)"abc", 3, + drbg_params) + && EVP_RAND_generate(drbg, buf, sizeof(buf), 0, 0, NULL, 0) + && EVP_RAND_reseed(drbg, 0, NULL, 0, (unsigned char *)"xyz", 3) + && EVP_RAND_generate(drbg, buf, sizeof(buf), 0, 0, + (unsigned char *)"adin", 4); + MFAIL_end(); + +end: + EVP_RAND_CTX_free(drbg); + EVP_RAND_CTX_free(parent); + EVP_RAND_free(parent_alg); + EVP_RAND_free(drbg_alg); + OSSL_LIB_CTX_free(ctx); + return rc; +} + int setup_tests(void) { if (!test_skip_common_options()) { @@ -301,5 +437,9 @@ int setup_tests(void) if (!OSSL_PROVIDER_available(NULL, "fips") || fips_provider_version_ge(NULL, 3, 5, 1)) ADD_TEST(test_rand_get0_primary); + + ADD_MFAIL_ALL_TESTS(test_rand_bytes_mfail, 2); + ADD_MFAIL_TEST(test_rand_seed_src_mfail); + ADD_MFAIL_TEST(test_rand_drbg_mfail); return 1; } From b1393a04464eaf8b769bb6dc606d36a7734e73aa Mon Sep 17 00:00:00 2001 From: Naveed Khan Date: Sat, 4 Jul 2026 22:39:47 +0530 Subject: [PATCH 227/349] pkcs12: free PKCS7 elements on error in PKCS12_unpack_authsafes Fixes: b536880c4572 "Add library context and property query support into the PKCS12 API" Reviewed-by: Shane Lontis Reviewed-by: Eugene Syromiatnikov MergeDate: Fri Jul 10 15:48:37 2026 (Merged from https://github.com/openssl/openssl/pull/31862) --- crypto/pkcs12/p12_add.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/pkcs12/p12_add.c b/crypto/pkcs12/p12_add.c index 8ea41676d8..1217317783 100644 --- a/crypto/pkcs12/p12_add.c +++ b/crypto/pkcs12/p12_add.c @@ -219,6 +219,6 @@ STACK_OF(PKCS7) *PKCS12_unpack_authsafes(const PKCS12 *p12) } return p7s; err: - sk_PKCS7_free(p7s); + sk_PKCS7_pop_free(p7s, PKCS7_free); return NULL; } From fce540139a7cf4de64d9ce8e17b9180778e12f4e Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Wed, 1 Jul 2026 23:43:18 +0900 Subject: [PATCH 228/349] x509: avoid NULL memcmp argument in nc_dn() An empty directoryName constraint has canon_enc == NULL and canon_enclen == 0. nc_dn() must not pass that pointer to memcmp(), even with a zero length. Return X509_V_OK before comparing an empty base Name. This preserves current match semantics and avoids UBSan-visible undefined behaviour. Fixes #31687 Fixes #31688 Reviewed-by: Paul Dale Reviewed-by: Daniel Kubec MergeDate: Fri Jul 10 15:51:01 2026 (Merged from https://github.com/openssl/openssl/pull/31814) --- crypto/x509/v3_ncons.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crypto/x509/v3_ncons.c b/crypto/x509/v3_ncons.c index 8217bedc7b..5f2710e9f5 100644 --- a/crypto/x509/v3_ncons.c +++ b/crypto/x509/v3_ncons.c @@ -615,6 +615,12 @@ static int nc_dn(const X509_NAME *nm, const X509_NAME *base) return X509_V_ERR_OUT_OF_MEM; if (base->canon_enclen > nm->canon_enclen) return X509_V_ERR_PERMITTED_VIOLATION; + /* + * An empty base Name has no canonical encoding (canon_enc == NULL) and is + * a prefix of every Name, so it matches unconditionally. + */ + if (base->canon_enclen == 0) + return X509_V_OK; if (memcmp(base->canon_enc, nm->canon_enc, base->canon_enclen)) return X509_V_ERR_PERMITTED_VIOLATION; return X509_V_OK; From 7f4ac8022492a54aedd0100af4e624600d60c590 Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Wed, 1 Jul 2026 23:43:18 +0900 Subject: [PATCH 229/349] test: cover empty directoryName name constraints Add NAME_CONSTRAINTS_check() coverage for empty directoryName subtrees in both excluded and permitted constraints. The tests assert the existing results in ordinary builds and catch the NULL memcmp() argument when run under UBSan. Reviewed-by: Paul Dale Reviewed-by: Daniel Kubec MergeDate: Fri Jul 10 15:51:03 2026 (Merged from https://github.com/openssl/openssl/pull/31814) --- test/x509_test.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/test/x509_test.c b/test/x509_test.c index e9529148d4..a5beb7ca2f 100644 --- a/test/x509_test.c +++ b/test/x509_test.c @@ -550,6 +550,101 @@ err: return ret; } +/* + * nameConstraints extnValue contents with one empty directoryName subtree. + * Empty X509_NAME has canon_enc == NULL / canon_enclen == 0. + * + * SEQUENCE { [0|1] { SEQUENCE { [4] { SEQUENCE {} } } } } + */ +static const unsigned char nc_excluded_empty_dirname[] = { + 0x30, 0x08, 0xa1, 0x06, 0x30, 0x04, 0xa4, 0x02, 0x30, 0x00 +}; +static const unsigned char nc_permitted_empty_dirname[] = { + 0x30, 0x08, 0xa0, 0x06, 0x30, 0x04, 0xa4, 0x02, 0x30, 0x00 +}; + +/* Decode a raw nameConstraints extnValue into a NAME_CONSTRAINTS object. */ +static NAME_CONSTRAINTS *nc_empty_dirname_from_der(const unsigned char *der, + unsigned int der_len) +{ + NAME_CONSTRAINTS *nc = NULL; + ASN1_OCTET_STRING *os = NULL; + X509_EXTENSION *ext = NULL; + + os = ASN1_OCTET_STRING_new(); + if (!TEST_ptr(os) + || !TEST_true(ASN1_OCTET_STRING_set(os, der, der_len))) + goto end; + ext = X509_EXTENSION_create_by_NID(NULL, NID_name_constraints, + 1 /* critical */, os); + if (!TEST_ptr(ext)) + goto end; + nc = X509V3_EXT_d2i(ext); + +end: + X509_EXTENSION_free(ext); + ASN1_OCTET_STRING_free(os); + return nc; +} + +/* Build a minimal certificate with a non-empty subject DN. */ +static X509 *nc_empty_dirname_subject(const char *cn) +{ + X509 *x = NULL; + X509_NAME *nm = NULL; + + if (!TEST_ptr(x = X509_new())) + goto err; + nm = X509_NAME_new(); + if (!TEST_ptr(nm) + || !TEST_true(X509_NAME_add_entry_by_txt(nm, "CN", MBSTRING_ASC, + (const unsigned char *)cn, -1, -1, 0)) + || !TEST_true(X509_set_subject_name(x, nm))) + goto err; + X509_NAME_free(nm); + return x; + +err: + X509_NAME_free(nm); + X509_free(x); + return NULL; +} + +/* Check an empty directoryName constraint against a non-empty subject DN. */ +static int nc_check_empty_dirname(const unsigned char *der, unsigned int der_len, + int expected) +{ + int ok = 0; + NAME_CONSTRAINTS *nc = NULL; + X509 *x = NULL; + + if (!TEST_ptr(nc = nc_empty_dirname_from_der(der, der_len)) + || !TEST_ptr(x = nc_empty_dirname_subject("leaf.example")) + || !TEST_int_eq(NAME_CONSTRAINTS_check(x, nc), expected)) + goto end; + + ok = 1; + +end: + X509_free(x); + NAME_CONSTRAINTS_free(nc); + return ok; +} + +/* Empty excluded directoryName matches the subject DN: excluded violation. */ +static int test_nc_empty_dirname_excluded(void) +{ + return nc_check_empty_dirname(nc_excluded_empty_dirname, + sizeof(nc_excluded_empty_dirname), X509_V_ERR_EXCLUDED_VIOLATION); +} + +/* Empty permitted directoryName matches the subject DN: permitted. */ +static int test_nc_empty_dirname_permitted(void) +{ + return nc_check_empty_dirname(nc_permitted_empty_dirname, + sizeof(nc_permitted_empty_dirname), X509_V_OK); +} + OPT_TEST_DECLARE_USAGE("\n") int setup_tests(void) @@ -591,6 +686,8 @@ int setup_tests(void) ADD_TEST(test_drop_empty_csr_keyids); ADD_TEST(test_rsaesoaep_spki); ADD_TEST(test_x509_verify_with_new); + ADD_TEST(test_nc_empty_dirname_excluded); + ADD_TEST(test_nc_empty_dirname_permitted); return 1; } From 10b5f9f8978cb4721b195bda93d0acbcb525e053 Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Thu, 18 Jun 2026 14:58:39 -0600 Subject: [PATCH 230/349] Drop Windows-on-Itanium (VC-WIN64I) support. Similar to Windows CE, Windows Itanium has not had a toolchain to work on it since VS 2010, and Windows server 2008 was the last thing that ran on it. This does *not* change the other (linux, HPUX) targets which have modern toolchain support, and likely even still run these days. While never letting go seems noble when you won't share the door you're floating on, the time has come. Reviewed-by: Milan Broz Reviewed-by: Tomas Mraz Reviewed-by: Neil Horman MergeDate: Fri Jul 10 17:39:39 2026 (Merged from https://github.com/openssl/openssl/pull/31913) --- CHANGES.md | 6 ++++ Configurations/10-main.conf | 13 +------- INSTALL.md | 8 ++--- crypto/build.info | 2 -- ms/uplink-ia64.pl | 60 ------------------------------------- util/perl/OpenSSL/config.pm | 5 ++-- 6 files changed, 12 insertions(+), 82 deletions(-) delete mode 100755 ms/uplink-ia64.pl diff --git a/CHANGES.md b/CHANGES.md index 70ed350c79..6d4af0b857 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -102,6 +102,12 @@ OpenSSL Releases *Jakub Zelenka* + * Windows-on-Itanium (VC-WIN64I) support was dropped - the Itanium + architecture has been discontinued and the platform is no longer + supported or tested. + + *Bob Beck* + * Windows CE support was dropped - Windows CE has been unsupported since 2018 and does not have a modern C99 toolchain. diff --git a/Configurations/10-main.conf b/Configurations/10-main.conf index 390aac5011..9c7261c3f2 100644 --- a/Configurations/10-main.conf +++ b/Configurations/10-main.conf @@ -1434,7 +1434,7 @@ my %targets = ( #### Visual C targets # -# Win64 targets, WIN64I denotes IA-64/Itanium and WIN64A - AMD64 +# Win64 target, WIN64A denotes AMD64 # # Note about /wd4090, disable warning C4090. This warning returns false # positives in some situations. Disabling it altogether masks both @@ -1542,17 +1542,6 @@ my %targets = ( }), bn_ops => add("SIXTY_FOUR_BIT"), }, - "VC-WIN64I" => { - inherit_from => [ "VC-WIN64-common" ], - AS => "ias", - ASFLAGS => "-d debug", - asoutflag => "-o ", - sys_id => "WIN64I", - uplink_arch => 'ia64', - asm_arch => 'ia64', - perlasm_scheme => "ias", - multilib => "-ia64", - }, "VC-WIN64A" => { inherit_from => [ "VC-WIN64-common" ], AS => sub { vc_win64a_info()->{AS} }, diff --git a/INSTALL.md b/INSTALL.md index 20535f2c27..51f74e70dd 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -173,8 +173,7 @@ of the Configure targets in the first command. Most likely you will be using the `VC-WIN64A`/`VC-WIN64A-HYBRIDCRT` target for 64bit Windows binaries (AMD64) or `VC-WIN32`/`VC-WIN32-HYBRIDCRT` for 32bit -Windows binaries (X86). `VC-WIN64I` (Intel IA64, Itanium) is also available -but rather uncommon nowadays. +Windows binaries (X86). Installing OpenSSL ------------------ @@ -1953,9 +1952,8 @@ on Cygwin, shared libraries are named `cygcrypto-1.1.dll` and `cygssl-1.1.dll` with import libraries `libcrypto.dll.a` and `libssl.dll.a`. On Windows build with MSVC or using MingW, shared libraries are named -`libcrypto-1_1.dll` and `libssl-1_1.dll` for 32-bit Windows, -`libcrypto-1_1-x64.dll` and `libssl-1_1-x64.dll` for 64-bit x86_64 Windows, -and `libcrypto-1_1-ia64.dll` and `libssl-1_1-ia64.dll` for IA64 Windows. +`libcrypto-1_1.dll` and `libssl-1_1.dll` for 32-bit Windows, and +`libcrypto-1_1-x64.dll` and `libssl-1_1-x64.dll` for 64-bit x86_64 Windows. With MSVC, the import libraries are named `libcrypto.lib` and `libssl.lib`, while with MingW, they are named `libcrypto.dll.a` and `libssl.dll.a`. diff --git a/crypto/build.info b/crypto/build.info index 8e4a885dde..4e9068407c 100644 --- a/crypto/build.info +++ b/crypto/build.info @@ -16,7 +16,6 @@ IF[{- !$disabled{uplink} -}] $UPLINKSRC_common=../ms/uplink.c $UPLINKSRC_x86=$UPLINKSRC_common uplink-x86.S $UPLINKSRC_x86_64=$UPLINKSRC_common uplink-x86_64.s - $UPLINKSRC_ia64=$UPLINKSRC_common uplink-ia64.s IF[$UPLINKSRC_{- $target{uplink_arch} -}] $UPLINKSRC=$UPLINKSRC_{- $target{uplink_arch} -} @@ -118,7 +117,6 @@ GENERATE[buildinf.h]=../util/mkbuildinf.pl "$(CC) $(LIB_CFLAGS) $(CPPFLAGS_Q)" " GENERATE[uplink-x86.S]=../ms/uplink-x86.pl GENERATE[uplink-x86_64.s]=../ms/uplink-x86_64.pl -GENERATE[uplink-ia64.s]=../ms/uplink-ia64.pl GENERATE[x86cpuid.S]=x86cpuid.pl DEPEND[x86cpuid.s]=perlasm/x86asm.pl diff --git a/ms/uplink-ia64.pl b/ms/uplink-ia64.pl deleted file mode 100755 index 757e77d29f..0000000000 --- a/ms/uplink-ia64.pl +++ /dev/null @@ -1,60 +0,0 @@ -#! /usr/bin/env perl -# Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved. -# -# Licensed under the Apache License 2.0 (the "License"). You may not use -# this file except in compliance with the License. You can obtain a copy -# in the file LICENSE in the source distribution or at -# https://www.openssl.org/source/license.html - -$output = pop and open STDOUT,">$output"; - -$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1; -push(@INC,"${dir}."); - -require "uplink-common.pl"; - -local $V=8; # max number of args uplink functions may accept... -my $loc0 = "r".(32+$V); -print <<___; -.text -.global OPENSSL_Uplink# -.type OPENSSL_Uplink#,\@function - -___ -for ($i=1;$i<=$N;$i++) { -print <<___; -.proc lazy$i# -lazy$i: - .prologue -{ .mii; .save ar.pfs,$loc0 - alloc loc0=ar.pfs,$V,3,2,0 - .save b0,loc1 - mov loc1=b0 - addl loc2=\@ltoff(OPENSSL_UplinkTable#),gp };; - .body -{ .mmi; ld8 out0=[loc2] - mov out1=$i };; -{ .mib; add loc2=8*$i,out0 - br.call.sptk.many b0=OPENSSL_Uplink# };; -{ .mmi; ld8 r31=[loc2];; - ld8 r30=[r31],8 };; -{ .mii; ld8 gp=[r31] - mov b6=r30 - mov b0=loc1 };; -{ .mib; mov ar.pfs=loc0 - br.many b6 };; -.endp lazy$i# - -___ -} -print <<___; -.data -.global OPENSSL_UplinkTable# -OPENSSL_UplinkTable: data8 $N // amount of following entries -___ -for ($i=1;$i<=$N;$i++) { print " data8 \@fptr(lazy$i#)\n"; } -print <<___; -.size OPENSSL_UplinkTable,.-OPENSSL_UplinkTable# -___ - -close STDOUT; diff --git a/util/perl/OpenSSL/config.pm b/util/perl/OpenSSL/config.pm index f781068bf5..e9dc9d3243 100755 --- a/util/perl/OpenSSL/config.pm +++ b/util/perl/OpenSSL/config.pm @@ -411,7 +411,7 @@ sub determine_compiler_settings { $CCVER = 0; my $v = `cl 2>&1`; - if ( $v =~ /Microsoft .* Version ([0-9\.]+) for (x86|x64|ARM|ia64)/ ) { + if ( $v =~ /Microsoft .* Version ([0-9\.]+) for (x86|x64|ARM)/ ) { $CCVER = $1; $CL_ARCH = $2; } @@ -898,7 +898,7 @@ EOF ], # Windows values found by looking at Perl 5's win32/win32.c - [ '(amd64|ia64|x86|ARM)-.*?-Windows NT', + [ '(amd64|x86|ARM)-.*?-Windows NT', sub { # If we determined the arch by asking cl, take that value, # otherwise the SYSTEM we got from from POSIX::uname(). @@ -907,7 +907,6 @@ EOF if ($arch) { $config = { 'amd64' => { target => 'VC-WIN64A' }, - 'ia64' => { target => 'VC-WIN64I' }, 'x86' => { target => 'VC-WIN32' }, 'x64' => { target => 'VC-WIN64A' }, 'ARM' => { target => 'VC-WIN64-ARM' }, From 914ba181cec9ca2c41a5542c94018de0fea9e059 Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Wed, 8 Jul 2026 11:40:31 -0600 Subject: [PATCH 231/349] Remove leftover _WIN32_WCE reference in ms/uplink.c Reviewed-by: Milan Broz Reviewed-by: Tomas Mraz Reviewed-by: Neil Horman MergeDate: Fri Jul 10 17:39:41 2026 (Merged from https://github.com/openssl/openssl/pull/31913) --- ms/uplink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ms/uplink.c b/ms/uplink.c index 56f525b7e4..c2d1bef80a 100644 --- a/ms/uplink.c +++ b/ms/uplink.c @@ -7,7 +7,7 @@ * https://www.openssl.org/source/license.html */ -#if (defined(_WIN64) || defined(_WIN32_WCE)) && !defined(UNICODE) +#if defined(_WIN64) && !defined(UNICODE) #define UNICODE #endif #if defined(UNICODE) && !defined(_UNICODE) From d7e77b66cabb770932091ed5986221e0d1e57144 Mon Sep 17 00:00:00 2001 From: Marc Gutman Date: Thu, 9 Jul 2026 15:23:28 -0500 Subject: [PATCH 232/349] Don't raise NOT_ENOUGH_DATA on a clean EOF at an object boundary asn1_d2i_read_bio() reads one ASN.1 object at a time from a BIO. Callers commonly loop, decoding concatenated DER values until the call fails, and rely on a failure with no queued error to recognise a clean end of input. CPython's ssl module does this in _add_ca_certs() when loading the Windows certificate store via SSLContext.load_verify_locations(cadata=...); it re-raises any leftover ASN.1 error other than ASN1_R_HEADER_TOO_LONG as fatal. Commit 9eb6922c59 ("asn1: raise NOT_ENOUGH_DATA on header EOF") changed the BIO_read() check from "i < 0" to "i <= 0", so a clean EOF (BIO_read() returning 0, as an exhausted BIO_new_mem_buf does) on an object boundary now raises ASN1_R_NOT_ENOUGH_DATA instead of failing with an empty error queue. The rewrite in commit 35852da1d9 carried this behaviour forward. As a result Python 3 on Windows fails to initialise an SSLContext with: ssl.SSLError: [ASN1: NOT_ENOUGH_DATA] not enough data Raise ASN1_R_NOT_ENOUGH_DATA only on an actual read error, on an EOF in the middle of an object (some bytes already buffered), or on an EOF while still inside an indefinite-length value awaiting its end-of-contents octets - all of which are genuine truncation. A clean EOF at a top-level object boundary again fails without queuing an error, restoring the long-standing behaviour that looping callers depend on. Add regression tests covering the clean-EOF, truncated, indefinite-length truncation and partial-header cases, and document the read behaviour in ASN1_item_d2i_bio(3). Fixes #31807 Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Dmitry Belyavskiy Reviewed-by: Igor Ustinov Reviewed-by: Tomas Mraz MergeDate: Mon Jul 13 08:05:03 2026 (Merged from https://github.com/openssl/openssl/pull/31818) --- crypto/asn1/a_d2i_fp.c | 17 +++- doc/man3/ASN1_item_d2i_bio.pod | 18 +++- test/asn1_decode_test.c | 167 ++++++++++++++++++++++++++++++++- 3 files changed, 198 insertions(+), 4 deletions(-) diff --git a/crypto/asn1/a_d2i_fp.c b/crypto/asn1/a_d2i_fp.c index 8f9e267689..41491b92a1 100644 --- a/crypto/asn1/a_d2i_fp.c +++ b/crypto/asn1/a_d2i_fp.c @@ -1,5 +1,5 @@ /* - * Copyright 1995-2025 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2026 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -141,7 +141,20 @@ int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb) i = BIO_read(in, &(b->data[len]), (int)want); if (i <= 0) { - ERR_raise(ERR_LIB_ASN1, ASN1_R_NOT_ENOUGH_DATA); + /* + * A read error (i < 0), an EOF in the middle of an object + * (diff != 0, some bytes already buffered), or an EOF while + * still inside an indefinite-length constructed value awaiting + * its end-of-contents octets (eos != 0) all mean the input is + * truncated. Only a clean EOF at a top-level object boundary + * (i == 0, diff == 0, eos == 0) is the normal end of input: + * fail without queuing an error so that callers looping over + * concatenated DER values (e.g. the libcrypto d2i_*_bio() + * consumers in CPython's ssl module) terminate cleanly instead + * of seeing a spurious ASN1_R_NOT_ENOUGH_DATA. + */ + if (i < 0 || diff != 0 || eos != 0) + ERR_raise(ERR_LIB_ASN1, ASN1_R_NOT_ENOUGH_DATA); goto err; } diff --git a/doc/man3/ASN1_item_d2i_bio.pod b/doc/man3/ASN1_item_d2i_bio.pod index f8e4678367..9b3f389a23 100644 --- a/doc/man3/ASN1_item_d2i_bio.pod +++ b/doc/man3/ASN1_item_d2i_bio.pod @@ -59,6 +59,16 @@ B provided in the I parameter and the property query string in I. See L for more information about algorithm fetching. +When reading from I, decoding consumes one complete DER-encoded structure +and leaves any following bytes in the BIO, so concatenated structures can be +read with successive calls. Reaching the end of the input cleanly, at a +structure boundary, is not treated as an error: the function returns NULL +without adding to the error queue. If the end of the input is reached in the +middle of a structure, or an indefinite-length value is missing its +end-of-contents octets (that is, the input is truncated), an error is queued +with reason code B. The same applies to +ASN1_item_d2i_fp_ex(). + ASN1_item_d2i_bio() is the same as ASN1_item_d2i_bio_ex() except that the default B is used (i.e. NULL) and with a NULL property query string. @@ -92,6 +102,12 @@ that the I and I can be used when doing algorithm fetching. ASN1_item_d2i_bio(), ASN1_item_unpack_ex() and ASN1_item_unpack() return a pointer to an B or NULL on error. +The ASN1_item_d2i_bio() and ASN1_item_d2i_fp() functions, including their +B<_ex> variants, also return NULL at a clean end of input. In that case the +error queue is left unchanged, so a caller reading concatenated structures in +a loop can distinguish a clean end of input from a decoding error by +inspecting the error queue, for example with L. + ASN1_item_i2d_mem_bio() returns a pointer to a memory BIO or NULL on error. ASN1_item_pack() returns a pointer to an B or NULL on error. @@ -105,7 +121,7 @@ The function ASN1_item_unpack_ex() was added in OpenSSL 3.2. =head1 COPYRIGHT -Copyright 2021-2023 The OpenSSL Project Authors. All Rights Reserved. +Copyright 2021-2026 The OpenSSL Project Authors. All Rights Reserved. Licensed under the Apache License 2.0 (the "License"). You may not use this file except in compliance with the License. You can obtain a copy diff --git a/test/asn1_decode_test.c b/test/asn1_decode_test.c index d7377c8ef7..8a9629c21d 100644 --- a/test/asn1_decode_test.c +++ b/test/asn1_decode_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2017-2026 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -14,7 +14,11 @@ #include #include #include +#include +#include +#include #include "internal/numbers.h" +#include "internal/asn1.h" #include "testutil.h" #ifdef __GNUC__ @@ -266,6 +270,163 @@ err: return ret; } +/* + * A minimal, complete DER object: SEQUENCE { INTEGER 0 }. + * asn1_d2i_read_bio() should consume exactly these bytes. + */ +static const unsigned char one_obj[] = { + 0x30, 0x03, /* SEQUENCE, length 3 */ + 0x02, 0x01, 0x00 /* INTEGER 0 */ +}; + +/* + * Reading concatenated DER objects from a BIO must stop cleanly at EOF: + * once the input is exhausted on an object boundary, asn1_d2i_read_bio() + * returns < 0 and must NOT leave an error on the queue. Callers that loop + * over concatenated values (e.g. CPython's ssl module loading the Windows + * certificate store via d2i_X509_bio()) rely on this to detect end-of-input; + * a spurious ASN1_R_NOT_ENOUGH_DATA there is reported as a fatal error. + */ +static int test_d2i_read_bio_clean_eof(void) +{ + unsigned char two_objs[sizeof(one_obj) * 2]; + BIO *bio = NULL; + BUF_MEM *buf = NULL; + int ret = 0; + + memcpy(two_objs, one_obj, sizeof(one_obj)); + memcpy(two_objs + sizeof(one_obj), one_obj, sizeof(one_obj)); + + if (!TEST_ptr(bio = BIO_new_mem_buf(two_objs, sizeof(two_objs)))) + goto err; + ERR_clear_error(); + + /* Both complete objects are read, one per call. */ + if (!TEST_int_eq(asn1_d2i_read_bio(bio, &buf), (int)sizeof(one_obj))) + goto err; + BUF_MEM_free(buf); + buf = NULL; + if (!TEST_int_eq(asn1_d2i_read_bio(bio, &buf), (int)sizeof(one_obj))) + goto err; + BUF_MEM_free(buf); + buf = NULL; + + /* Clean EOF: failure return, but no error must be queued. */ + if (!TEST_int_lt(asn1_d2i_read_bio(bio, &buf), 0)) + goto err; + if (!TEST_ulong_eq(ERR_peek_error(), 0)) + goto err; + + ret = 1; +err: + BUF_MEM_free(buf); + BIO_free(bio); + return ret; +} + +/* + * In contrast, hitting EOF in the middle of an object is genuine truncation + * and must still be reported as ASN1_R_NOT_ENOUGH_DATA. + */ +static int test_d2i_read_bio_truncated(void) +{ + static const unsigned char truncated[] = { + 0x30, 0x05, /* SEQUENCE claims 5 content bytes ... */ + 0x02, 0x01 /* ... but only 2 are present */ + }; + BIO *bio = NULL; + BUF_MEM *buf = NULL; + unsigned long e; + int ret = 0; + + if (!TEST_ptr(bio = BIO_new_mem_buf(truncated, sizeof(truncated)))) + goto err; + ERR_clear_error(); + + if (!TEST_int_lt(asn1_d2i_read_bio(bio, &buf), 0)) + goto err; + e = ERR_peek_last_error(); + if (!TEST_int_eq(ERR_GET_LIB(e), ERR_LIB_ASN1) + || !TEST_int_eq(ERR_GET_REASON(e), ASN1_R_NOT_ENOUGH_DATA)) + goto err; + + ret = 1; +err: + BUF_MEM_free(buf); + BIO_free(bio); + return ret; +} + +/* + * An EOF reached while still inside an indefinite-length constructed value, + * before its end-of-contents octets, is truncation too (not a clean boundary), + * so it must also report ASN1_R_NOT_ENOUGH_DATA rather than an empty queue. + */ +static int test_d2i_read_bio_indefinite_truncated(void) +{ + /* SEQUENCE (indefinite) { INTEGER 0 } with the 00 00 EOC missing */ + static const unsigned char truncated_indefinite[] = { + 0x30, 0x80, /* SEQUENCE, indefinite length */ + 0x02, 0x01, 0x00 /* INTEGER 0; no end-of-contents octets follow */ + }; + BIO *bio = NULL; + BUF_MEM *buf = NULL; + unsigned long e; + int ret = 0; + + bio = BIO_new_mem_buf(truncated_indefinite, sizeof(truncated_indefinite)); + if (!TEST_ptr(bio)) + goto err; + ERR_clear_error(); + + if (!TEST_int_lt(asn1_d2i_read_bio(bio, &buf), 0)) + goto err; + e = ERR_peek_last_error(); + if (!TEST_int_eq(ERR_GET_LIB(e), ERR_LIB_ASN1) + || !TEST_int_eq(ERR_GET_REASON(e), ASN1_R_NOT_ENOUGH_DATA)) + goto err; + + ret = 1; +err: + BUF_MEM_free(buf); + BIO_free(bio); + return ret; +} + +/* + * An EOF reached part-way through an object's header, with some header bytes + * already buffered, is truncation as well. This exercises the "diff != 0" arm + * of the header-read check (distinct from the body read handled elsewhere). + */ +static int test_d2i_read_bio_partial_header(void) +{ + /* SEQUENCE with a 2-byte long-form length, but only one length byte given */ + static const unsigned char partial_header[] = { + 0x30, 0x82, 0x01 /* SEQUENCE, length declared as 2 bytes, 1 present */ + }; + BIO *bio = NULL; + BUF_MEM *buf = NULL; + unsigned long e; + int ret = 0; + + if (!TEST_ptr(bio = BIO_new_mem_buf(partial_header, sizeof(partial_header)))) + goto err; + ERR_clear_error(); + + if (!TEST_int_lt(asn1_d2i_read_bio(bio, &buf), 0)) + goto err; + e = ERR_peek_last_error(); + if (!TEST_int_eq(ERR_GET_LIB(e), ERR_LIB_ASN1) + || !TEST_int_eq(ERR_GET_REASON(e), ASN1_R_NOT_ENOUGH_DATA)) + goto err; + + ret = 1; +err: + BUF_MEM_free(buf); + BIO_free(bio); + return ret; +} + int setup_tests(void) { #ifndef OPENSSL_NO_DEPRECATED_3_0 @@ -279,5 +440,9 @@ int setup_tests(void) ADD_TEST(test_utctime); ADD_TEST(test_invalid_template); ADD_TEST(test_reuse_asn1_object); + ADD_TEST(test_d2i_read_bio_clean_eof); + ADD_TEST(test_d2i_read_bio_truncated); + ADD_TEST(test_d2i_read_bio_indefinite_truncated); + ADD_TEST(test_d2i_read_bio_partial_header); return 1; } From 47ddf0095df9f864c86d67fdf68826ac46a4dc48 Mon Sep 17 00:00:00 2001 From: Ondrej Moris Date: Thu, 9 Jul 2026 11:40:53 +0200 Subject: [PATCH 233/349] Update pkcs11-provider submodule (5dcc876) Signed-off-by: Ondrej Moris Reviewed-by: Dmitry Belyavskiy Reviewed-by: Igor Ustinov MergeDate: Mon Jul 13 08:14:30 2026 (Merged from https://github.com/openssl/openssl/pull/31907) --- pkcs11-provider | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkcs11-provider b/pkcs11-provider index 64fc325ac0..5dcc876263 160000 --- a/pkcs11-provider +++ b/pkcs11-provider @@ -1 +1 @@ -Subproject commit 64fc325ac0f91d03d76b3546df2998d3a38c525b +Subproject commit 5dcc876263c083d44944d84e6425497529f8ff54 From 4b8fece0f1bc9bcc693773f721fbb4acadc1ad43 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 29 Jun 2026 22:46:21 +0200 Subject: [PATCH 234/349] mfail: add sampled and count-only modes to test driver Sampled tests cap allocation-failure injection at a fixed number of sampled points, running exhaustively when the allocation count is below that. Non-sampled tests fall back to counting only on non-cached-fetch builds, where exhaustive injection is impractical. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Nikola Pajkovsky Reviewed-by: Tomas Mraz MergeDate: Mon Jul 13 09:43:50 2026 (Merged from https://github.com/openssl/openssl/pull/31780) --- test/README.md | 12 ++++++++++++ test/mfail/mfail.c | 32 ++++++++++++++++++++++++++++---- test/mfail/mfail.h | 6 ++++++ test/testutil.h | 27 +++++++++++++++++++++------ test/testutil/driver.c | 28 +++++++++++++++++++++++++--- 5 files changed, 92 insertions(+), 13 deletions(-) diff --git a/test/README.md b/test/README.md index 4bc558817f..1575fb69dc 100644 --- a/test/README.md +++ b/test/README.md @@ -196,6 +196,13 @@ return 0 when a failure was triggered. The `ADD_MFAIL_ALL_TESTS` and `ADD_MFAIL_ALL_NO_CHECK_TESTS` variants apply the same cycle to each index of a parameterised test, the same way `ADD_ALL_TESTS` does. +The `ADD_MFAIL_SAMPLED_TEST(fn, cnt)` variant (and its `NO_CHECK` and `ALL` +forms) caps injection at `cnt` points: it injects at every allocation when +there are at most `cnt` of them, and otherwise samples `cnt` points spread +across the run. This keeps tests with very many allocations bounded. On +`no-cached-fetch` builds, where allocation counts explode, non-sampled tests +run the counting phase only and skip injection; sampled tests still run. + An mfail test returns 1 on success or 0 on failure; under `NO_CHECK` a 0 is tolerated since a function may legitimately fail when an allocation fails. Returning -1 forces a failure that is reported even under `NO_CHECK`, for @@ -212,6 +219,11 @@ Behavior is controlled with the following environment variables: OPENSSL_TEST_MFAIL_SLOW=N Slow threshold (default 1000). + OPENSSL_TEST_MFAIL_COUNT=N Override the sampled point count, taking + precedence over the per-test value. + + OPENSSL_TEST_MFAIL_COUNT_ONLY=1 Run the counting phase only, never inject. + OPENSSL_TEST_MFAIL_POINT=N Run only failure point N (0-indexed), useful for debugging a specific failure. diff --git a/test/mfail/mfail.c b/test/mfail/mfail.c index 254a6a965f..ec391c8e85 100644 --- a/test/mfail/mfail.c +++ b/test/mfail/mfail.c @@ -38,6 +38,9 @@ static struct { int single_point; int start_point; int env_count; + int count_only; + int count_only_env; + int sample_count; int slow_threshold; int print_bt; int mode; @@ -155,11 +158,13 @@ int mfail_install(int optional) mf.single_point = env_int("OPENSSL_TEST_MFAIL_POINT", -1); mf.start_point = env_int("OPENSSL_TEST_MFAIL_START", 0); mf.env_count = env_int("OPENSSL_TEST_MFAIL_COUNT", 0); + mf.count_only_env = env_is_true("OPENSSL_TEST_MFAIL_COUNT_ONLY"); mf.slow_threshold = env_int("OPENSSL_TEST_MFAIL_SLOW", 1000); mf.print_bt = env_is_true("OPENSSL_TEST_MFAIL_BACKTRACE"); /* if optional and nothing configured, then no point installing hooks */ - if (optional && mf.env_count <= 0 && mf.single_point < 0) + if (optional && mf.env_count <= 0 && mf.single_point < 0 + && !mf.count_only_env) return 0; if (!CRYPTO_set_mem_functions(mf_malloc, mf_realloc, mf_free)) @@ -208,6 +213,11 @@ static int compute_point(int i, int total, int n, int seq, int start) } void mfail_init(int seq, int flags) +{ + mfail_init_ex(seq, flags, 0); +} + +void mfail_init_ex(int seq, int flags, int count) { mf.seq = seq; mf.iter_index = 0; @@ -221,10 +231,13 @@ void mfail_init(int seq, int flags) mf.counting = 0; mf.slow_skipped = 0; mf.no_check = flags & MFAIL_FLAG_NO_CHECK; + mf.count_only = mf.count_only_env || (flags & MFAIL_FLAG_COUNT_ONLY) != 0; + /* env count overrides the requested per-test sample count */ + mf.sample_count = mf.env_count > 0 ? mf.env_count : count; if (mf.single_point >= 0) { mf.mode = MFAIL_MODE_SINGLE; - } else if ((flags & MFAIL_FLAG_COUNT) && mf.env_count > 0) { + } else if ((flags & MFAIL_FLAG_COUNT) && mf.sample_count > 0) { mf.mode = MFAIL_MODE_SAMPLED; } else { mf.mode = MFAIL_MODE_EXHAUSTIVE; @@ -240,7 +253,13 @@ int mfail_has_next(void) switch (mf.phase) { case MFAIL_PHASE_COUNTING: mf.total = mf.alloc_count; - if (mf.skip_slow && mf.total > mf.slow_threshold) { + if (mf.count_only) { + mf.phase = MFAIL_PHASE_DONE; + break; + } + /* sampled runs are bounded, so slow-skip only applies otherwise */ + if (mf.skip_slow && mf.mode != MFAIL_MODE_SAMPLED + && mf.total > mf.slow_threshold) { mf.slow_skipped = 1; mf.phase = MFAIL_PHASE_DONE; break; @@ -256,7 +275,7 @@ int mfail_has_next(void) mf.phase = MFAIL_PHASE_DONE; } } else { /* mf.mode is MFAIL_MODE_SAMPLED */ - mf.n = mf.env_count; + mf.n = mf.sample_count; if (mf.n > mf.total) mf.n = mf.total; if (mf.n > 0 && mf.total > mf.start_point) { @@ -336,6 +355,11 @@ int mfail_was_slow_skipped(void) return mf.slow_skipped; } +int mfail_is_count_only(void) +{ + return mf.count_only; +} + int mfail_get_count(void) { return mf.alloc_count; diff --git a/test/mfail/mfail.h b/test/mfail/mfail.h index 6c16e4fe65..c93be44fca 100644 --- a/test/mfail/mfail.h +++ b/test/mfail/mfail.h @@ -13,6 +13,8 @@ /* Flags for mfail_init(). */ #define MFAIL_FLAG_COUNT (1 << 0) #define MFAIL_FLAG_NO_CHECK (1 << 1) +/* Force count-only run for this init */ +#define MFAIL_FLAG_COUNT_ONLY (1 << 2) /* Modes */ #define MFAIL_MODE_EXHAUSTIVE 0 @@ -30,6 +32,8 @@ int mfail_install(int optional); int mfail_is_installed(void); /* Initialize the mfail for test case runs */ void mfail_init(int seq, int flags); +/* As mfail_init() but caps injection at |count| sampled points */ +void mfail_init_ex(int seq, int flags, int count); /* Check for the failure loop if another fail execution should be done */ int mfail_has_next(void); /* Start the failure triggering block */ @@ -40,6 +44,8 @@ void mfail_end(void); int mfail_was_triggered(void); /* Check if the inject phase was skipped because it got over slow threshold */ int mfail_was_slow_skipped(void); +/* Check if mfail counts allocations and not inject */ +int mfail_is_count_only(void); /* If the counting was executed, get the total number of allocations */ int mfail_get_count(void); /* Get the total number of failure points */ diff --git a/test/testutil.h b/test/testutil.h index f606ff1f75..7e23f53827 100644 --- a/test/testutil.h +++ b/test/testutil.h @@ -73,17 +73,32 @@ /* Per-test flags for add_mfail_test() */ #define MFAIL_TEST_NO_CHECK (1 << 0) +#define MFAIL_TEST_SAMPLED (1 << 1) #define ADD_MFAIL_TEST(test_fn) \ - add_mfail_test(#test_fn, test_fn, 0) + add_mfail_test(#test_fn, test_fn, 0, 0) #define ADD_MFAIL_NO_CHECK_TEST(test_fn) \ - add_mfail_test(#test_fn, test_fn, MFAIL_TEST_NO_CHECK) + add_mfail_test(#test_fn, test_fn, MFAIL_TEST_NO_CHECK, 0) + +/* Caps injection at |cnt| points (exhaustive when allocations <= cnt) */ +#define ADD_MFAIL_SAMPLED_TEST(test_fn, cnt) \ + add_mfail_test(#test_fn, test_fn, MFAIL_TEST_SAMPLED, cnt) +#define ADD_MFAIL_SAMPLED_NO_CHECK_TEST(test_fn, cnt) \ + add_mfail_test(#test_fn, test_fn, \ + MFAIL_TEST_NO_CHECK | MFAIL_TEST_SAMPLED, cnt) /* Runs the exhaustive mfail cycle for each 0 <= idx < num */ #define ADD_MFAIL_ALL_TESTS(test_fn, num) \ - add_mfail_all_tests(#test_fn, test_fn, num, 0) + add_mfail_all_tests(#test_fn, test_fn, num, 0, 0) #define ADD_MFAIL_ALL_NO_CHECK_TESTS(test_fn, num) \ - add_mfail_all_tests(#test_fn, test_fn, num, MFAIL_TEST_NO_CHECK) + add_mfail_all_tests(#test_fn, test_fn, num, MFAIL_TEST_NO_CHECK, 0) + +/* Sampled variants of the above */ +#define ADD_MFAIL_SAMPLED_ALL_TESTS(test_fn, num, cnt) \ + add_mfail_all_tests(#test_fn, test_fn, num, MFAIL_TEST_SAMPLED, cnt) +#define ADD_MFAIL_SAMPLED_ALL_NO_CHECK_TESTS(test_fn, num, cnt) \ + add_mfail_all_tests(#test_fn, test_fn, num, \ + MFAIL_TEST_NO_CHECK | MFAIL_TEST_SAMPLED, cnt) /* * A variant of the same without TAP output. @@ -256,9 +271,9 @@ void add_test(const char *test_case_name, int (*test_fn)(void)); void add_all_tests(const char *test_case_name, int (*test_fn)(int idx), int num, int subtest); void add_mfail_test(const char *test_case_name, int (*test_fn)(void), - int flags); + int flags, int sampled); void add_mfail_all_tests(const char *test_case_name, int (*test_fn)(int idx), - int num, int flags); + int num, int flags, int sampled); #define MFAIL_start mfail_start #define MFAIL_end mfail_end diff --git a/test/testutil/driver.c b/test/testutil/driver.c index 54aa2a061a..bfcb65b7bc 100644 --- a/test/testutil/driver.c +++ b/test/testutil/driver.c @@ -39,6 +39,7 @@ typedef struct test_info { unsigned int subtest : 1; unsigned int mfail : 1; int mfail_flags; + int mfail_sampled; } TEST_INFO; static TEST_INFO all_tests[1024]; @@ -84,7 +85,8 @@ void add_all_tests(const char *test_case_name, int (*test_fn)(int idx), num_test_cases += num; } -void add_mfail_test(const char *test_case_name, int (*test_fn)(void), int flags) +void add_mfail_test(const char *test_case_name, int (*test_fn)(void), int flags, + int sampled) { assert(num_tests != OSSL_NELEM(all_tests)); all_tests[num_tests].test_case_name = test_case_name; @@ -92,12 +94,13 @@ void add_mfail_test(const char *test_case_name, int (*test_fn)(void), int flags) all_tests[num_tests].num = -1; all_tests[num_tests].mfail = 1; all_tests[num_tests].mfail_flags = flags; + all_tests[num_tests].mfail_sampled = sampled; ++num_tests; ++num_test_cases; } void add_mfail_all_tests(const char *test_case_name, int (*test_fn)(int idx), - int num, int flags) + int num, int flags, int sampled) { assert(num_tests != OSSL_NELEM(all_tests)); all_tests[num_tests].test_case_name = test_case_name; @@ -106,6 +109,7 @@ void add_mfail_all_tests(const char *test_case_name, int (*test_fn)(int idx), all_tests[num_tests].subtest = 1; all_tests[num_tests].mfail = 1; all_tests[num_tests].mfail_flags = flags; + all_tests[num_tests].mfail_sampled = sampled; ++num_tests; ++num_test_cases; } @@ -321,8 +325,24 @@ static int mfail_run_test(const TEST_INFO *t, int idx) int injections = 0; int allocations = 0; int no_check = (t->mfail_flags & MFAIL_TEST_NO_CHECK) != 0; + int sampled = (t->mfail_flags & MFAIL_TEST_SAMPLED) != 0; + int init_flags = no_check ? MFAIL_FLAG_NO_CHECK : 0; clock_t start = clock(); + if (sampled) + /* cap injection at mfail_sampled points (exhaustive below that) */ + init_flags |= MFAIL_FLAG_COUNT; +#ifdef OPENSSL_NO_CACHED_FETCH + else + /* + * The non-cached does too many allocations, which results in a + * significant slowdown of the tests. It does not provide much value, + * as it also requires NO_CHECK variant, so just run counting + * correctness check and skip the memory failure injection part. + */ + init_flags |= MFAIL_FLAG_COUNT_ONLY; +#endif + level += 4; test_adjust_streams_tap_level(level); test_printf_stdout("Subtest: %s[%d]\n", t->test_case_name, idx); @@ -330,7 +350,7 @@ static int mfail_run_test(const TEST_INFO *t, int idx) test_flush_stdout(); test_flush_tapout(); - mfail_init(0, no_check ? MFAIL_FLAG_NO_CHECK : 0); + mfail_init_ex(idx, init_flags, t->mfail_sampled); while (mfail_has_next()) { int phase = mfail_get_phase(); @@ -383,6 +403,8 @@ static int mfail_run_test(const TEST_INFO *t, int idx) test_verdict(TEST_SKIP_CODE, "2 - injection (mfail not installed)"); else if (mfail_env_skip_all()) test_verdict(TEST_SKIP_CODE, "2 - injection (mfail skip-all set)"); + else if (mfail_is_count_only()) + test_verdict(TEST_SKIP_CODE, "2 - injection (count only)"); else if (mfail_was_slow_skipped()) test_verdict(TEST_SKIP_CODE, "2 - injection (%d allocations exceeds slow threshold %d)", From e1386ecb2dc1c0e1ca80069c0c9b6af9f6bffab4 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 29 Jun 2026 22:46:22 +0200 Subject: [PATCH 235/349] test: drop no-cached-fetch mfail guards Reviewed-by: Nikola Pajkovsky Reviewed-by: Tomas Mraz MergeDate: Mon Jul 13 09:43:51 2026 (Merged from https://github.com/openssl/openssl/pull/31780) --- test/evp_extra_test.c | 5 ----- test/quic_srt_gen_test.c | 4 ---- test/quic_srtm_test.c | 4 ---- test/quicapitest.c | 12 ------------ test/rsa_test.c | 5 ----- test/statem_clnt_construct_test.c | 8 +------- 6 files changed, 1 insertion(+), 37 deletions(-) diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c index 7c8059350a..c767114f39 100644 --- a/test/evp_extra_test.c +++ b/test/evp_extra_test.c @@ -9050,12 +9050,7 @@ int setup_tests(void) ADD_TEST(test_EVP_SM2_verify); #endif ADD_ALL_TESTS(test_set_get_raw_keys, OSSL_NELEM(keys)); -#if defined(_MSC_VER) || defined(OPENSSL_NO_CACHED_FETCH) - ADD_MFAIL_ALL_NO_CHECK_TESTS(test_set_get_raw_keys_mfail, - OSSL_NELEM(keys)); -#else ADD_MFAIL_ALL_TESTS(test_set_get_raw_keys_mfail, OSSL_NELEM(keys)); -#endif ADD_ALL_TESTS(test_EVP_PKEY_check, OSSL_NELEM(keycheckdata)); #ifndef OPENSSL_NO_CMAC ADD_TEST(test_CMAC_keygen); diff --git a/test/quic_srt_gen_test.c b/test/quic_srt_gen_test.c index d4a8f66896..cfcee4a953 100644 --- a/test/quic_srt_gen_test.c +++ b/test/quic_srt_gen_test.c @@ -90,10 +90,6 @@ static int test_srt_gen_new_mfail(int idx) int setup_tests(void) { ADD_ALL_TESTS(test_srt_gen, OSSL_NELEM(tests)); -#ifdef OPENSSL_NO_CACHED_FETCH - ADD_MFAIL_ALL_NO_CHECK_TESTS(test_srt_gen_new_mfail, OSSL_NELEM(tests)); -#else ADD_MFAIL_ALL_TESTS(test_srt_gen_new_mfail, OSSL_NELEM(tests)); -#endif return 1; } diff --git a/test/quic_srtm_test.c b/test/quic_srtm_test.c index db29198918..6a1d6f3618 100644 --- a/test/quic_srtm_test.c +++ b/test/quic_srtm_test.c @@ -119,11 +119,7 @@ err: int setup_tests(void) { ADD_TEST(test_srtm); -#ifdef OPENSSL_NO_CACHED_FETCH - ADD_MFAIL_NO_CHECK_TEST(test_srtm_new_mfail); -#else ADD_MFAIL_TEST(test_srtm_new_mfail); -#endif ADD_MFAIL_TEST(test_srtm_ops_mfail); return 1; } diff --git a/test/quicapitest.c b/test/quicapitest.c index dd574b56fc..a8de8f9670 100644 --- a/test/quicapitest.c +++ b/test/quicapitest.c @@ -218,7 +218,6 @@ end: return ret; } -#ifndef OPENSSL_NO_CACHED_FETCH static int test_ssl_read_key_update_mfail(void) { SSL_CTX *cctx = NULL, *sctx = NULL; @@ -302,7 +301,6 @@ err: return ret; } -#endif /* * Test that sending FIN with no data to a client blocking in SSL_read_ex() will @@ -3520,7 +3518,6 @@ static int test_quic_peer_addr_v6(void) } #endif -#ifndef OPENSSL_NO_CACHED_FETCH /* * Advance the fake clock to the next QUIC timer event when both endpoints are * idle, consuming no real time. @@ -3660,7 +3657,6 @@ err: return ret; } -#endif /* Test ECH with quic */ static int test_ech(void) @@ -3913,9 +3909,7 @@ int setup_tests(void) goto err; ADD_ALL_TESTS(test_quic_write_read, 3); -#ifndef OPENSSL_NO_CACHED_FETCH ADD_MFAIL_NO_CHECK_TEST(test_ssl_read_key_update_mfail); -#endif ADD_TEST(test_fin_only_blocking); ADD_TEST(test_ciphersuites); ADD_TEST(test_cipher_find); @@ -3957,16 +3951,10 @@ int setup_tests(void) ADD_TEST(test_quic_peer_addr_v6); #endif ADD_TEST(test_quic_peer_addr_v4); -#ifndef OPENSSL_NO_CACHED_FETCH ADD_MFAIL_NO_CHECK_TEST(test_quic_handshake_multipkt_mfail); -#endif ADD_TEST(test_ech); ADD_TEST(test_quic_resize_txe); -#ifdef OPENSSL_NO_CACHED_FETCH - ADD_MFAIL_NO_CHECK_TEST(test_ssl_new_mfail); -#else ADD_MFAIL_TEST(test_ssl_new_mfail); -#endif return 1; err: diff --git a/test/rsa_test.c b/test/rsa_test.c index 2150a3cb6d..de2966b2a2 100644 --- a/test/rsa_test.c +++ b/test/rsa_test.c @@ -757,13 +757,8 @@ int setup_tests(void) { ADD_ALL_TESTS(test_rsa_pkcs1, 3); ADD_ALL_TESTS(test_rsa_oaep, 3); -#if defined(_MSC_VER) || defined(OPENSSL_NO_CACHED_FETCH) - ADD_MFAIL_ALL_NO_CHECK_TESTS(test_rsa_pkcs1_mfail, 3); - ADD_MFAIL_ALL_NO_CHECK_TESTS(test_rsa_oaep_mfail, 3); -#else ADD_MFAIL_ALL_TESTS(test_rsa_pkcs1_mfail, 3); ADD_MFAIL_ALL_TESTS(test_rsa_oaep_mfail, 3); -#endif ADD_ALL_TESTS(test_rsa_security_bit, OSSL_NELEM(rsa_security_bits_cases)); ADD_TEST(test_rsa_saos); ADD_TEST(test_EVP_rsa_legacy_key); diff --git a/test/statem_clnt_construct_test.c b/test/statem_clnt_construct_test.c index 459c8ac6d4..459c7ffe3e 100644 --- a/test/statem_clnt_construct_test.c +++ b/test/statem_clnt_construct_test.c @@ -558,7 +558,7 @@ err: } #endif -#if !defined(OSSL_NO_USABLE_TLS1_3) && !defined(OPENSSL_NO_CACHED_FETCH) +#ifndef OSSL_NO_USABLE_TLS1_3 static int mfail_construct_ch_tls13(void) { CH_CONFIG cfg = { 0, TLS1_3_VERSION, TLS1_3_VERSION, 0 }; @@ -670,14 +670,12 @@ static int test_construct_ch_ech_tls12(void) } #endif /* OPENSSL_NO_TLS1_2 */ -#ifndef OPENSSL_NO_CACHED_FETCH static int mfail_construct_ch_ech(void) { CH_CONFIG cfg = { 0, TLS1_3_VERSION, TLS1_3_VERSION, 0 }; return mfail_construct_ch_common(&cfg, prep_ech); } -#endif /* OPENSSL_NO_CACHED_FETCH */ #endif /* OSSL_NO_USABLE_ECH */ int setup_tests(void) @@ -695,7 +693,6 @@ int setup_tests(void) ADD_TEST(test_construct_ch_tls13); ADD_TEST(test_construct_ch_tls13_no_middlebox); ADD_TEST(test_construct_ch_hrr); -#ifndef OPENSSL_NO_CACHED_FETCH /* * The non-cached mfail run takes too long and does not test too much extra * so better to skip it. @@ -712,7 +709,6 @@ int setup_tests(void) #else ADD_MFAIL_TEST(mfail_construct_ch_tls13); #endif /* OPENSSL_NO_ECX */ -#endif /* OPENSSL_NO_CACHED_FETCH */ #endif /* OSSL_NO_USABLE_TLS1_3 */ #if !defined(OPENSSL_NO_DTLS) && !defined(OPENSSL_NO_DTLS1_2) @@ -726,9 +722,7 @@ int setup_tests(void) #ifndef OPENSSL_NO_TLS1_2 ADD_TEST(test_construct_ch_ech_tls12); #endif -#ifndef OPENSSL_NO_CACHED_FETCH ADD_MFAIL_TEST(mfail_construct_ch_ech); -#endif /* OPENSSL_NO_CACHED_FETCH */ #endif /* OSSL_NO_USABLE_ECH */ return 1; } From f1be99832fcb614f24c68a7888a7d0269268ad00 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Mon, 6 Jul 2026 11:29:42 -0400 Subject: [PATCH 236/349] Remove the rio_notifier run_once routine We do this odd thing in rio_notifier. when we initalize it, we call a run_once routine to call WSAStartup(), create a lock and init a refcount. The purpose of those last two items is to track the refcount so that we record how many times we init that rio notifier. when the refcount reaches zero, we tear down the windows socket api by calling WSA cleanup, destroy the lock and refcount, and then re-initzlize the run_once gate. That last step is sketchy. Even though our implementations of run_once allow doing so, we should never be re-initing those gates, as its going to be very prone to races, and they are, well, run_once, so we should only run them once. It would be nice to get rid of that behavior, which we can fortunately do. https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-wsastartup Indicates that WSAStartup is internally refcounted, so instead of just calling it once and tracking when we need to correspondingly call WSACleanup(), just call it every time we initalize an rio_notifier object, and call WSACleanup when we tear it down. The Winsock api will take care of knowing when it actually needs to be cleaned up for us. As such we can eliminate the run_once routine, the refcount and the lock entirely. Reviewed-by: Bob Beck Reviewed-by: Nikola Pajkovsky MergeDate: Mon Jul 13 14:26:58 2026 (Merged from https://github.com/openssl/openssl/pull/31777) --- ssl/rio/rio_notifier.c | 47 +++++------------------------------------- 1 file changed, 5 insertions(+), 42 deletions(-) diff --git a/ssl/rio/rio_notifier.c b/ssl/rio/rio_notifier.c index ab635aa7a0..3f5225db0e 100644 --- a/ssl/rio/rio_notifier.c +++ b/ssl/rio/rio_notifier.c @@ -10,7 +10,6 @@ #include "internal/sockets.h" #include #include -#include "internal/thread_once.h" #include "internal/rio_notifier.h" #if !defined(OPENSSL_SYS_WINDOWS) || RIO_NOTIFIER_METHOD == RIO_NOTIFIER_METHOD_SOCKETPAIR @@ -30,64 +29,30 @@ static int set_cloexec(int fd) #if defined(OPENSSL_SYS_WINDOWS) -static CRYPTO_ONCE ensure_wsa_startup_once = CRYPTO_ONCE_STATIC_INIT; -static CRYPTO_RWLOCK *wsa_lock; -static int wsa_started; -static int wsa_ref; - static void ossl_wsa_cleanup(void) { - if (wsa_started) { - wsa_started = 0; - WSACleanup(); - } - - CRYPTO_THREAD_lock_free(wsa_lock); - wsa_lock = NULL; + WSACleanup(); } -DEFINE_RUN_ONCE_STATIC(do_wsa_startup) +static int do_wsa_startup(void) { WORD versionreq = 0x0202; /* Version 2.2 */ WSADATA wsadata; - wsa_lock = CRYPTO_THREAD_lock_new(); - if (wsa_lock == NULL) + if (WSAStartup(versionreq, &wsadata) != 0) return 0; - if (WSAStartup(versionreq, &wsadata) != 0) { - CRYPTO_THREAD_lock_free(wsa_lock); - wsa_lock = NULL; - return 0; - } - wsa_started = 1; - return 1; } static ossl_inline int ensure_wsa_startup(void) { - int rv, unused; - - rv = RUN_ONCE(&ensure_wsa_startup_once, do_wsa_startup); - if (rv != 0) - CRYPTO_atomic_add(&wsa_ref, 1, &unused, wsa_lock); - - return rv; + return do_wsa_startup(); } static void wsa_done(void) { - int ref; - - if (wsa_lock != NULL) { - CRYPTO_atomic_add(&wsa_ref, -1, &ref, wsa_lock); - if (ref == 0) { - ossl_wsa_cleanup(); - ensure_wsa_startup_once = CRYPTO_ONCE_STATIC_INIT; - wsa_lock = NULL; - } - } + ossl_wsa_cleanup(); } #endif @@ -188,8 +153,6 @@ int ossl_rio_notifier_init(RIO_NOTIFIER *nfy) if (!ensure_wsa_startup()) { ERR_raise_data(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR, "Cannot start Windows sockets"); - - wsa_done(); return 0; } #endif From 58f032a04254baab114449e018eb7eaff6e4f648 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 23 Jun 2026 23:45:51 +0200 Subject: [PATCH 237/349] cms: fix AuthEnvelopedData authAttrs tags and verify them as AEAD AAD The CMS_AuthEnvelopedData ASN.1 template used the implicit tags and the X509_ALGOR type copied from CMS_AuthenticatedData. Per RFC 5083 the authAttrs and unauthAttrs fields are [1] and [2] (not [2] and [3]) and are SET OF Attribute, so use X509_ATTRIBUTE with the correct tags, matching the STACK_OF(X509_ATTRIBUTE) members already declared in the structure. With the tags fixed, authEnvelopedData carrying authAttrs now parses, so the authenticated attributes must also be fed to the content cipher as the AEAD associated data required by RFC 5083 section 2.1. Encode their DER (with the universal SET OF tag) for both encryption and decryption; without this the GCM tag fails to verify against compliant senders such as BouncyCastle. RFC 5083 also requires that plaintext is not released until its integrity has been verified. The AEAD tag is only checked once all the ciphertext has been processed, so buffer the decrypted content and forward it to the output BIO only after that check succeeds; a tampered message then leaks nothing to -out. Add an interop test using a BouncyCastle-generated AES-128-GCM message with authenticated and unauthenticated attributes, plus a tampered copy that must fail the tag check and leave -out empty. Closes #31635 Closes #26101 Closes #31629 Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Dmitry Belyavskiy Reviewed-by: Tomas Mraz MergeDate: Mon Jul 13 14:41:34 2026 (Merged from https://github.com/openssl/openssl/pull/31695) --- crypto/cms/cms_asn1.c | 4 +- crypto/cms/cms_env.c | 40 +++++++++++++++++- crypto/cms/cms_local.h | 3 ++ crypto/cms/cms_smime.c | 39 ++++++++++++++++++ test/recipes/80-test_cms.t | 41 ++++++++++++++++++- .../80-test_cms_data/authenveloped_attrs.pem | 7 ++++ .../bad_authenveloped_attrs.pem | 7 ++++ 7 files changed, 136 insertions(+), 5 deletions(-) create mode 100644 test/recipes/80-test_cms_data/authenveloped_attrs.pem create mode 100644 test/recipes/80-test_cms_data/bad_authenveloped_attrs.pem diff --git a/crypto/cms/cms_asn1.c b/crypto/cms/cms_asn1.c index 63a26de742..96b125e930 100644 --- a/crypto/cms/cms_asn1.c +++ b/crypto/cms/cms_asn1.c @@ -306,9 +306,9 @@ ASN1_NDEF_SEQUENCE(CMS_AuthEnvelopedData) = { ASN1_IMP_OPT(CMS_AuthEnvelopedData, originatorInfo, CMS_OriginatorInfo, 0), ASN1_SET_OF(CMS_AuthEnvelopedData, recipientInfos, CMS_RecipientInfo), ASN1_SIMPLE(CMS_AuthEnvelopedData, authEncryptedContentInfo, CMS_EncryptedContentInfo), - ASN1_IMP_SET_OF_OPT(CMS_AuthEnvelopedData, authAttrs, X509_ALGOR, 2), + ASN1_IMP_SET_OF_OPT(CMS_AuthEnvelopedData, authAttrs, X509_ATTRIBUTE, 1), ASN1_SIMPLE(CMS_AuthEnvelopedData, mac, ASN1_OCTET_STRING), - ASN1_IMP_SET_OF_OPT(CMS_AuthEnvelopedData, unauthAttrs, X509_ALGOR, 3) + ASN1_IMP_SET_OF_OPT(CMS_AuthEnvelopedData, unauthAttrs, X509_ATTRIBUTE, 2) } ASN1_NDEF_SEQUENCE_END(CMS_AuthEnvelopedData) ASN1_NDEF_SEQUENCE(CMS_AuthenticatedData) = { diff --git a/crypto/cms/cms_env.c b/crypto/cms/cms_env.c index e702703758..8413f497db 100644 --- a/crypto/cms/cms_env.c +++ b/crypto/cms/cms_env.c @@ -1236,6 +1236,35 @@ BIO *ossl_cms_EnvelopedData_init_bio(CMS_ContentInfo *cms) return cms_EnvelopedData_Decryption_init_bio(cms); } +/* The DER encoding of authAttrs, with the universal SET OF tag, is the AAD */ +static int cms_AuthEnvelopedData_set_aad(BIO *b, + STACK_OF(X509_ATTRIBUTE) *authAttrs) +{ + EVP_CIPHER_CTX *ctx; + unsigned char *aad = NULL; + int aadlen, outl, ok = 0; + const ASN1_ITEM *item; + + if (!BIO_get_cipher_ctx(b, &ctx)) + return 0; + item = EVP_CIPHER_CTX_is_encrypting(ctx) + ? ASN1_ITEM_rptr(CMS_Attributes_AadEncrypt) + : ASN1_ITEM_rptr(CMS_Attributes_AadDecrypt); + aadlen = ASN1_item_i2d((ASN1_VALUE *)authAttrs, &aad, item); + if (aadlen <= 0 || aad == NULL) { + ERR_raise(ERR_LIB_CMS, ERR_R_ASN1_LIB); + goto err; + } + if (EVP_CipherUpdate(ctx, NULL, &outl, aad, aadlen) <= 0) { + ERR_raise(ERR_LIB_CMS, CMS_R_CTRL_FAILURE); + goto err; + } + ok = 1; +err: + OPENSSL_free(aad); + return ok; +} + BIO *ossl_cms_AuthEnvelopedData_init_bio(CMS_ContentInfo *cms) { CMS_EncryptedContentInfo *ec; @@ -1252,9 +1281,16 @@ BIO *ossl_cms_AuthEnvelopedData_init_bio(CMS_ContentInfo *cms) ec->taglen = aenv->mac->length; } ret = ossl_cms_EncryptedContent_init_bio(ec, ossl_cms_get0_cmsctx(cms), 1); + if (ret == NULL) + return NULL; - /* If error or no cipher end of processing */ - if (ret == NULL || ec->cipher == NULL) + /* authAttrs, if present, are the AEAD associated data */ + if (aenv->authAttrs != NULL + && !cms_AuthEnvelopedData_set_aad(ret, aenv->authAttrs)) + goto err; + + /* If no cipher end of processing */ + if (ec->cipher == NULL) return ret; /* Now encrypt content key according to each RecipientInfo type */ diff --git a/crypto/cms/cms_local.h b/crypto/cms/cms_local.h index 5e0ac3907f..bfb0ca729b 100644 --- a/crypto/cms/cms_local.h +++ b/crypto/cms/cms_local.h @@ -401,6 +401,9 @@ DECLARE_ASN1_ITEM(CMS_EncryptedContentInfo) DECLARE_ASN1_ITEM(CMS_IssuerAndSerialNumber) DECLARE_ASN1_ITEM(CMS_Attributes_Sign) DECLARE_ASN1_ITEM(CMS_Attributes_Verify) +/* The authAttrs AAD encoding matches the signed-attributes one */ +#define CMS_Attributes_AadEncrypt_it CMS_Attributes_Sign_it +#define CMS_Attributes_AadDecrypt_it CMS_Attributes_Verify_it DECLARE_ASN1_ITEM(CMS_RecipientInfo) DECLARE_ASN1_ITEM(CMS_PasswordRecipientInfo) DECLARE_ASN1_ALLOC_FUNCTIONS(CMS_IssuerAndSerialNumber) diff --git a/crypto/cms/cms_smime.c b/crypto/cms/cms_smime.c index 659c033482..044cb2326f 100644 --- a/crypto/cms/cms_smime.c +++ b/crypto/cms/cms_smime.c @@ -36,6 +36,7 @@ static int cms_copy_content(BIO *out, BIO *in, unsigned int flags) unsigned char buf[4096]; int r = 0, i; BIO *tmpout; + BIO *aeadbuf = NULL; tmpout = cms_get_text_bio(out, flags); @@ -44,6 +45,33 @@ static int cms_copy_content(BIO *out, BIO *in, unsigned int flags) goto err; } + /* + * For AEAD content (AuthEnvelopedData) the integrity tag is only verified + * once all the ciphertext has been processed, by the + * BIO_get_cipher_status() call below. RFC 5083 requires that the plaintext + * is not released to the caller until that verification succeeds, so + * buffer it in memory and only forward it to the output BIO once the tag + * has been checked. When CMS_TEXT is set tmpout is already a memory BIO + * that is flushed only on success, so the extra buffering is not needed. + */ + if (tmpout == out && BIO_method_type(in) == BIO_TYPE_CIPHER) { + EVP_CIPHER_CTX *ctx = NULL; + + if (BIO_get_cipher_ctx(in, &ctx) > 0 && ctx != NULL + && (EVP_CIPHER_get_flags(EVP_CIPHER_CTX_get0_cipher(ctx)) + & EVP_CIPH_FLAG_AEAD_CIPHER) + != 0) { + aeadbuf = BIO_new(BIO_s_mem()); + if (aeadbuf == NULL) { + ERR_raise(ERR_LIB_CMS, ERR_R_BIO_LIB); + goto err; + } + /* Return 0 (EOF) rather than a retryable -1 once drained. */ + BIO_set_mem_eof_return(aeadbuf, 0); + tmpout = aeadbuf; + } + } + /* Read all content through chain to process digest, decrypt etc */ for (;;) { i = BIO_read(in, buf, sizeof(buf)); @@ -66,6 +94,17 @@ static int cms_copy_content(BIO *out, BIO *in, unsigned int flags) ERR_raise(ERR_LIB_CMS, CMS_R_SMIME_TEXT_ERROR); goto err; } + } else if (aeadbuf != NULL) { + /* Forward the AEAD BIO to out BIO as the tag has been verified. */ + for (;;) { + i = BIO_read(aeadbuf, buf, sizeof(buf)); + if (i < 0) + goto err; + if (i == 0) + break; + if (BIO_write(out, buf, i) != i) + goto err; + } } r = 1; diff --git a/test/recipes/80-test_cms.t b/test/recipes/80-test_cms.t index 078e50123d..7516cf024e 100644 --- a/test/recipes/80-test_cms.t +++ b/test/recipes/80-test_cms.t @@ -56,7 +56,7 @@ my ($no_des, $no_dh, $no_dsa, $no_ec, $no_ec2m, $no_rc2, $no_zlib) $no_rc2 = 1 if disabled("legacy"); -plan tests => 39; +plan tests => 40; ok(run(test(["pkcs7_test"])), "test pkcs7"); @@ -822,6 +822,18 @@ sub zero_compare { return (-e "$opts{output}.txt" && -z "$opts{output}.txt"); } +sub read_file_text { + my ($file) = @_; + open(my $fh, "<", $file) or return undef; + binmode $fh; + local $/; + my $data = <$fh>; + close($fh); + # Normalise line endings as -out is written in text mode on Windows. + $data =~ s/\r\n/\n/g if defined $data; + return $data; +} + subtest "CMS => PKCS#7 compatibility tests\n" => sub { plan tests => scalar @smime_pkcs7_tests; @@ -1030,6 +1042,33 @@ subtest "CMS Decrypt message encrypted with OpenSSL 1.1.1\n" => sub { } }; +subtest "CMS decrypt authEnvelopedData with authenticated attributes\n" => sub { + plan tests => 4; + + # BouncyCastle AES-128-GCM authEnvelopedData (KEK) carrying authAttrs; + # a clean decrypt confirms the authAttrs are verified as the AEAD AAD. + 1 while unlink "authattrs.txt"; + ok(run(app(["openssl", "cms", @defaultprov, "-decrypt", "-inform", "PEM", + "-secretkey", "000102030405060708090A0B0C0D0E0F", + "-secretkeyid", "C0FEE0", + "-in", catfile($datadir, "authenveloped_attrs.pem"), + "-out", "authattrs.txt" ])), + "decrypt authEnvelopedData with authAttrs"); + is(read_file_text("authattrs.txt"), "Hello AuthEnvelopedData world\n", + "decrypted authEnvelopedData plaintext matches expected"); + + # A flipped authAttrs byte must fail the tag check and leave -out empty. + 1 while unlink "bad_authattrs.txt"; + ok(!run(app(["openssl", "cms", @defaultprov, "-decrypt", "-inform", "PEM", + "-secretkey", "000102030405060708090A0B0C0D0E0F", + "-secretkeyid", "C0FEE0", + "-in", catfile($datadir, "bad_authenveloped_attrs.pem"), + "-out", "bad_authattrs.txt" ])), + "reject authEnvelopedData with tampered authAttrs"); + ok(!-s "bad_authattrs.txt", + "tampered authEnvelopedData leaks no plaintext to -out"); +}; + subtest "CAdES <=> CAdES consistency tests\n" => sub { plan tests => (scalar @smime_cms_cades_tests); diff --git a/test/recipes/80-test_cms_data/authenveloped_attrs.pem b/test/recipes/80-test_cms_data/authenveloped_attrs.pem new file mode 100644 index 0000000000..75c8eab00b --- /dev/null +++ b/test/recipes/80-test_cms_data/authenveloped_attrs.pem @@ -0,0 +1,7 @@ +-----BEGIN CMS----- +MIAGCyqGSIb3DQEJEAEXoIAwgAIBADEzojECAQQwBQQDwP7gMAsGCWCGSAFlAwQB +BQQYknpV85muZoLZSPkwi5Ll1Z1HwzAeZThVMIAGCSqGSIb3DQEHATAeBglghkgB +ZQMEAQYwEQQMkCQb305essfGO2nqAgEQoIAEHvYjM7EK9qZAHgoohdcbSHXe0lGJ +/Hjk3nkK5VsHxgAAAAChFjAUBgkrBgEEAYaNHwExBwwFaGVsbG8EENeGq4IXAd1O +iv8hMl+lHZOiFjAUBgkrBgEEAYaNHwIxBwwFd29ybGQAAAAAAAA= +-----END CMS----- diff --git a/test/recipes/80-test_cms_data/bad_authenveloped_attrs.pem b/test/recipes/80-test_cms_data/bad_authenveloped_attrs.pem new file mode 100644 index 0000000000..e14946c96d --- /dev/null +++ b/test/recipes/80-test_cms_data/bad_authenveloped_attrs.pem @@ -0,0 +1,7 @@ +-----BEGIN CMS----- +MIAGCyqGSIb3DQEJEAEXoIAwgAIBADEzojECAQQwBQQDwP7gMAsGCWCGSAFlAwQB +BQQYknpV85muZoLZSPkwi5Ll1Z1HwzAeZThVMIAGCSqGSIb3DQEHATAeBglghkgB +ZQMEAQYwEQQMkCQb305essfGO2nqAgEQoIAEHvYjM7EK9qZAHgoohdcbSHXe0lGJ +/Hjk3nkK5VsHxgAAAAChFjAUBgkrBgEEAYaNHwExBwwFaWVsbG8EENeGq4IXAd1O +iv8hMl+lHZOiFjAUBgkrBgEEAYaNHwIxBwwFd29ybGQAAAAAAAA= +-----END CMS----- From 6d9a69861562176d3c2dbdd115aa71357de89e51 Mon Sep 17 00:00:00 2001 From: Timo Keller Date: Fri, 26 Jun 2026 11:29:26 +0200 Subject: [PATCH 238/349] s390x: Fix montgomery_multiplication_vectorized Introduce `reduce_twice_signed` that reduces from `(-2q,q)` to `[0,q)`. Fix `montgomery_multiplication_vectorized` in `ml_dsa_ntt_vec128.c` by calling `reduce_twice_signed` at the end of the computation ensuring that the result is in `[0,q)` and not only in `(-2q,q)` or `(-q,q)`. Do not call `reduce_once_signed` in `ossl_poly_ntt_mult_scalar_vec128` and at the end of `ossl_ml_dsa_poly_ntt_inverse_vec128` anymore as it is not necessary anymore after `reduce_twice_signed`. Without this fix, keygen, sign or verify might fail or produce wrong results. Signed-off-by: Timo Keller Reviewed-by: Neil Horman Reviewed-by: Viktor Dukhovni MergeDate: Mon Jul 13 14:45:11 2026 (Merged from https://github.com/openssl/openssl/pull/31744) --- crypto/ml_dsa/ml_dsa_ntt_vec128.c | 59 +++++++++++++++++++------------ 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/crypto/ml_dsa/ml_dsa_ntt_vec128.c b/crypto/ml_dsa/ml_dsa_ntt_vec128.c index 8176cbcdfb..54d59a9a08 100644 --- a/crypto/ml_dsa/ml_dsa_ntt_vec128.c +++ b/crypto/ml_dsa/ml_dsa_ntt_vec128.c @@ -309,15 +309,47 @@ static const int32_t neg_zetas_montgomery_twisted[256] = { static const vec_int32_t vec_q = { ML_DSA_Q, ML_DSA_Q, ML_DSA_Q, ML_DSA_Q }; static const vec_int32_t vec_q_inv = { ML_DSA_Q_INV, ML_DSA_Q_INV, ML_DSA_Q_INV, ML_DSA_Q_INV }; +/* + * @brief Reduce a in (-q, q) to a mod q in [0, q). + * + * @param a in (-q, q) + * @returns a mod q in [0, q) + */ +static ossl_inline + vec_int32_t + reduce_once_signed(vec_int32_t a) +{ + /* mask is 11..11 when a is negative, else 0 */ + vec_uint32_t mask = -(((vec_uint32_t)a) >> 31); + return a + (vec_int32_t)(mask & (vec_uint32_t)vec_q); +} + +/* + * @brief Reduce a in (-2q, q) to a mod q in [0, q). + * + * @param a in (-2q, q) + * @returns a mod q in [0, q) + */ +static ossl_inline + vec_int32_t + reduce_twice_signed(vec_int32_t a) +{ + /* mask is 11..11 when a is negative, else 0 */ + vec_uint32_t mask = -(((vec_uint32_t)a) >> 31); + /* b is in (-q, q) */ + vec_int32_t b = a + (vec_int32_t)(mask & (vec_uint32_t)vec_q); + return reduce_once_signed(b); +} + /* * @brief Computes the Montgomery product of a and b. * See [Seiler 2018, Algorithm 3]. * - * @param a is the first factor, assumed to be non-negative. + * @param a is the first factor, assumed to be in [0, q). * @param a_twist is (int32)((uint32)a * ML_DSA_Q_INV). * @param b is the second factor. * @returns The Montgomery product of a and b in the range - * -q+1..q-1. + * [0, q). */ static ossl_inline @@ -329,22 +361,7 @@ static ossl_inline vec_int32_t c = (vec_int32_t)c_u; vec_int32_t z_high = vec_mulh((vec_int32_alias_t)a, (vec_int32_alias_t)b); vec_int32_t r = z_high - c; - return r; -} - -/* - * @brief Reduce a in (-q, q) to a mod q in [0, q-1]. - * - * @param a in (-q, q) - * @returns a mod q in [0, q-1] - */ -static ossl_inline - vec_int32_t - reduce_once_signed(vec_int32_t a) -{ - /* mask is 11..11 when a is negative, else 0 */ - vec_uint32_t mask = -(((vec_uint32_t)a) >> 31); - return a + (vec_int32_t)(mask & (vec_uint32_t)vec_q); + return reduce_twice_signed(r); } /* @@ -376,9 +393,8 @@ void ossl_poly_ntt_mult_scalar_vec128(const POLY *lhs, const POLY *rhs, POLY *ou for (i = 0; i < ML_DSA_NUM_POLY_COEFFICIENTS / NUM_INT32_IN_VECTOR; i++) { vec_int32_t twist_vec = (vec_int32_t)((vec_uint32_t)lhs_vec_ptr[i] * (vec_uint32_t)vec_q_inv); - vec_int32_t result = montgomery_multiplication_vectorized( + out_vec_ptr[i] = montgomery_multiplication_vectorized( lhs_vec_ptr[i], twist_vec, rhs_vec_ptr[i]); - out_vec_ptr[i] = reduce_once_signed(result); } } @@ -682,11 +698,10 @@ void ossl_ml_dsa_poly_ntt_inverse_vec128(POLY *p) } for (i = 0; i < ML_DSA_NUM_POLY_COEFFICIENTS / NUM_INT32_IN_VECTOR; i += 1) { - vec_int32_t coeff_i_vec = montgomery_multiplication_vectorized( + p_vec[i] = montgomery_multiplication_vectorized( vec_inverse_degree_montgomery, vec_inverse_degree_montgomery_twisted, p_vec[i]); - p_vec[i] = reduce_once_signed(coeff_i_vec); } } From 79373ca3bd4c26e9a4896e4c198828909cc6deec Mon Sep 17 00:00:00 2001 From: slontis Date: Mon, 29 Jun 2026 09:50:07 +1000 Subject: [PATCH 239/349] FIPS: EC keygen - remove unnecessary self tests. In FIPS mode EC keygen was doing 3 self tests. ec_generate_key() was calling both ecdsa_keygen_pairwise_test() and ecdsa_keygen_knownanswer_test(). The KAT did a key recomputation and comparison with the generated key, as per Sp80056Ar3 section 5.6.2.1.4. These tests covered both Keygen PCT for Key Agreement and Signatures. ossl_ec_key_pairwise_check() was also being called from within ec_gen(). The advice from Atsec (lab) is that the sign/verify test within ecdsa_keygen_pairwise_test() is sufficient according to the updated rules in FIPS 140-3 IG 10.3.A Additional comment 1, Since the usage of the generated key is unknown at the time of key generation. Detected during testing of Jipher by Roshith Alankandy (Oracle). Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz MergeDate: Mon Jul 13 14:47:00 2026 (Merged from https://github.com/openssl/openssl/pull/31761) --- crypto/ec/ec_key.c | 53 +------------------- providers/implementations/keymgmt/ec_kmgmt.c | 12 ----- test/pairwise_fail_test.c | 2 - test/recipes/30-test_pairwise_fail.t | 10 +--- 4 files changed, 3 insertions(+), 74 deletions(-) diff --git a/crypto/ec/ec_key.c b/crypto/ec/ec_key.c index f5175653fa..44791a2c3e 100644 --- a/crypto/ec/ec_key.c +++ b/crypto/ec/ec_key.c @@ -213,56 +213,6 @@ int ossl_ec_key_gen(EC_KEY *eckey) return ret; } -/* - * Refer: FIPS 140-3 IG 10.3.A Additional Comment 1 - * Perform a KAT by duplicating the public key generation. - * - * NOTE: This issue requires a background understanding, provided in a separate - * document; the current IG 10.3.A AC1 is insufficient regarding the PCT for - * the key agreement scenario. - * - * Currently IG 10.3.A requires PCT in the mode of use prior to use of the - * key pair, citing the PCT defined in the associated standard. For key - * agreement, the only PCT defined in SP 800-56A is that of Section 5.6.2.4: - * the comparison of the original public key to a newly calculated public key. - */ -static int ecdsa_keygen_knownanswer_test(EC_KEY *eckey, BN_CTX *ctx, - OSSL_CALLBACK *cb, void *cbarg) -{ - int len, ret = 0; - OSSL_SELF_TEST *st = NULL; - unsigned char bytes[512] = { 0 }; - EC_POINT *pub_key2 = NULL; - - st = OSSL_SELF_TEST_new(cb, cbarg); - if (st == NULL) - return 0; - - OSSL_SELF_TEST_onbegin(st, OSSL_SELF_TEST_TYPE_PCT_KAT, - OSSL_SELF_TEST_DESC_PCT_ECDSA); - - if ((pub_key2 = EC_POINT_new(eckey->group)) == NULL) - goto err; - - /* pub_key = priv_key * G (where G is a point on the curve) */ - if (!EC_POINT_mul(eckey->group, pub_key2, eckey->priv_key, NULL, NULL, ctx)) - goto err; - - if (BN_num_bytes(pub_key2->X) > (int)sizeof(bytes)) - goto err; - len = BN_bn2bin(pub_key2->X, bytes); - if (OSSL_SELF_TEST_oncorrupt_byte(st, bytes) - && BN_bin2bn(bytes, len, pub_key2->X) == NULL) - goto err; - ret = !EC_POINT_cmp(eckey->group, eckey->pub_key, pub_key2, ctx); - -err: - OSSL_SELF_TEST_onend(st, ret); - OSSL_SELF_TEST_free(st); - EC_POINT_free(pub_key2); - return ret; -} - /* * ECC Key generation. * See SP800-56AR3 5.6.1.2.2 "Key Pair Generation by Testing Candidates" @@ -359,8 +309,7 @@ static int ec_generate_key(EC_KEY *eckey, int pairwise_test) void *cbarg = NULL; OSSL_SELF_TEST_get_callback(eckey->libctx, &cb, &cbarg); - ok = ecdsa_keygen_pairwise_test(eckey, cb, cbarg) - && ecdsa_keygen_knownanswer_test(eckey, ctx, cb, cbarg); + ok = ecdsa_keygen_pairwise_test(eckey, cb, cbarg); } err: /* Step (9): If there is an error return an invalid keypair. */ diff --git a/providers/implementations/keymgmt/ec_kmgmt.c b/providers/implementations/keymgmt/ec_kmgmt.c index 57ff812793..28404502eb 100644 --- a/providers/implementations/keymgmt/ec_kmgmt.c +++ b/providers/implementations/keymgmt/ec_kmgmt.c @@ -1325,18 +1325,6 @@ static void *ec_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg) if (gctx->group_check != NULL) ret = ret && ossl_ec_set_check_group_type_from_name(ec, gctx->group_check); -#ifdef FIPS_MODULE - if (ret > 0 - && !ossl_fips_self_testing() - && EC_KEY_get0_public_key(ec) != NULL - && EC_KEY_get0_private_key(ec) != NULL - && EC_KEY_get0_group(ec) != NULL) { - BN_CTX *bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(ec)); - - ret = bnctx != NULL && ossl_ec_key_pairwise_check(ec, bnctx); - BN_CTX_free(bnctx); - } -#endif /* FIPS_MODULE */ if (ret > 0) return ec; diff --git a/test/pairwise_fail_test.c b/test/pairwise_fail_test.c index 3173225a0c..3446d23166 100644 --- a/test/pairwise_fail_test.c +++ b/test/pairwise_fail_test.c @@ -99,8 +99,6 @@ static int test_keygen_pairwise_failure(void) if (!TEST_ptr_null(pkey = EVP_PKEY_Q_keygen(libctx, NULL, "RSA", (size_t)2048))) goto err; } else if (strncmp(pairwise_name, "ec", 2) == 0) { - if (strcmp(pairwise_name, "eckat") == 0) - type = OSSL_SELF_TEST_TYPE_PCT_KAT; if (!TEST_true(setup_selftest_pairwise_failure(type))) goto err; if (!TEST_ptr_null(pkey = EVP_PKEY_Q_keygen(libctx, NULL, "EC", "P-256"))) diff --git a/test/recipes/30-test_pairwise_fail.t b/test/recipes/30-test_pairwise_fail.t index eaf0dbbb42..ca2d1f96e4 100644 --- a/test/recipes/30-test_pairwise_fail.t +++ b/test/recipes/30-test_pairwise_fail.t @@ -22,7 +22,7 @@ use lib bldtop_dir('.'); plan skip_all => "These tests are unsupported in a non fips build" if disabled("fips"); -plan tests => 9; +plan tests => 8; my $provconf = srctop_file("test", "fips-and-base.cnf"); run(test(["fips_version_test", "-config", $provconf, ">=3.1.0"]), @@ -37,17 +37,11 @@ SKIP: { } SKIP: { - skip "Skip EC test because of no ec in this build", 2 + skip "Skip EC test because of no ec in this build", 1 if disabled("ec"); ok(run(test(["pairwise_fail_test", "-config", $provconf, "-pairwise", "ec"])), "fips provider ec keygen pairwise failure test"); - - skip "FIPS provider version is too old", 1 - if !$fips_exit; - ok(run(test(["pairwise_fail_test", "-config", $provconf, - "-pairwise", "eckat"])), - "fips provider ec keygen kat failure test"); } SKIP: { From cedff47f3895f17979988f4b5e93d0482ae5931c Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 30 Jun 2026 00:29:23 +0200 Subject: [PATCH 240/349] test: build the fake cipher provider as a loadable module The fake cipher provider was only available in-process, linked into test binaries via fake_cipher_start(). To exercise app success paths (e.g. skeyutl -genkey) the openssl app needs to load it as a provider module the same way it loads legacy. Make test/fake_cipherprov.c dual-buildable: drop the testutil dependency so the source links cleanly into a module, add an OSSL_provider_init entry point under FAKE_CIPHER_AS_MODULE, and add a fake-cipher MODULES target in test/build.info. Also implement skeymgmt generate so opaque key generation works, and cover the skeyutl -genkey success path in 20-test_skeyutl.t. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Matt Caswell Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz MergeDate: Mon Jul 13 14:58:53 2026 (Merged from https://github.com/openssl/openssl/pull/31781) --- test/build.info | 10 ++++++++ test/fake_cipherprov.c | 43 +++++++++++++++++++++++++++++----- test/recipes/20-test_skeyutl.t | 23 ++++++++++++++++-- 3 files changed, 68 insertions(+), 8 deletions(-) diff --git a/test/build.info b/test/build.info index 86174be7c5..94e6d08865 100644 --- a/test/build.info +++ b/test/build.info @@ -1265,6 +1265,16 @@ IF[{- !$disabled{tests} -}] SOURCE[p_minimal]=p_minimal.ld GENERATE[p_minimal.ld]=../util/providers.num ENDIF + # Loadable form of the fake cipher provider. + MODULES{noinst}=fake-cipher + SOURCE[fake-cipher]=fake_cipherprov.c + DEFINE[fake-cipher]=FAKE_CIPHER_AS_MODULE + INCLUDE[fake-cipher]=../include + DEPEND[fake-cipher]=../libcrypto + IF[{- defined $target{shared_defflag} -}] + SOURCE[fake-cipher]=fake-cipher.ld + GENERATE[fake-cipher.ld]=../util/providers.num + ENDIF ENDIF IF[{- $disabled{module} || !$target{dso_scheme} -}] DEFINE[provider_test]=NO_PROVIDER_MODULE diff --git a/test/fake_cipherprov.c b/test/fake_cipherprov.c index 16fde61221..ad28a6b94b 100644 --- a/test/fake_cipherprov.c +++ b/test/fake_cipherprov.c @@ -9,13 +9,13 @@ */ #include +#include #include #include #include #include #include #include -#include "testutil.h" #include "fake_cipherprov.h" #define MAX_KEYNAME 32 @@ -65,7 +65,7 @@ static void *fake_skeymgmt_import(void *provctx, int selection, const OSSL_PARAM { PROV_CIPHER_FAKE_CTX *ctx = NULL; - if (!TEST_ptr(ctx = OPENSSL_zalloc(sizeof(PROV_CIPHER_FAKE_CTX)))) + if ((ctx = OPENSSL_zalloc(sizeof(PROV_CIPHER_FAKE_CTX))) == NULL) return 0; if (ctx_from_key_params(ctx, p) != 1) { @@ -76,6 +76,26 @@ static void *fake_skeymgmt_import(void *provctx, int selection, const OSSL_PARAM return ctx; } +static void *fake_skeymgmt_generate(void *provctx, const OSSL_PARAM *params) +{ + PROV_CIPHER_FAKE_CTX *ctx = NULL; + size_t i; + + if ((ctx = OPENSSL_zalloc(sizeof(PROV_CIPHER_FAKE_CTX))) == NULL) + return NULL; + + if (ctx_from_key_params(ctx, params) != 1) { + OPENSSL_free(ctx); + return NULL; + } + + /* Deterministic fill so the provider doesn't depend on a DRBG. */ + for (i = 0; i < sizeof(ctx->key); i++) + ctx->key[i] = (unsigned char)i; + + return ctx; +} + static int fake_skeymgmt_export(void *keydata, int selection, OSSL_CALLBACK *param_callback, void *cbarg) { @@ -103,6 +123,7 @@ static int fake_skeymgmt_export(void *keydata, int selection, static const OSSL_DISPATCH fake_skeymgmt_funcs[] = { { OSSL_FUNC_SKEYMGMT_FREE, (void (*)(void))fake_skeymgmt_free }, + { OSSL_FUNC_SKEYMGMT_GENERATE, (void (*)(void))fake_skeymgmt_generate }, { OSSL_FUNC_SKEYMGMT_IMPORT, (void (*)(void))fake_skeymgmt_import }, { OSSL_FUNC_SKEYMGMT_EXPORT, (void (*)(void))fake_skeymgmt_export }, OSSL_DISPATCH_END @@ -308,19 +329,29 @@ static int fake_cipher_provider_init(const OSSL_CORE_HANDLE *handle, const OSSL_DISPATCH *in, const OSSL_DISPATCH **out, void **provctx) { - if (!TEST_ptr(*provctx = OSSL_LIB_CTX_new())) + if ((*provctx = OSSL_LIB_CTX_new()) == NULL) return 0; *out = fake_cipher_method; return 1; } +#ifdef FAKE_CIPHER_AS_MODULE +/* Entry point when built as a loadable module (e.g. -provider fake-cipher). */ +int OSSL_provider_init(const OSSL_CORE_HANDLE *handle, + const OSSL_DISPATCH *in, + const OSSL_DISPATCH **out, void **provctx) +{ + return fake_cipher_provider_init(handle, in, out, provctx); +} +#endif + OSSL_PROVIDER *fake_cipher_start(OSSL_LIB_CTX *libctx) { OSSL_PROVIDER *p; - if (!TEST_true(OSSL_PROVIDER_add_builtin(libctx, FAKE_PROV_NAME, - fake_cipher_provider_init)) - || !TEST_ptr(p = OSSL_PROVIDER_try_load(libctx, FAKE_PROV_NAME, 1))) + if (!OSSL_PROVIDER_add_builtin(libctx, FAKE_PROV_NAME, + fake_cipher_provider_init) + || (p = OSSL_PROVIDER_try_load(libctx, FAKE_PROV_NAME, 1)) == NULL) return NULL; return p; diff --git a/test/recipes/20-test_skeyutl.t b/test/recipes/20-test_skeyutl.t index e173ba1d13..1c69935cac 100644 --- a/test/recipes/20-test_skeyutl.t +++ b/test/recipes/20-test_skeyutl.t @@ -9,12 +9,16 @@ use strict; use warnings; -use OpenSSL::Test qw/:DEFAULT with/; +use OpenSSL::Test qw/:DEFAULT bldtop_dir with/; use OpenSSL::Test::Utils; setup("test_skeyutl"); -plan tests => 14; +# The success path needs the loadable fake-cipher provider, which is only built +# when module support is enabled. +my $fake_cipher = !disabled('module'); + +plan tests => 14 + ($fake_cipher ? 2 : 0); # Helper: run skeyutl expecting a non-zero (failure) exit code, and optionally # check that stderr matches a regular expression. @@ -78,3 +82,18 @@ skeyutl_fails("skeyutl with an unknown cipher fails", skeyutl_fails("skeyutl with an unknown option fails", qr/Unknown option/, '-not-an-option'); + +# Success path: load the fake-cipher provider, which implements opaque key +# generation, and generate a key with it. +if ($fake_cipher) { + $ENV{OPENSSL_MODULES} = bldtop_dir("test"); + my @prov = ('-provider-path', bldtop_dir("test"), '-provider', 'fake-cipher'); + + my $status; + my @out = run(app(['openssl', 'skeyutl', @prov, + '-genkey', '-skeymgmt', 'fake_cipher']), + capture => 1, statusvar => \$status); + ok($status, "skeyutl -genkey with fake-cipher provider succeeds"); + ok(grep(/opaque key/, @out), + "skeyutl -genkey reports the generated opaque key"); +} From da37b2b65600920662daffb70fa5c835642bcc69 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 30 Jun 2026 18:48:57 +0200 Subject: [PATCH 241/349] apps: test pkey -ec_param_enc option Exercise the previously untested -ec_param_enc option for pkey, covering named_curve and explicit parameter encodings as well as rejection of the option on a non-EC key. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Matt Caswell Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz MergeDate: Mon Jul 13 14:59:39 2026 (Merged from https://github.com/openssl/openssl/pull/31798) --- test/recipes/15-test_pkey.t | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/test/recipes/15-test_pkey.t b/test/recipes/15-test_pkey.t index 7d36e0e83d..fa4363f057 100644 --- a/test/recipes/15-test_pkey.t +++ b/test/recipes/15-test_pkey.t @@ -16,7 +16,7 @@ use OpenSSL::Test qw/:DEFAULT srctop_file/; setup("test_pkey"); -plan tests => 6; +plan tests => 7; my @app = ('openssl', 'pkey'); @@ -183,3 +183,37 @@ subtest "=== pkey EC point conversion form ===" => sub { ok(!run(app([@app, '-in', $in_key, '-ec_conv_form', 'compressed', '-noout'])), "-ec_conv_form on a non-EC key fails"); }; + +subtest "=== pkey EC parameter encoding ===" => sub { + plan skip_all => "EC not supported in this build" if disabled("ec"); + plan tests => 6; + + my $ec_key = 'ec_p256_enc.pem'; + ok(run(app(['openssl', 'genpkey', '-algorithm', 'EC', + '-pkeyopt', 'ec_paramgen_curve:P-256', '-out', $ec_key])), + "generate P-256 EC key"); + + # A named_curve encoding identifies the group by its OID (prime256v1), + # whereas an explicit encoding inlines the full curve parameters, which + # asn1parse shows via the field-type OID (prime-field). + my $named = 'pub_named.der'; + ok(run(app([@app, '-in', $ec_key, '-pubout', '-ec_param_enc', 'named_curve', + '-outform', 'DER', '-out', $named])), + "write public key with named_curve parameters"); + ok((grep /prime256v1/, + run(app(['openssl', 'asn1parse', '-in', $named, '-inform', 'DER']), + capture => 1)), + "named_curve encoding references the curve by OID"); + + my $explicit = 'pub_explicit.der'; + ok(run(app([@app, '-in', $ec_key, '-pubout', '-ec_param_enc', 'explicit', + '-outform', 'DER', '-out', $explicit])), + "write public key with explicit parameters"); + ok((grep /prime-field/, + run(app(['openssl', 'asn1parse', '-in', $explicit, '-inform', 'DER']), + capture => 1)), + "explicit encoding inlines the curve parameters"); + + ok(!run(app([@app, '-in', $in_key, '-ec_param_enc', 'explicit', '-noout'])), + "-ec_param_enc on a non-EC key fails"); +}; From 804fc3ecd8dbced41171c7e7eb249ce54dff50a3 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 30 Jun 2026 19:14:23 +0200 Subject: [PATCH 242/349] apps: test ec app -param_enc option Exercise the previously untested -param_enc option of the ec app, covering named_curve and explicit parameter encodings (compared against checked-in reference encodings) as well as rejection of an invalid value. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Matt Caswell Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz MergeDate: Mon Jul 13 15:00:25 2026 (Merged from https://github.com/openssl/openssl/pull/31799) --- test/recipes/15-test_ec.t | 26 +++++++++++++++++- .../15-test_ec_data/ec-param-explicit.der | Bin 0 -> 335 bytes .../15-test_ec_data/ec-param-named.der | Bin 0 -> 91 bytes 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 test/recipes/15-test_ec_data/ec-param-explicit.der create mode 100644 test/recipes/15-test_ec_data/ec-param-named.der diff --git a/test/recipes/15-test_ec.t b/test/recipes/15-test_ec.t index 5ae3943cb6..5b0a69f0ca 100644 --- a/test/recipes/15-test_ec.t +++ b/test/recipes/15-test_ec.t @@ -19,7 +19,7 @@ setup("test_ec"); plan skip_all => 'EC is not supported in this build' if disabled('ec'); -plan tests => 17; +plan tests => 18; my $no_fips = disabled('fips') || ($ENV{NO_FIPS} // 0); @@ -128,6 +128,30 @@ subtest 'EC point conversion form (-conv_form)' => sub { "an invalid conversion form is rejected"); }; +subtest 'EC parameter encoding (-param_enc)' => sub { + plan tests => 6; + + my $key = srctop_file("test", "testec-p256.pem"); + + ok(run(app(['openssl', 'ec', '-in', $key, '-pubout', '-param_enc', + 'named_curve', '-outform', 'DER', '-out', 'ec-param-named.der'])), + "writing public key with named_curve parameter encoding"); + ok(run(app(['openssl', 'ec', '-in', $key, '-pubout', '-param_enc', + 'explicit', '-outform', 'DER', '-out', 'ec-param-explicit.der'])), + "writing public key with explicit parameter encoding"); + ok((-s 'ec-param-named.der') < (-s 'ec-param-explicit.der'), + "named_curve encoding is smaller than explicit"); + # The encodings are deterministic for a fixed key, so compare them + # against the checked-in reference files. + is(compare('ec-param-named.der', data_file('ec-param-named.der')), 0, + "named_curve encoding matches the reference file"); + is(compare('ec-param-explicit.der', data_file('ec-param-explicit.der')), 0, + "explicit encoding matches the reference file"); + ok(!run(app(['openssl', 'ec', '-in', $key, '-noout', + '-param_enc', 'bogus'])), + "an invalid parameter encoding is rejected"); +}; + subtest 'Check loading of fips and non-fips keys' => sub { plan skip_all => "FIPS is disabled" if $no_fips; diff --git a/test/recipes/15-test_ec_data/ec-param-explicit.der b/test/recipes/15-test_ec_data/ec-param-explicit.der new file mode 100644 index 0000000000000000000000000000000000000000..e29a3a720888d38c1283e8c460430abe6ea70294 GIT binary patch literal 335 zcmXqLV)Qm>Vq|7x*J|@PXUoKB(Dap5Y3_h zVgn^{srkdA5OvJ-#wx4H&o{r`8(KD_ZBME!+lDj28@^eev$0dZl;F)Q%5Y?^8Asc5 zmdROZ6%U2ys%>waUpIlpktJLF;-`l?PVPVUJbRkF#Nkspb8VyUYvTo97FxRAm~m*D z|6`{||D@_?_dS2@Rl`3|O#7U1c(!5OxzpPmGR*e}enofz>=lqx_dNTxcDeYxmd`s+ z9r}~38VK|=vl9c0Y7Ik4%Ojg`p`c?El{257H4dEre$|WEKXDuX%swrD`@n_Y$t&`w gOgZ;MIaF8fy_d&|y4juu4{Xn?Xr2 vxAD*H)AF|uT=<>5B7e%1b3c?rb>-fBd90|L?OE`^_Pm Date: Tue, 30 Jun 2026 19:21:26 +0200 Subject: [PATCH 243/349] apps: test ecparam app -param_enc option Exercise the previously untested -param_enc option of the ecparam app by round-tripping the secp384r1 fixtures between named_curve and explicit encodings (compared byte for byte against the reference files), and check that an invalid value is rejected. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Matt Caswell Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz MergeDate: Mon Jul 13 15:00:27 2026 (Merged from https://github.com/openssl/openssl/pull/31799) --- test/recipes/15-test_ecparam.t | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/test/recipes/15-test_ecparam.t b/test/recipes/15-test_ecparam.t index 0d72154745..571c62cf1a 100644 --- a/test/recipes/15-test_ecparam.t +++ b/test/recipes/15-test_ecparam.t @@ -30,7 +30,7 @@ if (disabled("sm2")) { @valid = grep { !/sm2-.*\.pem/} @valid; } -plan tests => 14; +plan tests => 15; sub checkload { my $files = shift; # List of files @@ -199,4 +199,29 @@ subtest "Check loading of fips and non-fips params" => sub { $ENV{OPENSSL_CONF} = $defaultconf; }; +subtest "Check ecparam -param_enc converts between named and explicit" => sub { + plan tests => 3; + + my $named = data_file('valid', 'secp384r1-named.pem'); + my $explicit = data_file('valid', 'secp384r1-explicit.pem'); + + # The encodings are canonical, so re-encoding a named curve as explicit + # (and vice versa) must reproduce the matching reference file byte for byte. + my $to_explicit = 'param-explicit.tst'; + ok(run(app(['openssl', 'ecparam', '-in', $named, '-param_enc', 'explicit', + '-out', $to_explicit])) + && !compare($to_explicit, $explicit), + "named_curve params re-encoded as explicit match the reference file"); + + my $to_named = 'param-named.tst'; + ok(run(app(['openssl', 'ecparam', '-in', $explicit, '-param_enc', + 'named_curve', '-out', $to_named])) + && !compare($to_named, $named), + "explicit params re-encoded as named_curve match the reference file"); + + ok(!run(app(['openssl', 'ecparam', '-in', $named, '-noout', + '-param_enc', 'bogus'])), + "an invalid parameter encoding is rejected"); +}; + ok(run(app(['openssl', 'ecparam', '-list_curves'])), "Test -list_curves"); From dc70836ce1c51e1ed7a3a9af757aad9589db89d9 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 30 Jun 2026 19:55:44 +0200 Subject: [PATCH 244/349] apps: test rsa app -RSAPublicKey_in/-RSAPublicKey_out options Cover the previously untested -RSAPublicKey_in and -RSAPublicKey_out options of the rsa app, which select the PKCS#1 RSAPublicKey structure rather than the SubjectPublicKeyInfo used by -pubin/-pubout. The new subtest checks that the RSA PUBLIC KEY header is written, that the encoding round-trips, and that it is interchangeable with the SubjectPublicKeyInfo form. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz MergeDate: Mon Jul 13 15:01:22 2026 (Merged from https://github.com/openssl/openssl/pull/31802) --- test/recipes/15-test_rsa.t | 62 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/test/recipes/15-test_rsa.t b/test/recipes/15-test_rsa.t index 851814e8af..3e60b23ef4 100644 --- a/test/recipes/15-test_rsa.t +++ b/test/recipes/15-test_rsa.t @@ -11,12 +11,13 @@ use strict; use warnings; use File::Spec; +use File::Compare qw/compare/; use OpenSSL::Test qw/:DEFAULT srctop_file/; use OpenSSL::Test::Utils; setup("test_rsa"); -plan tests => 14; +plan tests => 16; require_ok(srctop_file('test', 'recipes', 'tconversion.pl')); @@ -54,7 +55,7 @@ sub run_rsa_tests { SKIP: { skip "Skipping msblob conversion test", 1 - if disabled($cmd) || $cmd eq 'pkey'; + if disabled("rsa") || $cmd eq 'pkey'; subtest "$cmd conversions -- public key" => sub { tconversion( -type => 'msb', -prefix => "$cmd-msb-pub", @@ -64,7 +65,7 @@ sub run_rsa_tests { } SKIP: { skip "Skipping PVK conversion test", 1 - if disabled($cmd) || $cmd eq 'pkey' || disabled("rc4") + if disabled("rsa") || $cmd eq 'pkey' || disabled("rc4") || disabled ("legacy") || disabled("pvkkdf"); subtest "$cmd conversions -- private key" => sub { @@ -76,4 +77,59 @@ sub run_rsa_tests { "-provider", "legacy"] ); }; } + + SKIP: { + # -RSAPublicKey_in/-RSAPublicKey_out are specific to the rsa app and + # select the PKCS#1 RSAPublicKey structure instead of the + # SubjectPublicKeyInfo used by -pubin/-pubout. + skip "Skipping RSAPublicKey conversion test", 1 + if disabled("rsa") || $cmd eq 'pkey'; + + subtest "$cmd conversions -- RSAPublicKey (PKCS#1) public key" => sub { + plan tests => 9; + + my $priv = srctop_file("test", "testrsa.pem"); + my $pub = srctop_file("test", "testrsapub.pem"); + + my $rsapub = "$cmd-rsapub.pem"; + ok(run(app(['openssl', 'rsa', '-in', $priv, '-RSAPublicKey_out', + '-out', $rsapub])), + "RSAPublicKey_out writes a public key"); + open(my $fh, '<', $rsapub); + my @rsapub_pem = <$fh>; + close($fh); + ok(grep(/BEGIN RSA PUBLIC KEY/, @rsapub_pem), + "RSAPublicKey_out uses the PKCS#1 RSA PUBLIC KEY header"); + + # Re-encoding an RSAPublicKey input as RSAPublicKey is stable. + my $rsapub2 = "$cmd-rsapub2.pem"; + ok(run(app(['openssl', 'rsa', '-in', $rsapub, '-RSAPublicKey_in', + '-RSAPublicKey_out', '-out', $rsapub2])), + "RSAPublicKey_in reads an RSAPublicKey"); + is(compare($rsapub, $rsapub2), 0, + "RSAPublicKey_in round-trips to an identical RSAPublicKey"); + + # RSAPublicKey input re-encoded as SubjectPublicKeyInfo matches the + # canonical SubjectPublicKeyInfo public key. + my $spki1 = "$cmd-spki1.pem"; + my $spki2 = "$cmd-spki2.pem"; + ok(run(app(['openssl', 'rsa', '-in', $rsapub, '-RSAPublicKey_in', + '-pubout', '-out', $spki1])), + "RSAPublicKey_in can be written as SubjectPublicKeyInfo"); + ok(run(app(['openssl', 'rsa', '-in', $pub, '-pubin', '-pubout', + '-out', $spki2])), + "canonical SubjectPublicKeyInfo public key written"); + is(compare($spki1, $spki2), 0, + "RSAPublicKey_in -pubout matches the SubjectPublicKeyInfo key"); + + # Conversely, a SubjectPublicKeyInfo input written as RSAPublicKey + # matches the RSAPublicKey extracted from the private key. + my $rsapub3 = "$cmd-rsapub3.pem"; + ok(run(app(['openssl', 'rsa', '-in', $pub, '-pubin', + '-RSAPublicKey_out', '-out', $rsapub3])), + "SubjectPublicKeyInfo input can be written as RSAPublicKey"); + is(compare($rsapub, $rsapub3), 0, + "pubin -RSAPublicKey_out matches the extracted RSAPublicKey"); + }; + } } From 1de3ee397c929eea04225a4fd214f2fe8c0cf98e Mon Sep 17 00:00:00 2001 From: David Foster Date: Wed, 1 Jul 2026 09:26:21 -0400 Subject: [PATCH 245/349] Explain inconsistency in X25519 ladder copies Fixes #31560 Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz MergeDate: Mon Jul 13 15:03:15 2026 (Merged from https://github.com/openssl/openssl/pull/31812) --- crypto/ec/curve25519.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crypto/ec/curve25519.c b/crypto/ec/curve25519.c index c6886763ab..53c8bff5ed 100644 --- a/crypto/ec/curve25519.c +++ b/crypto/ec/curve25519.c @@ -236,6 +236,13 @@ static void x25519_scalar_mulx(uint8_t out[32], const uint8_t scalar[32], fe64_sub(tmp1, x2, z2); fe64_add(x2, x2, z2); fe64_add(z2, x3, z3); + /* The original copy in x25519_scalar_mult_generic uses argument order + * fe_mul(z3, tmp0, x2), with the input arguments swapped. + * + * The assembly implementation of fe64_mul used here runs faster in + * parallel with its nearby instructions when an earlier-computable + * input (like tmp0) is passed as the 2nd input because it consumes + * the 2nd input at a faster rate than the 1st input. */ fe64_mul(z3, x2, tmp0); fe64_mul(z2, z2, tmp1); fe64_sqr(tmp0, tmp1); From 2d3e267b2b035859b1da38644721afef8d88d792 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 2 Jul 2026 13:45:16 +0200 Subject: [PATCH 246/349] x509: add ocsptest for the OCSP stapled-response verification path Add test/ocsptest.c, exercising check_cert_ocsp_resp() in x509_vfy.c through X509_verify_cert() with X509_V_FLAG_OCSP_RESP_CHECK and responses attached via X509_STORE_CTX_set_ocsp_resp(). This path was previously only covered indirectly through the TLS multi-stapling tests in sslapitest.c. The test builds signed OCSP responses at run time from a flat root -> leaf PKI (the root is both the trust anchor and the authorized responder), and covers the good, grace-period, non-successful status, expired, no-response, and wrong-certificate cases, plus a mfail run over the success path. The PKI is generated by the test-tools ocsptest command. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Daniel Kubec Reviewed-by: Tomas Mraz MergeDate: Mon Jul 13 15:04:59 2026 (Merged from https://github.com/openssl/openssl/pull/31828) --- test/build.info | 6 +- test/ocsptest.c | 408 ++++++++++++++++++++++++++++++++++++ test/recipes/80-test_ocsp.t | 8 +- 3 files changed, 420 insertions(+), 2 deletions(-) create mode 100644 test/ocsptest.c diff --git a/test/build.info b/test/build.info index 94e6d08865..a5dcc7f89d 100644 --- a/test/build.info +++ b/test/build.info @@ -65,7 +65,7 @@ IF[{- !$disabled{tests} -}] x509_time_test x509_dup_cert_test x509_check_cert_pkey_test \ recordlentest drbgtest rand_status_test sslbuffertest \ time_offset_test pemtest ssl_cert_table_internal_test ciphername_test \ - servername_test ocspapitest fatalerrtest tls13ccstest \ + servername_test ocspapitest ocsptest fatalerrtest tls13ccstest \ sysdefaulttest errtest ssl_ctx_test build_wincrypt_test \ context_internal_test aesgcmtest params_test evp_pkey_dparams_test \ keymgmt_internal_test hexstr_test provider_status_test defltfips_test \ @@ -356,6 +356,10 @@ IF[{- !$disabled{tests} -}] INCLUDE[crltest]=../include ../apps/include DEPEND[crltest]=../libcrypto libtestutil.a + SOURCE[ocsptest]=ocsptest.c + INCLUDE[ocsptest]=../include ../apps/include + DEPEND[ocsptest]=../libcrypto libtestutil.a + SOURCE[v3ext]=v3ext.c INCLUDE[v3ext]=../include ../apps/include DEPEND[v3ext]=../libcrypto libtestutil.a diff --git a/test/ocsptest.c b/test/ocsptest.c new file mode 100644 index 0000000000..96dc07423b --- /dev/null +++ b/test/ocsptest.c @@ -0,0 +1,408 @@ +/* + * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include +#include +#include +#include +#ifndef OPENSSL_NO_OCSP +#include +#endif + +#include "testutil.h" + +#ifndef OPENSSL_NO_OCSP + +/* + * Fixtures for the OCSP stapled-response verification path + * (check_cert_ocsp_resp() in x509_vfy.c), reached from X509_verify_cert() + * when X509_V_FLAG_OCSP_RESP_CHECK is set and responses are attached with + * X509_STORE_CTX_set_ocsp_resp(). + * + * Root CA (self-signed trust anchor) + * \-- leaf (signed by the Root CA) + * + * The flat chain makes the root both the trust anchor and the authorized OCSP + * responder for the leaf, and having the root key lets each test build its own + * signed responses at run time. The certificates have a long validity because + * OCSP_check_validity() compares against the wall clock and cannot be pinned + * via X509_VERIFY_PARAM_set_time(). + * + * The arrays below are generated by the test-tools ocsptest command. + */ + +static const char *kOcspTestRoot[] = { + "-----BEGIN CERTIFICATE-----\n", + "MIID+DCCAuCgAwIBAgIBATANBgkqhkiG9w0BAQsFADCBmjELMAkGA1UEBhMCVVMx\n", + "EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xFTAT\n", + "BgNVBAoMDEV4YW1wbGUgQ29ycDEeMBwGA1UECwwVQ2VydGlmaWNhdGUgQXV0aG9y\n", + "aXR5MScwJQYDVQQDDB5FeGFtcGxlIENvcnAgT0NTUCBUZXN0IFJvb3QgQ0EwIBcN\n", + "MjYwMTAxMDAwMDAwWhgPMjEyNjAxMDEwMDAwMDBaMIGaMQswCQYDVQQGEwJVUzET\n", + "MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEVMBMG\n", + "A1UECgwMRXhhbXBsZSBDb3JwMR4wHAYDVQQLDBVDZXJ0aWZpY2F0ZSBBdXRob3Jp\n", + "dHkxJzAlBgNVBAMMHkV4YW1wbGUgQ29ycCBPQ1NQIFRlc3QgUm9vdCBDQTCCASIw\n", + "DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKuZRsVUiwQoVlwfXY0nVpvMQiJN\n", + "Su5YfrxzKJ9sMfPI2rEg9d8kW2qsfnCB0isj+SK+CkcfVu5aXcUVUnp08VuKpsI9\n", + "idkeC7bhXkQgBR1p1mBwChc+UUD6qEindJPhFxYur+7gCmpamSHYr8iYmjVe78l8\n", + "vRHVuw8KMsiGegvPsuxIYWdY4mgKSCu3o6DeK4XehXU4Ll5j0fGX5eJgbaI6g8qE\n", + "j71J6G8Y4Ur/WqzTp/GZueTY0wzU8k45HS5uWarGYB2PH8DfM2SRQBw5hYd2xIvS\n", + "jgFYzCXeIZFnHVCMeB//VaUyYGIaw+Dreh+Wb5zBPh9FyJkrOXYyMeFZUScCAwEA\n", + "AaNFMEMwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0O\n", + "BBYEFGSeLzT7Ur6wrLpFYVDHmNi/TQp5MA0GCSqGSIb3DQEBCwUAA4IBAQAMMukk\n", + "I0VRfxY1vZngu3LdnBk5If6TbQ6lmWatT4q3BrAID3L9zG/qev6DlyruvnVqoNdU\n", + "WcWXnzFSDInOKM64wU2oxdosMPWuAKKLHgDdCicikJtF+N0qJgOcyD+Gv9t0kwDB\n", + "O094nZUgrkCafJiy5y7KtZvqOjcvfEK+oD7fXX/VURv8NMOxpuassPHnHGvHqwKu\n", + "7jBZMjkOoZH4hJSo5EzZxRZHz6ay7Q8e+6C6PG8CIYXpIqukjVNq9jBb2yog67mW\n", + "lHlfnEENCyQDSmnEXAVNpJS67jmcItoD1utnDINVzu87DY9WiLZB06Y1xSp1ie7s\n", + "FIfS7bZ1cxVkt5wS\n", + "-----END CERTIFICATE-----\n", + NULL +}; + +static const char *kOcspTestRootKey[] = { + "-----BEGIN RSA PRIVATE KEY-----\n", + "MIIEowIBAAKCAQEAq5lGxVSLBChWXB9djSdWm8xCIk1K7lh+vHMon2wx88jasSD1\n", + "3yRbaqx+cIHSKyP5Ir4KRx9W7lpdxRVSenTxW4qmwj2J2R4LtuFeRCAFHWnWYHAK\n", + "Fz5RQPqoSKd0k+EXFi6v7uAKalqZIdivyJiaNV7vyXy9EdW7DwoyyIZ6C8+y7Ehh\n", + "Z1jiaApIK7ejoN4rhd6FdTguXmPR8Zfl4mBtojqDyoSPvUnobxjhSv9arNOn8Zm5\n", + "5NjTDNTyTjkdLm5ZqsZgHY8fwN8zZJFAHDmFh3bEi9KOAVjMJd4hkWcdUIx4H/9V\n", + "pTJgYhrD4Ot6H5ZvnME+H0XImSs5djIx4VlRJwIDAQABAoIBAAj+6w/d47/JtuVI\n", + "xlMSXzRMW/cyYsg7SWxAWT5/oeAW2n1zWJBkdopmv+YkAsw81uBfDWjhwraSHtz9\n", + "xioh8U6MO+ZuQB4VY3soi2mPiCpyPvPP9nzLc9+v2ZydcrtsjxTxnkrWjJU7afsK\n", + "l1nbw3x4HU1McG5RQbzYcFsaJFHJcVdxIYkDfEgnBok1ALaQuRUGfUVYZDVk6Ztg\n", + "fMUh5nfNSHWu6y2i8YCphprIfu/zwadicaYLYljYqgP9J1MOPwuMcoCoWgotBUCZ\n", + "+29qIPn1mK3ReBmYdE8IIEiSLHYJjJ7EVu8IUfSPotBGGC5PjC11HcCXx0xB9AfB\n", + "I8VcDikCgYEA089WizVCWZMxWnCPLDG036vsug3/rrPr/O3W5VXTdpr00CBRzund\n", + "7QnaxUyxw37SOAQUtzcKhC0CF3c7Dbryb8d51koROVlWC8rngoDMxiZkbZZe0kL1\n", + "jHkOHEOoi83uY0tFoRhYtrcOYObNT9JDifdIDYCnxbLW4UQLo/9mFmUCgYEAz2ZH\n", + "hxoxXJ9y3qmIcc2vZTRPdaB4r+qQi5krUgtYg1uKImrvDnnl/4xCnCG9G1s1M/wE\n", + "cvuFG5FWGz6TtS0TsCGUXVWZ9s67JT53l02RIVTog/VRHbKsOnj+d9oXglGcxALT\n", + "qG02lKocNHzrPUXEw/EIMETKR6WvATZCzhM9mpsCgYB9RoKfb27A4Cgun6husS+T\n", + "o3IuUR1KzSvkux+BIRQjcF8fwh3gzb3u9wcn7satJBNeAjvmaW2U47H7AxAwfMPr\n", + "jQXo0oIBc29LJkVrkJaNFCQOFQQcRHJLFUZdPT8xASngHKMgNvAxkW+1rIz+ixRb\n", + "Q6CgK9oPOkmRjtd7thFBaQKBgQCMbt0QBhRWe0D0tCbHqFaTWJBVPYt60oF9hQFo\n", + "VHZiu6EVHQMx8ihimT6hKdc6ps+nm4YHtXez6v07BWxOyW8DXDlx2XyfOexOk7W2\n", + "pbcXsr6eW4XJbipgjX0A+pPgkhJsRt26tfi3QVhH0i4XFx7c7mB1Dp9JVE7jqzIh\n", + "B7Y28QKBgCg93v7zP5GFmOa6zCZreIIfi1dy50otxfqYewcXy4Egt90cE67vY55u\n", + "3B44ySrLkKDuzX2Inrwp5Vao8b9pDQ/AisRzt617eO1H5JtRF3lzZJukYC6j4urU\n", + "24kB2IhZCB6/3pwGiS21ma9E6OF9tO5YaGGYo02ZqkoflN+XgoeZ\n", + "-----END RSA PRIVATE KEY-----\n", + NULL +}; + +static const char *kOcspTestLeaf[] = { + "-----BEGIN CERTIFICATE-----\n", + "MIIEOzCCAyOgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZoxCzAJBgNVBAYTAlVT\n", + "MRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRUw\n", + "EwYDVQQKDAxFeGFtcGxlIENvcnAxHjAcBgNVBAsMFUNlcnRpZmljYXRlIEF1dGhv\n", + "cml0eTEnMCUGA1UEAwweRXhhbXBsZSBDb3JwIE9DU1AgVGVzdCBSb290IENBMCAX\n", + "DTI2MDEwMTAwMDAwMFoYDzIxMjYwMTAxMDAwMDAwWjCBiDELMAkGA1UEBhMCVVMx\n", + "EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xFTAT\n", + "BgNVBAoMDEV4YW1wbGUgQ29ycDEVMBMGA1UECwwMV2ViIFNlcnZpY2VzMR4wHAYD\n", + "VQQDDBVvY3NwLWxlYWYuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB\n", + "DwAwggEKAoIBAQCoQbsBGnLjATBzU8eWYMeyzu6vy4scpVDdhGnTWMaSIrqoXXge\n", + "JXrMHavT13cv2wNTExA6BNqFZA6YLBXYpDJ5oXyOlY1SYjbapX4M0kry4tBZdGWu\n", + "vnh04Q07SI8JvjtScB3lAIIhGr1WrQNLfEz+O80j4Pp8kLe1fWi0joB1CG4RWiuH\n", + "/2Ls1rSEMTr6dABtQ+zwxRHcFlEAoR9ZEDzfN1hIQEByWJd7TMnv738usyp52wMi\n", + "Gh/sruYHkpsO2tVoLCwm1OMSR1BLdGO1tJWSYN2+Tj2JVleQWcwwCgzDX4OlIdPF\n", + "/CM/6aQA6BkDeMW//tO4Ec4X+530zPiFKcZLAgMBAAGjgZgwgZUwDAYDVR0TAQH/\n", + "BAIwADAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwHQYDVR0O\n", + "BBYEFBEZYONIpHIGBtlfPSlUTcDccGMiMB8GA1UdIwQYMBaAFGSeLzT7Ur6wrLpF\n", + "YVDHmNi/TQp5MCAGA1UdEQQZMBeCFW9jc3AtbGVhZi5leGFtcGxlLmNvbTANBgkq\n", + "hkiG9w0BAQsFAAOCAQEAaFYoQjmWdBBiD/hfSgV34Jf+PVKkecuu60VKDasPLNyV\n", + "ZwZcW8dXF9NOYcebGIEx4rjQTC3xiHUknqfOzc2jmPdU/xBAyRiltQnUh3OFH1qn\n", + "bX0YNUgfluLW+YSwNkriFLuzESBVadGhlX94mQwoqYyQJ+6/Ht2j4P4ZiIDRWfgc\n", + "z9pN8YoCiXh/I/IxVIWunk7Dla+Gr6BDJ762iQMMzPQ5D3cRAe9BDRgGiN0lPeIa\n", + "LMtqDeZZ+dR0KZAr3yZcfyci8SNXwCBbjsoVjmuUj6dlN12pRKxxtBSa23KT6/Dy\n", + "ijXZ+PtyIJz8FVBxO0RoECLimwbUPHfNjAWjuJkugA==\n", + "-----END CERTIFICATE-----\n", + NULL +}; + +/* Load the fixed PKI. Any of the out params may be NULL to skip it. */ +static int load_pki(X509 **root, EVP_PKEY **root_key, X509 **leaf) +{ + if (root != NULL && !TEST_ptr(*root = X509_from_strings(kOcspTestRoot))) + return 0; + if (root_key != NULL + && !TEST_ptr(*root_key = PKEY_from_strings(kOcspTestRootKey))) + return 0; + if (leaf != NULL && !TEST_ptr(*leaf = X509_from_strings(kOcspTestLeaf))) + return 0; + return 1; +} + +/* + * Build a signed OCSP response for |cert| (issued by |issuer|). |resp_status| + * is the outer response status, |cert_status| the single-response certificate + * status. thisUpdate and nextUpdate are the current time offset by + * |this_off_sec| and |next_off_sec| seconds, so callers can also produce + * expired or not-yet-valid responses. The response is signed by + * |signer|/|signer_key|. Returns a response the caller must free, or NULL. + */ +static OCSP_RESPONSE *make_ocsp_response(X509 *cert, X509 *issuer, + int resp_status, int cert_status, int this_off_sec, int next_off_sec, + X509 *signer, EVP_PKEY *signer_key) +{ + OCSP_RESPONSE *resp = NULL; + OCSP_BASICRESP *bs = NULL; + OCSP_CERTID *cid = NULL; + ASN1_TIME *thisupd = NULL, *nextupd = NULL, *revtime = NULL; + + if (cert_status == V_OCSP_CERTSTATUS_REVOKED + && !TEST_ptr(revtime = X509_gmtime_adj(NULL, 0))) + goto end; + + if (!TEST_ptr(bs = OCSP_BASICRESP_new()) + || !TEST_ptr(thisupd = X509_time_adj_ex(NULL, 0, this_off_sec, NULL)) + || !TEST_ptr(nextupd = X509_time_adj_ex(NULL, 0, next_off_sec, NULL)) + || !TEST_ptr(cid = OCSP_cert_to_id(EVP_sha256(), cert, issuer)) + || !TEST_ptr(OCSP_basic_add1_status(bs, cid, cert_status, 0, revtime, + thisupd, nextupd)) + || !TEST_true(OCSP_basic_sign(bs, signer, signer_key, EVP_sha256(), + NULL, OCSP_NOCERTS))) + goto end; + + resp = OCSP_response_create(resp_status, bs); + +end: + ASN1_TIME_free(revtime); + ASN1_TIME_free(thisupd); + ASN1_TIME_free(nextupd); + OCSP_CERTID_free(cid); + OCSP_BASICRESP_free(bs); + return resp; +} + +/* Wrap |resp| into a stack, taking ownership on success. */ +static STACK_OF(OCSP_RESPONSE) *make_ocsp_resp_stack(OCSP_RESPONSE *resp) +{ + STACK_OF(OCSP_RESPONSE) *sk = sk_OCSP_RESPONSE_new_null(); + + if (!TEST_ptr(sk)) + return NULL; + if (!TEST_true(sk_OCSP_RESPONSE_push(sk, resp))) { + sk_OCSP_RESPONSE_free(sk); + return NULL; + } + return sk; +} + +/* + * Verify |leaf| against |root| with the stapled |resps| and |flags|, taking + * ownership of |resps|. Returns X509_V_OK or an X509_V_ERR_xxx code. The + * X509_verify_cert() call is wrapped for malloc-failure injection. + */ +static int verify_ocsp(X509 *leaf, X509 *root, STACK_OF(OCSP_RESPONSE) *resps, + unsigned long flags) +{ + X509_STORE *store = X509_STORE_new(); + X509_STORE_CTX *ctx = X509_STORE_CTX_new(); + X509_VERIFY_PARAM *param = X509_VERIFY_PARAM_new(); + int status = X509_V_ERR_UNSPECIFIED; + + if (!TEST_ptr(store) || !TEST_ptr(ctx) || !TEST_ptr(param)) + goto end; + + if (!TEST_true(X509_STORE_add_cert(store, root)) + || !TEST_true(X509_STORE_CTX_init(ctx, store, leaf, NULL))) + goto end; + + X509_STORE_CTX_set_ocsp_resp(ctx, resps); + + X509_VERIFY_PARAM_set_depth(param, 16); + if (flags != 0) + X509_VERIFY_PARAM_set_flags(param, flags); + X509_STORE_CTX_set0_param(ctx, param); + param = NULL; + + ERR_clear_error(); + MFAIL_start(); + status = X509_verify_cert(ctx) == 1 ? X509_V_OK + : X509_STORE_CTX_get_error(ctx); + MFAIL_end(); + +end: + X509_VERIFY_PARAM_free(param); + X509_STORE_CTX_free(ctx); + X509_STORE_free(store); + sk_OCSP_RESPONSE_pop_free(resps, OCSP_RESPONSE_free); + return status; +} + +/* + * Build a single response for the leaf and verify it, expecting |expected|. + * Every failure inside check_cert_ocsp_resp() surfaces as + * X509_V_ERR_OCSP_VERIFY_FAILED at the X509_verify_cert() level. + */ +static int run_ocsp_verify(int resp_status, int cert_status, int this_off_sec, + int next_off_sec, int expected) +{ + X509 *root = NULL, *leaf = NULL; + EVP_PKEY *root_key = NULL; + OCSP_RESPONSE *resp = NULL; + STACK_OF(OCSP_RESPONSE) *resps = NULL; + int testresult = 0; + + if (!load_pki(&root, &root_key, &leaf)) + goto end; + + if (!TEST_ptr(resp = make_ocsp_response(leaf, root, resp_status, cert_status, + this_off_sec, next_off_sec, root, root_key)) + || !TEST_ptr(resps = make_ocsp_resp_stack(resp))) + goto end; + resp = NULL; /* owned by resps */ + + testresult = TEST_int_eq(verify_ocsp(leaf, root, resps, + X509_V_FLAG_OCSP_RESP_CHECK), + expected); + resps = NULL; /* freed by verify_ocsp */ + +end: + sk_OCSP_RESPONSE_pop_free(resps, OCSP_RESPONSE_free); + OCSP_RESPONSE_free(resp); + EVP_PKEY_free(root_key); + X509_free(leaf); + X509_free(root); + return testresult; +} + +/* + * A good response that expired a moment ago is still accepted, because stapled + * responses are honoured for up to five minutes past nextUpdate. + */ +static int test_ocsp_resp_good(void) +{ + return run_ocsp_verify(OCSP_RESPONSE_STATUS_SUCCESSFUL, + V_OCSP_CERTSTATUS_GOOD, -10 * 60, -2 * 60, X509_V_OK); +} + +/* An outer response status other than successful is rejected. */ +static int test_ocsp_resp_not_successful(void) +{ + return run_ocsp_verify(OCSP_RESPONSE_STATUS_TRYLATER, + V_OCSP_CERTSTATUS_GOOD, 0, 24 * 60 * 60, X509_V_ERR_OCSP_VERIFY_FAILED); +} + +/* A response more than five minutes past nextUpdate is rejected as expired. */ +static int test_ocsp_resp_expired(void) +{ + return run_ocsp_verify(OCSP_RESPONSE_STATUS_SUCCESSFUL, + V_OCSP_CERTSTATUS_GOOD, -20 * 60, -10 * 60, X509_V_ERR_OCSP_VERIFY_FAILED); +} + +/* No response for the leaf's depth: sk_OCSP_RESPONSE_num() <= error_depth. */ +static int test_ocsp_resp_none(void) +{ + X509 *root = NULL, *leaf = NULL; + STACK_OF(OCSP_RESPONSE) *resps = NULL; + int testresult = 0; + + if (!load_pki(&root, NULL, &leaf) + || !TEST_ptr(resps = sk_OCSP_RESPONSE_new_null())) + goto end; + + testresult = TEST_int_eq(verify_ocsp(leaf, root, resps, + X509_V_FLAG_OCSP_RESP_CHECK), + X509_V_ERR_OCSP_VERIFY_FAILED); + resps = NULL; /* freed by verify_ocsp */ + +end: + sk_OCSP_RESPONSE_pop_free(resps, OCSP_RESPONSE_free); + X509_free(leaf); + X509_free(root); + return testresult; +} + +/* + * A well-formed response carrying a single response for a different certificate: + * no CertID matches the leaf, so no status is found for it. + */ +static int test_ocsp_resp_wrong_cert(void) +{ + X509 *root = NULL, *leaf = NULL; + EVP_PKEY *root_key = NULL; + OCSP_RESPONSE *resp = NULL; + STACK_OF(OCSP_RESPONSE) *resps = NULL; + int testresult = 0; + + if (!load_pki(&root, &root_key, &leaf)) + goto end; + + /* the response is about the root, not the leaf being verified */ + if (!TEST_ptr(resp = make_ocsp_response(root, root, + OCSP_RESPONSE_STATUS_SUCCESSFUL, V_OCSP_CERTSTATUS_GOOD, 0, + 24 * 60 * 60, root, root_key)) + || !TEST_ptr(resps = make_ocsp_resp_stack(resp))) + goto end; + resp = NULL; /* owned by resps */ + + testresult = TEST_int_eq(verify_ocsp(leaf, root, resps, + X509_V_FLAG_OCSP_RESP_CHECK), + X509_V_ERR_OCSP_VERIFY_FAILED); + resps = NULL; /* freed by verify_ocsp */ + +end: + sk_OCSP_RESPONSE_pop_free(resps, OCSP_RESPONSE_free); + OCSP_RESPONSE_free(resp); + EVP_PKEY_free(root_key); + X509_free(leaf); + X509_free(root); + return testresult; +} + +/* Exercise the success path under mfail. */ +static int test_ocsp_resp_mfail(void) +{ + X509 *root = NULL, *leaf = NULL; + EVP_PKEY *root_key = NULL; + OCSP_RESPONSE *resp = NULL; + STACK_OF(OCSP_RESPONSE) *resps = NULL; + int testresult = 0; + + if (!load_pki(&root, &root_key, &leaf)) + goto end; + + if (!TEST_ptr(resp = make_ocsp_response(leaf, root, + OCSP_RESPONSE_STATUS_SUCCESSFUL, V_OCSP_CERTSTATUS_GOOD, 0, + 24 * 60 * 60, root, root_key)) + || !TEST_ptr(resps = make_ocsp_resp_stack(resp))) + goto end; + resp = NULL; /* owned by resps */ + + testresult = verify_ocsp(leaf, root, resps, X509_V_FLAG_OCSP_RESP_CHECK) + == X509_V_OK; + resps = NULL; /* freed by verify_ocsp */ + +end: + sk_OCSP_RESPONSE_pop_free(resps, OCSP_RESPONSE_free); + OCSP_RESPONSE_free(resp); + EVP_PKEY_free(root_key); + X509_free(leaf); + X509_free(root); + return testresult; +} + +#endif /* OPENSSL_NO_OCSP */ + +int setup_tests(void) +{ +#ifndef OPENSSL_NO_OCSP + ADD_TEST(test_ocsp_resp_good); + ADD_TEST(test_ocsp_resp_not_successful); + ADD_TEST(test_ocsp_resp_expired); + ADD_TEST(test_ocsp_resp_none); + ADD_TEST(test_ocsp_resp_wrong_cert); + ADD_MFAIL_NO_CHECK_TEST(test_ocsp_resp_mfail); +#endif + return 1; +} diff --git a/test/recipes/80-test_ocsp.t b/test/recipes/80-test_ocsp.t index 3e12a0b23e..62ee9f13ca 100644 --- a/test/recipes/80-test_ocsp.t +++ b/test/recipes/80-test_ocsp.t @@ -54,7 +54,7 @@ sub test_ocsp { $title); }); } -plan tests => 13; +plan tests => 14; subtest "=== VALID OCSP RESPONSES ===" => sub { plan tests => 7; @@ -232,6 +232,12 @@ subtest "=== OCSP API TESTS===" => sub { "running ocspapitest"); }; +subtest "=== OCSP VERIFICATION TESTS ===" => sub { + plan tests => 1; + + ok(run(test(["ocsptest"])), "running ocsptest"); +}; + subtest "=== UNTRUSTED ISSUER HINTS ===" => sub { plan tests => 1; From 59a8cf3d86324a6c774c23a7341158f02d96f6fc Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 7 Jul 2026 20:21:27 +0200 Subject: [PATCH 247/349] apps: add test coverage for dgst -list Exercise the previously uncovered show_digests() path in dgst app by adding a subtest that runs "openssl dgst -list". It checks the header and that sha256 and sha512 are listed, without assuming the full set of digests which depends on the build configuration. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz MergeDate: Mon Jul 13 15:05:45 2026 (Merged from https://github.com/openssl/openssl/pull/31886) --- test/recipes/20-test_dgst.t | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/test/recipes/20-test_dgst.t b/test/recipes/20-test_dgst.t index e287bd32ff..6cabaf3be5 100644 --- a/test/recipes/20-test_dgst.t +++ b/test/recipes/20-test_dgst.t @@ -18,7 +18,7 @@ use Cwd qw(abs_path); setup("test_dgst"); -plan tests => 25; +plan tests => 26; sub tsignverify { my $testtext = shift; @@ -424,6 +424,19 @@ subtest "signing with xoflen is not supported `dgst` CLI" => sub { "Generating signature with xoflen should fail"); }; +subtest "Listing supported digests with `dgst` CLI" => sub { + plan tests => 3; + + my @listdata = run(app(['openssl', 'dgst', '-list']), capture => 1); + chomp(@listdata); + my $listing = join("\n", @listdata); + + ok($listing =~ /Supported digests:/, "LIST: Check header is printed"); + # Only check digests that are always present, each printed as "-" + ok($listing =~ /-sha256\b/, "LIST: Check sha256 is listed"); + ok($listing =~ /-sha512\b/, "LIST: Check sha512 is listed"); +}; + subtest "signing using the nonce-type sigopt" => sub { if (disabled("ec")) { plan tests => 1; From d1d74cf253d376b47015b675a7400800729253aa Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 7 Jul 2026 23:05:33 +0200 Subject: [PATCH 248/349] apps: test dsa app -modulus option Add coverage for the -modulus option of the dsa app, checking the public value is printed for both private and public key input. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz MergeDate: Mon Jul 13 15:31:54 2026 (Merged from https://github.com/openssl/openssl/pull/31887) --- test/recipes/15-test_dsa.t | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/test/recipes/15-test_dsa.t b/test/recipes/15-test_dsa.t index 602e94e6a0..2d16ebd02c 100644 --- a/test/recipes/15-test_dsa.t +++ b/test/recipes/15-test_dsa.t @@ -17,7 +17,7 @@ use OpenSSL::Test::Utils; setup("test_dsa"); plan skip_all => 'DSA is not supported in this build' if disabled('dsa'); -plan tests => 9; +plan tests => 10; require_ok(srctop_file('test','recipes','tconversion.pl')); @@ -60,6 +60,34 @@ SKIP: { }; } +subtest "dsa -modulus prints the DSA public value" => sub { + plan tests => 2; + + # The public value (y) of the committed testdsa.pem / testdsapub.pem + # keypair, i.e. the "pub:" field of 'openssl pkey -text'. + my $expected = "Public Key=CC99A07D9817BFF03BB09B183E9B19EB77ABECF192" + . "C3A9FBA833DBE69EDB719A8E9777BB82736CEC6A8E4E2FAD0693ACC3D1456" + . "5D62710B95B02CC6A5CF091EEF9C22F20193EBE114C45A0B5E54A645037E8" + . "787FE01B3871508A25BDBF7C6B81428F89858F133FDB858C390C2EF7BCF7E" + . "41D7C66578F792A2488C787EF7C7D41"; + + my @priv = run(app(['openssl', 'dsa', '-modulus', '-noout', + '-in', srctop_file("test", "testdsa.pem")], + stderr => undef), + capture => 1); + chomp @priv; + ok(grep(/^\Q$expected\E$/, @priv), + "-modulus prints the expected public value for a private key"); + + my @pub = run(app(['openssl', 'dsa', '-pubin', '-modulus', '-noout', + '-in', srctop_file("test", "testdsapub.pem")], + stderr => undef), + capture => 1); + chomp @pub; + ok(grep(/^\Q$expected\E$/, @pub), + "-modulus prints the expected public value for a public key"); +}; + subtest "dsa PVK output is rejected for public key input" => sub { plan tests => 1; From d8a7e8eec78b04249c2f5dbdaab99fbadd634bd9 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 7 Jul 2026 23:11:44 +0200 Subject: [PATCH 249/349] apps: test dsaparam app DER output paths Add coverage for the DER (ASN.1) output of the dsaparam app, exercising both the parameter output (i2d_KeyParams_bio) and the -genkey private key output (i2d_PrivateKey_bio). Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz MergeDate: Mon Jul 13 15:32:33 2026 (Merged from https://github.com/openssl/openssl/pull/31888) --- test/recipes/15-test_dsaparam.t | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/test/recipes/15-test_dsaparam.t b/test/recipes/15-test_dsaparam.t index 8f7d2af175..fe7a52d836 100644 --- a/test/recipes/15-test_dsaparam.t +++ b/test/recipes/15-test_dsaparam.t @@ -68,7 +68,7 @@ plan skip_all => "DSA isn't supported in this build" my @valid = glob(data_file("valid", "*.pem")); my @invalid = glob(data_file("invalid", "*.pem")); -my $num_tests = scalar @valid + scalar @invalid + 2; +my $num_tests = scalar @valid + scalar @invalid + 4; plan tests => $num_tests; foreach (@valid) { @@ -85,3 +85,32 @@ copy($input, $inout); ok(run(app(['openssl', 'dsaparam', '-in', $inout, '-out', $inout])), "identical infile and outfile"); ok(!compare_text($input, $inout), "converted file $inout did not change"); + +# Cover the DER (ASN.1) output paths of the dsaparam app. +my $srcparams = data_file("valid", "p1024_q160_t1862.pem"); +my $params_der = "dsaparam.der"; +my $key_der = "dsakey.der"; + +subtest "dsaparam DER parameter output" => sub { + plan tests => 2; + + # Exercises i2d_KeyParams_bio(). + ok(run(app(['openssl', 'dsaparam', '-in', $srcparams, + '-outform', 'DER', '-out', $params_der])), + "write DSA parameters in DER form"); + ok(run(app(['openssl', 'dsaparam', '-inform', 'DER', '-in', $params_der, + '-noout'])), + "read the DER DSA parameters back"); +}; + +subtest "dsaparam DER private key output with -genkey" => sub { + plan tests => 2; + + # Exercises i2d_PrivateKey_bio(). + ok(run(app(['openssl', 'dsaparam', '-in', $srcparams, '-genkey', + '-outform', 'DER', '-out', $key_der])), + "generate a DSA key and write it in DER form"); + ok(run(app(['openssl', 'pkey', '-inform', 'DER', '-in', $key_der, + '-noout', '-check'])), + "read the DER DSA private key back"); +}; From 0321c2919c72f9bfef9e4f63ae10b94888c55bb4 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Wed, 8 Jul 2026 12:32:18 +0200 Subject: [PATCH 250/349] apps: test pkeyutl app -rev option Add coverage for the -rev option of the pkeyutl app, checking that the input buffer is reversed before the operation and that -rev is rejected together with raw input. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Daniel Kubec Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz MergeDate: Mon Jul 13 15:35:56 2026 (Merged from https://github.com/openssl/openssl/pull/31891) --- test/recipes/20-test_pkeyutl.t | 38 +++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/test/recipes/20-test_pkeyutl.t b/test/recipes/20-test_pkeyutl.t index 7964a0db06..dec9b4b183 100644 --- a/test/recipes/20-test_pkeyutl.t +++ b/test/recipes/20-test_pkeyutl.t @@ -17,7 +17,7 @@ use File::Compare qw/compare_text compare/; setup("test_pkeyutl"); -plan tests => 32; +plan tests => 33; # For the tests below we use the cert itself as the TBS file @@ -187,6 +187,42 @@ SKIP: { "-pkeyopt", "rsa_padding_mode:pss"); }; + subtest "pkeyutl -rev reverses the input buffer" => sub { + plan tests => 4; + + my $key = srctop_file("test", "testrsa.pem"); + my $in = "rev_in.bin"; + my $in_rev = "rev_in_reversed.bin"; + + # A non-palindromic input, short enough to be signed as a raw digest. + my $data = "0123456789abcdefghijklmnopqrstuv"; + open(my $fh, '>:raw', $in) or die "cannot create $in: $!"; + print $fh $data; + close($fh); + open($fh, '>:raw', $in_rev) or die "cannot create $in_rev: $!"; + print $fh scalar reverse $data; + close($fh); + + # RSA signing is deterministic, so signing with -rev must match signing + # the manually reversed input. + ok(run(app(['openssl', 'pkeyutl', '-sign', '-inkey', $key, + '-rev', '-in', $in, '-out', 'rev.sig'])), + "Sign with -rev"); + ok(run(app(['openssl', 'pkeyutl', '-sign', '-inkey', $key, + '-in', $in_rev, '-out', 'rev_manual.sig'])), + "Sign the manually reversed input"); + is(compare('rev.sig', 'rev_manual.sig'), 0, + "-rev signature matches signing the reversed input"); + + # -rev is rejected together with raw input. + with({ exit_checker => sub { return shift == 1; } }, + sub { + ok(run(app(['openssl', 'pkeyutl', '-sign', '-inkey', $key, + '-rawin', '-digest', 'sha256', '-rev', '-in', $in])), + "-rev cannot be used with -rawin"); + }); + }; + } SKIP: { From 28071a8c0112c0bebfaa5eb73ddf051d94d9e3b4 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 9 Jul 2026 09:27:35 +0200 Subject: [PATCH 251/349] apps: cover the req -pkeyopt option The -pkeyopt option of the req app was previously untested. It adds a subtest that generates an EC request with -pkeyopt ec_paramgen_curve:P-384 and verifies the selected curve is used, and that an unknown -pkeyopt value makes the command fail. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz MergeDate: Mon Jul 13 15:36:42 2026 (Merged from https://github.com/openssl/openssl/pull/31905) --- test/recipes/25-test_req.t | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/test/recipes/25-test_req.t b/test/recipes/25-test_req.t index b23e41ee2c..a37686736f 100644 --- a/test/recipes/25-test_req.t +++ b/test/recipes/25-test_req.t @@ -15,7 +15,7 @@ use OpenSSL::Test qw/:DEFAULT srctop_file/; setup("test_req"); -plan tests => 130; +plan tests => 131; require_ok(srctop_file('test', 'recipes', 'tconversion.pl')); @@ -522,6 +522,34 @@ subtest "generating certificate with -set_serial" => sub { "Supplying -set_serial twice fails"); }; +subtest "generating certificate requests with -pkeyopt" => sub { + plan tests => 3; + + SKIP: { + skip "EC is not supported by this OpenSSL build", 3 if disabled("ec"); + + my $key = "testreq-pkeyopt-key.pem"; + my $req = "testreq-pkeyopt.pem"; + my $text = "testreq-pkeyopt.txt"; + + ok(run(app(["openssl", "req", "-new", + "-config", srctop_file("test", "test.cnf"), + "-newkey", "ec", "-pkeyopt", "ec_paramgen_curve:P-384", + "-nodes", "-keyout", $key, "-out", $req])), + "Generating request with -pkeyopt ec_paramgen_curve:P-384"); + + run(app(["openssl", "req", "-in", $req, "-noout", "-text", + "-out", $text])); + test_file_contains("request", $text, "ASN1 OID: secp384r1", 1); + + ok(!run(app(["openssl", "req", "-new", + "-config", srctop_file("test", "test.cnf"), + "-newkey", "ec", "-pkeyopt", "bogus_opt:1", + "-nodes", "-keyout", $key, "-out", $req])), + "Supplying an unknown -pkeyopt fails"); + } +}; + my @openssl_args = ("req", "-config", srctop_file("apps", "openssl.cnf")); run_conversion('req conversions', From 283f6bd6bff8aa0ca019573fdaea10d0bfea0067 Mon Sep 17 00:00:00 2001 From: Billy Brumley Date: Thu, 9 Jul 2026 05:33:01 -0400 Subject: [PATCH 252/349] [test] check late AAD rejection across AEADs A late AAD update (AAD supplied after the payload has started) must be rejected, and reported the same way, for every AEAD. #31673 checked this for ChaCha20-Poly1305 alone, so this change extends it to all AEADs. test_evp_aead_late_aad covers both the encrypt and decrypt directions and asserts ERR_LIB_PROV / PROV_R_UPDATE_CALL_OUT_OF_ORDER on the late update. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Daniel Kubec Reviewed-by: Paul Dale MergeDate: Mon Jul 13 15:40:39 2026 (Merged from https://github.com/openssl/openssl/pull/31906) --- .../ciphers/cipher_chacha20_poly1305_hw.c | 5 +- test/evp_extra_test.c | 154 +++++++++++++++--- 2 files changed, 133 insertions(+), 26 deletions(-) diff --git a/providers/implementations/ciphers/cipher_chacha20_poly1305_hw.c b/providers/implementations/ciphers/cipher_chacha20_poly1305_hw.c index ffd2ee744c..31a163e57e 100644 --- a/providers/implementations/ciphers/cipher_chacha20_poly1305_hw.c +++ b/providers/implementations/ciphers/cipher_chacha20_poly1305_hw.c @@ -9,6 +9,7 @@ /* chacha20_poly1305 cipher implementation */ +#include #include "internal/endian.h" #include "cipher_chacha20_poly1305.h" @@ -301,8 +302,10 @@ static int chacha20_poly1305_aead_cipher(PROV_CIPHER_CTX *bctx, if (in != NULL) { /* aad or text */ if (out == NULL) { /* aad */ - if (ctx->len.text != 0) + if (ctx->len.text != 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_UPDATE_CALL_OUT_OF_ORDER); goto err; + } Poly1305_Update(poly, in, inl); ctx->len.aad += inl; ctx->aad = 1; diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c index c767114f39..f1de702ec7 100644 --- a/test/evp_extra_test.c +++ b/test/evp_extra_test.c @@ -6054,6 +6054,134 @@ err: return testresult; } +/* + * With AEAD ciphers, associated data must precede the payload. Once plaintext + * or ciphertext processing has begun, a further AAD update (out == NULL) must + * be rejected, and the rejection must be reported the same way across every + * AEAD: ERR_LIB_PROV / PROV_R_UPDATE_CALL_OUT_OF_ORDER. The invariant is + * checked in both the encrypt and decrypt directions. + */ +static int test_evp_aead_late_aad(int idx) +{ + const EVP_CIPHER_TEST_INFO *info = &cipher_list[idx]; + EVP_CIPHER_CTX *ctx_enc = NULL; /* late AAD after plaintext: must fail */ + EVP_CIPHER_CTX *ctx_dec = NULL; /* late AAD after ciphertext: must fail */ + + unsigned char key[EVP_MAX_KEY_LENGTH] = { 0 }; + unsigned char iv[EVP_MAX_IV_LENGTH] = { 0 }; + unsigned char aad[] = "aad"; + unsigned char msg[] = "message"; + unsigned char out[sizeof(msg) + EVP_MAX_BLOCK_LENGTH]; + + int i = 0, len = 0, testresult = 0, expected = 0; + char *errmsg = NULL; + unsigned long err_code = 0; + + if (info->taglen == 0 /* skip non-AEAD */ + || info->mode == EVP_CIPH_GCM_MODE /* rejects, raises 102 PROV_R_CIPHER_OPERATION_FAILED */ + || info->mode == EVP_CIPH_CCM_MODE /* fails at first AAD */ + || info->mode == EVP_CIPH_OCB_MODE /* accepts late AAD */ + || info->mode == EVP_CIPH_GCM_SIV_MODE /* accepts late AAD */ + /* skip TLS stitched MTE cipher */ + || EVP_CIPHER_is_a(info->ciph, "AES-128-CBC-HMAC-SHA1") + /* skip TLS stitched MTE cipher */ + || EVP_CIPHER_is_a(info->ciph, "AES-256-CBC-HMAC-SHA1") + /* skip TLS stitched MTE cipher */ + || EVP_CIPHER_is_a(info->ciph, "AES-128-CBC-HMAC-SHA256") + /* skip TLS stitched MTE cipher */ + || EVP_CIPHER_is_a(info->ciph, "AES-256-CBC-HMAC-SHA256")) + return 1; + + for (i = 0; i < info->keylen && i < (int)sizeof(key); i++) + key[i] = (unsigned char)(0xA0 + i); + for (i = 0; i < info->ivlen && i < (int)sizeof(iv); i++) + iv[i] = (unsigned char)(0xB0 + i); + + /* encrypt: aad, then plaintext, then a late aad update must be rejected */ + if (!TEST_ptr(ctx_enc = EVP_CIPHER_CTX_new())) { + errmsg = "ENC_ALLOC"; + goto err; + } + if (!TEST_true(EVP_EncryptInit_ex2(ctx_enc, info->ciph, key, iv, NULL))) { + errmsg = "ENC_INIT"; + goto err; + } + if (!TEST_true(EVP_EncryptUpdate(ctx_enc, NULL, &len, aad, sizeof(aad)))) { + errmsg = "ENC_AAD"; + goto err; + } + if (!TEST_true(EVP_EncryptUpdate(ctx_enc, out, &len, msg, sizeof(msg)))) { + errmsg = "ENC_PLAINTEXT"; + goto err; + } + ERR_set_mark(); + if (!TEST_false(EVP_EncryptUpdate(ctx_enc, NULL, &len, aad, sizeof(aad)))) { + ERR_clear_last_mark(); + errmsg = "ENC_LATE_AAD_NOT_REJECTED"; + goto err; + } + err_code = ERR_peek_last_error(); + if (!TEST_int_eq(ERR_GET_LIB(err_code), ERR_LIB_PROV) + || !TEST_int_eq(ERR_GET_REASON(err_code), PROV_R_UPDATE_CALL_OUT_OF_ORDER)) { + ERR_clear_last_mark(); + expected = PROV_R_UPDATE_CALL_OUT_OF_ORDER; + errmsg = "ENC_LATE_AAD_WRONG_REASON"; + goto err; + } + ERR_pop_to_mark(); + + /* decrypt: same sequence, late aad after ciphertext must be rejected */ + if (!TEST_ptr(ctx_dec = EVP_CIPHER_CTX_new())) { + errmsg = "DEC_ALLOC"; + goto err; + } + if (!TEST_true(EVP_DecryptInit_ex2(ctx_dec, info->ciph, key, iv, NULL))) { + errmsg = "DEC_INIT"; + goto err; + } + if (!TEST_true(EVP_DecryptUpdate(ctx_dec, NULL, &len, aad, sizeof(aad)))) { + errmsg = "DEC_AAD"; + goto err; + } + /* the ciphertext content is irrelevant; the tag is never finalized here */ + if (!TEST_true(EVP_DecryptUpdate(ctx_dec, out, &len, msg, sizeof(msg)))) { + errmsg = "DEC_CIPHERTEXT"; + goto err; + } + ERR_set_mark(); + if (!TEST_false(EVP_DecryptUpdate(ctx_dec, NULL, &len, aad, sizeof(aad)))) { + ERR_clear_last_mark(); + errmsg = "DEC_LATE_AAD_NOT_REJECTED"; + goto err; + } + err_code = ERR_peek_last_error(); + if (!TEST_int_eq(ERR_GET_LIB(err_code), ERR_LIB_PROV) + || !TEST_int_eq(ERR_GET_REASON(err_code), PROV_R_UPDATE_CALL_OUT_OF_ORDER)) { + ERR_clear_last_mark(); + expected = PROV_R_UPDATE_CALL_OUT_OF_ORDER; + errmsg = "DEC_LATE_AAD_WRONG_REASON"; + goto err; + } + ERR_pop_to_mark(); + + testresult = 1; + +err: + if (errmsg != NULL) { + if (expected != 0) + TEST_info("test_evp_aead_late_aad %d, %s: %s" + " (expected reason %d, got %d)", + idx, errmsg, info->name, + expected, ERR_GET_REASON(err_code)); + else + TEST_info("test_evp_aead_late_aad %d, %s: %s", + idx, errmsg, info->name); + } + EVP_CIPHER_CTX_free(ctx_enc); + EVP_CIPHER_CTX_free(ctx_dec); + return testresult; +} + /* * Verify stale key is not being used after providing a new key in multiple steps. * This test performs a full round of encryption and then changes the @@ -7761,30 +7889,6 @@ err: return ret; } -#if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305) -static int test_chacha20_poly1305_late_aad(void) -{ - EVP_CIPHER_CTX *ctx = NULL; - EVP_CIPHER *c = NULL; - unsigned char key[32] = { 0 }; - unsigned char iv[12] = { 0 }; - unsigned char aad[4] = "aad"; - unsigned char msg[8] = "message"; - unsigned char out[32]; - int len, test; - - test = TEST_ptr(ctx = EVP_CIPHER_CTX_new()) - && TEST_ptr(c = EVP_CIPHER_fetch(testctx, "ChaCha20-Poly1305", testpropq)) - && TEST_true(EVP_EncryptInit_ex2(ctx, c, key, iv, NULL)) - && TEST_true(EVP_EncryptUpdate(ctx, NULL, &len, aad, sizeof(aad))) - && TEST_true(EVP_EncryptUpdate(ctx, out, &len, msg, sizeof(msg))) - && TEST_false(EVP_EncryptUpdate(ctx, NULL, &len, aad, sizeof(aad))); - - EVP_CIPHER_free(c); - EVP_CIPHER_CTX_free(ctx); - return test; -} -#endif /* * AES-SIV reuse-without-rekey: * msg1: legit non-empty CT, tag verifies, final_ret=0 @@ -9078,7 +9182,6 @@ int setup_tests(void) #endif #if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305) ADD_TEST(test_decrypt_null_chunks); - ADD_TEST(test_chacha20_poly1305_late_aad); #endif #ifndef OPENSSL_NO_DH ADD_TEST(test_DH_priv_pub); @@ -9122,6 +9225,7 @@ int setup_tests(void) ADD_ALL_TESTS(test_evp_decrypt_roundtrip_multistep, cipher_list_n); ADD_ALL_TESTS(test_evp_oneshot_aead_zerolen, cipher_list_n); ADD_ALL_TESTS(test_evp_aead_tag_direction, cipher_list_n); + ADD_ALL_TESTS(test_evp_aead_late_aad, cipher_list_n); ADD_ALL_TESTS(test_evp_init_seq, OSSL_NELEM(evp_init_tests)); ADD_ALL_TESTS(test_evp_reset, OSSL_NELEM(evp_reset_tests)); From d6c49df0396e9b49c4d2ddd8ae1c39b70d4995d1 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 9 Jul 2026 12:18:18 +0200 Subject: [PATCH 253/349] apps: cover the x509 -sigopt and -vfyopt options The -sigopt and -vfyopt options of the x509 app were previously untested. It adds a subtest that signs a certificate from a CSR with -sigopt rsa_padding_mode:pss and verifies the issued certificate uses the rsassaPss signature algorithm, and that verifies an SM2 CSR whose self-signature uses a non-default distinguishing id supplied via -vfyopt. It also checks that an unknown -sigopt or -vfyopt makes the command fail. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz MergeDate: Mon Jul 13 15:41:57 2026 (Merged from https://github.com/openssl/openssl/pull/31908) --- test/recipes/25-test_x509.t | 57 ++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/test/recipes/25-test_x509.t b/test/recipes/25-test_x509.t index de467569f2..736d185de4 100644 --- a/test/recipes/25-test_x509.t +++ b/test/recipes/25-test_x509.t @@ -17,7 +17,7 @@ use File::Compare qw/compare_text/; setup("test_x509"); -plan tests => 152; +plan tests => 153; # Prevent MSys2 filename munging for arguments that look like file paths but # aren't @@ -569,6 +569,61 @@ has_version($b_cert, 3); has_SKID($b_cert, 1); has_AKID($b_cert, 1); +subtest "signing with -sigopt and verifying a CSR with -vfyopt" => sub { + plan tests => 6; + + # -sigopt is passed to the signature algorithm; force RSA-PSS padding + # and check it ends up in the issued certificate. + my $pss_cert = "sigopt-pss.pem"; + ok(run(app(["openssl", "x509", "-req", "-CAcreateserial", + "-CA", $ca_cert, "-CAkey", $ca_key, + "-sigopt", "rsa_padding_mode:pss", + "-in", $b_csr, "-out", $pss_cert])), + "sign cert from CSR with -sigopt rsa_padding_mode:pss"); + cert_contains($pss_cert, "Signature Algorithm: rsassaPss", 1, + "issued cert is signed with PSS as selected via -sigopt"); + + # An unknown -sigopt must abort signing. + ok(!run(app(["openssl", "x509", "-req", "-CAcreateserial", + "-CA", $ca_cert, "-CAkey", $ca_key, + "-sigopt", "bogus:1", + "-in", $b_csr, "-out", "sigopt-bogus.pem"])), + "an unknown -sigopt makes signing fail"); + + # An unknown -vfyopt must abort CSR verification. + ok(!run(app(["openssl", "x509", "-req", "-CAcreateserial", + "-CA", $ca_cert, "-CAkey", $ca_key, + "-vfyopt", "bogus:1", + "-in", $b_csr, "-out", "vfyopt-bogus.pem"])), + "an unknown -vfyopt makes CSR verification fail"); + + SKIP: { + skip "SM2 is not supported by this OpenSSL build", 2 if disabled("sm2"); + + # -vfyopt is used to verify the CSR self-signature. Sign an SM2 CSR + # with a non-default distinguishing id so that the id must be supplied + # via -vfyopt for verification to succeed. + my $sm2_key = "sm2-vfyopt-key.pem"; + my $sm2_csr = "sm2-vfyopt.csr"; + my $distid = "0102030405060708"; + run(app(["openssl", "req", "-new", "-newkey", "sm2", + "-keyout", $sm2_key, "-out", $sm2_csr, "-nodes", + "-config", $cnf, "-subj", "/CN=SM2", + "-sigopt", "distid:$distid"])); + + ok(run(app(["openssl", "x509", "-req", "-CAcreateserial", + "-CA", $ca_cert, "-CAkey", $ca_key, + "-vfyopt", "distid:$distid", + "-in", $sm2_csr, "-out", "sm2-vfyopt.pem"])), + "SM2 CSR verifies when its distid is given via -vfyopt"); + + ok(!run(app(["openssl", "x509", "-req", "-CAcreateserial", + "-CA", $ca_cert, "-CAkey", $ca_key, + "-in", $sm2_csr, "-out", "sm2-novfyopt.pem"])), + "SM2 CSR fails to verify without the matching -vfyopt distid"); + } +}; + # Tests for https://github.com/openssl/openssl/issues/10442 (fixed in 1.1.1a) # (incorrect default `-CAcreateserial` if `-CA` path has a dot in it) my $folder_with_dot = "test_x509.folder"; From 95f95b59dded1ad376458152e4c8533018ee2f43 Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Wed, 3 Jun 2026 23:44:22 +0900 Subject: [PATCH 254/349] poly1305: reject no-key update and NULL key params Poly1305 permits EVP_MAC_init(ctx, NULL, 0, ...) as part of staged initialization. If no key has been installed, update still dispatched into the uninitialized Poly1305 state, which can crash on POLY1305_ASM builds. Guard update with the same key_set check used by final and report no key set. Also reject an explicit OSSL_MAC_PARAM_KEY whose data pointer is NULL before calling Poly1305_Init(), even when the supplied size is POLY1305_KEY_SIZE. Fixes #31332 Reviewed-by: Dmitry Belyavskiy Reviewed-by: Daniel Kubec MergeDate: Mon Jul 13 15:44:48 2026 (Merged from https://github.com/openssl/openssl/pull/31382) --- .../implementations/macs/poly1305_prov.c | 6 +- test/evp_extra_test.c | 69 ++++++++++++++++++- 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/providers/implementations/macs/poly1305_prov.c b/providers/implementations/macs/poly1305_prov.c index 8d6f9c952e..a9ca6e4f68 100644 --- a/providers/implementations/macs/poly1305_prov.c +++ b/providers/implementations/macs/poly1305_prov.c @@ -86,7 +86,7 @@ static size_t poly1305_size(void) static int poly1305_setkey(struct poly1305_data_st *ctx, const unsigned char *key, size_t keylen) { - if (keylen != POLY1305_KEY_SIZE) { + if (key == NULL || keylen != POLY1305_KEY_SIZE) { ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); return 0; } @@ -115,6 +115,10 @@ static int poly1305_update(void *vmacctx, const unsigned char *data, { struct poly1305_data_st *ctx = vmacctx; + if (!ctx->key_set) { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return 0; + } ctx->updated = 1; if (datalen == 0) return 1; diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c index f1de702ec7..5c37102088 100644 --- a/test/evp_extra_test.c +++ b/test/evp_extra_test.c @@ -2323,20 +2323,83 @@ out: } #ifndef OPENSSL_NO_POLY1305 -/* Test that EVP_MAC_final fails for Poly1305 when no key was set */ +/* Test Poly1305 no-key failures and staged key initialization */ static int test_evp_mac_poly1305_no_key(void) { int ret = 0; EVP_MAC *mac = NULL; EVP_MAC_CTX *ctx = NULL; + /* RFC 7539 Poly1305 test vector. */ + static const unsigned char staged_data[] = "Cryptographic Forum Research Group"; + static const unsigned char expected[16] = { + 0xa8, 0x06, 0x1d, 0xc1, 0x30, 0x51, 0x36, 0xc6, + 0xc2, 0x2b, 0x8b, 0xaf, 0x0c, 0x01, 0x27, 0xa9 + }; + unsigned char no_key_data[16] = { 0 }; + unsigned char key[32] = { + 0x85, 0xd6, 0xbe, 0x78, 0x57, 0x55, 0x6d, 0x33, + 0x7f, 0x44, 0x52, 0xfe, 0x42, 0xd5, 0x06, 0xa8, + 0x01, 0x03, 0x80, 0x8a, 0xfb, 0x0d, 0xb2, 0xfd, + 0x4a, 0xbf, 0xf6, 0xaf, 0x41, 0x49, 0xf5, 0x1b + }; unsigned char out[16]; + OSSL_PARAM key_params[2]; + OSSL_PARAM null_key_params[2]; size_t outl = 0; + key_params[0] = OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_KEY, + key, sizeof(key)); + key_params[1] = OSSL_PARAM_construct_end(); + null_key_params[0] = OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_KEY, + NULL, sizeof(key)); + null_key_params[1] = OSSL_PARAM_construct_end(); + if (!TEST_ptr(mac = EVP_MAC_fetch(testctx, "Poly1305", testpropq)) || !TEST_ptr(ctx = EVP_MAC_CTX_new(mac)) - || !TEST_int_eq(EVP_MAC_init(ctx, NULL, 0, NULL), 1) - || !TEST_int_eq(EVP_MAC_final(ctx, out, &outl, sizeof(out)), 0)) + || !TEST_int_eq(EVP_MAC_init(ctx, NULL, 0, NULL), 1)) goto err; + + ERR_clear_error(); + if (!TEST_int_eq(EVP_MAC_update(ctx, no_key_data, sizeof(no_key_data)), 0) + || !TEST_int_eq(ERR_GET_REASON(ERR_get_error()), PROV_R_NO_KEY_SET)) + goto err; + + /* The failed update must not block staged key initialization. */ + if (!TEST_int_eq(EVP_MAC_CTX_set_params(ctx, key_params), 1) + || !TEST_int_eq(EVP_MAC_update(ctx, staged_data, + sizeof(staged_data) - 1), + 1) + || !TEST_int_eq(EVP_MAC_final(ctx, out, &outl, sizeof(out)), 1) + || !TEST_size_t_eq(outl, sizeof(expected)) + || !TEST_mem_eq(out, outl, expected, sizeof(expected))) + goto err; + + EVP_MAC_CTX_free(ctx); + ctx = NULL; + + if (!TEST_ptr(ctx = EVP_MAC_CTX_new(mac)) + || !TEST_int_eq(EVP_MAC_init(ctx, NULL, 0, NULL), 1)) + goto err; + + ERR_clear_error(); + if (!TEST_int_eq(EVP_MAC_final(ctx, out, &outl, sizeof(out)), 0) + || !TEST_int_eq(ERR_GET_REASON(ERR_get_error()), PROV_R_NO_KEY_SET)) + goto err; + + ERR_clear_error(); + if (!TEST_int_eq(EVP_MAC_init(ctx, NULL, 0, null_key_params), 0) + || !TEST_int_eq(ERR_GET_REASON(ERR_get_error()), + PROV_R_INVALID_KEY_LENGTH)) + goto err; + + ERR_clear_error(); + if (!TEST_int_eq(EVP_MAC_CTX_set_params(ctx, null_key_params), 0) + || !TEST_int_eq(ERR_GET_REASON(ERR_get_error()), + PROV_R_INVALID_KEY_LENGTH)) + goto err; + + EVP_MAC_CTX_free(ctx); + ctx = NULL; ret = 1; err: EVP_MAC_CTX_free(ctx); From 6736bd2cb606aeaf8f0960816152cb27fa710258 Mon Sep 17 00:00:00 2001 From: "knut st. osmundsen" Date: Tue, 9 Jun 2026 08:49:39 +0200 Subject: [PATCH 255/349] Fix nasm version check for sm3 & sm4 perlasm files Make the check correctly handle versions such as '3.00rc8'. It was incorrectly expecting major.minor.patch. Reviewed-by: Norbert Pocs Reviewed-by: Tom Cosgrove MergeDate: Mon Jul 13 15:48:03 2026 (Merged from https://github.com/openssl/openssl/pull/31420) --- crypto/sm3/asm/sm3-x86_64.pl | 4 ++-- crypto/sm4/asm/sm4-x86_64.pl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crypto/sm3/asm/sm3-x86_64.pl b/crypto/sm3/asm/sm3-x86_64.pl index d3c9d0541a..2f6ddf5616 100755 --- a/crypto/sm3/asm/sm3-x86_64.pl +++ b/crypto/sm3/asm/sm3-x86_64.pl @@ -35,8 +35,8 @@ if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1` } if (!$avx2_sm3_ni && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) && - `nasm -v 2>&1` =~ /NASM version ([2-9])\.([0-9]+)\.([0-9]+)/) { - my ($major, $minor, $patch) = ($1, $2, $3); + `nasm -v 2>&1` =~ /NASM version ([2-9])\.([0-9]+)(?:\.([0-9]+))?/) { + my ($major, $minor, $patch) = ($1, $2, defined($3) ? $3 : 0); $avx2_sm3_ni = ($major > 2) || ($major == 2 && $minor > 10); # minimal avx2 supported version, binary translation for SM3 instructions (sub sm3op) is used $avx2_sm3_ni_native = ($major > 2) || ($major == 2 && $minor > 16) || ($major == 2 && $minor == 16 && $patch >= 2); # support added at NASM 2.16.02 } diff --git a/crypto/sm4/asm/sm4-x86_64.pl b/crypto/sm4/asm/sm4-x86_64.pl index 9fc40fb96a..f5b485968e 100644 --- a/crypto/sm4/asm/sm4-x86_64.pl +++ b/crypto/sm4/asm/sm4-x86_64.pl @@ -35,8 +35,8 @@ if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1` } if (!$avx2_sm4_ni && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) && - `nasm -v 2>&1` =~ /NASM version ([2-9])\.([0-9]+)\.([0-9]+)/) { - my ($major, $minor, $patch) = ($1, $2, $3); + `nasm -v 2>&1` =~ /NASM version ([2-9])\.([0-9]+)(?:\.([0-9]+))?/) { + my ($major, $minor, $patch) = ($1, $2, defined($3) ? $3 : 0); $avx2_sm4_ni = ($major > 2) || ($major == 2 && $minor > 10); # minimal avx2 supported version, binary translation for SM4 instructions (sub sm4op) is used $avx2_sm4_ni_native = ($major > 2) || ($major == 2 && $minor > 16) || ($major == 2 && $minor == 16 && $patch >= 2); # support added at NASM 2.16.02 } From a248ec771e8d111db6f69a29cab50cff7610f70b Mon Sep 17 00:00:00 2001 From: Marcel Cornu Date: Fri, 10 Apr 2026 09:36:25 +0000 Subject: [PATCH 256/349] ML-DSA: Add AVX512VL SHAKE x4 multi-buffer integration Changes: - Adds new SHAKE x4 API to perform 4 SHAKE operations in parallel when AVX512VL is supported. - Adds AVX512VL Keccak x4 assembly module (keccak1600x4-avx512vl). - Adds internal SHA3 x4 APIs/context in sha3.h and wrappers in sha3_x4.c modules. - Adds runtime dispatch for ML-DSA sample operations with an OSSL_ML_DSA_SAMPLE_OPS vtable. Callers obtain the correct implementation via ossl_ml_dsa_sample_ops(), which returns either the generic scalar ops functions, or the AVX512VL multi-buffer ops depending on the build and CPU capabilities. - Adds x86-64 multi-buffer function implementation into ml_dsa_sample_hw_x86_64.inc, included in ml_dsa_sample.c when KECCAK1600_ASM and x86_64 are defined. Co-authored-by: Tomasz Kantecki Signed-off-by: Marcel Cornu Reviewed-by: Viktor Dukhovni Reviewed-by: Neil Horman Reviewed-by: Shane Lontis MergeDate: Mon Jul 13 15:55:54 2026 (Merged from https://github.com/openssl/openssl/pull/31090) --- CHANGES.md | 4 + crypto/ml_dsa/ml_dsa_key.c | 16 +- crypto/ml_dsa/ml_dsa_local.h | 17 +- crypto/ml_dsa/ml_dsa_matrix.h | 7 - crypto/ml_dsa/ml_dsa_sample.c | 80 +- crypto/ml_dsa/ml_dsa_sample_hw_x86_64.inc | 307 +++ crypto/ml_dsa/ml_dsa_sign.c | 10 +- crypto/ml_dsa/ml_dsa_vector.h | 27 - crypto/sha/asm/keccak1600x4-avx512vl.pl | 2344 +++++++++++++++++++++ crypto/sha/build.info | 6 +- crypto/sha/sha3_x4_avx512vl.c | 213 ++ include/internal/sha3.h | 71 + providers/fips-sources.checksums | 5 +- providers/fips.checksum | 2 +- providers/fips.module.sources | 3 + 15 files changed, 3051 insertions(+), 61 deletions(-) create mode 100644 crypto/ml_dsa/ml_dsa_sample_hw_x86_64.inc create mode 100755 crypto/sha/asm/keccak1600x4-avx512vl.pl create mode 100644 crypto/sha/sha3_x4_avx512vl.c diff --git a/CHANGES.md b/CHANGES.md index 6d4af0b857..da1bfd15ec 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -31,6 +31,10 @@ OpenSSL Releases ### Changes between 4.0 and 4.1 [xx XXX xxxx] + * Added AVX512 optimized SHAKE x4 operations for ML-DSA on x86_64. + + *Marcel Cornu and Tomasz Kantecki* + * EC key point format simplification. The point conversion form (compressed, uncompressed, or hybrid) diff --git a/crypto/ml_dsa/ml_dsa_key.c b/crypto/ml_dsa/ml_dsa_key.c index 6b5b8b092f..ea5f4ee4da 100644 --- a/crypto/ml_dsa/ml_dsa_key.c +++ b/crypto/ml_dsa/ml_dsa_key.c @@ -332,7 +332,7 @@ int ossl_ml_dsa_key_has(const ML_DSA_KEY *key, int selection) * @returns 1 on success, or 0 on failure. */ static int public_from_private(const ML_DSA_KEY *key, EVP_MD_CTX *md_ctx, - VECTOR *t1, VECTOR *t0) + const OSSL_ML_DSA_SAMPLE_OPS *sample_ops, VECTOR *t1, VECTOR *t0) { int ret = 0; const ML_DSA_PARAMS *params = key->params; @@ -351,7 +351,7 @@ static int public_from_private(const ML_DSA_KEY *key, EVP_MD_CTX *md_ctx, matrix_init(&a_ntt, s1_ntt.poly + l, k, l); /* Using rho generate A' = A in NTT form */ - if (!matrix_expand_A(md_ctx, key->shake128_md, key->rho, &a_ntt)) + if (!sample_ops->matrix_expand_A(md_ctx, key->shake128_md, key->rho, &a_ntt)) goto err; /* t = NTT_inv(A' * NTT(s1)) + s2 */ @@ -376,6 +376,7 @@ err: int ossl_ml_dsa_key_public_from_private(ML_DSA_KEY *key) { int ret = 0; + const OSSL_ML_DSA_SAMPLE_OPS *sample_ops = ossl_ml_dsa_sample_ops(); VECTOR t0; EVP_MD_CTX *md_ctx = NULL; @@ -383,7 +384,7 @@ int ossl_ml_dsa_key_public_from_private(ML_DSA_KEY *key) return 0; ret = ((md_ctx = EVP_MD_CTX_new()) != NULL) && ossl_ml_dsa_key_pub_alloc(key) /* allocate space for t1 */ - && public_from_private(key, md_ctx, &key->t1, &t0) + && public_from_private(key, md_ctx, sample_ops, &key->t1, &t0) && vector_equal(&t0, &key->t0) /* compare the generated t0 to the expected */ && ossl_ml_dsa_pk_encode(key) && shake_xof(md_ctx, key->shake256_md, @@ -397,6 +398,7 @@ int ossl_ml_dsa_key_public_from_private(ML_DSA_KEY *key) int ossl_ml_dsa_key_pairwise_check(const ML_DSA_KEY *key) { int ret = 0; + const OSSL_ML_DSA_SAMPLE_OPS *sample_ops = ossl_ml_dsa_sample_ops(); VECTOR t1, t0; POLY *polys = NULL; uint32_t k = (uint32_t)key->params->k; @@ -414,7 +416,7 @@ int ossl_ml_dsa_key_pairwise_check(const ML_DSA_KEY *key) vector_init(&t1, polys, k); vector_init(&t0, polys + k, k); - if (!public_from_private(key, md_ctx, &t1, &t0)) + if (!public_from_private(key, md_ctx, sample_ops, &t1, &t0)) goto err; ret = vector_equal(&t1, &key->t1) && vector_equal(&t0, &key->t0); @@ -435,6 +437,7 @@ err: static int keygen_internal(ML_DSA_KEY *out) { int ret = 0; + const OSSL_ML_DSA_SAMPLE_OPS *sample_ops = ossl_ml_dsa_sample_ops(); uint8_t augmented_seed[ML_DSA_SEED_BYTES + 2]; uint8_t expanded_seed[ML_DSA_RHO_BYTES + ML_DSA_PRIV_SEED_BYTES + ML_DSA_K_BYTES]; const uint8_t *const rho = expanded_seed; /* p = Public Random Seed */ @@ -461,8 +464,9 @@ static int keygen_internal(ML_DSA_KEY *out) memcpy(out->rho, rho, sizeof(out->rho)); memcpy(out->K, K, sizeof(out->K)); - ret = vector_expand_S(md_ctx, out->shake256_md, params->eta, priv_seed, &out->s1, &out->s2) - && public_from_private(out, md_ctx, &out->t1, &out->t0) + ret = sample_ops->vector_expand_S(md_ctx, out->shake256_md, params->eta, + priv_seed, &out->s1, &out->s2) + && public_from_private(out, md_ctx, sample_ops, &out->t1, &out->t0) && ossl_ml_dsa_pk_encode(out) && shake_xof(md_ctx, out->shake256_md, out->pub_encoding, out->params->pk_len, out->tr, sizeof(out->tr)) diff --git a/crypto/ml_dsa/ml_dsa_local.h b/crypto/ml_dsa/ml_dsa_local.h index 9d01856ce3..23e2db247c 100644 --- a/crypto/ml_dsa/ml_dsa_local.h +++ b/crypto/ml_dsa/ml_dsa_local.h @@ -59,10 +59,21 @@ typedef struct vector_st VECTOR; typedef struct matrix_st MATRIX; typedef struct ml_dsa_sig_st ML_DSA_SIG; -int ossl_ml_dsa_matrix_expand_A(EVP_MD_CTX *g_ctx, const EVP_MD *md, +typedef int(ML_DSA_MATRIX_EXPAND_A_FN)(EVP_MD_CTX *g_ctx, const EVP_MD *md, const uint8_t *rho, MATRIX *out); -int ossl_ml_dsa_vector_expand_S(EVP_MD_CTX *h_ctx, const EVP_MD *md, int eta, - const uint8_t *seed, VECTOR *s1, VECTOR *s2); +typedef int(ML_DSA_VECTOR_EXPAND_S_FN)(EVP_MD_CTX *h_ctx, const EVP_MD *md, + int eta, const uint8_t *seed, VECTOR *s1, VECTOR *s2); +typedef void(ML_DSA_VECTOR_EXPAND_MASK_FN)(VECTOR *out, + const uint8_t rho_prime[ML_DSA_RHO_PRIME_BYTES], uint32_t kappa, uint32_t gamma1, + EVP_MD_CTX *h_ctx, const EVP_MD *md); + +typedef struct ossl_ml_dsa_sample_ops_st { + ML_DSA_MATRIX_EXPAND_A_FN *matrix_expand_A; + ML_DSA_VECTOR_EXPAND_S_FN *vector_expand_S; + ML_DSA_VECTOR_EXPAND_MASK_FN *vector_expand_mask; +} OSSL_ML_DSA_SAMPLE_OPS; + +const OSSL_ML_DSA_SAMPLE_OPS *ossl_ml_dsa_sample_ops(void); void ossl_ml_dsa_matrix_mult_vector(const MATRIX *matrix_kl, const VECTOR *vl, VECTOR *vk); int ossl_ml_dsa_poly_expand_mask(POLY *out, const uint8_t *seed, size_t seed_len, diff --git a/crypto/ml_dsa/ml_dsa_matrix.h b/crypto/ml_dsa/ml_dsa_matrix.h index 3bc053720b..e5f4ebf6d9 100644 --- a/crypto/ml_dsa/ml_dsa_matrix.h +++ b/crypto/ml_dsa/ml_dsa_matrix.h @@ -41,11 +41,4 @@ matrix_mult_vector(const MATRIX *a, const VECTOR *s, VECTOR *t) ossl_ml_dsa_matrix_mult_vector(a, s, t); } -static ossl_inline ossl_unused int -matrix_expand_A(EVP_MD_CTX *g_ctx, const EVP_MD *md, const uint8_t *rho, - MATRIX *out) -{ - return ossl_ml_dsa_matrix_expand_A(g_ctx, md, rho, out); -} - #endif /* !defined(OSSL_LIBCRYPTO_ML_DSA_ML_DSA_MATRIX_H) */ diff --git a/crypto/ml_dsa/ml_dsa_sample.c b/crypto/ml_dsa/ml_dsa_sample.c index 5d9dc84a54..afa09b7971 100644 --- a/crypto/ml_dsa/ml_dsa_sample.c +++ b/crypto/ml_dsa/ml_dsa_sample.c @@ -8,6 +8,7 @@ */ #include +#include #include "ml_dsa_local.h" #include "ml_dsa_vector.h" #include "ml_dsa_matrix.h" @@ -35,6 +36,10 @@ typedef int(COEFF_FROM_NIBBLE_FUNC)(uint32_t nibble, uint32_t *out); static COEFF_FROM_NIBBLE_FUNC coeff_from_nibble_4; static COEFF_FROM_NIBBLE_FUNC coeff_from_nibble_2; +static ML_DSA_MATRIX_EXPAND_A_FN matrix_expand_A_scalar; +static ML_DSA_VECTOR_EXPAND_S_FN vector_expand_S_scalar; +static ML_DSA_VECTOR_EXPAND_MASK_FN vector_expand_mask_scalar; + /** * @brief Combine 3 bytes to form an coefficient. * See FIPS 204, Algorithm 14, CoeffFromThreeBytes() @@ -160,13 +165,14 @@ static int rej_bounded_poly(EVP_MD_CTX *h_ctx, const EVP_MD *md, COEFF_FROM_NIBBLE_FUNC *coef_from_nibble, const uint8_t *seed, size_t seed_len, POLY *out) { + int ret = 0; int j = 0; uint32_t z0, z1; uint8_t blocks[SHAKE256_BLOCKSIZE], *b, *end = blocks + sizeof(blocks); /* Instead of just squeezing 1 byte at a time, we grab a whole block */ if (!shake_xof(h_ctx, md, seed, seed_len, blocks, sizeof(blocks))) - return 0; + goto err; while (1) { for (b = blocks; b < end; b++) { @@ -174,15 +180,22 @@ static int rej_bounded_poly(EVP_MD_CTX *h_ctx, const EVP_MD *md, z1 = *b >> 4; /* high nibble of byte */ if (coef_from_nibble(z0, &out->coeff[j]) - && ++j >= ML_DSA_NUM_POLY_COEFFICIENTS) - return 1; + && ++j >= ML_DSA_NUM_POLY_COEFFICIENTS) { + ret = 1; + goto err; + } if (coef_from_nibble(z1, &out->coeff[j]) - && ++j >= ML_DSA_NUM_POLY_COEFFICIENTS) - return 1; + && ++j >= ML_DSA_NUM_POLY_COEFFICIENTS) { + ret = 1; + goto err; + } } if (!EVP_DigestSqueeze(h_ctx, blocks, sizeof(blocks))) - return 0; + goto err; } +err: + OPENSSL_cleanse(blocks, sizeof(blocks)); + return ret; } /** @@ -198,7 +211,7 @@ static int rej_bounded_poly(EVP_MD_CTX *h_ctx, const EVP_MD *md, * in the range of 0..q-1. * @returns 1 if the matrix was generated, or 0 on error. */ -int ossl_ml_dsa_matrix_expand_A(EVP_MD_CTX *g_ctx, const EVP_MD *md, +static int matrix_expand_A_scalar(EVP_MD_CTX *g_ctx, const EVP_MD *md, const uint8_t *rho, MATRIX *out) { int ret = 0; @@ -208,7 +221,6 @@ int ossl_ml_dsa_matrix_expand_A(EVP_MD_CTX *g_ctx, const EVP_MD *md, /* The seed used for each matrix element is rho + column_index + row_index */ memcpy(derived_seed, rho, ML_DSA_RHO_BYTES); - for (i = 0; i < out->k; i++) { for (j = 0; j < out->l; j++) { derived_seed[ML_DSA_RHO_BYTES + 1] = (uint8_t)i; @@ -241,7 +253,7 @@ err: * the range (q-eta)..0..eta * @returns 1 if s1 and s2 were successfully generated, or 0 otherwise. */ -int ossl_ml_dsa_vector_expand_S(EVP_MD_CTX *h_ctx, const EVP_MD *md, int eta, +static int vector_expand_S_scalar(EVP_MD_CTX *h_ctx, const EVP_MD *md, int eta, const uint8_t *seed, VECTOR *s1, VECTOR *s2) { int ret = 0; @@ -275,6 +287,7 @@ int ossl_ml_dsa_vector_expand_S(EVP_MD_CTX *h_ctx, const EVP_MD *md, int eta, } ret = 1; err: + OPENSSL_cleanse(derived_seed, sizeof(derived_seed)); return ret; } @@ -285,9 +298,11 @@ int ossl_ml_dsa_poly_expand_mask(POLY *out, const uint8_t *seed, size_t seed_len { uint8_t buf[32 * 20]; size_t buf_len = 32 * (gamma1 == ML_DSA_GAMMA1_TWO_POWER_19 ? 20 : 18); - - return shake_xof(h_ctx, md, seed, seed_len, buf, buf_len) + int ret = shake_xof(h_ctx, md, seed, seed_len, buf, buf_len) && ossl_ml_dsa_poly_decode_expand_mask(out, buf, buf_len, gamma1); + + OPENSSL_cleanse(buf, sizeof(buf)); + return ret; } /* @@ -376,3 +391,46 @@ int ossl_ml_dsa_poly_sample_in_ball(POLY *out_c, const uint8_t *seed, int seed_l } return 1; } + +static void vector_expand_mask_scalar(VECTOR *out, + const uint8_t rho_prime[ML_DSA_RHO_PRIME_BYTES], uint32_t kappa, uint32_t gamma1, + EVP_MD_CTX *h_ctx, const EVP_MD *md) +{ + size_t i; + uint8_t derived_seed[ML_DSA_RHO_PRIME_BYTES + 2]; + + memcpy(derived_seed, rho_prime, ML_DSA_RHO_PRIME_BYTES); + + for (i = 0; i < out->num_poly; i++) { + size_t index = kappa + i; + + derived_seed[ML_DSA_RHO_PRIME_BYTES] = index & 0xFF; + derived_seed[ML_DSA_RHO_PRIME_BYTES + 1] = (index >> 8) & 0xFF; + poly_expand_mask(out->poly + i, derived_seed, sizeof(derived_seed), + gamma1, h_ctx, md); + } + OPENSSL_cleanse(derived_seed, sizeof(derived_seed)); +} + +static const OSSL_ML_DSA_SAMPLE_OPS ml_dsa_sample_generic_meth = { + matrix_expand_A_scalar, + vector_expand_S_scalar, + vector_expand_mask_scalar +}; + +#if defined(KECCAK1600_ASM) \ + && (defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_X64)) \ + && !defined(OPENSSL_NO_ASM) +#include "ml_dsa_sample_hw_x86_64.inc" +const OSSL_ML_DSA_SAMPLE_OPS *ossl_ml_dsa_sample_ops(void) +{ + if (SHA3_avx512vl_capable()) + return &ml_dsa_sample_x86_64; + return &ml_dsa_sample_generic_meth; +} +#else +const OSSL_ML_DSA_SAMPLE_OPS *ossl_ml_dsa_sample_ops(void) +{ + return &ml_dsa_sample_generic_meth; +} +#endif diff --git a/crypto/ml_dsa/ml_dsa_sample_hw_x86_64.inc b/crypto/ml_dsa/ml_dsa_sample_hw_x86_64.inc new file mode 100644 index 0000000000..fcf5f03323 --- /dev/null +++ b/crypto/ml_dsa/ml_dsa_sample_hw_x86_64.inc @@ -0,0 +1,307 @@ +/* + * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. + * Copyright (c) 2026 Intel Corporation. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#define ML_DSA_SHAKE_X4_BATCH_SIZE 4 +#define ML_DSA_SHAKE_X4_DONE_MASK ((1 << ML_DSA_SHAKE_X4_BATCH_SIZE) - 1) +#define ML_DSA_EXPAND_MASK_BYTES_PER_COEFF 32 +#define ML_DSA_EXPAND_MASK_COEFFS_GAMMA1_19 20 +#define ML_DSA_EXPAND_MASK_COEFFS_GAMMA1_17 18 +#define ML_DSA_EXPAND_MASK_BUF_SIZE_GAMMA1_19 \ + (ML_DSA_EXPAND_MASK_BYTES_PER_COEFF * ML_DSA_EXPAND_MASK_COEFFS_GAMMA1_19) +#define ML_DSA_EXPAND_MASK_BUF_SIZE_GAMMA1_17 \ + (ML_DSA_EXPAND_MASK_BYTES_PER_COEFF * ML_DSA_EXPAND_MASK_COEFFS_GAMMA1_17) +#define ML_DSA_EXPAND_MASK_BUF_SIZE(gamma1) \ + ((gamma1) == ML_DSA_GAMMA1_TWO_POWER_19 \ + ? ML_DSA_EXPAND_MASK_BUF_SIZE_GAMMA1_19 \ + : ML_DSA_EXPAND_MASK_BUF_SIZE_GAMMA1_17) + +static ossl_unused int rej_ntt_poly_mb(const uint8_t *seeds[ML_DSA_SHAKE_X4_BATCH_SIZE], + const size_t seed_len, POLY *outs[ML_DSA_SHAKE_X4_BATCH_SIZE], const size_t count) +{ + KECCAK1600_X4_AVX512VL_CTX ctx; + uint8_t blocks[ML_DSA_SHAKE_X4_BATCH_SIZE][SHAKE128_BLOCKSIZE]; + int coeff_idx[ML_DSA_SHAKE_X4_BATCH_SIZE] = { 0, 0, 0, 0 }; + size_t done_mask = 0; + size_t lane; + + for (lane = count; lane < ML_DSA_SHAKE_X4_BATCH_SIZE; lane++) + done_mask |= ((size_t)1 << lane); + + ossl_sha3_shake128_x4_inc_init_avx512vl(&ctx); + ossl_sha3_shake128_x4_inc_absorb_avx512vl(&ctx, seeds[0], seeds[1], + seeds[2], seeds[3], seed_len); + + while (done_mask != ML_DSA_SHAKE_X4_DONE_MASK) { + ossl_sha3_shake128_x4_inc_squeeze_avx512vl(blocks[0], blocks[1], + blocks[2], blocks[3], SHAKE128_BLOCKSIZE, &ctx); + + for (lane = 0; lane < ML_DSA_SHAKE_X4_BATCH_SIZE; lane++) { + if (done_mask & ((size_t)1 << lane)) + continue; + + const uint8_t *b = blocks[lane]; + const uint8_t *end = b + SHAKE128_BLOCKSIZE; + + for (; b < end && coeff_idx[lane] < ML_DSA_NUM_POLY_COEFFICIENTS; b += 3) { + uint32_t *coeff_ptr = &(outs[lane]->coeff[coeff_idx[lane]]); + + if (coeff_from_three_bytes(b, coeff_ptr)) + coeff_idx[lane]++; + } + + if (coeff_idx[lane] >= ML_DSA_NUM_POLY_COEFFICIENTS) + done_mask |= ((size_t)1 << lane); + } + } + + return 1; +} + +static void vector_expand_mask_mb(VECTOR *out, + const uint8_t rho_prime[ML_DSA_RHO_PRIME_BYTES], const uint32_t kappa, const uint32_t gamma1, + EVP_MD_CTX *h_ctx, const EVP_MD *md) +{ + size_t i; + const size_t num_polys = out->num_poly; + uint8_t derived_seeds[ML_DSA_SHAKE_X4_BATCH_SIZE][ML_DSA_RHO_PRIME_BYTES + 2]; + const size_t seed_len = sizeof(derived_seeds[0]); + const size_t buf_size = ML_DSA_EXPAND_MASK_BUF_SIZE(gamma1); + uint8_t buffers[ML_DSA_SHAKE_X4_BATCH_SIZE][ML_DSA_EXPAND_MASK_BUF_SIZE_GAMMA1_19]; + + (void)h_ctx; + (void)md; + + for (i = 0; i < ML_DSA_SHAKE_X4_BATCH_SIZE; i++) + memcpy(derived_seeds[i], rho_prime, ML_DSA_RHO_PRIME_BYTES); + + for (i = 0; i + (ML_DSA_SHAKE_X4_BATCH_SIZE - 1) < num_polys; i += ML_DSA_SHAKE_X4_BATCH_SIZE) { + size_t b; + + for (b = 0; b < ML_DSA_SHAKE_X4_BATCH_SIZE; b++) { + const size_t index = kappa + i + b; + + derived_seeds[b][ML_DSA_RHO_PRIME_BYTES] = index & 0xFF; + derived_seeds[b][ML_DSA_RHO_PRIME_BYTES + 1] = (index >> 8) & 0xFF; + } + + ossl_sha3_shake256_x4_avx512vl(buffers[0], buffers[1], buffers[2], buffers[3], buf_size, + derived_seeds[0], derived_seeds[1], derived_seeds[2], derived_seeds[3], seed_len); + + ossl_ml_dsa_poly_decode_expand_mask(&out->poly[i + 0], buffers[0], buf_size, gamma1); + ossl_ml_dsa_poly_decode_expand_mask(&out->poly[i + 1], buffers[1], buf_size, gamma1); + ossl_ml_dsa_poly_decode_expand_mask(&out->poly[i + 2], buffers[2], buf_size, gamma1); + ossl_ml_dsa_poly_decode_expand_mask(&out->poly[i + 3], buffers[3], buf_size, gamma1); + } + + /* + * num_polys is always 4 (ML-DSA-44), 5 (ML-DSA-65), or 7 (ML-DSA-87), so the + * above loops will always runs at least once, initializing derived_seeds. + * As a result, 'left' below will be 0, 1, or 3, meaning the 4 way shake will + * recalculate values that are not used. + */ + if (i < num_polys) { + const size_t left = num_polys - i; + size_t b; + + for (b = 0; b < left; b++) { + const size_t index = kappa + i + b; + + derived_seeds[b][ML_DSA_RHO_PRIME_BYTES] = (uint8_t)index; + derived_seeds[b][ML_DSA_RHO_PRIME_BYTES + 1] = (uint8_t)(index >> 8); + } + + ossl_sha3_shake256_x4_avx512vl(buffers[0], buffers[1], buffers[2], buffers[3], buf_size, + derived_seeds[0], derived_seeds[1], derived_seeds[2], derived_seeds[3], seed_len); + + ossl_ml_dsa_poly_decode_expand_mask(&out->poly[i + 0], buffers[0], buf_size, gamma1); + + if ((i + 1) < num_polys) + ossl_ml_dsa_poly_decode_expand_mask(&out->poly[i + 1], buffers[1], buf_size, gamma1); + + if ((i + 2) < num_polys) + ossl_ml_dsa_poly_decode_expand_mask(&out->poly[i + 2], buffers[2], buf_size, gamma1); + } + + OPENSSL_cleanse(buffers, sizeof(buffers)); + OPENSSL_cleanse(derived_seeds, sizeof(derived_seeds)); +} + +static ossl_unused int rej_bounded_poly_mb(COEFF_FROM_NIBBLE_FUNC *coef_from_nibble, + const uint8_t *seeds[ML_DSA_SHAKE_X4_BATCH_SIZE], const size_t seed_len, + POLY *outs[ML_DSA_SHAKE_X4_BATCH_SIZE], const size_t count) +{ + KECCAK1600_X4_AVX512VL_CTX ctx; + uint8_t blocks[ML_DSA_SHAKE_X4_BATCH_SIZE][SHAKE256_BLOCKSIZE]; + int coeff_idx[ML_DSA_SHAKE_X4_BATCH_SIZE] = { 0, 0, 0, 0 }; + size_t done_mask = 0; + size_t lane; + + for (lane = count; lane < ML_DSA_SHAKE_X4_BATCH_SIZE; lane++) + done_mask |= ((size_t)1 << lane); + + ossl_sha3_shake256_x4_inc_init_avx512vl(&ctx); + ossl_sha3_shake256_x4_inc_absorb_avx512vl(&ctx, seeds[0], seeds[1], + seeds[2], seeds[3], seed_len); + + while (done_mask != ML_DSA_SHAKE_X4_DONE_MASK) { + ossl_sha3_shake256_x4_inc_squeeze_avx512vl(blocks[0], blocks[1], + blocks[2], blocks[3], SHAKE256_BLOCKSIZE, &ctx); + + for (lane = 0; lane < ML_DSA_SHAKE_X4_BATCH_SIZE; lane++) { + if (done_mask & ((size_t)1 << lane)) + continue; + + const uint8_t *b = blocks[lane]; + const uint8_t *end = b + SHAKE256_BLOCKSIZE; + + for (; b < end && coeff_idx[lane] < ML_DSA_NUM_POLY_COEFFICIENTS; b++) { + uint32_t z0 = *b & 0x0F; + uint32_t z1 = *b >> 4; + + if (coef_from_nibble(z0, &outs[lane]->coeff[coeff_idx[lane]])) + coeff_idx[lane]++; + + if (coeff_idx[lane] >= ML_DSA_NUM_POLY_COEFFICIENTS) { + done_mask |= ((size_t)1 << lane); + break; + } + + if (coef_from_nibble(z1, &outs[lane]->coeff[coeff_idx[lane]])) + coeff_idx[lane]++; + + if (coeff_idx[lane] >= ML_DSA_NUM_POLY_COEFFICIENTS) { + done_mask |= ((size_t)1 << lane); + break; + } + } + } + } + + OPENSSL_cleanse(blocks, sizeof(blocks)); + ossl_sha3_shake256_x4_inc_cleanup_avx512vl(&ctx); + return 1; +} + +static int matrix_expand_A_mb(EVP_MD_CTX *g_ctx, const EVP_MD *md, + const uint8_t *rho, MATRIX *out) +{ + size_t b, idx; + uint8_t derived_seeds[ML_DSA_SHAKE_X4_BATCH_SIZE][ML_DSA_RHO_BYTES + 2]; + const size_t seed_len = sizeof(derived_seeds[0]); + const uint8_t *seeds[ML_DSA_SHAKE_X4_BATCH_SIZE]; + POLY *polys[ML_DSA_SHAKE_X4_BATCH_SIZE]; + POLY *poly = out->m_poly; + + for (b = 0; b < ML_DSA_SHAKE_X4_BATCH_SIZE; b++) { + memcpy(derived_seeds[b], rho, ML_DSA_RHO_BYTES); + seeds[b] = derived_seeds[b]; + } + + for (idx = 0; (idx + ML_DSA_SHAKE_X4_BATCH_SIZE - 1) < (out->k * out->l); + idx += ML_DSA_SHAKE_X4_BATCH_SIZE) { + for (b = 0; b < ML_DSA_SHAKE_X4_BATCH_SIZE; b++) { + const size_t row = (idx + b) / out->l; + const size_t col = (idx + b) % out->l; + + derived_seeds[b][ML_DSA_RHO_BYTES] = (uint8_t)col; + derived_seeds[b][ML_DSA_RHO_BYTES + 1] = (uint8_t)row; + polys[b] = &poly[idx + b]; + } + + if (!rej_ntt_poly_mb(seeds, seed_len, polys, 4)) + return 0; + } + + if (idx < (out->k * out->l)) { + const size_t left = (out->k * out->l) - idx; + + for (b = 0; b < left; b++) { + const size_t row = (idx + b) / out->l; + const size_t col = (idx + b) % out->l; + + derived_seeds[b][ML_DSA_RHO_BYTES] = (uint8_t)col; + derived_seeds[b][ML_DSA_RHO_BYTES + 1] = (uint8_t)row; + polys[b] = &poly[idx + b]; + } + + if (!rej_ntt_poly_mb(seeds, seed_len, polys, left)) + return 0; + } + + return 1; +} + +static int vector_expand_S_mb(EVP_MD_CTX *h_ctx, const EVP_MD *md, const int eta, + const uint8_t *seed, VECTOR *s1, VECTOR *s2) +{ + int ret = 0; + size_t b, idx; + const size_t l = s1->num_poly; + const size_t total = l + s2->num_poly; + uint8_t derived_seeds[ML_DSA_SHAKE_X4_BATCH_SIZE][ML_DSA_PRIV_SEED_BYTES + 2]; + const uint8_t *seeds[ML_DSA_SHAKE_X4_BATCH_SIZE]; + const size_t seed_len = sizeof(derived_seeds[0]); + POLY *polys[ML_DSA_SHAKE_X4_BATCH_SIZE]; + COEFF_FROM_NIBBLE_FUNC *coef_from_nibble_fn = (eta == ML_DSA_ETA_4) ? coeff_from_nibble_4 : coeff_from_nibble_2; + + for (b = 0; b < ML_DSA_SHAKE_X4_BATCH_SIZE; b++) { + memcpy(derived_seeds[b], seed, ML_DSA_PRIV_SEED_BYTES); + seeds[b] = derived_seeds[b]; + } + + for (idx = 0; (idx + ML_DSA_SHAKE_X4_BATCH_SIZE - 1) < total; idx += ML_DSA_SHAKE_X4_BATCH_SIZE) { + for (b = 0; b < ML_DSA_SHAKE_X4_BATCH_SIZE; b++) { + const size_t poly_idx = idx + b; + + derived_seeds[b][ML_DSA_PRIV_SEED_BYTES] = (uint8_t)(poly_idx); + derived_seeds[b][ML_DSA_PRIV_SEED_BYTES + 1] = (uint8_t)(poly_idx >> 8); + + if (poly_idx < l) + polys[b] = &s1->poly[poly_idx]; + else + polys[b] = &s2->poly[poly_idx - l]; + } + + if (!rej_bounded_poly_mb(coef_from_nibble_fn, + seeds, seed_len, polys, ML_DSA_SHAKE_X4_BATCH_SIZE)) + goto err; + } + + if (idx < total) { + const size_t batch_count = total - idx; + + for (b = 0; b < batch_count; b++) { + const size_t poly_idx = idx + b; + + derived_seeds[b][ML_DSA_PRIV_SEED_BYTES] = (uint8_t)(poly_idx); + derived_seeds[b][ML_DSA_PRIV_SEED_BYTES + 1] = (uint8_t)(poly_idx >> 8); + + if (poly_idx < l) + polys[b] = &s1->poly[poly_idx]; + else + polys[b] = &s2->poly[poly_idx - l]; + } + + if (!rej_bounded_poly_mb(coef_from_nibble_fn, + seeds, seed_len, polys, batch_count)) + goto err; + } + + ret = 1; +err: + OPENSSL_cleanse(derived_seeds, sizeof(derived_seeds)); + return ret; +} + +static const OSSL_ML_DSA_SAMPLE_OPS ml_dsa_sample_x86_64 = { + matrix_expand_A_mb, + vector_expand_S_mb, + vector_expand_mask_mb +}; diff --git a/crypto/ml_dsa/ml_dsa_sign.c b/crypto/ml_dsa/ml_dsa_sign.c index 05251a6dd0..62dfd08d53 100644 --- a/crypto/ml_dsa/ml_dsa_sign.c +++ b/crypto/ml_dsa/ml_dsa_sign.c @@ -164,6 +164,7 @@ static int ml_dsa_sign_internal(const ML_DSA_KEY *priv, uint8_t *out_sig) { int ret = 0; + const OSSL_ML_DSA_SAMPLE_OPS *sample_ops = ossl_ml_dsa_sample_ops(); const ML_DSA_PARAMS *params = priv->params; EVP_MD_CTX *md_ctx = NULL; uint32_t k = (uint32_t)params->k, l = (uint32_t)params->l; @@ -236,7 +237,7 @@ static int ml_dsa_sign_internal(const ML_DSA_KEY *priv, CONSTTIME_SECRET_VECTOR(priv->s2); CONSTTIME_SECRET_VECTOR(priv->t0); - if (!matrix_expand_A(md_ctx, priv->shake128_md, priv->rho, &a_ntt)) + if (!sample_ops->matrix_expand_A(md_ctx, priv->shake128_md, priv->rho, &a_ntt)) goto err; /* @@ -267,8 +268,8 @@ static int ml_dsa_sign_internal(const ML_DSA_KEY *priv, VECTOR *ct0 = &w1; uint32_t z_max, r0_max, ct0_max, h_ones; - vector_expand_mask(&y, rho_prime, sizeof(rho_prime), (uint32_t)kappa, - gamma1, md_ctx, priv->shake256_md); + sample_ops->vector_expand_mask(&y, rho_prime, + (uint32_t)kappa, gamma1, md_ctx, priv->shake256_md); vector_copy(y_ntt, &y); vector_ntt(y_ntt); @@ -391,6 +392,7 @@ static int ml_dsa_verify_internal(const ML_DSA_KEY *pub, const uint8_t *sig_enc, size_t sig_enc_len) { int ret = 0; + const OSSL_ML_DSA_SAMPLE_OPS *sample_ops = ossl_ml_dsa_sample_ops(); uint8_t *alloc = NULL, *w1_encoded = NULL; void *alloc_freeptr = NULL; POLY *p, *c_ntt; @@ -448,7 +450,7 @@ static int ml_dsa_verify_internal(const ML_DSA_KEY *pub, vector_init(&ct1_ntt, p + k, k); if (!ossl_ml_dsa_sig_decode(&sig, sig_enc, sig_enc_len, pub->params) - || !matrix_expand_A(md_ctx, pub->shake128_md, pub->rho, &a_ntt)) + || !sample_ops->matrix_expand_A(md_ctx, pub->shake128_md, pub->rho, &a_ntt)) goto err; /* Compute verifiers challenge c_ntt = NTT(SampleInBall(c_tilde)) */ diff --git a/crypto/ml_dsa/ml_dsa_vector.h b/crypto/ml_dsa/ml_dsa_vector.h index 6b408acdc2..9b83c0420e 100644 --- a/crypto/ml_dsa/ml_dsa_vector.h +++ b/crypto/ml_dsa/ml_dsa_vector.h @@ -152,33 +152,6 @@ vector_mult_scalar(const VECTOR *lhs, const POLY *rhs, VECTOR *out) ossl_ml_dsa_poly_ntt_mult(lhs->poly + i, rhs, out->poly + i); } -static ossl_inline ossl_unused int -vector_expand_S(EVP_MD_CTX *h_ctx, const EVP_MD *md, int eta, - const uint8_t *seed, VECTOR *s1, VECTOR *s2) -{ - return ossl_ml_dsa_vector_expand_S(h_ctx, md, eta, seed, s1, s2); -} - -static ossl_inline ossl_unused void -vector_expand_mask(VECTOR *out, const uint8_t *rho_prime, size_t rho_prime_len, - uint32_t kappa, uint32_t gamma1, - EVP_MD_CTX *h_ctx, const EVP_MD *md) -{ - size_t i; - uint8_t derived_seed[ML_DSA_RHO_PRIME_BYTES + 2]; - - memcpy(derived_seed, rho_prime, ML_DSA_RHO_PRIME_BYTES); - - for (i = 0; i < out->num_poly; i++) { - size_t index = kappa + i; - - derived_seed[ML_DSA_RHO_PRIME_BYTES] = index & 0xFF; - derived_seed[ML_DSA_RHO_PRIME_BYTES + 1] = (index >> 8) & 0xFF; - poly_expand_mask(out->poly + i, derived_seed, sizeof(derived_seed), - gamma1, h_ctx, md); - } -} - /* Scale back previously rounded value */ static ossl_inline ossl_unused void vector_scale_power2_round_ntt(const VECTOR *in, VECTOR *out) diff --git a/crypto/sha/asm/keccak1600x4-avx512vl.pl b/crypto/sha/asm/keccak1600x4-avx512vl.pl new file mode 100755 index 0000000000..b5a6ac7377 --- /dev/null +++ b/crypto/sha/asm/keccak1600x4-avx512vl.pl @@ -0,0 +1,2344 @@ +#!/usr/bin/env perl +# +# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. +# Copyright (c) 2026 Intel Corporation. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +############################################################################### +# Keccak x4 AVX512VL SHA3/SHAKE Assembly Routines +# +# Description: +# This file emits x86_64 assembly for AVX512VL accelerated Keccak-f[1600] +# processing of 4 independent states in parallel ("x4"). +# +# It provides the core 24-round Keccak permutation and x4 helper routines +# used by SHA3 and SHAKE absorb/finalize/squeeze paths. Data from four +# input/output lanes is packed across YMM registers so lane-local operations +# execute in SIMD. +# +############################################################################### + +# $output is the last argument if it looks like a file (it has an extension) +# $flavour is the first argument if it doesn't look like a file +$output = $#ARGV >= 0 && $ARGV[$#ARGV] =~ m|\.\w+$| ? pop : undef; +$flavour = $#ARGV >= 0 && $ARGV[0] !~ m|\.| ? shift : undef; + +$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/); + +$avx512vl = 0; + +$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1; +( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or +( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or +die "can't locate x86_64-xlate.pl"; + +# Check for AVX512VL support in assembler +if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1` =~ /GNU assembler version (\d+)\.(\d+)/) { + my ($gas_major, $gas_minor) = ($1, $2); + $avx512vl = ($gas_major > 2 || ($gas_major == 2 && $gas_minor >= 26)); +} + +if (!$avx512vl + && $win64 + && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) + && `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)(?:\.([0-9]+))?/) +{ + $avx512vl = ($1 >= 2.12); +} + +if (!$avx512vl && `$ENV{CC} -v 2>&1` =~ /((?:clang|LLVM) version|.*based on LLVM) ([0-9]+\.[0-9]+)/) { + $avx512vl = ($2>=3.9); +} + +open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"" + or die "can't call $xlate: $!"; +*STDOUT=*OUT; + +$arg1="%rdi"; +$arg2="%rsi"; +$arg3="%rdx"; +$arg4="%rcx"; +$arg5="%r8"; +$arg6="%r9"; +$roundn="%r13d"; +$tblptr="%r14"; + +# Define SHAKE rates +$SHAKE128_RATE="\$168"; +$SHAKE256_RATE="\$136"; + +# Stack frame offsets for SHAKE x4 wrapper functions +$STATE_SIZE="808"; # (25 * 8 * 4) + 8 = 808 bytes +$sf_arg1="0"; +$sf_arg2="8"; +$sf_arg3="16"; +$sf_arg4="24"; +$sf_arg5="32"; +$sf_state_ptr="40"; +$sf_state_x4="48"; +$sf_size="856"; # 48 + 808 = 856 bytes + +# Emit an internal helper call used by one-shot wrappers. +# - Win64: call the provided *_internal shim and bracket it with 32-byte +# shadow space so shim entry can use xlate-compatible [rsp+8]/[rsp+16]. +# - non-Win64: call the public API symbol (same base name without _internal). +# The argument must be the shim/internal symbol name, e.g. +# SHA3_shake128_x4_inc_squeeze_avx512vl_internal +sub call_internal { + my ($shim_name) = @_; + my $external_name = $shim_name; + + $external_name =~ s/_internal$//; + + return <<___ if ($win64); + sub \$32, %rsp + call $shim_name + add \$32, %rsp +___ + + return <<___; + call $external_name +___ +} + +if ($avx512vl>0) {{{ + +my $avx512_mask = (1<<31)|(1<<30)|(1<<17)|(1<<16); # AVX512VL|BW|DQ|F + +$code .= <<___; +.text + +.extern OPENSSL_ia32cap_P + +.globl SHA3_avx512vl_capable +.type SHA3_avx512vl_capable,\@abi-omnipotent +.align 32 +SHA3_avx512vl_capable: + mov OPENSSL_ia32cap_P+8(%rip), %ecx + xor %eax, %eax + # 1<<31|1<<30|1<<17|1<<16: AVX512VL|AVX512BW|AVX512DQ|AVX512F + and \$$avx512_mask, %ecx + cmp \$$avx512_mask, %ecx + cmove %ecx, %eax + ret +.size SHA3_avx512vl_capable, .-SHA3_avx512vl_capable +___ + +$code.=<<___; +.text + +# Perform Keccak permutation +# +# YMM registers 0 to 24 are used as Keccak state registers. +# This function, as is, can work on 1 to 4 independent states at the same time. +# +# There is no clear boundary between Theta, Rho, Pi, Chi and Iota steps. +# Instructions corresponding to these steps overlap for better efficiency. +# +# Arguments: +# ymm0-ymm24 [in/out] Keccak state registers (one SIMD per one state register) +# ymm25-ymm31 [clobbered] temporary SIMD registers +# $roundn [clobbered] used for round tracking +# $tblptr [clobbered] used for access to SHA3 constant table +.type keccak_1600_permute,\@abi-omnipotent +.align 32 +keccak_1600_permute: +.cfi_startproc + mov \$24, $roundn # 24 rounds + lea iotas(%rip), $tblptr # Load the address of the SHA3 round constants + +.align 32 +.Lkeccak_rnd_loop: + # Theta step + + # Compute column parities + # C[5] = [0, 0, 0, 0, 0] + # for x in 0 to 4: + # C[x] = state[x][0] XOR state[x][1] XOR state[x][2] XOR state[x][3] XOR state[x][4] + + vmovdqa64 %ymm0, %ymm25 + vpternlogq \$0x96, %ymm5, %ymm10, %ymm25 + vmovdqa64 %ymm1, %ymm26 + vpternlogq \$0x96, %ymm11, %ymm6, %ymm26 + vmovdqa64 %ymm2, %ymm27 + vpternlogq \$0x96, %ymm12, %ymm7, %ymm27 + + vmovdqa64 %ymm3, %ymm28 + vpternlogq \$0x96, %ymm13, %ymm8, %ymm28 + vmovdqa64 %ymm4, %ymm29 + vpternlogq \$0x96, %ymm14, %ymm9, %ymm29 + vpternlogq \$0x96, %ymm20, %ymm15, %ymm25 + + vpternlogq \$0x96, %ymm21, %ymm16, %ymm26 + vpternlogq \$0x96, %ymm22, %ymm17, %ymm27 + vpternlogq \$0x96, %ymm23, %ymm18, %ymm28 + + # Start computing D values and keep computing column parity + # D[5] = [0, 0, 0, 0, 0] + # for x in 0 to 4: + # D[x] = C[(x+4) mod 5] XOR ROTATE_LEFT(C[(x+1) mod 5], 1) + + vprolq \$1, %ymm26, %ymm30 + vprolq \$1, %ymm27, %ymm31 + vpternlogq \$0x96, %ymm24, %ymm19, %ymm29 + + # Continue computing D values and apply Theta + # for x in 0 to 4: + # for y in 0 to 4: + # state[x][y] = state[x][y] XOR D[x] + + vpternlogq \$0x96, %ymm30, %ymm29, %ymm0 + vpternlogq \$0x96, %ymm30, %ymm29, %ymm10 + vpternlogq \$0x96, %ymm30, %ymm29, %ymm20 + + vpternlogq \$0x96, %ymm30, %ymm29, %ymm5 + vpternlogq \$0x96, %ymm30, %ymm29, %ymm15 + vprolq \$1, %ymm28, %ymm30 + + vpternlogq \$0x96, %ymm31, %ymm25, %ymm6 + vpternlogq \$0x96, %ymm31, %ymm25, %ymm16 + vpternlogq \$0x96, %ymm31, %ymm25, %ymm1 + + vpternlogq \$0x96, %ymm31, %ymm25, %ymm11 + vpternlogq \$0x96, %ymm31, %ymm25, %ymm21 + vprolq \$1, %ymm29, %ymm31 + + vpbroadcastq ($tblptr), %ymm29 # Load the round constant into ymm29 (Iota) + add \$8, $tblptr # Increment the pointer to the next round constant + + vpternlogq \$0x96, %ymm30, %ymm26, %ymm12 + vpternlogq \$0x96, %ymm30, %ymm26, %ymm7 + vpternlogq \$0x96, %ymm30, %ymm26, %ymm22 + + vpternlogq \$0x96, %ymm30, %ymm26, %ymm17 + vpternlogq \$0x96, %ymm30, %ymm26, %ymm2 + vprolq \$1, %ymm25, %ymm30 + + # Rho step + # Keep applying Theta and start Rho step + # + # ROTATION_OFFSETS[5][5] = [ + # [0, 1, 62, 28, 27], + # [36, 44, 6, 55, 20], + # [3, 10, 43, 25, 39], + # [41, 45, 15, 21, 8], + # [18, 2, 61, 56, 14] ] + # + # for x in 0 to 4: + # for y in 0 to 4: + # state[x][y] = ROTATE_LEFT(state[x][y], ROTATION_OFFSETS[x][y]) + + vpternlogq \$0x96, %ymm31, %ymm27, %ymm3 + vpternlogq \$0x96, %ymm31, %ymm27, %ymm13 + vpternlogq \$0x96, %ymm31, %ymm27, %ymm23 + + vprolq \$44, %ymm6, %ymm6 + vpternlogq \$0x96, %ymm31, %ymm27, %ymm18 + vpternlogq \$0x96, %ymm31, %ymm27, %ymm8 + + vprolq \$43, %ymm12, %ymm12 + vprolq \$21, %ymm18, %ymm18 + vpternlogq \$0x96, %ymm30, %ymm28, %ymm24 + + vprolq \$14, %ymm24, %ymm24 + vprolq \$28, %ymm3, %ymm3 + vpternlogq \$0x96, %ymm30, %ymm28, %ymm9 + + vprolq \$20, %ymm9, %ymm9 + vprolq \$3, %ymm10, %ymm10 + vpternlogq \$0x96, %ymm30, %ymm28, %ymm19 + + vprolq \$45, %ymm16, %ymm16 + vprolq \$61, %ymm22, %ymm22 + vpternlogq \$0x96, %ymm30, %ymm28, %ymm4 + + vprolq \$1, %ymm1, %ymm1 + vprolq \$6, %ymm7, %ymm7 + vpternlogq \$0x96, %ymm30, %ymm28, %ymm14 + + # Continue with Rho and start Pi and Chi steps at the same time + # Ternary logic 0xD2 is used for Chi step + # + # for x in 0 to 4: + # for y in 0 to 4: + # state[x][y] = state[x][y] XOR ((NOT state[(x+1) mod 5][y]) AND state[(x+2) mod 5][y]) + + vprolq \$25, %ymm13, %ymm13 + vprolq \$8, %ymm19, %ymm19 + vmovdqa64 %ymm0, %ymm30 + vpternlogq \$0xD2, %ymm12, %ymm6, %ymm30 + + vprolq \$18, %ymm20, %ymm20 + vprolq \$27, %ymm4, %ymm4 + vpxorq %ymm29, %ymm30, %ymm30 # Iota step + + vprolq \$36, %ymm5, %ymm5 + vprolq \$10, %ymm11, %ymm11 + vmovdqa64 %ymm6, %ymm31 + vpternlogq \$0xD2, %ymm18, %ymm12, %ymm31 + + vprolq \$15, %ymm17, %ymm17 + vprolq \$56, %ymm23, %ymm23 + vpternlogq \$0xD2, %ymm24, %ymm18, %ymm12 + + vprolq \$62, %ymm2, %ymm2 + vprolq \$55, %ymm8, %ymm8 + vpternlogq \$0xD2, %ymm0, %ymm24, %ymm18 + + vprolq \$39, %ymm14, %ymm14 + vprolq \$41, %ymm15, %ymm15 + vpternlogq \$0xD2, %ymm6, %ymm0, %ymm24 + vmovdqa64 %ymm30, %ymm0 + vmovdqa64 %ymm31, %ymm6 + + vprolq \$2, %ymm21, %ymm21 + vmovdqa64 %ymm3, %ymm30 + vpternlogq \$0xD2, %ymm10, %ymm9, %ymm30 + vmovdqa64 %ymm9, %ymm31 + vpternlogq \$0xD2, %ymm16, %ymm10, %ymm31 + + vpternlogq \$0xD2, %ymm22, %ymm16, %ymm10 + vpternlogq \$0xD2, %ymm3, %ymm22, %ymm16 + vpternlogq \$0xD2, %ymm9, %ymm3, %ymm22 + vmovdqa64 %ymm30, %ymm3 + vmovdqa64 %ymm31, %ymm9 + + vmovdqa64 %ymm1, %ymm30 + vpternlogq \$0xD2, %ymm13, %ymm7, %ymm30 + vmovdqa64 %ymm7, %ymm31 + vpternlogq \$0xD2, %ymm19, %ymm13, %ymm31 + vpternlogq \$0xD2, %ymm20, %ymm19, %ymm13 + + vpternlogq \$0xD2, %ymm1, %ymm20, %ymm19 + vpternlogq \$0xD2, %ymm7, %ymm1, %ymm20 + vmovdqa64 %ymm30, %ymm1 + vmovdqa64 %ymm31, %ymm7 + vmovdqa64 %ymm4, %ymm30 + vpternlogq \$0xD2, %ymm11, %ymm5, %ymm30 + + vmovdqa64 %ymm5, %ymm31 + vpternlogq \$0xD2, %ymm17, %ymm11, %ymm31 + vpternlogq \$0xD2, %ymm23, %ymm17, %ymm11 + vpternlogq \$0xD2, %ymm4, %ymm23, %ymm17 + + vpternlogq \$0xD2, %ymm5, %ymm4, %ymm23 + vmovdqa64 %ymm30, %ymm4 + vmovdqa64 %ymm31, %ymm5 + vmovdqa64 %ymm2, %ymm30 + vpternlogq \$0xD2, %ymm14, %ymm8, %ymm30 + vmovdqa64 %ymm8, %ymm31 + vpternlogq \$0xD2, %ymm15, %ymm14, %ymm31 + + vpternlogq \$0xD2, %ymm21, %ymm15, %ymm14 + vpternlogq \$0xD2, %ymm2, %ymm21, %ymm15 + vpternlogq \$0xD2, %ymm8, %ymm2, %ymm21 + vmovdqa64 %ymm30, %ymm2 + vmovdqa64 %ymm31, %ymm8 + + # Complete the steps and get updated state registers in ymm0 to ymm24 + vmovdqa64 %ymm3, %ymm30 + vmovdqa64 %ymm18, %ymm3 + vmovdqa64 %ymm17, %ymm18 + vmovdqa64 %ymm11, %ymm17 + vmovdqa64 %ymm7, %ymm11 + vmovdqa64 %ymm10, %ymm7 + vmovdqa64 %ymm1, %ymm10 + vmovdqa64 %ymm6, %ymm1 + vmovdqa64 %ymm9, %ymm6 + vmovdqa64 %ymm22, %ymm9 + vmovdqa64 %ymm14, %ymm22 + vmovdqa64 %ymm20, %ymm14 + vmovdqa64 %ymm2, %ymm20 + vmovdqa64 %ymm12, %ymm2 + vmovdqa64 %ymm13, %ymm12 + vmovdqa64 %ymm19, %ymm13 + vmovdqa64 %ymm23, %ymm19 + vmovdqa64 %ymm15, %ymm23 + vmovdqa64 %ymm4, %ymm15 + vmovdqa64 %ymm24, %ymm4 + vmovdqa64 %ymm21, %ymm24 + vmovdqa64 %ymm8, %ymm21 + vmovdqa64 %ymm16, %ymm8 + vmovdqa64 %ymm5, %ymm16 + vmovdqa64 %ymm30, %ymm5 + + dec $roundn # Decrement the round counter + jnz .Lkeccak_rnd_loop # Jump to the start of the loop if r13d is not zero + ret +.cfi_endproc +.size keccak_1600_permute,.-keccak_1600_permute + +# Initialize YMM registers 0-24 to zero +.globl keccak_1600_init_state +.type keccak_1600_init_state,\@abi-omnipotent +.align 32 +keccak_1600_init_state: +.cfi_startproc + vpxorq %ymm0, %ymm0, %ymm0 + vmovdqa64 %ymm0, %ymm1 + vmovdqa64 %ymm0, %ymm2 + vmovdqa64 %ymm0, %ymm3 + vmovdqa64 %ymm0, %ymm4 + vmovdqa64 %ymm0, %ymm5 + vmovdqa64 %ymm0, %ymm6 + vmovdqa64 %ymm0, %ymm7 + vmovdqa64 %ymm0, %ymm8 + vmovdqa64 %ymm0, %ymm9 + vmovdqa64 %ymm0, %ymm10 + vmovdqa64 %ymm0, %ymm11 + vmovdqa64 %ymm0, %ymm12 + vmovdqa64 %ymm0, %ymm13 + vmovdqa64 %ymm0, %ymm14 + vmovdqa64 %ymm0, %ymm15 + vmovdqa64 %ymm0, %ymm16 + vmovdqa64 %ymm0, %ymm17 + vmovdqa64 %ymm0, %ymm18 + vmovdqa64 %ymm0, %ymm19 + vmovdqa64 %ymm0, %ymm20 + vmovdqa64 %ymm0, %ymm21 + vmovdqa64 %ymm0, %ymm22 + vmovdqa64 %ymm0, %ymm23 + vmovdqa64 %ymm0, %ymm24 + ret +.cfi_endproc +.size keccak_1600_init_state,.-keccak_1600_init_state + +.globl keccak_1600_load_state_x4 +.type keccak_1600_load_state_x4,\@abi-omnipotent +.align 32 +keccak_1600_load_state_x4: +.cfi_startproc + vmovdqu64 32*0($arg1), %ymm0 + vmovdqu64 32*1($arg1), %ymm1 + vmovdqu64 32*2($arg1), %ymm2 + vmovdqu64 32*3($arg1), %ymm3 + vmovdqu64 32*4($arg1), %ymm4 + vmovdqu64 32*5($arg1), %ymm5 + vmovdqu64 32*6($arg1), %ymm6 + vmovdqu64 32*7($arg1), %ymm7 + vmovdqu64 32*8($arg1), %ymm8 + vmovdqu64 32*9($arg1), %ymm9 + vmovdqu64 32*10($arg1), %ymm10 + vmovdqu64 32*11($arg1), %ymm11 + vmovdqu64 32*12($arg1), %ymm12 + vmovdqu64 32*13($arg1), %ymm13 + vmovdqu64 32*14($arg1), %ymm14 + vmovdqu64 32*15($arg1), %ymm15 + vmovdqu64 32*16($arg1), %ymm16 + vmovdqu64 32*17($arg1), %ymm17 + vmovdqu64 32*18($arg1), %ymm18 + vmovdqu64 32*19($arg1), %ymm19 + vmovdqu64 32*20($arg1), %ymm20 + vmovdqu64 32*21($arg1), %ymm21 + vmovdqu64 32*22($arg1), %ymm22 + vmovdqu64 32*23($arg1), %ymm23 + vmovdqu64 32*24($arg1), %ymm24 + ret +.cfi_endproc +.size keccak_1600_load_state_x4,.-keccak_1600_load_state_x4 + + +.globl keccak_1600_save_state_x4 +.type keccak_1600_save_state_x4,\@abi-omnipotent +.align 32 +keccak_1600_save_state_x4: +.cfi_startproc + vmovdqu64 %ymm0, 32*0($arg1) + vmovdqu64 %ymm1, 32*1($arg1) + vmovdqu64 %ymm2, 32*2($arg1) + vmovdqu64 %ymm3, 32*3($arg1) + vmovdqu64 %ymm4, 32*4($arg1) + vmovdqu64 %ymm5, 32*5($arg1) + vmovdqu64 %ymm6, 32*6($arg1) + vmovdqu64 %ymm7, 32*7($arg1) + vmovdqu64 %ymm8, 32*8($arg1) + vmovdqu64 %ymm9, 32*9($arg1) + vmovdqu64 %ymm10, 32*10($arg1) + vmovdqu64 %ymm11, 32*11($arg1) + vmovdqu64 %ymm12, 32*12($arg1) + vmovdqu64 %ymm13, 32*13($arg1) + vmovdqu64 %ymm14, 32*14($arg1) + vmovdqu64 %ymm15, 32*15($arg1) + vmovdqu64 %ymm16, 32*16($arg1) + vmovdqu64 %ymm17, 32*17($arg1) + vmovdqu64 %ymm18, 32*18($arg1) + vmovdqu64 %ymm19, 32*19($arg1) + vmovdqu64 %ymm20, 32*20($arg1) + vmovdqu64 %ymm21, 32*21($arg1) + vmovdqu64 %ymm22, 32*22($arg1) + vmovdqu64 %ymm23, 32*23($arg1) + vmovdqu64 %ymm24, 32*24($arg1) + ret +.cfi_endproc +.size keccak_1600_save_state_x4,.-keccak_1600_save_state_x4 + + +# Add input data to state when message length is less than rate +# Arguments: +# r10: state pointer to absorb into (clobbered) +# arg2 (rsi): message pointer lane 0 (updated on output) +# arg3 (rdx): message pointer lane 1 (updated on output) +# arg4 (rcx): message pointer lane 2 (updated on output) +# arg5 (r8): message pointer lane 3 (updated on output) +# r12: length in bytes (clobbered on output) +# Clobbers: r9, rbx, r15, k1, ymm31-ymm29 +.globl keccak_1600_partial_add_x4 +.type keccak_1600_partial_add_x4,\@abi-omnipotent +.align 32 +keccak_1600_partial_add_x4: +.cfi_startproc + mov 8*100(%r10), %r9 + test \$7, %r9d + jz .Lstart_aligned_to_4x8 + + # Start offset is not aligned to register size + mov %r9, %r15 # %r15 = s[100] + + and \$7, %r9d + neg %r9d + add \$8, %r9d # register capacity = 8 - (offset % 8) + cmp %r9d, %r12d + cmovnae %r12d, %r9d # %r9d = min(register capacity, length) + + lea byte_kmask_0_to_7(%rip), %rbx + kmovb (%rbx,%r9), %k1 # message load mask + + mov %r15, %rbx + and \$~7, %ebx + lea (%r10,%rbx,4), %r10 # get to state starting register + + mov %r15, %rbx + and \$7, %ebx + + vmovdqu8 (%r10), %ymm31 # load & store / allocate SB for the register + vmovdqu8 %ymm31, (%r10) + + vmovdqu8 ($arg2), %xmm31{%k1}{z} # Read 1 to 7 bytes from lane 0 + vmovdqu8 8*0(%r10,%rbx), %xmm30{%k1}{z} # Read 1 to 7 bytes from state reg lane 0 + vpxorq %xmm30, %xmm31, %xmm31 + vmovdqu8 %xmm31, 8*0(%r10,%rbx){%k1} # Write 1 to 7 bytes to state reg lane 0 + + vmovdqu8 ($arg3), %xmm31{%k1}{z} # Read 1 to 7 bytes from lane 1 + vmovdqu8 8*1(%r10,%rbx), %xmm30{%k1}{z} # Read 1 to 7 bytes from state reg lane 1 + vpxorq %xmm30, %xmm31, %xmm31 + vmovdqu8 %xmm31, 8*1(%r10,%rbx){%k1} # Write 1 to 7 bytes to state reg lane 1 + + vmovdqu8 ($arg4), %xmm31{%k1}{z} # Read 1 to 7 bytes from lane 2 + vmovdqu8 8*2(%r10,%rbx), %xmm30{%k1}{z} # Read 1 to 7 bytes from state reg lane 2 + vpxorq %xmm30, %xmm31, %xmm31 + vmovdqu8 %xmm31, 8*2(%r10,%rbx){%k1} # Write 1 to 7 bytes to state reg lane 2 + + vmovdqu8 ($arg5), %xmm31{%k1}{z} # Read 1 to 7 bytes from lane 3 + vmovdqu8 8*3(%r10,%rbx), %xmm30{%k1}{z} # Read 1 to 7 bytes from state reg lane 3 + vpxorq %xmm30, %xmm31, %xmm31 + vmovdqu8 %xmm31, 8*3(%r10,%rbx){%k1} # Write 1 to 7 bytes to state reg lane 3 + + sub %r9, %r12 + jz .Lzero_bytes + + add %r9, $arg2 + add %r9, $arg3 + add %r9, $arg4 + add %r9, $arg5 + add \$32, %r10 + xor %r9, %r9 + jmp .Lymm_loop + +.Lstart_aligned_to_4x8: + lea (%r10,%r9,4), %r10 + xor %r9, %r9 + +.align 32 +.Lymm_loop: + cmp \$8, %r12d + jb .Llt_8_bytes + + vmovq ($arg2,%r9), %xmm31 # Read 8 bytes from lane 0 + vpinsrq \$1, ($arg3,%r9), %xmm31, %xmm31 # Read 8 bytes from lane 1 + vmovq ($arg4,%r9), %xmm30 # Read 8 bytes from lane 2 + vpinsrq \$1, ($arg5,%r9),%xmm30, %xmm30 # Read 8 bytes from lane 3 + vinserti32x4 \$1, %xmm30, %ymm31, %ymm31 + vpxorq (%r10,%r9,4), %ymm31, %ymm31 # Add data with the state + vmovdqu64 %ymm31, (%r10,%r9,4) + add \$8, %r9 + sub \$8, %r12 + jz .Lzero_bytes + + jmp .Lymm_loop + +.align 32 +.Lzero_bytes: + add %r9, $arg2 + add %r9, $arg3 + add %r9, $arg4 + add %r9, $arg5 + ret + +.align 32 +.Llt_8_bytes: + add %r9, $arg2 + add %r9, $arg3 + add %r9, $arg4 + add %r9, $arg5 + lea (%r10,%r9,4), %r10 + + lea byte_kmask_0_to_7(%rip), %rbx + kmovb (%rbx,%r12), %k1 # message load mask + + vmovdqu8 ($arg2), %xmm31{%k1}{z} # Read 1 to 7 bytes from lane 0 + vmovdqu8 ($arg3), %xmm30{%k1}{z} # Read 1 to 7 bytes from lane 1 + vpunpcklqdq %xmm30, %xmm31, %xmm31 # Interleave data from lane 0 and lane 1 + vmovdqu8 ($arg4), %xmm30{%k1}{z} # Read 1 to 7 bytes from lane 2 + vmovdqu8 ($arg5), %xmm29{%k1}{z} # Read 1 to 7 bytes from lane 3 + vpunpcklqdq %xmm29, %xmm30, %xmm30 # Interleave data from lane 2 and lane 3 + vinserti32x4 \$1, %xmm30, %ymm31, %ymm31 + + vpxorq (%r10), %ymm31, %ymm31 # Add data to the state + vmovdqu64 %ymm31, (%r10) # Update state in memory + + add %r12, $arg2 # increment message pointer lane 0 + add %r12, $arg3 # increment message pointer lane 1 + add %r12, $arg4 # increment message pointer lane 2 + add %r12, $arg5 # increment message pointer lane 3 + ret +.cfi_endproc +.size keccak_1600_partial_add_x4,.-keccak_1600_partial_add_x4 + + +# Extract bytes from state and write to outputs +# Arguments: +# r10: state pointer to start extracting from (clobbered) +# arg1 (rdi): output pointer lane 0 (updated on output) +# arg2 (rsi): output pointer lane 1 (updated on output) +# arg3 (rdx): output pointer lane 2 (updated on output) +# arg4 (rcx): output pointer lane 3 (updated on output) +# r12: length in bytes (clobbered on output) +# r11: state offset to start extract from +.globl keccak_1600_extract_bytes_x4 +.type keccak_1600_extract_bytes_x4,\@abi-omnipotent +.align 32 +keccak_1600_extract_bytes_x4: +.cfi_startproc + or %r12, %r12 + jz .Lextract_zero_bytes + + test \$7, %r11d + jz .Lextract_start_aligned_to_4x8 + + # Extract offset is not aligned to the register size (8 bytes) + mov %r11, %r9 + + and \$7, %r9d + neg %r9d + add \$8, %r9d # register capacity = 8 - (offset % 8) + cmp %r9d, %r12d + cmovnae %r12d, %r9d # %r9d = min(register capacity, length) + + lea byte_kmask_0_to_7(%rip), %rbx + kmovb (%rbx,%r9), %k1 # message store mask + + mov %r11, %rbx + and \$~7, %ebx + lea (%r10,%rbx,4), %r10 # get to state starting register + + mov %r11, %rbx + and \$7, %ebx + + vmovdqu8 8*0(%r10,%rbx), %xmm31{%k1}{z} # Read 1-7 bytes from state reg lane 0 + vmovdqu8 %xmm31, ($arg1){%k1} # Write 1-7 bytes to lane 0 output + + vmovdqu8 8*1(%r10,%rbx), %xmm31{%k1}{z} # Read 1-7 bytes from state reg lane 1 + vmovdqu8 %xmm31, ($arg2){%k1} # Write 1-7 bytes to lane 1 output + + vmovdqu8 8*2(%r10,%rbx), %xmm31{%k1}{z} # Read 1-7 bytes from state reg lane 2 + vmovdqu8 %xmm31, ($arg3){%k1} # Write 1-7 bytes to lane 2 output + + vmovdqu8 8*3(%r10,%rbx), %xmm31{%k1}{z} # Read 1-7 bytes from state reg lane 3 + vmovdqu8 %xmm31, ($arg4){%k1} # Write 1-7 bytes to lane 3 output + + # Increment output registers + add %r9, $arg1 + add %r9, $arg2 + add %r9, $arg3 + add %r9, $arg4 + + # Decrement length to extract + sub %r9, %r12 + jz .Lextract_zero_bytes + + # More data to extract, update state register pointer + add \$32, %r10 + xor %r9, %r9 + jmp .Lextract_ymm_loop + +.Lextract_start_aligned_to_4x8: + lea (%r10,%r11,4), %r10 + xor %r9, %r9 + +.align 32 +.Lextract_ymm_loop: + cmp \$8, %r12 + jb .Lextract_lt_8_bytes + + vmovdqu64 (%r10), %xmm31 + vmovdqu64 16(%r10), %xmm30 + vmovq %xmm31, ($arg1,%r9) + vpextrq \$1, %xmm31, ($arg2,%r9) + vmovq %xmm30, ($arg3,%r9) + vpextrq \$1, %xmm30, ($arg4,%r9) + add \$8, %r9 + sub \$8, %r12 + jz .Lzero_bytes_left + + add \$32, %r10 + jmp .Lextract_ymm_loop + +.align 32 +.Lzero_bytes_left: + # Increment output pointers + add %r9, $arg1 + add %r9, $arg2 + add %r9, $arg3 + add %r9, $arg4 +.Lextract_zero_bytes: + ret + +.align 32 +.Lextract_lt_8_bytes: + add %r9, $arg1 + add %r9, $arg2 + add %r9, $arg3 + add %r9, $arg4 + + lea byte_kmask_0_to_7(%rip), %r9 + kmovb (%r9,%r12), %k1 # k1 is the mask of message bytes to read + + vmovq 0*8(%r10), %xmm31 # Read 8 bytes from state lane 0 + vmovdqu8 %xmm31, ($arg1){%k1} # Extract 1-7 bytes into output 0 + vmovq 1*8(%r10), %xmm31 # Read 8 bytes from state lane 1 + vmovdqu8 %xmm31, ($arg2){%k1} # Extract 1-7 bytes into output 1 + vmovq 2*8(%r10), %xmm31 # Read 8 bytes from state lane 2 + vmovdqu8 %xmm31, ($arg3){%k1} # Extract 1-7 bytes into output 2 + vmovq 3*8(%r10), %xmm31 # Read 8 bytes from state lane 3 + vmovdqu8 %xmm31, ($arg4){%k1} # Extract 1-7 bytes into output 3 + + # Increment output pointers + add %r12, $arg1 + add %r12, $arg2 + add %r12, $arg3 + add %r12, $arg4 + ret +.cfi_endproc +.size keccak_1600_extract_bytes_x4,.-keccak_1600_extract_bytes_x4 + + +# SHAKE128 x4 multi-buffer functions +# These functions process 4 independent SHAKE128 streams in parallel using AVX-512VL +# State layout: 25 ymm registers (200 bytes each) + 1 qword = 808 bytes per context +# Rate: 168 bytes for SHAKE128 + +# SHA3_shake128_x4_avx512vl +# One-shot SHAKE-128 x4 function: init + absorb + finalize + squeeze +# Arguments: +# arg1 (rdi): pointer to output lane 0 +# arg2 (rsi): pointer to output lane 1 +# arg3 (rdx): pointer to output lane 2 +# arg4 (rcx): pointer to output lane 3 +# arg5 (r8): output length in bytes (must be same for all lanes) +# arg6 (r9): pointer to input lane 0 +# [stack+0]: pointer to input lane 1 +# [stack+8]: pointer to input lane 2 +# [stack+16]: pointer to input lane 3 +# [stack+24]: input length in bytes (must be same for all lanes) +# Returns: void +.globl SHA3_shake128_x4_avx512vl +.type SHA3_shake128_x4_avx512vl,\@function,10 +.align 32 +SHA3_shake128_x4_avx512vl: +.cfi_startproc + push %rbp +.cfi_push %rbp + mov %rsp, %rbp + push %rbx +.cfi_push %rbx +___ +$code .= <<___ if ($win64); + sub \$160, %rsp + vmovups %xmm6, 0(%rsp) + vmovups %xmm7, 16(%rsp) + vmovups %xmm8, 32(%rsp) + vmovups %xmm9, 48(%rsp) + vmovups %xmm10, 64(%rsp) + vmovups %xmm11, 80(%rsp) + vmovups %xmm12, 96(%rsp) + vmovups %xmm13, 112(%rsp) + vmovups %xmm14, 128(%rsp) + vmovups %xmm15, 144(%rsp) +___ +$code.=<<___; + + sub \$$sf_size, %rsp + mov %rsp, %rbx + +.Lshake128_x4_body: + mov $arg1, $sf_arg1(%rbx) + mov $arg2, $sf_arg2(%rbx) + mov $arg3, $sf_arg3(%rbx) + mov $arg4, $sf_arg4(%rbx) + mov $arg5, $sf_arg5(%rbx) + + lea $sf_state_x4(%rbx), $arg1 # start of x4 state on the stack frame + mov $arg1, $sf_state_ptr(%rbx) + + # Initialize the state array to zero + call keccak_1600_init_state + + call keccak_1600_save_state_x4 + + movq \$0, 8*100($arg1) # clear s[100] + + mov $sf_state_ptr(%rbx), $arg1 + mov $arg6, $arg2 +___ +$code .= <<___ if ($win64); + # xlate prologue handles up to six arguments. For one-shot x4 wrappers + # (10 args), the remaining four stay in Win64 stack slots. + mov 64(%rbp), $arg3 # arg7 from stack + mov 72(%rbp), $arg4 # arg8 from stack + mov 80(%rbp), $arg5 # arg9 from stack + mov 88(%rbp), $arg6 # arg10 from stack +___ +$code .= <<___ if (!$win64); + mov 16(%rbp), $arg3 # arg7 from stack + mov 24(%rbp), $arg4 # arg8 from stack + mov 32(%rbp), $arg5 # arg9 from stack + mov 40(%rbp), $arg6 # arg10 from stack +___ +$code.=<<___; + # Internal entry avoids Win64 xlate prologue argument remapping. +___ +$code .= call_internal("SHA3_shake128_x4_inc_absorb_avx512vl_internal"); +$code.=<<___; + + mov $sf_state_ptr(%rbx), $arg1 + call .L_SHA3_shake128_x4_inc_finalize_avx512vl + + # squeeze + mov $sf_arg1(%rbx), $arg1 + mov $sf_arg2(%rbx), $arg2 + mov $sf_arg3(%rbx), $arg3 + mov $sf_arg4(%rbx), $arg4 + mov $sf_arg5(%rbx), $arg5 + mov $sf_state_ptr(%rbx), $arg6 +___ +$code .= call_internal("SHA3_shake128_x4_inc_squeeze_avx512vl_internal"); +$code.=<<___; + + # Clear the temporary buffer + lea $sf_state_x4(%rbx), %r9 + vpxorq %ymm31, %ymm31, %ymm31 + vmovdqu64 %ymm31, 32*0(%r9) + vmovdqu64 %ymm31, 32*1(%r9) + vmovdqu64 %ymm31, 32*2(%r9) + vmovdqu64 %ymm31, 32*3(%r9) + vmovdqu64 %ymm31, 32*4(%r9) + vmovdqu64 %ymm31, 32*5(%r9) + vmovdqu64 %ymm31, 32*6(%r9) + vmovdqu64 %ymm31, 32*7(%r9) + vmovdqu64 %ymm31, 32*8(%r9) + vmovdqu64 %ymm31, 32*9(%r9) + vmovdqu64 %ymm31, 32*10(%r9) + vmovdqu64 %ymm31, 32*11(%r9) + vmovdqu64 %ymm31, 32*12(%r9) + vmovdqu64 %ymm31, 32*13(%r9) + vmovdqu64 %ymm31, 32*14(%r9) + vmovdqu64 %ymm31, 32*15(%r9) + vmovdqu64 %ymm31, 32*16(%r9) + vmovdqu64 %ymm31, 32*17(%r9) + vmovdqu64 %ymm31, 32*18(%r9) + vmovdqu64 %ymm31, 32*19(%r9) + vmovdqu64 %ymm31, 32*20(%r9) + vmovdqu64 %ymm31, 32*21(%r9) + vmovdqu64 %ymm31, 32*22(%r9) + vmovdqu64 %ymm31, 32*23(%r9) + vmovdqu64 %ymm31, 32*24(%r9) + vmovq %xmm31, 32*25(%r9) + +.Lshake128_x4_epilogue: +___ +$code .= <<___ if ($win64); + vmovups $sf_size+0(%rsp), %xmm6 + vmovups $sf_size+16(%rsp), %xmm7 + vmovups $sf_size+32(%rsp), %xmm8 + vmovups $sf_size+48(%rsp), %xmm9 + vmovups $sf_size+64(%rsp), %xmm10 + vmovups $sf_size+80(%rsp), %xmm11 + vmovups $sf_size+96(%rsp), %xmm12 + vmovups $sf_size+112(%rsp), %xmm13 + vmovups $sf_size+128(%rsp), %xmm14 + vmovups $sf_size+144(%rsp), %xmm15 + add \$160, %rsp +___ +$code.=<<___; + add \$$sf_size, %rsp + pop %rbx +.cfi_pop %rbx + pop %rbp +.cfi_pop %rbp + ret +.cfi_endproc +.size SHA3_shake128_x4_avx512vl,.-SHA3_shake128_x4_avx512vl + +___ + +$code .= <<___ if ($win64); +# Internal Win64 shim for absorb entry. It establishes xlate-compatible +# unwind state and then jumps to the function entry after the prologue. +# This is required for internal calls since the xlate ABI conversion +# is already done in the caller function. +.type SHA3_shake128_x4_inc_absorb_avx512vl_internal,\@abi-omnipotent +.align 32 +.LSEH_begin_SHA3_shake128_x4_inc_absorb_avx512vl_internal: +SHA3_shake128_x4_inc_absorb_avx512vl_internal: + mov %rsp, %rax + mov $arg1, 8(%rsp) + mov $arg2, 16(%rsp) + jmp .L_SHA3_shake128_x4_inc_absorb_avx512vl +.LSEH_end_SHA3_shake128_x4_inc_absorb_avx512vl_internal: +.size SHA3_shake128_x4_inc_absorb_avx512vl_internal,.-SHA3_shake128_x4_inc_absorb_avx512vl_internal +___ +$code.=<<___; + +# SHA3_shake128_x4_inc_absorb_avx512vl +# Absorb input data into 4 parallel SHAKE128 states +# Arguments: +# arg1 (rdi): pointer to state context (808 bytes) +# arg2 (rsi): pointer to lane 0 input data +# arg3 (rdx): pointer to lane 1 input data +# arg4 (rcx): pointer to lane 2 input data +# arg5 (r8): pointer to lane 3 input data +# arg6 (r9): input length in bytes (must be same for all lanes) +# Returns: void +# Note: Input is XORed into state and Keccak permutation is applied for each rate-sized block +.globl SHA3_shake128_x4_inc_absorb_avx512vl +.type SHA3_shake128_x4_inc_absorb_avx512vl,\@function,6 +.align 32 +SHA3_shake128_x4_inc_absorb_avx512vl: +.L_SHA3_shake128_x4_inc_absorb_avx512vl: +.cfi_startproc + push %rbp +.cfi_push %rbp + push %rbx +.cfi_push %rbx + push %r12 +.cfi_push %r12 + push %r13 +.cfi_push %r13 + push %r14 +.cfi_push %r14 + push %r15 +.cfi_push %r15 +___ +$code .= <<___ if ($win64); + sub \$160, %rsp + vmovups %xmm6, 0(%rsp) + vmovups %xmm7, 16(%rsp) + vmovups %xmm8, 32(%rsp) + vmovups %xmm9, 48(%rsp) + vmovups %xmm10, 64(%rsp) + vmovups %xmm11, 80(%rsp) + vmovups %xmm12, 96(%rsp) + vmovups %xmm13, 112(%rsp) + vmovups %xmm14, 128(%rsp) + vmovups %xmm15, 144(%rsp) +___ +$code.=<<___; + +.Lshake128_absorb_body: + # check for partially processed block + mov 8*100($arg1), %r14 + or %r14, %r14 # s[100] == 0? + je .Lshake128_absorb_main_loop_start + + # process remaining bytes if message long enough + mov \$168, %r12 # SHAKE128_RATE = 168 + sub %r14, %r12 # %r12 = capacity + + cmp %r12, $arg6 # if mlen <= capacity then no permute + jbe .Lshake128_absorb_skip_permute + + sub %r12, $arg6 + mov $arg6, %r11 # preserve remaining length across helper calls + + # r10/state, arg2-arg5/inputs, r12/length + mov $arg1, %r10 # %r10 = state + call keccak_1600_partial_add_x4 # arg2-arg5 are updated + + call keccak_1600_load_state_x4 + + call keccak_1600_permute + + movq \$0, 8*100($arg1) # clear s[100] + jmp .Lshake128_absorb_partial_block_done + +.Lshake128_absorb_skip_permute: + # r10/state, arg2-arg5/inputs, r12/length + mov $arg1, %r10 + mov $arg6, %r12 + mov $arg6, %r11 # preserve input length across helper call + call keccak_1600_partial_add_x4 + + lea (%r11,%r14), %r15 + mov %r15, 8*100($arg1) # s[100] += inlen + + cmp \$168, %r15 # check s[100] below SHAKE128_RATE + jb .Lshake128_absorb_exit + + call keccak_1600_load_state_x4 + + call keccak_1600_permute + + call keccak_1600_save_state_x4 + + movq \$0, 8*100($arg1) # clear s[100] + jmp .Lshake128_absorb_exit + +.Lshake128_absorb_main_loop_start: + call keccak_1600_load_state_x4 + mov $arg6, %r11 # full input length when no prior partial block + +.Lshake128_absorb_partial_block_done: + xor %r12, %r12 # zero message offset + + # Process the input message in blocks +.align 32 +.Lshake128_absorb_while_loop: + cmp \$168, %r11 # compare mlen to SHAKE128_RATE + jb .Lshake128_absorb_while_loop_done + + # Inline absorb_bytes_x4 for SHAKE128_RATE (168 bytes = 21 ymm registers) +___ + +# Generate absorb code for SHAKE128 rate (168 bytes) +for (my $i = 0; $i < 21; $i++) { + my $offset = $i * 8; + $code.=<<___; + vmovq $offset($arg2,%r12), %xmm31 + vpinsrq \$1, $offset($arg3,%r12), %xmm31, %xmm31 + vmovq $offset($arg4,%r12), %xmm30 + vpinsrq \$1, $offset($arg5,%r12), %xmm30, %xmm30 + vinserti32x4 \$1, %xmm30, %ymm31, %ymm31 + vpxorq %ymm31, %ymm$i, %ymm$i +___ +} + +$code.=<<___; + sub \$168, %r11 # Subtract the rate from the remaining length + add \$168, %r12 # Adjust offset to next block + call keccak_1600_permute # Perform the Keccak permutation + + jmp .Lshake128_absorb_while_loop + +.align 32 +.Lshake128_absorb_while_loop_done: + call keccak_1600_save_state_x4 + + mov %r11, 8*100($arg1) # update s[100] + or %r11, %r11 + jz .Lshake128_absorb_exit + + movq \$0, 8*100($arg1) # clear s[100] + + # r10/state, arg2-arg5/input, r12/length + mov $arg1, %r10 + add %r12, $arg2 + add %r12, $arg3 + add %r12, $arg4 + add %r12, $arg5 + mov %r11, %r12 + call keccak_1600_partial_add_x4 + + mov %r11, 8*100($arg1) # update s[100] + +.Lshake128_absorb_exit: + # Clear sensitive registers + vpxorq %xmm16, %xmm16, %xmm16 + vmovdqa64 %ymm16, %ymm17 + vmovdqa64 %ymm16, %ymm18 + vmovdqa64 %ymm16, %ymm19 + vmovdqa64 %ymm16, %ymm20 + vmovdqa64 %ymm16, %ymm21 + vmovdqa64 %ymm16, %ymm22 + vmovdqa64 %ymm16, %ymm23 + vmovdqa64 %ymm16, %ymm24 + vmovdqa64 %ymm16, %ymm25 + vmovdqa64 %ymm16, %ymm26 + vmovdqa64 %ymm16, %ymm27 + vmovdqa64 %ymm16, %ymm28 + vmovdqa64 %ymm16, %ymm29 + vmovdqa64 %ymm16, %ymm30 + vmovdqa64 %ymm16, %ymm31 +.Lshake128_absorb_epilogue: + vzeroall +___ +$code .= <<___ if ($win64); + vmovups 0(%rsp), %xmm6 + vmovups 16(%rsp), %xmm7 + vmovups 32(%rsp), %xmm8 + vmovups 48(%rsp), %xmm9 + vmovups 64(%rsp), %xmm10 + vmovups 80(%rsp), %xmm11 + vmovups 96(%rsp), %xmm12 + vmovups 112(%rsp), %xmm13 + vmovups 128(%rsp), %xmm14 + vmovups 144(%rsp), %xmm15 + add \$160, %rsp +___ +$code.=<<___; + + pop %r15 +.cfi_pop %r15 + pop %r14 +.cfi_pop %r14 + pop %r13 +.cfi_pop %r13 + pop %r12 +.cfi_pop %r12 + pop %rbx +.cfi_pop %rbx + pop %rbp +.cfi_pop %rbp + ret +.cfi_endproc +.size SHA3_shake128_x4_inc_absorb_avx512vl,.-SHA3_shake128_x4_inc_absorb_avx512vl + + +# SHA3_shake128_x4_inc_finalize_avx512vl +# Finalize absorption phase for 4 parallel SHAKE-128 states +# Adds padding and terminator bytes and clears the absorb offset +# Arguments: +# arg1 (rdi): pointer to state context (808 bytes) +# Returns: void +# Note: After this call, state is ready for squeezing output +.globl SHA3_shake128_x4_inc_finalize_avx512vl +.type SHA3_shake128_x4_inc_finalize_avx512vl,\@function,1 +.align 32 +SHA3_shake128_x4_inc_finalize_avx512vl: +.L_SHA3_shake128_x4_inc_finalize_avx512vl: +.cfi_startproc + mov 8*100($arg1), %r11 # load state offset from s[100] + mov %r11, %r10 + and \$~7, %r10d # offset to the state register + and \$7, %r11d # offset within the register + + # add EOM byte right after the message + vmovdqu32 ($arg1,%r10,4), %ymm31 + lea shake_msg_pad_x4(%rip), %r9 + sub %r11, %r9 + vmovdqu32 (%r9), %ymm30 + vpxorq %ymm30, %ymm31, %ymm31 + vmovdqu32 %ymm31, ($arg1,%r10,4) + + # add terminating byte at offset equal to rate - 1 (SHAKE128_RATE = 168) + vmovdqu32 640($arg1), %ymm31 # 168*4 - 32 = 672 - 32 = 640 + vmovdqa32 shake_terminator_byte_x4(%rip), %ymm30 + vpxorq %ymm30, %ymm31, %ymm31 + vmovdqu32 %ymm31, 640($arg1) + + movq \$0, 8*100($arg1) # clear s[100] + vpxorq %ymm31, %ymm31, %ymm31 + ret +.cfi_endproc +.size SHA3_shake128_x4_inc_finalize_avx512vl,.-SHA3_shake128_x4_inc_finalize_avx512vl + +___ + +$code .= <<___ if ($win64); +# Internal Win64 shim for squeeze entry. It establishes xlate-compatible +# unwind state and then jumps to the function entry after the prologue. +# This is required for internal calls since the xlate ABI conversion +# is already done in the caller function. +.type SHA3_shake128_x4_inc_squeeze_avx512vl_internal,\@abi-omnipotent +.align 32 +.LSEH_begin_SHA3_shake128_x4_inc_squeeze_avx512vl_internal: +SHA3_shake128_x4_inc_squeeze_avx512vl_internal: + mov %rsp, %rax + mov $arg1, 8(%rsp) + mov $arg2, 16(%rsp) + jmp .L_SHA3_shake128_x4_inc_squeeze_avx512vl +.LSEH_end_SHA3_shake128_x4_inc_squeeze_avx512vl_internal: +.size SHA3_shake128_x4_inc_squeeze_avx512vl_internal,.-SHA3_shake128_x4_inc_squeeze_avx512vl_internal +___ +$code.=<<___; + +# SHA3_shake128_x4_inc_squeeze_avx512vl +# Squeeze output from 4 parallel SHAKE128 states +# Arguments: +# arg1 (rdi): pointer to lane 0 output buffer +# arg2 (rsi): pointer to lane 1 output buffer +# arg3 (rdx): pointer to lane 2 output buffer +# arg4 (rcx): pointer to lane 3 output buffer +# arg5 (r8): output length in bytes (must be same for all lanes) +# arg6 (r9): pointer to state context (808 bytes) +# Returns: void +# Note: Can be called multiple times to generate arbitrary-length output +.globl SHA3_shake128_x4_inc_squeeze_avx512vl +.type SHA3_shake128_x4_inc_squeeze_avx512vl,\@function,6 +.align 32 +SHA3_shake128_x4_inc_squeeze_avx512vl: +.L_SHA3_shake128_x4_inc_squeeze_avx512vl: +.cfi_startproc + push %rbp +.cfi_push %rbp + push %rbx +.cfi_push %rbx + push %r12 +.cfi_push %r12 + push %r13 +.cfi_push %r13 + push %r14 +.cfi_push %r14 + push %r15 +.cfi_push %r15 +___ +$code .= <<___ if ($win64); + sub \$160, %rsp + vmovups %xmm6, 0(%rsp) + vmovups %xmm7, 16(%rsp) + vmovups %xmm8, 32(%rsp) + vmovups %xmm9, 48(%rsp) + vmovups %xmm10, 64(%rsp) + vmovups %xmm11, 80(%rsp) + vmovups %xmm12, 96(%rsp) + vmovups %xmm13, 112(%rsp) + vmovups %xmm14, 128(%rsp) + vmovups %xmm15, 144(%rsp) +___ +$code.=<<___; + +.Lshake128_squeeze_body: + or $arg5, $arg5 + jz .Lshake128_squeeze_done + + # check for partially processed block + mov 8*100($arg6), %r15 # s[100] - capacity + or %r15, %r15 + jnz .Lshake128_squeeze_no_init_permute + + mov $arg1, %r14 + mov $arg6, $arg1 + call keccak_1600_load_state_x4 + + mov %r14, $arg1 + + xor %rbp, %rbp + jmp .Lshake128_squeeze_loop + +.align 32 +.Lshake128_squeeze_no_init_permute: + # extract bytes: r10 - state/src, arg1-arg4 - output/dst, r12 - length = min(capacity, outlen), r11 - offset + mov $arg6, %r10 + mov $arg6, %r14 # preserve state pointer across extract helper + + mov %r15, %r12 + cmp %r15, $arg5 + cmovnae $arg5, %r12 # %r12 = min(capacity, outlen) + + sub %r12, $arg5 # outlen -= length + + mov \$168, %r11d # SHAKE128_RATE + sub %r15, %r11 # state offset + + sub %r12, %r15 # capacity -= length + mov %r15, 8*100($arg6) # update s[100] + + call keccak_1600_extract_bytes_x4 + mov %r14, $arg6 # restore state pointer after helper clobbers + + or %r15, %r15 + jnz .Lshake128_squeeze_done # check s[100] not zero + + mov $arg1, %r13 # preserve arg1 + mov %r14, $arg1 + call keccak_1600_load_state_x4 + + mov %r13, $arg1 + xor %rbp, %rbp + +.align 32 +.Lshake128_squeeze_loop: + cmp \$168, $arg5 # outlen > SHAKE128_RATE + jb .Lshake128_squeeze_final_extract + + call keccak_1600_permute + + # Extract SHAKE128 rate bytes (168 bytes = 21 x 8 bytes) inline +___ + +# Generate extract code for SHAKE128 rate (168 bytes = 21 ymm registers) +for (my $i = 0; $i < 21; $i++) { + my $offset = $i * 8; + $code.=<<___; + vextracti64x2 \$1, %ymm$i, %xmm31 + vmovq %xmm$i, $offset($arg1,%rbp) + vpextrq \$1, %xmm$i, $offset($arg2,%rbp) + vmovq %xmm31, $offset($arg3,%rbp) + vpextrq \$1, %xmm31, $offset($arg4,%rbp) +___ +} + +$code.=<<___; + add \$168, %rbp # dst offset += SHAKE128_RATE + sub \$168, $arg5 # outlen -= SHAKE128_RATE + jmp .Lshake128_squeeze_loop + +.align 32 +.Lshake128_squeeze_final_extract: + or $arg5, $arg5 + jz .Lshake128_squeeze_no_end_permute + + # update output pointers + add %rbp, $arg1 + add %rbp, $arg2 + add %rbp, $arg3 + add %rbp, $arg4 + + mov \$168, %r15d # SHAKE128_RATE + sub $arg5, %r15 + mov %r15, 8*100($arg6) # s[100] = capacity + + call keccak_1600_permute + + mov $arg1, %r14 + mov $arg6, $arg1 + call keccak_1600_save_state_x4 + + mov %r14, $arg1 + + # extract bytes: r10 - state/src, arg1-arg4 - output/dst, r12 - length, r11 - offset = 0 + mov $arg6, %r10 + mov $arg5, %r12 + xor %r11, %r11 + call keccak_1600_extract_bytes_x4 + + jmp .Lshake128_squeeze_done + +.Lshake128_squeeze_no_end_permute: + movq \$0, 8*100($arg6) # s[100] = 0 + mov $arg6, $arg1 + call keccak_1600_save_state_x4 + +.Lshake128_squeeze_done: + # Clear sensitive registers + vpxorq %xmm16, %xmm16, %xmm16 + vmovdqa64 %ymm16, %ymm17 + vmovdqa64 %ymm16, %ymm18 + vmovdqa64 %ymm16, %ymm19 + vmovdqa64 %ymm16, %ymm20 + vmovdqa64 %ymm16, %ymm21 + vmovdqa64 %ymm16, %ymm22 + vmovdqa64 %ymm16, %ymm23 + vmovdqa64 %ymm16, %ymm24 + vmovdqa64 %ymm16, %ymm25 + vmovdqa64 %ymm16, %ymm26 + vmovdqa64 %ymm16, %ymm27 + vmovdqa64 %ymm16, %ymm28 + vmovdqa64 %ymm16, %ymm29 + vmovdqa64 %ymm16, %ymm30 + vmovdqa64 %ymm16, %ymm31 +.Lshake128_squeeze_epilogue: + vzeroall +___ +$code .= <<___ if ($win64); + vmovups 0(%rsp), %xmm6 + vmovups 16(%rsp), %xmm7 + vmovups 32(%rsp), %xmm8 + vmovups 48(%rsp), %xmm9 + vmovups 64(%rsp), %xmm10 + vmovups 80(%rsp), %xmm11 + vmovups 96(%rsp), %xmm12 + vmovups 112(%rsp), %xmm13 + vmovups 128(%rsp), %xmm14 + vmovups 144(%rsp), %xmm15 + add \$160, %rsp +___ +$code.=<<___; + + pop %r15 +.cfi_pop %r15 + pop %r14 +.cfi_pop %r14 + pop %r13 +.cfi_pop %r13 + pop %r12 +.cfi_pop %r12 + pop %rbx +.cfi_pop %rbx + pop %rbp +.cfi_pop %rbp + ret +.cfi_endproc +.size SHA3_shake128_x4_inc_squeeze_avx512vl,.-SHA3_shake128_x4_inc_squeeze_avx512vl + + +# SHAKE256 x4 multi-buffer functions +# These functions process 4 independent SHAKE256 streams in parallel using AVX-512VL +# State layout: 25 ymm registers (200 bytes each) + 1 qword = 808 bytes per context +# Rate: 136 bytes for SHAKE256 + +# SHA3_shake256_x4_avx512vl +# One-shot SHAKE-256 x4 function: init + absorb + finalize + squeeze +# Arguments: +# arg1 (rdi): pointer to output lane 0 +# arg2 (rsi): pointer to output lane 1 +# arg3 (rdx): pointer to output lane 2 +# arg4 (rcx): pointer to output lane 3 +# arg5 (r8): output length in bytes (must be same for all lanes) +# arg6 (r9): pointer to input lane 0 +# [stack+0]: pointer to input lane 1 +# [stack+8]: pointer to input lane 2 +# [stack+16]: pointer to input lane 3 +# [stack+24]: input length in bytes (must be same for all lanes) +# Returns: void +.globl SHA3_shake256_x4_avx512vl +.type SHA3_shake256_x4_avx512vl,\@function,10 +.align 32 +SHA3_shake256_x4_avx512vl: +.cfi_startproc + push %rbp +.cfi_push %rbp + mov %rsp, %rbp + push %rbx +.cfi_push %rbx +___ +$code .= <<___ if ($win64); + sub \$160, %rsp + vmovups %xmm6, 0(%rsp) + vmovups %xmm7, 16(%rsp) + vmovups %xmm8, 32(%rsp) + vmovups %xmm9, 48(%rsp) + vmovups %xmm10, 64(%rsp) + vmovups %xmm11, 80(%rsp) + vmovups %xmm12, 96(%rsp) + vmovups %xmm13, 112(%rsp) + vmovups %xmm14, 128(%rsp) + vmovups %xmm15, 144(%rsp) +___ +$code.=<<___; + + sub \$$sf_size, %rsp + mov %rsp, %rbx + +.Lshake256_x4_body: + mov $arg1, $sf_arg1(%rbx) + mov $arg2, $sf_arg2(%rbx) + mov $arg3, $sf_arg3(%rbx) + mov $arg4, $sf_arg4(%rbx) + mov $arg5, $sf_arg5(%rbx) + + lea $sf_state_x4(%rbx), $arg1 # start of x4 state on the stack frame + mov $arg1, $sf_state_ptr(%rbx) + + # Initialize the state array to zero + call keccak_1600_init_state + + call keccak_1600_save_state_x4 + + movq \$0, 8*100($arg1) # clear s[100] + + mov $sf_state_ptr(%rbx), $arg1 + mov $arg6, $arg2 +___ +$code .= <<___ if ($win64); + # xlate prologue handles up to six arguments. For one-shot x4 wrappers + # (10 args), the remaining four stay in Win64 stack slots. + mov 64(%rbp), $arg3 # arg7 from stack + mov 72(%rbp), $arg4 # arg8 from stack + mov 80(%rbp), $arg5 # arg9 from stack + mov 88(%rbp), $arg6 # arg10 from stack +___ +$code .= <<___ if (!$win64); + mov 16(%rbp), $arg3 # arg7 from stack + mov 24(%rbp), $arg4 # arg8 from stack + mov 32(%rbp), $arg5 # arg9 from stack + mov 40(%rbp), $arg6 # arg10 from stack +___ +$code.=<<___; + # Internal entry avoids Win64 xlate prologue argument remapping. +___ +$code .= call_internal("SHA3_shake256_x4_inc_absorb_avx512vl_internal"); +$code.=<<___; + + mov $sf_state_ptr(%rbx), $arg1 + call .L_SHA3_shake256_x4_inc_finalize_avx512vl + + # squeeze + mov $sf_arg1(%rbx), $arg1 + mov $sf_arg2(%rbx), $arg2 + mov $sf_arg3(%rbx), $arg3 + mov $sf_arg4(%rbx), $arg4 + mov $sf_arg5(%rbx), $arg5 + mov $sf_state_ptr(%rbx), $arg6 +___ +$code .= call_internal("SHA3_shake256_x4_inc_squeeze_avx512vl_internal"); +$code.=<<___; + + # Clear the temporary buffer + lea $sf_state_x4(%rbx), %r9 + vpxorq %ymm31, %ymm31, %ymm31 + vmovdqu64 %ymm31, 32*0(%r9) + vmovdqu64 %ymm31, 32*1(%r9) + vmovdqu64 %ymm31, 32*2(%r9) + vmovdqu64 %ymm31, 32*3(%r9) + vmovdqu64 %ymm31, 32*4(%r9) + vmovdqu64 %ymm31, 32*5(%r9) + vmovdqu64 %ymm31, 32*6(%r9) + vmovdqu64 %ymm31, 32*7(%r9) + vmovdqu64 %ymm31, 32*8(%r9) + vmovdqu64 %ymm31, 32*9(%r9) + vmovdqu64 %ymm31, 32*10(%r9) + vmovdqu64 %ymm31, 32*11(%r9) + vmovdqu64 %ymm31, 32*12(%r9) + vmovdqu64 %ymm31, 32*13(%r9) + vmovdqu64 %ymm31, 32*14(%r9) + vmovdqu64 %ymm31, 32*15(%r9) + vmovdqu64 %ymm31, 32*16(%r9) + vmovdqu64 %ymm31, 32*17(%r9) + vmovdqu64 %ymm31, 32*18(%r9) + vmovdqu64 %ymm31, 32*19(%r9) + vmovdqu64 %ymm31, 32*20(%r9) + vmovdqu64 %ymm31, 32*21(%r9) + vmovdqu64 %ymm31, 32*22(%r9) + vmovdqu64 %ymm31, 32*23(%r9) + vmovdqu64 %ymm31, 32*24(%r9) + vmovq %xmm31, 32*25(%r9) + +.Lshake256_x4_epilogue: +___ +$code .= <<___ if ($win64); + vmovups $sf_size+0(%rsp), %xmm6 + vmovups $sf_size+16(%rsp), %xmm7 + vmovups $sf_size+32(%rsp), %xmm8 + vmovups $sf_size+48(%rsp), %xmm9 + vmovups $sf_size+64(%rsp), %xmm10 + vmovups $sf_size+80(%rsp), %xmm11 + vmovups $sf_size+96(%rsp), %xmm12 + vmovups $sf_size+112(%rsp), %xmm13 + vmovups $sf_size+128(%rsp), %xmm14 + vmovups $sf_size+144(%rsp), %xmm15 + add \$160, %rsp +___ +$code.=<<___; + add \$$sf_size, %rsp + pop %rbx +.cfi_pop %rbx + pop %rbp +.cfi_pop %rbp + ret +.cfi_endproc +.size SHA3_shake256_x4_avx512vl,.-SHA3_shake256_x4_avx512vl + +___ + +$code .= <<___ if ($win64); +# Internal Win64 shim for absorb entry. It establishes xlate-compatible +# unwind state and then jumps to the function entry after the prologue. +# This is required for internal calls since the xlate ABI conversion +# is already done in the caller function. +.type SHA3_shake256_x4_inc_absorb_avx512vl_internal,\@abi-omnipotent +.align 32 +.LSEH_begin_SHA3_shake256_x4_inc_absorb_avx512vl_internal: +SHA3_shake256_x4_inc_absorb_avx512vl_internal: + mov %rsp, %rax + mov $arg1, 8(%rsp) + mov $arg2, 16(%rsp) + jmp .L_SHA3_shake256_x4_inc_absorb_avx512vl +.LSEH_end_SHA3_shake256_x4_inc_absorb_avx512vl_internal: +.size SHA3_shake256_x4_inc_absorb_avx512vl_internal,.-SHA3_shake256_x4_inc_absorb_avx512vl_internal +___ +$code.=<<___; + +# SHA3_shake256_x4_inc_absorb_avx512vl +# Absorb input data into 4 parallel SHAKE256 states +# Arguments: +# arg1 (rdi): pointer to state context (808 bytes) +# arg2 (rsi): pointer to lane 0 input data +# arg3 (rdx): pointer to lane 1 input data +# arg4 (rcx): pointer to lane 2 input data +# arg5 (r8): pointer to lane 3 input data +# arg6 (r9): input length in bytes (must be same for all lanes) +# Returns: void +# Note: Input is XORed into state and Keccak permutation is applied for each rate-sized block +.globl SHA3_shake256_x4_inc_absorb_avx512vl +.type SHA3_shake256_x4_inc_absorb_avx512vl,\@function,6 +.align 32 +SHA3_shake256_x4_inc_absorb_avx512vl: +.L_SHA3_shake256_x4_inc_absorb_avx512vl: +.cfi_startproc + push %rbp +.cfi_push %rbp + push %rbx +.cfi_push %rbx + push %r12 +.cfi_push %r12 + push %r13 +.cfi_push %r13 + push %r14 +.cfi_push %r14 + push %r15 +.cfi_push %r15 +___ +$code .= <<___ if ($win64); + sub \$160, %rsp + vmovups %xmm6, 0(%rsp) + vmovups %xmm7, 16(%rsp) + vmovups %xmm8, 32(%rsp) + vmovups %xmm9, 48(%rsp) + vmovups %xmm10, 64(%rsp) + vmovups %xmm11, 80(%rsp) + vmovups %xmm12, 96(%rsp) + vmovups %xmm13, 112(%rsp) + vmovups %xmm14, 128(%rsp) + vmovups %xmm15, 144(%rsp) +___ +$code.=<<___; + +.Lshake256_absorb_body: + # check for partially processed block + mov 8*100($arg1), %r14 + or %r14, %r14 # s[100] == 0? + je .Lshake256_absorb_main_loop_start + + # process remaining bytes if message long enough + mov \$136, %r12 # SHAKE256_RATE = 136 + sub %r14, %r12 # %r12 = capacity + + cmp %r12, $arg6 # if mlen <= capacity then no permute + jbe .Lshake256_absorb_skip_permute + + sub %r12, $arg6 + mov $arg6, %r11 # preserve remaining length across helper calls + + # r10/state, arg2-arg5/inputs, r12/length + mov $arg1, %r10 # %r10 = state + call keccak_1600_partial_add_x4 # arg2-arg5 are updated + + call keccak_1600_load_state_x4 + + call keccak_1600_permute + + movq \$0, 8*100($arg1) # clear s[100] + jmp .Lshake256_absorb_partial_block_done + +.Lshake256_absorb_skip_permute: + # r10/state, arg2-arg5/inputs, r12/length + mov $arg1, %r10 + mov $arg6, %r12 + mov $arg6, %r11 # preserve input length across helper call + call keccak_1600_partial_add_x4 + + lea (%r11,%r14), %r15 + mov %r15, 8*100($arg1) # s[100] += inlen + + cmp \$136, %r15 # check s[100] below SHAKE256_RATE + jb .Lshake256_absorb_exit + + call keccak_1600_load_state_x4 + + call keccak_1600_permute + + call keccak_1600_save_state_x4 + + movq \$0, 8*100($arg1) # clear s[100] + jmp .Lshake256_absorb_exit + +.Lshake256_absorb_main_loop_start: + call keccak_1600_load_state_x4 + mov $arg6, %r11 # full input length when no prior partial block + +.Lshake256_absorb_partial_block_done: + xor %r12, %r12 # zero message offset + + # Process the input message in blocks +.align 32 +.Lshake256_absorb_while_loop: + cmp \$136, %r11 # compare mlen to SHAKE256_RATE + jb .Lshake256_absorb_while_loop_done + + # Inline absorb_bytes_x4 for SHAKE256_RATE (136 bytes = 17 ymm registers) +___ + +# Generate absorb code for SHAKE256 rate (136 bytes) +for (my $i = 0; $i < 17; $i++) { + my $offset = $i * 8; + $code.=<<___; + vmovq $offset($arg2,%r12), %xmm31 + vpinsrq \$1, $offset($arg3,%r12), %xmm31, %xmm31 + vmovq $offset($arg4,%r12), %xmm30 + vpinsrq \$1, $offset($arg5,%r12), %xmm30, %xmm30 + vinserti32x4 \$1, %xmm30, %ymm31, %ymm31 + vpxorq %ymm31, %ymm$i, %ymm$i +___ +} + +$code.=<<___; + sub \$136, %r11 # Subtract the rate from the remaining length + add \$136, %r12 # Adjust offset to next block + call keccak_1600_permute # Perform the Keccak permutation + + jmp .Lshake256_absorb_while_loop + +.align 32 +.Lshake256_absorb_while_loop_done: + call keccak_1600_save_state_x4 + + mov %r11, 8*100($arg1) # update s[100] + or %r11, %r11 + jz .Lshake256_absorb_exit + + movq \$0, 8*100($arg1) # clear s[100] + + # r10/state, arg2-arg5/input, r12/length + mov $arg1, %r10 + add %r12, $arg2 + add %r12, $arg3 + add %r12, $arg4 + add %r12, $arg5 + mov %r11, %r12 + call keccak_1600_partial_add_x4 + + mov %r11, 8*100($arg1) # update s[100] + +.Lshake256_absorb_exit: + # Clear sensitive registers + vpxorq %xmm16, %xmm16, %xmm16 + vmovdqa64 %ymm16, %ymm17 + vmovdqa64 %ymm16, %ymm18 + vmovdqa64 %ymm16, %ymm19 + vmovdqa64 %ymm16, %ymm20 + vmovdqa64 %ymm16, %ymm21 + vmovdqa64 %ymm16, %ymm22 + vmovdqa64 %ymm16, %ymm23 + vmovdqa64 %ymm16, %ymm24 + vmovdqa64 %ymm16, %ymm25 + vmovdqa64 %ymm16, %ymm26 + vmovdqa64 %ymm16, %ymm27 + vmovdqa64 %ymm16, %ymm28 + vmovdqa64 %ymm16, %ymm29 + vmovdqa64 %ymm16, %ymm30 + vmovdqa64 %ymm16, %ymm31 +.Lshake256_absorb_epilogue: + vzeroall +___ +$code .= <<___ if ($win64); + vmovups 0(%rsp), %xmm6 + vmovups 16(%rsp), %xmm7 + vmovups 32(%rsp), %xmm8 + vmovups 48(%rsp), %xmm9 + vmovups 64(%rsp), %xmm10 + vmovups 80(%rsp), %xmm11 + vmovups 96(%rsp), %xmm12 + vmovups 112(%rsp), %xmm13 + vmovups 128(%rsp), %xmm14 + vmovups 144(%rsp), %xmm15 + add \$160, %rsp +___ +$code.=<<___; + + pop %r15 +.cfi_pop %r15 + pop %r14 +.cfi_pop %r14 + pop %r13 +.cfi_pop %r13 + pop %r12 +.cfi_pop %r12 + pop %rbx +.cfi_pop %rbx + pop %rbp +.cfi_pop %rbp + ret +.cfi_endproc +.size SHA3_shake256_x4_inc_absorb_avx512vl,.-SHA3_shake256_x4_inc_absorb_avx512vl + + +# SHA3_shake256_x4_inc_finalize_avx512vl +# Finalize absorption phase for 4 parallel SHAKE-256 states +# Adds padding and terminator bytes and clears the absorb offset +# Arguments: +# arg1 (rdi): pointer to state context (808 bytes) +# Returns: void +# Note: After this call, state is ready for squeezing output +.globl SHA3_shake256_x4_inc_finalize_avx512vl +.type SHA3_shake256_x4_inc_finalize_avx512vl,\@function,1 +.align 32 +SHA3_shake256_x4_inc_finalize_avx512vl: +.L_SHA3_shake256_x4_inc_finalize_avx512vl: +.cfi_startproc + mov 8*100($arg1), %r11 # load state offset from s[100] + mov %r11, %r10 + and \$~7, %r10d # offset to the state register + and \$7, %r11d # offset within the register + + # add EOM byte right after the message + vmovdqu32 ($arg1,%r10,4), %ymm31 + lea shake_msg_pad_x4(%rip), %r9 + sub %r11, %r9 + vmovdqu32 (%r9), %ymm30 + vpxorq %ymm30, %ymm31, %ymm31 + vmovdqu32 %ymm31, ($arg1,%r10,4) + + # add terminating byte at offset equal to rate - 1 (SHAKE256_RATE = 136) + vmovdqu32 512($arg1), %ymm31 # 136*4 - 32 = 544 - 32 = 512 + vmovdqa32 shake_terminator_byte_x4(%rip), %ymm30 + vpxorq %ymm30, %ymm31, %ymm31 + vmovdqu32 %ymm31, 512($arg1) + + movq \$0, 8*100($arg1) # clear s[100] + vpxorq %ymm31, %ymm31, %ymm31 + ret +.cfi_endproc +.size SHA3_shake256_x4_inc_finalize_avx512vl,.-SHA3_shake256_x4_inc_finalize_avx512vl + +___ + +$code .= <<___ if ($win64); +# Internal Win64 shim for squeeze entry. It establishes xlate-compatible +# unwind state and then jumps to the function entry after the prologue. +# This is required for internal calls since the xlate ABI conversion +# is already done in the caller function. +.type SHA3_shake256_x4_inc_squeeze_avx512vl_internal,\@abi-omnipotent +.align 32 +.LSEH_begin_SHA3_shake256_x4_inc_squeeze_avx512vl_internal: +SHA3_shake256_x4_inc_squeeze_avx512vl_internal: + mov %rsp, %rax + mov $arg1, 8(%rsp) + mov $arg2, 16(%rsp) + jmp .L_SHA3_shake256_x4_inc_squeeze_avx512vl +.LSEH_end_SHA3_shake256_x4_inc_squeeze_avx512vl_internal: +.size SHA3_shake256_x4_inc_squeeze_avx512vl_internal,.-SHA3_shake256_x4_inc_squeeze_avx512vl_internal +___ +$code.=<<___; + +# SHA3_shake256_x4_inc_squeeze_avx512vl +# Squeeze output from 4 parallel SHAKE256 states +# Arguments: +# arg1 (rdi): pointer to lane 0 output buffer +# arg2 (rsi): pointer to lane 1 output buffer +# arg3 (rdx): pointer to lane 2 output buffer +# arg4 (rcx): pointer to lane 3 output buffer +# arg5 (r8): output length in bytes (must be same for all lanes) +# arg6 (r9): pointer to state context (808 bytes) +# Returns: void +# Note: Can be called multiple times to generate arbitrary-length output +.globl SHA3_shake256_x4_inc_squeeze_avx512vl +.type SHA3_shake256_x4_inc_squeeze_avx512vl,\@function,6 +.align 32 +SHA3_shake256_x4_inc_squeeze_avx512vl: +.L_SHA3_shake256_x4_inc_squeeze_avx512vl: +.cfi_startproc + push %rbp +.cfi_push %rbp + push %rbx +.cfi_push %rbx + push %r12 +.cfi_push %r12 + push %r13 +.cfi_push %r13 + push %r14 +.cfi_push %r14 + push %r15 +.cfi_push %r15 +___ +$code .= <<___ if ($win64); + sub \$160, %rsp + vmovups %xmm6, 0(%rsp) + vmovups %xmm7, 16(%rsp) + vmovups %xmm8, 32(%rsp) + vmovups %xmm9, 48(%rsp) + vmovups %xmm10, 64(%rsp) + vmovups %xmm11, 80(%rsp) + vmovups %xmm12, 96(%rsp) + vmovups %xmm13, 112(%rsp) + vmovups %xmm14, 128(%rsp) + vmovups %xmm15, 144(%rsp) +___ +$code.=<<___; + +.Lshake256_squeeze_body: + or $arg5, $arg5 + jz .Lshake256_squeeze_done + + # check for partially processed block + mov 8*100($arg6), %r15 # s[100] - capacity + or %r15, %r15 + jnz .Lshake256_squeeze_no_init_permute + + mov $arg1, %r14 + mov $arg6, $arg1 + call keccak_1600_load_state_x4 + + mov %r14, $arg1 + + xor %rbp, %rbp + jmp .Lshake256_squeeze_loop + +.align 32 +.Lshake256_squeeze_no_init_permute: + # extract bytes: r10 - state/src, arg1-arg4 - output/dst, r12 - length = min(capacity, outlen), r11 - offset + mov $arg6, %r10 + mov $arg6, %r14 # preserve state pointer across extract helper + + mov %r15, %r12 + cmp %r15, $arg5 + cmovnae $arg5, %r12 # %r12 = min(capacity, outlen) + + sub %r12, $arg5 # outlen -= length + + mov \$136, %r11d # SHAKE256_RATE + sub %r15, %r11 # state offset + + sub %r12, %r15 # capacity -= length + mov %r15, 8*100($arg6) # update s[100] + + call keccak_1600_extract_bytes_x4 + mov %r14, $arg6 # restore state pointer after helper clobbers + + or %r15, %r15 + jnz .Lshake256_squeeze_done # check s[100] not zero + + mov $arg1, %r13 # preserve arg1 + mov %r14, $arg1 + call keccak_1600_load_state_x4 + + mov %r13, $arg1 + xor %rbp, %rbp + +.align 32 +.Lshake256_squeeze_loop: + cmp \$136, $arg5 # outlen > SHAKE256_RATE + jb .Lshake256_squeeze_final_extract + + call keccak_1600_permute + + # Extract SHAKE256 rate bytes (136 bytes = 17 x 8 bytes) inline +___ + +# Generate extract code for SHAKE256 rate (136 bytes = 17 ymm registers) +for (my $i = 0; $i < 17; $i++) { + my $offset = $i * 8; + $code.=<<___; + vextracti64x2 \$1, %ymm$i, %xmm31 + vmovq %xmm$i, $offset($arg1,%rbp) + vpextrq \$1, %xmm$i, $offset($arg2,%rbp) + vmovq %xmm31, $offset($arg3,%rbp) + vpextrq \$1, %xmm31, $offset($arg4,%rbp) +___ +} + +$code.=<<___; + add \$136, %rbp # dst offset += SHAKE256_RATE + sub \$136, $arg5 # outlen -= SHAKE256_RATE + jmp .Lshake256_squeeze_loop + +.align 32 +.Lshake256_squeeze_final_extract: + or $arg5, $arg5 + jz .Lshake256_squeeze_no_end_permute + + # update output pointers + add %rbp, $arg1 + add %rbp, $arg2 + add %rbp, $arg3 + add %rbp, $arg4 + + mov \$136, %r15d # SHAKE256_RATE + sub $arg5, %r15 + mov %r15, 8*100($arg6) # s[100] = capacity + + call keccak_1600_permute + + mov $arg1, %r14 + mov $arg6, $arg1 + call keccak_1600_save_state_x4 + + mov %r14, $arg1 + + # extract bytes: r10 - state/src, arg1-arg4 - output/dst, r12 - length, r11 - offset = 0 + mov $arg6, %r10 + mov $arg5, %r12 + xor %r11, %r11 + call keccak_1600_extract_bytes_x4 + + jmp .Lshake256_squeeze_done + +.Lshake256_squeeze_no_end_permute: + movq \$0, 8*100($arg6) # s[100] = 0 + mov $arg6, $arg1 + call keccak_1600_save_state_x4 + +.Lshake256_squeeze_done: + # Clear sensitive registers + vpxorq %xmm16, %xmm16, %xmm16 + vmovdqa64 %ymm16, %ymm17 + vmovdqa64 %ymm16, %ymm18 + vmovdqa64 %ymm16, %ymm19 + vmovdqa64 %ymm16, %ymm20 + vmovdqa64 %ymm16, %ymm21 + vmovdqa64 %ymm16, %ymm22 + vmovdqa64 %ymm16, %ymm23 + vmovdqa64 %ymm16, %ymm24 + vmovdqa64 %ymm16, %ymm25 + vmovdqa64 %ymm16, %ymm26 + vmovdqa64 %ymm16, %ymm27 + vmovdqa64 %ymm16, %ymm28 + vmovdqa64 %ymm16, %ymm29 + vmovdqa64 %ymm16, %ymm30 + vmovdqa64 %ymm16, %ymm31 +.Lshake256_squeeze_epilogue: + vzeroall +___ +$code .= <<___ if ($win64); + vmovups 0(%rsp), %xmm6 + vmovups 16(%rsp), %xmm7 + vmovups 32(%rsp), %xmm8 + vmovups 48(%rsp), %xmm9 + vmovups 64(%rsp), %xmm10 + vmovups 80(%rsp), %xmm11 + vmovups 96(%rsp), %xmm12 + vmovups 112(%rsp), %xmm13 + vmovups 128(%rsp), %xmm14 + vmovups 144(%rsp), %xmm15 + add \$160, %rsp +___ +$code.=<<___; + + pop %r15 +.cfi_pop %r15 + pop %r14 +.cfi_pop %r14 + pop %r13 +.cfi_pop %r13 + pop %r12 +.cfi_pop %r12 + pop %rbx +.cfi_pop %rbx + pop %rbp +.cfi_pop %rbp + ret +.cfi_endproc +.size SHA3_shake256_x4_inc_squeeze_avx512vl,.-SHA3_shake256_x4_inc_squeeze_avx512vl +___ + +if ($win64) { +my $context = "%r8"; +my $disp = "%r9"; + +$code.=<<___; +.extern __imp_RtlVirtualUnwind +.type keccak_se_handler,\@abi-omnipotent +.align 16 +keccak_se_handler: + push %rsi + push %rdi + push %rbx + push %rbp + push %r12 + push %r13 + push %r14 + push %r15 + pushfq + sub \$64, %rsp + + mov 120($context), %rax # context->Rax = original %rsp from xlate prologue + mov 248($context), %rbx # context->Rip + + mov 8($disp), %rsi # disp->ImageBase + mov 56($disp), %r11 # disp->HandlerData + + mov 0(%r11), %r10d # HandlerData[0]: body label (rva) + lea (%rsi,%r10), %r10 + cmp %r10, %rbx # Rip < body? + jb .Lkeccak_in_prologue + + mov 4(%r11), %r10d # HandlerData[1]: epilogue label (rva) + lea (%rsi,%r10), %r10 + cmp %r10, %rbx # Rip >= epilogue? + jae .Lkeccak_in_epilogue + + # In function body: + # HandlerData[2]: delta from context->Rsp(body) to original %rsp + # HandlerData[3]: offset of XMM6 save area from context->Rsp(body), -1 if none + # HandlerData[4]: number of saved non-volatiles in stack frame layout (2 or 6) + # HandlerData[5]: delta from context->Rsp(epilogue) to original %rsp + mov 152($context), %rdx # body rsp + mov 8(%r11), %r10d + lea (%rdx,%r10), %rax # original rsp + jmp .Lkeccak_restore_body_or_epilogue + +.Lkeccak_in_epilogue: + mov 152($context), %rdx # epilogue rsp + mov 20(%r11), %r10d + lea (%rdx,%r10), %rax # original rsp + +.Lkeccak_restore_body_or_epilogue: + mov 8(%rax), %rcx # xlate shadow save of original rdi + mov 16(%rax), %rsi # xlate shadow save of original rsi + mov %rax, 152($context) # context->Rsp = original rsp + mov %rsi, 168($context) # context->Rsi + mov %rcx, 176($context) # context->Rdi + + mov 16(%r11), %r10d # gpr save count + cmp \$6, %r10d + jne .Lkeccak_restore_two + + mov -24(%rax), %r12 + mov -32(%rax), %r13 + mov -40(%rax), %r14 + mov -48(%rax), %r15 + mov %r12, 216($context) # context->R12 + mov %r13, 224($context) # context->R13 + mov %r14, 232($context) # context->R14 + mov %r15, 240($context) # context->R15 + +.Lkeccak_restore_two: + mov -8(%rax), %rbp + mov -16(%rax), %rbx + mov %rbp, 160($context) # context->Rbp + mov %rbx, 144($context) # context->Rbx + + mov 12(%r11), %r10d # xmm save offset from body rsp + cmp \$-1, %r10d + je .Lkeccak_in_prologue + + lea (%rdx,%r10), %rsi # source = xmm save area + lea 512($context), %rdi # &context->Xmm6 + mov \$20, %ecx # 10 XMM * 2 qwords + .long 0xa548f3fc # cld; rep movsq + +.Lkeccak_in_prologue: + mov 8(%rax), %rcx + mov 16(%rax), %rdx + mov %rcx, 176($context) # context->Rdi + mov %rdx, 168($context) # context->Rsi + mov %rax, 152($context) # context->Rsp = original rsp + + mov 40($disp), %rdi # disp->ContextRecord + mov $context, %rsi + mov \$154, %ecx # sizeof(CONTEXT)/8 + .long 0xa548f3fc # cld; rep movsq + + mov $disp, %rsi + xor %rcx, %rcx # UNW_FLAG_NHANDLER + mov 8(%rsi), %rdx # disp->ImageBase + mov 0(%rsi), %r8 # disp->ControlPc + mov 16(%rsi), %r9 # disp->FunctionEntry + mov 40(%rsi), %r10 # disp->ContextRecord + lea 56(%rsi), %r11 # &disp->HandlerData + lea 24(%rsi), %r12 # &disp->EstablisherFrame + mov %r10, 32(%rsp) + mov %r11, 40(%rsp) + mov %r12, 48(%rsp) + mov %rcx, 56(%rsp) + call *__imp_RtlVirtualUnwind(%rip) + + mov \$1, %eax # ExceptionContinueSearch + add \$64, %rsp + popfq + pop %r15 + pop %r14 + pop %r13 + pop %r12 + pop %rbp + pop %rbx + pop %rdi + pop %rsi + ret +.size keccak_se_handler,.-keccak_se_handler + +.section .pdata +.align 4 + .rva .LSEH_begin_SHA3_shake128_x4_avx512vl + .rva .LSEH_end_SHA3_shake128_x4_avx512vl + .rva .LSEH_info_SHA3_shake128_x4_avx512vl + .rva .LSEH_begin_SHA3_shake128_x4_inc_absorb_avx512vl_internal + .rva .LSEH_end_SHA3_shake128_x4_inc_absorb_avx512vl_internal + .rva .LSEH_info_SHA3_shake128_x4_inc_absorb_avx512vl_internal + .rva .LSEH_begin_SHA3_shake128_x4_inc_absorb_avx512vl + .rva .LSEH_end_SHA3_shake128_x4_inc_absorb_avx512vl + .rva .LSEH_info_SHA3_shake128_x4_inc_absorb_avx512vl + .rva .LSEH_begin_SHA3_shake128_x4_inc_squeeze_avx512vl_internal + .rva .LSEH_end_SHA3_shake128_x4_inc_squeeze_avx512vl_internal + .rva .LSEH_info_SHA3_shake128_x4_inc_squeeze_avx512vl_internal + .rva .LSEH_begin_SHA3_shake128_x4_inc_squeeze_avx512vl + .rva .LSEH_end_SHA3_shake128_x4_inc_squeeze_avx512vl + .rva .LSEH_info_SHA3_shake128_x4_inc_squeeze_avx512vl + .rva .LSEH_begin_SHA3_shake256_x4_avx512vl + .rva .LSEH_end_SHA3_shake256_x4_avx512vl + .rva .LSEH_info_SHA3_shake256_x4_avx512vl + .rva .LSEH_begin_SHA3_shake256_x4_inc_absorb_avx512vl_internal + .rva .LSEH_end_SHA3_shake256_x4_inc_absorb_avx512vl_internal + .rva .LSEH_info_SHA3_shake256_x4_inc_absorb_avx512vl_internal + .rva .LSEH_begin_SHA3_shake256_x4_inc_absorb_avx512vl + .rva .LSEH_end_SHA3_shake256_x4_inc_absorb_avx512vl + .rva .LSEH_info_SHA3_shake256_x4_inc_absorb_avx512vl + .rva .LSEH_begin_SHA3_shake256_x4_inc_squeeze_avx512vl_internal + .rva .LSEH_end_SHA3_shake256_x4_inc_squeeze_avx512vl_internal + .rva .LSEH_info_SHA3_shake256_x4_inc_squeeze_avx512vl_internal + .rva .LSEH_begin_SHA3_shake256_x4_inc_squeeze_avx512vl + .rva .LSEH_end_SHA3_shake256_x4_inc_squeeze_avx512vl + .rva .LSEH_info_SHA3_shake256_x4_inc_squeeze_avx512vl + +.section .xdata +.align 8 +.LSEH_info_SHA3_shake128_x4_avx512vl: + .byte 9,0,0,0 + .rva keccak_se_handler + .rva .Lshake128_x4_body,.Lshake128_x4_epilogue + .long 1032,856,2,1032 +.LSEH_info_SHA3_shake128_x4_inc_absorb_avx512vl: + .byte 9,0,0,0 + .rva keccak_se_handler + .rva .Lshake128_absorb_body,.Lshake128_absorb_epilogue + .long 208,0,6,208 +.LSEH_info_SHA3_shake128_x4_inc_absorb_avx512vl_internal: + .byte 9,0,0,0 + .rva keccak_se_handler + .rva .Lshake128_absorb_body,.Lshake128_absorb_epilogue + .long 208,0,6,208 +.LSEH_info_SHA3_shake128_x4_inc_squeeze_avx512vl: + .byte 9,0,0,0 + .rva keccak_se_handler + .rva .Lshake128_squeeze_body,.Lshake128_squeeze_epilogue + .long 208,0,6,208 +.LSEH_info_SHA3_shake128_x4_inc_squeeze_avx512vl_internal: + .byte 9,0,0,0 + .rva keccak_se_handler + .rva .Lshake128_squeeze_body,.Lshake128_squeeze_epilogue + .long 208,0,6,208 +.LSEH_info_SHA3_shake256_x4_avx512vl: + .byte 9,0,0,0 + .rva keccak_se_handler + .rva .Lshake256_x4_body,.Lshake256_x4_epilogue + .long 1032,856,2,1032 +.LSEH_info_SHA3_shake256_x4_inc_absorb_avx512vl: + .byte 9,0,0,0 + .rva keccak_se_handler + .rva .Lshake256_absorb_body,.Lshake256_absorb_epilogue + .long 208,0,6,208 +.LSEH_info_SHA3_shake256_x4_inc_absorb_avx512vl_internal: + .byte 9,0,0,0 + .rva keccak_se_handler + .rva .Lshake256_absorb_body,.Lshake256_absorb_epilogue + .long 208,0,6,208 +.LSEH_info_SHA3_shake256_x4_inc_squeeze_avx512vl: + .byte 9,0,0,0 + .rva keccak_se_handler + .rva .Lshake256_squeeze_body,.Lshake256_squeeze_epilogue + .long 208,0,6,208 +.LSEH_info_SHA3_shake256_x4_inc_squeeze_avx512vl_internal: + .byte 9,0,0,0 + .rva keccak_se_handler + .rva .Lshake256_squeeze_body,.Lshake256_squeeze_epilogue + .long 208,0,6,208 +___ +} + +$code.=<<___; + +.section .rodata align=128 +.align 128 +.type iotas,\@object +iotas: + .quad 0x0000000000000001 + .quad 0x0000000000008082 + .quad 0x800000000000808a + .quad 0x8000000080008000 + .quad 0x000000000000808b + .quad 0x0000000080000001 + .quad 0x8000000080008081 + .quad 0x8000000000008009 + .quad 0x000000000000008a + .quad 0x0000000000000088 + .quad 0x0000000080008009 + .quad 0x000000008000000a + .quad 0x000000008000808b + .quad 0x800000000000008b + .quad 0x8000000000008089 + .quad 0x8000000000008003 + .quad 0x8000000000008002 + .quad 0x8000000000000080 + .quad 0x000000000000800a + .quad 0x800000008000000a + .quad 0x8000000080008081 + .quad 0x8000000000008080 + .quad 0x0000000080000001 + .quad 0x8000000080008008 +.size iotas,.-iotas + +.align 8 +byte_kmask_0_to_7: + .byte 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f + +.align 32 +shake_terminator_byte_x4: + .byte 0, 0, 0, 0, 0, 0, 0, 0x80 + .byte 0, 0, 0, 0, 0, 0, 0, 0x80 + .byte 0, 0, 0, 0, 0, 0, 0, 0x80 + .byte 0, 0, 0, 0, 0, 0, 0, 0x80 + +.align 8 + .byte 0, 0, 0, 0, 0, 0, 0, 0 +shake_msg_pad_x4: + .byte 0x1F, 0, 0, 0, 0, 0, 0, 0 + .byte 0x1F, 0, 0, 0, 0, 0, 0, 0 + .byte 0x1F, 0, 0, 0, 0, 0, 0, 0 + .byte 0x1F, 0, 0, 0, 0, 0, 0, 0 + +.asciz "Keccak-1600 absorb and squeeze for AVX512VL, CRYPTOGAMS by " +___ + +}}} else {{{ + +# When AVX512VL is not available, output stub functions +# The capable function returns 0, and the operation functions are not defined (will use C fallback) + +$code .= <<___; +.text + +.globl SHA3_avx512vl_capable +.type SHA3_avx512vl_capable,\@abi-omnipotent +SHA3_avx512vl_capable: + xor %eax, %eax + ret +.size SHA3_avx512vl_capable, .-SHA3_avx512vl_capable + +.globl SHA3_shake128_x4_inc_absorb_avx512vl +.globl SHA3_shake256_x4_inc_absorb_avx512vl +.globl SHA3_shake128_x4_inc_finalize_avx512vl +.globl SHA3_shake256_x4_inc_finalize_avx512vl +.globl SHA3_shake128_x4_inc_squeeze_avx512vl +.globl SHA3_shake256_x4_inc_squeeze_avx512vl +.globl SHA3_shake128_x4_avx512vl +.globl SHA3_shake256_x4_avx512vl +.type SHA3_shake128_x4_inc_absorb_avx512vl,\@abi-omnipotent +SHA3_shake128_x4_inc_absorb_avx512vl: +SHA3_shake256_x4_inc_absorb_avx512vl: +SHA3_shake128_x4_inc_finalize_avx512vl: +SHA3_shake256_x4_inc_finalize_avx512vl: +SHA3_shake128_x4_inc_squeeze_avx512vl: +SHA3_shake256_x4_inc_squeeze_avx512vl: +SHA3_shake128_x4_avx512vl: +SHA3_shake256_x4_avx512vl: + .byte 0x0f,0x0b # ud2 + ret +.size SHA3_shake128_x4_inc_absorb_avx512vl, .-SHA3_shake128_x4_inc_absorb_avx512vl +___ +}}} + +print $code; +close STDOUT or die "error closing STDOUT: $!"; diff --git a/crypto/sha/build.info b/crypto/sha/build.info index 457ac8d06a..88e8b9cc5e 100644 --- a/crypto/sha/build.info +++ b/crypto/sha/build.info @@ -65,7 +65,7 @@ ENDIF $KECCAK1600ASM=keccak1600.c IF[{- !$disabled{asm} -}] $KECCAK1600ASM_x86= - $KECCAK1600ASM_x86_64=keccak1600-x86_64.s + $KECCAK1600ASM_x86_64=keccak1600-x86_64.s keccak1600x4-avx512vl.s sha3_x4_avx512vl.c $KECCAK1600ASM_s390x=keccak1600-s390x.S @@ -198,4 +198,8 @@ GENERATE[keccak1600-avx512vl.S]=asm/keccak1600-avx512vl.pl GENERATE[keccak1600-mmx.S]=asm/keccak1600-mmx.pl GENERATE[keccak1600p8-ppc.S]=asm/keccak1600p8-ppc.pl +# keccak1600x4-avx512vl.s supports multi-squeeze +# Currently only used in ML-DSA on x86_64 with AVX-512VL support +GENERATE[keccak1600x4-avx512vl.s]=asm/keccak1600x4-avx512vl.pl + GENERATE[sha1-thumb.S]=asm/sha1-thumb.pl diff --git a/crypto/sha/sha3_x4_avx512vl.c b/crypto/sha/sha3_x4_avx512vl.c new file mode 100644 index 0000000000..86a8282814 --- /dev/null +++ b/crypto/sha/sha3_x4_avx512vl.c @@ -0,0 +1,213 @@ +/* + * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. + * Copyright (c) 2026 Intel Corporation. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * SHAKE x4 multi-buffer implementation for AVX-512VL + * + * This file provides incremental API wrappers around the AVX-512VL + * assembly implementations for processing 4 SHAKE instances in parallel. + * + * Callers should check SHA3_avx512vl_capable() before calling. + */ + +#include "internal/sha3.h" +#include +#include + +#if defined(KECCAK1600_ASM) \ + && (defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_X64)) \ + && !defined(OPENSSL_NO_ASM) + +/* External assembly function declarations */ +extern void SHA3_shake128_x4_inc_absorb_avx512vl( + uint64_t *state, + const void *in0, const void *in1, + const void *in2, const void *in3, + size_t inlen); + +extern void SHA3_shake256_x4_inc_absorb_avx512vl( + uint64_t *state, + const void *in0, const void *in1, + const void *in2, const void *in3, + size_t inlen); + +extern void SHA3_shake128_x4_inc_finalize_avx512vl(uint64_t *state); +extern void SHA3_shake256_x4_inc_finalize_avx512vl(uint64_t *state); + +extern void SHA3_shake128_x4_inc_squeeze_avx512vl( + void *out0, void *out1, + void *out2, void *out3, + size_t outlen, + uint64_t *state); + +extern void SHA3_shake256_x4_inc_squeeze_avx512vl( + void *out0, void *out1, + void *out2, void *out3, + size_t outlen, + uint64_t *state); + +/* One-shot assembly function declarations */ +extern void SHA3_shake128_x4_avx512vl( + void *out0, void *out1, + void *out2, void *out3, + size_t outlen, + const void *in0, const void *in1, + const void *in2, const void *in3, + size_t inlen); + +extern void SHA3_shake256_x4_avx512vl( + void *out0, void *out1, + void *out2, void *out3, + size_t outlen, + const void *in0, const void *in1, + const void *in2, const void *in3, + size_t inlen); + +/* + * SHAKE-128 x4 Implementation + */ + +void ossl_sha3_shake128_x4_inc_init_avx512vl(KECCAK1600_X4_AVX512VL_CTX *ctx) +{ + memset(ctx->A, 0, sizeof(ctx->A)); + ctx->rate = SHA3_BLOCKSIZE(128); + ctx->finalized = 0; +} + +void ossl_sha3_shake128_x4_inc_absorb_avx512vl( + KECCAK1600_X4_AVX512VL_CTX *ctx, + const void *in0, const void *in1, + const void *in2, const void *in3, + size_t inlen) +{ + if (ctx->finalized) { + /* Error: cannot absorb after finalize */ + return; + } + + SHA3_shake128_x4_inc_absorb_avx512vl( + ctx->A, in0, in1, in2, in3, inlen); +} + +void ossl_sha3_shake128_x4_inc_cleanup_avx512vl(KECCAK1600_X4_AVX512VL_CTX *ctx) +{ + OPENSSL_cleanse(ctx, sizeof(*ctx)); +} + +static void ossl_sha3_shake128_x4_inc_finalize_avx512vl(KECCAK1600_X4_AVX512VL_CTX *ctx) +{ + if (ctx->finalized) { + return; /* Already finalized */ + } + + SHA3_shake128_x4_inc_finalize_avx512vl(ctx->A); + ctx->finalized = 1; +} + +void ossl_sha3_shake128_x4_inc_squeeze_avx512vl( + void *out0, void *out1, + void *out2, void *out3, + size_t outlen, + KECCAK1600_X4_AVX512VL_CTX *ctx) +{ + if (!ctx->finalized) { + /* Auto-finalize on first squeeze */ + ossl_sha3_shake128_x4_inc_finalize_avx512vl(ctx); + } + + SHA3_shake128_x4_inc_squeeze_avx512vl( + out0, out1, out2, out3, outlen, ctx->A); +} + +/* + * SHAKE-256 x4 Implementation + */ + +void ossl_sha3_shake256_x4_inc_init_avx512vl(KECCAK1600_X4_AVX512VL_CTX *ctx) +{ + memset(ctx->A, 0, sizeof(ctx->A)); + ctx->rate = SHA3_BLOCKSIZE(256); + ctx->finalized = 0; +} + +void ossl_sha3_shake256_x4_inc_absorb_avx512vl( + KECCAK1600_X4_AVX512VL_CTX *ctx, + const void *in0, const void *in1, + const void *in2, const void *in3, + size_t inlen) +{ + if (ctx->finalized) { + /* Error: cannot absorb after finalize */ + return; + } + + SHA3_shake256_x4_inc_absorb_avx512vl( + ctx->A, in0, in1, in2, in3, inlen); +} + +void ossl_sha3_shake256_x4_inc_cleanup_avx512vl(KECCAK1600_X4_AVX512VL_CTX *ctx) +{ + OPENSSL_cleanse(ctx, sizeof(*ctx)); +} + +static void ossl_sha3_shake256_x4_inc_finalize_avx512vl(KECCAK1600_X4_AVX512VL_CTX *ctx) +{ + if (ctx->finalized) { + return; /* Already finalized */ + } + + SHA3_shake256_x4_inc_finalize_avx512vl(ctx->A); + ctx->finalized = 1; +} + +void ossl_sha3_shake256_x4_inc_squeeze_avx512vl( + void *out0, void *out1, + void *out2, void *out3, + size_t outlen, + KECCAK1600_X4_AVX512VL_CTX *ctx) +{ + if (!ctx->finalized) { + /* Auto-finalize on first squeeze */ + ossl_sha3_shake256_x4_inc_finalize_avx512vl(ctx); + } + + SHA3_shake256_x4_inc_squeeze_avx512vl( + out0, out1, out2, out3, outlen, ctx->A); +} + +/* + * Single-call wrapper APIs + */ + +void ossl_sha3_shake128_x4_avx512vl( + void *out0, void *out1, + void *out2, void *out3, + size_t outlen, + const void *in0, const void *in1, + const void *in2, const void *in3, + size_t inlen) +{ + SHA3_shake128_x4_avx512vl(out0, out1, out2, out3, outlen, + in0, in1, in2, in3, inlen); +} + +void ossl_sha3_shake256_x4_avx512vl( + void *out0, void *out1, + void *out2, void *out3, + size_t outlen, + const void *in0, const void *in1, + const void *in2, const void *in3, + size_t inlen) +{ + SHA3_shake256_x4_avx512vl(out0, out1, out2, out3, outlen, + in0, in1, in2, in3, inlen); +} + +#endif /* KECCAK1600_ASM && x86_64 && !OPENSSL_NO_ASM */ diff --git a/include/internal/sha3.h b/include/internal/sha3.h index f91d00a74f..0e0ab76fb5 100644 --- a/include/internal/sha3.h +++ b/include/internal/sha3.h @@ -65,4 +65,75 @@ int ossl_shake_squeeze_default(KECCAK1600_CTX *ctx, unsigned char *out, size_t o size_t SHA3_absorb(uint64_t A[5][5], const unsigned char *inp, size_t len, size_t r); +/* Multi-buffer (x4) Keccak-f[1600] context and API */ +#if defined(KECCAK1600_ASM) \ + && (defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_X64)) \ + && !defined(OPENSSL_NO_ASM) + +/* Runtime capability check for AVX512VL */ +int SHA3_avx512vl_capable(void); + +/* Context for 4-way parallel SHAKE operations */ +typedef struct { + /* 4 interleaved Keccak states (800 bytes) + plus 8 bytes to store the number of + already absorbed or not yet squeezed bytes */ + uint64_t A[(25 * 4) + 1]; + size_t rate; /* Rate in bytes: 168 (SHAKE-128) or 136 (SHAKE-256) */ + unsigned finalized; /* Has finalize been called? 0=no, 1=yes */ +} KECCAK1600_X4_AVX512VL_CTX; + +/* SHAKE-128 x4 incremental API */ +void ossl_sha3_shake128_x4_inc_init_avx512vl(KECCAK1600_X4_AVX512VL_CTX *ctx); + +void ossl_sha3_shake128_x4_inc_absorb_avx512vl( + KECCAK1600_X4_AVX512VL_CTX *ctx, + const void *in0, const void *in1, + const void *in2, const void *in3, + size_t inlen); + +void ossl_sha3_shake128_x4_inc_cleanup_avx512vl(KECCAK1600_X4_AVX512VL_CTX *ctx); + +void ossl_sha3_shake128_x4_inc_squeeze_avx512vl( + void *out0, void *out1, + void *out2, void *out3, + size_t outlen, + KECCAK1600_X4_AVX512VL_CTX *ctx); + +/* SHAKE-256 x4 incremental API */ +void ossl_sha3_shake256_x4_inc_init_avx512vl(KECCAK1600_X4_AVX512VL_CTX *ctx); + +void ossl_sha3_shake256_x4_inc_absorb_avx512vl( + KECCAK1600_X4_AVX512VL_CTX *ctx, + const void *in0, const void *in1, + const void *in2, const void *in3, + size_t inlen); + +void ossl_sha3_shake256_x4_inc_cleanup_avx512vl(KECCAK1600_X4_AVX512VL_CTX *ctx); + +void ossl_sha3_shake256_x4_inc_squeeze_avx512vl( + void *out0, void *out1, + void *out2, void *out3, + size_t outlen, + KECCAK1600_X4_AVX512VL_CTX *ctx); + +/* Single-call SHAKE x4 APIs (wrapper functions) */ +void ossl_sha3_shake128_x4_avx512vl( + void *out0, void *out1, + void *out2, void *out3, + size_t outlen, + const void *in0, const void *in1, + const void *in2, const void *in3, + size_t inlen); + +void ossl_sha3_shake256_x4_avx512vl( + void *out0, void *out1, + void *out2, void *out3, + size_t outlen, + const void *in0, const void *in1, + const void *in2, const void *in3, + size_t inlen); + +#endif /* KECCAK1600_ASM && x86_64 && !OPENSSL_NO_ASM */ + #endif /* OSSL_INTERNAL_SHA3_H */ diff --git a/providers/fips-sources.checksums b/providers/fips-sources.checksums index 527e124d76..598c3bb44d 100644 --- a/providers/fips-sources.checksums +++ b/providers/fips-sources.checksums @@ -270,6 +270,7 @@ ff65c82c56e341f47df03d0c74de7fb537de0e68a4fa23fa07a9fdb51c511f1c crypto/ml_dsa/ 3e0980e67842c4d8637fa449ac41e9d650c614c1074c29f1021605d229a4f73d crypto/ml_dsa/ml_dsa_params.c 10e37ab3ee09a45d99007665e073efb2b062c819f30af8694c6b0f411eb33822 crypto/ml_dsa/ml_dsa_poly.h 26be5266a9f1a33999a5a68c96cffc7932ba64521d9554dabe7397591611c852 crypto/ml_dsa/ml_dsa_sample.c +9e57d844f2acedb490b6e8f32e125240078ddab380a7faa533baa9c447dee262 crypto/ml_dsa/ml_dsa_sample_hw_x86_64.inc 2127303173eff12cb2b71f92179d10370b555c26e7a305e87ce7066580d57689 crypto/ml_dsa/ml_dsa_sign.c 5217ef237e21872205703b95577290c34898423466a465c7bd609b2eb4627964 crypto/ml_dsa/ml_dsa_sign.h e3ef4cf1598420c94eee4f53d13491ff0f9dc8cfb1fe1cacd3a1225e2073fa56 crypto/ml_dsa/ml_dsa_vector.h @@ -359,6 +360,7 @@ b7f1f4e69d41812dba39226aea0942fb2e3a638308c3130b0c83bb636a258ce6 crypto/sha/asm 82a84e6ae5ffe90e76530de560f6261c5004c23923fa9fd9aa2aae3852976426 crypto/sha/asm/keccak1600-s390x.pl e485942ed7f7bf1f376059ed2e5e194907348f44a4308dfc4bbc2d8be05fed99 crypto/sha/asm/keccak1600-x86_64.pl af173b53537e18453705a3843e0629334b37e8ca03483a2b164fd48252289da3 crypto/sha/asm/keccak1600p8-ppc.pl +d27078e0478f34536f596e037887ad673b05537d9cd4b79376e902774a4c8340 crypto/sha/asm/keccak1600x4-avx512vl.pl 3dd5e288f70b684d337f4054119935ab46af4042a40dffdca203868fe943bae0 crypto/sha/asm/sha1-586.pl 58378cb694b61022d70956ab344331d7577f9a26431acd00a60bed91229b29eb crypto/sha/asm/sha1-alpha.pl 8576b406e8596c8ac56a351a92175accfcf4121dd3dbffc59bae7d0ece0b0dab crypto/sha/asm/sha1-armv4-large.pl @@ -401,6 +403,7 @@ feb4283e1bb2a36f0a98d4cd7b43ac78c40b72efeab8d58b4f0aad8b65a1d2ba crypto/sha/asm f6021e6e5f4694579ba5a014e2aba44b6d436b2fd25c18ecd474a7383042b027 crypto/sha/sha256.c ed6049496d9786296d011a4582ffcfe0859a609b37f1f4ce57cee36136ee526c crypto/sha/sha3.c db0f16a4cb9c86f971b4defbee9dccd77345766f64efc45e0f1bc8d448bfb3e3 crypto/sha/sha3_encode.c +8def0b2e5996f2a2e187abcb7443e5e98edd4be0f71ff918a1b8c717cb97eb27 crypto/sha/sha3_x4_avx512vl.c 833e0990a4b8590a40990bfe1cfe59542ece6923fde37d3dd6faddfe1b9418fc crypto/sha/sha512.c 7b99b3c9fa26c5e58a56cbbb62b6e2bfe62542a662799f910387e76e5688a13a crypto/sha/sha_local.h dfd99e02830973ab349409ac6ba0ee901ba7736216030965bd7e5a54356abd7c crypto/slh_dsa/slh_adrs.c @@ -497,7 +500,7 @@ ee75ecd35b3ae90c51ace957ab7ce06de3c7d5064b97a878241ff65cc943a6db include/intern 3f476694478c1125574dfb2e75c4c0c0d04c7d3d763176686a4730028513fcd5 include/internal/rcu.h b6e33da6011b2b74d27e39f27cf98e6f123fe47826562b6479d0dbd5c758c4c2 include/internal/refcount.h f77c0844cc44bd92965647cd8cb6addb210f0300a8d1090da8c26e4382e87c2c include/internal/safe_math.h -ea565ff98b36d3ae2d8eb709a52cba75720b30e2527cdfda3b90aa426291ac38 include/internal/sha3.h +17f6585bc81ad324d00aaacd558e0176dbe22cc7cbdb353c8c9072fa40000a58 include/internal/sha3.h 8e672cc0620606b044f63b8446125f5233d64e3eea59df54c1fcca6bc90ba537 include/internal/sizes.h 188be736ff23a2202fe594e6d49a7ccf4c23a7baa86e2dac5d58360f21d9c986 include/internal/skey.h abf03dc8635f2925bdc2299feabe115f8d5d6eaa450b421172ded222872386ba include/internal/ssl3_cbc.h diff --git a/providers/fips.checksum b/providers/fips.checksum index b70425fb45..2c7bab5eab 100644 --- a/providers/fips.checksum +++ b/providers/fips.checksum @@ -1 +1 @@ -7e5428946b50e077e099dd7bd3891b854b37c623ede0cc8ace51c09b94871db7 providers/fips-sources.checksums +864d3535e188440ca60b8268ce8137e0cffb870be990531fd9f68a23f0ffa898 providers/fips-sources.checksums diff --git a/providers/fips.module.sources b/providers/fips.module.sources index 7b404dbece..5358458573 100644 --- a/providers/fips.module.sources +++ b/providers/fips.module.sources @@ -270,6 +270,7 @@ crypto/ml_dsa/ml_dsa_ntt.c crypto/ml_dsa/ml_dsa_params.c crypto/ml_dsa/ml_dsa_poly.h crypto/ml_dsa/ml_dsa_sample.c +crypto/ml_dsa/ml_dsa_sample_hw_x86_64.inc crypto/ml_dsa/ml_dsa_sign.c crypto/ml_dsa/ml_dsa_sign.h crypto/ml_dsa/ml_dsa_vector.h @@ -359,6 +360,7 @@ crypto/sha/asm/keccak1600-ppc64.pl crypto/sha/asm/keccak1600-s390x.pl crypto/sha/asm/keccak1600-x86_64.pl crypto/sha/asm/keccak1600p8-ppc.pl +crypto/sha/asm/keccak1600x4-avx512vl.pl crypto/sha/asm/sha1-586.pl crypto/sha/asm/sha1-alpha.pl crypto/sha/asm/sha1-armv4-large.pl @@ -401,6 +403,7 @@ crypto/sha/sha1dgst.c crypto/sha/sha256.c crypto/sha/sha3.c crypto/sha/sha3_encode.c +crypto/sha/sha3_x4_avx512vl.c crypto/sha/sha512.c crypto/sha/sha_local.h crypto/slh_dsa/slh_adrs.c From 27caecd416627c18f9d038aa076f270afbfa4d60 Mon Sep 17 00:00:00 2001 From: Marcel Cornu Date: Mon, 13 Apr 2026 16:44:04 +0000 Subject: [PATCH 257/349] test: add SHAKE x4 internal cross-validation tests Add a new `sha3_x4_internal_test` target and recipe to validate the internal SHAKE x4 implementation against scalar SHA3 reference paths. Cover SHAKE-128 and SHAKE-256 in one-shot and incremental modes, plus multi-absorb and multi-squeeze cases across varied input and output sizes. Tests are skipped when AVX512VL extensions are not available. Signed-off-by: Marcel Cornu Reviewed-by: Viktor Dukhovni Reviewed-by: Neil Horman Reviewed-by: Shane Lontis MergeDate: Mon Jul 13 15:55:55 2026 (Merged from https://github.com/openssl/openssl/pull/31090) --- test/build.info | 6 + test/recipes/03-test_sha3_x4_internal.t | 16 + test/sha3_x4_internal_test.c | 430 ++++++++++++++++++++++++ 3 files changed, 452 insertions(+) create mode 100644 test/recipes/03-test_sha3_x4_internal.t create mode 100644 test/sha3_x4_internal_test.c diff --git a/test/build.info b/test/build.info index a5dcc7f89d..5c730f5482 100644 --- a/test/build.info +++ b/test/build.info @@ -941,6 +941,8 @@ IF[{- !$disabled{tests} -}] PROGRAMS{noinst}=cmactest ENDIF + PROGRAMS{noinst}=sha3_x4_internal_test + SOURCE[poly1305_internal_test]=poly1305_internal_test.c INCLUDE[poly1305_internal_test]=.. ../include ../apps/include DEPEND[poly1305_internal_test]=../libcrypto.a libtestutil.a @@ -949,6 +951,10 @@ IF[{- !$disabled{tests} -}] INCLUDE[chacha_internal_test]=.. ../include ../apps/include DEPEND[chacha_internal_test]=../libcrypto.a libtestutil.a + SOURCE[sha3_x4_internal_test]=sha3_x4_internal_test.c + INCLUDE[sha3_x4_internal_test]=.. ../include ../apps/include + DEPEND[sha3_x4_internal_test]=../libcrypto.a libtestutil.a + SOURCE[asn1_internal_test]=asn1_internal_test.c INCLUDE[asn1_internal_test]=.. ../include ../apps/include DEPEND[asn1_internal_test]=../libcrypto.a libtestutil.a diff --git a/test/recipes/03-test_sha3_x4_internal.t b/test/recipes/03-test_sha3_x4_internal.t new file mode 100644 index 0000000000..9e5793aaf3 --- /dev/null +++ b/test/recipes/03-test_sha3_x4_internal.t @@ -0,0 +1,16 @@ +#! /usr/bin/env perl +# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. +# Copyright (c) 2026 Intel Corporation. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +use strict; +use OpenSSL::Test; +use OpenSSL::Test::Simple; + +setup("test_sha3_x4_internal"); + +simple_test("test_sha3_x4_internal", "sha3_x4_internal_test"); diff --git a/test/sha3_x4_internal_test.c b/test/sha3_x4_internal_test.c new file mode 100644 index 0000000000..aa6a452db6 --- /dev/null +++ b/test/sha3_x4_internal_test.c @@ -0,0 +1,430 @@ +/* + * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. + * Copyright (c) 2026 Intel Corporation. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * Internal cross-validation tests for the SHAKE x4 multi-buffer API. + * + * Each test computes SHAKE-128 or SHAKE-256 on four independent inputs + * using the x4 (AVX-512VL) path and compares every lane's output to the + * equivalent result produced by the scalar ossl_sha3_* API. + * + * Tests cover: + * - Single-call (ossl_sha3_shake{128,256}_x4_avx512vl) for many (inlen, outlen) pairs + * - Incremental init/absorb/squeeze for the same (inlen, outlen) pairs + * - Multi-absorb: input split at every possible block boundary + * - Multi-squeeze: output produced in two successive squeeze calls + */ + +#include +#include "testutil.h" + +/* + * KECCAK1600_ASM is only added to the library compilation flags by the build + * system, not to test binaries. Since the x4 declarations in internal/sha3.h + * are guarded by that macro, we define it here before the include so that the + * KECCAK1600_X4_AVX512VL_CTX type and function prototypes are visible. + * The symbols themselves live in libcrypto and are always present. + * We additionally gate all x4 code on x86_64 (GCC/Clang: __x86_64__, + * MSVC: _M_AMD64/_M_X64) and !OPENSSL_NO_ASM so that the test still + * compiles on other platforms or in no-asm builds. + */ +#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_X64)) \ + && !defined(OPENSSL_NO_ASM) +#ifndef KECCAK1600_ASM +#define KECCAK1600_ASM +#endif +#endif +#include "internal/sha3.h" + +/* + * A single deterministic 1024-byte message. Each of the four lanes receives + * a different slice of this buffer, with lane base pointers spaced 64 bytes + * apart, so their inputs are distinct yet entirely self-contained. + */ +#define MSG_BUF_SIZE 1024 +#define LANE_STRIDE 64 /* byte offset between lane base pointers */ +#define NUM_LANES 4 + +static unsigned char msg[MSG_BUF_SIZE]; + +/* Maximum output length used in this file – must fit chunk1 + chunk2. */ +#define MAX_OUT 640 + +#if defined(KECCAK1600_ASM) \ + && (defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_X64)) \ + && !defined(OPENSSL_NO_ASM) + +/* + * Input lengths exercising: empty, tiny, sub-block, block boundary ±1, + * multiple blocks and a longer message for SHAKE-128 (rate=168) and + * SHAKE-256 (rate=136). + */ +static const size_t input_sizes[] = { + 0, 1, 17, 100, 135, 136, 137, 168, 169, 200, 400 +}; +#define NUM_INPUT_SIZES (sizeof(input_sizes) / sizeof(input_sizes[0])) + +/* Output lengths chosen to straddle rate boundaries for both variants. */ +static const size_t output_sizes[] = { + 16, 32, 64, 136, 168, 256, 512 +}; +#define NUM_OUTPUT_SIZES (sizeof(output_sizes) / sizeof(output_sizes[0])) + +/* Helpers functions */ + +/* + * Compute a scalar SHAKE-128 or SHAKE-256 digest. + * bitlen: 128 or 256. Returns 1 on success, 0 on failure. + */ +static int scalar_shake(const unsigned int bitlen, + const unsigned char *in, const size_t inlen, + unsigned char *out, const size_t outlen) +{ + KECCAK1600_CTX ctx; + + if (!ossl_sha3_init(&ctx, 0x1f, bitlen)) + return 0; + /* ossl_sha3_init does not populate the method vtable; do it here. */ + ctx.meth.absorb = ossl_sha3_absorb_default; + ctx.meth.final = ossl_sha3_final_default; + ctx.meth.squeeze = ossl_shake_squeeze_default; + return ossl_sha3_absorb(&ctx, in, inlen) + && ossl_sha3_squeeze(&ctx, out, outlen); +} + +/* + * Encode (inlen_idx, outlen_idx) into a single test index and back. + * test index n = inlen_idx * NUM_OUTPUT_SIZES + outlen_idx + */ +static void decode_idx(const int n, size_t *inlen, size_t *outlen) +{ + *inlen = input_sizes[n / (int)NUM_OUTPUT_SIZES]; + *outlen = output_sizes[n % (int)NUM_OUTPUT_SIZES]; +} + +/* One-shot tests */ + +static int test_shake_x4_oneshot(const unsigned int bitlen, const int n) +{ + size_t inlen, outlen; + const unsigned char *in[NUM_LANES]; + unsigned char x4_out[NUM_LANES][MAX_OUT]; + unsigned char ref_out[NUM_LANES][MAX_OUT]; + int i; + + decode_idx(n, &inlen, &outlen); + + for (i = 0; i < NUM_LANES; i++) + in[i] = msg + i * LANE_STRIDE; + + /* Ensure the lane inputs fit within the message buffer. */ + if (!TEST_size_t_le(inlen + (NUM_LANES - 1) * LANE_STRIDE, MSG_BUF_SIZE)) + return 0; + if (!TEST_size_t_le(outlen, MAX_OUT)) + return 0; + + /* x4 single-call */ + if (bitlen == 128) + ossl_sha3_shake128_x4_avx512vl(x4_out[0], x4_out[1], x4_out[2], x4_out[3], + outlen, + in[0], in[1], in[2], in[3], inlen); + else + ossl_sha3_shake256_x4_avx512vl(x4_out[0], x4_out[1], x4_out[2], x4_out[3], + outlen, + in[0], in[1], in[2], in[3], inlen); + + /* scalar reference */ + for (i = 0; i < NUM_LANES; i++) + if (!TEST_true(scalar_shake(bitlen, in[i], inlen, ref_out[i], outlen))) + return 0; + + /* compare */ + for (i = 0; i < NUM_LANES; i++) { + if (!TEST_mem_eq(x4_out[i], outlen, ref_out[i], outlen)) { + TEST_info("SHAKE-%u x4 oneshot lane %d: inlen=%zu outlen=%zu", + bitlen, i, inlen, outlen); + return 0; + } + } + return 1; +} + +static int test_shake128_x4_oneshot(const int n) +{ + return test_shake_x4_oneshot(128, n); +} + +static int test_shake256_x4_oneshot(const int n) +{ + return test_shake_x4_oneshot(256, n); +} + +/* Incremental (init / absorb / finalize / squeeze) tests */ + +static int test_shake_x4_incremental(const unsigned int bitlen, const int n) +{ + size_t inlen, outlen; + const unsigned char *in[NUM_LANES]; + unsigned char x4_out[NUM_LANES][MAX_OUT]; + unsigned char ref_out[NUM_LANES][MAX_OUT]; + KECCAK1600_X4_AVX512VL_CTX ctx; + int i; + + decode_idx(n, &inlen, &outlen); + + for (i = 0; i < NUM_LANES; i++) + in[i] = msg + i * LANE_STRIDE; + + if (!TEST_size_t_le(inlen + (NUM_LANES - 1) * LANE_STRIDE, MSG_BUF_SIZE)) + return 0; + + /* x4 incremental */ + if (bitlen == 128) { + ossl_sha3_shake128_x4_inc_init_avx512vl(&ctx); + ossl_sha3_shake128_x4_inc_absorb_avx512vl(&ctx, in[0], in[1], in[2], in[3], + inlen); + ossl_sha3_shake128_x4_inc_squeeze_avx512vl(x4_out[0], x4_out[1], + x4_out[2], x4_out[3], outlen, &ctx); + } else { + ossl_sha3_shake256_x4_inc_init_avx512vl(&ctx); + ossl_sha3_shake256_x4_inc_absorb_avx512vl(&ctx, in[0], in[1], in[2], in[3], + inlen); + ossl_sha3_shake256_x4_inc_squeeze_avx512vl(x4_out[0], x4_out[1], + x4_out[2], x4_out[3], outlen, &ctx); + } + + /* scalar reference */ + for (i = 0; i < NUM_LANES; i++) + if (!TEST_true(scalar_shake(bitlen, in[i], inlen, ref_out[i], outlen))) + return 0; + + for (i = 0; i < NUM_LANES; i++) { + if (!TEST_mem_eq(x4_out[i], outlen, ref_out[i], outlen)) { + TEST_info("SHAKE-%u x4 incremental lane %d: inlen=%zu outlen=%zu", + bitlen, i, inlen, outlen); + return 0; + } + } + return 1; +} + +static int test_shake128_x4_incremental(const int n) +{ + return test_shake_x4_incremental(128, n); +} + +static int test_shake256_x4_incremental(const int n) +{ + return test_shake_x4_incremental(256, n); +} + +/* Multi-absorb tests */ + +/* + * Split the input at every tested input size, absorbing the two halves + * in separate calls. The split length is chosen as input_sizes[n] so that + * we exercise sub-block, at-block and multi-block split points. + * + * Full message length is fixed at the largest tested input size so that + * every split index is meaningful. + */ +static int test_shake_x4_multi_absorb(const unsigned int bitlen, const int n) +{ + const size_t total = input_sizes[NUM_INPUT_SIZES - 1]; + const size_t split = input_sizes[n]; + const size_t outlen = 64; /* fixed output length for this sub-test */ + const unsigned char *in[NUM_LANES]; + unsigned char x4_out[NUM_LANES][MAX_OUT]; + unsigned char ref_out[NUM_LANES][MAX_OUT]; + KECCAK1600_X4_AVX512VL_CTX ctx; + int i; + + if (split > total) + return 1; /* nothing to test */ + + for (i = 0; i < NUM_LANES; i++) + in[i] = msg + i * LANE_STRIDE; + + if (!TEST_size_t_le(total + (NUM_LANES - 1) * LANE_STRIDE, MSG_BUF_SIZE)) + return 0; + + /* x4 split absorb */ + if (bitlen == 128) { + ossl_sha3_shake128_x4_inc_init_avx512vl(&ctx); + ossl_sha3_shake128_x4_inc_absorb_avx512vl(&ctx, + in[0], in[1], in[2], in[3], split); + ossl_sha3_shake128_x4_inc_absorb_avx512vl(&ctx, + in[0] + split, in[1] + split, in[2] + split, in[3] + split, + total - split); + ossl_sha3_shake128_x4_inc_squeeze_avx512vl(x4_out[0], x4_out[1], + x4_out[2], x4_out[3], outlen, &ctx); + } else { + ossl_sha3_shake256_x4_inc_init_avx512vl(&ctx); + ossl_sha3_shake256_x4_inc_absorb_avx512vl(&ctx, + in[0], in[1], in[2], in[3], split); + ossl_sha3_shake256_x4_inc_absorb_avx512vl(&ctx, + in[0] + split, in[1] + split, in[2] + split, in[3] + split, + total - split); + ossl_sha3_shake256_x4_inc_squeeze_avx512vl(x4_out[0], x4_out[1], + x4_out[2], x4_out[3], outlen, &ctx); + } + + /* scalar reference (single absorb of full message) */ + for (i = 0; i < NUM_LANES; i++) + if (!TEST_true(scalar_shake(bitlen, in[i], total, ref_out[i], outlen))) + return 0; + + for (i = 0; i < NUM_LANES; i++) { + if (!TEST_mem_eq(x4_out[i], outlen, ref_out[i], outlen)) { + TEST_info("SHAKE-%u x4 multi-absorb lane %d: total=%zu split=%zu", + bitlen, i, total, split); + return 0; + } + } + return 1; +} + +static int test_shake128_x4_multi_absorb(const int n) +{ + return test_shake_x4_multi_absorb(128, n); +} + +static int test_shake256_x4_multi_absorb(const int n) +{ + return test_shake_x4_multi_absorb(256, n); +} + +/* Multi-squeeze tests */ + +/* + * Squeeze in two successive calls and verify that the concatenated output + * matches a single scalar squeeze of the same total length. + * Parameterized over output_sizes[] for the first chunk; the second chunk + * is always 64 bytes so the total length varies. + */ +static int test_shake_x4_multi_squeeze(const unsigned int bitlen, const int n) +{ + const size_t inlen = 200; /* fixed input length */ + const size_t chunk1 = output_sizes[n]; + const size_t chunk2 = 64; + const size_t total = chunk1 + chunk2; + const unsigned char *in[NUM_LANES]; + unsigned char x4_a[NUM_LANES][MAX_OUT]; /* first chunk */ + unsigned char x4_b[NUM_LANES][MAX_OUT]; /* second chunk */ + unsigned char ref_out[NUM_LANES][MAX_OUT]; + KECCAK1600_X4_AVX512VL_CTX ctx; + int i; + + if (!TEST_size_t_le(total, MAX_OUT)) + return 0; + if (!TEST_size_t_le(inlen + (NUM_LANES - 1) * LANE_STRIDE, MSG_BUF_SIZE)) + return 0; + + for (i = 0; i < NUM_LANES; i++) + in[i] = msg + i * LANE_STRIDE; + + /* x4 two-shot squeeze */ + if (bitlen == 128) { + ossl_sha3_shake128_x4_inc_init_avx512vl(&ctx); + ossl_sha3_shake128_x4_inc_absorb_avx512vl(&ctx, in[0], in[1], in[2], in[3], + inlen); + /* first squeeze */ + ossl_sha3_shake128_x4_inc_squeeze_avx512vl(x4_a[0], x4_a[1], x4_a[2], x4_a[3], + chunk1, &ctx); + /* second squeeze – context carries state from previous call */ + ossl_sha3_shake128_x4_inc_squeeze_avx512vl(x4_b[0], x4_b[1], x4_b[2], x4_b[3], + chunk2, &ctx); + } else { + ossl_sha3_shake256_x4_inc_init_avx512vl(&ctx); + ossl_sha3_shake256_x4_inc_absorb_avx512vl(&ctx, in[0], in[1], in[2], in[3], + inlen); + ossl_sha3_shake256_x4_inc_squeeze_avx512vl(x4_a[0], x4_a[1], x4_a[2], x4_a[3], + chunk1, &ctx); + ossl_sha3_shake256_x4_inc_squeeze_avx512vl(x4_b[0], x4_b[1], x4_b[2], x4_b[3], + chunk2, &ctx); + } + + /* scalar reference – squeeze the full total in one call */ + for (i = 0; i < NUM_LANES; i++) + if (!TEST_true(scalar_shake(bitlen, in[i], inlen, ref_out[i], total))) + return 0; + + /* check first chunk, then second chunk */ + for (i = 0; i < NUM_LANES; i++) { + if (!TEST_mem_eq(x4_a[i], chunk1, ref_out[i], chunk1)) { + TEST_info("SHAKE-%u x4 multi-squeeze lane %d chunk1: " + "inlen=%zu chunk1=%zu chunk2=%zu", + bitlen, i, inlen, chunk1, chunk2); + return 0; + } + if (!TEST_mem_eq(x4_b[i], chunk2, ref_out[i] + chunk1, chunk2)) { + TEST_info("SHAKE-%u x4 multi-squeeze lane %d chunk2: " + "inlen=%zu chunk1=%zu chunk2=%zu", + bitlen, i, inlen, chunk1, chunk2); + return 0; + } + } + return 1; +} + +static int test_shake128_x4_multi_squeeze(const int n) +{ + return test_shake_x4_multi_squeeze(128, n); +} + +static int test_shake256_x4_multi_squeeze(const int n) +{ + return test_shake_x4_multi_squeeze(256, n); +} + +#endif /* KECCAK1600_ASM && x86_64 && !OPENSSL_NO_ASM */ + +/* Test entry point */ + +int setup_tests(void) +{ + size_t i; + + /* Fill the message buffer with a deterministic non-zero pattern. */ + for (i = 0; i < MSG_BUF_SIZE; i++) + msg[i] = (unsigned char)(251 * i + 17); + +#ifdef OPENSSL_CPUID_OBJ + OPENSSL_cpuid_setup(); +#endif + +#if !defined(KECCAK1600_ASM) \ + || !(defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_X64)) \ + || defined(OPENSSL_NO_ASM) + return TEST_skip("SHAKE x4 API not available in this build"); +#else + if (!SHA3_avx512vl_capable()) { + return TEST_skip("AVX-512VL not available; skipping SHAKE x4 tests"); + } + + ADD_ALL_TESTS(test_shake128_x4_oneshot, + (int)(NUM_INPUT_SIZES * NUM_OUTPUT_SIZES)); + ADD_ALL_TESTS(test_shake256_x4_oneshot, + (int)(NUM_INPUT_SIZES * NUM_OUTPUT_SIZES)); + + ADD_ALL_TESTS(test_shake128_x4_incremental, + (int)(NUM_INPUT_SIZES * NUM_OUTPUT_SIZES)); + ADD_ALL_TESTS(test_shake256_x4_incremental, + (int)(NUM_INPUT_SIZES * NUM_OUTPUT_SIZES)); + + ADD_ALL_TESTS(test_shake128_x4_multi_absorb, (int)NUM_INPUT_SIZES); + ADD_ALL_TESTS(test_shake256_x4_multi_absorb, (int)NUM_INPUT_SIZES); + + ADD_ALL_TESTS(test_shake128_x4_multi_squeeze, (int)NUM_OUTPUT_SIZES); + ADD_ALL_TESTS(test_shake256_x4_multi_squeeze, (int)NUM_OUTPUT_SIZES); +#endif + + return 1; +} From 626ff8fd9344eb46e50464960ce84dcbacf5a4dd Mon Sep 17 00:00:00 2001 From: Marcel Cornu Date: Tue, 5 May 2026 13:33:04 +0000 Subject: [PATCH 258/349] .github: add AVX512VL workflow using Intel SDE Add a new CI workflow that runs AVX512 specific tests under Intel SDE v10.8, since GitHub Actions runners do not currently have AVX512 hardware. SDE emulates AVX512 instructions and spoofs CPUID so the AVX512 code paths can be exercised. Two jobs are included: linux (ubuntu-latest) and windows (windows-2022). Each job builds OpenSSL with no-shared and enable-fips, then runs the following tests under `sde64 -icx` (Icelake Server): - ml_dsa_internal_test: exercises AVX512VL ML-DSA sampling - sha3_x4_internal_test: exercises AVX512VL SHAKE x4 functions - openssl fipsinstall: runs the full FIPS KAT suite (including ML-DSA and SHA3 self-tests) against the FIPS provider under emulation Signed-off-by: Marcel Cornu Reviewed-by: Viktor Dukhovni Reviewed-by: Neil Horman Reviewed-by: Shane Lontis MergeDate: Mon Jul 13 15:55:57 2026 (Merged from https://github.com/openssl/openssl/pull/31090) --- .github/workflows/avx512-sde.yml | 167 +++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 .github/workflows/avx512-sde.yml diff --git a/.github/workflows/avx512-sde.yml b/.github/workflows/avx512-sde.yml new file mode 100644 index 0000000000..1b94df9922 --- /dev/null +++ b/.github/workflows/avx512-sde.yml @@ -0,0 +1,167 @@ +# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. +# Copyright (c) 2026 Intel Corporation. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +# Run AVX512-specific tests under Intel SDE. +# +# GitHub Actions runners currently do not have AVX512 hardware. +# Intel SDE emulates AVX512 instructions and spoofs CPUID, +# so AVX512 code paths are exercised. +# +# To update Intel SDE: find the new mirror ID and file date from +# https://www.intel.com/content/www/us/en/download/684897 +# and update the three env vars below. + +name: AVX512 tests via Intel SDE + +on: + schedule: + - cron: '30 02 * * *' + workflow_dispatch: + +permissions: + contents: read + +env: + SDE_VERSION: 10.8.0 + SDE_DATE: 2026-03-15 + SDE_MIRROR_ID: 915934 + +jobs: + linux: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: install NASM + run: sudo apt-get install -y nasm + + - name: install Intel SDE + run: | + SDE_URL="https://downloadmirror.intel.com/${SDE_MIRROR_ID}/sde-external-${SDE_VERSION}-${SDE_DATE}-lin.tar.xz" + SDE_SHA256="50b320cd226acef7a491f5b321fc1be3c3c7984f9e27a456e64894b5b0979dd3" + curl -fsSL -o /tmp/sde.tar.xz "$SDE_URL" + echo "$SDE_SHA256 /tmp/sde.tar.xz" | sha256sum -c - + mkdir /tmp/sde + tar -xf /tmp/sde.tar.xz -C /tmp/sde/ + sudo mv /tmp/sde/sde-external-${SDE_VERSION}-${SDE_DATE}-lin /opt/sde + echo "/opt/sde" >> "$GITHUB_PATH" + + - name: config + run: | + ./config --banner=Configured --strict-warnings no-shared enable-fips + + - name: build + run: make -j4 + + - name: show CPU and OpenSSL build info + run: | + cat /proc/cpuinfo | grep -m1 "model name" + sde64 -icx -- ./apps/openssl version -c + + - name: ml_dsa_internal_test (AVX512 via SDE) + run: sde64 -icx -- ./test/ml_dsa_internal_test + + - name: sha3_x4_internal_test (AVX512 via SDE) + run: sde64 -icx -- ./test/sha3_x4_internal_test + + - name: fipsinstall (FIPS KAT via SDE) + run: sde64 -icx -- ./apps/openssl fipsinstall -module ./providers/fips.so -out /tmp/fipsmodule.cnf -provider_name fips + + windows: + runs-on: windows-2022 + env: + VCVARS: C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: install nasm + if: github.repository == 'openssl/openssl' + run: | + $installer = "nasm-3.01-installer-x64.exe" + Invoke-WebRequest -Uri "https://openssl-library.org/ci-deps/$installer" -OutFile $installer + $expected = (Get-Content "$env:GITHUB_WORKSPACE\.github\ci-deps.json" -Raw | ConvertFrom-Json).$installer + $actual = (Get-FileHash $installer -Algorithm SHA256).Hash + if ($actual -ne $expected) { throw "SHA256 mismatch for $installer (expected $expected, got $actual)" } + Start-Process -FilePath ".\$installer" -ArgumentList '/S' -Wait + "C:\Program Files\NASM" | Out-File -FilePath "$env:GITHUB_PATH" -Append + - name: install nasm (forks) + if: github.repository != 'openssl/openssl' + run: | + $installer = "nasm-3.01-installer-x64.exe" + Invoke-WebRequest -Uri "https://www.nasm.us/pub/nasm/releasebuilds/3.01/win64/$installer" -OutFile $installer + Start-Process -FilePath ".\$installer" -ArgumentList '/S' -Wait + "C:\Program Files\NASM" | Out-File -FilePath "$env:GITHUB_PATH" -Append + + - name: install jom + if: github.repository == 'openssl/openssl' + run: | + mkdir C:\jom + Invoke-WebRequest -Uri "https://openssl-library.org/ci-deps/jom-1.1.7.exe" -OutFile C:\jom\jom.exe + $expected = (Get-Content "$env:GITHUB_WORKSPACE\.github\ci-deps.json" -Raw | ConvertFrom-Json).'jom-1.1.7.exe' + $actual = (Get-FileHash C:\jom\jom.exe -Algorithm SHA256).Hash + if ($actual -ne $expected) { throw "SHA256 mismatch for jom.exe (expected $expected, got $actual)" } + "C:\jom" | Out-File -FilePath "$env:GITHUB_PATH" -Append + - name: install jom (forks) + if: github.repository != 'openssl/openssl' + run: | + mkdir C:\jom + Invoke-WebRequest -Uri "https://download.qt.io/official_releases/jom/jom_1_1_7.zip" -OutFile C:\jom\jom.zip + Expand-Archive -Path C:\jom\jom.zip -DestinationPath C:\jom + "C:\jom" | Out-File -FilePath "$env:GITHUB_PATH" -Append + + - name: install Intel SDE + run: | + $url = "https://downloadmirror.intel.com/$env:SDE_MIRROR_ID/sde-external-$env:SDE_VERSION-$env:SDE_DATE-win.tar.xz" + $expected = "176F87C80EB42BB91B73E1428F4A0FD067DF322F901F9B4359B20B86B92C2BAE" + curl.exe -fsSL -o sde-win.tar.xz $url + $actual = (Get-FileHash sde-win.tar.xz -Algorithm SHA256).Hash + if ($actual -ne $expected) { throw "SDE SHA256 mismatch: got $actual" } + & "C:\Program Files\7-Zip\7z.exe" x sde-win.tar.xz -so | & "C:\Program Files\7-Zip\7z.exe" x -si -ttar -o"C:\sde" + $sdeRoot = "C:\sde\sde-external-$env:SDE_VERSION-$env:SDE_DATE-win" + if (-not (Test-Path "$sdeRoot\sde.exe")) { throw "sde.exe not found in $sdeRoot" } + "$sdeRoot" | Out-File -FilePath $env:GITHUB_PATH -Append + + - name: prepare build directory + run: mkdir _build + + - name: config + working-directory: _build + shell: cmd + run: | + call "%VCVARS%" + perl ..\Configure --banner=Configured --strict-warnings no-shared enable-fips no-makedepend + + - name: build + working-directory: _build + shell: cmd + run: | + call "%VCVARS%" + jom /j4 /S + + - name: show CPU and OpenSSL build info + working-directory: _build + run: sde -icx -- apps\openssl.exe version -c + + - name: ml_dsa_internal_test (AVX512 via SDE) + working-directory: _build + shell: cmd + run: sde -icx -- test\ml_dsa_internal_test.exe + + - name: sha3_x4_internal_test (AVX512 via SDE) + working-directory: _build + shell: cmd + run: sde -icx -- test\sha3_x4_internal_test.exe + + - name: fipsinstall (FIPS KAT via SDE) + working-directory: _build + shell: cmd + run: sde -icx -- apps\openssl.exe fipsinstall -module providers\fips.dll -out fipsmodule.cnf -provider_name fips From f454c8fd5d97b80f431dba16b8deacad6b69cbfd Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 3 Jul 2026 08:40:06 -0400 Subject: [PATCH 259/349] Fix refcounting for ENCODER/DECODER/STORE methods without caching https://github.com/openssl/openssl/pull/31782 Fixed method refcounting for EVP objects when the provider they are fetched from requests no-caching, but I neglected to add simmilar refcounting fixes for DECODERS/ENCODERS and STORE objects, who follow a different fetch path (these use inner_[decoder|encoder|loader]_fetch rather than inner_evp_generic_fetch. They got missed because the p_ossltest provider that we use to test these paths don't provide these objects, so the path never got exercised. Reviewed-by: Bob Beck Reviewed-by: Paul Dale MergeDate: Tue Jul 14 05:21:36 2026 (Merged from https://github.com/openssl/openssl/pull/31844) --- crypto/encode_decode/decoder_meth.c | 12 +++++++++++- crypto/encode_decode/encoder_meth.c | 15 +++++++++++++-- crypto/store/store_meth.c | 15 +++++++++++++-- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/crypto/encode_decode/decoder_meth.c b/crypto/encode_decode/decoder_meth.c index 995cb675ba..772c29c031 100644 --- a/crypto/encode_decode/decoder_meth.c +++ b/crypto/encode_decode/decoder_meth.c @@ -406,9 +406,19 @@ inner_ossl_decoder_fetch(struct decoder_data_st *methdata, */ if (id == 0 && name != NULL) id = ossl_namemap_name2num(namemap, name); - if (id != 0) + if (id != 0 && methdata->tmp_store == NULL) { ossl_method_store_cache_set(store, prov, id, propq, method, ossl_decoder_up_ref, ossl_decoder_free); + } else { + /* + * Like with EVP methods, if the provider requests no caching we need + * to take an extra refcount here so that the tmp_stored decoder + * lives beyond the freeing of that tmp_store + */ +#ifndef OPENSSL_NO_CACHED_FETCH + OSSL_DECODER_up_ref((OSSL_DECODER *)method); +#endif + } } /* diff --git a/crypto/encode_decode/encoder_meth.c b/crypto/encode_decode/encoder_meth.c index f329d0a307..23dcccebb9 100644 --- a/crypto/encode_decode/encoder_meth.c +++ b/crypto/encode_decode/encoder_meth.c @@ -405,8 +405,19 @@ inner_ossl_encoder_fetch(struct encoder_data_st *methdata, */ if (id == 0) id = ossl_namemap_name2num(namemap, name); - ossl_method_store_cache_set(store, prov, id, propq, method, - ossl_encoder_up_ref, ossl_encoder_free); + if (id != 0 && methdata->tmp_store == NULL) { + ossl_method_store_cache_set(store, prov, id, propq, method, + ossl_encoder_up_ref, ossl_encoder_free); + } else { + /* + * Like with EVP methods, if the provider requests no caching we need + * to take an extra refcount here so that the tmp_stored encoder + * lives beyond the freeing of that tmp_store + */ +#ifndef OPENSSL_NO_CACHED_FETCH + OSSL_ENCODER_up_ref((OSSL_ENCODER *)method); +#endif + } } /* diff --git a/crypto/store/store_meth.c b/crypto/store/store_meth.c index 96b092e5bb..976e4aa734 100644 --- a/crypto/store/store_meth.c +++ b/crypto/store/store_meth.c @@ -353,8 +353,19 @@ inner_loader_fetch(struct loader_data_st *methdata, */ if (id == 0) id = ossl_namemap_name2num(namemap, scheme); - ossl_method_store_cache_set(store, prov, id, propq, method, - up_ref_loader, free_loader); + if (id != 0 && methdata->tmp_store == NULL) { + ossl_method_store_cache_set(store, prov, id, propq, method, + up_ref_loader, free_loader); + } else { + /* + * Like with EVP methods, if the provider requests no caching we need + * to take an extra refcount here so that the tmp_stored loader + * lives beyond the freeing of that tmp_store + */ +#ifndef OPENSSL_NO_CACHED_FETCH + OSSL_STORE_LOADER_up_ref((OSSL_STORE_LOADER *)method); +#endif + } } /* From ac946a80d56672ad783f7239056357b11730e72d Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 3 Jul 2026 09:05:58 -0400 Subject: [PATCH 260/349] ugment p_ossltest with encoder/decoder/store algs Create dummy encoder/decoder and store algs in p_ossltest. They do nothing, except return algorithms on query. This allows our unit test 30-test_evp_list_noncache.t to exercise the refcounting of these objects when the provider requests no caching Reviewed-by: Bob Beck Reviewed-by: Paul Dale MergeDate: Tue Jul 14 05:21:38 2026 (Merged from https://github.com/openssl/openssl/pull/31844) --- test/p_ossltest.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/test/p_ossltest.c b/test/p_ossltest.c index 7864994485..2f545ebf16 100644 --- a/test/p_ossltest.c +++ b/test/p_ossltest.c @@ -1753,6 +1753,84 @@ static const OSSL_ALGORITHM ossltest_rands[] = { { NULL, NULL, NULL } }; +/* + * The implementations for the p_ossltest provider decoder/encoder/store objects + * are defined below, but they are dummy implementations and do nothing. They exist + * for the sole purpose of returning algorithms from this provider so that we can test if + * object creation and refcounting work when a provider requests no caching of the algorithm. + */ + +static int ossl_test_decode(void *ctx, OSSL_CORE_BIO *in, int selection, + OSSL_CALLBACK *cb, void *cbarg, + OSSL_PASSPHRASE_CALLBACK *pb, void *pbarg) +{ + return 1; +} + +static const OSSL_DISPATCH ossl_test_decoder_functions[] = { + { OSSL_FUNC_DECODER_DECODE, (void (*)(void))ossl_test_decode }, + OSSL_DISPATCH_END +}; + +static const OSSL_ALGORITHM ossl_test_decoders[] = { + ALG("p_ossltest_decoder", ossl_test_decoder_functions), + { NULL, NULL, NULL } +}; + +static int ossl_test_encode(void *ctx, OSSL_CORE_BIO *out, const void *obj, + const OSSL_PARAM obj_abstract[], + int selection, OSSL_PASSPHRASE_CALLBACK *pb, + void *pbarg) +{ + return 1; +} + +static const OSSL_DISPATCH ossl_test_encoder_functions[] = { + { OSSL_FUNC_ENCODER_ENCODE, (void (*)(void))ossl_test_encode }, + OSSL_DISPATCH_END +}; + +static const OSSL_ALGORITHM ossl_test_encoders[] = { + ALG("p_ossltest_encoder", ossl_test_encoder_functions), + { NULL, NULL, NULL } +}; + +static int dummy_store_value = 0; + +static void *ossl_test_store_open(void *provctx, const char *uri) +{ + return (void *)&dummy_store_value; +} + +static int ossl_test_store_load(void *ctx, OSSL_CALLBACK *object_fn, void *cbarg, + OSSL_PASSPHRASE_CALLBACK *pb, void *pbarg) +{ + return 0; +} + +static int ossl_test_store_eof(void *ctx) +{ + return 1; +} + +static int ossl_test_store_close(void *ctx) +{ + return 1; +} + +static const OSSL_DISPATCH ossl_test_store_functions[] = { + { OSSL_FUNC_STORE_OPEN, (void (*)(void))ossl_test_store_open }, + { OSSL_FUNC_STORE_LOAD, (void (*)(void))ossl_test_store_load }, + { OSSL_FUNC_STORE_EOF, (void (*)(void))ossl_test_store_eof }, + { OSSL_FUNC_STORE_CLOSE, (void (*)(void))ossl_test_store_close }, + OSSL_DISPATCH_END +}; + +static const OSSL_ALGORITHM ossl_test_stores[] = { + ALG("p_ossltest_store", ossl_test_store_functions), + { NULL, NULL, NULL } +}; + /** * @brief Implement ossltest query. * @@ -1777,6 +1855,12 @@ static const OSSL_ALGORITHM *ossltest_query(void *provctx, int operation_id, return ossltest_ciphers; case OSSL_OP_RAND: return ossltest_rands; + case OSSL_OP_DECODER: + return ossl_test_decoders; + case OSSL_OP_ENCODER: + return ossl_test_encoders; + case OSSL_OP_STORE: + return ossl_test_stores; } return NULL; } From 380d6afcb3737da1e50709e84e538a9125cdb948 Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Tue, 14 Jul 2026 11:34:56 +0200 Subject: [PATCH 261/349] keccak1600x4-avx512vl: fix undefined symbols on macOS The one-shot SHAKE x4 wrappers call the incremental absorb and squeeze routines through call_internal(), which on non-Win64 emitted a call to the public global symbol by its bare name. These calls textually precede the callees' .globl declarations, so x86_64-xlate.pl never prepends the platform's leading underscore to the referenced symbol. On ELF (Linux) that is harmless since symbols carry no leading underscore, but on Mach-O (macOS) the call references the un-decorated SHA3_shake*_x4_inc_*_avx512vl while the defined symbol is _SHA3_shake*_x4_inc_*_avx512vl, leaving four undefined externals and breaking the darwin64-x86_64 link of libcrypto: SHA3_shake128_x4_inc_absorb_avx512vl SHA3_shake256_x4_inc_absorb_avx512vl SHA3_shake128_x4_inc_squeeze_avx512vl SHA3_shake256_x4_inc_squeeze_avx512vl Call the local .L_ entry label instead -- the same address as the public symbol and the pattern the finalize calls already use -- so the reference resolves locally and these internal routines cannot be interposed. The Win64 path is unchanged. Fixes: https://github.com/openssl/openssl/issues/31941 Fixes: a248ec771e ("ML-DSA: Add AVX512VL SHAKE x4 multi-buffer integration") Assisted-by: Pi:z-ai/glm-5.2 Reviewed-by: Milan Broz Reviewed-by: Neil Horman Reviewed-by: Nikola Pajkovsky Reviewed-by: Tomas Mraz MergeDate: Tue Jul 14 12:33:50 2026 (Merged from https://github.com/openssl/openssl/pull/31942) --- crypto/sha/asm/keccak1600x4-avx512vl.pl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crypto/sha/asm/keccak1600x4-avx512vl.pl b/crypto/sha/asm/keccak1600x4-avx512vl.pl index b5a6ac7377..a7fc1814f5 100755 --- a/crypto/sha/asm/keccak1600x4-avx512vl.pl +++ b/crypto/sha/asm/keccak1600x4-avx512vl.pl @@ -85,7 +85,12 @@ $sf_size="856"; # 48 + 808 = 856 bytes # Emit an internal helper call used by one-shot wrappers. # - Win64: call the provided *_internal shim and bracket it with 32-byte # shadow space so shim entry can use xlate-compatible [rsp+8]/[rsp+16]. -# - non-Win64: call the public API symbol (same base name without _internal). +# - non-Win64: call the local function entry label (.L_), which +# sits at the same address as the public symbol. Calling the public +# global symbol by name here would break Mach-O builds: the call textually +# precedes the symbol's .globl declaration, so x86_64-xlate.pl never gets +# a chance to prepend the platform's leading-underscore, leaving an +# undefined reference to the un-decorated name. # The argument must be the shim/internal symbol name, e.g. # SHA3_shake128_x4_inc_squeeze_avx512vl_internal sub call_internal { @@ -101,7 +106,7 @@ sub call_internal { ___ return <<___; - call $external_name + call .L_$external_name ___ } From 857afd02b282314687bc1290d7d12a313d3b9f85 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 11 Jun 2026 19:05:43 +0200 Subject: [PATCH 262/349] test: add Windows unit tests setup and initial dgram test This adds an initial setup for unit testing on Windows that allows mocking of system functions using Detour library. This works only for library functions and not object function like wrap so it is a bit limited but it is still useful for BIO mocking. An initial BIO bss_dgram test is added covering the Windows specific parts. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Igor Ustinov Reviewed-by: Tomas Mraz MergeDate: Wed Jul 15 16:00:35 2026 (Merged from https://github.com/openssl/openssl/pull/31915) --- .github/workflows/windows.yml | 53 +++ Configurations/unix-Makefile.tmpl | 15 +- Configurations/windows-makefile.tmpl | 8 +- Configure | 71 +++- test/recipes/02-test_unit.t | 24 +- test/unit/build.info | 109 ++--- test/unit/crypto/bio/test_bss_dgram_win.c | 478 ++++++++++++++++++++++ 7 files changed, 688 insertions(+), 70 deletions(-) create mode 100644 test/unit/crypto/bio/test_bss_dgram_win.c diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index dd513c658b..6d8ebe39b7 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -190,6 +190,59 @@ jobs: call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" nmake test VERBOSE_FAILURE=yes HARNESS_JOBS=4 + unit-tests: + runs-on: windows-2022 + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - name: checkout fuzz/corpora submodule + run: git submodule update --init --depth 1 fuzz/corpora + - name: install jom + if: github.repository == 'openssl/openssl' + run: | + mkdir C:\jom + Invoke-WebRequest -Uri "https://openssl-library.org/ci-deps/jom-1.1.7.exe" -OutFile C:\jom\jom.exe + $expected = (Get-Content "$env:GITHUB_WORKSPACE\.github\ci-deps.json" -Raw | ConvertFrom-Json).'jom-1.1.7.exe' + $actual = (Get-FileHash C:\jom\jom.exe -Algorithm SHA256).Hash + if ($actual -ne $expected) { throw "SHA256 mismatch for jom.exe (expected $expected, got $actual)" } + "C:\jom" | Out-File -FilePath "$env:GITHUB_PATH" -Append + - name: install jom (forks) + if: github.repository != 'openssl/openssl' + run: | + mkdir C:\jom + Invoke-WebRequest -Uri "https://download.qt.io/official_releases/jom/jom_1_1_7.zip" -OutFile C:\jom\jom.zip + Expand-Archive -Path C:\jom\jom.zip -DestinationPath C:\jom + "C:\jom" | Out-File -FilePath "$env:GITHUB_PATH" -Append + - name: install cmocka and detours via vcpkg + shell: pwsh + run: | + & "$env:VCPKG_INSTALLATION_ROOT\vcpkg.exe" install cmocka:x64-windows-static-md detours:x64-windows-static-md + "VCPKG_INST=$env:VCPKG_INSTALLATION_ROOT\installed\x64-windows-static-md" | Out-File -FilePath $env:GITHUB_ENV -Append + - name: prepare the build directory + run: mkdir _build + - name: config + working-directory: _build + shell: cmd + run: | + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + perl ..\Configure VC-WIN64A --banner=Configured --strict-warnings no-makedepend no-asm enable-unit-tests ^ + --with-cmocka-include=%VCPKG_INST%\include --with-cmocka-lib=%VCPKG_INST%\lib ^ + --with-detours-include=%VCPKG_INST%\include --with-detours-lib=%VCPKG_INST%\lib + perl configdata.pm --dump + - name: build + working-directory: _build + shell: cmd + run: | + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + jom /j4 /S + - name: test + working-directory: _build + shell: cmd + run: | + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + jom test VERBOSE=1 TESTS=test_unit + minimal: runs-on: windows-2022 steps: diff --git a/Configurations/unix-Makefile.tmpl b/Configurations/unix-Makefile.tmpl index f8e44e73f7..0d4ec0f983 100644 --- a/Configurations/unix-Makefile.tmpl +++ b/Configurations/unix-Makefile.tmpl @@ -505,6 +505,7 @@ BIN_LDFLAGS={- join(' ', $target{bin_lflags} || (), BIN_EX_LIBS=$(CNF_EX_LIBS) $(EX_LIBS) CMOCKA_LIBS={- $config{cmocka_libs} // '' -} +DETOURS_LIBS={- $config{detours_libs} // '' -} # CPPFLAGS_Q is used for one thing only: to build up buildinf.h CPPFLAGS_Q={- $cppflags1 =~ s|([\\"])|\\$1|g; @@ -2024,14 +2025,14 @@ EOF } } my $wrapflags = ''; - my $cmocka = ''; if (defined $unified_info{wraps}->{$args{bin}}) { - $wrapflags = join(' ', - map { "-Wl,--wrap=$_" } - @{$unified_info{wraps}->{$args{bin}}}); - $wrapflags = ' ' . $wrapflags; - $cmocka = ' $(CMOCKA_LIBS)'; + $wrapflags = ' ' . join(' ', + map { "-Wl,--wrap=$_" } + @{$unified_info{wraps}->{$args{bin}}}); } + my $utlibs = $unified_info{unit_test_libs}->{$args{bin}}; + $utlibs = $utlibs ne '' ? ' ' . $utlibs : '' if defined $utlibs; + $utlibs //= ''; my $linkflags = join("", map { $_." " } @linkdirs); my $linklibs = join("", map { $_." " } @linklibs); my $cmd = '$(CC)'; @@ -2052,7 +2053,7 @@ $bin: $deps \$\${LDCMD:-$cmd} $cmdflags $linkflags\$(BIN_LDFLAGS)$wrapflags \\ -o $bin \\ $objs \\ - $linklibs\$(BIN_EX_LIBS)$cmocka + $linklibs\$(BIN_EX_LIBS)$utlibs EOF } sub in2script { diff --git a/Configurations/windows-makefile.tmpl b/Configurations/windows-makefile.tmpl index 16fed4670d..a7f2b6652b 100644 --- a/Configurations/windows-makefile.tmpl +++ b/Configurations/windows-makefile.tmpl @@ -380,6 +380,9 @@ BIN_LDFLAGS={- join(' ', $target{bin_lflags} || (), '$(CNF_LDFLAGS)', '$(LDFLAGS)') -} BIN_EX_LIBS=$(CNF_EX_LIBS) $(EX_LIBS) +CMOCKA_LIBS={- $config{cmocka_libs} // '' -} +DETOURS_LIBS={- $config{detours_libs} // '' -} + # CPPFLAGS_Q is used for one thing only: to build up buildinf.h CPPFLAGS_Q={- $cppflags1 =~ s|([\\"])|\\$1|g; $cppflags2 =~ s|([\\"])|\\$1|g; @@ -1001,11 +1004,14 @@ EOF my $ress = join($target{ld_resp_delim}, @ress); my $linklibs = join("", map { "$_$target{ld_resp_delim}" } @deps); my $deps = join(" ", @objs, @ress, @deps); + my $utlibs = $unified_info{unit_test_libs}->{$args{bin}}; + $utlibs = (defined $utlibs && $utlibs ne '') + ? "$utlibs$target{ld_resp_delim}" : ''; return <<"EOF"; $bin: $deps IF EXIST $bin.manifest DEL /F /Q $bin.manifest \$(LD) \$(LDFLAGS) \$(BIN_LDFLAGS) @<< -$objs$target{ld_resp_delim}\$(LDOUTFLAG)$bin$target{ldpostoutflag}$target{ld_resp_delim}$linklibs\$(BIN_EX_LIBS)$target{ldresflag}$target{ldresflag}$ress +$objs$target{ld_resp_delim}\$(LDOUTFLAG)$bin$target{ldpostoutflag}$target{ld_resp_delim}$utlibs$linklibs\$(BIN_EX_LIBS)$target{ldresflag}$target{ldresflag}$ress << IF EXIST $bin.manifest \\ \$(MT) \$(MTFLAGS) \$(MTINFLAG)$bin.manifest \$(MTOUTFLAG)$bin diff --git a/Configure b/Configure index 10a218954e..5ee8597f2e 100755 --- a/Configure +++ b/Configure @@ -1125,6 +1125,14 @@ while (@argvcopy) { $withargs{cmocka_include}=$1; } + elsif (/^--with-detours-lib=(.*)$/) + { + $withargs{detours_lib}=$1; + } + elsif (/^--with-detours-include=(.*)$/) + { + $withargs{detours_include}=$1; + } elsif (/^--with-rand-seed=(.*)$/) { foreach my $x (split(m|,|, $1)) @@ -1927,15 +1935,26 @@ unless ($disabled{winstore}) { push @{$config{openssl_other_defines}}, "OPENSSL_NO_KTLS" if ($disabled{ktls}); +# Keywords accepted in a build.info UNIT_TEST[] link set. +my @unit_test_keywords = qw(cmocka detours); + unless ($disabled{"unit-tests"}) { if ($target =~ /^linux/ || $target =~ /^BSD/) { $config{cmocka_includes} = $withargs{cmocka_include} ? [$withargs{cmocka_include}] : []; $config{cmocka_libs} = $withargs{cmocka_lib} - ? "-L$withargs{cmocka_lib} -lcmocka" - : "-lcmocka"; + ? "-L$withargs{cmocka_lib} -lcmocka" : "-lcmocka"; + } elsif ($target =~ /^VC-/) { + $config{cmocka_includes} = + $withargs{cmocka_include} ? [$withargs{cmocka_include}] : []; + $config{cmocka_libs} = $withargs{cmocka_lib} + ? "/LIBPATH:$withargs{cmocka_lib} cmocka.lib" : "cmocka.lib"; + $config{detours_includes} = + $withargs{detours_include} ? [$withargs{detours_include}] : []; + $config{detours_libs} = $withargs{detours_lib} + ? "/LIBPATH:$withargs{detours_lib} detours.lib" : "detours.lib"; } else { - disable('no-wrap-support', 'unit-tests'); + disable('no-unit-test-support', 'unit-tests'); } } @@ -2163,6 +2182,7 @@ if ($builder eq "unified") { my %includes = (); my %defines = (); my %depends = (); + my %unit_tests = (); my %wraps = (); my %generate = (); my %imagedocs = (); @@ -2419,11 +2439,16 @@ if ($builder eq "unified") { \$attributes{depends}, $+{ATTRIBS}, tokenize($expand_variables->($+{VALUE}))) if !@skip || $skip[$#skip] > 0; }, + qr/^\s* UNIT_TEST ${index_re} \s* = \s* ${value_re} \s* $/x + => sub { $push_to->(\%unit_tests, $expand_variables->($+{INDEX}), + undef, undef, + tokenize($expand_variables->($+{VALUE}))) + if !@skip || $skip[$#skip] > 0; }, qr/^\s* WRAP ${index_re} \s* = \s* ${value_re} \s* $/x => sub { $push_to->(\%wraps, $expand_variables->($+{INDEX}), undef, undef, tokenize($expand_variables->($+{VALUE}))) - if !@skip || $skip[$#skip] > 0; }, + if !@skip || $skip[$#skip] > 0; }, qr/^\s* GENERATE ${index_re} ${attribs_re} \s* = \s* ${value_re} \s* $/x => sub { $push_to->(\%generate, $expand_variables->($+{INDEX}), \$attributes{generate}, $+{ATTRIBS}, @@ -2717,6 +2742,20 @@ if ($builder eq "unified") { } } + foreach my $dest (keys %unit_tests) { + my $ddest = cleanfile($buildd, $dest, $blddir); + foreach my $kw (@{$unit_tests{$dest}}) { + die "***** Unknown keyword '$kw' in UNIT_TEST[$dest] at $sourced/$f\n" + unless grep { $_ eq $kw } @unit_test_keywords; + } + $unified_info{unit_tests}->{$ddest} = + [ @{$unit_tests{$dest}} ]; + } + # WRAP implies cmocka unless an explicit UNIT_TEST set was given + foreach my $dest (keys %{$unified_info{wraps} // {}}) { + $unified_info{unit_tests}->{$dest} //= [ "cmocka" ]; + } + foreach my $section (keys %imagedocs) { foreach (@{$imagedocs{$section}}) { my $imagedocs = cleanfile($buildd, $_, $blddir); @@ -3049,11 +3088,25 @@ EOF } } -# Add cmocka include path to all targets that use WRAP -if (!$disabled{"unit-tests"} && @{$config{cmocka_includes} // []}) { - foreach my $dest (keys %{$unified_info{wraps} // {}}) { - push @{$unified_info{includes}->{$dest}}, - @{$config{cmocka_includes}}; +# Attach cmocka (and, on Windows, Detours) include paths to unit tests, +# based on each test's UNIT_TEST[] link set. +if (!$disabled{"unit-tests"}) { + while (my ($dest, $libs) = each %{$unified_info{unit_tests} // {}}) { + my %want = map { $_ => 1 } @$libs; + push @{$unified_info{includes}->{$dest}}, @{$config{cmocka_includes}} + if $want{cmocka} && @{$config{cmocka_includes} // []}; + push @{$unified_info{includes}->{$dest}}, @{$config{detours_includes}} + if $want{detours} && @{$config{detours_includes} // []}; + } +} + +if (!$disabled{"unit-tests"}) { + foreach my $dest (sort keys %{$unified_info{unit_tests} // {}}) { + my %want = map { $_ => 1 } @{$unified_info{unit_tests}->{$dest}}; + my @resolved; + push @resolved, '$(CMOCKA_LIBS)' if $want{cmocka} && $config{cmocka_libs}; + push @resolved, '$(DETOURS_LIBS)' if $want{detours} && $config{detours_libs}; + $unified_info{unit_test_libs}->{$dest} = join(' ', @resolved); } } diff --git a/test/recipes/02-test_unit.t b/test/recipes/02-test_unit.t index 0c43c28a36..d88c3dcf31 100644 --- a/test/recipes/02-test_unit.t +++ b/test/recipes/02-test_unit.t @@ -18,14 +18,32 @@ use OpenSSL::Test::Utils; setup("test_unit"); my $unit_dir = bldtop_dir('test', 'unit'); +my $exeext = ''; + +if ($^O eq 'MSWin32') { + $exeext = '.exe'; + # The shared libraries (libcrypto/libssl DLLs) are only placed at the + # build top and copied into apps/, test/ and fuzz/. The unit test + # executables live in nested directories under test/unit/, so add the + # build top to PATH to let the loader find the DLLs. + $ENV{PATH} = bldtop_dir() . ';' . ($ENV{PATH} // ''); +} my @tests = (); if (-d $unit_dir) { find({ wanted => sub { - return unless -f $_ && -x $_; - return unless $_ =~ m|/test_[^/]*$|; - return if $_ =~ m|\.\w+$|; + return unless -f $_; + my $base = $_; + if ($exeext ne '') { + # require + strip .exe + return unless $base =~ s/\Q$exeext\E$//; + } else { + return unless -x $_; + } + return unless $base =~ m|/test_[^/]*$|; + # reject .pdb/.obj/etc + return if $base =~ m|\.\w+$|; push @tests, $_; }, no_chdir => 1, diff --git a/test/unit/build.info b/test/unit/build.info index bc0465dc13..ac04787457 100644 --- a/test/unit/build.info +++ b/test/unit/build.info @@ -1,58 +1,67 @@ -# Make sure all tests have WRAP otherwise -lcmocka is not added. +IF[{- $config{target} =~ /^(?:linux|BSD)/ -}] + PROGRAMS{noinst}=crypto/bio/test_bio_addr + SOURCE[crypto/bio/test_bio_addr]=crypto/bio/test_bio_addr.c + INCLUDE[crypto/bio/test_bio_addr]=../../include ../../crypto/bio + DEPEND[crypto/bio/test_bio_addr]=../../libcrypto.a + WRAP[crypto/bio/test_bio_addr]=BIO_sock_init getnameinfo freeaddrinfo -PROGRAMS{noinst}=crypto/bio/test_bio_addr -SOURCE[crypto/bio/test_bio_addr]=crypto/bio/test_bio_addr.c -INCLUDE[crypto/bio/test_bio_addr]=../../include ../../crypto/bio -DEPEND[crypto/bio/test_bio_addr]=../../libcrypto.a -WRAP[crypto/bio/test_bio_addr]=BIO_sock_init getnameinfo freeaddrinfo + PROGRAMS{noinst}=crypto/bio/test_bio_sock + SOURCE[crypto/bio/test_bio_sock]=crypto/bio/test_bio_sock.c + INCLUDE[crypto/bio/test_bio_sock]=../../include ../../crypto/bio + DEPEND[crypto/bio/test_bio_sock]=../../libcrypto.a + WRAP[crypto/bio/test_bio_sock]=getsockopt setsockopt getsockname ioctl poll \ + gethostbyname BIO_lookup BIO_socket BIO_listen BIO_closesocket \ + BIO_ADDRINFO_free BIO_accept_ex BIO_sock_should_retry \ + BIO_ADDR_hostname_string BIO_ADDR_service_string -PROGRAMS{noinst}=crypto/bio/test_bio_sock -SOURCE[crypto/bio/test_bio_sock]=crypto/bio/test_bio_sock.c -INCLUDE[crypto/bio/test_bio_sock]=../../include ../../crypto/bio -DEPEND[crypto/bio/test_bio_sock]=../../libcrypto.a -WRAP[crypto/bio/test_bio_sock]=getsockopt setsockopt getsockname ioctl poll \ - gethostbyname BIO_lookup BIO_socket BIO_listen BIO_closesocket \ - BIO_ADDRINFO_free BIO_accept_ex BIO_sock_should_retry \ - BIO_ADDR_hostname_string BIO_ADDR_service_string + PROGRAMS{noinst}=crypto/bio/test_bio_sock2 + SOURCE[crypto/bio/test_bio_sock2]=crypto/bio/test_bio_sock2.c + INCLUDE[crypto/bio/test_bio_sock2]=../../include ../../crypto/bio + DEPEND[crypto/bio/test_bio_sock2]=../../libcrypto.a + WRAP[crypto/bio/test_bio_sock2]=socket connect bind listen accept close \ + getsockopt setsockopt BIO_socket_nbio BIO_sock_should_retry -PROGRAMS{noinst}=crypto/bio/test_bio_sock2 -SOURCE[crypto/bio/test_bio_sock2]=crypto/bio/test_bio_sock2.c -INCLUDE[crypto/bio/test_bio_sock2]=../../include ../../crypto/bio -DEPEND[crypto/bio/test_bio_sock2]=../../libcrypto.a -WRAP[crypto/bio/test_bio_sock2]=socket connect bind listen accept close \ - getsockopt setsockopt BIO_socket_nbio BIO_sock_should_retry + PROGRAMS{noinst}=crypto/bio/test_bss_acpt + SOURCE[crypto/bio/test_bss_acpt]=crypto/bio/test_bss_acpt.c + WRAP[crypto/bio/test_bss_acpt]=BIO_lookup BIO_socket BIO_listen \ + BIO_accept_ex BIO_sock_info BIO_sock_should_retry BIO_closesocket \ + BIO_ADDR_hostname_string BIO_ADDR_service_string + INCLUDE[crypto/bio/test_bss_acpt]=../../include ../../crypto/bio + DEPEND[crypto/bio/test_bss_acpt]=../../libcrypto.a -PROGRAMS{noinst}=crypto/bio/test_bss_acpt -SOURCE[crypto/bio/test_bss_acpt]=crypto/bio/test_bss_acpt.c -WRAP[crypto/bio/test_bss_acpt]=BIO_lookup BIO_socket BIO_listen \ - BIO_accept_ex BIO_sock_info BIO_sock_should_retry BIO_closesocket \ - BIO_ADDR_hostname_string BIO_ADDR_service_string -INCLUDE[crypto/bio/test_bss_acpt]=../../include ../../crypto/bio -DEPEND[crypto/bio/test_bss_acpt]=../../libcrypto.a + PROGRAMS{noinst}=crypto/bio/test_bss_conn + SOURCE[crypto/bio/test_bss_conn]=crypto/bio/test_bss_conn.c + WRAP[crypto/bio/test_bss_conn]=BIO_lookup BIO_socket BIO_connect \ + BIO_sock_should_retry BIO_closesocket BIO_socket_wait BIO_sock_error \ + read write + INCLUDE[crypto/bio/test_bss_conn]=../../include ../../crypto/bio + DEPEND[crypto/bio/test_bss_conn]=../../libcrypto.a -PROGRAMS{noinst}=crypto/bio/test_bss_conn -SOURCE[crypto/bio/test_bss_conn]=crypto/bio/test_bss_conn.c -WRAP[crypto/bio/test_bss_conn]=BIO_lookup BIO_socket BIO_connect \ - BIO_sock_should_retry BIO_closesocket BIO_socket_wait BIO_sock_error \ - read write -INCLUDE[crypto/bio/test_bss_conn]=../../include ../../crypto/bio -DEPEND[crypto/bio/test_bss_conn]=../../libcrypto.a + PROGRAMS{noinst}=crypto/bio/test_bss_dgram + SOURCE[crypto/bio/test_bss_dgram]=crypto/bio/test_bss_dgram.c + WRAP[crypto/bio/test_bss_dgram]=recvfrom sendto write getsockname \ + getpeername BIO_closesocket BIO_socket_nbio + INCLUDE[crypto/bio/test_bss_dgram]=../../include ../../crypto/bio + DEPEND[crypto/bio/test_bss_dgram]=../../libcrypto.a -PROGRAMS{noinst}=crypto/bio/test_bss_dgram -SOURCE[crypto/bio/test_bss_dgram]=crypto/bio/test_bss_dgram.c -WRAP[crypto/bio/test_bss_dgram]=recvfrom sendto write getsockname \ - getpeername BIO_closesocket BIO_socket_nbio -INCLUDE[crypto/bio/test_bss_dgram]=../../include ../../crypto/bio -DEPEND[crypto/bio/test_bss_dgram]=../../libcrypto.a + PROGRAMS{noinst}=crypto/bio/test_bss_fd + SOURCE[crypto/bio/test_bss_fd]=crypto/bio/test_bss_fd.c + INCLUDE[crypto/bio/test_bss_fd]=../../include ../../crypto/bio + DEPEND[crypto/bio/test_bss_fd]=../../libcrypto.a + WRAP[crypto/bio/test_bss_fd]=read write lseek close -PROGRAMS{noinst}=crypto/bio/test_bss_fd -SOURCE[crypto/bio/test_bss_fd]=crypto/bio/test_bss_fd.c -INCLUDE[crypto/bio/test_bss_fd]=../../include ../../crypto/bio -DEPEND[crypto/bio/test_bss_fd]=../../libcrypto.a -WRAP[crypto/bio/test_bss_fd]=read write lseek close + PROGRAMS{noinst}=crypto/bio/test_bss_sock + SOURCE[crypto/bio/test_bss_sock]=crypto/bio/test_bss_sock.c + WRAP[crypto/bio/test_bss_sock]=read write BIO_closesocket + INCLUDE[crypto/bio/test_bss_sock]=../../include ../../crypto/bio + DEPEND[crypto/bio/test_bss_sock]=../../libcrypto.a +ENDIF -PROGRAMS{noinst}=crypto/bio/test_bss_sock -SOURCE[crypto/bio/test_bss_sock]=crypto/bio/test_bss_sock.c -WRAP[crypto/bio/test_bss_sock]=read write BIO_closesocket -INCLUDE[crypto/bio/test_bss_sock]=../../include ../../crypto/bio -DEPEND[crypto/bio/test_bss_sock]=../../libcrypto.a +IF[{- $config{target} =~ /^VC-/ -}] + PROGRAMS{noinst}=crypto/bio/test_bss_dgram_win + SOURCE[crypto/bio/test_bss_dgram_win]=crypto/bio/test_bss_dgram_win.c + UNIT_TEST[crypto/bio/test_bss_dgram_win]=cmocka detours + INCLUDE[crypto/bio/test_bss_dgram_win]=../../include ../../include/internal \ + ../../crypto/bio + DEPEND[crypto/bio/test_bss_dgram_win]=../../libcrypto +ENDIF diff --git a/test/unit/crypto/bio/test_bss_dgram_win.c b/test/unit/crypto/bio/test_bss_dgram_win.c new file mode 100644 index 0000000000..080d91c4e2 --- /dev/null +++ b/test/unit/crypto/bio/test_bss_dgram_win.c @@ -0,0 +1,478 @@ +/* + * test/bss_dgram_win_test.c + * + * Windows-only side test for bss_dgram.c Windows-specific paths. + * Uses Microsoft Detours to intercept Winsock calls at runtime. + * Does NOT replace the normal --wrap-based bss_dgram test; it only + * covers branches that differ under OPENSSL_SYS_WINDOWS. + * + * NOTE: not compiled/verified by the author's toolchain. The first + * test (detour_probe) is a hard gate: if Detours does not intercept + * cross-module Winsock calls, every other result is meaningless. + */ + +#include "openssl/e_os2.h" + +#if defined(OPENSSL_NO_SOCK) || defined(OPENSSL_NO_DGRAM) \ + || !defined(OPENSSL_SYS_WINDOWS) +int main(void) { return 0; } +#else + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "internal/sockets.h" +#include "bio_local.h" +#include + +#define FAKE_SOCKET ((SOCKET)42) + +/* + * Real function pointers. After DetourAttach commits, Detours rewrites + * these to trampolines pointing at the original code. + */ +static int(WSAAPI *real_getsockname)(SOCKET, struct sockaddr *, int *) + = getsockname; +static int(WSAAPI *real_getpeername)(SOCKET, struct sockaddr *, int *) + = getpeername; +static int(WSAAPI *real_setsockopt)(SOCKET, int, int, const char *, int) + = setsockopt; +static int(WSAAPI *real_getsockopt)(SOCKET, int, int, char *, int *) + = getsockopt; +static int(WSAAPI *real_recvfrom)(SOCKET, char *, int, int, + struct sockaddr *, int *) + = recvfrom; + +static struct sockaddr_in g_sin; + +/* + * detour_probe uses this to confirm the mock actually fired. It is the + * only place a mock is allowed to run outside a cmocka expectation, so + * the mocks below special-case it. + */ +static int g_probe_active; +static int g_probe_getsockopt_hits; + +/* mocks */ + +static int WSAAPI mock_getsockname(SOCKET s, struct sockaddr *name, + int *namelen) +{ + int rc; + + function_called(); + check_expected_uint(s); + + rc = mock_type(int); + if (rc == 0) { + const struct sockaddr *sa = mock_ptr_type(const struct sockaddr *); + int sl = mock_type(int); + + assert_non_null(name); + assert_non_null(namelen); + assert_true(*namelen >= sl); + memcpy(name, sa, (size_t)sl); + *namelen = sl; + } else { + WSASetLastError(mock_type(int)); + } + return rc; +} + +static int WSAAPI mock_getpeername(SOCKET s, struct sockaddr *name, + int *namelen) +{ + int rc; + + function_called(); + check_expected_uint(s); + + rc = mock_type(int); + if (rc == 0) { + assert_non_null(name); + assert_non_null(namelen); + assert_true(*namelen >= (int)sizeof(g_sin)); + memcpy(name, &g_sin, sizeof(g_sin)); + *namelen = (int)sizeof(g_sin); + } else { + WSASetLastError(mock_type(int)); + } + return rc; +} + +static int WSAAPI mock_setsockopt(SOCKET s, int level, int optname, + const char *optval, int optlen) +{ + int expected_int; + int rc; + + function_called(); + check_expected_uint(s); + check_expected_int(level); + check_expected_int(optname); + check_expected_int(optlen); + + /* Every Windows path routed here passes a single int. */ + assert_int_equal(optlen, (int)sizeof(int)); + expected_int = mock_type(int); + assert_int_equal(*(const int *)optval, expected_int); + + rc = mock_type(int); + if (rc == SOCKET_ERROR) + WSASetLastError(mock_type(int)); + return rc; +} + +static int WSAAPI mock_getsockopt(SOCKET s, int level, int optname, + char *optval, int *optlen) +{ + int out_value; + int rc; + + /* Probe path: no expectations queued, just record and answer. */ + if (g_probe_active) { + g_probe_getsockopt_hits++; + if (optval != NULL && optlen != NULL && *optlen >= (int)sizeof(int)) { + *(int *)optval = 0; + *optlen = (int)sizeof(int); + } + return 0; + } + + function_called(); + check_expected_uint(s); + check_expected_int(level); + check_expected_int(optname); + + assert_non_null(optval); + assert_non_null(optlen); + assert_int_equal(*optlen, (int)sizeof(int)); + + out_value = mock_type(int); + rc = mock_type(int); + if (rc == 0) { + *(int *)optval = out_value; + *optlen = (int)sizeof(int); + } else { + WSASetLastError(mock_type(int)); + } + return rc; +} + +static int WSAAPI mock_recvfrom(SOCKET s, char *buf, int len, int flags, + struct sockaddr *from, int *fromlen) +{ + int rc; + + (void)from; + (void)fromlen; + + function_called(); + check_expected_uint(s); + check_expected_ptr(buf); + check_expected_int(len); + check_expected_int(flags); + + rc = mock_type(int); + if (rc == SOCKET_ERROR) + WSASetLastError(mock_type(int)); + return rc; +} + +/* expectations */ + +static void expect_getsockname_ok(void) +{ + expect_function_call(mock_getsockname); + expect_uint_value(mock_getsockname, s, FAKE_SOCKET); + will_return_int(mock_getsockname, 0); + will_return_ptr(mock_getsockname, (const struct sockaddr *)&g_sin); + will_return_int(mock_getsockname, (int)sizeof(g_sin)); +} + +static void expect_getpeername_fail(void) +{ + expect_function_call(mock_getpeername); + expect_uint_value(mock_getpeername, s, FAKE_SOCKET); + will_return_int(mock_getpeername, SOCKET_ERROR); + will_return_int(mock_getpeername, WSAENOTCONN); +} + +static void expect_setsockopt_int(int level, int optname, int value, int rc) +{ + expect_function_call(mock_setsockopt); + expect_uint_value(mock_setsockopt, s, FAKE_SOCKET); + expect_int_value(mock_setsockopt, level, level); + expect_int_value(mock_setsockopt, optname, optname); + expect_int_value(mock_setsockopt, optlen, (int)sizeof(int)); + will_return_int(mock_setsockopt, value); + will_return_int(mock_setsockopt, rc); + if (rc == SOCKET_ERROR) + will_return(mock_setsockopt, WSAEINVAL); +} + +static void expect_getsockopt_int(int level, int optname, int value, int rc) +{ + expect_function_call(mock_getsockopt); + expect_uint_value(mock_getsockopt, s, FAKE_SOCKET); + expect_int_value(mock_getsockopt, level, level); + expect_int_value(mock_getsockopt, optname, optname); + will_return_int(mock_getsockopt, value); + will_return_int(mock_getsockopt, rc); + if (rc == SOCKET_ERROR) + will_return_int(mock_getsockopt, WSAEINVAL); +} + +static void expect_recvfrom_error(char *buf, int len, int flags, int wsaerr) +{ + expect_function_call(mock_recvfrom); + expect_uint_value(mock_recvfrom, s, FAKE_SOCKET); + expect_value(mock_recvfrom, buf, buf); + expect_int_value(mock_recvfrom, len, len); + expect_int_value(mock_recvfrom, flags, flags); + will_return_int(mock_recvfrom, SOCKET_ERROR); + will_return_int(mock_recvfrom, wsaerr); +} + +/* detours */ + +static int attach_detours(void) +{ + if (DetourTransactionBegin() != NO_ERROR) + return 0; + if (DetourUpdateThread(GetCurrentThread()) != NO_ERROR) + return 0; + if (DetourAttach((PVOID *)&real_getsockname, mock_getsockname) != NO_ERROR) + return 0; + if (DetourAttach((PVOID *)&real_getpeername, mock_getpeername) != NO_ERROR) + return 0; + if (DetourAttach((PVOID *)&real_setsockopt, mock_setsockopt) != NO_ERROR) + return 0; + if (DetourAttach((PVOID *)&real_getsockopt, mock_getsockopt) != NO_ERROR) + return 0; + if (DetourAttach((PVOID *)&real_recvfrom, mock_recvfrom) != NO_ERROR) + return 0; + return DetourTransactionCommit() == NO_ERROR; +} + +static int detach_detours(void) +{ + if (DetourTransactionBegin() != NO_ERROR) + return 0; + if (DetourUpdateThread(GetCurrentThread()) != NO_ERROR) + return 0; + DetourDetach((PVOID *)&real_getsockname, mock_getsockname); + DetourDetach((PVOID *)&real_getpeername, mock_getpeername); + DetourDetach((PVOID *)&real_setsockopt, mock_setsockopt); + DetourDetach((PVOID *)&real_getsockopt, mock_getsockopt); + DetourDetach((PVOID *)&real_recvfrom, mock_recvfrom); + return DetourTransactionCommit() == NO_ERROR; +} + +/* setup / teardown */ + +static int setup_io(void **state) +{ + BIO *bio = BIO_new(BIO_s_datagram()); + + assert_non_null(bio); + expect_getsockname_ok(); + expect_getpeername_fail(); + BIO_set_fd(bio, (int)FAKE_SOCKET, BIO_NOCLOSE); + *state = bio; + return 0; +} + +static int teardown_io(void **state) +{ + if (*state != NULL) { + BIO *bio = *state; + + bio->num = (int)INVALID_SOCKET; + bio->shutdown = BIO_NOCLOSE; + BIO_free(bio); + } + return 0; +} + +static int group_setup(void **state) +{ + WSADATA wsa; + + (void)state; + memset(&g_sin, 0, sizeof(g_sin)); + g_sin.sin_family = AF_INET; + g_sin.sin_port = htons(443); + g_sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + assert_int_equal(WSAStartup(MAKEWORD(2, 2), &wsa), 0); + assert_true(attach_detours()); + return 0; +} + +static int group_teardown(void **state) +{ + (void)state; + assert_true(detach_detours()); + WSACleanup(); + return 0; +} + +/* + * GATE: prove Detours intercepts a Winsock call made through the same + * import machinery the production library uses. If this fails, fix the + * linkage (e.g. DetourFindFunction on ws2_32.dll) before trusting any + * other test below. + */ +static void detour_probe(void **state) +{ + int val = -1; + int len = (int)sizeof(val); + int rc; + + (void)state; + g_probe_getsockopt_hits = 0; + g_probe_active = 1; + rc = getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, (char *)&val, &len); + g_probe_active = 0; + + assert_int_equal(rc, 0); + assert_int_equal(g_probe_getsockopt_hits, 1); +} + +/* SO_RCVTIMEO / SO_SNDTIMEO use int milliseconds on Windows. */ + +static void test_win_set_recv_timeout_uses_milliseconds(void **state) +{ + BIO *bio = *state; + struct timeval tv = { 1, 500000 }; + + expect_setsockopt_int(SOL_SOCKET, SO_RCVTIMEO, 1500, 0); + assert_int_equal( + BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &tv), 0); +} + +static void test_win_get_recv_timeout_converts_milliseconds(void **state) +{ + BIO *bio = *state; + struct timeval tv; + + memset(&tv, 0, sizeof(tv)); + expect_getsockopt_int(SOL_SOCKET, SO_RCVTIMEO, 2500, 0); + assert_int_equal( + BIO_ctrl(bio, BIO_CTRL_DGRAM_GET_RECV_TIMEOUT, 0, &tv), + (int)sizeof(tv)); + assert_int_equal(tv.tv_sec, 2); + assert_int_equal(tv.tv_usec, 500000); +} + +static void test_win_set_send_timeout_uses_milliseconds(void **state) +{ + BIO *bio = *state; + struct timeval tv = { 3, 250000 }; + + expect_setsockopt_int(SOL_SOCKET, SO_SNDTIMEO, 3250, 0); + assert_int_equal( + BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_SEND_TIMEOUT, 0, &tv), 0); +} + +static void test_win_get_send_timeout_converts_milliseconds(void **state) +{ + BIO *bio = *state; + struct timeval tv; + + memset(&tv, 0, sizeof(tv)); + expect_getsockopt_int(SOL_SOCKET, SO_SNDTIMEO, 4250, 0); + assert_int_equal( + BIO_ctrl(bio, BIO_CTRL_DGRAM_GET_SEND_TIMEOUT, 0, &tv), + (int)sizeof(tv)); + assert_int_equal(tv.tv_sec, 4); + assert_int_equal(tv.tv_usec, 250000); +} + +/* + * GET_RECV_TIMER_EXP checks data->_errno == WSAETIMEDOUT on Windows + * (EAGAIN elsewhere), then consumes/clears it. WSAETIMEDOUT is treated + * as fatal by BIO_dgram_non_fatal_error, so we set _errno directly + * rather than driving it through a recvfrom retry that never sets it. + */ +static void test_win_recv_timer_exp_consumes_errno(void **state) +{ + BIO *bio = *state; + bio_dgram_data *data = (bio_dgram_data *)bio->ptr; + + data->_errno = WSAETIMEDOUT; + assert_int_equal( + BIO_ctrl(bio, BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP, 0, NULL), 1); + /* second read reports 0: the ctrl cleared _errno */ + assert_int_equal( + BIO_ctrl(bio, BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP, 0, NULL), 0); +} + +/* + * A fatal recvfrom error (WSAECONNRESET) must NOT set a retry flag. + * This exercises the real Windows recvfrom signature through Detours. + */ +static void test_win_recvfrom_fatal_no_retry(void **state) +{ + BIO *bio = *state; + char buf[16]; + + memset(buf, 0, sizeof(buf)); + expect_recvfrom_error(buf, (int)sizeof(buf), 0, WSAECONNRESET); + assert_true(BIO_read(bio, buf, (int)sizeof(buf)) <= 0); + assert_false(BIO_should_retry(bio)); +} + +#if defined(IP_DONTFRAGMENT) +/* + * IPv4 don't-fragment falls to IP_DONTFRAGMENT on Windows. This branch + * is reached only when IP_DONTFRAG is NOT defined (it is the #elif). + */ +static void test_win_set_dont_frag_ipv4(void **state) +{ + BIO *bio = *state; + struct sockaddr_in peer; + + memset(&peer, 0, sizeof(peer)); + peer.sin_family = AF_INET; + peer.sin_port = htons(4433); + peer.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_PEER, 0, &peer); + + expect_setsockopt_int(IPPROTO_IP, IP_DONTFRAGMENT, 1, 0); + assert_int_equal(BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_DONT_FRAG, 1, NULL), 0); +} +#endif + +#define DG_WIN(name) \ + cmocka_unit_test_setup_teardown(name, setup_io, teardown_io) + +int main(void) +{ + const struct CMUnitTest tests[] = { + DG_WIN(detour_probe), + DG_WIN(test_win_set_recv_timeout_uses_milliseconds), + DG_WIN(test_win_get_recv_timeout_converts_milliseconds), + DG_WIN(test_win_set_send_timeout_uses_milliseconds), + DG_WIN(test_win_get_send_timeout_converts_milliseconds), + DG_WIN(test_win_recv_timer_exp_consumes_errno), + DG_WIN(test_win_recvfrom_fatal_no_retry), +#if defined(IP_DONTFRAGMENT) + DG_WIN(test_win_set_dont_frag_ipv4), +#endif + }; + + cmocka_set_message_output(CM_OUTPUT_TAP); + return cmocka_run_group_tests(tests, group_setup, group_teardown); +} + +#endif From c263ea553c1d19f12d0040ecd184e4bf4e054af3 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Wed, 22 Apr 2026 15:33:23 +0200 Subject: [PATCH 263/349] Fix fuzz test to handle memory failures It also fixes related memory leaks and removes extensive asserts that should not be present in fuzzy tests. Reviewed-by: Nikola Pajkovsky Reviewed-by: Tomas Mraz MergeDate: Wed Jul 15 16:01:42 2026 (Merged from https://github.com/openssl/openssl/pull/30944) --- fuzz/acert.c | 9 ++- fuzz/asn1.c | 5 +- fuzz/bignum.c | 28 ++++--- fuzz/bndiv.c | 33 +++++--- fuzz/client.c | 9 ++- fuzz/cmp.c | 5 ++ fuzz/cms.c | 16 +++- fuzz/conf.c | 8 +- fuzz/crl.c | 7 +- fuzz/ct.c | 7 +- fuzz/decoder.c | 44 +++++++---- fuzz/dtlsclient.c | 9 ++- fuzz/dtlsserver.c | 171 ++++++++++++++++++---------------------- fuzz/hashtable.c | 94 ++++++---------------- fuzz/ml-dsa.c | 6 +- fuzz/ml-kem.c | 2 +- fuzz/pem.c | 10 ++- fuzz/pkcs12.c | 10 ++- fuzz/provider.c | 12 +++ fuzz/quic-client.c | 6 +- fuzz/quic-srtm.c | 22 +++--- fuzz/server.c | 191 ++++++++++++++++++++++++--------------------- fuzz/slh-dsa.c | 145 +++++++++++++++++----------------- fuzz/smime.c | 14 +++- fuzz/v3name.c | 16 ++-- fuzz/x509.c | 3 +- 26 files changed, 477 insertions(+), 405 deletions(-) diff --git a/fuzz/acert.c b/fuzz/acert.c index 14c2f657ee..3af3c3e87a 100644 --- a/fuzz/acert.c +++ b/fuzz/acert.c @@ -26,13 +26,16 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) { const unsigned char *p = buf; unsigned char *der = NULL; + X509_ACERT *acert; - X509_ACERT *acert = d2i_X509_ACERT(NULL, &p, (long)len); + acert = d2i_X509_ACERT(NULL, &p, (long)len); if (acert != NULL) { BIO *bio = BIO_new(BIO_s_null()); - X509_ACERT_print(bio, acert); - BIO_free(bio); + if (bio != NULL) { + X509_ACERT_print(bio, acert); + BIO_free(bio); + } i2d_X509_ACERT(acert, &der); OPENSSL_free(der); diff --git a/fuzz/asn1.c b/fuzz/asn1.c index 934e1447e9..12ba8a13d4 100644 --- a/fuzz/asn1.c +++ b/fuzz/asn1.c @@ -277,9 +277,6 @@ static ASN1_PCTX *pctx; TYPE *type = D2I(NULL, &p, (long)len); \ \ if (type != NULL) { \ - BIO *bio = BIO_new(BIO_s_null()); \ - \ - BIO_free(bio); \ I2D(type, &der); \ OPENSSL_free(der); \ TYPE##_free(type); \ @@ -290,6 +287,8 @@ int FuzzerInitialize(int *argc, char ***argv) { FuzzerSetRand(); pctx = ASN1_PCTX_new(); + if (pctx == NULL) + return 0; ASN1_PCTX_set_flags(pctx, ASN1_PCTX_FLAGS_SHOW_ABSENT | ASN1_PCTX_FLAGS_SHOW_SEQUENCE | ASN1_PCTX_FLAGS_SHOW_SSOF | ASN1_PCTX_FLAGS_SHOW_TYPE | ASN1_PCTX_FLAGS_SHOW_FIELD_STRUCT_NAME); ASN1_PCTX_set_str_flags(pctx, ASN1_STRFLGS_UTF8_CONVERT | ASN1_STRFLGS_SHOW_TYPE | ASN1_STRFLGS_DUMP_ALL); diff --git a/fuzz/bignum.c b/fuzz/bignum.c index b9dcb49ebe..eec776e7b3 100644 --- a/fuzz/bignum.c +++ b/fuzz/bignum.c @@ -28,7 +28,6 @@ int FuzzerInitialize(int *argc, char ***argv) int FuzzerTestOneInput(const uint8_t *buf, size_t len) { - int success = 0; size_t l1 = 0, l2 = 0, l3 = 0; int s1 = 0, s3 = 0; BN_CTX *ctx; @@ -45,6 +44,10 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) b5 = BN_new(); ctx = BN_CTX_new(); + if (b1 == NULL || b2 == NULL || b3 == NULL || b4 == NULL || b5 == NULL + || ctx == NULL) + goto done; + /* Divide the input into three parts, using the values of the first two * bytes to choose lengths, which generate b1, b2 and b3. Use three bits * of the third byte to choose signs for the three numbers. @@ -62,23 +65,25 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) s3 = buf[0] & 4; ++buf; } - OPENSSL_assert(BN_bin2bn(buf, (int)l1, b1) == b1); + if (BN_bin2bn(buf, (int)l1, b1) != b1) + goto done; BN_set_negative(b1, s1); - OPENSSL_assert(BN_bin2bn(buf + l1, (int)l2, b2) == b2); - OPENSSL_assert(BN_bin2bn(buf + l1 + l2, (int)l3, b3) == b3); + if (BN_bin2bn(buf + l1, (int)l2, b2) != b2) + goto done; + if (BN_bin2bn(buf + l1 + l2, (int)l3, b3) != b3) + goto done; BN_set_negative(b3, s3); /* mod 0 is undefined */ - if (BN_is_zero(b3)) { - success = 1; + if (BN_is_zero(b3)) goto done; - } - OPENSSL_assert(BN_mod_exp(b4, b1, b2, b3, ctx)); - OPENSSL_assert(BN_mod_exp_simple(b5, b1, b2, b3, ctx)); + if (!BN_mod_exp(b4, b1, b2, b3, ctx)) + goto done; + if (!BN_mod_exp_simple(b5, b1, b2, b3, ctx)) + goto done; - success = BN_cmp(b4, b5) == 0; - if (!success) { + if (BN_cmp(b4, b5) != 0) { BN_print_fp(stdout, b1); putchar('\n'); BN_print_fp(stdout, b2); @@ -92,7 +97,6 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) } done: - OPENSSL_assert(success); BN_free(b1); BN_free(b2); BN_free(b3); diff --git a/fuzz/bndiv.c b/fuzz/bndiv.c index a52f7b598b..c3a2d2f305 100644 --- a/fuzz/bndiv.c +++ b/fuzz/bndiv.c @@ -37,6 +37,19 @@ int FuzzerInitialize(int *argc, char ***argv) b5 = BN_new(); ctx = BN_CTX_new(); + if (b1 == NULL || b2 == NULL || b3 == NULL || b4 == NULL || b5 == NULL + || ctx == NULL) { + BN_free(b1); + BN_free(b2); + BN_free(b3); + BN_free(b4); + BN_free(b5); + BN_CTX_free(ctx); + b1 = b2 = b3 = b4 = b5 = NULL; + ctx = NULL; + return 0; + } + OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); ERR_clear_error(); @@ -69,18 +82,19 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) ++buf; l2 = len - l1; } - OPENSSL_assert(BN_bin2bn(buf, (int)l1, b1) == b1); + if (BN_bin2bn(buf, (int)l1, b1) != b1) + goto done; BN_set_negative(b1, s1); - OPENSSL_assert(BN_bin2bn(buf + l1, (int)l2, b2) == b2); + if (BN_bin2bn(buf + l1, (int)l2, b2) != b2) + goto done; BN_set_negative(b2, s2); /* divide by 0 is an error */ - if (BN_is_zero(b2)) { - success = 1; + if (BN_is_zero(b2)) goto done; - } - OPENSSL_assert(BN_div(b3, b4, b1, b2, ctx)); + if (!BN_div(b3, b4, b1, b2, ctx)) + goto done; if (BN_is_zero(b1)) success = BN_is_zero(b3) && BN_is_zero(b4); else if (BN_is_negative(b1)) @@ -89,8 +103,10 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) else success = (BN_is_negative(b3) == BN_is_negative(b2) || BN_is_zero(b3)) && (!BN_is_negative(b4) || BN_is_zero(b4)); - OPENSSL_assert(BN_mul(b5, b3, b2, ctx)); - OPENSSL_assert(BN_add(b5, b5, b4)); + if (!BN_mul(b5, b3, b2, ctx)) + goto done; + if (!BN_add(b5, b5, b4)) + goto done; success = success && BN_cmp(b5, b1) == 0; if (!success) { @@ -114,7 +130,6 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) } done: - OPENSSL_assert(success); ERR_clear_error(); return 0; diff --git a/fuzz/client.c b/fuzz/client.c index 86cec0cd43..64d30dd09e 100644 --- a/fuzz/client.c +++ b/fuzz/client.c @@ -76,8 +76,10 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) client = SSL_new(ctx); if (client == NULL) goto end; - OPENSSL_assert(SSL_set_min_proto_version(client, 0) == 1); - OPENSSL_assert(SSL_set_cipher_list(client, "ALL:eNULL:@SECLEVEL=0") == 1); + if (SSL_set_min_proto_version(client, 0) != 1) + goto end; + if (SSL_set_cipher_list(client, "ALL:eNULL:@SECLEVEL=0") != 1) + goto end; SSL_set_tlsext_host_name(client, "localhost"); in = BIO_new(BIO_s_mem()); if (in == NULL) @@ -89,7 +91,8 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) } SSL_set_bio(client, in, out); SSL_set_connect_state(client); - OPENSSL_assert((size_t)BIO_write(in, buf, (int)len) == len); + if ((size_t)BIO_write(in, buf, (int)len) != len) + goto end; if (SSL_do_handshake(client) == 1) { /* Keep reading application data until error or EOF. */ uint8_t tmp[1024]; diff --git a/fuzz/cmp.c b/fuzz/cmp.c index c86d390f8b..ae4dcc033d 100644 --- a/fuzz/cmp.c +++ b/fuzz/cmp.c @@ -176,8 +176,13 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) return 0; in = BIO_new(BIO_s_mem()); + if (in == NULL) { + ERR_clear_error(); + return 0; + } if ((size_t)BIO_write(in, buf, (int)len) != len) { BIO_free(in); + ERR_clear_error(); return 0; } diff --git a/fuzz/cms.c b/fuzz/cms.c index 0cd8ea9551..d45bf1b63c 100644 --- a/fuzz/cms.c +++ b/fuzz/cms.c @@ -34,13 +34,23 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) return 0; in = BIO_new(BIO_s_mem()); - OPENSSL_assert((size_t)BIO_write(in, buf, (int)len) == len); + if (in == NULL) { + ERR_clear_error(); + return 0; + } + if ((size_t)BIO_write(in, buf, (int)len) != len) { + BIO_free(in); + ERR_clear_error(); + return 0; + } cms = d2i_CMS_bio(in, NULL); if (cms != NULL) { BIO *out = BIO_new(BIO_s_null()); - i2d_CMS_bio(out, cms); - BIO_free(out); + if (out != NULL) { + i2d_CMS_bio(out, cms); + BIO_free(out); + } CMS_ContentInfo_free(cms); } diff --git a/fuzz/conf.c b/fuzz/conf.c index 9c5353868b..116759a0a1 100644 --- a/fuzz/conf.c +++ b/fuzz/conf.c @@ -33,9 +33,15 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) return 0; conf = NCONF_new(NULL); + if (conf == NULL) + return 0; in = BIO_new(BIO_s_mem()); - OPENSSL_assert((size_t)BIO_write(in, buf, (int)len) == len); + if (in == NULL) + goto end; + if ((size_t)BIO_write(in, buf, (int)len) != len) + goto end; NCONF_load_bio(conf, in, &eline); +end: NCONF_free(conf); BIO_free(in); ERR_clear_error(); diff --git a/fuzz/crl.c b/fuzz/crl.c index c22f5d55e2..f704321ac5 100644 --- a/fuzz/crl.c +++ b/fuzz/crl.c @@ -29,8 +29,11 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) X509_CRL *crl = d2i_X509_CRL(NULL, &p, (long)len); if (crl != NULL) { BIO *bio = BIO_new(BIO_s_null()); - X509_CRL_print(bio, crl); - BIO_free(bio); + + if (bio != NULL) { + X509_CRL_print(bio, crl); + BIO_free(bio); + } i2d_X509_CRL(crl, &der); OPENSSL_free(der); diff --git a/fuzz/ct.c b/fuzz/ct.c index e46574d8eb..74fbf66c04 100644 --- a/fuzz/ct.c +++ b/fuzz/ct.c @@ -32,8 +32,11 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) STACK_OF(SCT) *scts = d2i_SCT_LIST(NULL, pp, (long)len); if (scts != NULL) { BIO *bio = BIO_new(BIO_s_null()); - SCT_LIST_print(scts, bio, 4, "\n", NULL); - BIO_free(bio); + + if (bio != NULL) { + SCT_LIST_print(scts, bio, 4, "\n", NULL); + BIO_free(bio); + } if (i2d_SCT_LIST(scts, &der)) { /* Silence unused result warning */ diff --git a/fuzz/decoder.c b/fuzz/decoder.c index 3a9a48ad21..227c24fd71 100644 --- a/fuzz/decoder.c +++ b/fuzz/decoder.c @@ -25,6 +25,8 @@ int FuzzerInitialize(int *argc, char ***argv) NULL); pctx = ASN1_PCTX_new(); + if (pctx == NULL) + return 0; ASN1_PCTX_set_flags(pctx, ASN1_PCTX_FLAGS_SHOW_ABSENT | ASN1_PCTX_FLAGS_SHOW_SEQUENCE | ASN1_PCTX_FLAGS_SHOW_SSOF | ASN1_PCTX_FLAGS_SHOW_TYPE | ASN1_PCTX_FLAGS_SHOW_FIELD_STRUCT_NAME); ASN1_PCTX_set_str_flags(pctx, ASN1_STRFLGS_UTF8_CONVERT | ASN1_STRFLGS_SHOW_TYPE | ASN1_STRFLGS_DUMP_ALL); @@ -41,9 +43,15 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) BIO *bio; bio = BIO_new(BIO_s_null()); + if (bio == NULL) { + ERR_clear_error(); + return 0; + } dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, NULL, NULL, NULL, 0, NULL, NULL); if (dctx == NULL) { + BIO_free(bio); + ERR_clear_error(); return 0; } if (OSSL_DECODER_from_data(dctx, &buf, &len)) { @@ -54,27 +62,29 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) EVP_PKEY_print_params(bio, pkey, 1, pctx); pkey2 = EVP_PKEY_dup(pkey); - OPENSSL_assert(pkey2 != NULL); - EVP_PKEY_eq(pkey, pkey2); - EVP_PKEY_free(pkey2); + if (pkey2 != NULL) { + EVP_PKEY_eq(pkey, pkey2); + EVP_PKEY_free(pkey2); + } ctx = EVP_PKEY_CTX_new(pkey, NULL); - /* - * Param check will take too long time on large DH parameters. - * Skip it. - */ - if ((!EVP_PKEY_is_a(pkey, "DH") && !EVP_PKEY_is_a(pkey, "DHX")) - || EVP_PKEY_get_bits(pkey) <= 2048) - EVP_PKEY_param_check(ctx); + if (ctx != NULL) { + /* + * Param check will take too long time on large DH parameters. + * Skip it. + */ + if ((!EVP_PKEY_is_a(pkey, "DH") && !EVP_PKEY_is_a(pkey, "DHX")) + || EVP_PKEY_get_bits(pkey) <= 2048) + EVP_PKEY_param_check(ctx); - EVP_PKEY_public_check(ctx); - /* Private and pairwise checks are unbounded, skip for large keys. */ - if (EVP_PKEY_get_bits(pkey) <= 4096) { - EVP_PKEY_private_check(ctx); - EVP_PKEY_pairwise_check(ctx); + EVP_PKEY_public_check(ctx); + /* Private and pairwise checks are unbounded, skip for large keys. */ + if (EVP_PKEY_get_bits(pkey) <= 4096) { + EVP_PKEY_private_check(ctx); + EVP_PKEY_pairwise_check(ctx); + } + EVP_PKEY_CTX_free(ctx); } - OPENSSL_assert(ctx != NULL); - EVP_PKEY_CTX_free(ctx); EVP_PKEY_free(pkey); } OSSL_DECODER_CTX_free(dctx); diff --git a/fuzz/dtlsclient.c b/fuzz/dtlsclient.c index f07bc6ed67..2dc5ed709c 100644 --- a/fuzz/dtlsclient.c +++ b/fuzz/dtlsclient.c @@ -76,8 +76,10 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) client = SSL_new(ctx); if (client == NULL) goto end; - OPENSSL_assert(SSL_set_min_proto_version(client, 0) == 1); - OPENSSL_assert(SSL_set_cipher_list(client, "ALL:eNULL:@SECLEVEL=0") == 1); + if (SSL_set_min_proto_version(client, 0) != 1) + goto end; + if (SSL_set_cipher_list(client, "ALL:eNULL:@SECLEVEL=0") != 1) + goto end; SSL_set_tlsext_host_name(client, "localhost"); in = BIO_new(BIO_s_mem()); if (in == NULL) @@ -89,7 +91,8 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) } SSL_set_bio(client, in, out); SSL_set_connect_state(client); - OPENSSL_assert((size_t)BIO_write(in, buf, (int)len) == len); + if ((size_t)BIO_write(in, buf, (int)len) != len) + goto end; if (SSL_do_handshake(client) == 1) { /* Keep reading application data until error or EOF. */ uint8_t tmp[1024]; diff --git a/fuzz/dtlsserver.c b/fuzz/dtlsserver.c index 15c6c8bb57..84ded9a4df 100644 --- a/fuzz/dtlsserver.c +++ b/fuzz/dtlsserver.c @@ -218,7 +218,6 @@ static const uint8_t RSACertificatePEM[] = { 0x2d, 0x0a }; -#ifndef OPENSSL_NO_DEPRECATED_3_0 /* -----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC1mQfTLWrNFfUs @@ -464,10 +463,8 @@ static const uint8_t RSAPrivateKeyPEM[] = { 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a }; -#endif #ifndef OPENSSL_NO_EC -#ifndef OPENSSL_NO_DEPRECATED_3_0 /* -----BEGIN EC PRIVATE KEY----- MHcCAQEEIJLyl7hJjpQL/RhP1x2zS79xdiPJQB683gWeqcqHPeZkoAoGCCqGSM49 @@ -496,7 +493,6 @@ static const char ECDSAPrivateKeyPEM[] = { 0x4e, 0x44, 0x20, 0x45, 0x43, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a }; -#endif /* -----BEGIN CERTIFICATE----- @@ -559,7 +555,7 @@ static const char ECDSACertPEM[] = { }; #endif -#if !defined(OPENSSL_NO_DSA) && !defined(OPENSSL_NO_DEPRECATED_3_0) +#ifndef OPENSSL_NO_DSA /* -----BEGIN DSA PRIVATE KEY----- MIIBuwIBAAKBgQDdkFKzNABLOha7Eqj7004+p5fhtR6bxpujToMmSZTYi8igVVXP @@ -779,121 +775,109 @@ time_t time(time_t *t) TIME_IMPL(t) return 1; } +static int use_pem_privkey(SSL_CTX *ctx, const void *pem, size_t pem_len) +{ + BIO *bio_buf; + EVP_PKEY *pkey; + int rv = 0; + + bio_buf = BIO_new(BIO_s_mem()); + if (bio_buf == NULL) + return 0; + if ((size_t)BIO_write(bio_buf, pem, (int)pem_len) != pem_len) { + BIO_free(bio_buf); + return 0; + } + pkey = PEM_read_bio_PrivateKey(bio_buf, NULL, NULL, NULL); + BIO_free(bio_buf); + if (pkey == NULL) + return 0; + if (SSL_CTX_use_PrivateKey(ctx, pkey) == 1) + rv = 1; + EVP_PKEY_free(pkey); + return rv; +} + +static int use_pem_cert(SSL_CTX *ctx, const void *pem, size_t pem_len) +{ + BIO *bio_buf; + X509 *cert; + int rv = 0; + + bio_buf = BIO_new(BIO_s_mem()); + if (bio_buf == NULL) + return 0; + if ((size_t)BIO_write(bio_buf, pem, (int)pem_len) != pem_len) { + BIO_free(bio_buf); + return 0; + } + cert = PEM_read_bio_X509(bio_buf, NULL, NULL, NULL); + BIO_free(bio_buf); + if (cert == NULL) + return 0; + if (SSL_CTX_use_certificate(ctx, cert) == 1) + rv = 1; + X509_free(cert); + return rv; +} + int FuzzerTestOneInput(const uint8_t *buf, size_t len) { - SSL *server; + SSL *server = NULL; BIO *in; BIO *out; - BIO *bio_buf; SSL_CTX *ctx; - int ret; -#ifndef OPENSSL_NO_DEPRECATED_3_0 - RSA *privkey; -#endif -#if !defined(OPENSSL_NO_DEPRECATED_3_0) - EVP_PKEY *pkey; -#endif - X509 *cert; -#ifndef OPENSSL_NO_DEPRECATED_3_0 -#ifndef OPENSSL_NO_EC - EC_KEY *ecdsakey = NULL; -#endif -#endif -#if !defined(OPENSSL_NO_DSA) && !defined(OPENSSL_NO_DEPRECATED_3_0) - DSA *dsakey = NULL; -#endif if (len < 2 || len > INT_MAX) return 0; - /* This only fuzzes the initial flow from the client so far. */ ctx = SSL_CTX_new(DTLS_server_method()); + if (ctx == NULL) + return 0; - ret = SSL_CTX_set_min_proto_version(ctx, 0); - OPENSSL_assert(ret == 1); - ret = SSL_CTX_set_cipher_list(ctx, "ALL:eNULL:@SECLEVEL=0"); - OPENSSL_assert(ret == 1); + if (SSL_CTX_set_min_proto_version(ctx, 0) != 1) + goto end; + if (SSL_CTX_set_cipher_list(ctx, "ALL:eNULL:@SECLEVEL=0") != 1) + goto end; -#ifndef OPENSSL_NO_DEPRECATED_3_0 /* RSA */ - bio_buf = BIO_new(BIO_s_mem()); - OPENSSL_assert((size_t)BIO_write(bio_buf, RSAPrivateKeyPEM, sizeof(RSAPrivateKeyPEM)) == sizeof(RSAPrivateKeyPEM)); - privkey = PEM_read_bio_RSAPrivateKey(bio_buf, NULL, NULL, NULL); - ERR_print_errors_fp(stderr); - OPENSSL_assert(privkey != NULL); - BIO_free(bio_buf); - pkey = EVP_PKEY_new(); - EVP_PKEY_assign_RSA(pkey, privkey); - ret = SSL_CTX_use_PrivateKey(ctx, pkey); - OPENSSL_assert(ret == 1); - EVP_PKEY_free(pkey); -#endif - - bio_buf = BIO_new(BIO_s_mem()); - OPENSSL_assert((size_t)BIO_write(bio_buf, RSACertificatePEM, sizeof(RSACertificatePEM)) == sizeof(RSACertificatePEM)); - cert = PEM_read_bio_X509(bio_buf, NULL, NULL, NULL); - BIO_free(bio_buf); - OPENSSL_assert(cert != NULL); - ret = SSL_CTX_use_certificate(ctx, cert); - OPENSSL_assert(ret == 1); - X509_free(cert); + if (!use_pem_privkey(ctx, RSAPrivateKeyPEM, sizeof(RSAPrivateKeyPEM))) + goto end; + if (!use_pem_cert(ctx, RSACertificatePEM, sizeof(RSACertificatePEM))) + goto end; #ifndef OPENSSL_NO_EC -#ifndef OPENSSL_NO_DEPRECATED_3_0 /* ECDSA */ - bio_buf = BIO_new(BIO_s_mem()); - OPENSSL_assert((size_t)BIO_write(bio_buf, ECDSAPrivateKeyPEM, sizeof(ECDSAPrivateKeyPEM)) == sizeof(ECDSAPrivateKeyPEM)); - ecdsakey = PEM_read_bio_ECPrivateKey(bio_buf, NULL, NULL, NULL); - ERR_print_errors_fp(stderr); - OPENSSL_assert(ecdsakey != NULL); - BIO_free(bio_buf); - pkey = EVP_PKEY_new(); - EVP_PKEY_assign_EC_KEY(pkey, ecdsakey); - ret = SSL_CTX_use_PrivateKey(ctx, pkey); - OPENSSL_assert(ret == 1); - EVP_PKEY_free(pkey); -#endif - bio_buf = BIO_new(BIO_s_mem()); - OPENSSL_assert((size_t)BIO_write(bio_buf, ECDSACertPEM, sizeof(ECDSACertPEM)) == sizeof(ECDSACertPEM)); - cert = PEM_read_bio_X509(bio_buf, NULL, NULL, NULL); - OPENSSL_assert(cert != NULL); - BIO_free(bio_buf); - ret = SSL_CTX_use_certificate(ctx, cert); - OPENSSL_assert(ret == 1); - X509_free(cert); + if (!use_pem_privkey(ctx, ECDSAPrivateKeyPEM, sizeof(ECDSAPrivateKeyPEM))) + goto end; + if (!use_pem_cert(ctx, ECDSACertPEM, sizeof(ECDSACertPEM))) + goto end; #endif -#if !defined(OPENSSL_NO_DSA) && !defined(OPENSSL_NO_DEPRECATED_3_0) +#ifndef OPENSSL_NO_DSA /* DSA */ - bio_buf = BIO_new(BIO_s_mem()); - OPENSSL_assert((size_t)BIO_write(bio_buf, DSAPrivateKeyPEM, sizeof(DSAPrivateKeyPEM)) == sizeof(DSAPrivateKeyPEM)); - dsakey = PEM_read_bio_DSAPrivateKey(bio_buf, NULL, NULL, NULL); - ERR_print_errors_fp(stderr); - OPENSSL_assert(dsakey != NULL); - BIO_free(bio_buf); - pkey = EVP_PKEY_new(); - EVP_PKEY_assign_DSA(pkey, dsakey); - ret = SSL_CTX_use_PrivateKey(ctx, pkey); - OPENSSL_assert(ret == 1); - EVP_PKEY_free(pkey); - - bio_buf = BIO_new(BIO_s_mem()); - OPENSSL_assert((size_t)BIO_write(bio_buf, DSACertPEM, sizeof(DSACertPEM)) == sizeof(DSACertPEM)); - cert = PEM_read_bio_X509(bio_buf, NULL, NULL, NULL); - OPENSSL_assert(cert != NULL); - BIO_free(bio_buf); - ret = SSL_CTX_use_certificate(ctx, cert); - OPENSSL_assert(ret == 1); - X509_free(cert); + if (!use_pem_privkey(ctx, DSAPrivateKeyPEM, sizeof(DSAPrivateKeyPEM))) + goto end; + if (!use_pem_cert(ctx, DSACertPEM, sizeof(DSACertPEM))) + goto end; #endif server = SSL_new(ctx); + if (server == NULL) + goto end; in = BIO_new(BIO_s_mem()); + if (in == NULL) + goto end; out = BIO_new(BIO_s_mem()); + if (out == NULL) { + BIO_free(in); + goto end; + } SSL_set_bio(server, in, out); SSL_set_accept_state(server); - OPENSSL_assert((size_t)BIO_write(in, buf, (int)len) == len); + if ((size_t)BIO_write(in, buf, (int)len) != len) + goto end; if (SSL_do_handshake(server) == 1) { /* Keep reading application data until error or EOF. */ @@ -904,6 +888,7 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) } } } +end: SSL_free(server); ERR_clear_error(); SSL_CTX_free(ctx); diff --git a/fuzz/hashtable.c b/fuzz/hashtable.c index 9e9519b865..9eb81c0a9e 100644 --- a/fuzz/hashtable.c +++ b/fuzz/hashtable.c @@ -162,22 +162,11 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) /* set the proper key value */ HT_SET_KEY_FIELD(&key, fuzzkey, keyval); + memcpy(&valptr->value, &buf[3], sizeof(uint64_t)); + /* lock the table */ ossl_ht_write_lock(fuzzer_table); - /* - * If the value to insert is already allocated - * then we expect a conflict in the insert - * i.e. we predict a return code of 0 instead - * of 1. On replacement, we expect it to succeed - * always - */ - if (valptr->flags & FZ_FLAG_ALLOCATED) { - if (!IS_REPLACE(op_flags)) - rc_prediction = 0; - } - - memcpy(&valptr->value, &buf[3], sizeof(uint64_t)); /* * do the insert/replace */ @@ -188,30 +177,18 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) rc = ossl_ht_fz_FUZZER_VALUE_insert(fuzzer_table, TO_HT_KEY(&key), valptr, NULL); - if (rc == -1) - /* failed to grow the hash table due to too many collisions */ - break; - - /* - * mark the entry as being allocated - */ - valptr->flags |= FZ_FLAG_ALLOCATED; - /* * unlock the table */ ossl_ht_write_unlock(fuzzer_table); /* - * Now check to make sure we did the right thing + * mark the entry as being allocated */ - OPENSSL_assert(rc == rc_prediction); - - /* - * successful insertion if there wasn't a conflict - */ - if (rc_prediction == 1) + if (rc == 1) { + valptr->flags |= FZ_FLAG_ALLOCATED; IS_REPLACE(op_flags) ? replacements++ : inserts++; + } break; case OP_DELETE: @@ -226,15 +203,6 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) /* lock the table */ ossl_ht_write_lock(fuzzer_table); - /* - * If the value to delete is not already allocated - * then we expect a miss in the delete - * i.e. we predict a return code of 0 instead - * of 1 - */ - if (!(valptr->flags & FZ_FLAG_ALLOCATED)) - rc_prediction = 0; - /* * do the delete */ @@ -245,22 +213,10 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) */ ossl_ht_write_unlock(fuzzer_table); - /* - * Now check to make sure we did the right thing - */ - OPENSSL_assert(rc == rc_prediction); - - /* - * once the unlock is done, the table rcu will have synced - * meaning the free function has run, so we can confirm now - * that the valptr is no longer allocated - */ - OPENSSL_assert(!(valptr->flags & FZ_FLAG_ALLOCATED)); - /* * successful deletion if there wasn't a conflict */ - if (rc_prediction == 1) + if (rc == 1) deletes++; break; @@ -301,24 +257,22 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) /* * Now check to make sure we did the right thing */ - OPENSSL_assert(lval == valptr); + if (valptr == NULL) + OPENSSL_assert(lval == NULL); + else + OPENSSL_assert(lval == NULL || lval == valptr); /* * if we expect a positive lookup, make sure that * we can use the _type and to_value functions */ - if (valptr != NULL) { + if (valptr != NULL && lval != NULL) { OPENSSL_assert(ossl_ht_fz_FUZZER_VALUE_type(v) == 1); v = ossl_ht_fz_FUZZER_VALUE_to_value(lval, &tv); OPENSSL_assert(v->value == lval); - } - - /* - * successful lookup if we didn't expect a miss - */ - if (valptr != NULL) lookups++; + } break; @@ -336,17 +290,17 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) * lock the table */ ossl_ht_write_lock(fuzzer_table); - ossl_ht_flush(fuzzer_table); + rc = ossl_ht_flush(fuzzer_table); ossl_ht_write_unlock(fuzzer_table); /* * now check to make sure everything is free */ - for (i = 0; i < USHRT_MAX; i++) - OPENSSL_assert((prediction_table[i].flags & FZ_FLAG_ALLOCATED) == 0); - - /* good flush */ - flushes++; + if (rc == 1) { + for (i = 0; i < USHRT_MAX; i++) + OPENSSL_assert((prediction_table[i].flags & FZ_FLAG_ALLOCATED) == 0); + flushes++; + } break; case OP_FOREACH: @@ -372,11 +326,11 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) rc_prediction = 1; htvlist = ossl_ht_filter(fuzzer_table, 1, filter_iterator, &keyval); - - OPENSSL_assert(htvlist->list_len == (size_t)rc_prediction); - - ossl_ht_value_list_free(htvlist); - filters++; + if (htvlist != NULL) { + OPENSSL_assert(htvlist->list_len == (size_t)rc_prediction); + ossl_ht_value_list_free(htvlist); + filters++; + } break; default: diff --git a/fuzz/ml-dsa.c b/fuzz/ml-dsa.c index 094af9096a..e56a023e46 100644 --- a/fuzz/ml-dsa.c +++ b/fuzz/ml-dsa.c @@ -254,6 +254,10 @@ static int keygen_ml_dsa_real_key_helper(uint8_t **buf, size_t *len, ret = 1; err: + if (!ret) { + EVP_PKEY_free(*key); + *key = NULL; + } EVP_PKEY_CTX_free(ctx); return ret; } @@ -659,7 +663,7 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) /* And run our setup/doit/cleanup sequence */ if (ops[operation].setup != NULL) ops[operation].setup(&buffer_cursor, &len, &in1, &in2); - if (ops[operation].doit != NULL) + if (ops[operation].doit != NULL && in1 != NULL) ops[operation].doit(&buffer_cursor, &len, in1, in2, &out1, &out2); if (ops[operation].cleanup != NULL) ops[operation].cleanup(in1, in2, out1, out2); diff --git a/fuzz/ml-kem.c b/fuzz/ml-kem.c index 6e3aed8b23..dfd4faf6dc 100644 --- a/fuzz/ml-kem.c +++ b/fuzz/ml-kem.c @@ -650,7 +650,7 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) */ if (ops[operation].setup != NULL) ops[operation].setup(&buffer_cursor, &len, &in1, &in2); - if (ops[operation].doit != NULL) + if (ops[operation].doit != NULL && in1 != NULL) ops[operation].doit(&buffer_cursor, &len, in1, in2, &out1, &out2); if (ops[operation].cleanup != NULL) ops[operation].cleanup(in1, in2, out1, out2); diff --git a/fuzz/pem.c b/fuzz/pem.c index a8a1d810f0..a2da9820a7 100644 --- a/fuzz/pem.c +++ b/fuzz/pem.c @@ -31,7 +31,15 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) return 0; in = BIO_new(BIO_s_mem()); - OPENSSL_assert((size_t)BIO_write(in, buf + 1, (int)(len - 1)) == len - 1); + if (in == NULL) { + ERR_clear_error(); + return 0; + } + if ((size_t)BIO_write(in, buf + 1, (int)(len - 1)) != len - 1) { + BIO_free(in); + ERR_clear_error(); + return 0; + } if (PEM_read_bio_ex(in, &name, &header, &data, &outlen, buf[0]) == 1) { /* Try to read all the data we get to see if allocated properly. */ BIO_write(in, name, (int)strlen(name)); diff --git a/fuzz/pkcs12.c b/fuzz/pkcs12.c index 3bc9b5f7d7..74e2b6dd8a 100644 --- a/fuzz/pkcs12.c +++ b/fuzz/pkcs12.c @@ -39,7 +39,15 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) return 0; in = BIO_new(BIO_s_mem()); - OPENSSL_assert((size_t)BIO_write(in, buf, (int)len) == len); + if (in == NULL) { + ERR_clear_error(); + return 0; + } + if ((size_t)BIO_write(in, buf, (int)len) != len) { + BIO_free(in); + ERR_clear_error(); + return 0; + } p12 = d2i_PKCS12_bio(in, NULL); if (p12 != NULL) { PKCS12_verify_mac(p12, NULL, 0); diff --git a/fuzz/provider.c b/fuzz/provider.c index e3acf3e470..bc45489a2f 100644 --- a/fuzz/provider.c +++ b/fuzz/provider.c @@ -304,6 +304,7 @@ static OSSL_PARAM *fuzz_params(OSSL_PARAM *param, const uint8_t **buf, size_t *l if (!read_int(buf, len, &use_param)) { use_param = OPENSSL_malloc(sizeof(uint64_t)); if (use_param == NULL) { + free_params(fuzzed_parameters); OPENSSL_free(fuzzed_parameters); return NULL; } @@ -315,6 +316,7 @@ static OSSL_PARAM *fuzz_params(OSSL_PARAM *param, const uint8_t **buf, size_t *l if (strcmp(param->key, OSSL_KDF_PARAM_ITER) == 0) { p_value_int = OPENSSL_malloc(sizeof(ITERS)); if (p_value_int == NULL) { + free_params(fuzzed_parameters); OPENSSL_free(fuzzed_parameters); OPENSSL_free(use_param); return NULL; @@ -323,6 +325,7 @@ static OSSL_PARAM *fuzz_params(OSSL_PARAM *param, const uint8_t **buf, size_t *l } else if (strcmp(param->key, OSSL_KDF_PARAM_SCRYPT_N) == 0) { p_value_int = OPENSSL_malloc(sizeof(ITERS)); if (p_value_int == NULL) { + free_params(fuzzed_parameters); OPENSSL_free(fuzzed_parameters); OPENSSL_free(use_param); return NULL; @@ -331,6 +334,7 @@ static OSSL_PARAM *fuzz_params(OSSL_PARAM *param, const uint8_t **buf, size_t *l } else if (strcmp(param->key, OSSL_KDF_PARAM_SCRYPT_R) == 0) { p_value_int = OPENSSL_malloc(sizeof(BLOCKSIZE)); if (p_value_int == NULL) { + free_params(fuzzed_parameters); OPENSSL_free(fuzzed_parameters); OPENSSL_free(use_param); return NULL; @@ -339,6 +343,7 @@ static OSSL_PARAM *fuzz_params(OSSL_PARAM *param, const uint8_t **buf, size_t *l } else if (strcmp(param->key, OSSL_KDF_PARAM_SCRYPT_P) == 0) { p_value_int = OPENSSL_malloc(sizeof(BLOCKSIZE)); if (p_value_int == NULL) { + free_params(fuzzed_parameters); OPENSSL_free(fuzzed_parameters); OPENSSL_free(use_param); return NULL; @@ -347,6 +352,7 @@ static OSSL_PARAM *fuzz_params(OSSL_PARAM *param, const uint8_t **buf, size_t *l } else if (!*use_param || !read_int(buf, len, &p_value_int)) { p_value_int = OPENSSL_malloc(sizeof(int64_t)); if (p_value_int == NULL) { + free_params(fuzzed_parameters); OPENSSL_free(fuzzed_parameters); OPENSSL_free(use_param); return NULL; @@ -362,6 +368,7 @@ static OSSL_PARAM *fuzz_params(OSSL_PARAM *param, const uint8_t **buf, size_t *l if (strcmp(param->key, OSSL_KDF_PARAM_ITER) == 0) { p_value_uint = OPENSSL_malloc(sizeof(UITERS)); if (p_value_uint == NULL) { + free_params(fuzzed_parameters); OPENSSL_free(fuzzed_parameters); OPENSSL_free(use_param); return NULL; @@ -370,6 +377,7 @@ static OSSL_PARAM *fuzz_params(OSSL_PARAM *param, const uint8_t **buf, size_t *l } else if (strcmp(param->key, OSSL_KDF_PARAM_SCRYPT_N) == 0) { p_value_uint = OPENSSL_malloc(sizeof(UITERS)); if (p_value_uint == NULL) { + free_params(fuzzed_parameters); OPENSSL_free(fuzzed_parameters); OPENSSL_free(use_param); return NULL; @@ -378,6 +386,7 @@ static OSSL_PARAM *fuzz_params(OSSL_PARAM *param, const uint8_t **buf, size_t *l } else if (strcmp(param->key, OSSL_KDF_PARAM_SCRYPT_R) == 0) { p_value_uint = OPENSSL_malloc(sizeof(UBLOCKSIZE)); if (p_value_uint == NULL) { + free_params(fuzzed_parameters); OPENSSL_free(fuzzed_parameters); OPENSSL_free(use_param); return NULL; @@ -386,6 +395,7 @@ static OSSL_PARAM *fuzz_params(OSSL_PARAM *param, const uint8_t **buf, size_t *l } else if (strcmp(param->key, OSSL_KDF_PARAM_SCRYPT_P) == 0) { p_value_uint = OPENSSL_malloc(sizeof(UBLOCKSIZE)); if (p_value_uint == NULL) { + free_params(fuzzed_parameters); OPENSSL_free(fuzzed_parameters); OPENSSL_free(use_param); return NULL; @@ -394,6 +404,7 @@ static OSSL_PARAM *fuzz_params(OSSL_PARAM *param, const uint8_t **buf, size_t *l } else if (!*use_param || !read_uint(buf, len, &p_value_uint)) { p_value_uint = OPENSSL_malloc(sizeof(uint64_t)); if (p_value_uint == NULL) { + free_params(fuzzed_parameters); OPENSSL_free(fuzzed_parameters); OPENSSL_free(use_param); return NULL; @@ -409,6 +420,7 @@ static OSSL_PARAM *fuzz_params(OSSL_PARAM *param, const uint8_t **buf, size_t *l if (!*use_param || !read_double(buf, len, &p_value_double)) { p_value_double = OPENSSL_malloc(sizeof(double)); if (p_value_double == NULL) { + free_params(fuzzed_parameters); OPENSSL_free(fuzzed_parameters); OPENSSL_free(use_param); return NULL; diff --git a/fuzz/quic-client.c b/fuzz/quic-client.c index 851ecc5cc9..e75390ad60 100644 --- a/fuzz/quic-client.c +++ b/fuzz/quic-client.c @@ -77,6 +77,8 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) if (client == NULL) goto end; + allstreams[0] = stream = client; + fake_now = ossl_ms2time(1); if (!ossl_quic_set_override_now_cb(client, fake_now_cb, NULL)) goto end; @@ -90,7 +92,8 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) if (!BIO_ADDR_rawmake(peer_addr, AF_INET, &ina, sizeof(ina), htons(4433))) goto end; - SSL_set_tlsext_host_name(client, "localhost"); + if (SSL_set_tlsext_host_name(client, "localhost") != 1) + goto end; in = BIO_new(BIO_s_dgram_mem()); if (in == NULL) goto end; @@ -116,7 +119,6 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) 0)) goto end; - allstreams[0] = stream = client; for (;;) { size_t size; uint64_t nxtpktms = 0; diff --git a/fuzz/quic-srtm.c b/fuzz/quic-srtm.c index a7897da710..6e152299e1 100644 --- a/fuzz/quic-srtm.c +++ b/fuzz/quic-srtm.c @@ -77,9 +77,9 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) sizeof(arg_token.token))) continue; /* just stop */ - ossl_quic_srtm_add(srtm, (void *)(uintptr_t)arg_opaque, - arg_seq_num, &arg_token); - ossl_quic_srtm_check(srtm); + if (ossl_quic_srtm_add(srtm, (void *)(uintptr_t)arg_opaque, + arg_seq_num, &arg_token)) + ossl_quic_srtm_check(srtm); break; case CMD_REMOVE: @@ -87,17 +87,17 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) || !PACKET_get_net_8(&pkt, &arg_seq_num)) continue; /* just stop */ - ossl_quic_srtm_remove(srtm, (void *)(uintptr_t)arg_opaque, - arg_seq_num); - ossl_quic_srtm_check(srtm); + if (ossl_quic_srtm_remove(srtm, (void *)(uintptr_t)arg_opaque, + arg_seq_num)) + ossl_quic_srtm_check(srtm); break; case CMD_CULL: if (!PACKET_get_net_8(&pkt, &arg_opaque)) continue; /* just stop */ - ossl_quic_srtm_cull(srtm, (void *)(uintptr_t)arg_opaque); - ossl_quic_srtm_check(srtm); + if (ossl_quic_srtm_cull(srtm, (void *)(uintptr_t)arg_opaque)) + ossl_quic_srtm_check(srtm); break; case CMD_LOOKUP: @@ -106,9 +106,9 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) || !PACKET_get_net_8(&pkt, &arg_idx)) continue; /* just stop */ - ossl_quic_srtm_lookup(srtm, &arg_token, (size_t)arg_idx, - NULL, NULL); - ossl_quic_srtm_check(srtm); + if (ossl_quic_srtm_lookup(srtm, &arg_token, (size_t)arg_idx, + NULL, NULL)) + ossl_quic_srtm_check(srtm); break; default: diff --git a/fuzz/server.c b/fuzz/server.c index 1723cecda5..740ade8513 100644 --- a/fuzz/server.c +++ b/fuzz/server.c @@ -25,6 +25,7 @@ #include #include "fuzzer.h" +#ifndef OPENSSL_NO_DEPRECATED_3_0 static const uint8_t kCertificateDER[] = { 0x30, 0x82, 0x02, 0xff, 0x30, 0x82, 0x01, 0xe7, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x11, 0x00, @@ -125,7 +126,6 @@ static const uint8_t kCertificateDER[] = { 0x76, 0x8a, 0xbb }; -#ifndef OPENSSL_NO_DEPRECATED_3_0 static const uint8_t kRSAPrivateKeyDER[] = { 0x30, 0x82, 0x04, 0xa5, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xce, 0x47, 0xcb, 0x11, @@ -281,7 +281,6 @@ static const uint8_t kRSAPrivateKeyDER[] = { #endif #ifndef OPENSSL_NO_EC -#ifndef OPENSSL_NO_DEPRECATED_3_0 /* * -----BEGIN EC PRIVATE KEY----- * MHcCAQEEIJLyl7hJjpQL/RhP1x2zS79xdiPJQB683gWeqcqHPeZkoAoGCCqGSM49 @@ -310,7 +309,6 @@ static const char ECDSAPrivateKeyPEM[] = { 0x4e, 0x44, 0x20, 0x45, 0x43, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a }; -#endif /* * -----BEGIN CERTIFICATE----- @@ -373,7 +371,7 @@ static const char ECDSACertPEM[] = { }; #endif -#if !defined(OPENSSL_NO_DSA) && !defined(OPENSSL_NO_DEPRECATED_3_0) +#ifndef OPENSSL_NO_DSA /* * -----BEGIN DSA PRIVATE KEY----- * MIIBuwIBAAKBgQDdkFKzNABLOha7Eqj7004+p5fhtR6bxpujToMmSZTYi8igVVXP @@ -593,139 +591,155 @@ time_t time(time_t *t) TIME_IMPL(t) return 1; } +#if !defined(OPENSSL_NO_EC) || !defined(OPENSSL_NO_DSA) +static int use_pem_privkey(SSL_CTX *ctx, const void *pem, size_t pem_len) +{ + BIO *bio_buf; + EVP_PKEY *pkey; + int rv = 0; + + bio_buf = BIO_new(BIO_s_mem()); + if (bio_buf == NULL) + return 0; + if ((size_t)BIO_write(bio_buf, pem, (int)pem_len) != pem_len) { + BIO_free(bio_buf); + return 0; + } + pkey = PEM_read_bio_PrivateKey(bio_buf, NULL, NULL, NULL); + BIO_free(bio_buf); + if (pkey == NULL) + return 0; + if (SSL_CTX_use_PrivateKey(ctx, pkey) == 1) + rv = 1; + EVP_PKEY_free(pkey); + return rv; +} + +static int use_pem_cert(SSL_CTX *ctx, const void *pem, size_t pem_len) +{ + BIO *bio_buf; + X509 *cert; + int rv = 0; + + bio_buf = BIO_new(BIO_s_mem()); + if (bio_buf == NULL) + return 0; + if ((size_t)BIO_write(bio_buf, pem, (int)pem_len) != pem_len) { + BIO_free(bio_buf); + return 0; + } + cert = PEM_read_bio_X509(bio_buf, NULL, NULL, NULL); + BIO_free(bio_buf); + if (cert == NULL) + return 0; + if (SSL_CTX_use_certificate(ctx, cert) == 1) + rv = 1; + X509_free(cert); + return rv; +} +#endif + int FuzzerTestOneInput(const uint8_t *buf, size_t len) { - SSL *server; + SSL *server = NULL; BIO *in; BIO *out; -#if !defined(OPENSSL_NO_EC) \ - || (!defined(OPENSSL_NO_DSA) && !defined(OPENSSL_NO_DEPRECATED_3_0)) - BIO *bio_buf; -#endif SSL_CTX *ctx; - int ret; #ifndef OPENSSL_NO_DEPRECATED_3_0 - RSA *privkey; -#endif const uint8_t *bufp; -#if !defined(OPENSSL_NO_DEPRECATED_3_0) - EVP_PKEY *pkey; -#endif - X509 *cert; -#ifndef OPENSSL_NO_DEPRECATED_3_0 -#ifndef OPENSSL_NO_EC - EC_KEY *ecdsakey = NULL; -#endif -#endif -#if !defined(OPENSSL_NO_DSA) && !defined(OPENSSL_NO_DEPRECATED_3_0) - DSA *dsakey = NULL; + RSA *privkey = NULL; + EVP_PKEY *pkey = NULL; + X509 *cert = NULL; #endif uint8_t opt; + int ret; if (len < 2 || len > INT_MAX) return 0; - /* This only fuzzes the initial flow from the client so far. */ ctx = SSL_CTX_new(TLS_method()); - OPENSSL_assert(ctx != NULL); - ret = SSL_CTX_set_min_proto_version(ctx, 0); - OPENSSL_assert(ret == 1); - ret = SSL_CTX_set_cipher_list(ctx, "ALL:eNULL:@SECLEVEL=0"); - OPENSSL_assert(ret == 1); + if (ctx == NULL) + return 0; + if (SSL_CTX_set_min_proto_version(ctx, 0) != 1) + goto end; + if (SSL_CTX_set_cipher_list(ctx, "ALL:eNULL:@SECLEVEL=0") != 1) + goto end; #ifndef OPENSSL_NO_DEPRECATED_3_0 /* RSA */ bufp = kRSAPrivateKeyDER; privkey = d2i_RSAPrivateKey(NULL, &bufp, sizeof(kRSAPrivateKeyDER)); - OPENSSL_assert(privkey != NULL); + if (privkey == NULL) + goto end; pkey = EVP_PKEY_new(); - OPENSSL_assert(pkey != NULL); - EVP_PKEY_assign_RSA(pkey, privkey); + if (pkey == NULL) { + RSA_free(privkey); + goto end; + } + if (!EVP_PKEY_assign_RSA(pkey, privkey)) { + /* assignment failed; pkey doesn't own privkey, clean both */ + RSA_free(privkey); + EVP_PKEY_free(pkey); + goto end; + } ret = SSL_CTX_use_PrivateKey(ctx, pkey); - OPENSSL_assert(ret == 1); EVP_PKEY_free(pkey); -#endif + if (ret != 1) + goto end; bufp = kCertificateDER; cert = d2i_X509(NULL, &bufp, sizeof(kCertificateDER)); - OPENSSL_assert(cert != NULL); + if (cert == NULL) + goto end; ret = SSL_CTX_use_certificate(ctx, cert); - OPENSSL_assert(ret == 1); X509_free(cert); + if (ret != 1) + goto end; +#endif #ifndef OPENSSL_NO_EC -#ifndef OPENSSL_NO_DEPRECATED_3_0 /* ECDSA */ - bio_buf = BIO_new(BIO_s_mem()); - OPENSSL_assert(bio_buf != NULL); - OPENSSL_assert((size_t)BIO_write(bio_buf, ECDSAPrivateKeyPEM, sizeof(ECDSAPrivateKeyPEM)) == sizeof(ECDSAPrivateKeyPEM)); - ecdsakey = PEM_read_bio_ECPrivateKey(bio_buf, NULL, NULL, NULL); - ERR_print_errors_fp(stderr); - OPENSSL_assert(ecdsakey != NULL); - BIO_free(bio_buf); - pkey = EVP_PKEY_new(); - OPENSSL_assert(pkey != NULL); - EVP_PKEY_assign_EC_KEY(pkey, ecdsakey); - ret = SSL_CTX_use_PrivateKey(ctx, pkey); - OPENSSL_assert(ret == 1); - EVP_PKEY_free(pkey); -#endif - bio_buf = BIO_new(BIO_s_mem()); - OPENSSL_assert(bio_buf != NULL); - OPENSSL_assert((size_t)BIO_write(bio_buf, ECDSACertPEM, sizeof(ECDSACertPEM)) == sizeof(ECDSACertPEM)); - cert = PEM_read_bio_X509(bio_buf, NULL, NULL, NULL); - OPENSSL_assert(cert != NULL); - BIO_free(bio_buf); - ret = SSL_CTX_use_certificate(ctx, cert); - OPENSSL_assert(ret == 1); - X509_free(cert); + if (!use_pem_privkey(ctx, ECDSAPrivateKeyPEM, sizeof(ECDSAPrivateKeyPEM))) + goto end; + if (!use_pem_cert(ctx, ECDSACertPEM, sizeof(ECDSACertPEM))) + goto end; #endif -#if !defined(OPENSSL_NO_DSA) && !defined(OPENSSL_NO_DEPRECATED_3_0) +#ifndef OPENSSL_NO_DSA /* DSA */ - bio_buf = BIO_new(BIO_s_mem()); - OPENSSL_assert(bio_buf != NULL); - OPENSSL_assert((size_t)BIO_write(bio_buf, DSAPrivateKeyPEM, sizeof(DSAPrivateKeyPEM)) == sizeof(DSAPrivateKeyPEM)); - dsakey = PEM_read_bio_DSAPrivateKey(bio_buf, NULL, NULL, NULL); - ERR_print_errors_fp(stderr); - OPENSSL_assert(dsakey != NULL); - BIO_free(bio_buf); - pkey = EVP_PKEY_new(); - OPENSSL_assert(pkey != NULL); - EVP_PKEY_assign_DSA(pkey, dsakey); - ret = SSL_CTX_use_PrivateKey(ctx, pkey); - OPENSSL_assert(ret == 1); - EVP_PKEY_free(pkey); - - bio_buf = BIO_new(BIO_s_mem()); - OPENSSL_assert(bio_buf != NULL); - OPENSSL_assert((size_t)BIO_write(bio_buf, DSACertPEM, sizeof(DSACertPEM)) == sizeof(DSACertPEM)); - cert = PEM_read_bio_X509(bio_buf, NULL, NULL, NULL); - OPENSSL_assert(cert != NULL); - BIO_free(bio_buf); - ret = SSL_CTX_use_certificate(ctx, cert); - OPENSSL_assert(ret == 1); - X509_free(cert); + if (!use_pem_privkey(ctx, DSAPrivateKeyPEM, sizeof(DSAPrivateKeyPEM))) + goto end; + if (!use_pem_cert(ctx, DSACertPEM, sizeof(DSACertPEM))) + goto end; #endif server = SSL_new(ctx); + if (server == NULL) + goto end; in = BIO_new(BIO_s_mem()); - OPENSSL_assert(in != NULL); + if (in == NULL) + goto end; out = BIO_new(BIO_s_mem()); - OPENSSL_assert(out != NULL); + if (out == NULL) { + BIO_free(in); + goto end; + } SSL_set_bio(server, in, out); SSL_set_accept_state(server); opt = (uint8_t)buf[len - 1]; len--; - OPENSSL_assert((size_t)BIO_write(in, buf, (int)len) == len); + if ((size_t)BIO_write(in, buf, (int)len) != len) + goto end; if ((opt & 0x01) != 0) { do { char early_buf[16384]; size_t early_len; - ret = SSL_read_early_data(server, early_buf, sizeof(early_buf), &early_len); + + ret = SSL_read_early_data(server, early_buf, sizeof(early_buf), + &early_len); if (ret != SSL_READ_EARLY_DATA_SUCCESS) break; @@ -741,6 +755,7 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) } } } +end: SSL_free(server); ERR_clear_error(); SSL_CTX_free(ctx); diff --git a/fuzz/slh-dsa.c b/fuzz/slh-dsa.c index 17238bcf8b..0f78a01da8 100644 --- a/fuzz/slh-dsa.c +++ b/fuzz/slh-dsa.c @@ -63,26 +63,32 @@ static EVP_PKEY *slh_dsa_gen_key(const char *name, uint32_t keysize, { EVP_PKEY_CTX *ctx; EVP_PKEY *new = NULL; - int rc; ctx = EVP_PKEY_CTX_new_from_name(NULL, name, NULL); - OPENSSL_assert(ctx != NULL); + if (ctx == NULL) + return NULL; if (params != NULL) { new = EVP_PKEY_new(); - OPENSSL_assert(EVP_PKEY_fromdata_init(ctx)); - if (*param_broken) { - rc = EVP_PKEY_fromdata(ctx, &new, EVP_PKEY_KEYPAIR, params); - OPENSSL_assert(rc == 0); + if (new == NULL) + goto out; + if (!EVP_PKEY_fromdata_init(ctx)) { + EVP_PKEY_free(new); + new = NULL; + goto out; + } + if (EVP_PKEY_fromdata(ctx, &new, EVP_PKEY_KEYPAIR, params) != 1) { EVP_PKEY_free(new); new = NULL; - } else { - OPENSSL_assert(EVP_PKEY_fromdata(ctx, &new, EVP_PKEY_KEYPAIR, params) == 1); } goto out; } - OPENSSL_assert(EVP_PKEY_keygen_init(ctx)); - OPENSSL_assert(EVP_PKEY_generate(ctx, &new)); + if (!EVP_PKEY_keygen_init(ctx)) + goto out; + if (!EVP_PKEY_generate(ctx, &new)) { + EVP_PKEY_free(new); + new = NULL; + } out: EVP_PKEY_CTX_free(ctx); @@ -221,9 +227,10 @@ static void slh_dsa_gen_key_with_params(uint8_t **buf, size_t *len, *buf = consume_uint8t(*buf, len, &selector); keytype = select_keytype(selector, &keysize); - RAND_bytes(pubbuf, PARAM_BUF_SZ); - RAND_bytes(prvbuf, PARAM_BUF_SZ); - RAND_bytes(sdbuf, PARAM_BUF_SZ); + if (!RAND_bytes(pubbuf, PARAM_BUF_SZ) + || !RAND_bytes(prvbuf, PARAM_BUF_SZ) + || !RAND_bytes(sdbuf, PARAM_BUF_SZ)) + return; /* * select an invalid length if the buffer 0th bit is one @@ -260,11 +267,6 @@ static void slh_dsa_gen_key_with_params(uint8_t **buf, size_t *len, params[2] = OSSL_PARAM_construct_end(); *out1 = (void *)slh_dsa_gen_key(keytype, keysize, params, &broken); - - if (broken) - OPENSSL_assert(*out1 == NULL); - else - OPENSSL_assert(*out1 != NULL); return; } @@ -319,7 +321,6 @@ static void slh_dsa_sign_verify(uint8_t **buf, size_t *len, void *key1, OSSL_PARAM params[4]; int paramidx = 0; int intval1, intval2; - int expect_init_rc = 1; *buf = consume_uint8t(*buf, len, &selector); if (*buf == NULL) @@ -340,10 +341,6 @@ static void slh_dsa_sign_verify(uint8_t **buf, size_t *len, void *key1, msg = (unsigned char *)*buf; msg_len = *len; - /* if msg_len > 255, sign_message_init will fail */ - if (msg_len > 255 && (selector & 0x1) != 0) - expect_init_rc = 0; - *len = 0; if (selector & 0x1) @@ -365,33 +362,39 @@ static void slh_dsa_sign_verify(uint8_t **buf, size_t *len, void *key1, params[paramidx] = OSSL_PARAM_construct_end(); key = (void *)slh_dsa_gen_key(keytype, keylen, NULL, 0); - OPENSSL_assert(key != NULL); + if (key == NULL) + return; *out1 = key; /* for cleanup */ ctx = EVP_PKEY_CTX_new_from_pkey(NULL, key, NULL); - OPENSSL_assert(ctx != NULL); - - sig_alg = EVP_SIGNATURE_fetch(NULL, keytype, NULL); - OPENSSL_assert(sig_alg != NULL); - - OPENSSL_assert(EVP_PKEY_sign_message_init(ctx, sig_alg, params) == expect_init_rc); - /* - * the context_string parameter can be no more than 255 bytes, so if - * our random input buffer is greater than that, we expect failure above, - * which we check for. In that event, there's nothing more we can do here - * so bail out - */ - if (expect_init_rc == 0) + if (ctx == NULL) goto out; - OPENSSL_assert(EVP_PKEY_sign(ctx, NULL, &sig_len, msg, msg_len)); + sig_alg = EVP_SIGNATURE_fetch(NULL, keytype, NULL); + if (sig_alg == NULL) + goto out; + + /* + * the context_string parameter can be no more than 255 bytes, so if + * our random input buffer is greater than that, sign_message_init will + * fail, in which case there's nothing more we can do here so bail out + */ + if (EVP_PKEY_sign_message_init(ctx, sig_alg, params) != 1) + goto out; + + if (EVP_PKEY_sign(ctx, NULL, &sig_len, msg, msg_len) != 1) + goto out; sig = OPENSSL_zalloc(sig_len); - OPENSSL_assert(sig != NULL); + if (sig == NULL) + goto out; - OPENSSL_assert(EVP_PKEY_sign(ctx, sig, &sig_len, msg, msg_len)); + if (EVP_PKEY_sign(ctx, sig, &sig_len, msg, msg_len) != 1) + goto out; - OPENSSL_assert(EVP_PKEY_verify_message_init(ctx, sig_alg, params)); - OPENSSL_assert(EVP_PKEY_verify(ctx, sig, sig_len, msg, msg_len)); + if (EVP_PKEY_verify_message_init(ctx, sig_alg, params) != 1) + goto out; + if (EVP_PKEY_verify(ctx, sig, sig_len, msg, msg_len) != 1) + fprintf(stderr, "Failed to verify message\n"); out: OPENSSL_free(sig); @@ -417,32 +420,34 @@ out: static void slh_dsa_export_import(uint8_t **buf, size_t *len, void *key1, void *key2, void **out1, void **out2) { - int rc; EVP_PKEY *alice = (EVP_PKEY *)key1; EVP_PKEY *bob = (EVP_PKEY *)key2; EVP_PKEY *new = NULL; EVP_PKEY_CTX *ctx = NULL; OSSL_PARAM *params = NULL; - OPENSSL_assert(EVP_PKEY_todata(alice, EVP_PKEY_KEYPAIR, ¶ms) == 1); + if (alice == NULL || bob == NULL) + return; + + if (!EVP_PKEY_todata(alice, EVP_PKEY_KEYPAIR, ¶ms)) + goto alice_done; ctx = EVP_PKEY_CTX_new_from_pkey(NULL, alice, NULL); - OPENSSL_assert(ctx != NULL); + if (ctx == NULL) + goto alice_done; - OPENSSL_assert(EVP_PKEY_fromdata_init(ctx)); + if (!EVP_PKEY_fromdata_init(ctx)) + goto alice_done; new = EVP_PKEY_new(); - OPENSSL_assert(new != NULL); - OPENSSL_assert(EVP_PKEY_fromdata(ctx, &new, EVP_PKEY_KEYPAIR, params) == 1); + if (new == NULL) + goto alice_done; + if (EVP_PKEY_fromdata(ctx, &new, EVP_PKEY_KEYPAIR, params) != 1) + goto alice_done; - /* - * EVP_PKEY returns: - * 1 if the keys are equivalent - * 0 if the keys are not equivalent - * -1 if the key types are different - * -2 if the operation is not supported - */ - OPENSSL_assert(EVP_PKEY_eq(alice, new) == 1); + (void)EVP_PKEY_eq(alice, new); + +alice_done: EVP_PKEY_free(new); EVP_PKEY_CTX_free(ctx); OSSL_PARAM_free(params); @@ -450,26 +455,26 @@ static void slh_dsa_export_import(uint8_t **buf, size_t *len, void *key1, ctx = NULL; new = NULL; - OPENSSL_assert(EVP_PKEY_todata(bob, EVP_PKEY_KEYPAIR, ¶ms) == 1); + if (!EVP_PKEY_todata(bob, EVP_PKEY_KEYPAIR, ¶ms)) + goto bob_done; ctx = EVP_PKEY_CTX_new_from_pkey(NULL, bob, NULL); - OPENSSL_assert(ctx != NULL); + if (ctx == NULL) + goto bob_done; - OPENSSL_assert(EVP_PKEY_fromdata_init(ctx)); + if (!EVP_PKEY_fromdata_init(ctx)) + goto bob_done; new = EVP_PKEY_new(); - OPENSSL_assert(new != NULL); - OPENSSL_assert(EVP_PKEY_fromdata(ctx, &new, EVP_PKEY_KEYPAIR, params) == 1); + if (new == NULL) + goto bob_done; + if (EVP_PKEY_fromdata(ctx, &new, EVP_PKEY_KEYPAIR, params) != 1) + goto bob_done; - OPENSSL_assert(EVP_PKEY_eq(bob, new) == 1); - - /* - * Depending on the types of eys that get generated - * we might get a simple non-equivalence or a type mismatch here - */ - rc = EVP_PKEY_eq(alice, new); - OPENSSL_assert(rc == 0 || rc == -1); + (void)EVP_PKEY_eq(bob, new); + (void)EVP_PKEY_eq(alice, new); +bob_done: EVP_PKEY_CTX_free(ctx); EVP_PKEY_free(new); OSSL_PARAM_free(params); @@ -589,7 +594,7 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) */ if (ops[operation].setup != NULL) ops[operation].setup(&buffer_cursor, &len, &in1, &in2); - if (ops[operation].doit != NULL) + if (ops[operation].doit != NULL && in1 != NULL) ops[operation].doit(&buffer_cursor, &len, in1, in2, &out1, &out2); if (ops[operation].cleanup != NULL) ops[operation].cleanup(in1, in2, out1, out2); diff --git a/fuzz/smime.c b/fuzz/smime.c index b55a1ef74b..ab68cb0165 100644 --- a/fuzz/smime.c +++ b/fuzz/smime.c @@ -21,8 +21,18 @@ int FuzzerInitialize(int *argc, char ***argv) int FuzzerTestOneInput(const uint8_t *buf, size_t len) { - BIO *b = BIO_new_mem_buf(buf, (int)len); - PKCS7 *p7 = SMIME_read_PKCS7(b, NULL); + BIO *b; + PKCS7 *p7; + + if (len > INT_MAX) + return 0; + + b = BIO_new_mem_buf(buf, (int)len); + if (b == NULL) { + ERR_clear_error(); + return 0; + } + p7 = SMIME_read_PKCS7(b, NULL); if (p7 != NULL) { STACK_OF(PKCS7_SIGNER_INFO) *p7si = PKCS7_get_signer_info(p7); diff --git a/fuzz/v3name.c b/fuzz/v3name.c index 9a525e6f48..87fd8c1471 100644 --- a/fuzz/v3name.c +++ b/fuzz/v3name.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include "internal/nelem.h" @@ -23,8 +24,11 @@ int FuzzerTestOneInput(const uint8_t *data, size_t size) { GENERAL_NAME *namesa; GENERAL_NAME *namesb; - const unsigned char *derp = data; + + if (size > LONG_MAX) + return 0; + /* * We create two versions of each GENERAL_NAME so that we ensure when * we compare them they are always different pointers. @@ -32,11 +36,11 @@ int FuzzerTestOneInput(const uint8_t *data, size_t size) namesa = d2i_GENERAL_NAME(NULL, &derp, (long)size); derp = data; namesb = d2i_GENERAL_NAME(NULL, &derp, (long)size); - GENERAL_NAME_cmp(namesa, namesb); - if (namesa != NULL) - GENERAL_NAME_free(namesa); - if (namesb != NULL) - GENERAL_NAME_free(namesb); + if (namesa != NULL && namesb != NULL) + GENERAL_NAME_cmp(namesa, namesb); + GENERAL_NAME_free(namesa); + GENERAL_NAME_free(namesb); + ERR_clear_error(); return 0; } diff --git a/fuzz/x509.c b/fuzz/x509.c index 68249b48db..a10e8c0067 100644 --- a/fuzz/x509.c +++ b/fuzz/x509.c @@ -99,7 +99,8 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) if (ctx == NULL) goto err; - X509_STORE_CTX_init(ctx, store, x509_1, NULL); + if (!X509_STORE_CTX_init(ctx, store, x509_1, NULL)) + goto err; if (crl != NULL) { crls = sk_X509_CRL_new_null(); From dca561d4cdb84c7e813e9b28352928edd5b2cfd8 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 28 Apr 2026 19:22:46 +0200 Subject: [PATCH 264/349] Integrate mfail functionality to fuzz tests Run the fuzz corpora under mfail in addition to the normal path, so the existing inputs also exercise malloc-failure handling. The fuzz.pl harness sizes the mfail runs to a time budget and, on a leak, bisects down to the exact file and injection point. Adds a budgeted asan/ubsan CI job to run it. Assisted-by: Claude:claude-opus-4-6 Reviewed-by: Nikola Pajkovsky Reviewed-by: Tomas Mraz MergeDate: Wed Jul 15 16:01:43 2026 (Merged from https://github.com/openssl/openssl/pull/30944) --- .github/workflows/ci.yml | 34 +++++ fuzz/build.info | 162 ++++++++++----------- fuzz/test-corpus.c | 97 +++++++++++-- test/recipes/fuzz.pl | 289 +++++++++++++++++++++++++++++++++++++- util/perl/OpenSSL/Test.pm | 19 ++- 5 files changed, 496 insertions(+), 105 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b10ab496c5..597501112f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -462,6 +462,40 @@ jobs: path: artifacts.tar.gz if-no-files-found: ignore + fuzz_tests_mfail: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - name: checkout fuzz/corpora submodule + run: git submodule update --init --depth 1 fuzz/corpora + - name: Adjust ASLR for sanitizer + run: sudo sysctl -w vm.mmap_rnd_bits=28 + - name: config + run: | + ./config --strict-warnings --banner=Configured --debug \ + -DPEDANTIC -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION \ + enable-asan enable-ec_explicit_curves enable-ubsan \ + enable-rc5 enable-md2 enable-ec_nistp_64_gcc_128 \ + enable-weak-ssl-ciphers enable-nextprotoneg + perl configdata.pm --dump + - name: make + run: make -s -j4 + - name: make test (fuzz with mfail) + env: + OSSL_FUZZ_TEST_BUDGET: 1200 + OSSL_FUZZ_TEST_JOBS: 4 + run: .github/workflows/make-test OPENSSL_TEST_RAND_ORDER=0 TESTS="test_fuzz*" + - name: save artifacts + if: success() || failure() + uses: actions/upload-artifact@v5 + with: + name: "ci@fuzz_tests_mfail" + path: artifacts.tar.gz + if-no-files-found: ignore + memory_sanitizer: runs-on: ubuntu-latest steps: diff --git a/fuzz/build.info b/fuzz/build.info index 3770c2df7e..5c410260e4 100644 --- a/fuzz/build.info +++ b/fuzz/build.info @@ -176,6 +176,8 @@ IF[{- !$disabled{"fuzz-afl"} || !$disabled{"fuzz-libfuzzer"} -}] ENDIF IF[{- !$disabled{tests} -}] + $FUZZTESTSRC=test-corpus.c ../test/mfail/mfail.c + PROGRAMS{noinst}=asn1-test asn1parse-test bignum-test bndiv-test client-test conf-test crl-test server-test smime-test PROGRAMS{noinst}=pkcs12-test punycode-test pem-test decoder-test hashtable-test acert-test PROGRAMS{noinst}=v3name-test @@ -218,131 +220,131 @@ IF[{- !$disabled{tests} -}] PROGRAMS{noinst}=dtlsclient-test dtlsserver-test ENDIF - SOURCE[asn1-test]=asn1.c test-corpus.c fuzz_rand.c - INCLUDE[asn1-test]=../include - DEPEND[asn1-test]=../libcrypto ../libssl + SOURCE[asn1-test]=asn1.c $FUZZTESTSRC fuzz_rand.c + INCLUDE[asn1-test]=../include ../test/mfail + DEPEND[asn1-test]=../libcrypto.a ../libssl.a - SOURCE[asn1parse-test]=asn1parse.c test-corpus.c - INCLUDE[asn1parse-test]=../include - DEPEND[asn1parse-test]=../libcrypto + SOURCE[asn1parse-test]=asn1parse.c $FUZZTESTSRC + INCLUDE[asn1parse-test]=../include ../test/mfail + DEPEND[asn1parse-test]=../libcrypto.a - SOURCE[bignum-test]=bignum.c test-corpus.c - INCLUDE[bignum-test]=../include - DEPEND[bignum-test]=../libcrypto + SOURCE[bignum-test]=bignum.c $FUZZTESTSRC + INCLUDE[bignum-test]=../include ../test/mfail + DEPEND[bignum-test]=../libcrypto.a - SOURCE[bndiv-test]=bndiv.c test-corpus.c - INCLUDE[bndiv-test]=../include - DEPEND[bndiv-test]=../libcrypto + SOURCE[bndiv-test]=bndiv.c $FUZZTESTSRC + INCLUDE[bndiv-test]=../include ../test/mfail + DEPEND[bndiv-test]=../libcrypto.a - SOURCE[client-test]=client.c test-corpus.c fuzz_rand.c - INCLUDE[client-test]=../include - DEPEND[client-test]=../libcrypto ../libssl + SOURCE[client-test]=client.c $FUZZTESTSRC fuzz_rand.c + INCLUDE[client-test]=../include ../test/mfail + DEPEND[client-test]=../libcrypto.a ../libssl.a - SOURCE[cmp-test]=cmp.c test-corpus.c fuzz_rand.c - INCLUDE[cmp-test]=../include + SOURCE[cmp-test]=cmp.c $FUZZTESTSRC fuzz_rand.c + INCLUDE[cmp-test]=../include ../test/mfail DEPEND[cmp-test]=../libcrypto.a # referring to static lib allows using non-exported functions - SOURCE[ml-kem-test]=ml-kem.c test-corpus.c fuzz_rand.c - INCLUDE[ml-kem-test]=../include + SOURCE[ml-kem-test]=ml-kem.c $FUZZTESTSRC + INCLUDE[ml-kem-test]=../include ../test/mfail DEPEND[ml-kem-test]=../libcrypto.a # referring to static lib allows using non-exported functions - SOURCE[ml-dsa-test]=ml-dsa.c test-corpus.c fuzz_rand.c - INCLUDE[ml-dsa-test]=../include + SOURCE[ml-dsa-test]=ml-dsa.c $FUZZTESTSRC + INCLUDE[ml-dsa-test]=../include ../test/mfail DEPEND[ml-dsa-test]=../libcrypto.a # referring to static lib allows using non-exported functions - SOURCE[slh-dsa-test]=slh-dsa.c test-corpus.c fuzz_rand.c - INCLUDE[slh-dsa-test]=../include + SOURCE[slh-dsa-test]=slh-dsa.c $FUZZTESTSRC fuzz_rand.c + INCLUDE[slh-dsa-test]=../include ../test/mfail DEPEND[slh-dsa-test]=../libcrypto.a # referring to static lib allows using non-exported functions - SOURCE[cms-test]=cms.c test-corpus.c - INCLUDE[cms-test]=../include - DEPEND[cms-test]=../libcrypto + SOURCE[cms-test]=cms.c $FUZZTESTSRC + INCLUDE[cms-test]=../include ../test/mfail + DEPEND[cms-test]=../libcrypto.a - SOURCE[pkcs12-test]=pkcs12.c test-corpus.c - INCLUDE[pkcs12-test]=../include - DEPEND[pkcs12-test]=../libcrypto + SOURCE[pkcs12-test]=pkcs12.c $FUZZTESTSRC + INCLUDE[pkcs12-test]=../include ../test/mfail + DEPEND[pkcs12-test]=../libcrypto.a - SOURCE[conf-test]=conf.c test-corpus.c - INCLUDE[conf-test]=../include - DEPEND[conf-test]=../libcrypto + SOURCE[conf-test]=conf.c $FUZZTESTSRC + INCLUDE[conf-test]=../include ../test/mfail + DEPEND[conf-test]=../libcrypto.a - SOURCE[crl-test]=crl.c test-corpus.c - INCLUDE[crl-test]=../include - DEPEND[crl-test]=../libcrypto + SOURCE[crl-test]=crl.c $FUZZTESTSRC + INCLUDE[crl-test]=../include ../test/mfail + DEPEND[crl-test]=../libcrypto.a - SOURCE[ct-test]=ct.c test-corpus.c - INCLUDE[ct-test]=../include - DEPEND[ct-test]=../libcrypto + SOURCE[ct-test]=ct.c $FUZZTESTSRC + INCLUDE[ct-test]=../include ../test/mfail + DEPEND[ct-test]=../libcrypto.a - SOURCE[dtlsclient-test]=dtlsclient.c test-corpus.c fuzz_rand.c - INCLUDE[dtlsclient-test]=../include - DEPEND[dtlsclient-test]=../libcrypto ../libssl + SOURCE[dtlsclient-test]=dtlsclient.c $FUZZTESTSRC fuzz_rand.c + INCLUDE[dtlsclient-test]=../include ../test/mfail + DEPEND[dtlsclient-test]=../libcrypto.a ../libssl.a - SOURCE[dtlsserver-test]=dtlsserver.c test-corpus.c fuzz_rand.c - INCLUDE[dtlsserver-test]=../include - DEPEND[dtlsserver-test]=../libcrypto ../libssl + SOURCE[dtlsserver-test]=dtlsserver.c $FUZZTESTSRC fuzz_rand.c + INCLUDE[dtlsserver-test]=../include ../test/mfail + DEPEND[dtlsserver-test]=../libcrypto.a ../libssl.a - SOURCE[pem-test]=pem.c test-corpus.c - INCLUDE[pem-test]=../include + SOURCE[pem-test]=pem.c $FUZZTESTSRC + INCLUDE[pem-test]=../include ../test/mfail DEPEND[pem-test]=../libcrypto.a - SOURCE[decoder-test]=decoder.c test-corpus.c fuzz_rand.c - INCLUDE[decoder-test]=../include - DEPEND[decoder-test]=../libcrypto + SOURCE[decoder-test]=decoder.c $FUZZTESTSRC fuzz_rand.c + INCLUDE[decoder-test]=../include ../test/mfail + DEPEND[decoder-test]=../libcrypto.a - SOURCE[hashtable-test]=hashtable.c test-corpus.c fuzz_rand.c - INCLUDE[hashtable-test]=../include + SOURCE[hashtable-test]=hashtable.c $FUZZTESTSRC fuzz_rand.c + INCLUDE[hashtable-test]=../include ../test/mfail DEPEND[hashtable-test]=../libcrypto.a - SOURCE[acert-test]=acert.c test-corpus.c - INCLUDE[acert-test]=../include - DEPEND[acert-test]=../libcrypto + SOURCE[acert-test]=acert.c $FUZZTESTSRC + INCLUDE[acert-test]=../include ../test/mfail + DEPEND[acert-test]=../libcrypto.a - SOURCE[punycode-test]=punycode.c test-corpus.c - INCLUDE[punycode-test]=../include + SOURCE[punycode-test]=punycode.c $FUZZTESTSRC + INCLUDE[punycode-test]=../include ../test/mfail DEPEND[punycode-test]=../libcrypto.a - SOURCE[smime-test]=smime.c test-corpus.c - INCLUDE[smime-test]=../include - DEPEND[smime-test]=../libcrypto ../libssl + SOURCE[smime-test]=smime.c $FUZZTESTSRC + INCLUDE[smime-test]=../include ../test/mfail + DEPEND[smime-test]=../libcrypto.a ../libssl.a - SOURCE[v3name-test]=v3name.c test-corpus.c - INCLUDE[v3name-test]=../include + SOURCE[v3name-test]=v3name.c $FUZZTESTSRC + INCLUDE[v3name-test]=../include ../test/mfail DEPEND[v3name-test]=../libcrypto.a - SOURCE[quic-client-test]=quic-client.c test-corpus.c fuzz_rand.c - INCLUDE[quic-client-test]=../include + SOURCE[quic-client-test]=quic-client.c $FUZZTESTSRC fuzz_rand.c + INCLUDE[quic-client-test]=../include ../test/mfail DEPEND[quic-client-test]=../libcrypto.a ../libssl.a - SOURCE[quic-server-test]=quic-server.c test-corpus.c fuzz_rand.c - INCLUDE[quic-server-test]=../include + SOURCE[quic-server-test]=quic-server.c $FUZZTESTSRC fuzz_rand.c + INCLUDE[quic-server-test]=../include ../test/mfail DEPEND[quic-server-test]=../libcrypto.a ../libssl.a - SOURCE[quic-srtm-test]=quic-srtm.c test-corpus.c fuzz_rand.c - INCLUDE[quic-srtm-test]=../include + SOURCE[quic-srtm-test]=quic-srtm.c $FUZZTESTSRC fuzz_rand.c + INCLUDE[quic-srtm-test]=../include ../test/mfail DEPEND[quic-srtm-test]=../libcrypto.a ../libssl.a - SOURCE[quic-lcidm-test]=quic-lcidm.c test-corpus.c fuzz_rand.c - INCLUDE[quic-lcidm-test]=../include + SOURCE[quic-lcidm-test]=quic-lcidm.c $FUZZTESTSRC fuzz_rand.c + INCLUDE[quic-lcidm-test]=../include ../test/mfail DEPEND[quic-lcidm-test]=../libcrypto.a ../libssl.a - SOURCE[quic-rcidm-test]=quic-rcidm.c test-corpus.c fuzz_rand.c - INCLUDE[quic-rcidm-test]=../include + SOURCE[quic-rcidm-test]=quic-rcidm.c $FUZZTESTSRC fuzz_rand.c + INCLUDE[quic-rcidm-test]=../include ../test/mfail DEPEND[quic-rcidm-test]=../libcrypto.a ../libssl.a - SOURCE[server-test]=server.c test-corpus.c fuzz_rand.c - INCLUDE[server-test]=../include - DEPEND[server-test]=../libcrypto ../libssl + SOURCE[server-test]=server.c $FUZZTESTSRC fuzz_rand.c + INCLUDE[server-test]=../include ../test/mfail + DEPEND[server-test]=../libcrypto.a ../libssl.a - SOURCE[x509-test]=x509.c test-corpus.c fuzz_rand.c - INCLUDE[x509-test]=../include - DEPEND[x509-test]=../libcrypto + SOURCE[x509-test]=x509.c $FUZZTESTSRC fuzz_rand.c + INCLUDE[x509-test]=../include ../test/mfail + DEPEND[x509-test]=../libcrypto.a - SOURCE[provider-test]=provider.c test-corpus.c - INCLUDE[provider-test]=../include - DEPEND[provider-test]=../libcrypto + SOURCE[provider-test]=provider.c $FUZZTESTSRC + INCLUDE[provider-test]=../include ../test/mfail + DEPEND[provider-test]=../libcrypto.a ENDIF diff --git a/fuzz/test-corpus.c b/fuzz/test-corpus.c index 783df1a076..a908f69644 100644 --- a/fuzz/test-corpus.c +++ b/fuzz/test-corpus.c @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2016-2026 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,10 +17,13 @@ #include #include #include +#include #include #include +#include #include "fuzzer.h" #include "internal/o_dir.h" +#include "mfail.h" #if defined(_WIN32) && defined(_MAX_PATH) && !defined(PATH_MAX) #define PATH_MAX _MAX_PATH @@ -34,7 +37,45 @@ #define S_ISREG(m) ((m) & S_IFREG) #endif -static void testfile(const char *pathname) +static double secs_since(clock_t start) +{ + return (double)(clock() - start) / CLOCKS_PER_SEC; +} + +static void run_baseline(const unsigned char *buf, size_t s) +{ + FuzzerTestOneInput(buf, s); +} + +static void run_mfail(const unsigned char *buf, size_t s, + const char *path, int file_idx) +{ + mfail_init(file_idx, MFAIL_FLAG_COUNT); + while (mfail_has_next()) { + if (mfail_get_phase() == MFAIL_PHASE_COUNTING) + fprintf(stderr, + "# MFAIL_BEGIN file_idx=%d phase=count\n", file_idx); + else + fprintf(stderr, + "# MFAIL_BEGIN file_idx=%d point=%d/%d\n", + file_idx, mfail_get_point(), mfail_get_total()); + + mfail_start(); + FuzzerTestOneInput(buf, s); + mfail_end(); + + if (mfail_get_phase() == MFAIL_PHASE_COUNTING) { + fprintf(stderr, "# %s: %d allocations\n", path, mfail_get_count()); + } else { + fprintf(stderr, "# %s: point %d/%d %s\n", path, + mfail_get_point(), mfail_get_total(), + mfail_was_triggered() ? "hit" : "unreached"); + } + ERR_clear_error(); + } +} + +static void testfile(const char *pathname, int file_idx) { struct stat st; FILE *f; @@ -43,26 +84,52 @@ static void testfile(const char *pathname) if (stat(pathname, &st) < 0 || !S_ISREG(st.st_mode)) return; - printf("# %s\n", pathname); - fflush(stdout); + + fprintf(stderr, "# CORPUS_FILE file_idx=%d size=%lld path=%s\n", + file_idx, (long long)st.st_size, pathname); f = fopen(pathname, "rb"); if (f == NULL) return; buf = malloc(st.st_size); - if (buf != NULL) { - s = fread(buf, 1, st.st_size, f); - OPENSSL_assert(s == (size_t)st.st_size); - FuzzerTestOneInput(buf, s); - free(buf); + if (buf == NULL) { + fclose(f); + return; } + s = fread(buf, 1, st.st_size, f); + OPENSSL_assert(s == (size_t)st.st_size); + + if (mfail_is_installed()) + run_mfail(buf, s, pathname, file_idx); + else + run_baseline(buf, s); + + free(buf); fclose(f); } int main(int argc, char **argv) { - int n; + int n, mfi_rc; + int file_idx = 0; + clock_t corpus_start; - FuzzerInitialize(&argc, &argv); + mfi_rc = mfail_install(1); + if (mfi_rc < 0) { + fprintf(stderr, "mfail: failed to install allocator hooks\n"); + return 1; + } else if (mfi_rc > 0) { + /* Disable buffering for better crash analysis */ + setvbuf(stdout, NULL, _IOLBF, 0); + setvbuf(stderr, NULL, _IOLBF, 0); + } + + if (FuzzerInitialize(&argc, &argv) < 0) { + if (mfail_is_installed()) + return 0; /* init failure under mfail is expected */ + return 1; + } + + corpus_start = clock(); for (n = 1; n < argc; ++n) { size_t dirname_len = strlen(argv[n]); @@ -88,18 +155,20 @@ int main(int argc, char **argv) pathname[dirname_len] = '\0'; } strcpy(pathname + dirname_len, filename); - testfile(pathname); + testfile(pathname, file_idx++); } OPENSSL_DIR_end(&ctx); /* If it wasn't a directory, treat it as a file instead */ if (!wasdir) - testfile(argv[n]); + testfile(argv[n], file_idx++); free(pathname); } - FuzzerCleanup(); + if (!mfail_is_installed() || mfail_is_count_only()) + fprintf(stderr, "# corpus_time: %.6f\n", secs_since(corpus_start)); + FuzzerCleanup(); return 0; } diff --git a/test/recipes/fuzz.pl b/test/recipes/fuzz.pl index 3f03eef4f7..e5182a362c 100644 --- a/test/recipes/fuzz.pl +++ b/test/recipes/fuzz.pl @@ -1,4 +1,4 @@ -# Copyright 2016-2020 The OpenSSL Project Authors. All Rights Reserved. +# Copyright 2016-2026 The OpenSSL Project Authors. All Rights Reserved. # # Licensed under the Apache License 2.0 (the "License"). You may not use # this file except in compliance with the License. You can obtain a copy @@ -8,18 +8,295 @@ use strict; use warnings; +use Cwd qw/abs_path/; + use OpenSSL::Glob; -use OpenSSL::Test qw/:DEFAULT srctop_dir/; +use OpenSSL::Test qw/:DEFAULT srctop_dir bldtop_file result_file/; + +# print logs +sub fuzz_dump_log { + my ($log) = @_; + return unless $ENV{HARNESS_VERBOSE} && open(my $fh, '<', $log); + print STDERR <$fh>; + close $fh; +} + +# execute test and print backtrace +sub fuzz_print_backtrace { + my ($f, $path, $point) = @_; + my $bt_log = result_file("$f-backtrace.stderr.log"); + local $ENV{OPENSSL_TEST_MFAIL_BACKTRACE} = 1; + local $ENV{OPENSSL_TEST_MFAIL_POINT} = $point if defined $point; + run(fuzz(["$f-test", $path], stderr => $bt_log)); + + diag("- backtrace at injection point:"); + if (open(my $fh, '<', $bt_log)) { + while (my $line = <$fh>) { + chomp $line; + diag(" $line"); + } + close $fh; + } +} + +# check fuzz test logs +sub fuzz_check_log { + my ($f, $log, $exit_ok, $silent_leak) = @_; + + my $has_leaks = 0; + my $corpus_time = 0; + my @tail; + my ($last_path, $last_point); + + # collect info from the executed fuzz log + if (open(my $fh, '<', $log)) { + while (my $line = <$fh>) { + chomp $line; + if ($line =~ /^#\s/) { + $corpus_time = $1 + if $line =~ /^#\s*corpus_time:\s*([\d.]+)/; + $last_path = $1 if $line =~ /\bpath=(.+?)\s*$/; + $last_point = $1 if $line =~ /\bpoint=(\d+)\//; + @tail = ($line); + } else { + push @tail, $line; + $has_leaks = 1 + if $line =~ /^(?:Direct|Indirect) leak of \d+ byte/; + } + } + close $fh; + } + + # return if the test passed and there is no leak + return (1, 0, $corpus_time) if $exit_ok && !$has_leaks; + # return if there is a leak but it's silent (should not be reported) + return (0, $has_leaks, $corpus_time) if $has_leaks && $silent_leak; + + # log leak info + my $why = !$exit_ok ? "non-zero exit" : "leaks (clean exit)"; + diag("fuzz $f failed: $why"); + diag("- full stderr: $log"); + if (defined $last_path) { + my $bin = abs_path(bldtop_file('fuzz', "$f-test")); + my $path = abs_path($last_path); + my $env = defined $last_point + ? "OPENSSL_TEST_MFAIL_POINT=$last_point " : ""; + diag("- reproduce: $env$bin $path"); + } + diag($_) for @tail; + fuzz_print_backtrace($f, $last_path, $last_point) + if defined $last_path && !$has_leaks; + return (0, $has_leaks, $corpus_time); +} + +# run fuzz test and dump logs +sub fuzz_run { + my ($f, $d, $log_name, $silent_leak) = @_; + my $log = result_file($log_name); + my $exit_ok = run(fuzz(["$f-test", $d], stderr => $log)); + fuzz_dump_log($log); + my ($passed, $leaks, $corpus_time) = fuzz_check_log($f, $log, $exit_ok, + $silent_leak); + return ($passed, $leaks, $corpus_time, $log); +} + +# run fuzz test in counting mode and no fail run +sub fuzz_run_count_only { + my ($f, $d) = @_; + my $log = result_file("$f-count.stderr.log"); + local $ENV{OPENSSL_TEST_MFAIL_COUNT_ONLY} = 1; + my $exit_ok = run(fuzz(["$f-test", $d], stderr => $log)); + fuzz_dump_log($log); + + my ($corpus_time, $cur, @allocs) = (0, undef); + if (open(my $fh, '<', $log)) { + while (my $line = <$fh>) { + $corpus_time = $1 if $line =~ /^#\s*corpus_time:\s*([\d.]+)/; + $cur = $1 if $line =~ /^#\s*CORPUS_FILE\s+file_idx=(\d+)/; + push @allocs, $1 + 0 if defined $cur + && $line =~ /:\s*(\d+)\s+allocations\s*$/; + } + close $fh; + } + return ($exit_ok, $corpus_time, \@allocs); +} + +# find path and point of the reported leak for easy recreation +sub fuzz_mfail_bisect { + my ($f, $log) = @_; + + # collect all paths and executed points from the output + my (%path, %points); + if (open(my $fh, '<', $log)) { + while (my $line = <$fh>) { + $path{$1} = $2 + if $line =~ + /CORPUS_FILE\s+file_idx=(\d+)\s+size=\d+\s+path=(\S+)/; + push @{$points{$1}}, $2 + if $line =~ /MFAIL_BEGIN\s+file_idx=(\d+)\s+point=(\d+)\/\d+/; + } + close $fh; + } + + # reset current envs so they don't get used in bisect run + delete local $ENV{OPENSSL_TEST_MFAIL_COUNT}; + delete local $ENV{OPENSSL_TEST_MFAIL_START}; + delete local $ENV{OPENSSL_TEST_MFAIL_POINT}; + + diag("bisecting mfail leak across isolated point reruns"); + + # go through all executed corpus files + for my $idx (sort { $a <=> $b } keys %path) { + # go trhout all executed points in path + for my $p (@{$points{$idx} || []}) { + local $ENV{OPENSSL_TEST_MFAIL_POINT} = $p; + my $plog = result_file("$f-bisect-$idx-$p.stderr.log"); + my $exit_ok = run(fuzz(["$f-test", $path{$idx}], stderr => $plog), + quiet => 1); + # silently skip runs without a leak + my (undef, $leaks) = fuzz_check_log($f, $plog, $exit_ok, 1); + next unless $leaks; + # report exact leak location + my $bin = abs_path(bldtop_file('fuzz', "$f-test")); + my $abs = abs_path($path{$idx}); + diag("isolated leak: file_idx=$idx point=$p path=$path{$idx}"); + diag("- log: $plog"); + diag("- reproduce: OPENSSL_TEST_MFAIL_POINT=$p $bin $abs"); + fuzz_print_backtrace($f, $path{$idx}, $p); + return; + } + } + diag("bisection did not reproduce the leak"); +} + +# get all test_fuzz tests calling this +sub fuzz_test_names { + my @names; + for my $p (glob(srctop_dir('test', 'recipes') . '/[0-9][0-9]-test_fuzz_*.t')) { + # push the actual name of the test used in TESTS filtering + push @names, $1 if $p =~ m{/\d+-(test_fuzz_\S+)\.t$}; + } + return @names; +} + +# match a test name against TESTS env filter (test/run_tests.pl semantics) +sub fuzz_match_tests_filter { + my ($name, $filter) = @_; + return 1 unless defined $filter && $filter ne ''; + + my @pats = grep { length } split /\s+/, $filter; + return 1 unless @pats; + + # a leading negative implies a starting "alltests" + my $included = $pats[0] =~ /^-/ ? 1 : 0; + + for my $pat (@pats) { + # alltests resets the set to all, ignoring everything before + if ($pat eq 'alltests') { + $included = 1; + next; + } + + my $neg = $pat =~ s/^-//; + + # glob -> regex + (my $re = quotemeta $pat) =~ s/\\\*/.*/g; + $re =~ s/\\\?/./g; + next unless $name =~ /\A$re\z/; + + $included = $neg ? 0 : 1; + } + + return $included; +} + +# get budget per test +sub fuzz_per_test_budget { + my $budget = $ENV{OSSL_FUZZ_TEST_BUDGET} or return 0; + my $jobs = $ENV{OSSL_FUZZ_TEST_JOBS} || 1; + my $filter = $ENV{TESTS}; + + my @active = grep { fuzz_match_tests_filter($_, $filter) } fuzz_test_names(); + my $count = scalar(@active) || 1; + + # we don't need all jobs if there are less tests + $jobs = $count if $jobs > $count; + + my $per_test = $budget * $jobs / $count; + diag(sprintf("budget=%ss jobs=%d active=%d -> per-test=%.3fs", + $budget, $jobs, $count, $per_test)); + return $per_test; +} sub fuzz_ok { - die "Only one argument accepted" if scalar @_ != 1; - - my $f = $_[0]; + my ($f, %opts) = @_; my $d = srctop_dir('fuzz', 'corpora', $f); SKIP: { skip "No directory $d", 1 unless -d $d; - ok(run(fuzz(["$f-test", $d])), "Fuzzing $f"); + + my $per_test = fuzz_per_test_budget(); + my $safety = 0.8; + my $target = $per_test * $safety; + + # no budget configured, just run the corpus + unless ($per_test > 0) { + ok(run(fuzz(["$f-test", $d])), "Fuzzing $f"); + return; + } + + # baseline run to measure the corpus run time + my ($ok, $corpus_time, $allocs) = fuzz_run_count_only($f, $d); + unless ($ok) { + ok(0, "Fuzzing $f (count-only)"); + return; + } + + # get the maximum allocations in instance and count total + my $total_allocs = 0; + my $max_k = 0; + for (@$allocs) { + $total_allocs += $_; + $max_k = $_ if $_ > $max_k; + } + my $num_files = scalar @$allocs; + diag(sprintf("%s: count-only %.3fs, allocs=%d, files=%d, max=%d", + $f, $corpus_time, $total_allocs, $num_files, $max_k)); + + # baseline alone consumed the budget, nothing left for mfail + if ($corpus_time <= 0 || $corpus_time >= $target) { + ok(1, "Fuzzing $f (no mfail budget; " + . "corpus=${corpus_time}s, target=${target}s)"); + return; + } + + # no allocations counted, can't size the mfail run + if ($total_allocs <= 0 || $num_files <= 0) { + ok(1, "Fuzzing $f (no allocations counted)"); + return; + } + + # number of mfail iterations that fit alongside the baseline: + # ~corpus_time * (1 + count / 2) <= target + my $count = int(2 * ($target - $corpus_time) / $corpus_time); + # never exceed max(K_i); injections beyond that are wasted + $count = $max_k if $count > $max_k; + if ($count <= 0) { + ok(1, "Fuzzing $f (budget too small for mfail)"); + return; + } + diag("$f: running mfail with count=$count"); + + local $ENV{OPENSSL_TEST_MFAIL_COUNT} = $count; + my $main_log = "$f-mfail.stderr.log"; + my ($passed, $leaks, undef, $log) = fuzz_run($f, $d, $main_log, 1); + + unless ($passed) { + fuzz_mfail_bisect($f, $log) if $leaks; + ok(0, "Fuzzing $f (mfail count=$count, per-test=${per_test}s)"); + return; + } + ok(1, "Fuzzing $f (mfail count=$count, per-test=${per_test}s)"); } } diff --git a/util/perl/OpenSSL/Test.pm b/util/perl/OpenSSL/Test.pm index 369d4f2b9a..0cdb41259c 100644 --- a/util/perl/OpenSSL/Test.pm +++ b/util/perl/OpenSSL/Test.pm @@ -448,6 +448,14 @@ If used, B must be a reference to a scalar variable. It will be assigned a boolean indicating if the command succeeded or not. This is particularly useful together with B. +=item B 0|1> + +If true, suppress the diagnostic line that C normally prints to STDERR +after the command completes (the C exitcode> line) when running +under a non-verbose test harness. Has no effect outside a harness or under +C. Useful for loops that invoke many commands where that +line would be noise. + =back Usually 1 indicates that the command was successful and 0 indicates failure. @@ -522,19 +530,20 @@ sub run { ${$opts{statusvar}} = $r; } - # Restore STDOUT / STDERR on VMS + my $harness_quiet = $ENV{HARNESS_ACTIVE} && !$ENV{HARNESS_VERBOSE}; if ($^O eq 'VMS') { - if ($ENV{HARNESS_ACTIVE} && !$ENV{HARNESS_VERBOSE}) { + # Restore STDOUT / STDERR on VMS + if ($harness_quiet) { close STDOUT; close STDERR; open STDOUT, '>&', $save_STDOUT or die "Can't restore STDOUT: $!"; open STDERR, '>&', $save_STDERR or die "Can't restore STDERR: $!"; } - print STDERR "$prefix$display_cmd => $e\n" - if !$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE}; + print STDERR "$prefix$display_cmd => $e\n" unless $harness_quiet; } else { - print STDERR "$prefix$display_cmd => $e\n"; + print STDERR "$prefix$display_cmd => $e\n" + unless $opts{quiet} && $harness_quiet; } # At this point, $? stops being interesting, and unfortunately, From e211f88c646aaf5dd57cbe13bc0613c605c33894 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Wed, 8 Jul 2026 13:29:24 +0200 Subject: [PATCH 265/349] apps: test genpkey app cipher option Add coverage for encrypting the generated private key with a cipher, checking it can only be read back with the correct passphrase, and that a cipher is rejected together with the -genparam option. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz MergeDate: Wed Jul 15 16:03:37 2026 (Merged from https://github.com/openssl/openssl/pull/31893) --- test/recipes/15-test_genpkey.t | 41 ++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/test/recipes/15-test_genpkey.t b/test/recipes/15-test_genpkey.t index b918f73f9f..ddef803ff1 100644 --- a/test/recipes/15-test_genpkey.t +++ b/test/recipes/15-test_genpkey.t @@ -9,7 +9,7 @@ use strict; use warnings; -use OpenSSL::Test qw/:DEFAULT/; +use OpenSSL::Test qw/:DEFAULT with/; use OpenSSL::Test::Utils; setup("test_genpkey"); @@ -22,7 +22,7 @@ push @algs, qw(EC) unless disabled("ec"); push @algs, qw(X25519 X448) unless disabled("ecx"); push @algs, qw(SM2) unless disabled("sm2"); -plan tests => scalar(@algs); +plan tests => scalar(@algs) + 2; foreach (@algs) { my $alg = $_; @@ -30,3 +30,40 @@ foreach (@algs) { ok(run(app([ 'openssl', 'genpkey', '-algorithm', $alg, '-help'])), "show genpkey pkeyopt values for $alg"); } + +SKIP: { + skip "RSA is not supported by this OpenSSL build", 1 if disabled("rsa"); + + subtest "genpkey with a cipher encrypts the private key" => sub { + plan tests => 3; + + my $key = "genpkey_enc.pem"; + + ok(run(app(['openssl', 'genpkey', '-algorithm', 'RSA', + '-pkeyopt', 'rsa_keygen_bits:512', + '-aes256', '-pass', 'pass:secret', '-out', $key])), + "Generate an AES-256 encrypted RSA key"); + ok(run(app(['openssl', 'pkey', '-in', $key, + '-passin', 'pass:secret', '-noout'])), + "Read the encrypted key back with the correct passphrase"); + # A wrong passphrase must not decrypt the key. + with({ exit_checker => sub { return shift == 1; } }, + sub { + ok(run(app(['openssl', 'pkey', '-in', $key, + '-passin', 'pass:wrong', '-noout'])), + "Reading with a wrong passphrase fails"); + }); + }; +} + +SKIP: { + skip "DSA is not supported by this OpenSSL build", 1 if disabled("dsa"); + + # A cipher only encrypts a private key, so it is rejected with -genparam. + with({ exit_checker => sub { return shift == 1; } }, + sub { + ok(run(app(['openssl', 'genpkey', '-genparam', '-algorithm', 'DSA', + '-pkeyopt', 'dsa_paramgen_bits:512', '-aes256'])), + "Cannot use a cipher with -genparam"); + }); +} From 385f12644cd369d1c247f4787097f8bdb4b13fd0 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 9 Jul 2026 12:53:29 +0200 Subject: [PATCH 266/349] apps: cover the crl -gendelta, -key and -keyform options The -gendelta, -key and -keyform options of the crl app were previously untested. It adds a subtest that generates two CRLs with an incrementing crlNumber and then uses -gendelta with -key to produce a delta CRL, checking the result carries a Delta CRL Indicator. It also loads the signing key from DER via -keyform DER, and checks that a mismatching -keyform and a missing -key both make -gendelta fail. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Dmitry Belyavskiy Reviewed-by: Tomas Mraz MergeDate: Wed Jul 15 16:05:31 2026 (Merged from https://github.com/openssl/openssl/pull/31910) --- test/recipes/25-test_crl.t | 69 +++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/test/recipes/25-test_crl.t b/test/recipes/25-test_crl.t index 6addde4b0b..a65b2c6ce8 100644 --- a/test/recipes/25-test_crl.t +++ b/test/recipes/25-test_crl.t @@ -16,7 +16,7 @@ use OpenSSL::Test qw/:DEFAULT srctop_file/; setup("test_crl"); -plan tests => 11; +plan tests => 12; require_ok(srctop_file('test','recipes','tconversion.pl')); @@ -85,6 +85,73 @@ subtest 'crl signature verification' => sub { } }; +# Cover -gendelta (delta CRL generation), which is the only code path in the +# crl app using the -key and -keyform options. +subtest 'crl delta generation with -gendelta, -key and -keyform' => sub { + plan tests => 5; + + # copy the CA cert and key in locally so the config uses plain paths + my $cacert = "gendelta-ca-cert.pem"; + my $cakey = "gendelta-ca-key.pem"; + copy(srctop_file("test", "certs", "ca-cert.pem"), $cacert); + copy(srctop_file("test", "certs", "ca-key.pem"), $cakey); + + open my $cfg, '>', "gencrl.cnf" or die "Could not create gencrl.cnf: $!"; + print $cfg <<"EOF"; +[ca] +default_ca = CA_default +[CA_default] +database = index.txt +certificate = $cacert +private_key = $cakey +crlnumber = crlnumber +default_md = sha256 +default_crl_days = 30 +EOF + close $cfg; + open my $db, '>', "index.txt" or die "Could not create index.txt: $!"; + close $db; # empty CA database + open my $num, '>', "crlnumber" or die "Could not create crlnumber: $!"; + print $num "1000\n"; + close $num; + + run(app(["openssl", "ca", "-gencrl", "-config", "gencrl.cnf", + "-out", "delta-base.crl"])); + run(app(["openssl", "ca", "-gencrl", "-config", "gencrl.cnf", + "-out", "delta-newer.crl"])); + + # -gendelta with a PEM signing key (the default -keyform) + ok(run(app(["openssl", "crl", "-in", "delta-base.crl", + "-gendelta", "delta-newer.crl", + "-key", $cakey, "-out", "delta.crl"])), + "generate delta CRL with -gendelta and a PEM -key"); + + run(app(["openssl", "crl", "-in", "delta.crl", "-noout", "-text", + "-out", "delta.txt"])); + test_file_contains("delta CRL", "delta.txt", "Delta CRL Indicator", 1); + + # The same, loading the signing key from DER via -keyform DER + run(app(["openssl", "pkey", "-in", $cakey, "-outform", "DER", + "-out", "ca-key.der"])); + ok(run(app(["openssl", "crl", "-in", "delta-base.crl", + "-gendelta", "delta-newer.crl", + "-key", "ca-key.der", "-keyform", "DER", + "-out", "delta-der.crl"])), + "generate delta CRL with a DER -key and -keyform DER"); + + # -keyform must match the key encoding: a DER key read as PEM fails. + ok(!run(app(["openssl", "crl", "-in", "delta-base.crl", + "-gendelta", "delta-newer.crl", + "-key", "ca-key.der", "-keyform", "PEM", + "-out", "delta-bad.crl"])), + "wrong -keyform for the signing key makes -gendelta fail"); + + # -gendelta requires a signing key. + ok(!run(app(["openssl", "crl", "-in", "delta-base.crl", + "-gendelta", "delta-newer.crl", "-out", "delta-nokey.crl"])), + "-gendelta without -key fails"); +}; + sub compare1stline { my ($cmdarray, $str) = @_; my @lines = run(app($cmdarray), capture => 1); From 96de7d164b7b7041bfcbfca82585673ea0df8896 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 13 Jul 2026 20:50:32 +0200 Subject: [PATCH 267/349] rand: pre-fetch JITTER seed when jitter used This fixes failing rand mfail test when jitter used Reviewed-by: Richard Levitte Reviewed-by: Tomas Mraz MergeDate: Wed Jul 15 16:18:29 2026 (Merged from https://github.com/openssl/openssl/pull/31938) --- test/rand_test.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/test/rand_test.c b/test/rand_test.c index e5c889a9e9..be3313fc6e 100644 --- a/test/rand_test.c +++ b/test/rand_test.c @@ -15,6 +15,13 @@ #include "crypto/rand.h" #include "testutil.h" +/* For builds where OPENSSL_DEFAULT_SEED_SRC is explicitly set. */ +/* clang-format off */ +#ifndef OPENSSL_DEFAULT_SEED_SRC +#define OPENSSL_DEFAULT_SEED_SRC SEED-SRC +#endif +/* clang-format on */ + static char *configfile; static int test_rand(void) @@ -312,7 +319,11 @@ static int test_rand_bytes_mfail(int idx) goto end; /* The default seed source may be unavailable in some configurations */ ERR_set_mark(); - seed = EVP_RAND_fetch(ctx, "SEED-SRC", NULL); +#if !defined(OPENSSL_NO_FIPS_JITTER) + seed = EVP_RAND_fetch(ctx, "JITTER", NULL); +#else + seed = EVP_RAND_fetch(ctx, OPENSSL_MSTR(OPENSSL_DEFAULT_SEED_SRC), NULL); +#endif ERR_pop_to_mark(); MFAIL_start(); From cb9a9021752b0c3499fccbb965e1e525b357a0f0 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Tue, 14 Jul 2026 13:00:59 -0400 Subject: [PATCH 268/349] Fix dsaparams decoding from DER files The tests addded in commit d8a7e8e uncovered an odd error case. https://github.com/openssl/openssl/actions/runs/29303845665/job/86993085732 Is failing when attempting to read in a der file converting from a corresponding PEM file containing DSA parameters. Interestingly The problem was only occuring when: 1) The input was a DER file and 2) Blake2 was not configured Doing some tracing of the decoder operation showed that this is occuring because the OSSL_STORE lookup used to find the proper decoder uses a "first successful decode wins" approach, after which the loading code checks to see if the decoded type matches the expected key type. When decoding PEM, this isn't a problem, as the PEM armoring gives the decoder a hint as to why type of data the input file is. But with DER, there is no such hint, and we're at the mercy of whichever decoder happens to decode the data correctly first. Normally it works just fine, but when features are disabled or enabled, the order in which the decoders are attempted may change, affecting the outcome. In this particular case, disabling blake2 caused the DHX decoder to be attempted first, which decodes the input der file without issue. That in turn caused the subsequent EVP_PKEY_is_a check to fail (as we were expecting a DSA key), and so the test fails. Fortunately, the code that the dsaparam applet uses to do this decode provides a keytype hint, which we can use to guide the decode process. keep the old store lookup method around in case anyone doesn't pass a uri that is a file path or provide a keytype, but if we do both those things, we can use OSSL_DECODER_CTX_new_for_pkey to specifically tell the decoder that we want to decode the input data as the expected type (in this case a DSA key). Fixes #31944 Reviewed-by: Milan Broz Reviewed-by: Tomas Mraz Reviewed-by: Tim Hudson MergeDate: Thu Jul 16 15:28:32 2026 (Merged from https://github.com/openssl/openssl/pull/31954) --- apps/lib/apps.c | 47 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/apps/lib/apps.c b/apps/lib/apps.c index 128c40d086..36fcb20e2a 100644 --- a/apps/lib/apps.c +++ b/apps/lib/apps.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "s_apps.h" #include "apps.h" @@ -605,20 +606,46 @@ EVP_PKEY *load_keyparams_suppress(const char *uri, int format, int maybe_stdin, int suppress_decode_errors) { EVP_PKEY *params = NULL; + OSSL_DECODER_CTX *dctx = NULL; + BIO *file_bio = BIO_new_file(uri, "rb"); + OSSL_LIB_CTX *libctx = app_get0_libctx(); + const char *propq = app_get0_propq(); if (desc == NULL) desc = "key parameters"; - (void)load_key_certs_crls(uri, format, maybe_stdin, NULL, desc, - suppress_decode_errors, - NULL, NULL, ¶ms, NULL, NULL, NULL, NULL, NULL); - if (params != NULL && keytype != NULL && !EVP_PKEY_is_a(params, keytype)) { - ERR_print_errors(bio_err); - BIO_printf(bio_err, - "Unable to load %s from %s (unexpected parameters type)\n", - desc, uri); - EVP_PKEY_free(params); - params = NULL; + /* + * Use the store lookup path for anything that is not DER/ASN1 format + * Or if we are unable to opens the uri as a file. + */ + if (format != FORMAT_ASN1 || file_bio == NULL) { + (void)load_key_certs_crls(uri, format, maybe_stdin, NULL, desc, + suppress_decode_errors, + NULL, NULL, ¶ms, NULL, NULL, NULL, NULL, NULL); + if (params != NULL && keytype != NULL && !EVP_PKEY_is_a(params, keytype)) { + ERR_print_errors(bio_err); + BIO_printf(bio_err, + "Unable to load %s from %s (unexpected parameters type)\n", + desc, uri); + EVP_PKEY_free(params); + params = NULL; + } + } else { + dctx = OSSL_DECODER_CTX_new_for_pkey(¶ms, NULL, NULL, keytype, + OSSL_KEYMGMT_SELECT_ALL_PARAMETERS, + libctx, propq); + if (dctx == NULL) { + ERR_print_errors(bio_err); + BIO_printf(bio_err, "Unable to allocate decoder context\n"); + } else { + if (!OSSL_DECODER_from_bio(dctx, file_bio)) { + ERR_print_errors(bio_err); + BIO_printf(bio_err, "Unable to decode file %s\n", uri); + } + } } + + BIO_free(file_bio); + OSSL_DECODER_CTX_free(dctx); return params; } From e8a742176a23b8c9b827bdb613d6e1c74130bebb Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 16 Jul 2026 10:53:53 +0200 Subject: [PATCH 269/349] rand: fix jitter seed macro logic The seeding macro logic was mixed up and incompletely applied. The macro logic was also cleaned up. Co-authored-by: Paul Dale Reviewed-by: Tim Hudson Reviewed-by: Paul Dale Reviewed-by: Nikola Pajkovsky Reviewed-by: Tomas Mraz MergeDate: Fri Jul 17 04:47:07 2026 (Merged from https://github.com/openssl/openssl/pull/31970) --- crypto/rand/rand_lib.c | 10 ++-------- include/crypto/rand.h | 10 ++++++++++ test/rand_test.c | 15 ++------------- 3 files changed, 14 insertions(+), 21 deletions(-) diff --git a/crypto/rand/rand_lib.c b/crypto/rand/rand_lib.c index c8d77c68e9..63a3d1bca8 100644 --- a/crypto/rand/rand_lib.c +++ b/crypto/rand/rand_lib.c @@ -25,12 +25,6 @@ #include "internal/provider.h" #include "internal/common.h" -/* clang-format off */ -#ifndef OPENSSL_DEFAULT_SEED_SRC -#define OPENSSL_DEFAULT_SEED_SRC SEED-SRC -#endif -/* clang-format on */ - typedef struct rand_global_st { /* * The three shared DRBG instances @@ -545,10 +539,10 @@ static EVP_RAND_CTX *rand_new_seed(OSSL_LIB_CTX *libctx) name = dgbl->seed_name; } else { fallback = 1; - name = OPENSSL_MSTR(OPENSSL_DEFAULT_SEED_SRC); + name = OPENSSL_SEED_SRC_NAME; } #else /* !OPENSSL_NO_FIPS_JITTER */ - name = "JITTER"; + name = OPENSSL_SEED_SRC_NAME; propq = ""; #endif /* OPENSSL_NO_FIPS_JITTER */ diff --git a/include/crypto/rand.h b/include/crypto/rand.h index 4845c84a94..357ce885b7 100644 --- a/include/crypto/rand.h +++ b/include/crypto/rand.h @@ -39,6 +39,16 @@ /* * Defines related to seed sources */ + +/* Name of the seed source used to seed the primary DRBG. */ +#ifndef OPENSSL_NO_FIPS_JITTER +#define OPENSSL_SEED_SRC_NAME "JITTER" +#elif defined(OPENSSL_DEFAULT_SEED_SRC) +#define OPENSSL_SEED_SRC_NAME OPENSSL_MSTR(OPENSSL_DEFAULT_SEED_SRC) +#else +#define OPENSSL_SEED_SRC_NAME "SEED-SRC" +#endif + #ifndef DEVRANDOM /* * set this to a comma-separated list of 'random' device files to try out. By diff --git a/test/rand_test.c b/test/rand_test.c index be3313fc6e..5b2270cb4e 100644 --- a/test/rand_test.c +++ b/test/rand_test.c @@ -15,13 +15,6 @@ #include "crypto/rand.h" #include "testutil.h" -/* For builds where OPENSSL_DEFAULT_SEED_SRC is explicitly set. */ -/* clang-format off */ -#ifndef OPENSSL_DEFAULT_SEED_SRC -#define OPENSSL_DEFAULT_SEED_SRC SEED-SRC -#endif -/* clang-format on */ - static char *configfile; static int test_rand(void) @@ -319,11 +312,7 @@ static int test_rand_bytes_mfail(int idx) goto end; /* The default seed source may be unavailable in some configurations */ ERR_set_mark(); -#if !defined(OPENSSL_NO_FIPS_JITTER) - seed = EVP_RAND_fetch(ctx, "JITTER", NULL); -#else - seed = EVP_RAND_fetch(ctx, OPENSSL_MSTR(OPENSSL_DEFAULT_SEED_SRC), NULL); -#endif + seed = EVP_RAND_fetch(ctx, OPENSSL_SEED_SRC_NAME, NULL); ERR_pop_to_mark(); MFAIL_start(); @@ -349,7 +338,7 @@ static int test_rand_seed_src_mfail(void) int rc = -1; if (!TEST_ptr(ctx = OSSL_LIB_CTX_new()) - || !TEST_ptr(rand = EVP_RAND_fetch(ctx, "SEED-SRC", NULL))) + || !TEST_ptr(rand = EVP_RAND_fetch(ctx, OPENSSL_SEED_SRC_NAME, NULL))) goto end; MFAIL_start(); From e1febbd01a416bc722cfda8fe89ce91608443f06 Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Wed, 1 Jul 2026 23:36:44 +0700 Subject: [PATCH 270/349] Port script10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Neil Horman Reviewed-by: Saša Nedvědický MergeDate: Fri Jul 17 08:02:00 2026 (Merged from https://github.com/openssl/openssl/pull/31821) --- test/quic_multistream_test.c | 12 +-------- test/radix/quic_tests.c | 49 +++++++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/test/quic_multistream_test.c b/test/quic_multistream_test.c index 1a70b07cf6..cf7cca98b9 100644 --- a/test/quic_multistream_test.c +++ b/test/quic_multistream_test.c @@ -2069,17 +2069,7 @@ static const struct script_op script_9[] = { /* 10. Shutdown */ static const struct script_op script_10[] = { - OP_C_SET_ALPN("ossltest"), - OP_C_CONNECT_WAIT(), - - OP_C_WRITE(DEFAULT, "apple", 5), - OP_S_BIND_STREAM_ID(a, C_BIDI_ID(0)), - OP_S_READ_EXPECT(a, "apple", 5), - - OP_C_SHUTDOWN_WAIT(NULL, 0), - OP_C_EXPECT_CONN_CLOSE_INFO(0, 1, 0), - OP_S_EXPECT_CONN_CLOSE_INFO(0, 1, 1), - + /* test moved to test/radix/quic_tests.c */ OP_END }; diff --git a/test/radix/quic_tests.c b/test/radix/quic_tests.c index a770f9f018..c732b62393 100644 --- a/test/radix/quic_tests.c +++ b/test/radix/quic_tests.c @@ -900,8 +900,55 @@ DEF_SCRIPT(script_9, "Unidirectional default stream mode (server sends bidi firs OP_READ_EXPECT(Sa, "orange", 6); } -DEF_SCRIPT(script_10, "place holder for multistrem script_10") +/* 10. Shutdown */ +DEF_SCRIPT(script_10, "Shutdown test") { + OP_SIMPLE_PAIR_CONN(); + + OP_WRITE(C, "apple", 5); + OP_ACCEPT_CONN_WAIT(L, S, 0); + OP_READ_EXPECT(S, "apple", 5); + + OP_SHUTDOWN_WAIT(C, 0, 0, NULL); + OP_EXPECT_CONN_CLOSE_INFO(C, 0, 1, 0); + OP_EXPECT_CONN_CLOSE_INFO(S, 0, 1, 1); +} + +/* 11. Many threads accepted on the same client connection */ +DEF_SCRIPT(script_11_child_0, + "child: accept stream from C, read, sleep, expect FIN") +{ + OP_ACCEPT_STREAM_WAIT(C, C0, 0 /* bidirectional */); + OP_READ_EXPECT_B(C0, "foo"); + OP_SLEEP(10); + OP_EXPECT_FIN(C0); +} + +DEF_SCRIPT(script_11_child_1, + "child: accept stream from C, read, sleep, expect FIN") +{ + OP_ACCEPT_STREAM_WAIT(C, C1, 0 /* bidirectional */); + OP_READ_EXPECT_B(C1, "foo"); + OP_SLEEP(10); + OP_EXPECT_FIN(C1); +} + +DEF_SCRIPT(script_11_child_2, + "child: accept stream from C, read, sleep, expect FIN") +{ + OP_ACCEPT_STREAM_WAIT(C, C2, 0 /* bidirectional */); + OP_READ_EXPECT_B(C2, "foo"); + OP_SLEEP(10); + OP_EXPECT_FIN(C2); +} + +DEF_SCRIPT(script_11_child_3, + "child: accept stream from C, read, sleep, expect FIN") +{ + OP_ACCEPT_STREAM_WAIT(C, C3, 0 /* bidirectional */); + OP_READ_EXPECT_B(C3, "foo"); + OP_SLEEP(10); + OP_EXPECT_FIN(C3); } DEF_SCRIPT(script_11, "place holder for multistrem script_11") From c86b202ef4973e0e4d4dcc48d56db0c8b0a834c5 Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Wed, 1 Jul 2026 23:37:20 +0700 Subject: [PATCH 271/349] Port script11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Neil Horman Reviewed-by: Saša Nedvědický MergeDate: Fri Jul 17 08:02:02 2026 (Merged from https://github.com/openssl/openssl/pull/31821) --- test/quic_multistream_test.c | 36 +----------------------------- test/radix/quic_tests.c | 43 +++++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 36 deletions(-) diff --git a/test/quic_multistream_test.c b/test/quic_multistream_test.c index cf7cca98b9..a8dae2966a 100644 --- a/test/quic_multistream_test.c +++ b/test/quic_multistream_test.c @@ -2074,42 +2074,8 @@ static const struct script_op script_10[] = { }; /* 11. Many threads accepted on the same client connection */ -static const struct script_op script_11_child[] = { - OP_C_ACCEPT_STREAM_WAIT(a), - OP_C_READ_EXPECT(a, "foo", 3), - OP_SLEEP(10), - OP_C_EXPECT_FIN(a), - - OP_END -}; - static const struct script_op script_11[] = { - OP_C_SET_ALPN("ossltest"), - OP_C_CONNECT_WAIT(), - OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE), - - OP_NEW_THREAD(5, script_11_child), - - OP_S_NEW_STREAM_BIDI(a, ANY_ID), - OP_S_WRITE(a, "foo", 3), - OP_S_CONCLUDE(a), - - OP_S_NEW_STREAM_BIDI(b, ANY_ID), - OP_S_WRITE(b, "foo", 3), - OP_S_CONCLUDE(b), - - OP_S_NEW_STREAM_BIDI(c, ANY_ID), - OP_S_WRITE(c, "foo", 3), - OP_S_CONCLUDE(c), - - OP_S_NEW_STREAM_BIDI(d, ANY_ID), - OP_S_WRITE(d, "foo", 3), - OP_S_CONCLUDE(d), - - OP_S_NEW_STREAM_BIDI(e, ANY_ID), - OP_S_WRITE(e, "foo", 3), - OP_S_CONCLUDE(e), - + /* test moved to test/radix/quic_tests.c */ OP_END }; diff --git a/test/radix/quic_tests.c b/test/radix/quic_tests.c index c732b62393..a101cad924 100644 --- a/test/radix/quic_tests.c +++ b/test/radix/quic_tests.c @@ -951,7 +951,48 @@ DEF_SCRIPT(script_11_child_3, OP_EXPECT_FIN(C3); } -DEF_SCRIPT(script_11, "place holder for multistrem script_11") +DEF_SCRIPT(script_11_child_4, + "child: accept stream from C, read, sleep, expect FIN") +{ + OP_ACCEPT_STREAM_WAIT(C, C4, 0 /* bidirectional */); + OP_READ_EXPECT_B(C4, "foo"); + OP_SLEEP(10); + OP_EXPECT_FIN(C4); +} + +DEF_SCRIPT(script_11, "Many threads accepted on the same client connection") +{ + OP_SIMPLE_PAIR_CONN_ND(); + OP_ACCEPT_CONN_WAIT(L, S, 0); + + OP_SPAWN_THREAD(script_11_child_0); + OP_SPAWN_THREAD(script_11_child_1); + OP_SPAWN_THREAD(script_11_child_2); + OP_SPAWN_THREAD(script_11_child_3); + OP_SPAWN_THREAD(script_11_child_4); + + OP_NEW_STREAM(S, Sa, 0 /* bidirectional */); + OP_WRITE_B(Sa, "foo"); + OP_CONCLUDE(Sa); + + OP_NEW_STREAM(S, Sb, 0 /* bidirectional */); + OP_WRITE_B(Sb, "foo"); + OP_CONCLUDE(Sb); + + OP_NEW_STREAM(S, Sc, 0 /* bidirectional */); + OP_WRITE_B(Sc, "foo"); + OP_CONCLUDE(Sc); + + OP_NEW_STREAM(S, Sd, 0 /* bidirectional */); + OP_WRITE_B(Sd, "foo"); + OP_CONCLUDE(Sd); + + OP_NEW_STREAM(S, Se, 0 /* bidirectional */); + OP_WRITE_B(Se, "foo"); + OP_CONCLUDE(Se); + OP_SLEEP(10); +} + { } From 4eab5e3bb2d17e44da2c9d22e56085b700841788 Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Wed, 1 Jul 2026 23:37:34 +0700 Subject: [PATCH 272/349] Port script12 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Neil Horman Reviewed-by: Saša Nedvědický MergeDate: Fri Jul 17 08:02:03 2026 (Merged from https://github.com/openssl/openssl/pull/31821) --- test/quic_multistream_test.c | 32 +----------------- test/radix/quic_tests.c | 65 +++++++++++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 32 deletions(-) diff --git a/test/quic_multistream_test.c b/test/quic_multistream_test.c index a8dae2966a..b9b263fe94 100644 --- a/test/quic_multistream_test.c +++ b/test/quic_multistream_test.c @@ -2080,38 +2080,8 @@ static const struct script_op script_11[] = { }; /* 12. Many threads initiated on the same client connection */ -static const struct script_op script_12_child[] = { - OP_C_NEW_STREAM_BIDI(a, ANY_ID), - OP_C_WRITE(a, "foo", 3), - OP_C_CONCLUDE(a), - OP_C_FREE_STREAM(a), - - OP_END -}; - static const struct script_op script_12[] = { - OP_C_SET_ALPN("ossltest"), - OP_C_CONNECT_WAIT(), - OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE), - - OP_NEW_THREAD(5, script_12_child), - - OP_S_BIND_STREAM_ID(a, C_BIDI_ID(0)), - OP_S_READ_EXPECT(a, "foo", 3), - OP_S_EXPECT_FIN(a), - OP_S_BIND_STREAM_ID(b, C_BIDI_ID(1)), - OP_S_READ_EXPECT(b, "foo", 3), - OP_S_EXPECT_FIN(b), - OP_S_BIND_STREAM_ID(c, C_BIDI_ID(2)), - OP_S_READ_EXPECT(c, "foo", 3), - OP_S_EXPECT_FIN(c), - OP_S_BIND_STREAM_ID(d, C_BIDI_ID(3)), - OP_S_READ_EXPECT(d, "foo", 3), - OP_S_EXPECT_FIN(d), - OP_S_BIND_STREAM_ID(e, C_BIDI_ID(4)), - OP_S_READ_EXPECT(e, "foo", 3), - OP_S_EXPECT_FIN(e), - + /* test moved to test/radix/quic_tests.c */ OP_END }; diff --git a/test/radix/quic_tests.c b/test/radix/quic_tests.c index a101cad924..d09c338505 100644 --- a/test/radix/quic_tests.c +++ b/test/radix/quic_tests.c @@ -993,11 +993,74 @@ DEF_SCRIPT(script_11, "Many threads accepted on the same client connection") OP_SLEEP(10); } +/* 12. Many threads initiated on the same client connection */ +DEF_SCRIPT(script_12_child_0, + "child: create stream on C, write, conclude") { + OP_NEW_STREAM(C, C0, 0 /* bidirectional */); + OP_WRITE_B(C0, "foo"); + OP_CONCLUDE(C0); } -DEF_SCRIPT(script_12, "place holder for multistrem script_12") +DEF_SCRIPT(script_12_child_1, + "child: create stream on C, write, conclude") { + OP_NEW_STREAM(C, C1, 0 /* bidirectional */); + OP_WRITE_B(C1, "foo"); + OP_CONCLUDE(C1); +} + +DEF_SCRIPT(script_12_child_2, + "child: create stream on C, write, conclude") +{ + OP_NEW_STREAM(C, C2, 0 /* bidirectional */); + OP_WRITE_B(C2, "foo"); + OP_CONCLUDE(C2); +} + +DEF_SCRIPT(script_12_child_3, + "child: create stream on C, write, conclude") +{ + OP_NEW_STREAM(C, C3, 0 /* bidirectional */); + OP_WRITE_B(C3, "foo"); + OP_CONCLUDE(C3); +} + +DEF_SCRIPT(script_12_child_4, + "child: create stream on C, write, conclude") +{ + OP_NEW_STREAM(C, C4, 0 /* bidirectional */); + OP_WRITE_B(C4, "foo"); + OP_CONCLUDE(C4); +} + +DEF_SCRIPT(script_12, "Many threads initiated on the same client connection") +{ + OP_SIMPLE_PAIR_CONN_ND(); + OP_ACCEPT_CONN_WAIT_ND(L, S, 0); + + OP_SPAWN_THREAD(script_12_child_0); + OP_SPAWN_THREAD(script_12_child_1); + OP_SPAWN_THREAD(script_12_child_2); + OP_SPAWN_THREAD(script_12_child_3); + OP_SPAWN_THREAD(script_12_child_4); + + OP_ACCEPT_STREAM_WAIT(S, Sa, 0); + OP_READ_EXPECT_B(Sa, "foo"); + OP_EXPECT_FIN(Sa); + OP_ACCEPT_STREAM_WAIT(S, Sb, 0); + OP_READ_EXPECT_B(Sb, "foo"); + OP_EXPECT_FIN(Sb); + OP_ACCEPT_STREAM_WAIT(S, Sc, 0); + OP_READ_EXPECT_B(Sc, "foo"); + OP_EXPECT_FIN(Sc); + OP_ACCEPT_STREAM_WAIT(S, Sd, 0); + OP_READ_EXPECT_B(Sd, "foo"); + OP_EXPECT_FIN(Sd); + OP_ACCEPT_STREAM_WAIT(S, Se, 0); + OP_READ_EXPECT_B(Se, "foo"); + OP_EXPECT_FIN(Se); + OP_SLEEP(10); } DEF_SCRIPT(script_13, "place holder for multistrem script_13") From 98a726d3ca369960d676e2691ca734448ef1a672 Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Fri, 3 Jul 2026 18:18:17 +0700 Subject: [PATCH 273/349] Introduce OP_BIND() to QUIC RADIX test framework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RADIX framework keeps objects needed by test scripts in two places: - hash table bound to radix process (`RP()->objs`), all objects are stored there - slot which is an array bound to radix thread (`RT()->slot[]`) The `slot` is an array which is used to pass arguments to RADIX ops. The typically script is doing something like this: ``` OP_SELECT_SSL(0, C); /* places 'C' object to slot 0 in thread */ OP_FUNC(print_ssl); /* calls print_ssl function, which prints object */ ``` All objects are managed by RADIX framework, scripts have very limited options to control object's lifetime. The only way for scripts to let object go is to use `OP_UNBIND()`. The operation removes the object from hastable (`RP()->objs`) and frees the object afterwards. This is good enough as long a all tests are running in single thread. Currently `OP_UNBIND()` is required when test needs to accept/create more than one stream. The test has two options. It can use unique name for each stream it creates/accepts: ``` OP_ACCEPT_STREAM_WAIT(C, C0, 0); OP_ACCEPT_STREAM_WAIT(C, C1, 0); OP_ACCEPT_STREAM_WAIT(C, C2, 0); ``` Or script may re-use the same variable for stream, in that case `OP_UNBIND()` is needed: ``` OP_ACCEPT_STREAM_WAIT(C, C0, 0); OP_UNBIND(C0); OP_ACCEPT_STREAM_WAIT(C, C0, 0); OP_UNBIND(C0); OP_ACCEPT_STREAM_WAIT(C, C0, 0); OP_UNBIND(C0); ``` Unfortunately `OP_UNBIND()` can not be used when test uses more than one thread due to missing locking of `RP()->objs`. Introducing a locking scheme seems to be bit invasive change, The OP_BIND() here hopes to be sufficient and good enough for now. The idea is as follows: - `OP_BIND()` allows script to insert empty object into `RP()->objs` OP_BIND() is supposed to run before script spawns thread(s). No manipulation of `RP()->objs` is allowed after threads are spawned, operations OP_BIND()/OP_UNBIND() are not thread safe. - Introduce `OP_F_REPLACE_STREAM` flag which tells `OP_ACCEPT_STREAM_WAIT()`/`OP_NEW_STREAM()` to re-use existing id for stream. This `_REPLACE_` flag requires read-only access to `RP()->objs` hash table. - change introduces a per radix object mutex so object can be updated safely w.r.t. RADIX thread which ticks SSL object bound in radix object. The guideline for tests which require more then one thread is as follows: - the first thread creates complete set of empty objects for all threads. - each test thread gets its own set of variables, so it can populate them later during test with SSL objects - objects are not supposed to be shared between threads This is a snippet of script executed by main thread before additional threads are spawned: ``` ... OP_BIND(C1); /* stream id for child */ OP_BIND(S1); /* stream id for parent */ OP_SPAWN_THREAD(child); for (i = 0; i < 10; i++) { OP_NEW_STREAM(S, S1, OP_F_REPLACE_STREAM); OP_WRITE_B(S1, "foo"); OP_CONCLUDE(S1); } ``` This snippet comes from child: ``` for (i = 0; i < 10; i++) { OP_ACCEPT_STREAM_WAIT(C, C1, OP_F_REPLACE_STREAM); OP_READ_EXPECT_B(C1, "foo"); OP_EXPECT_FIN(C1); } ``` As you can see parent and child don't use OP_BIND()/OP_UNBIND() after child thread is spawned. Reviewed-by: Neil Horman Reviewed-by: Saša Nedvědický MergeDate: Fri Jul 17 08:02:04 2026 (Merged from https://github.com/openssl/openssl/pull/31821) --- test/radix/quic_bindings.c | 52 ++++++++++++++++++++++++++++++-- test/radix/quic_ops.c | 50 ++++++++++++++++++++++++++---- test/radix/quic_tests.c | 62 ++++++++++++++++++++++++++------------ 3 files changed, 135 insertions(+), 29 deletions(-) diff --git a/test/radix/quic_bindings.c b/test/radix/quic_bindings.c index 9f887d55c6..02356f48eb 100644 --- a/test/radix/quic_bindings.c +++ b/test/radix/quic_bindings.c @@ -52,6 +52,7 @@ typedef struct radix_obj_st { SSL *ssl; /* owns one reference */ unsigned int registered : 1; /* in LHASH? */ unsigned int active : 1; /* tick? */ + CRYPTO_MUTEX *mx; } RADIX_OBJ; DEFINE_LHASH_OF_EX(RADIX_OBJ); @@ -104,11 +105,11 @@ typedef struct radix_thread_st { DEFINE_STACK_OF(RADIX_THREAD) /* ssl reference is transferred. name is copied and is required. */ -static RADIX_OBJ *RADIX_OBJ_new(const char *name, SSL *ssl) +static RADIX_OBJ *RADIX_OBJ_new_empty(const char *name) { RADIX_OBJ *obj; - if (!TEST_ptr(name) || !TEST_ptr(ssl)) + if (!TEST_ptr(name)) return NULL; if (!TEST_ptr(obj = OPENSSL_zalloc(sizeof(*obj)))) @@ -119,7 +120,31 @@ static RADIX_OBJ *RADIX_OBJ_new(const char *name, SSL *ssl) return NULL; } + obj->mx = ossl_crypto_mutex_new(); +#if !defined(OPENSSL_THREADS_NONE) + if (obj->mx == NULL) { + OPENSSL_free(obj->name); + OPENSSL_free(obj); + return NULL; + } +#endif + + return obj; +} + +static RADIX_OBJ *RADIX_OBJ_new(const char *name, SSL *ssl) +{ + RADIX_OBJ *obj; + + if (!TEST_ptr(ssl)) + return NULL; + + obj = RADIX_OBJ_new_empty(name); + if (!TEST_ptr(obj)) + return NULL; + obj->ssl = ssl; + return obj; } @@ -132,6 +157,7 @@ static void RADIX_OBJ_free(RADIX_OBJ *obj) SSL_free(obj->ssl); OPENSSL_free(obj->name); + ossl_crypto_mutex_free(&obj->mx); OPENSSL_free(obj); } @@ -444,6 +470,9 @@ static int RADIX_PROCESS_set_obj(RADIX_PROCESS *rp, const char *name, RADIX_OBJ *obj) { RADIX_OBJ *existing; + SSL *existing_ssl = NULL; + RADIX_THREAD *rt; + int i, j; if (obj != NULL && !TEST_false(obj->registered)) return 0; @@ -455,7 +484,10 @@ static int RADIX_PROCESS_set_obj(RADIX_PROCESS *rp, lh_RADIX_OBJ_delete(rp->objs, existing); existing->registered = 0; + existing_ssl = existing->ssl; RADIX_OBJ_free(existing); + } else { + existing = NULL; } if (obj != NULL) { @@ -463,6 +495,18 @@ static int RADIX_PROCESS_set_obj(RADIX_PROCESS *rp, obj->registered = 1; } + if (existing != NULL) { + for (i = 0; i < sk_RADIX_THREAD_num(rp->threads); i++) { + rt = (RADIX_THREAD *)sk_RADIX_THREAD_value(rp->threads, i); + for (j = 0; j < NUM_SLOTS; j++) { + if (rt->slot[j] == existing) + rt->slot[j] = obj; + if (rt->ssl[j] == existing_ssl) + rt->ssl[j] = (obj == NULL) ? NULL : obj->ssl; + } + } + } + return 1; } @@ -650,8 +694,10 @@ ossl_unused static void radix_skip_time(OSSL_TIME t) static void per_op_tick_obj(RADIX_OBJ *obj) { - if (obj->active) + ossl_crypto_mutex_lock(obj->mx); + if (obj->active && obj->ssl) SSL_handle_events(obj->ssl); + ossl_crypto_mutex_unlock(obj->mx); } static int do_per_op(TERP *terp, void *arg) diff --git a/test/radix/quic_ops.c b/test/radix/quic_ops.c index 5d69d88de0..902dab4aed 100644 --- a/test/radix/quic_ops.c +++ b/test/radix/quic_ops.c @@ -27,6 +27,24 @@ err: return ok; } +DEF_FUNC(hf_bind) +{ + const char *name; + RADIX_OBJ *empty_obj; + + F_POP(name); + + empty_obj = RADIX_OBJ_new_empty(name); + if (empty_obj == NULL) + return 0; + + RADIX_PROCESS_set_obj(RP(), name, empty_obj); + + return 1; +err: + return 0; +} + static int ssl_ctx_select_alpn(SSL *ssl, const unsigned char **out, unsigned char *out_len, const unsigned char *in, unsigned int in_len, @@ -248,27 +266,37 @@ err: return ok; } +#define OP_F_REPLACE_STREAM 0x8000000000000000 +#define OP_F_MASK 0x7fffffffffffffff + DEF_FUNC(hf_new_stream) { int ok = 0; + int replace; + RADIX_OBJ *stream_obj; const char *stream_name; - SSL *conn, *stream; + SSL *conn, *stream, *old; uint64_t flags, do_accept; F_POP2(flags, do_accept); F_POP(stream_name); REQUIRE_SSL(conn); + replace = ((OP_F_REPLACE_STREAM & flags) != 0); - if (!TEST_ptr_null(RADIX_PROCESS_get_obj(RP(), stream_name))) + stream_obj = RADIX_PROCESS_get_obj(RP(), stream_name); + if (replace == 0) { + if (!TEST_ptr_null(stream_obj)) + goto err; + } else if (TEST_ptr_null(stream_obj)) goto err; if (do_accept) { - stream = SSL_accept_stream(conn, flags); + stream = SSL_accept_stream(conn, flags & OP_F_MASK); if (stream == NULL) F_SPIN_AGAIN(); } else { - stream = SSL_new_stream(conn, flags); + stream = SSL_new_stream(conn, flags & OP_F_MASK); } if (!TEST_ptr(stream)) @@ -276,8 +304,14 @@ DEF_FUNC(hf_new_stream) /* TODO(QUIC RADIX): Implement wait behaviour */ - if (stream != NULL - && !TEST_true(RADIX_PROCESS_set_ssl(RP(), stream_name, stream))) { + if (stream_obj != NULL) { + ossl_crypto_mutex_lock(stream_obj->mx); + old = stream_obj->ssl; + stream_obj->ssl = stream; + stream = NULL; + ossl_crypto_mutex_unlock(stream_obj->mx); + SSL_free(old); + } else if (!TEST_true(RADIX_PROCESS_set_ssl(RP(), stream_name, stream))) { SSL_free(stream); goto err; } @@ -928,6 +962,10 @@ err: (OP_PUSH_PZ(#name), \ OP_FUNC(hf_unbind)) +#define OP_BIND(name) \ + (OP_PUSH_PZ(#name), \ + OP_FUNC(hf_bind)) + #define OP_SELECT_SSL(slot, name) \ (OP_PUSH_U64(slot), \ OP_PUSH_PZ(#name), \ diff --git a/test/radix/quic_tests.c b/test/radix/quic_tests.c index d09c338505..3047ef08a1 100644 --- a/test/radix/quic_tests.c +++ b/test/radix/quic_tests.c @@ -918,7 +918,7 @@ DEF_SCRIPT(script_10, "Shutdown test") DEF_SCRIPT(script_11_child_0, "child: accept stream from C, read, sleep, expect FIN") { - OP_ACCEPT_STREAM_WAIT(C, C0, 0 /* bidirectional */); + OP_ACCEPT_STREAM_WAIT(C, C0, OP_F_REPLACE_STREAM /* bidirectional */); OP_READ_EXPECT_B(C0, "foo"); OP_SLEEP(10); OP_EXPECT_FIN(C0); @@ -927,7 +927,7 @@ DEF_SCRIPT(script_11_child_0, DEF_SCRIPT(script_11_child_1, "child: accept stream from C, read, sleep, expect FIN") { - OP_ACCEPT_STREAM_WAIT(C, C1, 0 /* bidirectional */); + OP_ACCEPT_STREAM_WAIT(C, C1, OP_F_REPLACE_STREAM /* bidirectional */); OP_READ_EXPECT_B(C1, "foo"); OP_SLEEP(10); OP_EXPECT_FIN(C1); @@ -936,7 +936,7 @@ DEF_SCRIPT(script_11_child_1, DEF_SCRIPT(script_11_child_2, "child: accept stream from C, read, sleep, expect FIN") { - OP_ACCEPT_STREAM_WAIT(C, C2, 0 /* bidirectional */); + OP_ACCEPT_STREAM_WAIT(C, C2, OP_F_REPLACE_STREAM /* bidirectional */); OP_READ_EXPECT_B(C2, "foo"); OP_SLEEP(10); OP_EXPECT_FIN(C2); @@ -945,7 +945,7 @@ DEF_SCRIPT(script_11_child_2, DEF_SCRIPT(script_11_child_3, "child: accept stream from C, read, sleep, expect FIN") { - OP_ACCEPT_STREAM_WAIT(C, C3, 0 /* bidirectional */); + OP_ACCEPT_STREAM_WAIT(C, C3, OP_F_REPLACE_STREAM /* bidirectional */); OP_READ_EXPECT_B(C3, "foo"); OP_SLEEP(10); OP_EXPECT_FIN(C3); @@ -954,7 +954,7 @@ DEF_SCRIPT(script_11_child_3, DEF_SCRIPT(script_11_child_4, "child: accept stream from C, read, sleep, expect FIN") { - OP_ACCEPT_STREAM_WAIT(C, C4, 0 /* bidirectional */); + OP_ACCEPT_STREAM_WAIT(C, C4, OP_F_REPLACE_STREAM /* bidirectional */); OP_READ_EXPECT_B(C4, "foo"); OP_SLEEP(10); OP_EXPECT_FIN(C4); @@ -965,29 +965,40 @@ DEF_SCRIPT(script_11, "Many threads accepted on the same client connection") OP_SIMPLE_PAIR_CONN_ND(); OP_ACCEPT_CONN_WAIT(L, S, 0); + OP_BIND(C0); + OP_BIND(C1); + OP_BIND(C2); + OP_BIND(C3); + OP_BIND(C4); + OP_BIND(Sa); + OP_BIND(Sb); + OP_BIND(Sc); + OP_BIND(Sd); + OP_BIND(Se); + OP_SPAWN_THREAD(script_11_child_0); OP_SPAWN_THREAD(script_11_child_1); OP_SPAWN_THREAD(script_11_child_2); OP_SPAWN_THREAD(script_11_child_3); OP_SPAWN_THREAD(script_11_child_4); - OP_NEW_STREAM(S, Sa, 0 /* bidirectional */); + OP_NEW_STREAM(S, Sa, OP_F_REPLACE_STREAM /* bidirectional */); OP_WRITE_B(Sa, "foo"); OP_CONCLUDE(Sa); - OP_NEW_STREAM(S, Sb, 0 /* bidirectional */); + OP_NEW_STREAM(S, Sb, OP_F_REPLACE_STREAM /* bidirectional */); OP_WRITE_B(Sb, "foo"); OP_CONCLUDE(Sb); - OP_NEW_STREAM(S, Sc, 0 /* bidirectional */); + OP_NEW_STREAM(S, Sc, OP_F_REPLACE_STREAM /* bidirectional */); OP_WRITE_B(Sc, "foo"); OP_CONCLUDE(Sc); - OP_NEW_STREAM(S, Sd, 0 /* bidirectional */); + OP_NEW_STREAM(S, Sd, OP_F_REPLACE_STREAM /* bidirectional */); OP_WRITE_B(Sd, "foo"); OP_CONCLUDE(Sd); - OP_NEW_STREAM(S, Se, 0 /* bidirectional */); + OP_NEW_STREAM(S, Se, OP_F_REPLACE_STREAM /* bidirectional */); OP_WRITE_B(Se, "foo"); OP_CONCLUDE(Se); OP_SLEEP(10); @@ -997,7 +1008,7 @@ DEF_SCRIPT(script_11, "Many threads accepted on the same client connection") DEF_SCRIPT(script_12_child_0, "child: create stream on C, write, conclude") { - OP_NEW_STREAM(C, C0, 0 /* bidirectional */); + OP_NEW_STREAM(C, C0, OP_F_REPLACE_STREAM /* bidirectional */); OP_WRITE_B(C0, "foo"); OP_CONCLUDE(C0); } @@ -1005,7 +1016,7 @@ DEF_SCRIPT(script_12_child_0, DEF_SCRIPT(script_12_child_1, "child: create stream on C, write, conclude") { - OP_NEW_STREAM(C, C1, 0 /* bidirectional */); + OP_NEW_STREAM(C, C1, OP_F_REPLACE_STREAM /* bidirectional */); OP_WRITE_B(C1, "foo"); OP_CONCLUDE(C1); } @@ -1013,7 +1024,7 @@ DEF_SCRIPT(script_12_child_1, DEF_SCRIPT(script_12_child_2, "child: create stream on C, write, conclude") { - OP_NEW_STREAM(C, C2, 0 /* bidirectional */); + OP_NEW_STREAM(C, C2, OP_F_REPLACE_STREAM /* bidirectional */); OP_WRITE_B(C2, "foo"); OP_CONCLUDE(C2); } @@ -1021,7 +1032,7 @@ DEF_SCRIPT(script_12_child_2, DEF_SCRIPT(script_12_child_3, "child: create stream on C, write, conclude") { - OP_NEW_STREAM(C, C3, 0 /* bidirectional */); + OP_NEW_STREAM(C, C3, OP_F_REPLACE_STREAM /* bidirectional */); OP_WRITE_B(C3, "foo"); OP_CONCLUDE(C3); } @@ -1029,7 +1040,7 @@ DEF_SCRIPT(script_12_child_3, DEF_SCRIPT(script_12_child_4, "child: create stream on C, write, conclude") { - OP_NEW_STREAM(C, C4, 0 /* bidirectional */); + OP_NEW_STREAM(C, C4, OP_F_REPLACE_STREAM /* bidirectional */); OP_WRITE_B(C4, "foo"); OP_CONCLUDE(C4); } @@ -1039,25 +1050,36 @@ DEF_SCRIPT(script_12, "Many threads initiated on the same client connection") OP_SIMPLE_PAIR_CONN_ND(); OP_ACCEPT_CONN_WAIT_ND(L, S, 0); + OP_BIND(C0); + OP_BIND(C1); + OP_BIND(C2); + OP_BIND(C3); + OP_BIND(C4); + OP_BIND(Sa); + OP_BIND(Sb); + OP_BIND(Sc); + OP_BIND(Sd); + OP_BIND(Se); + OP_SPAWN_THREAD(script_12_child_0); OP_SPAWN_THREAD(script_12_child_1); OP_SPAWN_THREAD(script_12_child_2); OP_SPAWN_THREAD(script_12_child_3); OP_SPAWN_THREAD(script_12_child_4); - OP_ACCEPT_STREAM_WAIT(S, Sa, 0); + OP_ACCEPT_STREAM_WAIT(S, Sa, OP_F_REPLACE_STREAM); OP_READ_EXPECT_B(Sa, "foo"); OP_EXPECT_FIN(Sa); - OP_ACCEPT_STREAM_WAIT(S, Sb, 0); + OP_ACCEPT_STREAM_WAIT(S, Sb, OP_F_REPLACE_STREAM); OP_READ_EXPECT_B(Sb, "foo"); OP_EXPECT_FIN(Sb); - OP_ACCEPT_STREAM_WAIT(S, Sc, 0); + OP_ACCEPT_STREAM_WAIT(S, Sc, OP_F_REPLACE_STREAM); OP_READ_EXPECT_B(Sc, "foo"); OP_EXPECT_FIN(Sc); - OP_ACCEPT_STREAM_WAIT(S, Sd, 0); + OP_ACCEPT_STREAM_WAIT(S, Sd, OP_F_REPLACE_STREAM); OP_READ_EXPECT_B(Sd, "foo"); OP_EXPECT_FIN(Sd); - OP_ACCEPT_STREAM_WAIT(S, Se, 0); + OP_ACCEPT_STREAM_WAIT(S, Se, OP_F_REPLACE_STREAM); OP_READ_EXPECT_B(Se, "foo"); OP_EXPECT_FIN(Se); OP_SLEEP(10); From 4ef7e3b3cdc483cebb55b240770a2ee6e705b8db Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 14 Jul 2026 19:01:24 +0200 Subject: [PATCH 274/349] apps: test ec and ecparam -text options The -text option was not exercised for the ec or ecparam apps. Add a subtest to 15-test_ec.t that prints a private and a public EC key and, after stripping the colon-separated hex formatting, verifies the printed private and public values match the committed testec-p256.pem keypair as well as the curve identification. Add a subtest to 15-test_ecparam.t that prints named and explicit parameters, checking the named form emits the expected curve OID and NIST name while the explicit form emits the field parameters and no OID. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Nikola Pajkovsky Reviewed-by: Daniel Kubec Reviewed-by: Tim Hudson MergeDate: Fri Jul 17 08:47:12 2026 (Merged from https://github.com/openssl/openssl/pull/31952) --- test/recipes/15-test_ec.t | 51 +++++++++++++++++++++++++++++++++- test/recipes/15-test_ecparam.t | 35 ++++++++++++++++++++++- 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/test/recipes/15-test_ec.t b/test/recipes/15-test_ec.t index 5b0a69f0ca..e3d18a8849 100644 --- a/test/recipes/15-test_ec.t +++ b/test/recipes/15-test_ec.t @@ -19,7 +19,7 @@ setup("test_ec"); plan skip_all => 'EC is not supported in this build' if disabled('ec'); -plan tests => 18; +plan tests => 19; my $no_fips = disabled('fips') || ($ENV{NO_FIPS} // 0); @@ -152,6 +152,55 @@ subtest 'EC parameter encoding (-param_enc)' => sub { "an invalid parameter encoding is rejected"); }; +subtest 'ec -text prints the key in text form' => sub { + plan tests => 7; + + my $priv_key = srctop_file("test", "testec-p256.pem"); + my $pub_key = srctop_file("test", "testecpub-p256.pem"); + + # The private (priv) and public (pub) values of the committed + # testec-p256.pem / testecpub-p256.pem keypair. -text prints them as + # colon-separated hex; we strip the formatting and compare against the + # known values so the actual key material, not just the labels, is checked. + my $priv_hex = "36045F6C909612570C8C0113071FC809F6788084289C6AB003C60A" + . "17B2D5ADAD"; + my $pub_hex = "04257C007484E23C571252C6912369E5CD33519FEFAAE85DFC5EB1FC" + . "9BCB1FDBC0D0FB63A86F9494CEF823552D1EEF4A48A87E9B4970E03DCF262AD" + . "4ACF598B6E9"; + + # ec -text on the private key. + my @priv = run(app(['openssl', 'ec', '-text', '-noout', '-in', $priv_key], + stderr => undef), + capture => 1); + chomp @priv; + my $priv_blob = uc join('', @priv); + $priv_blob =~ s/[^0-9A-F]//g; + ok(grep(/^Private-Key: \(256 bit field, 128 bit security level\)$/, @priv), + "ec -text prints the private key header"); + ok(index($priv_blob, $priv_hex) >= 0, + "ec -text prints the expected private value"); + ok(index($priv_blob, $pub_hex) >= 0, + "ec -text prints the expected public value"); + ok(grep(/^ASN1 OID: prime256v1$/, @priv) + && grep(/^NIST CURVE: P-256$/, @priv), + "ec -text prints the curve identification"); + + # ec -text on the public key. + my @pub = run(app(['openssl', 'ec', '-pubin', '-text', '-noout', + '-in', $pub_key], + stderr => undef), + capture => 1); + chomp @pub; + my $pub_blob = uc join('', @pub); + $pub_blob =~ s/[^0-9A-F]//g; + ok(grep(/^Public-Key: \(256 bit field, 128 bit security level\)$/, @pub), + "ec -text prints the public key header"); + ok(index($pub_blob, $pub_hex) >= 0, + "ec -text prints the expected public value for a public key"); + ok(!grep(/^priv:/, @pub), + "ec -text does not print a private component for a public key"); +}; + subtest 'Check loading of fips and non-fips keys' => sub { plan skip_all => "FIPS is disabled" if $no_fips; diff --git a/test/recipes/15-test_ecparam.t b/test/recipes/15-test_ecparam.t index 571c62cf1a..169814b4e5 100644 --- a/test/recipes/15-test_ecparam.t +++ b/test/recipes/15-test_ecparam.t @@ -30,7 +30,7 @@ if (disabled("sm2")) { @valid = grep { !/sm2-.*\.pem/} @valid; } -plan tests => 15; +plan tests => 16; sub checkload { my $files = shift; # List of files @@ -224,4 +224,37 @@ subtest "Check ecparam -param_enc converts between named and explicit" => sub { "an invalid parameter encoding is rejected"); }; +subtest "Check ecparam -text prints the parameters in text form" => sub { + plan tests => 6; + + my $named = data_file('valid', 'secp384r1-named.pem'); + my $explicit = data_file('valid', 'secp384r1-explicit.pem'); + + # Named parameters print the curve identification. + my @named = run(app(['openssl', 'ecparam', '-text', '-noout', '-in', $named], + stderr => undef), + capture => 1); + chomp @named; + ok(grep(/^EC-Parameters: \(384 bit field, 192 bit security level\)$/, @named), + "named parameters print the EC-Parameters header"); + ok(grep(/^ASN1 OID: secp384r1$/, @named), + "named parameters print the expected curve OID"); + ok(grep(/^NIST CURVE: P-384$/, @named), + "named parameters print the expected NIST curve name"); + + # Explicit parameters print the field parameters instead of the curve name. + my @explicit = run(app(['openssl', 'ecparam', '-text', '-noout', + '-in', $explicit], + stderr => undef), + capture => 1); + chomp @explicit; + ok(grep(/^EC-Parameters: \(384 bit field, 192 bit security level\)$/, @explicit), + "explicit parameters print the EC-Parameters header"); + ok(grep(/^Field Type: prime-field$/, @explicit) + && grep(/^Cofactor:/, @explicit), + "explicit parameters print the field parameters"); + ok(!grep(/^ASN1 OID:/, @explicit), + "explicit parameters do not print a curve OID"); +}; + ok(run(app(['openssl', 'ecparam', '-list_curves'])), "Test -list_curves"); From 49057cbe91eef40fcc1e4973feeaef8be60b9434 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 14 Jul 2026 18:38:57 +0200 Subject: [PATCH 275/349] apps: test pkey -encopt option The -encopt option of the pkey app was not exercised by any test; the existing ML-DSA codec tests only used genpkey -encopt and pkey with -provparam. Re-encode the seed-priv key into each supported PKCS#8 output format via 'pkey -encopt output_formats:
' and check the result matches the reference for that form. A control compares against the default (no -encopt) encoding so the match is attributed to -encopt rather than the default behaviour. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Nikola Pajkovsky Reviewed-by: Daniel Kubec Reviewed-by: Tim Hudson MergeDate: Fri Jul 17 08:51:40 2026 (Merged from https://github.com/openssl/openssl/pull/31951) --- test/recipes/15-test_ml_dsa_codecs.t | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/test/recipes/15-test_ml_dsa_codecs.t b/test/recipes/15-test_ml_dsa_codecs.t index f5b5134c8a..16fc5c3021 100644 --- a/test/recipes/15-test_ml_dsa_codecs.t +++ b/test/recipes/15-test_ml_dsa_codecs.t @@ -26,7 +26,7 @@ my @formats = qw(seed-priv priv-only seed-only oqskeypair bare-seed bare-priv); plan skip_all => "ML-DSA isn't supported in this build" if disabled("ml-dsa"); -plan tests => @algs * (26 + 10 * @formats); +plan tests => @algs * (27 + 13 * @formats); my $seed = join ("", map {sprintf "%02x", $_} (0..31)); my $weed = join ("", map {sprintf "%02x", $_} (1..32)); my $ikme = join ("", map {sprintf "%02x", $_} (0..31)); @@ -45,6 +45,12 @@ foreach my $alg (@algs) { my $der0 = sprintf("pub-%s.%d.der", $alg, $i++); ok(run(app(['openssl', 'pkey', '-pubin', '-in', $in0, '-outform', 'DER', '-out', $der0]))); + + # Default encoding (no -encopt) is seed-priv; used as a control below. + my $plain = sprintf("prv-%s.plain.pem", $alg); + ok(run(app(['openssl', 'pkey', '-in', data_file($formats{'seed-priv'}), + '-out', $plain])), + sprintf("pkey default re-encode: %s", $alg)); foreach my $f (keys %formats) { my $kf = $formats{$f}; my %pruned = %formats; @@ -71,6 +77,20 @@ foreach my $alg (@algs) { '-provparam', "ml-dsa.input_formats=$f"]))); ok(!run(app(['openssl', 'pkey', '-in', $in, '-noout', '-provparam', "ml-dsa.input_formats=$rest"]))); + + # Re-encode the seed-priv key into format $f via 'pkey -encopt' and + # check it matches the reference; the control asserts the match is due + # to -encopt, i.e. it differs from the default for every non-seed-priv + # format (and equals it for seed-priv). + my $enc = sprintf("prv-%s-%s.enc.pem", $alg, $f); + ok(run(app(['openssl', 'pkey', '-in', data_file($formats{'seed-priv'}), + '-encopt', "output_formats:$f", '-out', $enc])), + sprintf("pkey -encopt re-encode: %s, %s", $alg, $f)); + ok(!compare_text($in, $enc), + sprintf("pkey -encopt output_formats match: %s, %s", $alg, $f)); + ok($f eq 'seed-priv' ? compare_text($plain, $enc) == 0 + : compare_text($plain, $enc) != 0, + sprintf("pkey -encopt changed the encoding: %s, %s", $alg, $f)); } # (1 + 2 * @formats) tests From d8bf6cdd4849925c30e4f1911c7acb49cb34b702 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sun, 12 Jul 2026 15:02:35 +0200 Subject: [PATCH 276/349] test: don't depend on DTLS alert delivery in sslrecords test The unknown-record-type tests (tests 5 and 6) inferred failure of a DTLS connection from TLSProxy's socket-teardown timing ($proxy_start_success == 0). This relied on the client's fatal alert reaching the peer before the client closes its socket, which is a race: DTLS alerts are best-effort and are never retransmitted (RFC 6347 section 4.2.7 / RFC 9147 section 5.10), and after the s_client shutdown drain was skipped for datagram protocols the alert can be lost during teardown, making the test flaky. Verify instead what is actually under test: that the DTLS client rejected the unrecognised record type, i.e. that s_client exited with a failure. This is a deterministic, local decision that does not depend on the alert being observed by the peer. Keep the alert observation as a best-effort diagnostic note. Capture the s_client exit status in TLSProxy (previously discarded after waitpid) and expose it via a new clientexit accessor. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Neil Horman Reviewed-by: Tomas Mraz MergeDate: Sat Jul 18 12:53:26 2026 (Merged from https://github.com/openssl/openssl/pull/31927) --- test/recipes/70-test_sslrecords.t | 10 ++++++++-- util/perl/TLSProxy/Proxy.pm | 8 ++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/test/recipes/70-test_sslrecords.t b/test/recipes/70-test_sslrecords.t index a09a818efe..a49e3a256c 100644 --- a/test/recipes/70-test_sslrecords.t +++ b/test/recipes/70-test_sslrecords.t @@ -146,7 +146,12 @@ sub run_tests $proxy_start_success = $proxy->start(); if ($run_test_as_dtls == 1) { - ok($proxy_start_success == 0, "Unrecognised record type in DTLS1.2"); + # DTLS alerts are best-effort (RFC 6347 section 4.2.7): the client's + # fatal alert may be lost, so we cannot rely on observing it. What we + # verify is that the client rejected the connection, i.e. exited with a + # failure. Whether we happened to see the alert is only diagnostic. + ok($proxy->clientexit != 0, "Unrecognised record type in DTLS1.2"); + note("client fatal alert observed") if $fatal_alert; } else { ok($fatal_alert, "Unrecognised record type in TLS1.2"); } @@ -166,7 +171,8 @@ sub run_tests $proxy->ciphers("AES128-SHA:\@SECLEVEL=0"); $proxy_start_success = $proxy->start(); if ($run_test_as_dtls == 1) { - ok($proxy_start_success == 0, "Unrecognised record type in DTLSv1"); + ok($proxy->clientexit != 0, "Unrecognised record type in DTLSv1"); + note("client fatal alert observed") if $fatal_alert; } else { ok($fatal_alert, "Unrecognised record type in TLSv1.1"); } diff --git a/util/perl/TLSProxy/Proxy.pm b/util/perl/TLSProxy/Proxy.pm index 5ba6579ed3..a7fe48e9ce 100644 --- a/util/perl/TLSProxy/Proxy.pm +++ b/util/perl/TLSProxy/Proxy.pm @@ -181,6 +181,7 @@ sub init server_port => 0, serverpid => 0, clientpid => 0, + clientexit => 0, execute => $execute, cert => $cert, debug => $debug, @@ -219,6 +220,7 @@ sub clearClient $self->{clientflags} = ""; $self->{sessionfile} = undef; $self->{clientpid} = 0; + $self->{clientexit} = 0; $is_tls13 = 0; $ciphersuite = undef; @@ -604,6 +606,7 @@ sub clientstart $pid = $self->{clientpid}; print "Waiting for s_client process to close: $pid...\n"; waitpid($pid, 0); + $self->{clientexit} = $?; return $success; } @@ -741,6 +744,11 @@ sub clientpid my $self = shift; return $self->{clientpid}; } +sub clientexit +{ + my $self = shift; + return $self->{clientexit}; +} #Read/write accessors sub filter From 28179061bfcdfe579f63a129bf0b97766a5d90a7 Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Mon, 11 May 2026 10:30:04 -0600 Subject: [PATCH 277/349] Prepare a now opaque ASN1_STRING for the size_t rapture. Now that ASN1_STRING is opaque, we can finally move away from an int for the length internally. The remaining problematic piece for this is that ASN1_STRING_length() returns an int and is public API. Therefore, we deprecate ASN1_STRING_length() and provide a replacement ASN1_STRING_length_ex() that returns a size_t length. We also provide setting functions that take size_t lengths, they are ASN1_STRING_set_data() which takes a uint8_t data pointer and a size_t length, and ASN1_STRING_set_string() which takes a argument that must be a c string and will use strlen to determine the length. (This replaces th previous arcane behaviour of calling "strlen" on a magical input length value of -1, which leads to bugs.) We then deprecate ASN1_STRING_set(). ASN1_STRING_set_string() requires a valid C string argument that may not be NULL - refer to the documentation. Both new functions do not magically add 0 bytes on the end of values, as ASN1_STRING has already been documented for a long time to not depend on this behaviour. Both new functions do not allow the setting of values on an ASN1_BIT_STRING, as ASN1_BIT_STRING_set1 must be used for that. Note that this does *NOT* yet change ASN1_STRING to use size_t internally, this must wait until the integer-returning ASN1_STRING_length() has been deprecated, and then removed in future major. Once ASN1_STRING_length() has been removed then ASN1_STRING internally can change to using a size_t for the length of the data. (And the setters will no longer return an error if the provided size_t length exceeds INT_MAX) Reviewed-by: Milan Broz Reviewed-by: Norbert Pocs MergeDate: Sat Jul 18 13:01:15 2026 (Merged from https://github.com/openssl/openssl/pull/31194) --- crypto/asn1/a_octet.c | 8 ++- crypto/asn1/asn1_lib.c | 111 ++++++++++++++++++++++---------- doc/man3/ASN1_STRING_length.pod | 53 ++++++++++++--- include/openssl/asn1.h.in | 9 ++- test/asn1_string_test.c | 87 +++++++++++++++++++++++-- util/libcrypto.num | 7 +- 6 files changed, 224 insertions(+), 51 deletions(-) diff --git a/crypto/asn1/a_octet.c b/crypto/asn1/a_octet.c index 4efb8ec517..99df7539a1 100644 --- a/crypto/asn1/a_octet.c +++ b/crypto/asn1/a_octet.c @@ -25,5 +25,11 @@ int ASN1_OCTET_STRING_cmp(const ASN1_OCTET_STRING *a, int ASN1_OCTET_STRING_set(ASN1_OCTET_STRING *x, const unsigned char *d, int len) { - return ASN1_STRING_set(x, d, len); + if (len < -1) { + ERR_raise(ERR_LIB_ASN1, ASN1_R_TOO_SMALL); + return 0; + } + if (len == -1) + return ASN1_STRING_set_string(x, (const char *)d); + return ASN1_STRING_set_data(x, d, len); } diff --git a/crypto/asn1/asn1_lib.c b/crypto/asn1/asn1_lib.c index 99903564e2..2bde572eaf 100644 --- a/crypto/asn1/asn1_lib.c +++ b/crypto/asn1/asn1_lib.c @@ -265,7 +265,8 @@ int ASN1_STRING_copy(ASN1_STRING *dst, const ASN1_STRING *str) if (str == NULL) return 0; dst->type = str->type; - if (!ASN1_STRING_set(dst, str->data, str->length)) + if (!ossl_asn1_string_set_internal(dst, str->data, str->length, + /*add_nul_byte=*/0)) return 0; /* Copy flags but preserve embed value */ dst->flags &= ASN1_STRING_FLAG_EMBED; @@ -289,12 +290,18 @@ ASN1_STRING *ASN1_STRING_dup(const ASN1_STRING *str) return ret; } -int ASN1_STRING_set(ASN1_STRING *str, const void *_data, int len_in) +int ossl_asn1_string_set_internal(ASN1_STRING *str, const uint8_t *data, + int len_in, int add_nul_byte) { - unsigned char *c; - const char *data = _data; - size_t len; + size_t len, alloc_len; +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + /* + * Force no NUL byte for callers that are requesting it + * 0 length object data will be NULL + */ + add_nul_byte = 0; +#endif if (len_in < -1) { ERR_raise(ERR_LIB_ASN1, ASN1_R_TOO_SMALL); return 0; @@ -302,16 +309,17 @@ int ASN1_STRING_set(ASN1_STRING *str, const void *_data, int len_in) if (len_in == -1) { if (data == NULL) return 0; - len = strlen(data); + len = strlen((const char *)data); } else { len = (size_t)len_in; } /* - * Verify that the length fits within an integer for assignment to - * str->length below. The additional 1 is subtracted to allow for the - * '\0' terminator even though this isn't strictly necessary. + * Add one to the length to allow for adding an a '\0' terminator + * "even though this isn't strictly necessary". */ - if (len > INT_MAX - 1) { + alloc_len = add_nul_byte ? len + 1 : len; + + if (alloc_len > INT_MAX) { ERR_raise(ERR_LIB_ASN1, ASN1_R_TOO_LARGE); return 0; } @@ -322,39 +330,47 @@ int ASN1_STRING_set(ASN1_STRING *str, const void *_data, int len_in) str->flags &= ~ASN1_STRING_FLAG_DATA_NOT_OWNED; } - if ((size_t)str->length <= len || str->data == NULL) { - c = str->data; -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - /* No NUL terminator in fuzzing builds */ - str->data = OPENSSL_realloc(c, len != 0 ? len : 1); -#else - str->data = OPENSSL_realloc(c, len + 1); -#endif - if (str->data == NULL) { - str->data = c; - return 0; - } + /* Ensure copying a 0 length data field is defined. */ + if (alloc_len == 0) { + OPENSSL_free(str->data); + str->data = NULL; + str->length = 0; + return 1; } + + if ((size_t)str->length != alloc_len) { + uint8_t *c; + c = OPENSSL_realloc(str->length == 0 ? NULL : str->data, alloc_len); + if (c == NULL) + return 0; + str->data = c; + } + /* length never includes the added \0 byte */ str->length = (int)len; - if (data != NULL) { + + if (data != NULL && str->data != NULL) { memcpy(str->data, data, len); -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - /* Set the unused byte to something non NUL and printable. */ - if (len == 0) - str->data[len] = '~'; -#else - /* - * Add a NUL terminator. This should not be necessary - but we add it as - * a safety precaution - */ - str->data[len] = '\0'; -#endif + if (add_nul_byte) { + /* + * Add a '\0' terminator. This should not be necessary - but we add it as + * a safety precaution + */ + str->data[len] = '\0'; + } } ossl_asn1_bit_string_clear_unused_bits(str); return 1; } +#ifndef OPENSSL_NO_DEPRECATED_4_1 +int ASN1_STRING_set(ASN1_STRING *str, const void *_data, int len_in) +{ + return ossl_asn1_string_set_internal(str, (const uint8_t *)_data, len_in, + /*add_nul_byte=*/1); +} +#endif + void ASN1_STRING_set0(ASN1_STRING *str, void *data, int len) { if (!(str->flags & ASN1_STRING_FLAG_DATA_NOT_OWNED)) { @@ -365,6 +381,26 @@ void ASN1_STRING_set0(ASN1_STRING *str, void *data, int len) str->length = len; } +int ASN1_STRING_set_data(ASN1_STRING *str, const uint8_t *data, size_t len_in) +{ + if (str->type == V_ASN1_BIT_STRING) { + ERR_raise(ERR_LIB_ASN1, ASN1_R_ILLEGAL_BITSTRING_FORMAT); + return 0; + } + /* This will go away once ASN1_STRING can size_t internally */ + if (len_in > INT_MAX) { + ERR_raise(ERR_LIB_ASN1, ASN1_R_TOO_LARGE); + return 0; + } + return ossl_asn1_string_set_internal(str, data, (int)len_in, /*add_nul_byte=*/0); +} + +int ASN1_STRING_set_string(ASN1_STRING *str, const char *c_string) +{ + return ASN1_STRING_set_data(str, (const uint8_t *)c_string, + strlen(c_string)); +} + ASN1_STRING *ASN1_STRING_new(void) { return ASN1_STRING_type_new(V_ASN1_OCTET_STRING); @@ -469,10 +505,17 @@ int ASN1_STRING_cmp(const ASN1_STRING *a, const ASN1_STRING *b) } } +#ifndef OPENSSL_NO_DEPRECATED_4_1 int ASN1_STRING_length(const ASN1_STRING *x) { return x->length; } +#endif + +size_t ASN1_STRING_length_ex(const ASN1_STRING *x) +{ + return (size_t)x->length; +} #ifndef OPENSSL_NO_DEPRECATED_3_0 void ASN1_STRING_length_set(ASN1_STRING *x, int len) diff --git a/doc/man3/ASN1_STRING_length.pod b/doc/man3/ASN1_STRING_length.pod index 47cacb253a..5b047f084a 100644 --- a/doc/man3/ASN1_STRING_length.pod +++ b/doc/man3/ASN1_STRING_length.pod @@ -2,6 +2,7 @@ =head1 NAME +ASN1_STRING_set_data, ASN1_STRING_set_string, ASN1_STRING_length_ex, ASN1_STRING_dup, ASN1_STRING_cmp, ASN1_STRING_set, ASN1_STRING_length, ASN1_STRING_type, ASN1_STRING_get0_data, ASN1_STRING_to_UTF8 - ASN1_STRING utility functions @@ -10,19 +11,30 @@ ASN1_STRING_to_UTF8 - ASN1_STRING utility functions #include - int ASN1_STRING_length(ASN1_STRING *x); const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *x); ASN1_STRING *ASN1_STRING_dup(const ASN1_STRING *a); int ASN1_STRING_cmp(ASN1_STRING *a, ASN1_STRING *b); - int ASN1_STRING_set(ASN1_STRING *str, const void *data, int len); - int ASN1_STRING_type(const ASN1_STRING *x); int ASN1_STRING_to_UTF8(unsigned char **out, const ASN1_STRING *in); + int ASN1_STRING_set_data(ASN1_STRING *str, const uint8_t *data, size_t len); + + int ASN1_STRING_set_string(ASN1_STRING *str, const char *data); + + size_t ASN1_STRING_length_ex(const ASN1_STRING *x); + +The following functions have been deprecated since OpenSSL 4.1, and can be +hidden entirely by defining B with a suitable version value, +see L: + + int ASN1_STRING_set(ASN1_STRING *str, const void *data, int len); + + int ASN1_STRING_length(ASN1_STRING *x); + =head1 DESCRIPTION These functions allow an B structure to be manipulated. @@ -38,9 +50,25 @@ ASN1_STRING_dup() returns a copy of the structure I. ASN1_STRING_cmp() compares I and I returning 0 if the two are identical. The string types and content are compared. -ASN1_STRING_set() sets the data of string I to the buffer -I or length I. The supplied data is copied. If I -is -1 then the length is determined by strlen(data). +ASN1_STRING_set() allocates memory for string I to hold I +bytes of data. Any previously allocated memory owned by I will be +freed or re-used. If I is not NULL, I bytes are copied +from the memory pointed to by I to I. If I is -1 then +the length is determined by strlen(data). + +ASN1_STRING_set_data() allocates memory for string I to hold +I bytes of data. Any previously allocated memory owned by I +will be freed or re-used. If I is not NULL, I bytes are +copied from the memory pointed to by I to I. It is an error +to use this function on a string of type B. + +ASN1_STRING_set_string() allocates memory for the string I and makes +a copy of the characters from I. Any previously +allocated memory owned by I will be freed or re-used. I +must point to a valid NUL-terminated C string, and must not be +NULL. The terminating NUL byte is not included in the data copied into +I. It is an error to use this function on a string of type +B. ASN1_STRING_type() returns the type of I, using standard constants such as B. @@ -70,8 +98,9 @@ actual string type itself: for example for an IA5String the data will be ASCII, for a BMPString two bytes per character in big endian format, and for a UTF8String it will be in UTF8 format. -Similar care should be take to ensure the data is in the correct format -when calling ASN1_STRING_set(). +Similar care should be taken to ensure the data is in the correct +format when calling ASN1_STRING_set(), ASN1_STRING_set_data(), or +ASN1_STRING_set_string(). =head1 RETURN VALUES @@ -86,7 +115,8 @@ error occurred. ASN1_STRING_cmp() returns an integer greater than, equal to, or less than 0, according to whether I is greater than, equal to, or less than I. -ASN1_STRING_set() returns 1 on success or 0 on error. +ASN1_STRING_set(), ASN1_STRING_set_data(), and +ASN1_STRING_set_string() return 1 on success or 0 on error or failure. ASN1_STRING_type() returns the type of I. @@ -97,6 +127,11 @@ negative value if an error occurred. L +=head1 HISTORY + +ASN1_STRING_set_data(), ASN1_STRING_set_string(), and ASN1_STRING_length_ex() +were added in OpenSSL 4.1. + =head1 COPYRIGHT Copyright 2002-2020 The OpenSSL Project Authors. All Rights Reserved. diff --git a/include/openssl/asn1.h.in b/include/openssl/asn1.h.in index a7291738ff..440a6b3766 100644 --- a/include/openssl/asn1.h.in +++ b/include/openssl/asn1.h.in @@ -540,9 +540,16 @@ int ASN1_STRING_cmp(const ASN1_STRING *a, const ASN1_STRING *b); * Since this is used to store all sorts of things, via macros, for now, * make its data void * */ +#if !defined(OPENSSL_NO_DEPRECATED_4_1) +OSSL_DEPRECATEDIN_4_1_FOR(" Use ASN1_STRING_set_data() or ASN1_STRING_set_string() instead.") int ASN1_STRING_set(ASN1_STRING *str, const void *data, int len); -void ASN1_STRING_set0(ASN1_STRING *str, void *data, int len); +OSSL_DEPRECATEDIN_4_1_FOR(" Use ASN1_STRING_length_ex() instead.") int ASN1_STRING_length(const ASN1_STRING *x); +#endif /* !defined(OPENSSL_NO_DEPRECATED_4_1) */ +void ASN1_STRING_set0(ASN1_STRING *str, void *data, int len); +int ASN1_STRING_set_data(ASN1_STRING *str, const uint8_t *data, size_t len); +int ASN1_STRING_set_string(ASN1_STRING *str, const char *cstring); +size_t ASN1_STRING_length_ex(const ASN1_STRING *x); #ifndef OPENSSL_NO_DEPRECATED_3_0 OSSL_DEPRECATEDIN_3_0 void ASN1_STRING_length_set(ASN1_STRING *x, int n); #endif diff --git a/test/asn1_string_test.c b/test/asn1_string_test.c index 6edc7619f7..24d679444b 100644 --- a/test/asn1_string_test.c +++ b/test/asn1_string_test.c @@ -390,6 +390,7 @@ asn1_bit_string_set1_test(int idx) } static int + asn1_string_new_not_owned_test(void) { int success = 0; @@ -410,13 +411,13 @@ asn1_string_new_not_owned_test(void) if (!TEST_ptr(tmp = ASN1_STRING_new_not_owned(V_ASN1_OCTET_STRING, data, sizeof(data)))) goto err; - if (!TEST_true(ASN1_STRING_set(tmp, "muppet", (int)strlen("muppet")))) + if (!TEST_true(ASN1_STRING_set_string(tmp, "muppet"))) goto err; if (!TEST_mem_eq(data, sizeof(data), data2, sizeof(data2))) goto err; - if (!TEST_int_eq(ASN1_STRING_length(tmp), (int)strlen("muppet"))) + if (!TEST_size_t_eq(ASN1_STRING_length_ex(tmp), strlen("muppet"))) goto err; if (!TEST_mem_eq(ASN1_STRING_get0_data(tmp), strlen("muppet"), "muppet", strlen("muppet"))) @@ -436,13 +437,13 @@ asn1_string_new_not_owned_test(void) if (!TEST_mem_eq(data, sizeof(data), data2, sizeof(data2))) goto err; - if (!TEST_int_eq(ASN1_STRING_length(tmp), 4)) + if (!TEST_size_t_eq(ASN1_STRING_length_ex(tmp), 4)) goto err; if (!TEST_mem_eq(ASN1_STRING_get0_data(tmp), strlen("puppet"), "puppet", strlen("puppet"))) goto err; - memset((uint8_t *)ASN1_STRING_get0_data(tmp), 'z', ASN1_STRING_length(tmp)); + memset((uint8_t *)ASN1_STRING_get0_data(tmp), 'z', ASN1_STRING_length_ex(tmp)); if (!TEST_mem_eq(data, sizeof(data), data2, sizeof(data2))) goto err; @@ -474,10 +475,88 @@ err: return success; } +static int +asn1_string_set_data_test(void) +{ + int success = 0; + ASN1_STRING *str = NULL; + const uint8_t *data; + + if (!TEST_ptr(str = ASN1_STRING_new())) + goto err; + + if (!TEST_false(ASN1_STRING_set_data(str, (uint8_t *)"hoobla", -1))) + goto err; + + if (!TEST_false(ASN1_STRING_set_data(str, (uint8_t *)"hoobla", (size_t)INT_MAX + 1))) + goto err; + + if (!TEST_true(ASN1_STRING_set_data(str, NULL, 10))) + goto err; + + if (!TEST_true(ASN1_STRING_set_data(str, (uint8_t *)"hoobla", strlen("hoobla")))) + goto err; + + data = ASN1_STRING_get0_data(str); + + if (!TEST_size_t_eq(ASN1_STRING_length_ex(str), 6)) + goto err; + + if (!TEST_int_eq(memcmp("hoobla", data, strlen("hoobla")), 0)) + goto err; + + if (!TEST_true(ASN1_STRING_set_data(str, (uint8_t *)"hoobla", strlen("hoobla") + 1))) + goto err; + + data = ASN1_STRING_get0_data(str); + + if (!TEST_size_t_eq(ASN1_STRING_length_ex(str), 7)) + goto err; + + if (!TEST_int_eq(strcmp("hoobla", (char *)data), 0)) + goto err; + + success = 1; + +err: + ASN1_STRING_free(str); + return success; +} + +static int +asn1_string_set_string_test(void) +{ + int success = 0; + ASN1_STRING *str = NULL; + + if (!TEST_ptr(str = ASN1_STRING_new())) + goto err; + + if (!TEST_true(ASN1_STRING_set_string(str, "foo"))) + goto err; + + if (!TEST_size_t_eq(ASN1_STRING_length_ex(str), 3)) + goto err; + + if (!TEST_true(ASN1_STRING_set_string(str, "hoob\0la"))) + goto err; + + if (!TEST_size_t_eq(ASN1_STRING_length_ex(str), 4)) + goto err; + + success = 1; + +err: + ASN1_STRING_free(str); + return success; +} + int setup_tests(void) { ADD_ALL_TESTS(asn1_bit_string_get_length_test, OSSL_NELEM(abs_get_length_tests)); ADD_ALL_TESTS(asn1_bit_string_set1_test, OSSL_NELEM(abs_set1_tests)); ADD_TEST(asn1_string_new_not_owned_test); + ADD_TEST(asn1_string_set_data_test); + ADD_TEST(asn1_string_set_string_test); return 1; } diff --git a/util/libcrypto.num b/util/libcrypto.num index a51b72cf93..c7b7777cc4 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -2571,9 +2571,9 @@ ASN1_STRING_copy 2569 4_0_0 EXIST::FUNCTION: ASN1_STRING_dup 2570 4_0_0 EXIST::FUNCTION: ASN1_STRING_type_new 2571 4_0_0 EXIST::FUNCTION: ASN1_STRING_cmp 2572 4_0_0 EXIST::FUNCTION: -ASN1_STRING_set 2573 4_0_0 EXIST::FUNCTION: +ASN1_STRING_set 2573 4_0_0 EXIST::FUNCTION:DEPRECATEDIN_4_1 ASN1_STRING_set0 2574 4_0_0 EXIST::FUNCTION: -ASN1_STRING_length 2575 4_0_0 EXIST::FUNCTION: +ASN1_STRING_length 2575 4_0_0 EXIST::FUNCTION:DEPRECATEDIN_4_1 ASN1_STRING_length_set 2576 4_0_0 EXIST::FUNCTION:DEPRECATEDIN_3_0 ASN1_STRING_type 2577 4_0_0 EXIST::FUNCTION: ASN1_STRING_get0_data 2578 4_0_0 EXIST::FUNCTION: @@ -5724,3 +5724,6 @@ OPENSSL_sk_set_copy_thunks ? 4_1_0 EXIST::FUNCTION: ASN1_STRING_new_not_owned ? 4_1_0 EXIST::FUNCTION: EVP_KDF_CTX_get0_kdf ? 4_1_0 EXIST::FUNCTION: EVP_KDF_CTX_get1_kdf ? 4_1_0 EXIST::FUNCTION: +ASN1_STRING_set_data ? 4_1_0 EXIST::FUNCTION: +ASN1_STRING_set_string ? 4_1_0 EXIST::FUNCTION: +ASN1_STRING_length_ex ? 4_1_0 EXIST::FUNCTION: From 8d9fb09b9e7457346cd619779609c59ed0ee7ad9 Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Thu, 14 May 2026 17:50:49 -0600 Subject: [PATCH 278/349] pass length in cmp_msg stuff ossl_cmp_sk_ASN1_UTF8STRING_push_str depended under the covers on passing a -1 to have strlen magically called in the setter. Don't depend on this and simply pass in the correct length Reviewed-by: Milan Broz Reviewed-by: Norbert Pocs MergeDate: Sat Jul 18 13:01:17 2026 (Merged from https://github.com/openssl/openssl/pull/31194) --- crypto/cmp/cmp_local.h | 2 +- crypto/cmp/cmp_msg.c | 6 +++--- crypto/cmp/cmp_util.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crypto/cmp/cmp_local.h b/crypto/cmp/cmp_local.h index e664a6ae22..ac892f62bd 100644 --- a/crypto/cmp/cmp_local.h +++ b/crypto/cmp/cmp_local.h @@ -819,7 +819,7 @@ int ossl_cmp_X509_STORE_add1_certs(X509_STORE *store, STACK_OF(X509) *certs, int only_self_issued); STACK_OF(X509) *ossl_cmp_X509_STORE_get1_certs(X509_STORE *store); int ossl_cmp_sk_ASN1_UTF8STRING_push_str(STACK_OF(ASN1_UTF8STRING) *sk, - const char *text, int len); + const char *text, size_t len); int ossl_cmp_asn1_octet_string_set1(ASN1_OCTET_STRING **tgt, const ASN1_OCTET_STRING *src); int ossl_cmp_asn1_octet_string_set1_bytes(ASN1_OCTET_STRING **tgt, diff --git a/crypto/cmp/cmp_msg.c b/crypto/cmp/cmp_msg.c index fec747458d..abea670ce9 100644 --- a/crypto/cmp/cmp_msg.c +++ b/crypto/cmp/cmp_msg.c @@ -824,13 +824,13 @@ OSSL_CMP_MSG *ossl_cmp_error_new(OSSL_CMP_CTX *ctx, const OSSL_CMP_PKISI *si, goto err; msg->body->value.error->errorDetails = ft; if (lib != NULL && *lib != '\0' - && !ossl_cmp_sk_ASN1_UTF8STRING_push_str(ft, lib, -1)) + && !ossl_cmp_sk_ASN1_UTF8STRING_push_str(ft, lib, strlen(lib))) goto err; if (reason != NULL && *reason != '\0' - && !ossl_cmp_sk_ASN1_UTF8STRING_push_str(ft, reason, -1)) + && !ossl_cmp_sk_ASN1_UTF8STRING_push_str(ft, reason, strlen(reason))) goto err; if (details != NULL - && !ossl_cmp_sk_ASN1_UTF8STRING_push_str(ft, details, -1)) + && !ossl_cmp_sk_ASN1_UTF8STRING_push_str(ft, details, strlen(details))) goto err; } diff --git a/crypto/cmp/cmp_util.c b/crypto/cmp/cmp_util.c index 5c710addf2..c658529c7e 100644 --- a/crypto/cmp/cmp_util.c +++ b/crypto/cmp/cmp_util.c @@ -219,7 +219,7 @@ int ossl_cmp_X509_STORE_add1_certs(X509_STORE *store, STACK_OF(X509) *certs, } int ossl_cmp_sk_ASN1_UTF8STRING_push_str(STACK_OF(ASN1_UTF8STRING) *sk, - const char *text, int len) + const char *text, size_t len) { ASN1_UTF8STRING *utf8string; From 0161fe45d1fce2247b0fe74b3e2524703cf337c5 Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Fri, 15 May 2026 12:51:29 -0600 Subject: [PATCH 279/349] Add a CHANGES.md entry Reviewed-by: Milan Broz Reviewed-by: Norbert Pocs MergeDate: Sat Jul 18 13:01:18 2026 (Merged from https://github.com/openssl/openssl/pull/31194) --- CHANGES.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index da1bfd15ec..6a262083a2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -131,6 +131,14 @@ OpenSSL Releases *Bob Beck* + * `ASN1_STRING_set()` and `ASN1_STRING_length()` have been + deprecated. The replacement functions `ASN1_STRING_set_data()` or + `ASN1_STRING_set_string()`, and `ASN1_STRING_length_ex()` should be + used in their place. This prepares the ASN1_STRING type to support + modern size_t length values in the future. + + *Bob Beck* + * `EVP_CIPHER_CTX_get_num()` and `EVP_CIPHER_CTX_set_num()' have been deprecated. Refer to ossl-migration-guide(7) for more info. From 9044e5f42556e5a3bdf49226406144d806123697 Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Mon, 11 May 2026 11:15:02 -0600 Subject: [PATCH 280/349] Convert internal use of ASN1_STRING_set and ASN1_STRING_length to use their non-deprecated replacements. For "pretty-printing" applications and test code that use a length and require an int, size_t lengths are simply cast to an int. For writes and protocol level things size_t lengths are checked against INT_MAX and take the error path if the returned value is too large to be used for a funciton that takes an integer length. Reviewed-by: Milan Broz Reviewed-by: Norbert Pocs MergeDate: Sat Jul 18 13:01:19 2026 (Merged from https://github.com/openssl/openssl/pull/31194) --- apps/asn1parse.c | 18 +++++++++++------ apps/ca.c | 36 ++++++++++++++++++---------------- apps/cmp.c | 4 ++-- apps/cms.c | 8 +++++--- apps/lib/apps.c | 6 +++--- apps/lib/cmp_mock_srv.c | 4 ++-- apps/pkcs12.c | 16 +++++++-------- apps/s_client.c | 10 +++++++++- apps/spkac.c | 4 ++-- apps/ts.c | 2 +- crypto/asn1/a_bitstr.c | 5 +++-- crypto/asn1/a_int.c | 8 ++++---- crypto/asn1/a_mbstr.c | 2 +- crypto/asn1/a_time.c | 2 +- crypto/asn1/asn1_gen.c | 4 ++-- crypto/asn1/asn1_lib.c | 4 ++-- crypto/asn1/asn1_local.h | 2 ++ crypto/asn1/evp_asn1.c | 19 ++++++++++++++++-- crypto/asn1/p5_scrypt.c | 2 +- crypto/asn1/p8_pkey.c | 4 +++- crypto/asn1/tasn_dec.c | 2 +- crypto/cmp/cmp_protect.c | 10 +++++++--- crypto/cmp/cmp_status.c | 4 ++-- crypto/cmp/cmp_util.c | 2 +- crypto/cms/cms_dd.c | 2 +- crypto/cms/cms_dh.c | 41 +++++++++++++++++++++++++-------------- crypto/cms/cms_ec.c | 23 +++++++++++++--------- crypto/cms/cms_env.c | 7 ++++++- crypto/cms/cms_ess.c | 2 +- crypto/cms/cms_kemri.c | 2 +- crypto/cms/cms_rsa.c | 8 +++++--- crypto/cms/cms_sd.c | 4 ++-- crypto/ct/ct_oct.c | 2 +- crypto/ec/ec_asn1.c | 15 ++++++++------ crypto/ec/ecx_backend.c | 9 +++++++-- crypto/ocsp/ocsp_ext.c | 4 ++-- crypto/ocsp/ocsp_srv.c | 2 +- crypto/pkcs12/p12_mutl.c | 2 +- crypto/pkcs7/pk7_attr.c | 11 +++++++---- crypto/pkcs7/pk7_doit.c | 33 +++++++++++++++++++------------ crypto/pkcs7/pk7_smime.c | 10 ++++++++-- crypto/sm2/sm2_crypt.c | 20 +++++++++++-------- crypto/ts/ts_asn1.c | 8 +++++++- crypto/ts/ts_lib.c | 2 +- crypto/ts/ts_rsp_sign.c | 8 ++++---- crypto/ts/ts_rsp_verify.c | 19 +++++++++++++++--- crypto/ts/ts_verify_ctx.c | 8 ++++++-- crypto/x509/t_x509.c | 2 +- crypto/x509/v3_addr.c | 25 ++++++++++++++++-------- crypto/x509/v3_akid.c | 2 +- crypto/x509/v3_cpols.c | 8 +++----- crypto/x509/v3_ia5.c | 2 +- crypto/x509/v3_ist.c | 8 ++++---- crypto/x509/v3_lib.c | 10 ++++++---- crypto/x509/v3_prn.c | 14 +++++++------ crypto/x509/v3_san.c | 3 ++- crypto/x509/v3_utf8.c | 2 +- crypto/x509/x509_att.c | 2 +- crypto/x509/x509name.c | 9 ++++++--- crypto/x509/x_x509a.c | 26 +++++++++++++++++++++++-- ssl/statem/statem_srvr.c | 8 ++++++-- test/asn1_string_test.c | 1 - test/cmp_hdr_test.c | 4 ++-- test/helpers/pkcs12.c | 6 +++--- test/tls-provider.c | 2 +- test/v3nametest.c | 2 +- test/x509_internal_test.c | 4 ++-- 67 files changed, 359 insertions(+), 201 deletions(-) diff --git a/apps/asn1parse.c b/apps/asn1parse.c index ed5ebd076f..b8449762fe 100644 --- a/apps/asn1parse.c +++ b/apps/asn1parse.c @@ -82,7 +82,8 @@ int asn1parse_main(int argc, char **argv) const unsigned char *ctmpbuf; int indent = 0, noout = 0, dump = 0, informat = FORMAT_PEM; int offset = 0, ret = 1, i, j; - long num, tmplen; + long num; + size_t tmplen; const unsigned char *tmpbuf; unsigned int length = 0; OPTION_CHOICE o; @@ -241,12 +242,12 @@ int asn1parse_main(int argc, char **argv) if (sk_OPENSSL_STRING_num(osk)) { tmpbuf = str; - tmplen = num; + tmplen = (size_t)num; for (i = 0; i < sk_OPENSSL_STRING_num(osk); i++) { ASN1_TYPE *atmp; int typ; j = strtol(sk_OPENSSL_STRING_value(osk, i), NULL, 0); - if (j <= 0 || j >= tmplen) { + if (j <= 0 || (size_t)j >= tmplen) { BIO_printf(bio_err, "'%s' is out of range\n", sk_OPENSSL_STRING_value(osk, i)); continue; @@ -255,7 +256,7 @@ int asn1parse_main(int argc, char **argv) tmplen -= j; atmp = at; ctmpbuf = tmpbuf; - at = d2i_ASN1_TYPE(NULL, &ctmpbuf, tmplen); + at = d2i_ASN1_TYPE(NULL, &ctmpbuf, (long)tmplen); ASN1_TYPE_free(atmp); if (!at) { BIO_puts(bio_err, "Error parsing structure\n"); @@ -272,11 +273,16 @@ int asn1parse_main(int argc, char **argv) } /* hmm... this is a little evil but it works */ tmpbuf = ASN1_STRING_get0_data(at->value.asn1_string); - tmplen = ASN1_STRING_length(at->value.asn1_string); + tmplen = ASN1_STRING_length_ex(at->value.asn1_string); + if (tmplen > INT_MAX) { + BIO_puts(bio_err, "ASN.1 string length exceeds INT_MAX\n"); + ERR_print_errors(bio_err); + goto end; + } } /* XXX casts away const */ str = (unsigned char *)tmpbuf; - num = tmplen; + num = (int)tmplen; } if (offset < 0 || offset >= num) { diff --git a/apps/ca.c b/apps/ca.c index c0a58f4d16..8f98bc9a6a 100644 --- a/apps/ca.c +++ b/apps/ca.c @@ -1077,8 +1077,8 @@ end_of_options: X509 *xi = sk_X509_value(cert_sk, i); const ASN1_INTEGER *serialNumber = X509_get0_serialNumber(xi); const unsigned char *psn = ASN1_STRING_get0_data(serialNumber); - const int snl = ASN1_STRING_length(serialNumber); - const int filen_len = 2 * (snl > 0 ? snl : 1) + sizeof(".pem"); + const size_t snl = ASN1_STRING_length_ex(serialNumber); + const size_t filen_len = 2 * (snl > 0 ? snl : 1) + sizeof(".pem"); char *n = new_cert + outdirlen; if (outdirlen + filen_len > PATH_MAX) { @@ -1089,7 +1089,7 @@ end_of_options: if (snl > 0) { static const char HEX_DIGITS[] = "0123456789ABCDEF"; - for (j = 0; j < snl; j++, psn++) { + for (j = 0; (size_t)j < snl; j++, psn++) { *n++ = HEX_DIGITS[*psn >> 4]; *n++ = HEX_DIGITS[*psn & 0x0F]; } @@ -1523,8 +1523,10 @@ static int do_body(X509 **xret, EVP_PKEY *pkey, X509 *x509, goto end; } if (type != V_ASN1_BMPSTRING && type != V_ASN1_UTF8STRING) { - j = ASN1_PRINTABLE_type(ASN1_STRING_get0_data(str), - ASN1_STRING_length(str)); + size_t tmp = ASN1_STRING_length_ex(str); + if (tmp > INT_MAX) + goto end; + j = ASN1_PRINTABLE_type(ASN1_STRING_get0_data(str), (int)tmp); if ((j == V_ASN1_T61STRING && type != V_ASN1_T61STRING) || (j == V_ASN1_IA5STRING && type == V_ASN1_PRINTABLESTRING)) { BIO_puts(bio_err, @@ -1901,9 +1903,9 @@ static int do_body(X509 **xret, EVP_PKEY *pkey, X509 *x509, /* We now just add it to the database as DB_TYPE_VAL('V') */ row[DB_type] = OPENSSL_strdup("V"); tm = X509_get0_notAfter(ret); - row[DB_exp_date] = app_malloc(ASN1_STRING_length(tm) + 1, "row expdate"); - memcpy(row[DB_exp_date], ASN1_STRING_get0_data(tm), ASN1_STRING_length(tm)); - row[DB_exp_date][ASN1_STRING_length(tm)] = '\0'; + row[DB_exp_date] = app_malloc(ASN1_STRING_length_ex(tm) + 1, "row expdate"); + memcpy(row[DB_exp_date], ASN1_STRING_get0_data(tm), ASN1_STRING_length_ex(tm)); + row[DB_exp_date][ASN1_STRING_length_ex(tm)] = '\0'; row[DB_rev_date] = NULL; row[DB_file] = OPENSSL_strdup("unknown"); if ((row[DB_type] == NULL) || (row[DB_file] == NULL) @@ -2137,9 +2139,9 @@ static int do_revoke(X509 *x509, CA_DB *db, REVINFO_TYPE rev_type, /* We now just add it to the database as DB_TYPE_REV('V') */ row[DB_type] = OPENSSL_strdup("V"); tm = X509_get0_notAfter(x509); - row[DB_exp_date] = app_malloc(ASN1_STRING_length(tm) + 1, "row exp_data"); - memcpy(row[DB_exp_date], ASN1_STRING_get0_data(tm), ASN1_STRING_length(tm)); - row[DB_exp_date][ASN1_STRING_length(tm)] = '\0'; + row[DB_exp_date] = app_malloc(ASN1_STRING_length_ex(tm) + 1, "row exp_data"); + memcpy(row[DB_exp_date], ASN1_STRING_get0_data(tm), ASN1_STRING_length_ex(tm)); + row[DB_exp_date][ASN1_STRING_length_ex(tm)] = '\0'; row[DB_rev_date] = NULL; row[DB_file] = OPENSSL_strdup("unknown"); @@ -2350,7 +2352,7 @@ static char *make_revocation_str(REVINFO_TYPE rev_type, const char *rev_arg) const char *reason = NULL, *other = NULL; ASN1_OBJECT *otmp; ASN1_UTCTIME *revtm = NULL; - int i; + size_t i; switch (rev_type) { case REV_NONE: @@ -2407,12 +2409,12 @@ static char *make_revocation_str(REVINFO_TYPE rev_type, const char *rev_arg) if (!revtm) return NULL; - i = ASN1_STRING_length(revtm) + 1; + i = ASN1_STRING_length_ex(revtm) + 1; if (reason) - i += (int)(strlen(reason) + 1); + i += strlen(reason) + 1; if (other) - i += (int)(strlen(other) + 1); + i += strlen(other) + 1; str = app_malloc(i, "revocation reason"); OPENSSL_strlcpy(str, (const char *)ASN1_STRING_get0_data(revtm), i); @@ -2492,7 +2494,7 @@ static int old_entry_print(const ASN1_OBJECT *obj, const ASN1_STRING *str) { char buf[25], *pbuf; const char *p; - int j; + size_t j; j = i2a_ASN1_OBJECT(bio_err, obj); pbuf = buf; @@ -2514,7 +2516,7 @@ static int old_entry_print(const ASN1_OBJECT *obj, const ASN1_STRING *str) BIO_printf(bio_err, "ASN.1 %2d:'", ASN1_STRING_type(str)); p = (const char *)ASN1_STRING_get0_data(str); - for (j = ASN1_STRING_length(str); j > 0; j--) { + for (j = ASN1_STRING_length_ex(str); j > 0; j--) { if ((*p >= ' ') && (*p <= '~')) BIO_printf(bio_err, "%c", *p); else if (*p & 0x80) diff --git a/apps/cmp.c b/apps/cmp.c index a0770dcb97..abe6de5cb9 100644 --- a/apps/cmp.c +++ b/apps/cmp.c @@ -2140,7 +2140,7 @@ static int add_certProfile(OSSL_CMP_CTX *ctx, const char *name) return 0; if ((utf8string = ASN1_UTF8STRING_new()) == NULL) goto err; - if (!ASN1_STRING_set(utf8string, name, (int)strlen(name))) { + if (!ASN1_STRING_set_string(utf8string, name)) { ASN1_STRING_free(utf8string); goto err; } @@ -2215,7 +2215,7 @@ static int handle_opt_geninfo(OSSL_CMP_CTX *ctx) else *end++ = '\0'; if ((text = ASN1_UTF8STRING_new()) == NULL - || !ASN1_STRING_set(text, ptr, -1)) + || !ASN1_STRING_set_string(text, ptr)) goto oom; ptr = end; ASN1_TYPE_set(type, V_ASN1_UTF8STRING, text); diff --git a/apps/cms.c b/apps/cms.c index 46f9b3b11e..5e32ab55af 100644 --- a/apps/cms.c +++ b/apps/cms.c @@ -1580,13 +1580,15 @@ static void receipt_request_print(CMS_ContentInfo *cms) ERR_print_errors(bio_err); } else { const char *id; - int idlen; + size_t idlen; CMS_ReceiptRequest_get0_values(rr, &scid, &allorfirst, &rlist, &rto); BIO_puts(bio_err, " Signed Content ID:\n"); - idlen = ASN1_STRING_length(scid); + idlen = ASN1_STRING_length_ex(scid); + if (idlen > INT_MAX) + idlen = INT_MAX; id = (const char *)ASN1_STRING_get0_data(scid); - BIO_dump_indent(bio_err, id, idlen, 4); + BIO_dump_indent(bio_err, id, (int)idlen, 4); BIO_puts(bio_err, " Receipts From"); if (rlist != NULL) { BIO_puts(bio_err, " List:\n"); diff --git a/apps/lib/apps.c b/apps/lib/apps.c index 36fcb20e2a..b44e4b2bef 100644 --- a/apps/lib/apps.c +++ b/apps/lib/apps.c @@ -2833,7 +2833,7 @@ static const char *get_dp_url(DIST_POINT *dp) for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { gen = sk_GENERAL_NAME_value(gens, i); uri = GENERAL_NAME_get0_value(gen, >ype); - if (gtype == GEN_URI && ASN1_STRING_length(uri) > 6) { + if (gtype == GEN_URI && ASN1_STRING_length_ex(uri) > 6) { const char *uptr = (const char *)ASN1_STRING_get0_data(uri); if (IS_HTTP(uptr)) /* can/should not use HTTPS here */ @@ -3722,7 +3722,7 @@ int has_stdin_waiting(void) int corrupt_signature(ASN1_STRING *signature) { const unsigned char *valid = ASN1_STRING_get0_data(signature); - int length = ASN1_STRING_length(signature); + size_t length = ASN1_STRING_length_ex(signature); unsigned char *s = OPENSSL_memdup(valid, length); if (s == NULL) @@ -3730,7 +3730,7 @@ int corrupt_signature(ASN1_STRING *signature) s[length - 1] ^= 0x1; - ASN1_STRING_set0(signature, s, length); + ASN1_STRING_set0(signature, s, (int)length); return 1; } diff --git a/apps/lib/cmp_mock_srv.c b/apps/lib/cmp_mock_srv.c index 43cf6af314..825a2b6709 100644 --- a/apps/lib/cmp_mock_srv.c +++ b/apps/lib/cmp_mock_srv.c @@ -345,7 +345,7 @@ static OSSL_CMP_PKISI *process_cert_request(OSSL_CMP_SRV_CTX *srv_ctx, STACK_OF(ASN1_UTF8STRING) *strs; ASN1_UTF8STRING *str; const char *data; - int len; + size_t len; if (OBJ_obj2nid(obj) == NID_id_it_certProfile) { if (!OSSL_CMP_ITAV_get0_certProfile(itav, &strs)) @@ -360,7 +360,7 @@ static OSSL_CMP_PKISI *process_cert_request(OSSL_CMP_SRV_CTX *srv_ctx, ERR_raise(ERR_LIB_CMP, ERR_R_PASSED_INVALID_ARGUMENT); return NULL; } - if (((len = ASN1_STRING_length(str)) != (int)sizeof("profile1") - 1) + if (((len = ASN1_STRING_length_ex(str)) != sizeof("profile1") - 1) || memcmp(data, "profile1", len) != 0) { ERR_raise(ERR_LIB_CMP, CMP_R_UNEXPECTED_CERTPROFILE); return NULL; diff --git a/apps/pkcs12.c b/apps/pkcs12.c index f817999562..e37aefc7a3 100644 --- a/apps/pkcs12.c +++ b/apps/pkcs12.c @@ -833,7 +833,7 @@ int pkcs12_main(int argc, char **argv) ASN1_INTEGER_get(pbkdf2_param->iter)); BIO_printf(bio_err, "Key length: %ld, Salt length: %d\n", ASN1_INTEGER_get(pbkdf2_param->keylength), - ASN1_STRING_length(pbkdf2_param->salt->value.octet_string)); + (int)ASN1_STRING_length_ex(pbkdf2_param->salt->value.octet_string)); if (pbkdf2_param->prf == NULL) { prfnid = NID_hmacWithSHA1; } else { @@ -847,8 +847,8 @@ int pkcs12_main(int argc, char **argv) BIO_printf(bio_err, ", Iteration %ld\n", tmaciter != NULL ? ASN1_INTEGER_get(tmaciter) : 1L); BIO_printf(bio_err, "MAC length: %ld, salt length: %ld\n", - tmac != NULL ? ASN1_STRING_length(tmac) : 0L, - tsalt != NULL ? ASN1_STRING_length(tsalt) : 0L); + tmac != NULL ? (long)ASN1_STRING_length_ex(tmac) : 0L, + tsalt != NULL ? (long)ASN1_STRING_length_ex(tsalt) : 0L); } } @@ -1231,7 +1231,7 @@ static int alg_print(const X509_ALGOR *alg) } BIO_printf(bio_err, ", Salt length: %d, Cost(N): %ld, " "Block size(r): %ld, Parallelism(p): %ld", - ASN1_STRING_length(kdf->salt), + (int)ASN1_STRING_length_ex(kdf->salt), ASN1_INTEGER_get(kdf->costParameter), ASN1_INTEGER_get(kdf->blockSize), ASN1_INTEGER_get(kdf->parallelizationParameter)); @@ -1282,25 +1282,25 @@ void print_attribute(BIO *out, const ASN1_TYPE *av) switch (av->type) { case V_ASN1_BMPSTRING: value = OPENSSL_uni2asc(ASN1_STRING_get0_data(av->value.bmpstring), - ASN1_STRING_length(av->value.bmpstring)); + (int)ASN1_STRING_length_ex(av->value.bmpstring)); BIO_printf(out, "%s\n", value); OPENSSL_free(value); break; case V_ASN1_UTF8STRING: - BIO_printf(out, "%.*s\n", ASN1_STRING_length(av->value.utf8string), + BIO_printf(out, "%.*s\n", (int)ASN1_STRING_length_ex(av->value.utf8string), ASN1_STRING_get0_data(av->value.utf8string)); break; case V_ASN1_OCTET_STRING: hex_print(out, ASN1_STRING_get0_data(av->value.octet_string), - ASN1_STRING_length(av->value.octet_string)); + (int)ASN1_STRING_length_ex(av->value.octet_string)); BIO_puts(out, "\n"); break; case V_ASN1_BIT_STRING: hex_print(out, ASN1_STRING_get0_data(av->value.bit_string), - ASN1_STRING_length(av->value.bit_string)); + (int)ASN1_STRING_length_ex(av->value.bit_string)); BIO_puts(out, "\n"); break; diff --git a/apps/s_client.c b/apps/s_client.c index d247cd836d..65aff3eb42 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -3049,6 +3049,7 @@ re_start: ASN1_TYPE *atyp = NULL; BIO *ldapbio = BIO_new(BIO_s_mem()); CONF *cnf = NCONF_new(NULL); + size_t ssl_request_len; if (ldapbio == NULL || cnf == NULL) { BIO_free(ldapbio); @@ -3081,11 +3082,18 @@ re_start: BIO_puts(bio_err, "ASN1_generate_nconf failed\n"); goto end; } + ssl_request_len = ASN1_STRING_length_ex(atyp->value.sequence); + if (ssl_request_len > INT_MAX) { + NCONF_free(cnf); + ASN1_TYPE_free(atyp); + BIO_puts(bio_err, "generated NCONF size is too large\n"); + goto end; + } NCONF_free(cnf); /* Send SSLRequest packet */ BIO_write(sbio, ASN1_STRING_get0_data(atyp->value.sequence), - ASN1_STRING_length(atyp->value.sequence)); + (int)ssl_request_len); (void)BIO_flush(sbio); ASN1_TYPE_free(atyp); diff --git a/apps/spkac.c b/apps/spkac.c index bcf5626277..27b2392bb9 100644 --- a/apps/spkac.c +++ b/apps/spkac.c @@ -155,8 +155,8 @@ int spkac_main(int argc, char **argv) if (spki == NULL) goto end; if (challenge != NULL - && !ASN1_STRING_set(spki->spkac->challenge, - challenge, (int)strlen(challenge))) + && !ASN1_STRING_set_string(spki->spkac->challenge, + challenge)) goto end; if (!NETSCAPE_SPKI_set_pubkey(spki, pkey)) { BIO_puts(bio_err, "Error setting public key\n"); diff --git a/apps/ts.c b/apps/ts.c index aaec526154..2049eb9331 100644 --- a/apps/ts.c +++ b/apps/ts.c @@ -583,7 +583,7 @@ static ASN1_INTEGER *create_nonce(int bits) if ((nonce = ASN1_INTEGER_new()) == NULL) goto err; - if (!ASN1_STRING_set(nonce, buf, len)) + if (!ASN1_STRING_set_data(nonce, buf, len)) goto err; ret = nonce; diff --git a/crypto/asn1/a_bitstr.c b/crypto/asn1/a_bitstr.c index 914ff98400..fdefb80e26 100644 --- a/crypto/asn1/a_bitstr.c +++ b/crypto/asn1/a_bitstr.c @@ -18,7 +18,7 @@ #ifndef OPENSSL_NO_DEPRECATED_4_1 int ASN1_BIT_STRING_set(ASN1_BIT_STRING *x, unsigned char *d, int len) { - return ASN1_STRING_set(x, d, len); + return ossl_asn1_string_set_internal(x, d, len, /*add_nul_byte=*/0); } #endif @@ -263,8 +263,9 @@ int ASN1_BIT_STRING_set1(ASN1_BIT_STRING *abs, const uint8_t *data, size_t lengt if (length > 0 && (data[length - 1] & ((1 << unused_bits) - 1)) != 0) return 0; - if (!ASN1_STRING_set(abs, data, (int)length)) + if (!ossl_asn1_string_set_internal(abs, data, (int)length, /*add_nul_byte=*/0)) return 0; + abs->type = V_ASN1_BIT_STRING; ossl_asn1_bit_string_set_unused_bits(abs, unused_bits); diff --git a/crypto/asn1/a_int.c b/crypto/asn1/a_int.c index 3b10ee99e5..10db6b6a3c 100644 --- a/crypto/asn1/a_int.c +++ b/crypto/asn1/a_int.c @@ -316,7 +316,7 @@ ASN1_INTEGER *ossl_c2i_ASN1_INTEGER(ASN1_INTEGER **a, const unsigned char **pp, } else ret = *a; - if (r > INT_MAX || ASN1_STRING_set(ret, NULL, (int)r) == 0) { + if (ASN1_STRING_set_data(ret, NULL, r) == 0) { ERR_raise(ERR_LIB_ASN1, ERR_R_ASN1_LIB); goto err; } @@ -371,7 +371,7 @@ static int asn1_string_set_int64(ASN1_STRING *a, int64_t r, int itype) off = asn1_put_uint64(tbuf, r); a->type &= ~V_ASN1_NEG; } - return ASN1_STRING_set(a, tbuf + off, (int)(sizeof(tbuf) - off)); + return ASN1_STRING_set_data(a, tbuf + off, (sizeof(tbuf) - off)); } static int asn1_string_get_uint64(uint64_t *pr, const ASN1_STRING *a, @@ -399,7 +399,7 @@ static int asn1_string_set_uint64(ASN1_STRING *a, uint64_t r, int itype) a->type = itype; off = asn1_put_uint64(tbuf, r); - return ASN1_STRING_set(a, tbuf + off, (int)(sizeof(tbuf) - off)); + return ASN1_STRING_set_data(a, tbuf + off, (sizeof(tbuf) - off)); } /* @@ -503,7 +503,7 @@ static ASN1_STRING *bn_to_asn1_string(const BIGNUM *bn, ASN1_STRING *ai, if (len == 0) len = 1; - if (ASN1_STRING_set(ret, NULL, len) == 0) { + if (ASN1_STRING_set_data(ret, NULL, len) == 0) { ERR_raise(ERR_LIB_ASN1, ERR_R_ASN1_LIB); goto err; } diff --git a/crypto/asn1/a_mbstr.c b/crypto/asn1/a_mbstr.c index 9329472e9b..e70fe92db5 100644 --- a/crypto/asn1/a_mbstr.c +++ b/crypto/asn1/a_mbstr.c @@ -171,7 +171,7 @@ int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len, } /* If both the same type just copy across */ if (inform == outform) { - if (!ASN1_STRING_set(dest, in, len)) { + if (!ASN1_STRING_set_data(dest, in, len)) { if (free_out) { ASN1_STRING_free(dest); *out = NULL; diff --git a/crypto/asn1/a_time.c b/crypto/asn1/a_time.c index 56366fe531..341bd5142d 100644 --- a/crypto/asn1/a_time.c +++ b/crypto/asn1/a_time.c @@ -271,7 +271,7 @@ ASN1_TIME *ossl_asn1_time_from_tm(ASN1_TIME *s, struct tm *ts, int type) if (tmps == NULL) return NULL; - if (!ASN1_STRING_set(tmps, NULL, len)) + if (!ASN1_STRING_set_data(tmps, NULL, len)) goto err; tmps->type = type; diff --git a/crypto/asn1/asn1_gen.c b/crypto/asn1/asn1_gen.c index 35abf85ca4..6fbf581b49 100644 --- a/crypto/asn1/asn1_gen.c +++ b/crypto/asn1/asn1_gen.c @@ -651,7 +651,7 @@ static ASN1_TYPE *asn1_str2type(const char *str, int format, int utype) ERR_raise(ERR_LIB_ASN1, ERR_R_ASN1_LIB); goto bad_str; } - if (!ASN1_STRING_set(atmp->value.asn1_string, str, -1)) { + if (!ASN1_STRING_set_string(atmp->value.asn1_string, str)) { ERR_raise(ERR_LIB_ASN1, ERR_R_ASN1_LIB); goto bad_str; } @@ -706,7 +706,7 @@ static ASN1_TYPE *asn1_str2type(const char *str, int format, int utype) atmp->value.asn1_string->length = rdlen; atmp->value.asn1_string->type = utype; } else if (format == ASN1_GEN_FORMAT_ASCII) { - if (!ASN1_STRING_set(atmp->value.asn1_string, str, -1)) { + if (!ASN1_STRING_set_string(atmp->value.asn1_string, str)) { ERR_raise(ERR_LIB_ASN1, ERR_R_ASN1_LIB); goto bad_str; } diff --git a/crypto/asn1/asn1_lib.c b/crypto/asn1/asn1_lib.c index 2bde572eaf..4b9e720bad 100644 --- a/crypto/asn1/asn1_lib.c +++ b/crypto/asn1/asn1_lib.c @@ -552,7 +552,7 @@ char *ossl_sk_ASN1_UTF8STRING2text(STACK_OF(ASN1_UTF8STRING) *text, current = sk_ASN1_UTF8STRING_value(text, i); if (i > 0) length += sep_len; - length += ASN1_STRING_length(current); + length += ASN1_STRING_length_ex(current); if (max_len != 0 && length > max_len) return NULL; } @@ -562,7 +562,7 @@ char *ossl_sk_ASN1_UTF8STRING2text(STACK_OF(ASN1_UTF8STRING) *text, p = result; for (i = 0; i < sk_ASN1_UTF8STRING_num(text); i++) { current = sk_ASN1_UTF8STRING_value(text, i); - length = ASN1_STRING_length(current); + length = ASN1_STRING_length_ex(current); if (i > 0 && sep_len > 0) { strncpy(p, sep, sep_len + 1); /* using + 1 to silence gcc warning */ p += sep_len; diff --git a/crypto/asn1/asn1_local.h b/crypto/asn1/asn1_local.h index 0fd1d0a842..6f3ee7983c 100644 --- a/crypto/asn1/asn1_local.h +++ b/crypto/asn1/asn1_local.h @@ -102,5 +102,7 @@ int ossl_asn1_time_time_t_to_tm(const time_t *time, struct tm *out_tm); int ossl_asn1_time_tm_to_time_t(const struct tm *tm, time_t *out); int ossl_asn1_call_aux_cb(const ASN1_AUX *aux, int operation, const ASN1_VALUE **in, const ASN1_ITEM *it, void *exarg); +int ossl_asn1_string_set_internal(ASN1_STRING *str, const uint8_t *data, + int len_in, int add_nul_byte); #endif /* !defined(OSSL_LIBCRYPTO_ASN1_ASN1_LOCAL_H) */ diff --git a/crypto/asn1/evp_asn1.c b/crypto/asn1/evp_asn1.c index 2d50dc657b..2f081e56d3 100644 --- a/crypto/asn1/evp_asn1.c +++ b/crypto/asn1/evp_asn1.c @@ -34,6 +34,7 @@ int ASN1_TYPE_set_octetstring(ASN1_TYPE *a, unsigned char *data, int len) int ASN1_TYPE_get_octetstring(const ASN1_TYPE *a, unsigned char *data, int max_len) { int ret, num; + size_t tmp; const unsigned char *p; if ((a->type != V_ASN1_OCTET_STRING) || (a->value.octet_string == NULL)) { @@ -41,7 +42,13 @@ int ASN1_TYPE_get_octetstring(const ASN1_TYPE *a, unsigned char *data, int max_l return -1; } p = ASN1_STRING_get0_data(a->value.octet_string); - ret = ASN1_STRING_length(a->value.octet_string); + tmp = ASN1_STRING_length_ex(a->value.octet_string); + if (tmp > INT_MAX) { + ERR_raise(ERR_LIB_ASN1, ASN1_R_TOO_LARGE); + return -1; + } + ret = (int)tmp; + if (ret < max_len) num = ret; else @@ -69,11 +76,19 @@ static ossl_inline void asn1_type_init_oct(ASN1_OCTET_STRING *oct, static int asn1_type_get_int_oct(ASN1_OCTET_STRING *oct, int32_t anum, long *num, unsigned char *data, int max_len) { - int ret = ASN1_STRING_length(oct), n; + int ret, n; + size_t tmp; if (num != NULL) *num = anum; + tmp = ASN1_STRING_length_ex(oct); + + if (tmp > INT_MAX) + tmp = INT_MAX; + + ret = (int)tmp; + if (max_len > ret) n = ret; else diff --git a/crypto/asn1/p5_scrypt.c b/crypto/asn1/p5_scrypt.c index 64980a1a68..9e1b537b9f 100644 --- a/crypto/asn1/p5_scrypt.c +++ b/crypto/asn1/p5_scrypt.c @@ -173,7 +173,7 @@ static X509_ALGOR *pkcs5_scrypt_set(const unsigned char *salt, int saltlen, saltlen = PKCS5_DEFAULT_PBE2_SALT_LEN; /* This will either copy salt or grow the buffer */ - if (ASN1_STRING_set(sparam->salt, salt, saltlen) == 0) { + if (ASN1_STRING_set_data(sparam->salt, salt, saltlen) == 0) { ERR_raise(ERR_LIB_ASN1, ERR_R_ASN1_LIB); goto err; } diff --git a/crypto/asn1/p8_pkey.c b/crypto/asn1/p8_pkey.c index 143f503dea..77f03e82ef 100644 --- a/crypto/asn1/p8_pkey.c +++ b/crypto/asn1/p8_pkey.c @@ -72,11 +72,13 @@ int PKCS8_pkey_get0(const ASN1_OBJECT **ppkalg, const unsigned char **pk, int *ppklen, const X509_ALGOR **pa, const PKCS8_PRIV_KEY_INFO *p8) { + if (ASN1_STRING_length_ex(p8->pkey) > INT_MAX) + return 0; if (ppkalg) *ppkalg = p8->pkeyalg->algorithm; if (pk) { *pk = ASN1_STRING_get0_data(p8->pkey); - *ppklen = ASN1_STRING_length(p8->pkey); + *ppklen = (int)ASN1_STRING_length_ex(p8->pkey); } if (pa) *pa = p8->pkeyalg; diff --git a/crypto/asn1/tasn_dec.c b/crypto/asn1/tasn_dec.c index 197fd24105..911eb42be7 100644 --- a/crypto/asn1/tasn_dec.c +++ b/crypto/asn1/tasn_dec.c @@ -983,7 +983,7 @@ static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, long len, ASN1_STRING_set0(stmp, (unsigned char *)cont /* UGLY CAST! */, ilen); *free_cont = 0; } else { - if (!ASN1_STRING_set(stmp, cont, ilen)) { + if (!ASN1_STRING_set_data(stmp, cont, len)) { ERR_raise(ERR_LIB_ASN1, ERR_R_ASN1_LIB); ASN1_STRING_free(stmp); *pval = NULL; diff --git a/crypto/cmp/cmp_protect.c b/crypto/cmp/cmp_protect.c index b0f52e9f36..c0dba8392d 100644 --- a/crypto/cmp/cmp_protect.c +++ b/crypto/cmp/cmp_protect.c @@ -73,7 +73,11 @@ ASN1_BIT_STRING *ossl_cmp_calc_protection(const OSSL_CMP_CTX *ctx, pbm_str = (ASN1_STRING *)ppval; pbm_str_uc = ASN1_STRING_get0_data(pbm_str); - pbm = d2i_OSSL_CRMF_PBMPARAMETER(NULL, &pbm_str_uc, ASN1_STRING_length(pbm_str)); + if (ASN1_STRING_length_ex(pbm_str) > INT_MAX) { + ERR_raise(ERR_LIB_CMP, CMP_R_WRONG_ALGORITHM_OID); + goto end; + } + pbm = d2i_OSSL_CRMF_PBMPARAMETER(NULL, &pbm_str_uc, (long)ASN1_STRING_length_ex(pbm_str)); if (pbm == NULL) { ERR_raise(ERR_LIB_CMP, CMP_R_WRONG_ALGORITHM_OID); goto end; @@ -81,7 +85,7 @@ ASN1_BIT_STRING *ossl_cmp_calc_protection(const OSSL_CMP_CTX *ctx, if (!OSSL_CRMF_pbm_new(ctx->libctx, ctx->propq, pbm, prot_part_der, prot_part_der_len, - ASN1_STRING_get0_data(ctx->secretValue), ASN1_STRING_length(ctx->secretValue), + ASN1_STRING_get0_data(ctx->secretValue), ASN1_STRING_length_ex(ctx->secretValue), &protection, &sig_len)) goto end; @@ -202,7 +206,7 @@ static X509_ALGOR *pbmac_algor(const OSSL_CMP_CTX *ctx) goto err; if ((pbm_der_len = i2d_OSSL_CRMF_PBMPARAMETER(pbm, &pbm_der)) < 0) goto err; - if (!ASN1_STRING_set(pbm_str, pbm_der, pbm_der_len)) + if (!ASN1_STRING_set_data(pbm_str, pbm_der, pbm_der_len)) goto err; alg = ossl_X509_ALGOR_from_nid(NID_id_PasswordBasedMAC, V_ASN1_SEQUENCE, pbm_str); diff --git a/crypto/cmp/cmp_status.c b/crypto/cmp/cmp_status.c index 40e1ee671e..063bbe808c 100644 --- a/crypto/cmp/cmp_status.c +++ b/crypto/cmp/cmp_status.c @@ -214,7 +214,7 @@ static char *snprint_PKIStatusInfo_parts(int status, int fail_info, for (i = 0; i < n_status_strings; i++) { text = sk_ASN1_UTF8STRING_value(status_strings, i); printed_chars = BIO_snprintf(write_ptr, bufsize, "\"%.*s\"%s", - ASN1_STRING_length(text), + (int)ASN1_STRING_length_ex(text), ASN1_STRING_get0_data(text), i < n_status_strings - 1 ? ", " : ""); ADVANCE_BUFFER; @@ -275,7 +275,7 @@ OSSL_CMP_PKISI *OSSL_CMP_STATUSINFO_new(int status, int fail_info, if (text != NULL) { if ((utf8_text = ASN1_UTF8STRING_new()) == NULL - || !ASN1_STRING_set(utf8_text, text, -1)) + || !ASN1_STRING_set_string(utf8_text, text)) goto err; if ((si->statusString = sk_ASN1_UTF8STRING_new_null()) == NULL) goto err; diff --git a/crypto/cmp/cmp_util.c b/crypto/cmp/cmp_util.c index c658529c7e..f3a0c86d53 100644 --- a/crypto/cmp/cmp_util.c +++ b/crypto/cmp/cmp_util.c @@ -227,7 +227,7 @@ int ossl_cmp_sk_ASN1_UTF8STRING_push_str(STACK_OF(ASN1_UTF8STRING) *sk, return 0; if ((utf8string = ASN1_UTF8STRING_new()) == NULL) return 0; - if (!ASN1_STRING_set(utf8string, text, len)) + if (!ASN1_STRING_set_data(utf8string, (const uint8_t *)text, len)) goto err; if (!sk_ASN1_UTF8STRING_push(sk, utf8string)) goto err; diff --git a/crypto/cms/cms_dd.c b/crypto/cms/cms_dd.c index 2e1dd78f5e..e307460e44 100644 --- a/crypto/cms/cms_dd.c +++ b/crypto/cms/cms_dd.c @@ -92,7 +92,7 @@ int ossl_cms_DigestedData_do_final(const CMS_ContentInfo *cms, BIO *chain, else r = 1; } else { - if (!ASN1_STRING_set(dd->digest, md, mdlen)) + if (!ASN1_STRING_set_data(dd->digest, md, mdlen)) goto err; r = 1; } diff --git a/crypto/cms/cms_dh.c b/crypto/cms/cms_dh.c index a3ae620dea..03cef7455a 100644 --- a/crypto/cms/cms_dh.c +++ b/crypto/cms/cms_dh.c @@ -29,7 +29,7 @@ static int dh_cms_set_peerkey(EVP_PKEY_CTX *pctx, BIGNUM *bnpub = NULL; const unsigned char *p; unsigned char *buf = NULL; - int plen; + size_t plen; X509_ALGOR_get0(&aoid, &atype, &aval, alg); if (OBJ_obj2nid(aoid) != NID_dhpublicnumber) @@ -43,29 +43,33 @@ static int dh_cms_set_peerkey(EVP_PKEY_CTX *pctx, goto err; /* Get public key */ - plen = ASN1_STRING_length(pubkey); + plen = ASN1_STRING_length_ex(pubkey); + if (plen > INT_MAX) + goto err; p = ASN1_STRING_get0_data(pubkey); if (p == NULL || plen == 0) goto err; - if ((public_key = d2i_ASN1_INTEGER(NULL, &p, plen)) == NULL) + if ((public_key = d2i_ASN1_INTEGER(NULL, &p, (int)plen)) == NULL) goto err; /* * Pad to full p parameter size as that is checked by * EVP_PKEY_set1_encoded_public_key() */ plen = EVP_PKEY_get_size(pk); + if (plen > INT_MAX) + goto err; if ((bnpub = ASN1_INTEGER_to_BN(public_key, NULL)) == NULL) goto err; if ((buf = OPENSSL_malloc(plen)) == NULL) goto err; - if (BN_bn2binpad(bnpub, buf, plen) < 0) + if (BN_bn2binpad(bnpub, buf, (int)plen) < 0) goto err; pkpeer = EVP_PKEY_new(); if (pkpeer == NULL || !EVP_PKEY_copy_parameters(pkpeer, pk) - || EVP_PKEY_set1_encoded_public_key(pkpeer, buf, plen) <= 0) + || EVP_PKEY_set1_encoded_public_key(pkpeer, buf, (int)plen) <= 0) goto err; if (EVP_PKEY_derive_set_peer(pctx, pkpeer) > 0) @@ -85,8 +89,9 @@ static int dh_cms_set_shared_info(EVP_PKEY_CTX *pctx, CMS_RecipientInfo *ri) ASN1_OCTET_STRING *ukm; const unsigned char *p; unsigned char *dukm = NULL; - int dukmlen = 0; - int keylen, plen; + size_t dukmlen = 0; + int keylen; + size_t plen; EVP_CIPHER *kekcipher = NULL; EVP_CIPHER_CTX *kekctx; const ASN1_OBJECT *aoid; @@ -116,8 +121,10 @@ static int dh_cms_set_shared_info(EVP_PKEY_CTX *pctx, CMS_RecipientInfo *ri) goto err; p = ASN1_STRING_get0_data(parameter); - plen = ASN1_STRING_length(parameter); - kekalg = d2i_X509_ALGOR(NULL, &p, plen); + plen = ASN1_STRING_length_ex(parameter); + if (plen > INT_MAX) + goto err; + kekalg = d2i_X509_ALGOR(NULL, &p, (int)plen); if (kekalg == NULL) goto err; kekctx = CMS_RecipientInfo_kari_get0_ctx(ri); @@ -146,13 +153,15 @@ static int dh_cms_set_shared_info(EVP_PKEY_CTX *pctx, CMS_RecipientInfo *ri) goto err; if (ukm != NULL) { - dukmlen = ASN1_STRING_length(ukm); - dukm = OPENSSL_memdup(ASN1_STRING_get0_data(ukm), dukmlen); + dukmlen = ASN1_STRING_length_ex(ukm); + if (dukmlen > INT_MAX) + goto err; + dukm = OPENSSL_memdup(ASN1_STRING_get0_data(ukm), (int)dukmlen); if (dukm == NULL) goto err; } - if (EVP_PKEY_CTX_set0_dh_kdf_ukm(pctx, dukm, dukmlen) <= 0) + if (EVP_PKEY_CTX_set0_dh_kdf_ukm(pctx, dukm, (int)dukmlen) <= 0) goto err; dukm = NULL; @@ -206,7 +215,7 @@ static int dh_cms_encrypt(CMS_RecipientInfo *ri) ASN1_OCTET_STRING *ukm; unsigned char *penc = NULL, *dukm = NULL; int penclen; - int dukmlen = 0; + size_t dukmlen = 0; int rv = 0; int kdf_type, wrap_nid; const EVP_MD *kdf_md; @@ -298,13 +307,15 @@ static int dh_cms_encrypt(CMS_RecipientInfo *ri) goto err; if (ukm != NULL) { - dukmlen = ASN1_STRING_length(ukm); + dukmlen = ASN1_STRING_length_ex(ukm); + if (dukmlen > INT_MAX) + goto err; dukm = OPENSSL_memdup(ASN1_STRING_get0_data(ukm), dukmlen); if (dukm == NULL) goto err; } - if (EVP_PKEY_CTX_set0_dh_kdf_ukm(pctx, dukm, dukmlen) <= 0) + if (EVP_PKEY_CTX_set0_dh_kdf_ukm(pctx, dukm, (int)dukmlen) <= 0) goto err; dukm = NULL; diff --git a/crypto/cms/cms_ec.c b/crypto/cms/cms_ec.c index 8a8fe3f912..98ab266779 100644 --- a/crypto/cms/cms_ec.c +++ b/crypto/cms/cms_ec.c @@ -79,7 +79,7 @@ static int ecdh_cms_set_peerkey(EVP_PKEY_CTX *pctx, int rv = 0; EVP_PKEY *pkpeer = NULL; const unsigned char *p; - int plen; + size_t plen; X509_ALGOR_get0(&aoid, &atype, &aval, alg); if (OBJ_obj2nid(aoid) != NID_X9_62_id_ecPublicKey) @@ -106,12 +106,14 @@ static int ecdh_cms_set_peerkey(EVP_PKEY_CTX *pctx, goto err; } /* We have parameters now set public key */ - plen = ASN1_STRING_length(pubkey); + plen = ASN1_STRING_length_ex(pubkey); + if (plen > INT_MAX) + goto err; p = ASN1_STRING_get0_data(pubkey); if (p == NULL || plen == 0) goto err; - if (EVP_PKEY_set1_encoded_public_key(pkpeer, p, plen) <= 0) + if (EVP_PKEY_set1_encoded_public_key(pkpeer, p, (int)plen) <= 0) goto err; if (EVP_PKEY_derive_set_peer(pctx, pkpeer) > 0) @@ -163,7 +165,8 @@ static int ecdh_cms_set_shared_info(EVP_PKEY_CTX *pctx, CMS_RecipientInfo *ri) ASN1_OCTET_STRING *ukm; const unsigned char *p; unsigned char *der = NULL; - int plen, keylen; + int keylen, plen_i; + size_t plen; EVP_CIPHER *kekcipher = NULL; EVP_CIPHER_CTX *kekctx; const ASN1_OBJECT *aoid = NULL; @@ -186,8 +189,10 @@ static int ecdh_cms_set_shared_info(EVP_PKEY_CTX *pctx, CMS_RecipientInfo *ri) return 0; p = ASN1_STRING_get0_data(parameter); - plen = ASN1_STRING_length(parameter); - kekalg = d2i_X509_ALGOR(NULL, &p, plen); + plen = ASN1_STRING_length_ex(parameter); + if (plen > INT_MAX) + goto err; + kekalg = d2i_X509_ALGOR(NULL, &p, (int)plen); if (kekalg == NULL) goto err; kekctx = CMS_RecipientInfo_kari_get0_ctx(ri); @@ -206,12 +211,12 @@ static int ecdh_cms_set_shared_info(EVP_PKEY_CTX *pctx, CMS_RecipientInfo *ri) if (EVP_PKEY_CTX_set_ecdh_kdf_outlen(pctx, keylen) <= 0) goto err; - plen = CMS_SharedInfo_encode(&der, kekalg, ukm, keylen); + plen_i = CMS_SharedInfo_encode(&der, kekalg, ukm, keylen); - if (plen <= 0) + if (plen_i <= 0) goto err; - if (EVP_PKEY_CTX_set0_ecdh_kdf_ukm(pctx, der, plen) <= 0) + if (EVP_PKEY_CTX_set0_ecdh_kdf_ukm(pctx, der, plen_i) <= 0) goto err; der = NULL; diff --git a/crypto/cms/cms_env.c b/crypto/cms/cms_env.c index 8413f497db..c29e2019ee 100644 --- a/crypto/cms/cms_env.c +++ b/crypto/cms/cms_env.c @@ -278,12 +278,17 @@ BIO *CMS_EnvelopedData_decrypt(CMS_EnvelopedData *env, BIO *detached_data, CMS_ContentInfo *ci; BIO *bio = NULL; int res = 0; + size_t secret_len = 0; if (env == NULL) { ERR_raise(ERR_LIB_CMS, ERR_R_PASSED_NULL_PARAMETER); return NULL; } + if (secret != NULL + && (secret_len = ASN1_STRING_length_ex(secret)) > INT_MAX) + return NULL; + if ((ci = CMS_ContentInfo_new_ex(libctx, propq)) == NULL || (bio = BIO_new(BIO_s_mem())) == NULL) goto end; @@ -291,7 +296,7 @@ BIO *CMS_EnvelopedData_decrypt(CMS_EnvelopedData *env, BIO *detached_data, ci->d.envelopedData = env; if (secret != NULL && CMS_decrypt_set1_password(ci, (unsigned char *)ASN1_STRING_get0_data(secret), - ASN1_STRING_length(secret)) + (int)secret_len) != 1) goto end; res = CMS_decrypt(ci, secret == NULL ? pkey : NULL, diff --git a/crypto/cms/cms_ess.c b/crypto/cms/cms_ess.c index cafb827394..bfe0ccbf3d 100644 --- a/crypto/cms/cms_ess.c +++ b/crypto/cms/cms_ess.c @@ -131,7 +131,7 @@ CMS_ReceiptRequest *CMS_ReceiptRequest_create0_ex( if (id) ASN1_STRING_set0(rr->signedContentIdentifier, id, idlen); else { - if (!ASN1_STRING_set(rr->signedContentIdentifier, NULL, 32)) { + if (!ASN1_STRING_set_data(rr->signedContentIdentifier, NULL, 32)) { ERR_raise(ERR_LIB_CMS, ERR_R_ASN1_LIB); goto err; } diff --git a/crypto/cms/cms_kemri.c b/crypto/cms/cms_kemri.c index 1d867c0db1..3284ebc23a 100644 --- a/crypto/cms/cms_kemri.c +++ b/crypto/cms/cms_kemri.c @@ -388,7 +388,7 @@ int ossl_cms_RecipientInfo_kemri_decrypt(const CMS_ContentInfo *cms, goto err; kem_ct = ASN1_STRING_get0_data(kemri->kemct); - kem_ct_len = ASN1_STRING_length(kemri->kemct); + kem_ct_len = ASN1_STRING_length_ex(kemri->kemct); if (EVP_PKEY_decapsulate(kemri->pctx, NULL, &kem_secret_len, kem_ct, kem_ct_len) <= 0) return 0; diff --git a/crypto/cms/cms_rsa.c b/crypto/cms/cms_rsa.c index fc7fc6c284..1b351f6cd1 100644 --- a/crypto/cms/cms_rsa.c +++ b/crypto/cms/cms_rsa.c @@ -43,7 +43,7 @@ static int rsa_cms_decrypt(CMS_RecipientInfo *ri) int nid; int rv = -1; const unsigned char *label = NULL; - int labellen = 0; + size_t labellen = 0; const EVP_MD *mgf1md = NULL, *md = NULL; RSA_OAEP_PARAMS *oaep; const ASN1_OBJECT *aoid; @@ -90,7 +90,9 @@ static int rsa_cms_decrypt(CMS_RecipientInfo *ri) } label = ASN1_STRING_get0_data(parameter); - labellen = ASN1_STRING_length(parameter); + labellen = ASN1_STRING_length_ex(parameter); + if (labellen > INT_MAX) + goto err; } if (EVP_PKEY_CTX_set_rsa_padding(pkctx, RSA_PKCS1_OAEP_PADDING) <= 0) @@ -105,7 +107,7 @@ static int rsa_cms_decrypt(CMS_RecipientInfo *ri) if (dup_label == NULL) goto err; - if (EVP_PKEY_CTX_set0_rsa_oaep_label(pkctx, dup_label, labellen) <= 0) { + if (EVP_PKEY_CTX_set0_rsa_oaep_label(pkctx, dup_label, (int)labellen) <= 0) { OPENSSL_free(dup_label); goto err; } diff --git a/crypto/cms/cms_sd.c b/crypto/cms/cms_sd.c index 6466aacec1..352f75a45c 100644 --- a/crypto/cms/cms_sd.c +++ b/crypto/cms/cms_sd.c @@ -304,7 +304,7 @@ static int ossl_cms_add1_signing_cert(CMS_SignerInfo *si, p = pp; i2d_ESS_SIGNING_CERT(sc, &p); - if (!(seq = ASN1_STRING_new()) || !ASN1_STRING_set(seq, pp, len)) { + if (!(seq = ASN1_STRING_new()) || !ASN1_STRING_set_data(seq, pp, len)) { ASN1_STRING_free(seq); OPENSSL_free(pp); return 0; @@ -329,7 +329,7 @@ static int ossl_cms_add1_signing_cert_v2(CMS_SignerInfo *si, p = pp; i2d_ESS_SIGNING_CERT_V2(sc, &p); - if (!(seq = ASN1_STRING_new()) || !ASN1_STRING_set(seq, pp, len)) { + if (!(seq = ASN1_STRING_new()) || !ASN1_STRING_set_data(seq, pp, len)) { ASN1_STRING_free(seq); OPENSSL_free(pp); return 0; diff --git a/crypto/ct/ct_oct.c b/crypto/ct/ct_oct.c index b8bef582a9..4f5fd8d027 100644 --- a/crypto/ct/ct_oct.c +++ b/crypto/ct/ct_oct.c @@ -381,7 +381,7 @@ STACK_OF(SCT) *d2i_SCT_LIST(STACK_OF(SCT) **a, const unsigned char **pp, return NULL; p = ASN1_STRING_get0_data(oct); - if ((sk = o2i_SCT_LIST(a, &p, ASN1_STRING_length(oct))) != NULL) + if ((sk = o2i_SCT_LIST(a, &p, ASN1_STRING_length_ex(oct))) != NULL) *pp += len; ASN1_OCTET_STRING_free(oct); diff --git a/crypto/ec/ec_asn1.c b/crypto/ec/ec_asn1.c index fdc3ce94c7..835f759935 100644 --- a/crypto/ec/ec_asn1.c +++ b/crypto/ec/ec_asn1.c @@ -966,9 +966,10 @@ EC_KEY *d2i_ECPrivateKey(EC_KEY **a, const unsigned char **in, long len) if (priv_key->privateKey) { ASN1_OCTET_STRING *pkey = priv_key->privateKey; - if (EC_KEY_oct2priv(ret, ASN1_STRING_get0_data(pkey), - ASN1_STRING_length(pkey)) - == 0) + size_t pkey_len = ASN1_STRING_length_ex(pkey); + if (pkey_len > INT_MAX) + goto err; + if (EC_KEY_oct2priv(ret, ASN1_STRING_get0_data(pkey), (int)pkey_len) == 0) goto err; } else { ERR_raise(ERR_LIB_EC, EC_R_MISSING_PRIVATE_KEY); @@ -987,11 +988,13 @@ EC_KEY *d2i_ECPrivateKey(EC_KEY **a, const unsigned char **in, long len) if (priv_key->publicKey) { const unsigned char *pub_oct; - int pub_oct_len; + size_t pub_oct_len; pub_oct = ASN1_STRING_get0_data(priv_key->publicKey); - pub_oct_len = ASN1_STRING_length(priv_key->publicKey); - if (!EC_KEY_oct2key(ret, pub_oct, pub_oct_len, NULL)) { + pub_oct_len = ASN1_STRING_length_ex(priv_key->publicKey); + if (pub_oct_len > INT_MAX) + goto err; + if (!EC_KEY_oct2key(ret, pub_oct, (int)pub_oct_len, NULL)) { ERR_raise(ERR_LIB_EC, ERR_R_EC_LIB); goto err; } diff --git a/crypto/ec/ecx_backend.c b/crypto/ec/ecx_backend.c index 710ad31a66..d95e8bb084 100644 --- a/crypto/ec/ecx_backend.c +++ b/crypto/ec/ecx_backend.c @@ -230,15 +230,19 @@ ECX_KEY *ossl_ecx_key_from_pkcs8(const PKCS8_PRIV_KEY_INFO *p8inf, const X509_ALGOR *palg; if (!PKCS8_pkey_get0(NULL, &p, &plen, &palg, p8inf)) - return 0; + goto err; oct = d2i_ASN1_OCTET_STRING(NULL, &p, plen); if (oct == NULL) { p = NULL; plen = 0; } else { + size_t tmp; p = ASN1_STRING_get0_data(oct); - plen = ASN1_STRING_length(oct); + tmp = ASN1_STRING_length_ex(oct); + if (tmp > INT_MAX) + goto err; + plen = (int)tmp; } /* @@ -247,6 +251,7 @@ ECX_KEY *ossl_ecx_key_from_pkcs8(const PKCS8_PRIV_KEY_INFO *p8inf, */ ecx = ossl_ecx_key_op(palg, p, plen, EVP_PKEY_NONE, KEY_OP_PRIVATE, libctx, propq); +err: ASN1_OCTET_STRING_free(oct); return ecx; } diff --git a/crypto/ocsp/ocsp_ext.c b/crypto/ocsp/ocsp_ext.c index e6467aa0ae..038be72b04 100644 --- a/crypto/ocsp/ocsp_ext.c +++ b/crypto/ocsp/ocsp_ext.c @@ -360,7 +360,7 @@ X509_EXTENSION *OCSP_crlID_new(const char *url, long *n, char *tim) if (url) { if ((cid->crlUrl = ASN1_IA5STRING_new()) == NULL) goto err; - if (!(ASN1_STRING_set(cid->crlUrl, url, -1))) + if (!(ASN1_STRING_set_string(cid->crlUrl, url))) goto err; } if (n) { @@ -446,7 +446,7 @@ X509_EXTENSION *OCSP_url_svcloc_new(const X509_NAME *issuer, const char **urls) goto err; if ((ia5 = ASN1_IA5STRING_new()) == NULL) goto err; - if (!ASN1_STRING_set((ASN1_STRING *)ia5, *urls, -1)) + if (!ASN1_STRING_set_string((ASN1_STRING *)ia5, *urls)) goto err; /* ad->location is allocated inside ACCESS_DESCRIPTION_new */ ad->location->type = GEN_URI; diff --git a/crypto/ocsp/ocsp_srv.c b/crypto/ocsp/ocsp_srv.c index beecad63a2..fa99da8a7f 100644 --- a/crypto/ocsp/ocsp_srv.c +++ b/crypto/ocsp/ocsp_srv.c @@ -301,7 +301,7 @@ int OCSP_RESPID_match_ex(OCSP_RESPID *respid, X509 *cert, OSSL_LIB_CTX *libctx, if (!X509_pubkey_digest(cert, sha1, md, NULL)) goto err; - ret = (ASN1_STRING_length(respid->value.byKey) == SHA_DIGEST_LENGTH) + ret = (ASN1_STRING_length_ex(respid->value.byKey) == SHA_DIGEST_LENGTH) && (memcmp(ASN1_STRING_get0_data(respid->value.byKey), md, SHA_DIGEST_LENGTH) == 0); diff --git a/crypto/pkcs12/p12_mutl.c b/crypto/pkcs12/p12_mutl.c index 2888efd689..60843bd951 100644 --- a/crypto/pkcs12/p12_mutl.c +++ b/crypto/pkcs12/p12_mutl.c @@ -350,7 +350,7 @@ int PKCS12_verify_mac(PKCS12 *p12, const char *pass, int passlen) } } X509_SIG_get0(p12->mac->dinfo, NULL, &macoct); - if ((maclen != (unsigned int)ASN1_STRING_length(macoct)) + if ((maclen != ASN1_STRING_length_ex(macoct)) || CRYPTO_memcmp(mac, ASN1_STRING_get0_data(macoct), maclen) != 0) return 0; diff --git a/crypto/pkcs7/pk7_attr.c b/crypto/pkcs7/pk7_attr.c index b865d97356..28f073f36f 100644 --- a/crypto/pkcs7/pk7_attr.c +++ b/crypto/pkcs7/pk7_attr.c @@ -30,7 +30,7 @@ int PKCS7_add_attrib_smimecap(PKCS7_SIGNER_INFO *si, } seq->length = ASN1_item_i2d((ASN1_VALUE *)cap, &seq->data, ASN1_ITEM_rptr(X509_ALGORS)); - if (ASN1_STRING_length(seq) <= 0 || ASN1_STRING_get0_data(seq) == NULL) { + if (ASN1_STRING_length_ex(seq) == 0 || ASN1_STRING_get0_data(seq) == NULL) { ASN1_STRING_free(seq); return 1; } @@ -46,14 +46,17 @@ STACK_OF(X509_ALGOR) *PKCS7_get_smimecap(PKCS7_SIGNER_INFO *si) { const ASN1_TYPE *cap; const unsigned char *p; + size_t len; cap = PKCS7_get_signed_attribute(si, NID_SMIMECapabilities); if (cap == NULL || (cap->type != V_ASN1_SEQUENCE)) return NULL; p = ASN1_STRING_get0_data(cap->value.sequence); + len = ASN1_STRING_length_ex(cap->value.sequence); + if (len > INT_MAX) + return NULL; return (STACK_OF(X509_ALGOR) *) - ASN1_item_d2i(NULL, &p, ASN1_STRING_length(cap->value.sequence), - ASN1_ITEM_rptr(X509_ALGORS)); + ASN1_item_d2i(NULL, &p, (int)len, ASN1_ITEM_rptr(X509_ALGORS)); } /* Basic smime-capabilities OID and optional integer arg */ @@ -129,7 +132,7 @@ int PKCS7_add1_attrib_digest(PKCS7_SIGNER_INFO *si, os = ASN1_OCTET_STRING_new(); if (os == NULL) return 0; - if (!ASN1_STRING_set(os, md, mdlen) + if (!ASN1_STRING_set_data(os, md, mdlen) || !PKCS7_add_signed_attribute(si, NID_pkcs9_messageDigest, V_ASN1_OCTET_STRING, os)) { ASN1_OCTET_STRING_free(os); diff --git a/crypto/pkcs7/pk7_doit.c b/crypto/pkcs7/pk7_doit.c index 1878b4aac2..33d2eaafdf 100644 --- a/crypto/pkcs7/pk7_doit.c +++ b/crypto/pkcs7/pk7_doit.c @@ -74,16 +74,19 @@ static ASN1_OCTET_STRING *pkcs7_get1_data(PKCS7 *p7) if (PKCS7_type_is_other(p7) && (p7->d.other != NULL) && (p7->d.other->type == V_ASN1_SEQUENCE) && (p7->d.other->value.sequence != NULL) - && (ASN1_STRING_length(p7->d.other->value.sequence) > 0)) { + && (ASN1_STRING_length_ex(p7->d.other->value.sequence) > 0)) { const unsigned char *data = ASN1_STRING_get0_data(p7->d.other->value.sequence); long len; int inf, tag, class; + size_t tmp; + tmp = ASN1_STRING_length_ex(p7->d.other->value.sequence); + if (tmp > INT_MAX) + return NULL; os = ASN1_OCTET_STRING_new(); if (os == NULL) return NULL; - inf = ASN1_get_object(&data, &len, &tag, &class, - ASN1_STRING_length(p7->d.other->value.sequence)); + inf = ASN1_get_object(&data, &len, &tag, &class, (int)tmp); if (inf != V_ASN1_CONSTRUCTED || tag != V_ASN1_SEQUENCE || !ASN1_OCTET_STRING_set(os, data, len)) { ASN1_OCTET_STRING_free(os); @@ -198,7 +201,7 @@ static int pkcs7_decrypt_rinfo(unsigned char **pek, int *peklen, goto err; ret = evp_pkey_decrypt_alloc(pctx, &ek, &eklen, fixlen, - ASN1_STRING_get0_data(ri->enc_key), ASN1_STRING_length(ri->enc_key)); + ASN1_STRING_get0_data(ri->enc_key), ASN1_STRING_length_ex(ri->enc_key)); if (ret <= 0) goto err; @@ -371,7 +374,7 @@ BIO *PKCS7_dataInit(PKCS7 *p7, BIO *bio) if (bio == NULL) { if (PKCS7_is_detached(p7)) { bio = BIO_new(BIO_s_null()); - } else if (os != NULL && ASN1_STRING_length(os) > 0) { + } else if (os != NULL && ASN1_STRING_length_ex(os) > 0) { /* * bio needs a copy of os->data instead of a pointer because * the data will be used after os has been freed @@ -380,8 +383,8 @@ BIO *PKCS7_dataInit(PKCS7 *p7, BIO *bio) if (bio != NULL) { BIO_set_mem_eof_return(bio, 0); const unsigned char *os_data = ASN1_STRING_get0_data(os); - int os_len = ASN1_STRING_length(os); - if (BIO_write(bio, os_data, os_len) != os_len) { + size_t os_len = ASN1_STRING_length_ex(os); + if (os_len > INT_MAX || BIO_write(bio, os_data, (int)os_len) != (int)os_len) { BIO_free_all(bio); bio = NULL; } @@ -656,10 +659,12 @@ BIO *PKCS7_dataDecode(PKCS7 *p7, EVP_PKEY *pkey, BIO *in_bio, X509 *pcert) if (in_bio != NULL) { bio = in_bio; } else { - int data_body_len = ASN1_STRING_length(data_body); + size_t data_body_len = ASN1_STRING_length_ex(data_body); + if (data_body_len > INT_MAX) + goto err; if (data_body_len > 0) bio = BIO_new_mem_buf(ASN1_STRING_get0_data(data_body), - data_body_len); + (int)data_body_len); else { bio = BIO_new(BIO_s_mem()); if (bio == NULL) @@ -1110,7 +1115,7 @@ int PKCS7_signatureVerify(BIO *bio, PKCS7 *p7, PKCS7_SIGNER_INFO *si, ERR_raise(ERR_LIB_PKCS7, PKCS7_R_UNABLE_TO_FIND_MESSAGE_DIGEST); goto err; } - if ((ASN1_STRING_length(message_digest) != (int)md_len) + if ((ASN1_STRING_length_ex(message_digest) != md_len) || (memcmp(ASN1_STRING_get0_data(message_digest), md_dat, md_len))) { ERR_raise(ERR_LIB_PKCS7, PKCS7_R_DIGEST_FAILURE); ret = -1; @@ -1142,8 +1147,12 @@ int PKCS7_signatureVerify(BIO *bio, PKCS7 *p7, PKCS7_SIGNER_INFO *si, } const unsigned char *sig_data = ASN1_STRING_get0_data(os); - int sig_len = ASN1_STRING_length(os); - i = EVP_VerifyFinal_ex(mdc_tmp, sig_data, sig_len, pkey, libctx, propq); + size_t sig_len = ASN1_STRING_length_ex(os); + if (sig_len > INT_MAX) { + ret = -1; + goto err; + } + i = EVP_VerifyFinal_ex(mdc_tmp, sig_data, (int)sig_len, pkey, libctx, propq); if (i <= 0) { ERR_raise(ERR_LIB_PKCS7, PKCS7_R_SIGNATURE_FAILURE); ret = -1; diff --git a/crypto/pkcs7/pk7_smime.c b/crypto/pkcs7/pk7_smime.c index 49129690de..7ede4b6694 100644 --- a/crypto/pkcs7/pk7_smime.c +++ b/crypto/pkcs7/pk7_smime.c @@ -199,9 +199,15 @@ static int pkcs7_copy_existing_digest(PKCS7 *p7, PKCS7_SIGNER_INFO *si) } } - if (osdig != NULL) - return PKCS7_add1_attrib_digest(si, ASN1_STRING_get0_data(osdig), ASN1_STRING_length(osdig)); + if (osdig != NULL) { + size_t len; + len = ASN1_STRING_length_ex(osdig); + if (len > INT_MAX) + goto err; + return PKCS7_add1_attrib_digest(si, ASN1_STRING_get0_data(osdig), (int)len); + } +err: ERR_raise(ERR_LIB_PKCS7, PKCS7_R_NO_MATCHING_DIGEST_TYPE_FOUND); return 0; } diff --git a/crypto/sm2/sm2_crypt.c b/crypto/sm2/sm2_crypt.c index a1cbd88c2d..6e787f5d28 100644 --- a/crypto/sm2/sm2_crypt.c +++ b/crypto/sm2/sm2_crypt.c @@ -78,7 +78,7 @@ int ossl_sm2_plaintext_size(const unsigned char *ct, size_t ct_size, return 0; } - *pt_size = ASN1_STRING_length(sm2_ctext->C2); + *pt_size = ASN1_STRING_length_ex(sm2_ctext->C2); SM2_Ciphertext_free(sm2_ctext); return 1; @@ -309,7 +309,7 @@ int ossl_sm2_decrypt(const EC_KEY *key, uint8_t *msg_mask = NULL; const uint8_t *C2 = NULL; const uint8_t *C3 = NULL; - int msg_len = 0; + size_t c3_len, msg_len = 0; EVP_MD_CTX *hash = NULL; OSSL_LIB_CTX *libctx = ossl_ec_key_get_libctx(key); const char *propq = ossl_ec_key_get0_propq(key); @@ -326,14 +326,18 @@ int ossl_sm2_decrypt(const EC_KEY *key, goto done; } - if (ASN1_STRING_length(sm2_ctext->C3) != hash_size) { + msg_len = ASN1_STRING_length_ex(sm2_ctext->C2); + if (msg_len > INT_MAX) + goto done; + + c3_len = ASN1_STRING_length_ex(sm2_ctext->C3); + if (c3_len > INT_MAX || c3_len != (size_t)hash_size) { ERR_raise(ERR_LIB_SM2, SM2_R_INVALID_ENCODING); goto done; } C2 = ASN1_STRING_get0_data(sm2_ctext->C2); C3 = ASN1_STRING_get0_data(sm2_ctext->C3); - msg_len = ASN1_STRING_length(sm2_ctext->C2); if (*ptext_len < (size_t)msg_len) { ERR_raise(ERR_LIB_SM2, SM2_R_BUFFER_TOO_SMALL); goto done; @@ -378,7 +382,7 @@ int ossl_sm2_decrypt(const EC_KEY *key, if (BN_bn2binpad(x2, x2y2, field_size) < 0 || BN_bn2binpad(y2, x2y2 + field_size, field_size) < 0 - || !ossl_ecdh_kdf_X9_63(msg_mask, msg_len, x2y2, 2 * field_size, + || !ossl_ecdh_kdf_X9_63(msg_mask, (int)msg_len, x2y2, 2 * field_size, NULL, 0, digest, libctx, propq)) { ERR_raise(ERR_LIB_SM2, ERR_R_INTERNAL_ERROR); goto done; @@ -389,7 +393,7 @@ int ossl_sm2_decrypt(const EC_KEY *key, goto done; } - for (i = 0; i != msg_len; ++i) + for (i = 0; i != (int)msg_len; ++i) ptext_buf[i] = C2[i] ^ msg_mask[i]; hash = EVP_MD_CTX_new(); @@ -400,7 +404,7 @@ int ossl_sm2_decrypt(const EC_KEY *key, if (!EVP_DigestInit(hash, digest) || !EVP_DigestUpdate(hash, x2y2, field_size) - || !EVP_DigestUpdate(hash, ptext_buf, msg_len) + || !EVP_DigestUpdate(hash, ptext_buf, (int)msg_len) || !EVP_DigestUpdate(hash, x2y2 + field_size, field_size) || !EVP_DigestFinal(hash, computed_C3, NULL)) { ERR_raise(ERR_LIB_SM2, ERR_R_EVP_LIB); @@ -413,7 +417,7 @@ int ossl_sm2_decrypt(const EC_KEY *key, } rc = 1; - *ptext_len = msg_len; + *ptext_len = (int)msg_len; done: if (rc == 0) diff --git a/crypto/ts/ts_asn1.c b/crypto/ts/ts_asn1.c index b44002ef2f..56d41df554 100644 --- a/crypto/ts/ts_asn1.c +++ b/crypto/ts/ts_asn1.c @@ -208,6 +208,7 @@ TS_TST_INFO *PKCS7_to_TS_TST_INFO(PKCS7 *token) ASN1_TYPE *tst_info_wrapper; ASN1_OCTET_STRING *tst_info_der; const unsigned char *p; + size_t len; if (!PKCS7_type_is_signed(token)) { ERR_raise(ERR_LIB_TS, TS_R_BAD_PKCS7_TYPE); @@ -230,5 +231,10 @@ TS_TST_INFO *PKCS7_to_TS_TST_INFO(PKCS7 *token) } tst_info_der = tst_info_wrapper->value.octet_string; p = ASN1_STRING_get0_data(tst_info_der); - return d2i_TS_TST_INFO(NULL, &p, ASN1_STRING_length(tst_info_der)); + len = ASN1_STRING_length_ex(tst_info_der); + if (len > INT_MAX) { + ERR_raise(ERR_LIB_TS, TS_R_BAD_TYPE); + return NULL; + } + return d2i_TS_TST_INFO(NULL, &p, (int)len); } diff --git a/crypto/ts/ts_lib.c b/crypto/ts/ts_lib.c index 8b46fb4744..26b3994ccb 100644 --- a/crypto/ts/ts_lib.c +++ b/crypto/ts/ts_lib.c @@ -86,7 +86,7 @@ int TS_MSG_IMPRINT_print_bio(BIO *bio, TS_MSG_IMPRINT *a) BIO_printf(bio, "Message data:\n"); msg = a->hashed_msg; BIO_dump_indent(bio, (const char *)ASN1_STRING_get0_data(msg), - ASN1_STRING_length(msg), 4); + (int)ASN1_STRING_length_ex(msg), 4); return 1; } diff --git a/crypto/ts/ts_rsp_sign.c b/crypto/ts/ts_rsp_sign.c index 1421275fd9..e9151f750e 100644 --- a/crypto/ts/ts_rsp_sign.c +++ b/crypto/ts/ts_rsp_sign.c @@ -298,7 +298,7 @@ int TS_RESP_CTX_set_status_info(TS_RESP_CTX *ctx, } if (text) { if ((utf8_text = ASN1_UTF8STRING_new()) == NULL - || !ASN1_STRING_set(utf8_text, text, (int)strlen(text))) { + || !ASN1_STRING_set_string(utf8_text, text)) { ERR_raise(ERR_LIB_TS, ERR_R_ASN1_LIB); goto err; } @@ -487,7 +487,7 @@ static int ts_RESP_check_request(TS_RESP_CTX *ctx) return 0; } digest = msg_imprint->hashed_msg; - if (ASN1_STRING_length(digest) != md_size) { + if (ASN1_STRING_length_ex(digest) != (size_t)md_size) { TS_RESP_CTX_set_status_info(ctx, TS_STATUS_REJECTION, "Bad message digest."); TS_RESP_CTX_add_failure_info(ctx, TS_INFO_BAD_DATA_FORMAT); @@ -645,7 +645,7 @@ static int ossl_ess_add1_signing_cert(PKCS7_SIGNER_INFO *si, p = pp; i2d_ESS_SIGNING_CERT(sc, &p); - if ((seq = ASN1_STRING_new()) == NULL || !ASN1_STRING_set(seq, pp, len)) { + if ((seq = ASN1_STRING_new()) == NULL || !ASN1_STRING_set_data(seq, pp, len)) { ASN1_STRING_free(seq); OPENSSL_free(pp); return 0; @@ -676,7 +676,7 @@ static int ossl_ess_add1_signing_cert_v2(PKCS7_SIGNER_INFO *si, p = pp; i2d_ESS_SIGNING_CERT_V2(sc, &p); - if ((seq = ASN1_STRING_new()) == NULL || !ASN1_STRING_set(seq, pp, len)) { + if ((seq = ASN1_STRING_new()) == NULL || !ASN1_STRING_set_data(seq, pp, len)) { ASN1_STRING_free(seq); OPENSSL_free(pp); return 0; diff --git a/crypto/ts/ts_rsp_verify.c b/crypto/ts/ts_rsp_verify.c index 1dc70c125b..1b45243ca8 100644 --- a/crypto/ts/ts_rsp_verify.c +++ b/crypto/ts/ts_rsp_verify.c @@ -207,24 +207,32 @@ static ESS_SIGNING_CERT *ossl_ess_get_signing_cert(const PKCS7_SIGNER_INFO *si) { const ASN1_TYPE *attr; const unsigned char *p; + size_t len; attr = PKCS7_get_signed_attribute(si, NID_id_smime_aa_signingCertificate); if (attr == NULL || attr->type != V_ASN1_SEQUENCE) return NULL; p = ASN1_STRING_get0_data(attr->value.sequence); - return d2i_ESS_SIGNING_CERT(NULL, &p, ASN1_STRING_length(attr->value.sequence)); + len = ASN1_STRING_length_ex(attr->value.sequence); + if (len > INT_MAX) + return NULL; + return d2i_ESS_SIGNING_CERT(NULL, &p, (int)len); } static ESS_SIGNING_CERT_V2 *ossl_ess_get_signing_cert_v2(const PKCS7_SIGNER_INFO *si) { const ASN1_TYPE *attr; const unsigned char *p; + size_t len; attr = PKCS7_get_signed_attribute(si, NID_id_smime_aa_signingCertificateV2); if (attr == NULL || attr->type != V_ASN1_SEQUENCE) return NULL; p = ASN1_STRING_get0_data(attr->value.sequence); - return d2i_ESS_SIGNING_CERT_V2(NULL, &p, ASN1_STRING_length(attr->value.sequence)); + len = ASN1_STRING_length_ex(attr->value.sequence); + if (len > INT_MAX) + return NULL; + return d2i_ESS_SIGNING_CERT_V2(NULL, &p, (int)len); } static int ts_check_signing_certs(const PKCS7_SIGNER_INFO *si, @@ -482,6 +490,7 @@ static int ts_check_imprints(X509_ALGOR *algor_a, TS_MSG_IMPRINT *b = tst_info->msg_imprint; X509_ALGOR *algor_b = b->hash_algo; int ret = 0; + size_t len; if (algor_a) { if (OBJ_cmp(algor_a->algorithm, algor_b->algorithm)) @@ -495,7 +504,11 @@ static int ts_check_imprints(X509_ALGOR *algor_a, goto err; } - ret = len_a == (unsigned)ASN1_STRING_length(b->hashed_msg) && memcmp(imprint_a, ASN1_STRING_get0_data(b->hashed_msg), len_a) == 0; + len = ASN1_STRING_length_ex(b->hashed_msg); + if (len > INT_MAX) + goto err; + + ret = len_a == (unsigned)len && memcmp(imprint_a, ASN1_STRING_get0_data(b->hashed_msg), len) == 0; err: if (!ret) ERR_raise(ERR_LIB_TS, TS_R_MESSAGE_IMPRINT_MISMATCH); diff --git a/crypto/ts/ts_verify_ctx.c b/crypto/ts/ts_verify_ctx.c index ec9993ed9f..76835866d7 100644 --- a/crypto/ts/ts_verify_ctx.c +++ b/crypto/ts/ts_verify_ctx.c @@ -142,6 +142,7 @@ TS_VERIFY_CTX *TS_REQ_to_TS_VERIFY_CTX(TS_REQ *req, TS_VERIFY_CTX *ctx) X509_ALGOR *md_alg; ASN1_OCTET_STRING *msg; const ASN1_INTEGER *nonce; + size_t tmp; OPENSSL_assert(req != NULL); if (ret) @@ -162,8 +163,11 @@ TS_VERIFY_CTX *TS_REQ_to_TS_VERIFY_CTX(TS_REQ *req, TS_VERIFY_CTX *ctx) if ((ret->md_alg = X509_ALGOR_dup(md_alg)) == NULL) goto err; msg = imprint->hashed_msg; - ret->imprint_len = ASN1_STRING_length(msg); - if (ret->imprint_len <= 0) + tmp = ASN1_STRING_length_ex(msg); + if (tmp > INT_MAX) + goto err; + ret->imprint_len = (unsigned int)tmp; + if (ret->imprint_len == 0) goto err; if ((ret->imprint = OPENSSL_malloc(ret->imprint_len)) == NULL) goto err; diff --git a/crypto/x509/t_x509.c b/crypto/x509/t_x509.c index cf8062a902..abe1b7557d 100644 --- a/crypto/x509/t_x509.c +++ b/crypto/x509/t_x509.c @@ -244,7 +244,7 @@ int X509_ocspid_print(BIO *bp, const X509 *x) goto err; if (!EVP_Digest(ASN1_STRING_get0_data(keybstr), - ASN1_STRING_length(keybstr), SHA1md, NULL, md, NULL)) + ASN1_STRING_length_ex(keybstr), SHA1md, NULL, md, NULL)) goto err; for (i = 0; i < SHA_DIGEST_LENGTH; i++) { if (BIO_printf(bp, "%02X", SHA1md[i]) <= 0) diff --git a/crypto/x509/v3_addr.c b/crypto/x509/v3_addr.c index bdaa74a0d1..e245e2b08a 100644 --- a/crypto/x509/v3_addr.c +++ b/crypto/x509/v3_addr.c @@ -409,6 +409,11 @@ static int make_addressPrefix(IPAddressOrRange **result, unsigned char *addr, { int bytelen = (prefixlen + 7) / 8, bitlen = prefixlen % 8; IPAddressOrRange *aor; + unsigned char *prefix = NULL; + uint8_t unused_bits = 0; + + if (bitlen > 0) + unused_bits = 8 - bitlen; if (prefixlen < 0 || prefixlen > (afilen * 8)) return 0; @@ -417,19 +422,23 @@ static int make_addressPrefix(IPAddressOrRange **result, unsigned char *addr, aor->type = IPAddressOrRange_addressPrefix; if (aor->u.addressPrefix == NULL && (aor->u.addressPrefix = ASN1_BIT_STRING_new()) == NULL) goto err; - /* BIT_STRING is a typedef of STRING - * this function allows to set value without checking invalid bits - * as they are nullified after setting */ - if (!ASN1_STRING_set(aor->u.addressPrefix, addr, bytelen)) + if (bytelen > 0) { + prefix = OPENSSL_malloc(bytelen); + if (prefix == NULL) + goto err; + memcpy(prefix, addr, bytelen); + if (unused_bits) + prefix[bytelen - 1] &= ~(0xFF >> bitlen); + } + if (!ASN1_BIT_STRING_set1(aor->u.addressPrefix, prefix, bytelen, unused_bits)) goto err; - if (bitlen > 0) - aor->u.addressPrefix->data[bytelen - 1] &= ~(0xFF >> bitlen); - ossl_asn1_bit_string_set_unused_bits(aor->u.addressPrefix, 8 - bitlen); - *result = aor; + + OPENSSL_free(prefix); return 1; err: + OPENSSL_free(prefix); IPAddressOrRange_free(aor); return 0; } diff --git a/crypto/x509/v3_akid.c b/crypto/x509/v3_akid.c index f500165d0d..95b904c757 100644 --- a/crypto/x509/v3_akid.c +++ b/crypto/x509/v3_akid.c @@ -186,7 +186,7 @@ static AUTHORITY_KEYID *v2i_AUTHORITY_KEYID(X509V3_EXT_METHOD *method, && !(same_issuer && !ss)) { ikeyid = X509V3_EXT_d2i(ext); /* Ignore empty keyids in the issuer cert */ - if (ASN1_STRING_length(ikeyid) == 0) { + if (ASN1_STRING_length_ex(ikeyid) == 0) { ASN1_OCTET_STRING_free(ikeyid); ikeyid = NULL; } diff --git a/crypto/x509/v3_cpols.c b/crypto/x509/v3_cpols.c index 0dc8f76ad4..2cc71f567b 100644 --- a/crypto/x509/v3_cpols.c +++ b/crypto/x509/v3_cpols.c @@ -208,8 +208,7 @@ static POLICYINFO *policy_section(X509V3_CTX *ctx, ERR_raise(ERR_LIB_X509V3, ERR_R_ASN1_LIB); goto err; } - if (!ASN1_STRING_set(qual->d.cpsuri, cnf->value, - (int)strlen(cnf->value))) { + if (!ASN1_STRING_set_string(qual->d.cpsuri, cnf->value)) { ERR_raise(ERR_LIB_X509V3, ERR_R_ASN1_LIB); goto err; } @@ -325,7 +324,7 @@ static POLICYQUALINFO *notice_section(X509V3_CTX *ctx, if (tag_len != 0) value += tag_len + 1; len = (int)strlen(value); - if (!ASN1_STRING_set(not->exptext, value, len)) { + if (!ASN1_STRING_set_data(not->exptext, (uint8_t *)value, len)) { ERR_raise(ERR_LIB_X509V3, ERR_R_ASN1_LIB); goto err; } @@ -344,8 +343,7 @@ static POLICYQUALINFO *notice_section(X509V3_CTX *ctx, nref->organization->type = V_ASN1_IA5STRING; else nref->organization->type = V_ASN1_VISIBLESTRING; - if (!ASN1_STRING_set(nref->organization, cnf->value, - (int)strlen(cnf->value))) { + if (!ASN1_STRING_set_string(nref->organization, cnf->value)) { ERR_raise(ERR_LIB_X509V3, ERR_R_ASN1_LIB); goto err; } diff --git a/crypto/x509/v3_ia5.c b/crypto/x509/v3_ia5.c index 539c43a141..a8fa52a439 100644 --- a/crypto/x509/v3_ia5.c +++ b/crypto/x509/v3_ia5.c @@ -52,7 +52,7 @@ ASN1_IA5STRING *s2i_ASN1_IA5STRING(X509V3_EXT_METHOD *method, ERR_raise(ERR_LIB_X509V3, ERR_R_ASN1_LIB); return NULL; } - if (!ASN1_STRING_set((ASN1_STRING *)ia5, str, (int)strlen(str))) { + if (!ASN1_STRING_set_string((ASN1_STRING *)ia5, str)) { ASN1_IA5STRING_free(ia5); return NULL; } diff --git a/crypto/x509/v3_ist.c b/crypto/x509/v3_ist.c index 0409b52d60..a4d3437192 100644 --- a/crypto/x509/v3_ist.c +++ b/crypto/x509/v3_ist.c @@ -52,28 +52,28 @@ static ISSUER_SIGN_TOOL *v2i_issuer_sign_tool(X509V3_EXT_METHOD *method, X509V3_ if (strcmp(cnf->name, "signTool") == 0) { if (ist->signTool == NULL || cnf->value == NULL - || !ASN1_STRING_set(ist->signTool, cnf->value, (int)strlen(cnf->value))) { + || !ASN1_STRING_set_string(ist->signTool, cnf->value)) { ERR_raise(ERR_LIB_X509V3, ERR_R_ASN1_LIB); goto err; } } else if (strcmp(cnf->name, "cATool") == 0) { if (ist->cATool == NULL || cnf->value == NULL - || !ASN1_STRING_set(ist->cATool, cnf->value, (int)strlen(cnf->value))) { + || !ASN1_STRING_set_string(ist->cATool, cnf->value)) { ERR_raise(ERR_LIB_X509V3, ERR_R_ASN1_LIB); goto err; } } else if (strcmp(cnf->name, "signToolCert") == 0) { if (ist->signToolCert == NULL || cnf->value == NULL - || !ASN1_STRING_set(ist->signToolCert, cnf->value, (int)strlen(cnf->value))) { + || !ASN1_STRING_set_string(ist->signToolCert, cnf->value)) { ERR_raise(ERR_LIB_X509V3, ERR_R_ASN1_LIB); goto err; } } else if (strcmp(cnf->name, "cAToolCert") == 0) { if (ist->cAToolCert == NULL || cnf->value == NULL - || !ASN1_STRING_set(ist->cAToolCert, cnf->value, (int)strlen(cnf->value))) { + || !ASN1_STRING_set_string(ist->cAToolCert, cnf->value)) { ERR_raise(ERR_LIB_X509V3, ERR_R_ASN1_LIB); goto err; } diff --git a/crypto/x509/v3_lib.c b/crypto/x509/v3_lib.c index aee7ad119f..a099177dba 100644 --- a/crypto/x509/v3_lib.c +++ b/crypto/x509/v3_lib.c @@ -169,16 +169,18 @@ void *X509V3_EXT_d2i(const X509_EXTENSION *ext) const X509V3_EXT_METHOD *method; const unsigned char *p; const ASN1_STRING *extvalue; - int extlen; + size_t extlen; if ((method = X509V3_EXT_get(ext)) == NULL) return NULL; extvalue = X509_EXTENSION_get_data(ext); p = ASN1_STRING_get0_data(extvalue); - extlen = ASN1_STRING_length(extvalue); + extlen = ASN1_STRING_length_ex(extvalue); + if (extlen > INT_MAX) + return NULL; if (method->it) - return ASN1_item_d2i(NULL, &p, extlen, ASN1_ITEM_ptr(method->it)); - return method->d2i(NULL, &p, extlen); + return ASN1_item_d2i(NULL, &p, (int)extlen, ASN1_ITEM_ptr(method->it)); + return method->d2i(NULL, &p, (int)extlen); } /*- diff --git a/crypto/x509/v3_prn.c b/crypto/x509/v3_prn.c index 4a0df33ea3..0fb7c9e38a 100644 --- a/crypto/x509/v3_prn.c +++ b/crypto/x509/v3_prn.c @@ -73,24 +73,26 @@ int X509V3_EXT_print(BIO *out, const X509_EXTENSION *ext, unsigned long flag, char *value = NULL; const ASN1_OCTET_STRING *extoct; const unsigned char *p; - int extlen; + size_t extlen; const X509V3_EXT_METHOD *method; STACK_OF(CONF_VALUE) *nval = NULL; int ok = 1; extoct = X509_EXTENSION_get_data(ext); p = ASN1_STRING_get0_data(extoct); - extlen = ASN1_STRING_length(extoct); + extlen = ASN1_STRING_length_ex(extoct); + if (extlen > INT_MAX) + return 0; if ((method = X509V3_EXT_get(ext)) == NULL) - return unknown_ext_print(out, p, extlen, flag, indent, 0); + return unknown_ext_print(out, p, (int)extlen, flag, indent, 0); if (method->it) - ext_str = ASN1_item_d2i(NULL, &p, extlen, ASN1_ITEM_ptr(method->it)); + ext_str = ASN1_item_d2i(NULL, &p, (int)extlen, ASN1_ITEM_ptr(method->it)); else - ext_str = method->d2i(NULL, &p, extlen); + ext_str = method->d2i(NULL, &p, (int)extlen); if (!ext_str) - return unknown_ext_print(out, p, extlen, flag, indent, 1); + return unknown_ext_print(out, p, (int)extlen, flag, indent, 1); if (method->i2s) { if ((value = method->i2s(method, ext_str)) == NULL) { diff --git a/crypto/x509/v3_san.c b/crypto/x509/v3_san.c index 0f12939d6d..f1b028f78c 100644 --- a/crypto/x509/v3_san.c +++ b/crypto/x509/v3_san.c @@ -575,7 +575,8 @@ GENERAL_NAME *a2i_GENERAL_NAME(GENERAL_NAME *out, } if (is_string) { - if ((gen->d.ia5 = ASN1_IA5STRING_new()) == NULL || !ASN1_STRING_set(gen->d.ia5, (unsigned char *)value, (int)strlen(value))) { + if ((gen->d.ia5 = ASN1_IA5STRING_new()) == NULL + || !ASN1_STRING_set_string(gen->d.ia5, value)) { ASN1_IA5STRING_free(gen->d.ia5); gen->d.ia5 = NULL; ERR_raise(ERR_LIB_X509V3, ERR_R_ASN1_LIB); diff --git a/crypto/x509/v3_utf8.c b/crypto/x509/v3_utf8.c index 49095ffdd9..dc7c86fb7f 100644 --- a/crypto/x509/v3_utf8.c +++ b/crypto/x509/v3_utf8.c @@ -55,7 +55,7 @@ ASN1_UTF8STRING *s2i_ASN1_UTF8STRING(X509V3_EXT_METHOD *method, ERR_raise(ERR_LIB_X509V3, ERR_R_ASN1_LIB); return NULL; } - if (!ASN1_STRING_set((ASN1_STRING *)utf8, str, (int)strlen(str))) { + if (!ASN1_STRING_set_string(utf8, str)) { ERR_raise(ERR_LIB_X509V3, ERR_R_ASN1_LIB); ASN1_UTF8STRING_free(utf8); return NULL; diff --git a/crypto/x509/x509_att.c b/crypto/x509/x509_att.c index ee631db75a..e08e490274 100644 --- a/crypto/x509/x509_att.c +++ b/crypto/x509/x509_att.c @@ -365,7 +365,7 @@ int X509_ATTRIBUTE_set1_data(X509_ATTRIBUTE *attr, int attrtype, atype = stmp->type; } else if (len != -1) { if ((stmp = ASN1_STRING_type_new(attrtype)) == NULL - || !ASN1_STRING_set(stmp, data, len)) { + || !ASN1_STRING_set_data(stmp, data, len)) { ERR_raise(ERR_LIB_X509, ERR_R_ASN1_LIB); goto err; } diff --git a/crypto/x509/x509name.c b/crypto/x509/x509name.c index ebd58a2012..58167d9a78 100644 --- a/crypto/x509/x509name.c +++ b/crypto/x509/x509name.c @@ -332,9 +332,12 @@ int X509_NAME_ENTRY_set_data(X509_NAME_ENTRY *ne, int type, OBJ_obj2nid(ne->object)) ? 1 : 0; - if (len < 0) - len = (int)strlen((const char *)bytes); - i = ASN1_STRING_set(ne->value, bytes, len); + if (len < -1) + return 0; + if (len == -1) + i = ASN1_STRING_set_string(ne->value, (const char *)bytes); + else + i = ASN1_STRING_set_data(ne->value, bytes, (size_t)len); if (!i) return 0; if (type != V_ASN1_UNDEF) { diff --git a/crypto/x509/x_x509a.c b/crypto/x509/x_x509a.c index 6dfd68f74d..3fa17fb925 100644 --- a/crypto/x509/x_x509a.c +++ b/crypto/x509/x_x509a.c @@ -50,6 +50,8 @@ static X509_CERT_AUX *aux_get(X509 *x) int X509_alias_set1(X509 *x, const unsigned char *name, int len) { X509_CERT_AUX *aux; + size_t len_s; + if (!name) { if (!x || !x->aux || !x->aux->alias) return 1; @@ -59,14 +61,25 @@ int X509_alias_set1(X509 *x, const unsigned char *name, int len) } if ((aux = aux_get(x)) == NULL) return 0; + + if (len < -1) + return 0; + + if (len == -1) + len_s = strlen((const char *)name); + else + len_s = len; + if (aux->alias == NULL && (aux->alias = ASN1_UTF8STRING_new()) == NULL) return 0; - return ASN1_STRING_set(aux->alias, name, len); + return ASN1_STRING_set_data(aux->alias, name, len_s); } int X509_keyid_set1(X509 *x, const unsigned char *id, int len) { X509_CERT_AUX *aux; + size_t len_s; + if (!id) { if (!x || !x->aux || !x->aux->keyid) return 1; @@ -76,10 +89,19 @@ int X509_keyid_set1(X509 *x, const unsigned char *id, int len) } if ((aux = aux_get(x)) == NULL) return 0; + + if (len < -1) + return 0; + + if (len == -1) + len_s = strlen((const char *)id); + else + len_s = len; + if (aux->keyid == NULL && (aux->keyid = ASN1_OCTET_STRING_new()) == NULL) return 0; - return ASN1_STRING_set(aux->keyid, id, len); + return ASN1_STRING_set_data(aux->keyid, id, len_s); } const unsigned char *X509_alias_get0(const X509 *x, int *len) diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c index a90d401679..2f8bdcea94 100644 --- a/ssl/statem/statem_srvr.c +++ b/ssl/statem/statem_srvr.c @@ -3562,7 +3562,8 @@ static int tls_process_cke_gost(SSL_CONNECTION *s, PACKET *pkt) EVP_PKEY *client_pub_pkey = NULL, *pk = NULL; unsigned char premaster_secret[32]; const unsigned char *start; - size_t outlen = sizeof(premaster_secret), inlen; + size_t outlen = sizeof(premaster_secret); + size_t inlen; unsigned long alg_a; GOST_KX_MESSAGE *pKX = NULL; const unsigned char *ptr; @@ -3628,7 +3629,10 @@ static int tls_process_cke_gost(SSL_CONNECTION *s, PACKET *pkt) goto err; } - inlen = ASN1_STRING_length(pKX->kxBlob->value.sequence); + inlen = ASN1_STRING_length_ex(pKX->kxBlob->value.sequence); + if (inlen > INT_MAX) + goto err; + start = ASN1_STRING_get0_data(pKX->kxBlob->value.sequence); if (EVP_PKEY_decrypt(pkey_ctx, premaster_secret, &outlen, start, diff --git a/test/asn1_string_test.c b/test/asn1_string_test.c index 24d679444b..865003eeb9 100644 --- a/test/asn1_string_test.c +++ b/test/asn1_string_test.c @@ -390,7 +390,6 @@ asn1_bit_string_set1_test(int idx) } static int - asn1_string_new_not_owned_test(void) { int success = 0; diff --git a/test/cmp_hdr_test.c b/test/cmp_hdr_test.c index cbffb87b23..30f19f6238 100644 --- a/test/cmp_hdr_test.c +++ b/test/cmp_hdr_test.c @@ -252,7 +252,7 @@ static int execute_HDR_push0_freeText_test(CMP_HDR_TEST_FIXTURE *fixture) if (!TEST_ptr(text)) return 0; - if (!ASN1_STRING_set(text, "A free text", -1)) + if (!ASN1_STRING_set_string(text, "A free text")) goto err; if (!TEST_int_eq(ossl_cmp_hdr_push0_freeText(fixture->hdr, text), 1)) @@ -285,7 +285,7 @@ static int execute_HDR_push1_freeText_test(CMP_HDR_TEST_FIXTURE *fixture) if (!TEST_ptr(text)) goto err; - if (!ASN1_STRING_set(text, "A free text", -1)) + if (!ASN1_STRING_set_string(text, "A free text")) goto err; if (!TEST_int_eq(ossl_cmp_hdr_push1_freeText(fixture->hdr, text), 1)) diff --git a/test/helpers/pkcs12.c b/test/helpers/pkcs12.c index abc70db1b7..3ecfd020b6 100644 --- a/test/helpers/pkcs12.c +++ b/test/helpers/pkcs12.c @@ -468,21 +468,21 @@ static int check_asn1_string(const ASN1_TYPE *av, const char *txt) switch (av->type) { case V_ASN1_BMPSTRING: value = OPENSSL_uni2asc(ASN1_STRING_get0_data(av->value.bmpstring), - ASN1_STRING_length(av->value.bmpstring)); + (int)ASN1_STRING_length_ex(av->value.bmpstring)); if (!TEST_str_eq(txt, (char *)value)) goto err; break; case V_ASN1_UTF8STRING: if (!TEST_mem_eq(txt, strlen(txt), ASN1_STRING_get0_data(av->value.utf8string), - ASN1_STRING_length(av->value.utf8string))) + ASN1_STRING_length_ex(av->value.utf8string))) goto err; break; case V_ASN1_OCTET_STRING: if (!TEST_mem_eq(txt, strlen(txt), (char *)ASN1_STRING_get0_data(av->value.octet_string), - ASN1_STRING_length(av->value.octet_string))) + ASN1_STRING_length_ex(av->value.octet_string))) goto err; break; diff --git a/test/tls-provider.c b/test/tls-provider.c index 6652034936..c516cb04b4 100644 --- a/test/tls-provider.c +++ b/test/tls-provider.c @@ -1282,7 +1282,7 @@ static XORKEY *xor_key_from_pkcs8(const PKCS8_PRIV_KEY_INFO *p8inf, plen = 0; } else { p = ASN1_STRING_get0_data(oct); - plen = ASN1_STRING_length(oct); + plen = (int)ASN1_STRING_length_ex(oct); } xork = xor_key_op(palg, p, plen, KEY_OP_PRIVATE, diff --git a/test/v3nametest.c b/test/v3nametest.c index 8ae7a9e54f..757db8a18e 100644 --- a/test/v3nametest.c +++ b/test/v3nametest.c @@ -149,7 +149,7 @@ static int set_altname(X509 *crt, ...) ia5 = ASN1_IA5STRING_new(); if (ia5 == NULL) goto out; - if (!ASN1_STRING_set(ia5, name, -1)) + if (!ASN1_STRING_set_string(ia5, name)) goto out; switch (type) { case GEN_EMAIL: diff --git a/test/x509_internal_test.c b/test/x509_internal_test.c index 19a7c2469d..1176ef49ef 100644 --- a/test/x509_internal_test.c +++ b/test/x509_internal_test.c @@ -152,7 +152,7 @@ static int test_a2i_ipaddress(int idx) { int good = 1; ASN1_OCTET_STRING *ip; - int len = a2i_ipaddress_tests[idx].length; + size_t len = a2i_ipaddress_tests[idx].length; ip = a2i_IPADDRESS(a2i_ipaddress_tests[idx].ipasc); if (len == 0) { @@ -162,7 +162,7 @@ static int test_a2i_ipaddress(int idx) } } else { if (!TEST_ptr(ip) - || !TEST_int_eq(ASN1_STRING_length(ip), len) + || !TEST_size_t_eq(ASN1_STRING_length_ex(ip), len) || !TEST_mem_eq(ASN1_STRING_get0_data(ip), len, a2i_ipaddress_tests[idx].data, len)) { good = 0; From ba569d08ed7ba946aaa1285c9e540b91d451a5bd Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Wed, 10 Jun 2026 16:44:04 -0600 Subject: [PATCH 281/349] Add a patch disabling the pem_encoder test This seems to have strange internal failures retrieving EC keys from the kryoptic soft hsm. (based on reading the meson stuff this also occurs on some linux distros and this is disabled there too) The same test appears to work fine with RSA keys, but diagnosing this is challenging with nothing int the voluminous output that might say why it didn't work. So disable this for now Reviewed-by: Milan Broz Reviewed-by: Norbert Pocs MergeDate: Sat Jul 18 13:01:21 2026 (Merged from https://github.com/openssl/openssl/pull/31194) --- ...-Temporarily-disable-the-pem-encoder.patch | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 test/recipes/95-test_external_pkcs11_provider_data/patches/0002-Temporarily-disable-the-pem-encoder.patch diff --git a/test/recipes/95-test_external_pkcs11_provider_data/patches/0002-Temporarily-disable-the-pem-encoder.patch b/test/recipes/95-test_external_pkcs11_provider_data/patches/0002-Temporarily-disable-the-pem-encoder.patch new file mode 100644 index 0000000000..2498938a69 --- /dev/null +++ b/test/recipes/95-test_external_pkcs11_provider_data/patches/0002-Temporarily-disable-the-pem-encoder.patch @@ -0,0 +1,27 @@ +From 891c0d9a615ce37506969875792afdb6defe9b79 Mon Sep 17 00:00:00 2001 +From: Bob Beck +Date: Wed, 10 Jun 2026 16:40:37 -0600 +Subject: [PATCH] Temporarily disable the pem encoder + +This appears to have some sort of internal error fetching ec keys +from the softhsm. (and appears to also be disabled for +similar reasons on some linux distros) +--- + tests/meson.build | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/tests/meson.build b/tests/meson.build +index 6050fe5..24520d1 100644 +--- a/tests/meson.build ++++ b/tests/meson.build +@@ -155,7 +155,6 @@ tests = { + 'oaepsha2': {'suites': ['softokn', 'kryoptic', 'kryoptic.nss']}, + 'hkdf': {'suites': ['softokn', 'kryoptic', 'kryoptic.nss']}, + 'imported' : {'suites': ['softokn', 'kryoptic', 'kryoptic.nss']}, +- 'pem_encoder': {'suites': all_suites}, + 'rsa': {'suites': all_suites}, + 'rsapss': {'suites': all_suites}, + 'rsapssam': {'suites': ['softhsm', 'kryoptic']}, +-- +2.53.0 + From 5eb81718bc4074ca6207c602b00a836e88017b96 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 14 Jul 2026 18:18:26 +0200 Subject: [PATCH 282/349] apps: test dsa -text option The -text option of the dsa app was not exercised by any test. Add a subtest that prints both a private and a public key in text form and, after stripping the colon-separated hex formatting, verifies the printed private and public values match the committed testdsa.pem keypair rather than merely checking that the labels are present. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Paul Dale Reviewed-by: Daniel Kubec MergeDate: Mon Jul 20 06:26:08 2026 (Merged from https://github.com/openssl/openssl/pull/31949) --- test/recipes/15-test_dsa.t | 45 +++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/test/recipes/15-test_dsa.t b/test/recipes/15-test_dsa.t index 2d16ebd02c..c30cbfbbe3 100644 --- a/test/recipes/15-test_dsa.t +++ b/test/recipes/15-test_dsa.t @@ -17,7 +17,7 @@ use OpenSSL::Test::Utils; setup("test_dsa"); plan skip_all => 'DSA is not supported in this build' if disabled('dsa'); -plan tests => 10; +plan tests => 11; require_ok(srctop_file('test','recipes','tconversion.pl')); @@ -88,6 +88,49 @@ subtest "dsa -modulus prints the DSA public value" => sub { "-modulus prints the expected public value for a public key"); }; +subtest "dsa -text prints the key in text form" => sub { + plan tests => 6; + + # The private (x) and public (y) values of the committed testdsa.pem / + # testdsapub.pem keypair. -text prints them as colon-separated hex; we + # strip the formatting and compare against the known values so the actual + # key material, not just the labels, is verified. + my $priv_hex = "BF71D497B89755D0C5E41285D81F9577CC3DF8C2"; + my $pub_hex = "CC99A07D9817BFF03BB09B183E9B19EB77ABECF192C3A9FBA833DBE" + . "69EDB719A8E9777BB82736CEC6A8E4E2FAD0693ACC3D14565D62710B95B02CC" + . "6A5CF091EEF9C22F20193EBE114C45A0B5E54A645037E8787FE01B3871508A2" + . "5BDBF7C6B81428F89858F133FDB858C390C2EF7BCF7E41D7C66578F792A2488" + . "C787EF7C7D41"; + + my @priv = run(app(['openssl', 'dsa', '-text', '-noout', + '-in', srctop_file("test", "testdsa.pem")], + stderr => undef), + capture => 1); + chomp @priv; + my $priv_blob = uc join('', @priv); + $priv_blob =~ s/[^0-9A-F]//g; + ok(grep(/^Private-Key: \(1024 bit\)$/, @priv), + "-text prints the private key header"); + ok(index($priv_blob, $priv_hex) >= 0, + "-text prints the expected private value"); + ok(index($priv_blob, $pub_hex) >= 0, + "-text prints the expected public value for a private key"); + + my @pub = run(app(['openssl', 'dsa', '-pubin', '-text', '-noout', + '-in', srctop_file("test", "testdsapub.pem")], + stderr => undef), + capture => 1); + chomp @pub; + my $pub_blob = uc join('', @pub); + $pub_blob =~ s/[^0-9A-F]//g; + ok(grep(/^Public-Key: \(1024 bit\)$/, @pub), + "-text prints the public key header"); + ok(index($pub_blob, $pub_hex) >= 0, + "-text prints the expected public value for a public key"); + ok(!grep(/^priv:/, @pub), + "-text does not print a private component for a public key"); +}; + subtest "dsa PVK output is rejected for public key input" => sub { plan tests => 1; From 61b8506921860deee7031d960c1f992fec642514 Mon Sep 17 00:00:00 2001 From: Ondrej Moris Date: Tue, 14 Jul 2026 17:16:10 +0200 Subject: [PATCH 283/349] tests: reduce pkcs11-provider log test to failures If pkcs11-provider external test fails, only output of failed tests is printed. Signed-off-by: Ondrej Moris Reviewed-by: Dmitry Belyavskiy Reviewed-by: Igor Ustinov MergeDate: Mon Jul 20 06:29:41 2026 (Merged from https://github.com/openssl/openssl/pull/31947) --- .../pkcs11-provider.sh | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/test/recipes/95-test_external_pkcs11_provider_data/pkcs11-provider.sh b/test/recipes/95-test_external_pkcs11_provider_data/pkcs11-provider.sh index 1b49ca933f..f75f1260a0 100755 --- a/test/recipes/95-test_external_pkcs11_provider_data/pkcs11-provider.sh +++ b/test/recipes/95-test_external_pkcs11_provider_data/pkcs11-provider.sh @@ -85,13 +85,10 @@ echo "Running tests" echo "------------------------------------------------------------------" # For maintenance reasons and simplicity we only run test with kryoptic token -meson test -C $PKCS11_PROVIDER_BUILDDIR --suite=kryoptic +meson test -C $PKCS11_PROVIDER_BUILDDIR --print-errorlogs --suite=kryoptic -if [ $? -ne 0 ]; then - cat $PKCS11_PROVIDER_BUILDDIR/meson-logs/testlog.txt - exit 1 -fi +RESULT=$? rm -rf $PKCS11_PROVIDER_BUILDDIR -exit 0 +exit $RESULT From caf63500b6ec5c5d31bacff2fe8d450a46df4a5a Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 14 Jul 2026 16:59:10 +0200 Subject: [PATCH 284/349] apps: add dgst test coverage for -keyform option The -keyform (OPT_KEYFORM) option of the dgst app was not exercised by any test. Add a subtest that converts the RSA test keys to DER and then signs and verifies with -keyform DER, covering the option for both the private and public key loading paths. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Daniel Kubec Reviewed-by: Dmitry Belyavskiy Reviewed-by: Tim Hudson MergeDate: Mon Jul 20 06:40:45 2026 (Merged from https://github.com/openssl/openssl/pull/31946) --- test/recipes/20-test_dgst.t | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/test/recipes/20-test_dgst.t b/test/recipes/20-test_dgst.t index 6cabaf3be5..00af8cfff2 100644 --- a/test/recipes/20-test_dgst.t +++ b/test/recipes/20-test_dgst.t @@ -18,7 +18,7 @@ use Cwd qw(abs_path); setup("test_dgst"); -plan tests => 26; +plan tests => 27; sub tsignverify { my $testtext = shift; @@ -437,6 +437,40 @@ subtest "Listing supported digests with `dgst` CLI" => sub { ok($listing =~ /-sha512\b/, "LIST: Check sha512 is listed"); }; +subtest "signing and verifying with DER `-keyform` `dgst` CLI" => sub { + if (disabled("rsa")) { + plan tests => 1; + ok(1, "Skipped (RSA not supported)"); + return; + } + plan tests => 4; + + my $data_to_sign = srctop_file('test', 'data.bin'); + my $privkey_pem = srctop_file("test", "testrsa.pem"); + my $pubkey_pem = srctop_file("test", "testrsapub.pem"); + my $privkey_der = "testrsa-keyform.der"; + my $pubkey_der = "testrsapub-keyform.der"; + my $sigfile = "testrsa-keyform.sig"; + + # Convert the keys to DER so the `-keyform DER` code path can be exercised. + ok(run(app(['openssl', 'pkey', '-in', $privkey_pem, + '-outform', 'DER', '-out', $privkey_der])), + "Convert private key to DER"); + ok(run(app(['openssl', 'pkey', '-in', $pubkey_pem, '-pubin', + '-outform', 'DER', '-pubout', '-out', $pubkey_der])), + "Convert public key to DER"); + + ok(run(app(['openssl', 'dgst', '-sign', $privkey_der, '-keyform', 'DER', + '-out', $sigfile, + $data_to_sign])), + "Generating signature with DER private key via -keyform"); + + ok(run(app(['openssl', 'dgst', '-verify', $pubkey_der, '-keyform', 'DER', + '-signature', $sigfile, + $data_to_sign])), + "Verify signature with DER public key via -keyform"); +}; + subtest "signing using the nonce-type sigopt" => sub { if (disabled("ec")) { plan tests => 1; From 4bf85819b7cba298108ea03f3e2aa067f20ded72 Mon Sep 17 00:00:00 2001 From: Billy Brumley Date: Tue, 14 Jul 2026 01:45:04 -0400 Subject: [PATCH 285/349] [providers/implementations/ciphers] GCM-SIV: reject out-of-order update calls For GCM-SIV: 1. AAD must precede the payload 2. the payload must be single shot (2) was already happening, this change moves from a silent fail to an explicit error message for multiple update calls on the payload. For (1), this change unifies the logic for (2) one level up in the wrapper. So the code previously allowed (1), and now errors out after this change. Follow-up to #31906 Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Nikola Pajkovsky Reviewed-by: Daniel Kubec Reviewed-by: Bob Beck MergeDate: Mon Jul 20 06:56:04 2026 (Merged from https://github.com/openssl/openssl/pull/31940) --- doc/man7/EVP_CIPHER-AES.pod | 7 ++++--- .../ciphers/cipher_aes_gcm_siv_hw.c | 17 +++++++++++++---- test/evp_extra_test.c | 1 - 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/doc/man7/EVP_CIPHER-AES.pod b/doc/man7/EVP_CIPHER-AES.pod index 6da3f96a2d..0cb6d20829 100644 --- a/doc/man7/EVP_CIPHER-AES.pod +++ b/doc/man7/EVP_CIPHER-AES.pod @@ -65,9 +65,10 @@ L. =head1 NOTES -The AES-SIV and AES-WRAP mode implementations do not support streaming. That -means to obtain correct results there can be only one L -or L call after the initialization of the context. +The AES-SIV, AES-WRAP, and GCM-SIV mode implementations do not support +streaming. That means to obtain correct results there can be only one +L or L call on the payload after +the initialization of the context. When wrapping with AES-WRAP-PAD ciphers, the output buffer must be at least I rounded up to the cipher block size (8 bytes) plus the block size. diff --git a/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c b/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c index 452c9f00fe..9622c2dcca 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c +++ b/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c @@ -15,6 +15,7 @@ #include "internal/deprecated.h" #include +#include #include #include #include "cipher_aes_gcm_siv.h" @@ -151,8 +152,6 @@ static int aes_gcm_siv_encrypt(PROV_AES_GCM_SIV_CTX *ctx, const unsigned char *i DECLARE_IS_ENDIAN; ctx->generated_tag = 0; - if (!ctx->speed && ctx->used_enc) - return 0; /* need to check the size of the input! */ if (len64 > ((int64_t)1 << 36)) return 0; @@ -212,8 +211,6 @@ static int aes_gcm_siv_decrypt(PROV_AES_GCM_SIV_CTX *ctx, const unsigned char *i DECLARE_IS_ENDIAN; ctx->generated_tag = 0; - if (!ctx->speed && ctx->used_dec) - return 0; /* need to check the size of the input! */ if (len64 > ((int64_t)1 << 36)) return 0; @@ -285,6 +282,18 @@ static int aes_gcm_siv_cipher(void *vctx, unsigned char *out, if (in == NULL) return aes_gcm_siv_finish(ctx); + /* + * SIV derives the CTR IV from the tag, which depends on the whole plaintext, + * so the payload cannot be streamed. + * Payload must arrive in a single update, after which the tag is fixed. + * Any later AAD or payload update is therefore out of order and errors out. + * The speed benchmark test is exempt. + */ + if (!ctx->speed && (ctx->used_enc || ctx->used_dec)) { + ERR_raise(ERR_LIB_PROV, PROV_R_UPDATE_CALL_OUT_OF_ORDER); + return 0; + } + /* Deal with associated data */ if (out == NULL) return aes_gcm_siv_aad(ctx, in, len); diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c index 5c37102088..22e4689982 100644 --- a/test/evp_extra_test.c +++ b/test/evp_extra_test.c @@ -6144,7 +6144,6 @@ static int test_evp_aead_late_aad(int idx) || info->mode == EVP_CIPH_GCM_MODE /* rejects, raises 102 PROV_R_CIPHER_OPERATION_FAILED */ || info->mode == EVP_CIPH_CCM_MODE /* fails at first AAD */ || info->mode == EVP_CIPH_OCB_MODE /* accepts late AAD */ - || info->mode == EVP_CIPH_GCM_SIV_MODE /* accepts late AAD */ /* skip TLS stitched MTE cipher */ || EVP_CIPHER_is_a(info->ciph, "AES-128-CBC-HMAC-SHA1") /* skip TLS stitched MTE cipher */ From d4357db45c936389da75fba3a95ed98e711865bd Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 13 Jul 2026 18:49:29 +0200 Subject: [PATCH 286/349] mkwraps: resolve system (libc) functions via compiler include paths WRAP[] lists mix OpenSSL functions with libc/POSIX ones such as read() or socket(), which mkwraps.pl reported as "declaration not found" since it only searched the project's INCLUDE[] directories. Fall back to the C compiler's default include search paths for functions missing from the project headers. They are queried lazily, only on such a miss, so the all-OpenSSL case still does not walk /usr/include. Parse the glibc trailing attribute macros (__THROW, __wur, ...) and restrict qualifier, and add --cc and --no-system. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Tomas Mraz Reviewed-by: Nikola Pajkovsky Reviewed-by: Paul Dale MergeDate: Mon Jul 20 07:00:57 2026 (Merged from https://github.com/openssl/openssl/pull/31936) --- test/unit/README.md | 8 ++- util/mkwraps.pl | 140 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 129 insertions(+), 19 deletions(-) diff --git a/test/unit/README.md b/test/unit/README.md index 42fd1873d5..b945b438ab 100644 --- a/test/unit/README.md +++ b/test/unit/README.md @@ -513,7 +513,9 @@ list is tedious and error-prone, so the helper script `util/mkwraps.pl` generates a first draft from the `build.info` declaration. It reads the `WRAP[]` list, searches the headers under the target's `INCLUDE[]` directories for each function's prototype, and emits matching wrap functions -and expectation helpers. +and expectation helpers. Functions not found there (typically libc/POSIX +functions such as `read` or `socket`) are looked up under the compiler's +default system include paths, and emitted with angle-bracket includes. ```console $ ./util/mkwraps.pl --build-info test/unit/build.info \ @@ -526,6 +528,10 @@ Useful options: `expect_*` helpers, or both (the default). * `--include DIR`: add an extra header search directory beyond those in `INCLUDE[]`. Cumulative. + * `--cc NAME`: C compiler queried for the system include paths (default + `$CC` or `cc`). + * `--no-system`: do not fall back to the compiler's system include + directories for functions missing from the project headers. * `--output FILE`: write to a file instead of standard output. * `--verbose`: report progress and where each prototype was found. diff --git a/util/mkwraps.pl b/util/mkwraps.pl index d7e8bcb7db..91a44cbb3f 100755 --- a/util/mkwraps.pl +++ b/util/mkwraps.pl @@ -18,6 +18,8 @@ my $target; my @extra_includes; my $output_file; my $mode = 'both'; +my $cc; +my $use_system = 1; my $verbose = 0; my $help = 0; @@ -26,10 +28,14 @@ GetOptions('build-info=s' => \$build_info_file, 'include=s' => \@extra_includes, 'output=s' => \$output_file, 'mode=s' => \$mode, + 'cc=s' => \$cc, + 'system!' => \$use_system, 'verbose' => \$verbose, 'help' => \$help) or die "Error in command line arguments\n"; +$cc = $ENV{CC} || 'cc' unless defined $cc; + sub help { print STDERR <<"EOF"; @@ -48,6 +54,14 @@ Options: --mode MODE What to emit: 'wraps', 'expects' or 'both'. Defaults to 'both'. + --cc NAME C compiler used to discover the default system + include search paths. Defaults to \$CC or 'cc'. + + --no-system Do not fall back to the compiler's default system + include directories. By default, functions not + found in the project headers (typically libc/POSIX + functions) are looked up there. + --verbose Print progress to stderr. --help Show this help text. @@ -60,6 +74,12 @@ mirroring how the compiler resolves via -I flags. The first header containing a matching declaration provides the prototype. +Functions not found in those directories (typically system functions +such as read() or socket()) are then looked up under the compiler's +default include search paths, as reported by the C compiler. System +prototypes are emitted with angle-bracket #include directives. Use +--no-system to disable this fallback. + The output is meant as a stub for further editing. Custom logic (out-parameters, variadic forwarding, side effects on globals) still needs to be written manually, and the emitted #include directives may @@ -120,18 +140,20 @@ print STDERR "Search dirs:\n ", join("\n ", @search_dirs), "\n" if $verbose; # Walk all search directories and get them in the order that level ones are # first followed by subdirs so if there are nested includes, we get the -# shortest ones first searched. +# shortest ones first searched. Each base is a [dir, is_system] pair and the +# is_system flag is carried onto every header found beneath it, so that system +# prototypes can later be emitted with angle-bracket includes. sub find_headers { my (@bases) = @_; my @found; - my @queue = map { [$_, ''] } @bases; + my @queue = map { [$_->[0], '', $_->[1]] } @bases; while (@queue) { my @next; my @level_files; foreach my $entry (@queue) { - my ($dir, $rel) = @$entry; + my ($dir, $rel, $sys) = @$entry; next unless -d $dir; opendir(my $dh, $dir) or next; foreach my $name (readdir $dh) { @@ -139,9 +161,9 @@ sub find_headers my $full = catfile($dir, $name); my $newrel = $rel eq '' ? $name : "$rel/$name"; if (-d $full) { - push @next, [$full, $newrel]; + push @next, [$full, $newrel, $sys]; } elsif (-f $full && $name =~ /\.h$/) { - push @level_files, [$full, $newrel]; + push @level_files, [$full, $newrel, $sys]; } } closedir $dh; @@ -153,7 +175,45 @@ sub find_headers return @found; } -my @search_files = find_headers(@search_dirs); +# Ask the C compiler for its default "#include <...>" search paths. Returns +# the list of existing directories, in search order. Empty on failure. +sub system_include_dirs +{ + my ($compiler) = @_; + my $out = `$compiler -xc -E -v /dev/null 2>&1`; + return () unless defined $out + && $out =~ /search starts here:(.*?)End of search list\./s; + my @dirs; + foreach my $line (split /\n/, $1) { + $line =~ s/^\s+//; + $line =~ s/\s+$//; + # clang annotates framework directories; skip those. + next if $line eq '' || $line =~ /\(framework directory\)$/; + push @dirs, $line if -d $line; + } + return @dirs; +} + +# Project headers are searched first (with is_system = 0) so that a project +# declaration always wins over a colliding system one. The system headers are +# appended lazily, only if some function is not found in the project headers. +my @search_files = find_headers(map { [$_, 0] } @search_dirs); +my $system_loaded = 0; + +sub load_system_headers +{ + return if $system_loaded; + $system_loaded = 1; + return unless $use_system; + my @sys_dirs = system_include_dirs($cc); + unless (@sys_dirs) { + warn "WARNING: could not determine system include dirs from '$cc'\n"; + return; + } + print STDERR "System include dirs:\n ", join("\n ", @sys_dirs), "\n" + if $verbose; + push @search_files, find_headers(map { [$_, 1] } @sys_dirs); +} my %file_cache; @@ -184,12 +244,41 @@ sub strip_attribute_macros return $s; } +# Consume reserved-namespace decorations between a declaration's closing +# parenthesis and its semicolon, e.g. glibc's __THROW, __wur or +# __attr_access ((...)). Only __-prefixed tokens (with an optional balanced +# argument list) are eaten, so a genuine following declaration is left alone. +sub skip_trailing_attributes +{ + my $s = shift; + while (1) { + $s =~ s/^\s+//; + last unless $s =~ /^(__\w+)/; + $s = substr($s, length($1)); + $s =~ s/^\s+//; + if ($s =~ /^\(/) { + my $depth = 0; + my $i = 0; + while ($i < length($s)) { + my $c = substr($s, $i, 1); + $depth++ if $c eq '('; + $depth-- if $c eq ')'; + $i++; + last if $depth == 0; + } + return $s if $depth != 0; + $s = substr($s, $i); + } + } + return $s; +} + sub find_function_decl { my ($funcname) = @_; foreach my $entry (@search_files) { - my ($file, $relpath) = @$entry; + my ($file, $relpath, $is_system) = @$entry; unless (exists $file_cache{$file}) { my $text = ''; if (open(my $fh, '<', $file)) { @@ -218,9 +307,10 @@ sub find_function_decl my $params_str = substr($text, $paren_start, $cursor - $paren_start - 1); - # What follows must be ; for this to be a declaration. + # What follows must be ; for this to be a declaration, possibly + # after trailing attribute macros (__THROW, __wur, ...). my $after = substr($text, $cursor); - $after =~ s/^\s+//; + $after = skip_trailing_attributes($after); next unless $after =~ /^;/; # Anything since the previous statement terminator is the return @@ -239,7 +329,8 @@ sub find_function_decl rettype => $rettype, params => $params_str, file => $file, - include_path => $include_path }; + include_path => $include_path, + system => $is_system }; } } return undef; @@ -282,6 +373,9 @@ sub parse_param return { type => '', name => '', is_variadic => 1, is_ptr => 0 } if $param eq '...'; + # Drop the restrict qualifier; it plays no role in a mock signature. + $param =~ s/\b(?:__restrict(?:__)?|restrict)\b//g; + # Reduce TYPE NAME[size] to TYPE * NAME for our purposes. my $is_array = 0; $is_array = 1 if $param =~ s/\[\s*[^\]]*\s*\]\s*$//; @@ -296,6 +390,10 @@ sub parse_param $name = ''; } + # System headers name parameters in the reserved __ namespace; strip the + # leading underscores so the generated wrap uses ordinary local names. + $name =~ s/^_+//; + return { type => $type, name => $name, is_ptr => (($type =~ /\*/) || $is_array) ? 1 : 0, @@ -320,6 +418,11 @@ my @found_includes; my %seen_include; foreach my $func (@wraps) { my $info = find_function_decl($func); + if (!defined $info && !$system_loaded) { + # Not in the project headers: pull in the system ones and retry. + load_system_headers(); + $info = find_function_decl($func); + } unless (defined $info) { warn "WARNING: $func: declaration not found in any include dir\n"; next; @@ -340,7 +443,8 @@ foreach my $func (@wraps) { unless ($seen_include{$info->{include_path}}) { $seen_include{$info->{include_path}} = 1; - push @found_includes, $info->{include_path}; + push @found_includes, { path => $info->{include_path}, + system => $info->{system} }; } print STDERR " found $func in $info->{file}\n" if $verbose; } @@ -369,19 +473,19 @@ EOF print $out_fh "#include \n"; print $out_fh "\n"; if (@found_includes) { - my @system; + my @angle; my @local; - foreach my $inc (sort @found_includes) { - if ($inc =~ m|^openssl/|) { - push @system, $inc; + foreach my $inc (sort { $a->{path} cmp $b->{path} } @found_includes) { + if ($inc->{system} || $inc->{path} =~ m|^openssl/|) { + push @angle, $inc->{path}; } else { - push @local, $inc; + push @local, $inc->{path}; } } - foreach my $inc (@system) { + foreach my $inc (@angle) { print $out_fh "#include <$inc>\n"; } - print $out_fh "\n" if @system && @local; + print $out_fh "\n" if @angle && @local; foreach my $inc (@local) { print $out_fh "#include \"$inc\"\n"; } From 5b445cd96a3de836c28105d1583f62472e8cc7e6 Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Sun, 12 Jul 2026 14:21:35 +1000 Subject: [PATCH 287/349] Fix removal of sole key exchange group in tuple - Don't attempt to float its keyshare if any - Include active tuple in memmove() that excises a newly empty closed tuple. Add comments to clarify the logic. Reviewed-by: Nikola Pajkovsky Reviewed-by: Milan Broz Reviewed-by: Norbert Pocs MergeDate: Mon Jul 20 07:12:30 2026 (Merged from https://github.com/openssl/openssl/pull/31926) --- ssl/t1_lib.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index 5d0276a5a1..f8056c82da 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -1255,6 +1255,17 @@ static const char prefixes[] = { TUPLE_DELIMITER_CHARACTER, * Those callback functions are (indirectly) called by CONF_parse_list with * different separators (nominally ':' or '/'), a variable based on gid_cb_st * is used to keep track of the parsing results between the various calls + * + * Bookkeeping invariants maintained throughout parsing (see gid_cb_st below): + * - gid_arr[0..gidcnt) is the flat list of groups, partitioned into tuples in + * order: tuple t occupies a contiguous run of tuplcnt_arr[t] entries. + * - The per-tuple counts therefore sum to the group count: + * sum(tuplcnt_arr[0..tplcnt]) == gidcnt + * (indices 0..tplcnt-1 are closed tuples, index tplcnt is the active one). + * - ksid_arr[0..ksidcnt) holds keyshare group IDs; each is one of the groups + * in gid_arr and they appear in the same relative order as their groups. + * Every add/remove path must preserve these; an OOB read in the remove path + * (GitHub #31315) was a symptom of the first invariant being violated. */ typedef struct { @@ -1542,9 +1553,16 @@ static int gid_cb(const char *elem, int len, void *arg) * Otherwise, iterate through the tuple check whether any keyshares * remain *after* the index of the group we're removing. The first * of these, if any, is at index `k+1` in the keyshare list, which - * is the only slow we need to check. + * is the only slot we need to check. + * + * If the removal emptied the tuple (tuplcnt_arr[j] == 0 after the + * decrement above) there is no remaining group to float onto: + * gid_arr[tpl_start_idx] would now name a group belonging to the + * next tuple (or be past gid_arr entirely). Drop the keyshare in + * that case too. */ - drop_ks = ks_check_idx > tpl_start_idx || j >= garg->tplcnt; + drop_ks = ks_check_idx > tpl_start_idx || j >= garg->tplcnt + || garg->tuplcnt_arr[j] == 0; if (!drop_ks) { size_t end; /* End index of affected tuple */ @@ -1573,11 +1591,19 @@ static int gid_cb(const char *elem, int len, void *arg) * Adjust closed or current tuple's group count, if a closed tuple * count reaches zero excise the resulting empty tuple. The current * (not yet closed) tuple at the end of the list stays even if empty. + * + * The active tuple lives at index tplcnt, so the slots in use are + * tuplcnt_arr[0..tplcnt] (tplcnt + 1 entries). Excising closed tuple + * j must therefore shift the closed tuples j+1..tplcnt-1 *and* the + * active tuple at index tplcnt down by one, i.e. (tplcnt - j) entries + * counted with the pre-decrement tplcnt. Decrement tplcnt only after + * the move so the active-tuple slot is not left behind (which would + * inflate the per-tuple counts and desynchronise them from gid_arr). */ if (garg->tuplcnt_arr[j] == 0 && j < garg->tplcnt) { - garg->tplcnt--; memmove(garg->tuplcnt_arr + j, garg->tuplcnt_arr + j + 1, (garg->tplcnt - j) * sizeof(size_t)); + garg->tplcnt--; } } else { /* Processing addition of a single new group */ From 5bf14bcaae73dd9328c09e6a206514f19e5da7ad Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Mon, 13 Jul 2026 21:31:24 +1000 Subject: [PATCH 288/349] Extend TLS group list parser test cases The new edge case tests cover potential parser invariant violations fixed in the prior commit. Reviewed-by: Nikola Pajkovsky Reviewed-by: Milan Broz Reviewed-by: Norbert Pocs MergeDate: Mon Jul 20 07:12:31 2026 (Merged from https://github.com/openssl/openssl/pull/31926) --- test/build.info | 5 + test/recipes/70-test_tls_groups_list.t | 17 + test/tls_groups_list_test.c | 411 +++++++++++++++++++++++++ 3 files changed, 433 insertions(+) create mode 100644 test/recipes/70-test_tls_groups_list.t create mode 100644 test/tls_groups_list_test.c diff --git a/test/build.info b/test/build.info index 5c730f5482..4fbe25030f 100644 --- a/test/build.info +++ b/test/build.info @@ -1179,6 +1179,11 @@ IF[{- !$disabled{tests} -}] INCLUDE[ssl_old_test]=.. ../include ../apps/include DEPEND[ssl_old_test]=../libcrypto.a ../libssl.a libtestutil.a + PROGRAMS{noinst}=tls_groups_list_test + SOURCE[tls_groups_list_test]=tls_groups_list_test.c + INCLUDE[tls_groups_list_test]=.. ../include ../apps/include + DEPEND[tls_groups_list_test]=../libcrypto.a ../libssl.a libtestutil.a + PROGRAMS{noinst}=ext_internal_test SOURCE[ext_internal_test]=ext_internal_test.c INCLUDE[ext_internal_test]=.. ../include ../apps/include diff --git a/test/recipes/70-test_tls_groups_list.t b/test/recipes/70-test_tls_groups_list.t new file mode 100644 index 0000000000..e200a63d0c --- /dev/null +++ b/test/recipes/70-test_tls_groups_list.t @@ -0,0 +1,17 @@ +#! /usr/bin/env perl +# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +use OpenSSL::Test::Simple; +use OpenSSL::Test qw/:DEFAULT/; +use OpenSSL::Test::Utils qw(disabled); + +setup("test_tls_groups_list"); + +plan skip_all => "needs EC and ECX enabled" if disabled("ecx"); + +simple_test("test_tls_groups_list", "tls_groups_list_test"); diff --git a/test/tls_groups_list_test.c b/test/tls_groups_list_test.c new file mode 100644 index 0000000000..9770393634 --- /dev/null +++ b/test/tls_groups_list_test.c @@ -0,0 +1,411 @@ +/* + * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * Tests for the TLS supported-groups list parser (tls1_set_groups_list()), + * driven through the public SSL_CTX_set1_groups_list() entry point. + * + * The parser maintains three flat arrays and their bookkeeping in SSL_CTX: + * ctx->ext.supportedgroups[0..supportedgroups_len) - groups, in order + * ctx->ext.tuples[0..tuples_len) - group count per tuple + * ctx->ext.keyshares[0..keyshares_len) - keyshare group IDs + * with the governing invariant that the per-tuple counts sum to the group + * count: sum(tuples) == supportedgroups_len. Those fields are not visible + * through the public API, so we include ssl_local.h and check them directly. + * + * Several of the cases below are regressions for GitHub #31315, where the + * remove-group path could leave tuples/keyshares out of step with the group + * array (manifesting as an out-of-bounds read under a sanitizer). + */ + +#include +#include "internal/nelem.h" +#include "internal/tlsgroups.h" +#include "../ssl/ssl_local.h" +#include "testutil.h" + +#define MAX_GROUPS 8 +#define MAX_TUPLES 8 +#define MAX_KS 8 + +/* + * Sentinel used in ctx->ext.keyshares to mean "a single keyshare from the + * first supported group" (set when no '*' prefix appears anywhere). + */ +#define KS_FIRST 0 + +typedef struct { + const char *desc; + const char *list; /* input passed to set1_groups_list */ + uint16_t groups[MAX_GROUPS]; /* expected groups, in order */ + size_t ngroups; + size_t tuples[MAX_TUPLES]; /* expected per-tuple group counts */ + size_t ntuples; + uint16_t keyshares[MAX_KS]; /* expected keyshares (KS_FIRST == 0) */ + size_t nkeyshares; +} TESTCASE; + +static const TESTCASE cases[] = { + /* --- Well-formed baselines --------------------------------------- */ + { + "single tuple, two groups, implicit keyshare", + "X25519:prime256v1", + { OSSL_TLS_GROUP_ID_x25519, OSSL_TLS_GROUP_ID_secp256r1 }, + 2, + { 2 }, + 1, + { KS_FIRST }, + 1, + }, + { + "two tuples, implicit keyshare", + "X25519/prime256v1", + { OSSL_TLS_GROUP_ID_x25519, OSSL_TLS_GROUP_ID_secp256r1 }, + 2, + { 1, 1 }, + 2, + { KS_FIRST }, + 1, + }, + { + "explicit keyshare prefix", + "*X25519:prime256v1", + { OSSL_TLS_GROUP_ID_x25519, OSSL_TLS_GROUP_ID_secp256r1 }, + 2, + { 2 }, + 1, + { OSSL_TLS_GROUP_ID_x25519 }, + 1, + }, + + /* --- #31315: removal that empties a *closed* tuple --------------- */ + { + /* + * -X25519 empties closed tuple 0; it must be excised and the + * active-tuple counter shifted down (was: "1 group, 0 tuples"). + */ + "remove empties closed tuple (excision)", + "X25519/prime256v1:-X25519", + { OSSL_TLS_GROUP_ID_secp256r1 }, + 1, + { 1 }, + 1, + { KS_FIRST }, + 1, + }, + { + /* + * Removed group carried the keyshare and its tuple empties: the + * keyshare must be dropped, not floated onto another tuple's group + * (was: "1 group, tuples {1,1}", keyshare pointing at prime256v1). + */ + "remove keyshared group empties tuple (drop, not float)", + "*X25519/prime256v1/-X25519", + { OSSL_TLS_GROUP_ID_secp256r1 }, + 1, + { 1 }, + 1, + { KS_FIRST }, + 1, + }, + { + /* + * Two removals, each emptying a distinct closed tuple (was: + * "3 groups but tuple counts summing to 5"). + */ + "two removals empty two closed tuples", + "X25519/secp256r1:secp384r1:secp521r1/*X448:-X25519/-X448", + { OSSL_TLS_GROUP_ID_secp256r1, OSSL_TLS_GROUP_ID_secp384r1, + OSSL_TLS_GROUP_ID_secp521r1 }, + 3, + { 3 }, + 1, + { KS_FIRST }, + 1, + }, + + /* --- Removal from the *active* tuple (no excision) --------------- */ + { + "remove empties the active tuple only", + "X25519:-X25519", + { 0 }, + 0, + { 0 }, + 0, + { 0 }, + 0, + }, + { + "closed tuple intact, active tuple emptied and discarded", + "X25519/prime256v1:-prime256v1", + { OSSL_TLS_GROUP_ID_x25519 }, + 1, + { 1 }, + 1, + { KS_FIRST }, + 1, + }, + { + "keyshared group removed from active tuple", + "X25519/secp384r1/*X448:-X448", + { OSSL_TLS_GROUP_ID_x25519, OSSL_TLS_GROUP_ID_secp384r1 }, + 2, + { 1, 1 }, + 2, + { KS_FIRST }, + 1, + }, + + /* --- Legitimate keyshare retention (tuple not emptied) ----------- */ + { + /* + * Removing the keyshared X25519 from a tuple that still has + * prime256v1: prime256v1's own keyshare is retained. + */ + "remove one of two keyshares, tuple survives", + "*X25519:*prime256v1:-X25519", + { OSSL_TLS_GROUP_ID_secp256r1 }, + 1, + { 1 }, + 1, + { OSSL_TLS_GROUP_ID_secp256r1 }, + 1, + }, + + /* --- Keyshare floats *within* its own (surviving, closed) tuple --- */ + { + /* + * X25519 carried the keyshare in closed tuple 0 {X25519,secp384r1}; + * removing it must float the keyshare to secp384r1 (the remaining + * group of tuple 0), NOT to secp256r1 which is in tuple 1. + */ + "keyshare floats to remaining group of same tuple", + "*X25519:secp384r1/secp256r1:-X25519", + { OSSL_TLS_GROUP_ID_secp384r1, OSSL_TLS_GROUP_ID_secp256r1 }, + 2, + { 1, 1 }, + 2, + { OSSL_TLS_GROUP_ID_secp384r1 }, + 1, + }, + { + /* + * Removed keyshared group is in the middle of tuple 0; its keyshare + * floats to the tuple's first group (X25519), and tuple 1's own + * keyshare (secp256r1) is untouched. A float that escaped tuple 0 + * would corrupt this to a different keyshare set. + */ + "mid-tuple keyshare floats to tuple head, not across tuples", + "X25519:*X448:secp384r1 / *secp256r1:-X448", + { OSSL_TLS_GROUP_ID_x25519, OSSL_TLS_GROUP_ID_secp384r1, + OSSL_TLS_GROUP_ID_secp256r1 }, + 3, + { 2, 1 }, + 2, + { OSSL_TLS_GROUP_ID_x25519, OSSL_TLS_GROUP_ID_secp256r1 }, + 2, + }, +}; + +/* + * Assert every structural invariant the parser must maintain, from the parsed + * state alone (independent of the specific input): + * + * 1. Partition: sum(tuples[0..tuples_len)) == supportedgroups_len. + * 2. No empty tuples survive the final compaction (every count > 0). + * 3. Groups are distinct. + * 4. Keyshares are either the lone "first group" sentinel {0}, or a set of + * distinct non-zero group IDs that appear as an ordered subsequence of + * the supported groups (never the sentinel mixed with real IDs). + * + * These are exactly the properties that were violated by GitHub #31315 (a + * broken partition led to an out-of-bounds read in the remove path). + */ +static int check_invariants(SSL_CTX *ctx) +{ + size_t i, j, sum; + int ok = 1; + + /* 1 + 2: partition, with no zero-count tuple left behind. */ + for (i = 0, sum = 0; i < ctx->ext.tuples_len; i++) { + if (!TEST_size_t_gt(ctx->ext.tuples[i], 0)) { + TEST_error("zero-count tuple at index %zu survived", i); + ok = 0; + } + sum += ctx->ext.tuples[i]; + } + if (!TEST_size_t_eq(sum, ctx->ext.supportedgroups_len)) + ok = 0; + + /* 3: groups distinct. */ + for (i = 0; i < ctx->ext.supportedgroups_len; i++) + for (j = 0; j < i; j++) + if (!TEST_uint_ne(ctx->ext.supportedgroups[i], + ctx->ext.supportedgroups[j])) + ok = 0; + + /* 4: keyshare shape. */ + if (ctx->ext.keyshares_len == 1 && ctx->ext.keyshares[0] == KS_FIRST) { + /* Sentinel form: a single implicit keyshare from the first group. */ + } else { + size_t g = 0; + + for (i = 0; i < ctx->ext.keyshares_len; i++) { + uint16_t ks = ctx->ext.keyshares[i]; + + if (!TEST_uint_ne(ks, KS_FIRST)) { /* sentinel must be alone */ + ok = 0; + continue; + } + for (j = 0; j < i; j++) /* distinct */ + if (!TEST_uint_ne(ks, ctx->ext.keyshares[j])) + ok = 0; + while (g < ctx->ext.supportedgroups_len + && ctx->ext.supportedgroups[g] != ks) + g++; /* ordered subsequence */ + if (!TEST_size_t_lt(g, ctx->ext.supportedgroups_len)) { + TEST_error("keyshare 0x%04X not an in-order group", ks); + ok = 0; + break; + } + g++; + } + } + + return ok; +} + +static int run_case(int idx) +{ + const TESTCASE *tc = &cases[idx]; + SSL_CTX *ctx = NULL; + int ret = 0; + size_t i; + + TEST_info("case %d: %s [\"%s\"]", idx, tc->desc, tc->list); + + if (!TEST_ptr(ctx = SSL_CTX_new(TLS_method()))) + goto end; + + if (!TEST_int_eq(SSL_CTX_set1_groups_list(ctx, tc->list), 1)) + goto end; + + /* Groups: exact contents and order. */ + if (!TEST_size_t_eq(ctx->ext.supportedgroups_len, tc->ngroups)) + goto end; + for (i = 0; i < tc->ngroups; i++) + if (!TEST_uint_eq(ctx->ext.supportedgroups[i], tc->groups[i])) + goto end; + + /* Tuples: exact per-tuple counts. */ + if (!TEST_size_t_eq(ctx->ext.tuples_len, tc->ntuples)) + goto end; + for (i = 0; i < tc->ntuples; i++) + if (!TEST_size_t_eq(ctx->ext.tuples[i], tc->tuples[i])) + goto end; + + /* Keyshares: exact contents (KS_FIRST == 0 sentinel). */ + if (!TEST_size_t_eq(ctx->ext.keyshares_len, tc->nkeyshares)) + goto end; + for (i = 0; i < tc->nkeyshares; i++) + if (!TEST_uint_eq(ctx->ext.keyshares[i], tc->keyshares[i])) + goto end; + + if (!check_invariants(ctx)) + goto end; + + ret = 1; +end: + SSL_CTX_free(ctx); + return ret; +} + +/* + * Synthetic edge-case forms. We do not spell out the exact parsed result for + * these (that would just re-derive the parser); instead we assert the parse + * succeeds or fails as expected and, on success, that all invariants hold. + * These deliberately stress the corners of the remove/dedup/keyshare paths. + */ +typedef struct { + const char *list; + int expect_ok; /* 1: parse succeeds; 0: syntax/parse error */ +} EDGECASE; + +static const EDGECASE edgecases[] = { + /* --- valid, invariant-preserving corner cases --- */ + { "X25519", 1 }, + { "X25519:secp256r1:secp384r1:secp521r1:X448", 1 }, /* one full tuple */ + { "X25519/secp256r1/secp384r1/secp521r1/X448", 1 }, /* many tuples */ + { "*X25519:secp256r1", 1 }, + { "X25519:X25519", 1 }, /* dup within tuple */ + { "X25519/X25519", 1 }, /* dup across tuples */ + { "X25519:-secp384r1", 1 }, /* remove absent: no-op */ + { "X25519:-X25519", 1 }, /* empty the active tuple */ + { "X25519/secp256r1:-X25519", 1 }, /* excise closed tuple */ + { "X25519/secp256r1/secp384r1:-secp256r1", 1 }, /* excise middle tuple */ + { "X25519:secp256r1/secp384r1:-secp384r1", 1 }, /* empty active, discard */ + { "*X25519/prime256v1/-X25519", 1 }, /* #31315: drop, not float */ + { "X25519/secp256r1:secp384r1:secp521r1/*X448:-X25519/-X448", 1 }, /* #31315 */ + { "*X25519:*secp256r1:-X25519", 1 }, /* keyshare survives sibling */ + { "X25519/secp256r1:-X25519:secp384r1", 1 }, /* excise then refill */ + { "X25519:secp256r1:X448:-X25519:-X448", 1 }, /* multiple removals */ + { "?*BOGUS:X25519 / *secp256r1", 1 }, /* stacked prefix, unknown */ + { "X25519:?BOGUS:secp256r1", 1 }, /* ignore unknown mid-tuple */ + { "*X25519:DEFAULT:-secp256r1:-X448", 1 }, /* DEFAULT + removals */ + { "DEFAULT:-X25519:-?curveSM2:-?ffdhe2048:-?ffdhe3072", 1 }, + { "secp256r1:DEFAULT", 1 }, /* prepend then DEFAULT */ + + /* --- expected syntax / parse errors --- */ + { "X25519//secp256r1", 0 }, /* empty tuple */ + { "X25519::secp256r1", 0 }, /* empty group */ + { ":X25519", 0 }, + { "X25519:", 0 }, + { "/X25519", 0 }, + { "X25519/", 0 }, + { "**X25519", 0 }, /* double keyshare prefix */ + { "??X25519", 0 }, + { "--X25519", 0 }, + { "X25519:NOTAREALGROUP", 0 }, /* unknown w/o '?' */ + { "-DEFAULT", 0 }, /* prefix on pseudo-group */ + { "?DEFAULT", 0 }, +}; + +static int run_edge(int idx) +{ + const EDGECASE *tc = &edgecases[idx]; + SSL_CTX *ctx = NULL; + int ret = 0, r; + + TEST_info("edge %d: [\"%s\"] expect %s", idx, tc->list, + tc->expect_ok ? "ok" : "error"); + + if (!TEST_ptr(ctx = SSL_CTX_new(TLS_method()))) + goto end; + + r = SSL_CTX_set1_groups_list(ctx, tc->list); + if (tc->expect_ok) { + if (!TEST_int_eq(r, 1) || !check_invariants(ctx)) + goto end; + } else { + if (!TEST_int_eq(r, 0)) + goto end; + } + + ret = 1; +end: + SSL_CTX_free(ctx); + return ret; +} + +int setup_tests(void) +{ + ADD_ALL_TESTS(run_case, (int)OSSL_NELEM(cases)); + ADD_ALL_TESTS(run_edge, (int)OSSL_NELEM(edgecases)); + return 1; +} From 13bef2616f3435429acfe2af21955bbbb0aaa2a0 Mon Sep 17 00:00:00 2001 From: Norbert Pocs Date: Tue, 14 Jul 2026 14:31:24 +0200 Subject: [PATCH 289/349] tests: Check the return value of BN_hex2bn() Fixes coverity issues 1398635, 1696548, 1696549. Signed-off-by: Norbert Pocs Reviewed-by: Nikola Pajkovsky Reviewed-by: Tim Hudson Reviewed-by: Paul Dale Reviewed-by: Eugene Syromiatnikov MergeDate: Mon Jul 20 09:04:13 2026 (Merged from https://github.com/openssl/openssl/pull/31943) --- test/bntest.c | 19 ++++++++++--------- test/ectest.c | 22 ++++++++++++---------- test/srptest.c | 19 ++++++++++--------- 3 files changed, 32 insertions(+), 28 deletions(-) diff --git a/test/bntest.c b/test/bntest.c index 5c8f76e1bc..d15af9dbe5 100644 --- a/test/bntest.c +++ b/test/bntest.c @@ -1800,15 +1800,16 @@ static int file_modexp(STANZA *s) } /* Regression test for carry propagation bug in sqr8x_reduction */ - BN_hex2bn(&a, "050505050505"); - BN_hex2bn(&b, "02"); - BN_hex2bn(&c, - "4141414141414141414141274141414141414141414141414141414141414141" - "4141414141414141414141414141414141414141414141414141414141414141" - "4141414141414141414141800000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000001"); + if (!TEST_true(BN_hex2bn(&a, "050505050505")) + || !TEST_true(BN_hex2bn(&b, "02")) + || !TEST_true(BN_hex2bn(&c, + "4141414141414141414141274141414141414141414141414141414141414141" + "4141414141414141414141414141414141414141414141414141414141414141" + "4141414141414141414141800000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000001"))) + goto err; if (!TEST_true(BN_mod_exp(d, a, b, c, ctx)) || !TEST_true(BN_mul(e, a, a, ctx)) || !TEST_BN_eq(d, e)) diff --git a/test/ectest.c b/test/ectest.c index 36fcb50089..363d119c96 100644 --- a/test/ectest.c +++ b/test/ectest.c @@ -1127,16 +1127,18 @@ static int group_field_test(void) EC_GROUP *secp521r1_group = NULL; EC_GROUP *sect163r2_group = NULL; - BN_hex2bn(&secp521r1_field, - "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - "FFFF"); - - BN_hex2bn(§163r2_field, - "08000000000000000000000000000000" - "00000000C9"); + if (!TEST_true(BN_hex2bn(&secp521r1_field, + "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFF")) + || !TEST_true(BN_hex2bn(§163r2_field, + "08000000000000000000000000000000" + "00000000C9"))) { + BN_free(secp521r1_field); + return 0; + } secp521r1_group = EC_GROUP_new_by_curve_name(NID_secp521r1); if (BN_cmp(secp521r1_field, EC_GROUP_get0_field(secp521r1_group))) diff --git a/test/srptest.c b/test/srptest.c index 0fceadd24b..d7620f95f9 100644 --- a/test/srptest.c +++ b/test/srptest.c @@ -149,11 +149,10 @@ static int run_srp_kat(void) /* use builtin 1024-bit params */ const SRP_gN *GN; - if (!TEST_ptr(GN = SRP_get_default_gN("1024"))) - goto err; - BN_hex2bn(&s, "BEB25379D1A8581EB5A727673A2441EE"); - /* Set up server's password entry */ - if (!TEST_true(SRP_create_verifier_BN("alice", "password123", &s, &v, GN->N, + if (!TEST_ptr(GN = SRP_get_default_gN("1024")) + || !TEST_true(BN_hex2bn(&s, "BEB25379D1A8581EB5A727673A2441EE")) + /* Set up server's password entry */ + || !TEST_true(SRP_create_verifier_BN("alice", "password123", &s, &v, GN->N, GN->g))) goto err; @@ -168,8 +167,9 @@ static int run_srp_kat(void) TEST_note(" okay"); /* Server random */ - BN_hex2bn(&b, "E487CB59D31AC550471E81F00F6928E01DDA08E974A004F49E61F5D1" - "05284D20"); + if (!TEST_true(BN_hex2bn(&b, "E487CB59D31AC550471E81F00F6928E01DDA08E974A004F49E61F5D1" + "05284D20"))) + goto err; /* Server's first message */ Bpub = SRP_Calc_B(b, GN->N, GN->g, v); @@ -187,8 +187,9 @@ static int run_srp_kat(void) TEST_note(" okay"); /* Client random */ - BN_hex2bn(&a, "60975527035CF2AD1989806F0407210BC81EDC04E2762A56AFD529DD" - "DA2D4393"); + if (!TEST_true(BN_hex2bn(&a, "60975527035CF2AD1989806F0407210BC81EDC04E2762A56AFD529DD" + "DA2D4393"))) + goto err; /* Client's response */ Apub = SRP_Calc_A(a, GN->N, GN->g); From 7f58002be84c84e0765a6b481f8162df6527d498 Mon Sep 17 00:00:00 2001 From: Frederik Wedel-Heinen Date: Wed, 24 Jun 2026 14:40:52 +0200 Subject: [PATCH 290/349] Removes unused functions and macros from ssl_local.h and recmethod_local.h Reviewed-by: Tim Hudson Reviewed-by: Paul Dale MergeDate: Mon Jul 20 09:06:08 2026 (Merged from https://github.com/openssl/openssl/pull/31719) --- ssl/record/methods/recmethod_local.h | 1 - ssl/ssl_local.h | 17 ----------------- 2 files changed, 18 deletions(-) diff --git a/ssl/record/methods/recmethod_local.h b/ssl/record/methods/recmethod_local.h index c5cdfc6cfc..1bfea26631 100644 --- a/ssl/record/methods/recmethod_local.h +++ b/ssl/record/methods/recmethod_local.h @@ -456,7 +456,6 @@ int tls_read_record(OSSL_RECORD_LAYER *rl, void **rechandle, int *rversion, uint8_t *type, const unsigned char **data, size_t *datalen, uint16_t *epoch, unsigned char *seq_num); int tls_release_record(OSSL_RECORD_LAYER *rl, void *rechandle, size_t length); -int tls_default_set_protocol_version(OSSL_RECORD_LAYER *rl, int version); int tls_set_protocol_version(OSSL_RECORD_LAYER *rl, int version); void tls_set_plain_alerts(OSSL_RECORD_LAYER *rl, int allow); void tls_set_first_handshake(OSSL_RECORD_LAYER *rl, int first); diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h index 978f627290..aeb0c170ad 100644 --- a/ssl/ssl_local.h +++ b/ssl/ssl_local.h @@ -294,12 +294,6 @@ */ #define SSL_USE_SIGALGS(s) \ (SSL_CONNECTION_GET_SSL(s)->method->ssl3_enc->enc_flags & SSL_ENC_FLAG_SIGALGS) -/* - * Allow TLS 1.2 ciphersuites: applies to DTLS 1.2 as well as TLS 1.2: may - * apply to others in future. - */ -#define SSL_USE_TLS1_2_CIPHERS(s) \ - (SSL_CONNECTION_GET_SSL(s)->method->ssl3_enc->enc_flags & SSL_ENC_FLAG_TLS1_2_CIPHERS) #define IS_MAX_FRAGMENT_LENGTH_EXT_VALID(value) \ (((value) >= TLSEXT_max_fragment_length_512) && ((value) <= TLSEXT_max_fragment_length_4096)) @@ -800,16 +794,6 @@ typedef struct { uint32_t amask; /* authmask corresponding to key type */ } SSL_CERT_LOOKUP; -/* flags values */ -#define TLS_GROUP_TYPE 0x0000000FU /* Mask for group type */ -#define TLS_GROUP_CURVE_PRIME 0x00000001U -#define TLS_GROUP_CURVE_CHAR2 0x00000002U -#define TLS_GROUP_CURVE_CUSTOM 0x00000004U -#define TLS_GROUP_FFDHE 0x00000008U -#define TLS_GROUP_ONLY_FOR_TLS1_3 0x00000010U - -#define TLS_GROUP_FFDHE_FOR_TLS1_3 (TLS_GROUP_FFDHE | TLS_GROUP_ONLY_FOR_TLS1_3) - #if !defined(OPENSSL_NO_TLS1) \ || !defined(OPENSSL_NO_TLS1_1) \ || !defined(OPENSSL_NO_TLS1_2) \ @@ -2508,7 +2492,6 @@ void ssl_cert_set_cert_cb(CERT *c, int (*cb)(SSL *ssl, void *arg), void *arg); __owur int ssl_verify_cert_chain(SSL_CONNECTION *s, STACK_OF(X509) *sk); __owur int ssl_verify_rpk(SSL_CONNECTION *s, EVP_PKEY *rpk); -__owur int ssl_verify_ocsp(SSL *s, STACK_OF(X509) *sk); __owur int ssl_build_cert_chain(SSL_CONNECTION *s, SSL_CTX *ctx, int flags); __owur int ssl_cert_set_cert_store(CERT *c, X509_STORE *store, int chain, int ref); From d00cf645c1d631299843d7dda3a21f24ef5848f8 Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Wed, 15 Jul 2026 09:51:51 +0200 Subject: [PATCH 291/349] test: include PID in valgrind log file names Append the %p (process ID) placeholder to valgrind's --log-file in both app() and test(). When a test spawns multiple processes sharing the same result index, they previously wrote to the same log file and clobbered each other's output. Using %p gives each process its own log, so no valgrind findings are lost. Signed-off-by: Nikola Pajkovsky Reviewed-by: Tim Hudson Reviewed-by: Paul Dale Reviewed-by: Neil Horman MergeDate: Mon Jul 20 09:07:07 2026 (Merged from https://github.com/openssl/openssl/pull/31961) --- util/perl/OpenSSL/Test.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/perl/OpenSSL/Test.pm b/util/perl/OpenSSL/Test.pm index 0cdb41259c..570d3a8631 100644 --- a/util/perl/OpenSSL/Test.pm +++ b/util/perl/OpenSSL/Test.pm @@ -333,7 +333,7 @@ sub app { $idx=$idx+1; my $resultdir = result_dir(); my $srcdir = srctop_dir(); - return cmd([ "valgrind", "--leak-check=full", "--show-leak-kinds=all", "--gen-suppressions=all", "--suppressions=$srcdir/util/valgrind.suppression", "--log-file=$resultdir/valgrind.log.$idx", "--suppressions=$srcdir/util/valgrind.suppression", @prog, @cmdargs ], + return cmd([ "valgrind", "--leak-check=full", "--show-leak-kinds=all", "--gen-suppressions=all", "--suppressions=$srcdir/util/valgrind.suppression", "--log-file=$resultdir/valgrind.log.$idx.%p", "--suppressions=$srcdir/util/valgrind.suppression", @prog, @cmdargs ], exe_shell => $ENV{EXE_SHELL}, %opts) -> (shift); } else { return cmd([ @prog, @cmdargs ], @@ -363,7 +363,7 @@ sub test { $idx=$idx+1; my $resultdir = result_dir(); my $srcdir = srctop_dir(); - return cmd([ "valgrind", "--leak-check=full", "--show-leak-kinds=all", "--gen-suppressions=all", "--suppressions=$srcdir/util/valgrind.suppression", "--log-file=$resultdir/valgrind.log.$idx", "--suppressions=$srcdir/util/valgrind.suppression", @prog, @cmdargs ], + return cmd([ "valgrind", "--leak-check=full", "--show-leak-kinds=all", "--gen-suppressions=all", "--suppressions=$srcdir/util/valgrind.suppression", "--log-file=$resultdir/valgrind.log.$idx.%p", "--suppressions=$srcdir/util/valgrind.suppression", @prog, @cmdargs ], exe_shell => $ENV{EXE_SHELL}, %opts) -> (shift); } elsif (defined $ENV{OSSL_VALGRIND_CT}) { # Constant-time validation mode: mark secret data as undefined and From a20fa498281912c5a34d6d14d2cfb0538fd836cf Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Wed, 15 Jul 2026 09:41:06 +0200 Subject: [PATCH 292/349] test/p_ossltest: fix uninitialised bytes written in TLS1-AAD GCM mode The test provider's fake AES-128-GCM cipher works by running the real sub-cipher for its side effects and then copying the memdup'd input back over the output buffer, so the "encrypted" record is really the plaintext. In a TLS record the buffer handed to the cipher reserves an uninitialised 8-byte explicit IV at the front and a 16-byte auth tag at the end for the cipher to fill. OPENSSL_memdup() duplicated those uninitialised IV/tag regions, and the subsequent memcpy(out, inbuf, inl) copied them into out. That output was then written to the socket, triggering valgrind's "Syscall param write(buf) points to uninitialised byte(s)": at sock_write (bss_sock.c:155) by bwrite_conv (bio_meth.c:79) by BIO_write (bio_lib.c:397) by statem_flush (statem.c:963) by ossl_statem_client_post_work (statem_clnt.c:881) ... Track whether EVP_CTRL_AEAD_TLS1_AAD was set (recorded in set_ctx_params) and, when encrypting a TLS record, copy only the plaintext payload back, leaving the explicit IV and tag that the real sub-cipher actually produced intact. Signed-off-by: Nikola Pajkovsky Reviewed-by: Tim Hudson Reviewed-by: Paul Dale Reviewed-by: Neil Horman MergeDate: Mon Jul 20 09:07:08 2026 (Merged from https://github.com/openssl/openssl/pull/31961) --- test/p_ossltest.c | 45 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/test/p_ossltest.c b/test/p_ossltest.c index 2f545ebf16..06bd9bc64c 100644 --- a/test/p_ossltest.c +++ b/test/p_ossltest.c @@ -695,6 +695,7 @@ static const OSSL_DISPATCH ossl_testaes128_cbc_functions[] = { typedef struct { OSSL_LIB_CTX *libctx; EVP_CIPHER_CTX *sub_ctx; + int tls1_aad; } PROV_EVP_AES128_GCM_CTX; /** @@ -838,10 +839,16 @@ static int ossl_test_aes128gcm_update(void *vprovctx, char *out, size_t *outl, size_t inl) { PROV_EVP_AES128_GCM_CTX *ctx = (PROV_EVP_AES128_GCM_CTX *)vprovctx; - int ret, soutl; - uint8_t *inbuf; + int ret = 0, soutl = 0; + uint8_t *inbuf = NULL; - inbuf = OPENSSL_memdup(in, inl); + *outl = 0; + + if (in != NULL && inl > 0) { + inbuf = OPENSSL_memdup(in, inl); + if (inbuf == NULL) + goto end; + } if (EVP_CIPHER_CTX_is_encrypting(ctx->sub_ctx)) ret = EVP_EncryptUpdate(ctx->sub_ctx, (unsigned char *)out, @@ -849,16 +856,31 @@ static int ossl_test_aes128gcm_update(void *vprovctx, char *out, size_t *outl, else ret = EVP_DecryptUpdate(ctx->sub_ctx, (unsigned char *)out, &soutl, in, (int)inl); - *outl = soutl; /* * Once the cipher is complete, throw it away and use the * plaintext as our output */ - if (inbuf != NULL && out != NULL) - memcpy(out, inbuf, inl); - OPENSSL_free(inbuf); + if (ret > 0 && inbuf != NULL && out != NULL) { + if (ctx->tls1_aad && EVP_CIPHER_CTX_is_encrypting(ctx->sub_ctx)) { + if (inl < EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN) { + ret = 0; + goto end; + } + memcpy(out + EVP_GCM_TLS_EXPLICIT_IV_LEN, + inbuf + EVP_GCM_TLS_EXPLICIT_IV_LEN, + inl - EVP_GCM_TLS_EXPLICIT_IV_LEN - EVP_GCM_TLS_TAG_LEN); + } else { + memcpy(out, inbuf, inl); + } + } + + *outl = soutl; + +end: + ctx->tls1_aad = 0; + OPENSSL_free(inbuf); return ret; } @@ -955,8 +977,15 @@ static int ossl_test_aes128gcm_get_ctx_params(void *vprovctx, OSSL_PARAM params[ static int ossl_test_aes128gcm_set_ctx_params(void *vprovctx, const OSSL_PARAM params[]) { PROV_EVP_AES128_GCM_CTX *ctx = (PROV_EVP_AES128_GCM_CTX *)vprovctx; + int tls1_aad; + int ret; - return EVP_CIPHER_CTX_set_params(ctx->sub_ctx, params); + tls1_aad = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD) != NULL; + ret = EVP_CIPHER_CTX_set_params(ctx->sub_ctx, params); + if (ret) + ctx->tls1_aad = tls1_aad; + + return ret; } /** From 29616c15e476eab4a81a6af20259d7d1809d4e03 Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Wed, 15 Jul 2026 11:05:35 +0200 Subject: [PATCH 293/349] test: clean up thread-local key in QUIC radix tests The QUIC radix test creates a new thread-local key for each script but only clears the associated value during teardown. The key itself is never deleted. Delete the thread-local key after all child threads have joined and the main-thread value has been cleared. 1,536 bytes in 3 blocks are still reachable in loss record 873 of 915 at 0x488D0B0: calloc (in /usr/libexec/valgrind/vgpreload_memcheck-arm64-linux.so) by 0x49561CF: pthread_setspecific@@GLIBC_2.34 (in /usr/lib64/libc.so.6) by 0x5C0FD7: CRYPTO_THREAD_set_local (threads_pthread.c:1030) by 0x41132F: radix_thread_init (quic_bindings.c:570) by 0x4114F7: bindings_process_init (quic_bindings.c:604) by 0x422563: test_script (main.c:28) by 0x5152DF: run_tests (driver.c:518) by 0x517447: main (main.c:52) Signed-off-by: Nikola Pajkovsky Reviewed-by: Tim Hudson Reviewed-by: Paul Dale Reviewed-by: Neil Horman MergeDate: Mon Jul 20 09:07:10 2026 (Merged from https://github.com/openssl/openssl/pull/31961) --- test/radix/quic_bindings.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/radix/quic_bindings.c b/test/radix/quic_bindings.c index 02356f48eb..7dc46f7950 100644 --- a/test/radix/quic_bindings.c +++ b/test/radix/quic_bindings.c @@ -662,6 +662,9 @@ static int bindings_process_finish(int testresult_main) radix_thread_cleanup(); /* cleanup main thread */ RADIX_PROCESS_cleanup(&radix_process); + if (!TEST_true(CRYPTO_THREAD_cleanup_local(&radix_thread))) + testresult = 0; + if (testresult) BIO_printf(bio_err, "==> OK\n\n"); else From 1865b55846700f48e31b93d6df7b7f95ea07a602 Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Wed, 15 Jul 2026 11:23:09 +0200 Subject: [PATCH 294/349] test: update provider Valgrind suppression The internal provider test intentionally retains its configured provider when OPENSSL_cleanup() is skipped under OSSL_USE_VALGRIND. The existing suppression accounts for this state but no longer matches the provider activation stack because provider_init() appears between OSSL_provider_init() and provider_activate(). Add the missing frame so the provider context and its diagnostic strings are correctly suppressed. Signed-off-by: Nikola Pajkovsky Reviewed-by: Tim Hudson Reviewed-by: Paul Dale Reviewed-by: Neil Horman MergeDate: Mon Jul 20 09:07:11 2026 (Merged from https://github.com/openssl/openssl/pull/31961) --- util/valgrind.suppression | 1 + 1 file changed, 1 insertion(+) diff --git a/util/valgrind.suppression b/util/valgrind.suppression index 4514e702b0..e4f14372d4 100644 --- a/util/valgrind.suppression +++ b/util/valgrind.suppression @@ -121,6 +121,7 @@ fun:malloc ... fun:OSSL_provider_init + fun:provider_init fun:provider_activate ... } From e0bf61917aa5e9f75c0ee36d0809620823fe818f Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sat, 18 Jul 2026 00:09:30 +0200 Subject: [PATCH 295/349] apps: decode DTLSv1.2 records in s_client/s_server -msg output The msg_cb message callback used by the -msg option only recognised DTLSv1.0 among the DTLS versions, so DTLSv1.2 records were logged as "Not TLS data or unknown version" instead of being decoded. Add DTLS1_2_VERSION to the recognised version check and to the ssl_versions lookup table, and wrap the now-overlong condition. Add a test that runs s_client against s_server over TLSv1.2, TLSv1.3 and DTLSv1.2, logging the protocol messages via -msg, and checks that every record is decoded (no "Not TLS data or unknown version" lines). Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Tim Hudson Reviewed-by: Paul Dale Reviewed-by: Frederik Wedel-Heinen MergeDate: Mon Jul 20 09:09:07 2026 (Merged from https://github.com/openssl/openssl/pull/31994) --- apps/lib/s_cb.c | 6 +- test/recipes/20-test_app_s_client_msg.t | 110 ++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 test/recipes/20-test_app_s_client_msg.t diff --git a/apps/lib/s_cb.c b/apps/lib/s_cb.c index 4f2503502b..f83ffd7236 100644 --- a/apps/lib/s_cb.c +++ b/apps/lib/s_cb.c @@ -576,6 +576,7 @@ static STRINT_PAIR ssl_versions[] = { { "TLS 1.2", TLS1_2_VERSION }, { "TLS 1.3", TLS1_3_VERSION }, { "DTLS 1.0", DTLS1_VERSION }, + { "DTLS 1.2", DTLS1_2_VERSION }, { "DTLS 1.0 (bad)", DTLS1_BAD_VER }, { NULL } }; @@ -653,7 +654,10 @@ void msg_cb(int write_p, int version, int content_type, const void *buf, const char *str_version, *str_content_type = "", *str_details1 = "", *str_details2 = ""; const unsigned char *bp = buf; - if (version == TLS1_VERSION || version == TLS1_1_VERSION || version == TLS1_2_VERSION || version == TLS1_3_VERSION || version == DTLS1_VERSION || version == DTLS1_BAD_VER) { + if (version == TLS1_VERSION || version == TLS1_1_VERSION + || version == TLS1_2_VERSION || version == TLS1_3_VERSION + || version == DTLS1_VERSION || version == DTLS1_2_VERSION + || version == DTLS1_BAD_VER) { str_version = lookup(version, ssl_versions, "???"); switch (content_type) { case SSL3_RT_CHANGE_CIPHER_SPEC: diff --git a/test/recipes/20-test_app_s_client_msg.t b/test/recipes/20-test_app_s_client_msg.t new file mode 100644 index 0000000000..75d388efe8 --- /dev/null +++ b/test/recipes/20-test_app_s_client_msg.t @@ -0,0 +1,110 @@ +#! /usr/bin/env perl +# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +use strict; +use warnings; + +use IPC::Open3; +use OpenSSL::Test qw/:DEFAULT result_dir srctop_file bldtop_file/; +use OpenSSL::Test::Utils; + +my $test_name = "test_app_s_client_msg"; +setup($test_name); + +plan skip_all => "$test_name needs sock enabled" + if disabled("sock"); +plan skip_all => "$test_name is not available on Windows or VMS" + if $^O =~ /^(VMS|MSWin32|msys)$/; + +my $shlib_wrap = bldtop_file("util", "wrap.pl"); +my $apps_openssl = bldtop_file("apps", "openssl"); +my $server_pem = srctop_file("test", "certs", "servercert.pem"); +my $server_key = srctop_file("test", "certs", "serverkey.pem"); +my $resultdir = result_dir(); + +# Each case exercises the s_client message callback (-msg) over a different +# protocol version. Every record must be decoded; before the DTLSv1.2 fix such +# records were logged as "Not TLS data or unknown version". +my @cases = ( + { name => "TLSv1.2", flag => "-tls1_2", disabled => "tls1_2" }, + { name => "TLSv1.3", flag => "-tls1_3", disabled => "tls1_3" }, + { name => "DTLSv1.2", flag => "-dtls1_2", disabled => "dtls1_2" }, +); +@cases = grep { !disabled($_->{disabled}) } @cases; + +plan tests => scalar @cases; + +# Run one s_server/s_client handshake logging protocol messages via -msgfile. +# Returns the number of decoded and undecoded records seen in the log. +sub run_case +{ + my $case = shift; + my $msgfile = "$resultdir/s_client-msg-$case->{disabled}.txt"; + my ($records, $unknown) = (0, 0); + + eval { + local $SIG{ALRM} = sub { die "timeout\n" }; + alarm 60; + + # Start a server speaking just this protocol version + my @s_server_cmd = ("s_server", $case->{flag}, "-accept", "0", + "-naccept", "1", "-cert", $server_pem, + "-key", $server_key); + my $s_server_pid = open3(my $s_server_i, my $s_server_o, my $s_server_e, + $shlib_wrap, $apps_openssl, @s_server_cmd); + + # Figure out what port it is listening on + my $server_port = "0"; + while (<$s_server_o>) { + print($_); + chomp; + if (/^ACCEPT \S+?:(\d+)/) { + $server_port = $1; + last; + } elsif (/^Using default/) { + ; + } else { + last; + } + } + + # Connect a client that logs the protocol messages to a file. -msgfile + # sets the log destination but selects SSL_trace; the trailing -msg + # switches the callback back to msg_cb (the code under test) while + # keeping the file destination. + my @s_client_cmd = ("s_client", $case->{flag}, "-msgfile", $msgfile, + "-msg", "-connect", "localhost:$server_port"); + my $s_client_pid = open3(my $s_client_i, my $s_client_o, my $s_client_e, + $shlib_wrap, $apps_openssl, @s_client_cmd); + + # Quit the client once connected, then reap both processes + print $s_client_i "Q\n"; + waitpid($s_client_pid, 0); + kill 'HUP', $s_server_pid if kill 0, $s_server_pid; + waitpid($s_server_pid, 0); + + alarm 0; + }; + die $@ if $@ && $@ ne "timeout\n"; + print("TIMEOUT: $case->{name} timed out\n") if $@; + + if (open(my $fh, '<', $msgfile)) { + while (<$fh>) { + $records++ if /^(?:>>>|<<<)/; + $unknown++ if /Not TLS data or unknown version/; + } + close($fh); + } + return ($records, $unknown); +} + +foreach my $case (@cases) { + my ($records, $unknown) = run_case($case); + ok($records > 0 && $unknown == 0, + "s_client -msg decodes all $case->{name} records"); +} From 3a38f542adb2fe9d50006343b7d7e264f5c0d46f Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sat, 4 Jul 2026 12:17:27 +0200 Subject: [PATCH 296/349] statem: generalize statem_clnt construct test helpers Parameterize prime_ssl() by message type and extract a generic finish_msg() so the scaffolding is reusable by other client construct tests. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Tomas Mraz Reviewed-by: Nikola Pajkovsky MergeDate: Mon Jul 20 09:11:45 2026 (Merged from https://github.com/openssl/openssl/pull/31861) --- test/statem_clnt_construct_test.c | 56 +++++++++++++++---------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/test/statem_clnt_construct_test.c b/test/statem_clnt_construct_test.c index 459c7ffe3e..985e30cb6a 100644 --- a/test/statem_clnt_construct_test.c +++ b/test/statem_clnt_construct_test.c @@ -88,10 +88,11 @@ static SSL_CTX *new_ctx(const CH_CONFIG *cfg, const SSL_METHOD *meth) /* * Set up the init_buf and handshake state the write state machine would have * established before calling a construct function. For the client a WPACKET - * with the handshake header is emitted into init_buf. initbuf_len of 0 means - * full size; a small value exercises WPACKET failures. + * with the handshake header for message type mt is emitted into init_buf. + * initbuf_len of 0 means full size; a small value exercises WPACKET failures. */ -static int prime_ssl(SSL *ssl, int is_client, size_t initbuf_len, WPACKET *pkt) +static int prime_ssl(SSL *ssl, int is_client, size_t initbuf_len, WPACKET *pkt, + int mt) { SSL_CONNECTION *s = SSL_CONNECTION_FROM_SSL(ssl); @@ -115,34 +116,33 @@ static int prime_ssl(SSL *ssl, int is_client, size_t initbuf_len, WPACKET *pkt) if (pkt != NULL && (!TEST_true(WPACKET_init(pkt, s->init_buf)) - || !TEST_true(ssl_set_handshake_header(s, pkt, - SSL3_MT_CLIENT_HELLO)))) + || !TEST_true(ssl_set_handshake_header(s, pkt, mt)))) return 0; return 1; } +/* Finalize a constructed message and return its bytes (header + body). */ +static int finish_msg(SSL *ssl, WPACKET *pkt, int mt, unsigned char **msg, + size_t *msglen) +{ + SSL_CONNECTION *s = SSL_CONNECTION_FROM_SSL(ssl); + + if (!TEST_true(ssl_close_construct_packet(s, pkt, mt)) + || !TEST_true(WPACKET_get_total_written(pkt, msglen)) + || !TEST_true(WPACKET_finish(pkt))) + return 0; + + *msg = (unsigned char *)s->init_buf->data; + return 1; +} + /* * =========================================================================== * tls_construct_client_hello * =========================================================================== */ -/* Finalize the constructed message and return its bytes (header + body). */ -static int finish_ch(SSL *ssl, WPACKET *pkt, unsigned char **msg, - size_t *msglen) -{ - SSL_CONNECTION *s = SSL_CONNECTION_FROM_SSL(ssl); - - if (!TEST_true(ssl_close_construct_packet(s, pkt, SSL3_MT_CLIENT_HELLO)) - || !TEST_true(WPACKET_get_total_written(pkt, msglen)) - || !TEST_true(WPACKET_finish(pkt))) - return 0; - - *msg = (unsigned char *)s->init_buf->data; - return 1; -} - /* Recover the session_id length, the main branching difference in construct. */ static int get_ch_sessid_len(const CH_CONFIG *cfg, const unsigned char *msg, size_t msglen, size_t *sidlen) @@ -190,7 +190,7 @@ static int roundtrip_process_ch(const CH_CONFIG *cfg, const unsigned char *msg, || !TEST_ptr(ssl = SSL_new(sctx))) goto err; - if (!prime_ssl(ssl, 0, 0, NULL)) + if (!prime_ssl(ssl, 0, 0, NULL, SSL3_MT_CLIENT_HELLO)) goto err; s = SSL_CONNECTION_FROM_SSL(ssl); @@ -233,7 +233,7 @@ static int do_construct_ch(const CH_CONFIG *cfg, if (cfg->clear_midbox) SSL_clear_options(ssl, SSL_OP_ENABLE_MIDDLEBOX_COMPAT); - if (!prime_ssl(ssl, 1, 0, &pkt)) + if (!prime_ssl(ssl, 1, 0, &pkt, SSL3_MT_CLIENT_HELLO)) goto err; s = SSL_CONNECTION_FROM_SSL(ssl); @@ -246,7 +246,7 @@ static int do_construct_ch(const CH_CONFIG *cfg, WPACKET_cleanup(&pkt); goto err; } - if (!finish_ch(ssl, &pkt, &msg, &msglen)) + if (!finish_msg(ssl, &pkt, SSL3_MT_CLIENT_HELLO, &msg, &msglen)) goto err; if (!get_ch_sessid_len(cfg, msg, msglen, &sidlen) @@ -294,7 +294,7 @@ static int do_construct_ch_expect_fail(const CH_CONFIG *cfg, (void)r2; } - if (!prime_ssl(ssl, 1, initbuf_len, &pkt)) + if (!prime_ssl(ssl, 1, initbuf_len, &pkt, SSL3_MT_CLIENT_HELLO)) goto err; have_pkt = 1; s = SSL_CONNECTION_FROM_SSL(ssl); @@ -534,7 +534,7 @@ static int mfail_construct_ch_common(const CH_CONFIG *cfg, if (cfg->clear_midbox) SSL_clear_options(ssl, SSL_OP_ENABLE_MIDDLEBOX_COMPAT); - if (!prime_ssl(ssl, 1, 0, &pkt)) + if (!prime_ssl(ssl, 1, 0, &pkt, SSL3_MT_CLIENT_HELLO)) goto err; s = SSL_CONNECTION_FROM_SSL(ssl); @@ -625,14 +625,14 @@ static int test_construct_ch_ech(void) if (!TEST_ptr(cctx = new_ctx(&cfg, client_method(&cfg))) || !TEST_ptr(cssl = SSL_new(cctx)) || !TEST_true(SSL_set1_echstore(cssl, es)) - || !prime_ssl(cssl, 1, 0, &pkt)) + || !prime_ssl(cssl, 1, 0, &pkt, SSL3_MT_CLIENT_HELLO)) goto err; cs = SSL_CONNECTION_FROM_SSL(cssl); if (!TEST_int_eq(tls_construct_client_hello(cs, &pkt), CON_FUNC_SUCCESS)) { WPACKET_cleanup(&pkt); goto err; } - if (!finish_ch(cssl, &pkt, &msg, &msglen) + if (!finish_msg(cssl, &pkt, SSL3_MT_CLIENT_HELLO, &msg, &msglen) || !get_ch_sessid_len(&cfg, msg, msglen, &sidlen) || !TEST_size_t_eq(sidlen, SSL_MAX_SSL_SESSION_ID_LENGTH)) goto err; @@ -641,7 +641,7 @@ static int test_construct_ch_ech(void) if (!TEST_ptr(sctx = new_ctx(&cfg, server_method(&cfg))) || !TEST_ptr(sssl = SSL_new(sctx)) || !TEST_true(SSL_set1_echstore(sssl, es)) - || !prime_ssl(sssl, 0, 0, NULL)) + || !prime_ssl(sssl, 0, 0, NULL, SSL3_MT_CLIENT_HELLO)) goto err; ss = SSL_CONNECTION_FROM_SSL(sssl); if (!TEST_true(PACKET_buf_init(&rpkt, msg + hdr_len(&cfg), From 7f850c3dbb2b388441fea5b55eed8dc20f5cab5e Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sat, 4 Jul 2026 12:42:36 +0200 Subject: [PATCH 297/349] statem: test tls_construct_end_of_early_data Cover the success path (state advances to FINISHED_WRITING, empty body) and the invalid-state error path. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Tomas Mraz Reviewed-by: Nikola Pajkovsky MergeDate: Mon Jul 20 09:11:47 2026 (Merged from https://github.com/openssl/openssl/pull/31861) --- test/statem_clnt_construct_test.c | 71 +++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 4 deletions(-) diff --git a/test/statem_clnt_construct_test.c b/test/statem_clnt_construct_test.c index 985e30cb6a..debedb3af1 100644 --- a/test/statem_clnt_construct_test.c +++ b/test/statem_clnt_construct_test.c @@ -678,6 +678,71 @@ static int mfail_construct_ch_ech(void) } #endif /* OSSL_NO_USABLE_ECH */ +/* + * =========================================================================== + * tls_construct_end_of_early_data + * =========================================================================== + */ + +#ifndef OSSL_NO_USABLE_TLS1_3 +/* + * EndOfEarlyData carries no body and only advances early_data_state; it is + * valid only from the WRITE_RETRY/FINISHED_WRITING states. + */ +static int do_construct_eoed(int state, CON_FUNC_RETURN expect) +{ + CH_CONFIG cfg = { 0, TLS1_3_VERSION, TLS1_3_VERSION, 0 }; + SSL_CTX *cctx = NULL; + SSL *ssl = NULL; + SSL_CONNECTION *s; + WPACKET pkt; + unsigned char *msg = NULL; + size_t msglen = 0; + int have_pkt = 0; + int ret = 0; + + if (!TEST_ptr(cctx = new_ctx(&cfg, client_method(&cfg))) + || !TEST_ptr(ssl = SSL_new(cctx)) + || !prime_ssl(ssl, 1, 0, &pkt, SSL3_MT_END_OF_EARLY_DATA)) + goto err; + have_pkt = 1; + s = SSL_CONNECTION_FROM_SSL(ssl); + s->early_data_state = state; + + if (!TEST_int_eq(tls_construct_end_of_early_data(s, &pkt), expect)) + goto err; + + if (expect == CON_FUNC_SUCCESS) { + /* State advances and the body is empty (only the header is written). */ + if (!TEST_int_eq(s->early_data_state, SSL_EARLY_DATA_FINISHED_WRITING) + || !finish_msg(ssl, &pkt, SSL3_MT_END_OF_EARLY_DATA, &msg, &msglen)) + goto err; + have_pkt = 0; + if (!TEST_size_t_eq(msglen, hdr_len(&cfg))) + goto err; + } + + ret = 1; +err: + if (have_pkt) + WPACKET_cleanup(&pkt); + SSL_free(ssl); + SSL_CTX_free(cctx); + return ret; +} + +static int test_construct_eoed(void) +{ + return do_construct_eoed(SSL_EARLY_DATA_WRITE_RETRY, CON_FUNC_SUCCESS); +} + +static int test_construct_eoed_bad_state(void) +{ + /* Called from an unexpected state: CON_FUNC_ERROR, nothing written. */ + return do_construct_eoed(SSL_EARLY_DATA_NONE, CON_FUNC_ERROR); +} +#endif /* OSSL_NO_USABLE_TLS1_3 */ + int setup_tests(void) { ADD_TEST(test_construct_ch_small_buf); @@ -693,10 +758,8 @@ int setup_tests(void) ADD_TEST(test_construct_ch_tls13); ADD_TEST(test_construct_ch_tls13_no_middlebox); ADD_TEST(test_construct_ch_hrr); - /* - * The non-cached mfail run takes too long and does not test too much extra - * so better to skip it. - */ + ADD_TEST(test_construct_eoed); + ADD_TEST(test_construct_eoed_bad_state); #if defined(OPENSSL_NO_ECX) /* * Without ECX the key_share falls back to EC keygen, which makes a From 2e49fe4ad8be3dc9a63a4533c404e0a2728ea8a3 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sat, 4 Jul 2026 13:58:00 +0200 Subject: [PATCH 298/349] statem: test tls_construct_client_certificate Exercise the certificate output functions under mfail with real key material embedded in the test: an x509 chain over TLS 1.3 and an RPK over TLS 1.2. Add deterministic tests for the error branches mfail cannot reach: an unknown certificate type, a failed write-key change, and WPACKET failures writing the TLS 1.3 context. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Tomas Mraz Reviewed-by: Nikola Pajkovsky MergeDate: Mon Jul 20 09:11:48 2026 (Merged from https://github.com/openssl/openssl/pull/31861) --- test/statem_clnt_construct_test.c | 322 +++++++++++++++++++++++++++++- 1 file changed, 316 insertions(+), 6 deletions(-) diff --git a/test/statem_clnt_construct_test.c b/test/statem_clnt_construct_test.c index debedb3af1..10f58a3ab1 100644 --- a/test/statem_clnt_construct_test.c +++ b/test/statem_clnt_construct_test.c @@ -8,10 +8,11 @@ */ /* - * Direct tests for tls_construct_client_hello(): prime a client SSL_CONNECTION - * enough to call the construct function without a full handshake, then check - * the result structurally and by round-tripping it through the server parser. - * OOM branches are covered with mfail tests. + * Direct tests for the client state-machine construct functions in + * statem_clnt.c: prime a client SSL_CONNECTION enough to call a construct + * function without a full handshake, then check the result structurally and, + * where useful, by round-tripping it through the server parser. OOM branches + * are covered with mfail tests. */ #include @@ -41,8 +42,8 @@ #endif /* - * Helpers down to prime_ssl() are generic and reusable by tests for any - * statem_clnt construct function; the ClientHello-specific code follows. + * Helpers down to finish_msg() are generic and reusable by tests for any + * statem_clnt construct function; the per-message code follows. */ /* Connection configuration shared by the construct tests. */ @@ -743,6 +744,301 @@ static int test_construct_eoed_bad_state(void) } #endif /* OSSL_NO_USABLE_TLS1_3 */ +/* + * =========================================================================== + * tls_construct_client_certificate + * =========================================================================== + */ + +#if !defined(OSSL_NO_USABLE_TLS1_3) || !defined(OPENSSL_NO_TLS1_2) +/* Self-signed client cert + signing-capable key; regenerate with the + * statem_clnt_construct_test ossl-test-tools subcommand. */ +static const char *kClientCert[] = { + "-----BEGIN CERTIFICATE-----\n", + "MIIDvzCCAqegAwIBAgICAQAwDQYJKoZIhvcNAQELBQAwgYExCzAJBgNVBAYTAlVT\n", + "MRAwDgYDVQQIDAdXeW9taW5nMREwDwYDVQQHDAhDaGV5ZW5uZTEVMBMGA1UECgwM\n", + "T3BlblNTTCBUZXN0MRQwEgYDVQQLDAtzdGF0ZW1fY2xudDEgMB4GA1UEAwwXc3Rh\n", + "dGVtX2NsbnQgdGVzdCBjbGllbnQwHhcNMjYwMTAxMDAwMDAwWhcNNDYwMTAxMDAw\n", + "MDAwWjCBgTELMAkGA1UEBhMCVVMxEDAOBgNVBAgMB1d5b21pbmcxETAPBgNVBAcM\n", + "CENoZXllbm5lMRUwEwYDVQQKDAxPcGVuU1NMIFRlc3QxFDASBgNVBAsMC3N0YXRl\n", + "bV9jbG50MSAwHgYDVQQDDBdzdGF0ZW1fY2xudCB0ZXN0IGNsaWVudDCCASIwDQYJ\n", + "KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMnUvJvluB2ZUoQNQlW4wgv0qceITB5X\n", + "cHQe60H1CMTapaRi32dpwEzoEMnMjULcrZshcTAkdke6J1ubJ6qviGp7n1kVYH18\n", + "rGYYk6VT+GPb/SZnjMX3+e5WEpH+53UEGVvBPHl/med0AzklOOf/0hDlMFzMBejA\n", + "z+T++88QIT19BoIwfilcMDZxE0uXbq3QLpugADGd93zLSCwM1vxd9Vi0EwyMpy7Q\n", + "Ot9eIR/+ML0HESXZ1AvVcLjvuhqm+xkNiR9qil68zqgJk+dUpK5hCpLBi7cfBpk7\n", + "jLultF09up6G3Y5KiXd8wS9upwJZXA9+9OKHTf05w4xWAA/kpp9gt0MCAwEAAaM/\n", + "MD0wDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0OBBYEFM9Nespo\n", + "NSyfQO8jPjRl4JfH5kOsMA0GCSqGSIb3DQEBCwUAA4IBAQBtTgy5ePCHR+iu3Ign\n", + "wlJzL5+zkWOkQsbAzJsbWrvzqwx2shXr1adM7OJy7tCkmDgwHsXjTTO2qAZrlmYQ\n", + "ktGA/UAtttIqgiiYcyGdrZas2vXUWLUps5YzMm4YdY8YNTvqQl3LCziUhO5YREDP\n", + "teXy4FF6ijGUDe84CYsmKvtbIn34LtZ2Vo3gsiRHvdiaxHavH30UqED9k4NjKnFx\n", + "XM6eMw+bs0Yl1vi/Dz5tHPRaAnsGvnKcEveSAdAoSWmodw8n8W5t8ZvPoPCoKz88\n", + "DOGzUvLma/kVL+3HLiSn6XFiQ2NLfO9ceUgDsSzcRo76XZHJnfGsK6Iewgje3NrB\n", + "WnZs\n", + "-----END CERTIFICATE-----\n", + NULL +}; + +static const char *kClientKey[] = { + "-----BEGIN RSA PRIVATE KEY-----\n", + "MIIEowIBAAKCAQEAydS8m+W4HZlShA1CVbjCC/Spx4hMHldwdB7rQfUIxNqlpGLf\n", + "Z2nATOgQycyNQtytmyFxMCR2R7onW5snqq+IanufWRVgfXysZhiTpVP4Y9v9JmeM\n", + "xff57lYSkf7ndQQZW8E8eX+Z53QDOSU45//SEOUwXMwF6MDP5P77zxAhPX0GgjB+\n", + "KVwwNnETS5durdAum6AAMZ33fMtILAzW/F31WLQTDIynLtA6314hH/4wvQcRJdnU\n", + "C9VwuO+6Gqb7GQ2JH2qKXrzOqAmT51SkrmEKksGLtx8GmTuMu6W0XT26nobdjkqJ\n", + "d3zBL26nAllcD3704odN/TnDjFYAD+Smn2C3QwIDAQABAoIBAGAjkzIJczG6MmmP\n", + "bU0q5FfQk7zlaii7yuetQK/a2fH3GpbauALpBz46/qA5bQJv3sw52lIt1B+nhw7m\n", + "MbdmxKrANy+2dI9hvzckttO2U2exxvyvr4kvbWB/pHnhu3vsV23y9m0DgJqVEuH6\n", + "Hoi4PWZp3aceUiRED+NLKERCMSs5lPSSWR7sUkzHku4x5fZXZppcQW4leo+Z/kNC\n", + "I36CGF4pI9FJXwcuRhbv3NsFMVl/Ng4hWgzgu7zwJEPw2OSKyhdwHV53qGQWyMnJ\n", + "7SawyQfyspKlMZnjWxplFZ5tgaV6O63zZZPEOC2mZNeiZKWC9Lut/wyriLs+W4w7\n", + "9BBX3q0CgYEA94sgemr3rGY2R+9kiPV/TC08IUflMJ7kt0epDDRnojWqobinJzQG\n", + "Nq25i5vZHbjn9g7l2hNmOcHVZZmCYZitmjwWr4ibthftU7+H6WkvcBvH6b6EmHQm\n", + "5IGtOxYPmtf2Ghnj1uQmsBe9vRYcqx7B8/anqRi3lQhebykkII0MLR8CgYEA0LnV\n", + "vXaFAwLOta4Kn43mXC6sVRRMt316d70zRpVRsSQ64TdhtwArmKq3RUmLH2r3ysRL\n", + "6+mxEGJriL7JERH3Jlm0YGsUUQvQWxCddccuTQRc16+UVt7+xzfhzJZNTD4+t0aA\n", + "jsVLpPQHzXI8Yqzh1p83oC4XV4hCYZNLlfleTV0CgYBWIG/yZ9k4gG+OY7pk9JWP\n", + "2YU8Rxl06zPEmQg2GN2d0HJHxklSGIW47ITMEDNgZf8+2zwZvfopSkmHCfwVHNv5\n", + "98Ik3LDgkD6gjtko2tIIfYH2z7SunmsRwhSVpD1VsKINvshI8iSLzBbV/SWIXDE7\n", + "Qqxe5xyom7rPjk7ljG2aHQKBgB36oxV8YWxmSdRUdBgopG6XEY+Cw+YS8rUiCqxX\n", + "pA0iXAafErzbHGfoFTyxbHcNwRtxiEoRHapxyGoypOR7xRjQB5VVq+xcGwgJYeRZ\n", + "wG+1cbRU9qRnkQaCIz9kUyPhSNbAHJTlB5Fgr4I1pzCxDhrqcW3jUNz0qDwlkNSw\n", + "pXfNAoGBAKlFbbPEzFonlkHVtdUuk6Chsi5k/ddrWsGxwUw5F5BSu0UaseolVls5\n", + "TyDVa8FEfDnUZRxl8HSfC/Qp/kAdveGKyhNaex22L5G8m20rXjsQBMFHMOy2Mho1\n", + "hgs0/emKuVyCs+wnYOqJlWZ8Vf/qGcUtDF3r4aEZ1JUDhUBAVEpo\n", + "-----END RSA PRIVATE KEY-----\n", + NULL +}; + +static int load_cert_and_key(SSL *ssl) +{ + X509 *cert = NULL; + EVP_PKEY *pkey = NULL; + int ret = 0; + + if (!TEST_ptr(cert = X509_from_strings(kClientCert)) + || !TEST_ptr(pkey = PKEY_from_strings(kClientKey)) + || !TEST_int_eq(SSL_use_certificate(ssl, cert), 1) + || !TEST_int_eq(SSL_use_PrivateKey(ssl, pkey), 1)) + goto err; + ret = 1; +err: + X509_free(cert); + EVP_PKEY_free(pkey); + return ret; +} + +/* + * Run tls_construct_client_certificate() under mfail; prep installs the cert + * material. For TLS 1.3 the method is swapped in (IS_TLS13 keys off it) and + * middlebox compat cleared to skip the write-key change. + */ +static int mfail_construct_cert(int is_tls13, int (*prep)(SSL_CONNECTION *s)) +{ + CH_CONFIG cfg = { 0, 0, 0, 0 }; + SSL_CTX *cctx = NULL; + SSL *ssl = NULL; + SSL_CONNECTION *s; + WPACKET pkt; + int ok = 0; + int ret = 0; + + if (!TEST_ptr(cctx = new_ctx(&cfg, client_method(&cfg))) + || !TEST_ptr(ssl = SSL_new(cctx))) + goto err; + if (is_tls13) + SSL_clear_options(ssl, SSL_OP_ENABLE_MIDDLEBOX_COMPAT); + if (!prime_ssl(ssl, 1, 0, &pkt, SSL3_MT_CERTIFICATE)) + goto err; + s = SSL_CONNECTION_FROM_SSL(ssl); +#ifndef OSSL_NO_USABLE_TLS1_3 + if (is_tls13) + ssl->method = tlsv1_3_client_method(); +#endif + + if (prep != NULL && !prep(s)) { + WPACKET_cleanup(&pkt); + goto err; + } + + MFAIL_start(); + ok = (tls_construct_client_certificate(s, &pkt) == CON_FUNC_SUCCESS); + MFAIL_end(); + + WPACKET_cleanup(&pkt); + + ret = ok ? 1 : 0; +err: + SSL_free(ssl); + SSL_CTX_free(cctx); + return ret; +} +#endif /* TLS 1.2 or usable TLS 1.3 */ + +#ifndef OSSL_NO_USABLE_TLS1_3 +/* x509 over TLS 1.3; NO_AUTO_CHAIN avoids best-effort verify swallowing OOM. */ +static int prep_cert_x509(SSL_CONNECTION *s) +{ + SSL *ssl = SSL_CONNECTION_GET_SSL(s); + + SSL_set_mode(ssl, SSL_MODE_NO_AUTO_CHAIN); + return load_cert_and_key(ssl); +} + +static int mfail_construct_cert_x509(void) +{ + return mfail_construct_cert(1, prep_cert_x509); +} +#endif /* OSSL_NO_USABLE_TLS1_3 */ + +#ifndef OPENSSL_NO_TLS1_2 +/* RPK derived from the certificate public key over TLS 1.2 (tls_output_rpk). */ +static int prep_cert_rpk(SSL_CONNECTION *s) +{ + if (!load_cert_and_key(SSL_CONNECTION_GET_SSL(s))) + return 0; + s->ext.client_cert_type = TLSEXT_cert_type_rpk; + return 1; +} + +static int mfail_construct_cert_rpk(void) +{ + return mfail_construct_cert(0, prep_cert_rpk); +} +#endif /* OPENSSL_NO_TLS1_2 */ + +/* Deterministic error branches that mfail (allocation-only) cannot reach. */ + +/* An unrecognized certificate type is rejected. */ +static int test_construct_cert_bad_type(void) +{ + CH_CONFIG cfg = { 0, 0, 0, 0 }; + SSL_CTX *cctx = NULL; + SSL *ssl = NULL; + SSL_CONNECTION *s; + WPACKET pkt; + int have_pkt = 0; + int ret = 0; + + if (!TEST_ptr(cctx = new_ctx(&cfg, client_method(&cfg))) + || !TEST_ptr(ssl = SSL_new(cctx)) + || !prime_ssl(ssl, 1, 0, &pkt, SSL3_MT_CERTIFICATE)) + goto err; + have_pkt = 1; + s = SSL_CONNECTION_FROM_SSL(ssl); + s->ext.client_cert_type = 0xff; + + if (!TEST_int_eq(tls_construct_client_certificate(s, &pkt), CON_FUNC_ERROR)) + goto err; + + ret = 1; +err: + if (have_pkt) + WPACKET_cleanup(&pkt); + SSL_free(ssl); + SSL_CTX_free(cctx); + return ret; +} + +#ifndef OSSL_NO_USABLE_TLS1_3 +/* + * With middlebox compat on, the TLS 1.3 path changes the write keys; without a + * negotiated cipher that fails rather than succeeding. + */ +static int test_construct_cert_change_cipher_fail(void) +{ + CH_CONFIG cfg = { 0, 0, 0, 0 }; + SSL_CTX *cctx = NULL; + SSL *ssl = NULL; + SSL_CONNECTION *s; + WPACKET pkt; + int have_pkt = 0; + int ret = 0; + + if (!TEST_ptr(cctx = new_ctx(&cfg, client_method(&cfg))) + || !TEST_ptr(ssl = SSL_new(cctx)) + || !prime_ssl(ssl, 1, 0, &pkt, SSL3_MT_CERTIFICATE)) + goto err; + have_pkt = 1; + s = SSL_CONNECTION_FROM_SSL(ssl); + ssl->method = tlsv1_3_client_method(); + + if (!TEST_int_eq(tls_construct_client_certificate(s, &pkt), CON_FUNC_ERROR)) + goto err; + + ret = 1; +err: + if (have_pkt) + WPACKET_cleanup(&pkt); + SSL_free(ssl); + SSL_CTX_free(cctx); + return ret; +} + +/* + * A WPACKET failure while writing the TLS 1.3 certificate_request_context + * yields CON_FUNC_ERROR. with_pha exercises the non-empty-context branch. + */ +static int do_construct_cert_ctx_small_buf(int with_pha) +{ + CH_CONFIG cfg = { 0, 0, 0, 0 }; + SSL_CTX *cctx = NULL; + SSL *ssl = NULL; + SSL_CONNECTION *s; + WPACKET pkt; + unsigned char buf[16]; + int have_pkt = 0; + int ret = 0; + + if (!TEST_ptr(cctx = new_ctx(&cfg, client_method(&cfg))) + || !TEST_ptr(ssl = SSL_new(cctx))) + goto err; + SSL_set_connect_state(ssl); + s = SSL_CONNECTION_FROM_SSL(ssl); + if (!TEST_ptr(s) + || !TEST_ptr(s->init_buf = BUF_MEM_new()) + || !TEST_true(BUF_MEM_grow(s->init_buf, SSL3_RT_MAX_PLAIN_LENGTH)) + || !TEST_true(tls_setup_handshake(s))) + goto err; + ssl->method = tlsv1_3_client_method(); + + if (with_pha) { + if (!TEST_ptr(s->pha_context = OPENSSL_malloc(4))) + goto err; + s->pha_context_len = 4; + } + + /* Only the handshake header fits, so the context write overflows. */ + if (!TEST_true(WPACKET_init_static_len(&pkt, buf, hdr_len(&cfg), 0))) + goto err; + have_pkt = 1; + if (!TEST_true(ssl_set_handshake_header(s, &pkt, SSL3_MT_CERTIFICATE))) + goto err; + + if (!TEST_int_eq(tls_construct_client_certificate(s, &pkt), CON_FUNC_ERROR)) + goto err; + + ret = 1; +err: + if (have_pkt) + WPACKET_cleanup(&pkt); + SSL_free(ssl); + SSL_CTX_free(cctx); + return ret; +} + +static int test_construct_cert_ctx_small_buf(void) +{ + return do_construct_cert_ctx_small_buf(0); +} + +static int test_construct_cert_pha_ctx_small_buf(void) +{ + return do_construct_cert_ctx_small_buf(1); +} +#endif /* OSSL_NO_USABLE_TLS1_3 */ + int setup_tests(void) { ADD_TEST(test_construct_ch_small_buf); @@ -787,5 +1083,19 @@ int setup_tests(void) #endif ADD_MFAIL_TEST(mfail_construct_ch_ech); #endif /* OSSL_NO_USABLE_ECH */ + + /* tls_construct_client_certificate: OOM coverage of the output functions. */ +#ifndef OSSL_NO_USABLE_TLS1_3 + ADD_MFAIL_TEST(mfail_construct_cert_x509); +#endif +#ifndef OPENSSL_NO_TLS1_2 + ADD_MFAIL_TEST(mfail_construct_cert_rpk); +#endif + ADD_TEST(test_construct_cert_bad_type); +#ifndef OSSL_NO_USABLE_TLS1_3 + ADD_TEST(test_construct_cert_change_cipher_fail); + ADD_TEST(test_construct_cert_ctx_small_buf); + ADD_TEST(test_construct_cert_pha_ctx_small_buf); +#endif return 1; } From 27858603cb3067fa2318b7af16af6ea64686cac0 Mon Sep 17 00:00:00 2001 From: olszomal Date: Fri, 12 Jun 2026 10:52:05 +0200 Subject: [PATCH 299/349] doc: clarify pkeyutl -rawin and -digest for no-prehash signatures Complements: 5421423 "Flexible encoders for ML-DSA" Signed-off-by: olszomal Reviewed-by: Tomas Mraz Reviewed-by: Daniel Kubec MergeDate: Mon Jul 20 09:13:08 2026 (Merged from https://github.com/openssl/openssl/pull/31478) --- doc/man1/openssl-pkeyutl.pod.in | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/man1/openssl-pkeyutl.pod.in b/doc/man1/openssl-pkeyutl.pod.in index 053385ca0b..590bfe3a6d 100644 --- a/doc/man1/openssl-pkeyutl.pod.in +++ b/doc/man1/openssl-pkeyutl.pod.in @@ -677,8 +677,10 @@ L, =head1 HISTORY Since OpenSSL 3.5, -the B<-digest> option implies B<-rawin>, and these two options are -no longer required when signing or verifying with an Ed25519 or Ed448 key. +the B<-digest> option implies B<-rawin>. The B<-rawin> option is no longer +required when signing or verifying with a key type that does not support a +prehash digest, such as Ed25519, Ed448, ML-DSA, or SLH-DSA. For these key +types, B<-digest> is not supported. Also since OpenSSL 3.5, the B<-kemop> option is no longer required for any of the supported algorithms, the only supported B is now the default. From 6a1f43c5dde182c9ee2383e66e24b631ca6b0b10 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 2 Jul 2026 17:37:37 +0200 Subject: [PATCH 300/349] apps: add offline OCSP request/responder/verify round-trip test Exercise the request-generation and built-in responder halves of the ocsp app without any sockets: build a request, have the responder answer it against the static index, then verify the self-generated response. This covers make_ocsp_response, lookup_serial, add_ocsp_cert, add_ocsp_serial and the status-printing body of print_ocsp_summary (GOOD and UNKNOWN), none of which were reached by the existing -respin-only app tests. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Tomas Mraz Reviewed-by: Daniel Kubec Reviewed-by: Tim Hudson MergeDate: Mon Jul 20 09:29:35 2026 (Merged from https://github.com/openssl/openssl/pull/31834) --- test/recipes/80-test_ocsp.t | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/test/recipes/80-test_ocsp.t b/test/recipes/80-test_ocsp.t index 62ee9f13ca..dc2312b0fc 100644 --- a/test/recipes/80-test_ocsp.t +++ b/test/recipes/80-test_ocsp.t @@ -263,3 +263,44 @@ subtest "=== OCSP handling of identical input and output files ===" => sub { ok(run(app(['openssl', 'ocsp', '-respin', $inout2, '-respout', $inout2, '-noverify']))); ok(!compare($inout2, $backup2), "copied response $inout2 did not change"); }; + +subtest "=== OCSP offline request/responder/verify round-trip ===" => sub { + plan tests => 6; + + # Offline round-trip: build a request, answer it with the built-in + # responder using a static index, then verify the response. + my $issuer = catfile($ocspdir, "intermediate-cert.pem"); + my $ee = catfile($ocspdir, "server-cert.pem"); + my $index = catfile($ocspdir, "index.txt"); + my $rsigner = catfile($ocspdir, "ocsp.pem"); + my $root = catfile($ocspdir, "root-cert.pem"); + + # The serial of server-cert.pem is listed as valid in index.txt, so its + # status is "good"; a serial absent from the index yields "unknown". + my $roundtrip = sub { + my ($title, $reqargs, $status) = @_; + my $req = "rt-req.der"; + my $resp = "rt-resp.der"; + + ok(run(app(['openssl', 'ocsp', '-issuer', $issuer, @$reqargs, + '-reqout', $req])), + "$title: produce request"); + ok(run(app(['openssl', 'ocsp', '-index', $index, '-rsigner', $rsigner, + '-CA', $issuer, '-reqin', $req, '-respout', $resp])), + "$title: responder produces response"); + # Passing the request again lets print_ocsp_summary report the status. + ok(run(app(['openssl', 'ocsp', '-issuer', $issuer, @$reqargs, + '-no_nonce', '-respin', $resp, '-CAfile', $root, + '-verify_other', $rsigner, + '-no-CApath', '-no-CAstore'])), + "$title: verify self-generated response ($status)"); + }; + + SKIP: { + # The responder certificates use EC keys. + skip "EC is not supported by this OpenSSL build", 6 if disabled("ec"); + + $roundtrip->("GOOD (by cert)", ['-cert', $ee], "good"); + $roundtrip->("UNKNOWN (by serial)", ['-serial', '0x1234'], "unknown"); + } +}; From f106d7632add327316577def015a267b908bfa3c Mon Sep 17 00:00:00 2001 From: Frederik Wedel-Heinen Date: Tue, 7 Apr 2026 21:55:52 +0200 Subject: [PATCH 301/349] Only verify software can build if it is not a documentation only PR. Resolves: https://github.com/openssl/openssl/issues/29167 Reviewed-by: Tomas Mraz Reviewed-by: Dmitry Belyavskiy MergeDate: Mon Jul 20 09:46:03 2026 (Merged from https://github.com/openssl/openssl/pull/30712) --- .github/workflows/ci-doc-changes.yml | 125 +++++++++++++++++ .github/workflows/ci.yml | 128 +++--------------- .github/workflows/cross-compiles.yml | 20 ++- .github/workflows/fips-checksums.yml | 11 +- .github/workflows/perl-minimal-checker.yml | 20 ++- .github/workflows/prov-compat-label.yml | 11 +- .../workflows/riscv-more-cross-compiles.yml | 8 ++ .github/workflows/run-checker-ci.yml | 20 ++- .github/workflows/style-checks.yml | 11 +- .github/workflows/windows.yml | 21 ++- 10 files changed, 259 insertions(+), 116 deletions(-) create mode 100644 .github/workflows/ci-doc-changes.yml diff --git a/.github/workflows/ci-doc-changes.yml b/.github/workflows/ci-doc-changes.yml new file mode 100644 index 0000000000..08919fc323 --- /dev/null +++ b/.github/workflows/ci-doc-changes.yml @@ -0,0 +1,125 @@ +# Copyright 2021-2026 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +name: Documentation and Installability CI + +on: [pull_request, push] + +permissions: + contents: read + +env: + OSSL_RUN_CI_TESTS: 1 + +jobs: + check_docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - name: config + run: ./config --strict-warnings --banner=Configured enable-fips && perl configdata.pm --dump + - name: make build_generated + run: make -s build_generated + - name: make doc-nits + run: make doc-nits + - name: make help + run: make help + - name: make md-nits + run: | + sudo gem install mdl + make md-nits + + # out-of-source-and-install checks multiple things at the same time: + # - That building, testing and installing works from an out-of-source + # build tree + # - That building, testing and installing works with a read-only source + # tree + out-of-readonly-source-and-install-ubuntu: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + path: ./source + persist-credentials: false + - name: checkout fuzz/corpora submodule + run: git submodule update --init --depth 1 fuzz/corpora + working-directory: ./source + - name: make source read-only + run: chmod -R a-w ./source + - name: create build and install directories + run: | + mkdir ./build + mkdir ./install + - name: config + run: | + ../source/config --banner=Configured enable-demos enable-h3demo enable-fips enable-lms enable-quic enable-acvp-tests --strict-warnings --prefix=$(cd ../install; pwd) + perl configdata.pm --dump + working-directory: ./build + - name: make + run: make -s -j4 + working-directory: ./build + - name: get cpu info + run: | + cat /proc/cpuinfo + ./util/opensslwrap.sh version -c + working-directory: ./build + - name: make test + run: ../source/.github/workflows/make-test + working-directory: ./build + - name: save artifacts + if: success() || failure() + uses: actions/upload-artifact@v5 + with: + name: "ci@out-of-readonly-source-and-install-ubuntu" + path: build/artifacts.tar.gz + - name: make install + run: make install + working-directory: ./build + + out-of-readonly-source-and-install-macos: + runs-on: macos-15 + steps: + - uses: actions/checkout@v6 + with: + path: ./source + persist-credentials: false + - name: checkout fuzz/corpora submodule + run: git submodule update --init --depth 1 fuzz/corpora + working-directory: ./source + - name: make source read-only + run: chmod -R a-w ./source + - name: create build and install directories + run: | + mkdir ./build + mkdir ./install + - name: config + run: | + ../source/config --banner=Configured enable-fips enable-lms enable-demos enable-h3demo enable-quic enable-acvp-tests --strict-warnings --prefix=$(cd ../install; pwd) + perl configdata.pm --dump + working-directory: ./build + - name: make + run: make -s -j4 + working-directory: ./build + - name: get cpu info + run: | + sysctl machdep.cpu + ./util/opensslwrap.sh version -c + working-directory: ./build + - name: make test + run: ../source/.github/workflows/make-test + working-directory: ./build + - name: save artifacts + if: success() || failure() + uses: actions/upload-artifact@v5 + with: + name: "ci@out-of-readonly-source-and-install-macos-15" + path: build/artifacts.tar.gz + - name: make install + run: make install + working-directory: ./build diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 597501112f..a80ea66b29 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,25 @@ name: GitHub CI -on: [pull_request, push] +on: + pull_request: + paths-ignore: + - 'doc/**' + - '*.md' + - '*.pod' + - 'README*' + - 'funding.json' + - 'LICENSE.txt' + - 'VERSION.dat' + push: + paths-ignore: + - 'doc/**' + - '*.md' + - '*.pod' + - 'README*' + - 'funding.json' + - 'LICENSE.txt' + - 'VERSION.dat' # for some reason, this does not work: # variables: @@ -45,25 +63,6 @@ jobs: - name: git diff run: git diff --exit-code - check_docs: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - with: - persist-credentials: false - - name: config - run: ./config --strict-warnings --banner=Configured enable-fips && perl configdata.pm --dump - - name: make build_generated - run: make -s build_generated - - name: make doc-nits - run: make doc-nits - - name: make help - run: make help - - name: make md-nits - run: | - sudo gem install mdl - make md-nits - # This checks that we use ANSI C language syntax and semantics. # We are not as strict with libraries, but rather adapt to what's # expected to be available in a certain version of each platform. @@ -665,95 +664,6 @@ jobs: name: "ci@legacy" path: artifacts.tar.gz - # out-of-source-and-install checks multiple things at the same time: - # - That building, testing and installing works from an out-of-source - # build tree - # - That building, testing and installing works with a read-only source - # tree - out-of-readonly-source-and-install-ubuntu: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - with: - path: ./source - persist-credentials: false - - name: checkout fuzz/corpora submodule - run: git submodule update --init --depth 1 fuzz/corpora - working-directory: ./source - - name: make source read-only - run: chmod -R a-w ./source - - name: create build and install directories - run: | - mkdir ./build - mkdir ./install - - name: config - run: | - ../source/config --banner=Configured enable-demos enable-h3demo enable-fips enable-lms enable-quic enable-acvp-tests --strict-warnings --prefix=$(cd ../install; pwd) - perl configdata.pm --dump - working-directory: ./build - - name: make - run: make -s -j4 - working-directory: ./build - - name: get cpu info - run: | - cat /proc/cpuinfo - ./util/opensslwrap.sh version -c - working-directory: ./build - - name: make test - run: ../source/.github/workflows/make-test - working-directory: ./build - - name: save artifacts - if: success() || failure() - uses: actions/upload-artifact@v5 - with: - name: "ci@out-of-readonly-source-and-install-ubuntu" - path: build/artifacts.tar.gz - - name: make install - run: make install - working-directory: ./build - - out-of-readonly-source-and-install-macos: - runs-on: macos-15 - steps: - - uses: actions/checkout@v6 - with: - path: ./source - persist-credentials: false - - name: checkout fuzz/corpora submodule - run: git submodule update --init --depth 1 fuzz/corpora - working-directory: ./source - - name: make source read-only - run: chmod -R a-w ./source - - name: create build and install directories - run: | - mkdir ./build - mkdir ./install - - name: config - run: | - ../source/config --banner=Configured enable-fips enable-lms enable-demos enable-h3demo enable-quic enable-acvp-tests --strict-warnings --prefix=$(cd ../install; pwd) - perl configdata.pm --dump - working-directory: ./build - - name: make - run: make -s -j4 - working-directory: ./build - - name: get cpu info - run: | - sysctl machdep.cpu - ./util/opensslwrap.sh version -c - working-directory: ./build - - name: make test - run: ../source/.github/workflows/make-test - working-directory: ./build - - name: save artifacts - if: success() || failure() - uses: actions/upload-artifact@v5 - with: - name: "ci@out-of-readonly-source-and-install-macos-15" - path: build/artifacts.tar.gz - - name: make install - run: make install - working-directory: ./build - external-tests-misc: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/cross-compiles.yml b/.github/workflows/cross-compiles.yml index 3e1fbc142d..d0f5ff8284 100644 --- a/.github/workflows/cross-compiles.yml +++ b/.github/workflows/cross-compiles.yml @@ -7,7 +7,25 @@ name: Cross Compile -on: [pull_request, push] +on: + pull_request: + paths-ignore: + - 'doc/**' + - '*.md' + - '*.pod' + - 'README*' + - 'funding.json' + - 'LICENSE.txt' + - 'VERSION.dat' + push: + paths-ignore: + - 'doc/**' + - '*.md' + - '*.pod' + - 'README*' + - 'funding.json' + - 'LICENSE.txt' + - 'VERSION.dat' permissions: contents: read diff --git a/.github/workflows/fips-checksums.yml b/.github/workflows/fips-checksums.yml index f82a604ab7..5fd96b3159 100644 --- a/.github/workflows/fips-checksums.yml +++ b/.github/workflows/fips-checksums.yml @@ -6,7 +6,16 @@ # https://www.openssl.org/source/license.html name: FIPS Check and ABIDIFF -on: [pull_request] +on: + pull_request: + paths-ignore: + - 'doc/**' + - '*.md' + - '*.pod' + - 'README*' + - 'funding.json' + - 'LICENSE.txt' + - 'VERSION.dat' permissions: contents: read diff --git a/.github/workflows/perl-minimal-checker.yml b/.github/workflows/perl-minimal-checker.yml index 9ca4e9b509..2606add5fb 100644 --- a/.github/workflows/perl-minimal-checker.yml +++ b/.github/workflows/perl-minimal-checker.yml @@ -7,7 +7,25 @@ # Jobs run per pull request submission name: Perl-minimal-checker CI -on: [pull_request, push] +on: + pull_request: + paths-ignore: + - 'doc/**' + - '*.md' + - '*.pod' + - 'README*' + - 'funding.json' + - 'LICENSE.txt' + - 'VERSION.dat' + push: + paths-ignore: + - 'doc/**' + - '*.md' + - '*.pod' + - 'README*' + - 'funding.json' + - 'LICENSE.txt' + - 'VERSION.dat' permissions: contents: read diff --git a/.github/workflows/prov-compat-label.yml b/.github/workflows/prov-compat-label.yml index cf2b44e169..94334da8e5 100644 --- a/.github/workflows/prov-compat-label.yml +++ b/.github/workflows/prov-compat-label.yml @@ -10,7 +10,16 @@ name: Provider compatibility for PRs -on: [pull_request] +on: + pull_request: + paths-ignore: + - 'doc/**' + - '*.md' + - '*.pod' + - 'README*' + - 'funding.json' + - 'LICENSE.txt' + - 'VERSION.dat' permissions: contents: read diff --git a/.github/workflows/riscv-more-cross-compiles.yml b/.github/workflows/riscv-more-cross-compiles.yml index cac662b8d3..069495e0f6 100644 --- a/.github/workflows/riscv-more-cross-compiles.yml +++ b/.github/workflows/riscv-more-cross-compiles.yml @@ -10,6 +10,14 @@ name: Cross Compile for RISC-V Extensions on: pull_request: types: [opened, reopened, edited, synchronize] + paths-ignore: + - 'doc/**' + - '*.md' + - '*.pod' + - 'README*' + - 'funding.json' + - 'LICENSE.txt' + - 'VERSION.dat' push: schedule: - cron: '35 02 * * *' diff --git a/.github/workflows/run-checker-ci.yml b/.github/workflows/run-checker-ci.yml index 70d105e3f2..89186ff03c 100644 --- a/.github/workflows/run-checker-ci.yml +++ b/.github/workflows/run-checker-ci.yml @@ -7,7 +7,25 @@ # Jobs run per pull request submission name: Run-checker CI -on: [pull_request, push] +on: + pull_request: + paths-ignore: + - 'doc/**' + - '*.md' + - '*.pod' + - 'README*' + - 'funding.json' + - 'LICENSE.txt' + - 'VERSION.dat' + push: + paths-ignore: + - 'doc/**' + - '*.md' + - '*.pod' + - 'README*' + - 'funding.json' + - 'LICENSE.txt' + - 'VERSION.dat' permissions: contents: read diff --git a/.github/workflows/style-checks.yml b/.github/workflows/style-checks.yml index b345ae5110..f4d251681d 100644 --- a/.github/workflows/style-checks.yml +++ b/.github/workflows/style-checks.yml @@ -7,7 +7,16 @@ name: Coding style validation -on: [pull_request] +on: + pull_request: + paths-ignore: + - 'doc/**' + - '*.md' + - '*.pod' + - 'README*' + - 'funding.json' + - 'LICENSE.txt' + - 'VERSION.dat' jobs: check-style: diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 6d8ebe39b7..eb8649f741 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -7,7 +7,26 @@ name: Windows GitHub CI -on: [pull_request, push] +on: + pull_request: + paths-ignore: + - 'doc/**' + - '*.md' + - '*.pod' + - 'README*' + - 'funding.json' + - 'LICENSE.txt' + - 'VERSION.dat' + push: + paths-ignore: + - 'doc/**' + - '*.md' + - '*.pod' + - 'README*' + - 'funding.json' + - 'LICENSE.txt' + - 'VERSION.dat' + permissions: contents: read From a5b3f8863fcefe98c0baeaf954f84a1f8017eba2 Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Tue, 7 Jul 2026 15:45:07 +0900 Subject: [PATCH 302/349] Fix SM2 RISC-V64 crash from functions emitted into .rodata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ecp_sm2p256-riscv64.pl generator switches to .section .rodata to emit constant data (.Lpoly, .Lord, .Lpoly_div_2, .Lord_div_2), but never switches back before emitting function code. As a result, the function symbols defined by this file are assembled into .rodata instead of executable .text. On systems enforcing NX for .rodata, calling the RISC-V64 SM2 assembly faults immediately. Fix this by adding .previous after the constant block, restoring the initial .text section before function emission. Verified with readelf: before the fix, .text is empty and the function symbols are in .rodata; after the fix, constants remain in .rodata and the function symbols are in executable .text. Introduced in commit 05301b100f (PR #25918). Reviewed-by: Paul Yang Reviewed-by: Saša Nedvědický MergeDate: Mon Jul 20 09:47:35 2026 (Merged from https://github.com/openssl/openssl/pull/31874) --- crypto/ec/asm/ecp_sm2p256-riscv64.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/ec/asm/ecp_sm2p256-riscv64.pl b/crypto/ec/asm/ecp_sm2p256-riscv64.pl index 2a17e124dc..40938e75c0 100644 --- a/crypto/ec/asm/ecp_sm2p256-riscv64.pl +++ b/crypto/ec/asm/ecp_sm2p256-riscv64.pl @@ -366,7 +366,7 @@ $code.=<<___; .type .Lord_div_2,\@object .Lord_div_2: .dword 0xa9ddfa049ceaa092,0xb901efb590e30295,0xffffffffffffffff,0x7fffffff7fffffff - +.previous // void bn_rshift1(BN_ULONG *a); .globl bn_rshift1 From 5bbc49a5c9107f40026a320364df9d9a9e5fef37 Mon Sep 17 00:00:00 2001 From: Enji Cooper Date: Thu, 4 Sep 2025 21:27:29 -0700 Subject: [PATCH 303/349] Add mandoc output support for manpages This change modifies the Makefile generator to support mandoc format manpages, in lieu of \*roff format manpages. After this commit the user has the ability of specifying the manpage format to the `--manpage-format` flag. The 2 supported manpage formats as of writing are "mdoc" and "roff" and the default remains the "roff" format for legacy and portability reasons. The mandoc format requires pod2mdoc to be installed, whereas the roff output format requires pod2man to be installed. The former requires an additional utility be installed, whereas the latter uses pod2man, a utility that has been present with perl distributions for well over a decade. mandoc format support is being added as it is an easier/arguably more structured manpage format to parse, making it easier for downstream consumers like FreeBSD to implement OS-specific build support, as the minimum dependencies for the OpenSSL build process are more involved than the tools available for the FreeBSD OS bootstrapping process. Signed-off-by: Enji Cooper Reviewed-by: Dmitry Belyavskiy Reviewed-by: Tomas Mraz MergeDate: Mon Jul 20 09:52:29 2026 (Merged from https://github.com/openssl/openssl/pull/28450) --- Configurations/unix-Makefile.tmpl | 14 +++++++++++++- Configure | 7 ++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Configurations/unix-Makefile.tmpl b/Configurations/unix-Makefile.tmpl index 0d4ec0f983..6c51e34f7a 100644 --- a/Configurations/unix-Makefile.tmpl +++ b/Configurations/unix-Makefile.tmpl @@ -1583,12 +1583,24 @@ EOF my $section = $1; my $name = uc basename($args{src}, ".$section"); my $pod = $gen0; - return <<"EOF"; + + if ($config{manpage_format} eq "mdoc") { + return <<"EOF"; +$args{src}: $pod + pod2mdoc -n $name -s $section\$(MANSUFFIX) \\ + -d \$(RELEASE_DATE) \\ + $pod >\$\@ +EOF + } elsif ($config{manpage_format} eq "roff") { + return <<"EOF"; $args{src}: $pod pod2man --name=$name --section=$section\$(MANSUFFIX) --center=OpenSSL \\ --date=\$(RELEASE_DATE) --release=\$(VERSION) \\ $pod >\$\@ EOF + } else { + die "Unhandled manpage format: $config{manpage_format}"; + } } elsif (platform->isdef($args{src})) { # # Linker script-ish generator diff --git a/Configure b/Configure index 5ee8597f2e..75f1f6b67b 100755 --- a/Configure +++ b/Configure @@ -27,7 +27,7 @@ use OpenSSL::config; my $orig_death_handler = $SIG{__DIE__}; $SIG{__DIE__} = \&death_handler; -my $usage="Usage: Configure [no- ...] [enable- ...] [-Dxxx] [-lxxx] [-Lxxx] [-fxxx] [-Kxxx] [no-hw-xxx|no-hw] [[no-]threads] [[no-]thread-pool] [[no-]default-thread-pool] [[no-]shared] [[no-]zlib|zlib-dynamic] [no-asm] [no-egd] [sctp] [386] [--prefix=DIR] [--openssldir=OPENSSLDIR] [--with-xxx[=vvv]] [--config=FILE] [--help] os/compiler[:flags]\n"; +my $usage="Usage: Configure [no- ...] [enable- ...] [-Dxxx] [-lxxx] [-Lxxx] [-fxxx] [-Kxxx] [no-hw-xxx|no-hw] [[no-]threads] [[no-]thread-pool] [[no-]default-thread-pool] [[no-]shared] [[no-]zlib|zlib-dynamic] [no-asm] [no-egd] [sctp] [386] [--prefix=DIR] [--openssldir=OPENSSLDIR] [--with-xxx[=vvv]] [--config=FILE] [--manpage-format={roff,mdoc}] [--help] os/compiler[:flags]\n"; my $banner = <<"EOF"; @@ -295,6 +295,7 @@ my $dofile = abs2rel(catfile($srcdir, "util/dofile.pl")); my $local_config_envname = 'OPENSSL_LOCAL_CONFIG_DIR'; +$config{manpage_format} = "roff"; $config{sourcedir} = abs2rel($srcdir, $blddir); $config{builddir} = abs2rel($blddir, $blddir); # echo -n 'holy hand grenade of antioch' | openssl sha256 @@ -1044,6 +1045,10 @@ while (@argvcopy) { $config{build_type} = "release"; } + elsif (/^--manpage-format=(mdoc|roff)$/) + { + $config{manpage_format}="$1"; + } elsif (/^--pgo$/) { $config{build_type} = "pgo"; From 1512b307326d33d12c522c18d64dd92eb4d0e4a4 Mon Sep 17 00:00:00 2001 From: Enji Cooper Date: Tue, 28 Oct 2025 09:43:29 -0700 Subject: [PATCH 304/349] INSTALL.md: document `--manpage-format` changes This change documents the new Configure option, `--manpage-format`. Address formatting issues and expound on the `Reconfigure` section. Signed-off-by: Enji Cooper Reviewed-by: Dmitry Belyavskiy Reviewed-by: Tomas Mraz MergeDate: Mon Jul 20 09:52:30 2026 (Merged from https://github.com/openssl/openssl/pull/28450) --- INSTALL.md | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 51f74e70dd..5d132b49f0 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1223,7 +1223,8 @@ Build without support for the specified algorithm. The `ripemd` algorithm is deprecated and if used is synonymous with `rmd160`. -### Compiler-specific options +Compiler-specific options +------------------------- -Dxxx, -Ixxx, -Wp, -lxxx, -Lxxx, -Wl, -rpath, -R, -framework, -static @@ -1254,7 +1255,17 @@ encoding. Take note of the [Environment Variables](#environment-variables) documentation below and how these flags interact with those variables. -### Environment Variables +Miscellaneous options +--------------------- + +### --manpage-format + +Specify a specific output manpage format. The supported output types are mandoc +and *roff. The *roff output format is the default for legacy and portability +reasons. + +Environment Variables +--------------------- VAR=value @@ -1331,10 +1342,18 @@ If `CC` is set, it is advisable to also set `CXX` to ensure both the C and C++ compiler are in the same "family". This becomes relevant with `enable-external-tests` and `enable-buildtest-c++`. -### Reconfigure +Reconfigure +----------- - reconf - reconfigure +### Make targets + + `$ make reconf` + +or + + `$ make reconfigure` + +### Description Reconfigure from earlier data. From 0345927a022020ab578f55b10b195f90aaec61b0 Mon Sep 17 00:00:00 2001 From: Norbert Pocs Date: Mon, 20 Jul 2026 12:02:17 +0200 Subject: [PATCH 305/349] test_ocsp: bump test number Signed-off-by: Norbert Pocs Reviewed-by: Eugene Syromiatnikov Reviewed-by: Tomas Mraz Reviewed-by: Nikola Pajkovsky MergeDate: Mon Jul 20 10:19:08 2026 (Merged from https://github.com/openssl/openssl/pull/32014) --- test/recipes/80-test_ocsp.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/recipes/80-test_ocsp.t b/test/recipes/80-test_ocsp.t index dc2312b0fc..3f9255b530 100644 --- a/test/recipes/80-test_ocsp.t +++ b/test/recipes/80-test_ocsp.t @@ -54,7 +54,7 @@ sub test_ocsp { $title); }); } -plan tests => 14; +plan tests => 15; subtest "=== VALID OCSP RESPONSES ===" => sub { plan tests => 7; From 4f258c3f3c016502b04681c2eb4c6732525beed5 Mon Sep 17 00:00:00 2001 From: John Claus Date: Fri, 10 Apr 2026 03:21:30 -0600 Subject: [PATCH 306/349] Fix DSA with SHA-384 / SHA-512 X.509 verification (issue openssl#30432) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Register dsa_with_SHA384 and dsa_with_SHA512 in obj_xref so signature verification resolves digest + DSA key type. Add test_verify regression tests and PEM fixtures; add executable DSA cert generation in test/certs/setup.sh. CHANGES.md and NEWS.md entries for 4.0→4.1. Address review: plan tests 215, remove CHANGES merge artifact, replace comment-only setup.sh notes with actual openssl commands. Fixes openssl#30432 Reviewed-by: Dmitry Belyavskiy Reviewed-by: Nikola Pajkovsky MergeDate: Mon Jul 20 11:10:47 2026 (Merged from https://github.com/openssl/openssl/pull/30655) --- CHANGES.md | 6 ++++++ NEWS.md | 2 ++ crypto/objects/obj_xref.h | 12 +++++++---- crypto/objects/obj_xref.txt | 2 ++ test/certs/ee-cert-dsa-sha384.pem | 26 ++++++++++++++++++++++++ test/certs/ee-cert-dsa-sha512.pem | 26 ++++++++++++++++++++++++ test/certs/root-cert-dsa-sha384.pem | 27 +++++++++++++++++++++++++ test/certs/root-cert-dsa-sha512.pem | 27 +++++++++++++++++++++++++ test/certs/setup.sh | 31 +++++++++++++++++++++++++++++ test/recipes/25-test_verify.t | 13 +++++++++++- 10 files changed, 167 insertions(+), 5 deletions(-) create mode 100644 test/certs/ee-cert-dsa-sha384.pem create mode 100644 test/certs/ee-cert-dsa-sha512.pem create mode 100644 test/certs/root-cert-dsa-sha384.pem create mode 100644 test/certs/root-cert-dsa-sha512.pem diff --git a/CHANGES.md b/CHANGES.md index 6a262083a2..6fb71f7d9c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -180,6 +180,12 @@ OpenSSL Releases *Bob Beck* + * Fixed X.509 verification of certificate chains that use DSA signatures + with SHA-384 or SHA-512 by registering `dsa_with_SHA384` and + `dsa_with_SHA512` in the signature-algorithm cross-reference table. + + *John Claus* + * Added AVX2 optimized ML-DSA NTT operations on `x86_64`. *Marcel Cornu and Tomasz Kantecki* diff --git a/NEWS.md b/NEWS.md index 278ec1309d..239f195de0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -31,6 +31,8 @@ OpenSSL 4.1 * API calls `CRYPTO_atomic_load_ptr`, `CRYPTO_atomic_store_ptr`, and `CRYPTO_atomic_cmp_exch_ptr` have been added. + * Fixed verification of DSA certificates signed with SHA-384 or SHA-512. + OpenSSL 4.0 ----------- diff --git a/crypto/objects/obj_xref.h b/crypto/objects/obj_xref.h index 955571e134..46de3ccf4a 100644 --- a/crypto/objects/obj_xref.h +++ b/crypto/objects/obj_xref.h @@ -81,6 +81,8 @@ static const nid_triple sigoid_srt[] = { NID_id_GostR3410_2012_512}, {NID_ED25519, NID_undef, NID_ED25519}, {NID_ED448, NID_undef, NID_ED448}, + {NID_dsa_with_SHA384, NID_sha384, NID_dsa}, + {NID_dsa_with_SHA512, NID_sha512, NID_dsa}, {NID_ecdsa_with_SHA3_224, NID_sha3_224, NID_X9_62_id_ecPublicKey}, {NID_ecdsa_with_SHA3_256, NID_sha3_256, NID_X9_62_id_ecPublicKey}, {NID_ecdsa_with_SHA3_384, NID_sha3_384, NID_X9_62_id_ecPublicKey}, @@ -129,10 +131,12 @@ static const nid_triple *const sigoid_srt_xref[] = { &sigoid_srt[32], &sigoid_srt[37], &sigoid_srt[14], + &sigoid_srt[44], &sigoid_srt[21], &sigoid_srt[33], &sigoid_srt[38], &sigoid_srt[15], + &sigoid_srt[45], &sigoid_srt[22], &sigoid_srt[34], &sigoid_srt[39], @@ -147,15 +151,15 @@ static const nid_triple *const sigoid_srt_xref[] = { &sigoid_srt[28], &sigoid_srt[40], &sigoid_srt[41], - &sigoid_srt[48], - &sigoid_srt[44], - &sigoid_srt[49], - &sigoid_srt[45], &sigoid_srt[50], &sigoid_srt[46], &sigoid_srt[51], &sigoid_srt[47], &sigoid_srt[52], + &sigoid_srt[48], + &sigoid_srt[53], + &sigoid_srt[49], + &sigoid_srt[54], }; /* clang-format on */ diff --git a/crypto/objects/obj_xref.txt b/crypto/objects/obj_xref.txt index 71bc12af74..2f82617a84 100644 --- a/crypto/objects/obj_xref.txt +++ b/crypto/objects/obj_xref.txt @@ -64,6 +64,8 @@ ecdsa_with_SHA3_512 sha3_512 X9_62_id_ecPublicKey dsa_with_SHA224 sha224 dsa dsa_with_SHA256 sha256 dsa +dsa_with_SHA384 sha384 dsa +dsa_with_SHA512 sha512 dsa id_GostR3411_94_with_GostR3410_2001 id_GostR3411_94 id_GostR3410_2001 id_GostR3411_94_with_GostR3410_94 id_GostR3411_94 id_GostR3410_94 diff --git a/test/certs/ee-cert-dsa-sha384.pem b/test/certs/ee-cert-dsa-sha384.pem new file mode 100644 index 0000000000..ab3fe14687 --- /dev/null +++ b/test/certs/ee-cert-dsa-sha384.pem @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE----- +MIIEdjCCBCSgAwIBAgIUNbayaptPn6T0hvP2mKuB8L/W9k8wCwYJYIZIAWUDBAMD +MCgxJjAkBgNVBAMMHU9wZW5TU0wgVGVzdCBEU0EgU0hBLTM4NCBSb290MB4XDTI2 +MDQxMDA3MzMzMFoXDTM2MDQwNzA3MzMzMFowJjEkMCIGA1UEAwwbT3BlblNTTCBU +ZXN0IERTQSBTSEEtMzg0IEVFMIIDQjCCAjUGByqGSM44BAEwggIoAoIBAQDA154m +/UL7G6MMLDAr2BoxQzgT8eNaOV2Ls89BvjF3SS9qgOMQSv9TK8gcb16jpqd6aQ6G +WLedKu3fRUrXCihRaZZaeftJCntAVaDIhtYaYdsua6IDGjWd1SHHmFdQnH3AYLxV +dRmnq3Jjt6oelPQYKDbpWAtT8DsFJ/d6bGpQ6mxW4J1u1FnmKwzuxbR/ZxOV1gQy +D08gfkct7+UtQ5rJUIiIeK2RWsJhF6tdvH+g8VhBXsXo0ZprA/iQHst/aeXSGUBC ++vby1LYog2VFLXD+zl5rzXZUNJLXPt/9Dvs/qYknPlUEboQ8J/afbyuA8S10gsAu +8bnyQ/oTzmA+4Uy5Ah0A0QDXg1zTwGDdtB3OqMZOhhyL+oLzZeFsowwBzwKCAQAs +wjjcjD2G/brbwZuNaZWDHqrpTmmAhvg17+QGp9gaWWb/08TfZ+PbhIL4jV8kf5PZ +kPZVJyocYGduRJsIxiJDlssdaYzgkJ4zJG0A6gYtpBpaD/h6di0E0nzjm+80a9jo +xrgq3jRxkGVHpHvDd4ps0q2UXH44nFCEPWaoebpR4BXCLzXuFw30gUldrClqcMWt +OqQUwf7/ZndhQvLA2PiJEC/5EQFzeMzjl+XAOjW+781rO8BPmniFVXmRGAVWCycL +Qj0pQzvfDDi1M4LZBAw+g6RAa/IAB1mpKNOfsh2wDYS+nJn6q77COZRVn0DZLiVz +M8ENRqH0uRF1osCgqAuBA4IBBQACggEAXj8recOQ2JRABFgypUmG2PA0ibD5z1j3 +Xx7i5UXdwzOMJrtPtzjTmykgg73JpYA38kyqUtaZLKqtR2up+M9Bpj0l8GPZ5brM +E5zJbWukvSw8pnAUvq92izRHlmEK7QUPrkYM2e+NjJKYEOeJ8I1O7mZUBHNsbYGW +jGi0FW+LpcUywCUH7ccxHLFBXJnMUS7zipc6fwBjumFtYTyjsoeE5pyVGs6Cau0o +dzCgR3Ss0gc73qnHv+7t4NP2385AaTKhNeykrtwhrFL6MDGYf0Bs2Y5SE5vEnIZJ +Rb4I+8YnhsxaPGiKy9N6R+1CisnBg+4cx1mEVl3moQtAT6POruJaMqNCMEAwHQYD +VR0OBBYEFNfDDHDjB5VsPYFzEcuRXWZilvyIMB8GA1UdIwQYMBaAFDXj0oV+Bxtx +dRByLwz7g8OjZg/ZMAsGCWCGSAFlAwQDAwM/ADA8AhwezQQknbLR8wtcYnQJLvlv +tILlrOxyRlvUCUGtAhxGW+R77sJNGghc76jH+g3Kkent+0+G3VxrgrOc +-----END CERTIFICATE----- diff --git a/test/certs/ee-cert-dsa-sha512.pem b/test/certs/ee-cert-dsa-sha512.pem new file mode 100644 index 0000000000..e3741ed0dd --- /dev/null +++ b/test/certs/ee-cert-dsa-sha512.pem @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE----- +MIIEdzCCBCSgAwIBAgIUG0h9XyzEews1+X1q3CMHaNZuXhkwCwYJYIZIAWUDBAME +MCgxJjAkBgNVBAMMHU9wZW5TU0wgVGVzdCBEU0EgU0hBLTUxMiBSb290MB4XDTI2 +MDQxMDA3MzMzMFoXDTM2MDQwNzA3MzMzMFowJjEkMCIGA1UEAwwbT3BlblNTTCBU +ZXN0IERTQSBTSEEtNTEyIEVFMIIDQjCCAjUGByqGSM44BAEwggIoAoIBAQDA154m +/UL7G6MMLDAr2BoxQzgT8eNaOV2Ls89BvjF3SS9qgOMQSv9TK8gcb16jpqd6aQ6G +WLedKu3fRUrXCihRaZZaeftJCntAVaDIhtYaYdsua6IDGjWd1SHHmFdQnH3AYLxV +dRmnq3Jjt6oelPQYKDbpWAtT8DsFJ/d6bGpQ6mxW4J1u1FnmKwzuxbR/ZxOV1gQy +D08gfkct7+UtQ5rJUIiIeK2RWsJhF6tdvH+g8VhBXsXo0ZprA/iQHst/aeXSGUBC ++vby1LYog2VFLXD+zl5rzXZUNJLXPt/9Dvs/qYknPlUEboQ8J/afbyuA8S10gsAu +8bnyQ/oTzmA+4Uy5Ah0A0QDXg1zTwGDdtB3OqMZOhhyL+oLzZeFsowwBzwKCAQAs +wjjcjD2G/brbwZuNaZWDHqrpTmmAhvg17+QGp9gaWWb/08TfZ+PbhIL4jV8kf5PZ +kPZVJyocYGduRJsIxiJDlssdaYzgkJ4zJG0A6gYtpBpaD/h6di0E0nzjm+80a9jo +xrgq3jRxkGVHpHvDd4ps0q2UXH44nFCEPWaoebpR4BXCLzXuFw30gUldrClqcMWt +OqQUwf7/ZndhQvLA2PiJEC/5EQFzeMzjl+XAOjW+781rO8BPmniFVXmRGAVWCycL +Qj0pQzvfDDi1M4LZBAw+g6RAa/IAB1mpKNOfsh2wDYS+nJn6q77COZRVn0DZLiVz +M8ENRqH0uRF1osCgqAuBA4IBBQACggEADpK5z3E7aC/bPkHbIApYwMui0PlPm9mZ +fJcOUYkUxrGwsVB7MLlFKaYogohKW/6llyTQ9KVv0GhXGBU8KZAwYi2IvEqZWt96 +QWMReZkr85mcfWPPr0nhPaiKPFLuzbrJS5+8pRZ0JZICAs7fWK2ieTFzF5T52+uU +HW3d8hRW0faGy3JVChySx2zKhznax0ugOeO6xpw+GIhh7DtoLN9+WCUD4gHxcT1e +ngH3SPr9kSAc179ZheLPV8K96D6h62xLyDOv66jKqxuEuwKMezKhI8NoWlIjFlwq +qeg+/URRSKaemF7CeEtjfxfyhPyWFBA3ied6v8lgcvm8xEK1fOuWtaNCMEAwHQYD +VR0OBBYEFG70aEHyZTHkRfESJAk59LuyXqj2MB8GA1UdIwQYMBaAFLO3mEJ4Js8i +80dWuZffkE17675DMAsGCWCGSAFlAwQDBANAADA9AhxQI/S0oZhUBxth3amipFkw +px3NJNYZEdKm1x4hAh0AotKopAK/hiDc2IGgHC+L2kjsvg/tqofl54aZ6w== +-----END CERTIFICATE----- diff --git a/test/certs/root-cert-dsa-sha384.pem b/test/certs/root-cert-dsa-sha384.pem new file mode 100644 index 0000000000..107e855b42 --- /dev/null +++ b/test/certs/root-cert-dsa-sha384.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEizCCBDigAwIBAgIUL44kPKEDy7eaEgXNH4xXTuVw77owCwYJYIZIAWUDBAMD +MCgxJjAkBgNVBAMMHU9wZW5TU0wgVGVzdCBEU0EgU0hBLTM4NCBSb290MB4XDTI2 +MDQxMDA3MzMzMFoXDTM2MDQwNzA3MzMzMFowKDEmMCQGA1UEAwwdT3BlblNTTCBU +ZXN0IERTQSBTSEEtMzg0IFJvb3QwggNDMIICNQYHKoZIzjgEATCCAigCggEBAMDX +nib9QvsbowwsMCvYGjFDOBPx41o5XYuzz0G+MXdJL2qA4xBK/1MryBxvXqOmp3pp +DoZYt50q7d9FStcKKFFpllp5+0kKe0BVoMiG1hph2y5rogMaNZ3VIceYV1CcfcBg +vFV1GaercmO3qh6U9BgoNulYC1PwOwUn93psalDqbFbgnW7UWeYrDO7FtH9nE5XW +BDIPTyB+Ry3v5S1DmslQiIh4rZFawmEXq128f6DxWEFexejRmmsD+JAey39p5dIZ +QEL69vLUtiiDZUUtcP7OXmvNdlQ0ktc+3/0O+z+piSc+VQRuhDwn9p9vK4DxLXSC +wC7xufJD+hPOYD7hTLkCHQDRANeDXNPAYN20Hc6oxk6GHIv6gvNl4WyjDAHPAoIB +ACzCONyMPYb9utvBm41plYMequlOaYCG+DXv5Aan2BpZZv/TxN9n49uEgviNXyR/ +k9mQ9lUnKhxgZ25EmwjGIkOWyx1pjOCQnjMkbQDqBi2kGloP+Hp2LQTSfOOb7zRr +2OjGuCreNHGQZUeke8N3imzSrZRcfjicUIQ9Zqh5ulHgFcIvNe4XDfSBSV2sKWpw +xa06pBTB/v9md2FC8sDY+IkQL/kRAXN4zOOX5cA6Nb7vzWs7wE+aeIVVeZEYBVYL +JwtCPSlDO98MOLUzgtkEDD6DpEBr8gAHWako05+yHbANhL6cmfqrvsI5lFWfQNku +JXMzwQ1GofS5EXWiwKCoC4EDggEGAAKCAQEAqBWUzoivwm10KhCOVj4dLJ376hik +coO7wh8szq+0lBCKVQu0XgZdpR67ibiInCslpi4D31QX1ciP8Ds3Tnr92Xq9fp6F +Q9+M94it5BWvXq8uWnsCYRLIXds+8DKnUMTPc0U5Qc/HnfkdgHockBya5ZKRLzgW +THC5n29NjBoIH9TBFddxanUqOqM2a0+0uC23jC/rmsmIXKKLIRnBjDjCfWGkpvDR +V0TmgU+wBN7Fu6/PDdb2h1omgx5iJ0hR9bSQMevPzxKWIIYt3jBa3JWRJVshZbCX +zGSycHIM4IPPEWtDEMR/s/aO5+HcQdV42itClvOQxhVfYTtBCV8TiMYFAKNTMFEw +HQYDVR0OBBYEFDXj0oV+BxtxdRByLwz7g8OjZg/ZMB8GA1UdIwQYMBaAFDXj0oV+ +BxtxdRByLwz7g8OjZg/ZMA8GA1UdEwEB/wQFMAMBAf8wCwYJYIZIAWUDBAMDA0AA +MD0CHQC/zbfFT+FeeYr9PmYtblMAGmtUgM2VyUcargqPAhwnaYxJwO0tJHKZDXOG +38UfhwBTUj0PNyAwbOG/ +-----END CERTIFICATE----- diff --git a/test/certs/root-cert-dsa-sha512.pem b/test/certs/root-cert-dsa-sha512.pem new file mode 100644 index 0000000000..5dbe393aea --- /dev/null +++ b/test/certs/root-cert-dsa-sha512.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEiTCCBDagAwIBAgITGBs2j74eZNOfebavEYuJJZKVKTALBglghkgBZQMEAwQw +KDEmMCQGA1UEAwwdT3BlblNTTCBUZXN0IERTQSBTSEEtNTEyIFJvb3QwHhcNMjYw +NDEwMDczMzMwWhcNMzYwNDA3MDczMzMwWjAoMSYwJAYDVQQDDB1PcGVuU1NMIFRl +c3QgRFNBIFNIQS01MTIgUm9vdDCCA0IwggI1BgcqhkjOOAQBMIICKAKCAQEAwNee +Jv1C+xujDCwwK9gaMUM4E/HjWjldi7PPQb4xd0kvaoDjEEr/UyvIHG9eo6anemkO +hli3nSrt30VK1wooUWmWWnn7SQp7QFWgyIbWGmHbLmuiAxo1ndUhx5hXUJx9wGC8 +VXUZp6tyY7eqHpT0GCg26VgLU/A7BSf3emxqUOpsVuCdbtRZ5isM7sW0f2cTldYE +Mg9PIH5HLe/lLUOayVCIiHitkVrCYRerXbx/oPFYQV7F6NGaawP4kB7Lf2nl0hlA +Qvr28tS2KINlRS1w/s5ea812VDSS1z7f/Q77P6mJJz5VBG6EPCf2n28rgPEtdILA +LvG58kP6E85gPuFMuQIdANEA14Nc08Bg3bQdzqjGToYci/qC82XhbKMMAc8CggEA +LMI43Iw9hv2628GbjWmVgx6q6U5pgIb4Ne/kBqfYGllm/9PE32fj24SC+I1fJH+T +2ZD2VScqHGBnbkSbCMYiQ5bLHWmM4JCeMyRtAOoGLaQaWg/4enYtBNJ845vvNGvY +6Ma4Kt40cZBlR6R7w3eKbNKtlFx+OJxQhD1mqHm6UeAVwi817hcN9IFJXawpanDF +rTqkFMH+/2Z3YULywNj4iRAv+REBc3jM45flwDo1vu/NazvAT5p4hVV5kRgFVgsn +C0I9KUM73ww4tTOC2QQMPoOkQGvyAAdZqSjTn7IdsA2EvpyZ+qu+wjmUVZ9A2S4l +czPBDUah9LkRdaLAoKgLgQOCAQUAAoIBAAHb5mq9RGf+mLtQi12GnLIlWF45bhHr +0+gNYMzTA7PBzb/E+SfeizOnNuJtNU3LSiNE+sHNMnn/WY4KvjMCryuRxz7b8Y8+ +0nGwN0YfyY/cK5CehRa+lfhahlTorF8VY98gcQJOyKjG0eloZ8H29MhPhYTF0qTS +5dbDb4nf5r5Uqx5/6gBmskpMG+xiXA2I7q2aLW92pZrFWYrkrNgaAAQqSl0whhG6 +EZXP6fAbndJbt706xFlySr/ZHkgWybEAXYO8TbjyDkZP/h9UyR6CnKeb3L6qw8be +8U5scYfx2HjhCv5GLqqauRm+0oJwbXmOWuMpeynGHy24O+wKAX/ePhujUzBRMB0G +A1UdDgQWBBSzt5hCeCbPIvNHVrmX35BNe+u+QzAfBgNVHSMEGDAWgBSzt5hCeCbP +IvNHVrmX35BNe+u+QzAPBgNVHRMBAf8EBTADAQH/MAsGCWCGSAFlAwQDBANAADA9 +AhxgdnguNQoOQZyseelUWc5tD7m/ESvTuAV2aiEwAh0Apmc4SMIg7nKTA2jp7M1E +V7kA7INPUKKx/+iWgA== +-----END CERTIFICATE----- diff --git a/test/certs/setup.sh b/test/certs/setup.sh index 3bee78ec32..3056b1f90a 100755 --- a/test/certs/setup.sh +++ b/test/certs/setup.sh @@ -497,6 +497,37 @@ OPENSSL_SIGALG="sha3-256" ./mkcert.sh genee server.example ee-key-ec-named-named OPENSSL_SIGALG="sha3-384" ./mkcert.sh genee server.example ee-key-ec-named-named ee-cert-ec-sha3-384 ca-key-ec-named ca-cert-ec-named OPENSSL_SIGALG="sha3-512" ./mkcert.sh genee server.example ee-key-ec-named-named ee-cert-ec-sha3-512 ca-key-ec-named ca-cert-ec-named +# DSA roots and EE certs: id-dsa-with-sha384 / id-dsa-with-sha512 +# (regression for https://github.com/openssl/openssl/issues/30432) +_DSA_CERT_DIR=$(cd "$(dirname "$0")" && pwd) +( + set -e + d=$(mktemp -d) + trap 'rm -rf "$d"' EXIT + cd "$d" + openssl dsaparam -out dsap.pem 2048 + openssl gendsa -out ca384k.pem dsap.pem + openssl req -new -x509 -key ca384k.pem -sha384 -out root-cert-dsa-sha384.pem \ + -days 3650 -subj "/CN=OpenSSL Test DSA SHA-384 Root" -nodes + openssl gendsa -out ee384k.pem dsap.pem + openssl req -new -key ee384k.pem -out ee384.csr \ + -subj "/CN=OpenSSL Test DSA SHA-384 EE" + openssl x509 -req -in ee384.csr -CA root-cert-dsa-sha384.pem -CAkey ca384k.pem \ + -CAcreateserial -out ee-cert-dsa-sha384.pem -days 3650 -sha384 + openssl gendsa -out ca512k.pem dsap.pem + openssl req -new -x509 -key ca512k.pem -sha512 -out root-cert-dsa-sha512.pem \ + -days 3650 -subj "/CN=OpenSSL Test DSA SHA-512 Root" -nodes + openssl gendsa -out ee512k.pem dsap.pem + openssl req -new -key ee512k.pem -out ee512.csr \ + -subj "/CN=OpenSSL Test DSA SHA-512 EE" + openssl x509 -req -in ee512.csr -CA root-cert-dsa-sha512.pem -CAkey ca512k.pem \ + -CAcreateserial -out ee-cert-dsa-sha512.pem -days 3650 -sha512 + cp root-cert-dsa-sha384.pem ee-cert-dsa-sha384.pem \ + root-cert-dsa-sha512.pem ee-cert-dsa-sha512.pem \ + "$_DSA_CERT_DIR" +) +unset _DSA_CERT_DIR + # EC cert seigned RSA intermediate CA OPENSSL_KEYALG=ec OPENSSL_KEYBITS=prime256v1 ./mkcert.sh genee \ "P-256 cert EE issuer" p256-ee-rsa-ca-key \ diff --git a/test/recipes/25-test_verify.t b/test/recipes/25-test_verify.t index 9564e78153..6efe24087a 100644 --- a/test/recipes/25-test_verify.t +++ b/test/recipes/25-test_verify.t @@ -30,7 +30,7 @@ sub verify { run(app([@args])); } -plan tests => 219; +plan tests => 221; # Canonical success ok(verify("ee-cert", "sslserver", ["root-cert"], ["ca-cert"]), @@ -362,6 +362,17 @@ SKIP: { ok(verify("ee-cert-ec-sha3-512", "", ["root-cert"], ["ca-cert-ec-named"], ), "accept cert generated with EC and SHA3-512"); } + +# DSA chains using id-dsa-with-sha384 / id-dsa-with-sha512 (GitHub issue #30432) +SKIP: { + skip "DSA is not supported by this OpenSSL build", 2 + if disabled("dsa"); + + ok(verify("ee-cert-dsa-sha384", "", ["root-cert-dsa-sha384"], [], ), + "accept DSA cert chain with SHA-384 signatures"); + ok(verify("ee-cert-dsa-sha512", "", ["root-cert-dsa-sha512"], [], ), + "accept DSA cert chain with SHA-512 signatures"); +} # Same as above but with base provider used for decoding SKIP: { my $no_fips = disabled('fips') || ($ENV{NO_FIPS} // 0); From 437e09afb1f3dd8d8f888ecca9aaf7cb53ae7c60 Mon Sep 17 00:00:00 2001 From: Frederik Wedel-Heinen Date: Sat, 27 Jun 2026 07:11:14 +0200 Subject: [PATCH 307/349] Make sure we always check return of CRYPTO_UP_REF which can fail. Reviewed-by: Kurt Roeckx Reviewed-by: Paul Dale MergeDate: Mon Jul 20 11:13:58 2026 (Merged from https://github.com/openssl/openssl/pull/31753) --- crypto/bio/bio_lib.c | 2 +- crypto/dh/dh_lib.c | 2 +- crypto/dsa/dsa_lib.c | 2 +- crypto/dso/dso_lib.c | 2 +- crypto/ec/ec_key.c | 2 +- crypto/ec/ec_mult.c | 4 +-- crypto/ec/ecp_nistp224.c | 4 +-- crypto/ec/ecp_nistp256.c | 4 +-- crypto/ec/ecp_nistp384.c | 4 +-- crypto/ec/ecp_nistp521.c | 4 +-- crypto/ec/ecp_nistz256.c | 4 +-- crypto/ec/ecx_key.c | 2 +- crypto/encode_decode/decoder_meth.c | 3 +- crypto/encode_decode/encoder_meth.c | 3 +- crypto/evp/asymcipher.c | 3 +- crypto/evp/digest.c | 2 +- crypto/evp/evp_enc.c | 2 +- crypto/evp/exchange.c | 3 +- crypto/evp/kdf_meth.c | 3 +- crypto/evp/kem.c | 3 +- crypto/evp/keymgmt_meth.c | 3 +- crypto/evp/mac_meth.c | 3 +- crypto/evp/p_lib.c | 2 +- crypto/evp/s_lib.c | 2 +- crypto/evp/signature.c | 3 +- crypto/evp/skeymgmt_meth.c | 3 +- crypto/provider_core.c | 4 +-- crypto/rsa/rsa_lib.c | 2 +- crypto/store/store_meth.c | 2 +- crypto/x509/x509_lu.c | 2 +- crypto/x509/x509_set.c | 2 +- crypto/x509/x509cset.c | 2 +- doc/internal/man3/evp_generic_fetch.pod | 3 +- include/internal/refcount.h | 30 ++++++++++--------- .../keymgmt/kdf_legacy_kmgmt.c | 3 +- .../keymgmt/mac_legacy_kmgmt.c | 3 +- ssl/quic/quic_impl.c | 3 +- ssl/ssl_cert_comp.c | 2 +- ssl/ssl_lib.c | 11 ++++--- ssl/ssl_sess.c | 2 +- test/tls-provider.c | 2 +- 41 files changed, 69 insertions(+), 78 deletions(-) diff --git a/crypto/bio/bio_lib.c b/crypto/bio/bio_lib.c index dbd55c8b2f..6066cc0d8c 100644 --- a/crypto/bio/bio_lib.c +++ b/crypto/bio/bio_lib.c @@ -193,7 +193,7 @@ int BIO_up_ref(BIO *a) { int i; - if (CRYPTO_UP_REF(&a->references, &i) <= 0) + if (!CRYPTO_UP_REF(&a->references, &i)) return 0; REF_PRINT_COUNT("BIO", i, a); diff --git a/crypto/dh/dh_lib.c b/crypto/dh/dh_lib.c index 27ca94d1a6..1934c71080 100644 --- a/crypto/dh/dh_lib.c +++ b/crypto/dh/dh_lib.c @@ -142,7 +142,7 @@ int DH_up_ref(DH *r) { int i; - if (CRYPTO_UP_REF(&r->references, &i) <= 0) + if (!CRYPTO_UP_REF(&r->references, &i)) return 0; REF_PRINT_COUNT("DH", i, r); diff --git a/crypto/dsa/dsa_lib.c b/crypto/dsa/dsa_lib.c index 834d271346..5936b84098 100644 --- a/crypto/dsa/dsa_lib.c +++ b/crypto/dsa/dsa_lib.c @@ -215,7 +215,7 @@ int DSA_up_ref(DSA *r) { int i; - if (CRYPTO_UP_REF(&r->references, &i) <= 0) + if (!CRYPTO_UP_REF(&r->references, &i)) return 0; REF_PRINT_COUNT("DSA", i, r); diff --git a/crypto/dso/dso_lib.c b/crypto/dso/dso_lib.c index 6f51e4d35a..1366bea66d 100644 --- a/crypto/dso/dso_lib.c +++ b/crypto/dso/dso_lib.c @@ -93,7 +93,7 @@ int DSO_up_ref(DSO *dso) return 0; } - if (CRYPTO_UP_REF(&dso->references, &i) <= 0) + if (!CRYPTO_UP_REF(&dso->references, &i)) return 0; REF_PRINT_COUNT("DSO", i, dso); diff --git a/crypto/ec/ec_key.c b/crypto/ec/ec_key.c index 44791a2c3e..4a84933bb6 100644 --- a/crypto/ec/ec_key.c +++ b/crypto/ec/ec_key.c @@ -175,7 +175,7 @@ int EC_KEY_up_ref(EC_KEY *r) { int i; - if (CRYPTO_UP_REF(&r->references, &i) <= 0) + if (!CRYPTO_UP_REF(&r->references, &i)) return 0; REF_PRINT_COUNT("EC_KEY", i, r); diff --git a/crypto/ec/ec_mult.c b/crypto/ec/ec_mult.c index 4771789151..f5c6ac7893 100644 --- a/crypto/ec/ec_mult.c +++ b/crypto/ec/ec_mult.c @@ -72,8 +72,8 @@ static EC_PRE_COMP *ec_pre_comp_new(const EC_GROUP *group) EC_PRE_COMP *EC_ec_pre_comp_dup(EC_PRE_COMP *pre) { int i; - if (pre != NULL) - CRYPTO_UP_REF(&pre->references, &i); + if (pre == NULL || !CRYPTO_UP_REF(&pre->references, &i)) + return NULL; return pre; } diff --git a/crypto/ec/ecp_nistp224.c b/crypto/ec/ecp_nistp224.c index 7183131622..1a0952083c 100644 --- a/crypto/ec/ecp_nistp224.c +++ b/crypto/ec/ecp_nistp224.c @@ -1234,8 +1234,8 @@ static NISTP224_PRE_COMP *nistp224_pre_comp_new(void) NISTP224_PRE_COMP *EC_nistp224_pre_comp_dup(NISTP224_PRE_COMP *p) { int i; - if (p != NULL) - CRYPTO_UP_REF(&p->references, &i); + if (p == NULL || !CRYPTO_UP_REF(&p->references, &i)) + return NULL; return p; } diff --git a/crypto/ec/ecp_nistp256.c b/crypto/ec/ecp_nistp256.c index e247e51c9c..fba197a8c7 100644 --- a/crypto/ec/ecp_nistp256.c +++ b/crypto/ec/ecp_nistp256.c @@ -1852,8 +1852,8 @@ static NISTP256_PRE_COMP *nistp256_pre_comp_new(void) NISTP256_PRE_COMP *EC_nistp256_pre_comp_dup(NISTP256_PRE_COMP *p) { int i; - if (p != NULL) - CRYPTO_UP_REF(&p->references, &i); + if (p == NULL || !CRYPTO_UP_REF(&p->references, &i)) + return NULL; return p; } diff --git a/crypto/ec/ecp_nistp384.c b/crypto/ec/ecp_nistp384.c index e03bda2178..3d212dcc15 100644 --- a/crypto/ec/ecp_nistp384.c +++ b/crypto/ec/ecp_nistp384.c @@ -1576,8 +1576,8 @@ NISTP384_PRE_COMP *ossl_ec_nistp384_pre_comp_dup(NISTP384_PRE_COMP *p) { int i; - if (p != NULL) - CRYPTO_UP_REF(&p->references, &i); + if (p == NULL || !CRYPTO_UP_REF(&p->references, &i)) + return NULL; return p; } diff --git a/crypto/ec/ecp_nistp521.c b/crypto/ec/ecp_nistp521.c index 7ea8d00c14..f7315f136c 100644 --- a/crypto/ec/ecp_nistp521.c +++ b/crypto/ec/ecp_nistp521.c @@ -1667,8 +1667,8 @@ static NISTP521_PRE_COMP *nistp521_pre_comp_new(void) NISTP521_PRE_COMP *EC_nistp521_pre_comp_dup(NISTP521_PRE_COMP *p) { int i; - if (p != NULL) - CRYPTO_UP_REF(&p->references, &i); + if (p == NULL || !CRYPTO_UP_REF(&p->references, &i)) + return NULL; return p; } diff --git a/crypto/ec/ecp_nistz256.c b/crypto/ec/ecp_nistz256.c index 301f90188a..df467b948f 100644 --- a/crypto/ec/ecp_nistz256.c +++ b/crypto/ec/ecp_nistz256.c @@ -1208,8 +1208,8 @@ static NISTZ256_PRE_COMP *ecp_nistz256_pre_comp_new(const EC_GROUP *group) NISTZ256_PRE_COMP *EC_nistz256_pre_comp_dup(NISTZ256_PRE_COMP *p) { int i; - if (p != NULL) - CRYPTO_UP_REF(&p->references, &i); + if (p == NULL || !CRYPTO_UP_REF(&p->references, &i)) + return NULL; return p; } diff --git a/crypto/ec/ecx_key.c b/crypto/ec/ecx_key.c index 4d8c945755..036e308e8b 100644 --- a/crypto/ec/ecx_key.c +++ b/crypto/ec/ecx_key.c @@ -92,7 +92,7 @@ int ossl_ecx_key_up_ref(ECX_KEY *key) { int i; - if (CRYPTO_UP_REF(&key->references, &i) <= 0) + if (!CRYPTO_UP_REF(&key->references, &i)) return 0; REF_PRINT_COUNT("ECX_KEY", i, key); diff --git a/crypto/encode_decode/decoder_meth.c b/crypto/encode_decode/decoder_meth.c index 772c29c031..03118f5843 100644 --- a/crypto/encode_decode/decoder_meth.c +++ b/crypto/encode_decode/decoder_meth.c @@ -47,8 +47,7 @@ static int ossl_decoder_up_ref(void *data) OSSL_DECODER *decoder = (OSSL_DECODER *)data; int ref = 0; - CRYPTO_UP_REF(&decoder->base.refcnt, &ref); - return 1; + return CRYPTO_UP_REF(&decoder->base.refcnt, &ref); } /* Simple method structure constructor and destructor */ diff --git a/crypto/encode_decode/encoder_meth.c b/crypto/encode_decode/encoder_meth.c index 23dcccebb9..cb167004ab 100644 --- a/crypto/encode_decode/encoder_meth.c +++ b/crypto/encode_decode/encoder_meth.c @@ -48,8 +48,7 @@ static int ossl_encoder_up_ref(void *data) OSSL_ENCODER *encoder = (OSSL_ENCODER *)data; int ref = 0; - CRYPTO_UP_REF(&encoder->base.refcnt, &ref); - return 1; + return CRYPTO_UP_REF(&encoder->base.refcnt, &ref); } /* Simple method structure constructor and destructor */ diff --git a/crypto/evp/asymcipher.c b/crypto/evp/asymcipher.c index 1665efd3e2..ec643bec9e 100644 --- a/crypto/evp/asymcipher.c +++ b/crypto/evp/asymcipher.c @@ -38,8 +38,7 @@ static int evp_asym_cipher_up_ref(void *data) EVP_ASYM_CIPHER *cipher = (EVP_ASYM_CIPHER *)data; int ref = 0; - CRYPTO_UP_REF(&cipher->refcnt, &ref); - return 1; + return CRYPTO_UP_REF(&cipher->refcnt, &ref); } static int evp_pkey_asym_cipher_init(EVP_PKEY_CTX *ctx, int operation, diff --git a/crypto/evp/digest.c b/crypto/evp/digest.c index 61e7dbc4ec..db7121f0a4 100644 --- a/crypto/evp/digest.c +++ b/crypto/evp/digest.c @@ -981,7 +981,7 @@ static int evp_md_up_ref(void *m) int ref = 0; if (md->origin == EVP_ORIG_DYNAMIC) - CRYPTO_UP_REF(&md->refcnt, &ref); + return CRYPTO_UP_REF(&md->refcnt, &ref); return 1; } diff --git a/crypto/evp/evp_enc.c b/crypto/evp/evp_enc.c index 830cfdb8d8..a5c513db0d 100644 --- a/crypto/evp/evp_enc.c +++ b/crypto/evp/evp_enc.c @@ -1346,7 +1346,7 @@ static int evp_cipher_up_ref(void *c) int ref = 0; if (cipher->origin == EVP_ORIG_DYNAMIC) - CRYPTO_UP_REF(&cipher->refcnt, &ref); + return CRYPTO_UP_REF(&cipher->refcnt, &ref); return 1; } diff --git a/crypto/evp/exchange.c b/crypto/evp/exchange.c index 83fcbeb104..8718726076 100644 --- a/crypto/evp/exchange.c +++ b/crypto/evp/exchange.c @@ -40,8 +40,7 @@ static int evp_keyexch_up_ref(void *data) EVP_KEYEXCH *exchange = (EVP_KEYEXCH *)data; int ref = 0; - CRYPTO_UP_REF(&exchange->refcnt, &ref); - return 1; + return CRYPTO_UP_REF(&exchange->refcnt, &ref); } static EVP_KEYEXCH *evp_keyexch_new(OSSL_PROVIDER *prov) diff --git a/crypto/evp/kdf_meth.c b/crypto/evp/kdf_meth.c index e0741450e9..c6df2971db 100644 --- a/crypto/evp/kdf_meth.c +++ b/crypto/evp/kdf_meth.c @@ -22,8 +22,7 @@ static int evp_kdf_up_ref(void *vkdf) EVP_KDF *kdf = (EVP_KDF *)vkdf; int ref = 0; - CRYPTO_UP_REF(&kdf->refcnt, &ref); - return 1; + return CRYPTO_UP_REF(&kdf->refcnt, &ref); } static void evp_kdf_free(void *vkdf) diff --git a/crypto/evp/kem.c b/crypto/evp/kem.c index 4041390106..8ae968d5a5 100644 --- a/crypto/evp/kem.c +++ b/crypto/evp/kem.c @@ -39,8 +39,7 @@ static int evp_kem_up_ref(void *data) EVP_KEM *kem = (EVP_KEM *)data; int ref = 0; - CRYPTO_UP_REF(&kem->refcnt, &ref); - return 1; + return CRYPTO_UP_REF(&kem->refcnt, &ref); } static int evp_kem_init(EVP_PKEY_CTX *ctx, int operation, diff --git a/crypto/evp/keymgmt_meth.c b/crypto/evp/keymgmt_meth.c index 07ea8f8b9e..9cf4ef5e4f 100644 --- a/crypto/evp/keymgmt_meth.c +++ b/crypto/evp/keymgmt_meth.c @@ -39,8 +39,7 @@ static int evp_keymgmt_up_ref(void *data) EVP_KEYMGMT *keymgmt = (EVP_KEYMGMT *)data; int ref = 0; - CRYPTO_UP_REF(&keymgmt->refcnt, &ref); - return 1; + return CRYPTO_UP_REF(&keymgmt->refcnt, &ref); } static void *keymgmt_new(void) diff --git a/crypto/evp/mac_meth.c b/crypto/evp/mac_meth.c index 62d94de45c..2a6fb0abe5 100644 --- a/crypto/evp/mac_meth.c +++ b/crypto/evp/mac_meth.c @@ -21,8 +21,7 @@ static int evp_mac_up_ref(void *vmac) EVP_MAC *mac = vmac; int ref = 0; - CRYPTO_UP_REF(&mac->refcnt, &ref); - return 1; + return CRYPTO_UP_REF(&mac->refcnt, &ref); } static void evp_mac_free(void *vmac) diff --git a/crypto/evp/p_lib.c b/crypto/evp/p_lib.c index 2895db09a7..f13390b54e 100644 --- a/crypto/evp/p_lib.c +++ b/crypto/evp/p_lib.c @@ -1630,7 +1630,7 @@ int EVP_PKEY_up_ref(EVP_PKEY *pkey) { int i; - if (CRYPTO_UP_REF(&pkey->references, &i) <= 0) + if (!CRYPTO_UP_REF(&pkey->references, &i)) return 0; REF_PRINT_COUNT("EVP_PKEY", i, pkey); diff --git a/crypto/evp/s_lib.c b/crypto/evp/s_lib.c index 5594dc81c5..3f76136324 100644 --- a/crypto/evp/s_lib.c +++ b/crypto/evp/s_lib.c @@ -196,7 +196,7 @@ int EVP_SKEY_up_ref(EVP_SKEY *skey) { int i; - if (CRYPTO_UP_REF(&skey->references, &i) <= 0) + if (!CRYPTO_UP_REF(&skey->references, &i)) return 0; REF_PRINT_COUNT("EVP_SKEY", i, skey); diff --git a/crypto/evp/signature.c b/crypto/evp/signature.c index f2e405aaa3..6c6aad7e92 100644 --- a/crypto/evp/signature.c +++ b/crypto/evp/signature.c @@ -41,8 +41,7 @@ static int evp_signature_up_ref(void *data) EVP_SIGNATURE *signature = (EVP_SIGNATURE *)data; int ref = 0; - CRYPTO_UP_REF(&signature->refcnt, &ref); - return 1; + return CRYPTO_UP_REF(&signature->refcnt, &ref); } static EVP_SIGNATURE *evp_signature_new(OSSL_PROVIDER *prov) diff --git a/crypto/evp/skeymgmt_meth.c b/crypto/evp/skeymgmt_meth.c index d1e5c9d446..dbc14c0e0d 100644 --- a/crypto/evp/skeymgmt_meth.c +++ b/crypto/evp/skeymgmt_meth.c @@ -135,8 +135,7 @@ static int evp_skeymgmt_up_ref(void *s) EVP_SKEYMGMT *skeymgmt = (EVP_SKEYMGMT *)s; int ref = 0; - CRYPTO_UP_REF(&skeymgmt->refcnt, &ref); - return 1; + return CRYPTO_UP_REF(&skeymgmt->refcnt, &ref); } static void evp_skeymgmt_free(void *s) diff --git a/crypto/provider_core.c b/crypto/provider_core.c index b408e95e10..93732995be 100644 --- a/crypto/provider_core.c +++ b/crypto/provider_core.c @@ -486,7 +486,7 @@ int ossl_provider_up_ref(OSSL_PROVIDER *prov) { int ref = 0; - if (CRYPTO_UP_REF(&prov->refcnt, &ref) <= 0) + if (!CRYPTO_UP_REF(&prov->refcnt, &ref)) return 0; #ifndef FIPS_MODULE @@ -1576,7 +1576,7 @@ int ossl_provider_doall_activated(OSSL_LIB_CTX *ctx, * to avoid upping the ref count on the parent provider, which we * must not do while holding locks. */ - if (CRYPTO_UP_REF(&prov->refcnt, &ref) <= 0) { + if (!CRYPTO_UP_REF(&prov->refcnt, &ref)) { CRYPTO_THREAD_unlock(prov->flag_lock); goto err_unlock; } diff --git a/crypto/rsa/rsa_lib.c b/crypto/rsa/rsa_lib.c index a7d5798c88..88a7a2b93f 100644 --- a/crypto/rsa/rsa_lib.c +++ b/crypto/rsa/rsa_lib.c @@ -163,7 +163,7 @@ int RSA_up_ref(RSA *r) { int i; - if (CRYPTO_UP_REF(&r->references, &i) <= 0) + if (!CRYPTO_UP_REF(&r->references, &i)) return 0; REF_PRINT_COUNT("RSA", i, r); diff --git a/crypto/store/store_meth.c b/crypto/store/store_meth.c index 976e4aa734..1d54d89978 100644 --- a/crypto/store/store_meth.c +++ b/crypto/store/store_meth.c @@ -22,7 +22,7 @@ static int up_ref_loader(void *method) int ref = 0; if (loader->prov != NULL) - CRYPTO_UP_REF(&loader->refcnt, &ref); + return CRYPTO_UP_REF(&loader->refcnt, &ref); return 1; } diff --git a/crypto/x509/x509_lu.c b/crypto/x509/x509_lu.c index d1f4c1eb40..5be50c2195 100644 --- a/crypto/x509/x509_lu.c +++ b/crypto/x509/x509_lu.c @@ -287,7 +287,7 @@ int X509_STORE_up_ref(X509_STORE *xs) { int i; - if (CRYPTO_UP_REF(&xs->references, &i) <= 0) + if (!CRYPTO_UP_REF(&xs->references, &i)) return 0; REF_PRINT_COUNT("X509_STORE", i, xs); diff --git a/crypto/x509/x509_set.c b/crypto/x509/x509_set.c index 8a2a12e4b6..11439bcafb 100644 --- a/crypto/x509/x509_set.c +++ b/crypto/x509/x509_set.c @@ -117,7 +117,7 @@ int X509_up_ref(X509 *x) { int i; - if (CRYPTO_UP_REF(&x->references, &i) <= 0) + if (!CRYPTO_UP_REF(&x->references, &i)) return 0; REF_PRINT_COUNT("X509", i, x); diff --git a/crypto/x509/x509cset.c b/crypto/x509/x509cset.c index 20de6a340e..ec3e1f5360 100644 --- a/crypto/x509/x509cset.c +++ b/crypto/x509/x509cset.c @@ -75,7 +75,7 @@ int X509_CRL_up_ref(X509_CRL *crl) { int i; - if (CRYPTO_UP_REF(&crl->references, &i) <= 0) + if (!CRYPTO_UP_REF(&crl->references, &i)) return 0; REF_PRINT_COUNT("X509_CRL", i, crl); diff --git a/doc/internal/man3/evp_generic_fetch.pod b/doc/internal/man3/evp_generic_fetch.pod index 016494239e..53f29c1e98 100644 --- a/doc/internal/man3/evp_generic_fetch.pod +++ b/doc/internal/man3/evp_generic_fetch.pod @@ -187,8 +187,7 @@ And here's the implementation of the FOO method fetcher: EVP_FOO *foo = vfoo; int ref = 0; - CRYPTO_UP_REF(&foo->refcnt, &ref); - return 1; + return CRYPTO_UP_REF(&foo->refcnt, &ref); } static void foo_free(void *vfoo) diff --git a/include/internal/refcount.h b/include/internal/refcount.h index 2e7dedf2c3..8bbb11e3fc 100644 --- a/include/internal/refcount.h +++ b/include/internal/refcount.h @@ -14,6 +14,8 @@ #include #include +#include + #if defined(OPENSSL_THREADS) && !defined(OPENSSL_DEV_NO_ATOMICS) #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L \ && !defined(__STDC_NO_ATOMICS__) @@ -36,10 +38,10 @@ typedef struct { _Atomic int val; } CRYPTO_REF_COUNT; -static inline int CRYPTO_UP_REF(CRYPTO_REF_COUNT *refcnt, int *ret) +static inline bool CRYPTO_UP_REF(CRYPTO_REF_COUNT *refcnt, int *ret) { *ret = atomic_fetch_add_explicit(&refcnt->val, 1, memory_order_relaxed) + 1; - return 1; + return true; } /* @@ -76,10 +78,10 @@ typedef struct { int val; } CRYPTO_REF_COUNT; -static __inline__ int CRYPTO_UP_REF(CRYPTO_REF_COUNT *refcnt, int *ret) +static __inline__ bool CRYPTO_UP_REF(CRYPTO_REF_COUNT *refcnt, int *ret) { *ret = __atomic_fetch_add(&refcnt->val, 1, __ATOMIC_RELAXED) + 1; - return 1; + return true; } static __inline__ int CRYPTO_DOWN_REF(CRYPTO_REF_COUNT *refcnt, int *ret) @@ -97,10 +99,10 @@ typedef struct { volatile int val; } CRYPTO_REF_COUNT; -static __inline int CRYPTO_UP_REF(CRYPTO_REF_COUNT *refcnt, int *ret) +static __inline bool CRYPTO_UP_REF(CRYPTO_REF_COUNT *refcnt, int *ret) { *ret = _InterlockedExchangeAdd((void *)&refcnt->val, 1) + 1; - return 1; + return true; } static __inline int CRYPTO_DOWN_REF(CRYPTO_REF_COUNT *refcnt, int *ret) @@ -123,10 +125,10 @@ typedef struct { #define _ARM_BARRIER_ISH _ARM64_BARRIER_ISH #endif -static __inline int CRYPTO_UP_REF(CRYPTO_REF_COUNT *refcnt, int *ret) +static __inline bool CRYPTO_UP_REF(CRYPTO_REF_COUNT *refcnt, int *ret) { *ret = _InterlockedExchangeAdd_nf(&refcnt->val, 1) + 1; - return 1; + return true; } static __inline int CRYPTO_DOWN_REF(CRYPTO_REF_COUNT *refcnt, int *ret) @@ -138,10 +140,10 @@ static __inline int CRYPTO_DOWN_REF(CRYPTO_REF_COUNT *refcnt, int *ret) #else #pragma intrinsic(_InterlockedExchangeAdd) -static __inline int CRYPTO_UP_REF(CRYPTO_REF_COUNT *refcnt, int *ret) +static __inline bool CRYPTO_UP_REF(CRYPTO_REF_COUNT *refcnt, int *ret) { *ret = _InterlockedExchangeAdd(&refcnt->val, 1) + 1; - return 1; + return true; } static __inline int CRYPTO_DOWN_REF(CRYPTO_REF_COUNT *refcnt, int *ret) @@ -171,10 +173,10 @@ typedef struct { #ifdef OPENSSL_THREADS -static ossl_unused ossl_inline int CRYPTO_UP_REF(CRYPTO_REF_COUNT *refcnt, +static ossl_unused ossl_inline bool CRYPTO_UP_REF(CRYPTO_REF_COUNT *refcnt, int *ret) { - return CRYPTO_atomic_add(&refcnt->val, 1, ret, refcnt->lock); + return CRYPTO_atomic_add(&refcnt->val, 1, ret, refcnt->lock) ? true : false; } static ossl_unused ossl_inline int CRYPTO_DOWN_REF(CRYPTO_REF_COUNT *refcnt, @@ -203,12 +205,12 @@ static ossl_unused ossl_inline void CRYPTO_FREE_REF(CRYPTO_REF_COUNT *refcnt) #else /* OPENSSL_THREADS */ -static ossl_unused ossl_inline int CRYPTO_UP_REF(CRYPTO_REF_COUNT *refcnt, +static ossl_unused ossl_inline bool CRYPTO_UP_REF(CRYPTO_REF_COUNT *refcnt, int *ret) { refcnt->val++; *ret = refcnt->val; - return 1; + return true; } static ossl_unused ossl_inline int CRYPTO_DOWN_REF(CRYPTO_REF_COUNT *refcnt, diff --git a/providers/implementations/keymgmt/kdf_legacy_kmgmt.c b/providers/implementations/keymgmt/kdf_legacy_kmgmt.c index deb4960006..7a772ad017 100644 --- a/providers/implementations/keymgmt/kdf_legacy_kmgmt.c +++ b/providers/implementations/keymgmt/kdf_legacy_kmgmt.c @@ -75,8 +75,7 @@ int ossl_kdf_data_up_ref(KDF_DATA *kdfdata) if (!ossl_prov_is_running()) return 0; - CRYPTO_UP_REF(&kdfdata->refcnt, &ref); - return 1; + return CRYPTO_UP_REF(&kdfdata->refcnt, &ref); } static void *kdf_newdata(void *provctx) diff --git a/providers/implementations/keymgmt/mac_legacy_kmgmt.c b/providers/implementations/keymgmt/mac_legacy_kmgmt.c index d3082ca772..4c2e8d8cd9 100644 --- a/providers/implementations/keymgmt/mac_legacy_kmgmt.c +++ b/providers/implementations/keymgmt/mac_legacy_kmgmt.c @@ -110,8 +110,7 @@ int ossl_mac_key_up_ref(MAC_KEY *mackey) if (!ossl_prov_is_running()) return 0; - CRYPTO_UP_REF(&mackey->refcnt, &ref); - return 1; + return CRYPTO_UP_REF(&mackey->refcnt, &ref); } static void *mac_new(void *provctx) diff --git a/ssl/quic/quic_impl.c b/ssl/quic/quic_impl.c index 087c0dd582..99ddfd082e 100644 --- a/ssl/quic/quic_impl.c +++ b/ssl/quic/quic_impl.c @@ -5433,9 +5433,8 @@ int ossl_quic_get_peer_token(SSL_CTX *ctx, BIO_ADDR *peer, ossl_crypto_mutex_lock(c->mutex); tok = lh_QUIC_TOKEN_retrieve(c->cache, key); - if (tok != NULL) { + if (tok != NULL && CRYPTO_UP_REF(&tok->references, &ret)) { *token = tok; - CRYPTO_UP_REF(&tok->references, &ret); rc = 1; } diff --git a/ssl/ssl_cert_comp.c b/ssl/ssl_cert_comp.c index 2c297178e5..d6eadc5365 100644 --- a/ssl/ssl_cert_comp.c +++ b/ssl/ssl_cert_comp.c @@ -151,7 +151,7 @@ int OSSL_COMP_CERT_up_ref(OSSL_COMP_CERT *cc) { int i; - if (CRYPTO_UP_REF(&cc->references, &i) <= 0) + if (!CRYPTO_UP_REF(&cc->references, &i)) return 0; REF_PRINT_COUNT("OSSL_COMP_CERT", i, cc); diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index a900ad557a..57d74f9091 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -1012,7 +1012,7 @@ int SSL_up_ref(SSL *s) { int i; - if (CRYPTO_UP_REF(&s->references, &i) <= 0) + if (!CRYPTO_UP_REF(&s->references, &i)) return 0; REF_PRINT_COUNT("SSL", i, s); @@ -2088,7 +2088,9 @@ int SSL_copy_session_id(SSL *t, const SSL *f) return 0; } - CRYPTO_UP_REF(&fsc->cert->references, &i); + if (!CRYPTO_UP_REF(&fsc->cert->references, &i)) + return 0; + ssl_cert_free(tsc->cert); tsc->cert = fsc->cert; if (!SSL_set_session_id_context(t, fsc->sid_ctx, (int)fsc->sid_ctx_length)) { @@ -4532,7 +4534,7 @@ int SSL_CTX_up_ref(SSL_CTX *ctx) { int i; - if (CRYPTO_UP_REF(&ctx->references, &i) <= 0) + if (!CRYPTO_UP_REF(&ctx->references, &i)) return 0; REF_PRINT_COUNT("SSL_CTX", i, ctx); @@ -5352,7 +5354,8 @@ SSL *SSL_dup(SSL *s) /* If we're not quiescent, just up_ref! */ if (!SSL_in_init(s) || !SSL_in_before(s)) { - CRYPTO_UP_REF(&s->references, &i); + if (!CRYPTO_UP_REF(&s->references, &i)) + return NULL; return s; } diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c index 5604ca1354..ba0dcd229f 100644 --- a/ssl/ssl_sess.c +++ b/ssl/ssl_sess.c @@ -932,7 +932,7 @@ int SSL_SESSION_up_ref(SSL_SESSION *ss) { int i; - if (CRYPTO_UP_REF(&ss->references, &i) <= 0) + if (!CRYPTO_UP_REF(&ss->references, &i)) return 0; REF_PRINT_COUNT("SSL_SESSION", i, ss); diff --git a/test/tls-provider.c b/test/tls-provider.c index c516cb04b4..34ea1652a0 100644 --- a/test/tls-provider.c +++ b/test/tls-provider.c @@ -725,7 +725,7 @@ static int xor_key_up_ref(XORKEY *key) { int refcnt; - if (CRYPTO_UP_REF(&key->references, &refcnt) <= 0) + if (!CRYPTO_UP_REF(&key->references, &refcnt)) return 0; assert(refcnt > 1); From 8194b426f9a8a3e92723fb838157985001eed85f Mon Sep 17 00:00:00 2001 From: Weidong Wang Date: Sat, 28 Mar 2026 03:33:47 -0500 Subject: [PATCH 308/349] Add regression test for negative EVP_CIPHER_get_iv_length() in PKCS5_pbe2_set_scrypt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A malicious provider returning SIZE_MAX as IV length causes evp_cipher_cache_constants() to store -1 via size_t->int truncation. Without the ivlen > 0 guard, this leads to memcpy(iv[16], aiv, SIZE_MAX) — a stack buffer overflow. The test registers a fake provider with SIZE_MAX IV length, then calls PKCS5_pbe2_set_scrypt() and asserts it returns NULL without crashing. test for #30510 Reviewed-by: Dmitry Belyavskiy Reviewed-by: Daniel Kubec MergeDate: Mon Jul 20 11:15:18 2026 (Merged from https://github.com/openssl/openssl/pull/30615) --- test/pbetest.c | 179 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) diff --git a/test/pbetest.c b/test/pbetest.c index 42ae4a4da9..66c67e603d 100644 --- a/test/pbetest.c +++ b/test/pbetest.c @@ -8,6 +8,8 @@ */ #include +#include +#include #include "testutil.h" @@ -17,6 +19,7 @@ #include #include #include +#include #if !defined OPENSSL_NO_RC4 && !defined OPENSSL_NO_MD5 \ || !defined OPENSSL_NO_DES && !defined OPENSSL_NO_SHA1 @@ -125,6 +128,179 @@ static int test_pkcs5_pbe_des_sha1(void) } #endif +/* + * Regression test for negative EVP_CIPHER_get_iv_length() return in + * PKCS5_pbe2_set_scrypt(). + * + * A malicious/buggy provider advertises SIZE_MAX as IV length. + * evp_cipher_cache_constants() casts (size_t)SIZE_MAX to int => -1. + * Without the ivlen > 0 guard, this -1 is implicitly converted to SIZE_MAX + * in the memcpy call, causing a stack buffer overflow. + * + * This test verifies that PKCS5_pbe2_set_scrypt() handles negative IV + * lengths gracefully (returns NULL, no crash). + */ +#ifndef OPENSSL_NO_SCRYPT + +static void *bad_iv_cipher_newctx(void *provctx) +{ + static int dummy; + return &dummy; +} + +static void bad_iv_cipher_freectx(void *vctx) +{ +} + +static int bad_iv_cipher_cipher(void *vctx, + unsigned char *out, size_t *outl, + size_t outsz, + const unsigned char *in, size_t inl) +{ + if (outl != NULL) + *outl = 0; + return 1; +} + +/* + * Advertise SIZE_MAX as IV length. After evp_cipher_cache_constants() + * stores (int)SIZE_MAX, EVP_CIPHER_get_iv_length() returns -1. + */ +static int bad_iv_cipher_get_params(OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_BLOCK_SIZE); + if (p != NULL && !OSSL_PARAM_set_size_t(p, 16)) + return 0; + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, 32)) + return 0; + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_MODE); + if (p != NULL && !OSSL_PARAM_set_uint(p, EVP_CIPH_CBC_MODE)) + return 0; + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, SIZE_MAX)) + return 0; + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD); + if (p != NULL && !OSSL_PARAM_set_int(p, 0)) + return 0; + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_CUSTOM_IV); + if (p != NULL && !OSSL_PARAM_set_int(p, 0)) + return 0; + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_CTS); + if (p != NULL && !OSSL_PARAM_set_int(p, 0)) + return 0; + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK); + if (p != NULL && !OSSL_PARAM_set_int(p, 0)) + return 0; + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_HAS_RAND_KEY); + if (p != NULL && !OSSL_PARAM_set_int(p, 0)) + return 0; + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_ENCRYPT_THEN_MAC); + if (p != NULL && !OSSL_PARAM_set_int(p, 0)) + return 0; + return 1; +} + +static const OSSL_DISPATCH bad_iv_cipher_fns[] = { + { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))bad_iv_cipher_newctx }, + { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))bad_iv_cipher_freectx }, + { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))bad_iv_cipher_cipher }, + { OSSL_FUNC_CIPHER_GET_PARAMS, (void (*)(void))bad_iv_cipher_get_params }, + OSSL_DISPATCH_END +}; + +static const OSSL_ALGORITHM bad_iv_cipher_algs[] = { + { "AES-256-CBC:AES256", "provider=bad-iv-prov", bad_iv_cipher_fns, + "Bad IV length cipher for regression testing" }, + { NULL, NULL, NULL, NULL } +}; + +static const OSSL_ALGORITHM *bad_iv_query(void *provctx, + int operation_id, + int *no_cache) +{ + *no_cache = 0; + if (operation_id == OSSL_OP_CIPHER) + return bad_iv_cipher_algs; + return NULL; +} + +static void bad_iv_teardown(void *provctx) { } + +static const OSSL_DISPATCH bad_iv_provider_fns[] = { + { OSSL_FUNC_PROVIDER_TEARDOWN, (void (*)(void))bad_iv_teardown }, + { OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void (*)(void))bad_iv_query }, + OSSL_DISPATCH_END +}; + +static int bad_iv_provider_init(const OSSL_CORE_HANDLE *handle, + const OSSL_DISPATCH *in, + const OSSL_DISPATCH **out, + void **provctx) +{ + static int ctx; + + *provctx = &ctx; + *out = bad_iv_provider_fns; + return 1; +} + +/* + * Test that PKCS5_pbe2_set_scrypt() does not crash when + * EVP_CIPHER_get_iv_length() returns a negative value. + */ +static int test_pkcs5_scrypt_bad_iv_length(void) +{ + int ret = 0; + OSSL_LIB_CTX *libctx = NULL; + OSSL_PROVIDER *bad_prov = NULL; + EVP_CIPHER *cipher = NULL; + X509_ALGOR *alg = NULL; + unsigned char salt[16] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 + }; + unsigned char iv[16] = { 0xAA }; + + if (!TEST_ptr(libctx = OSSL_LIB_CTX_new())) + goto err; + + if (!TEST_true(OSSL_PROVIDER_add_builtin(libctx, "bad-iv-prov", + bad_iv_provider_init))) + goto err; + + if (!TEST_ptr(bad_prov = OSSL_PROVIDER_load(libctx, "bad-iv-prov"))) + goto err; + + if (!TEST_ptr(cipher = EVP_CIPHER_fetch(libctx, "AES-256-CBC", + "provider=bad-iv-prov"))) + goto err; + + if (!TEST_int_lt(EVP_CIPHER_get_iv_length(cipher), 0)) + goto err; + + /* + * Before the fix, this would trigger memcpy(iv[16], aiv, SIZE_MAX) + * — a stack buffer overflow. After the fix, the function must + * return NULL. + */ + alg = PKCS5_pbe2_set_scrypt(cipher, salt, (int)sizeof(salt), + iv, 1024, 8, 1); + if (!TEST_ptr_null(alg)) + goto err; + + ret = 1; +err: + X509_ALGOR_free(alg); + EVP_CIPHER_free(cipher); + OSSL_PROVIDER_unload(bad_prov); + OSSL_LIB_CTX_free(libctx); + return ret; +} +#endif /* OPENSSL_NO_SCRYPT */ + #ifdef OPENSSL_NO_AUTOLOAD_CONFIG /* * For configurations where we are not autoloading configuration, we need @@ -152,6 +328,9 @@ int setup_tests(void) #if !defined OPENSSL_NO_DES && !defined OPENSSL_NO_SHA1 ADD_TEST(test_pkcs5_pbe_des_sha1); #endif +#ifndef OPENSSL_NO_SCRYPT + ADD_TEST(test_pkcs5_scrypt_bad_iv_length); +#endif return 1; } From 940f4930c856acf21ee4280550632a59215a6127 Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Fri, 26 Jun 2026 20:02:01 +0200 Subject: [PATCH 309/349] demos/guide/tls-server-block.c: check results of __owur API calls Check results of SSL_CTX_set_session_id_context() and SSL_CTX_set_timeout() calls, as these functions are marked with __owur, leading to compilation error when compiled with -Werror=unused-result. Signed-off-by: Eugene Syromiatnikov Reviewed-by: Neil Horman Reviewed-by: Bob Beck MergeDate: Mon Jul 20 11:18:51 2026 (Merged from https://github.com/openssl/openssl/pull/31751) --- demos/guide/tls-server-block.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/demos/guide/tls-server-block.c b/demos/guide/tls-server-block.c index 2bee2219ed..8e8410f995 100644 --- a/demos/guide/tls-server-block.c +++ b/demos/guide/tls-server-block.c @@ -64,6 +64,7 @@ int main(int argc, char *argv[]) { int res = EXIT_FAILURE; long opts; + long old_timeout; const char *hostport; SSL_CTX *ctx = NULL; BIO *acceptor_bio; @@ -174,7 +175,11 @@ int main(int argc, char *argv[]) * byte array, that identifies the server application, and reduces the * chance of inappropriate cache sharing. */ - SSL_CTX_set_session_id_context(ctx, (void *)cache_id, sizeof(cache_id)); + if (SSL_CTX_set_session_id_context(ctx, (void *)cache_id, sizeof(cache_id)) <= 0) { + SSL_CTX_free(ctx); + ERR_print_errors_fp(stderr); + errx(res, "Failed to set server session ID context"); + } SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER); /* @@ -191,7 +196,9 @@ int main(int argc, char *argv[]) * loaded servers with sporadic connections from any given client, a longer * time may be appropriate. */ - SSL_CTX_set_timeout(ctx, 3600); + old_timeout = SSL_CTX_set_timeout(ctx, 3600); + if (old_timeout != 3600) + warnx("Changing session timeout from %ld to 3600", old_timeout); /* * Clients rarely employ certificate-based authentication, and so we don't From 8f8c42fa2d5f10fec9e21ff95290c70bc9acb326 Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Fri, 26 Jun 2026 14:56:11 +0200 Subject: [PATCH 310/349] demos: wire up tls-server-block The blocking TLS server demo code was added f4b4a185b546 "MVP demo TLS server", which, while was applied after 2000281dad31 "Convert demos to primary build system", was authored before, and hasn't been updated accordingly. Rescind that omission by add the relevant introes to build.info. Also, while at it, mention tls-server-block in demos/README.txt. Complements: f4b4a185b546 "MVP demo TLS server" Reported-by: Frederik Wedel-Heinen Signed-off-by: Eugene Syromiatnikov Reviewed-by: Neil Horman Reviewed-by: Bob Beck MergeDate: Mon Jul 20 11:18:52 2026 (Merged from https://github.com/openssl/openssl/pull/31751) --- demos/README.txt | 1 + demos/guide/build.info | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/demos/README.txt b/demos/README.txt index 9ccb5f1c2f..9b9d90aba2 100644 --- a/demos/README.txt +++ b/demos/README.txt @@ -38,6 +38,7 @@ quic-client-non-block.c: A simple non-blocking QUIC client quic-multi-stream.c: A simple QUIC client using multiple streams tls-client-block.c: A simple blocking SSL/TLS client tls-client-non-block.c: A simple non-blocking SSL/TLS client +tls-server-block.c: A simple blocking SSL/TLS server http3: Demonstration of how to use OpenSSL's QUIC capabilities for HTTP/3. diff --git a/demos/guide/build.info b/demos/guide/build.info index de184ff0d1..7b5b54f073 100644 --- a/demos/guide/build.info +++ b/demos/guide/build.info @@ -5,6 +5,7 @@ # LD_LIBRARY_PATH=../.. ./tls-client-block www.example.com 443 PROGRAMS{noinst} = tls-client-block \ + tls-server-block \ quic-client-block \ quic-multi-stream \ tls-client-non-block \ @@ -17,6 +18,10 @@ INCLUDE[tls-client-block]=../../include SOURCE[tls-client-block]=tls-client-block.c DEPEND[tls-client-block]=../../libcrypto ../../libssl +INCLUDE[tls-server-block]=../../include +SOURCE[tls-server-block]=tls-server-block.c +DEPEND[tls-server-block]=../../libcrypto ../../libssl + INCLUDE[quic-client-block]=../../include SOURCE[quic-client-block]=quic-client-block.c DEPEND[quic-client-block]=../../libcrypto ../../libssl From 25a7067b3d0bd49d2518c0860b8b9dfd82f8cc51 Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Fri, 26 Jun 2026 16:51:29 +0200 Subject: [PATCH 311/349] demos/README.txt: mention quic-server-block.c and quic-server-non-block.c Complements: dad45ea769dc "Adds a new demo blocking QUIC server for use with the existing demo QUIC clients" Complements: d9d4d84ceb01 "Add demo QUIC non-blocking server" Signed-off-by: Eugene Syromiatnikov Reviewed-by: Neil Horman Reviewed-by: Bob Beck MergeDate: Mon Jul 20 11:18:53 2026 (Merged from https://github.com/openssl/openssl/pull/31751) --- demos/README.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/demos/README.txt b/demos/README.txt index 9b9d90aba2..ff42e4f037 100644 --- a/demos/README.txt +++ b/demos/README.txt @@ -36,6 +36,8 @@ guide: Sample code from the OpenSSL Guide tutorials. See quic-client-block.c: A simple blocking QUIC client quic-client-non-block.c: A simple non-blocking QUIC client quic-multi-stream.c: A simple QUIC client using multiple streams +quic-server-block.c: A simple blocking QUIC server +quic-server-non-block.c: A simple non-blocking QUIC server tls-client-block.c: A simple blocking SSL/TLS client tls-client-non-block.c: A simple non-blocking SSL/TLS client tls-server-block.c: A simple blocking SSL/TLS server From 3227ca41ec4c8332cbd0caefa73a8ccb80ac8d84 Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Sat, 27 Jun 2026 13:25:02 +0200 Subject: [PATCH 312/349] demos/pkcs12/pkwrite.c: drop deprecated init calls Signed-off-by: Eugene Syromiatnikov Reviewed-by: Neil Horman Reviewed-by: Bob Beck MergeDate: Mon Jul 20 11:18:54 2026 (Merged from https://github.com/openssl/openssl/pull/31751) --- demos/pkcs12/pkwrite.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/demos/pkcs12/pkwrite.c b/demos/pkcs12/pkwrite.c index 7bb73f35a4..b274943ce8 100644 --- a/demos/pkcs12/pkwrite.c +++ b/demos/pkcs12/pkwrite.c @@ -25,8 +25,6 @@ int main(int argc, char **argv) fprintf(stderr, "Usage: pkwrite infile password name p12file\n"); exit(EXIT_FAILURE); } - OpenSSL_add_all_algorithms(); - ERR_load_crypto_strings(); if ((fp = fopen(argv[1], "r")) == NULL) { fprintf(stderr, "Error opening file %s\n", argv[1]); exit(EXIT_FAILURE); From 913cd240c7b16a4440e5616ac82b608a8385ce0c Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Fri, 26 Jun 2026 19:12:46 +0200 Subject: [PATCH 313/349] demos: add PKCS#12 reading and writing demos to the primary build system For some reason, demos in the pkcs12 directory were omitted during the initial conversion done in commit 2000281dad31 "Convert demos to primary build system". Rescind this omission. Complements: 2000281dad31 "Convert demos to primary build system" Reported-by: Frederik Wedel-Heinen Signed-off-by: Eugene Syromiatnikov Reviewed-by: Neil Horman Reviewed-by: Bob Beck MergeDate: Mon Jul 20 11:18:55 2026 (Merged from https://github.com/openssl/openssl/pull/31751) --- demos/build.info | 2 +- demos/pkcs12/build.info | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 demos/pkcs12/build.info diff --git a/demos/build.info b/demos/build.info index 49068f74ec..be3252fa9d 100644 --- a/demos/build.info +++ b/demos/build.info @@ -1,4 +1,4 @@ -SUBDIRS=bio cipher digest info keyexch mac kdf pkey signature \ +SUBDIRS=bio cipher digest info keyexch mac kdf pkcs12 pkey signature \ encrypt encode sslecho IF[{- !$disabled{"h3demo"} -}] diff --git a/demos/pkcs12/build.info b/demos/pkcs12/build.info new file mode 100644 index 0000000000..6fd35d1822 --- /dev/null +++ b/demos/pkcs12/build.info @@ -0,0 +1,16 @@ +# +# To run the demos when linked with a shared library (default) ensure that +# libcrypto is on the library path. For example: +# +# LD_LIBRARY_PATH=../.. ./pkread + +PROGRAMS{noinst} = pkread \ + pkwrite + +INCLUDE[pkread]=../../include +SOURCE[pkread]=pkread.c +DEPEND[pkread]=../../libcrypto + +INCLUDE[pkwrite]=../../include +SOURCE[pkwrite]=pkwrite.c +DEPEND[pkwrite]=../../libcrypto From 0e1176078f909d4c47dac0462b087c5f55528e96 Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Sat, 27 Jun 2026 12:26:28 +0200 Subject: [PATCH 314/349] demos/sslecho/echecho.c: reflow overly long lines Complements: 50580382caca "Documents initial agreed APIs for Encrypted Client Hello (ECH) and includes a minimal demo for some of those APIs." Signed-off-by: Eugene Syromiatnikov Reviewed-by: Neil Horman Reviewed-by: Bob Beck MergeDate: Mon Jul 20 11:18:57 2026 (Merged from https://github.com/openssl/openssl/pull/31751) --- demos/sslecho/echecho.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/demos/sslecho/echecho.c b/demos/sslecho/echecho.c index bcd097d383..44fe11e4ce 100644 --- a/demos/sslecho/echecho.c +++ b/demos/sslecho/echecho.c @@ -17,13 +17,17 @@ static const int server_port = 4433; -static const char echconfig[] = "AD7+DQA65wAgACA8wVN2BtscOl3vQheUzHeIkVmKIiydUhDCliA4iyQRCwAEAAEAAQALZXhhbXBsZS5jb20AAA=="; -static const char echprivbuf[] = "-----BEGIN PRIVATE KEY-----\n" - "MC4CAQAwBQYDK2VuBCIEICjd4yGRdsoP9gU7YT7My8DHx1Tjme8GYDXrOMCi8v1V\n" - "-----END PRIVATE KEY-----\n" - "-----BEGIN ECHCONFIG-----\n" - "AD7+DQA65wAgACA8wVN2BtscOl3vQheUzHeIkVmKIiydUhDCliA4iyQRCwAEAAEAAQALZXhhbXBsZS5jb20AAA==\n" - "-----END ECHCONFIG-----\n"; +static const char echconfig[] + = "AD7+DQA65wAgACA8wVN2BtscOl3vQheUzHeIkVmKIiydUhDCliA4iyQRCwAEAAEA" + "AQALZXhhbXBsZS5jb20AAA=="; +static const char echprivbuf[] + = "-----BEGIN PRIVATE KEY-----\n" + "MC4CAQAwBQYDK2VuBCIEICjd4yGRdsoP9gU7YT7My8DHx1Tjme8GYDXrOMCi8v1V\n" + "-----END PRIVATE KEY-----\n" + "-----BEGIN ECHCONFIG-----\n" + "AD7+DQA65wAgACA8wVN2BtscOl3vQheUzHeIkVmKIiydUhDCliA4iyQRCwAEAAEA" + "AQALZXhhbXBsZS5jb20AAA==\n" + "-----END ECHCONFIG-----\n"; typedef unsigned char bool; #define true 1 @@ -144,9 +148,11 @@ void configure_client_context(SSL_CTX *ctx) */ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); /* - * In a real application you would probably just use the default system certificate trust store and call: + * In a real application you would probably just use the default system + * certificate trust store and call: * SSL_CTX_set_default_verify_paths(ctx); - * In this demo though we are using a self-signed certificate, so the client must trust it directly. + * In this demo though we are using a self-signed certificate, + * so the client must trust it directly. */ if (!SSL_CTX_load_verify_locations(ctx, "cert.pem", NULL)) { ERR_print_errors_fp(stderr); @@ -268,7 +274,10 @@ int main(int argc, char **argv) /* Echo loop */ while (true) { - /* Get message from client; will fail if client closes connection */ + /* + * Get message from client; will fail if client closes + * connection + */ if ((rxlen = SSL_read(ssl, rxbuf, rxcap)) <= 0) { if (rxlen == 0) { printf("Client closed connection\n"); From 936a9ce2838f75ca64d45ee7a89ec8cf84d6e335 Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Sat, 27 Jun 2026 12:32:52 +0200 Subject: [PATCH 315/349] demos/sslecho/echecho.c: use ...instead of defining it (incorrectly). Complements: 50580382caca "Documents initial agreed APIs for Encrypted Client Hello (ECH) and includes a minimal demo for some of those APIs." Signed-off-by: Eugene Syromiatnikov Reviewed-by: Neil Horman Reviewed-by: Bob Beck MergeDate: Mon Jul 20 11:18:58 2026 (Merged from https://github.com/openssl/openssl/pull/31751) --- demos/sslecho/echecho.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/demos/sslecho/echecho.c b/demos/sslecho/echecho.c index 44fe11e4ce..3977f6756e 100644 --- a/demos/sslecho/echecho.c +++ b/demos/sslecho/echecho.c @@ -7,6 +7,7 @@ * https://www.openssl.org/source/license.html */ +#include #include #include #include @@ -29,10 +30,6 @@ static const char echprivbuf[] "AQALZXhhbXBsZS5jb20AAA==\n" "-----END ECHCONFIG-----\n"; -typedef unsigned char bool; -#define true 1 -#define false 0 - /* * This flag won't be useful until both accept/read (TCP & SSL) methods * can be called with a timeout. TBD. From bf1f522f34ed253a6139ed197f50043217fca62a Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Sat, 27 Jun 2026 12:40:59 +0200 Subject: [PATCH 316/349] demos/sslecho/echecho.c: declare locally used functions as static Otherwise it triggers -Werror=missing-prototypes when built with --strict-warnings. Complements: 50580382caca "Documents initial agreed APIs for Encrypted Client Hello (ECH) and includes a minimal demo for some of those APIs." Signed-off-by: Eugene Syromiatnikov Reviewed-by: Neil Horman Reviewed-by: Bob Beck MergeDate: Mon Jul 20 11:18:59 2026 (Merged from https://github.com/openssl/openssl/pull/31751) --- demos/sslecho/echecho.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/demos/sslecho/echecho.c b/demos/sslecho/echecho.c index 3977f6756e..2e300b95a1 100644 --- a/demos/sslecho/echecho.c +++ b/demos/sslecho/echecho.c @@ -36,7 +36,7 @@ static const char echprivbuf[] */ static volatile bool server_running = true; -int create_socket(bool isServer) +static int create_socket(bool isServer) { int s; int optval = 1; @@ -74,7 +74,7 @@ int create_socket(bool isServer) return s; } -SSL_CTX *create_context(bool isServer) +static SSL_CTX *create_context(bool isServer) { const SSL_METHOD *method; SSL_CTX *ctx; @@ -116,7 +116,7 @@ err: return 0; } -void configure_server_context(SSL_CTX *ctx) +static void configure_server_context(SSL_CTX *ctx) { /* Set the key and cert */ if (SSL_CTX_use_certificate_chain_file(ctx, "cert.pem") <= 0) { @@ -137,7 +137,7 @@ void configure_server_context(SSL_CTX *ctx) } } -void configure_client_context(SSL_CTX *ctx) +static void configure_client_context(SSL_CTX *ctx) { /* * Configure the client to abort the handshake if certificate verification @@ -163,7 +163,7 @@ void configure_client_context(SSL_CTX *ctx) } } -void usage() +static void usage(void) { printf("Usage: echecho s\n"); printf(" --or--\n"); From 7a4522bf2607f6fa9136273d375c71d78e6e8cbc Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Sat, 27 Jun 2026 12:42:27 +0200 Subject: [PATCH 317/349] demos/sslecho/echecho.c: use SSL_set1_ipaddr() instead of SSL_set1_host() As the latter was deprecated in commit f584ae959cbc "Let's support multiple names for certificate verification". Complements: 50580382caca "Documents initial agreed APIs for Encrypted Client Hello (ECH) and includes a minimal demo for some of those APIs." Signed-off-by: Eugene Syromiatnikov Reviewed-by: Neil Horman Reviewed-by: Bob Beck MergeDate: Mon Jul 20 11:19:00 2026 (Merged from https://github.com/openssl/openssl/pull/31751) --- demos/sslecho/echecho.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demos/sslecho/echecho.c b/demos/sslecho/echecho.c index 2e300b95a1..c8dc57b263 100644 --- a/demos/sslecho/echecho.c +++ b/demos/sslecho/echecho.c @@ -336,7 +336,7 @@ int main(int argc, char **argv) /* Set hostname for SNI */ SSL_set_tlsext_host_name(ssl, rem_server_ip); /* Configure server hostname check */ - SSL_set1_host(ssl, rem_server_ip); + SSL_set1_ipaddr(ssl, rem_server_ip); /* Now do SSL connect with server */ if (SSL_connect(ssl) == 1) { From 87b0f363dccbdbdbeb22202de0c1db3e5fbc4f72 Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Sat, 27 Jun 2026 12:44:06 +0200 Subject: [PATCH 318/349] demos/sslecho/echecho.c: check return values of SSL_* calls As otherwise it triggers -Werror=unused-result when built with --strict-warnings. Complements: 50580382caca "Documents initial agreed APIs for Encrypted Client Hello (ECH) and includes a minimal demo for some of those APIs." Signed-off-by: Eugene Syromiatnikov Reviewed-by: Neil Horman Reviewed-by: Bob Beck MergeDate: Mon Jul 20 11:19:01 2026 (Merged from https://github.com/openssl/openssl/pull/31751) --- demos/sslecho/echecho.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/demos/sslecho/echecho.c b/demos/sslecho/echecho.c index c8dc57b263..71b3d5565b 100644 --- a/demos/sslecho/echecho.c +++ b/demos/sslecho/echecho.c @@ -251,7 +251,11 @@ int main(int argc, char **argv) /* Create server SSL structure using newly accepted client socket */ ssl = SSL_new(ssl_ctx); - SSL_set_fd(ssl, client_skt); + if (SSL_set_fd(ssl, client_skt) <= 0) { + puts("Unable to set fd for the SSL object"); + ERR_print_errors_fp(stderr); + exit(EXIT_FAILURE); + } /* Wait for SSL connection from the client */ if (SSL_accept(ssl) <= 0) { @@ -332,11 +336,19 @@ int main(int argc, char **argv) /* Create client SSL structure using dedicated client socket */ ssl = SSL_new(ssl_ctx); - SSL_set_fd(ssl, client_skt); + if (SSL_set_fd(ssl, client_skt) <= 0) { + puts("Unable to set fd for the SSL object"); + ERR_print_errors_fp(stderr); + exit(EXIT_FAILURE); + } /* Set hostname for SNI */ SSL_set_tlsext_host_name(ssl, rem_server_ip); /* Configure server hostname check */ - SSL_set1_ipaddr(ssl, rem_server_ip); + if (SSL_set1_ipaddr(ssl, rem_server_ip) <= 0) { + puts("Unable to set IP address for the SSL object"); + ERR_print_errors_fp(stderr); + exit(EXIT_FAILURE); + } /* Now do SSL connect with server */ if (SSL_connect(ssl) == 1) { From 2216d3b10f7a71f9fd166a4f741da9ab0b73107b Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Sat, 27 Jun 2026 13:16:07 +0200 Subject: [PATCH 319/349] demos/sslecho/echecho.c: apply Windows-specific changes Similar to the ones present in demos/sslecho/main.c. Complements: 50580382caca "Documents initial agreed APIs for Encrypted Client Hello (ECH) and includes a minimal demo for some of those APIs." Signed-off-by: Eugene Syromiatnikov Reviewed-by: Neil Horman Reviewed-by: Bob Beck MergeDate: Mon Jul 20 11:19:03 2026 (Merged from https://github.com/openssl/openssl/pull/31751) --- demos/sslecho/echecho.c | 70 ++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/demos/sslecho/echecho.c b/demos/sslecho/echecho.c index 71b3d5565b..d9c757cba2 100644 --- a/demos/sslecho/echecho.c +++ b/demos/sslecho/echecho.c @@ -9,13 +9,23 @@ #include #include -#include #include -#include -#include #include #include +#if !defined(OPENSSL_SYS_WINDOWS) +#include +#include +#include + +#define SOCKET int +#define INVALID_SOCKET -1 +#define closesocket(s) close(s) +#else /* defined(OPENSSL_SYS_WINDOWS) */ +#include +#include +#endif /* !defined(OPENSSL_SYS_WINDOWS) */ + static const int server_port = 4433; static const char echconfig[] @@ -36,14 +46,14 @@ static const char echprivbuf[] */ static volatile bool server_running = true; -static int create_socket(bool isServer) +static SOCKET create_socket(bool isServer) { - int s; + SOCKET s; int optval = 1; struct sockaddr_in addr = { 0 }; s = socket(AF_INET, SOCK_STREAM, 0); - if (s < 0) { + if (s == INVALID_SOCKET) { perror("Unable to create socket"); exit(EXIT_FAILURE); } @@ -54,7 +64,7 @@ static int create_socket(bool isServer) addr.sin_addr.s_addr = INADDR_ANY; /* Reuse the address; good for quick restarts */ - if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void *)&optval, sizeof(optval)) < 0) { perror("setsockopt(SO_REUSEADDR) failed"); exit(EXIT_FAILURE); @@ -98,7 +108,7 @@ static int configure_ech(SSL_CTX *ctx, int server, unsigned char *buf, size_t len) { OSSL_ECHSTORE *es = NULL; - BIO *es_in = BIO_new_mem_buf(buf, len); + BIO *es_in = BIO_new_mem_buf(buf, (int)len); if (es_in == NULL || (es = OSSL_ECHSTORE_new(NULL, NULL)) == NULL) goto err; @@ -172,6 +182,7 @@ static void usage(void) exit(1); } +#define BUFFERSIZE 1024 int main(int argc, char **argv) { bool isServer; @@ -180,13 +191,13 @@ int main(int argc, char **argv) SSL_CTX *ssl_ctx = NULL; SSL *ssl = NULL; - int server_skt = -1; - int client_skt = -1; + SOCKET server_skt = INVALID_SOCKET; + SOCKET client_skt = INVALID_SOCKET; - /* used by getline relying on realloc, can't be statically allocated */ + /* used by fgets */ + char buffer[BUFFERSIZE]; char *txbuf = NULL; size_t txcap = 0; - int txlen; char rxbuf[128]; size_t rxcap = sizeof(rxbuf); @@ -195,7 +206,7 @@ int main(int argc, char **argv) char *rem_server_ip = NULL; struct sockaddr_in addr = { 0 }; - unsigned int addr_len = sizeof(addr); + socklen_t addr_len = (socklen_t)sizeof(addr); char *outer_sni = NULL, *inner_sni = NULL; int ech_status; @@ -242,7 +253,7 @@ int main(int argc, char **argv) /* Wait for TCP connection from client */ client_skt = accept(server_skt, (struct sockaddr *)&addr, &addr_len); - if (client_skt < 0) { + if (client_skt == INVALID_SOCKET) { perror("Unable to accept"); exit(EXIT_FAILURE); } @@ -251,7 +262,7 @@ int main(int argc, char **argv) /* Create server SSL structure using newly accepted client socket */ ssl = SSL_new(ssl_ctx); - if (SSL_set_fd(ssl, client_skt) <= 0) { + if (SSL_set_fd(ssl, (int)client_skt) <= 0) { puts("Unable to set fd for the SSL object"); ERR_print_errors_fp(stderr); exit(EXIT_FAILURE); @@ -279,7 +290,7 @@ int main(int argc, char **argv) * Get message from client; will fail if client closes * connection */ - if ((rxlen = SSL_read(ssl, rxbuf, rxcap)) <= 0) { + if ((rxlen = SSL_read(ssl, rxbuf, (int)rxcap)) <= 0) { if (rxlen == 0) { printf("Client closed connection\n"); } @@ -307,7 +318,12 @@ int main(int argc, char **argv) /* Cleanup for next client */ SSL_shutdown(ssl); SSL_free(ssl); - close(client_skt); + closesocket(client_skt); + /* + * Set client_skt to INVALID_SOCKET to avoid double close when + * server_running become false before next accept + */ + client_skt = INVALID_SOCKET; } } printf("Server exiting...\n"); @@ -336,7 +352,7 @@ int main(int argc, char **argv) /* Create client SSL structure using dedicated client socket */ ssl = SSL_new(ssl_ctx); - if (SSL_set_fd(ssl, client_skt) <= 0) { + if (SSL_set_fd(ssl, (int)client_skt) <= 0) { puts("Unable to set fd for the SSL object"); ERR_print_errors_fp(stderr); exit(EXIT_FAILURE); @@ -366,9 +382,11 @@ int main(int argc, char **argv) /* Loop to send input from keyboard */ while (true) { /* Get a line of input */ - txlen = getline(&txbuf, &txcap, stdin); + memset(buffer, 0, BUFFERSIZE); + txbuf = fgets(buffer, BUFFERSIZE, stdin); + /* Exit loop on error */ - if (txlen < 0 || txbuf == NULL) { + if (txbuf == NULL) { break; } /* Exit loop if just a carriage return */ @@ -376,14 +394,14 @@ int main(int argc, char **argv) break; } /* Send it to the server */ - if ((result = SSL_write(ssl, txbuf, txlen)) <= 0) { + if ((result = SSL_write(ssl, txbuf, (int)strlen(txbuf))) <= 0) { printf("Server closed connection\n"); ERR_print_errors_fp(stderr); break; } /* Wait for the echo */ - rxlen = SSL_read(ssl, rxbuf, rxcap); + rxlen = SSL_read(ssl, rxbuf, (int)rxcap); if (rxlen <= 0) { printf("Server closed connection\n"); ERR_print_errors_fp(stderr); @@ -410,10 +428,10 @@ exit: } SSL_CTX_free(ssl_ctx); - if (client_skt != -1) - close(client_skt); - if (server_skt != -1) - close(server_skt); + if (client_skt != INVALID_SOCKET) + closesocket(client_skt); + if (server_skt != INVALID_SOCKET) + closesocket(server_skt); if (txbuf != NULL && txcap > 0) free(txbuf); From ef6f59329d8e795d5a09ff784bf6e0aae2d2f123 Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Fri, 26 Jun 2026 19:23:21 +0200 Subject: [PATCH 320/349] demos: wire up echecho Complements: 50580382caca "Documents initial agreed APIs for Encrypted Client Hello (ECH) and includes a minimal demo for some of those APIs." Reported-by: Frederik Wedel-Heinen Signed-off-by: Eugene Syromiatnikov Reviewed-by: Neil Horman Reviewed-by: Bob Beck MergeDate: Mon Jul 20 11:19:04 2026 (Merged from https://github.com/openssl/openssl/pull/31751) --- demos/README.txt | 1 + demos/sslecho/build.info | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/demos/README.txt b/demos/README.txt index ff42e4f037..9caafcad77 100644 --- a/demos/README.txt +++ b/demos/README.txt @@ -84,4 +84,5 @@ rsa_pss_hash.c Compute and verify an RSA-PSS signature over a buffer smime: Demonstrations related to S/MIME sslecho: +echecho.c Simple SSL/TLS echo client/server that uses ECH. main.c Simple SSL/TLS echo client/server. diff --git a/demos/sslecho/build.info b/demos/sslecho/build.info index d42716cd51..7784357ed6 100644 --- a/demos/sslecho/build.info +++ b/demos/sslecho/build.info @@ -6,6 +6,15 @@ PROGRAMS{noinst} = sslecho + INCLUDE[sslecho]=../../include SOURCE[sslecho]=main.c DEPEND[sslecho]=../../libcrypto ../../libssl + +IF[{- !$disabled{"ech"} -}] + PROGRAMS{noinst} = echecho + + INCLUDE[echecho]=../../include + SOURCE[echecho]=echecho.c + DEPEND[echecho]=../../libcrypto ../../libssl +ENDIF From 695eb4bd6488ea017fd35b32f70c7754aa81110a Mon Sep 17 00:00:00 2001 From: Loganaden Velvindron Date: Thu, 2 Jul 2026 09:13:45 +0400 Subject: [PATCH 321/349] ml_kem: Add a check for shared_secret Reviewed-by: Viktor Dukhovni Reviewed-by: Paul Dale Reviewed-by: Tim Hudson MergeDate: Mon Jul 20 11:21:15 2026 (Merged from https://github.com/openssl/openssl/pull/31822) --- crypto/ml_kem/ml_kem.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crypto/ml_kem/ml_kem.c b/crypto/ml_kem/ml_kem.c index 27fa15e246..89960dc105 100644 --- a/crypto/ml_kem/ml_kem.c +++ b/crypto/ml_kem/ml_kem.c @@ -2032,11 +2032,13 @@ int ossl_ml_kem_decap(uint8_t *shared_secret, size_t slen, #endif /* Need a private key here */ - if (!ossl_ml_kem_have_prvkey(key)) + if (!ossl_ml_kem_have_prvkey(key) + || shared_secret == NULL + || slen < ML_KEM_SHARED_SECRET_BYTES) return 0; vinfo = key->vinfo; - if (shared_secret == NULL || slen != ML_KEM_SHARED_SECRET_BYTES + if (slen != ML_KEM_SHARED_SECRET_BYTES || ctext == NULL || clen != vinfo->ctext_bytes || (mdctx = EVP_MD_CTX_new()) == NULL) { (void)RAND_bytes_ex(key->libctx, shared_secret, From a44ba221abc62a8ca61107749cc776eb72e16969 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 14 Jul 2026 18:26:40 +0200 Subject: [PATCH 322/349] apps: test rsa -text option The -text option of the rsa app was not exercised by any test. Add a subtest that prints both a private and a public key in text form and, after stripping the colon-separated hex formatting, verifies the printed modulus and private exponent match the committed testrsa.pem keypair rather than merely checking that the labels are present. Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Daniel Kubec Reviewed-by: Paul Dale (Merged from https://github.com/openssl/openssl/pull/31950) --- test/recipes/15-test_rsa.t | 49 +++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/test/recipes/15-test_rsa.t b/test/recipes/15-test_rsa.t index 3e60b23ef4..c7eafbfb1c 100644 --- a/test/recipes/15-test_rsa.t +++ b/test/recipes/15-test_rsa.t @@ -17,7 +17,7 @@ use OpenSSL::Test::Utils; setup("test_rsa"); -plan tests => 16; +plan tests => 17; require_ok(srctop_file('test', 'recipes', 'tconversion.pl')); @@ -27,6 +27,53 @@ run_rsa_tests("pkey"); run_rsa_tests("rsa"); +SKIP: { + skip "RSA is not supported in this build", 1 if disabled("rsa"); + + subtest "rsa -text prints the key in text form" => sub { + plan tests => 6; + + # The modulus (n) and private exponent (d) of the committed + # testrsa.pem keypair. -text prints them as colon-separated hex; we + # strip the formatting and compare against the known values so the + # actual key material, not just the labels, is verified. + my $modulus = "AADB7AA92E464F15711996166B4FF8BBE2301DFEE9D8B3596DC3" + . "C1A7DFCE7C87180170509FC84EFD17B5BB02CA5DD0A3228686B380CB746F" + . "3CAE4CDFC8AE5D3D"; + my $priv_exp = "677727CDA1D733F6F119A479091D51AC3D6A1410157E840588E1" + . "FDB8F26031AA00BA84048AC3C755C64329C3AFE30120EBF4C89C02170671" + . "2282DAAF473BB2A1"; + + my @priv = run(app(['openssl', 'rsa', '-text', '-noout', + '-in', srctop_file("test", "testrsa.pem")], + stderr => undef), + capture => 1); + chomp @priv; + my $priv_blob = uc join('', @priv); + $priv_blob =~ s/[^0-9A-F]//g; + ok(grep(/^Private-Key: \(512 bit, 2 primes\)$/, @priv), + "-text prints the private key header"); + ok(index($priv_blob, $modulus) >= 0, + "-text prints the expected modulus for a private key"); + ok(index($priv_blob, $priv_exp) >= 0, + "-text prints the expected private exponent"); + + my @pub = run(app(['openssl', 'rsa', '-pubin', '-text', '-noout', + '-in', srctop_file("test", "testrsapub.pem")], + stderr => undef), + capture => 1); + chomp @pub; + my $pub_blob = uc join('', @pub); + $pub_blob =~ s/[^0-9A-F]//g; + ok(grep(/^Public-Key: \(512 bit\)$/, @pub), + "-text prints the public key header"); + ok(index($pub_blob, $modulus) >= 0, + "-text prints the expected modulus for a public key"); + ok(!grep(/privateExponent/, @pub), + "-text does not print a private exponent for a public key"); + }; +} + sub run_rsa_tests { my $cmd = shift; From db5b9853692acc99d07626c2e23e54cae2ef4faa Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Mon, 13 Jul 2026 22:43:41 +0700 Subject: [PATCH 323/349] Migrate QUIC_TSERVER script 13 and script 14 to radix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Saša Nedvědický Reviewed-by: Bob Beck MergeDate: Tue Jul 21 09:19:57 2026 (Merged from https://github.com/openssl/openssl/pull/31945) --- test/quic_multistream_test.c | 58 +----------- test/radix/quic_tests.c | 179 ++++++++++++++++++++++++++++++++++- 2 files changed, 179 insertions(+), 58 deletions(-) diff --git a/test/quic_multistream_test.c b/test/quic_multistream_test.c index b9b263fe94..3bed82376f 100644 --- a/test/quic_multistream_test.c +++ b/test/quic_multistream_test.c @@ -2086,68 +2086,14 @@ static const struct script_op script_12[] = { }; /* 13. Many threads accepted on the same client connection (stress test) */ -static const struct script_op script_13_child[] = { - OP_BEGIN_REPEAT(10), - - OP_C_ACCEPT_STREAM_WAIT(a), - OP_C_READ_EXPECT(a, "foo", 3), - OP_C_EXPECT_FIN(a), - OP_C_FREE_STREAM(a), - - OP_END_REPEAT(), - - OP_END -}; - static const struct script_op script_13[] = { - OP_C_SET_ALPN("ossltest"), - OP_C_CONNECT_WAIT(), - OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE), - - OP_NEW_THREAD(5, script_13_child), - - OP_BEGIN_REPEAT(50), - - OP_S_NEW_STREAM_BIDI(a, ANY_ID), - OP_S_WRITE(a, "foo", 3), - OP_S_CONCLUDE(a), - OP_S_UNBIND_STREAM_ID(a), - - OP_END_REPEAT(), - + /* test moved to test/radix/quic_tests.c */ OP_END }; /* 14. Many threads initiating on the same client connection (stress test) */ -static const struct script_op script_14_child[] = { - OP_BEGIN_REPEAT(10), - - OP_C_NEW_STREAM_BIDI(a, ANY_ID), - OP_C_WRITE(a, "foo", 3), - OP_C_CONCLUDE(a), - OP_C_FREE_STREAM(a), - - OP_END_REPEAT(), - - OP_END -}; - static const struct script_op script_14[] = { - OP_C_SET_ALPN("ossltest"), - OP_C_CONNECT_WAIT(), - OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE), - - OP_NEW_THREAD(5, script_14_child), - - OP_BEGIN_REPEAT(50), - - OP_S_ACCEPT_STREAM_WAIT(a), - OP_S_READ_EXPECT(a, "foo", 3), - OP_S_EXPECT_FIN(a), - OP_S_UNBIND_STREAM_ID(a), - - OP_END_REPEAT(), - + /* test moved to test/radix/quic_tests.c */ OP_END }; diff --git a/test/radix/quic_tests.c b/test/radix/quic_tests.c index 3047ef08a1..a6e15f7c8a 100644 --- a/test/radix/quic_tests.c +++ b/test/radix/quic_tests.c @@ -1085,12 +1085,187 @@ DEF_SCRIPT(script_12, "Many threads initiated on the same client connection") OP_SLEEP(10); } -DEF_SCRIPT(script_13, "place holder for multistrem script_13") +/* 13. Many threads accepted on the same client connection (stress test) */ +DEF_SCRIPT(script_13_child_1, + "child: 10x accept stream from C, read, expect FIN, free") { + size_t i; + + for (i = 0; i < 10; i++) { + OP_ACCEPT_STREAM_WAIT(C, C1, OP_F_REPLACE_STREAM); + OP_READ_EXPECT_B(C1, "foo"); + OP_EXPECT_FIN(C1); + } } -DEF_SCRIPT(script_14, "place holder for multistrem script_14") +DEF_SCRIPT(script_13_child_2, + "child: 10x accept stream from C, read, expect FIN, free") { + size_t i; + + for (i = 0; i < 10; i++) { + OP_ACCEPT_STREAM_WAIT(C, C2, OP_F_REPLACE_STREAM); + OP_READ_EXPECT_B(C2, "foo"); + OP_EXPECT_FIN(C2); + } +} + +DEF_SCRIPT(script_13_child_3, + "child: 10x accept stream from C, read, expect FIN, free") +{ + size_t i; + + for (i = 0; i < 10; i++) { + OP_ACCEPT_STREAM_WAIT(C, C3, OP_F_REPLACE_STREAM); + OP_READ_EXPECT_B(C3, "foo"); + OP_EXPECT_FIN(C3); + } +} + +DEF_SCRIPT(script_13_child_4, + "child: 10x accept stream from C, read, expect FIN, free") +{ + size_t i; + + for (i = 0; i < 10; i++) { + OP_ACCEPT_STREAM_WAIT(C, C4, OP_F_REPLACE_STREAM); + OP_READ_EXPECT_B(C4, "foo"); + OP_EXPECT_FIN(C4); + } +} + +DEF_SCRIPT(script_13_child_5, + "child: 10x accept stream from C, read, expect FIN, free") +{ + size_t i; + + for (i = 0; i < 10; i++) { + OP_ACCEPT_STREAM_WAIT(C, C5, OP_F_REPLACE_STREAM); + OP_READ_EXPECT_B(C5, "foo"); + OP_EXPECT_FIN(C5); + } +} + +DEF_SCRIPT(script_13, + "Many threads accepted on same client connection (stress test)") +{ + size_t i; + + OP_SIMPLE_PAIR_CONN_ND(); + OP_ACCEPT_CONN_WAIT_ND(L, S, 0); + + /* + * put empty objects to radix process cache. + * objects C1 - C5 are going to be used for + * SSL streams in _child_1 - _child_5 threads. + */ + OP_BIND(C1); + OP_BIND(C2); + OP_BIND(C3); + OP_BIND(C4); + OP_BIND(C5); + OP_BIND(Sa); + + OP_SPAWN_THREAD(script_13_child_1); + OP_SPAWN_THREAD(script_13_child_2); + OP_SPAWN_THREAD(script_13_child_3); + OP_SPAWN_THREAD(script_13_child_4); + OP_SPAWN_THREAD(script_13_child_5); + + for (i = 0; i < 50; ++i) { + OP_NEW_STREAM(S, Sa, OP_F_REPLACE_STREAM); + OP_WRITE_B(Sa, "foo"); + OP_CONCLUDE(Sa); + } +} + +/* 14. Many threads initiating on the same client connection (stress test) */ +DEF_SCRIPT(script_14_child_1, + "child: 10x create stream on C, write, conclude, free") +{ + size_t i; + + for (i = 0; i < 10; i++) { + OP_NEW_STREAM(C, C1, OP_F_REPLACE_STREAM); + OP_WRITE_B(C1, "foo"); + OP_CONCLUDE(C1); + } +} + +DEF_SCRIPT(script_14_child_2, + "child: 10x create stream on C, write, conclude, free") +{ + size_t i; + + for (i = 0; i < 10; i++) { + OP_NEW_STREAM(C, C2, OP_F_REPLACE_STREAM); + OP_WRITE_B(C2, "foo"); + OP_CONCLUDE(C2); + } +} + +DEF_SCRIPT(script_14_child_3, + "child: 10x create stream on C, write, conclude, free") +{ + size_t i; + + for (i = 0; i < 10; i++) { + OP_NEW_STREAM(C, C3, OP_F_REPLACE_STREAM); + OP_WRITE_B(C3, "foo"); + OP_CONCLUDE(C3); + } +} + +DEF_SCRIPT(script_14_child_4, + "child: 10x create stream on C, write, conclude, free") +{ + size_t i; + + for (i = 0; i < 10; i++) { + OP_NEW_STREAM(C, C4, OP_F_REPLACE_STREAM); + OP_WRITE_B(C4, "foo"); + OP_CONCLUDE(C4); + } +} + +DEF_SCRIPT(script_14_child_5, + "child: 10x create stream on C, write, conclude, free") +{ + size_t i; + + for (i = 0; i < 10; i++) { + OP_NEW_STREAM(C, C5, OP_F_REPLACE_STREAM); + OP_WRITE_B(C5, "foo"); + OP_CONCLUDE(C5); + } +} + +DEF_SCRIPT(script_14, + "Many threads initiating on same client connection (stress test)") +{ + size_t i; + + OP_SIMPLE_PAIR_CONN_ND(); + OP_ACCEPT_CONN_WAIT_ND(L, S, 0); + + OP_BIND(C1); + OP_BIND(C2); + OP_BIND(C3); + OP_BIND(C4); + OP_BIND(C5); + OP_BIND(Sa); + + OP_SPAWN_THREAD(script_14_child_1); + OP_SPAWN_THREAD(script_14_child_2); + OP_SPAWN_THREAD(script_14_child_3); + OP_SPAWN_THREAD(script_14_child_4); + OP_SPAWN_THREAD(script_14_child_5); + + for (i = 0; i < 50; ++i) { + OP_ACCEPT_STREAM_WAIT(S, Sa, OP_F_REPLACE_STREAM); + OP_READ_EXPECT_B(Sa, "foo"); + OP_EXPECT_FIN(Sa); + } } DEF_SCRIPT(script_15, "place holder for multistrem script_15") From 458bfef110dc0f1d2bbe4eb93840f32c51b9d50d Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Mon, 20 Jul 2026 09:05:59 +0700 Subject: [PATCH 324/349] Fix clean target find/-prune precedence bug The clean target's find command intended -prune to apply to the whole -o chain of excluded submodule paths, but -prune only binds to the immediately preceding -path, so only the last path (wycheproof) was actually protected from recursion. Earlier submodules (cloudflare-quiche, pkcs11-provider, etc.) were still descended into, letting make clean delete vendored symlinks inside them. Move -prune outside the parenthesized path list so it applies whenever any excluded path matches. Assisted-by: Claude:claude-sonnet-5 Fixes: 70741ee62a50 "Configurations/unix-Makefile.tmpl: make cleanup faster" Reviewed-by: Nikola Pajkovsky Reviewed-by: Bob Beck Reviewed-by: Milan Broz Reviewed-by: Eugene Syromiatnikov MergeDate: Tue Jul 21 09:21:37 2026 (Merged from https://github.com/openssl/openssl/pull/32006) --- Configurations/unix-Makefile.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Configurations/unix-Makefile.tmpl b/Configurations/unix-Makefile.tmpl index 6c51e34f7a..3f9d04b6d0 100644 --- a/Configurations/unix-Makefile.tmpl +++ b/Configurations/unix-Makefile.tmpl @@ -660,8 +660,8 @@ clean: libclean ## Clean the workspace, keep the configuration -o -path './python-ecdsa' \ -o -path './tlsfuzzer' \ -o -path './tlslite-ng' \ - -o -path './wycheproof' \ - -prune \) \ + -o -path './wycheproof' \) \ + -prune \ -o \! -type d \ \( -name '*{- platform->depext() -}' \ -o -name '*{- platform->objext() -}' \ From b27fe1419c2a043eb34d18cdc9469a093f9892c9 Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Mon, 29 Jun 2026 22:38:43 +0700 Subject: [PATCH 325/349] Port script_15 to radix test framework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Saša Nedvědický Reviewed-by: Neil Horman Reviewed-by: Bob Beck MergeDate: Tue Jul 21 09:22:44 2026 (Merged from https://github.com/openssl/openssl/pull/31889) --- test/quic_multistream_test.c | 40 +----------------------------------- test/radix/quic_tests.c | 38 +++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 40 deletions(-) diff --git a/test/quic_multistream_test.c b/test/quic_multistream_test.c index 3bed82376f..c78ffaceca 100644 --- a/test/quic_multistream_test.c +++ b/test/quic_multistream_test.c @@ -2099,45 +2099,7 @@ static const struct script_op script_14[] = { /* 15. Client sending large number of streams, MAX_STREAMS test */ static const struct script_op script_15[] = { - OP_C_SET_ALPN("ossltest"), - OP_C_CONNECT_WAIT(), - OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE), - - /* - * This will cause a protocol violation to be raised by the server if we are - * not handling the stream limit correctly on the TX side. - */ - OP_BEGIN_REPEAT(200), - - OP_C_NEW_STREAM_BIDI_EX(a, ANY_ID, SSL_STREAM_FLAG_ADVANCE), - OP_C_WRITE(a, "foo", 3), - OP_C_CONCLUDE(a), - OP_C_FREE_STREAM(a), - - OP_END_REPEAT(), - - /* Prove the connection is still good. */ - OP_S_NEW_STREAM_BIDI(a, S_BIDI_ID(0)), - OP_S_WRITE(a, "bar", 3), - OP_S_CONCLUDE(a), - - OP_C_ACCEPT_STREAM_WAIT(a), - OP_C_READ_EXPECT(a, "bar", 3), - OP_C_EXPECT_FIN(a), - - /* - * Drain the queue of incoming streams. We should be able to get all 200 - * even though only 100 can be initiated at a time. - */ - OP_BEGIN_REPEAT(200), - - OP_S_ACCEPT_STREAM_WAIT(b), - OP_S_READ_EXPECT(b, "foo", 3), - OP_S_EXPECT_FIN(b), - OP_S_UNBIND_STREAM_ID(b), - - OP_END_REPEAT(), - + /* test moved to test/radix/quic_tests.c */ OP_END }; diff --git a/test/radix/quic_tests.c b/test/radix/quic_tests.c index a6e15f7c8a..c8026ce7c3 100644 --- a/test/radix/quic_tests.c +++ b/test/radix/quic_tests.c @@ -1268,8 +1268,44 @@ DEF_SCRIPT(script_14, } } -DEF_SCRIPT(script_15, "place holder for multistrem script_15") +/* 15. Client sending large number of streams, MAX_STREAMS test */ +DEF_SCRIPT(script_15, "Client sending large number of streams, MAX_STREAMS test") { + size_t i; + + OP_SIMPLE_PAIR_CONN_ND(); + OP_ACCEPT_CONN_WAIT_ND(L, S, 0); + + /* + * This will cause a protocol violation to be raised by the server if we are + * not handling the stream limit correctly on the TX side. + */ + for (i = 0; i < 200; ++i) { + OP_NEW_STREAM(C, Ca, SSL_STREAM_FLAG_ADVANCE); + OP_WRITE(Ca, "foo", 3); + OP_CONCLUDE(Ca); + OP_UNBIND(Ca); + } + + /* Prove the connection is still good. */ + OP_NEW_STREAM(S, Sa, 0); + OP_WRITE(Sa, "bar", 3); + OP_CONCLUDE(Sa); + + OP_ACCEPT_STREAM_WAIT(C, Ca, 0); + OP_READ_EXPECT(Ca, "bar", 3); + OP_EXPECT_FIN(Ca); + + /* + * Drain the queue of incoming streams. We should be able to get all 200 + * even though only 100 can be initiated at a time. + */ + for (i = 0; i < 200; ++i) { + OP_ACCEPT_STREAM_WAIT(S, Sb, 0); + OP_READ_EXPECT(Sb, "foo", 3); + OP_EXPECT_FIN(Sb); + OP_UNBIND(Sb); + } } DEF_SCRIPT(script_16, "place holder for multistrem script_16") From e528888bc069f216620ea9d7062c673c18bafdcc Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Mon, 29 Jun 2026 22:55:25 +0700 Subject: [PATCH 326/349] Port script_16 to radix test framework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Saša Nedvědický Reviewed-by: Neil Horman Reviewed-by: Bob Beck MergeDate: Tue Jul 21 09:22:46 2026 (Merged from https://github.com/openssl/openssl/pull/31889) --- test/quic_multistream_test.c | 37 +----------------------------------- test/radix/quic_tests.c | 35 +++++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 37 deletions(-) diff --git a/test/quic_multistream_test.c b/test/quic_multistream_test.c index c78ffaceca..b7472c4c37 100644 --- a/test/quic_multistream_test.c +++ b/test/quic_multistream_test.c @@ -2105,42 +2105,7 @@ static const struct script_op script_15[] = { /* 16. Server sending large number of streams, MAX_STREAMS test */ static const struct script_op script_16[] = { - OP_C_SET_ALPN("ossltest"), - OP_C_CONNECT_WAIT(), - OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE), - - /* - * This will cause a protocol violation to be raised by the client if we are - * not handling the stream limit correctly on the TX side. - */ - OP_BEGIN_REPEAT(200), - - OP_S_NEW_STREAM_BIDI(a, ANY_ID), - OP_S_WRITE(a, "foo", 3), - OP_S_CONCLUDE(a), - OP_S_UNBIND_STREAM_ID(a), - - OP_END_REPEAT(), - - /* Prove that the connection is still good. */ - OP_C_NEW_STREAM_BIDI(a, ANY_ID), - OP_C_WRITE(a, "bar", 3), - OP_C_CONCLUDE(a), - - OP_S_ACCEPT_STREAM_WAIT(b), - OP_S_READ_EXPECT(b, "bar", 3), - OP_S_EXPECT_FIN(b), - - /* Drain the queue of incoming streams. */ - OP_BEGIN_REPEAT(200), - - OP_C_ACCEPT_STREAM_WAIT(b), - OP_C_READ_EXPECT(b, "foo", 3), - OP_C_EXPECT_FIN(b), - OP_C_FREE_STREAM(b), - - OP_END_REPEAT(), - + /* test moved to test/radix/quic_tests.c */ OP_END }; diff --git a/test/radix/quic_tests.c b/test/radix/quic_tests.c index c8026ce7c3..350d763524 100644 --- a/test/radix/quic_tests.c +++ b/test/radix/quic_tests.c @@ -1308,8 +1308,41 @@ DEF_SCRIPT(script_15, "Client sending large number of streams, MAX_STREAMS test" } } -DEF_SCRIPT(script_16, "place holder for multistrem script_16") +/* 16. Server sending large number of streams, MAX_STREAMS test */ +DEF_SCRIPT(script_16, "Server sending large number of streams, MAX_STREAMS test") { + size_t i; + + OP_SIMPLE_PAIR_CONN_ND(); + OP_ACCEPT_CONN_WAIT_ND(L, S, 0); + + /* + * This will cause a protocol violation to be raised by the client if we are + * not handling the stream limit correctly on the TX side. + */ + for (i = 0; i < 200; ++i) { + OP_NEW_STREAM(S, Sa, SSL_STREAM_FLAG_ADVANCE); + OP_WRITE(Sa, "foo", 3); + OP_CONCLUDE(Sa); + OP_UNBIND(Sa); + } + + /* Prove that the connection is still good. */ + OP_NEW_STREAM(C, Ca, 0); + OP_WRITE(Ca, "bar", 3); + OP_CONCLUDE(Ca); + + OP_ACCEPT_STREAM_WAIT(S, Sb, 0); + OP_READ_EXPECT(Sb, "bar", 3); + OP_EXPECT_FIN(Sb); + + /* Drain the queue of incoming streams. */ + for (i = 0; i < 200; ++i) { + OP_ACCEPT_STREAM_WAIT(C, Cb, 0); + OP_READ_EXPECT(Cb, "foo", 3); + OP_EXPECT_FIN(Cb); + OP_UNBIND(Cb); + } } DEF_SCRIPT(script_17, "place holder for multistrem script_17") From 9b94348003b2412187b22e3ee2201d16816e44fb Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Tue, 30 Jun 2026 17:07:37 +0700 Subject: [PATCH 327/349] Port script_17 to radix test framework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Saša Nedvědický Reviewed-by: Neil Horman Reviewed-by: Bob Beck MergeDate: Tue Jul 21 09:22:48 2026 (Merged from https://github.com/openssl/openssl/pull/31889) --- test/quic_multistream_test.c | 39 +----------------------- test/radix/quic_ops.c | 57 ++++++++++++++++++++++++++++++++++++ test/radix/quic_tests.c | 37 ++++++++++++++++++++++- 3 files changed, 94 insertions(+), 39 deletions(-) diff --git a/test/quic_multistream_test.c b/test/quic_multistream_test.c index b7472c4c37..eda2647034 100644 --- a/test/quic_multistream_test.c +++ b/test/quic_multistream_test.c @@ -2111,44 +2111,7 @@ static const struct script_op script_16[] = { /* 17. Key update test - unlimited */ static const struct script_op script_17[] = { - OP_C_SET_ALPN("ossltest"), - OP_C_CONNECT_WAIT(), - - OP_C_WRITE(DEFAULT, "apple", 5), - - OP_S_BIND_STREAM_ID(a, C_BIDI_ID(0)), - OP_S_READ_EXPECT(a, "apple", 5), - - OP_CHECK(override_key_update, 1), - - OP_BEGIN_REPEAT(200), - - OP_C_WRITE(DEFAULT, "apple", 5), - OP_S_READ_EXPECT(a, "apple", 5), - - /* - * TXKU frequency is bounded by RTT because a previous TXKU needs to be - * acknowledged by the peer first before another one can be begin. By - * waiting this long, we eliminate any such concern and ensure as many key - * updates as possible can occur for the purposes of this test. - */ - OP_CHECK(skip_time_ms, 100), - - OP_END_REPEAT(), - - /* At least 5 RXKUs detected */ - OP_CHECK(check_key_update_ge, 5), - - /* - * Prove the connection is still healthy by sending something in both - * directions. - */ - OP_C_WRITE(DEFAULT, "xyzzy", 5), - OP_S_READ_EXPECT(a, "xyzzy", 5), - - OP_S_WRITE(a, "plugh", 5), - OP_C_READ_EXPECT(DEFAULT, "plugh", 5), - + /* test moved to test/radix/quic_tests.c */ OP_END }; diff --git a/test/radix/quic_ops.c b/test/radix/quic_ops.c index 902dab4aed..7095a2a34e 100644 --- a/test/radix/quic_ops.c +++ b/test/radix/quic_ops.c @@ -958,6 +958,53 @@ err: return ok; } +DEF_FUNC(hf_override_key_update) +{ + int ok = 0; + SSL *ssl; + uint64_t threshold; + QUIC_CHANNEL *ch; + + F_POP(threshold); + REQUIRE_SSL(ssl); + ch = ossl_quic_conn_get_channel(ssl); + ossl_quic_channel_set_txku_threshold_override(ch, threshold); + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_check_key_update_ge) +{ + int ok = 0; + SSL *ssl; + uint64_t min_rxke, txke, rxke; + int64_t diff; + QUIC_CHANNEL *ch; + + F_POP(min_rxke); + REQUIRE_SSL(ssl); + ch = ossl_quic_conn_get_channel(ssl); + txke = ossl_quic_channel_get_tx_key_epoch(ch); + rxke = ossl_quic_channel_get_rx_key_epoch(ch); + diff = (int64_t)txke - (int64_t)rxke; + + /* + * TXKE must always be equal to or ahead of RXKE. + * It can be ahead of RXKE by at most 1. + */ + if (!TEST_int64_t_ge(diff, 0) || !TEST_int64_t_le(diff, 1)) + goto err; + + /* Caller specifies a minimum number of RXKEs which must have happened. */ + if (!TEST_uint64_t_ge(rxke, min_rxke)) + goto err; + + ok = 1; +err: + return ok; +} + #define OP_UNBIND(name) \ (OP_PUSH_PZ(#name), \ OP_FUNC(hf_unbind)) @@ -1184,3 +1231,13 @@ err: #define OP_SLEEP(ms) \ (OP_PUSH_U64(ms), \ OP_FUNC(hf_sleep)) + +#define OP_OVERRIDE_KEY_UPDATE(name, threshold) \ + (OP_SELECT_SSL(0, name), \ + OP_PUSH_U64(threshold), \ + OP_FUNC(hf_override_key_update)) + +#define OP_CHECK_KEY_UPDATE_GE(name, min_rxke) \ + (OP_SELECT_SSL(0, name), \ + OP_PUSH_U64(min_rxke), \ + OP_FUNC(hf_check_key_update_ge)) diff --git a/test/radix/quic_tests.c b/test/radix/quic_tests.c index 350d763524..f1bb1527e1 100644 --- a/test/radix/quic_tests.c +++ b/test/radix/quic_tests.c @@ -1345,8 +1345,43 @@ DEF_SCRIPT(script_16, "Server sending large number of streams, MAX_STREAMS test" } } -DEF_SCRIPT(script_17, "place holder for multistrem script_17") +/* 17. Key update test - unlimited */ +DEF_SCRIPT(script_17, "Key update test - unlimited") { + size_t i; + + OP_SIMPLE_PAIR_CONN(); + OP_ACCEPT_CONN_WAIT(L, S, 0); + + OP_WRITE(C, "apple", 5); + OP_READ_EXPECT(S, "apple", 5); + + OP_OVERRIDE_KEY_UPDATE(C, 1); + + for (i = 0; i < 200; ++i) { + OP_WRITE(C, "apple", 5); + OP_READ_EXPECT(S, "apple", 5); + /* + * TXKU frequency is bounded by RTT because a previous TXKU needs to be + * acknowledged by the peer first before another one can begin. By + * waiting this long, we eliminate any such concern and ensure as many key + * updates as possible can occur for the purposes of this test. + */ + OP_SKIP_TIME(100); + } + + /* At least 5 RXKUs detected */ + OP_CHECK_KEY_UPDATE_GE(C, 5); + + /* + * Prove the connection is still healthy by sending something in both + * directions. + */ + OP_WRITE(C, "xyzzy", 5); + OP_READ_EXPECT(S, "xyzzy", 5); + + OP_WRITE(S, "plugh", 5); + OP_READ_EXPECT(C, "plugh", 5); } DEF_SCRIPT(script_18, "place holder for multistrem script_18") From 7c6adaee6b15eed7eb44bb0c08411dc165940bdb Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Tue, 30 Jun 2026 17:17:48 +0700 Subject: [PATCH 328/349] Port script_18 to radix test framework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Saša Nedvědický Reviewed-by: Neil Horman Reviewed-by: Bob Beck MergeDate: Tue Jul 21 09:22:49 2026 (Merged from https://github.com/openssl/openssl/pull/31889) --- test/quic_multistream_test.c | 97 +----------------------------------- test/radix/quic_tests.c | 36 ++++++++++++- 2 files changed, 36 insertions(+), 97 deletions(-) diff --git a/test/quic_multistream_test.c b/test/quic_multistream_test.c index eda2647034..3f3fd82885 100644 --- a/test/quic_multistream_test.c +++ b/test/quic_multistream_test.c @@ -356,72 +356,12 @@ static OSSL_TIME get_time(void *arg) return t; } -static int skip_time_ms(struct helper *h, struct helper_local *hl) -{ - if (!TEST_true(CRYPTO_THREAD_write_lock(h->time_lock))) - return 0; - - h->time_slip = ossl_time_add(h->time_slip, ossl_ms2time(hl->check_op->arg2)); - - CRYPTO_THREAD_unlock(h->time_lock); - return 1; -} - static QUIC_TSERVER *s_lock(struct helper *h, struct helper_local *hl); static void s_unlock(struct helper *h, struct helper_local *hl); #define ACQUIRE_S() s_lock(h, hl) #define ACQUIRE_S_NOHL() s_lock(h, NULL) -static int override_key_update(struct helper *h, struct helper_local *hl) -{ - QUIC_CHANNEL *ch = ossl_quic_conn_get_channel(h->c_conn); - - ossl_quic_channel_set_txku_threshold_override(ch, hl->check_op->arg2); - return 1; -} - -static int trigger_key_update(struct helper *h, struct helper_local *hl) -{ - if (!TEST_true(SSL_key_update(h->c_conn, SSL_KEY_UPDATE_REQUESTED))) - return 0; - - return 1; -} - -static int check_key_update_ge(struct helper *h, struct helper_local *hl) -{ - QUIC_CHANNEL *ch = ossl_quic_conn_get_channel(h->c_conn); - int64_t txke = (int64_t)ossl_quic_channel_get_tx_key_epoch(ch); - int64_t rxke = (int64_t)ossl_quic_channel_get_rx_key_epoch(ch); - int64_t diff = txke - rxke; - - /* - * TXKE must always be equal to or ahead of RXKE. - * It can be ahead of RXKE by at most 1. - */ - if (!TEST_int64_t_ge(diff, 0) || !TEST_int64_t_le(diff, 1)) - return 0; - - /* Caller specifies a minimum number of RXKEs which must have happened. */ - if (!TEST_uint64_t_ge((uint64_t)rxke, hl->check_op->arg2)) - return 0; - - return 1; -} - -static int check_key_update_lt(struct helper *h, struct helper_local *hl) -{ - QUIC_CHANNEL *ch = ossl_quic_conn_get_channel(h->c_conn); - uint64_t txke = ossl_quic_channel_get_tx_key_epoch(ch); - - /* Caller specifies a maximum number of TXKEs which must have happened. */ - if (!TEST_uint64_t_lt(txke, hl->check_op->arg2)) - return 0; - - return 1; -} - static unsigned long stream_info_hash(const STREAM_INFO *info) { return OPENSSL_LH_strhash(info->name); @@ -2117,42 +2057,7 @@ static const struct script_op script_17[] = { /* 18. Key update test - RTT-bounded */ static const struct script_op script_18[] = { - OP_C_SET_ALPN("ossltest"), - OP_C_CONNECT_WAIT(), - - OP_C_WRITE(DEFAULT, "apple", 5), - - OP_S_BIND_STREAM_ID(a, C_BIDI_ID(0)), - OP_S_READ_EXPECT(a, "apple", 5), - - OP_CHECK(override_key_update, 1), - - OP_BEGIN_REPEAT(200), - - OP_C_WRITE(DEFAULT, "apple", 5), - OP_S_READ_EXPECT(a, "apple", 5), - OP_CHECK(skip_time_ms, 8), - - OP_END_REPEAT(), - - /* - * This time we simulate far less time passing between writes, so there are - * fewer opportunities to initiate TXKUs. Note that we ask for a TXKU every - * 1 packet above, which is absurd; thus this ensures we only actually - * generate TXKUs when we are allowed to. - */ - OP_CHECK(check_key_update_lt, 240), - - /* - * Prove the connection is still healthy by sending something in both - * directions. - */ - OP_C_WRITE(DEFAULT, "xyzzy", 5), - OP_S_READ_EXPECT(a, "xyzzy", 5), - - OP_S_WRITE(a, "plugh", 5), - OP_C_READ_EXPECT(DEFAULT, "plugh", 5), - + /* test moved to test/radix/quic_tests.c */ OP_END }; diff --git a/test/radix/quic_tests.c b/test/radix/quic_tests.c index f1bb1527e1..5b223cd00a 100644 --- a/test/radix/quic_tests.c +++ b/test/radix/quic_tests.c @@ -1384,8 +1384,42 @@ DEF_SCRIPT(script_17, "Key update test - unlimited") OP_READ_EXPECT(C, "plugh", 5); } -DEF_SCRIPT(script_18, "place holder for multistrem script_18") +/* 18. Key update test - RTT-bounded */ +DEF_SCRIPT(script_18, "Key update test - RTT-bounded") { + size_t i; + + OP_SIMPLE_PAIR_CONN(); + OP_ACCEPT_CONN_WAIT(L, S, 0); + + OP_WRITE(C, "apple", 5); + OP_READ_EXPECT(S, "apple", 5); + + OP_OVERRIDE_KEY_UPDATE(C, 1); + + for (i = 0; i < 200; ++i) { + OP_WRITE(C, "apple", 5); + OP_READ_EXPECT(S, "apple", 5); + OP_SKIP_TIME(8); + } + + /* + * This time we simulate far less time passing between writes, so there are + * fewer opportunities to initiate TXKUs. Note that we ask for a TXKU every + * 1 packet above, which is absurd; thus this ensures we only actually + * generate TXKUs when we are allowed to. + */ + OP_CHECK_KEY_UPDATE_LT(C, 240); + + /* + * Prove the connection is still healthy by sending something in both + * directions. + */ + OP_WRITE(C, "xyzzy", 5); + OP_READ_EXPECT(S, "xyzzy", 5); + + OP_WRITE(S, "plugh", 5); + OP_READ_EXPECT(C, "plugh", 5); } DEF_SCRIPT(script_19, "place holder for multistrem script_19") From 08d3dd81b2e4ca23afbd3dc0520ae3b499107dc5 Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Tue, 30 Jun 2026 20:52:51 +0700 Subject: [PATCH 329/349] Port script_19 to radix test framework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Saša Nedvědický Reviewed-by: Neil Horman Reviewed-by: Bob Beck MergeDate: Tue Jul 21 09:22:51 2026 (Merged from https://github.com/openssl/openssl/pull/31889) --- test/quic_multistream_test.c | 25 +------------------ test/radix/quic_ops.c | 48 ++++++++++++++++++++++++++++++++++++ test/radix/quic_tests.c | 25 ++++++++++++++++++- 3 files changed, 73 insertions(+), 25 deletions(-) diff --git a/test/quic_multistream_test.c b/test/quic_multistream_test.c index 3f3fd82885..a921f13610 100644 --- a/test/quic_multistream_test.c +++ b/test/quic_multistream_test.c @@ -2063,30 +2063,7 @@ static const struct script_op script_18[] = { /* 19. Key update test - artificially triggered */ static const struct script_op script_19[] = { - OP_C_SET_ALPN("ossltest"), - OP_C_CONNECT_WAIT(), - - OP_C_WRITE(DEFAULT, "apple", 5), - - OP_S_BIND_STREAM_ID(a, C_BIDI_ID(0)), - OP_S_READ_EXPECT(a, "apple", 5), - - OP_C_WRITE(DEFAULT, "orange", 6), - OP_S_READ_EXPECT(a, "orange", 6), - - OP_S_WRITE(a, "strawberry", 10), - OP_C_READ_EXPECT(DEFAULT, "strawberry", 10), - - OP_CHECK(check_key_update_lt, 1), - OP_CHECK(trigger_key_update, 0), - - OP_C_WRITE(DEFAULT, "orange", 6), - OP_S_READ_EXPECT(a, "orange", 6), - OP_S_WRITE(a, "ok", 2), - - OP_C_READ_EXPECT(DEFAULT, "ok", 2), - OP_CHECK(check_key_update_ge, 1), - + /* test moved to test/radix/quic_tests.c */ OP_END }; diff --git a/test/radix/quic_ops.c b/test/radix/quic_ops.c index 7095a2a34e..f022907596 100644 --- a/test/radix/quic_ops.c +++ b/test/radix/quic_ops.c @@ -1005,6 +1005,44 @@ err: return ok; } +DEF_FUNC(hf_check_key_update_lt) +{ + int ok = 0; + SSL *ssl; + uint64_t max_txke, txke; + QUIC_CHANNEL *ch; + + F_POP(max_txke); + REQUIRE_SSL(ssl); + ch = ossl_quic_conn_get_channel(ssl); + txke = ossl_quic_channel_get_tx_key_epoch(ch); + + /* Caller specifies a maximum number of TXKEs which must not be exceeded. */ + if (!TEST_uint64_t_lt(txke, max_txke)) + goto err; + + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_trigger_key_update) +{ + int ok = 0; + SSL *ssl; + uint64_t update_type; + + F_POP(update_type); + REQUIRE_SSL(ssl); + + if (!TEST_true(SSL_key_update(ssl, (int)update_type))) + goto err; + + ok = 1; +err: + return ok; +} + #define OP_UNBIND(name) \ (OP_PUSH_PZ(#name), \ OP_FUNC(hf_unbind)) @@ -1241,3 +1279,13 @@ err: (OP_SELECT_SSL(0, name), \ OP_PUSH_U64(min_rxke), \ OP_FUNC(hf_check_key_update_ge)) + +#define OP_CHECK_KEY_UPDATE_LT(name, max_txke) \ + (OP_SELECT_SSL(0, name), \ + OP_PUSH_U64(max_txke), \ + OP_FUNC(hf_check_key_update_lt)) + +#define OP_TRIGGER_KEY_UPDATE(name, update_type) \ + (OP_SELECT_SSL(0, name), \ + OP_PUSH_U64(update_type), \ + OP_FUNC(hf_trigger_key_update)) diff --git a/test/radix/quic_tests.c b/test/radix/quic_tests.c index 5b223cd00a..154926a16f 100644 --- a/test/radix/quic_tests.c +++ b/test/radix/quic_tests.c @@ -1422,8 +1422,31 @@ DEF_SCRIPT(script_18, "Key update test - RTT-bounded") OP_READ_EXPECT(C, "plugh", 5); } -DEF_SCRIPT(script_19, "place holder for multistrem script_19") +/* 19. Key update test - artificially triggered */ +DEF_SCRIPT(script_19, "Key update test - artificially triggered") { + OP_SIMPLE_PAIR_CONN(); + OP_ACCEPT_CONN_WAIT(L, S, 0); + + OP_WRITE(C, "apple", 5); + OP_READ_EXPECT(S, "apple", 5); + + OP_WRITE(C, "orange", 6); + OP_READ_EXPECT(S, "orange", 6); + + OP_WRITE(S, "strawberry", 10); + OP_READ_EXPECT(C, "strawberry", 10); + + OP_CHECK_KEY_UPDATE_LT(C, 1); + + OP_TRIGGER_KEY_UPDATE(C, SSL_KEY_UPDATE_REQUESTED); + + OP_WRITE(C, "orange", 6); + OP_READ_EXPECT(S, "orange", 6); + OP_WRITE(S, "ok", 2); + + OP_READ_EXPECT(C, "ok", 2); + OP_CHECK_KEY_UPDATE_GE(C, 1); } DEF_SCRIPT(script_20, "place holder for multistrem script_20") From fcba83dc4d2a50581466eadd1598e1dd9c0cea03 Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Sun, 12 Jul 2026 23:50:05 +0700 Subject: [PATCH 330/349] Wire radix QUIC test framework clock to simulated time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit script_17/18/19 (key update tests) rely on OP_SKIP_TIME to advance simulated time so that TXKU cooldown/RTT-gated behaviour can be exercised deterministically, but the framework never actually plumbed that simulated clock into the QUIC channels under test -- OP_SKIP_TIME only affected the terp's own bookkeeping, so real key updates only ever occurred by chance regardless of how much time was "skipped". Fix this by: - Calling ossl_quic_set_override_now_cb() in hf_new_ssl() so each QUIC channel actually uses the framework's simulated clock, matching what the older quictestlib.c harness already did via fake_now_cb(). - Activating the server-side connection object in hf_accept_conn() so it gets ticked (SSL_handle_events()) like every other object, driving its internal key-update/timer processing forward. Two correctness issues had to be addressed to make this clock plumbing safe and deterministic: - The clock handed to QUIC channels must be purely virtual (a fixed base time plus an explicit slip that only advances via radix_skip_time()/OP_SKIP_TIME), not real wall-clock time, or protocol-timing-sensitive assertions become flaky depending on how long real cryptographic work takes on a given machine (e.g. much slower on ASAN+UBSAN debug builds), occasionally triggering a spurious second TXKU before the first is confirmed. radix_process is a single static struct reused across every script in the suite, so time_slip must be reset in RADIX_PROCESS_init() -- otherwise a script would inherit the previous script's accumulated slip and see time jump forward all at once for a brand new connection. The virtual clock also needs a small, fixed per-tick advance (do_per_op(), mirroring the old harness's qtest_add_time(1) in its own connect-wait loop), since QUIC's internal timers need to observe time actually passing to make progress during a busy-wait such as hf_connect_wait spinning on SSL_connect(). - time_slip needs its own dedicated mutex (time_m) rather than being protected by the existing global mutex (gm): get_time() is now called by libssl/QUIC's internals (e.g. from within the reactor tick while holding QUIC's own locks), whereas gm is held by test code across calls into libssl (e.g. hf_clear() holds gm while calling SSL_free()). Sharing gm would take gm and QUIC's internal lock in opposite orders across those two paths, risking a real deadlock (confirmed via a ThreadSanitizer lock-order-inversion report). - The terp's own script-execution watchdog (max_execution_time) needs its own now_cb (terp_now) based on real wall-clock time, so that OP_SKIP_TIME doesn't eat into its execution budget, and so it can still catch a genuinely stuck script independent of the virtual protocol clock. Some scripts also legitimately need a larger budget than the terp default on slow or heavily instrumented CI machines. Assisted-by: Claude:claude-sonnet-5 Reviewed-by: Saša Nedvědický Reviewed-by: Neil Horman Reviewed-by: Bob Beck MergeDate: Tue Jul 21 09:22:53 2026 (Merged from https://github.com/openssl/openssl/pull/31889) --- test/radix/quic_bindings.c | 33 +++++++++++++++++++++++++-------- test/radix/quic_ops.c | 6 ++++++ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/test/radix/quic_bindings.c b/test/radix/quic_bindings.c index 7dc46f7950..63bcecad99 100644 --- a/test/radix/quic_bindings.c +++ b/test/radix/quic_bindings.c @@ -67,9 +67,12 @@ typedef struct radix_process_st { /* Process-global state. */ CRYPTO_MUTEX *gm; /* global mutex */ LHASH_OF(RADIX_OBJ) *objs; /* protected by gm */ - OSSL_TIME time_slip; /* protected by gm */ BIO *keylog_out; /* protected by gm */ + CRYPTO_MUTEX *time_m; + OSSL_TIME base_time; /* set once at init, constant thereafter */ + OSSL_TIME time_slip; /* protected by time_m */ + int done_join_all_threads; /* @@ -178,6 +181,8 @@ static int RADIX_PROCESS_init(RADIX_PROCESS *rp, size_t node_idx, size_t process #if defined(OPENSSL_THREADS) if (!TEST_ptr(rp->gm = ossl_crypto_mutex_new())) goto err; + if (!TEST_ptr(rp->time_m = ossl_crypto_mutex_new())) + goto err; #endif if (!TEST_ptr(rp->objs = lh_RADIX_OBJ_new(RADIX_OBJ_hash, RADIX_OBJ_cmp))) @@ -196,12 +201,15 @@ static int RADIX_PROCESS_init(RADIX_PROCESS *rp, size_t node_idx, size_t process rp->process_idx = process_idx; rp->done_join_all_threads = 0; rp->next_thread_idx = 0; + rp->base_time = ossl_time_now(); + rp->time_slip = ossl_time_zero(); return 1; err: lh_RADIX_OBJ_free(rp->objs); rp->objs = NULL; ossl_crypto_mutex_free(&rp->gm); + ossl_crypto_mutex_free(&rp->time_m); return 0; } @@ -456,6 +464,7 @@ static void RADIX_PROCESS_cleanup(RADIX_PROCESS *rp) BIO_free_all(rp->keylog_out); rp->keylog_out = NULL; ossl_crypto_mutex_free(&rp->gm); + ossl_crypto_mutex_free(&rp->time_m); } static RADIX_OBJ *RADIX_PROCESS_get_obj(RADIX_PROCESS *rp, const char *name) @@ -681,18 +690,23 @@ static OSSL_TIME get_time(void *arg) { OSSL_TIME time_slip; - ossl_crypto_mutex_lock(RP()->gm); + ossl_crypto_mutex_lock(RP()->time_m); time_slip = RP()->time_slip; - ossl_crypto_mutex_unlock(RP()->gm); + ossl_crypto_mutex_unlock(RP()->time_m); - return ossl_time_add(ossl_time_now(), time_slip); + return ossl_time_add(RP()->base_time, time_slip); } -ossl_unused static void radix_skip_time(OSSL_TIME t) +static OSSL_TIME terp_now(void *arg) { - ossl_crypto_mutex_lock(RP()->gm); + return ossl_time_now(); +} + +static void radix_skip_time(OSSL_TIME t) +{ + ossl_crypto_mutex_lock(RP()->time_m); RP()->time_slip = ossl_time_add(RP()->time_slip, t); - ossl_crypto_mutex_unlock(RP()->gm); + ossl_crypto_mutex_unlock(RP()->time_m); } static void per_op_tick_obj(RADIX_OBJ *obj) @@ -705,14 +719,17 @@ static void per_op_tick_obj(RADIX_OBJ *obj) static int do_per_op(TERP *terp, void *arg) { + radix_skip_time(ossl_ms2time(1)); lh_RADIX_OBJ_doall(RP()->objs, per_op_tick_obj); return 1; } static int bindings_adjust_terp_config(TERP_CONFIG *cfg) { - cfg->now_cb = get_time; + cfg->now_cb = terp_now; cfg->per_op_cb = do_per_op; + + cfg->max_execution_time = ossl_ms2time(60000); return 1; } diff --git a/test/radix/quic_ops.c b/test/radix/quic_ops.c index f022907596..ed129d0ff7 100644 --- a/test/radix/quic_ops.c +++ b/test/radix/quic_ops.c @@ -204,6 +204,11 @@ DEF_FUNC(hf_new_ssl) if (!is_domain && !TEST_true(ssl_attach_bio_dgram(ssl, 0, NULL))) goto err; + if (!TEST_true(ossl_quic_set_override_now_cb(ssl, get_time, NULL))) { + SSL_free(ssl); + goto err; + } + if (!TEST_true(RADIX_PROCESS_set_ssl(RP(), name, ssl))) { SSL_free(ssl); goto err; @@ -342,6 +347,7 @@ DEF_FUNC(hf_accept_conn) SSL_free(conn); goto err; } + radix_activate_obj(RADIX_PROCESS_get_obj(RP(), conn_name)); ok = 1; err: From 63afe639141faf49a62611808651b2b80030c64b Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Thu, 16 Jul 2026 14:15:56 +0200 Subject: [PATCH 331/349] quic: report SRTM entry match status via an output argument Coverity (CID 1696969, CHECKED_RETURN) flagged the ignored return value of ossl_quic_srtm_remove() in ch_enqueue_retire_conn_id(). The return value could not be usefully checked because 0 was overloaded to mean both "no matching entry" and a genuine internal failure (alloc_failed or an lhash consistency error). Give the return value a single meaning (1 on success, 0 on internal error) and report whether a matching entry was found through a new uint8_t *match output argument, which may be NULL if the caller does not need it. Resolved: https://scan5.scan.coverity.com/#/project-view/65138/10222?selectedIssue=1696969 Signed-off-by: Nikola Pajkovsky Reviewed-by: Bob Beck Reviewed-by: Paul Dale Reviewed-by: Neil Horman MergeDate: Tue Jul 21 09:24:45 2026 (Merged from https://github.com/openssl/openssl/pull/31974) --- fuzz/quic-srtm.c | 2 +- include/internal/quic_srtm.h | 20 ++++++++++++++++---- ssl/quic/quic_channel.c | 3 ++- ssl/quic/quic_srtm.c | 12 ++++++++++-- test/quic_srtm_test.c | 17 +++++++++++------ 5 files changed, 40 insertions(+), 14 deletions(-) diff --git a/fuzz/quic-srtm.c b/fuzz/quic-srtm.c index 6e152299e1..9eb4117adc 100644 --- a/fuzz/quic-srtm.c +++ b/fuzz/quic-srtm.c @@ -88,7 +88,7 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) continue; /* just stop */ if (ossl_quic_srtm_remove(srtm, (void *)(uintptr_t)arg_opaque, - arg_seq_num)) + arg_seq_num, NULL)) ossl_quic_srtm_check(srtm); break; diff --git a/include/internal/quic_srtm.h b/include/internal/quic_srtm.h index 1a8f55da5d..77c0af6ac4 100644 --- a/include/internal/quic_srtm.h +++ b/include/internal/quic_srtm.h @@ -11,6 +11,7 @@ #define OSSL_INTERNAL_QUIC_SRTM_H #pragma once +#include #include "internal/e_os.h" #include "internal/time.h" #include "internal/quic_types.h" @@ -69,11 +70,22 @@ void ossl_quic_srtm_free(QUIC_SRTM *srtm); int ossl_quic_srtm_add(QUIC_SRTM *srtm, void *opaque, uint64_t seq_num, const QUIC_STATELESS_RESET_TOKEN *token); -/* - * Removes an entry by identifying it via its (opaque, seq_num) tuple. - * Returns 1 if the entry was found and removed, and 0 if it was not found. +/** + * \brief Removes an entry identified by its (opaque, seq_num) tuple. + * + * The absence of a matching entry is not an error. + * + * \param srtm SRTM instance to remove the entry from. + * \param opaque Opaque pointer identifying the entry. + * \param seq_num Sequence number identifying the entry. + * \param match If non-NULL, \c *match is set to 1 if a matching entry was + * found or to 0 if not. May be NULL if this information is not + * required. + * + * \return 1 on success and 0 on internal error. */ -int ossl_quic_srtm_remove(QUIC_SRTM *srtm, void *opaque, uint64_t seq_num); +int ossl_quic_srtm_remove(QUIC_SRTM *srtm, void *opaque, uint64_t seq_num, + uint8_t *match); /* * Removes all entries (opaque, *) with the given opaque pointer. diff --git a/ssl/quic/quic_channel.c b/ssl/quic/quic_channel.c index aaabf5a432..99f131cb78 100644 --- a/ssl/quic/quic_channel.c +++ b/ssl/quic/quic_channel.c @@ -3325,7 +3325,8 @@ static int ch_enqueue_retire_conn_id(QUIC_CHANNEL *ch, uint64_t seq_num) WPACKET wpkt; size_t l; - ossl_quic_srtm_remove(ch->srtm, ch, seq_num); + if (!ossl_quic_srtm_remove(ch->srtm, ch, seq_num, NULL)) + goto err; if ((buf_mem = BUF_MEM_new()) == NULL) goto err; diff --git a/ssl/quic/quic_srtm.c b/ssl/quic/quic_srtm.c index 1cc2ae6962..4c8ea10734 100644 --- a/ssl/quic/quic_srtm.c +++ b/ssl/quic/quic_srtm.c @@ -382,16 +382,24 @@ static int srtm_remove_from_rev(QUIC_SRTM *srtm, SRTM_ITEM *item) return 1; } -int ossl_quic_srtm_remove(QUIC_SRTM *srtm, void *opaque, uint64_t seq_num) +int ossl_quic_srtm_remove(QUIC_SRTM *srtm, void *opaque, uint64_t seq_num, + uint8_t *match) { SRTM_ITEM *item, *prev = NULL; + uint8_t match_sink; + + if (match == NULL) + match = &match_sink; + *match = 0; if (srtm->alloc_failed) return 0; if ((item = srtm_find(srtm, opaque, seq_num, NULL, &prev)) == NULL) /* No match */ - return 0; + return 1; + + *match = 1; /* Remove from forward mapping. */ if (prev == NULL) { diff --git a/test/quic_srtm_test.c b/test/quic_srtm_test.c index 6a1d6f3618..7838d46a77 100644 --- a/test/quic_srtm_test.c +++ b/test/quic_srtm_test.c @@ -22,14 +22,17 @@ static int test_srtm(void) QUIC_SRTM *srtm; void *opaque = NULL; uint64_t seq_num = 0; + uint8_t match; if (!TEST_ptr(srtm = ossl_quic_srtm_new(NULL, NULL))) goto err; if (!TEST_true(ossl_quic_srtm_add(srtm, ptrs + 0, 0, &token_1)) || !TEST_false(ossl_quic_srtm_add(srtm, ptrs + 0, 0, &token_1)) - || !TEST_false(ossl_quic_srtm_remove(srtm, ptrs + 0, 1)) - || !TEST_false(ossl_quic_srtm_remove(srtm, ptrs + 3, 0)) + || !TEST_true(ossl_quic_srtm_remove(srtm, ptrs + 0, 1, &match)) + || !TEST_uint_eq(match, 0) + || !TEST_true(ossl_quic_srtm_remove(srtm, ptrs + 3, 0, &match)) + || !TEST_uint_eq(match, 0) || !TEST_true(ossl_quic_srtm_cull(srtm, ptrs + 3)) || !TEST_true(ossl_quic_srtm_cull(srtm, ptrs + 3)) || !TEST_true(ossl_quic_srtm_add(srtm, ptrs + 0, 1, &token_1)) @@ -38,7 +41,8 @@ static int test_srtm(void) || !TEST_true(ossl_quic_srtm_add(srtm, ptrs + 1, 0, &token_1)) || !TEST_true(ossl_quic_srtm_add(srtm, ptrs + 2, 0, &token_2)) || !TEST_true(ossl_quic_srtm_add(srtm, ptrs + 3, 3, &token_2)) - || !TEST_true(ossl_quic_srtm_remove(srtm, ptrs + 3, 3)) + || !TEST_true(ossl_quic_srtm_remove(srtm, ptrs + 3, 3, &match)) + || !TEST_uint_eq(match, 1) || !TEST_true(ossl_quic_srtm_lookup(srtm, &token_1, 0, &opaque, &seq_num)) || !TEST_ptr_eq(opaque, ptrs + 1) || !TEST_uint64_t_eq(seq_num, 0) @@ -62,7 +66,8 @@ static int test_srtm(void) || !TEST_true(ossl_quic_srtm_lookup(srtm, &token_2, 0, &opaque, &seq_num)) || !TEST_ptr_eq(opaque, ptrs + 2) || !TEST_uint64_t_eq(seq_num, 0) - || !TEST_true(ossl_quic_srtm_remove(srtm, ptrs + 2, 0)) + || !TEST_true(ossl_quic_srtm_remove(srtm, ptrs + 2, 0, &match)) + || !TEST_uint_eq(match, 1) || !TEST_false(ossl_quic_srtm_lookup(srtm, &token_2, 0, &opaque, &seq_num))) goto err; @@ -102,11 +107,11 @@ static int test_srtm_ops_mfail(void) || !ossl_quic_srtm_add(srtm, ptrs + 1, 0, &token_1) || !ossl_quic_srtm_add(srtm, ptrs + 2, 0, &token_2) || !ossl_quic_srtm_add(srtm, ptrs + 3, 3, &token_2) - || !ossl_quic_srtm_remove(srtm, ptrs + 3, 3) + || !ossl_quic_srtm_remove(srtm, ptrs + 3, 3, NULL) || !ossl_quic_srtm_lookup(srtm, &token_1, 0, &opaque, &seq_num) || !ossl_quic_srtm_cull(srtm, ptrs + 0) || !ossl_quic_srtm_lookup(srtm, &token_2, 0, &opaque, &seq_num) - || !ossl_quic_srtm_remove(srtm, ptrs + 2, 0)) + || !ossl_quic_srtm_remove(srtm, ptrs + 2, 0, NULL)) goto err; testresult = 1; From 4cef487f2a5615e59d919e6fd4094a02038950ff Mon Sep 17 00:00:00 2001 From: Norbert Pocs Date: Sat, 18 Jul 2026 16:49:06 +0200 Subject: [PATCH 332/349] Guard against NULL data in empty ASN1_STRINGs Since 28179061bf a zero-length ASN1_STRING has data == NULL in fuzzing builds instead of a 1-byte allocation. Several call sites did pointer arithmetic or memcpy on the data pointer before any length check, which is undefined behaviour for NULL even with a zero offset and aborts the fuzz targets under UBSan: - asn1_string_canon: skip canonicalisation of empty values - do_buf, do_hex_dump: return early on an empty buffer - i2d_ocsp_nonce: skip the memcpy for an empty nonce The loops at these sites were already no-ops for zero length, so there is no behaviour change outside sanitizer builds. Assisted-By: Claude:claude-fable-5 Signed-off-by: Norbert Pocs Reviewed-by: Nikola Pajkovsky Reviewed-by: Tomas Mraz MergeDate: Tue Jul 21 12:54:48 2026 (Merged from https://github.com/openssl/openssl/pull/31998) --- crypto/asn1/a_strex.c | 8 ++++++++ crypto/ocsp/v3_ocsp.c | 5 +++-- crypto/x509/x_name.c | 4 +++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/crypto/asn1/a_strex.c b/crypto/asn1/a_strex.c index e488c87f5b..57a14f6f90 100644 --- a/crypto/asn1/a_strex.c +++ b/crypto/asn1/a_strex.c @@ -142,6 +142,10 @@ static int do_buf(const unsigned char *buf, int buflen, const unsigned char *p, *q; uint32_t c; + if (buflen < 0) + return -1; + if (buflen == 0) + return 0; p = buf; q = buf + buflen; outlen = 0; @@ -236,6 +240,10 @@ static int do_hex_dump(char_io *io_ch, void *arg, unsigned char *buf, unsigned char *p, *q; char hextmp[2]; + if (buflen < 0) + return -1; + if (buflen == 0) + return 0; if (arg) { p = buf; q = buf + buflen; diff --git a/crypto/ocsp/v3_ocsp.c b/crypto/ocsp/v3_ocsp.c index d31c74ef45..408c2a1548 100644 --- a/crypto/ocsp/v3_ocsp.c +++ b/crypto/ocsp/v3_ocsp.c @@ -143,7 +143,7 @@ static void *ocsp_nonce_new(void) static int i2d_ocsp_nonce(const void *a, unsigned char **pp) { const ASN1_OCTET_STRING *os = a; - if (pp) { + if (pp != NULL && os->length > 0) { memcpy(*pp, os->data, os->length); *pp += os->length; } @@ -164,7 +164,8 @@ static void *d2i_ocsp_nonce(void *a, const unsigned char **pp, long length) if (!ASN1_OCTET_STRING_set(os, *pp, length)) goto err; - *pp += length; + if (length > 0) + *pp += length; if (pos) *pos = os; diff --git a/crypto/x509/x_name.c b/crypto/x509/x_name.c index 90588c8c67..961f474e5b 100644 --- a/crypto/x509/x_name.c +++ b/crypto/x509/x_name.c @@ -404,8 +404,10 @@ static int asn1_string_canon(ASN1_STRING *out, const ASN1_STRING *in) out->type = V_ASN1_UTF8STRING; out->length = ASN1_STRING_to_UTF8(&out->data, in); - if (out->length == -1) + if (out->length < 0) return 0; + if (out->length == 0) + return 1; to = out->data; from = to; From ecd8d329f3476ee54629416738542df0d626a30f Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Sun, 12 Jul 2026 00:35:35 +1000 Subject: [PATCH 333/349] Fix TLS 1.3 PSKs with SSL_VERIFY_PEER and no sid_ctx. A server with client certificate verification requested, but no session ID context configured, wrongly rejected every TLS 1.3 PSK-based connection, whether a resumption ticket, or an external PSK. After a full non-PSK handshake the same server issued a poison resumption PSK (session ticket) that led to handshake failure if/when used. The session ID context check exists to stop SSL acceptors with distinct authentication policies that share a session cache from resuming each other's sessions and trusting their authentication results; it doesn't apply to a just-validated external PSK, so the corresponding sessions are now exempted. Ticket issuance is also suppressed when it's already known that the ticket would lead to a handshake failure with the same server's configuration. Reviewed-by: Bob Beck Reviewed-by: Norbert Pocs MergeDate: Tue Jul 21 14:12:04 2026 (Merged from https://github.com/openssl/openssl/pull/31964) --- ssl/ssl_local.h | 15 +++++++++++++++ ssl/ssl_sess.c | 18 ++++++++++++++++-- ssl/statem/extensions_srvr.c | 19 ++++++++++++++++--- ssl/statem/statem_srvr.c | 15 +++++++++------ 4 files changed, 56 insertions(+), 11 deletions(-) diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h index aeb0c170ad..2cfc5cb815 100644 --- a/ssl/ssl_local.h +++ b/ssl/ssl_local.h @@ -512,6 +512,21 @@ struct ssl_session_st { * to disable session caching and tickets. */ int not_resumable; + /* + * Set when this session's master key was resolved from an external PSK + * identity (psk_find_session_cb(), or the legacy psk_server_callback()) + * rather than from a resumption ticket or session-cache lookup. + * ssl_get_prev_session() uses this to exempt such sessions from sid_ctx + * checks that only make sense for a real cache lookup. + * + * Deliberately not part of the SSL_SESSION ASN.1 encoding: it must not + * survive a real ticket round-trip (a session reconstructed by + * d2i_SSL_SESSION() from a genuine, previously-issued ticket is by + * definition not an external-PSK match, and should get the ordinary + * sid_ctx treatment). ssl_session_dup() resets it to 0 on every copy, + * mirroring not_resumable just above, for the same reason. + */ + int psk_external; /* Peer raw public key, if available */ EVP_PKEY *peer_rpk; /* This is the cert and type for the other end. */ diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c index ba0dcd229f..094ab1a74e 100644 --- a/ssl/ssl_sess.c +++ b/ssl/ssl_sess.c @@ -279,8 +279,16 @@ SSL_SESSION *ssl_session_dup(const SSL_SESSION *src, int ticket) { SSL_SESSION *sess = ssl_session_dup_intern(src, ticket); - if (sess != NULL) + if (sess != NULL) { sess->not_resumable = 0; + /* + * A duplicated session can land in the stateful session cache, and is + * not necessarily a live session just built for an external PSK. The + * caller must explicitly set this field non-zero after duplication as + * needed. + */ + sess->psk_external = 0; + } return sess; } @@ -648,7 +656,13 @@ int ssl_get_prev_session(SSL_CONNECTION *s, CLIENTHELLO_MSG *hello) goto err; /* treat like cache miss */ } - if ((s->verify_mode & SSL_VERIFY_PEER) && s->sid_ctx_length == 0) { + /* + * sid_ctx exists to keep multiple services that happen to share one + * session cache from resuming each other's sessions. This check is not + * relevant to external PSK sessions that are not restored from a cache. + */ + if (!ret->psk_external + && (s->verify_mode & SSL_VERIFY_PEER) && s->sid_ctx_length == 0) { /* * We can't be sure if this session is being used out of context, * which is especially important for SSL_VERIFY_PEER. The application diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c index 12c06eef9b..a597085e1b 100644 --- a/ssl/statem/extensions_srvr.c +++ b/ssl/statem/extensions_srvr.c @@ -1397,7 +1397,10 @@ int tls_parse_ctos_psk(SSL_CONNECTION *s, PACKET *pkt, unsigned int context, #endif /* OPENSSL_NO_PSK */ if (sess != NULL) { - /* We found a PSK */ + /* + * We found an external (not a resumption) PSK - duplicate the + * session, set the session id to our own, and mark it as external. + */ SSL_SESSION *sesstmp = ssl_session_dup(sess, 0); if (sesstmp == NULL) { @@ -1413,7 +1416,7 @@ int tls_parse_ctos_psk(SSL_CONNECTION *s, PACKET *pkt, unsigned int context, */ memcpy(sess->sid_ctx, s->sid_ctx, s->sid_ctx_length); sess->sid_ctx_length = s->sid_ctx_length; - ext = 1; + sess->psk_external = ext = 1; if (id == 0) s->ext.early_data_ok = 1; s->ext.ticket_expected = 1; @@ -1484,6 +1487,8 @@ int tls_parse_ctos_psk(SSL_CONNECTION *s, PACKET *pkt, unsigned int context, */ s->ext.early_data_ok = 1; } + /* This PSK is not external, use the correct binder label, ... */ + ext = 0; } md = ssl_md(sctx, sess->cipher->algorithm2); @@ -1760,7 +1765,15 @@ EXT_RETURN tls_construct_stoc_session_ticket(SSL_CONNECTION *s, WPACKET *pkt, unsigned int context, X509 *x, size_t chainidx) { - if (!s->ext.ticket_expected || !tls_use_ticket(s)) { + /* + * Don't tell the client to expect a NewSessionTicket when any + * ticket we'd mint would be rejected by ssl_get_prev_session() + * whenever SSL_VERIFY_PEER is set with no sid_ctx configured (see + * the checks there). In TLS 1.2, once promised the ticket MUST + * be sent. + */ + if (!s->ext.ticket_expected || !tls_use_ticket(s) + || ((s->verify_mode & SSL_VERIFY_PEER) != 0 && s->sid_ctx_length == 0)) { s->ext.ticket_expected = 0; return EXT_RETURN_NOT_SENT; } diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c index 2f8bdcea94..fc3769a017 100644 --- a/ssl/statem/statem_srvr.c +++ b/ssl/statem/statem_srvr.c @@ -712,17 +712,20 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL_CONNECTION *s) * session tickets or resumption (e.g. new_session_count = 0 or * resumption_count = 0), this implementation does not currently * interpret or enforce those parameters. + * + * Also skip issuance when SSL_VERIFY_PEER is set with no sid_ctx + * configured: any ticket minted here would be rejected by + * ssl_get_prev_session() in that configuration. */ - if (((s->options & SSL_OP_NO_TICKET) != 0 + if (s->num_tickets <= s->sent_tickets + || ((s->options & SSL_OP_NO_TICKET) != 0 && (SSL_CONNECTION_GET_CTX(s)->session_cache_mode & SSL_SESS_CACHE_SERVER) == 0) - || s->ext.psk_kex_mode == TLSEXT_KEX_MODE_FLAG_NONE) { + || s->ext.psk_kex_mode == TLSEXT_KEX_MODE_FLAG_NONE + || ((s->verify_mode & SSL_VERIFY_PEER) != 0 && s->sid_ctx_length == 0)) st->hand_state = TLS_ST_OK; - } else if (s->num_tickets > s->sent_tickets) { + else st->hand_state = TLS_ST_SW_SESSION_TICKET; - } else { - st->hand_state = TLS_ST_OK; - } return WRITE_TRAN_CONTINUE; case TLS_ST_SR_KEY_UPDATE: From 4abb70d32719d15a1db1868484123a1f630b720b Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Sun, 12 Jul 2026 00:35:56 +1000 Subject: [PATCH 334/349] PSK: Handle non-empty client sid_ctx - Do not reject external PSKs when client-side sid_ctx is set, fixed by copying the sid_ctx into the pending session. Tests added for this and also the server side sid_ctx issues. Reviewed-by: Bob Beck Reviewed-by: Norbert Pocs MergeDate: Tue Jul 21 14:12:06 2026 (Merged from https://github.com/openssl/openssl/pull/31964) --- ssl/statem/extensions_clnt.c | 20 +++++ test/sslapitest.c | 163 +++++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+) diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c index e3e713f5f6..f17fe2c495 100644 --- a/ssl/statem/extensions_clnt.c +++ b/ssl/statem/extensions_clnt.c @@ -2415,6 +2415,7 @@ int tls_parse_stoc_psk(SSL_CONNECTION *s, PACKET *pkt, size_t chainidx) { #ifndef OPENSSL_NO_TLS1_3 + SSL_SESSION *sesstmp; unsigned int identity; if (!PACKET_get_net_2(pkt, &identity) || PACKET_remaining(pkt) != 0) { @@ -2456,6 +2457,25 @@ int tls_parse_stoc_psk(SSL_CONNECTION *s, PACKET *pkt, || s->psksession->ext.max_early_data == 0) memcpy(s->early_secret, s->psksession->early_secret, EVP_MAX_MD_SIZE); + /* + * The psk_use_session_cb()/psk_client_callback() may reuse + * the session across connections we can't mutate it directly. + */ + if ((sesstmp = ssl_session_dup(s->psksession, 0)) == NULL) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + SSL_SESSION_free(s->psksession); + s->psksession = sesstmp; + + /* + * s->psksession (now our private copy) was built by the callback, not via + * ssl_get_new_session(), so it was never stamped with our own sid_ctx. Do + * so now, to avoid rejection of the PSK session in tls_process_server_hello(). + */ + memcpy(s->psksession->sid_ctx, s->sid_ctx, s->sid_ctx_length); + s->psksession->sid_ctx_length = s->sid_ctx_length; + SSL_SESSION_free(s->session); s->session = s->psksession; s->psksession = NULL; diff --git a/test/sslapitest.c b/test/sslapitest.c index d751385fd2..6fc18a0dc0 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -6546,6 +6546,165 @@ end: return testresult; } +/* + * A server with SSL_VERIFY_PEER set but no session ID context configured + * must still accept a TLS 1.3 external PSK connection: the session was + * just resolved via the application's own callback for this identity, not + * read back out of a shared cache, so the sid_ctx check that guards + * against cross-context cache reuse does not apply to it. + */ +static int test_tls13_psk_verify_peer_no_sid_ctx(void) +{ + SSL_CTX *sctx = NULL, *cctx = NULL; + SSL *serverssl = NULL, *clientssl = NULL; + int testresult = 0; + + if (!TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(), + TLS_client_method(), TLS1_VERSION, 0, &sctx, &cctx, NULL, NULL)) + || !TEST_true(SSL_CTX_set_ciphersuites(cctx, "TLS_AES_128_GCM_SHA256"))) + goto end; + + SSL_CTX_set_verify(sctx, SSL_VERIFY_PEER, NULL); + + SSL_CTX_set_psk_use_session_callback(cctx, use_session_cb); + SSL_CTX_set_psk_find_session_callback(sctx, find_session_cb); + srvid = pskid; + use_session_cb_cnt = 0; + find_session_cb_cnt = 0; + + if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl, + NULL, NULL))) + goto end; + + clientpsk = create_a_psk(clientssl, SHA256_DIGEST_LENGTH); + if (!TEST_ptr(clientpsk) || !TEST_true(SSL_SESSION_up_ref(clientpsk))) + goto end; + serverpsk = clientpsk; + + if (!TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)) + || !TEST_true(SSL_session_reused(clientssl)) + || !TEST_true(SSL_session_reused(serverssl))) + goto end; + + testresult = 1; +end: + SSL_SESSION_free(clientpsk); + SSL_SESSION_free(serverpsk); + clientpsk = serverpsk = NULL; + SSL_free(serverssl); + SSL_free(clientssl); + SSL_CTX_free(sctx); + SSL_CTX_free(cctx); + return testresult; +} + +/* + * A server with SSL_VERIFY_PEER set but no session ID context configured + * must not issue a session ticket after a full (non-PSK) handshake: any + * such ticket would be a poison pill, since resuming it would hit exactly + * the sid_ctx check that a fresh external PSK is exempted from above. + */ +static int test_tls13_psk_verify_peer_no_ticket(void) +{ + SSL_CTX *sctx = NULL, *cctx = NULL; + SSL *serverssl = NULL, *clientssl = NULL; + SSL_SESSION *sess = NULL; + int testresult = 0; + + if (!TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(), + TLS_client_method(), TLS1_VERSION, 0, &sctx, &cctx, cert, privkey))) + goto end; + + SSL_CTX_set_verify(sctx, SSL_VERIFY_PEER, NULL); + + if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl, + NULL, NULL)) + || !TEST_true(create_ssl_connection(serverssl, clientssl, + SSL_ERROR_NONE))) + goto end; + + sess = SSL_get1_session(clientssl); + if (!TEST_ptr(sess) || !TEST_false(SSL_SESSION_has_ticket(sess))) + goto end; + + testresult = 1; +end: + SSL_SESSION_free(sess); + SSL_free(serverssl); + SSL_free(clientssl); + SSL_CTX_free(sctx); + SSL_CTX_free(cctx); + return testresult; +} + +/* + * A client with its own session ID context configured must still be able + * to resume a TLS 1.3 external PSK obtained via the legacy + * psk_use_session_cb()/psk_client_callback() callbacks. s->psksession is + * never routed through ssl_get_new_session(), so, unlike an ordinary + * session, it was never stamped with the client's own sid_ctx; without + * that stamp tls_process_server_hello()'s own sid_ctx self-consistency + * check fatally rejects marking it reused. + * + * Test 0: new style callback (psk_use_session_cb()/psk_find_session_cb()). + * Test 1: old style callback (psk_client_callback()/psk_server_callback()). + */ +static int test_tls13_psk_client_sid_ctx(int idx) +{ + SSL_CTX *sctx = NULL, *cctx = NULL; + SSL *serverssl = NULL, *clientssl = NULL; + int sess_id_ctx = 1; + int testresult = 0; + + if (!TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(), + TLS_client_method(), TLS1_VERSION, 0, &sctx, &cctx, NULL, NULL)) + || !TEST_true(SSL_CTX_set_ciphersuites(cctx, "TLS_AES_128_GCM_SHA256")) + || !TEST_true(SSL_CTX_set_session_id_context(cctx, + (void *)&sess_id_ctx, sizeof(sess_id_ctx)))) + goto end; + + srvid = pskid; + if (idx == 0) { + SSL_CTX_set_psk_use_session_callback(cctx, use_session_cb); + SSL_CTX_set_psk_find_session_callback(sctx, find_session_cb); + use_session_cb_cnt = 0; + find_session_cb_cnt = 0; + } +#ifndef OPENSSL_NO_PSK + else { + SSL_CTX_set_psk_client_callback(cctx, psk_client_cb); + SSL_CTX_set_psk_server_callback(sctx, psk_server_cb); + psk_client_cb_cnt = 0; + psk_server_cb_cnt = 0; + } +#endif + + if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl, + NULL, NULL))) + goto end; + + clientpsk = create_a_psk(clientssl, SHA256_DIGEST_LENGTH); + if (!TEST_ptr(clientpsk) || !TEST_true(SSL_SESSION_up_ref(clientpsk))) + goto end; + serverpsk = clientpsk; + + if (!TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)) + || !TEST_true(SSL_session_reused(clientssl)) + || !TEST_true(SSL_session_reused(serverssl))) + goto end; + + testresult = 1; +end: + SSL_SESSION_free(clientpsk); + SSL_SESSION_free(serverpsk); + clientpsk = serverpsk = NULL; + SSL_free(serverssl); + SSL_free(clientssl); + SSL_CTX_free(sctx); + SSL_CTX_free(cctx); + return testresult; +} + #ifndef OSSL_NO_USABLE_TLS1_3 /* * Test TLS1.3 connection establishment succeeds with various configurations of @@ -15325,9 +15484,13 @@ int setup_tests(void) ADD_ALL_TESTS(test_tls13_ciphersuite, 4); #ifdef OPENSSL_NO_PSK ADD_ALL_TESTS(test_tls13_psk, 1); + ADD_ALL_TESTS(test_tls13_psk_client_sid_ctx, 1); #else ADD_ALL_TESTS(test_tls13_psk, 4); + ADD_ALL_TESTS(test_tls13_psk_client_sid_ctx, 2); #endif /* OPENSSL_NO_PSK */ + ADD_TEST(test_tls13_psk_verify_peer_no_sid_ctx); + ADD_TEST(test_tls13_psk_verify_peer_no_ticket); #ifndef OSSL_NO_USABLE_TLS1_3 ADD_ALL_TESTS(test_tls13_no_dhe_kex, 8); #endif /* OSSL_NO_USABLE_TLS1_3 */ From 9f8231027bcc9a895537fcab4e0a2e65afc9befc Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Thu, 16 Jul 2026 05:36:42 +1000 Subject: [PATCH 335/349] CHANGES.md updates Reviewed-by: Bob Beck Reviewed-by: Norbert Pocs MergeDate: Tue Jul 21 14:12:07 2026 (Merged from https://github.com/openssl/openssl/pull/31964) --- CHANGES.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 6fb71f7d9c..8d507e4f32 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -31,7 +31,17 @@ OpenSSL Releases ### Changes between 4.0 and 4.1 [xx XXX xxxx] - * Added AVX512 optimized SHAKE x4 operations for ML-DSA on x86_64. + * Fixed TLS 1.3 external PSK connections being wrongly rejected when + the client sets a non-empty session ID context. + + *Viktor Dukhovni* + + * Fixed a TLS 1.3 server with no session ID context to accept external PSK + connections and to stop issuing unusable session tickets. + + *Viktor Dukhovni* + + * Added AVX512 optimized SHAKE x4 operations for ML-DSA on `x86_64`. *Marcel Cornu and Tomasz Kantecki* From ba2f3b557a660cbe2a0fc0e99b1830cb930a090f Mon Sep 17 00:00:00 2001 From: Todd Short Date: Fri, 17 Jul 2026 17:34:11 -0400 Subject: [PATCH 336/349] errors: fix stale string overrides in openssl.txt for renamed SSL error codes Commit b3d26e438a ("merge feature/removesslv3", PR #29338) renamed SSL3 error codes to TLS equivalents and updated the string descriptions in crypto/ssl_err.c (e.g. from "ssl/tls alert bad certificate" to "tls alert bad certificate"), but did not update the corresponding strings in crypto/err/openssl.txt. This caused a latent divergence: running mkerr.pl -rebuild would regenerate ssl_err.c with the old openssl.txt strings, undoing the intentional name cleanup. Similarly, adding any new SSL error code triggers a full SSL regeneration that would revert the strings and reorder entries to their correct alphabetical positions, producing spurious changes unrelated to the new code. Update openssl.txt to match the strings already in ssl_err.c, which also happen to be the auto-generated strings derived from the reason code names. Regenerate ssl_err.c and sslerr.h with the correct alphabetical ordering so that future make update runs are idempotent: adding and removing a new SSL error code now produces only the expected changes to the generated files, with no side-effect reordering or string reversions. Fixes: b3d26e438a ("Rename SSL3 error codes to TLS equivalents") Signed-off-by: Todd Short Reviewed-by: Tim Hudson Reviewed-by: Paul Dale MergeDate: Tue Jul 21 14:28:22 2026 (Merged from https://github.com/openssl/openssl/pull/31993) --- crypto/err/openssl.txt | 31 ++++++++++----------- crypto/ssl_err.c | 60 ++++++++++++++++++++-------------------- crypto/sslerr.h | 2 +- include/openssl/sslerr.h | 30 ++++++++++---------- 4 files changed, 61 insertions(+), 62 deletions(-) diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index 14658d7634..8b6c912977 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -1645,25 +1645,24 @@ SSL_R_TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE:1113:\ SSL_R_TLSV1_CERTIFICATE_UNOBTAINABLE:1111:tlsv1 certificate unobtainable SSL_R_TLSV1_UNRECOGNIZED_NAME:1112:tlsv1 unrecognized name SSL_R_TLSV1_UNSUPPORTED_EXTENSION:1110:tlsv1 unsupported extension -SSL_R_TLS_ALERT_BAD_CERTIFICATE:1042:ssl/tls alert bad certificate -SSL_R_TLS_ALERT_BAD_RECORD_MAC:1020:ssl/tls alert bad record mac -SSL_R_TLS_ALERT_CERTIFICATE_EXPIRED:1045:ssl/tls alert certificate expired -SSL_R_TLS_ALERT_CERTIFICATE_REVOKED:1044:ssl/tls alert certificate revoked -SSL_R_TLS_ALERT_CERTIFICATE_UNKNOWN:1046:ssl/tls alert certificate unknown -SSL_R_TLS_ALERT_DECOMPRESSION_FAILURE:1030:ssl/tls alert decompression failure -SSL_R_TLS_ALERT_HANDSHAKE_FAILURE:1040:ssl/tls alert handshake failure -SSL_R_TLS_ALERT_ILLEGAL_PARAMETER:1047:ssl/tls alert illegal parameter -SSL_R_TLS_ALERT_NO_CERTIFICATE:1041:ssl/tls alert no certificate -SSL_R_TLS_ALERT_UNEXPECTED_MESSAGE:1010:ssl/tls alert unexpected message -SSL_R_TLS_ALERT_UNSUPPORTED_CERTIFICATE:1043:\ - ssl/tls alert unsupported certificate +SSL_R_TLS_ALERT_BAD_CERTIFICATE:1042:tls alert bad certificate +SSL_R_TLS_ALERT_BAD_RECORD_MAC:1020:tls alert bad record mac +SSL_R_TLS_ALERT_CERTIFICATE_EXPIRED:1045:tls alert certificate expired +SSL_R_TLS_ALERT_CERTIFICATE_REVOKED:1044:tls alert certificate revoked +SSL_R_TLS_ALERT_CERTIFICATE_UNKNOWN:1046:tls alert certificate unknown +SSL_R_TLS_ALERT_DECOMPRESSION_FAILURE:1030:tls alert decompression failure +SSL_R_TLS_ALERT_HANDSHAKE_FAILURE:1040:tls alert handshake failure +SSL_R_TLS_ALERT_ILLEGAL_PARAMETER:1047:tls alert illegal parameter +SSL_R_TLS_ALERT_NO_CERTIFICATE:1041:tls alert no certificate +SSL_R_TLS_ALERT_UNEXPECTED_MESSAGE:1010:tls alert unexpected message +SSL_R_TLS_ALERT_UNSUPPORTED_CERTIFICATE:1043:tls alert unsupported certificate SSL_R_TLS_EXT_INVALID_MAX_FRAGMENT_LENGTH:232:\ - ssl3 ext invalid max fragment length -SSL_R_TLS_EXT_INVALID_SERVERNAME:319:ssl3 ext invalid servername -SSL_R_TLS_EXT_INVALID_SERVERNAME_TYPE:320:ssl3 ext invalid servername type + tls ext invalid max fragment length +SSL_R_TLS_EXT_INVALID_SERVERNAME:319:tls ext invalid servername +SSL_R_TLS_EXT_INVALID_SERVERNAME_TYPE:320:tls ext invalid servername type SSL_R_TLS_ILLEGAL_EXPORTER_LABEL:367:tls illegal exporter label SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST:157:tls invalid ecpointformat list -SSL_R_TLS_SESSION_ID_TOO_LONG:300:ssl3 session id too long +SSL_R_TLS_SESSION_ID_TOO_LONG:300:tls session id too long SSL_R_TOO_MANY_KEY_UPDATES:132:too many key updates SSL_R_TOO_MANY_WARN_ALERTS:409:too many warn alerts SSL_R_TOO_MUCH_EARLY_DATA:164:too much early data diff --git a/crypto/ssl_err.c b/crypto/ssl_err.c index 0dca54bb1d..9766b64314 100644 --- a/crypto/ssl_err.c +++ b/crypto/ssl_err.c @@ -426,36 +426,6 @@ static const ERR_STRING_DATA SSL_str_reasons[] = { "srtp protection profile list too long" }, { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SRTP_UNKNOWN_PROTECTION_PROFILE), "srtp unknown protection profile" }, - { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_EXT_INVALID_MAX_FRAGMENT_LENGTH), - "tls ext invalid max fragment length" }, - { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_EXT_INVALID_SERVERNAME), - "tls ext invalid servername" }, - { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_EXT_INVALID_SERVERNAME_TYPE), - "tls ext invalid servername type" }, - { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_SESSION_ID_TOO_LONG), - "tls session id too long" }, - { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ALERT_BAD_CERTIFICATE), - "tls alert bad certificate" }, - { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ALERT_BAD_RECORD_MAC), - "tls alert bad record mac" }, - { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ALERT_CERTIFICATE_EXPIRED), - "tls alert certificate expired" }, - { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ALERT_CERTIFICATE_REVOKED), - "tls alert certificate revoked" }, - { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ALERT_CERTIFICATE_UNKNOWN), - "tls alert certificate unknown" }, - { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ALERT_DECOMPRESSION_FAILURE), - "tls alert decompression failure" }, - { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ALERT_HANDSHAKE_FAILURE), - "tls alert handshake failure" }, - { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ALERT_ILLEGAL_PARAMETER), - "tls alert illegal parameter" }, - { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ALERT_NO_CERTIFICATE), - "tls alert no certificate" }, - { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ALERT_UNEXPECTED_MESSAGE), - "tls alert unexpected message" }, - { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ALERT_UNSUPPORTED_CERTIFICATE), - "tls alert unsupported certificate" }, { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_COMMAND_SECTION_EMPTY), "ssl command section empty" }, { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_COMMAND_SECTION_NOT_FOUND), @@ -534,10 +504,40 @@ static const ERR_STRING_DATA SSL_str_reasons[] = { "tlsv1 unrecognized name" }, { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_UNSUPPORTED_EXTENSION), "tlsv1 unsupported extension" }, + { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ALERT_BAD_CERTIFICATE), + "tls alert bad certificate" }, + { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ALERT_BAD_RECORD_MAC), + "tls alert bad record mac" }, + { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ALERT_CERTIFICATE_EXPIRED), + "tls alert certificate expired" }, + { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ALERT_CERTIFICATE_REVOKED), + "tls alert certificate revoked" }, + { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ALERT_CERTIFICATE_UNKNOWN), + "tls alert certificate unknown" }, + { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ALERT_DECOMPRESSION_FAILURE), + "tls alert decompression failure" }, + { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ALERT_HANDSHAKE_FAILURE), + "tls alert handshake failure" }, + { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ALERT_ILLEGAL_PARAMETER), + "tls alert illegal parameter" }, + { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ALERT_NO_CERTIFICATE), + "tls alert no certificate" }, + { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ALERT_UNEXPECTED_MESSAGE), + "tls alert unexpected message" }, + { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ALERT_UNSUPPORTED_CERTIFICATE), + "tls alert unsupported certificate" }, + { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_EXT_INVALID_MAX_FRAGMENT_LENGTH), + "tls ext invalid max fragment length" }, + { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_EXT_INVALID_SERVERNAME), + "tls ext invalid servername" }, + { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_EXT_INVALID_SERVERNAME_TYPE), + "tls ext invalid servername type" }, { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ILLEGAL_EXPORTER_LABEL), "tls illegal exporter label" }, { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST), "tls invalid ecpointformat list" }, + { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_SESSION_ID_TOO_LONG), + "tls session id too long" }, { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TOO_MANY_KEY_UPDATES), "too many key updates" }, { ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TOO_MANY_WARN_ALERTS), diff --git a/crypto/sslerr.h b/crypto/sslerr.h index 968f27b00a..f2936b7d99 100644 --- a/crypto/sslerr.h +++ b/crypto/sslerr.h @@ -1,6 +1,6 @@ /* * Generated by util/mkerr.pl DO NOT EDIT - * Copyright 2020-2025 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2020-2026 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/include/openssl/sslerr.h b/include/openssl/sslerr.h index 28e3886169..6ae701dba0 100644 --- a/include/openssl/sslerr.h +++ b/include/openssl/sslerr.h @@ -269,21 +269,6 @@ #define SSL_R_SRTP_COULD_NOT_ALLOCATE_PROFILES 362 #define SSL_R_SRTP_PROTECTION_PROFILE_LIST_TOO_LONG 363 #define SSL_R_SRTP_UNKNOWN_PROTECTION_PROFILE 364 -#define SSL_R_TLS_EXT_INVALID_MAX_FRAGMENT_LENGTH 232 -#define SSL_R_TLS_EXT_INVALID_SERVERNAME 319 -#define SSL_R_TLS_EXT_INVALID_SERVERNAME_TYPE 320 -#define SSL_R_TLS_SESSION_ID_TOO_LONG 300 -#define SSL_R_TLS_ALERT_BAD_CERTIFICATE 1042 -#define SSL_R_TLS_ALERT_BAD_RECORD_MAC 1020 -#define SSL_R_TLS_ALERT_CERTIFICATE_EXPIRED 1045 -#define SSL_R_TLS_ALERT_CERTIFICATE_REVOKED 1044 -#define SSL_R_TLS_ALERT_CERTIFICATE_UNKNOWN 1046 -#define SSL_R_TLS_ALERT_DECOMPRESSION_FAILURE 1030 -#define SSL_R_TLS_ALERT_HANDSHAKE_FAILURE 1040 -#define SSL_R_TLS_ALERT_ILLEGAL_PARAMETER 1047 -#define SSL_R_TLS_ALERT_NO_CERTIFICATE 1041 -#define SSL_R_TLS_ALERT_UNEXPECTED_MESSAGE 1010 -#define SSL_R_TLS_ALERT_UNSUPPORTED_CERTIFICATE 1043 #define SSL_R_SSL_COMMAND_SECTION_EMPTY 117 #define SSL_R_SSL_COMMAND_SECTION_NOT_FOUND 125 #define SSL_R_SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION 228 @@ -326,8 +311,23 @@ #define SSL_R_TLSV1_CERTIFICATE_UNOBTAINABLE 1111 #define SSL_R_TLSV1_UNRECOGNIZED_NAME 1112 #define SSL_R_TLSV1_UNSUPPORTED_EXTENSION 1110 +#define SSL_R_TLS_ALERT_BAD_CERTIFICATE 1042 +#define SSL_R_TLS_ALERT_BAD_RECORD_MAC 1020 +#define SSL_R_TLS_ALERT_CERTIFICATE_EXPIRED 1045 +#define SSL_R_TLS_ALERT_CERTIFICATE_REVOKED 1044 +#define SSL_R_TLS_ALERT_CERTIFICATE_UNKNOWN 1046 +#define SSL_R_TLS_ALERT_DECOMPRESSION_FAILURE 1030 +#define SSL_R_TLS_ALERT_HANDSHAKE_FAILURE 1040 +#define SSL_R_TLS_ALERT_ILLEGAL_PARAMETER 1047 +#define SSL_R_TLS_ALERT_NO_CERTIFICATE 1041 +#define SSL_R_TLS_ALERT_UNEXPECTED_MESSAGE 1010 +#define SSL_R_TLS_ALERT_UNSUPPORTED_CERTIFICATE 1043 +#define SSL_R_TLS_EXT_INVALID_MAX_FRAGMENT_LENGTH 232 +#define SSL_R_TLS_EXT_INVALID_SERVERNAME 319 +#define SSL_R_TLS_EXT_INVALID_SERVERNAME_TYPE 320 #define SSL_R_TLS_ILLEGAL_EXPORTER_LABEL 367 #define SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST 157 +#define SSL_R_TLS_SESSION_ID_TOO_LONG 300 #define SSL_R_TOO_MANY_KEY_UPDATES 132 #define SSL_R_TOO_MANY_WARN_ALERTS 409 #define SSL_R_TOO_MUCH_EARLY_DATA 164 From eac8ed4825213a80644bb5ea15234a59daa57352 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 17 Jul 2026 13:04:29 -0400 Subject: [PATCH 337/349] Add return check to OSSL_DECODER_up_ref Coverity flagged a location where we didn't check this return code, fix it up. Fixes https://scan5.scan.coverity.com/#/project-view/60762/10222?selectedIssue=1696759 Reviewed-by: Milan Broz Reviewed-by: Andrew Dinh Reviewed-by: Nikola Pajkovsky Reviewed-by: Norbert Pocs MergeDate: Thu Jul 23 08:24:22 2026 (Merged from https://github.com/openssl/openssl/pull/31992) --- crypto/encode_decode/decoder_meth.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crypto/encode_decode/decoder_meth.c b/crypto/encode_decode/decoder_meth.c index 03118f5843..632d20c996 100644 --- a/crypto/encode_decode/decoder_meth.c +++ b/crypto/encode_decode/decoder_meth.c @@ -415,7 +415,10 @@ inner_ossl_decoder_fetch(struct decoder_data_st *methdata, * lives beyond the freeing of that tmp_store */ #ifndef OPENSSL_NO_CACHED_FETCH - OSSL_DECODER_up_ref((OSSL_DECODER *)method); + if (!OSSL_DECODER_up_ref((OSSL_DECODER *)method)) { + ossl_decoder_free(method); + method = NULL; + } #endif } } From 9375418a8758b9229182da5d5922342c08bc7c74 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 17 Jul 2026 14:22:50 -0400 Subject: [PATCH 338/349] use safe_math on do_name_ex Coverity caught some potential overflows in do_name_ex. Seems unlikely to occur, but lets just do all the adds under safe_math to ensure we don't overflow our buffer length. Fixes https://scan5.scan.coverity.com/#/project-view/60762/10222?selectedIssue=1695278 Reviewed-by: Milan Broz Reviewed-by: Andrew Dinh Reviewed-by: Nikola Pajkovsky Reviewed-by: Norbert Pocs MergeDate: Thu Jul 23 08:24:23 2026 (Merged from https://github.com/openssl/openssl/pull/31992) --- crypto/asn1/a_strex.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/crypto/asn1/a_strex.c b/crypto/asn1/a_strex.c index 57a14f6f90..4315355632 100644 --- a/crypto/asn1/a_strex.c +++ b/crypto/asn1/a_strex.c @@ -12,6 +12,7 @@ #include "internal/cryptlib.h" #include "internal/sizes.h" #include "internal/unicode.h" +#include "internal/safe_math.h" #include "crypto/asn1.h" #include #include @@ -31,6 +32,8 @@ #define ESC_FLAGS (ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_2254 | ASN1_STRFLGS_ESC_QUOTE | ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB) +OSSL_SAFE_MATH_SIGNED(int, int) + /* * Three IO functions for sending data to memory, a BIO and a FILE * pointer. @@ -438,6 +441,7 @@ static int do_name_ex(char_io *io_ch, void *arg, const X509_NAME *n, char objtmp[80]; const char *objbuf; int outlen, len; + int err = 0; char *sep_dn, *sep_mv, *sep_eq; int sep_dn_len, sep_mv_len, sep_eq_len; if (indent < 0) @@ -501,14 +505,20 @@ static int do_name_ex(char_io *io_ch, void *arg, const X509_NAME *n, if (prev == X509_NAME_ENTRY_set(ent)) { if (!io_ch(arg, sep_mv, sep_mv_len)) return -1; - outlen += sep_mv_len; + outlen = safe_add_int(outlen, sep_mv_len, &err); + if (err != 0) + return -1; } else { if (!io_ch(arg, sep_dn, sep_dn_len)) return -1; - outlen += sep_dn_len; + outlen = safe_add_int(outlen, sep_dn_len, &err); + if (err != 0) + return -1; if (!do_indent(io_ch, arg, indent)) return -1; - outlen += indent; + outlen = safe_add_int(outlen, indent, &err); + if (err != 0) + return -1; } } prev = X509_NAME_ENTRY_set(ent); @@ -539,11 +549,18 @@ static int do_name_ex(char_io *io_ch, void *arg, const X509_NAME *n, if ((objlen < fld_len) && (flags & XN_FLAG_FN_ALIGN)) { if (!do_indent(io_ch, arg, fld_len - objlen)) return -1; - outlen += fld_len - objlen; + outlen = safe_add_int(outlen, fld_len - objlen, &err); + if (err != 0) + return -1; } if (!io_ch(arg, sep_eq, sep_eq_len)) return -1; - outlen += objlen + sep_eq_len; + outlen = safe_add_int(outlen, objlen, &err); + if (err != 0) + return -1; + outlen = safe_add_int(outlen, sep_eq_len, &err); + if (err != 0) + return -1; } /* * If the field name is unknown then fix up the DER dump flag. We @@ -558,7 +575,9 @@ static int do_name_ex(char_io *io_ch, void *arg, const X509_NAME *n, len = do_print_ex(io_ch, arg, flags | orflags, val); if (len < 0) return -1; - outlen += len; + outlen = safe_add_int(outlen, len, &err); + if (err != 0) + return -1; } return outlen; } From 05b241cb094941c7f83ebd94d5516648d82bc2d9 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Tue, 21 Jul 2026 14:52:59 -0400 Subject: [PATCH 339/349] Fix missing NULL check in statem_clnt_construct_test Fixes https://scan5.scan.coverity.com/#/project-view/60762/10222?selectedIssue=1695905 Reviewed-by: Milan Broz Reviewed-by: Andrew Dinh Reviewed-by: Nikola Pajkovsky Reviewed-by: Norbert Pocs MergeDate: Thu Jul 23 08:24:25 2026 (Merged from https://github.com/openssl/openssl/pull/31992) --- test/statem_clnt_construct_test.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/statem_clnt_construct_test.c b/test/statem_clnt_construct_test.c index 10f58a3ab1..350e59ce2f 100644 --- a/test/statem_clnt_construct_test.c +++ b/test/statem_clnt_construct_test.c @@ -129,7 +129,8 @@ static int finish_msg(SSL *ssl, WPACKET *pkt, int mt, unsigned char **msg, { SSL_CONNECTION *s = SSL_CONNECTION_FROM_SSL(ssl); - if (!TEST_true(ssl_close_construct_packet(s, pkt, mt)) + if (!TEST_ptr(s) + || !TEST_true(ssl_close_construct_packet(s, pkt, mt)) || !TEST_true(WPACKET_get_total_written(pkt, msglen)) || !TEST_true(WPACKET_finish(pkt))) return 0; From e7a8d9c19ea933df2f4f97600c94aa40b14b4f93 Mon Sep 17 00:00:00 2001 From: Alexander Nesterovskiy <15937600+nskyav@users.noreply.github.com> Date: Fri, 15 May 2026 12:09:33 +0000 Subject: [PATCH 340/349] Added Nvidia/Olympus CPU dispatch for SHA3/EOR3 AES GCM on Vera CPU CLA: trivial Reviewed-by: Milan Broz Reviewed-by: Andrew Dinh Reviewed-by: Bob Beck MergeDate: Thu Jul 23 08:26:09 2026 (Merged from https://github.com/openssl/openssl/pull/31191) --- crypto/armcap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crypto/armcap.c b/crypto/armcap.c index 31b17f06a5..f4f6b0d7f9 100644 --- a/crypto/armcap.c +++ b/crypto/armcap.c @@ -429,7 +429,8 @@ void OPENSSL_cpuid_setup(void) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_N3) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_V3_AE) || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_ARM, ARM_CPU_PART_V3) - || MIDR_IMPLEMENTER(OPENSSL_arm_midr) == ARM_CPU_IMP_AMPERE)) + || MIDR_IMPLEMENTER(OPENSSL_arm_midr) == ARM_CPU_IMP_AMPERE + || MIDR_IS_CPU_MODEL(OPENSSL_arm_midr, ARM_CPU_IMP_NVIDIA, NVIDIA_CPU_PART_OLYMPUS))) OPENSSL_armcap_P |= ARMV8_UNROLL8_EOR3; if ((OPENSSL_armcap_P & ARMV8_SHA3) From f7112c925c0f3f6239bcab218cb32fa3e52bf621 Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Sat, 18 Jul 2026 11:11:26 +0900 Subject: [PATCH 341/349] docs: clarify EVP_PKEY context workflow and operation descriptions Document that a generated key is returned separately from its generation context and that a new key-bound context is needed for subsequent operations. Correct the encrypt and decrypt initialization descriptions and examples accordingly. Correct two copy-and-paste errors in the verify-recover documentation: describe its initializer as starting a verify-recover operation and refer to EVP_PKEY_verify_recover() when discussing its input. Fixes #31989 Assisted-by: Grok:Grok-4.5 Reviewed-by: Tomas Mraz Reviewed-by: Dmitry Belyavskiy MergeDate: Thu Jul 23 08:29:24 2026 (Merged from https://github.com/openssl/openssl/pull/31995) --- doc/man3/EVP_PKEY_CTX_new.pod | 5 ++++- doc/man3/EVP_PKEY_decrypt.pod | 15 ++++++++------- doc/man3/EVP_PKEY_encrypt.pod | 15 ++++++++------- doc/man3/EVP_PKEY_keygen.pod | 20 +++++++++++++++++--- doc/man3/EVP_PKEY_verify_recover.pod | 14 +++++++------- 5 files changed, 44 insertions(+), 25 deletions(-) diff --git a/doc/man3/EVP_PKEY_CTX_new.pod b/doc/man3/EVP_PKEY_CTX_new.pod index fff102a769..9ded13de5d 100644 --- a/doc/man3/EVP_PKEY_CTX_new.pod +++ b/doc/man3/EVP_PKEY_CTX_new.pod @@ -48,6 +48,9 @@ EVP_PKEY_CTX_new_id() and EVP_PKEY_CTX_new_from_name() are normally used when no B structure is associated with the operations, for example during parameter generation or key generation for some algorithms. +The key returned by L is not associated with the +generation context. To perform operations using that key, create a new context +with L. EVP_PKEY_CTX_dup() duplicates the context I. It is not supported for a keygen operation. @@ -125,7 +128,7 @@ added in OpenSSL 3.0. =head1 COPYRIGHT -Copyright 2006-2025 The OpenSSL Project Authors. All Rights Reserved. +Copyright 2006-2026 The OpenSSL Project Authors. All Rights Reserved. Licensed under the Apache License 2.0 (the "License"). You may not use this file except in compliance with the License. You can obtain a copy diff --git a/doc/man3/EVP_PKEY_decrypt.pod b/doc/man3/EVP_PKEY_decrypt.pod index 5e624e8c61..6b46329793 100644 --- a/doc/man3/EVP_PKEY_decrypt.pod +++ b/doc/man3/EVP_PKEY_decrypt.pod @@ -17,12 +17,13 @@ EVP_PKEY_decrypt - decrypt using a public key algorithm =head1 DESCRIPTION -The EVP_PKEY_decrypt_init() function initializes a public key algorithm -context using key I for a decryption operation. +The EVP_PKEY_decrypt_init() function initializes the public key algorithm +context I for a decryption operation. A key must already be associated +with I; this is normally done by creating it with +L or L. -The EVP_PKEY_decrypt_init_ex() function initializes a public key algorithm -context using key I for a decryption operation and sets the -algorithm specific I. +The EVP_PKEY_decrypt_init_ex() function is the same as +EVP_PKEY_decrypt_init() but additionally sets the algorithm-specific I. The EVP_PKEY_decrypt() function performs a public key decryption operation using I. The data to be decrypted is specified using the I and @@ -88,7 +89,7 @@ Decrypt data using OAEP (for RSA keys): * NB: assumes key, in, inlen are already set up * and that key is an RSA private key */ - ctx = EVP_PKEY_CTX_new(key, NULL); + ctx = EVP_PKEY_CTX_new_from_pkey(NULL, key, NULL); if (!ctx) /* Error occurred */ if (EVP_PKEY_decrypt_init(ctx) <= 0) @@ -125,7 +126,7 @@ These functions were added in OpenSSL 1.0.0. =head1 COPYRIGHT -Copyright 2006-2025 The OpenSSL Project Authors. All Rights Reserved. +Copyright 2006-2026 The OpenSSL Project Authors. All Rights Reserved. Licensed under the Apache License 2.0 (the "License"). You may not use this file except in compliance with the License. You can obtain a copy diff --git a/doc/man3/EVP_PKEY_encrypt.pod b/doc/man3/EVP_PKEY_encrypt.pod index 1fb41f99f4..fda95c218e 100644 --- a/doc/man3/EVP_PKEY_encrypt.pod +++ b/doc/man3/EVP_PKEY_encrypt.pod @@ -17,12 +17,13 @@ EVP_PKEY_encrypt_init, EVP_PKEY_encrypt - encrypt using a public key algorithm =head1 DESCRIPTION -The EVP_PKEY_encrypt_init() function initializes a public key algorithm -context using key B for an encryption operation. +The EVP_PKEY_encrypt_init() function initializes the public key algorithm +context I for an encryption operation. A key must already be associated +with I; this is normally done by creating it with +L or L. -The EVP_PKEY_encrypt_init_ex() function initializes a public key algorithm -context using key B for an encryption operation and sets the -algorithm specific B. +The EVP_PKEY_encrypt_init_ex() function is the same as +EVP_PKEY_encrypt_init() but additionally sets the algorithm-specific I. The EVP_PKEY_encrypt() function performs a public key encryption operation using B. The data to be encrypted is specified using the B and @@ -66,7 +67,7 @@ L for means to load a public key. * NB: assumes key, in, inlen are already set up, * and that key is an RSA public key */ - ctx = EVP_PKEY_CTX_new(key, NULL); + ctx = EVP_PKEY_CTX_new_from_pkey(NULL, key, NULL); if (!ctx) /* Error occurred */ if (EVP_PKEY_encrypt_init(ctx) <= 0) @@ -104,7 +105,7 @@ These functions were added in OpenSSL 1.0.0. =head1 COPYRIGHT -Copyright 2006-2021 The OpenSSL Project Authors. All Rights Reserved. +Copyright 2006-2026 The OpenSSL Project Authors. All Rights Reserved. Licensed under the Apache License 2.0 (the "License"). You may not use this file except in compliance with the License. You can obtain a copy diff --git a/doc/man3/EVP_PKEY_keygen.pod b/doc/man3/EVP_PKEY_keygen.pod index 1d5180ef6f..71daed31c0 100644 --- a/doc/man3/EVP_PKEY_keygen.pod +++ b/doc/man3/EVP_PKEY_keygen.pod @@ -66,6 +66,12 @@ parameters or key are written to I<*ppkey>. If I<*ppkey> is NULL when this function is called, it will be allocated, and should be freed by the caller when no longer useful, using L. +When a key is generated, EVP_PKEY_generate() does not associate it with I +or change I into a context for operations using that key. To use the +generated key, create a new context with L, +passing I<*ppkey>. The generation context can be reused for further generation +operations or freed. + EVP_PKEY_paramgen() and EVP_PKEY_keygen() do exactly the same thing as EVP_PKEY_generate(), after checking that the corresponding EVP_PKEY_paramgen_init() or EVP_PKEY_keygen_init() was used to initialize I. @@ -153,7 +159,7 @@ in functions which require the use of a public key or parameters. =head1 EXAMPLES -Generate a 2048 bit RSA key: +Generate a 2048 bit RSA key, then initialize a context for encryption: #include #include @@ -161,7 +167,7 @@ Generate a 2048 bit RSA key: EVP_PKEY_CTX *ctx; EVP_PKEY *pkey = NULL; - ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); + ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); if (!ctx) /* Error occurred */ if (EVP_PKEY_keygen_init(ctx) <= 0) @@ -170,7 +176,15 @@ Generate a 2048 bit RSA key: /* Error */ /* Generate key */ - if (EVP_PKEY_keygen(ctx, &pkey) <= 0) + if (EVP_PKEY_generate(ctx, &pkey) <= 0) + /* Error */ + + /* ctx is still a generation context; pkey contains the generated key */ + EVP_PKEY_CTX_free(ctx); + ctx = EVP_PKEY_CTX_new_from_pkey(NULL, pkey, NULL); + if (!ctx) + /* Error occurred */ + if (EVP_PKEY_encrypt_init(ctx) <= 0) /* Error */ Generate a key from a set of parameters: diff --git a/doc/man3/EVP_PKEY_verify_recover.pod b/doc/man3/EVP_PKEY_verify_recover.pod index 10084d61c1..52682f2aa5 100644 --- a/doc/man3/EVP_PKEY_verify_recover.pod +++ b/doc/man3/EVP_PKEY_verify_recover.pod @@ -22,10 +22,10 @@ EVP_PKEY_verify_recover_init_ex2, EVP_PKEY_verify_recover =head1 DESCRIPTION EVP_PKEY_verify_recover_init() initializes a public key algorithm context -I for signing using the algorithm given when the context was created -using L or variants thereof. The algorithm is used to -fetch a B method implicitly, see L -for more information about implicit fetches. +I for a verify-recover operation using the algorithm given when the +context was created using L or variants thereof. The +algorithm is used to fetch a B method implicitly, see +L for more information about implicit fetches. EVP_PKEY_verify_recover_init_ex() is the same as EVP_PKEY_verify_recover_init() but additionally sets the passed parameters @@ -35,8 +35,8 @@ EVP_PKEY_verify_recover_init_ex2() is the same as EVP_PKEY_verify_recover_init_e but works with an explicitly fetched B I. A context I without a pre-loaded key cannot be used with this function. Depending on what algorithm was fetched, certain details revolving around the -treatment of the input to EVP_PKEY_verify() may be pre-determined, and in that -case, those details may normally not be changed. +treatment of the input to EVP_PKEY_verify_recover() may be pre-determined, and +in that case, those details may normally not be changed. See L below for a deeper explanation. The EVP_PKEY_verify_recover() function recovers signed data @@ -132,7 +132,7 @@ The EVP_PKEY_verify_recover_init_ex() function was added in OpenSSL 3.0. =head1 COPYRIGHT -Copyright 2013-2024 The OpenSSL Project Authors. All Rights Reserved. +Copyright 2013-2026 The OpenSSL Project Authors. All Rights Reserved. Licensed under the Apache License 2.0 (the "License"). You may not use this file except in compliance with the License. You can obtain a copy From b80761945b62bda5d78110bb4bb39f0fedbef17a Mon Sep 17 00:00:00 2001 From: codingsushi79 Date: Fri, 17 Jul 2026 22:02:19 -0400 Subject: [PATCH 342/349] Fix capitalization of 'Swiss Army knife' in README CLA: trivial Reviewed-by: Andrew Dinh Reviewed-by: Eugene Syromiatnikov Reviewed-by: Nikola Pajkovsky Reviewed-by: Paul Dale MergeDate: Thu Jul 23 08:30:57 2026 (Merged from https://github.com/openssl/openssl/pull/31871) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2701f9004b..fcefc19923 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ The OpenSSL toolkit includes: basis of the TLS implementation, but can also be used independently. - **openssl** - the OpenSSL command line tool, a swiss army knife for cryptographic tasks, + the OpenSSL command line tool, a Swiss Army knife for cryptographic tasks, testing and analyzing. It can be used for - creation of key parameters - creation of X.509 certificates, CSRs and CRLs From 0723430177b6dc5a5c7adc72af843a29ef05080b Mon Sep 17 00:00:00 2001 From: Nicky Mouha Date: Wed, 17 Jun 2026 16:18:49 -0400 Subject: [PATCH 343/349] doc: document that `HMAC_Update` cannot be called after `HMAC_Final` Reviewed-by: Paul Dale Reviewed-by: Frederik Wedel-Heinen MergeDate: Thu Jul 23 08:33:21 2026 (Merged from https://github.com/openssl/openssl/pull/31570) --- doc/man3/HMAC.pod | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/man3/HMAC.pod b/doc/man3/HMAC.pod index 53a3853eb2..d6cd404ab6 100644 --- a/doc/man3/HMAC.pod +++ b/doc/man3/HMAC.pod @@ -112,6 +112,9 @@ be authenticated (I bytes at I). HMAC_Final() places the message authentication code in I, which must have space for the hash function output. +After calling HMAC_Final() no calls to HMAC_Update() or HMAC_Final() can be +made, but HMAC_Init_ex() can be called to initialize a new HMAC +operation. HMAC_CTX_copy() copies all of the internal state from I into I. From 10413cceb66411389bbd65a9e4698dc6a172113c Mon Sep 17 00:00:00 2001 From: Greensi7 Date: Mon, 20 Jul 2026 23:34:08 +0200 Subject: [PATCH 344/349] Add OSSL_ECHSTORE_read_echconfiglist fuzzer Fuzz OSSL_ECHSTORE_read_echconfiglist with raw fuzzer input in stage 1. Then fixup the starting bytes to reach deeper code withotu relying only on corpus. Fixup is done for binary encoding only (not base64). Assisted-by: ChatGPT:gpt-5.5 Reviewed-by: Milan Broz Reviewed-by: Nikola Pajkovsky Reviewed-by: Norbert Pocs MergeDate: Thu Jul 23 13:57:54 2026 (Merged from https://github.com/openssl/openssl/pull/32021) --- fuzz/build.info | 16 ++++ fuzz/echconfiglist_parser.c | 88 +++++++++++++++++++ .../99-test_fuzz_echconfiglist_parser.t | 25 ++++++ 3 files changed, 129 insertions(+) create mode 100644 fuzz/echconfiglist_parser.c create mode 100644 test/recipes/99-test_fuzz_echconfiglist_parser.t diff --git a/fuzz/build.info b/fuzz/build.info index 5c410260e4..fcbb76234c 100644 --- a/fuzz/build.info +++ b/fuzz/build.info @@ -50,6 +50,10 @@ IF[{- !$disabled{"fuzz-afl"} || !$disabled{"fuzz-libfuzzer"} -}] PROGRAMS{noinst}=dtlsclient dtlsserver ENDIF + IF[{- !$disabled{"ech"} -}] + PROGRAMS{noinst}=echconfiglist_parser + ENDIF + SOURCE[asn1]=asn1.c driver.c fuzz_rand.c INCLUDE[asn1]=../include {- $ex_inc -} DEPEND[asn1]=../libcrypto ../libssl {- $ex_lib -} @@ -106,6 +110,10 @@ IF[{- !$disabled{"fuzz-afl"} || !$disabled{"fuzz-libfuzzer"} -}] INCLUDE[dtlsserver]=../include {- $ex_inc -} DEPEND[dtlsserver]=../libcrypto ../libssl {- $ex_lib -} + SOURCE[echconfiglist_parser]=echconfiglist_parser.c driver.c + INCLUDE[echconfiglist_parser]=../include {- $ex_inc -} + DEPEND[echconfiglist_parser]=../libcrypto ../libssl {- $ex_lib -} + SOURCE[pem]=pem.c driver.c INCLUDE[pem]=../include {- $ex_inc -} DEPEND[pem]=../libcrypto.a {- $ex_lib -} @@ -220,6 +228,10 @@ IF[{- !$disabled{tests} -}] PROGRAMS{noinst}=dtlsclient-test dtlsserver-test ENDIF + IF[{- !$disabled{"ech"} -}] + PROGRAMS{noinst}=echconfiglist_parser-test + ENDIF + SOURCE[asn1-test]=asn1.c $FUZZTESTSRC fuzz_rand.c INCLUDE[asn1-test]=../include ../test/mfail DEPEND[asn1-test]=../libcrypto.a ../libssl.a @@ -288,6 +300,10 @@ IF[{- !$disabled{tests} -}] INCLUDE[dtlsserver-test]=../include ../test/mfail DEPEND[dtlsserver-test]=../libcrypto.a ../libssl.a + SOURCE[echconfiglist_parser-test]=echconfiglist_parser.c $FUZZTESTSRC + INCLUDE[echconfiglist_parser-test]=../include ../test/mfail + DEPEND[echconfiglist_parser-test]=../libcrypto.a ../libssl.a + SOURCE[pem-test]=pem.c $FUZZTESTSRC INCLUDE[pem-test]=../include ../test/mfail DEPEND[pem-test]=../libcrypto.a diff --git a/fuzz/echconfiglist_parser.c b/fuzz/echconfiglist_parser.c new file mode 100644 index 0000000000..fce416ec4d --- /dev/null +++ b/fuzz/echconfiglist_parser.c @@ -0,0 +1,88 @@ +/* + * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * https://www.openssl.org/source/license.html + * or in the file LICENSE in the source distribution. + */ +#include +#include +#include +#include +#include +#include +#include +#include "fuzzer.h" + +static void parse_one(const uint8_t *buf, int len) +{ + OSSL_ECHSTORE *es; + BIO *in; + + es = OSSL_ECHSTORE_new(NULL, NULL); + if (es == NULL) + return; + + in = BIO_new_mem_buf(buf, len); + if (in == NULL) { + OSSL_ECHSTORE_free(es); + return; + } + + OSSL_ECHSTORE_read_echconfiglist(es, in); + + OSSL_ECHSTORE_free(es); + BIO_free(in); +} + +int FuzzerInitialize(int *argc, char ***argv) +{ + return 1; +} + +int FuzzerTestOneInput(const uint8_t *buf, size_t len) +{ + uint8_t *fixed_buf = NULL; + int bio_len; + uint16_t outer_len, inner_len; + + if (len > INT_MAX) + return 0; + bio_len = (int)len; + + /* Target raw without any fixup */ + parse_one(buf, bio_len); + + /* + * ech_decode_and_flatten has a strict size check: + * OSSL_ECH_MIN_ECHCONFIG_LEN = 32 + * OSSL_ECH_MAX_ECHCONFIG_LEN = 1500 + */ + if (len < OSSL_ECH_MIN_ECHCONFIG_LEN || len >= OSSL_ECH_MAX_ECHCONFIG_LEN) + goto end; + outer_len = (uint16_t)(len - 2); + inner_len = (uint16_t)(len - 6); + + fixed_buf = OPENSSL_memdup(buf, len); + if (fixed_buf == NULL) + goto end; + + /* Fix up to pass initial checks*/ + OPENSSL_store_u16_be(fixed_buf, outer_len); + OPENSSL_store_u16_be(fixed_buf + 2, OSSL_ECH_RFC9849_VERSION); + OPENSSL_store_u16_be(fixed_buf + 4, inner_len); + + parse_one(fixed_buf, bio_len); + +end: + OPENSSL_free(fixed_buf); + ERR_clear_error(); + + return 0; +} + +void FuzzerCleanup(void) +{ +} diff --git a/test/recipes/99-test_fuzz_echconfiglist_parser.t b/test/recipes/99-test_fuzz_echconfiglist_parser.t new file mode 100644 index 0000000000..09e115caa6 --- /dev/null +++ b/test/recipes/99-test_fuzz_echconfiglist_parser.t @@ -0,0 +1,25 @@ +#!/usr/bin/env perl +# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +use strict; +use warnings; + +use OpenSSL::Test qw/:DEFAULT srctop_file/; +use OpenSSL::Test::Utils; + +my $fuzzer = "echconfiglist_parser"; +setup("test_fuzz_${fuzzer}"); + +plan skip_all => "This test requires ech support" + if disabled("ech"); + +plan tests => 2; # one more due to below require_ok(...) + +require_ok(srctop_file('test','recipes','fuzz.pl')); + +fuzz_ok($fuzzer); From 4499e9abe162d6ef7a6600cd6573681572e431a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20M=C3=BCllner?= Date: Fri, 10 Apr 2026 02:22:52 +0200 Subject: [PATCH 345/349] crypto: use void * in RISC-V digest shims MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The RISC-V MD5 and SHA dispatch files use low-level digest context types that are hidden in no-deprecated builds. Switch their shim entry points to void * so they no longer depend on deprecated typed interfaces. Update the local MD5 declaration to match the untyped shim interface. Fixes: openssl/openssl#29357 Signed-off-by: Christoph Müllner Reviewed-by: Milan Broz Reviewed-by: Tomas Mraz MergeDate: Thu Jul 23 15:43:30 2026 (Merged from https://github.com/openssl/openssl/pull/30763) --- crypto/md5/md5_riscv.c | 8 ++++---- crypto/sha/sha_riscv.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crypto/md5/md5_riscv.c b/crypto/md5/md5_riscv.c index 64dd1a5a26..3bd87a13c4 100644 --- a/crypto/md5/md5_riscv.c +++ b/crypto/md5/md5_riscv.c @@ -11,10 +11,10 @@ #include #include "arch/riscv_arch.h" -void ossl_md5_block_asm_data_order(MD5_CTX *c, const void *p, size_t num); -void ossl_md5_block_asm_data_order_zbb(MD5_CTX *c, const void *p, size_t num); -void ossl_md5_block_asm_data_order_riscv64(MD5_CTX *c, const void *p, size_t num); -void ossl_md5_block_asm_data_order(MD5_CTX *c, const void *p, size_t num) +void ossl_md5_block_asm_data_order(void *c, const void *p, size_t num); +void ossl_md5_block_asm_data_order_zbb(void *c, const void *p, size_t num); +void ossl_md5_block_asm_data_order_riscv64(void *c, const void *p, size_t num); +void ossl_md5_block_asm_data_order(void *c, const void *p, size_t num) { if (RISCV_HAS_ZBB()) { ossl_md5_block_asm_data_order_zbb(c, p, num); diff --git a/crypto/sha/sha_riscv.c b/crypto/sha/sha_riscv.c index 28248fd5ac..100ecacdde 100644 --- a/crypto/sha/sha_riscv.c +++ b/crypto/sha/sha_riscv.c @@ -18,9 +18,9 @@ void sha256_block_data_order_zvkb_zvknha_or_zvknhb(void *ctx, const void *in, size_t num); void sha256_block_data_order_zbb(void *ctx, const void *in, size_t num); void sha256_block_data_order_riscv64(void *ctx, const void *in, size_t num); -void sha256_block_data_order(SHA256_CTX *ctx, const void *in, size_t num); +void sha256_block_data_order(void *ctx, const void *in, size_t num); -void sha256_block_data_order(SHA256_CTX *ctx, const void *in, size_t num) +void sha256_block_data_order(void *ctx, const void *in, size_t num) { if (RISCV_HAS_ZVKB() && (RISCV_HAS_ZVKNHA() || RISCV_HAS_ZVKNHB()) && riscv_vlen() >= 128) { sha256_block_data_order_zvkb_zvknha_or_zvknhb(ctx, in, num); @@ -34,9 +34,9 @@ void sha256_block_data_order(SHA256_CTX *ctx, const void *in, size_t num) void sha512_block_data_order_zvkb_zvknhb(void *ctx, const void *in, size_t num); void sha512_block_data_order_zbb(void *ctx, const void *in, size_t num); void sha512_block_data_order_c(void *ctx, const void *in, size_t num); -void sha512_block_data_order(SHA512_CTX *ctx, const void *in, size_t num); +void sha512_block_data_order(void *ctx, const void *in, size_t num); -void sha512_block_data_order(SHA512_CTX *ctx, const void *in, size_t num) +void sha512_block_data_order(void *ctx, const void *in, size_t num) { if (RISCV_HAS_ZVKB_AND_ZVKNHB() && riscv_vlen() >= 128) { sha512_block_data_order_zvkb_zvknhb(ctx, in, num); From d2c0db0d9d7fa2658215fa3f34db1810ec4a7f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20M=C3=BCllner?= Date: Fri, 10 Apr 2026 02:23:01 +0200 Subject: [PATCH 346/349] crypto: use void * in LoongArch SHA shim MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The LoongArch SHA dispatch shim uses the same deprecated typed interfaces as the RISC-V shim. Switch its entry points to void * so it no longer depends on deprecated SHA context types. Signed-off-by: Christoph Müllner Reviewed-by: Milan Broz Reviewed-by: Tomas Mraz MergeDate: Thu Jul 23 15:43:31 2026 (Merged from https://github.com/openssl/openssl/pull/30763) --- crypto/sha/sha_loongarch.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crypto/sha/sha_loongarch.c b/crypto/sha/sha_loongarch.c index 55ececfe04..1576e6f493 100644 --- a/crypto/sha/sha_loongarch.c +++ b/crypto/sha/sha_loongarch.c @@ -16,9 +16,9 @@ void sha256_block_data_order_la64v100(void *ctx, const void *in, size_t num); void sha256_block_data_order_lsx(void *ctx, const void *in, size_t num); -void sha256_block_data_order(SHA256_CTX *ctx, const void *in, size_t num); +void sha256_block_data_order(void *ctx, const void *in, size_t num); -void sha256_block_data_order(SHA256_CTX *ctx, const void *in, size_t num) +void sha256_block_data_order(void *ctx, const void *in, size_t num) { if (OPENSSL_loongarch_hwcap_P & LOONGARCH_HWCAP_LSX) { sha256_block_data_order_lsx(ctx, in, num); @@ -29,9 +29,9 @@ void sha256_block_data_order(SHA256_CTX *ctx, const void *in, size_t num) void sha512_block_data_order_la64v100(void *ctx, const void *in, size_t num); void sha512_block_data_order_lsx(void *ctx, const void *in, size_t num); -void sha512_block_data_order(SHA512_CTX *ctx, const void *in, size_t num); +void sha512_block_data_order(void *ctx, const void *in, size_t num); -void sha512_block_data_order(SHA512_CTX *ctx, const void *in, size_t num) +void sha512_block_data_order(void *ctx, const void *in, size_t num) { if (OPENSSL_loongarch_hwcap_P & LOONGARCH_HWCAP_LSX) { sha512_block_data_order_lsx(ctx, in, num); From 3e4f7e77208138174ce50f0403ce6b947cb1d509 Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Tue, 5 May 2026 19:30:49 +0900 Subject: [PATCH 347/349] Reject unknown TLS 1.3 ServerHello extensions TLS 1.3 does not permit clients to ignore unknown extensions in ServerHello or HelloRetryRequest. Add a validation pass for these messages so fully unknown extension types fail early with unsupported_extension. Keep the existing ignore behavior for other extension contexts and leave TLS 1.2 ServerHello processing unchanged. Add TLSProxy coverage for the TLS 1.3 ServerHello case. Correct the unknown-extension path in tls_collect_extensions(). Collection still runs before the new TLS 1.3 validation, and verify_extension() returns success with thisex == NULL for fully unknown extension types. The previous idx calculation used invalid pointer arithmetic on NULL in that path. Map unknown extensions explicitly outside the built-in extension range instead, preserving the existing ignore semantics while avoiding undefined behavior. Update the truncated HRR ECH corruption vector to keep testing malformed ECH length after the new unknown-extension validation. The old 0xdddd bytes were only padding to preserve the replacement length, but they now trigger the unknown-extension check before the ECH parser. Use a known HRR cookie extension as padding so the test still reaches the intended SSL_R_LENGTH_MISMATCH path. Reviewed-by: Andrew Dinh Reviewed-by: Daniel Kubec Reviewed-by: Tomas Mraz MergeDate: Thu Jul 23 15:55:09 2026 (Merged from https://github.com/openssl/openssl/pull/31086) --- ssl/statem/extensions.c | 57 ++++++++++++++++++++++++++++- ssl/statem/statem_clnt.c | 6 +++ ssl/statem/statem_local.h | 2 + test/ech_corrupt_test.c | 4 +- test/recipes/70-test_sslextension.t | 22 +++++++++-- util/perl/TLSProxy/Message.pm | 3 +- 6 files changed, 86 insertions(+), 8 deletions(-) diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c index c3ef683d75..753402b84f 100644 --- a/ssl/statem/extensions.c +++ b/ssl/statem/extensions.c @@ -798,6 +798,62 @@ static int tls_parse_ec_pt_formats(SSL_CONNECTION *s, PACKET *pkt, return 1; } +/* + * Verify that all extensions in |packet| are known built-in or custom + * extension types. This is used for TLS 1.3 server extension responses where + * unknown extensions are not ignored. + */ +int tls_validate_no_unknown_extensions(SSL_CONNECTION *s, PACKET *packet, + unsigned int context) +{ + PACKET extensions = *packet; + custom_ext_methods *exts = &s->cert->custext; + ENDPOINT role = ENDPOINT_BOTH; + + if ((context & SSL_EXT_CLIENT_HELLO) != 0) { +#ifndef OPENSSL_NO_ECH + if (s->ext.ech.attempted == 1 && s->ext.ech.ch_depth == 1) + role = ENDPOINT_CLIENT; + else + role = ENDPOINT_SERVER; +#else + role = ENDPOINT_SERVER; +#endif + } else if ((context & SSL_EXT_TLS1_2_SERVER_HELLO) != 0) { + role = ENDPOINT_CLIENT; + } + + while (PACKET_remaining(&extensions) > 0) { + unsigned int type; + size_t i; + PACKET extension; + const EXTENSION_DEFINITION *thisext; + + if (!PACKET_get_net_2(&extensions, &type) + || !PACKET_get_length_prefixed_2(&extensions, &extension)) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION); + return 0; + } + + for (i = 0, thisext = ext_defs; i < OSSL_NELEM(ext_defs); + i++, thisext++) { + if (type == thisext->type) + break; + } + if (i < OSSL_NELEM(ext_defs)) + continue; + + if (exts != NULL && custom_ext_find(exts, role, type, NULL) != NULL) + continue; + + SSLfatal(s, SSL_AD_UNSUPPORTED_EXTENSION, + SSL_R_UNSOLICITED_EXTENSION); + return 0; + } + + return 1; +} + /* * Check whether the context defined for an extension |extctx| means whether * the extension is relevant for the current context |thisctx| or not. Returns @@ -907,7 +963,6 @@ int tls_collect_extensions(SSL_CONNECTION *s, PACKET *packet, SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_EXTENSION); goto err; } - /* The server must tolerate the unknown extension and complete. */ if (thisex == NULL) continue; diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c index 15ed8ef8d5..d719ef8a84 100644 --- a/ssl/statem/statem_clnt.c +++ b/ssl/statem/statem_clnt.c @@ -1799,6 +1799,8 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL_CONNECTION *s, PACKET *pkt) if (hrr) { if (!tls_collect_extensions(s, &extpkt, SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST, &extensions, NULL, 1) + || !tls_validate_no_unknown_extensions(s, &extpkt, + SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST) || !tls_parse_extension(s, TLSEXT_IDX_ech, SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST, extensions, NULL, 0)) { @@ -1959,6 +1961,10 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL_CONNECTION *s, PACKET *pkt) SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_EXTENSION); goto err; } + if (SSL_CONNECTION_IS_TLS13(s) + && !tls_validate_no_unknown_extensions(s, &extpkt, context)) + /* SSLfatal() already called */ + goto err; s->hit = 0; diff --git a/ssl/statem/statem_local.h b/ssl/statem/statem_local.h index b73c1e100f..de9ff299be 100644 --- a/ssl/statem/statem_local.h +++ b/ssl/statem/statem_local.h @@ -269,6 +269,8 @@ __owur int tls_validate_all_contexts(SSL_CONNECTION *s, unsigned int thisctx, RAW_EXTENSION *exts); __owur int extension_is_relevant(SSL_CONNECTION *s, unsigned int extctx, unsigned int thisctx); +__owur int tls_validate_no_unknown_extensions(SSL_CONNECTION *s, + PACKET *packet, unsigned int context); __owur int tls_collect_extensions(SSL_CONNECTION *s, PACKET *packet, unsigned int context, RAW_EXTENSION **res, size_t *len, int init); diff --git a/test/ech_corrupt_test.c b/test/ech_corrupt_test.c index 7070f4ef37..79d59844fd 100644 --- a/test/ech_corrupt_test.c +++ b/test/ech_corrupt_test.c @@ -710,10 +710,10 @@ typedef struct { #define OSSL_ECH_BORK_GREASE (1 << 4) #define OSSL_ECH_BORK_REPLACE (1 << 5) -/* a truncated ECH, with another bogus ext to match overall length */ +/* a truncated ECH, padded with a known HRR ext to match overall length */ static unsigned char shortech[] = { 0xfe, 0x0d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, - 0xdd, 0xdd, 0x00, 0x00 + 0x00, 0x2c, 0x00, 0x00 }; /* a too-long ECH internal length */ diff --git a/test/recipes/70-test_sslextension.t b/test/recipes/70-test_sslextension.t index 3327df474e..89b241efee 100644 --- a/test/recipes/70-test_sslextension.t +++ b/test/recipes/70-test_sslextension.t @@ -38,7 +38,8 @@ use constant { UNSOLICITED_SERVER_NAME => 0, UNSOLICITED_SERVER_NAME_TLS13 => 1, UNSOLICITED_SCT => 2, - NONCOMPLIANT_SUPPORTED_GROUPS => 3 + NONCOMPLIANT_SUPPORTED_GROUPS => 3, + UNKNOWN_SERVER_HELLO_TLS13 => 4 }; my $testtype; @@ -146,7 +147,8 @@ sub inject_unsolicited_extension if ($proxy->flight != 1) { if ($sent_unsolisited_extension) { my $last_record = @{$proxy->record_list}[-1]; - $fatal_alert = 1 if $last_record->is_fatal_alert(0); + my $alert = $last_record->is_fatal_alert(0); + $fatal_alert = $alert if $alert; } return; } @@ -172,6 +174,8 @@ sub inject_unsolicited_extension $type = TLSProxy::Message::EXT_SCT; } elsif ($testtype == NONCOMPLIANT_SUPPORTED_GROUPS) { $type = TLSProxy::Message::EXT_SUPPORTED_GROUPS; + } elsif ($testtype == UNKNOWN_SERVER_HELLO_TLS13) { + $type = TLSProxy::Message::EXT_UNKNOWN; } $message->set_extension($type, $ext); $message->repack(); @@ -194,7 +198,7 @@ sub inject_cryptopro_extension # Test 1-2: Sending a duplicate extension should fail. $proxy->start() or plan skip_all => "Unable to start up Proxy for tests"; -plan tests => 8; +plan tests => 9; ok($fatal_alert, "Duplicate ClientHello extension"); SKIP: { @@ -261,7 +265,7 @@ SKIP: { } SKIP: { - skip "TLS 1.3 disabled", 1 + skip "TLS 1.3 disabled", 2 if disabled("tls1_3") || (disabled("ec") && disabled("dh")); #Test 8: Inject an unsolicited extension (TLSv1.3) $fatal_alert = 0; @@ -271,4 +275,14 @@ SKIP: { $proxy->clientflags("-noservername"); $proxy->start(); ok($fatal_alert, "Unsolicited server name extension (TLSv1.3)"); + + #Test 9: Inject an unknown extension in ServerHello (TLSv1.3) + $fatal_alert = 0; + $proxy->clear(); + $proxy->filter(\&inject_unsolicited_extension); + $testtype = UNKNOWN_SERVER_HELLO_TLS13; + $proxy->clientflags(""); + $proxy->start(); + ok($fatal_alert == TLSProxy::Message::AL_DESC_UNSUPPORTED_EXTENSION, + "Unknown ServerHello extension (TLSv1.3)"); } diff --git a/util/perl/TLSProxy/Message.pm b/util/perl/TLSProxy/Message.pm index 4c1ba50de4..8c4f9564fa 100644 --- a/util/perl/TLSProxy/Message.pm +++ b/util/perl/TLSProxy/Message.pm @@ -51,7 +51,8 @@ use constant { AL_DESC_DECRYPT_ERROR => 51, AL_DESC_PROTOCOL_VERSION => 70, AL_DESC_NO_RENEGOTIATION => 100, - AL_DESC_MISSING_EXTENSION => 109 + AL_DESC_MISSING_EXTENSION => 109, + AL_DESC_UNSUPPORTED_EXTENSION => 110 }; my %message_type = ( From 5f48317d6d847e555bf788f8b71d51ac032a6344 Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Sun, 19 Jul 2026 10:10:14 +0900 Subject: [PATCH 348/349] Restore empty raw PKCS#1 verify-recover behavior EVP_PKEY_verify_recover() with RSA PKCS#1 v1.5 padding and no configured signature digest rejected a valid signature whose recovered payload is empty, instead of returning success with a recovered length of zero. RSA_public_decrypt() returns -1 on error and otherwise the number of recovered bytes, which may legitimately be zero for a raw PKCS#1 v1.5 signature that encodes an empty payload. Pull request #28306 ("Make error checks on RSA_public_decrypt() consistent") changed the error check in the raw, no-digest verify-recover path from "ret < 0" to "ret <= 0", so a valid zero recovered length was mistaken for an error. Restore the "< 0" check in that branch only, leaving the digest-aware PKCS#1, X9.31, PSS and ordinary verification paths untouched. Add test_RSA_verify_recover_empty_payload, which performs a complete zero-length raw PKCS#1 v1.5 sign-and-recover round trip through EVP and fails on the unmodified implementation specifically at the EVP_PKEY_verify_recover() call. OpenSSL 3.0.13 recovers the same signature and reports a zero-byte result. The behavior regressed as a result of #28306. Fixes #32000 Assisted-by: OpenCode:GLM-5.2 Reviewed-by: Paul Dale Reviewed-by: Jakub Zelenka MergeDate: Fri Jul 24 13:06:58 2026 (Merged from https://github.com/openssl/openssl/pull/32001) --- providers/implementations/signature/rsa_sig.c | 8 ++- test/evp_extra_test.c | 71 +++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/providers/implementations/signature/rsa_sig.c b/providers/implementations/signature/rsa_sig.c index 3d56bd0185..232414935f 100644 --- a/providers/implementations/signature/rsa_sig.c +++ b/providers/implementations/signature/rsa_sig.c @@ -1026,7 +1026,13 @@ static int rsa_verify_recover(void *vprsactx, } ret = RSA_public_decrypt((int)siglen, sig, rout, prsactx->rsa, prsactx->pad_mode); - if (ret <= 0) { + /* + * RSA_public_decrypt() returns -1 on error and otherwise the number + * of recovered bytes, which may legitimately be zero for a raw + * PKCS#1 v1.5 signature that encodes an empty payload. Treat only + * a negative result as an error. + */ + if (ret < 0) { ERR_raise(ERR_LIB_PROV, ERR_R_RSA_LIB); return 0; } diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c index 22e4689982..00ed62f264 100644 --- a/test/evp_extra_test.c +++ b/test/evp_extra_test.c @@ -4244,6 +4244,76 @@ done: return ret; } +/* + * A raw RSA PKCS#1 v1.5 signature whose recovered data is empty must be + * recovered successfully with a length of zero, not rejected as an error. + */ +static int test_RSA_verify_recover_empty_payload(void) +{ + int ret = 0; + int recovered_cap = 0; + EVP_PKEY *pkey = NULL; + EVP_PKEY_CTX *sign_ctx = NULL, *verify_ctx = NULL; + unsigned char *sig = NULL, *recovered = NULL; + size_t sig_len = 0, recovered_len = 0; + /* + * The signed input has zero length, but a valid non-null address is still + * passed so the result does not depend on how lower layers treat NULL for + * zero-length data. + */ + const unsigned char empty[] = { 0 }; + + if (OSSL_PROVIDER_available(testctx, "fips")) + return TEST_skip("Test skipped for FIPS provider"); + + if (!TEST_ptr(pkey = load_example_rsa_key()) + || !TEST_ptr(sign_ctx = EVP_PKEY_CTX_new_from_pkey(testctx, pkey, NULL)) + || !TEST_int_gt(EVP_PKEY_sign_init(sign_ctx), 0) + || !TEST_int_gt(EVP_PKEY_CTX_set_rsa_padding(sign_ctx, RSA_PKCS1_PADDING), 0) + /* + * Deliberately do not configure a signature digest so that the raw + * PKCS#1 v1.5 sign and verify-recover paths are exercised. + */ + || !TEST_int_gt(EVP_PKEY_sign(sign_ctx, NULL, &sig_len, empty, 0), 0) + || !TEST_ptr(sig = OPENSSL_malloc(sig_len)) + || !TEST_int_gt(EVP_PKEY_sign(sign_ctx, sig, &sig_len, empty, 0), 0) + || !TEST_int_gt(recovered_cap = EVP_PKEY_get_size(pkey), 0) + || !TEST_ptr(recovered = OPENSSL_malloc(recovered_cap)) + || !TEST_ptr(verify_ctx = EVP_PKEY_CTX_new_from_pkey(testctx, pkey, NULL)) + || !TEST_int_gt(EVP_PKEY_verify_recover_init(verify_ctx), 0) + || !TEST_int_gt(EVP_PKEY_CTX_set_rsa_padding(verify_ctx, RSA_PKCS1_PADDING), + 0)) + goto done; + + /* Size-query call must succeed. */ + recovered_len = (size_t)recovered_cap; + if (!TEST_int_gt(EVP_PKEY_verify_recover(verify_ctx, NULL, + &recovered_len, sig, sig_len), + 0)) + goto done; + + /* + * The actual recovery call is essential: a NULL output buffer would only + * run the size-query path, which never decodes the signature and so would + * not reproduce the regression. + */ + recovered_len = (size_t)recovered_cap; + if (!TEST_int_gt(EVP_PKEY_verify_recover(verify_ctx, recovered, + &recovered_len, sig, sig_len), + 0) + || !TEST_size_t_eq(recovered_len, 0)) + goto done; + + ret = 1; +done: + EVP_PKEY_CTX_free(sign_ctx); + EVP_PKEY_CTX_free(verify_ctx); + EVP_PKEY_free(pkey); + OPENSSL_free(sig); + OPENSSL_free(recovered); + return ret; +} + static int test_RSA_encrypt(void) { int ret = 0; @@ -9238,6 +9308,7 @@ int setup_tests(void) ADD_TEST(test_RSA_OAEP_set_get_params); ADD_TEST(test_RSA_OAEP_set_null_label); ADD_TEST(test_RSA_verify_recover_rejects_short_buffer); + ADD_TEST(test_RSA_verify_recover_empty_payload); ADD_TEST(test_RSA_encrypt); #ifndef OPENSSL_NO_DEPRECATED_3_0 ADD_TEST(test_RSA_legacy); From 971b8d060e52499d6ffd2f9ca697fe23f72a629a Mon Sep 17 00:00:00 2001 From: Brady Foster <209960252+bef15703@users.noreply.github.com> Date: Sun, 19 Jul 2026 22:44:18 -0400 Subject: [PATCH 349/349] Update SSL_CTX_set1_curves.pod for OpenSSL 3.5 additions Added documentation of the addition of group tuples in openSSL 3.5 in HISTORY section of SSL_CTX_set1_curves.pod. CLA: trivial Reviewed-by: Viktor Dukhovni Reviewed-by: Nikola Pajkovsky MergeDate: Fri Jul 24 13:13:26 2026 (Merged from https://github.com/openssl/openssl/pull/32007) --- doc/man3/SSL_CTX_set1_curves.pod | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/man3/SSL_CTX_set1_curves.pod b/doc/man3/SSL_CTX_set1_curves.pod index f8f9a149f7..f791c28682 100755 --- a/doc/man3/SSL_CTX_set1_curves.pod +++ b/doc/man3/SSL_CTX_set1_curves.pod @@ -402,6 +402,10 @@ SSL_set1_groups_list() was added in OpenSSL 3.3. Support for B was added in OpenSSL 3.5. +OpenSSL 3.5 also introduced support for group tuples, the C<*> keyshare +prediction prefix, the C tuple separator, the C pseudo-group, +and the C<-> prefix. + OpenSSL 3.5 also introduced support for three I ECDH PQ key exchange TLS groups: B, B and B.