Skip to content

Commit 28d17d8

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

File tree

7 files changed

+148
-0
lines changed

7 files changed

+148
-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 < 200000; 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: 3 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

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
@@ -96,6 +96,16 @@ static void secp256k1_num_shift(secp256k1_num *r, int bits) {
9696
r->data[i] = 0;
9797
}
9898

99+
SECP256K1_INLINE static int secp256k1_num_is_one(const secp256k1_num *a) {
100+
int i;
101+
if (a->data[0] != 1)
102+
return 0;
103+
for (i = 1; i < NUM_N_WORDS - 1; ++i)
104+
if (a->data[i] != 0)
105+
return 0;
106+
return 1;
107+
}
108+
99109
SECP256K1_INLINE static int secp256k1_num_is_zero(const secp256k1_num *a) {
100110
int i;
101111
for (i = 0; i < NUM_N_WORDS - 1; ++i)
@@ -591,4 +601,80 @@ static void secp256k1_num_mod_inverse(secp256k1_num *rr, const secp256k1_num *a,
591601
}
592602
/* end mod inverse */
593603

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

src/tests.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,11 +498,32 @@ void test_num_add_sub(void) {
498498
CHECK(secp256k1_num_eq(&n2p1, &n1));
499499
}
500500

501+
void test_num_jacobi(void) {
502+
secp256k1_scalar sqr;
503+
secp256k1_scalar five; /* five is not a quadratic residue */
504+
secp256k1_num order, n;
505+
506+
/* setup values */
507+
random_scalar_order_test(&sqr);
508+
secp256k1_scalar_sqr(&sqr, &sqr);
509+
secp256k1_scalar_set_int(&five, 5);
510+
secp256k1_scalar_order_get_num(&order);
511+
512+
/* test residue */
513+
secp256k1_scalar_get_num(&n, &sqr);
514+
CHECK(secp256k1_num_jacobi(&n, &order) == 1);
515+
/* test nonresidue */
516+
secp256k1_scalar_mul(&sqr, &sqr, &five);
517+
secp256k1_scalar_get_num(&n, &sqr);
518+
CHECK(secp256k1_num_jacobi(&n, &order) == -1);
519+
}
520+
501521
void run_num_smalltests(void) {
502522
int i;
503523
for (i = 0; i < 100*count; i++) {
504524
test_num_negate();
505525
test_num_add_sub();
526+
test_num_jacobi();
506527
}
507528
}
508529

0 commit comments

Comments
 (0)