Skip to content

Commit 1f18ac7

Browse files
Make 64x64->64 multiplications constant-time with MSVC on 32bit x86
1 parent 96d8ccb commit 1f18ac7

File tree

4 files changed

+61
-16
lines changed

4 files changed

+61
-16
lines changed

src/field_10x26_impl.h

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -454,18 +454,15 @@ void secp256k1_fe_sqr_inner(uint32_t *r, const uint32_t *a);
454454

455455
#else
456456

457-
#ifdef VERIFY
458-
#define VERIFY_BITS(x, n) VERIFY_CHECK(((x) >> (n)) == 0)
459-
#else
460-
#define VERIFY_BITS(x, n) do { } while(0)
461-
#endif
462-
463457
SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint32_t *r, const uint32_t *a, const uint32_t * SECP256K1_RESTRICT b) {
464458
uint64_t c, d;
465459
uint64_t u0, u1, u2, u3, u4, u5, u6, u7, u8;
466460
uint32_t t9, t1, t0, t2, t3, t4, t5, t6, t7;
467461
const uint32_t M = 0x3FFFFFFUL, R0 = 0x3D10UL, R1 = 0x400UL;
468462

463+
VERIFY_BITS(R0, 14);
464+
VERIFY_BITS(R1, 11);
465+
469466
VERIFY_BITS(a[0], 30);
470467
VERIFY_BITS(a[1], 30);
471468
VERIFY_BITS(a[2], 30);
@@ -765,14 +762,14 @@ SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint32_t *r, const uint32_t
765762
/* [d 0 0 0 0 0 0 0 -d*R1 r9+(c<<22)-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
766763
/* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
767764

768-
d = c * (R0 >> 4) + t0;
765+
d = MUL_64X64_63(c, R0 >> 4) + t0;
769766
VERIFY_BITS(d, 56);
770767
/* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 d-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
771768
r[0] = d & M; d >>= 26;
772769
VERIFY_BITS(r[0], 26);
773770
VERIFY_BITS(d, 30);
774771
/* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1+d r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
775-
d += c * (R1 >> 4) + t1;
772+
d += MUL_64X64_63(c, R1 >> 4) + t1;
776773
VERIFY_BITS(d, 53);
777774
VERIFY_CHECK(d <= 0x10000003FFFFBFULL);
778775
/* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 d-c*R1>>4 r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
@@ -1039,14 +1036,14 @@ SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint32_t *r, const uint32_t
10391036
/* [d 0 0 0 0 0 0 0 -d*R1 r9+(c<<22)-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
10401037
/* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
10411038

1042-
d = c * (R0 >> 4) + t0;
1039+
d = MUL_64X64_63(c, R0 >> 4) + t0;
10431040
VERIFY_BITS(d, 56);
10441041
/* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 d-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
10451042
r[0] = d & M; d >>= 26;
10461043
VERIFY_BITS(r[0], 26);
10471044
VERIFY_BITS(d, 30);
10481045
/* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1+d r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
1049-
d += c * (R1 >> 4) + t1;
1046+
d += MUL_64X64_63(c, R1 >> 4) + t1;
10501047
VERIFY_BITS(d, 53);
10511048
VERIFY_CHECK(d <= 0x10000003FFFFBFULL);
10521049
/* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 d-c*R1>>4 r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */

src/field_5x52_int128_impl.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,6 @@
99

1010
#include <stdint.h>
1111

12-
#ifdef VERIFY
13-
#define VERIFY_BITS(x, n) VERIFY_CHECK(((x) >> (n)) == 0)
14-
#else
15-
#define VERIFY_BITS(x, n) do { } while(0)
16-
#endif
17-
1812
SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t *a, const uint64_t * SECP256K1_RESTRICT b) {
1913
uint128_t c, d;
2014
uint64_t t3, t4, tx, u0;

src/scalar_8x32_impl.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,9 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
265265
/** Add a*b to the number defined by (c0,c1,c2). c2 must never overflow. */
266266
#define muladd(a,b) { \
267267
uint32_t tl, th; \
268+
/* VERIFY that the 64x64->64 mul a*b is in fact a 32x32->64 mul for MSVC, see util.h. */ \
269+
VERIFY_BITS((uint64_t)a, 32); \
270+
VERIFY_BITS((uint64_t)b, 32); \
268271
{ \
269272
uint64_t t = (uint64_t)a * b; \
270273
th = t >> 32; /* at most 0xFFFFFFFE */ \
@@ -280,6 +283,9 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
280283
/** Add a*b to the number defined by (c0,c1). c1 must never overflow. */
281284
#define muladd_fast(a,b) { \
282285
uint32_t tl, th; \
286+
/* VERIFY that the 64x64->64 mul a*b is in fact a 32x32->64 mul for MSVC, see util.h. */ \
287+
VERIFY_BITS((uint64_t)a, 32); \
288+
VERIFY_BITS((uint64_t)b, 32); \
283289
{ \
284290
uint64_t t = (uint64_t)a * b; \
285291
th = t >> 32; /* at most 0xFFFFFFFE */ \
@@ -294,6 +300,9 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
294300
/** Add 2*a*b to the number defined by (c0,c1,c2). c2 must never overflow. */
295301
#define muladd2(a,b) { \
296302
uint32_t tl, th, th2, tl2; \
303+
/* VERIFY that the 64x64->64 mul a*b is in fact a 32x32->64 mul for MSVC, see util.h. */ \
304+
VERIFY_BITS((uint64_t)a, 32); \
305+
VERIFY_BITS((uint64_t)b, 32); \
297306
{ \
298307
uint64_t t = (uint64_t)a * b; \
299308
th = t >> 32; /* at most 0xFFFFFFFE */ \

src/util.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ static SECP256K1_INLINE void secp256k1_callback_call(const secp256k1_callback *
6969
#define VERIFY_SETUP(stmt)
7070
#endif
7171

72+
#ifdef VERIFY
73+
#define VERIFY_BITS(x, n) VERIFY_CHECK(((x) >> (n)) == 0)
74+
#else
75+
#define VERIFY_BITS(x, n) do { } while(0)
76+
#endif
77+
7278
static SECP256K1_INLINE void *checked_malloc(const secp256k1_callback* cb, size_t size) {
7379
void *ret = malloc(size);
7480
if (ret == NULL) {
@@ -172,4 +178,43 @@ static SECP256K1_INLINE void memczero(void *s, size_t len, int flag) {
172178
}
173179
}
174180

181+
/* MSVC for x86 32-bit targets implements 64x64->64 bit multiplications using
182+
a non-constant subroutine. The subroutine is not constant-time because it
183+
shortcuts when the high 32 bits of both multiplicands are all 0.
184+
See https://research.kudelskisecurity.com/2017/01/16/when-constant-time-source-may-not-save-you/
185+
and also https://www.bearssl.org/ctmul.html for more information.
186+
187+
To work around this we can use the following macro if all we need is a
188+
64x64->63 multiplication. We set the MSB of the right multiplicand
189+
before the multiplication and clear the MSB of the product.
190+
191+
For 64-bit integers a, b where a, b, and a*b can be represented in 63
192+
bits, we have
193+
194+
(a * (b | 0x8000000000000000)) & 0x7FFFFFFFFFFFFFFF
195+
= (a * (b + 0x8000000000000000)) & 0x7FFFFFFFFFFFFFFF
196+
= ((a * b) + (a * 0x8000000000000000)) & 0x7FFFFFFFFFFFFFFF
197+
= ((a * b) + (a << 63)) & 0x7FFFFFFFFFFFFFFF
198+
= ((a * b) | (a << 63)) & 0x7FFFFFFFFFFFFFFF
199+
= ((a * b) & 0x7FFFFFFFFFFFFFFF) | (a << 63) & 0x7FFFFFFFFFFFFFFF
200+
= (a * b) | 0
201+
= a * b.
202+
203+
If a*b can be represented in 63 bits but a or b can't, we necessarily
204+
have a == 0 or b == 0. Then it's easy to check that
205+
206+
(0 * (b | 0x8000000000000000)) & 0x7FFFFFFFFFFFFFFF = 0, and
207+
(a * (0 | 0x8000000000000000)) & 0x7FFFFFFFFFFFFFFF = 0.
208+
209+
Thus our macro works for any 64x64->63 multiplication.
210+
211+
A variant of the macro for 16x16->15 bit multiplications has been
212+
verified for all acceptable inputs by brute-force computation.
213+
*/
214+
#if defined(_MSC_VER) && defined(_M_IX86)
215+
#define MUL_64X64_63(a, b) ((((a) | UINT64_C(0x8000000000000000)) * (b)) & UINT64_C(0x7FFFFFFFFFFFFFFF))
216+
#else
217+
#define MUL_64X64_63(a, b) ((a) * (b))
218+
#endif
219+
175220
#endif /* SECP256K1_UTIL_H */

0 commit comments

Comments
 (0)