commit
8a6ee82e17
@ -3822,11 +3822,15 @@ key, and any hash that produces at least a 256--bit output. However, to make th
|
||||
it has been fixed to those choices.
|
||||
|
||||
Fortuna is more secure than Yarrow in the sense that attackers who learn parts of the entropy being
|
||||
added to the PRNG learn far less about the state than that of Yarrow. Without getting into to many
|
||||
added to the PRNG learn far less about the state than that of Yarrow. Without getting into too many
|
||||
details Fortuna has the ability to recover from state determination attacks where the attacker starts
|
||||
to learn information from the PRNGs output about the internal state. Yarrow on the other hand, cannot
|
||||
recover from that problem until new entropy is added to the pool and put to use through the ready() function.
|
||||
|
||||
For detailed information on how the algorithm works and what you have to do to maintain the secure state
|
||||
get a copy of the book\footnote{Niels Ferguson and Bruce Schneier, Practical Cryptography. ISBN 0-471-22357-3.} or
|
||||
read the paper online\footnote{\url{https://www.schneier.com/academic/paperfiles/fortuna.pdf} [Accessed on 7th Dec. 2017]}.
|
||||
|
||||
\subsubsection{RC4}
|
||||
|
||||
RC4 is an old stream cipher that can also double duty as a PRNG in a pinch. You key RC4 by
|
||||
|
@ -212,6 +212,31 @@ int register_all_prngs(void);
|
||||
int prng_is_valid(int idx);
|
||||
LTC_MUTEX_PROTO(ltc_prng_mutex)
|
||||
|
||||
#ifdef LTC_SOURCE
|
||||
/* internal helper functions */
|
||||
#define _LTC_PRNG_EXPORT(which) \
|
||||
int which ## _export(unsigned char *out, unsigned long *outlen, prng_state *prng) \
|
||||
{ \
|
||||
unsigned long len = which ## _desc.export_size; \
|
||||
\
|
||||
LTC_ARGCHK(prng != NULL); \
|
||||
LTC_ARGCHK(out != NULL); \
|
||||
LTC_ARGCHK(outlen != NULL); \
|
||||
\
|
||||
if (*outlen < len) { \
|
||||
*outlen = len; \
|
||||
return CRYPT_BUFFER_OVERFLOW; \
|
||||
} \
|
||||
\
|
||||
if (which ## _read(out, len, prng) != len) { \
|
||||
return CRYPT_ERROR_READPRNG; \
|
||||
} \
|
||||
\
|
||||
*outlen = len; \
|
||||
return CRYPT_OK; \
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Slow RNG you **might** be able to use to seed a PRNG with. Be careful as this
|
||||
* might not work on all platforms as planned
|
||||
*/
|
||||
|
@ -150,26 +150,7 @@ int chacha20_prng_done(prng_state *prng)
|
||||
@param prng The PRNG to export
|
||||
@return CRYPT_OK if successful
|
||||
*/
|
||||
int chacha20_prng_export(unsigned char *out, unsigned long *outlen, prng_state *prng)
|
||||
{
|
||||
unsigned long len = chacha20_prng_desc.export_size;
|
||||
|
||||
LTC_ARGCHK(prng != NULL);
|
||||
LTC_ARGCHK(out != NULL);
|
||||
LTC_ARGCHK(outlen != NULL);
|
||||
|
||||
if (*outlen < len) {
|
||||
*outlen = len;
|
||||
return CRYPT_BUFFER_OVERFLOW;
|
||||
}
|
||||
|
||||
if (chacha20_prng_read(out, len, prng) != len) {
|
||||
return CRYPT_ERROR_READPRNG;
|
||||
}
|
||||
|
||||
*outlen = len;
|
||||
return CRYPT_OK;
|
||||
}
|
||||
_LTC_PRNG_EXPORT(chacha20_prng)
|
||||
|
||||
/**
|
||||
Import a PRNG state
|
||||
|
@ -37,7 +37,7 @@ we reseed automatically when len(pool0) >= 64 or every LTC_FORTUNA_WD calls to t
|
||||
|
||||
const struct ltc_prng_descriptor fortuna_desc = {
|
||||
"fortuna",
|
||||
(32 * LTC_FORTUNA_POOLS), /* default: 1024 */
|
||||
64,
|
||||
&fortuna_start,
|
||||
&fortuna_add_entropy,
|
||||
&fortuna_ready,
|
||||
@ -66,9 +66,9 @@ static int _fortuna_reseed(prng_state *prng)
|
||||
{
|
||||
unsigned char tmp[MAXBLOCKSIZE];
|
||||
hash_state md;
|
||||
ulong64 reset_cnt;
|
||||
int err, x;
|
||||
|
||||
++prng->fortuna.reset_cnt;
|
||||
|
||||
/* new K == LTC_SHA256(K || s) where s == LTC_SHA256(P0) || LTC_SHA256(P1) ... */
|
||||
sha256_init(&md);
|
||||
@ -77,8 +77,10 @@ static int _fortuna_reseed(prng_state *prng)
|
||||
return err;
|
||||
}
|
||||
|
||||
reset_cnt = prng->fortuna.reset_cnt + 1;
|
||||
|
||||
for (x = 0; x < LTC_FORTUNA_POOLS; x++) {
|
||||
if (x == 0 || ((prng->fortuna.reset_cnt >> (x-1)) & 1) == 0) {
|
||||
if (x == 0 || ((reset_cnt >> (x-1)) & 1) == 0) {
|
||||
/* terminate this hash */
|
||||
if ((err = sha256_done(&prng->fortuna.pool[x], tmp)) != CRYPT_OK) {
|
||||
sha256_done(&md, tmp);
|
||||
@ -108,9 +110,10 @@ static int _fortuna_reseed(prng_state *prng)
|
||||
}
|
||||
_fortuna_update_iv(prng);
|
||||
|
||||
/* reset pool len */
|
||||
/* reset/update internals */
|
||||
prng->fortuna.pool0_len = 0;
|
||||
prng->fortuna.wd = 0;
|
||||
prng->fortuna.reset_cnt = reset_cnt;
|
||||
|
||||
|
||||
#ifdef LTC_CLEAN_STACK
|
||||
@ -121,6 +124,46 @@ static int _fortuna_reseed(prng_state *prng)
|
||||
return CRYPT_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
"Update Seed File"-compliant update of K
|
||||
|
||||
@param in The PRNG state
|
||||
@param inlen Size of the state
|
||||
@param prng The PRNG to import
|
||||
@return CRYPT_OK if successful
|
||||
*/
|
||||
static int _fortuna_update_seed(const unsigned char *in, unsigned long inlen, prng_state *prng)
|
||||
{
|
||||
int err;
|
||||
unsigned char tmp[MAXBLOCKSIZE];
|
||||
hash_state md;
|
||||
|
||||
LTC_MUTEX_LOCK(&prng->lock);
|
||||
/* new K = LTC_SHA256(K || in) */
|
||||
sha256_init(&md);
|
||||
if ((err = sha256_process(&md, prng->fortuna.K, 32)) != CRYPT_OK) {
|
||||
sha256_done(&md, tmp);
|
||||
goto LBL_UNLOCK;
|
||||
}
|
||||
if ((err = sha256_process(&md, in, inlen)) != CRYPT_OK) {
|
||||
sha256_done(&md, tmp);
|
||||
goto LBL_UNLOCK;
|
||||
}
|
||||
/* finish key */
|
||||
if ((err = sha256_done(&md, prng->fortuna.K)) != CRYPT_OK) {
|
||||
goto LBL_UNLOCK;
|
||||
}
|
||||
_fortuna_update_iv(prng);
|
||||
|
||||
LBL_UNLOCK:
|
||||
LTC_MUTEX_UNLOCK(&prng->lock);
|
||||
#ifdef LTC_CLEAN_STACK
|
||||
zeromem(&md, sizeof(md));
|
||||
#endif
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
Start the PRNG
|
||||
@param prng [out] The PRNG state to initialize
|
||||
@ -251,6 +294,11 @@ unsigned long fortuna_read(unsigned char *out, unsigned long outlen, prng_state
|
||||
}
|
||||
}
|
||||
|
||||
/* ensure that one reseed happened before allowing to read */
|
||||
if (prng->fortuna.reset_cnt == 0) {
|
||||
goto LBL_UNLOCK;
|
||||
}
|
||||
|
||||
/* now generate the blocks required */
|
||||
tlen = outlen;
|
||||
|
||||
@ -329,71 +377,7 @@ LBL_UNLOCK:
|
||||
@param prng The PRNG to export
|
||||
@return CRYPT_OK if successful
|
||||
*/
|
||||
int fortuna_export(unsigned char *out, unsigned long *outlen, prng_state *prng)
|
||||
{
|
||||
int x, err;
|
||||
hash_state *md;
|
||||
unsigned long len = fortuna_desc.export_size;
|
||||
|
||||
LTC_ARGCHK(out != NULL);
|
||||
LTC_ARGCHK(outlen != NULL);
|
||||
LTC_ARGCHK(prng != NULL);
|
||||
|
||||
LTC_MUTEX_LOCK(&prng->lock);
|
||||
|
||||
if (!prng->ready) {
|
||||
err = CRYPT_ERROR;
|
||||
goto LBL_UNLOCK;
|
||||
}
|
||||
|
||||
/* we'll write bytes for s&g's */
|
||||
if (*outlen < len) {
|
||||
*outlen = len;
|
||||
err = CRYPT_BUFFER_OVERFLOW;
|
||||
goto LBL_UNLOCK;
|
||||
}
|
||||
|
||||
md = XMALLOC(sizeof(hash_state));
|
||||
if (md == NULL) {
|
||||
err = CRYPT_MEM;
|
||||
goto LBL_UNLOCK;
|
||||
}
|
||||
|
||||
/* to emit the state we copy each pool, terminate it then hash it again so
|
||||
* an attacker who sees the state can't determine the current state of the PRNG
|
||||
*/
|
||||
for (x = 0; x < LTC_FORTUNA_POOLS; x++) {
|
||||
/* copy the PRNG */
|
||||
XMEMCPY(md, &(prng->fortuna.pool[x]), sizeof(*md));
|
||||
|
||||
/* terminate it */
|
||||
if ((err = sha256_done(md, out+x*32)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
|
||||
/* now hash it */
|
||||
if ((err = sha256_init(md)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
if ((err = sha256_process(md, out+x*32, 32)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
if ((err = sha256_done(md, out+x*32)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
}
|
||||
*outlen = len;
|
||||
err = CRYPT_OK;
|
||||
|
||||
LBL_ERR:
|
||||
#ifdef LTC_CLEAN_STACK
|
||||
zeromem(md, sizeof(*md));
|
||||
#endif
|
||||
XFREE(md);
|
||||
LBL_UNLOCK:
|
||||
LTC_MUTEX_UNLOCK(&prng->lock);
|
||||
return err;
|
||||
}
|
||||
_LTC_PRNG_EXPORT(fortuna)
|
||||
|
||||
/**
|
||||
Import a PRNG state
|
||||
@ -404,10 +388,10 @@ LBL_UNLOCK:
|
||||
*/
|
||||
int fortuna_import(const unsigned char *in, unsigned long inlen, prng_state *prng)
|
||||
{
|
||||
int err, x;
|
||||
int err;
|
||||
|
||||
LTC_ARGCHK(in != NULL);
|
||||
LTC_ARGCHK(prng != NULL);
|
||||
LTC_ARGCHK(in != NULL);
|
||||
LTC_ARGCHK(prng != NULL);
|
||||
|
||||
if (inlen < (unsigned long)fortuna_desc.export_size) {
|
||||
return CRYPT_INVALID_ARG;
|
||||
@ -416,12 +400,12 @@ int fortuna_import(const unsigned char *in, unsigned long inlen, prng_state *prn
|
||||
if ((err = fortuna_start(prng)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
for (x = 0; x < LTC_FORTUNA_POOLS; x++) {
|
||||
if ((err = fortuna_add_entropy(in+x*32, 32, prng)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((err = _fortuna_update_seed(in, inlen, prng)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
return CRYPT_OK;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -153,26 +153,7 @@ int rc4_done(prng_state *prng)
|
||||
@param prng The PRNG to export
|
||||
@return CRYPT_OK if successful
|
||||
*/
|
||||
int rc4_export(unsigned char *out, unsigned long *outlen, prng_state *prng)
|
||||
{
|
||||
unsigned long len = rc4_desc.export_size;
|
||||
|
||||
LTC_ARGCHK(prng != NULL);
|
||||
LTC_ARGCHK(out != NULL);
|
||||
LTC_ARGCHK(outlen != NULL);
|
||||
|
||||
if (*outlen < len) {
|
||||
*outlen = len;
|
||||
return CRYPT_BUFFER_OVERFLOW;
|
||||
}
|
||||
|
||||
if (rc4_read(out, len, prng) != len) {
|
||||
return CRYPT_ERROR_READPRNG;
|
||||
}
|
||||
|
||||
*outlen = len;
|
||||
return CRYPT_OK;
|
||||
}
|
||||
_LTC_PRNG_EXPORT(rc4)
|
||||
|
||||
/**
|
||||
Import a PRNG state
|
||||
|
@ -16,7 +16,12 @@
|
||||
|
||||
/**
|
||||
Create a PRNG from a RNG
|
||||
@param bits Number of bits of entropy desired (64 ... 1024)
|
||||
|
||||
In case you pass bits as '-1' the PRNG will be setup
|
||||
as if the export/import functionality has been used,
|
||||
but the imported data comes directly from the RNG.
|
||||
|
||||
@param bits Number of bits of entropy desired (-1 or 64 ... 1024)
|
||||
@param wprng Index of which PRNG to setup
|
||||
@param prng [out] PRNG state to initialize
|
||||
@param callback A pointer to a void function for when the RNG is slow, this can be NULL
|
||||
@ -25,7 +30,8 @@
|
||||
int rng_make_prng(int bits, int wprng, prng_state *prng,
|
||||
void (*callback)(void))
|
||||
{
|
||||
unsigned char buf[256];
|
||||
unsigned char* buf;
|
||||
unsigned long bytes;
|
||||
int err;
|
||||
|
||||
LTC_ARGCHK(prng != NULL);
|
||||
@ -35,31 +41,47 @@ int rng_make_prng(int bits, int wprng, prng_state *prng,
|
||||
return err;
|
||||
}
|
||||
|
||||
if (bits < 64 || bits > 1024) {
|
||||
if (bits == -1) {
|
||||
bytes = prng_descriptor[wprng].export_size;
|
||||
} else if (bits < 64 || bits > 1024) {
|
||||
return CRYPT_INVALID_PRNGSIZE;
|
||||
} else {
|
||||
bytes = (unsigned long)((bits+7)/8) * 2;
|
||||
}
|
||||
|
||||
if ((err = prng_descriptor[wprng].start(prng)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
bits = ((bits+7)/8) * 2;
|
||||
if (rng_get_bytes(buf, (unsigned long)bits, callback) != (unsigned long)bits) {
|
||||
return CRYPT_ERROR_READPRNG;
|
||||
buf = XMALLOC(bytes);
|
||||
if (buf == NULL) {
|
||||
return CRYPT_MEM;
|
||||
}
|
||||
|
||||
if ((err = prng_descriptor[wprng].add_entropy(buf, (unsigned long)bits, prng)) != CRYPT_OK) {
|
||||
return err;
|
||||
if (rng_get_bytes(buf, bytes, callback) != bytes) {
|
||||
err = CRYPT_ERROR_READPRNG;
|
||||
goto LBL_ERR;
|
||||
}
|
||||
|
||||
if (bits == -1) {
|
||||
if ((err = prng_descriptor[wprng].pimport(buf, bytes, prng)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
} else {
|
||||
if ((err = prng_descriptor[wprng].add_entropy(buf, bytes, prng)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
}
|
||||
if ((err = prng_descriptor[wprng].ready(prng)) != CRYPT_OK) {
|
||||
return err;
|
||||
goto LBL_ERR;
|
||||
}
|
||||
|
||||
LBL_ERR:
|
||||
#ifdef LTC_CLEAN_STACK
|
||||
zeromem(buf, sizeof(buf));
|
||||
zeromem(buf, bytes);
|
||||
#endif
|
||||
return CRYPT_OK;
|
||||
XFREE(buf);
|
||||
return err;
|
||||
}
|
||||
#endif /* #ifdef LTC_RNG_MAKE_PRNG */
|
||||
|
||||
|
@ -152,26 +152,7 @@ int sober128_done(prng_state *prng)
|
||||
@param prng The PRNG to export
|
||||
@return CRYPT_OK if successful
|
||||
*/
|
||||
int sober128_export(unsigned char *out, unsigned long *outlen, prng_state *prng)
|
||||
{
|
||||
unsigned long len = sober128_desc.export_size;
|
||||
|
||||
LTC_ARGCHK(prng != NULL);
|
||||
LTC_ARGCHK(out != NULL);
|
||||
LTC_ARGCHK(outlen != NULL);
|
||||
|
||||
if (*outlen < len) {
|
||||
*outlen = len;
|
||||
return CRYPT_BUFFER_OVERFLOW;
|
||||
}
|
||||
|
||||
if (sober128_read(out, len, prng) != len) {
|
||||
return CRYPT_ERROR_READPRNG;
|
||||
}
|
||||
|
||||
*outlen = len;
|
||||
return CRYPT_OK;
|
||||
}
|
||||
_LTC_PRNG_EXPORT(sober128)
|
||||
|
||||
/**
|
||||
Import a PRNG state
|
||||
@ -189,7 +170,7 @@ int sober128_import(const unsigned char *in, unsigned long inlen, prng_state *pr
|
||||
if (inlen < (unsigned long)sober128_desc.export_size) return CRYPT_INVALID_ARG;
|
||||
|
||||
if ((err = sober128_start(prng)) != CRYPT_OK) return err;
|
||||
if ((err = sober128_add_entropy(in, sober128_desc.export_size, prng)) != CRYPT_OK) return err;
|
||||
if ((err = sober128_add_entropy(in, inlen, prng)) != CRYPT_OK) return err;
|
||||
return CRYPT_OK;
|
||||
}
|
||||
|
||||
|
@ -273,26 +273,7 @@ int yarrow_done(prng_state *prng)
|
||||
@param prng The PRNG to export
|
||||
@return CRYPT_OK if successful
|
||||
*/
|
||||
int yarrow_export(unsigned char *out, unsigned long *outlen, prng_state *prng)
|
||||
{
|
||||
unsigned long len = yarrow_desc.export_size;
|
||||
|
||||
LTC_ARGCHK(out != NULL);
|
||||
LTC_ARGCHK(outlen != NULL);
|
||||
LTC_ARGCHK(prng != NULL);
|
||||
|
||||
if (*outlen < len) {
|
||||
*outlen = len;
|
||||
return CRYPT_BUFFER_OVERFLOW;
|
||||
}
|
||||
|
||||
if (yarrow_read(out, len, prng) != len) {
|
||||
return CRYPT_ERROR_READPRNG;
|
||||
}
|
||||
|
||||
*outlen = len;
|
||||
return CRYPT_OK;
|
||||
}
|
||||
_LTC_PRNG_EXPORT(yarrow)
|
||||
|
||||
/**
|
||||
Import a PRNG state
|
||||
|
@ -42,10 +42,11 @@ int prng_test(void)
|
||||
|
||||
before = my_test_rng_read;
|
||||
|
||||
if ((err = rng_make_prng(128, find_prng("yarrow"), &yarrow_prng, NULL)) != CRYPT_OK) {
|
||||
if ((err = rng_make_prng(128, find_prng("yarrow"), &nprng, NULL)) != CRYPT_OK) {
|
||||
fprintf(stderr, "rng_make_prng with 'my_test_rng' failed: %s\n", error_to_string(err));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
DO(yarrow_done(&nprng));
|
||||
|
||||
if (before == my_test_rng_read) {
|
||||
fprintf(stderr, "somehow there was no read from the ltc_rng! %lu == %lu\n", before, my_test_rng_read);
|
||||
@ -58,7 +59,6 @@ int prng_test(void)
|
||||
/* test prngs (test, import/export) */
|
||||
for (x = 0; prng_descriptor[x].name != NULL; x++) {
|
||||
if(strstr(prng_descriptor[x].name, "no_prng") == prng_descriptor[x].name) continue;
|
||||
err = CRYPT_OK;
|
||||
DOX(prng_descriptor[x].test(), prng_descriptor[x].name);
|
||||
DOX(prng_descriptor[x].start(&nprng), prng_descriptor[x].name);
|
||||
DOX(prng_descriptor[x].add_entropy((unsigned char *)"helloworld12", 12, &nprng), prng_descriptor[x].name);
|
||||
@ -82,6 +82,11 @@ int prng_test(void)
|
||||
}
|
||||
prng_descriptor[x].done(&nprng);
|
||||
}
|
||||
|
||||
if ((err = rng_make_prng(-1, find_prng("yarrow"), &nprng, NULL)) != CRYPT_OK) {
|
||||
fprintf(stderr, "rng_make_prng(-1,..) with 'yarrow' failed: %s\n", error_to_string(err));
|
||||
}
|
||||
DO(yarrow_done(&nprng));
|
||||
return err;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user