Skip to content

Commit ff0d9e0

Browse files
peterdettmanapoelstra
authored andcommitted
Add Jacobi symbol test via GMP
Also add native Jacobi symbol test (Andrew) Rebased-by: Andrew Poelstra
1 parent 79a59be commit ff0d9e0

File tree

7 files changed

+208
-0
lines changed

7 files changed

+208
-0
lines changed

src/bench_internal.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,19 @@ void bench_context_sign(void* arg) {
299299
}
300300
}
301301

302+
void bench_num_jacobi(void* arg) {
303+
int i;
304+
bench_inv_t *data = (bench_inv_t*)arg;
305+
secp256k1_num nx, norder;
306+
307+
secp256k1_scalar_get_num(&nx, &data->scalar_x);
308+
secp256k1_scalar_order_get_num(&norder);
309+
secp256k1_scalar_get_num(&norder, &data->scalar_y);
310+
311+
for (i = 0; i < 2000; i++) {
312+
secp256k1_num_jacobi(&nx, &norder);
313+
}
314+
}
302315

303316
int have_flag(int argc, char** argv, char *flag) {
304317
char** argm = argv + argc;
@@ -350,5 +363,6 @@ int main(int argc, char **argv) {
350363
if (have_flag(argc, argv, "context") || have_flag(argc, argv, "verify")) run_benchmark("context_verify", bench_context_verify, bench_setup, NULL, &data, 10, 20);
351364
if (have_flag(argc, argv, "context") || have_flag(argc, argv, "sign")) run_benchmark("context_sign", bench_context_sign, bench_setup, NULL, &data, 10, 200);
352365

366+
if (have_flag(argc, argv, "num") || have_flag(argc, argv, "jacobi")) run_benchmark("num_jacobi", bench_num_jacobi, bench_setup, NULL, &data, 10, 200000);
353367
return 0;
354368
}

src/num.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ static void secp256k1_num_set_bin(secp256k1_num *r, const unsigned char *a, unsi
3434
/** Compute a modular inverse. The input must be less than the modulus. */
3535
static void secp256k1_num_mod_inverse(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *m);
3636

37+
/** Compute the jacobi symbol (a|b). b must be positive and odd. */
38+
static int secp256k1_num_jacobi(const secp256k1_num *a, const secp256k1_num *b);
39+
3740
/** Compare the absolute value of two numbers. */
3841
static int secp256k1_num_cmp(const secp256k1_num *a, const secp256k1_num *b);
3942

@@ -56,6 +59,9 @@ static void secp256k1_num_shift(secp256k1_num *r, int bits);
5659
/** Check whether a number is zero. */
5760
static int secp256k1_num_is_zero(const secp256k1_num *a);
5861

62+
/** Check whether a number is one. */
63+
static int secp256k1_num_is_one(const secp256k1_num *a);
64+
5965
/** Check whether a number is strictly negative. */
6066
static int secp256k1_num_is_neg(const secp256k1_num *a);
6167

src/num_5x64.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#define NUM_N_WORDS 5
1313
#define NUM_WORD_WIDTH 64
1414
#define NUM_WORD_CTLZ __builtin_clzl
15+
#define NUM_WORD_CTZ __builtin_ctzl
1516
typedef uint64_t secp256k1_num_word;
1617
typedef int64_t secp256k1_num_sword;
1718
typedef uint128_t secp256k1_num_dword;

src/num_9x32.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#define NUM_N_WORDS 9
1313
#define NUM_WORD_WIDTH 32
1414
#define NUM_WORD_CTLZ __builtin_clz
15+
#define NUM_WORD_CTZ __builtin_ctz
1516
typedef uint32_t secp256k1_num_word;
1617
typedef int32_t secp256k1_num_sword;
1718
typedef uint64_t secp256k1_num_dword;

src/num_gmp_impl.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,28 @@ static void secp256k1_num_mod_inverse(secp256k1_num *r, const secp256k1_num *a,
144144
memset(v, 0, sizeof(v));
145145
}
146146

147+
static int secp256k1_num_jacobi(const secp256k1_num *a, const secp256k1_num *b) {
148+
int ret;
149+
mpz_t ga, gb;
150+
secp256k1_num_sanity(a);
151+
secp256k1_num_sanity(b);
152+
VERIFY_CHECK(!b->neg && (b->limbs > 0) && (b->data[0] & 1));
153+
154+
mpz_inits(ga, gb, NULL);
155+
156+
mpz_import(gb, b->limbs, -1, sizeof(mp_limb_t), 0, 0, b->data);
157+
mpz_import(ga, a->limbs, -1, sizeof(mp_limb_t), 0, 0, a->data);
158+
if (a->neg) {
159+
mpz_neg(ga, ga);
160+
}
161+
162+
ret = mpz_jacobi(ga, gb);
163+
164+
mpz_clears(ga, gb, NULL);
165+
166+
return ret;
167+
}
168+
147169
static int secp256k1_num_is_zero(const secp256k1_num *a) {
148170
return (a->limbs == 1 && a->data[0] == 0);
149171
}

src/num_native_impl.h

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,16 @@ SECP256K1_INLINE static int secp256k1_num_is_zero(const secp256k1_num *a) {
102102
return 1;
103103
}
104104

105+
SECP256K1_INLINE static int secp256k1_num_is_one(const secp256k1_num *a) {
106+
int i;
107+
if (a->data[0] != 1)
108+
return 0;
109+
for (i = 1; i < NUM_N_WORDS - 1; ++i)
110+
if (a->data[i] != 0)
111+
return 0;
112+
return 1;
113+
}
114+
105115
SECP256K1_INLINE static int secp256k1_num_is_neg(const secp256k1_num *a) {
106116
return a->data[NUM_N_WORDS - 1] >> (NUM_WORD_WIDTH - 1);
107117
}
@@ -599,4 +609,80 @@ static void secp256k1_num_mod_inverse(secp256k1_num *rr, const secp256k1_num *a,
599609
}
600610
/* end mod inverse */
601611

612+
/* start jacobi symbol */
613+
/* Compute a number modulo some power of 2 */
614+
SECP256K1_INLINE static int secp256k1_num_mod_2(const secp256k1_num *a, int m) {
615+
VERIFY_CHECK(m > 0);
616+
VERIFY_CHECK((m & (m - 1)) == 0); /* check that m is a power of 2 */
617+
/* Since our words are powers of 2 we only need to mod the lowest digit */
618+
return a->data[0] % m;
619+
}
620+
621+
static int secp256k1_num_jacobi_1(secp256k1_num_word a, secp256k1_num_word b) {
622+
int ret = 1;
623+
secp256k1_num_word t;
624+
/* Iterate, left-multiplying it by [[0 1] [1 -w]] as many times as we can. */
625+
while (1) {
626+
a %= b;
627+
if (a == 0)
628+
return 0;
629+
if (a % 2 == 0) {
630+
int shift = NUM_WORD_CTZ(a);
631+
a >>= shift;
632+
if ((b % 8 == 3 || b % 8 == 5) && shift % 2 == 1)
633+
ret *= -1;
634+
}
635+
if (a == 1)
636+
break;
637+
if (b % 4 == 3 && a % 4 == 3)
638+
ret *= -1;
639+
t = a; a = b; b = t;
640+
}
641+
return ret;
642+
}
643+
644+
/* Compute the Jacobi symbol (a|b) assuming b is an odd prime */
645+
static int secp256k1_num_jacobi(const secp256k1_num *a, const secp256k1_num *b) {
646+
secp256k1_num top = *a, bot = *b, scratch;
647+
secp256k1_num_word x, y;
648+
int index[2];
649+
int ret = 1;
650+
651+
while (1) {
652+
int mod8 = secp256k1_num_mod_2(&bot, 8);
653+
secp256k1_num_leading_digit(&x, &index[0], &top);
654+
secp256k1_num_leading_digit(&y, &index[1], &bot);
655+
656+
if (index[0] == 0 && index[1] == 0)
657+
return ret * secp256k1_num_jacobi_1(x, y);
658+
659+
/* Algorithm from https://en.wikipedia.org/wiki/Jacobi_symbol#Calculating_the_Jacobi_symbol */
660+
secp256k1_num_div_mod(&scratch, &top, &top, &bot); /* top <- top mod bottom */
661+
662+
/* are we done? */
663+
if (secp256k1_num_is_zero(&top))
664+
return 0;
665+
666+
/* cast out powers of two from the "numerator" */
667+
while (secp256k1_num_mod_2(&top, 2) == 0) {
668+
int shift = NUM_WORD_CTZ(top.data[0]);
669+
secp256k1_num_shift(&top, shift);
670+
if ((mod8 == 3 || mod8 == 5) && shift % 2 == 1)
671+
ret *= -1;
672+
}
673+
674+
/* are we done? */
675+
if (secp256k1_num_is_one(&top))
676+
return ret;
677+
/* if not, iterate */
678+
if (mod8 % 4 == 3 && secp256k1_num_mod_2(&top, 4) == 3)
679+
ret *= -1;
680+
681+
scratch = top;
682+
top = bot;
683+
bot = scratch;
684+
}
685+
}
686+
/* end jacobi symbol */
687+
602688
#endif

src/tests.c

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,8 @@ void test_num_negate(void) {
472472
}
473473

474474
void test_num_add_sub(void) {
475+
int i;
476+
secp256k1_scalar s;
475477
secp256k1_num n1;
476478
secp256k1_num n2;
477479
secp256k1_num n1p2, n2p1, n1m2, n2m1;
@@ -498,6 +500,18 @@ void test_num_add_sub(void) {
498500
CHECK(!secp256k1_num_eq(&n2p1, &n1));
499501
secp256k1_num_sub(&n2p1, &n2p1, &n2); /* n2p1 = R2 + R1 - R2 = R1 */
500502
CHECK(secp256k1_num_eq(&n2p1, &n1));
503+
504+
/* check is_one */
505+
secp256k1_scalar_set_int(&s, 1);
506+
secp256k1_scalar_get_num(&n1, &s);
507+
CHECK(secp256k1_num_is_one(&n1));
508+
/* check that 2^n + 1 is never 1 */
509+
secp256k1_scalar_get_num(&n2, &s);
510+
for (i = 0; i < 250; ++i) {
511+
secp256k1_num_add(&n1, &n1, &n1); /* n1 *= 2 */
512+
secp256k1_num_add(&n1p2, &n1, &n2); /* n1p2 = n1 + 1 */
513+
CHECK(!secp256k1_num_is_one(&n1p2));
514+
}
501515
}
502516

503517
void test_num_mod(void) {
@@ -531,12 +545,74 @@ void test_num_mod(void) {
531545
CHECK(secp256k1_num_is_zero(&n));
532546
}
533547

548+
void test_num_jacobi(void) {
549+
secp256k1_scalar sqr;
550+
secp256k1_scalar small;
551+
secp256k1_scalar five; /* five is not a quadratic residue */
552+
secp256k1_num order, n;
553+
int i;
554+
/* squares mod 5 are 1, 4 */
555+
const int jacobi5[10] = { 0, 1, -1, -1, 1, 0, 1, -1, -1, 1 };
556+
557+
/* check some small values with 5 as the order */
558+
secp256k1_scalar_set_int(&five, 5);
559+
secp256k1_scalar_get_num(&order, &five);
560+
for (i = 0; i < 10; ++i) {
561+
secp256k1_scalar_set_int(&small, i);
562+
secp256k1_scalar_get_num(&n, &small);
563+
CHECK(secp256k1_num_jacobi(&n, &order) == jacobi5[i]);
564+
}
565+
566+
/** test large values with 5 as group order */
567+
secp256k1_scalar_get_num(&order, &five);
568+
/* we first need a scalar which is not a multiple of 5 */
569+
do {
570+
secp256k1_num fiven;
571+
random_scalar_order_test(&sqr);
572+
secp256k1_scalar_get_num(&fiven, &five);
573+
secp256k1_scalar_get_num(&n, &sqr);
574+
secp256k1_num_mod(&n, &fiven);
575+
} while (secp256k1_num_is_zero(&n));
576+
/* next force it to be a residue. 2 is a nonresidue mod 5 so we can
577+
* just multiply by two, i.e. add the number to itself */
578+
if (secp256k1_num_jacobi(&n, &order) == -1) {
579+
secp256k1_num_add(&n, &n, &n);
580+
}
581+
582+
/* test residue */
583+
CHECK(secp256k1_num_jacobi(&n, &order) == 1);
584+
/* test nonresidue */
585+
secp256k1_num_add(&n, &n, &n);
586+
CHECK(secp256k1_num_jacobi(&n, &order) == -1);
587+
588+
/** test with secp group order as order */
589+
secp256k1_scalar_order_get_num(&order);
590+
random_scalar_order_test(&sqr);
591+
secp256k1_scalar_sqr(&sqr, &sqr);
592+
/* test residue */
593+
secp256k1_scalar_get_num(&n, &sqr);
594+
CHECK(secp256k1_num_jacobi(&n, &order) == 1);
595+
/* test nonresidue */
596+
secp256k1_scalar_mul(&sqr, &sqr, &five);
597+
secp256k1_scalar_get_num(&n, &sqr);
598+
CHECK(secp256k1_num_jacobi(&n, &order) == -1);
599+
/* test multiple of the order*/
600+
CHECK(secp256k1_num_jacobi(&order, &order) == 0);
601+
602+
/* check one less than the order */
603+
secp256k1_scalar_set_int(&small, 1);
604+
secp256k1_scalar_get_num(&n, &small);
605+
secp256k1_num_sub(&n, &order, &n);
606+
CHECK(secp256k1_num_jacobi(&n, &order) == 1); /* sage confirms this is 1 */
607+
}
608+
534609
void run_num_smalltests(void) {
535610
int i;
536611
for (i = 0; i < 100*count; i++) {
537612
test_num_negate();
538613
test_num_add_sub();
539614
test_num_mod();
615+
test_num_jacobi();
540616
}
541617
}
542618

@@ -677,6 +753,8 @@ void scalar_test(void) {
677753
secp256k1_scalar_inverse(&inv, &inv);
678754
/* Inverting one must result in one. */
679755
CHECK(secp256k1_scalar_is_one(&inv));
756+
secp256k1_scalar_get_num(&invnum, &inv);
757+
CHECK(secp256k1_num_is_one(&invnum));
680758
}
681759
}
682760

0 commit comments

Comments
 (0)