306420a397dcc67db67cb44709bd4926a46d8a45
[project/urngd.git] / urngd.c
1 /*
2 * Non-physical true random number generator based on timing jitter.
3 *
4 * Copyright Stephan Mueller <smueller@chronox.de>, 2014
5 * Copyright Petr Štetiar <ynezz@true.cz>, 2019
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, and the entire permission notice in its entirety,
12 * including the disclaimer of warranties.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote
17 * products derived from this software without specific prior
18 * written permission.
19 *
20 * ALTERNATIVELY, this product may be distributed under the terms of
21 * the GNU General Public License, in which case the provisions of the GPL are
22 * required INSTEAD OF the above restrictions. (This clause is
23 * necessary due to a potential bad interaction between the GPL and
24 * the restrictions contained in a BSD-style copyright.)
25 *
26 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
27 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
29 * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
30 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
32 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
33 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
36 * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
37 * DAMAGE.
38 */
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <unistd.h>
43
44 #include <sys/ioctl.h>
45 #include <linux/random.h>
46
47 #include <libubox/uloop.h>
48
49 #include "log.h"
50 #include "jitterentropy.h"
51
52 #define ENTROPYBYTES 32
53 #define ENTROPYTHRESH 1024
54 #define OVERSAMPLINGFACTOR 2
55 #define DEV_RANDOM "/dev/random"
56 #define ENTROPYAVAIL "/proc/sys/kernel/random/entropy_avail"
57 #define ENTROPYPOOLBYTES (sizeof(struct rand_pool_info) + \
58 (ENTROPYBYTES * OVERSAMPLINGFACTOR * sizeof(char)))
59
60 #ifdef URNGD_DEBUG
61 unsigned int debug;
62 #endif
63
64 struct urngd {
65 struct uloop_fd rnd_fd;
66 struct rand_data *ec;
67 struct rand_pool_info *rpi;
68 };
69
70 static struct urngd urngd_service;
71
72 static inline void memset_secure(void *s, int c, size_t n)
73 {
74 memset(s, c, n);
75 __asm__ __volatile__("" : : "r" (s) : "memory");
76 }
77
78 static size_t write_entropy(struct urngd *u, char *buf, size_t len,
79 size_t entropy_bytes)
80 {
81 int ret;
82 size_t written = 0;
83
84 /* value is in bits */
85 u->rpi->entropy_count = (entropy_bytes * 8);
86 u->rpi->buf_size = len;
87 memcpy(u->rpi->buf, buf, len);
88 memset(buf, 0, len);
89
90 ret = ioctl(u->rnd_fd.fd, RNDADDENTROPY, u->rpi);
91 if (0 > ret) {
92 ERROR("error injecting entropy: %s\n", strerror(errno));
93 } else {
94 DEBUG(1, "injected %ub (%ub of entropy)\n", len, entropy_bytes);
95 written = len;
96 }
97
98 u->rpi->entropy_count = 0;
99 u->rpi->buf_size = 0;
100 memset(u->rpi->buf, 0, len);
101
102 return written;
103 }
104
105 static size_t gather_entropy(struct urngd *u)
106 {
107 size_t ret = 0;
108 char buf[(ENTROPYBYTES * OVERSAMPLINGFACTOR)];
109
110 if (jent_read_entropy(u->ec, buf, sizeof(buf)) < 0) {
111 ERROR("cannot read entropy\n");
112 return 0;
113 }
114
115 ret = write_entropy(u, buf, sizeof(buf), ENTROPYBYTES);
116 if (sizeof(buf) != ret) {
117 ERROR("injected %zub of entropy, less then %zub expected\n",
118 ret, sizeof(buf));
119 } else {
120 ret = sizeof(buf);
121 }
122
123 memset_secure(buf, 0, sizeof(buf));
124 DEBUG(2, DEV_RANDOM " fed with %lub of entropy\n", ret);
125
126 return ret;
127 }
128
129 static void low_entropy_cb(struct uloop_fd *ufd, unsigned int events)
130 {
131 struct urngd *u = container_of(ufd, struct urngd, rnd_fd);
132
133 DEBUG(2, DEV_RANDOM " signals low entropy\n");
134 gather_entropy(u);
135 }
136
137 static void urngd_done(struct urngd *u)
138 {
139 if (u->ec) {
140 jent_entropy_collector_free(u->ec);
141 u->ec = NULL;
142 }
143
144 if (u->rpi) {
145 memset(u->rpi, 0, ENTROPYPOOLBYTES);
146 free(u->rpi);
147 u->rpi = NULL;
148 }
149
150 if (u->rnd_fd.fd) {
151 close(u->rnd_fd.fd);
152 u->rnd_fd.fd = 0;
153 }
154 }
155
156 static bool urngd_init(struct urngd *u)
157 {
158 int ret = jent_entropy_init();
159 if (ret) {
160 ERROR("jent-rng init failed, err: %d\n", ret);
161 return false;
162 }
163
164 u->ec = jent_entropy_collector_alloc(1, 0);
165 if (!u->ec) {
166 ERROR("jent-rng alloc failed\n");
167 return false;
168 }
169
170 u->rpi = malloc(ENTROPYPOOLBYTES);
171 if (!u->rpi) {
172 ERROR("rand pool alloc failed\n");
173 return false;
174 }
175
176 u->rnd_fd.cb = low_entropy_cb;
177 u->rnd_fd.fd = open(DEV_RANDOM, O_WRONLY);
178 if (u->rnd_fd.fd < 1) {
179 ERROR(DEV_RANDOM " open failed: %s\n", strerror(errno));
180 return false;
181 }
182
183 uloop_fd_add(&u->rnd_fd, ULOOP_READ);
184
185 return true;
186 }
187
188 static int usage(const char *prog)
189 {
190 fprintf(stderr, "Usage: %s [options]\n"
191 "Options:\n"
192 #ifdef URNGD_DEBUG
193 " -d <level> Enable debug messages\n"
194 #endif
195 " -S Print messages to stdout\n"
196 "\n", prog);
197 return 1;
198 }
199
200 int main(int argc, char **argv)
201 {
202 int ch;
203 int ulog_channels = ULOG_KMSG;
204 #ifdef URNGD_DEBUG
205 char *dbglvl = getenv("DBGLVL");
206
207 if (dbglvl) {
208 debug = atoi(dbglvl);
209 unsetenv("DBGLVL");
210 }
211 #endif
212
213 while ((ch = getopt(argc, argv, "d:S")) != -1) {
214 switch (ch) {
215 #ifdef URNGD_DEBUG
216 case 'd':
217 debug = atoi(optarg);
218 break;
219 #endif
220 case 'S':
221 ulog_channels = ULOG_STDIO;
222 break;
223 default:
224 return usage(argv[0]);
225 }
226 }
227
228 ulog_open(ulog_channels, LOG_DAEMON, "urngd");
229
230 if (!urngd_init(&urngd_service))
231 return -1;
232
233 LOG("v%s started.\n", URNGD_VERSION);
234
235 gather_entropy(&urngd_service);
236
237 uloop_init();
238 uloop_run();
239 uloop_done();
240
241 urngd_done(&urngd_service);
242
243 return 0;
244 }