Skip to content

Commit 4c08c59

Browse files
committed
Faster const-time normalization
1 parent a1102b1 commit 4c08c59

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
@@ -53,33 +53,15 @@ static void secp256k1_fe_normalize(secp256k1_fe *r) {
5353
uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4],
5454
t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9];
5555

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

6061
/* The first pass ensures the magnitude is 1, ... */
6162
t0 += x * 0x3D1UL; t1 += (x << 6);
6263
t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL;
6364
t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL;
64-
t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; m = t2;
65-
t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; m &= t3;
66-
t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; m &= t4;
67-
t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; m &= t5;
68-
t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; m &= t6;
69-
t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; m &= t7;
70-
t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; m &= t8;
71-
72-
/* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */
73-
VERIFY_CHECK(t9 >> 23 == 0);
74-
75-
/* At most a single final reduction is needed; check if the value is >= the field characteristic */
76-
x = (t9 >> 22) | ((t9 == 0x03FFFFFUL) & (m == 0x3FFFFFFUL)
77-
& ((t1 + 0x40UL + ((t0 + 0x3D1UL) >> 26)) > 0x3FFFFFFUL));
78-
79-
/* Apply the final reduction (for constant-time behaviour, we do it always) */
80-
t0 += x * 0x3D1UL; t1 += (x << 6);
81-
t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL;
82-
t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL;
8365
t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL;
8466
t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL;
8567
t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL;
@@ -88,11 +70,24 @@ static void secp256k1_fe_normalize(secp256k1_fe *r) {
8870
t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL;
8971
t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL;
9072

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

94-
/* Mask off the possible multiple of 2^256 from the final reduction */
95-
t9 &= 0x03FFFFFUL;
76+
/* The second pass subtracts (2^256 - P) from (t0..t9) iff there was no carry.
77+
* No underflow is possible as we just added at least that amount in the first pass. */
78+
x = (t9 >> 22) - 1; t9 &= 0x03FFFFFUL;
79+
VERIFY_CHECK(x == 0 || x == -(uint32_t)1);
80+
81+
t0 -= x & 0x3D1UL; t1 -= x & 0x40UL;
82+
t1 -= (t0 >> 31); t0 &= 0x3FFFFFFUL;
83+
t2 -= (t1 >> 31); t1 &= 0x3FFFFFFUL;
84+
t3 -= (t2 >> 31); t2 &= 0x3FFFFFFUL;
85+
t4 -= (t3 >> 31); t3 &= 0x3FFFFFFUL;
86+
t5 -= (t4 >> 31); t4 &= 0x3FFFFFFUL;
87+
t6 -= (t5 >> 31); t5 &= 0x3FFFFFFUL;
88+
t7 -= (t6 >> 31); t6 &= 0x3FFFFFFUL;
89+
t8 -= (t7 >> 31); t7 &= 0x3FFFFFFUL;
90+
t9 -= (t8 >> 31); t8 &= 0x3FFFFFFUL;
9691

9792
r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4;
9893
r->n[5] = t5; r->n[6] = t6; r->n[7] = t7; r->n[8] = t8; r->n[9] = t9;
@@ -195,29 +190,32 @@ static int secp256k1_fe_normalizes_to_zero(const secp256k1_fe *r) {
195190
uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4],
196191
t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9];
197192

198-
/* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */
199-
uint32_t z0, z1;
193+
/* z1 tracks a possible raw value of 0, z2 tracks a possible raw value of P */
194+
uint32_t z0, z1, z2;
200195

201-
/* Reduce t9 at the start so there will be at most a single carry from the first pass */
202-
uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL;
196+
/* Reduce t9 at the start so there will be at most a single carry from the first pass
197+
* x is incremented before the first pass so both match values have internal zeros */
198+
uint32_t x = (t9 >> 22) + 1; t9 &= 0x03FFFFFUL;
203199

204200
/* The first pass ensures the magnitude is 1, ... */
205201
t0 += x * 0x3D1UL; t1 += (x << 6);
206-
t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; z0 = t0; z1 = t0 ^ 0x3D0UL;
207-
t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; z0 |= t1; z1 &= t1 ^ 0x40UL;
208-
t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; z0 |= t2; z1 &= t2;
209-
t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; z0 |= t3; z1 &= t3;
210-
t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; z0 |= t4; z1 &= t4;
211-
t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; z0 |= t5; z1 &= t5;
212-
t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; z0 |= t6; z1 &= t6;
213-
t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; z0 |= t7; z1 &= t7;
214-
t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; z0 |= t8; z1 &= t8;
215-
z0 |= t9; z1 &= t9 ^ 0x3C00000UL;
202+
t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL;
203+
t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL;
204+
t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; z0 = t2;
205+
t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; z0 |= t3;
206+
t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; z0 |= t4;
207+
t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; z0 |= t5;
208+
t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; z0 |= t6;
209+
t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; z0 |= t7;
210+
t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; z0 |= t8;
211+
212+
z1 = z0 | (t0 ^ 0x3D1UL) | (t1 ^ 0x40UL) | t9;
213+
z2 = z0 | t0 | t1 | (t9 ^ 0x400000UL);
216214

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

220-
return (z0 == 0) | (z1 == 0x3FFFFFFUL);
218+
return (z1 == 0) | (z2 == 0);
221219
}
222220

223221
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
@@ -61,36 +61,31 @@ static void secp256k1_fe_verify(const secp256k1_fe *a) {
6161
static void secp256k1_fe_normalize(secp256k1_fe *r) {
6262
uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4];
6363

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

6869
/* The first pass ensures the magnitude is 1, ... */
6970
t0 += x * 0x1000003D1ULL;
7071
t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL;
71-
t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; m = t1;
72-
t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; m &= t2;
73-
t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; m &= t3;
74-
75-
/* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */
76-
VERIFY_CHECK(t4 >> 49 == 0);
77-
78-
/* At most a single final reduction is needed; check if the value is >= the field characteristic */
79-
x = (t4 >> 48) | ((t4 == 0x0FFFFFFFFFFFFULL) & (m == 0xFFFFFFFFFFFFFULL)
80-
& (t0 >= 0xFFFFEFFFFFC2FULL));
81-
82-
/* Apply the final reduction (for constant-time behaviour, we do it always) */
83-
t0 += x * 0x1000003D1ULL;
84-
t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL;
8572
t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL;
8673
t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL;
8774
t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL;
8875

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

92-
/* Mask off the possible multiple of 2^256 from the final reduction */
93-
t4 &= 0x0FFFFFFFFFFFFULL;
84+
t0 -= x & 0x1000003D1ULL;
85+
t1 -= (t0 >> 63); t0 &= 0xFFFFFFFFFFFFFULL;
86+
t2 -= (t1 >> 63); t1 &= 0xFFFFFFFFFFFFFULL;
87+
t3 -= (t2 >> 63); t2 &= 0xFFFFFFFFFFFFFULL;
88+
t4 -= (t3 >> 63); t3 &= 0xFFFFFFFFFFFFFULL;
9489

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

@@ -172,24 +167,27 @@ static void secp256k1_fe_normalize_var(secp256k1_fe *r) {
172167
static int secp256k1_fe_normalizes_to_zero(const secp256k1_fe *r) {
173168
uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4];
174169

175-
/* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */
176-
uint64_t z0, z1;
170+
/* z1 tracks a possible raw value of 0, z2 tracks a possible raw value of P */
171+
uint64_t z0, z1, z2;
177172

178-
/* Reduce t4 at the start so there will be at most a single carry from the first pass */
179-
uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL;
173+
/* Reduce t4 at the start so there will be at most a single carry from the first pass
174+
* x is incremented before the first pass so both match values have internal zeros */
175+
uint64_t x = (t4 >> 48) + 1; t4 &= 0x0FFFFFFFFFFFFULL;
180176

181177
/* The first pass ensures the magnitude is 1, ... */
182178
t0 += x * 0x1000003D1ULL;
183-
t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; z0 = t0; z1 = t0 ^ 0x1000003D0ULL;
184-
t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; z0 |= t1; z1 &= t1;
185-
t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; z0 |= t2; z1 &= t2;
186-
t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; z0 |= t3; z1 &= t3;
187-
z0 |= t4; z1 &= t4 ^ 0xF000000000000ULL;
179+
t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL;
180+
t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; z0 = t1;
181+
t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; z0 |= t2;
182+
t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; z0 |= t3;
183+
184+
z1 = z0 | (t0 ^ 0x1000003D1ULL) | t4;
185+
z2 = z0 | t0 | (t4 ^ 0x1000000000000ULL);
188186

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

192-
return (z0 == 0) | (z1 == 0xFFFFFFFFFFFFFULL);
190+
return (z1 == 0) | (z2 == 0);
193191
}
194192

195193
static int secp256k1_fe_normalizes_to_zero_var(const secp256k1_fe *r) {

0 commit comments

Comments
 (0)