-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Exhaustive test improvements + exhaustive schnorrsig tests #808
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
sipa
merged 14 commits into
bitcoin-core:master
from
sipa:202009_exhaustive_scalar_overflow
Sep 26, 2020
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
be31791
Make group order purely compile-time in exhaustive tests
sipa c498366
Move exhaustive tests for recovery to module
sipa 8bcd78c
Make secp256k1_scalar_b32 detect overflow in scalar_low
sipa d7f39ae
Delete gej_is_valid_var: unused outside tests
sipa 78f6cdf
Make the curve B constant a secp256k1_fe
sipa cec7b18
Select exhaustive lambda in function of order
sipa b110c10
Change exhaustive test groups so they have a point with X=1
sipa 49e6630
refactor: move RNG seeding to testrand
sipa e99b26f
Give exhaustive_tests count and seed cmdline inputs
sipa 39f67dd
Support splitting exhaustive tests across cores
sipa 63e1b2a
Disable output buffering in tests_exhaustive.c
sipa 87af00b
Abstract out challenge computation in schnorrsig
sipa 08d7d89
Make pubkey parsing test whether points are in the correct subgroup
sipa 8b7dcdd
Add exhaustive test for extrakeys and schnorrsig
sipa File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| # Define field size and field | ||
| P = 2^256 - 2^32 - 977 | ||
| F = GF(P) | ||
| BETA = F(0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee) | ||
|
|
||
| assert(BETA != F(1) and BETA^3 == F(1)) | ||
|
|
||
| orders_done = set() | ||
| results = {} | ||
| first = True | ||
| for b in range(1, P): | ||
| # There are only 6 curves (up to isomorphism) of the form y^2=x^3+B. Stop once we have tried all. | ||
| if len(orders_done) == 6: | ||
| break | ||
|
|
||
| E = EllipticCurve(F, [0, b]) | ||
| print("Analyzing curve y^2 = x^3 + %i" % b) | ||
| n = E.order() | ||
| # Skip curves with an order we've already tried | ||
| if n in orders_done: | ||
| print("- Isomorphic to earlier curve") | ||
| continue | ||
| orders_done.add(n) | ||
| # Skip curves isomorphic to the real secp256k1 | ||
| if n.is_pseudoprime(): | ||
| print(" - Isomorphic to secp256k1") | ||
| continue | ||
|
|
||
| print("- Finding subgroups") | ||
|
|
||
| # Find what prime subgroups exist | ||
| for f, _ in n.factor(): | ||
| print("- Analyzing subgroup of order %i" % f) | ||
| # Skip subgroups of order >1000 | ||
| if f < 4 or f > 1000: | ||
| print(" - Bad size") | ||
| continue | ||
|
|
||
| # Iterate over X coordinates until we find one that is on the curve, has order f, | ||
| # and for which curve isomorphism exists that maps it to X coordinate 1. | ||
| for x in range(1, P): | ||
| # Skip X coordinates not on the curve, and construct the full point otherwise. | ||
| if not E.is_x_coord(x): | ||
| continue | ||
| G = E.lift_x(F(x)) | ||
|
|
||
| print(" - Analyzing (multiples of) point with X=%i" % x) | ||
|
|
||
| # Skip points whose order is not a multiple of f. Project the point to have | ||
| # order f otherwise. | ||
| if (G.order() % f): | ||
| print(" - Bad order") | ||
| continue | ||
| G = G * (G.order() // f) | ||
|
|
||
| # Find lambda for endomorphism. Skip if none can be found. | ||
| lam = None | ||
| for l in Integers(f)(1).nth_root(3, all=True): | ||
| if int(l)*G == E(BETA*G[0], G[1]): | ||
| lam = int(l) | ||
| break | ||
| if lam is None: | ||
| print(" - No endomorphism for this subgroup") | ||
| break | ||
|
|
||
| # Now look for an isomorphism of the curve that gives this point an X | ||
| # coordinate equal to 1. | ||
| # If (x,y) is on y^2 = x^3 + b, then (a^2*x, a^3*y) is on y^2 = x^3 + a^6*b. | ||
| # So look for m=a^2=1/x. | ||
| m = F(1)/G[0] | ||
| if not m.is_square(): | ||
| print(" - No curve isomorphism maps it to a point with X=1") | ||
| continue | ||
| a = m.sqrt() | ||
| rb = a^6*b | ||
| RE = EllipticCurve(F, [0, rb]) | ||
|
|
||
| # Use as generator twice the image of G under the above isormorphism. | ||
| # This means that generator*(1/2 mod f) will have X coordinate 1. | ||
| RG = RE(1, a^3*G[1]) * 2 | ||
| # And even Y coordinate. | ||
| if int(RG[1]) % 2: | ||
| RG = -RG | ||
| assert(RG.order() == f) | ||
| assert(lam*RG == RE(BETA*RG[0], RG[1])) | ||
|
|
||
| # We have found curve RE:y^2=x^3+rb with generator RG of order f. Remember it | ||
| results[f] = {"b": rb, "G": RG, "lambda": lam} | ||
| print(" - Found solution") | ||
| break | ||
|
|
||
| print("") | ||
|
|
||
| print("") | ||
| print("") | ||
| print("/* To be put in src/group_impl.h: */") | ||
| first = True | ||
| for f in sorted(results.keys()): | ||
| b = results[f]["b"] | ||
| G = results[f]["G"] | ||
| print("# %s EXHAUSTIVE_TEST_ORDER == %i" % ("if" if first else "elif", f)) | ||
| first = False | ||
| print("static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(") | ||
| print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(G[0]) >> (32 * (7 - i))) & 0xffffffff for i in range(4))) | ||
| print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(G[0]) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8))) | ||
| print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(G[1]) >> (32 * (7 - i))) & 0xffffffff for i in range(4))) | ||
| print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x" % tuple((int(G[1]) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8))) | ||
| print(");") | ||
| print("static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(") | ||
| print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(b) >> (32 * (7 - i))) & 0xffffffff for i in range(4))) | ||
| print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x" % tuple((int(b) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8))) | ||
| print(");") | ||
| print("# else") | ||
| print("# error No known generator for the specified exhaustive test group order.") | ||
| print("# endif") | ||
|
|
||
| print("") | ||
| print("") | ||
| print("/* To be put in src/scalar_impl.h: */") | ||
| first = True | ||
| for f in sorted(results.keys()): | ||
| lam = results[f]["lambda"] | ||
| print("# %s EXHAUSTIVE_TEST_ORDER == %i" % ("if" if first else "elif", f)) | ||
| first = False | ||
| print("# define EXHAUSTIVE_TEST_LAMBDA %i" % lam) | ||
| print("# else") | ||
| print("# error No known lambda for the specified exhaustive test group order.") | ||
| print("# endif") | ||
| print("") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| include_HEADERS += include/secp256k1_extrakeys.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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.