diff --git a/tests.c b/tests.c index 982582adc5..d359fa7af4 100644 --- a/tests.c +++ b/tests.c @@ -1030,7 +1030,7 @@ END_TEST START_TEST(test_pubkey_validity) { uint8_t pub_key[65]; - curve_point pub; + curve_point pub; int res; memcpy(pub_key, fromhex("0226659c1cf7321c178c07437150639ff0c5b7679c7ea195253ed9abda2e081a37"), 33); @@ -1203,6 +1203,157 @@ START_TEST(test_ecdsa_der) } END_TEST +START_TEST(test_secp256k1_cp) { + int i, j; + bignum256 a; + curve_point p, p1; + for (i = 0; i < 64; i++) { + for (j = 0; j < 8; j++) { + bn_zero(&a); + a.val[(4*i)/30] = (2*j+1) << (4*i % 30); + bn_normalize(&a); + // note that this is not a trivial test. We add 64 curve + // points in the table to get that particular curve point. + scalar_multiply(&a, &p); + ck_assert_mem_eq(&p, &secp256k1_cp[i][j], sizeof(curve_point)); + bn_zero(&p.y); // test that point_multiply is not a noop + point_multiply(&a, &G256k1, &p); + ck_assert_mem_eq(&p, &secp256k1_cp[i][j], sizeof(curve_point)); + + // even/odd has different behaviour; + // increment by one and test again + p1 = p; + point_add(&G256k1, &p1); + bn_addmodi(&a, 1, &order256k1); + scalar_multiply(&a, &p); + ck_assert_mem_eq(&p, &p1, sizeof(curve_point)); + bn_zero(&p.y); // test that point_multiply is not a noop + point_multiply(&a, &G256k1, &p); + ck_assert_mem_eq(&p, &p1, sizeof(curve_point)); + } + } +} +END_TEST + +START_TEST(test_mult_border_cases) { + bignum256 a; + curve_point p; + curve_point expected; + bn_zero(&a); // a == 0 + scalar_multiply(&a, &p); + ck_assert(point_is_infinity(&p)); + point_multiply(&a, &p, &p); + ck_assert(point_is_infinity(&p)); + point_multiply(&a, &G256k1, &p); + ck_assert(point_is_infinity(&p)); + + bn_addmodi(&a, 1, &order256k1); // a == 1 + scalar_multiply(&a, &p); + ck_assert_mem_eq(&p, &G256k1, sizeof(curve_point)); + point_multiply(&a, &G256k1, &p); + ck_assert_mem_eq(&p, &G256k1, sizeof(curve_point)); + + bn_subtract(&order256k1, &a, &a); // a == -1 + expected = G256k1; + bn_subtract(&prime256k1, &expected.y, &expected.y); + scalar_multiply(&a, &p); + ck_assert_mem_eq(&p, &expected, sizeof(curve_point)); + point_multiply(&a, &G256k1, &p); + ck_assert_mem_eq(&p, &expected, sizeof(curve_point)); + + bn_subtract(&order256k1, &a, &a); + bn_addmodi(&a, 1, &order256k1); // a == 2 + expected = G256k1; + point_add(&expected, &expected); + scalar_multiply(&a, &p); + ck_assert_mem_eq(&p, &expected, sizeof(curve_point)); + point_multiply(&a, &G256k1, &p); + ck_assert_mem_eq(&p, &expected, sizeof(curve_point)); + + bn_subtract(&order256k1, &a, &a); // a == -2 + expected = G256k1; + point_add(&expected, &expected); + bn_subtract(&prime256k1, &expected.y, &expected.y); + scalar_multiply(&a, &p); + ck_assert_mem_eq(&p, &expected, sizeof(curve_point)); + point_multiply(&a, &G256k1, &p); + ck_assert_mem_eq(&p, &expected, sizeof(curve_point)); +} +END_TEST + +START_TEST(test_scalar_mult) { + int i; + // get two "random" numbers + bignum256 a = G256k1.x; + bignum256 b = G256k1.y; + curve_point p1, p2, p3; + for (i = 0; i < 1000; i++) { + /* test distributivity: (a + b)G = aG + bG */ + scalar_multiply(&a, &p1); + scalar_multiply(&b, &p2); + bn_addmod(&a, &b, &order256k1); + scalar_multiply(&a, &p3); + point_add(&p1, &p2); + ck_assert_mem_eq(&p2, &p3, sizeof(curve_point)); + // new "random" numbers + a = p3.x; + b = p3.y; + } +} +END_TEST + +START_TEST(test_point_mult) { + int i; + // get two "random" numbers and a "random" point + bignum256 a = G256k1.x; + bignum256 b = G256k1.y; + curve_point p = G256k1; + curve_point p1, p2, p3; + for (i = 0; i < 200; i++) { + /* test distributivity: (a + b)P = aP + bP */ + point_multiply(&a, &p, &p1); + point_multiply(&b, &p, &p2); + bn_addmod(&a, &b, &order256k1); + point_multiply(&a, &p, &p3); + point_add(&p1, &p2); + ck_assert_mem_eq(&p2, &p3, sizeof(curve_point)); + // new "random" numbers and a "random" point + a = p1.x; + b = p1.y; + p = p3; + } +} +END_TEST + +START_TEST(test_scalar_point_mult) { + int i; + // get two "random" numbers + bignum256 a = G256k1.x; + bignum256 b = G256k1.y; + curve_point p1, p2; + for (i = 0; i < 200; i++) { + /* test commutativity and associativity: + * a(bG) = (ab)G = b(aG) + */ + scalar_multiply(&a, &p1); + point_multiply(&b, &p1, &p1); + + scalar_multiply(&b, &p2); + point_multiply(&a, &p2, &p2); + + ck_assert_mem_eq(&p1, &p2, sizeof(curve_point)); + + bn_multiply(&a, &b, &order256k1); + scalar_multiply(&b, &p2); + + ck_assert_mem_eq(&p1, &p2, sizeof(curve_point)); + + // new "random" numbers + a = p1.x; + b = p1.y; + } +} +END_TEST // define test suite and cases Suite *test_suite(void) @@ -1265,6 +1416,25 @@ Suite *test_suite(void) tcase_add_test(tc, test_pubkey_validity); suite_add_tcase(s, tc); + tc = tcase_create("secp256k1_cp"); + tcase_add_test(tc, test_secp256k1_cp); + suite_add_tcase(s, tc); + + tc = tcase_create("mult_border_cases"); + tcase_add_test(tc, test_mult_border_cases); + suite_add_tcase(s, tc); + + tc = tcase_create("scalar_mult"); + tcase_add_test(tc, test_scalar_mult); + suite_add_tcase(s, tc); + + tc = tcase_create("point_mult"); + tcase_add_test(tc, test_point_mult); + suite_add_tcase(s, tc); + + tc = tcase_create("scalar_point_mult"); + tcase_add_test(tc, test_scalar_point_mult); + suite_add_tcase(s, tc); return s; } @@ -1278,5 +1448,7 @@ int main(void) srunner_run_all(sr, CK_VERBOSE); number_failed = srunner_ntests_failed(sr); srunner_free(sr); + if (number_failed == 0) + printf("PASSED ALL TESTS\n"); return number_failed; } diff --git a/tools/.gitignore b/tools/.gitignore index ac00e6bcd2..3073e2e521 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -1 +1,2 @@ xpubaddrgen +mksecptable diff --git a/tools/Makefile b/tools/Makefile index 67c652637f..519bb1de9a 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -23,7 +23,7 @@ CFLAGS += $(OPTFLAGS) \ -Werror \ -I.. -all: xpubaddrgen +all: xpubaddrgen mksecptable OBJS = ../bip32.o ../ecdsa.o ../sha2.o ../bignum.o ../base58.o ../secp256k1.o ../ripemd160.o ../hmac.o ../rand.o @@ -33,5 +33,8 @@ OBJS = ../bip32.o ../ecdsa.o ../sha2.o ../bignum.o ../base58.o ../secp256k1.o .. xpubaddrgen: xpubaddrgen.o $(OBJS) $(CC) xpubaddrgen.o $(OBJS) -o xpubaddrgen +mksecptable: mksecptable.o $(OBJS) + $(CC) mksecptable.o $(OBJS) -o mksecptable + clean: - rm -f *.o xpubaddrgen + rm -f *.o xpubaddrgen mksecptable diff --git a/tools/README.md b/tools/README.md index 8f24f19ab0..93ea9fb1de 100644 --- a/tools/README.md +++ b/tools/README.md @@ -43,3 +43,11 @@ Example output: It will print ``` error``` when there was an error processing job jobid. It will print ```error``` when it encountered a malformed line. + + +mksecptable +----------- + +mksecptable computes the points of the form `(2*j+1)*16^i*G` and prints them in the format to be included in `secp256k1.c`. These points are used by the fast ECC multiplication. + +It is only meant to be run if the `scalar_mult` algorithm changes. diff --git a/tools/mksecptable.c b/tools/mksecptable.c new file mode 100644 index 0000000000..e6a1997d90 --- /dev/null +++ b/tools/mksecptable.c @@ -0,0 +1,59 @@ +#include +#include +#include "bignum.h" +#include "ecdsa.h" +#include "secp256k1.h" +#include "rand.h" + +/* + * This program prints the contents of the secp256k1_cp array. + * The entry secp256k1_cp[i][j] contains the number (2*j+1)*16^i*G, + * where G is the generator of secp256k1. + */ +int main(int __attribute__((unused)) argc, char __attribute__((unused)) **argv) { + int i,j,k; + curve_point ng = G256k1; + curve_point pow2ig = G256k1; +#ifndef NDEBUG + init_rand(); // needed for point_multiply() +#endif + for (i = 0; i < 64; i++) { + // invariants: + // pow2ig = 16^i * G + // ng = pow2ig + printf("\t{\n"); + for (j = 0; j < 8; j++) { + // invariants: + // pow2ig = 16^i * G + // ng = (2*j+1) * 16^i * G +#ifndef NDEBUG + curve_point checkresult; + bignum256 a; + bn_zero(&a); + a.val[(4*i) / 30] = ((uint32_t) 2*j+1) << ((4*i) % 30); + bn_normalize(&a); + point_multiply(&a, &G256k1, &checkresult); + assert(point_is_equal(&checkresult, &ng)); +#endif + printf("\t\t/* %2d*16^%d*G: */\n\t\t{{{", 2*j + 1, i); + // print x coordinate + for (k = 0; k < 9; k++) { + printf((k < 8 ? "0x%08x, " : "0x%04x"), ng.x.val[k]); + } + printf("}},\n\t\t {{"); + // print y coordinate + for (k = 0; k < 9; k++) { + printf((k < 8 ? "0x%08x, " : "0x%04x"), ng.y.val[k]); + } + if (j == 7) { + printf("}}}\n\t},\n"); + } else { + printf("}}},\n"); + point_add(&pow2ig, &ng); + } + point_add(&pow2ig, &ng); + } + pow2ig = ng; + } + return 0; +}