diff --git a/src/tls.c b/src/tls.c index f8ba67db70..dc24bc75bf 100644 --- a/src/tls.c +++ b/src/tls.c @@ -1809,16 +1809,20 @@ static void TLSX_ALPN_FreeAll(ALPN *list, void* heap) static word16 TLSX_ALPN_GetSize(ALPN *list) { ALPN* alpn; - word16 length = OPAQUE16_LEN; /* list length */ + word32 length = OPAQUE16_LEN; /* list length */ while ((alpn = list)) { list = alpn->next; length++; /* protocol name length is on one byte */ - length += (word16)XSTRLEN(alpn->protocol_name); + length += (word32)XSTRLEN(alpn->protocol_name); + + if (length > WOLFSSL_MAX_16BIT) { + return 0; + } } - return length; + return (word16)length; } /** Writes the ALPN objects of a list in a buffer. */ @@ -2941,7 +2945,7 @@ static void TLSX_TCA_FreeAll(TCA* list, void* heap) static word16 TLSX_TCA_GetSize(TCA* list) { TCA* tca; - word16 length = OPAQUE16_LEN; /* list length */ + word32 length = OPAQUE16_LEN; /* list length */ while ((tca = list)) { list = tca->next; @@ -2959,9 +2963,13 @@ static word16 TLSX_TCA_GetSize(TCA* list) length += OPAQUE16_LEN + tca->idSz; break; } + + if (length > WOLFSSL_MAX_16BIT) { + return 0; + } } - return length; + return (word16)length; } /** Writes the TCA objects of a list in a buffer. */ @@ -7568,7 +7576,7 @@ static word16 TLSX_CA_Names_GetSize(void* data) { WOLFSSL* ssl = (WOLFSSL*)data; WOLF_STACK_OF(WOLFSSL_X509_NAME)* names; - word16 size = 0; + word32 size = 0; /* Length of names */ size += OPAQUE16_LEN; @@ -7578,11 +7586,14 @@ static word16 TLSX_CA_Names_GetSize(void* data) if (name != NULL) { /* 16-bit length | SEQ | Len | DER of name */ - size += (word16)(OPAQUE16_LEN + SetSequence(name->rawLen, seq) + + size += (word32)(OPAQUE16_LEN + SetSequence(name->rawLen, seq) + name->rawLen); + if (size > WOLFSSL_MAX_16BIT) { + return 0; + } } } - return size; + return (word16)size; } static word16 TLSX_CA_Names_Write(void* data, byte* output) @@ -11903,14 +11914,22 @@ static int TLSX_PreSharedKey_GetSize(PreSharedKey* list, byte msgType, { if (msgType == client_hello) { /* Length of identities + Length of binders. */ - word16 len = OPAQUE16_LEN + OPAQUE16_LEN; + word32 len = OPAQUE16_LEN + OPAQUE16_LEN; while (list != NULL) { /* Each entry has: identity, ticket age and binder. */ len += OPAQUE16_LEN + list->identityLen + OPAQUE32_LEN + - OPAQUE8_LEN + (word16)list->binderLen; + OPAQUE8_LEN + (word32)list->binderLen; + if (len > WOLFSSL_MAX_16BIT) { + WOLFSSL_ERROR_VERBOSE(LENGTH_ERROR); + return LENGTH_ERROR; + } list = list->next; } - *pSz += len; + if ((word32)*pSz + len > WOLFSSL_MAX_16BIT) { + WOLFSSL_ERROR_VERBOSE(LENGTH_ERROR); + return LENGTH_ERROR; + } + *pSz += (word16)len; return 0; } @@ -11933,7 +11952,7 @@ static int TLSX_PreSharedKey_GetSize(PreSharedKey* list, byte msgType, int TLSX_PreSharedKey_GetSizeBinders(PreSharedKey* list, byte msgType, word16* pSz) { - word16 len; + word32 len; if (msgType != client_hello) { WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E); @@ -11943,11 +11962,15 @@ int TLSX_PreSharedKey_GetSizeBinders(PreSharedKey* list, byte msgType, /* Length of all binders. */ len = OPAQUE16_LEN; while (list != NULL) { - len += OPAQUE8_LEN + (word16)list->binderLen; + len += OPAQUE8_LEN + (word32)list->binderLen; + if (len > WOLFSSL_MAX_16BIT) { + WOLFSSL_ERROR_VERBOSE(LENGTH_ERROR); + return LENGTH_ERROR; + } list = list->next; } - *pSz = len; + *pSz = (word16)len; return 0; } @@ -14850,10 +14873,15 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType, { int ret = 0; TLSX* extension; - word16 length = 0; + /* Accumulate in word32 so per-extension additions can't silently wrap + * a word16 total - the final 16-bit bound is enforced once at the end. */ + word32 length = 0; + word16 hsz; byte isRequest = (msgType == client_hello || msgType == certificate_request); + (void)hsz; + while ((extension = list)) { list = extension->next; @@ -14884,8 +14912,15 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType, case TLSX_TRUSTED_CA_KEYS: /* TCA only sends the list on the request. */ - if (isRequest) - length += TCA_GET_SIZE((TCA*)extension->data); + if (isRequest) { + word16 tcaSz = TCA_GET_SIZE((TCA*)extension->data); + /* 0 on non-empty list means 16-bit overflow. */ + if (tcaSz == 0 && extension->data != NULL) { + ret = LENGTH_ERROR; + break; + } + length += tcaSz; + } break; case TLSX_MAX_FRAGMENT_LENGTH: @@ -14926,9 +14961,16 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType, isRequest); break; - case TLSX_APPLICATION_LAYER_PROTOCOL: - length += ALPN_GET_SIZE((ALPN*)extension->data); + case TLSX_APPLICATION_LAYER_PROTOCOL: { + word16 alpnSz = ALPN_GET_SIZE((ALPN*)extension->data); + /* 0 on non-empty list means 16-bit overflow. */ + if (alpnSz == 0 && extension->data != NULL) { + ret = LENGTH_ERROR; + break; + } + length += alpnSz; break; + } #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_SIGALG) case TLSX_SIGNATURE_ALGORITHMS: length += SA_GET_SIZE(extension->data); @@ -14936,23 +14978,31 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType, #endif #if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY) case TLSX_ENCRYPT_THEN_MAC: - ret = ETM_GET_SIZE(msgType, &length); + hsz = 0; + ret = ETM_GET_SIZE(msgType, &hsz); + length += hsz; break; #endif /* HAVE_ENCRYPT_THEN_MAC */ #if defined(WOLFSSL_TLS13) || !defined(WOLFSSL_NO_TLS12) || !defined(NO_OLD_TLS) #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) case TLSX_PRE_SHARED_KEY: + hsz = 0; ret = PSK_GET_SIZE((PreSharedKey*)extension->data, msgType, - &length); + &hsz); + length += hsz; break; #ifdef WOLFSSL_TLS13 case TLSX_PSK_KEY_EXCHANGE_MODES: - ret = PKM_GET_SIZE((byte)extension->val, msgType, &length); + hsz = 0; + ret = PKM_GET_SIZE((byte)extension->val, msgType, &hsz); + length += hsz; break; #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK case TLSX_CERT_WITH_EXTERN_PSK: - ret = PSK_WITH_CERT_GET_SIZE(msgType, &length); + hsz = 0; + ret = PSK_WITH_CERT_GET_SIZE(msgType, &hsz); + length += hsz; break; #endif #endif @@ -14964,22 +15014,30 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType, #ifdef WOLFSSL_TLS13 case TLSX_SUPPORTED_VERSIONS: - ret = SV_GET_SIZE(extension->data, msgType, &length); + hsz = 0; + ret = SV_GET_SIZE(extension->data, msgType, &hsz); + length += hsz; break; case TLSX_COOKIE: - ret = CKE_GET_SIZE((Cookie*)extension->data, msgType, &length); + hsz = 0; + ret = CKE_GET_SIZE((Cookie*)extension->data, msgType, &hsz); + length += hsz; break; #ifdef WOLFSSL_EARLY_DATA case TLSX_EARLY_DATA: - ret = EDI_GET_SIZE(msgType, &length); + hsz = 0; + ret = EDI_GET_SIZE(msgType, &hsz); + length += hsz; break; #endif #ifdef WOLFSSL_POST_HANDSHAKE_AUTH case TLSX_POST_HANDSHAKE_AUTH: - ret = PHA_GET_SIZE(msgType, &length); + hsz = 0; + ret = PHA_GET_SIZE(msgType, &hsz); + length += hsz; break; #endif @@ -14990,9 +15048,16 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType, #endif #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CA_NAMES) - case TLSX_CERTIFICATE_AUTHORITIES: - length += CAN_GET_SIZE(extension->data); + case TLSX_CERTIFICATE_AUTHORITIES: { + word16 canSz = CAN_GET_SIZE(extension->data); + /* 0 on non-empty list means 16-bit overflow. */ + if (canSz == 0 && extension->data != NULL) { + ret = LENGTH_ERROR; + break; + } + length += canSz; break; + } #endif #endif #ifdef WOLFSSL_SRTP @@ -15037,7 +15102,11 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType, TURN_ON(semaphore, TLSX_ToSemaphore((word16)extension->type)); } - *pLength += length; + if (ret == 0 && (word32)*pLength + length > WOLFSSL_MAX_16BIT) { + WOLFSSL_ERROR_VERBOSE(LENGTH_ERROR); + return LENGTH_ERROR; + } + *pLength += (word16)length; return ret; } diff --git a/tests/api.c b/tests/api.c index 7cde2a6bdc..e763f09784 100644 --- a/tests/api.c +++ b/tests/api.c @@ -10436,6 +10436,78 @@ static int test_wolfSSL_SCR_check_enabled(void) return EXPECT_RESULT(); } +/* F-2922: wolfSSL_TicketKeyCb must reject a session ticket whose HMAC + * does not match its encrypted contents. */ +static int test_wolfSSL_ticket_keycb_bad_hmac(void) +{ + EXPECT_DECLS; +#if defined(HAVE_SESSION_TICKET) && !defined(WOLFSSL_NO_TLS12) && \ + defined(OPENSSL_EXTRA) && defined(HAVE_AES_CBC) && \ + defined(WOLFSSL_AES_256) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + WOLFSSL_SESSION *session = NULL; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + + ExpectIntEQ(OpenSSLTicketInit(), 0); + ExpectIntEQ(wolfSSL_CTX_set_tlsext_ticket_key_cb(ctx_s, + myTicketEncCbOpenSSL), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c), WOLFSSL_SUCCESS); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectNotNull(session = wolfSSL_get1_session(ssl_c)); + ExpectIntGT(session->ticketLen, 0); + + /* Corrupt a byte of the ticket HMAC so the server's HMAC + * verification rejects it. */ + if (session != NULL && session->ticket != NULL && session->ticketLen > 0) + session->ticket[session->ticketLen - 1] ^= 0xFF; + + wolfSSL_free(ssl_c); + ssl_c = NULL; + wolfSSL_free(ssl_s); + ssl_s = NULL; + test_memio_clear_buffer(&test_ctx, 0); + test_memio_clear_buffer(&test_ctx, 1); + + ExpectNotNull(ssl_c = wolfSSL_new(ctx_c)); + ExpectNotNull(ssl_s = wolfSSL_new(ctx_s)); + wolfSSL_SetIOReadCtx(ssl_c, &test_ctx); + wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx); + wolfSSL_SetIOReadCtx(ssl_s, &test_ctx); + wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx); + ExpectIntEQ(wolfSSL_set_session(ssl_c, session), WOLFSSL_SUCCESS); + /* Disable the process-global session cache lookup on the server so that + * the ticket is the only resumption path - with WOLFSSL_TICKET_HAVE_ID + * the server could otherwise resume by session ID. */ + if (ssl_s != NULL) + ssl_s->options.sessionCacheOff = 1; + + /* Corrupted ticket bytes fail the HMAC check in + * wolfSSL_TicketKeyCb; the session must not resume. */ + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(wolfSSL_session_reused(ssl_c), 0); + + wolfSSL_SESSION_free(session); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + OpenSSLTicketCleanup(); +#endif + return EXPECT_RESULT(); +} + + #if !defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS) && \ !defined(NO_FILESYSTEM) && (!defined(NO_RSA) || defined(HAVE_ECC)) /* Called when writing. */ @@ -36298,6 +36370,12 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_select_next_proto), #endif TEST_DECL(test_tls_ems_downgrade), + TEST_DECL(test_tls_ems_resumption_downgrade), + TEST_DECL(test_tls12_chacha20_poly1305_bad_tag), + TEST_DECL(test_tls13_null_cipher_bad_hmac), + TEST_DECL(test_scr_verify_data_mismatch), + TEST_DECL(test_tls13_hrr_cipher_suite_mismatch), + TEST_DECL(test_tls13_ticket_age_out_of_window), TEST_DECL(test_wolfSSL_DisableExtendedMasterSecret), TEST_DECL(test_certificate_authorities_certificate_request), TEST_DECL(test_certificate_authorities_client_hello), @@ -36306,6 +36384,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_wolfSSL_UseSecureRenegotiation), TEST_DECL(test_wolfSSL_SCR_Reconnect), TEST_DECL(test_wolfSSL_SCR_check_enabled), + TEST_DECL(test_wolfSSL_ticket_keycb_bad_hmac), TEST_DECL(test_tls_ext_duplicate), TEST_DECL(test_tls_bad_legacy_version), #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) diff --git a/tests/api/test_tls_ext.c b/tests/api/test_tls_ext.c index ad6053f727..d183bfe6db 100644 --- a/tests/api/test_tls_ext.c +++ b/tests/api/test_tls_ext.c @@ -103,6 +103,379 @@ int test_tls_ems_downgrade(void) } +/* F-2915: resumption of an EMS session without EMS must abort with + * EXT_MASTER_SECRET_NEEDED_E (RFC 7627 Section 5.3). */ +int test_tls_ems_resumption_downgrade(void) +{ + EXPECT_DECLS; +#if !defined(WOLFSSL_NO_TLS12) && defined(HAVE_EXTENDED_MASTER) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(NO_SESSION_CACHE) + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + WOLFSSL_SESSION *session = NULL; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + ExpectNotNull(session = wolfSSL_get1_session(ssl_c)); + + wolfSSL_free(ssl_c); + ssl_c = NULL; + wolfSSL_free(ssl_s); + ssl_s = NULL; + test_memio_clear_buffer(&test_ctx, 0); + test_memio_clear_buffer(&test_ctx, 1); + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_set_session(ssl_c, session), WOLFSSL_SUCCESS); + /* Drop EMS from the resumption ClientHello to simulate a downgrade. */ + ExpectIntEQ(wolfSSL_DisableExtendedMasterSecret(ssl_c), WOLFSSL_SUCCESS); + + ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(wolfSSL_get_error(ssl_s, 0), + WC_NO_ERR_TRACE(EXT_MASTER_SECRET_NEEDED_E)); + + wolfSSL_SESSION_free(session); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + + +#if !defined(WOLFSSL_NO_TLS12) && \ + defined(BUILD_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) +static int test_chacha_bad_tag_trigger = 0; + +static int test_chacha_bad_tag_io_recv(WOLFSSL* ssl, char* buf, int sz, + void* ctx) +{ + int ret = test_memio_read_cb(ssl, buf, sz, ctx); + /* Tamper with a byte from the encrypted record payload on the first + * read that spans past the 5-byte TLS record header, so the Poly1305 + * authentication check no longer matches. */ + if (test_chacha_bad_tag_trigger && ret > 5) { + buf[ret - 1] ^= 0xFF; + test_chacha_bad_tag_trigger = 0; + } + return ret; +} +#endif + +/* F-2921: TLS 1.2 ChaCha20-Poly1305 must surface VERIFY_MAC_ERROR when + * the Poly1305 tag is corrupted. */ +int test_tls12_chacha20_poly1305_bad_tag(void) +{ + EXPECT_DECLS; +#if !defined(WOLFSSL_NO_TLS12) && \ + defined(BUILD_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + const char msg[] = "tamper me"; + char recvBuf[32]; + int ret; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + test_ctx.c_ciphers = test_ctx.s_ciphers = + "ECDHE-RSA-CHACHA20-POLY1305"; + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + wolfSSL_SSLSetIORecv(ssl_s, test_chacha_bad_tag_io_recv); + + ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)XSTRLEN(msg)), + (int)XSTRLEN(msg)); + + test_chacha_bad_tag_trigger = 1; + ret = wolfSSL_read(ssl_s, recvBuf, sizeof(recvBuf)); + ExpectIntLE(ret, 0); + ExpectIntEQ(wolfSSL_get_error(ssl_s, ret), + WC_NO_ERR_TRACE(VERIFY_MAC_ERROR)); + + test_chacha_bad_tag_trigger = 0; + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + + +#if defined(WOLFSSL_TLS13) && defined(HAVE_NULL_CIPHER) && \ + defined(BUILD_TLS_SHA256_SHA256) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) +static int test_tls13_null_bad_hmac_trigger = 0; + +static int test_tls13_null_bad_hmac_io_recv(WOLFSSL* ssl, char* buf, int sz, + void* ctx) +{ + int ret = test_memio_read_cb(ssl, buf, sz, ctx); + /* Tamper with a byte from the encrypted record payload on the first + * read that spans past the 5-byte TLS record header, so the HMAC tag + * check in Tls13IntegrityOnly_Decrypt no longer matches. */ + if (test_tls13_null_bad_hmac_trigger && ret > 5) { + buf[ret - 1] ^= 0xFF; + test_tls13_null_bad_hmac_trigger = 0; + } + return ret; +} +#endif + +/* F-2916: TLS 1.3 integrity-only decryption must surface DECRYPT_ERROR + * when the HMAC tag is corrupted. */ +int test_tls13_null_cipher_bad_hmac(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_TLS13) && defined(HAVE_NULL_CIPHER) && \ + defined(BUILD_TLS_SHA256_SHA256) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + const char msg[] = "integrity only"; + char recvBuf[32]; + int ret; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + test_ctx.c_ciphers = test_ctx.s_ciphers = "TLS13-SHA256-SHA256"; + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + wolfSSL_SSLSetIORecv(ssl_s, test_tls13_null_bad_hmac_io_recv); + + ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)XSTRLEN(msg)), + (int)XSTRLEN(msg)); + + test_tls13_null_bad_hmac_trigger = 1; + ret = wolfSSL_read(ssl_s, recvBuf, sizeof(recvBuf)); + ExpectIntLE(ret, 0); + ExpectIntEQ(wolfSSL_get_error(ssl_s, ret), + WC_NO_ERR_TRACE(DECRYPT_ERROR)); + + test_tls13_null_bad_hmac_trigger = 0; + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + + +/* F-2913 and F-2914: the TLSX_SecureRenegotiation_Parse + * ConstantCompare against the cached Finished verify_data must reject + * a mismatch on both the client and server sides. */ +int test_scr_verify_data_mismatch(void) +{ + EXPECT_DECLS; +#if defined(HAVE_SECURE_RENEGOTIATION) && !defined(WOLFSSL_NO_TLS12) && \ + defined(BUILD_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) + int side; + + for (side = 0; side < 2; side++) { + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + WOLFSSL *failing; + byte data; + int ret; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + test_ctx.c_ciphers = test_ctx.s_ciphers = + "ECDHE-RSA-AES128-GCM-SHA256"; + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, + &ssl_s, wolfTLSv1_2_client_method, + wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_CTX_UseSecureRenegotiation(ctx_c), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_UseSecureRenegotiation(ctx_s), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_c), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_s), WOLFSSL_SUCCESS); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* side 0: corrupt the client's copy; side 1: corrupt the + * server's copy. */ + if (side == 0) { + if (ssl_c != NULL && ssl_c->secure_renegotiation != NULL) + ssl_c->secure_renegotiation->server_verify_data[0] ^= 0xFF; + failing = ssl_c; + } + else { + if (ssl_s != NULL && ssl_s->secure_renegotiation != NULL) + ssl_s->secure_renegotiation->client_verify_data[0] ^= 0xFF; + failing = ssl_s; + } + + ret = wolfSSL_Rehandshake(ssl_c); + (void)ret; + (void)wolfSSL_read(ssl_s, &data, 1); + (void)wolfSSL_read(ssl_c, &data, 1); + ExpectIntEQ(wolfSSL_get_error(failing, 0), + WC_NO_ERR_TRACE(SECURE_RENEGOTIATION_E)); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + } +#endif + return EXPECT_RESULT(); +} + +/* F-2126: DoTls13ClientHello must reject a second ClientHello whose + * cipher suite does not match the server's HelloRetryRequest. The + * client offers two suites in CH1 and only a different one in CH2. */ +int test_tls13_hrr_cipher_suite_mismatch(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_TLS13) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \ + defined(BUILD_TLS_AES_128_GCM_SHA256) && \ + defined(BUILD_TLS_AES_256_GCM_SHA384) + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + int ret; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + /* Both suites supported on both ends; server prefers the first + * offered suite, which will be the one committed in the HRR. */ + test_ctx.c_ciphers = test_ctx.s_ciphers = + "TLS13-AES128-GCM-SHA256:TLS13-AES256-GCM-SHA384"; + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + /* Force HRR by withholding key_share entries in CH1. */ + ExpectIntEQ(wolfSSL_NoKeyShares(ssl_c), WOLFSSL_SUCCESS); + + /* CH1 / HRR */ + ExpectIntEQ(wolfSSL_connect(ssl_c), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + ExpectIntEQ(wolfSSL_get_error(ssl_c, 0), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(wolfSSL_accept(ssl_s), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + ExpectIntEQ(wolfSSL_get_error(ssl_s, 0), WOLFSSL_ERROR_WANT_READ); + + /* Restrict the client to a different suite than the one the + * server committed to in the HRR, so CH2 offers only that. */ + ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, "TLS13-AES256-GCM-SHA384"), + WOLFSSL_SUCCESS); + + /* CH2 */ + (void)wolfSSL_connect(ssl_c); + (void)wolfSSL_accept(ssl_s); + (void)wolfSSL_connect(ssl_c); + /* The cipher-suite mismatch is caught server-side; the server's + * alert reaches the client, so either peer can surface it. */ + ret = wolfSSL_get_error(ssl_s, 0); + if (ret != WC_NO_ERR_TRACE(INVALID_PARAMETER)) + ret = wolfSSL_get_error(ssl_c, 0); + ExpectIntEQ(ret, WC_NO_ERR_TRACE(INVALID_PARAMETER)); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + + +/* F-1824: DoClientTicketCheck must reject a PSK whose obfuscated age + * falls outside the [-1000, MAX_TICKET_AGE_DIFF*1000+1000] ms window. */ +int test_tls13_ticket_age_out_of_window(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + WOLFSSL_SESSION *session = NULL; + byte tmp; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* Pump post-handshake reads so the NewSessionTicket reaches the + * client. */ + (void)wolfSSL_read(ssl_c, &tmp, sizeof(tmp)); + (void)wolfSSL_read(ssl_s, &tmp, sizeof(tmp)); + (void)wolfSSL_read(ssl_c, &tmp, sizeof(tmp)); + + ExpectNotNull(session = wolfSSL_get1_session(ssl_c)); + /* The test only exercises the age window check if the client actually + * received a NewSessionTicket and the session carries ticket material. */ + ExpectIntGT(session->ticketLen, 0); + + /* Flip the high bit to push the unobfuscated age out of window. */ + if (session != NULL) + session->ticketAdd ^= 0x80000000U; + + wolfSSL_free(ssl_c); + ssl_c = NULL; + wolfSSL_free(ssl_s); + ssl_s = NULL; + test_memio_clear_buffer(&test_ctx, 0); + test_memio_clear_buffer(&test_ctx, 1); + + ExpectNotNull(ssl_c = wolfSSL_new(ctx_c)); + ExpectNotNull(ssl_s = wolfSSL_new(ctx_s)); + wolfSSL_SetIOReadCtx(ssl_c, &test_ctx); + wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx); + wolfSSL_SetIOReadCtx(ssl_s, &test_ctx); + wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx); + ExpectIntEQ(wolfSSL_set_session(ssl_c, session), WOLFSSL_SUCCESS); + + /* PSK rejected, full handshake must still succeed. */ + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0); + + wolfSSL_SESSION_free(session); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + + int test_wolfSSL_DisableExtendedMasterSecret(void) { EXPECT_DECLS; diff --git a/tests/api/test_tls_ext.h b/tests/api/test_tls_ext.h index ec7a5223be..7d0921c9ad 100644 --- a/tests/api/test_tls_ext.h +++ b/tests/api/test_tls_ext.h @@ -23,6 +23,12 @@ #define TESTS_API_TEST_TLS_EXT_H int test_tls_ems_downgrade(void); +int test_tls_ems_resumption_downgrade(void); +int test_tls12_chacha20_poly1305_bad_tag(void); +int test_tls13_null_cipher_bad_hmac(void); +int test_scr_verify_data_mismatch(void); +int test_tls13_hrr_cipher_suite_mismatch(void); +int test_tls13_ticket_age_out_of_window(void); int test_wolfSSL_DisableExtendedMasterSecret(void); int test_certificate_authorities_certificate_request(void); int test_certificate_authorities_client_hello(void);