xref: /freebsd/contrib/bearssl/src/rand/sysrng.c (revision f7c32ed617858bcd22f8d1b03199099d50125721)
1 /*
2  * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 
25 #define BR_ENABLE_INTRINSICS   1
26 #include "inner.h"
27 
28 #if BR_USE_URANDOM
29 #include <sys/types.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #endif
34 
35 #if BR_USE_WIN32_RAND
36 #include <windows.h>
37 #include <wincrypt.h>
38 #pragma comment(lib, "advapi32")
39 #endif
40 
41 #if BR_RDRAND
42 BR_TARGETS_X86_UP
43 BR_TARGET("rdrnd")
44 static int
45 seeder_rdrand(const br_prng_class **ctx)
46 {
47 	unsigned char tmp[32];
48 	size_t u;
49 
50 	for (u = 0; u < sizeof tmp; u += sizeof(uint32_t)) {
51 		int j;
52 		uint32_t x;
53 
54 		/*
55 		 * We use the 32-bit intrinsic so that code is compatible
56 		 * with both 32-bit and 64-bit architectures.
57 		 *
58 		 * Intel recommends trying at least 10 times in case of
59 		 * failure.
60 		 */
61 		for (j = 0; j < 10; j ++) {
62 			if (_rdrand32_step(&x)) {
63 				goto next_word;
64 			}
65 		}
66 		return 0;
67 	next_word:
68 		br_enc32le(tmp + u, x);
69 	}
70 	(*ctx)->update(ctx, tmp, sizeof tmp);
71 	return 1;
72 }
73 BR_TARGETS_X86_DOWN
74 
75 static int
76 rdrand_supported(void)
77 {
78 	/*
79 	 * The RDRND support is bit 30 of ECX, as returned by CPUID.
80 	 */
81 	return br_cpuid(0, 0, 0x40000000, 0);
82 }
83 
84 #endif
85 
86 #if BR_USE_URANDOM
87 static int
88 seeder_urandom(const br_prng_class **ctx)
89 {
90 	int f;
91 
92 	f = open("/dev/urandom", O_RDONLY);
93 	if (f >= 0) {
94 		unsigned char tmp[32];
95 		size_t u;
96 
97 		for (u = 0; u < sizeof tmp;) {
98 			ssize_t len;
99 
100 			len = read(f, tmp + u, (sizeof tmp) - u);
101 			if (len < 0) {
102 				if (errno == EINTR) {
103 					continue;
104 				}
105 				break;
106 			}
107 			u += (size_t)len;
108 		}
109 		close(f);
110 		if (u == sizeof tmp) {
111 			(*ctx)->update(ctx, tmp, sizeof tmp);
112 			return 1;
113 		}
114 	}
115 	return 0;
116 }
117 #endif
118 
119 #if BR_USE_WIN32_RAND
120 static int
121 seeder_win32(const br_prng_class **ctx)
122 {
123 	HCRYPTPROV hp;
124 
125 	if (CryptAcquireContext(&hp, 0, 0, PROV_RSA_FULL,
126 		CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
127 	{
128 		BYTE buf[32];
129 		BOOL r;
130 
131 		r = CryptGenRandom(hp, sizeof buf, buf);
132 		CryptReleaseContext(hp, 0);
133 		if (r) {
134 			(*ctx)->update(ctx, buf, sizeof buf);
135 			return 1;
136 		}
137 	}
138 	return 0;
139 }
140 #endif
141 
142 /* see bearssl_rand.h */
143 br_prng_seeder
144 br_prng_seeder_system(const char **name)
145 {
146 #if BR_RDRAND
147 	if (rdrand_supported()) {
148 		if (name != NULL) {
149 			*name = "rdrand";
150 		}
151 		return &seeder_rdrand;
152 	}
153 #endif
154 #if BR_USE_URANDOM
155 	if (name != NULL) {
156 		*name = "urandom";
157 	}
158 	return &seeder_urandom;
159 #elif BR_USE_WIN32_RAND
160 	if (name != NULL) {
161 		*name = "win32";
162 	}
163 	return &seeder_win32;
164 #else
165 	if (name != NULL) {
166 		*name = "none";
167 	}
168 	return 0;
169 #endif
170 }
171