Skip to content

Commit 0a4741a

Browse files
committed
Implement the extended euclidian algorithm to compute the inverse of scalars in variable time.
This is faster than the exponentiation method. It is still significantly slower than GMP, so we keep that option as well.
1 parent 9055761 commit 0a4741a

File tree

4 files changed

+166
-5
lines changed

4 files changed

+166
-5
lines changed

src/scalar_4x64_impl.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,31 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
181181
return 2 * (mask == 0) - 1;
182182
}
183183

184+
static int secp256k1_scalar_complement(secp256k1_scalar *r, const secp256k1_scalar *a) {
185+
uint128_t t = 1;
186+
t += ~a->d[0];
187+
r->d[0] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64;
188+
t += ~a->d[1];
189+
r->d[1] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64;
190+
t += ~a->d[2];
191+
r->d[2] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64;
192+
t += ~a->d[3];
193+
r->d[3] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64;
194+
return t;
195+
}
196+
197+
static int secp256k1_scalar_binadd(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) {
198+
uint128_t t = (uint128_t)a->d[0] + b->d[0];
199+
r->d[0] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64;
200+
t += (uint128_t)a->d[1] + b->d[1];
201+
r->d[1] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64;
202+
t += (uint128_t)a->d[2] + b->d[2];
203+
r->d[2] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64;
204+
t += (uint128_t)a->d[3] + b->d[3];
205+
r->d[3] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64;
206+
return t;
207+
}
208+
184209
/* Inspired by the macros in OpenSSL's crypto/bn/asm/x86_64-gcc.c. */
185210

186211
/** Add a*b to the number defined by (c0,c1,c2). c2 must never overflow. */

src/scalar_8x32_impl.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,46 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
259259
return 2 * (mask == 0) - 1;
260260
}
261261

262+
static int secp256k1_scalar_complement(secp256k1_scalar *r, const secp256k1_scalar *a) {
263+
uint64_t t = 1;
264+
t += ~a->d[0];
265+
r->d[0] = t & 0xFFFFFFFFULL; t >>= 32;
266+
t += ~a->d[1];
267+
r->d[1] = t & 0xFFFFFFFFULL; t >>= 32;
268+
t += ~a->d[2];
269+
r->d[2] = t & 0xFFFFFFFFULL; t >>= 32;
270+
t += ~a->d[3];
271+
r->d[3] = t & 0xFFFFFFFFULL; t >>= 32;
272+
t += ~a->d[4];
273+
r->d[4] = t & 0xFFFFFFFFULL; t >>= 32;
274+
t += ~a->d[5];
275+
r->d[5] = t & 0xFFFFFFFFULL; t >>= 32;
276+
t += ~a->d[6];
277+
r->d[6] = t & 0xFFFFFFFFULL; t >>= 32;
278+
t += ~a->d[7];
279+
r->d[7] = t & 0xFFFFFFFFULL; t >>= 32;
280+
return t;
281+
}
282+
283+
static int secp256k1_scalar_binadd(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) {
284+
uint64_t t = (uint64_t)a->d[0] + b->d[0];
285+
r->d[0] = t & 0xFFFFFFFFULL; t >>= 32;
286+
t += (uint64_t)a->d[1] + b->d[1];
287+
r->d[1] = t & 0xFFFFFFFFULL; t >>= 32;
288+
t += (uint64_t)a->d[2] + b->d[2];
289+
r->d[2] = t & 0xFFFFFFFFULL; t >>= 32;
290+
t += (uint64_t)a->d[3] + b->d[3];
291+
r->d[3] = t & 0xFFFFFFFFULL; t >>= 32;
292+
t += (uint64_t)a->d[4] + b->d[4];
293+
r->d[4] = t & 0xFFFFFFFFULL; t >>= 32;
294+
t += (uint64_t)a->d[5] + b->d[5];
295+
r->d[5] = t & 0xFFFFFFFFULL; t >>= 32;
296+
t += (uint64_t)a->d[6] + b->d[6];
297+
r->d[6] = t & 0xFFFFFFFFULL; t >>= 32;
298+
t += (uint64_t)a->d[7] + b->d[7];
299+
r->d[7] = t & 0xFFFFFFFFULL; t >>= 32;
300+
return t;
301+
}
262302

263303
/* Inspired by the macros in OpenSSL's crypto/bn/asm/x86_64-gcc.c. */
264304

src/scalar_impl.h

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar
222222
#endif
223223
}
224224

225+
#if !defined(EXHAUSTIVE_TEST_ORDER)
225226
static void secp256k1_scalar_pow2_div(secp256k1_scalar *r, const secp256k1_scalar *a, int k) {
226227
static const secp256k1_scalar lookup[16] = {
227228
SECP256K1_SCALAR_CONST(
@@ -293,9 +294,108 @@ static void secp256k1_scalar_pow2_div(secp256k1_scalar *r, const secp256k1_scala
293294
VERIFY_CHECK(k == 0);
294295
}
295296

297+
SECP256K1_INLINE static int secp256k1_scalar_shr_zeros(secp256k1_scalar *r) {
298+
int n, k = 0;
299+
300+
/* Ensure that we do not have more than 15 trailing zeros. */
301+
while ((n = __builtin_ctz(r->d[0] | (1 << 15)))) {
302+
k += n;
303+
secp256k1_scalar_shr_int(r, n);
304+
}
305+
306+
return k;
307+
}
308+
309+
static int secp256k1_scalar_eea_inverse(secp256k1_scalar *r, const secp256k1_scalar *n) {
310+
secp256k1_scalar u, v, i, j, acomp, negx;
311+
secp256k1_scalar *a, *b, *x0, *x1, *tmp;
312+
int ka, kb;
313+
314+
/* zero is not invertible */
315+
if (secp256k1_scalar_is_zero(n)) {
316+
secp256k1_scalar_set_int(r, 0);
317+
return 0;
318+
}
319+
320+
/**
321+
* The extended euclidian algorithm compute x, y and gcd(a, b) such as
322+
* a*x + b*y = gcd(a, b)
323+
* If we use this algorithm with b = p, then we solve a*x + p*y = gcd(a, p)
324+
* We note that:
325+
* - The order is a prime, so gcd(a, p) = 1.
326+
* - We compute modulo p, and y*p = 0 mod p.
327+
* So the equation simplify to a*x = 1, and x = a^-1.
328+
*/
329+
330+
/* a = n */
331+
u = *n;
332+
a = &u;
333+
334+
/* Because 2 is not a common factor between a and b, we can detect
335+
* multiples of 2 using the LSB and eliminate them aggressively. */
336+
ka = secp256k1_scalar_shr_zeros(a);
337+
338+
/* b = p - a */
339+
secp256k1_scalar_negate(&v, a);
340+
b = &v;
341+
342+
/* x0 = 1 */
343+
secp256k1_scalar_set_int(&i, 1);
344+
secp256k1_scalar_negate(&negx, &i);
345+
x0 = &i;
346+
347+
/* x1 = 0 */
348+
secp256k1_scalar_set_int(&j, 0);
349+
x1 = &j;
350+
351+
if (secp256k1_scalar_is_one(a)) {
352+
goto done;
353+
}
354+
355+
/* For a and b, we use 2 comlement math and ensure no overflow happens. */
356+
secp256k1_scalar_complement(&acomp, a);
357+
goto bzero;
358+
359+
while (!secp256k1_scalar_is_one(a)) {
360+
secp256k1_scalar_complement(&acomp, a);
361+
secp256k1_scalar_negate(&negx, x0);
362+
363+
VERIFY_CHECK(secp256k1_scalar_cmp_var(b, a) > 0);
364+
do {
365+
secp256k1_scalar_binadd(b, b, &acomp);
366+
367+
bzero:
368+
/* We ensure that a and b are odd, so b must be even after subtracting a. */
369+
VERIFY_CHECK(secp256k1_scalar_is_even(b));
370+
kb = secp256k1_scalar_shr_zeros(b);
371+
secp256k1_scalar_add(x1, x1, &negx);
372+
secp256k1_scalar_pow2_div(x1, x1, kb);
373+
} while (secp256k1_scalar_cmp_var(b, a) > 0);
374+
375+
/* a and b can never be equal, so if we exited, it is because a > b. */
376+
VERIFY_CHECK(secp256k1_scalar_cmp_var(a, b) > 0);
377+
378+
/* In order to speed things up, we only swap pointers */
379+
tmp = a;
380+
a = b;
381+
b = tmp;
382+
383+
tmp = x0;
384+
x0 = x1;
385+
x1 = tmp;
386+
}
387+
388+
done:
389+
secp256k1_scalar_pow2_div(r, x0, ka);
390+
return 1;
391+
}
392+
#endif
393+
296394
static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) {
297-
#if defined(USE_SCALAR_INV_BUILTIN)
395+
#if defined(EXHAUSTIVE_TEST_ORDER)
298396
secp256k1_scalar_inverse(r, x);
397+
#elif defined(USE_SCALAR_INV_BUILTIN)
398+
secp256k1_scalar_eea_inverse(r, x);
299399
#elif defined(USE_SCALAR_INV_NUM)
300400
unsigned char b[32];
301401
secp256k1_num n, m;

src/tests.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,9 +1199,7 @@ void run_scalar_tests(void) {
11991199
secp256k1_scalar one;
12001200
secp256k1_scalar r1;
12011201
secp256k1_scalar r2;
1202-
#if defined(USE_SCALAR_INV_NUM)
12031202
secp256k1_scalar zzv;
1204-
#endif
12051203
int overflow;
12061204
unsigned char chal[33][2][32] = {
12071205
{{0xff, 0xff, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00,
@@ -1751,10 +1749,8 @@ void run_scalar_tests(void) {
17511749
if (!secp256k1_scalar_is_zero(&y)) {
17521750
secp256k1_scalar_inverse(&zz, &y);
17531751
CHECK(!secp256k1_scalar_check_overflow(&zz));
1754-
#if defined(USE_SCALAR_INV_NUM)
17551752
secp256k1_scalar_inverse_var(&zzv, &y);
17561753
CHECK(secp256k1_scalar_eq(&zzv, &zz));
1757-
#endif
17581754
secp256k1_scalar_mul(&z, &z, &zz);
17591755
CHECK(!secp256k1_scalar_check_overflow(&z));
17601756
CHECK(secp256k1_scalar_eq(&x, &z));

0 commit comments

Comments
 (0)