Merge bitcoin-core/secp256k1#1852: Add exhaustive test for ECDH module

5698e66c64 Add exhaustive test for ECDH module (Sebastian Falbesoner)

Pull request description:

  This PR adds an exhaustive test for the ECDH module, looping over all key combinations and verifying the commutativity property (ECDH(i\*G, j) == ECDH(j\*G, i)) and checking against a recalculated ECDH result (by manually invoking the default ECDH hash function on the precalculated group element `group[i * j]`'s coordinates). The existing test coverage is already solid (including Wycheproof test vectors), but I figured it likely wouldn't hurt to add this as well.

ACKs for top commit:
  sipa:
    ACK 5698e66c64
  real-or-random:
    utACK 5698e66c64

Tree-SHA512: e80b8508ee61e3bf5230951393a08c8937f19d2d16220ff2b02fe69b039195a1545809d3ea420dfed1f192c3711f403c63e86c3af2bb2fc4ab1254388ba50287
This commit is contained in:
merge-script
2026-06-07 13:38:34 +02:00
3 changed files with 64 additions and 0 deletions

View File

@@ -1,5 +1,6 @@
include_HEADERS += include/secp256k1_ecdh.h
noinst_HEADERS += src/modules/ecdh/main_impl.h
noinst_HEADERS += src/modules/ecdh/tests_impl.h
noinst_HEADERS += src/modules/ecdh/tests_exhaustive_impl.h
noinst_HEADERS += src/modules/ecdh/bench_impl.h
noinst_HEADERS += src/wycheproof/ecdh_secp256k1_test.h

View File

@@ -0,0 +1,56 @@
/***********************************************************************
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_MODULE_ECDH_TESTS_EXHAUSTIVE_H
#define SECP256K1_MODULE_ECDH_TESTS_EXHAUSTIVE_H
#include "../../../include/secp256k1_ecdh.h"
#include "main_impl.h"
static void test_exhaustive_ecdh(const secp256k1_context *ctx, const secp256k1_ge *group) {
int i, j;
unsigned char seckeys[EXHAUSTIVE_TEST_ORDER - 1][32];
secp256k1_pubkey pubkeys[EXHAUSTIVE_TEST_ORDER - 1];
/* Construct key pairs (32-byte secret key, public key object) for the entire group. */
for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) {
secp256k1_scalar scalar;
secp256k1_scalar_set_int(&scalar, i);
secp256k1_scalar_get_b32(seckeys[i - 1], &scalar);
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkeys[i - 1], seckeys[i - 1]));
}
/* Loop over key combinations. */
for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) {
for (j = 1; j < EXHAUSTIVE_TEST_ORDER; j++) {
unsigned char ecdh_result_ij[32];
unsigned char ecdh_result_ji[32];
/* Calculate ECDH(i*G, j) and ECDH(j*G, i) using API function and verify that the results match. */
CHECK(secp256k1_ecdh(ctx, ecdh_result_ij, &pubkeys[i - 1], seckeys[j - 1], NULL, NULL));
CHECK(secp256k1_ecdh(ctx, ecdh_result_ji, &pubkeys[j - 1], seckeys[i - 1], NULL, NULL));
CHECK(secp256k1_memcmp_var(ecdh_result_ij, ecdh_result_ji, 32) == 0);
/* Recalculate the expected ECDH result manually by invoking the default ECDH hash
* function on the precomputed group element (group[i * j]) coordinates, and verify
* that it matches the previously calculated public API results. */
{
secp256k1_ge ecdh_ge_expected = group[(i * j) % EXHAUSTIVE_TEST_ORDER];
unsigned char ecdh_result_expected[32];
unsigned char x[32];
unsigned char y[32];
secp256k1_fe_normalize_var(&ecdh_ge_expected.x);
secp256k1_fe_normalize_var(&ecdh_ge_expected.y);
secp256k1_fe_get_b32(x, &ecdh_ge_expected.x);
secp256k1_fe_get_b32(y, &ecdh_ge_expected.y);
CHECK(secp256k1_ecdh_hash_function_default(ecdh_result_expected, x, y, NULL));
CHECK(secp256k1_memcmp_var(ecdh_result_ij, ecdh_result_expected, 32) == 0);
}
}
}
}
#endif

View File

@@ -337,6 +337,10 @@ static void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_g
*/
}
#ifdef ENABLE_MODULE_ECDH
#include "modules/ecdh/tests_exhaustive_impl.h"
#endif
#ifdef ENABLE_MODULE_RECOVERY
#include "modules/recovery/tests_exhaustive_impl.h"
#endif
@@ -437,6 +441,9 @@ int main(int argc, char** argv) {
test_exhaustive_sign(ctx, group);
test_exhaustive_verify(ctx, group);
#ifdef ENABLE_MODULE_ECDH
test_exhaustive_ecdh(ctx, group);
#endif
#ifdef ENABLE_MODULE_RECOVERY
test_exhaustive_recovery(ctx, group);
#endif