Skip to content

Instantly share code, notes, and snippets.

@vincenthz
Created October 10, 2012 07:43
Show Gist options
  • Save vincenthz/3863813 to your computer and use it in GitHub Desktop.
Save vincenthz/3863813 to your computer and use it in GitHub Desktop.
CPRNG with a block cipher in CTR mode.
/*
* 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