From 94bed7e7c2217ceead894e591945e732e58b245b Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Mon, 8 Jun 2026 18:12:05 +0000 Subject: [PATCH 1/2] Fix cipher property NIDs for SSL_get_current_cipher and add PSK kx mapping The cipher property helpers (SSL_CIPHER_get_kx_nid / get_auth_nid / get_cipher_nid / get_digest_nid / is_aead) parse the cipher name looked up via cipher->offset in GetCipherSegment(). That offset is only populated when the cipher is obtained through wolfSSL_get_ciphers_compat() (SSL_get_ciphers()). When the cipher comes from SSL_get_current_cipher(), offset is left at 0, so these helpers parsed cipher_names[0] (a TLS 1.3 suite) instead of the negotiated cipher - e.g. returning NID_kx_any for a plain PSK suite while SSL_CIPHER_get_name() (which uses the suite bytes) reported the correct name. Resolve the cipher_names entry from the always-populated suite bytes in GetCipherSegment(), falling back to cipher->offset when no match is found. Also add the missing plain "PSK" -> NID_kx_psk entry to the kx lookup table so PSK suites report NID_kx_psk instead of NID_undef. Add a regression test that drives the SSL_get_current_cipher() path for TLS_PSK_WITH_AES_128_GCM_SHA256 and checks all five property helpers. --- src/internal.c | 23 +++++++++++++++++++++++ src/ssl.c | 1 + tests/api.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/src/internal.c b/src/internal.c index a3be7ee448..612341a70c 100644 --- a/src/internal.c +++ b/src/internal.c @@ -28749,6 +28749,29 @@ const char* GetCipherSegment(const WOLFSSL_CIPHER* cipher, char n[][MAX_SEGMENT_ offset = cipher->offset; + /* The offset field is only populated when the cipher is obtained through + * wolfSSL_get_ciphers_compat() (e.g. SSL_get_ciphers()). When the cipher + * comes from wolfSSL_get_current_cipher() the offset is left at its default + * value of 0, which would make the parsing below operate on + * cipher_names[0] (a TLS 1.3 suite) rather than on the actual cipher. The + * suite bytes are always populated, so resolve the offset from them. */ + { + int idx; + int namesSz = GetCipherNamesSize(); + for (idx = 0; idx < namesSz; idx++) { + if (cipher_names[idx].cipherSuite0 == cipher->cipherSuite0 && + cipher_names[idx].cipherSuite == cipher->cipherSuite + #ifndef NO_CIPHER_SUITE_ALIASES + && (!(cipher_names[idx].flags & + WOLFSSL_CIPHER_SUITE_FLAG_NAMEALIAS)) + #endif + ) { + offset = (unsigned long)idx; + break; + } + } + } + if (offset >= (unsigned long)GetCipherNamesSize()) return NULL; diff --git a/src/ssl.c b/src/ssl.c index c215101175..6abe460f93 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -11321,6 +11321,7 @@ int wolfSSL_CIPHER_get_kx_nid(const WOLFSSL_CIPHER* cipher) {"RSAPSK", WC_NID_kx_rsa_psk}, {"SRP", WC_NID_kx_srp}, {"EDH", WC_NID_kx_dhe}, + {"PSK", WC_NID_kx_psk}, {"RSA", WC_NID_kx_rsa}, {NULL, WC_NID_undef} }; diff --git a/tests/api.c b/tests/api.c index 6208cc2b2d..010d66e185 100644 --- a/tests/api.c +++ b/tests/api.c @@ -28172,6 +28172,52 @@ static int test_SSL_CIPHER_get_xxx(void) return EXPECT_RESULT(); } +/* Regression test: the cipher property helpers (kx/auth/cipher/digest/aead) + * must report the actual negotiated cipher when it is obtained through + * SSL_get_current_cipher(). That path does not populate cipher->offset, so a + * previous bug caused these helpers to parse cipher_names[0] (a TLS 1.3 suite) + * instead, e.g. returning NID_kx_any for a plain PSK suite. */ +static int test_SSL_CIPHER_get_current_kx(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_ALL) && !defined(NO_TLS) && \ + defined(BUILD_TLS_PSK_WITH_AES_128_GCM_SHA256) + SSL_CTX* ctx = NULL; + SSL* ssl = NULL; + const SSL_CIPHER* cipher = NULL; + +#ifndef NO_WOLFSSL_CLIENT + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method())); +#else + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_server_method())); +#endif + ExpectNotNull(ssl = SSL_new(ctx)); + + /* Simulate a negotiated plain-PSK cipher suite without doing a full + * handshake. SSL_get_current_cipher() reports the suite from these + * fields, and leaves cipher->offset at its default value of 0. */ + if (ssl != NULL) { + ssl->options.cipherSuite0 = CIPHER_BYTE; /* 0x00 */ + ssl->options.cipherSuite = TLS_PSK_WITH_AES_128_GCM_SHA256; + } + + ExpectNotNull(cipher = SSL_get_current_cipher(ssl)); + /* Name is resolved from the suite bytes and was already correct. */ + ExpectStrEQ(SSL_CIPHER_get_name(cipher), "TLS_PSK_WITH_AES_128_GCM_SHA256"); + /* These were wrong before the fix (read cipher_names[0]). */ + ExpectIntEQ(wolfSSL_CIPHER_get_kx_nid(cipher), NID_kx_psk); + ExpectIntEQ(wolfSSL_CIPHER_get_auth_nid(cipher), NID_auth_psk); + ExpectIntEQ(wolfSSL_CIPHER_get_cipher_nid(cipher), NID_aes_128_gcm); + ExpectIntEQ(wolfSSL_CIPHER_get_digest_nid(cipher), NID_sha256); + ExpectIntEQ(wolfSSL_CIPHER_is_aead(cipher), 1); + + SSL_free(ssl); + SSL_CTX_free(ctx); +#endif + + return EXPECT_RESULT(); +} + #if defined(WOLF_CRYPTO_CB) && defined(HAVE_IO_TESTS_DEPENDENCIES) && \ (!defined(WOLF_CRYPTO_CB_ONLY_SHA256) && !defined(WOLF_CRYPTO_CB_ONLY_AES) && \ !defined(WOLF_CRYPTO_CB_ONLY_ECC) && !defined(WOLF_CRYPTO_CB_ONLY_RSA)) @@ -34955,6 +35001,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_get_peer_finished_overrun), #endif TEST_DECL(test_SSL_CIPHER_get_xxx), + TEST_DECL(test_SSL_CIPHER_get_current_kx), TEST_DECL(test_wolfSSL_ERR_strings), TEST_DECL(test_wolfSSL_CTX_set_cipher_list_bytes), TEST_DECL(test_wolfSSL_set_cipher_list_tls12_keeps_tls13), From 921cf5dc206125098f79a8f03723a3dfcbb5ab9f Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Mon, 8 Jun 2026 18:12:05 +0000 Subject: [PATCH 2/2] Simplify GetCipherSegment offset lookup and trim comments --- src/internal.c | 31 +++++++++++-------------------- tests/api.c | 16 +++++----------- 2 files changed, 16 insertions(+), 31 deletions(-) diff --git a/src/internal.c b/src/internal.c index 612341a70c..1930548c17 100644 --- a/src/internal.c +++ b/src/internal.c @@ -28749,26 +28749,17 @@ const char* GetCipherSegment(const WOLFSSL_CIPHER* cipher, char n[][MAX_SEGMENT_ offset = cipher->offset; - /* The offset field is only populated when the cipher is obtained through - * wolfSSL_get_ciphers_compat() (e.g. SSL_get_ciphers()). When the cipher - * comes from wolfSSL_get_current_cipher() the offset is left at its default - * value of 0, which would make the parsing below operate on - * cipher_names[0] (a TLS 1.3 suite) rather than on the actual cipher. The - * suite bytes are always populated, so resolve the offset from them. */ - { - int idx; - int namesSz = GetCipherNamesSize(); - for (idx = 0; idx < namesSz; idx++) { - if (cipher_names[idx].cipherSuite0 == cipher->cipherSuite0 && - cipher_names[idx].cipherSuite == cipher->cipherSuite - #ifndef NO_CIPHER_SUITE_ALIASES - && (!(cipher_names[idx].flags & - WOLFSSL_CIPHER_SUITE_FLAG_NAMEALIAS)) - #endif - ) { - offset = (unsigned long)idx; - break; - } + /* offset is not set via wolfSSL_get_current_cipher(), so resolve it from + * the always-populated suite bytes. */ + for (i = 0; i < GetCipherNamesSize(); i++) { + if (cipher_names[i].cipherSuite0 == cipher->cipherSuite0 && + cipher_names[i].cipherSuite == cipher->cipherSuite + #ifndef NO_CIPHER_SUITE_ALIASES + && (!(cipher_names[i].flags & WOLFSSL_CIPHER_SUITE_FLAG_NAMEALIAS)) + #endif + ) { + offset = (unsigned long)i; + break; } } diff --git a/tests/api.c b/tests/api.c index 010d66e185..97b307b786 100644 --- a/tests/api.c +++ b/tests/api.c @@ -28172,11 +28172,9 @@ static int test_SSL_CIPHER_get_xxx(void) return EXPECT_RESULT(); } -/* Regression test: the cipher property helpers (kx/auth/cipher/digest/aead) - * must report the actual negotiated cipher when it is obtained through - * SSL_get_current_cipher(). That path does not populate cipher->offset, so a - * previous bug caused these helpers to parse cipher_names[0] (a TLS 1.3 suite) - * instead, e.g. returning NID_kx_any for a plain PSK suite. */ +/* Cipher property helpers must report the negotiated cipher when it is + * obtained via SSL_get_current_cipher(), which does not populate + * cipher->offset. */ static int test_SSL_CIPHER_get_current_kx(void) { EXPECT_DECLS; @@ -28193,18 +28191,14 @@ static int test_SSL_CIPHER_get_current_kx(void) #endif ExpectNotNull(ssl = SSL_new(ctx)); - /* Simulate a negotiated plain-PSK cipher suite without doing a full - * handshake. SSL_get_current_cipher() reports the suite from these - * fields, and leaves cipher->offset at its default value of 0. */ + /* Set a negotiated plain-PSK suite without a full handshake. */ if (ssl != NULL) { - ssl->options.cipherSuite0 = CIPHER_BYTE; /* 0x00 */ + ssl->options.cipherSuite0 = CIPHER_BYTE; ssl->options.cipherSuite = TLS_PSK_WITH_AES_128_GCM_SHA256; } ExpectNotNull(cipher = SSL_get_current_cipher(ssl)); - /* Name is resolved from the suite bytes and was already correct. */ ExpectStrEQ(SSL_CIPHER_get_name(cipher), "TLS_PSK_WITH_AES_128_GCM_SHA256"); - /* These were wrong before the fix (read cipher_names[0]). */ ExpectIntEQ(wolfSSL_CIPHER_get_kx_nid(cipher), NID_kx_psk); ExpectIntEQ(wolfSSL_CIPHER_get_auth_nid(cipher), NID_auth_psk); ExpectIntEQ(wolfSSL_CIPHER_get_cipher_nid(cipher), NID_aes_128_gcm);