diff options
Diffstat (limited to '')
-rw-r--r-- | libblkid/lib/randutils.c | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/libblkid/lib/randutils.c b/libblkid/lib/randutils.c new file mode 100644 index 000000000..684ac0ac1 --- /dev/null +++ b/libblkid/lib/randutils.c @@ -0,0 +1,147 @@ +/* + * General purpose random utilities + * + * Based on libuuid code. + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> + +#include <sys/syscall.h> + +#include "c.h" +#include "randutils.h" +#include "nls.h" + +#ifdef HAVE_TLS +#define THREAD_LOCAL static __thread +#else +#define THREAD_LOCAL static +#endif + +#if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48) +#define DO_JRAND_MIX +THREAD_LOCAL unsigned short ul_jrand_seed[3]; +#endif + +int random_get_fd(void) +{ + int i, fd; + struct timeval tv; + + gettimeofday(&tv, 0); + fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); + if (fd == -1) + fd = open("/dev/random", O_RDONLY | O_NONBLOCK | O_CLOEXEC); + if (fd >= 0) { + i = fcntl(fd, F_GETFD); + if (i >= 0) + fcntl(fd, F_SETFD, i | FD_CLOEXEC); + } + srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec); + +#ifdef DO_JRAND_MIX + ul_jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF); + ul_jrand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF); + ul_jrand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16; +#endif + /* Crank the random number generator a few times */ + gettimeofday(&tv, 0); + for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--) + rand(); + return fd; +} + + +/* + * Generate a stream of random nbytes into buf. + * Use /dev/urandom if possible, and if not, + * use glibc pseudo-random functions. + */ +void random_get_bytes(void *buf, size_t nbytes) +{ + size_t i, n = nbytes; + int fd = random_get_fd(); + int lose_counter = 0; + unsigned char *cp = (unsigned char *) buf; + + if (fd >= 0) { + while (n > 0) { + ssize_t x = read(fd, cp, n); + if (x <= 0) { + if (lose_counter++ > 16) + break; + continue; + } + n -= x; + cp += x; + lose_counter = 0; + } + + close(fd); + } + + /* + * We do this all the time, but this is the only source of + * randomness if /dev/random/urandom is out to lunch. + */ + for (cp = buf, i = 0; i < nbytes; i++) + *cp++ ^= (rand() >> 7) & 0xFF; + +#ifdef DO_JRAND_MIX + { + unsigned short tmp_seed[3]; + + memcpy(tmp_seed, ul_jrand_seed, sizeof(tmp_seed)); + ul_jrand_seed[2] = ul_jrand_seed[2] ^ syscall(__NR_gettid); + for (cp = buf, i = 0; i < nbytes; i++) + *cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF; + memcpy(ul_jrand_seed, tmp_seed, + sizeof(ul_jrand_seed)-sizeof(unsigned short)); + } +#endif + + return; +} + + +/* + * Tell source of randomness. + */ +const char *random_tell_source(void) +{ + size_t i; + static const char *random_sources[] = { + "/dev/urandom", + "/dev/random" + }; + + for (i = 0; i < ARRAY_SIZE(random_sources); i++) { + if (!access(random_sources[i], R_OK)) + return random_sources[i]; + } + + return _("libc pseudo-random functions"); +} + +#ifdef TEST_PROGRAM +int main(int argc __attribute__ ((__unused__)), + char *argv[] __attribute__ ((__unused__))) +{ + unsigned int v, i; + + /* generate and print 10 random numbers */ + for (i = 0; i < 10; i++) { + random_get_bytes(&v, sizeof(v)); + printf("%d\n", v); + } + + return EXIT_SUCCESS; +} +#endif /* TEST_PROGRAM */ |