]> git.ipfire.org Git - thirdparty/rng-tools.git/blame - rngd_rdrand.c
rdrand: Fix the RDRAND data reduction
[thirdparty/rng-tools.git] / rngd_rdrand.c
CommitLineData
101309bc 1/*
62dec321 2 * Copyright (c) 2012-2014, Intel Corporation
101309bc 3 * Authors: Richard B. Hill <richard.b.hill@intel.com>,
04361df2
JM
4 * H. Peter Anvin <hpa@linux.intel.com>,
5 * John P. Mechalas <john.p.mechalas@intel.com>
101309bc
PA
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms and conditions of the GNU General Public License,
9 * version 2, as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 */
21
22#define _GNU_SOURCE
23
24#ifndef HAVE_CONFIG_H
25#error Invalid or missing autoconf build environment
26#endif
27
28#include "rng-tools-config.h"
29
30#include <unistd.h>
31#include <stdint.h>
32#include <stdlib.h>
33#include <sys/types.h>
34#include <sys/stat.h>
35#include <fcntl.h>
36#include <errno.h>
37#include <syslog.h>
38#include <string.h>
39#include <stddef.h>
04361df2
JM
40#ifdef HAVE_LIBGCRYPT
41#include <gcrypt.h>
42#endif
101309bc
PA
43
44#include "rngd.h"
45#include "fips.h"
46#include "exits.h"
47#include "rngd_entsource.h"
48
49#if defined(__i386__) || defined(__x86_64__)
50
101309bc
PA
51/* Struct for CPUID return values */
52struct cpuid {
53 uint32_t eax, ecx, edx, ebx;
54};
55
3164d3eb
PA
56/*
57 * Get data from RDRAND. The count is in bytes, but the function can
58 * round *up* the count to the nearest 4- or 8-byte boundary. The caller
59 * needs to take that into account.
60 *
61 * The function returns the number of bytes actually written.
62 */
63extern unsigned int x86_rdrand_bytes(void *ptr, unsigned int count);
64
65/* Condition RDRAND for seed-grade entropy */
101309bc 66extern void x86_aes_mangle(void *data, void *state);
3164d3eb 67
94f03c9a
PA
68/* Expand an AES key for future use */
69extern void x86_aes_expand_key(const void *key);
101309bc 70
0ad21ad0
PA
71#ifdef __x86_64__
72typedef uint64_t unative_t; /* x86-64 or x32 */
73#else
74typedef uint32_t unative_t; /* i386 */
75#endif
76
101309bc 77/* Checking eflags to confirm cpuid instruction available */
0ad21ad0 78static inline int x86_has_eflag(unative_t flag)
101309bc 79{
0ad21ad0 80 unative_t f0, f1;
62dec321
PA
81 asm("pushf ; "
82 "pushf ; "
83 "pop %0 ; "
84 "mov %0,%1 ; "
85 "xor %2,%1 ; "
86 "push %1 ; "
87 "popf ; "
88 "pushf ; "
89 "pop %1 ; "
90 "popf"
91 : "=&r" (f0), "=&r" (f1)
92 : "ri" (flag));
93 return !!((f0^f1) & flag);
101309bc 94}
62dec321
PA
95
96static inline int x86_has_cpuid(void)
97{
98#ifdef __i386__
99 return x86_has_eflag(1 << 21); /* ID flag */
100#else
101 return 1; /* x86-64 always has CPUID */
101309bc 102#endif
62dec321 103}
101309bc 104
04361df2 105/* Calling cpuid instruction to verify rdrand and aes-ni capability */
101309bc
PA
106static void cpuid(unsigned int leaf, unsigned int subleaf, struct cpuid *out)
107{
108#ifdef __i386__
109 /* %ebx is a forbidden register if we compile with -fPIC or -fPIE */
110 asm volatile("movl %%ebx,%0 ; cpuid ; xchgl %%ebx,%0"
111 : "=r" (out->ebx),
112 "=a" (out->eax),
113 "=c" (out->ecx),
114 "=d" (out->edx)
115 : "a" (leaf), "c" (subleaf));
116#else
117 asm volatile("cpuid"
118 : "=b" (out->ebx),
119 "=a" (out->eax),
120 "=c" (out->ecx),
121 "=d" (out->edx)
122 : "a" (leaf), "c" (subleaf));
123#endif
124}
125
932e25b2 126/* Read data from the drng in chunks of 128 bytes for AES scrambling */
3e89e082
PA
127#define AES_BLOCK 16
128#define CHUNK_SIZE (AES_BLOCK*8) /* 8 parallel streams */
129#define RDRAND_ROUNDS 512 /* 512:1 data reduction */
932e25b2
PA
130
131static unsigned char iv_buf[CHUNK_SIZE] __attribute__((aligned(128)));
62dec321 132static int have_aesni;
04361df2
JM
133
134/* Necessary if we have RDRAND but not AES-NI */
135
136#ifdef HAVE_LIBGCRYPT
137
138#define MIN_GCRYPT_VERSION "1.0.0"
139
140static gcry_cipher_hd_t gcry_cipher_hd;
141
62dec321 142#endif
04361df2 143
62dec321
PA
144static inline int gcrypt_mangle(unsigned char *tmp)
145{
146#ifdef HAVE_LIBGCRYPT
147 gcry_error_t gcry_error;
04361df2 148
62dec321 149 /* Encrypt tmp in-place. */
04361df2 150
3e89e082
PA
151 gcry_error = gcry_cipher_encrypt(gcry_cipher_hd, tmp,
152 AES_BLOCK * RDRAND_ROUNDS,
62dec321
PA
153 NULL, 0);
154
155 if (gcry_error) {
156 message(LOG_DAEMON|LOG_ERR,
157 "gcry_cipher_encrypt error: %s\n",
158 gcry_strerror(gcry_error));
159 return -1;
160 }
161 return 0;
162#else
94f03c9a 163 (void)tmp;
62dec321
PA
164 return -1;
165#endif
166}
932e25b2 167
101309bc
PA
168int xread_drng(void *buf, size_t size, struct rng *ent_src)
169{
932e25b2
PA
170 char *p = buf;
171 size_t chunk;
3e89e082
PA
172 void *data;
173 unsigned char rdrand_buf[CHUNK_SIZE * RDRAND_ROUNDS]
174 __attribute__((aligned(128)));
175 unsigned int rand_bytes;
101309bc
PA
176 int i;
177
94f03c9a
PA
178 (void)ent_src;
179
3e89e082
PA
180 rand_bytes = have_aesni
181 ? CHUNK_SIZE * RDRAND_ROUNDS
182 : AES_BLOCK * RDRAND_ROUNDS;
183
932e25b2 184 while (size) {
3e89e082
PA
185 if (x86_rdrand_bytes(rdrand_buf, rand_bytes) != rand_bytes) {
186 message(LOG_DAEMON|LOG_ERR, "read error\n");
187 return -1;
188 }
189
190 /*
191 * Use 128-bit AES in CBC mode to reduce the
192 * data by a factor of RDRAND_ROUNDS
193 */
194 if (have_aesni) {
195 x86_aes_mangle(rdrand_buf, iv_buf);
196 data = iv_buf;
197 chunk = CHUNK_SIZE;
198 } else if (!gcrypt_mangle(rdrand_buf)) {
199 data = rdrand_buf + AES_BLOCK * (RDRAND_ROUNDS - 1);
200 chunk = AES_BLOCK;
201 } else {
202 return -1;
101309bc 203 }
3e89e082
PA
204
205 chunk = (chunk > size) ? size : chunk;
206 memcpy(p, data, chunk);
932e25b2
PA
207 p += chunk;
208 size -= chunk;
101309bc
PA
209 }
210
101309bc
PA
211 return 0;
212}
213
94f03c9a
PA
214static int init_aesni(const void *key)
215{
216 if (!have_aesni)
217 return 1;
218
219 x86_aes_expand_key(key);
220 return 0;
221}
222
223static int init_gcrypt(const void *key)
62dec321
PA
224{
225#ifdef HAVE_LIBGCRYPT
62dec321
PA
226 gcry_error_t gcry_error;
227
228 if (!gcry_check_version(MIN_GCRYPT_VERSION)) {
229 message(LOG_DAEMON|LOG_ERR,
230 "libgcrypt version mismatch: have %s, require >= %s\n",
231 gcry_check_version(NULL), MIN_GCRYPT_VERSION);
232 return 1;
233 }
234
235 gcry_error = gcry_cipher_open(&gcry_cipher_hd, GCRY_CIPHER_AES128,
236 GCRY_CIPHER_MODE_CBC, 0);
237
238 if (!gcry_error)
3e89e082 239 gcry_error = gcry_cipher_setkey(gcry_cipher_hd, key, AES_BLOCK);
62dec321
PA
240
241 if (!gcry_error) {
242 /*
243 * Only need the first 16 bytes of iv_buf. AES-NI can
244 * encrypt multiple blocks in parallel but we can't.
245 */
3e89e082 246 gcry_error = gcry_cipher_setiv(gcry_cipher_hd, iv_buf, AES_BLOCK);
62dec321
PA
247 }
248
249 if (gcry_error) {
250 message(LOG_DAEMON|LOG_ERR,
251 "could not set key or IV: %s\n",
252 gcry_strerror(gcry_error));
253 gcry_cipher_close(gcry_cipher_hd);
254 return 1;
255 }
256 return 0;
257#else
94f03c9a 258 (void)key;
62dec321
PA
259 return 1;
260#endif
261}
262
101309bc
PA
263/*
264 * Confirm RDRAND capabilities for drng entropy source
265 */
266int init_drng_entropy_source(struct rng *ent_src)
267{
268 struct cpuid info;
04361df2
JM
269 /* We need RDRAND, but AESni is optional */
270 const uint32_t features_ecx1_rdrand = 1 << 30;
62dec321 271 const uint32_t features_ecx1_aesni = 1 << 25;
3e89e082 272 static unsigned char key[AES_BLOCK] = {
94f03c9a
PA
273 0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70,
274 0x80,0x90,0xa0,0xb0,0xc0,0xd0,0xe0,0xf0
275 }; /* AES data reduction key */
3e89e082 276 unsigned char xkey[AES_BLOCK]; /* Material to XOR into the key */
94f03c9a
PA
277 int fd;
278 int i;
101309bc 279
62dec321 280 if (!x86_has_cpuid())
101309bc 281 return 1; /* No CPUID instruction */
101309bc
PA
282
283 cpuid(0, 0, &info);
284 if (info.eax < 1)
285 return 1;
286 cpuid(1, 0, &info);
62dec321 287 if (!(info.ecx & features_ecx1_rdrand))
101309bc
PA
288 return 1;
289
62dec321
PA
290 have_aesni = !!(info.ecx & features_ecx1_aesni);
291
94f03c9a 292 /* Randomize the AES data reduction key the best we can */
3164d3eb 293 if (x86_rdrand_bytes(xkey, sizeof xkey) != sizeof xkey)
94f03c9a
PA
294 return 1;
295
296 fd = open("/dev/urandom", O_RDONLY);
297 if (fd >= 0) {
298 read(fd, key, sizeof key);
299 close(fd);
300 }
301
302 for (i = 0; i < sizeof key; i++)
303 key[i] ^= xkey[i];
04361df2 304
932e25b2 305 /* Initialize the IV buffer */
3164d3eb 306 if (x86_rdrand_bytes(iv_buf, CHUNK_SIZE) != CHUNK_SIZE)
932e25b2
PA
307 return 1;
308
94f03c9a 309 if (init_aesni(key) && init_gcrypt(key))
62dec321 310 return 1; /* We need one crypto or the other... */
04361df2 311
101309bc
PA
312 src_list_add(ent_src);
313 /* Bootstrap FIPS tests */
314 ent_src->fipsctx = malloc(sizeof(fips_ctx_t));
315 fips_init(ent_src->fipsctx, 0);
316 return 0;
317}
318
319#else /* Not i386 or x86-64 */
320
321int init_drng_entropy_source(struct rng *ent_src)
322{
323 (void)ent_src;
324 return 1;
325}
326
327int xread_drng(void *buf, size_t size, struct rng *ent_src)
328{
329 (void)buf;
330 (void)size;
331 (void)ent_src;
332
333 return -1;
334}
335
336#endif /* Not i386 or x86-64 */