Add exhaustive test for extrakeys and schnorrsig

This commit is contained in:
Pieter Wuille 2020-09-04 18:58:25 -07:00
parent 08d7d89299
commit 8b7dcdd955
5 changed files with 290 additions and 0 deletions

View File

@ -1,3 +1,4 @@
include_HEADERS += include/secp256k1_extrakeys.h include_HEADERS += include/secp256k1_extrakeys.h
noinst_HEADERS += src/modules/extrakeys/tests_impl.h noinst_HEADERS += src/modules/extrakeys/tests_impl.h
noinst_HEADERS += src/modules/extrakeys/tests_exhaustive_impl.h
noinst_HEADERS += src/modules/extrakeys/main_impl.h noinst_HEADERS += src/modules/extrakeys/main_impl.h

View File

@ -0,0 +1,68 @@
/**********************************************************************
* Copyright (c) 2020 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
#ifndef _SECP256K1_MODULE_EXTRAKEYS_TESTS_EXHAUSTIVE_
#define _SECP256K1_MODULE_EXTRAKEYS_TESTS_EXHAUSTIVE_
#include "src/modules/extrakeys/main_impl.h"
#include "include/secp256k1_extrakeys.h"
static void test_exhaustive_extrakeys(const secp256k1_context *ctx, const secp256k1_ge* group) {
secp256k1_keypair keypair[EXHAUSTIVE_TEST_ORDER - 1];
secp256k1_pubkey pubkey[EXHAUSTIVE_TEST_ORDER - 1];
secp256k1_xonly_pubkey xonly_pubkey[EXHAUSTIVE_TEST_ORDER - 1];
int parities[EXHAUSTIVE_TEST_ORDER - 1];
unsigned char xonly_pubkey_bytes[EXHAUSTIVE_TEST_ORDER - 1][32];
int i;
for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) {
secp256k1_fe fe;
secp256k1_scalar scalar_i;
unsigned char buf[33];
int parity;
secp256k1_scalar_set_int(&scalar_i, i);
secp256k1_scalar_get_b32(buf, &scalar_i);
/* Construct pubkey and keypair. */
CHECK(secp256k1_keypair_create(ctx, &keypair[i - 1], buf));
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey[i - 1], buf));
/* Construct serialized xonly_pubkey from keypair. */
CHECK(secp256k1_keypair_xonly_pub(ctx, &xonly_pubkey[i - 1], &parities[i - 1], &keypair[i - 1]));
CHECK(secp256k1_xonly_pubkey_serialize(ctx, xonly_pubkey_bytes[i - 1], &xonly_pubkey[i - 1]));
/* Parse the xonly_pubkey back and verify it matches the previously serialized value. */
CHECK(secp256k1_xonly_pubkey_parse(ctx, &xonly_pubkey[i - 1], xonly_pubkey_bytes[i - 1]));
CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf, &xonly_pubkey[i - 1]));
CHECK(memcmp(xonly_pubkey_bytes[i - 1], buf, 32) == 0);
/* Construct the xonly_pubkey from the pubkey, and verify it matches the same. */
CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pubkey[i - 1], &parity, &pubkey[i - 1]));
CHECK(parity == parities[i - 1]);
CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf, &xonly_pubkey[i - 1]));
CHECK(memcmp(xonly_pubkey_bytes[i - 1], buf, 32) == 0);
/* Compare the xonly_pubkey bytes against the precomputed group. */
secp256k1_fe_set_b32(&fe, xonly_pubkey_bytes[i - 1]);
CHECK(secp256k1_fe_equal_var(&fe, &group[i].x));
/* Check the parity against the precomputed group. */
fe = group[i].y;
secp256k1_fe_normalize_var(&fe);
CHECK(secp256k1_fe_is_odd(&fe) == parities[i - 1]);
/* Verify that the higher half is identical to the lower half mirrored. */
if (i > EXHAUSTIVE_TEST_ORDER / 2) {
CHECK(memcmp(xonly_pubkey_bytes[i - 1], xonly_pubkey_bytes[EXHAUSTIVE_TEST_ORDER - i - 1], 32) == 0);
CHECK(parities[i - 1] == 1 - parities[EXHAUSTIVE_TEST_ORDER - i - 1]);
}
}
/* TODO: keypair/xonly_pubkey tweak tests */
}
#endif

View File

@ -1,6 +1,7 @@
include_HEADERS += include/secp256k1_schnorrsig.h include_HEADERS += include/secp256k1_schnorrsig.h
noinst_HEADERS += src/modules/schnorrsig/main_impl.h noinst_HEADERS += src/modules/schnorrsig/main_impl.h
noinst_HEADERS += src/modules/schnorrsig/tests_impl.h noinst_HEADERS += src/modules/schnorrsig/tests_impl.h
noinst_HEADERS += src/modules/schnorrsig/tests_exhaustive_impl.h
if USE_BENCHMARK if USE_BENCHMARK
noinst_PROGRAMS += bench_schnorrsig noinst_PROGRAMS += bench_schnorrsig
bench_schnorrsig_SOURCES = src/bench_schnorrsig.c bench_schnorrsig_SOURCES = src/bench_schnorrsig.c

View File

@ -0,0 +1,206 @@
/**********************************************************************
* Copyright (c) 2020 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
#ifndef _SECP256K1_MODULE_SCHNORRSIG_TESTS_EXHAUSTIVE_
#define _SECP256K1_MODULE_SCHNORRSIG_TESTS_EXHAUSTIVE_
#include "include/secp256k1_schnorrsig.h"
#include "src/modules/schnorrsig/main_impl.h"
static const unsigned char invalid_pubkey_bytes[][32] = {
/* 0 */
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
},
/* 2 */
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2
},
/* order */
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
((EXHAUSTIVE_TEST_ORDER + 0UL) >> 24) & 0xFF,
((EXHAUSTIVE_TEST_ORDER + 0UL) >> 16) & 0xFF,
((EXHAUSTIVE_TEST_ORDER + 0UL) >> 8) & 0xFF,
(EXHAUSTIVE_TEST_ORDER + 0UL) & 0xFF
},
/* order + 1 */
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
((EXHAUSTIVE_TEST_ORDER + 1UL) >> 24) & 0xFF,
((EXHAUSTIVE_TEST_ORDER + 1UL) >> 16) & 0xFF,
((EXHAUSTIVE_TEST_ORDER + 1UL) >> 8) & 0xFF,
(EXHAUSTIVE_TEST_ORDER + 1UL) & 0xFF
},
/* field size */
{
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2F
},
/* field size + 1 (note that 1 is legal) */
{
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x30
},
/* 2^256 - 1 */
{
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
}
};
#define NUM_INVALID_KEYS (sizeof(invalid_pubkey_bytes) / sizeof(invalid_pubkey_bytes[0]))
static int secp256k1_hardened_nonce_function_smallint(unsigned char *nonce32, const unsigned char *msg32,
const unsigned char *key32, const unsigned char *xonly_pk32,
const unsigned char *algo16, void* data) {
secp256k1_scalar s;
int *idata = data;
(void)msg32;
(void)key32;
(void)xonly_pk32;
(void)algo16;
secp256k1_scalar_set_int(&s, *idata);
secp256k1_scalar_get_b32(nonce32, &s);
return 1;
}
static void test_exhaustive_schnorrsig_verify(const secp256k1_context *ctx, const secp256k1_xonly_pubkey* pubkeys, unsigned char (*xonly_pubkey_bytes)[32], const int* parities) {
int d;
uint64_t iter = 0;
/* Iterate over the possible public keys to verify against (through their corresponding DL d). */
for (d = 1; d <= EXHAUSTIVE_TEST_ORDER / 2; ++d) {
int actual_d;
unsigned k;
unsigned char pk32[32];
memcpy(pk32, xonly_pubkey_bytes[d - 1], 32);
actual_d = parities[d - 1] ? EXHAUSTIVE_TEST_ORDER - d : d;
/* Iterate over the possible valid first 32 bytes in the signature, through their corresponding DL k.
Values above EXHAUSTIVE_TEST_ORDER/2 refer to the entries in invalid_pubkey_bytes. */
for (k = 1; k <= EXHAUSTIVE_TEST_ORDER / 2 + NUM_INVALID_KEYS; ++k) {
unsigned char sig64[64];
int actual_k = -1;
int e_done[EXHAUSTIVE_TEST_ORDER] = {0};
int e_count_done = 0;
if (skip_section(&iter)) continue;
if (k <= EXHAUSTIVE_TEST_ORDER / 2) {
memcpy(sig64, xonly_pubkey_bytes[k - 1], 32);
actual_k = parities[k - 1] ? EXHAUSTIVE_TEST_ORDER - k : k;
} else {
memcpy(sig64, invalid_pubkey_bytes[k - 1 - EXHAUSTIVE_TEST_ORDER / 2], 32);
}
/* Randomly generate messages until all challenges have been hit. */
while (e_count_done < EXHAUSTIVE_TEST_ORDER) {
secp256k1_scalar e;
unsigned char msg32[32];
secp256k1_rand256(msg32);
secp256k1_schnorrsig_challenge(&e, sig64, msg32, pk32);
/* Only do work if we hit a challenge we haven't tried before. */
if (!e_done[e]) {
/* Iterate over the possible valid last 32 bytes in the signature.
0..order=that s value; order+1=random bytes */
int count_valid = 0, s;
for (s = 0; s <= EXHAUSTIVE_TEST_ORDER + 1; ++s) {
int expect_valid, valid;
if (s <= EXHAUSTIVE_TEST_ORDER) {
secp256k1_scalar s_s;
secp256k1_scalar_set_int(&s_s, s);
secp256k1_scalar_get_b32(sig64 + 32, &s_s);
expect_valid = actual_k != -1 && s != EXHAUSTIVE_TEST_ORDER &&
(s_s == (actual_k + actual_d * e) % EXHAUSTIVE_TEST_ORDER);
} else {
secp256k1_rand256(sig64 + 32);
expect_valid = 0;
}
valid = secp256k1_schnorrsig_verify(ctx, sig64, msg32, &pubkeys[d - 1]);
CHECK(valid == expect_valid);
count_valid += valid;
}
/* Exactly one s value must verify, unless R is illegal. */
CHECK(count_valid == (actual_k != -1));
/* Don't retry other messages that result in the same challenge. */
e_done[e] = 1;
++e_count_done;
}
}
}
}
}
static void test_exhaustive_schnorrsig_sign(const secp256k1_context *ctx, unsigned char (*xonly_pubkey_bytes)[32], const secp256k1_keypair* keypairs, const int* parities) {
int d, k;
uint64_t iter = 0;
/* Loop over keys. */
for (d = 1; d < EXHAUSTIVE_TEST_ORDER; ++d) {
int actual_d = d;
if (parities[d - 1]) actual_d = EXHAUSTIVE_TEST_ORDER - d;
/* Loop over nonces. */
for (k = 1; k < EXHAUSTIVE_TEST_ORDER; ++k) {
int e_done[EXHAUSTIVE_TEST_ORDER] = {0};
int e_count_done = 0;
unsigned char msg32[32];
unsigned char sig64[64];
int actual_k = k;
if (skip_section(&iter)) continue;
if (parities[k - 1]) actual_k = EXHAUSTIVE_TEST_ORDER - k;
/* Generate random messages until all challenges have been tried. */
while (e_count_done < EXHAUSTIVE_TEST_ORDER) {
secp256k1_scalar e;
secp256k1_rand256(msg32);
secp256k1_schnorrsig_challenge(&e, xonly_pubkey_bytes[k - 1], msg32, xonly_pubkey_bytes[d - 1]);
/* Only do work if we hit a challenge we haven't tried before. */
if (!e_done[e]) {
secp256k1_scalar expected_s = (actual_k + e * actual_d) % EXHAUSTIVE_TEST_ORDER;
unsigned char expected_s_bytes[32];
secp256k1_scalar_get_b32(expected_s_bytes, &expected_s);
/* Invoke the real function to construct a signature. */
CHECK(secp256k1_schnorrsig_sign(ctx, sig64, msg32, &keypairs[d - 1], secp256k1_hardened_nonce_function_smallint, &k));
/* The first 32 bytes must match the xonly pubkey for the specified k. */
CHECK(memcmp(sig64, xonly_pubkey_bytes[k - 1], 32) == 0);
/* The last 32 bytes must match the expected s value. */
CHECK(memcmp(sig64 + 32, expected_s_bytes, 32) == 0);
/* Don't retry other messages that result in the same challenge. */
e_done[e] = 1;
++e_count_done;
}
}
}
}
}
static void test_exhaustive_schnorrsig(const secp256k1_context *ctx) {
secp256k1_keypair keypair[EXHAUSTIVE_TEST_ORDER - 1];
secp256k1_xonly_pubkey xonly_pubkey[EXHAUSTIVE_TEST_ORDER - 1];
int parity[EXHAUSTIVE_TEST_ORDER - 1];
unsigned char xonly_pubkey_bytes[EXHAUSTIVE_TEST_ORDER - 1][32];
unsigned i;
/* Verify that all invalid_pubkey_bytes are actually invalid. */
for (i = 0; i < NUM_INVALID_KEYS; ++i) {
secp256k1_xonly_pubkey pk;
CHECK(!secp256k1_xonly_pubkey_parse(ctx, &pk, invalid_pubkey_bytes[i]));
}
/* Construct keypairs and xonly-pubkeys for the entire group. */
for (i = 1; i < EXHAUSTIVE_TEST_ORDER; ++i) {
secp256k1_scalar scalar_i;
unsigned char buf[32];
secp256k1_scalar_set_int(&scalar_i, i);
secp256k1_scalar_get_b32(buf, &scalar_i);
CHECK(secp256k1_keypair_create(ctx, &keypair[i - 1], buf));
CHECK(secp256k1_keypair_xonly_pub(ctx, &xonly_pubkey[i - 1], &parity[i - 1], &keypair[i - 1]));
CHECK(secp256k1_xonly_pubkey_serialize(ctx, xonly_pubkey_bytes[i - 1], &xonly_pubkey[i - 1]));
}
test_exhaustive_schnorrsig_sign(ctx, xonly_pubkey_bytes, keypair, parity);
test_exhaustive_schnorrsig_verify(ctx, xonly_pubkey, xonly_pubkey_bytes, parity);
}
#endif

View File

@ -346,6 +346,14 @@ void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *grou
#include "src/modules/recovery/tests_exhaustive_impl.h" #include "src/modules/recovery/tests_exhaustive_impl.h"
#endif #endif
#ifdef ENABLE_MODULE_EXTRAKEYS
#include "src/modules/extrakeys/tests_exhaustive_impl.h"
#endif
#ifdef ENABLE_MODULE_SCHNORRSIG
#include "src/modules/schnorrsig/tests_exhaustive_impl.h"
#endif
int main(int argc, char** argv) { int main(int argc, char** argv) {
int i; int i;
secp256k1_gej groupj[EXHAUSTIVE_TEST_ORDER]; secp256k1_gej groupj[EXHAUSTIVE_TEST_ORDER];
@ -433,6 +441,12 @@ int main(int argc, char** argv) {
#ifdef ENABLE_MODULE_RECOVERY #ifdef ENABLE_MODULE_RECOVERY
test_exhaustive_recovery(ctx, group); test_exhaustive_recovery(ctx, group);
#endif #endif
#ifdef ENABLE_MODULE_EXTRAKEYS
test_exhaustive_extrakeys(ctx, group);
#endif
#ifdef ENABLE_MODULE_SCHNORRSIG
test_exhaustive_schnorrsig(ctx);
#endif
secp256k1_context_destroy(ctx); secp256k1_context_destroy(ctx);
} }