Line data Source code
1 : /* rndhw.c - Access to the external random daemon
2 : * Copyright (C) 2007 Free Software Foundation, Inc.
3 : * Copyright (C) 2012 Dmitry Kasatkin
4 : *
5 : * This file is part of Libgcrypt.
6 : *
7 : * Libgcrypt is free software; you can redistribute it and/or modify
8 : * it under the terms of the GNU Lesser General Public License as
9 : * published by the Free Software Foundation; either version 2.1 of
10 : * the License, or (at your option) any later version.
11 : *
12 : * Libgcrypt is distributed in the hope that it will be useful,
13 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : * GNU Lesser General Public License for more details.
16 : *
17 : * You should have received a copy of the GNU Lesser General Public
18 : * License along with this program; if not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include <config.h>
22 : #include <stdio.h>
23 : #include <stdlib.h>
24 :
25 : #include "types.h"
26 : #include "g10lib.h"
27 : #include "rand-internal.h"
28 :
29 : #undef USE_PADLOCK
30 : #ifdef ENABLE_PADLOCK_SUPPORT
31 : # ifdef HAVE_GCC_ATTRIBUTE_ALIGNED
32 : # if (defined (__i386__) && SIZEOF_UNSIGNED_LONG == 4) || defined(__x86_64__)
33 : # define USE_PADLOCK 1
34 : # endif
35 : # endif
36 : #endif /*ENABLE_PADLOCK_SUPPORT*/
37 :
38 : #undef USE_DRNG
39 : #ifdef ENABLE_DRNG_SUPPORT
40 : # ifdef HAVE_GCC_ATTRIBUTE_ALIGNED
41 : # if (defined (__i386__) && SIZEOF_UNSIGNED_LONG == 4) || defined(__x86_64__)
42 : # define USE_DRNG 1
43 : # endif
44 : # endif
45 : #endif /*ENABLE_RDRAND_SUPPORT*/
46 :
47 : typedef void (*add_fn_t)(const void*, size_t, enum random_origins);
48 :
49 : /* Keep track on whether the RNG has problems. */
50 : static volatile int rng_failed;
51 :
52 :
53 : #ifdef USE_PADLOCK
54 : static size_t
55 0 : poll_padlock (void (*add)(const void*, size_t, enum random_origins),
56 : enum random_origins origin, int fast)
57 : {
58 : volatile char buffer[64+8] __attribute__ ((aligned (8)));
59 : volatile char *p;
60 : unsigned int nbytes, status;
61 :
62 : /* Peter Gutmann's cryptlib tests again whether the RNG is enabled
63 : but we don't do so. We would have to do this also for our AES
64 : implementaion and that is definitely too time consuming. There
65 : would be a race condition anyway. Thus we assume that the OS
66 : does not change the Padlock initialization while a user process
67 : is running. */
68 0 : p = buffer;
69 0 : nbytes = 0;
70 0 : while (nbytes < 64)
71 : {
72 : #if defined(__x86_64__) && SIZEOF_VOID_P == 8
73 0 : asm volatile
74 : ("movq %1, %%rdi\n\t" /* Set buffer. */
75 : "xorq %%rdx, %%rdx\n\t" /* Request up to 8 bytes. */
76 : ".byte 0x0f, 0xa7, 0xc0\n\t" /* XSTORE RNG. */
77 : : "=a" (status)
78 : : "g" (p)
79 : : "%rdx", "%rdi", "cc"
80 : );
81 : #else
82 : asm volatile
83 : ("movl %1, %%edi\n\t" /* Set buffer. */
84 : "xorl %%edx, %%edx\n\t" /* Request up to 8 bytes. */
85 : ".byte 0x0f, 0xa7, 0xc0\n\t" /* XSTORE RNG. */
86 : : "=a" (status)
87 : : "g" (p)
88 : : "%edx", "%edi", "cc"
89 : );
90 : #endif
91 0 : if ((status & (1<<6)) /* RNG still enabled. */
92 0 : && !(status & (1<<13)) /* von Neumann corrector is enabled. */
93 0 : && !(status & (1<<14)) /* String filter is disabled. */
94 0 : && !(status & 0x1c00) /* BIAS voltage at default. */
95 0 : && (!(status & 0x1f) || (status & 0x1f) == 8) /* Sanity check. */
96 : )
97 : {
98 0 : nbytes += (status & 0x1f);
99 0 : if (fast)
100 0 : break; /* Don't get into the loop with the fast flag set. */
101 0 : p += (status & 0x1f);
102 : }
103 : else
104 : {
105 : /* If there was an error we need to break the loop and
106 : record that there is something wrong with the padlock
107 : RNG. */
108 0 : rng_failed = 1;
109 0 : break;
110 : }
111 : }
112 :
113 0 : if (nbytes)
114 : {
115 0 : (*add) ((void*)buffer, nbytes, origin);
116 0 : wipememory (buffer, nbytes);
117 : }
118 0 : return nbytes;
119 : }
120 : #endif /*USE_PADLOCK*/
121 :
122 :
123 : #ifdef USE_DRNG
124 : # define RDRAND_RETRY_LOOPS 10
125 : # define RDRAND_INT ".byte 0x0f,0xc7,0xf0"
126 : # if defined(__x86_64__) && SIZEOF_UNSIGNED_LONG == 8
127 : # define RDRAND_LONG ".byte 0x48,0x0f,0xc7,0xf0"
128 : # else
129 : # define RDRAND_LONG RDRAND_INT
130 : # endif
131 : static inline int
132 0 : rdrand_long (unsigned long *v)
133 : {
134 : int ok;
135 0 : asm volatile ("1: " RDRAND_LONG "\n\t"
136 : "jc 2f\n\t"
137 : "decl %0\n\t"
138 : "jnz 1b\n\t"
139 : "2:"
140 : : "=r" (ok), "=a" (*v)
141 : : "0" (RDRAND_RETRY_LOOPS)
142 : : "cc");
143 0 : return ok;
144 : }
145 :
146 :
147 : static inline int
148 0 : rdrand_nlong (unsigned long *v, int count)
149 : {
150 0 : while (count--)
151 0 : if (!rdrand_long(v++))
152 0 : return 0;
153 0 : return 1;
154 : }
155 :
156 :
157 : static size_t
158 0 : poll_drng (add_fn_t add, enum random_origins origin, int fast)
159 : {
160 : volatile char buffer[64] __attribute__ ((aligned (8)));
161 0 : unsigned int nbytes = sizeof (buffer);
162 :
163 : (void)fast;
164 :
165 0 : if (!rdrand_nlong ((unsigned long *)buffer, sizeof(buffer)/sizeof(long)))
166 0 : return 0;
167 0 : (*add)((void *)buffer, nbytes, origin);
168 0 : return nbytes;
169 : }
170 : #endif /*USE_DRNG*/
171 :
172 :
173 : int
174 0 : _gcry_rndhw_failed_p (void)
175 : {
176 0 : return rng_failed;
177 : }
178 :
179 :
180 : /* Try to read random from a hardware RNG if a fast one is
181 : available. */
182 : void
183 0 : _gcry_rndhw_poll_fast (void (*add)(const void*, size_t, enum random_origins),
184 : enum random_origins origin)
185 : {
186 : (void)add;
187 : (void)origin;
188 :
189 : #ifdef USE_DRNG
190 0 : if ((_gcry_get_hw_features () & HWF_INTEL_RDRAND))
191 0 : poll_drng (add, origin, 1);
192 : #endif
193 : #ifdef USE_PADLOCK
194 0 : if ((_gcry_get_hw_features () & HWF_PADLOCK_RNG))
195 0 : poll_padlock (add, origin, 1);
196 : #endif
197 0 : }
198 :
199 :
200 : /* Read 64 bytes from a hardware RNG and return the number of bytes
201 : actually read. */
202 : size_t
203 0 : _gcry_rndhw_poll_slow (void (*add)(const void*, size_t, enum random_origins),
204 : enum random_origins origin)
205 : {
206 0 : size_t nbytes = 0;
207 :
208 : (void)add;
209 : (void)origin;
210 :
211 : #ifdef USE_DRNG
212 0 : if ((_gcry_get_hw_features () & HWF_INTEL_RDRAND))
213 0 : nbytes += poll_drng (add, origin, 0);
214 : #endif
215 : #ifdef USE_PADLOCK
216 0 : if ((_gcry_get_hw_features () & HWF_PADLOCK_RNG))
217 0 : nbytes += poll_padlock (add, origin, 0);
218 : #endif
219 :
220 0 : return nbytes;
221 : }
|