]> git.ipfire.org Git - thirdparty/rng-tools.git/blame - rngd_rdrand.c
Provide support for RDRAND capable systems that don't have AES-NI.
[thirdparty/rng-tools.git] / rngd_rdrand.c
CommitLineData
101309bc
PA
1/*
2 * Copyright (c) 2012, Intel Corporation
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
56/* Get data from RDRAND */
57extern int x86_rdrand_nlong(void *ptr, size_t count);
58/* Conditioning RDRAND for seed-grade entropy */
59extern void x86_aes_mangle(void *data, void *state);
60
61/* Checking eflags to confirm cpuid instruction available */
62/* Only necessary for 32 bit processors */
63#if defined (__i386__)
64static int x86_has_eflag(uint32_t flag)
65{
66 uint32_t f0, f1;
67 asm("pushfl ; "
68 "pushfl ; "
69 "popl %0 ; "
70 "movl %0,%1 ; "
71 "xorl %2,%1 ; "
72 "pushl %1 ; "
73 "popfl ; "
74 "pushfl ; "
75 "popl %1 ; "
76 "popfl"
77 : "=&r" (f0), "=&r" (f1)
78 : "ri" (flag));
79 return !!((f0^f1) & flag);
80}
81#endif
82
04361df2 83/* Calling cpuid instruction to verify rdrand and aes-ni capability */
101309bc
PA
84static void cpuid(unsigned int leaf, unsigned int subleaf, struct cpuid *out)
85{
86#ifdef __i386__
87 /* %ebx is a forbidden register if we compile with -fPIC or -fPIE */
88 asm volatile("movl %%ebx,%0 ; cpuid ; xchgl %%ebx,%0"
89 : "=r" (out->ebx),
90 "=a" (out->eax),
91 "=c" (out->ecx),
92 "=d" (out->edx)
93 : "a" (leaf), "c" (subleaf));
94#else
95 asm volatile("cpuid"
96 : "=b" (out->ebx),
97 "=a" (out->eax),
98 "=c" (out->ecx),
99 "=d" (out->edx)
100 : "a" (leaf), "c" (subleaf));
101#endif
102}
103
932e25b2
PA
104/* Read data from the drng in chunks of 128 bytes for AES scrambling */
105#define CHUNK_SIZE (16*8)
106
107static unsigned char iv_buf[CHUNK_SIZE] __attribute__((aligned(128)));
04361df2
JM
108static int have_aesni= 0;
109
110/* Necessary if we have RDRAND but not AES-NI */
111
112#ifdef HAVE_LIBGCRYPT
113
114#define MIN_GCRYPT_VERSION "1.0.0"
115
116static gcry_cipher_hd_t gcry_cipher_hd;
117
118/* Arbitrary 128-bit AES key 0x00102030405060708090A0B0C0D0E0F0 */
119
120static const unsigned char key[16]= {
121 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,
122 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0
123};
124
125#endif
126
932e25b2 127
101309bc
PA
128int xread_drng(void *buf, size_t size, struct rng *ent_src)
129{
932e25b2
PA
130 char *p = buf;
131 size_t chunk;
132 const int rdrand_round_count = 512;
133 unsigned char tmp[CHUNK_SIZE] __attribute__((aligned(128)));
101309bc
PA
134 int i;
135
932e25b2
PA
136 while (size) {
137 for (i = 0; i < rdrand_round_count; i++) {
138 if (!x86_rdrand_nlong(tmp, CHUNK_SIZE/sizeof(long))) {
139 message(LOG_DAEMON|LOG_ERR, "read error\n");
140 return -1;
101309bc 141 }
04361df2
JM
142
143 // Use 128-bit AES in CBC mode to mangle our random data
144
145 if ( have_aesni ) x86_aes_mangle(tmp, iv_buf);
146 else {
147#ifdef HAVE_LIBGCRYPT
148 gcry_error_t gcry_error;
149
150 /* Encrypt tmp in-place. */
151
152 gcry_error= gcry_cipher_encrypt(gcry_cipher_hd,
153 tmp, CHUNK_SIZE, 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#else
162 return -1;
163#endif
164 }
101309bc 165 }
932e25b2
PA
166 chunk = (sizeof(tmp) > size) ? size : sizeof(tmp);
167 memcpy(p, tmp, chunk);
168 p += chunk;
169 size -= chunk;
101309bc
PA
170 }
171
101309bc
PA
172 return 0;
173}
174
175/*
176 * Confirm RDRAND capabilities for drng entropy source
177 */
178int init_drng_entropy_source(struct rng *ent_src)
179{
180 struct cpuid info;
04361df2
JM
181 /* We need RDRAND, but AESni is optional */
182 const uint32_t features_ecx1_rdrand = 1 << 30;
183 const uint32_t features_ecx1_aesni = 1 << 25;
101309bc
PA
184
185#if defined(__i386__)
186 if (!x86_has_eflag(1 << 21))
187 return 1; /* No CPUID instruction */
188#endif
189
190 cpuid(0, 0, &info);
191 if (info.eax < 1)
192 return 1;
193 cpuid(1, 0, &info);
04361df2 194 if (! (info.ecx & features_ecx1_rdrand) )
101309bc
PA
195 return 1;
196
04361df2
JM
197 have_aesni= (info.ecx & features_ecx1_aesni) ? 1 : 0;
198#ifndef HAVE_LIBGCRYPT
199 if ( ! have_aesni ) return 1;
200#endif
201
932e25b2
PA
202 /* Initialize the IV buffer */
203 if (!x86_rdrand_nlong(iv_buf, CHUNK_SIZE/sizeof(long)))
204 return 1;
205
04361df2
JM
206#ifdef HAVE_LIBGCRYPT
207 if ( ! have_aesni ) {
208 gcry_error_t gcry_error;
209
210 if (! gcry_check_version(MIN_GCRYPT_VERSION) ) {
211 message(LOG_DAEMON|LOG_ERR,
212 "libgcrypt version mismatch: have %s, require >= %s\n",
213 gcry_check_version(NULL), MIN_GCRYPT_VERSION);
214 return 1;
215 }
216
217 gcry_error= gcry_cipher_open(&gcry_cipher_hd,
218 GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC, 0);
219
220 if ( ! gcry_error ) {
221 gcry_error= gcry_cipher_setkey(gcry_cipher_hd, key, 16);
222 }
223
224 if ( ! gcry_error ) {
225 /*
226 * Only need the first 16 bytes of iv_buf. AES-NI can
227 * encrypt multiple blocks in parallel but we can't.
228 */
229
230 gcry_error= gcry_cipher_setiv(gcry_cipher_hd, iv_buf, 16);
231 }
232
233 if ( gcry_error ) {
234 message(LOG_DAEMON|LOG_ERR,
235 "could not set key or IV: %s\n",
236 gcry_strerror(gcry_error));
237 gcry_cipher_close(gcry_cipher_hd);
238 return 1;
239 }
240 }
241#endif
242
101309bc
PA
243 src_list_add(ent_src);
244 /* Bootstrap FIPS tests */
245 ent_src->fipsctx = malloc(sizeof(fips_ctx_t));
246 fips_init(ent_src->fipsctx, 0);
247 return 0;
248}
249
250#else /* Not i386 or x86-64 */
251
252int init_drng_entropy_source(struct rng *ent_src)
253{
254 (void)ent_src;
255 return 1;
256}
257
258int xread_drng(void *buf, size_t size, struct rng *ent_src)
259{
260 (void)buf;
261 (void)size;
262 (void)ent_src;
263
264 return -1;
265}
266
267#endif /* Not i386 or x86-64 */