Fix timing leak in BN_from_montgomery_word.
BN_from_montgomery_word doesn't have a constant memory access pattern.
Replace the pointer trick with a constant-time select. There is, of
course, still the bn_correct_top leak pervasive in BIGNUM itself.
See also https://boringssl-review.googlesource.com/22904 from BoringSSL.
Reviewed-by: Andy Polyakov <appro@openssl.org>
Reviewed-by: Kurt Roeckx <kurt@roeckx.be>
(Merged from https://github.com/openssl/openssl/pull/5228)
(cherry picked from commit f345b1f39d9b4e4c9ef07e7522e9b2a870c9ca09)
David Benjamin authored 6 years ago
Andy Polyakov committed 6 years ago
100 | 100 | r->top = max; |
101 | 101 | n0 = mont->n0[0]; |
102 | 102 | |
103 | /* | |
104 | * Add multiples of |n| to |r| until R = 2^(nl * BN_BITS2) divides it. On | |
105 | * input, we had |r| < |n| * R, so now |r| < 2 * |n| * R. Note that |r| | |
106 | * includes |carry| which is stored separately. | |
107 | */ | |
103 | 108 | for (carry = 0, i = 0; i < nl; i++, rp++) { |
104 | 109 | v = bn_mul_add_words(rp, np, nl, (rp[0] * n0) & BN_MASK2); |
105 | 110 | v = (v + carry + rp[nl]) & BN_MASK2; |
114 | 119 | ret->neg = r->neg; |
115 | 120 | |
116 | 121 | rp = ret->d; |
122 | ||
123 | /* | |
124 | * Shift |nl| words to divide by R. We have |ap| < 2 * |n|. Note that |ap| | |
125 | * includes |carry| which is stored separately. | |
126 | */ | |
117 | 127 | ap = &(r->d[nl]); |
118 | 128 | |
119 | # define BRANCH_FREE 1 | |
120 | # if BRANCH_FREE | |
121 | { | |
122 | BN_ULONG *nrp; | |
123 | size_t m; | |
124 | ||
125 | v = bn_sub_words(rp, ap, np, nl) - carry; | |
126 | /* | |
127 | * if subtraction result is real, then trick unconditional memcpy | |
128 | * below to perform in-place "refresh" instead of actual copy. | |
129 | */ | |
130 | m = (0 - (size_t)v); | |
131 | nrp = | |
132 | (BN_ULONG *)(((PTR_SIZE_INT) rp & ~m) | ((PTR_SIZE_INT) ap & m)); | |
133 | ||
134 | for (i = 0, nl -= 4; i < nl; i += 4) { | |
135 | BN_ULONG t1, t2, t3, t4; | |
136 | ||
137 | t1 = nrp[i + 0]; | |
138 | t2 = nrp[i + 1]; | |
139 | t3 = nrp[i + 2]; | |
140 | ap[i + 0] = 0; | |
141 | t4 = nrp[i + 3]; | |
142 | ap[i + 1] = 0; | |
143 | rp[i + 0] = t1; | |
144 | ap[i + 2] = 0; | |
145 | rp[i + 1] = t2; | |
146 | ap[i + 3] = 0; | |
147 | rp[i + 2] = t3; | |
148 | rp[i + 3] = t4; | |
149 | } | |
150 | for (nl += 4; i < nl; i++) | |
151 | rp[i] = nrp[i], ap[i] = 0; | |
152 | } | |
153 | # else | |
154 | if (bn_sub_words(rp, ap, np, nl) - carry) | |
155 | memcpy(rp, ap, nl * sizeof(BN_ULONG)); | |
156 | # endif | |
129 | /* | |
130 | * |v| is one if |ap| - |np| underflowed or zero if it did not. Note |v| | |
131 | * cannot be -1. That would imply the subtraction did not fit in |nl| words, | |
132 | * and we know at most one subtraction is needed. | |
133 | */ | |
134 | v = bn_sub_words(rp, ap, np, nl) - carry; | |
135 | v = 0 - v; | |
136 | for (i = 0; i < nl; i++) { | |
137 | rp[i] = (v & ap[i]) | (~v & rp[i]); | |
138 | ap[i] = 0; | |
139 | } | |
157 | 140 | bn_correct_top(r); |
158 | 141 | bn_correct_top(ret); |
159 | 142 | bn_check_top(ret); |