X-Git-Url: https://git.openssl.org/?p=openssl.git;a=blobdiff_plain;f=crypto%2Frsa%2Frsa_oaep.c;h=ed486acbe651fb23eeb1bdad45e94295574b8ac9;hp=c57507d214480eff5872d3ec5ce5ceb5cbb2c25a;hb=0f2deef59d13e852a4bde0e853e9b49bab51a108;hpb=7c770d572a719fa40fa9c82807a0bd3840baf4a0 diff --git a/crypto/rsa/rsa_oaep.c b/crypto/rsa/rsa_oaep.c index c57507d214..ed486acbe6 100644 --- a/crypto/rsa/rsa_oaep.c +++ b/crypto/rsa/rsa_oaep.c @@ -1,237 +1,376 @@ -/* crypto/rsa/rsa_oaep.c */ -/* Written by Ulf Moeller. This software is distributed on an "AS IS" - basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. */ +/* + * Copyright 1999-2019 The OpenSSL Project Authors. All Rights Reserved. + * + * 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 + */ /* EME-OAEP as defined in RFC 2437 (PKCS #1 v2.0) */ -/* See Victor Shoup, "OAEP reconsidered," Nov. 2000, - * - * for problems with the security proof for the - * original OAEP scheme, which EME-OAEP is based on. - * - * A new proof can be found in E. Fujisaki, T. Okamoto, - * D. Pointcheval, J. Stern, "RSA-OEAP is Still Alive!", - * Dec. 2000, . - * The new proof has stronger requirements for the - * underlying permutation: "partial-one-wayness" instead - * of one-wayness. For the RSA function, this is - * an equivalent notion. +/* + * See Victor Shoup, "OAEP reconsidered," Nov. 2000, for problems with the security + * proof for the original OAEP scheme, which EME-OAEP is based on. A new + * proof can be found in E. Fujisaki, T. Okamoto, D. Pointcheval, J. Stern, + * "RSA-OEAP is Still Alive!", Dec. 2000, . The new proof has stronger requirements + * for the underlying permutation: "partial-one-wayness" instead of + * one-wayness. For the RSA function, this is an equivalent notion. */ -#define OPENSSL_FIPSAPI +/* + * RSA low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" +#include "internal/constant_time.h" -#if !defined(OPENSSL_NO_SHA) && !defined(OPENSSL_NO_SHA1) #include -#include "cryptlib.h" +#include "internal/cryptlib.h" #include -#include #include #include #include - -static int MGF1(unsigned char *mask, long len, - const unsigned char *seed, long seedlen); +#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) - { - int i, emlen = tlen - 1; - unsigned char *db, *seed; - unsigned char *dbmask, seedmask[SHA_DIGEST_LENGTH]; - - if (flen > emlen - 2 * SHA_DIGEST_LENGTH - 1) - { - RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_OAEP, - RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE); - return 0; - } - - if (emlen < 2 * SHA_DIGEST_LENGTH + 1) - { - RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_OAEP, RSA_R_KEY_SIZE_TOO_SMALL); - return 0; - } - - to[0] = 0; - seed = to + 1; - db = to + SHA_DIGEST_LENGTH + 1; - - if (!EVP_Digest((void *)param, plen, db, NULL, EVP_sha1(), NULL)) - return 0; - memset(db + SHA_DIGEST_LENGTH, 0, - emlen - flen - 2 * SHA_DIGEST_LENGTH - 1); - db[emlen - flen - SHA_DIGEST_LENGTH - 1] = 0x01; - memcpy(db + emlen - flen - SHA_DIGEST_LENGTH, from, (unsigned int) flen); - if (RAND_bytes(seed, SHA_DIGEST_LENGTH) <= 0) - return 0; -#ifdef PKCS_TESTVECT - memcpy(seed, - "\xaa\xfd\x12\xf6\x59\xca\xe6\x34\x89\xb4\x79\xe5\x07\x6d\xde\xc2\xf0\x6c\xb5\x8f", - 20); + const unsigned char *from, int flen, + const unsigned char *param, int plen) +{ + return rsa_padding_add_PKCS1_OAEP_mgf1_with_libctx(NULL, to, tlen, from, + flen, param, plen, NULL, + NULL); +} + +/* + * 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 = 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(0, RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE); + return 0; + } + + if (emlen < 2 * mdlen + 1) { + 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; - dbmask = OPENSSL_malloc(emlen - SHA_DIGEST_LENGTH); - if (dbmask == NULL) - { - RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_OAEP, ERR_R_MALLOC_FAILURE); - return 0; - } + /* step 3a: hash the additional input */ + if (!EVP_Digest((void *)param, plen, db, NULL, md, NULL)) + 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); + /* step 3d: generate random byte string */ + if (RAND_bytes_ex(libctx, seed, mdlen) <= 0) + goto err; - if (MGF1(dbmask, emlen - SHA_DIGEST_LENGTH, seed, SHA_DIGEST_LENGTH) < 0) - return 0; - for (i = 0; i < emlen - SHA_DIGEST_LENGTH; i++) - db[i] ^= dbmask[i]; + dbmask_len = emlen - mdlen; + dbmask = OPENSSL_malloc(dbmask_len); + if (dbmask == NULL) { + RSAerr(0, ERR_R_MALLOC_FAILURE); + goto err; + } - if (MGF1(seedmask, SHA_DIGEST_LENGTH, db, emlen - SHA_DIGEST_LENGTH) < 0) - return 0; - for (i = 0; i < SHA_DIGEST_LENGTH; i++) - seed[i] ^= seedmask[i]; + /* step 3e: dbMask = MGF(mgfSeed, nLen - HLen - 1) */ + if (PKCS1_MGF1(dbmask, dbmask_len, seed, mdlen, mgf1md) < 0) + goto err; + /* step 3f: maskedDB = DB XOR dbMask */ + for (i = 0; i < dbmask_len; i++) + db[i] ^= dbmask[i]; - OPENSSL_free(dbmask); - return 1; - } + /* 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]; + rv = 1; + + err: + 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, - const unsigned char *from, int flen, int num, - const unsigned char *param, int plen) - { - int i, dblen, mlen = -1; - const unsigned char *maskeddb; - int lzero; - unsigned char *db = NULL, seed[SHA_DIGEST_LENGTH], phash[SHA_DIGEST_LENGTH]; - unsigned char *padded_from; - int bad = 0; - - if (--num < 2 * SHA_DIGEST_LENGTH + 1) - /* 'num' is the length of the modulus, i.e. does not depend on the - * particular ciphertext. */ - goto decoding_err; - - lzero = num - flen; - if (lzero < 0) - { - /* signalling this error immediately after detection might allow - * for side-channel attacks (e.g. timing if 'plen' is huge - * -- cf. James H. Manger, "A Chosen Ciphertext Attack on RSA Optimal - * Asymmetric Encryption Padding (OAEP) [...]", CRYPTO 2001), - * so we use a 'bad' flag */ - bad = 1; - lzero = 0; - flen = num; /* don't overflow the memcpy to padded_from */ - } - - dblen = num - SHA_DIGEST_LENGTH; - db = OPENSSL_malloc(dblen + num); - if (db == NULL) - { - RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP, ERR_R_MALLOC_FAILURE); - return -1; - } - - /* Always do this zero-padding copy (even when lzero == 0) - * to avoid leaking timing info about the value of lzero. */ - padded_from = db + dblen; - memset(padded_from, 0, lzero); - memcpy(padded_from + lzero, from, flen); - - maskeddb = padded_from + SHA_DIGEST_LENGTH; - - if (MGF1(seed, SHA_DIGEST_LENGTH, maskeddb, dblen)) - return -1; - for (i = 0; i < SHA_DIGEST_LENGTH; i++) - seed[i] ^= padded_from[i]; - - if (MGF1(db, dblen, seed, SHA_DIGEST_LENGTH)) - return -1; - for (i = 0; i < dblen; i++) - db[i] ^= maskeddb[i]; - - if (!EVP_Digest((void *)param, plen, phash, NULL, EVP_sha1(), NULL)) - return -1; - - if (CRYPTO_memcmp(db, phash, SHA_DIGEST_LENGTH) != 0 || bad) - goto decoding_err; - else - { - for (i = SHA_DIGEST_LENGTH; i < dblen; i++) - if (db[i] != 0x00) - break; - if (i == dblen || db[i] != 0x01) - goto decoding_err; - else - { - /* everything looks OK */ - - mlen = dblen - ++i; - if (tlen < mlen) - { - RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP, RSA_R_DATA_TOO_LARGE); - mlen = -1; - } - else - memcpy(to, db + i, mlen); - } - } - OPENSSL_free(db); - return mlen; - -decoding_err: - /* to avoid chosen ciphertext attacks, the error message should not reveal - * which kind of decoding error happened */ - RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP, RSA_R_OAEP_DECODING_ERROR); - if (db != NULL) OPENSSL_free(db); - return -1; - } + const unsigned char *from, int flen, int num, + const unsigned char *param, int plen) +{ + return RSA_padding_check_PKCS1_OAEP_mgf1(to, tlen, from, flen, num, + param, plen, NULL, NULL); +} -int PKCS1_MGF1(unsigned char *mask, long len, - const unsigned char *seed, long seedlen, const EVP_MD *dgst) - { - long i, outlen = 0; - unsigned char cnt[4]; - EVP_MD_CTX c; - unsigned char md[EVP_MAX_MD_SIZE]; - int mdlen; - int rv = -1; - - EVP_MD_CTX_init(&c); - mdlen = M_EVP_MD_size(dgst); - if (mdlen < 0) - goto err; - for (i = 0; outlen < len; i++) - { - 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); - if (!EVP_DigestInit_ex(&c,dgst, NULL) - || !EVP_DigestUpdate(&c, seed, seedlen) - || !EVP_DigestUpdate(&c, cnt, 4)) - goto err; - if (outlen + mdlen <= len) - { - if (!EVP_DigestFinal_ex(&c, mask + outlen, NULL)) - goto err; - outlen += mdlen; - } - else - { - if (!EVP_DigestFinal_ex(&c, md, NULL)) - goto err; - memcpy(mask + outlen, md, len - outlen); - outlen = len; - } - } - rv = 0; - err: - EVP_MD_CTX_cleanup(&c); - return rv; - } - -static int MGF1(unsigned char *mask, long len, const unsigned char *seed, - long seedlen) - { - return PKCS1_MGF1(mask, len, seed, seedlen, EVP_sha1()); - } +int RSA_padding_check_PKCS1_OAEP_mgf1(unsigned char *to, int tlen, + const unsigned char *from, int flen, + int num, const unsigned char *param, + int plen, const EVP_MD *md, + const EVP_MD *mgf1md) +{ + int i, dblen = 0, mlen = -1, one_index = 0, msg_index; + 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 = + * Y || maskedSeed || maskedDB + */ + unsigned char *db = NULL, *em = NULL, seed[EVP_MAX_MD_SIZE], + phash[EVP_MAX_MD_SIZE]; + int mdlen; + + 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; + + mdlen = EVP_MD_size(md); + + if (tlen <= 0 || flen <= 0) + return -1; + /* + * |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 + * 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) { + RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP_MGF1, + RSA_R_OAEP_DECODING_ERROR); + return -1; + } + + dblen = num - mdlen - 1; + db = OPENSSL_malloc(dblen); + 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; + } + + /* + * 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. + */ + 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 + * true. See James H. Manger, "A Chosen Ciphertext Attack on RSA + * Optimal Asymmetric Encryption Padding (OAEP) [...]", CRYPTO 2001). + */ + good = constant_time_is_zero(em[0]); + + maskedseed = em + 1; + maskeddb = em + 1 + mdlen; + + if (PKCS1_MGF1(seed, mdlen, maskeddb, dblen, mgf1md)) + goto cleanup; + for (i = 0; i < mdlen; i++) + seed[i] ^= maskedseed[i]; + + if (PKCS1_MGF1(db, dblen, seed, mdlen, mgf1md)) + goto cleanup; + for (i = 0; i < dblen; i++) + db[i] ^= maskeddb[i]; + + if (!EVP_Digest((void *)param, plen, phash, NULL, md, NULL)) + goto cleanup; + + good &= constant_time_is_zero(CRYPTO_memcmp(db, phash, mdlen)); + + found_one_byte = 0; + for (i = mdlen; i < dblen; i++) { + /* + * Padding consists of a number of 0-bytes, followed by a 1. + */ + unsigned int equals1 = constant_time_eq(db[i], 1); + unsigned int equals0 = constant_time_is_zero(db[i]); + one_index = constant_time_select_int(~found_one_byte & equals1, + i, one_index); + found_one_byte |= equals1; + good &= (found_one_byte | equals0); + } + + good &= found_one_byte; + + /* + * At this point |good| is zero unless the plaintext was valid, + * so plaintext-awareness ensures timing side-channels are no longer a + * concern. + */ + msg_index = one_index + 1; + mlen = dblen - msg_index; + + /* + * 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]); + } + +#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 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) +{ + long i, outlen = 0; + unsigned char cnt[4]; + EVP_MD_CTX *c = EVP_MD_CTX_new(); + unsigned char md[EVP_MAX_MD_SIZE]; + int mdlen; + int rv = -1; + + if (c == NULL) + goto err; + 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)) + goto err; + if (outlen + mdlen <= len) { + if (!EVP_DigestFinal_ex(c, mask + outlen, NULL)) + goto err; + outlen += mdlen; + } else { + if (!EVP_DigestFinal_ex(c, md, NULL)) + goto err; + memcpy(mask + outlen, md, len - outlen); + outlen = len; + } + } + rv = 0; + err: + OPENSSL_cleanse(md, sizeof(md)); + EVP_MD_CTX_free(c); + return rv; +}