diff options
| author | jsing <> | 2019-10-17 14:31:56 +0000 |
|---|---|---|
| committer | jsing <> | 2019-10-17 14:31:56 +0000 |
| commit | 78da4fbcb1122d6b38cf34a8f34949282b622fe8 (patch) | |
| tree | d4d1d31e3dd32330ddca0199ceba659dc7eecb99 /src | |
| parent | 4ce592fed0bd2e72e8c8fb0e2ce5995f9987495f (diff) | |
| download | openbsd-78da4fbcb1122d6b38cf34a8f34949282b622fe8.tar.gz openbsd-78da4fbcb1122d6b38cf34a8f34949282b622fe8.tar.bz2 openbsd-78da4fbcb1122d6b38cf34a8f34949282b622fe8.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@
Diffstat (limited to 'src')
| -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 |
