diff --git a/src/tls.c b/src/tls.c index 18aad71d39..33f8ae654f 100644 --- a/src/tls.c +++ b/src/tls.c @@ -7547,7 +7547,7 @@ int TLSX_Cookie_Use(const WOLFSSL* ssl, const byte* data, word16 len, byte* mac, #else -#define CKE_FREE_ALL(a, b) 0 +#define CKE_FREE_ALL(a, b) WC_DO_NOTHING #define CKE_GET_SIZE(a, b, c) 0 #define CKE_WRITE(a, b, c, d) 0 #define CKE_PARSE(a, b, c, d) 0 @@ -14675,12 +14675,10 @@ void TLSX_FreeAll(TLSX* list, void* heap) WOLFSSL_MSG("Supported Versions extension free"); break; - #ifdef WOLFSSL_SEND_HRR_COOKIE case TLSX_COOKIE: WOLFSSL_MSG("Cookie extension free"); CKE_FREE_ALL((Cookie*)extension->data, heap); break; - #endif #ifdef WOLFSSL_EARLY_DATA case TLSX_EARLY_DATA: @@ -14872,11 +14870,9 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType, ret = SV_GET_SIZE(extension->data, msgType, &length); break; - #ifdef WOLFSSL_SEND_HRR_COOKIE case TLSX_COOKIE: ret = CKE_GET_SIZE((Cookie*)extension->data, msgType, &length); break; - #endif #ifdef WOLFSSL_EARLY_DATA case TLSX_EARLY_DATA: @@ -15107,13 +15103,11 @@ static int TLSX_Write(TLSX* list, byte* output, byte* semaphore, &offset); break; - #ifdef WOLFSSL_SEND_HRR_COOKIE case TLSX_COOKIE: WOLFSSL_MSG("Cookie extension to write"); ret = CKE_WRITE((Cookie*)extension->data, output + offset, msgType, &offset); break; - #endif #ifdef WOLFSSL_EARLY_DATA case TLSX_EARLY_DATA: @@ -17414,8 +17408,6 @@ int TLSX_Parse(WOLFSSL* ssl, const byte* input, word16 length, byte msgType, break; - - #ifdef WOLFSSL_SEND_HRR_COOKIE case TLSX_COOKIE: WOLFSSL_MSG("Cookie extension received"); #ifdef WOLFSSL_DEBUG_TLS @@ -17431,7 +17423,6 @@ int TLSX_Parse(WOLFSSL* ssl, const byte* input, word16 length, byte msgType, ret = CKE_PARSE(ssl, input + offset, size, msgType); break; - #endif #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) case TLSX_PRE_SHARED_KEY: @@ -17671,6 +17662,29 @@ int TLSX_Parse(WOLFSSL* ssl, const byte* input, word16 length, byte msgType, #endif default: WOLFSSL_MSG("Unknown TLS extension type"); +#if defined(WOLFSSL_TLS13) + /* RFC 8446 Sec. 4.2: a TLS 1.3 client MUST abort with an + * unsupported_extension alert when it receives an extension + * "response" that was not advertised in the ClientHello. The + * rule applies only to messages whose extensions are responses + * to the ClientHello: ServerHello, HelloRetryRequest, + * EncryptedExtensions and Certificate. + * + * Extensions in CertificateRequest and NewSessionTicket are + * independent server-initiated payloads, not responses, and + * per RFC 8701 (GREASE) the server MAY include unknown + * (GREASE) extension types there which the client MUST treat + * like any other unknown value (i.e. ignore them). */ + if (IsAtLeastTLSv1_3(ssl->version) && + (msgType == server_hello || + msgType == hello_retry_request || + msgType == encrypted_extensions || + msgType == certificate)) { + SendAlert((WOLFSSL*)ssl, alert_fatal, unsupported_extension); + WOLFSSL_ERROR_VERBOSE(UNSUPPORTED_EXTENSION); + return UNSUPPORTED_EXTENSION; + } +#endif } /* offset should be updated here! */ diff --git a/tests/api.c b/tests/api.c index 0d40dbb636..c30024713a 100644 --- a/tests/api.c +++ b/tests/api.c @@ -31596,7 +31596,8 @@ static int test_TLSX_CA_NAMES_bad_extension(void) ExpectIntEQ(wolfSSL_connect(ssl_c), -1); #ifndef WOLFSSL_DISABLE_EARLY_SANITY_CHECKS - ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WC_NO_ERR_TRACE(EXT_MISSING)); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), + WC_NO_ERR_TRACE(UNSUPPORTED_EXTENSION)); #else ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WC_NO_ERR_TRACE(BUFFER_ERROR)); #endif diff --git a/tests/api/test_tls13.c b/tests/api/test_tls13.c index 63ddffb7aa..6c4de12a10 100644 --- a/tests/api/test_tls13.c +++ b/tests/api/test_tls13.c @@ -3352,6 +3352,98 @@ int test_tls13_warning_alert_is_fatal(void) return EXPECT_RESULT(); } +/* Test that an unknown extension in a TLS 1.3 server-to-client message is + * rejected with unsupported_extension (RFC 8446 Sec. 4.2). The client MUST + * abort the handshake when it receives an extension it did not advertise. + */ + int test_tls13_unknown_ext_rejected(void) + { + EXPECT_DECLS; + #if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(NO_WOLFSSL_CLIENT) && defined(WOLFSSL_AES_128) && \ + defined(HAVE_AESGCM) && !defined(NO_SHA256) && \ + !defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT) + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL *ssl_c = NULL; + struct test_memio_ctx test_ctx; + /* HelloRetryRequest carrying TLS_AES_128_GCM_SHA256, supported_versions + * (TLS 1.3), and an extra unknown extension type 0xFABC. + * + * The base HRR (from test_tls13_same_ch) extended with 4 bytes: + * extensions length: 6 -> 10 (0x00,0x0a) + * handshake body length: 46 -> 50 (0x00,0x00,0x32) + * record body length: 50 -> 54 (0x00,0x36) + * appended: 0xfa,0xbc,0x00,0x00 (unknown type, zero-length value) + */ + static const unsigned char hrr_unknown_ext[] = { + /* TLS record header: handshake, TLS 1.2 compat, len=54 */ + 0x16, 0x03, 0x03, 0x00, 0x36, + /* Handshake header: ServerHello, len=50 */ + 0x02, 0x00, 0x00, 0x32, + /* legacy_version: TLS 1.2 */ + 0x03, 0x03, + /* HelloRetryRequest magic random */ + 0xcf, 0x21, 0xad, 0x74, 0xe5, 0x9a, 0x61, 0x11, + 0xbe, 0x1d, 0x8c, 0x02, 0x1e, 0x65, 0xb8, 0x91, + 0xc2, 0xa2, 0x11, 0x16, 0x7a, 0xbb, 0x8c, 0x5e, + 0x07, 0x9e, 0x09, 0xe2, 0xc8, 0xa8, 0x33, 0x9c, + /* session ID length: 0 */ + 0x00, + /* cipher suite: TLS_AES_128_GCM_SHA256 */ + 0x13, 0x01, + /* compression: null */ + 0x00, + /* extensions length: 10 */ + 0x00, 0x0a, + /* supported_versions: TLS 1.3 (0x0304) */ + 0x00, 0x2b, 0x00, 0x02, 0x03, 0x04, + /* unknown extension type 0xFABC, zero-length value */ + 0xfa, 0xbc, 0x00, 0x00 + }; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, NULL, &ssl_c, NULL, + wolfTLSv1_3_client_method, NULL), 0); + + /* Inject the crafted HRR before the client starts the handshake. + * wolfSSL_connect will send the ClientHello and then read this message. */ + ExpectIntEQ(test_memio_inject_message(&test_ctx, 1, + (const char *)hrr_unknown_ext, sizeof(hrr_unknown_ext)), 0); + + /* RFC 8446 Sec. 4.2: the client MUST abort with unsupported_extension. */ + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), + WC_NO_ERR_TRACE(UNSUPPORTED_EXTENSION)); + + /* The client MUST also transmit the fatal unsupported_extension alert + * on the wire, not merely surface a local error. The client's outgoing + * data lands in test_ctx.s_buff; at this point in the handshake no + * traffic keys are derived yet, so the alert record is plaintext. + * Expected record: type=alert(0x15), version=TLS1.2(0x0303), len=2, + * level=fatal(0x02), description=unsupported_extension(0x6e=110). */ + { + static const unsigned char expected_alert[] = + { 0x15, 0x03, 0x03, 0x00, 0x02, 0x02, 0x6e }; + int found = 0; + int i; + for (i = 0; + i + (int)sizeof(expected_alert) <= test_ctx.s_len; + i++) { + if (XMEMCMP(test_ctx.s_buff + i, expected_alert, + sizeof(expected_alert)) == 0) { + found = 1; + break; + } + } + ExpectIntEQ(found, 1); + } + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + #endif + return EXPECT_RESULT(); + } + /* Test that wolfSSL_set1_sigalgs_list() is honored in TLS 1.3 */ int test_tls13_cert_req_sigalgs(void) diff --git a/tests/api/test_tls13.h b/tests/api/test_tls13.h index c8eaa3b7fa..fd40bdc868 100644 --- a/tests/api/test_tls13.h +++ b/tests/api/test_tls13.h @@ -40,6 +40,7 @@ int test_key_share_mismatch(void); int test_tls13_middlebox_compat_empty_session_id(void); int test_tls13_plaintext_alert(void); int test_tls13_warning_alert_is_fatal(void); +int test_tls13_unknown_ext_rejected(void); int test_tls13_cert_req_sigalgs(void); int test_tls13_derive_keys_no_key(void); int test_tls13_pqc_hybrid_truncated_keyshare(void); @@ -65,6 +66,7 @@ int test_tls13_short_session_ticket(void); TEST_DECL_GROUP("tls13", test_tls13_cert_req_sigalgs), \ TEST_DECL_GROUP("tls13", test_tls13_derive_keys_no_key), \ TEST_DECL_GROUP("tls13", test_tls13_pqc_hybrid_truncated_keyshare), \ - TEST_DECL_GROUP("tls13", test_tls13_short_session_ticket) + TEST_DECL_GROUP("tls13", test_tls13_short_session_ticket), \ + TEST_DECL_GROUP("tls13", test_tls13_unknown_ext_rejected) #endif /* WOLFCRYPT_TEST_TLS13_H */ diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 5e512f76eb..2450331aac 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -3009,9 +3009,7 @@ typedef enum { TLSX_EARLY_DATA = TLSXT_EARLY_DATA, #endif TLSX_SUPPORTED_VERSIONS = TLSXT_SUPPORTED_VERSIONS, - #ifdef WOLFSSL_SEND_HRR_COOKIE TLSX_COOKIE = TLSXT_COOKIE, - #endif #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) TLSX_PSK_KEY_EXCHANGE_MODES = TLSXT_PSK_KEY_EXCHANGE_MODES, #endif