From df13f5cd52a46f7829dfbb673e6f0c704543481d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Thu, 26 Mar 2026 11:43:21 +0100 Subject: [PATCH 01/15] ECDH: document sufficient size for shared secret MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Manuel Pégourié-Gonnard --- include/mbedtls/ecdh.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/mbedtls/ecdh.h b/include/mbedtls/ecdh.h index e81d5c3a5e..3c22264a96 100644 --- a/include/mbedtls/ecdh.h +++ b/include/mbedtls/ecdh.h @@ -416,6 +416,7 @@ int mbedtls_ecdh_read_public(mbedtls_ecdh_context *ctx, * Bytes written on success. This must not be \c NULL. * \param buf The buffer to write the generated shared key to. This * must be a writable buffer of size \p blen Bytes. + * A sufficient size is given by #MBEDTLS_ECP_MAX_BYTES. * \param blen The length of the destination buffer \p buf in Bytes. * \param f_rng The RNG function to use. This must not be \c NULL. * \param p_rng The RNG context. This may be \c NULL if \p f_rng From 0d318a0f3a5aed3b5f1eedd21fc28746279c126c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Thu, 26 Mar 2026 12:00:37 +0100 Subject: [PATCH 02/15] ECDH: test calc_secret output buffer too small MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Manuel Pégourié-Gonnard --- tests/suites/test_suite_ecdh.function | 52 +++++++++++++++++++++------ 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/tests/suites/test_suite_ecdh.function b/tests/suites/test_suite_ecdh.function index 300916feaa..71fe10ce19 100644 --- a/tests/suites/test_suite_ecdh.function +++ b/tests/suites/test_suite_ecdh.function @@ -374,7 +374,8 @@ void ecdh_exchange_calc_secret(int grp_id, mbedtls_ecp_keypair our_key; mbedtls_ecp_keypair their_key; mbedtls_ecdh_context ecdh; - unsigned char shared_secret[MBEDTLS_ECP_MAX_BYTES]; + unsigned char *buf = NULL; + size_t min_buf_size = 0; size_t shared_secret_length = 0; memset(&rnd_info, 0x00, sizeof(mbedtls_test_rnd_pseudo_info)); @@ -402,17 +403,48 @@ void ecdh_exchange_calc_secret(int grp_id, &ecdh, &our_key, MBEDTLS_ECDH_OURS) == 0); } - /* Perform the ECDH calculation. */ - TEST_ASSERT(mbedtls_ecdh_calc_secret( - &ecdh, - &shared_secret_length, - shared_secret, sizeof(shared_secret), - &mbedtls_test_rnd_pseudo_rand, &rnd_info) == 0); - TEST_ASSERT(shared_secret_length == expected->len); - TEST_ASSERT(memcmp(expected->x, shared_secret, - shared_secret_length) == 0); + /* Compute minimal size of output buffer */ + min_buf_size = (our_key.grp.pbits + 7) / 8; + TEST_LE_U(min_buf_size, MBEDTLS_ECP_MAX_BYTES); + + /* Perform the ECDH calculation with exact size output buffer */ + TEST_CALLOC(buf, min_buf_size); + TEST_EQUAL(0, + mbedtls_ecdh_calc_secret( + &ecdh, &shared_secret_length, + buf, min_buf_size, + &mbedtls_test_rnd_pseudo_rand, &rnd_info)); + TEST_MEMORY_COMPARE(expected->x, expected->len, + buf, shared_secret_length); + + mbedtls_free(buf); + buf = NULL; + shared_secret_length = 0; + + /* Try again with an output buffer that's larger. */ + TEST_CALLOC(buf, min_buf_size + 1); + TEST_EQUAL(0, + mbedtls_ecdh_calc_secret( + &ecdh, &shared_secret_length, + buf, min_buf_size + 1, + &mbedtls_test_rnd_pseudo_rand, &rnd_info)); + TEST_MEMORY_COMPARE(expected->x, expected->len, + buf, shared_secret_length); + + mbedtls_free(buf); + buf = NULL; + shared_secret_length = 0; + + /* Try again with an output buffer that's too short. */ + TEST_CALLOC(buf, min_buf_size - 1); + TEST_EQUAL(MBEDTLS_ERR_ECP_BAD_INPUT_DATA, + mbedtls_ecdh_calc_secret( + &ecdh, &shared_secret_length, + buf, min_buf_size - 1, + &mbedtls_test_rnd_pseudo_rand, &rnd_info)); exit: + mbedtls_free(buf); mbedtls_ecdh_free(&ecdh); mbedtls_ecp_keypair_free(&our_key); mbedtls_ecp_keypair_free(&their_key); From 216ec7d6fc8ddcc7afddf7b4d57ad768783e80bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Mon, 30 Mar 2026 10:05:25 +0200 Subject: [PATCH 03/15] ECDH: add test cases with MSB 0 in shared secret MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The goal is to catch possible bugs where we would compute the output length based on the bitlength of the shared secret MPI. So, what matters is the most significant byte, even for Montgomery curves where the all values are written out little endian. The values were generated by the following sage script. The script could be a bit more compact by using E.random_point() instead of the conventional base point G as the peer's key, but I prefered making the script deterministic so its output can easily be checked. p256 = { "name": "p256", "p": 2^224 * (2^32 - 1) + 2^192 + 2^96 - 1, "a": -3, "b": 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b, "gx": 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296, "gy": 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5, } p521 = { "name": "p521", "p": 2^521 - 1, "a": -3, "b": 0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00, "gx": 0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66, "gy": 0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650, } x255 = { "name": "x25519", "p": 2^255 - 19, "a": 486662, "gx": 9, "gy": 14781619447589544791020593568409986887264606134616475288964881837755586237401, } x448 = { "name": "x448", "p": 2^448 - 2^224 - 1, "a": 156326, "gx": 5, "gy": 355293926785568175264127502063783334808976399387714271831880898435169088786967410002932673765864550910142774147268105838985595290606362, } def byte_len(i): return (i.bit_length() + 7) // 8 def leading0(name, p, gx, gy, a, b=None): is_montgomery = b is None if is_montgomery: E = EllipticCurve(GF(p), [0, a, 0, 1, 0]) increment = 8 # x22519 scalars have the low 3 bits unset else: E = EllipticCurve(GF(p), [a, b]) increment = 1 G = E(gx, gy) s = 2^(p.bit_length() - 1) # x25519 scalars have bit 254 set Z = G * s while byte_len(Z.x().lift()) >= byte_len(p): s += increment Z += increment * G print(name) if is_montgomery: nbytes = byte_len(p) print(f"s: {s.to_bytes(nbytes, "little").hex()}") print(f"G: {G.x().lift().to_bytes(nbytes, "little").hex()}") print(f"Z: {Z.x().lift().to_bytes(nbytes, "little").hex()}") else: nibbles = byte_len(p) * 2 print(f"s: {s:0{nibbles}x}") print(f"G: 04{G.x().lift():0{nibbles}x}{G.y().lift():0{nibbles}x}") print(f"Z: {Z.x().lift():0{nibbles}x}") leading0(**p256) leading0(**p521) leading0(**x255) leading0(**x448) Signed-off-by: Manuel Pégourié-Gonnard --- tests/suites/test_suite_psa_crypto.data | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data index b709f04003..a5fb4c712b 100644 --- a/tests/suites/test_suite_psa_crypto.data +++ b/tests/suites/test_suite_psa_crypto.data @@ -7256,6 +7256,11 @@ PSA (raw) key agreement: ECDH SECP256R1 (RFC 5903) depends_on:PSA_WANT_ALG_ECDH:PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_DERIVE:PSA_WANT_ECC_SECP_R1_256 key_agreement:PSA_ALG_ECDH:PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1):"c88f01f510d9ac3f70a292daa2316de544e9aab8afe84049c62a9c57862d1433":"04d12dfb5289c8d4f81208b70270398c342296970a0bccb74c736fc7554494bf6356fbf3ca366cc23e8157854c13c58d6aac23f046ada30f8353e74f33039872ab":"d6840f6b42f6edafd13116e0e12565202fef8e9ece7dce03812464d04b9442de":PSA_SUCCESS +# see generating script in commit message +PSA (raw) key agreement: ECDH SECP256R1 (shared secret with leading 0) +depends_on:PSA_WANT_ALG_ECDH:PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_DERIVE:PSA_WANT_ECC_SECP_R1_256 +key_agreement:PSA_ALG_ECDH:PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1):"800000000000000000000000000000000000000000000000000000000000000e":"046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5":"0054fe92ec56ef5ad8bf4d9c46d2ca773af76c2e8c8b00e20ab772cf8b34348f":PSA_SUCCESS + PSA (raw) key agreement: ECDH SECP384R1 (RFC 5903) depends_on:PSA_WANT_ALG_ECDH:PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_DERIVE:PSA_WANT_ECC_SECP_R1_384 key_agreement:PSA_ALG_ECDH:PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1):"099f3c7034d4a2c699884d73a375a67f7624ef7c6b3c0f160647b67414dce655e35b538041e649ee3faef896783ab194":"04e558dbef53eecde3d3fccfc1aea08a89a987475d12fd950d83cfa41732bc509d0d1ac43a0336def96fda41d0774a3571dcfbec7aacf3196472169e838430367f66eebe3c6e70c416dd5f0c68759dd1fff83fa40142209dff5eaad96db9e6386c":"11187331c279962d93d604243fd592cb9d0a926f422e47187521287e7156c5c4d603135569b9e9d09cf5d4a270f59746":PSA_SUCCESS @@ -7264,6 +7269,11 @@ PSA (raw) key agreement: ECDH SECP521R1 (RFC 5903) depends_on:PSA_WANT_ALG_ECDH:PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_DERIVE:PSA_WANT_ECC_SECP_R1_521 key_agreement:PSA_ALG_ECDH:PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1):"0037ade9319a89f4dabdb3ef411aaccca5123c61acab57b5393dce47608172a095aa85a30fe1c2952c6771d937ba9777f5957b2639bab072462f68c27a57382d4a52":"0400d0b3975ac4b799f5bea16d5e13e9af971d5e9b984c9f39728b5e5739735a219b97c356436adc6e95bb0352f6be64a6c2912d4ef2d0433ced2b6171640012d9460f015c68226383956e3bd066e797b623c27ce0eac2f551a10c2c724d9852077b87220b6536c5c408a1d2aebb8e86d678ae49cb57091f4732296579ab44fcd17f0fc56a":"01144c7d79ae6956bc8edb8e7c787c4521cb086fa64407f97894e5e6b2d79b04d1427e73ca4baa240a34786859810c06b3c715a3a8cc3151f2bee417996d19f3ddea":PSA_SUCCESS +# see generating script in commit message +PSA (raw) key agreement: ECDH SECP521R1 (shared secret with leading 0) +depends_on:PSA_WANT_ALG_ECDH:PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_DERIVE:PSA_WANT_ECC_SECP_R1_521 +key_agreement:PSA_ALG_ECDH:PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1):"010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000":"0400c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650":"002da7db02840f023a36e1fffeaee16d3c47bb435bec6a231d4aab1ec5412f56fb90fcc4eaab9fd8571084cb9da252466c052d21913ce0fda47e61829972ce8f9a17":PSA_SUCCESS + PSA (raw) key agreement: ECDH brainpoolP256r1 (RFC 7027) depends_on:PSA_WANT_ALG_ECDH:PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_DERIVE:PSA_WANT_ECC_BRAINPOOL_P_R1_256 key_agreement:PSA_ALG_ECDH:PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_BRAINPOOL_P_R1):"81db1ee100150ff2ea338d708271be38300cb54241d79950f77b063039804f1d":"048d2d688c6cf93e1160ad04cc4429117dc2c41825e1e9fca0addd34e6f1b39f7b990c57520812be512641e47034832106bc7d3e8dd0e4c7f1136d7006547cec6a":"89afc39d41d3b327814b80940b042590f96556ec91e6ae7939bce31f3a18bf2b":PSA_SUCCESS @@ -7284,6 +7294,11 @@ PSA (raw) key agreement: X25519 (RFC 7748: Bob) depends_on:PSA_WANT_ALG_ECDH:PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_DERIVE:PSA_WANT_ECC_MONTGOMERY_255 key_agreement:PSA_ALG_ECDH:PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_MONTGOMERY):"5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb":"8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a":"4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742":PSA_SUCCESS +# see generating script in commit message +PSA (raw) key agreement: X25519 (shared secret with MSB 0) +depends_on:PSA_WANT_ALG_ECDH:PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_DERIVE:PSA_WANT_ECC_MONTGOMERY_255 +key_agreement:PSA_ALG_ECDH:PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_MONTGOMERY):"8813000000000000000000000000000000000000000000000000000000000040":"0900000000000000000000000000000000000000000000000000000000000000":"9ec15224edbe326c5b40fa30311421f1fb309fdd0fcaf472b5d0ad2067b7db00":PSA_SUCCESS + PSA (raw) key agreement: X448 (RFC 7748: Alice) depends_on:PSA_WANT_ALG_ECDH:PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_DERIVE:PSA_WANT_ECC_MONTGOMERY_448 key_agreement:PSA_ALG_ECDH:PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_MONTGOMERY):"9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28dd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b":"3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b43027d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609":"07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282bb60c0b56fd2464c335543936521c24403085d59a449a5037514a879d":PSA_SUCCESS @@ -7292,6 +7307,11 @@ PSA (raw) key agreement: X448 (RFC 7748: Bob) depends_on:PSA_WANT_ALG_ECDH:PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_DERIVE:PSA_WANT_ECC_MONTGOMERY_448 key_agreement:PSA_ALG_ECDH:PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_MONTGOMERY):"1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d":"9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0":"07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282bb60c0b56fd2464c335543936521c24403085d59a449a5037514a879d":PSA_SUCCESS +# see generating script in commit message +PSA (raw) key agreement: X448 (shared secret with MSB 0) +depends_on:PSA_WANT_ALG_ECDH:PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_DERIVE:PSA_WANT_ECC_MONTGOMERY_448 +key_agreement:PSA_ALG_ECDH:PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_MONTGOMERY):"d805000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080":"0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000":"517b6be2c2edd157319082d4ddc688add71297284b6da74b050170035d764c19b8a14da4600715b18d8a3320a8e18a1d80fc9e6f7ea85600":PSA_SUCCESS + PSA (raw) key agreement: FFDH 2048 bits depends_on:PSA_WANT_ALG_FFDH:PSA_WANT_KEY_TYPE_DH_KEY_PAIR_BASIC:PSA_WANT_KEY_TYPE_DH_KEY_PAIR_IMPORT:PSA_WANT_DH_RFC7919_2048 key_agreement:PSA_ALG_FFDH:PSA_KEY_TYPE_DH_KEY_PAIR(PSA_DH_FAMILY_RFC7919):"4bd2bd426bda18aa94501942095ffe5a9affed1535b942f3449bce8e90f9e57f512c8fdda496c3ac051d951be206365fb5dd03a7d7db5236b98ddfa68237a45ef4513b381a82863cdb6521b44e10aa45de28d040326c5d95e9399ae25f6cad681f1cbf8c71934b91d5c8765f56d3978544784f297aa60afadd824e4b9525867fea33d873c379e3e7bd48528ec89aa01691b57df1c87c871b955331697e6a64db0837e1d24c80e2770179a98cae9da54d21cc5af4cc7b713b04554e2cdf417d78f12e8c749a2669e036a5b89eda7b087eb911c629f16128ab04f0ee7a3a9bec5772cfc68bbd0b492a781b36d26c2ec1f83953e192247e52714c3f32f0635f698c":"6d34e084b8d0e253a894237be9977e1a821b556ed4bc01cda691a927885979b59e55a30daa2a707769474b760e9f1c10544b2ce74b26efa4f069e05ce70471bf6b7e6c08a16fa880930790204e8b482478de0682ce3f58450a4e15abc14d05e13ef773a10a3e8bf2219f8ab556c88dc2a301b362c2d4e94bf2f0006bb36d15a5096ed1342f3f111ccf123ceae9bdc7bc0cde5edc9f0203f35f8a98aff6d75975357733a429364ed3aca32acaf9f857ef751e0e246140eebdfc2b403b644e42c48922f7f6cdaa6a2ef9ddfa54fb83657492f9f9a2c8aa4831601f9b11663e94d968d8be6e121aee2c79156e44aaa650bb26083983a76cc5883538d4794855ded1":"718ab2b5da3bc6e7767a98fb2c172bd74003fae2acffbc9a53d9b358401c1c748da36cab277e9397bc5eeec3010321d0f882d959eb097adddc99745526b213e30dc0df9fb1e4cd3fc27bfb1d6e89c715373439a66b9a13aa1334c84799827c17be1c36c1bc02fe60ea698da790fe4d2af710a435a1aae7fb11cd2a90a17ad87dde4f154b325dc47d8ea107a29d10a3bfa17149a1f9e8a1f7b680bfdca90fb0913c0b681670d904de49d7d000d24060330d4d2e4a2381d78c49e272d313174218561ceeb37e2ef824905d0fa42d13d49a73018411aeb749f7f4fc765bdc6db58bcebd995d4c949b0061f20759e1263d8f9ba3fd56afda07c178997256bb7d5230":PSA_SUCCESS From 058f57a4dd7cc06a05263cb4e9482fbd9d7954b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Mon, 30 Mar 2026 10:20:33 +0200 Subject: [PATCH 04/15] ECDH: test MSB 0 in shared secret (legacy API) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The values were generated using the script in the message of the previous commit. Currently all 4 new tests are failing: without sanitizers, the function returns 0 instead of an error; with sanitizers, the buffer overwrite is caught. This will be fixed in the next commit. Signed-off-by: Manuel Pégourié-Gonnard --- tests/suites/test_suite_ecdh.data | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/suites/test_suite_ecdh.data b/tests/suites/test_suite_ecdh.data index 8d0606704f..395bbbbde2 100644 --- a/tests/suites/test_suite_ecdh.data +++ b/tests/suites/test_suite_ecdh.data @@ -81,6 +81,16 @@ ECDH calc_secret: theirs first, SECP256R1 (RFC 5903) depends_on:MBEDTLS_ECP_DP_SECP256R1_ENABLED ecdh_exchange_calc_secret:MBEDTLS_ECP_DP_SECP256R1:"c6ef9c5d78ae012a011164acb397ce2088685d8f06bf9be0b283ab46476bee53":"04dad0b65394221cf9b051e1feca5787d098dfe637fc90b9ef945d0c37725811805271a0461cdb8252d61f1c456fa3e59ab1f45b33accf5f58389e0577b8990bb3":1:"d6840f6b42f6edafd13116e0e12565202fef8e9ece7dce03812464d04b9442de" +# see generating script in previous commit message +ECDH calc_secret: leading zero, SECP256R1 +depends_on:MBEDTLS_ECP_DP_SECP256R1_ENABLED +ecdh_exchange_calc_secret:MBEDTLS_ECP_DP_SECP256R1:"800000000000000000000000000000000000000000000000000000000000000e":"046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5":0:"0054fe92ec56ef5ad8bf4d9c46d2ca773af76c2e8c8b00e20ab772cf8b34348f" + +# see generating script in previous commit message +ECDH calc_secret: leading zero, SECP521R1 +depends_on:MBEDTLS_ECP_DP_SECP521R1_ENABLED +ecdh_exchange_calc_secret:MBEDTLS_ECP_DP_SECP521R1:"010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000":"0400c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650":0:"002da7db02840f023a36e1fffeaee16d3c47bb435bec6a231d4aab1ec5412f56fb90fcc4eaab9fd8571084cb9da252466c052d21913ce0fda47e61829972ce8f9a17" + ecdh calc_secret: ours first (Alice), curve25519 (rfc 7748) depends_on:MBEDTLS_ECP_DP_CURVE25519_ENABLED ecdh_exchange_calc_secret:MBEDTLS_ECP_DP_CURVE25519:"77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a":"de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f":0:"4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742" @@ -93,6 +103,16 @@ ecdh calc_secret: ours first (Bob), curve25519 (rfc 7748) depends_on:MBEDTLS_ECP_DP_CURVE25519_ENABLED ecdh_exchange_calc_secret:MBEDTLS_ECP_DP_CURVE25519:"5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb":"8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a":0:"4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742" +# see generating script in previous commit message +ecdh calc_secret: MSB zero, curve25519 +depends_on:MBEDTLS_ECP_DP_CURVE25519_ENABLED +ecdh_exchange_calc_secret:MBEDTLS_ECP_DP_CURVE25519:"8813000000000000000000000000000000000000000000000000000000000040":"0900000000000000000000000000000000000000000000000000000000000000":0:"9ec15224edbe326c5b40fa30311421f1fb309fdd0fcaf472b5d0ad2067b7db00" + +# see generating script in previous commit message +ecdh calc_secret: MSB zero, curve448 +depends_on:MBEDTLS_ECP_DP_CURVE448_ENABLED +ecdh_exchange_calc_secret:MBEDTLS_ECP_DP_CURVE448:"d805000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080":"0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000":0:"517b6be2c2edd157319082d4ddc688add71297284b6da74b050170035d764c19b8a14da4600715b18d8a3320a8e18a1d80fc9e6f7ea85600" + ECDH get_params with mismatched groups: our BP256R1, their SECP256R1 depends_on:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_ECP_DP_BP256R1_ENABLED ecdh_exchange_get_params_fail:MBEDTLS_ECP_DP_BP256R1:"1234567812345678123456781234567812345678123456781234567812345678":MBEDTLS_ECP_DP_SECP256R1:"04dad0b65394221cf9b051e1feca5787d098dfe637fc90b9ef945d0c37725811805271a0461cdb8252d61f1c456fa3e59ab1f45b33accf5f58389e0577b8990bb3":0:MBEDTLS_ERR_ECP_BAD_INPUT_DATA From 1d71bcc31cb8080b14b0dcbc6bc859c11d622c0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Mon, 30 Mar 2026 11:20:52 +0200 Subject: [PATCH 05/15] ECDH: fix possible buffer overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We were not comparing the size of the output buffer with the actual number of bytes we were going to write to it, but with something possibly smaller. Signed-off-by: Manuel Pégourié-Gonnard --- library/ecdh.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/library/ecdh.c b/library/ecdh.c index b276c6adad..8f83fa842e 100644 --- a/library/ecdh.c +++ b/library/ecdh.c @@ -646,11 +646,13 @@ static int ecdh_calc_secret_internal(mbedtls_ecdh_context_mbed *ctx, } #endif /* MBEDTLS_ECP_RESTARTABLE */ - if (mbedtls_mpi_size(&ctx->z) > blen) { + size_t p_bytes = ctx->grp.pbits / 8 + ((ctx->grp.pbits % 8) != 0); + + if (p_bytes > blen) { return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; } - *olen = ctx->grp.pbits / 8 + ((ctx->grp.pbits % 8) != 0); + *olen = p_bytes; if (mbedtls_ecp_get_type(&ctx->grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) { return mbedtls_mpi_write_binary_le(&ctx->z, buf, *olen); From 070ba17a8afd02df563d12fdb8d478cbfb5d39bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Mon, 30 Mar 2026 11:29:43 +0200 Subject: [PATCH 06/15] ECDH: Add ChangeLog for legacy buffer overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Manuel Pégourié-Gonnard --- ChangeLog.d/ecdh-legacy-buffer-overflow.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 ChangeLog.d/ecdh-legacy-buffer-overflow.txt diff --git a/ChangeLog.d/ecdh-legacy-buffer-overflow.txt b/ChangeLog.d/ecdh-legacy-buffer-overflow.txt new file mode 100644 index 0000000000..aa3b5811d8 --- /dev/null +++ b/ChangeLog.d/ecdh-legacy-buffer-overflow.txt @@ -0,0 +1,7 @@ +Security + * Possible buffer overflow in mbedtls_ecdh_calc_secret(): when the provided + output buffer is too small, sometimes (depending on the value of the + computed shared secret), the function would not return an error as it + should, but instead write past the end of the buffer, with consequences + that may range up to arbitrary code execution depending on what follows the + buffer. Reported by Eva Crystal (0xiviel) from XSource Security. From f34f6138ca6f4afc6f203b01cbf4649de0fda493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Mon, 30 Mar 2026 11:35:29 +0200 Subject: [PATCH 07/15] TLS: pass actual size to ecdh_calc_secret() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is probably a leftover from ancient times where the premaster buffer was always that size (for FFDH). But these days it can be smaller depending on the compile-time config. So, use the correct size macro. Signed-off-by: Manuel Pégourié-Gonnard --- library/ssl_tls12_client.c | 2 +- library/ssl_tls12_server.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/ssl_tls12_client.c b/library/ssl_tls12_client.c index 0196c0cc53..a56e8cd11c 100644 --- a/library/ssl_tls12_client.c +++ b/library/ssl_tls12_client.c @@ -2910,7 +2910,7 @@ ecdh_calc_secret: if ((ret = mbedtls_ecdh_calc_secret(&ssl->handshake->ecdh_ctx, &ssl->handshake->pmslen, ssl->handshake->premaster, - MBEDTLS_MPI_MAX_SIZE, + MBEDTLS_PREMASTER_SIZE, ssl->conf->f_rng, ssl->conf->p_rng)) != 0) { MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ecdh_calc_secret", ret); #if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c index d2ee9a0760..a6783ef2a0 100644 --- a/library/ssl_tls12_server.c +++ b/library/ssl_tls12_server.c @@ -3675,7 +3675,7 @@ static int ssl_parse_client_key_exchange(mbedtls_ssl_context *ssl) if ((ret = mbedtls_ecdh_calc_secret(&ssl->handshake->ecdh_ctx, &ssl->handshake->pmslen, ssl->handshake->premaster, - MBEDTLS_MPI_MAX_SIZE, + MBEDTLS_PREMASTER_SIZE, ssl->conf->f_rng, ssl->conf->p_rng)) != 0) { MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ecdh_calc_secret", ret); return MBEDTLS_ERR_SSL_DECODE_ERROR; From fbabe53462ca284d85b85bbc744853269e6044c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Tue, 31 Mar 2026 10:18:08 +0200 Subject: [PATCH 08/15] ECDH: Everest: Fix private key managment (static) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wiping out our private key is OK if we're only doing ephemeral ECDH, but our API also supports static Diffie-Hellman, where our private key is long term and should not be wiped out on first use... Found accidentally with the new test that try calling `calc_secret()` again with a larger output buffer and expect the same result. Signed-off-by: Manuel Pégourié-Gonnard --- 3rdparty/everest/library/x25519.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/3rdparty/everest/library/x25519.c b/3rdparty/everest/library/x25519.c index 83064dc619..68b636f487 100644 --- a/3rdparty/everest/library/x25519.c +++ b/3rdparty/everest/library/x25519.c @@ -131,12 +131,16 @@ int mbedtls_x25519_calc_secret( mbedtls_x25519_context *ctx, size_t *olen, if( blen < *olen ) return( MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL ); - Hacl_Curve25519_crypto_scalarmult( buf, ctx->our_secret, ctx->peer_point); + /* scalarmult modifies this input, let's make a copy... */ + unsigned char secret[MBEDTLS_X25519_KEY_SIZE_BYTES]; + memcpy(secret, ctx->our_secret, sizeof(secret)); - /* Wipe the DH secret and don't let the peer chose a small subgroup point */ - mbedtls_platform_zeroize( ctx->our_secret, MBEDTLS_X25519_KEY_SIZE_BYTES ); + Hacl_Curve25519_crypto_scalarmult( buf, secret, ctx->peer_point); - if( memcmp( buf, ctx->our_secret, MBEDTLS_X25519_KEY_SIZE_BYTES) == 0 ) + /* Wipe the copy and don't let the peer chose a small subgroup point */ + mbedtls_platform_zeroize( secret, MBEDTLS_X25519_KEY_SIZE_BYTES ); + + if( memcmp( buf, secret, MBEDTLS_X25519_KEY_SIZE_BYTES) == 0 ) return MBEDTLS_ERR_ECP_RANDOM_FAILED; return( 0 ); From a2ca5fb23a2c16d09f0283d425c5e029c2c80f33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Tue, 31 Mar 2026 10:28:45 +0200 Subject: [PATCH 09/15] ECDH: Everest: align error code with ecdh.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Arguably the value returned by Everest was more precise, but considering we're in an LTS branch, I'd rather not change the value returned by ecdh.c, and align the special case on the general case. The added tests made it apparent that Everest and the generic code were not returning the same value here. Alignment between the two values is desirable to keep the test code simple and sweet. Signed-off-by: Manuel Pégourié-Gonnard --- 3rdparty/everest/library/x25519.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/everest/library/x25519.c b/3rdparty/everest/library/x25519.c index 68b636f487..6763d04aed 100644 --- a/3rdparty/everest/library/x25519.c +++ b/3rdparty/everest/library/x25519.c @@ -129,7 +129,7 @@ int mbedtls_x25519_calc_secret( mbedtls_x25519_context *ctx, size_t *olen, *olen = MBEDTLS_X25519_KEY_SIZE_BYTES; if( blen < *olen ) - return( MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL ); + return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA ); /* scalarmult modifies this input, let's make a copy... */ unsigned char secret[MBEDTLS_X25519_KEY_SIZE_BYTES]; From 89aee1e63ad7aa12ac254f6cf064aec69e4b0df1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Thu, 2 Apr 2026 09:35:42 +0200 Subject: [PATCH 10/15] Improve Changelog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Manuel Pégourié-Gonnard --- ChangeLog.d/ecdh-legacy-buffer-overflow.txt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ChangeLog.d/ecdh-legacy-buffer-overflow.txt b/ChangeLog.d/ecdh-legacy-buffer-overflow.txt index aa3b5811d8..490fd2ed6f 100644 --- a/ChangeLog.d/ecdh-legacy-buffer-overflow.txt +++ b/ChangeLog.d/ecdh-legacy-buffer-overflow.txt @@ -1,7 +1,6 @@ Security - * Possible buffer overflow in mbedtls_ecdh_calc_secret(): when the provided - output buffer is too small, sometimes (depending on the value of the - computed shared secret), the function would not return an error as it - should, but instead write past the end of the buffer, with consequences - that may range up to arbitrary code execution depending on what follows the - buffer. Reported by Eva Crystal (0xiviel) from XSource Security. + * Fix a possible buffer overflow in mbedtls_ecdh_calc_secret(): when the + provided output buffer is too small, sometimes (depending on the value of + the computed shared secret), the function would not return an error as it + should, but instead write past the end of the buffer. Reported by Eva + Crystal (0xiviel) from XSource Security. CVE-2026-35336 From 801f3b3f429a5198f2622ec33c0ab799fa8c2dfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Thu, 2 Apr 2026 09:39:00 +0200 Subject: [PATCH 11/15] ECDH: improve comment in test function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Manuel Pégourié-Gonnard --- tests/suites/test_suite_ecdh.function | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/suites/test_suite_ecdh.function b/tests/suites/test_suite_ecdh.function index 71fe10ce19..9f469a4ed3 100644 --- a/tests/suites/test_suite_ecdh.function +++ b/tests/suites/test_suite_ecdh.function @@ -422,6 +422,10 @@ void ecdh_exchange_calc_secret(int grp_id, shared_secret_length = 0; /* Try again with an output buffer that's larger. */ + /* Note: this doubles as a weak test for re-using a context. A proper test + * for static ECDH with context re-use should also import multiple peer keys + * in a row and compute the corresponding shared secrets, but the below is + * already better then nothing. */ TEST_CALLOC(buf, min_buf_size + 1); TEST_EQUAL(0, mbedtls_ecdh_calc_secret( From 559525cd7f6499b0baf8ca121b640283424b9995 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Thu, 2 Apr 2026 09:42:05 +0200 Subject: [PATCH 12/15] ECDH: use more precise error code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The documentation says "or another error code", so it's OK to change the error code returned here for a more correct one. Signed-off-by: Manuel Pégourié-Gonnard --- 3rdparty/everest/library/x25519.c | 2 +- library/ecdh.c | 2 +- tests/suites/test_suite_ecdh.function | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/3rdparty/everest/library/x25519.c b/3rdparty/everest/library/x25519.c index 6763d04aed..68b636f487 100644 --- a/3rdparty/everest/library/x25519.c +++ b/3rdparty/everest/library/x25519.c @@ -129,7 +129,7 @@ int mbedtls_x25519_calc_secret( mbedtls_x25519_context *ctx, size_t *olen, *olen = MBEDTLS_X25519_KEY_SIZE_BYTES; if( blen < *olen ) - return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA ); + return( MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL ); /* scalarmult modifies this input, let's make a copy... */ unsigned char secret[MBEDTLS_X25519_KEY_SIZE_BYTES]; diff --git a/library/ecdh.c b/library/ecdh.c index 8f83fa842e..d0ebe3f9c5 100644 --- a/library/ecdh.c +++ b/library/ecdh.c @@ -649,7 +649,7 @@ static int ecdh_calc_secret_internal(mbedtls_ecdh_context_mbed *ctx, size_t p_bytes = ctx->grp.pbits / 8 + ((ctx->grp.pbits % 8) != 0); if (p_bytes > blen) { - return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + return MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; } *olen = p_bytes; diff --git a/tests/suites/test_suite_ecdh.function b/tests/suites/test_suite_ecdh.function index 9f469a4ed3..121ae4657a 100644 --- a/tests/suites/test_suite_ecdh.function +++ b/tests/suites/test_suite_ecdh.function @@ -441,7 +441,7 @@ void ecdh_exchange_calc_secret(int grp_id, /* Try again with an output buffer that's too short. */ TEST_CALLOC(buf, min_buf_size - 1); - TEST_EQUAL(MBEDTLS_ERR_ECP_BAD_INPUT_DATA, + TEST_EQUAL(MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL, mbedtls_ecdh_calc_secret( &ecdh, &shared_secret_length, buf, min_buf_size - 1, From 7e45882dc278137ccb0df866fc4ed38c5af73f6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Mon, 13 Apr 2026 11:23:17 +0200 Subject: [PATCH 13/15] ECDH: add tests for static ECDH with context reuse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A bug there was fixed in a previous commit, but without complete tests or a ChangeLog entry. This commit completes the fix. Signed-off-by: Manuel Pégourié-Gonnard --- ChangeLog.d/fix-ecdh-context-reuse.txt | 4 ++ tests/suites/test_suite_ecdh.data | 16 ++++++ tests/suites/test_suite_ecdh.function | 73 ++++++++++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 ChangeLog.d/fix-ecdh-context-reuse.txt diff --git a/ChangeLog.d/fix-ecdh-context-reuse.txt b/ChangeLog.d/fix-ecdh-context-reuse.txt new file mode 100644 index 0000000000..51b2bd0b25 --- /dev/null +++ b/ChangeLog.d/fix-ecdh-context-reuse.txt @@ -0,0 +1,4 @@ +Bugfix + * Fix bug in configurations with MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED that + prevented re-use of an mbedtls_ecdh_context for static ECDH: our secret key + was wiped after its first use to compute a shared secret. diff --git a/tests/suites/test_suite_ecdh.data b/tests/suites/test_suite_ecdh.data index 395bbbbde2..de0dbdc33a 100644 --- a/tests/suites/test_suite_ecdh.data +++ b/tests/suites/test_suite_ecdh.data @@ -113,6 +113,22 @@ ecdh calc_secret: MSB zero, curve448 depends_on:MBEDTLS_ECP_DP_CURVE448_ENABLED ecdh_exchange_calc_secret:MBEDTLS_ECP_DP_CURVE448:"d805000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080":"0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000":0:"517b6be2c2edd157319082d4ddc688add71297284b6da74b050170035d764c19b8a14da4600715b18d8a3320a8e18a1d80fc9e6f7ea85600" +ECDH context re-use: secp256r1 +depends_on:MBEDTLS_ECP_DP_SECP256R1_ENABLED +ecdh_context_reuse:MBEDTLS_ECP_DP_SECP256R1:"8000000000000000000000000000000000000000000000000000000000000000":"046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5":"77b20a912e6b23135066e911891524bc4efe3560e3e92350b52dec8f375f2b54":"047cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc4766997807775510db8ed040293d9ac69f7430dbba7dade63ce982299e04b79d227873d1":"0b197a2e1e67a44b5afb62de48adde6400b60867487cab5739912513c420924a" + +ECDH context re-use: secp521r1 +depends_on:MBEDTLS_ECP_DP_SECP521R1_ENABLED +ecdh_context_reuse:MBEDTLS_ECP_DP_SECP521R1:"010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000":"0400c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650":"002da7db02840f023a36e1fffeaee16d3c47bb435bec6a231d4aab1ec5412f56fb90fcc4eaab9fd8571084cb9da252466c052d21913ce0fda47e61829972ce8f9a17":"0400433c219024277e7e682fcb288148c282747403279b1ccc06352c6e5505d769be97b3b204da6ef55507aa104a3a35c5af41cf2fa364d60fd967f43e3933ba6d783d00f4bb8cc7f86db26700a7f3eceeeed3f0b5c6b5107c4da97740ab21a29906c42dbbb3e377de9f251f6b93937fa99a3248f4eafcbe95edc0f4f71be356d661f41b02":"0033ffb964e05d5f6799c7865c906e2a0bd0c9b131eef6bf6453c960bca9bf06dea4650bd0df069416992b17027d972d1c60830492593fc3431582e051426b4c3f67" + +ECDH context re-use: x25519 +depends_on:MBEDTLS_ECP_DP_CURVE25519_ENABLED +ecdh_context_reuse:MBEDTLS_ECP_DP_CURVE25519:"0000000000000000000000000000000000000000000000000000000000000040":"0900000000000000000000000000000000000000000000000000000000000000":"2fe57da347cd62431528daac5fbb290730fff684afc4cfc2ed90995f58cb3b74":"fb4e68dd9c46ae5c5c0b351eed5c3f8f1471157d680c75d9b7f17318d542d320":"9d8e35e77dfa6c16ed6df587251ee0d6379bd2556344aecf8f85d83fb6fa6476" + +ECDH context re-use: x448 +depends_on:MBEDTLS_ECP_DP_CURVE448_ENABLED +ecdh_context_reuse:MBEDTLS_ECP_DP_CURVE448:"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080":"0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000":"e9b820a44dba3bc569bee7214b62b09ee239b50978a7a1c69a9ade46858cc37c48eb03fd88c289badd708fc635c7d863cc40e4dfdd6d5d40":"b63c741cbca4327d0808125a25f0e82571ed00daf14737bfc16f261666763c5fd7831e51fbbe9aaccc5cbdd86546ef9ad4e3ca5722329163":"b23511da3c119e32b7b146122b59d1f122f22008f02715326e62d6ca162b4f20188a95a52dd9e40cb5b729f1f153da0d51c3acfc4c81944c" + ECDH get_params with mismatched groups: our BP256R1, their SECP256R1 depends_on:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_ECP_DP_BP256R1_ENABLED ecdh_exchange_get_params_fail:MBEDTLS_ECP_DP_BP256R1:"1234567812345678123456781234567812345678123456781234567812345678":MBEDTLS_ECP_DP_SECP256R1:"04dad0b65394221cf9b051e1feca5787d098dfe637fc90b9ef945d0c37725811805271a0461cdb8252d61f1c456fa3e59ab1f45b33accf5f58389e0577b8990bb3":0:MBEDTLS_ERR_ECP_BAD_INPUT_DATA diff --git a/tests/suites/test_suite_ecdh.function b/tests/suites/test_suite_ecdh.function index 121ae4657a..0bae4c46d0 100644 --- a/tests/suites/test_suite_ecdh.function +++ b/tests/suites/test_suite_ecdh.function @@ -363,6 +363,79 @@ exit: } /* END_CASE */ +/* BEGIN_CASE */ +void ecdh_context_reuse(int grp_id, data_t *our_private_key, + data_t *their_point1, data_t *shared1, + data_t *their_point2, data_t *shared2) +{ + /* This test exercises re-using the context for static ECDH */ + mbedtls_test_rnd_pseudo_info rnd_info; + mbedtls_ecp_keypair our_key; + mbedtls_ecp_keypair their_key1; + mbedtls_ecp_keypair their_key2; + mbedtls_ecdh_context ecdh; + unsigned char *buf = NULL; + size_t min_buf_size = 0; + size_t shared_secret_length = 0; + + memset(&rnd_info, 0x00, sizeof(mbedtls_test_rnd_pseudo_info)); + mbedtls_ecdh_init(&ecdh); + mbedtls_ecp_keypair_init(&our_key); + mbedtls_ecp_keypair_init(&their_key1); + mbedtls_ecp_keypair_init(&their_key2); + + if (!load_private_key(grp_id, our_private_key, &our_key, &rnd_info)) { + goto exit; + } + if (!load_public_key(grp_id, their_point1, &their_key1)) { + goto exit; + } + if (!load_public_key(grp_id, their_point2, &their_key2)) { + goto exit; + } + + /* Import our long-term private key */ + TEST_ASSERT(mbedtls_ecdh_get_params( + &ecdh, &our_key, MBEDTLS_ECDH_OURS) == 0); + + /* Allocate output buffer of minimal size */ + min_buf_size = (our_key.grp.pbits + 7) / 8; + TEST_LE_U(min_buf_size, MBEDTLS_ECP_MAX_BYTES); + TEST_CALLOC(buf, min_buf_size); + + /* Import first peer key and perform first ECDH calculation */ + TEST_ASSERT(mbedtls_ecdh_get_params( + &ecdh, &their_key1, MBEDTLS_ECDH_THEIRS) == 0); + TEST_EQUAL(0, + mbedtls_ecdh_calc_secret( + &ecdh, &shared_secret_length, + buf, min_buf_size, + &mbedtls_test_rnd_pseudo_rand, &rnd_info)); + TEST_MEMORY_COMPARE(shared1->x, shared1->len, + buf, shared_secret_length); + + shared_secret_length = 0; + + /* Import 2nd peer key and perform 2nd ECDH calculation */ + TEST_ASSERT(mbedtls_ecdh_get_params( + &ecdh, &their_key2, MBEDTLS_ECDH_THEIRS) == 0); + TEST_EQUAL(0, + mbedtls_ecdh_calc_secret( + &ecdh, &shared_secret_length, + buf, min_buf_size, + &mbedtls_test_rnd_pseudo_rand, &rnd_info)); + TEST_MEMORY_COMPARE(shared2->x, shared2->len, + buf, shared_secret_length); + +exit: + mbedtls_free(buf); + mbedtls_ecdh_free(&ecdh); + mbedtls_ecp_keypair_free(&our_key); + mbedtls_ecp_keypair_free(&their_key1); + mbedtls_ecp_keypair_free(&their_key2); +} +/* END_CASE */ + /* BEGIN_CASE */ void ecdh_exchange_calc_secret(int grp_id, data_t *our_private_key, From d5cf74c7da74686060a4b155e5f4941ddd4100e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Mon, 13 Apr 2026 11:38:07 +0200 Subject: [PATCH 14/15] ECDH: update comment to reference new test fucntion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Manuel Pégourié-Gonnard --- tests/suites/test_suite_ecdh.function | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/suites/test_suite_ecdh.function b/tests/suites/test_suite_ecdh.function index 0bae4c46d0..d65513c6e9 100644 --- a/tests/suites/test_suite_ecdh.function +++ b/tests/suites/test_suite_ecdh.function @@ -495,10 +495,8 @@ void ecdh_exchange_calc_secret(int grp_id, shared_secret_length = 0; /* Try again with an output buffer that's larger. */ - /* Note: this doubles as a weak test for re-using a context. A proper test - * for static ECDH with context re-use should also import multiple peer keys - * in a row and compute the corresponding shared secrets, but the below is - * already better then nothing. */ + /* Note: this doubles as a weak test for re-using a context. + * For a proper test, see ecdh_context_reuse(). */ TEST_CALLOC(buf, min_buf_size + 1); TEST_EQUAL(0, mbedtls_ecdh_calc_secret( From 00d767aef58b69ac97ae718e9c941775788f93e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Mon, 13 Apr 2026 11:55:07 +0200 Subject: [PATCH 15/15] Copy-editing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Manuel Pégourié-Gonnard --- 3rdparty/everest/library/x25519.c | 2 +- ChangeLog.d/ecdh-legacy-buffer-overflow.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/3rdparty/everest/library/x25519.c b/3rdparty/everest/library/x25519.c index 68b636f487..526dc37954 100644 --- a/3rdparty/everest/library/x25519.c +++ b/3rdparty/everest/library/x25519.c @@ -137,7 +137,7 @@ int mbedtls_x25519_calc_secret( mbedtls_x25519_context *ctx, size_t *olen, Hacl_Curve25519_crypto_scalarmult( buf, secret, ctx->peer_point); - /* Wipe the copy and don't let the peer chose a small subgroup point */ + /* Wipe the copy and don't let the peer choose a small subgroup point */ mbedtls_platform_zeroize( secret, MBEDTLS_X25519_KEY_SIZE_BYTES ); if( memcmp( buf, secret, MBEDTLS_X25519_KEY_SIZE_BYTES) == 0 ) diff --git a/ChangeLog.d/ecdh-legacy-buffer-overflow.txt b/ChangeLog.d/ecdh-legacy-buffer-overflow.txt index 490fd2ed6f..54deb795e1 100644 --- a/ChangeLog.d/ecdh-legacy-buffer-overflow.txt +++ b/ChangeLog.d/ecdh-legacy-buffer-overflow.txt @@ -3,4 +3,4 @@ Security provided output buffer is too small, sometimes (depending on the value of the computed shared secret), the function would not return an error as it should, but instead write past the end of the buffer. Reported by Eva - Crystal (0xiviel) from XSource Security. CVE-2026-35336 + Crystal (0xiviel) from XSource Security. CVE-2026-35336.