diff --git a/src/dtls13.c b/src/dtls13.c index 9c729fa1e8..a140b2d10a 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -979,7 +979,7 @@ static int Dtls13SendFragmentedInternal(WOLFSSL* ssl) { int fragLength, rlHeaderLength; int remainingSize, maxFragment; - int recordLength; + int recordLength, outputSz; byte isEncrypted; byte* output; int ret; @@ -987,23 +987,27 @@ static int Dtls13SendFragmentedInternal(WOLFSSL* ssl) isEncrypted = Dtls13TypeIsEncrypted( (enum HandShakeType)ssl->dtls13FragHandshakeType); rlHeaderLength = Dtls13GetRlHeaderLength(ssl, isEncrypted); - maxFragment = wolfSSL_GetMaxFragSize(ssl, MAX_RECORD_SIZE); + maxFragment = wolfssl_local_GetMaxPlaintextSize(ssl); remainingSize = ssl->dtls13MessageLength - ssl->dtls13FragOffset; while (remainingSize > 0) { - fragLength = maxFragment - rlHeaderLength - DTLS_HANDSHAKE_HEADER_SZ; - - recordLength = maxFragment; + fragLength = maxFragment - DTLS_HANDSHAKE_HEADER_SZ; if (fragLength > remainingSize) { fragLength = remainingSize; - recordLength = - fragLength + rlHeaderLength + DTLS_HANDSHAKE_HEADER_SZ; } - ret = CheckAvailableSize(ssl, recordLength + MAX_MSG_EXTRA); + recordLength = fragLength + rlHeaderLength + DTLS_HANDSHAKE_HEADER_SZ; + outputSz = wolfssl_local_GetRecordSize(ssl, + fragLength + DTLS_HANDSHAKE_HEADER_SZ, isEncrypted); + if (outputSz < 0) { + Dtls13FreeFragmentsBuffer(ssl); + return recordLength; + } + + ret = CheckAvailableSize(ssl, outputSz); if (ret != 0) { Dtls13FreeFragmentsBuffer(ssl); return ret; @@ -1025,7 +1029,7 @@ static int Dtls13SendFragmentedInternal(WOLFSSL* ssl) ret = Dtls13SendOneFragmentRtx(ssl, (enum HandShakeType)ssl->dtls13FragHandshakeType, - (word16)recordLength + MAX_MSG_EXTRA, output, (word32)recordLength, 0); + (word16)outputSz, output, (word32)recordLength, 0); if (ret == WC_NO_ERR_TRACE(WANT_WRITE)) { ssl->dtls13FragOffset += fragLength; return ret; @@ -2018,7 +2022,7 @@ int Dtls13HandshakeSend(WOLFSSL* ssl, byte* message, word16 outputSize, return ret; } - maxFrag = wolfSSL_GetMaxFragSize(ssl, MAX_RECORD_SIZE); + maxFrag = wolfssl_local_GetMaxPlaintextSize(ssl); maxLen = length; if (handshakeType == key_update) diff --git a/src/internal.c b/src/internal.c index dc0bad9ea8..46e4c335b5 100644 --- a/src/internal.c +++ b/src/internal.c @@ -10745,7 +10745,13 @@ static int SendHandshakeMsg(WOLFSSL* ssl, byte* input, word32 inputSz, inputSz += HANDSHAKE_HEADER_SZ; rHdrSz = RECORD_HEADER_SZ; } - maxFrag = wolfSSL_GetMaxFragSize(ssl, (int)inputSz); + maxFrag = wolfssl_local_GetMaxPlaintextSize(ssl); +#ifdef WOLFSSL_DTLS + if (ssl->options.dtls) { + /* In DTLS the handshake header is per fragment */ + maxFrag -= DTLS_HANDSHAKE_HEADER_SZ; + } +#endif /* Make sure input is not the ssl output buffer as this * function doesn't handle that */ @@ -24793,9 +24799,12 @@ int SendCertificate(WOLFSSL* ssl) if (ssl->fragOffset != 0) length -= (ssl->fragOffset + headerSz); - maxFragment = MAX_RECORD_SIZE; - maxFragment = (word32)wolfSSL_GetMaxFragSize(ssl, (int)maxFragment); + maxFragment = (word32)wolfssl_local_GetMaxPlaintextSize(ssl); + if (ssl->options.dtls) + maxFragment -= DTLS_HANDSHAKE_HEADER_SZ; + else + maxFragment -= HANDSHAKE_HEADER_SZ; while (length > 0 && ret == 0) { byte* output = NULL; @@ -25572,27 +25581,6 @@ int IsSCR(WOLFSSL* ssl) } -#ifdef WOLFSSL_DTLS -static int ModifyForMTU(WOLFSSL* ssl, int buffSz, int outputSz, int mtuSz) -{ - int recordExtra = outputSz - buffSz; - - (void)ssl; - - if (recordExtra > 0 && outputSz > mtuSz) { - buffSz = mtuSz - recordExtra; -#ifndef WOLFSSL_AEAD_ONLY - /* Subtract a block size to be certain that returned fragment - * size won't get more padding. */ - if (ssl->specs.cipher_type == block) - buffSz -= ssl->specs.block_size; -#endif - } - - return buffSz; -} -#endif /* WOLFSSL_DTLS */ - #if !defined(NO_TLS) && defined(WOLFSSL_TLS13) && \ !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS) /* @@ -25970,31 +25958,33 @@ int SendData(WOLFSSL* ssl, const void* data, size_t sz) } #endif /* WOLFSSL_DTLS13 */ - buffSz = wolfSSL_GetMaxFragSize(ssl, (word32)sz - sent); - if (sent == (word32)sz) break; -#if defined(WOLFSSL_DTLS) && !defined(WOLFSSL_NO_DTLS_SIZE_CHECK) - if (ssl->options.dtls && ((size_t)buffSz < (word32)sz - sent)) { - error = DTLS_SIZE_ERROR; - ssl->error = error; - WOLFSSL_ERROR(error); - return error; - } -#endif - outputSz = buffSz + COMP_EXTRA + DTLS_RECORD_HEADER_SZ; - if (IsEncryptionOn(ssl, 1) || ssl->options.tls1_3) - outputSz += cipherExtraData(ssl); - -#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID) + buffSz = (word32)sz - sent; + outputSz = wolfssl_local_GetRecordSize(ssl, (word32)buffSz, 1); +#if defined(WOLFSSL_DTLS) if (ssl->options.dtls) { - byte cidSz = 0; - if ((cidSz = DtlsGetCidTxSize(ssl)) > 0) - outputSz += cidSz + 1; /* +1 for inner content type */ - } +#if defined(WOLFSSL_DTLS_MTU) + int mtu = ssl->dtlsMtuSz; +#else + int mtu = MAX_MTU; #endif + if (outputSz > mtu) { +#if defined(WOLFSSL_NO_DTLS_SIZE_CHECK) + /* split instead of error out */ + buffSz = min(buffSz, wolfssl_local_GetMaxPlaintextSize(ssl)); + outputSz = wolfssl_local_GetRecordSize(ssl, (word32)buffSz, 1); +#else + error = DTLS_SIZE_ERROR; + ssl->error = error; + WOLFSSL_ERROR(error); + return error; +#endif /* WOLFSSL_NO_DTLS_SIZE_CHECK */ + } + } +#endif /* WOLFSSL_DTLS */ - /* check for available size */ + /* check for available size, it does also DTLS MTU checks */ if ((ret = CheckAvailableSize(ssl, outputSz)) != 0) return (ssl->error = ret); @@ -41811,53 +41801,125 @@ int wolfSSL_AsyncPush(WOLFSSL* ssl, WC_ASYNC_DEV* asyncDev) #endif /* WOLFSSL_ASYNC_CRYPT */ +#if !defined(NO_TLS) +/** Return the record size for sending payloadSz of data + * @param ssl WOLFSSL object + * @param payloadSz Size of data to be sent in record + * @param isEncrypted 1 if encryption is on, 0 if not + * @return Record size for sending payloadSz of data + */ +int wolfssl_local_GetRecordSize(WOLFSSL *ssl, int payloadSz, int isEncrypted) +{ + int recordSz; + if (ssl == NULL) + return BAD_FUNC_ARG; + + if (isEncrypted) { + recordSz = BuildMessage(ssl, NULL, 0, NULL, payloadSz, application_data, + 0, 1, 0, CUR_ORDER); + /* use a safe upper bound in case of error */ + if (recordSz < 0) { + recordSz = payloadSz + RECORD_HEADER_SZ + + cipherExtraData(ssl) + COMP_EXTRA; + if (ssl->options.dtls) { + recordSz += DTLS_RECORD_EXTRA; + } + } + } + else { + recordSz = payloadSz + RECORD_HEADER_SZ; + if (ssl->options.dtls) { + recordSz += DTLS_RECORD_EXTRA; + } + } + return recordSz; +} +#endif + +/** Return the maximum plaintext size for the current Max Fragment and MTU. + * @param ssl WOLFSSL object containing ciphersuite information. + * @return Max plaintext size for current MTU + */ +int wolfssl_local_GetMaxPlaintextSize(WOLFSSL *ssl) +{ + int maxFrag; + + if (ssl == NULL) + return BAD_FUNC_ARG; + + maxFrag = wolfSSL_GetMaxFragSize(ssl); + +#if defined(WOLFSSL_DTLS) + if (IsDtlsNotSctpMode(ssl)) { + int recordSz; + int mtu; + +#if defined(WOLFSSL_DTLS_MTU) + mtu = ssl->dtlsMtuSz; +#else + mtu = MAX_MTU; +#endif + + recordSz = wolfssl_local_GetRecordSize(ssl, maxFrag, + IsEncryptionOn(ssl, 1)); + /* record size of maxFrag fits in MTU */ + if (recordSz <= mtu) { + return maxFrag; + } + + /* adjust plaintext size to fit in MTU */ + maxFrag -= (recordSz - mtu); + if (maxFrag <= 0) { + WOLFSSL_MSG("MTU too small for any plaintext"); + return DTLS_SIZE_ERROR; + } + +#ifndef WOLFSSL_AEAD_ONLY + /* For block ciphers, reducing maxFrag may change padding alignment, + * causing the record to still exceed MTU. Iterate to find exact fit. + * Converges in at most 2 iterations due to bounded padding variance. */ + if (ssl->specs.cipher_type == block) { + int iter; + for (iter = 0; iter < 2; iter++) { + recordSz = wolfssl_local_GetRecordSize(ssl, maxFrag, + IsEncryptionOn(ssl, 1)); + if (recordSz <= mtu) + break; + maxFrag -= (recordSz - mtu); + } + if (recordSz > mtu) { + /* this should never happen */ + WOLFSSL_MSG("Failed to fit record in MTU after padding adjust"); + return DTLS_SIZE_ERROR; + } + } +#endif + } +#endif /* WOLFSSL_DTLS */ + + return maxFrag; +} /** * Return the max fragment size. This is essentially the maximum * fragment_length available. * @param ssl WOLFSSL object containing ciphersuite information. - * @param maxFragment The amount of space we want to check is available. This - * is only the fragment length WITHOUT the (D)TLS headers. * @return Max fragment size */ -int wolfSSL_GetMaxFragSize(WOLFSSL* ssl, int maxFragment) +int wolfSSL_GetMaxFragSize(WOLFSSL* ssl) { - (void) ssl; /* Avoid compiler warnings */ + int maxFragment; - if (maxFragment > MAX_RECORD_SIZE) { - maxFragment = MAX_RECORD_SIZE; - } + if (ssl == NULL) + return BAD_FUNC_ARG; + + maxFragment = MAX_RECORD_SIZE; #ifdef HAVE_MAX_FRAGMENT if ((ssl->max_fragment != 0) && ((word16)maxFragment > ssl->max_fragment)) { maxFragment = ssl->max_fragment; } #endif /* HAVE_MAX_FRAGMENT */ -#ifdef WOLFSSL_DTLS - if (IsDtlsNotSctpMode(ssl)) { - int outputSz, mtuSz; - - /* Given a input buffer size of maxFragment, how big will the - * encrypted output be? */ - if (IsEncryptionOn(ssl, 1)) { - outputSz = BuildMessage(ssl, NULL, 0, NULL, - maxFragment + DTLS_HANDSHAKE_HEADER_SZ, - application_data, 0, 1, 0, CUR_ORDER); - } - else { - outputSz = maxFragment + DTLS_RECORD_HEADER_SZ + - DTLS_HANDSHAKE_HEADER_SZ; - } - - /* Readjust maxFragment for MTU size. */ - #if defined(WOLFSSL_DTLS_MTU) - mtuSz = ssl->dtlsMtuSz; - #else - mtuSz = MAX_MTU; - #endif - maxFragment = ModifyForMTU(ssl, maxFragment, outputSz, mtuSz); - } -#endif return maxFragment; } diff --git a/src/ssl.c b/src/ssl.c index 4c1f7b1856..d746175283 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -2905,7 +2905,7 @@ int wolfSSL_GetMaxOutputSize(WOLFSSL* ssl) return BAD_FUNC_ARG; } - return wolfSSL_GetMaxFragSize(ssl, OUTPUT_RECORD_SIZE); + return min(OUTPUT_RECORD_SIZE, wolfssl_local_GetMaxPlaintextSize(ssl)); } @@ -2925,8 +2925,7 @@ int wolfSSL_GetOutputSize(WOLFSSL* ssl, int inSz) if (inSz > maxSize) return INPUT_SIZE_E; - return BuildMessage(ssl, NULL, 0, NULL, inSz, application_data, 0, 1, 0, - CUR_ORDER); + return wolfssl_local_GetRecordSize(ssl, inSz, 1); } diff --git a/src/tls13.c b/src/tls13.c index 6eeabb4ee6..94e10ba517 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -4522,7 +4522,7 @@ int SendTls13ClientHello(WOLFSSL* ssl) { #ifdef WOLFSSL_DTLS_CH_FRAG - word16 maxFrag = wolfSSL_GetMaxFragSize(ssl, MAX_RECORD_SIZE); + word16 maxFrag = wolfssl_local_GetMaxPlaintextSize(ssl); word16 lenWithoutExts = args->length; #endif @@ -8872,7 +8872,7 @@ static int SendTls13Certificate(WOLFSSL* ssl) if (ssl->fragOffset != 0) length -= (ssl->fragOffset + headerSz); - maxFragment = (word32)wolfSSL_GetMaxFragSize(ssl, MAX_RECORD_SIZE); + maxFragment = (word32)wolfssl_local_GetMaxPlaintextSize(ssl); extIdx = 0; diff --git a/tests/api/test_dtls.c b/tests/api/test_dtls.c index fe13b1a45c..78e83d02c4 100644 --- a/tests/api/test_dtls.c +++ b/tests/api/test_dtls.c @@ -1487,6 +1487,125 @@ int test_records_span_network_boundaries(void) #endif /* defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ !defined(WOLFSSL_NO_TLS12) */ +int test_dtls_mtu_fragment_headroom(void) +{ +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(WOLFSSL_DTLS_MTU) && defined(HAVE_AESGCM) && defined(HAVE_ECC) && \ + !defined(WOLFSSL_NO_DTLS_SIZE_CHECK) + EXPECT_DECLS; + struct { + method_provider client_meth; + method_provider server_meth; + const char* cipher; + int use_cid; + } params[] = { +#if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_TLS13) + { wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method, + "TLS13-AES128-GCM-SHA256", 0 }, +#ifdef WOLFSSL_DTLS_CID + { wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method, + "TLS13-AES128-GCM-SHA256", 1 }, +#endif +#endif +#if defined(WOLFSSL_DTLS) && !defined(WOLFSSL_NO_TLS12) + { wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method, + "ECDHE-RSA-AES128-GCM-SHA256", 0 }, +#ifdef WOLFSSL_DTLS_CID + { wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method, + "ECDHE-RSA-AES128-GCM-SHA256", 1 }, +#endif +#if !defined(WOLFSSL_AEAD_ONLY) && !defined(NO_AES) && !defined(NO_SHA) + { wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method, + "ECDHE-RSA-AES128-SHA", 0 }, +#ifdef WOLFSSL_DTLS_CID + { wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method, + "ECDHE-RSA-AES128-SHA", 1 }, +#endif +#endif +#endif + }; + size_t i; + + for (i = 0; i < XELEM_CNT(params) && EXPECT_SUCCESS(); i++) { + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + unsigned char payload[33]; + word16 mtu; + int recordLen; + int overhead; + int ret; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + XMEMSET(payload, 'A', sizeof(payload)); + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + params[i].client_meth, params[i].server_meth), + 0); + + ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, params[i].cipher), 1); + ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, params[i].cipher), 1); + +#ifdef WOLFSSL_DTLS_CID + if (params[i].use_cid) { + unsigned char cid_c[] = { 0,1,2,3 }; + unsigned char cid_s[] = { 4,5,6,7,8,9 }; + ExpectIntEQ(wolfSSL_dtls_cid_use(ssl_c), 1); + ExpectIntEQ(wolfSSL_dtls_cid_use(ssl_s), 1); + ExpectIntEQ(wolfSSL_dtls_cid_set(ssl_c, cid_s, (int)sizeof(cid_s)), + 1); + ExpectIntEQ(wolfSSL_dtls_cid_set(ssl_s, cid_c, (int)sizeof(cid_c)), + 1); + } +#endif + + /* Complete handshake and clear any leftover records. */ + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + test_memio_clear_buffer(&test_ctx, 1); + test_memio_clear_buffer(&test_ctx, 0); + + /* Measure application-data record overhead. */ + ExpectIntEQ(wolfSSL_write(ssl_c, payload, 32), 32); + ExpectIntEQ(test_ctx.s_msg_count, 1); + recordLen = test_ctx.s_len; + ExpectIntGT(recordLen, 32); + overhead = recordLen - 32; + + /* Reset buffers before MTU-limited send. */ + test_memio_clear_buffer(&test_ctx, 0); + test_memio_clear_buffer(&test_ctx, 1); + + /* Set MTU to overhead + 32 bytes of payload. */ + mtu = (word16)(overhead + 32); + ExpectIntEQ(wolfSSL_dtls_set_mtu(ssl_c, mtu), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_dtls_set_mtu(ssl_s, mtu), WOLFSSL_SUCCESS); + + /* With the tightened MTU, we should still be able to send 32 bytes. */ + ExpectIntEQ(wolfSSL_write(ssl_c, payload, 32), 32); + ExpectIntEQ(test_ctx.s_msg_count, 1); + recordLen = test_ctx.s_len; + ExpectIntEQ(recordLen, overhead + 32); + ExpectIntLE(recordLen, mtu); + + /* Underestimation: drop MTU by 1 and expect DTLS_SIZE_ERROR. */ + test_memio_clear_buffer(&test_ctx, 0); + test_memio_clear_buffer(&test_ctx, 1); + ExpectIntEQ(wolfSSL_dtls_set_mtu(ssl_c, mtu - 1), WOLFSSL_SUCCESS); + ret = wolfSSL_write(ssl_c, payload, 32); + ExpectIntNE(ret, 32); + ExpectIntEQ(wolfSSL_get_error(ssl_c, ret), DTLS_SIZE_ERROR); + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); + } + return EXPECT_RESULT(); +#else + return TEST_SKIPPED; +#endif +} + int test_dtls_rtx_across_epoch_change(void) { EXPECT_DECLS; @@ -2257,3 +2376,116 @@ int test_dtls_memio_wolfio_stateless(void) #endif return EXPECT_RESULT(); } + +int test_dtls_mtu_split_messages(void) +{ +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(WOLFSSL_DTLS_MTU) && defined(WOLFSSL_NO_DTLS_SIZE_CHECK) && \ + defined(HAVE_AESGCM) && defined(HAVE_ECC) + EXPECT_DECLS; + struct { + method_provider client_meth; + method_provider server_meth; + const char* cipher; + } params[] = { +#if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_TLS13) + { wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method, + "TLS13-AES128-GCM-SHA256" }, +#endif +#if defined(WOLFSSL_DTLS) && !defined(WOLFSSL_NO_TLS12) + { wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method, + "ECDHE-RSA-AES128-GCM-SHA256" }, +#if !defined(WOLFSSL_AEAD_ONLY) && !defined(NO_AES) && !defined(NO_SHA) + /* Block cipher test */ + { wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method, + "ECDHE-RSA-AES128-SHA" }, +#endif +#endif + }; + size_t i; + + for (i = 0; i < XELEM_CNT(params) && EXPECT_SUCCESS(); i++) { + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + /* Payload larger than typical MTU to force splitting */ + unsigned char payload[200]; + unsigned char readBuf[200]; + word16 mtu; + int recordLen; + int overhead; + int totalRead; + int ret; + int j; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + XMEMSET(payload, 'A', sizeof(payload)); + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + params[i].client_meth, params[i].server_meth), + 0); + + ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, params[i].cipher), 1); + ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, params[i].cipher), 1); + + /* Complete handshake and clear any leftover records. */ + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + test_memio_clear_buffer(&test_ctx, 1); + test_memio_clear_buffer(&test_ctx, 0); + + /* Measure application-data record overhead with small payload. */ + ExpectIntEQ(wolfSSL_write(ssl_c, payload, 32), 32); + ExpectIntEQ(test_ctx.s_msg_count, 1); + recordLen = test_ctx.s_len; + ExpectIntGT(recordLen, 32); + overhead = recordLen - 32; + + /* Reset buffers before MTU-limited send. */ + test_memio_clear_buffer(&test_ctx, 0); + test_memio_clear_buffer(&test_ctx, 1); + + /* Set MTU to allow only ~50 bytes of payload per record. + * This ensures a 200-byte payload must be split into multiple msgs. */ + mtu = (word16)(overhead + 50); + ExpectIntEQ(wolfSSL_dtls_set_mtu(ssl_c, mtu), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_dtls_set_mtu(ssl_s, mtu), WOLFSSL_SUCCESS); + + /* Write payload larger than MTU allows in single record. + * With WOLFSSL_NO_DTLS_SIZE_CHECK, this should split into multiple + * messages instead of returning DTLS_SIZE_ERROR. */ + ExpectIntEQ(wolfSSL_write(ssl_c, payload, (int)sizeof(payload)), + (int)sizeof(payload)); + + /* Verify multiple messages were sent */ + ExpectIntGT(test_ctx.s_msg_count, 1); + + /* Each record should fit within MTU */ + for (j = 0; j < test_ctx.s_msg_count && EXPECT_SUCCESS(); j++) { + ExpectIntLE(test_ctx.s_msg_sizes[j], mtu); + } + + /* Read all data on server side and verify it matches */ + totalRead = 0; + while (totalRead < (int)sizeof(payload) && EXPECT_SUCCESS()) { + ret = wolfSSL_read(ssl_s, readBuf + totalRead, + (int)sizeof(readBuf) - totalRead); + if (ret > 0) { + totalRead += ret; + } + else { + break; + } + } + ExpectIntEQ(totalRead, (int)sizeof(payload)); + ExpectIntEQ(XMEMCMP(payload, readBuf, sizeof(payload)), 0); + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); + } + return EXPECT_RESULT(); +#else + return TEST_SKIPPED; +#endif +} diff --git a/tests/api/test_dtls.h b/tests/api/test_dtls.h index c0fb4bcfba..429536bd59 100644 --- a/tests/api/test_dtls.h +++ b/tests/api/test_dtls.h @@ -47,6 +47,8 @@ int test_dtls_timeout(void); int test_dtls_certreq_order(void); int test_dtls_memio_wolfio(void); int test_dtls_memio_wolfio_stateless(void); +int test_dtls_mtu_fragment_headroom(void); +int test_dtls_mtu_split_messages(void); #define TEST_DTLS_DECLS \ TEST_DECL_GROUP("dtls", test_dtls12_basic_connection_id), \ @@ -73,5 +75,7 @@ int test_dtls_memio_wolfio_stateless(void); TEST_DECL_GROUP("dtls", test_dtls_certreq_order), \ TEST_DECL_GROUP("dtls", test_dtls_timeout), \ TEST_DECL_GROUP("dtls", test_dtls_memio_wolfio), \ + TEST_DECL_GROUP("dtls", test_dtls_mtu_fragment_headroom), \ + TEST_DECL_GROUP("dtls", test_dtls_mtu_split_messages), \ TEST_DECL_GROUP("dtls", test_dtls_memio_wolfio_stateless) #endif /* TESTS_API_DTLS_H */ diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 79182b9eaf..0ae416675a 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -6595,7 +6595,10 @@ WOLFSSL_LOCAL int VerifyClientSuite(word16 havePSK, byte cipherSuite0, byte cipherSuite); WOLFSSL_LOCAL int SetTicket(WOLFSSL* ssl, const byte* ticket, word32 length); -WOLFSSL_LOCAL int wolfSSL_GetMaxFragSize(WOLFSSL* ssl, int maxFragment); +WOLFSSL_LOCAL int wolfssl_local_GetRecordSize(WOLFSSL *ssl, int payloadSz, + int isEncrypted); +WOLFSSL_LOCAL int wolfssl_local_GetMaxPlaintextSize(WOLFSSL *ssl); +WOLFSSL_LOCAL int wolfSSL_GetMaxFragSize(WOLFSSL* ssl); #if defined(WOLFSSL_IOTSAFE) && defined(HAVE_PK_CALLBACKS) WOLFSSL_LOCAL IOTSAFE *wolfSSL_get_iotsafe_ctx(WOLFSSL *ssl);