/* * Implements the get_entropy() callback (see RAND_DRBG_set_callbacks()) * * If the DRBG has a parent, then the required amount of entropy input * is fetched using the parent's RAND_DRBG_generate(). * * Otherwise, the entropy is polled from the system entropy sources * using RAND_POOL_acquire_entropy(). * * If a random pool has been added to the DRBG using RAND_add(), then * its entropy will be used up first. */ size_t rand_drbg_get_entropy(RAND_DRBG *drbg, unsigned char **pout, int entropy, size_t min_len, size_t max_len) { size_t ret = 0; size_t entropy_available = 0; RAND_POOL *pool = RAND_POOL_new(entropy, min_len, max_len); if (pool == NULL) return 0; if (drbg->pool) { RAND_POOL_add(pool, RAND_POOL_buffer(drbg->pool), RAND_POOL_length(drbg->pool), RAND_POOL_entropy(drbg->pool)); RAND_POOL_free(drbg->pool); drbg->pool = NULL; } if (drbg->parent) { size_t bytes_needed = RAND_POOL_bytes_needed(pool, 8); unsigned char *buffer = RAND_POOL_add_begin(pool, bytes_needed); if (buffer != NULL) { size_t bytes = 0; /* * Get random from parent, include our state as additional input. * Our lock is already held, but we need to lock our parent before * generating bits from it. */ if (drbg->parent->lock) CRYPTO_THREAD_write_lock(drbg->parent->lock); if (RAND_DRBG_generate(drbg->parent, buffer, bytes_needed, 0, (unsigned char *)drbg, sizeof(*drbg)) != 0) bytes = bytes_needed; if (drbg->parent->lock) CRYPTO_THREAD_unlock(drbg->parent->lock); entropy_available = RAND_POOL_add_end(pool, bytes, 8 * bytes); } } else { /* Get entropy by polling system entropy sources. */ entropy_available = RAND_POOL_acquire_entropy(pool); } if (entropy_available > 0) { ret = RAND_POOL_length(pool); *pout = RAND_POOL_detach(pool); } RAND_POOL_free(pool); return ret; }
/* * Generate additional data that can be used for the drbg. The data does * not need to contain entropy, but it's useful if it contains at least * some bits that are unpredictable. * * Returns 0 on failure. * * On success it allocates a buffer at |*pout| and returns the length of * the data. The buffer should get freed using OPENSSL_secure_clear_free(). */ size_t rand_drbg_get_additional_data(unsigned char **pout, size_t max_len) { RAND_POOL *pool; CRYPTO_THREAD_ID thread_id; size_t len; #ifdef OPENSSL_SYS_UNIX pid_t pid; #elif defined(OPENSSL_SYS_WIN32) DWORD pid; #endif uint64_t tbits; pool = RAND_POOL_new(0, 0, max_len); if (pool == NULL) return 0; #ifdef OPENSSL_SYS_UNIX pid = getpid(); RAND_POOL_add(pool, (unsigned char *)&pid, sizeof(pid), 0); #elif defined(OPENSSL_SYS_WIN32) pid = GetCurrentProcessId(); RAND_POOL_add(pool, (unsigned char *)&pid, sizeof(pid), 0); #endif thread_id = CRYPTO_THREAD_get_current_id(); if (thread_id != 0) RAND_POOL_add(pool, (unsigned char *)&thread_id, sizeof(thread_id), 0); tbits = get_timer_bits(); if (tbits != 0) RAND_POOL_add(pool, (unsigned char *)&tbits, sizeof(tbits), 0); /* TODO: Use RDSEED? */ len = RAND_POOL_length(pool); if (len != 0) *pout = RAND_POOL_detach(pool); RAND_POOL_free(pool); return len; }
/* * Acquire entropy from high-speed clock * * Since we get some randomness from the low-order bits of the * high-speed clock, it can help. * * Returns the total entropy count, if it exceeds the requested * entropy count. Otherwise, returns an entropy count of 0. */ size_t rand_acquire_entropy_from_tsc(RAND_POOL *pool) { unsigned char c; int i; if ((OPENSSL_ia32cap_P[0] & (1 << 4)) != 0) { for (i = 0; i < TSC_READ_COUNT; i++) { c = (unsigned char)(OPENSSL_rdtsc() & 0xFF); RAND_POOL_add(pool, &c, 1, 4); } } return RAND_POOL_entropy_available(pool); }
/* * Restart |drbg|, using the specified entropy or additional input * * Tries its best to get the drbg instantiated by all means, * regardless of its current state. * * Optionally, a |buffer| of |len| random bytes can be passed, * which is assumed to contain at least |entropy| bits of entropy. * * If |entropy| > 0, the buffer content is used as entropy input. * * If |entropy| == 0, the buffer content is used as additional input * * Returns 1 on success, 0 on failure. * * This function is used internally only. */ int rand_drbg_restart(RAND_DRBG *drbg, const unsigned char *buffer, size_t len, size_t entropy) { int reseeded = 0; const unsigned char *adin = NULL; size_t adinlen = 0; if (drbg->pool != NULL) { RANDerr(RAND_F_RAND_DRBG_RESTART, ERR_R_INTERNAL_ERROR); RAND_POOL_free(drbg->pool); drbg->pool = NULL; } if (buffer != NULL) { if (entropy > 0) { if (drbg->max_entropylen < len) { RANDerr(RAND_F_RAND_DRBG_RESTART, RAND_R_ENTROPY_INPUT_TOO_LONG); return 0; } if (entropy > 8 * len) { RANDerr(RAND_F_RAND_DRBG_RESTART, RAND_R_ENTROPY_OUT_OF_RANGE); return 0; } /* will be picked up by the rand_drbg_get_entropy() callback */ drbg->pool = RAND_POOL_new(entropy, len, len); 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); return 0; } adin = buffer; adinlen = len; } } /* repair error state */ if (drbg->state == DRBG_ERROR) RAND_DRBG_uninstantiate(drbg); /* repair uninitialized state */ if (drbg->state == DRBG_UNINITIALISED) { /* reinstantiate drbg */ RAND_DRBG_instantiate(drbg, (const unsigned char *) ossl_pers_string, sizeof(ossl_pers_string) - 1); /* already reseeded. prevent second reseeding below */ reseeded = (drbg->state == DRBG_READY); } /* refresh current state if entropy or additional input has been provided */ if (drbg->state == DRBG_READY) { if (adin != NULL) { /* * mix in additional input without reseeding * * Similar to RAND_DRBG_reseed(), but the provided additional * data |adin| is mixed into the current state without pulling * entropy from the trusted entropy source using get_entropy(). * This is not a reseeding in the strict sense of NIST SP 800-90A. */ drbg->meth->reseed(drbg, adin, adinlen, NULL, 0); } else if (reseeded == 0) { /* do a full reseeding if it has not been done yet above */ RAND_DRBG_reseed(drbg, NULL, 0); } } /* check whether a given entropy pool was cleared properly during reseed */ if (drbg->pool != NULL) { drbg->state = DRBG_ERROR; RANDerr(RAND_F_RAND_DRBG_RESTART, ERR_R_INTERNAL_ERROR); RAND_POOL_free(drbg->pool); drbg->pool = NULL; return 0; } return drbg->state == DRBG_READY; }