/*
- silcrng.c
+ silcrng.c
Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 1997 - 2002 Pekka Riikonen
+ Copyright (C) 1997 - 2003 Pekka Riikonen
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
* Created: Sun Mar 9 00:09:18 1997
*
* The original RNG was based on Secure Shell's random number generator
- * by Tatu Ylönen and was used as reference when programming this RNG.
+ * by Tatu Ylönen and was used as reference when programming this RNG.
* This RNG has been rewritten twice since the creation.
*/
-#include "silcincludes.h"
+#include "silc.h"
#ifndef WIN32
#ifdef HAVE_GETSID
#define SILC_RNG_STATE_NUM 4
/* Byte size of the random data pool. */
-#define SILC_RNG_POOLSIZE 1024
+#define SILC_RNG_POOLSIZE (20 * 48)
static SilcUInt32 silc_rng_get_position(SilcRng rng);
static void silc_rng_stir_pool(SilcRng rng);
static void silc_rng_get_medium_noise(SilcRng rng);
static void silc_rng_get_soft_noise(SilcRng rng);
-/*
+/*
SILC SilcRng State context.
This object is used by the random number generator to provide
struct SilcRngStateContext *next;
} *SilcRngState;
-/*
- SILC Random Number Generator object.
+/*
+ SILC Random Number Generator object.
This object holds random pool which is used to generate the random
numbers used by various routines needing cryptographically strong
void silc_rng_free(SilcRng rng)
{
if (rng) {
+ SilcRngState t, n;
+
memset(rng->pool, 0, sizeof(rng->pool));
memset(rng->key, 0, sizeof(rng->key));
silc_hash_free(rng->sha1);
if (rng->fd_devurandom != -1)
close(rng->fd_devurandom);
+ for (t = rng->state->next; t != rng->state; ) {
+ n = t->next;
+ silc_free(t);
+ t = n;
+ }
+ silc_free(rng->state);
+
silc_free(rng);
}
}
-/* Initializes random number generator by getting noise from environment.
+/* Initializes random number generator by getting noise from environment.
The environmental noise is our so called seed. One should not call
this function more than once. */
first = rng->state;
for (i = SILC_RNG_STATE_NUM - 1; i >= 1; i--) {
next = silc_calloc(1, sizeof(*rng->state));
- next->low =
+ next->low =
(i * (sizeof(rng->pool) / SILC_RNG_STATE_NUM));
next->pos =
(i * (sizeof(rng->pool) / SILC_RNG_STATE_NUM)) + 8;
struct tms ptime;
#endif
SilcUInt32 pos;
-
+#ifdef HAVE_GETRUSAGE
+ struct rusage r;
+#endif
+
pos = silc_rng_get_position(rng);
silc_rng_xor(rng, clock(), 0);
silc_rng_xor(rng, getpgid(getpid()) << 8, 2);
silc_rng_xor(rng, getpgid(getpid()) << 8, 3);
#endif
+#ifdef HAVE_GETGID
silc_rng_xor(rng, getgid(), 4);
#endif
+#endif
#ifdef HAVE_GETPGRP
silc_rng_xor(rng, getpgrp(), 5);
#endif
#ifdef HAVE_GETSID
silc_rng_xor(rng, getsid(getpid()) << 16, 6);
#endif
+#ifndef SILC_SYMBIAN
silc_rng_xor(rng, times(&ptime), 7);
silc_rng_xor(rng, ptime.tms_utime, 8);
silc_rng_xor(rng, (ptime.tms_utime + ptime.tms_stime), pos++);
silc_rng_xor(rng, (ptime.tms_stime ^ ptime.tms_cutime), pos++);
silc_rng_xor(rng, (ptime.tms_cutime + ptime.tms_stime), pos++);
silc_rng_xor(rng, (ptime.tms_stime << 8), pos++);
+#endif /* SILC_SYMBIAN */
#endif
silc_rng_xor(rng, clock() << 4, pos++);
#ifndef SILC_WIN32
#ifdef HAVE_SETSID
silc_rng_xor(rng, getsid(getpid()) << 16, pos++);
#endif
+#ifndef SILC_SYMBIAN
silc_rng_xor(rng, times(&ptime), pos++);
silc_rng_xor(rng, ptime.tms_utime, pos++);
+#endif /* SILC_SYMBIAN */
#ifdef HAVE_GETPGRP
silc_rng_xor(rng, getpgrp(), pos++);
#endif
#endif
-
+#ifdef HAVE_GETRUSAGE
+ getrusage(RUSAGE_SELF, &r);
+ silc_rng_xor(rng, (r.ru_utime.tv_sec + r.ru_utime.tv_usec), pos++);
+ silc_rng_xor(rng, (r.ru_utime.tv_sec ^ r.ru_utime.tv_usec), pos++);
+ silc_rng_xor(rng, (r.ru_stime.tv_sec + r.ru_stime.tv_usec), pos++);
+ silc_rng_xor(rng, (r.ru_stime.tv_sec ^ r.ru_stime.tv_usec), pos++);
+#ifndef SILC_SYMBIAN
+ silc_rng_xor(rng, (r.ru_maxrss + r.ru_ixrss), pos++);
+ silc_rng_xor(rng, (r.ru_maxrss ^ r.ru_ixrss), pos++);
+ silc_rng_xor(rng, (r.ru_idrss + r.ru_idrss), pos++);
+ silc_rng_xor(rng, (r.ru_idrss ^ r.ru_idrss), pos++);
+ silc_rng_xor(rng, (r.ru_idrss << 16), pos++);
+ silc_rng_xor(rng, (r.ru_minflt + r.ru_majflt), pos++);
+ silc_rng_xor(rng, (r.ru_minflt ^ r.ru_majflt), pos++);
+ silc_rng_xor(rng, (r.ru_nswap + r.ru_oublock + r.ru_inblock), pos++);
+ silc_rng_xor(rng, (r.ru_nswap << 8), pos++);
+ silc_rng_xor(rng, (r.ru_inblock + r.ru_oublock), pos++);
+ silc_rng_xor(rng, (r.ru_inblock ^ r.ru_oublock), pos++);
+ silc_rng_xor(rng, (r.ru_msgsnd ^ r.ru_msgrcv), pos++);
+ silc_rng_xor(rng, (r.ru_nsignals + r.ru_msgsnd + r.ru_msgrcv), pos++);
+ silc_rng_xor(rng, (r.ru_nsignals << 16), pos++);
+ silc_rng_xor(rng, (r.ru_nvcsw + r.ru_nivcsw), pos++);
+ silc_rng_xor(rng, (r.ru_nvcsw ^ r.ru_nivcsw), pos++);
+#endif /* SILC_SYMBIAN */
+#endif /* HAVE_GETRUSAGE */
+
#ifdef SILC_RNG_DEBUG
SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
#endif
static void silc_rng_get_medium_noise(SilcRng rng)
{
+ /* If getrusage is available, there is no need for shell commands */
+#ifdef HAVE_GETRUSAGE
+ return;
+#endif
silc_rng_exec_command(rng, "ps -leaww 2> /dev/null");
silc_rng_exec_command(rng, "ls -afiln ~ 2> /dev/null");
silc_rng_exec_command(rng, "ls -afiln /proc 2> /dev/null");
static void silc_rng_get_hard_noise(SilcRng rng)
{
-#ifndef SILC_WIN32
+#if defined(SILC_UNIX)
unsigned char buf[32];
int fd, len, i;
-
+
/* Get noise from /dev/[u]random if available */
fd = open(rng->devrandom, O_RDONLY);
if (fd < 0)
static void silc_rng_exec_command(SilcRng rng, char *command)
{
-#ifndef SILC_WIN32
+#if defined(SILC_UNIX)
unsigned char buf[1024];
FILE *fd;
int i;
int c;
-
+
/* Open process */
fd = popen(command, "r");
if (!fd)
return;
-
+
/* Get data as much as we can get into the buffer */
for (i = 0; i < sizeof(buf); i++) {
c = fgetc(fd);
- if (c == EOF) {
- if (!i)
- return;
- break;
- }
+ if (c == EOF)
+ break;
buf[i] = c;
}
-
+
pclose(fd);
-
- /* Add the buffer into random pool */
- silc_rng_add_noise(rng, buf, i);
- memset(buf, 0, sizeof(buf));
+
+ if (i != 0) {
+ /* Add the buffer into random pool */
+ silc_rng_add_noise(rng, buf, i);
+ memset(buf, 0, sizeof(buf));
+ }
#endif
}
-/* This function adds the contents of the buffer as noise into random
+/* This function adds the contents of the buffer as noise into random
pool. After adding the noise the pool is stirred. */
void silc_rng_add_noise(SilcRng rng, unsigned char *buffer, SilcUInt32 len)
static void silc_rng_xor(SilcRng rng, SilcUInt32 val, unsigned int pos)
{
- assert(rng != NULL);
- rng->pool[pos] ^= val + val;
+ SilcUInt32 tmp;
+
+ SILC_GET32_MSB(tmp, &rng->pool[pos]);
+ val ^= tmp + val;
+ SILC_PUT32_MSB(val, &rng->pool[pos]);
}
-/* This function stirs the random pool by encrypting buffer in CFB
+/* This function stirs the random pool by encrypting buffer in CFB
(cipher feedback) mode with SHA1 algorithm. */
static void silc_rng_stir_pool(SilcRng rng)
{
int i;
- SilcUInt32 iv[5];
+ SilcUInt32 iv[5], tmp;
/* Get the IV */
- memcpy(iv, &rng->pool[16], sizeof(iv));
+ SILC_GET32_MSB(iv[0], &rng->pool[16 ]);
+ SILC_GET32_MSB(iv[1], &rng->pool[16 + 4]);
+ SILC_GET32_MSB(iv[2], &rng->pool[16 + 8]);
+ SILC_GET32_MSB(iv[3], &rng->pool[16 + 12]);
+ SILC_GET32_MSB(iv[4], &rng->pool[16 + 16]);
/* First CFB pass */
- for (i = 0; i < SILC_RNG_POOLSIZE; i += 5) {
+ for (i = 0; i < SILC_RNG_POOLSIZE; i += 20) {
silc_hash_transform(rng->sha1, iv, rng->key);
- iv[0] = rng->pool[i] ^= iv[0];
- iv[1] = rng->pool[i + 1] ^= iv[1];
- iv[2] = rng->pool[i + 2] ^= iv[2];
- iv[3] = rng->pool[i + 3] ^= iv[3];
- iv[4] = rng->pool[i + 4] ^= iv[4];
+
+ SILC_GET32_MSB(tmp, &rng->pool[i]);
+ iv[0] ^= tmp;
+ SILC_PUT32_MSB(iv[0], &rng->pool[i]);
+
+ SILC_GET32_MSB(tmp, &rng->pool[i + 4]);
+ iv[1] ^= tmp;
+ SILC_PUT32_MSB(iv[1], &rng->pool[i + 4]);
+
+ SILC_GET32_MSB(tmp, &rng->pool[i + 8]);
+ iv[2] ^= tmp;
+ SILC_PUT32_MSB(iv[2], &rng->pool[i + 8]);
+
+ SILC_GET32_MSB(tmp, &rng->pool[i + 12]);
+ iv[3] ^= tmp;
+ SILC_PUT32_MSB(iv[3], &rng->pool[i + 12]);
+
+ SILC_GET32_MSB(tmp, &rng->pool[i + 16]);
+ iv[4] ^= tmp;
+ SILC_PUT32_MSB(iv[4], &rng->pool[i + 16]);
}
/* Get new key */
memcpy(rng->key, &rng->pool[silc_rng_get_position(rng)], sizeof(rng->key));
/* Second CFB pass */
- for (i = 0; i < SILC_RNG_POOLSIZE; i += 5) {
+ for (i = 0; i < SILC_RNG_POOLSIZE; i += 20) {
silc_hash_transform(rng->sha1, iv, rng->key);
- iv[0] = rng->pool[i] ^= iv[0];
- iv[1] = rng->pool[i + 1] ^= iv[1];
- iv[2] = rng->pool[i + 2] ^= iv[2];
- iv[3] = rng->pool[i + 3] ^= iv[3];
- iv[4] = rng->pool[i + 4] ^= iv[4];
+
+ SILC_GET32_MSB(tmp, &rng->pool[i]);
+ iv[0] ^= tmp;
+ SILC_PUT32_MSB(iv[0], &rng->pool[i]);
+
+ SILC_GET32_MSB(tmp, &rng->pool[i + 4]);
+ iv[1] ^= tmp;
+ SILC_PUT32_MSB(iv[1], &rng->pool[i + 4]);
+
+ SILC_GET32_MSB(tmp, &rng->pool[i + 8]);
+ iv[2] ^= tmp;
+ SILC_PUT32_MSB(iv[2], &rng->pool[i + 8]);
+
+ SILC_GET32_MSB(tmp, &rng->pool[i + 12]);
+ iv[3] ^= tmp;
+ SILC_PUT32_MSB(iv[3], &rng->pool[i + 12]);
+
+ SILC_GET32_MSB(tmp, &rng->pool[i + 16]);
+ iv[4] ^= tmp;
+ SILC_PUT32_MSB(iv[4], &rng->pool[i + 16]);
}
memset(iv, 0, sizeof(iv));
rng->state->pos = rng->state->low;
#ifdef SILC_RNG_DEBUG
- fprintf(stderr, "state: %p: low: %lu, pos: %lu\n",
+ fprintf(stderr, "state: %p: low: %lu, pos: %lu\n",
rng->state, rng->state->low, rng->state->pos);
#endif
SilcUInt8 silc_rng_get_byte(SilcRng rng)
{
+ SilcUInt8 byte;
+
rng->threshold++;
/* Get more soft noise after 64 bits threshold */
silc_rng_get_hard_noise(rng);
}
- return rng->pool[silc_rng_get_position(rng)];
+ do byte = rng->pool[silc_rng_get_position(rng)]; while (byte == 0x00);
+ return byte;
}
/* Return random byte as fast as possible. Reads from /dev/urandom if
SilcUInt8 silc_rng_get_byte_fast(SilcRng rng)
{
-#ifndef SILC_WIN32
+#if defined(SILC_UNIX)
unsigned char buf[1];
if (rng->fd_devurandom == -1) {
rng->fd_devurandom = open("/dev/urandom", O_RDONLY);
- if (rng < 0)
+ if (rng->fd_devurandom < 0)
return silc_rng_get_byte(rng);
fcntl(rng->fd_devurandom, F_SETFL, O_NONBLOCK);
}
if (read(rng->fd_devurandom, buf, sizeof(buf)) < 0)
return silc_rng_get_byte(rng);
- return buf[0];
+ return buf[0] != 0x00 ? buf[0] : silc_rng_get_byte(rng);
#else
return silc_rng_get_byte(rng);
#endif
SilcUInt32 silc_rng_get_rn32(SilcRng rng)
{
unsigned char rn[4];
- SilcUInt16 num;
+ SilcUInt32 num;
rn[0] = silc_rng_get_byte(rng);
rn[1] = silc_rng_get_byte(rng);
return num;
}
-/* Returns random number string. Returned string is in HEX format. */
+/* Returns non-zero random number string. Returned string is in HEX format. */
unsigned char *silc_rng_get_rn_string(SilcRng rng, SilcUInt32 len)
{
return string;
}
-/* Returns random number binary data. */
+/* Returns non-zero random number binary data. */
unsigned char *silc_rng_get_rn_data(SilcRng rng, SilcUInt32 len)
{
/* Initialize global RNG. If `rng' is provided it is set as the global
RNG object (it can be allocated by the application for example). */
-bool silc_rng_global_init(SilcRng rng)
+SilcBool silc_rng_global_init(SilcRng rng)
{
- if (rng)
+ if (rng) {
global_rng = rng;
- else
- global_rng = silc_rng_alloc();
+ return TRUE;
+ }
+
+ global_rng = silc_rng_alloc();
+ silc_rng_init(global_rng);
return TRUE;
}
/* Uninitialize global RNG */
-bool silc_rng_global_uninit(void)
+SilcBool silc_rng_global_uninit(void)
{
if (global_rng) {
silc_rng_free(global_rng);