X-Git-Url: https://git.openssl.org/gitweb/?p=openssl.git;a=blobdiff_plain;f=crypto%2Frsa%2Frsa_oaep.c;h=d0e1ffa82e84adfcb6c8aaa9c04b1f2e737fd09a;hp=d4de71dfde3090f9c387106959377600c630653e;hb=33388b44b67145af2181b1e9528c381c8ea0d1b6;hpb=e670db0183079b5f6325ce2abd9d785e0f966890 diff --git a/crypto/rsa/rsa_oaep.c b/crypto/rsa/rsa_oaep.c index d4de71dfde..d0e1ffa82e 100644 --- a/crypto/rsa/rsa_oaep.c +++ b/crypto/rsa/rsa_oaep.c @@ -1,7 +1,7 @@ /* - * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1999-2020 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html @@ -20,7 +20,13 @@ * one-wayness. For the RSA function, this is an equivalent notion. */ -#include "internal/constant_time_locl.h" +/* + * RSA low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include "internal/constant_time.h" #include #include "internal/cryptlib.h" @@ -28,79 +34,115 @@ #include #include #include -#include "rsa_locl.h" +#include "rsa_local.h" int RSA_padding_add_PKCS1_OAEP(unsigned char *to, int tlen, const unsigned char *from, int flen, const unsigned char *param, int plen) { - return RSA_padding_add_PKCS1_OAEP_mgf1(to, tlen, from, flen, - param, plen, NULL, NULL); + return rsa_padding_add_PKCS1_OAEP_mgf1_with_libctx(NULL, to, tlen, from, + flen, param, plen, NULL, + NULL); } -int RSA_padding_add_PKCS1_OAEP_mgf1(unsigned char *to, int tlen, - const unsigned char *from, int flen, - const unsigned char *param, int plen, - const EVP_MD *md, const EVP_MD *mgf1md) +/* + * Perform ihe padding as per NIST 800-56B 7.2.2.3 + * from (K) is the key material. + * param (A) is the additional input. + * Step numbers are included here but not in the constant time inverse below + * to avoid complicating an already difficult enough function. + */ +int rsa_padding_add_PKCS1_OAEP_mgf1_with_libctx(OPENSSL_CTX *libctx, + unsigned char *to, int tlen, + const unsigned char *from, + int flen, + const unsigned char *param, + int plen, const EVP_MD *md, + const EVP_MD *mgf1md) { + int rv = 0; int i, emlen = tlen - 1; unsigned char *db, *seed; - unsigned char *dbmask, seedmask[EVP_MAX_MD_SIZE]; - int mdlen; + unsigned char *dbmask = NULL; + unsigned char seedmask[EVP_MAX_MD_SIZE]; + int mdlen, dbmask_len = 0; +#ifndef FIPS_MODE if (md == NULL) md = EVP_sha1(); +#else + RSAerr(0, ERR_R_PASSED_NULL_PARAMETER); + return 0; +#endif if (mgf1md == NULL) mgf1md = md; mdlen = EVP_MD_size(md); + /* step 2b: check KLen > nLen - 2 HLen - 2 */ if (flen > emlen - 2 * mdlen - 1) { - RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_OAEP_MGF1, - RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE); + RSAerr(0, RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE); return 0; } if (emlen < 2 * mdlen + 1) { - RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_OAEP_MGF1, - RSA_R_KEY_SIZE_TOO_SMALL); + RSAerr(0, RSA_R_KEY_SIZE_TOO_SMALL); return 0; } + /* step 3i: EM = 00000000 || maskedMGF || maskedDB */ to[0] = 0; seed = to + 1; db = to + mdlen + 1; + /* step 3a: hash the additional input */ if (!EVP_Digest((void *)param, plen, db, NULL, md, NULL)) - return 0; + goto err; + /* step 3b: zero bytes array of length nLen - KLen - 2 HLen -2 */ memset(db + mdlen, 0, emlen - flen - 2 * mdlen - 1); + /* step 3c: DB = HA || PS || 00000001 || K */ db[emlen - flen - mdlen - 1] = 0x01; memcpy(db + emlen - flen - mdlen, from, (unsigned int)flen); - if (RAND_bytes(seed, mdlen) <= 0) - return 0; + /* step 3d: generate random byte string */ + if (RAND_bytes_ex(libctx, seed, mdlen) <= 0) + goto err; - dbmask = OPENSSL_malloc(emlen - mdlen); + dbmask_len = emlen - mdlen; + dbmask = OPENSSL_malloc(dbmask_len); if (dbmask == NULL) { - RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_OAEP_MGF1, ERR_R_MALLOC_FAILURE); - return 0; + RSAerr(0, ERR_R_MALLOC_FAILURE); + goto err; } - if (PKCS1_MGF1(dbmask, emlen - mdlen, seed, mdlen, mgf1md) < 0) + /* step 3e: dbMask = MGF(mgfSeed, nLen - HLen - 1) */ + if (PKCS1_MGF1(dbmask, dbmask_len, seed, mdlen, mgf1md) < 0) goto err; - for (i = 0; i < emlen - mdlen; i++) + /* step 3f: maskedDB = DB XOR dbMask */ + for (i = 0; i < dbmask_len; i++) db[i] ^= dbmask[i]; - if (PKCS1_MGF1(seedmask, mdlen, db, emlen - mdlen, mgf1md) < 0) + /* step 3g: mgfSeed = MGF(maskedDB, HLen) */ + if (PKCS1_MGF1(seedmask, mdlen, db, dbmask_len, mgf1md) < 0) goto err; + /* stepo 3h: maskedMGFSeed = mgfSeed XOR mgfSeedMask */ for (i = 0; i < mdlen; i++) seed[i] ^= seedmask[i]; - - OPENSSL_free(dbmask); - return 1; + rv = 1; err: - OPENSSL_free(dbmask); - return 0; + OPENSSL_cleanse(seedmask, sizeof(seedmask)); + OPENSSL_clear_free(dbmask, dbmask_len); + return rv; +} + +int RSA_padding_add_PKCS1_OAEP_mgf1(unsigned char *to, int tlen, + const unsigned char *from, int flen, + const unsigned char *param, int plen, + const EVP_MD *md, const EVP_MD *mgf1md) +{ + return rsa_padding_add_PKCS1_OAEP_mgf1_with_libctx(NULL, to, tlen, from, + flen, param, plen, md, + mgf1md); } int RSA_padding_check_PKCS1_OAEP(unsigned char *to, int tlen, @@ -118,7 +160,7 @@ int RSA_padding_check_PKCS1_OAEP_mgf1(unsigned char *to, int tlen, const EVP_MD *mgf1md) { int i, dblen = 0, mlen = -1, one_index = 0, msg_index; - unsigned int good, found_one_byte; + unsigned int good = 0, found_one_byte, mask; const unsigned char *maskedseed, *maskeddb; /* * |em| is the encoded message, zero-padded to exactly |num| bytes: em = @@ -128,8 +170,15 @@ int RSA_padding_check_PKCS1_OAEP_mgf1(unsigned char *to, int tlen, phash[EVP_MAX_MD_SIZE]; int mdlen; - if (md == NULL) + if (md == NULL) { +#ifndef FIPS_MODE md = EVP_sha1(); +#else + RSAerr(0, ERR_R_PASSED_NULL_PARAMETER); + return -1; +#endif + } + if (mgf1md == NULL) mgf1md = md; @@ -141,31 +190,42 @@ int RSA_padding_check_PKCS1_OAEP_mgf1(unsigned char *to, int tlen, * |num| is the length of the modulus; |flen| is the length of the * encoded message. Therefore, for any |from| that was obtained by * decrypting a ciphertext, we must have |flen| <= |num|. Similarly, - * num < 2 * mdlen + 2 must hold for the modulus irrespective of + * |num| >= 2 * |mdlen| + 2 must hold for the modulus irrespective of * the ciphertext, see PKCS #1 v2.2, section 7.1.2. * This does not leak any side-channel information. */ - if (num < flen || num < 2 * mdlen + 2) - goto decoding_err; + if (num < flen || num < 2 * mdlen + 2) { + RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP_MGF1, + RSA_R_OAEP_DECODING_ERROR); + return -1; + } dblen = num - mdlen - 1; db = OPENSSL_malloc(dblen); - em = OPENSSL_malloc(num); - if (db == NULL || em == NULL) { + if (db == NULL) { RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP_MGF1, ERR_R_MALLOC_FAILURE); goto cleanup; } + em = OPENSSL_malloc(num); + if (em == NULL) { + RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP_MGF1, + ERR_R_MALLOC_FAILURE); + goto cleanup; + } + /* - * Always do this zero-padding copy (even when num == flen) to avoid - * leaking that information. The copy still leaks some side-channel - * information, but it's impossible to have a fixed memory access - * pattern since we can't read out of the bounds of |from|. - * - * TODO(emilia): Consider porting BN_bn2bin_padded from BoringSSL. + * Caller is encouraged to pass zero-padded message created with + * BN_bn2binpad. Trouble is that since we can't read out of |from|'s + * bounds, it's impossible to have an invariant memory access pattern + * in case |from| was not zero-padded in advance. */ - memset(em, 0, num); - memcpy(em + num - flen, from, flen); + for (from += flen, em += num, i = 0; i < num; i++) { + mask = ~constant_time_is_zero(flen); + flen -= 1 & mask; + from -= 1 & mask; + *--em = *from & mask; + } /* * The first byte must be zero, however we must not leak if this is @@ -212,33 +272,64 @@ int RSA_padding_check_PKCS1_OAEP_mgf1(unsigned char *to, int tlen, * so plaintext-awareness ensures timing side-channels are no longer a * concern. */ - if (!good) - goto decoding_err; - msg_index = one_index + 1; mlen = dblen - msg_index; - if (tlen < mlen) { - RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP_MGF1, RSA_R_DATA_TOO_LARGE); - mlen = -1; - } else { - memcpy(to, db + msg_index, mlen); - goto cleanup; + /* + * For good measure, do this check in constant time as well. + */ + good &= constant_time_ge(tlen, mlen); + + /* + * Move the result in-place by |dblen|-|mdlen|-1-|mlen| bytes to the left. + * Then if |good| move |mlen| bytes from |db|+|mdlen|+1 to |to|. + * Otherwise leave |to| unchanged. + * Copy the memory back in a way that does not reveal the size of + * the data being copied via a timing side channel. This requires copying + * parts of the buffer multiple times based on the bits set in the real + * length. Clear bits do a non-copy with identical access pattern. + * The loop below has overall complexity of O(N*log(N)). + */ + tlen = constant_time_select_int(constant_time_lt(dblen - mdlen - 1, tlen), + dblen - mdlen - 1, tlen); + for (msg_index = 1; msg_index < dblen - mdlen - 1; msg_index <<= 1) { + mask = ~constant_time_eq(msg_index & (dblen - mdlen - 1 - mlen), 0); + for (i = mdlen + 1; i < dblen - msg_index; i++) + db[i] = constant_time_select_8(mask, db[i + msg_index], db[i]); + } + for (i = 0; i < tlen; i++) { + mask = good & constant_time_lt(i, mlen); + to[i] = constant_time_select_8(mask, db[i + mdlen + 1], to[i]); } - decoding_err: +#ifndef FIPS_MODE /* * To avoid chosen ciphertext attacks, the error message should not * reveal which kind of decoding error happened. + * + * This trick doesn't work in the FIPS provider because libcrypto manages + * the error stack. Instead we opt not to put an error on the stack at all + * in case of padding failure in the FIPS provider. */ RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP_MGF1, RSA_R_OAEP_DECODING_ERROR); + err_clear_last_constant_time(1 & good); +#endif cleanup: + OPENSSL_cleanse(seed, sizeof(seed)); OPENSSL_clear_free(db, dblen); OPENSSL_clear_free(em, num); - return mlen; + + return constant_time_select_int(good, mlen, -1); } +/* + * Mask Generation Function corresponding to section 7.2.2.2 of NIST SP 800-56B. + * The variables are named differently to NIST: + * mask (T) and len (maskLen)are the returned mask. + * seed (mgfSeed). + * The range checking steps inm the process are performed outside. + */ int PKCS1_MGF1(unsigned char *mask, long len, const unsigned char *seed, long seedlen, const EVP_MD *dgst) { @@ -254,11 +345,14 @@ int PKCS1_MGF1(unsigned char *mask, long len, mdlen = EVP_MD_size(dgst); if (mdlen < 0) goto err; + /* step 4 */ for (i = 0; outlen < len; i++) { + /* step 4a: D = I2BS(counter, 4) */ cnt[0] = (unsigned char)((i >> 24) & 255); cnt[1] = (unsigned char)((i >> 16) & 255); cnt[2] = (unsigned char)((i >> 8)) & 255; cnt[3] = (unsigned char)(i & 255); + /* step 4b: T =T || hash(mgfSeed || D) */ if (!EVP_DigestInit_ex(c, dgst, NULL) || !EVP_DigestUpdate(c, seed, seedlen) || !EVP_DigestUpdate(c, cnt, 4)) @@ -276,6 +370,7 @@ int PKCS1_MGF1(unsigned char *mask, long len, } rv = 0; err: + OPENSSL_cleanse(md, sizeof(md)); EVP_MD_CTX_free(c); return rv; }