diff options
Diffstat (limited to 'shared/ossp_uuid/uuid_prng.c')
-rw-r--r-- | shared/ossp_uuid/uuid_prng.c | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/shared/ossp_uuid/uuid_prng.c b/shared/ossp_uuid/uuid_prng.c new file mode 100644 index 00000000..9ebca40f --- /dev/null +++ b/shared/ossp_uuid/uuid_prng.c @@ -0,0 +1,198 @@ +/* +** OSSP uuid - Universally Unique Identifier +** Copyright (c) 2004-2008 Ralf S. Engelschall <rse@engelschall.com> +** Copyright (c) 2004-2008 The OSSP Project <http://www.ossp.org/> +** +** This file is part of OSSP uuid, a library for the generation +** of UUIDs which can found at http://www.ossp.org/pkg/lib/uuid/ +** +** Permission to use, copy, modify, and distribute this software for +** any purpose with or without fee is hereby granted, provided that +** the above copyright notice and this permission notice appear in all +** copies. +** +** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED +** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR +** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +** +** uuid_prng.c: PRNG API implementation +*/ + +/* own headers (part 1/2) */ +#include "uuid_ac.h" + +/* system headers */ +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <sys/time.h> +#include <fcntl.h> +#if defined(WIN32) +#define WINVER 0x0500 +#include <windows.h> +#include <wincrypt.h> +#endif + +/* own headers (part 2/2) */ +#include "uuid_time.h" +#include "uuid_prng.h" +#include "uuid_md5.h" + +struct prng_st { + int dev; /* system PRNG device */ + md5_t *md5; /* local MD5 PRNG engine */ + long cnt; /* time resolution compensation counter */ +}; + +prng_rc_t prng_create(prng_t **prng) +{ +#if !defined(WIN32) + int fd = -1; +#endif + struct timeval tv; + pid_t pid; + unsigned int i; + + /* sanity check argument(s) */ + if (prng == NULL) + return PRNG_RC_ARG; + + /* allocate object */ + if ((*prng = (prng_t *)malloc(sizeof(prng_t))) == NULL) + return PRNG_RC_MEM; + + /* try to open the system PRNG device */ + (*prng)->dev = -1; +#if !defined(WIN32) + if ((fd = open("/dev/urandom", O_RDONLY)) == -1) + fd = open("/dev/random", O_RDONLY|O_NONBLOCK); + if (fd != -1) { + (void)fcntl(fd, F_SETFD, FD_CLOEXEC); + (*prng)->dev = fd; + } +#endif + + /* initialize MD5 engine */ + if (md5_create(&((*prng)->md5)) != MD5_RC_OK) { + free(*prng); + return PRNG_RC_INT; + } + + /* initialize time resolution compensation counter */ + (*prng)->cnt = 0; + + /* seed the C library PRNG once */ + (void)time_gettimeofday(&tv); + pid = getpid(); + srand((unsigned int)( + ((unsigned int)pid << 16) + ^ (unsigned int)pid + ^ (unsigned int)tv.tv_sec + ^ (unsigned int)tv.tv_usec)); + for (i = (unsigned int)((tv.tv_sec ^ tv.tv_usec) & 0x1F); i > 0; i--) + (void)rand(); + + return PRNG_RC_OK; +} + +prng_rc_t prng_data(prng_t *prng, void *data_ptr, size_t data_len) +{ + size_t n; + unsigned char *p; + struct { + struct timeval tv; + long cnt; + int rnd; + } entropy; + unsigned char md5_buf[MD5_LEN_BIN]; + unsigned char *md5_ptr; + size_t md5_len; + int retries; + int i; +#if defined(WIN32) + HCRYPTPROV hProv; +#endif + + /* sanity check argument(s) */ + if (prng == NULL || data_len == 0) + return PRNG_RC_ARG; + + /* prepare for generation */ + p = (unsigned char *)data_ptr; + n = data_len; + + /* approach 1: try to gather data via stronger system PRNG device */ + if (prng->dev != -1) { + retries = 0; + while (n > 0) { + i = (int)read(prng->dev, (void *)p, n); + if (i <= 0) { + if (retries++ > 16) + break; + continue; + } + retries = 0; + n -= (unsigned int)i; + p += (unsigned int)i; + } + } +#if defined(WIN32) + else { + if (CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) + CryptGenRandom(hProv, n, p); + } +#endif + + /* approach 2: try to gather data via weaker libc PRNG API. */ + while (n > 0) { + /* gather new entropy */ + (void)time_gettimeofday(&(entropy.tv)); /* source: libc time */ + entropy.rnd = rand(); /* source: libc PRNG */ + entropy.cnt = prng->cnt++; /* source: local counter */ + + /* pass entropy into MD5 engine */ + if (md5_update(prng->md5, (void *)&entropy, sizeof(entropy)) != MD5_RC_OK) + return PRNG_RC_INT; + + /* store MD5 engine state as PRN output */ + md5_ptr = md5_buf; + md5_len = sizeof(md5_buf); + if (md5_store(prng->md5, (void **)(void *)&md5_ptr, &md5_len) != MD5_RC_OK) + return PRNG_RC_INT; + for (i = 0; i < MD5_LEN_BIN && n > 0; i++, n--) + *p++ ^= md5_buf[i]; /* intentionally no assignment because arbitrary + caller buffer content is leveraged, too */ + } + + return PRNG_RC_OK; +} + +prng_rc_t prng_destroy(prng_t *prng) +{ + /* sanity check argument(s) */ + if (prng == NULL) + return PRNG_RC_ARG; + + /* close PRNG device */ + if (prng->dev != -1) + (void)close(prng->dev); + + /* destroy MD5 engine */ + (void)md5_destroy(prng->md5); + + /* free object */ + free(prng); + + return PRNG_RC_OK; +} + |