/*
- silcrng.c
+ silcrng.c
- Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+ Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 1997 - 2000 Pekka Riikonen
+ Copyright (C) 1997 - 2002 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
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
+ the Free Software Foundation; version 2 of the License.
+
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
/*
* Created: Sun Mar 9 00:09:18 1997
*
- * This RNG is based on Secure Shell's random number generator.
+ * 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.
+ * This RNG has been rewritten twice since the creation.
*/
-/* XXX: Some operations block resulting slow initialization.
- * XXX: I have some pending changes to make this better. */
#include "silcincludes.h"
+#ifndef WIN32
+#ifdef HAVE_GETSID
+extern pid_t getsid (pid_t __pid);
+#endif
+
+#ifdef HAVE_GETPGID
+extern pid_t getpgid (pid_t __pid);
+#endif
+#endif
+
#undef SILC_RNG_DEBUG
-/* #define SILC_RNG_DEBUG */
+/*#define SILC_RNG_DEBUG*/
+
+/* Number of states to fetch data from pool. */
+#define SILC_RNG_STATE_NUM 4
+
+/* Byte size of the random data pool. */
+#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_xor(SilcRng rng, SilcUInt32 val, unsigned int pos);
+static void silc_rng_exec_command(SilcRng rng, char *command);
+static void silc_rng_get_hard_noise(SilcRng rng);
+static void silc_rng_get_medium_noise(SilcRng rng);
+static void silc_rng_get_soft_noise(SilcRng rng);
/*
SILC SilcRng State context.
from the same point of the pool. Short description of the fields
following.
- unsigned int low
- unsigned int pos
+ SilcUInt32 low
+ SilcUInt32 pos
The index for the random pool buffer. Lowest and current
positions.
*/
typedef struct SilcRngStateContext {
- unsigned int low;
- unsigned int pos;
+ SilcUInt32 low;
+ SilcUInt32 pos;
struct SilcRngStateContext *next;
} *SilcRngState;
random pool. This is allocated when RNG object is allocated and
free'd when RNG object is free'd.
+ SilcUInt8 threshold
+
+ Threshold to indicate when it is required to acquire more
+ noise from the environment. More soft noise is acquired after
+ 64 bits of output and hard noise every 160 bits of output.
+
*/
-typedef struct SilcRngObjectStruct {
+struct SilcRngStruct {
unsigned char pool[SILC_RNG_POOLSIZE];
unsigned char key[64];
SilcRngState state;
SilcHash sha1;
-} SilcRngObject;
+ SilcUInt8 threshold;
+ char *devrandom;
+ int fd_devurandom;
+};
/* Allocates new RNG object. */
-SilcRng silc_rng_alloc()
+SilcRng silc_rng_alloc(void)
{
SilcRng new;
SILC_LOG_DEBUG(("Allocating new RNG object"));
new = silc_calloc(1, sizeof(*new));
+ new->fd_devurandom = -1;
memset(new->pool, 0, sizeof(new->pool));
memset(new->key, 0, sizeof(new->key));
new->state = NULL;
- silc_hash_alloc("sha1", &new->sha1);
+ if (!silc_hash_alloc("sha1", &new->sha1)) {
+ silc_free(new);
+ SILC_LOG_ERROR(("Could not allocate sha1 hash, probably not registered"));
+ return NULL;
+ }
+
+ new->devrandom = strdup("/dev/random");
return new;
}
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_free(rng->sha1);
+ silc_hash_free(rng->sha1);
+ silc_free(rng->devrandom);
+
+ 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);
}
}
silc_rng_get_soft_noise(rng);
silc_rng_get_medium_noise(rng);
silc_rng_get_hard_noise(rng);
+ silc_rng_get_soft_noise(rng);
+ silc_free(rng->devrandom);
+ rng->devrandom = strdup("/dev/urandom");
}
/* This function gets 'soft' noise from environment. */
-void silc_rng_get_soft_noise(SilcRng rng)
+static void silc_rng_get_soft_noise(SilcRng rng)
{
+#ifndef SILC_WIN32
struct tms ptime;
+#endif
+ SilcUInt32 pos;
+ pos = silc_rng_get_position(rng);
+
silc_rng_xor(rng, clock(), 0);
+#ifndef SILC_WIN32
+#ifdef HAVE_GETPID
silc_rng_xor(rng, getpid(), 1);
- silc_rng_xor(rng, getpgid(getpid() << 8), 2);
- silc_rng_xor(rng, getpgid(getpid() << 8), 3);
+#ifdef HAVE_GETPGID
+ silc_rng_xor(rng, getpgid(getpid()) << 8, 2);
+ silc_rng_xor(rng, getpgid(getpid()) << 8, 3);
+#endif
silc_rng_xor(rng, getgid(), 4);
+#endif
+#ifdef HAVE_GETPGRP
silc_rng_xor(rng, getpgrp(), 5);
- silc_rng_xor(rng, getsid(getpid() << 16), 6);
+#endif
+#ifdef HAVE_GETSID
+ silc_rng_xor(rng, getsid(getpid()) << 16, 6);
+#endif
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), 9);
- silc_rng_xor(rng, (ptime.tms_stime + ptime.tms_cutime), 10);
- silc_rng_xor(rng, (ptime.tms_utime + ptime.tms_stime), 11);
- silc_rng_xor(rng, (ptime.tms_cutime ^ ptime.tms_stime), 12);
- silc_rng_xor(rng, (ptime.tms_cutime ^ ptime.tms_cstime), 13);
- silc_rng_xor(rng, (ptime.tms_utime ^ ptime.tms_stime), 14);
- silc_rng_xor(rng, (ptime.tms_stime ^ ptime.tms_cutime), 15);
- silc_rng_xor(rng, (ptime.tms_cutime + ptime.tms_stime), 16);
- silc_rng_xor(rng, (ptime.tms_stime << 8), 17);
- silc_rng_xor(rng, clock() << 4, 18);
- silc_rng_xor(rng, getpgid(getpid() << 8), 19);
- silc_rng_xor(rng, getpgrp(), 20);
- silc_rng_xor(rng, getsid(getpid() << 16), 21);
- silc_rng_xor(rng, times(&ptime), 22);
- silc_rng_xor(rng, ptime.tms_utime, 23);
- silc_rng_xor(rng, getpgrp(), 24);
+ 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_utime + ptime.tms_stime), pos++);
+ silc_rng_xor(rng, (ptime.tms_cutime ^ ptime.tms_stime), pos++);
+ silc_rng_xor(rng, (ptime.tms_cutime ^ ptime.tms_cstime), pos++);
+ 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_rng_xor(rng, clock() << 4, pos++);
+#ifndef SILC_WIN32
+#ifdef HAVE_GETPGID
+ silc_rng_xor(rng, getpgid(getpid()) << 8, pos++);
+#endif
+#ifdef HAVE_GETPGRP
+ silc_rng_xor(rng, getpgrp(), pos++);
+#endif
+#ifdef HAVE_SETSID
+ silc_rng_xor(rng, getsid(getpid()) << 16, pos++);
+#endif
+ silc_rng_xor(rng, times(&ptime), pos++);
+ silc_rng_xor(rng, ptime.tms_utime, pos++);
+#ifdef HAVE_GETPGRP
+ silc_rng_xor(rng, getpgrp(), pos++);
+#endif
+#endif
+
+#ifdef SILC_RNG_DEBUG
+ SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
+#endif
/* Stir random pool */
silc_rng_stir_pool(rng);
/* This function gets noise from different commands */
-void silc_rng_get_medium_noise(SilcRng rng)
+static void silc_rng_get_medium_noise(SilcRng rng)
{
- silc_rng_exec_command(rng, "ps -lefaww 2> /dev/null");
- silc_rng_exec_command(rng, "ls -afiln 2> /dev/null");
- silc_rng_exec_command(rng, "ps -asww 2> /dev/null");
+ 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");
- /*
- silc_rng_exec_command(rng, "ps -ef 2> /dev/null");
- silc_rng_exec_command(rng, "ls -alin /dev 2> /dev/null");
- */
+ silc_rng_exec_command(rng, "ps -axww 2> /dev/null");
+
+#ifdef SILC_RNG_DEBUG
+ SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
+#endif
}
/* This function gets 'hard' noise from environment. This tries to
get the noise from /dev/random if available. */
-void silc_rng_get_hard_noise(SilcRng rng)
+static void silc_rng_get_hard_noise(SilcRng rng)
{
- char buf[32];
+#ifndef SILC_WIN32
+ unsigned char buf[32];
int fd, len, i;
- /* Get noise from /dev/random if available */
- fd = open("/dev/random", O_RDONLY);
+ /* Get noise from /dev/[u]random if available */
+ fd = open(rng->devrandom, O_RDONLY);
if (fd < 0)
return;
fcntl(fd, F_SETFL, O_NONBLOCK);
- for (i = 0; i < 8; i++) {
+ for (i = 0; i < 2; i++) {
len = read(fd, buf, sizeof(buf));
if (len <= 0)
goto out;
silc_rng_add_noise(rng, buf, len);
}
+#ifdef SILC_RNG_DEBUG
+ SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
+#endif
+
out:
close(fd);
memset(buf, 0, sizeof(buf));
+#endif
}
/* Execs command and gets noise from its output */
-void silc_rng_exec_command(SilcRng rng, char *command)
+static void silc_rng_exec_command(SilcRng rng, char *command)
{
- char buf[2048];
+#ifndef SILC_WIN32
+ unsigned char buf[1024];
FILE *fd;
int i;
int c;
/* 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;
+ if (c == EOF)
break;
- }
buf[i] = c;
}
pclose(fd);
- /* Add the buffer into random pool */
- silc_rng_add_noise(rng, buf, strlen(buf));
- 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
pool. After adding the noise the pool is stirred. */
-void silc_rng_add_noise(SilcRng rng, unsigned char *buffer,
- unsigned int len)
+void silc_rng_add_noise(SilcRng rng, unsigned char *buffer, SilcUInt32 len)
{
- unsigned int i, pos;
+ SilcUInt32 i, pos;
pos = silc_rng_get_position(rng);
/* XOR's data into the pool */
-void silc_rng_xor(SilcRng rng, unsigned int val, unsigned int pos)
+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
(cipher feedback) mode with SHA1 algorithm. */
-void silc_rng_stir_pool(SilcRng rng)
+static void silc_rng_stir_pool(SilcRng rng)
{
int i;
- unsigned long iv[5];
+ SilcUInt32 iv[5], tmp;
/* Get the IV */
- memcpy(iv, &rng->pool[SILC_RNG_POOLSIZE - 256], 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) {
- rng->sha1->hash->transform(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];
+ for (i = 0; i < SILC_RNG_POOLSIZE; i += 20) {
+ silc_hash_transform(rng->sha1, iv, rng->key);
+
+ 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) {
- rng->sha1->hash->transform(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];
+ for (i = 0; i < SILC_RNG_POOLSIZE; i += 20) {
+ silc_hash_transform(rng->sha1, iv, rng->key);
+
+ 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));
/* Returns next position where data is fetched from the pool or
put to the pool. */
-unsigned int silc_rng_get_position(SilcRng rng)
+static SilcUInt32 silc_rng_get_position(SilcRng rng)
{
SilcRngState next;
- unsigned int pos;
+ SilcUInt32 pos;
next = rng->state->next;
rng->state->pos = rng->state->low;
#ifdef SILC_RNG_DEBUG
- fprintf(stderr, "state: %p: low: %d, pos: %d\n",
+ fprintf(stderr, "state: %p: low: %lu, pos: %lu\n",
rng->state, rng->state->low, rng->state->pos);
#endif
return pos;
}
-/* returns random byte. Every two byte is from pools low or high state. */
+/* Returns random byte. */
-unsigned char silc_rng_get_byte(SilcRng rng)
+SilcUInt8 silc_rng_get_byte(SilcRng rng)
{
+ rng->threshold++;
+
+ /* Get more soft noise after 64 bits threshold */
+ if (rng->threshold >= 8)
+ silc_rng_get_soft_noise(rng);
+
+ /* Get hard noise after 160 bits threshold, zero the threshold. */
+ if (rng->threshold >= 20) {
+ rng->threshold = 0;
+ silc_rng_get_hard_noise(rng);
+ }
+
return rng->pool[silc_rng_get_position(rng)];
}
+/* Return random byte as fast as possible. Reads from /dev/urandom if
+ available. If not then return from normal RNG (not so fast). */
+
+SilcUInt8 silc_rng_get_byte_fast(SilcRng rng)
+{
+#ifndef SILC_WIN32
+ unsigned char buf[1];
+
+ if (rng->fd_devurandom == -1) {
+ rng->fd_devurandom = open("/dev/urandom", O_RDONLY);
+ 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];
+#else
+ return silc_rng_get_byte(rng);
+#endif
+}
+
/* Returns 16 bit random number */
-unsigned short silc_rng_get_rn16(SilcRng rng)
+SilcUInt16 silc_rng_get_rn16(SilcRng rng)
{
unsigned char rn[2];
- unsigned short num;
+ SilcUInt16 num;
rn[0] = silc_rng_get_byte(rng);
rn[1] = silc_rng_get_byte(rng);
/* Returns 32 bit random number */
-unsigned int silc_rng_get_rn32(SilcRng rng)
+SilcUInt32 silc_rng_get_rn32(SilcRng rng)
{
unsigned char rn[4];
- unsigned short num;
+ SilcUInt32 num;
rn[0] = silc_rng_get_byte(rng);
rn[1] = silc_rng_get_byte(rng);
/* Returns random number string. Returned string is in HEX format. */
-unsigned char *silc_rng_get_rn_string(SilcRng rng, unsigned int len)
+unsigned char *silc_rng_get_rn_string(SilcRng rng, SilcUInt32 len)
{
int i;
unsigned char *string;
/* Returns random number binary data. */
-unsigned char *silc_rng_get_rn_data(SilcRng rng, unsigned int len)
+unsigned char *silc_rng_get_rn_data(SilcRng rng, SilcUInt32 len)
{
int i;
unsigned char *data;
return data;
}
+
+/* Global RNG. This is global RNG that application can initialize so
+ that any part of code anywhere can use RNG without having to allocate
+ new RNG object everytime. If this is not initialized then these routines
+ will fail. Note: currently in SILC applications always initialize this. */
+
+SilcRng global_rng = NULL;
+
+/* 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)
+{
+ if (rng)
+ global_rng = rng;
+ else
+ global_rng = silc_rng_alloc();
+
+ return TRUE;
+}
+
+/* Uninitialize global RNG */
+
+bool silc_rng_global_uninit(void)
+{
+ if (global_rng) {
+ silc_rng_free(global_rng);
+ global_rng = NULL;
+ }
+
+ return TRUE;
+}
+
+/* These are analogous to the functions above. */
+
+SilcUInt8 silc_rng_global_get_byte(void)
+{
+ return global_rng ? silc_rng_get_byte(global_rng) : 0;
+}
+
+/* Return random byte as fast as possible. Reads from /dev/urandom if
+ available. If not then return from normal RNG (not so fast). */
+
+SilcUInt8 silc_rng_global_get_byte_fast(void)
+{
+ return global_rng ? silc_rng_get_byte_fast(global_rng) : 0;
+}
+
+SilcUInt16 silc_rng_global_get_rn16(void)
+{
+ return global_rng ? silc_rng_get_rn16(global_rng) : 0;
+}
+
+SilcUInt32 silc_rng_global_get_rn32(void)
+{
+ return global_rng ? silc_rng_get_rn32(global_rng) : 0;
+}
+
+unsigned char *silc_rng_global_get_rn_string(SilcUInt32 len)
+{
+ return global_rng ? silc_rng_get_rn_string(global_rng, len) : NULL;
+}
+
+unsigned char *silc_rng_global_get_rn_data(SilcUInt32 len)
+{
+ return global_rng ? silc_rng_get_rn_data(global_rng, len) : NULL;
+}
+
+void silc_rng_global_add_noise(unsigned char *buffer, SilcUInt32 len)
+{
+ if (global_rng)
+ silc_rng_add_noise(global_rng, buffer, len);
+}