/*****************************************************************************\
 *  $Id: genrand.c 68 2006-02-14 21:54:16Z garlick $
 *****************************************************************************
 *  Copyright (C) 2001-2005 The Regents of the University of California.
 *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
 *  Written by Jim Garlick <garlick@llnl.gov>.
 *  UCRL-CODE-2003-006.
 *  
 *  This file is part of Scrub, a program for erasing disks.
 *  For details, see <http://www.llnl.gov/linux/scrub/>.
 *  
 *  Scrub is free software; you can redistribute it and/or modify it under
 *  the terms of the GNU General Public License as published by the Free
 *  Software Foundation; either version 2 of the License, or (at your option)
 *  any later version.
 *  
 *  Scrub is distributed in the hope that it will be useful, but WITHOUT ANY
 *  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 *  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 *  details.
 *  
 *  You should have received a copy of the GNU General Public License along
 *  with Scrub; if not, write to the Free Software Foundation, Inc.,
 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
\*****************************************************************************/

#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <sys/time.h>
#include <assert.h>
#include <libgen.h>

#include "aes.h"
#include "util.h"
#include "genrand.h"

#define PATH_URANDOM    "/dev/urandom"

#define PAYLOAD_SZ  16
#define KEY_SZ      16

#ifdef STAND
static char *prog;
#else
extern char *prog;
#endif

static aes_context  ctx;
static unsigned char ctr[PAYLOAD_SZ];

/* Increment 128 bit counter.
 * NOTE: we are not concerned with endianness here since the counter is
 * just sixteen bytes of payload to AES and outside of this function isn't 
 * operated upon numerically.
 */
static void
incr128(unsigned char *val)
{
    unsigned long long *t = (unsigned long long *)val;

    assert(sizeof(unsigned long long) == 8);
    assert(PAYLOAD_SZ == 16);
    if (++t[0] == 0)
        ++t[1];
}

/* Copy 'buflen' bytes of raw randomness into 'buf'.
 */
static void 
genrandraw(unsigned char *buf, int buflen)
{
    int fd, n;

    if ((fd = open(PATH_URANDOM, O_RDONLY)) >= 0) {
        n = read_all(fd, buf, buflen);
        if (n < 0) {
            fprintf(stderr, "%s: open ", prog);
            perror(PATH_URANDOM);
            exit(1);
        }
        if (n == 0) {
            fprintf(stderr, "%s: premature EOF on %s\n", prog, PATH_URANDOM);
            exit(1);
        }
        (void)close(fd);
    } else {
        for (n = 0; n < buflen; n++)
            buf[n] = rand();
    }
}

/* Pick new (random) key and counter values.
 */
void 
churnrand(void)
{
    unsigned char key[KEY_SZ];

    genrandraw(ctr, PAYLOAD_SZ);
    genrandraw(key, KEY_SZ);
    if (aes_set_key(&ctx, key, KEY_SZ*8) != 0) {
        fprintf(stderr, "%s: aes_set_key error\n", prog);
        exit(1);
    }
}

/* Initialize the module.
 */
void 
initrand(void)
{
    struct timeval tv;

    if (access(PATH_URANDOM, R_OK) < 0) {
        if (gettimeofday(&tv, NULL) < 0) {
            fprintf(stderr, "%s: gettimeofday", prog);
            perror("");
            exit(1);
        }
        srand(tv.tv_usec);
    }
    churnrand();
}

/* Fill buf with random data.
 */
void 
genrand(unsigned char *buf, int buflen)
{
    int i;
    unsigned char out[PAYLOAD_SZ];
    int cpylen = PAYLOAD_SZ;

    for (i = 0; i < buflen; i += cpylen) {
        aes_encrypt(&ctx, ctr, out);
        incr128(ctr);
        if (cpylen > buflen - i)
            cpylen = buflen - i;
        memcpy(&buf[i], out, cpylen);
    }
    assert(i == buflen);
}

#ifdef STAND
int main(int argc, char *argv[])
{
    unsigned char buf[24];
    int i, j;

    prog = basename(argv[0]);

    initrand();
    for (j = 0; j < 20; j++) {
        genrand(buf, 24);
        for (i = 0; i < 24; i++)
            printf("%-.2hhx", buf[i]);
        printf("\n");
    }
    exit(0);
}
#endif

/*
 * vi:tabstop=4 shiftwidth=4 expandtab
 */


syntax highlighted by Code2HTML, v. 0.9.1