X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilccrypt%2Fsilcrng.c;h=ab680bf04c929389a8d38ed558d1c92c6bfede9c;hb=e7b6c157b80152bf9fb9266e6bdd93f9fb0db776;hp=4c69e9bf566cbb578272750015f340eeb6c6b9f6;hpb=4548e3912f812e41b9e48939fbb4a1e1ed60294b;p=silc.git diff --git a/lib/silccrypt/silcrng.c b/lib/silccrypt/silcrng.c index 4c69e9bf..ab680bf0 100644 --- a/lib/silccrypt/silcrng.c +++ b/lib/silccrypt/silcrng.c @@ -1,10 +1,10 @@ /* - silcrng.c + silcrng.c Author: Pekka Riikonen - Copyright (C) 1997 - 2002 Pekka Riikonen + Copyright (C) 1997 - 2007 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 @@ -21,11 +21,11 @@ * 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 @@ -44,17 +44,17 @@ extern pid_t getpgid (pid_t __pid); #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 uint32 silc_rng_get_position(SilcRng rng); +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_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. This object is used by the random number generator to provide @@ -63,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. @@ -76,13 +76,13 @@ static void silc_rng_get_soft_noise(SilcRng rng); */ typedef struct SilcRngStateContext { - uint32 low; - uint32 pos; + SilcUInt32 low; + SilcUInt32 pos; 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 @@ -111,7 +111,7 @@ typedef struct SilcRngStateContext { random pool. This is allocated when RNG object is allocated and free'd when RNG object is free'd. - uint8 threshold + SilcUInt8 threshold Threshold to indicate when it is required to acquire more noise from the environment. More soft noise is acquired after @@ -123,7 +123,7 @@ struct SilcRngStruct { unsigned char key[64]; SilcRngState state; SilcHash sha1; - uint8 threshold; + SilcUInt8 threshold; char *devrandom; int fd_devurandom; }; @@ -158,6 +158,8 @@ SilcRng silc_rng_alloc(void) 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); @@ -166,11 +168,18 @@ void silc_rng_free(SilcRng rng) 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. */ @@ -191,7 +200,7 @@ void silc_rng_init(SilcRng rng) 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; @@ -219,8 +228,11 @@ static void silc_rng_get_soft_noise(SilcRng rng) #ifndef SILC_WIN32 struct tms ptime; #endif - uint32 pos; - + SilcUInt32 pos; +#ifdef HAVE_GETRUSAGE + struct rusage r; +#endif + pos = silc_rng_get_position(rng); silc_rng_xor(rng, clock(), 0); @@ -231,14 +243,17 @@ static void silc_rng_get_soft_noise(SilcRng rng) 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++); @@ -250,6 +265,7 @@ static void silc_rng_get_soft_noise(SilcRng rng) 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 @@ -262,12 +278,39 @@ static void silc_rng_get_soft_noise(SilcRng rng) #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)); @@ -281,6 +324,10 @@ static void silc_rng_get_soft_noise(SilcRng rng) 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"); @@ -296,10 +343,10 @@ static void silc_rng_get_medium_noise(SilcRng rng) 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) @@ -328,42 +375,41 @@ static void silc_rng_get_hard_noise(SilcRng rng) 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, 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); @@ -380,44 +426,81 @@ void silc_rng_add_noise(SilcRng rng, unsigned char *buffer, uint32 len) /* 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; + 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; - uint32 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) { - 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)); @@ -426,10 +509,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; @@ -438,7 +521,7 @@ static uint32 silc_rng_get_position(SilcRng rng) 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 @@ -449,8 +532,10 @@ static uint32 silc_rng_get_position(SilcRng rng) /* Returns random byte. */ -uint8 silc_rng_get_byte(SilcRng rng) +SilcUInt8 silc_rng_get_byte(SilcRng rng) { + SilcUInt8 byte; + rng->threshold++; /* Get more soft noise after 64 bits threshold */ @@ -463,15 +548,40 @@ uint8 silc_rng_get_byte(SilcRng rng) 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 + available. If not then return from normal RNG (not so fast). */ + +SilcUInt8 silc_rng_get_byte_fast(SilcRng rng) +{ +#if defined(SILC_UNIX) + 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] != 0x00 ? buf[0] : silc_rng_get_byte(rng); +#else + return silc_rng_get_byte(rng); +#endif } /* 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); @@ -482,10 +592,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; + SilcUInt32 num; rn[0] = silc_rng_get_byte(rng); rn[1] = silc_rng_get_byte(rng); @@ -496,9 +606,9 @@ uint32 silc_rng_get_rn32(SilcRng 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, uint32 len) +unsigned char *silc_rng_get_rn_string(SilcRng rng, SilcUInt32 len) { int i; unsigned char *string; @@ -511,19 +621,20 @@ unsigned char *silc_rng_get_rn_string(SilcRng rng, uint32 len) return string; } -/* Returns random number binary data. */ +/* Returns non-zero random number binary data. */ -unsigned char *silc_rng_get_rn_data(SilcRng rng, uint32 len) +SilcBool silc_rng_get_rn_data(SilcRng rng, SilcUInt32 len, unsigned char *buf, + SilcUInt32 buf_size) { int i; - unsigned char *data; - data = silc_calloc(len + 1, sizeof(*data)); + if (len > buf_size) + return FALSE; for (i = 0; i < len; i++) - data[i] = silc_rng_get_byte(rng); + buf[i] = silc_rng_get_byte(rng); - return data; + return TRUE; } /* Global RNG. This is global RNG that application can initialize so @@ -536,19 +647,22 @@ 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) +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); @@ -560,7 +674,7 @@ bool silc_rng_global_uninit(void) /* These are analogous to the functions above. */ -uint8 silc_rng_global_get_byte(void) +SilcUInt8 silc_rng_global_get_byte(void) { return global_rng ? silc_rng_get_byte(global_rng) : 0; } @@ -568,51 +682,34 @@ uint8 silc_rng_global_get_byte(void) /* Return random byte as fast as possible. Reads from /dev/urandom if available. If not then return from normal RNG (not so fast). */ -uint8 silc_rng_global_get_byte_fast(void) +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 + return global_rng ? silc_rng_get_byte_fast(global_rng) : 0; } -uint16 silc_rng_global_get_rn16(void) +SilcUInt16 silc_rng_global_get_rn16(void) { return global_rng ? silc_rng_get_rn16(global_rng) : 0; } -uint32 silc_rng_global_get_rn32(void) +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) +SilcBool silc_rng_global_get_rn_data(SilcRng rng, SilcUInt32 len, + unsigned char *buf, SilcUInt32 buf_size) { - return global_rng ? silc_rng_get_rn_data(global_rng, len) : NULL; + return global_rng ? silc_rng_get_rn_data(global_rng, len, buf, + buf_size) : FALSE; } -void silc_rng_global_add_noise(unsigned char *buffer, uint32 len) +void silc_rng_global_add_noise(unsigned char *buffer, SilcUInt32 len) { if (global_rng) silc_rng_add_noise(global_rng, buffer, len);