diff options
author | jsing <> | 2019-10-17 14:31:56 +0000 |
---|---|---|
committer | jsing <> | 2019-10-17 14:31:56 +0000 |
commit | 5005c768b214178dfda133906da127d8d4c0a5bd (patch) | |
tree | d4d1d31e3dd32330ddca0199ceba659dc7eecb99 | |
parent | f94e8cb1dd847cd2efa0211e711707fc705f11eb (diff) | |
download | openbsd-5005c768b214178dfda133906da127d8d4c0a5bd.tar.gz openbsd-5005c768b214178dfda133906da127d8d4c0a5bd.tar.bz2 openbsd-5005c768b214178dfda133906da127d8d4c0a5bd.zip |
Sync RSA_padding_check_PKCS1_OAEP_mgf1().
Update RSA_padding_check_PKCS1_OAEP_mgf1() with code from OpenSSL 1.1.1d
(with some improvements/corrections to comments).
This brings in code to make the padding check constant time.
ok inoguchi@ tb@
-rw-r--r-- | src/lib/libcrypto/rsa/rsa_oaep.c | 175 |
1 files changed, 111 insertions, 64 deletions
diff --git a/src/lib/libcrypto/rsa/rsa_oaep.c b/src/lib/libcrypto/rsa/rsa_oaep.c index 6b1760da60..e54600b094 100644 --- a/src/lib/libcrypto/rsa/rsa_oaep.c +++ b/src/lib/libcrypto/rsa/rsa_oaep.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: rsa_oaep.c,v 1.32 2019/10/09 16:17:59 jsing Exp $ */ | 1 | /* $OpenBSD: rsa_oaep.c,v 1.33 2019/10/17 14:31:56 jsing Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright 1999-2018 The OpenSSL Project Authors. All Rights Reserved. | 3 | * Copyright 1999-2018 The OpenSSL Project Authors. All Rights Reserved. |
4 | * | 4 | * |
@@ -79,6 +79,7 @@ | |||
79 | #include <openssl/rsa.h> | 79 | #include <openssl/rsa.h> |
80 | #include <openssl/sha.h> | 80 | #include <openssl/sha.h> |
81 | 81 | ||
82 | #include "constant_time_locl.h" | ||
82 | #include "rsa_locl.h" | 83 | #include "rsa_locl.h" |
83 | 84 | ||
84 | int | 85 | int |
@@ -169,13 +170,11 @@ RSA_padding_check_PKCS1_OAEP_mgf1(unsigned char *to, int tlen, | |||
169 | const unsigned char *from, int flen, int num, const unsigned char *param, | 170 | const unsigned char *from, int flen, int num, const unsigned char *param, |
170 | int plen, const EVP_MD *md, const EVP_MD *mgf1md) | 171 | int plen, const EVP_MD *md, const EVP_MD *mgf1md) |
171 | { | 172 | { |
172 | int i, dblen, mlen = -1; | 173 | int i, dblen = 0, mlen = -1, one_index = 0, msg_index; |
173 | const unsigned char *maskeddb; | 174 | unsigned int good = 0, found_one_byte, mask; |
174 | int lzero; | 175 | const unsigned char *maskedseed, *maskeddb; |
175 | unsigned char *db = NULL; | ||
176 | unsigned char seed[EVP_MAX_MD_SIZE], phash[EVP_MAX_MD_SIZE]; | 176 | unsigned char seed[EVP_MAX_MD_SIZE], phash[EVP_MAX_MD_SIZE]; |
177 | unsigned char *padded_from; | 177 | unsigned char *db = NULL, *em = NULL; |
178 | int bad = 0; | ||
179 | int mdlen; | 178 | int mdlen; |
180 | 179 | ||
181 | if (md == NULL) | 180 | if (md == NULL) |
@@ -184,88 +183,136 @@ RSA_padding_check_PKCS1_OAEP_mgf1(unsigned char *to, int tlen, | |||
184 | mgf1md = md; | 183 | mgf1md = md; |
185 | 184 | ||
186 | if ((mdlen = EVP_MD_size(md)) <= 0) | 185 | if ((mdlen = EVP_MD_size(md)) <= 0) |
187 | goto err; | 186 | return -1; |
188 | 187 | ||
189 | if (--num < 2 * mdlen + 1) | 188 | if (tlen <= 0 || flen <= 0) |
190 | /* | 189 | return -1; |
191 | * 'num' is the length of the modulus, i.e. does not depend | ||
192 | * on the particular ciphertext. | ||
193 | */ | ||
194 | goto decoding_err; | ||
195 | 190 | ||
196 | lzero = num - flen; | 191 | /* |
197 | if (lzero < 0) { | 192 | * |num| is the length of the modulus; |flen| is the length of the |
198 | /* | 193 | * encoded message. Therefore, for any |from| that was obtained by |
199 | * signalling this error immediately after detection might allow | 194 | * decrypting a ciphertext, we must have |flen| <= |num|. Similarly, |
200 | * for side-channel attacks (e.g. timing if 'plen' is huge | 195 | * |num| >= 2 * |mdlen| + 2 must hold for the modulus irrespective |
201 | * -- cf. James H. Manger, "A Chosen Ciphertext Attack on RSA | 196 | * of the ciphertext, see PKCS #1 v2.2, section 7.1.2. |
202 | * Optimal Asymmetric Encryption Padding (OAEP) [...]", | 197 | * This does not leak any side-channel information. |
203 | * CRYPTO 2001), so we use a 'bad' flag | 198 | */ |
204 | */ | 199 | if (num < flen || num < 2 * mdlen + 2) { |
205 | bad = 1; | 200 | RSAerror(RSA_R_OAEP_DECODING_ERROR); |
206 | lzero = 0; | 201 | return -1; |
207 | flen = num; /* don't overflow the memcpy to padded_from */ | ||
208 | } | 202 | } |
209 | 203 | ||
210 | dblen = num - mdlen; | 204 | dblen = num - mdlen - 1; |
211 | if ((db = malloc(dblen + num)) == NULL) { | 205 | if ((db = malloc(dblen)) == NULL) { |
212 | RSAerror(ERR_R_MALLOC_FAILURE); | 206 | RSAerror(ERR_R_MALLOC_FAILURE); |
213 | return -1; | 207 | goto cleanup; |
208 | } | ||
209 | if ((em = malloc(num)) == NULL) { | ||
210 | RSAerror(ERR_R_MALLOC_FAILURE); | ||
211 | goto cleanup; | ||
212 | } | ||
213 | |||
214 | /* | ||
215 | * Caller is encouraged to pass zero-padded message created with | ||
216 | * BN_bn2binpad. Trouble is that since we can't read out of |from|'s | ||
217 | * bounds, it's impossible to have an invariant memory access pattern | ||
218 | * in case |from| was not zero-padded in advance. | ||
219 | */ | ||
220 | for (from += flen, em += num, i = 0; i < num; i++) { | ||
221 | mask = ~constant_time_is_zero(flen); | ||
222 | flen -= 1 & mask; | ||
223 | from -= 1 & mask; | ||
224 | *--em = *from & mask; | ||
214 | } | 225 | } |
226 | from = em; | ||
215 | 227 | ||
216 | /* | 228 | /* |
217 | * Always do this zero-padding copy (even when lzero == 0) | 229 | * The first byte must be zero, however we must not leak if this is |
218 | * to avoid leaking timing info about the value of lzero. | 230 | * true. See James H. Manger, "A Chosen Ciphertext Attack on RSA |
231 | * Optimal Asymmetric Encryption Padding (OAEP) [...]", CRYPTO 2001). | ||
219 | */ | 232 | */ |
220 | padded_from = db + dblen; | 233 | good = constant_time_is_zero(from[0]); |
221 | memset(padded_from, 0, lzero); | ||
222 | memcpy(padded_from + lzero, from, flen); | ||
223 | 234 | ||
224 | maskeddb = padded_from + mdlen; | 235 | maskedseed = from + 1; |
236 | maskeddb = from + 1 + mdlen; | ||
225 | 237 | ||
226 | if (PKCS1_MGF1(seed, mdlen, maskeddb, dblen, mgf1md)) | 238 | if (PKCS1_MGF1(seed, mdlen, maskeddb, dblen, mgf1md)) |
227 | goto err; | 239 | goto cleanup; |
228 | for (i = 0; i < mdlen; i++) | 240 | for (i = 0; i < mdlen; i++) |
229 | seed[i] ^= padded_from[i]; | 241 | seed[i] ^= maskedseed[i]; |
242 | |||
230 | if (PKCS1_MGF1(db, dblen, seed, mdlen, mgf1md)) | 243 | if (PKCS1_MGF1(db, dblen, seed, mdlen, mgf1md)) |
231 | goto err; | 244 | goto cleanup; |
232 | for (i = 0; i < dblen; i++) | 245 | for (i = 0; i < dblen; i++) |
233 | db[i] ^= maskeddb[i]; | 246 | db[i] ^= maskeddb[i]; |
234 | 247 | ||
235 | if (!EVP_Digest((void *)param, plen, phash, NULL, md, NULL)) | 248 | if (!EVP_Digest((void *)param, plen, phash, NULL, md, NULL)) |
236 | goto err; | 249 | goto cleanup; |
237 | 250 | ||
238 | if (timingsafe_memcmp(db, phash, mdlen) != 0 || bad) | 251 | good &= constant_time_is_zero(timingsafe_memcmp(db, phash, mdlen)); |
239 | goto decoding_err; | 252 | |
240 | else { | 253 | found_one_byte = 0; |
241 | for (i = mdlen; i < dblen; i++) | 254 | for (i = mdlen; i < dblen; i++) { |
242 | if (db[i] != 0x00) | 255 | /* |
243 | break; | 256 | * Padding consists of a number of 0-bytes, followed by a 1. |
244 | if (i == dblen || db[i] != 0x01) | 257 | */ |
245 | goto decoding_err; | 258 | unsigned int equals1 = constant_time_eq(db[i], 1); |
246 | else { | 259 | unsigned int equals0 = constant_time_is_zero(db[i]); |
247 | /* everything looks OK */ | 260 | |
248 | 261 | one_index = constant_time_select_int(~found_one_byte & equals1, | |
249 | mlen = dblen - ++i; | 262 | i, one_index); |
250 | if (tlen < mlen) { | 263 | found_one_byte |= equals1; |
251 | RSAerror(RSA_R_DATA_TOO_LARGE); | 264 | good &= (found_one_byte | equals0); |
252 | mlen = -1; | 265 | } |
253 | } else | 266 | |
254 | memcpy(to, db + i, mlen); | 267 | good &= found_one_byte; |
255 | } | 268 | |
269 | /* | ||
270 | * At this point |good| is zero unless the plaintext was valid, | ||
271 | * so plaintext-awareness ensures timing side-channels are no longer a | ||
272 | * concern. | ||
273 | */ | ||
274 | msg_index = one_index + 1; | ||
275 | mlen = dblen - msg_index; | ||
276 | |||
277 | /* | ||
278 | * For good measure, do this check in constant time as well. | ||
279 | */ | ||
280 | good &= constant_time_ge(tlen, mlen); | ||
281 | |||
282 | /* | ||
283 | * Even though we can't fake result's length, we can pretend copying | ||
284 | * |tlen| bytes where |mlen| bytes would be real. The last |tlen| of | ||
285 | * |dblen| bytes are viewed as a circular buffer starting at |tlen|-|mlen'|, | ||
286 | * where |mlen'| is the "saturated" |mlen| value. Deducing information | ||
287 | * about failure or |mlen| would require an attacker to observe | ||
288 | * memory access patterns with byte granularity *as it occurs*. It | ||
289 | * should be noted that failure is indistinguishable from normal | ||
290 | * operation if |tlen| is fixed by protocol. | ||
291 | */ | ||
292 | tlen = constant_time_select_int(constant_time_lt(dblen, tlen), dblen, tlen); | ||
293 | msg_index = constant_time_select_int(good, msg_index, dblen - tlen); | ||
294 | mlen = dblen - msg_index; | ||
295 | for (from = db + msg_index, mask = good, i = 0; i < tlen; i++) { | ||
296 | unsigned int equals = constant_time_eq(i, mlen); | ||
297 | |||
298 | from -= dblen & equals; /* if (i == mlen) rewind */ | ||
299 | mask &= mask ^ equals; /* if (i == mlen) mask = 0 */ | ||
300 | to[i] = constant_time_select_8(mask, from[i], to[i]); | ||
256 | } | 301 | } |
257 | free(db); | ||
258 | return mlen; | ||
259 | 302 | ||
260 | decoding_err: | ||
261 | /* | 303 | /* |
262 | * To avoid chosen ciphertext attacks, the error message should not | 304 | * To avoid chosen ciphertext attacks, the error message should not |
263 | * reveal which kind of decoding error happened | 305 | * reveal which kind of decoding error happened. |
264 | */ | 306 | */ |
265 | RSAerror(RSA_R_OAEP_DECODING_ERROR); | 307 | RSAerror(RSA_R_OAEP_DECODING_ERROR); |
266 | err: | 308 | err_clear_last_constant_time(1 & good); |
267 | free(db); | 309 | |
268 | return -1; | 310 | cleanup: |
311 | explicit_bzero(seed, sizeof(seed)); | ||
312 | freezero(db, dblen); | ||
313 | freezero(em, num); | ||
314 | |||
315 | return constant_time_select_int(good, mlen, -1); | ||
269 | } | 316 | } |
270 | 317 | ||
271 | int | 318 | int |