/* random.c: random huge generator for Linux and other unix's
   This has nothing to do with cryptography.
   Copyright (C) 1998 Paul Sheer

   This program 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.

   This program 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 this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "mostincludes.h"
#include "huge-number.h"
#include "random.h"
#include "md5.h"
#include "diffie/compat.h"

#define INTS_RANDOM_SOURCE	(8)
#define CHARS_RANDOM_SOURCE	(INTS_RANDOM_SOURCE * (sizeof (unsigned int)))
#define CHARS_MD5_HASH		(128 / 8)

#ifdef __linux__

/* returns at least n chars. result must be free'd */
char *get_random_chars (int n)
{
    char *s, *t;
    int f;
    f = open ("/dev/urandom", O_RDONLY);
    if (f <= 0) {
	fprintf (stderr, "random.c: cannot open /dev/urandom: use mknod to create this\ndevice. See /usr/src/linux/drivers/char/random.c\n");
	abort ();
    }
    t = s = malloc (n);
    while (n) {
	int count;
	count = read (f, t, n);
	if (count < 0 && errno == EINTR)
	    continue;
	if (count <= 0) {
	    fprintf (stderr, "random.c: error reading /dev/urandom\n");
	    abort ();
	}
	n -= count;
	t += count;
    }
    close (f);
    return s;
}

#else

/* returns at least n chars. result must be free'd */
char *get_random_chars (int n)
{
/* Heres a made-up way to get a random sequence. I think its unsafe personally */
    char *r, *s, *t;
    struct stat st;
    unsigned int x[INTS_RANDOM_SOURCE];
    int i;
    x[0] = time (0);
    x[1] = getpid ();
    x[2] = getppid ();
    stat ("/tmp", &st);
    x[3] = st.st_atime;
    x[4] = st.st_mtime;
    stat ("/etc", &st);
    x[5] = st.st_atime;
    x[6] = st.st_mtime;
    x[7] = clock ();
    s = malloc (n + CHARS_MD5_HASH);
    t = md5_hash ((const char *) x, CHARS_RANDOM_SOURCE);
    memcpy (s, t, CHARS_MD5_HASH);
    for (i = 0; i < n; i += CHARS_MD5_HASH) {
	r = t;
	t = md5_hash (r, CHARS_MD5_HASH);
	free (r);
	memcpy (s + i, t, CHARS_MD5_HASH);
    }
    free (t);
    return s;
}

#endif

/* size must be a multiple of 8 bits */
Huge *get_random_huge (int size)
{
    char *r;
    Huge *z;
    size /= 8;
    r = get_random_chars (size);
    z = huge_from_binary ((unsigned char *) r, size);
    free (r);
    return z;
}

