Browse Source

SHAKE-based rng

master
Mike Hamburg 10 years ago
parent
commit
6c9230e398
2 changed files with 283 additions and 5 deletions
  1. +90
    -1
      include/shake.h
  2. +193
    -4
      src/shake.c

+ 90
- 1
include/shake.h View File

@@ -15,8 +15,11 @@
#include <stdint.h>
#include <sys/types.h>

/* TODO: unify with other headers (maybe all into one??); add nonnull attributes */
#define API_VIS __attribute__((visibility("default")))
#define WARN_UNUSED __attribute__((warn_unused_result))

/* TODO: different containing structs for each primitive? */
#ifndef INTERNAL_SPONGE_STRUCT
typedef struct keccak_sponge_s {
uint64_t opaque[26];
@@ -131,7 +134,93 @@ DECSHA3(224)
DECSHA3(256)
DECSHA3(384)
DECSHA3(512)

/**
* @brief Initialize a sponge-based CSPRNG from a buffer.
*
* @param [out] sponge The sponge object.
* @param [in] in The initial data.
* @param [in] len The length of the initial data.
* @param [in] deterministic If zero, allow RNG to stir in nondeterministic
* data from RDRAND or RDTSC.
*/
void spongerng_init_from_buffer (
keccak_sponge_t sponge,
const uint8_t * __restrict__ in,
size_t len,
int deterministic
) API_VIS;

/* FIXME!! This interface has the opposite retval convention from other functions
* in the library. (0=success). Should they be harmonized?
*/

/**
* @brief Initialize a sponge-based CSPRNG from a file.
*
* @param [out] sponge The sponge object.
* @param [in] file A name of a file containing initial data.
* @param [in] len The length of the initial data. Must be positive.
* @param [in] deterministic If zero, allow RNG to stir in nondeterministic
* data from RDRAND or RDTSC.
*
* @retval 0 Success.
* @retval positive An error has occurred, and this was the errno.
* @retval -1 An unknown error has occurred.
* @retval -2 len was 0.
*/
int spongerng_init_from_file (
keccak_sponge_t sponge,
const char *file,
size_t len,
int deterministic
) API_VIS WARN_UNUSED;


/* FIXME!! This interface has the opposite retval convention from other functions
* in the library. (0=success). Should they be harmonized?
*/

/**
* @brief Initialize a nondeterministic sponge-based CSPRNG from /dev/urandom.
*
* @param [out] sponge The sponge object.
*
* @retval 0 Success.
* @retval positive An error has occurred, and this was the errno.
* @retval -1 An unknown error has occurred.
*/
int spongerng_init_from_dev_urandom (
keccak_sponge_t sponge
) API_VIS WARN_UNUSED;

/**
* @brief Output bytes from a sponge-based CSPRNG.
*
* @param [inout] sponge The sponge object.
* @param [out] out The output buffer.
* @param [in] out The output buffer's length.
*/
void spongerng_next (
keccak_sponge_t sponge,
uint8_t * __restrict__ out,
size_t len
) API_VIS;

/**
* @brief Stir entropy data into a sponge-based CSPRNG from a buffer.
*
* @param [out] sponge The sponge object.
* @param [in] in The entropy data.
* @param [in] len The length of the initial data.
*/
void spongerng_stir (
keccak_sponge_t sponge,
const uint8_t * __restrict__ in,
size_t len
) API_VIS;

#undef API_VIS
#undef WARN_UNUSED
#endif /* __SHAKE_H__ */

+ 193
- 4
src/shake.c View File

@@ -17,6 +17,13 @@
#include <stdint.h>
#include <string.h>

/* to open and read from /dev/urandom */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>

/* Subset of Mathias Panzenböck's portable endian code, public domain */
#if defined(__linux__) || defined(__CYGWIN__)
# include <endian.h>
@@ -64,6 +71,12 @@ typedef struct keccak_sponge_s {

#define FLAG_ABSORBING 'A'
#define FLAG_SQUEEZING 'Z'
#define FLAG_RNG_SQU 'R'
#define FLAG_DET_SQU 'D'
#define FLAG_RNG_ABS 'r'
#define FLAG_DET_ABS 'd'
#define FLAG_RNG_UNI 'u'
#define FLAG_DET_UNI 'g'

/** Constants. **/
static const uint8_t pi[24] = {
@@ -96,7 +109,9 @@ static inline uint64_t rol(uint64_t x, int s) {
#endif

/*** The Keccak-f[1600] permutation ***/
static void keccakf(kdomain_t state, uint8_t startRound) {
static void
__attribute__((noinline))
keccakf(kdomain_t state, uint8_t startRound) {
uint64_t* a = state->w;
uint64_t b[5] = {0}, t, u;
uint8_t x, y, i;
@@ -139,14 +154,14 @@ void sha3_update (
assert(sponge->params->rate < sizeof(sponge->state));
assert(sponge->params->flags == FLAG_ABSORBING);
while (len) {
size_t cando = sponge->params->rate - sponge->params->position;
size_t cando = sponge->params->rate - sponge->params->position, i;
uint8_t* state = &sponge->state->b[sponge->params->position];
if (cando > len) {
for (size_t i = 0; i < len; i += 1) state[i] ^= in[i];
for (i = 0; i < len; i += 1) state[i] ^= in[i];
sponge->params->position += len;
return;
} else {
for (size_t i = 0; i < cando; i += 1) state[i] ^= in[i];
for (i = 0; i < cando; i += 1) state[i] ^= in[i];
dokeccak(sponge);
len -= cando;
in += cando;
@@ -251,4 +266,178 @@ DEFSHA3(256)
DEFSHA3(384)
DEFSHA3(512)

/** Get entropy from a CPU, preferably in the form of RDRAND, but possibly instead from RDTSC. */
static void get_cpu_entropy(uint8_t *entropy, size_t len) {
# if (defined(__i386__) || defined(__x86_64__))
static char tested = 0, have_rdrand = 0;
if (!tested) {
u_int32_t a,b,c,d;
a=0x80000001; __asm__("cpuid" : "+a"(a), "=b"(b), "=c"(c), "=d"(d));
have_rdrand = (c>>30)&1;
tested = 1;
}

if (have_rdrand) {
# if defined(__x86_64__)
uint64_t out, a=0, *eo = (uint64_t *)entropy;
# elif defined(__i386__)
uint32_t out, a=0, *eo = (uint64_t *)entropy;
#endif
len /= sizeof(out);

uint32_t tries;
for (tries = 100+len; tries && len; len--, eo++) {
for (a = 0; tries && !a; tries--) {
__asm__ __volatile__ ("rdrand %0\n\tsetc %%al" : "=r"(out), "+a"(a) :: "cc" );
}
*eo ^= out;
}
} else if (len>8) {
uint64_t out;
__asm__ __volatile__ ("rdtsc" : "=A"(out));
*(uint64_t*) entropy ^= out;
}

#else
(void) entropy;
(void) len;
#endif
}

void spongerng_next (
keccak_sponge_t sponge,
uint8_t * __restrict__ out,
size_t len
) {
assert(sponge->params->position < sponge->params->rate);
assert(sponge->params->rate < sizeof(sponge->state));

switch(sponge->params->flags) {
case FLAG_DET_SQU: case FLAG_RNG_SQU: break;
case FLAG_DET_ABS: case FLAG_RNG_ABS:
{
uint8_t* state = sponge->state->b;
state[sponge->params->position] ^= sponge->params->pad;
state[sponge->params->rate - 1] ^= sponge->params->ratePad;
dokeccak(sponge);
sponge->params->flags = (sponge->params->flags == FLAG_DET_ABS) ? FLAG_DET_SQU : FLAG_RNG_SQU;
break;
}
default: assert(0);
};

while (len) {
size_t cando = sponge->params->rate - sponge->params->position;
uint8_t* state = &sponge->state->b[sponge->params->position];
if (cando > len) {
memcpy(out, state, len);
memset(state, 0, len);
sponge->params->position += len;
return;
} else {
memcpy(out, state, cando);
memset(state, 0, cando);
if (sponge->params->flags == FLAG_RNG_SQU)
get_cpu_entropy(sponge->state->b, 32);
dokeccak(sponge);
len -= cando;
out += cando;
}
}

/* Anti-rollback */
if (sponge->params->position < 32) {
memset(&sponge->state->b, 0, 32);
sponge->params->position = 32;
}
}

void spongerng_stir (
keccak_sponge_t sponge,
const uint8_t * __restrict__ in,
size_t len
) {
assert(sponge->params->position < sponge->params->rate);
assert(sponge->params->rate < sizeof(sponge->state));

switch(sponge->params->flags) {
case FLAG_RNG_SQU:
get_cpu_entropy(sponge->state->b, 32);
/* fall through */
case FLAG_DET_SQU:
sponge->params->flags = (sponge->params->flags == FLAG_DET_SQU) ? FLAG_DET_ABS : FLAG_RNG_ABS;
dokeccak(sponge);
break;
case FLAG_DET_ABS: case FLAG_RNG_ABS: break;
case FLAG_DET_UNI: case FLAG_RNG_UNI: break;
default: assert(0);
};

while (len) {
size_t i;
size_t cando = sponge->params->rate - sponge->params->position;
uint8_t* state = &sponge->state->b[sponge->params->position];
if (cando > len) {
for (i = 0; i < len; i += 1) state[i] ^= in[i];
sponge->params->position += len;
return;
} else {
for (i = 0; i < cando; i += 1) state[i] ^= in[i];
dokeccak(sponge);
len -= cando;
in += cando;
}
}
}

static const struct kparams_s spongerng_params = {
0, FLAG_RNG_UNI, 200-256/4, 0, 0x06, 0x80, 0xFF, 0
};

void spongerng_init_from_buffer (
keccak_sponge_t sponge,
const uint8_t * __restrict__ in,
size_t len,
int deterministic
) {
sponge_init(sponge, &spongerng_params);
sponge->params->flags = deterministic ? FLAG_DET_ABS : FLAG_RNG_ABS;
spongerng_stir(sponge, in, len);
}

int spongerng_init_from_file (
keccak_sponge_t sponge,
const char *file,
size_t len,
int deterministic
) {
sponge_init(sponge, &spongerng_params);
sponge->params->flags = deterministic ? FLAG_DET_UNI : FLAG_RNG_UNI;
if (!len) return -2;

int fd = open(file, O_RDONLY);
if (fd < 0) return errno ? errno : -1;
uint8_t buffer[128];
while (len) {
ssize_t red = read(fd, buffer, (len > sizeof(buffer)) ? sizeof(buffer) : len);
if (red <= 0) {
close(fd);
return errno ? errno : -1;
}
spongerng_stir(sponge,buffer,red);
len -= red;
};
close(fd);

sponge->params->flags = deterministic ? FLAG_DET_ABS : FLAG_RNG_ABS;
return 0;
}

int spongerng_init_from_dev_urandom (
keccak_sponge_t sponge
) {
return spongerng_init_from_file(sponge, "/dev/urandom", 64, 0);
}

/* TODO: Keyak instances, etc */

Loading…
Cancel
Save