Skip to content

Commit 50ef2da

Browse files
fixup! Reduce side channels from single-bit reads
1 parent 7dec5ab commit 50ef2da

File tree

2 files changed

+36
-8
lines changed

2 files changed

+36
-8
lines changed

src/ecmult_gen_impl.h

+24-8
Original file line numberDiff line numberDiff line change
@@ -200,15 +200,31 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp25
200200
* together: bit (tooth) of bits = bit
201201
* ((block*COMB_TEETH + tooth)*COMB_SPACING + comb_off) of d. */
202202
uint32_t bits = 0, sign, abs, index, tooth;
203+
/* Instead of reading individual bits here to construct the bits variable,
204+
* build up the result by xoring rotated reads together. In every iteration,
205+
* one additional bit is made correct, starting at the bottom. The bits
206+
* above that contain junk. This reduces leakage by avoiding computations
207+
* on variables that can have only a low number of possible values (e.g.,
208+
* just two values when reading a single bit into a variable.) See:
209+
* https://www.usenix.org/system/files/conference/usenixsecurity18/sec18-alam.pdf
210+
*/
203211
for (tooth = 0; tooth < COMB_TEETH; ++tooth) {
204-
/* Instead of reading individual bits here to construct bits, build up
205-
* the result by xoring shifted reads together. In every iteration, one
206-
* additional bit is made correct, starting at the bottom. The bits
207-
* above that contain junk. This reduces leakage from single bits. See
208-
* https://www.usenix.org/system/files/conference/usenixsecurity18/sec18-alam.pdf
209-
*/
210-
uint32_t bitdata = recoded[bit_pos >> 5] >> (bit_pos & 0x1f);
211-
bits &= ~(1 << tooth);
212+
/* Construct bitdata s.t. the bottom bit is the bit we'd like to read.
213+
*
214+
* We could just set bitdata = recoded[bit_pos >> 5] >> (bit_pos & 0x1f)
215+
* but this would simply discard the bits that fall off at the bottom,
216+
* and thus, for example, bitdata could still have only two values if we
217+
* happen to shift by exactly 31 positions. We use a rotation instead,
218+
* which ensures that bitdata doesn't loose entropy. This relies on the
219+
* rotation being atomic, i.e., the compiler emitting an actual rot
220+
* instruction. */
221+
uint32_t bitdata = secp256k1_rotr32(recoded[bit_pos >> 5], bit_pos & 0x1f);
222+
223+
/* Clear the bit at position tooth, but sssh, don't tell clang. */
224+
uint32_t volatile vmask = ~(1 << tooth);
225+
bits &= vmask;
226+
227+
/* Write the bit into position tooth (and junk into higher bits). */
212228
bits ^= bitdata << tooth;
213229
bit_pos += COMB_SPACING;
214230
}

src/util.h

+12
Original file line numberDiff line numberDiff line change
@@ -391,4 +391,16 @@ SECP256K1_INLINE static void secp256k1_write_be64(unsigned char* p, uint64_t x)
391391
p[0] = x >> 56;
392392
}
393393

394+
/* Rotate a uint32_t to the right. */
395+
SECP256K1_INLINE static uint32_t secp256k1_rotr32(const uint32_t x, const unsigned int by) {
396+
#if defined(_MSC_VER)
397+
return _rotr(x, by); /* needs <stdlib.h> */
398+
#else
399+
/* Reduce rotation amount to avoid UB when shifting. */
400+
const unsigned int mask = CHAR_BIT * sizeof(x) - 1;
401+
/* Turned it into a rot instruction by GCC and clang. */
402+
return (x >> (by & mask)) | (x << ((-by) & mask));
403+
#endif
404+
}
405+
394406
#endif /* SECP256K1_UTIL_H */

0 commit comments

Comments
 (0)