X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilccrypt%2Fsilcrng.c;h=06340066165b94cb1dfd6371e88750a2b1d13ff0;hb=a818c5b5411bbc4436d1c5f011236985c96bb787;hp=5f0230ccc167de1edf0f52f29872a76504df8abe;hpb=72cfa31520ebc0058763a30e21c13f6e9a964aa0;p=silc.git diff --git a/lib/silccrypt/silcrng.c b/lib/silccrypt/silcrng.c index 5f0230cc..06340066 100644 --- a/lib/silccrypt/silcrng.c +++ b/lib/silccrypt/silcrng.c @@ -1,16 +1,15 @@ /* - silcrng.c + silcrng.c - Author: Pekka Riikonen + Author: Pekka Riikonen - 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 @@ -21,21 +20,35 @@ /* * 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 -static uint32 silc_rng_get_position(SilcRng rng); +/* Byte size of the random data pool. */ +#define SILC_RNG_POOLSIZE 1024 + +static SilcUInt32 silc_rng_get_position(SilcRng rng); static void silc_rng_stir_pool(SilcRng rng); -static void silc_rng_xor(SilcRng rng, uint32 val, unsigned int pos); -static void silc_rng_add_noise(SilcRng rng, unsigned char *buffer, - uint32 len); +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); @@ -50,8 +63,8 @@ static void silc_rng_get_soft_noise(SilcRng rng); from the same point of the pool. Short description of the fields following. - uint32 low - uint32 pos + SilcUInt32 low + SilcUInt32 pos The index for the random pool buffer. Lowest and current positions. @@ -63,8 +76,8 @@ static void silc_rng_get_soft_noise(SilcRng rng); */ typedef struct SilcRngStateContext { - uint32 low; - uint32 pos; + SilcUInt32 low; + SilcUInt32 pos; struct SilcRngStateContext *next; } *SilcRngState; @@ -98,28 +111,44 @@ typedef struct SilcRngStateContext { 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; } @@ -131,7 +160,12 @@ void silc_rng_free(SilcRng rng) if (rng) { 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); + silc_free(rng); } } @@ -173,39 +207,71 @@ void silc_rng_init(SilcRng 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. */ 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); @@ -215,14 +281,14 @@ static void silc_rng_get_soft_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 @@ -230,33 +296,40 @@ static void silc_rng_get_medium_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 */ 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; @@ -280,17 +353,17 @@ static void silc_rng_exec_command(SilcRng rng, char *command) pclose(fd); /* Add the buffer into random pool */ - silc_rng_add_noise(rng, buf, strlen(buf)); + 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. */ -static void silc_rng_add_noise(SilcRng rng, unsigned char *buffer, - uint32 len) +void silc_rng_add_noise(SilcRng rng, unsigned char *buffer, SilcUInt32 len) { - uint32 i, pos; + SilcUInt32 i, pos; pos = silc_rng_get_position(rng); @@ -307,7 +380,7 @@ static void silc_rng_add_noise(SilcRng rng, unsigned char *buffer, /* XOR's data into the pool */ -static void silc_rng_xor(SilcRng rng, uint32 val, unsigned int pos) +static void silc_rng_xor(SilcRng rng, SilcUInt32 val, unsigned int pos) { assert(rng != NULL); rng->pool[pos] ^= val + val; @@ -319,10 +392,10 @@ static void silc_rng_xor(SilcRng rng, uint32 val, unsigned int pos) static void silc_rng_stir_pool(SilcRng rng) { int i; - uint32 iv[5]; + SilcUInt32 iv[5]; /* Get the IV */ - memcpy(iv, &rng->pool[SILC_RNG_POOLSIZE - 256], sizeof(iv)); + memcpy(iv, &rng->pool[16], sizeof(iv)); /* First CFB pass */ for (i = 0; i < SILC_RNG_POOLSIZE; i += 5) { @@ -353,10 +426,10 @@ static void silc_rng_stir_pool(SilcRng rng) /* Returns next position where data is fetched from the pool or put to the pool. */ -static uint32 silc_rng_get_position(SilcRng rng) +static SilcUInt32 silc_rng_get_position(SilcRng rng) { SilcRngState next; - uint32 pos; + SilcUInt32 pos; next = rng->state->next; @@ -365,7 +438,7 @@ static uint32 silc_rng_get_position(SilcRng rng) 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 @@ -374,19 +447,31 @@ static uint32 silc_rng_get_position(SilcRng rng) 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)]; } /* Returns 16 bit random number */ -uint16 silc_rng_get_rn16(SilcRng rng) +SilcUInt16 silc_rng_get_rn16(SilcRng rng) { unsigned char rn[2]; - uint16 num; + SilcUInt16 num; rn[0] = silc_rng_get_byte(rng); rn[1] = silc_rng_get_byte(rng); @@ -397,10 +482,10 @@ uint16 silc_rng_get_rn16(SilcRng rng) /* Returns 32 bit random number */ -uint32 silc_rng_get_rn32(SilcRng rng) +SilcUInt32 silc_rng_get_rn32(SilcRng rng) { unsigned char rn[4]; - uint16 num; + SilcUInt16 num; rn[0] = silc_rng_get_byte(rng); rn[1] = silc_rng_get_byte(rng); @@ -413,7 +498,7 @@ uint32 silc_rng_get_rn32(SilcRng rng) /* Returns random number string. Returned string is in HEX format. */ -unsigned char *silc_rng_get_rn_string(SilcRng rng, uint32 len) +unsigned char *silc_rng_get_rn_string(SilcRng rng, SilcUInt32 len) { int i; unsigned char *string; @@ -428,7 +513,7 @@ unsigned char *silc_rng_get_rn_string(SilcRng rng, uint32 len) /* Returns random number binary data. */ -unsigned char *silc_rng_get_rn_data(SilcRng rng, uint32 len) +unsigned char *silc_rng_get_rn_data(SilcRng rng, SilcUInt32 len) { int i; unsigned char *data; @@ -451,7 +536,7 @@ 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). */ -int silc_rng_global_init(SilcRng rng) +bool silc_rng_global_init(SilcRng rng) { if (rng) global_rng = rng; @@ -463,7 +548,7 @@ int silc_rng_global_init(SilcRng rng) /* Uninitialize global RNG */ -int silc_rng_global_uninit() +bool silc_rng_global_uninit(void) { if (global_rng) { silc_rng_free(global_rng); @@ -475,27 +560,60 @@ int silc_rng_global_uninit() /* These are analogous to the functions above. */ -unsigned char silc_rng_global_get_byte() +SilcUInt8 silc_rng_global_get_byte(void) { return global_rng ? silc_rng_get_byte(global_rng) : 0; } -uint16 silc_rng_global_get_rn16() +/* 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) +{ +#ifndef SILC_WIN32 + unsigned char buf[1]; + + if (!global_rng) + return 0; + + if (global_rng->fd_devurandom == -1) { + global_rng->fd_devurandom = open("/dev/urandom", O_RDONLY); + if (global_rng < 0) + return silc_rng_global_get_byte(); + fcntl(global_rng->fd_devurandom, F_SETFL, O_NONBLOCK); + } + + if (read(global_rng->fd_devurandom, buf, sizeof(buf)) < 0) + return silc_rng_global_get_byte(); + + return buf[0]; +#else + return silc_rng_global_get_byte(); +#endif +} + +SilcUInt16 silc_rng_global_get_rn16(void) { return global_rng ? silc_rng_get_rn16(global_rng) : 0; } -uint32 silc_rng_global_get_rn32() +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(uint32 len) +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(uint32 len) +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); +}