summaryrefslogtreecommitdiff
path: root/crypto
diff options
context:
space:
mode:
authorylavic <ylavic@13f79535-47bb-0310-9956-ffa450edef68>2018-06-27 22:17:42 +0000
committerylavic <ylavic@13f79535-47bb-0310-9956-ffa450edef68>2018-06-27 22:17:42 +0000
commit706ebb6a4486f281cfde7297edea17fbe09d51dc (patch)
tree3d0907483faff8f2ffb7e713b2bd64186f3c0b13 /crypto
parent5073a987f3c97b5e62ccae63df98b24d309cfe8e (diff)
downloadlibapr-706ebb6a4486f281cfde7297edea17fbe09d51dc.tar.gz
apr_crypto: follow up to r1833359: improve CPRNGs fork()ing.
Rework apr_crypto_prng_after_fork() which now handles rekeying of all the CPRNGs created within apr_crypto, by maintaining them in a global APR_RING, with the notable exception of per-thread ones (never forked). For each maintained CPRNG, apr_crypto_prng_after_fork() will now first rekey both the parent and child processes (determined by the 'in_child' argument provided by the caller), and for the parent only rekey a second time so that the initial states finally differ for both processes. Once these new keys are committed to their respective CPRNGs, thanks to and in continuity with the forward secrecy construct of apr_crypto_prng, there will be no in memory key material or stream that one process can inherit or infer from the other. The user can also rekey a CPRNG explicitely by calling the new function apr_crypto_prng_rekey(), and this is done by apr_fork() implicitely before forking any child, thus for the parent process. This safe guard ensures both the clearing of the pooled random bytes (buffered keystream) and the renewal of key material (cheap and preventive against _atfork() handlers or alike). Rekeying is done by using each CPRNG's keystream directly, there isn't anymore the use of a PID (or SHA256 thereof) for children processes nor any extra reads from the system RNG. All the apr_crypto_prng API is now self contained and can work entirely with a single stream cipher as primitive (Chacha20 or AES256-CTR, in that order of availability) and the initial entropy of 32 bytes gathered from the system. IOW, there is only one call issued to the system RNG for the global CPRNG's initial key, and if more CPRNGs are created their own initial key is produced by the global CPRNG. The KAT arrays in the tests suite needed adjustment too because the initial seed (if provided, like the zeros-input for the KAT) is no more used directly as the first key. Instead the first 32 bytes of the keystream generated from the seed are, and the seed (like any just used key) is then cleared immediatly from internal memory. Finally some private APR_CRYPTO_PRNG_* macros (in .c file only) are renamed to CPRNG_* to shorten colomns and avoid multilines in several cases. git-svn-id: http://svn.apache.org/repos/asf/apr/apr/trunk@1834551 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'crypto')
-rw-r--r--crypto/apr_crypto_prng.c446
1 files changed, 327 insertions, 119 deletions
diff --git a/crypto/apr_crypto_prng.c b/crypto/apr_crypto_prng.c
index 4b302fb33..69e1de976 100644
--- a/crypto/apr_crypto_prng.c
+++ b/crypto/apr_crypto_prng.c
@@ -18,6 +18,22 @@
* Cryptographic Pseudo Random Number Generator (CPRNG), based on
* "Fast-key-erasure random-number generators" from D.J. Bernstein ([1]),
* and public domain implementation in libpqcrypto's randombytes() ([2]).
+ *
+ * The CPRNG key is changed as soon as it's used to initialize the stream
+ * cipher, so it never resides in memory at the same time as the keystream
+ * it produced (a.k.a. the random bytes, which for efficiency are pooled).
+ *
+ * Likewise, the keystream is always cleared from the internal state before
+ * being returned to the user, thus there is no way to recover the produced
+ * random bytes afterward (e.g. from a memory/core dump after a crash).
+ *
+ * IOW, this CPRNG ensures forward secrecy, one may want to run it in a process
+ * and/or environment protected from live memory eavesdropping, thus keep the
+ * pooled/future random bytes secret by design, and use it as a replacement
+ * for some blocking/inefficient system RNG. The random bytes could then be
+ * serviced through a named pipe/socket, RPC, or any specific API. This is
+ * outside the scope of this/below code, though.
+ *
* [1] https://blog.cr.yp.to/20170723-random.html
* [2] https://libpqcrypto.org/
*/
@@ -29,6 +45,7 @@
#if APU_HAVE_CRYPTO
#if APU_HAVE_CRYPTO_PRNG
+#include "apr_ring.h"
#include "apr_pools.h"
#include "apr_strings.h"
#include "apr_thread_mutex.h"
@@ -36,19 +53,28 @@
#include <stdlib.h> /* for malloc() */
-#define APR_CRYPTO_PRNG_KEY_SIZE 32
-#if APR_CRYPTO_PRNG_SEED_SIZE > APR_CRYPTO_PRNG_KEY_SIZE
-#error apr_crypto_prng uses 256bit stream ciphers only
+#define CPRNG_KEY_SIZE 32
+
+/* Be consistent with the .h (the seed is xor-ed with key on reseed). */
+#if CPRNG_KEY_SIZE != APR_CRYPTO_PRNG_SEED_SIZE
+#error apr_crypto_prng handles stream ciphers with 256bit keys only
#endif
-#define APR_CRYPTO_PRNG_BUF_SIZE_MIN (APR_CRYPTO_PRNG_KEY_SIZE * (8 - 1))
-#define APR_CRYPTO_PRNG_BUF_SIZE_DEF (APR_CRYPTO_PRNG_KEY_SIZE * (24 - 1))
+#define CPRNG_BUF_SIZE_MIN (CPRNG_KEY_SIZE * (8 - 1))
+#define CPRNG_BUF_SIZE_DEF (CPRNG_KEY_SIZE * (24 - 1))
#if APU_HAVE_OPENSSL
#include <openssl/evp.h>
#include <openssl/sha.h>
+/* We only handle Chacha20 and AES256-CTR stream ciphers, for now.
+ * AES256-CTR should be in any openssl version of this century but is used
+ * only if Chacha20 is missing (openssl < 1.1). This is because Chacha20 is
+ * fast (enough) in software and timing attacks safe, though AES256-CTR can
+ * be faster and constant-time but only when the CPU (aesni) or some crypto
+ * hardware are in the place.
+ */
#include <openssl/obj_mac.h> /* for NID_* */
#if !defined(NID_chacha20) && !defined(NID_aes_256_ctr)
/* XXX: APU_HAVE_CRYPTO_PRNG && APU_HAVE_OPENSSL shoudn't be defined! */
@@ -93,30 +119,43 @@ void cprng_stream_ctx_free(cprng_stream_ctx_t *ctx)
}
static APR_INLINE
-apr_status_t cprng_stream_ctx_mix(cprng_stream_ctx_t **pctx,
- unsigned char *key, unsigned char *to,
- const unsigned char *z, apr_size_t n)
+apr_status_t cprng_stream_ctx_bytes(cprng_stream_ctx_t **pctx,
+ unsigned char *key, unsigned char *to,
+ apr_size_t n, const unsigned char *z)
{
cprng_stream_ctx_t *ctx = *pctx;
int len;
- EVP_EncryptInit_ex(ctx, NULL, NULL, key, NULL);
- EVP_CIPHER_CTX_set_padding(ctx, 0);
-
- memset(key, 0, APR_CRYPTO_PRNG_KEY_SIZE);
- EVP_EncryptUpdate(ctx, key, &len, key, APR_CRYPTO_PRNG_KEY_SIZE);
- EVP_EncryptUpdate(ctx, to, &len, z, n);
-
- return APR_SUCCESS;
-}
-
-static apr_status_t cprng_hash_to_seed(pid_t pid, unsigned char seed[])
-{
- SHA256_CTX ctx;
+ /* Can be called for rekeying (key != NULL), streaming more bytes
+ * with the current key (n != 0), or both successively in a one go.
+ */
+ if (key) {
+ /* We never encrypt twice with the same key, so we can use an
+ * all zeros IV. Also EVP_EncryptInit() can be called multiple
+ * times and it will recycle its previous/replaced resources by
+ * itself, so since padding is disabled/irrelevant too we don't
+ * need EVP_EncryptFinish() after each call or before rekeying.
+ */
+#if defined(NID_chacha20)
+ /* With CHACHA20, iv=NULL is the same as zeros but it's faster
+ * to (re-)init; use that for efficiency.
+ */
+ EVP_EncryptInit_ex(ctx, NULL, NULL, key, NULL);
+#else
+ /* With AES256-CTR, iv=NULL seems to peek up and random one (for
+ * the initial CTR), while we can live with zeros (fixed CTR);
+ * efficiency still.
+ */
+ EVP_EncryptInit_ex(ctx, NULL, NULL, key, z);
+#endif
+ EVP_CIPHER_CTX_set_padding(ctx, 0);
- SHA256_Init(&ctx);
- SHA256_Update(&ctx, &pid, sizeof(pid));
- SHA256_Final(seed, &ctx);
+ memset(key, 0, CPRNG_KEY_SIZE);
+ EVP_EncryptUpdate(ctx, key, &len, key, CPRNG_KEY_SIZE);
+ }
+ if (n) {
+ EVP_EncryptUpdate(ctx, to, &len, z, n);
+ }
return APR_SUCCESS;
}
@@ -129,10 +168,11 @@ static apr_status_t cprng_hash_to_seed(pid_t pid, unsigned char seed[])
#endif /* APU_HAVE_OPENSSL */
struct apr_crypto_prng_t {
+ APR_RING_ENTRY(apr_crypto_prng_t) link;
apr_pool_t *pool;
cprng_stream_ctx_t *ctx;
#if APR_HAS_THREADS
- apr_thread_mutex_t *lock;
+ apr_thread_mutex_t *mutex;
#endif
unsigned char *key, *buf;
apr_size_t len, pos;
@@ -140,10 +180,29 @@ struct apr_crypto_prng_t {
};
static apr_crypto_prng_t *cprng_global = NULL;
+static APR_RING_HEAD(apr_cprng_ring, apr_crypto_prng_t) *cprng_ring;
#if APR_HAS_THREADS
+static apr_thread_mutex_t *cprng_ring_mutex;
+
static apr_threadkey_t *cprng_thread_key = NULL;
+#define cprng_lock(g) \
+ if ((g)->mutex) \
+ apr_thread_mutex_lock((g)->mutex)
+
+#define cprng_unlock(g) \
+ if ((g)->mutex) \
+ apr_thread_mutex_unlock((g)->mutex)
+
+#define cprng_ring_lock() \
+ if (cprng_ring_mutex) \
+ apr_thread_mutex_lock(cprng_ring_mutex)
+
+#define cprng_ring_unlock() \
+ if (cprng_ring_mutex) \
+ apr_thread_mutex_unlock(cprng_ring_mutex)
+
static void cprng_thread_destroy(void *cprng)
{
if (!cprng_global) {
@@ -152,7 +211,13 @@ static void cprng_thread_destroy(void *cprng)
}
apr_crypto_prng_destroy(cprng);
}
-#endif
+
+#else /* !APR_HAS_THREADS */
+#define cprng_lock(g)
+#define cprng_unlock(g)
+#define cprng_ring_lock()
+#define cprng_ring_unlock()
+#endif /* !APR_HAS_THREADS */
APR_DECLARE(apr_status_t) apr_crypto_prng_init(apr_pool_t *pool,
apr_size_t bufsize,
@@ -182,10 +247,25 @@ APR_DECLARE(apr_status_t) apr_crypto_prng_init(apr_pool_t *pool,
#endif
}
+ cprng_ring = apr_palloc(pool, sizeof(*cprng_ring));
+ if (!cprng_ring) {
+ return APR_ENOMEM;
+ }
+ APR_RING_INIT(cprng_ring, apr_crypto_prng_t, link);
+
#if APR_HAS_THREADS
- /* Global CPRNG is locked */
+ rv = apr_thread_mutex_create(&cprng_ring_mutex, APR_THREAD_MUTEX_DEFAULT,
+ pool);
+ if (rv != APR_SUCCESS) {
+ apr_threadkey_private_delete(cprng_thread_key);
+ cprng_thread_key = NULL;
+ return rv;
+ }
+
+ /* Global CPRNG is locked (and obviously not per-thread) */
flags = (flags | APR_CRYPTO_PRNG_LOCKED) & ~APR_CRYPTO_PRNG_PER_THREAD;
#endif
+
return apr_crypto_prng_create(&cprng_global, bufsize, flags, seed, pool);
}
@@ -201,26 +281,6 @@ APR_DECLARE(apr_status_t) apr_crypto_prng_term(void)
return APR_SUCCESS;
}
-APR_DECLARE(apr_status_t) apr_crypto_prng_after_fork(apr_proc_t *proc)
-{
- unsigned char seedb[APR_CRYPTO_PRNG_SEED_SIZE], *seed = NULL;
-
- if (!cprng_global) {
- return APR_EINIT;
- }
-
- if (proc) {
- apr_status_t rv;
- rv = cprng_hash_to_seed(proc->pid, seedb);
- if (rv != APR_SUCCESS) {
- return rv;
- }
- seed = seedb;
- }
-
- return apr_crypto_prng_reseed(cprng_global, seed);
-}
-
APR_DECLARE(apr_status_t) apr_crypto_random_bytes(void *buf, apr_size_t len)
{
if (!cprng_global) {
@@ -249,7 +309,8 @@ APR_DECLARE(apr_status_t) apr_crypto_random_thread_bytes(void *buf,
cprng = private;
if (!cprng) {
- rv = apr_crypto_prng_create(&cprng, 0, 0, NULL, NULL);
+ rv = apr_crypto_prng_create(&cprng, 0, APR_CRYPTO_PRNG_PER_THREAD,
+ NULL, NULL);
if (rv != APR_SUCCESS) {
return rv;
}
@@ -271,6 +332,15 @@ static apr_status_t cprng_cleanup(void *arg)
if (cprng == cprng_global) {
cprng_global = NULL;
+#if APR_HAS_THREADS
+ cprng_ring_mutex = NULL;
+#endif
+ cprng_ring = NULL;
+ }
+ else if (cprng_global && !(cprng->flags & APR_CRYPTO_PRNG_PER_THREAD)) {
+ cprng_ring_lock();
+ APR_RING_REMOVE(cprng, link);
+ cprng_ring_unlock();
}
if (cprng->ctx) {
@@ -278,7 +348,7 @@ static apr_status_t cprng_cleanup(void *arg)
}
if (cprng->key) {
- apr_crypto_memzero(cprng->key, APR_CRYPTO_PRNG_KEY_SIZE + cprng->len);
+ apr_crypto_memzero(cprng->key, CPRNG_KEY_SIZE + cprng->len);
}
if (!cprng->pool) {
@@ -299,7 +369,11 @@ APR_DECLARE(apr_status_t) apr_crypto_prng_create(apr_crypto_prng_t **pcprng,
*pcprng = NULL;
- if (bufsize > APR_INT32_MAX - APR_CRYPTO_PRNG_KEY_SIZE
+ if (!cprng_global && pcprng != &cprng_global) {
+ return APR_EINIT;
+ }
+
+ if (bufsize > APR_INT32_MAX - CPRNG_KEY_SIZE
|| (flags & APR_CRYPTO_PRNG_LOCKED && !pool)
|| (flags & ~APR_CRYPTO_PRNG_MASK)) {
return APR_EINVAL;
@@ -324,30 +398,36 @@ APR_DECLARE(apr_status_t) apr_crypto_prng_create(apr_crypto_prng_t **pcprng,
cprng->pool = pool;
if (bufsize == 0) {
- bufsize = APR_CRYPTO_PRNG_BUF_SIZE_DEF;
+ bufsize = CPRNG_BUF_SIZE_DEF;
}
- else if (bufsize < APR_CRYPTO_PRNG_BUF_SIZE_MIN) {
- bufsize = APR_CRYPTO_PRNG_BUF_SIZE_MIN;
+ else if (bufsize < CPRNG_BUF_SIZE_MIN) {
+ bufsize = CPRNG_BUF_SIZE_MIN;
}
- else if (bufsize % APR_CRYPTO_PRNG_KEY_SIZE) {
- bufsize += APR_CRYPTO_PRNG_KEY_SIZE;
- bufsize -= bufsize % APR_CRYPTO_PRNG_KEY_SIZE;
+ else if (bufsize % CPRNG_KEY_SIZE) {
+ bufsize += CPRNG_KEY_SIZE;
+ bufsize -= bufsize % CPRNG_KEY_SIZE;
}
if (pool) {
- cprng->key = apr_palloc(pool, APR_CRYPTO_PRNG_KEY_SIZE + bufsize);
+ cprng->key = apr_palloc(pool, CPRNG_KEY_SIZE + bufsize);
}
else {
- cprng->key = malloc(APR_CRYPTO_PRNG_KEY_SIZE + bufsize);
+ cprng->key = malloc(CPRNG_KEY_SIZE + bufsize);
}
if (!cprng->key) {
cprng_cleanup(cprng);
return APR_ENOMEM;
}
- cprng->buf = cprng->key + APR_CRYPTO_PRNG_KEY_SIZE;
+ cprng->buf = cprng->key + CPRNG_KEY_SIZE;
cprng->len = bufsize;
+ rv = cprng_stream_ctx_make(&cprng->ctx);
+ if (rv != APR_SUCCESS) {
+ cprng_cleanup(cprng);
+ return rv;
+ }
+
if (seed) {
- memset(cprng->key, 0, APR_CRYPTO_PRNG_KEY_SIZE);
+ memset(cprng->key, 0, CPRNG_KEY_SIZE);
}
rv = apr_crypto_prng_reseed(cprng, seed);
if (rv != APR_SUCCESS) {
@@ -357,7 +437,7 @@ APR_DECLARE(apr_status_t) apr_crypto_prng_create(apr_crypto_prng_t **pcprng,
#if APR_HAS_THREADS
if (flags & APR_CRYPTO_PRNG_LOCKED) {
- rv = apr_thread_mutex_create(&cprng->lock, APR_THREAD_MUTEX_DEFAULT,
+ rv = apr_thread_mutex_create(&cprng->mutex, APR_THREAD_MUTEX_DEFAULT,
pool);
if (rv != APR_SUCCESS) {
cprng_cleanup(cprng);
@@ -366,10 +446,13 @@ APR_DECLARE(apr_status_t) apr_crypto_prng_create(apr_crypto_prng_t **pcprng,
}
#endif
- rv = cprng_stream_ctx_make(&cprng->ctx);
- if (rv != APR_SUCCESS) {
- cprng_cleanup(cprng);
- return rv;
+ if (cprng_global && !(flags & APR_CRYPTO_PRNG_PER_THREAD)) {
+ cprng_ring_lock();
+ APR_RING_INSERT_TAIL(cprng_ring, cprng, apr_crypto_prng_t, link);
+ cprng_ring_unlock();
+ }
+ else {
+ APR_RING_ELEM_INIT(cprng, link);
}
if (pool) {
@@ -390,96 +473,106 @@ APR_DECLARE(apr_status_t) apr_crypto_prng_destroy(apr_crypto_prng_t *cprng)
return apr_pool_cleanup_run(cprng->pool, cprng, cprng_cleanup);
}
+static apr_status_t cprng_stream_bytes(apr_crypto_prng_t *cprng,
+ unsigned char *key, void *to,
+ size_t len)
+{
+ apr_status_t rv;
+
+ rv = cprng_stream_ctx_bytes(&cprng->ctx, key, to, len, cprng->buf);
+ if (rv != APR_SUCCESS && len) {
+ apr_crypto_memzero(to, len);
+ }
+ return rv;
+}
+
APR_DECLARE(apr_status_t) apr_crypto_prng_reseed(apr_crypto_prng_t *cprng,
const unsigned char seed[])
{
apr_status_t rv = APR_SUCCESS;
-#if APR_HAS_THREADS
- if (cprng->lock) {
- rv = apr_thread_mutex_lock(cprng->lock);
- if (rv != APR_SUCCESS) {
- return rv;
+ if (!cprng) {
+ /* Fall through with global CPRNG. */
+ cprng = cprng_global;
+ if (!cprng) {
+ return APR_EINIT;
}
}
-#endif
+ cprng_lock(cprng);
+
+ cprng->pos = cprng->len;
+ apr_crypto_memzero(cprng->buf, cprng->len);
if (seed) {
apr_size_t n = 0;
do {
cprng->key[n] ^= seed[n];
- } while (++n < APR_CRYPTO_PRNG_KEY_SIZE);
+ } while (++n < CPRNG_KEY_SIZE);
+ }
+ else if (cprng_global && cprng_global != cprng) {
+ /* Use the global CPRNG: no need for more than the initial entropy. */
+ rv = apr_crypto_random_bytes(cprng->key, CPRNG_KEY_SIZE);
}
else {
- rv = apr_generate_random_bytes(cprng->key, APR_CRYPTO_PRNG_KEY_SIZE);
+ /* Use the system entropy, i.e. one of "/dev/[u]random", getrandom(),
+ * arc4random()... This may block but still we really want to wait for
+ * the system to gather enough entropy for these 32 initial bytes, much
+ * more than we want non-random bytes, and that's once and for all!
+ */
+ rv = apr_generate_random_bytes(cprng->key, CPRNG_KEY_SIZE);
+ }
+ if (rv == APR_SUCCESS) {
+ /* Init/set the stream with the new key, without buffering for now
+ * so that the buffer and/or the next random bytes won't be generated
+ * directly from this key but from the first stream bytes it generates,
+ * i.e. the next key is always extracted from the stream cipher state
+ * and cleared upon use.
+ */
+ rv = cprng_stream_bytes(cprng, cprng->key, NULL, 0);
}
- apr_crypto_memzero(cprng->buf, cprng->len);
- cprng->pos = cprng->len;
-#if APR_HAS_THREADS
- if (cprng->lock) {
- apr_status_t rt = apr_thread_mutex_unlock(cprng->lock);
- if (rv == APR_SUCCESS && rt != APR_SUCCESS) {
- rv = rt;
- }
- }
-#endif
+ cprng_unlock(cprng);
return rv;
}
-static APR_INLINE
-apr_status_t cprng_stream_mix(apr_crypto_prng_t *cprng, unsigned char *to)
-{
- return cprng_stream_ctx_mix(&cprng->ctx, cprng->key, to,
- cprng->buf, cprng->len);
-}
-
-APR_DECLARE(apr_status_t) apr_crypto_prng_bytes(apr_crypto_prng_t *cprng,
- void *buf, apr_size_t len)
+static apr_status_t cprng_bytes(apr_crypto_prng_t *cprng,
+ void *buf, apr_size_t len)
{
unsigned char *ptr = buf;
apr_status_t rv;
apr_size_t n;
-#if APR_HAS_THREADS
- if (cprng->lock) {
- rv = apr_thread_mutex_lock(cprng->lock);
- if (rv != APR_SUCCESS) {
- return rv;
- }
- }
-#endif
-
while (len) {
- if (cprng->pos == cprng->len) {
- if (len >= cprng->len) {
+ n = cprng->len - cprng->pos;
+ if (n == 0) {
+ n = cprng->len;
+ if (len >= n) {
do {
- rv = cprng_stream_mix(cprng, ptr);
+ rv = cprng_stream_bytes(cprng, cprng->key, ptr, n);
if (rv != APR_SUCCESS) {
return rv;
}
- ptr += cprng->len;
- len -= cprng->len;
- } while (len >= cprng->len);
+ ptr += n;
+ len -= n;
+ } while (len >= n);
if (!len) {
break;
}
}
- rv = cprng_stream_mix(cprng, cprng->buf);
+ rv = cprng_stream_bytes(cprng, cprng->key, cprng->buf, n);
if (rv != APR_SUCCESS) {
return rv;
}
cprng->pos = 0;
}
+ if (n > len) {
+ n = len;
+ }
/* Random bytes are consumed then zero-ed to ensure
* both forward secrecy and cleared next mixed data.
*/
- n = cprng->len - cprng->pos;
- if (n > len) {
- n = len;
- }
memcpy(ptr, cprng->buf + cprng->pos, n);
apr_crypto_memzero(cprng->buf + cprng->pos, n);
cprng->pos += n;
@@ -488,17 +581,132 @@ APR_DECLARE(apr_status_t) apr_crypto_prng_bytes(apr_crypto_prng_t *cprng,
len -= n;
}
-#if APR_HAS_THREADS
- if (cprng->lock) {
- apr_status_t rt = apr_thread_mutex_unlock(cprng->lock);
- if (rv == APR_SUCCESS && rt != APR_SUCCESS) {
- rv = rt;
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_crypto_prng_bytes(apr_crypto_prng_t *cprng,
+ void *buf, apr_size_t len)
+{
+ apr_status_t rv;
+
+ if (!cprng) {
+ /* Fall through with global CPRNG. */
+ cprng = cprng_global;
+ if (!cprng) {
+ return APR_EINIT;
}
}
-#endif
- return APR_SUCCESS;
+ cprng_lock(cprng);
+
+ rv = cprng_bytes(cprng, buf, len);
+
+ cprng_unlock(cprng);
+
+ return rv;
+}
+
+/* Reset the buffer and use the next stream bytes as the new key. */
+static apr_status_t cprng_newkey(apr_crypto_prng_t *cprng)
+{
+ cprng->pos = cprng->len;
+ apr_crypto_memzero(cprng->buf, cprng->len);
+ return cprng_stream_bytes(cprng, NULL, cprng->key, CPRNG_KEY_SIZE);
+}
+
+APR_DECLARE(apr_status_t) apr_crypto_prng_rekey(apr_crypto_prng_t *cprng)
+{
+ apr_status_t rv;
+
+ if (!cprng) {
+ /* Fall through with global CPRNG. */
+ cprng = cprng_global;
+ if (!cprng) {
+ return APR_EINIT;
+ }
+ }
+
+ cprng_lock(cprng);
+
+ /* Renew and apply the new key. */
+ rv = cprng_newkey(cprng);
+ if (rv == APR_SUCCESS) {
+ rv = cprng_stream_bytes(cprng, cprng->key, NULL, 0);
+ }
+
+ cprng_unlock(cprng);
+
+ if (cprng == cprng_global) {
+ /* Forward to all maintained CPRNGs. */
+ cprng_ring_lock();
+ for (cprng = APR_RING_FIRST(cprng_ring);
+ cprng != APR_RING_SENTINEL(cprng_ring, apr_crypto_prng_t, link);
+ cprng = APR_RING_NEXT(cprng, link)) {
+ apr_status_t rt = apr_crypto_prng_rekey(cprng);
+ if (rt != APR_SUCCESS && rv == APR_SUCCESS) {
+ rv = rt;
+ }
+ }
+ cprng_ring_unlock();
+ }
+
+ return rv;
+}
+
+#if APR_HAS_FORK
+APR_DECLARE(apr_status_t) apr_crypto_prng_after_fork(apr_crypto_prng_t *cprng,
+ int in_child)
+{
+ apr_status_t rv;
+
+ if (!cprng) {
+ /* Fall through with global CPRNG. */
+ cprng = cprng_global;
+ if (!cprng) {
+ return APR_EINIT;
+ }
+ }
+
+ cprng_lock(cprng);
+
+ /* Make sure the parent and child processes never share the same state, so
+ * renew the key first (also clears the buffer) for both parent and child,
+ * so that further fork()s (from parent or child) won't use the same state.
+ */
+ rv = cprng_newkey(cprng);
+ if (rv == APR_SUCCESS && !in_child) {
+ /* For the parent process only, renew a second time to ensure that key
+ * material is different from the child.
+ */
+ rv = cprng_stream_bytes(cprng, NULL, cprng->key, CPRNG_KEY_SIZE);
+ }
+ if (rv == APR_SUCCESS) {
+ /* Finally apply the new key, parent and child ones will now be
+ * different and unknown to each other (including at the stream ctx
+ * level).
+ */
+ rv = cprng_stream_bytes(cprng, cprng->key, NULL, 0);
+ }
+
+ cprng_unlock(cprng);
+
+ if (cprng == cprng_global) {
+ /* Forward to all maintained CPRNGs. */
+ cprng_ring_lock();
+ for (cprng = APR_RING_FIRST(cprng_ring);
+ cprng != APR_RING_SENTINEL(cprng_ring, apr_crypto_prng_t, link);
+ cprng = APR_RING_NEXT(cprng, link)) {
+ apr_status_t rt = apr_crypto_prng_after_fork(cprng, in_child);
+ if (rt != APR_SUCCESS && rv == APR_SUCCESS) {
+ rv = rt;
+ }
+ }
+ cprng_ring_unlock();
+ }
+
+ return rv;
}
+#endif /* APR_HAS_FORK */
#endif /* APU_HAVE_CRYPTO_PRNG */
#endif /* APU_HAVE_CRYPTO */