Skip to content

Commit 2a7b0e1

Browse files
committed
Faster const-time normalization
1 parent be6944a commit 2a7b0e1

File tree

2 files changed

+68
-72
lines changed

2 files changed

+68
-72
lines changed

src/field_10x26_impl.h

Lines changed: 39 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -44,33 +44,15 @@ static void secp256k1_fe_normalize(secp256k1_fe *r) {
4444
uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4],
4545
t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9];
4646

47-
/* Reduce t9 at the start so there will be at most a single carry from the first pass */
48-
uint32_t m;
49-
uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL;
47+
/* Reduce t9 at the start so there will be at most a single carry from the first pass.
48+
* x is incremented before the first pass and then decremented before the second pass
49+
* to ensure that the result doesn't fall into the range [P, 2^256). */
50+
uint32_t x = (t9 >> 22) + 1; t9 &= 0x03FFFFFUL;
5051

5152
/* The first pass ensures the magnitude is 1, ... */
5253
t0 += x * 0x3D1UL; t1 += (x << 6);
5354
t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL;
5455
t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL;
55-
t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; m = t2;
56-
t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; m &= t3;
57-
t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; m &= t4;
58-
t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; m &= t5;
59-
t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; m &= t6;
60-
t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; m &= t7;
61-
t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; m &= t8;
62-
63-
/* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */
64-
VERIFY_CHECK(t9 >> 23 == 0);
65-
66-
/* At most a single final reduction is needed; check if the value is >= the field characteristic */
67-
x = (t9 >> 22) | ((t9 == 0x03FFFFFUL) & (m == 0x3FFFFFFUL)
68-
& ((t1 + 0x40UL + ((t0 + 0x3D1UL) >> 26)) > 0x3FFFFFFUL));
69-
70-
/* Apply the final reduction (for constant-time behaviour, we do it always) */
71-
t0 += x * 0x3D1UL; t1 += (x << 6);
72-
t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL;
73-
t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL;
7456
t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL;
7557
t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL;
7658
t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL;
@@ -79,11 +61,24 @@ static void secp256k1_fe_normalize(secp256k1_fe *r) {
7961
t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL;
8062
t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL;
8163

82-
/* If t9 didn't carry to bit 22 already, then it should have after any final reduction */
83-
VERIFY_CHECK(t9 >> 22 == x);
64+
/* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */
65+
VERIFY_CHECK(t9 >> 23 == 0);
8466

85-
/* Mask off the possible multiple of 2^256 from the final reduction */
86-
t9 &= 0x03FFFFFUL;
67+
/* The second pass subtracts (2^256 - P) from (t0..t9) iff there was no carry.
68+
* No underflow is possible as we just added at least that amount in the first pass. */
69+
x = (t9 >> 22) - 1; t9 &= 0x03FFFFFUL;
70+
VERIFY_CHECK(x == 0 || x == -(uint32_t)1);
71+
72+
t0 -= x & 0x3D1UL; t1 -= x & 0x40UL;
73+
t1 -= (t0 >> 31); t0 &= 0x3FFFFFFUL;
74+
t2 -= (t1 >> 31); t1 &= 0x3FFFFFFUL;
75+
t3 -= (t2 >> 31); t2 &= 0x3FFFFFFUL;
76+
t4 -= (t3 >> 31); t3 &= 0x3FFFFFFUL;
77+
t5 -= (t4 >> 31); t4 &= 0x3FFFFFFUL;
78+
t6 -= (t5 >> 31); t5 &= 0x3FFFFFFUL;
79+
t7 -= (t6 >> 31); t6 &= 0x3FFFFFFUL;
80+
t8 -= (t7 >> 31); t7 &= 0x3FFFFFFUL;
81+
t9 -= (t8 >> 31); t8 &= 0x3FFFFFFUL;
8782

8883
r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4;
8984
r->n[5] = t5; r->n[6] = t6; r->n[7] = t7; r->n[8] = t8; r->n[9] = t9;
@@ -186,29 +181,32 @@ static int secp256k1_fe_normalizes_to_zero(const secp256k1_fe *r) {
186181
uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4],
187182
t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9];
188183

189-
/* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */
190-
uint32_t z0, z1;
184+
/* z1 tracks a possible raw value of 0, z2 tracks a possible raw value of P */
185+
uint32_t z0, z1, z2;
191186

192-
/* Reduce t9 at the start so there will be at most a single carry from the first pass */
193-
uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL;
187+
/* Reduce t9 at the start so there will be at most a single carry from the first pass
188+
* x is incremented before the first pass so both match values have internal zeros */
189+
uint32_t x = (t9 >> 22) + 1; t9 &= 0x03FFFFFUL;
194190

195191
/* The first pass ensures the magnitude is 1, ... */
196192
t0 += x * 0x3D1UL; t1 += (x << 6);
197-
t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; z0 = t0; z1 = t0 ^ 0x3D0UL;
198-
t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; z0 |= t1; z1 &= t1 ^ 0x40UL;
199-
t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; z0 |= t2; z1 &= t2;
200-
t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; z0 |= t3; z1 &= t3;
201-
t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; z0 |= t4; z1 &= t4;
202-
t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; z0 |= t5; z1 &= t5;
203-
t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; z0 |= t6; z1 &= t6;
204-
t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; z0 |= t7; z1 &= t7;
205-
t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; z0 |= t8; z1 &= t8;
206-
z0 |= t9; z1 &= t9 ^ 0x3C00000UL;
193+
t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL;
194+
t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL;
195+
t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; z0 = t2;
196+
t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; z0 |= t3;
197+
t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; z0 |= t4;
198+
t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; z0 |= t5;
199+
t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; z0 |= t6;
200+
t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; z0 |= t7;
201+
t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; z0 |= t8;
202+
203+
z1 = z0 | (t0 ^ 0x3D1UL) | (t1 ^ 0x40UL) | t9;
204+
z2 = z0 | t0 | t1 | (t9 ^ 0x400000UL);
207205

208206
/* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */
209207
VERIFY_CHECK(t9 >> 23 == 0);
210208

211-
return (z0 == 0) | (z1 == 0x3FFFFFFUL);
209+
return (z1 == 0) | (z2 == 0);
212210
}
213211

214212
static int secp256k1_fe_normalizes_to_zero_var(const secp256k1_fe *r) {

src/field_5x52_impl.h

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -54,36 +54,31 @@ static void secp256k1_fe_verify(const secp256k1_fe *a) {
5454
static void secp256k1_fe_normalize(secp256k1_fe *r) {
5555
uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4];
5656

57-
/* Reduce t4 at the start so there will be at most a single carry from the first pass */
58-
uint64_t m;
59-
uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL;
57+
/* Reduce t4 at the start so there will be at most a single carry from the first pass.
58+
* x is incremented before the first pass and then decremented before the second pass
59+
* to ensure that the result doesn't fall into the range [P, 2^256). */
60+
uint64_t x = (t4 >> 48) + 1; t4 &= 0x0FFFFFFFFFFFFULL;
6061

6162
/* The first pass ensures the magnitude is 1, ... */
6263
t0 += x * 0x1000003D1ULL;
6364
t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL;
64-
t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; m = t1;
65-
t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; m &= t2;
66-
t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; m &= t3;
67-
68-
/* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */
69-
VERIFY_CHECK(t4 >> 49 == 0);
70-
71-
/* At most a single final reduction is needed; check if the value is >= the field characteristic */
72-
x = (t4 >> 48) | ((t4 == 0x0FFFFFFFFFFFFULL) & (m == 0xFFFFFFFFFFFFFULL)
73-
& (t0 >= 0xFFFFEFFFFFC2FULL));
74-
75-
/* Apply the final reduction (for constant-time behaviour, we do it always) */
76-
t0 += x * 0x1000003D1ULL;
77-
t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL;
7865
t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL;
7966
t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL;
8067
t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL;
8168

82-
/* If t4 didn't carry to bit 48 already, then it should have after any final reduction */
83-
VERIFY_CHECK(t4 >> 48 == x);
69+
/* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element). */
70+
VERIFY_CHECK(t4 >> 49 == 0);
71+
72+
/* The second pass subtracts (2^256 - P) from (t0..t4) iff there was no carry.
73+
* No underflow is possible as we just added at least that amount in the first pass. */
74+
x = (t4 >> 48) - 1; t4 &= 0x0FFFFFFFFFFFFULL;
75+
VERIFY_CHECK(x == 0 || x == -(uint64_t)1);
8476

85-
/* Mask off the possible multiple of 2^256 from the final reduction */
86-
t4 &= 0x0FFFFFFFFFFFFULL;
77+
t0 -= x & 0x1000003D1ULL;
78+
t1 -= (t0 >> 63); t0 &= 0xFFFFFFFFFFFFFULL;
79+
t2 -= (t1 >> 63); t1 &= 0xFFFFFFFFFFFFFULL;
80+
t3 -= (t2 >> 63); t2 &= 0xFFFFFFFFFFFFFULL;
81+
t4 -= (t3 >> 63); t3 &= 0xFFFFFFFFFFFFFULL;
8782

8883
r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4;
8984

@@ -165,24 +160,27 @@ static void secp256k1_fe_normalize_var(secp256k1_fe *r) {
165160
static int secp256k1_fe_normalizes_to_zero(const secp256k1_fe *r) {
166161
uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4];
167162

168-
/* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */
169-
uint64_t z0, z1;
163+
/* z1 tracks a possible raw value of 0, z2 tracks a possible raw value of P */
164+
uint64_t z0, z1, z2;
170165

171-
/* Reduce t4 at the start so there will be at most a single carry from the first pass */
172-
uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL;
166+
/* Reduce t4 at the start so there will be at most a single carry from the first pass
167+
* x is incremented before the first pass so both match values have internal zeros */
168+
uint64_t x = (t4 >> 48) + 1; t4 &= 0x0FFFFFFFFFFFFULL;
173169

174170
/* The first pass ensures the magnitude is 1, ... */
175171
t0 += x * 0x1000003D1ULL;
176-
t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; z0 = t0; z1 = t0 ^ 0x1000003D0ULL;
177-
t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; z0 |= t1; z1 &= t1;
178-
t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; z0 |= t2; z1 &= t2;
179-
t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; z0 |= t3; z1 &= t3;
180-
z0 |= t4; z1 &= t4 ^ 0xF000000000000ULL;
172+
t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL;
173+
t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; z0 = t1;
174+
t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; z0 |= t2;
175+
t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; z0 |= t3;
176+
177+
z1 = z0 | (t0 ^ 0x1000003D1ULL) | t4;
178+
z2 = z0 | t0 | (t4 ^ 0x1000000000000ULL);
181179

182180
/* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */
183181
VERIFY_CHECK(t4 >> 49 == 0);
184182

185-
return (z0 == 0) | (z1 == 0xFFFFFFFFFFFFFULL);
183+
return (z1 == 0) | (z2 == 0);
186184
}
187185

188186
static int secp256k1_fe_normalizes_to_zero_var(const secp256k1_fe *r) {

0 commit comments

Comments
 (0)