Created
October 10, 2012 07:43
-
-
Save vincenthz/3863813 to your computer and use it in GitHub Desktop.
CPRNG with a block cipher in CTR mode.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (C) 2012 Vincent Hanquez | |
* | |
* All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions | |
* are met: | |
* | |
* 1. Redistributions of source code must retain the above copyright | |
* notice, this list of conditions and the following disclaimer. | |
* 2. Redistributions in binary form must reproduce the above copyright | |
* notice, this list of conditions and the following disclaimer in the | |
* documentation and/or other materials provided with the distribution. | |
* 3. Neither the name of the author nor the names of his contributors | |
* may be used to endorse or promote products derived from this software | |
* without specific prior written permission. | |
* | |
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE | |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
* SUCH DAMAGE. | |
*/ | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/select.h> | |
#include <unistd.h> | |
#include <fcntl.h> | |
#define USE_OPENSSL | |
#ifdef USE_OPENSSL | |
#include <openssl/aes.h> | |
#else | |
#error "no aes backend define. use USE_OPENSSL" | |
#endif | |
#define INIT_SIZE 32 | |
#define CHUNK_SIZE 1024 | |
#define DEFAULT_RESEED 100 | |
#ifdef DEBUG | |
#define debug fprintf | |
#else | |
#define debug(...) | |
#endif | |
typedef struct | |
{ | |
#ifdef USE_OPENSSL | |
AES_KEY key; | |
#endif | |
uint64_t next[2]; | |
uint8_t buffer[CHUNK_SIZE]; | |
uint32_t bufleft; | |
uint32_t reseed_ctr; | |
uint32_t reseed; | |
} prng; | |
static int random_init_with(prng *prng, uint8_t *seedbuf) | |
{ | |
#ifdef USE_OPENSSL | |
AES_set_encrypt_key(seedbuf, 128, &prng->key); | |
#endif | |
prng->next[0] ^= *((uint64_t *) (seedbuf+16)); | |
prng->next[1] ^= *((uint64_t *) (seedbuf+24)); | |
prng->reseed_ctr = prng->reseed; | |
return 0; | |
} | |
static int random_get_entropy(uint8_t *seedbuf, int tries) | |
{ | |
int fd, n, m, r; | |
struct timeval tv = { .tv_sec = 0, .tv_usec = 10000 }; | |
fd = open("/dev/random", O_RDONLY|O_NONBLOCK); | |
if (!fd == -1) | |
return 0; | |
m = 0; | |
while (m < INIT_SIZE && tries > 0) { | |
fd_set readfds; | |
FD_SET(fd, &readfds); | |
r = select(fd+1, &readfds, NULL, NULL, &tv); | |
if (FD_ISSET(fd, &readfds)) { | |
n = read(fd, seedbuf+m, INIT_SIZE-m); | |
if (n > 0) { | |
debug(stderr, "seeded %d from random\n", n); | |
m += n; | |
} | |
} | |
tries--; | |
} | |
close(fd); | |
if (m < INIT_SIZE) { | |
fd = open("/dev/urandom", O_RDONLY); | |
if (fd == -1) | |
return -1; | |
n = read(fd, seedbuf+m, INIT_SIZE-m); | |
if (n > 0) | |
m += n; | |
close(fd); | |
} | |
return m; | |
} | |
int random_init(prng *prng, uint32_t reseed) | |
{ | |
int m; | |
uint8_t seedbuf[INIT_SIZE]; | |
m = random_get_entropy(seedbuf, 10000); | |
debug(stderr, "M: %d\n", m); | |
memset(prng, 0, sizeof(*prng)); | |
prng->reseed = reseed; | |
return random_init_with(prng, seedbuf); | |
} | |
static int random_reseed(prng *prng) | |
{ | |
uint8_t seedbuf[INIT_SIZE]; | |
int m; | |
m = random_get_entropy(seedbuf, 2); | |
if (m >= (INIT_SIZE / 2)) { | |
random_init_with(prng, seedbuf); | |
} | |
return 0; | |
} | |
static void random_refill(prng *prng) | |
{ | |
uint64_t *buf64 = (uint64_t *) prng->buffer; | |
int i; | |
//debug(stderr, "refilling buf\n"); | |
if (!--prng->reseed_ctr) { | |
fprintf(stderr, "reseeding\n"); | |
random_reseed(prng); | |
prng->reseed_ctr = prng->reseed; | |
} | |
/* reinitialize a counter mode string in the buffer */ | |
fprintf(stderr, "next: %016lx %016lx\n", prng->next[0], prng->next[1]); | |
for (i = 0; i < CHUNK_SIZE / 16; i++) { | |
buf64[i*2] = prng->next[0]; | |
buf64[i*2+1] = prng->next[1]; | |
if (++prng->next[0] == 0) { | |
prng->next[1]++; | |
} | |
} | |
for (i = 0; i < CHUNK_SIZE / 16; i++) { | |
#ifdef USE_OPENSSL | |
AES_encrypt(prng->buffer + i*16, prng->buffer + i*16, &prng->key); | |
#endif | |
} | |
prng->bufleft = CHUNK_SIZE; | |
} | |
int random_getbytes(prng *prng, uint8_t *dst, int len) | |
{ | |
int filled = 0; | |
while (filled < len) { | |
int round; | |
if (prng->bufleft == 0) | |
random_refill(prng); | |
round = (len < prng->bufleft) ? len : prng->bufleft; | |
//debug(stderr, "filling buf %d %d\n", round, (CHUNK_SIZE - prng->bufleft)); | |
memcpy(dst + filled, prng->buffer + (CHUNK_SIZE - prng->bufleft), round); | |
prng->bufleft -= round; | |
filled += round; | |
} | |
return 0; | |
} | |
int random_deinit(prng *prng) | |
{ | |
memset(prng, 0, sizeof(*prng)); | |
memset(prng, 0xff, sizeof(*prng)); | |
return 0; | |
} | |
int main(int argc, char **argv) | |
{ | |
uint8_t buf[1024]; | |
prng prng; | |
int i; | |
random_init(&prng, DEFAULT_RESEED); | |
for (i = 0; i < 1024; i++) { | |
random_getbytes(&prng, buf, 432); | |
write(1, buf, 432); | |
} | |
random_deinit(&prng); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment