corrected lock usage.
[gnutls.git] / lib / nettle / rnd.c
blob26d9e127146c6a521d3f12a8101f6b0f4135e969
1 /*
2 * Copyright (C) 2008, 2010 Free Software Foundation, Inc.
4 * Author: Nikos Mavrogiannopoulos
6 * This file is part of GNUTLS.
8 * The GNUTLS library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2.1 of
11 * the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
21 * USA
25 /* Here is the libgcrypt random generator layer.
28 #include <gnutls_int.h>
29 #include <gnutls_errors.h>
30 #include <gnutls_num.h>
31 #include <nettle/yarrow.h>
32 #include <time.h>
33 #include <errno.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <sys/time.h>
37 #include <fcntl.h>
38 #include <locks.h>
40 #define SOURCES 2
42 static void* rnd_mutex;
44 #define RND_LOCK if (gnutls_mutex_lock(rnd_mutex)!=0) abort()
45 #define RND_UNLOCK if (gnutls_mutex_unlock(rnd_mutex)!=0) abort()
47 enum {
48 RANDOM_SOURCE_TRIVIA=0,
49 RANDOM_SOURCE_DEVICE,
52 static struct yarrow256_ctx yctx;
53 static struct yarrow_source ysources[SOURCES];
54 static time_t device_last_read = 0;
55 static int device_fd;
57 static time_t trivia_previous_time = 0;
58 static time_t trivia_time_count = 0;
60 static int do_trivia_source(int init)
62 struct {
63 struct timeval now;
64 #ifdef HAVE_GETRUSAGE
65 struct rusage rusage;
66 #endif
67 unsigned count;
68 pid_t pid;
69 } event;
71 unsigned entropy = 0;
73 if (gettimeofday(&event.now, NULL) < 0) {
74 _gnutls_debug_log("gettimeofday failed: %s\n", strerror(errno));
75 abort();
77 #ifdef HAVE_GETRUSAGE
78 if (getrusage(RUSAGE_SELF, &event.rusage) < 0) {
79 _gnutls_debug_log("getrusage failed: %s\n", strerror(errno));
80 abort();
82 #endif
84 event.count = 0;
85 if (init) {
86 trivia_time_count = 0;
87 } else {
88 event.count = trivia_time_count++;
90 if (event.now.tv_sec != trivia_previous_time) {
91 /* Count one bit of entropy if we either have more than two
92 * invocations in one second, or more than two seconds
93 * between invocations. */
94 if ((trivia_time_count > 2)
95 || ((event.now.tv_sec - trivia_previous_time) > 2))
96 entropy++;
98 trivia_time_count = 0;
101 trivia_previous_time = event.now.tv_sec;
102 event.pid = getpid();
104 return yarrow256_update(&yctx, RANDOM_SOURCE_TRIVIA, entropy,
105 sizeof(event), (const uint8_t *) &event);
108 #define DEVICE_READ_SIZE 16
109 #define DEVICE_READ_SIZE_MAX 32
110 #define DEVICE_READ_INTERVAL 360
111 static int do_device_source(int init)
113 time_t now = time(NULL);
114 int read_size = DEVICE_READ_SIZE;
116 if (init) {
117 int old;
119 device_fd = open("/dev/urandom", O_RDONLY);
120 if (device_fd < 0) {
121 _gnutls_debug_log("Cannot open urandom!\n");
122 abort();
125 old = fcntl(device_fd, F_GETFD);
126 fcntl(device_fd, F_SETFD, old | 1);
127 device_last_read = now;
129 read_size = DEVICE_READ_SIZE_MAX; /* initially read more data */
132 if ((device_fd > 0)
133 && (init || ((now - device_last_read) > DEVICE_READ_INTERVAL))) {
135 /* More than a minute since we last read the device */
136 uint8_t buf[DEVICE_READ_SIZE_MAX];
137 uint32_t done;
139 for (done = 0; done < read_size;) {
140 int res;
142 res =
143 read(device_fd, buf + done, sizeof(buf) - done);
144 while (res < 0 && errno == EINTR);
146 if (res <= 0) {
147 if (res < 0) {
148 _gnutls_debug_log("Failed to read /dev/urandom: %s\n",
149 strerror(errno));
150 } else {
151 _gnutls_debug_log
152 ("Failed to read /dev/urandom: end of file\n");
155 return 0;
158 done += res;
161 device_last_read = now;
162 return yarrow256_update(&yctx, RANDOM_SOURCE_DEVICE, read_size*8/3 /* be more conservative */,
163 read_size, buf);
165 return 0;
168 static void wrap_nettle_rnd_deinit(void* ctx)
170 RND_LOCK;
171 close(device_fd);
172 RND_UNLOCK;
175 static int wrap_nettle_rnd_init(void **ctx)
177 int ret;
179 if (gnutls_mutex_init)
181 ret = gnutls_mutex_init(&rnd_mutex);
182 if (ret < 0)
184 gnutls_assert();
185 return ret;
189 yarrow256_init(&yctx, SOURCES, ysources);
190 do_device_source(1);
191 do_trivia_source(1);
193 yarrow256_slow_reseed(&yctx);
195 return 0;
200 static int
201 wrap_nettle_rnd(void *_ctx, int level, void *data, size_t datasize)
203 RND_LOCK;
204 do_trivia_source( 0);
205 do_device_source( 0);
207 yarrow256_random(&yctx, datasize, data);
208 RND_UNLOCK;
209 return 0;
212 int crypto_rnd_prio = INT_MAX;
214 gnutls_crypto_rnd_st _gnutls_rnd_ops = {
215 .init = wrap_nettle_rnd_init,
216 .deinit = wrap_nettle_rnd_deinit,
217 .rnd = wrap_nettle_rnd,