Skip to content

Commit 966f173

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 78eb24e commit 966f173

File tree

4 files changed

+169
-6
lines changed

4 files changed

+169
-6
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: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,8 @@ static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar
222222
#endif
223223
}
224224

225-
SECP256K1_INLINE static void secp256k1_scalar_pow2_div(secp256k1_scalar *r, const secp256k1_scalar *a, int k) {
225+
#if !defined(EXHAUSTIVE_TEST_ORDER)
226+
static void secp256k1_scalar_pow2_div(secp256k1_scalar *r, const secp256k1_scalar *a, int k) {
226227
static const secp256k1_scalar lookup[15] = {
227228
SECP256K1_SCALAR_CONST(
228229
0xEFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL,
@@ -297,9 +298,110 @@ SECP256K1_INLINE static void secp256k1_scalar_pow2_div(secp256k1_scalar *r, cons
297298
VERIFY_CHECK(k == 0);
298299
}
299300

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

src/tests.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,9 +1163,7 @@ void run_scalar_tests(void) {
11631163
secp256k1_scalar one;
11641164
secp256k1_scalar r1;
11651165
secp256k1_scalar r2;
1166-
#if defined(USE_SCALAR_INV_NUM)
11671166
secp256k1_scalar zzv;
1168-
#endif
11691167
int overflow;
11701168
unsigned char chal[33][2][32] = {
11711169
{{0xff, 0xff, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00,
@@ -1715,10 +1713,8 @@ void run_scalar_tests(void) {
17151713
if (!secp256k1_scalar_is_zero(&y)) {
17161714
secp256k1_scalar_inverse(&zz, &y);
17171715
CHECK(!secp256k1_scalar_check_overflow(&zz));
1718-
#if defined(USE_SCALAR_INV_NUM)
17191716
secp256k1_scalar_inverse_var(&zzv, &y);
17201717
CHECK(secp256k1_scalar_eq(&zzv, &zz));
1721-
#endif
17221718
secp256k1_scalar_mul(&z, &z, &zz);
17231719
CHECK(!secp256k1_scalar_check_overflow(&z));
17241720
CHECK(secp256k1_scalar_eq(&x, &z));

0 commit comments

Comments
 (0)