Commit dbf0a496 authored by Dr. Matthias St. Pierre's avatar Dr. Matthias St. Pierre
Browse files

DRBG: fix reseeding via RAND_add()/RAND_seed() with large input



In pull request #4328 the seeding of the DRBG via RAND_add()/RAND_seed()
was implemented by buffering the data in a random pool where it is
picked up later by the rand_drbg_get_entropy() callback. This buffer
was limited to the size of 4096 bytes.

When a larger input was added via RAND_add() or RAND_seed() to the DRBG,
the reseeding failed, but the error returned by the DRBG was ignored
by the two calling functions, which both don't return an error code.
As a consequence, the data provided by the application was effectively
ignored.

This commit fixes the problem by a more efficient implementation which
does not copy the data in memory and by raising the buffer the size limit
to INT32_MAX (2 gigabytes). This is less than the NIST limit of 2^35 bits
but it was chosen intentionally to avoid platform dependent problems
like integer sizes and/or signed/unsigned conversion.

Additionally, the DRBG is now less permissive on errors: In addition to
pushing a message to the openssl error stack, it enters the error state,
which forces a reinstantiation on next call.

Thanks go to Dr. Falko Strenzke for reporting this issue to the
openssl-security mailing list. After internal discussion the issue
has been categorized as not being security relevant, because the DRBG
reseeds automatically and is fully functional even without additional
randomness provided by the application.

Fixes #7381

Reviewed-by: default avatarPaul Dale <paul.dale@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/7382)

(cherry picked from commit 3064b55134434a0b2850f07eff57120f35bb269a)
parent 26d97bf6
Loading
Loading
Loading
Loading
+11 −1
Original line number Diff line number Diff line
@@ -11,6 +11,17 @@
  *)
 Changes between 1.1.1 and 1.1.1a [xx XXX xxxx]
  *) Fixed the issue that RAND_add()/RAND_seed() silently discards random input
     if its length exceeds 4096 bytes. The limit has been raised to a buffer size
     of two gigabytes and the error handling improved.
     This issue was reported to OpenSSL by Dr. Falko Strenzke. It has been
     categorized as a normal bug, not a security issue, because the DRBG reseeds
     automatically and is fully functional even without additional randomness
     provided by the application.
 Changes between 1.1.0i and 1.1.1 [11 Sep 2018]
  *) Add a new ClientHello callback. Provides a callback interface that gives
@@ -13107,4 +13118,3 @@ des-cbc 3624.96k 5258.21k 5530.91k 5624.30k 5628.26k
  *) A minor bug in ssl/s3_clnt.c where there would always be 4 0
     bytes sent in the client random.
     [Edward Bishop <ebishop@spyglass.com>]
+1 −0
Original line number Diff line number Diff line
@@ -1014,6 +1014,7 @@ RAND_F_RAND_POOL_ACQUIRE_ENTROPY:122:rand_pool_acquire_entropy
RAND_F_RAND_POOL_ADD:103:rand_pool_add
RAND_F_RAND_POOL_ADD_BEGIN:113:rand_pool_add_begin
RAND_F_RAND_POOL_ADD_END:114:rand_pool_add_end
RAND_F_RAND_POOL_ATTACH:124:rand_pool_attach
RAND_F_RAND_POOL_BYTES_NEEDED:115:rand_pool_bytes_needed
RAND_F_RAND_POOL_NEW:116:rand_pool_new
RAND_F_RAND_WRITE_FILE:112:RAND_write_file
+2 −0
Original line number Diff line number Diff line
@@ -53,6 +53,8 @@ void rand_drbg_cleanup_additional_data(unsigned char *out, size_t outlen);
 * RAND_POOL functions
 */
RAND_POOL *rand_pool_new(int entropy_requested, size_t min_len, size_t max_len);
RAND_POOL *rand_pool_attach(const unsigned char *buffer, size_t len,
                            size_t entropy);
void rand_pool_free(RAND_POOL *pool);

const unsigned char *rand_pool_buffer(RAND_POOL *pool);
+2 −2
Original line number Diff line number Diff line
@@ -417,9 +417,9 @@ int drbg_ctr_init(RAND_DRBG *drbg)
            return 0;

        drbg->min_entropylen = ctr->keylen;
        drbg->max_entropylen = DRBG_MINMAX_FACTOR * drbg->min_entropylen;
        drbg->max_entropylen = DRBG_MAX_LENGTH;
        drbg->min_noncelen = drbg->min_entropylen / 2;
        drbg->max_noncelen = DRBG_MINMAX_FACTOR * drbg->min_noncelen;
        drbg->max_noncelen = DRBG_MAX_LENGTH;
        drbg->max_perslen = DRBG_MAX_LENGTH;
        drbg->max_adinlen = DRBG_MAX_LENGTH;
    } else {
+11 −6
Original line number Diff line number Diff line
@@ -481,8 +481,10 @@ int rand_drbg_restart(RAND_DRBG *drbg,

    if (drbg->pool != NULL) {
        RANDerr(RAND_F_RAND_DRBG_RESTART, ERR_R_INTERNAL_ERROR);
        drbg->state = DRBG_ERROR;
        rand_pool_free(drbg->pool);
        drbg->pool = NULL;
        return 0;
    }

    if (buffer != NULL) {
@@ -490,24 +492,25 @@ int rand_drbg_restart(RAND_DRBG *drbg,
            if (drbg->max_entropylen < len) {
                RANDerr(RAND_F_RAND_DRBG_RESTART,
                    RAND_R_ENTROPY_INPUT_TOO_LONG);
                drbg->state = DRBG_ERROR;
                return 0;
            }

            if (entropy > 8 * len) {
                RANDerr(RAND_F_RAND_DRBG_RESTART, RAND_R_ENTROPY_OUT_OF_RANGE);
                drbg->state = DRBG_ERROR;
                return 0;
            }

            /* will be picked up by the rand_drbg_get_entropy() callback */
            drbg->pool = rand_pool_new(entropy, len, len);
            drbg->pool = rand_pool_attach(buffer, len, entropy);
            if (drbg->pool == NULL)
                return 0;

            rand_pool_add(drbg->pool, buffer, len, entropy);
        } else {
            if (drbg->max_adinlen < len) {
                RANDerr(RAND_F_RAND_DRBG_RESTART,
                        RAND_R_ADDITIONAL_INPUT_TOO_LONG);
                drbg->state = DRBG_ERROR;
                return 0;
            }
            adin = buffer;
@@ -964,14 +967,16 @@ static int drbg_add(const void *buf, int num, double randomness)
    if (num < 0 || randomness < 0.0)
        return 0;

    if (randomness > (double)drbg->max_entropylen) {
    if (randomness > (double)RAND_DRBG_STRENGTH) {
        /*
         * The purpose of this check is to bound |randomness| by a
         * relatively small value in order to prevent an integer
         * overflow when multiplying by 8 in the rand_drbg_restart()
         * call below.
         * call below. Note that randomness is measured in bytes,
         * not bits, so this value corresponds to eight times the
         * security strength.
         */
        return 0;
        randomness = (double)RAND_DRBG_STRENGTH;
    }

    rand_drbg_lock(drbg);
Loading