diff options
Diffstat (limited to '')
| -rw-r--r-- | librecrypt_rng_.c | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/librecrypt_rng_.c b/librecrypt_rng_.c new file mode 100644 index 0000000..8b9f138 --- /dev/null +++ b/librecrypt_rng_.c @@ -0,0 +1,146 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +ssize_t +librecrypt_rng_(void *out, size_t n, void *user) +{ + static unsigned int seed = 0u; + unsigned char *buf = out; + int v, fd, saved_errno = errno; + unsigned int state; + size_t i, min, ret = 0u; + ssize_t r; + struct timespec ts; +#if defined(__linux__) && defined(AT_RANDOM) + const unsigned char *at_random; + uintptr_t at_random_addr; +#endif + + (void) user; + + if (!n) + return 0; + + if (n > (size_t)SSIZE_MAX) + n = (size_t)SSIZE_MAX; + +#if defined(__linux__) + for (;;) { + r = getrandom(buf, n, 0u); + if (r < 0) { + if (errno != EINTR) + break; + saved_errno = EINTR; + } else if (r > 0) { + ret += (size_t)r; + buf = &buf[(size_t)r]; + n -= (size_t)r; + if (!n) + goto out; + } else { + break; + } + } +#endif + + for (;;) { + min = n < 256u ? n : 256u; + if (getentropy(buf, min)) { + if (errno != EINTR) + break; + saved_errno = EINTR; + } else { + ret += min; + buf = &buf[min]; + n -= min; + if (!n) + goto out; + } + } + +#ifndef O_CLOEXEC +# define O_CLOEXEC 0 +#endif + fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); + if (fd >= 0) { + for (;;) { + r = read(fd, buf, n); + if (r < 0) { + if (errno != EINTR) { + close(fd); + break; + } + saved_errno = EINTR; + } else if (r > 0) { + ret += (size_t)r; + buf = &buf[(size_t)r]; + n -= (size_t)r; + if (!n) { + close(fd); + goto out; + } + } else { + close(fd); + break; + } + } + } + + state = seed; + if (!state) { + state = (unsigned int)rand(); + state ^= (unsigned int)time(NULL); + if (!clock_gettime(CLOCK_MONOTONIC, &ts)) { + state ^= (unsigned int)ts.tv_sec; + state ^= (unsigned int)ts.tv_nsec; + } +#if defined(__linux__) && defined(AT_RANDOM) + at_random_addr = (uintptr_t)getauxval(AT_RANDOM); + if (at_random_addr) { + at_random = (const void *)at_random_addr; + for (i = 0u; i < 16u; i++) + state ^= (unsigned int)at_random[i] << (i % sizeof(unsigned int) * 8u); + } + } +#endif + for (i = 0u; i < n; i++) { + v = rand_r(&state); + v = (int)((uintmax_t)v * 255u / (uintmax_t)RAND_MAX); + buf[i] = (unsigned char)v; + } + ret += n; + seed = state; + +out: + errno = saved_errno; + return (ssize_t)ret; +} + + +#else + + +int +main(void) +{ + /* TODO test failure cases */ + + unsigned char buf1[1024u]; + unsigned char buf2[sizeof(buf1)]; + ssize_t n1, n2; + + SET_UP_ALARM(); + + n1 = librecrypt_rng_(buf1, sizeof(buf1), NULL); + n2 = librecrypt_rng_(buf2, sizeof(buf2), NULL); + EXPECT(n1 >= 128 && (size_t)n1 <= sizeof(buf1)); + EXPECT(n2 >= 128 && (size_t)n2 <= sizeof(buf2)); + EXPECT(memcmp(buf1, buf2, (size_t)(n1 < n2 ? n1 : n2))); + + return 0; +} + + +#endif |
