blob: f6719298039272906df1bb0b7244235e42f2385b [file] [log] [blame]
Michael Clark64e36902014-04-09 13:48:21 +08001/*
2 * random_seed.c
3 *
4 * Copyright (c) 2013 Metaparadigm Pte. Ltd.
5 * Michael Clark <michael@metaparadigm.com>
6 *
7 * This library is free software; you can redistribute it and/or modify
8 * it under the terms of the MIT license. See COPYING for details.
9 *
10 */
11
12#include <stdio.h>
13#include "config.h"
14
15#define DEBUG_SEED(s)
16
17
18#if defined ENABLE_RDRAND
19
20/* cpuid */
21
22#if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
23#define HAS_X86_CPUID 1
24
25static void do_cpuid(int regs[], int h)
26{
27 __asm__ __volatile__(
28#if defined __x86_64__
29 "pushq %%rbx;\n"
30#else
31 "pushl %%ebx;\n"
32#endif
33 "cpuid;\n"
34#if defined __x86_64__
35 "popq %%rbx;\n"
36#else
37 "popl %%ebx;\n"
38#endif
39 : "=a"(regs[0]), [ebx] "=r"(regs[1]), "=c"(regs[2]), "=d"(regs[3])
40 : "a"(h));
41}
42
43#elif defined _MSC_VER
44
45#define HAS_X86_CPUID 1
46#define do_cpuid __cpuid
47
48#endif
49
50/* has_rdrand */
51
52#if HAS_X86_CPUID
53
54static int has_rdrand()
55{
56 // CPUID.01H:ECX.RDRAND[bit 30] == 1
57 int regs[4];
58 do_cpuid(regs, 1);
59 return (regs[2] & (1 << 30)) != 0;
60}
61
62#endif
63
64/* get_rdrand_seed - GCC x86 and X64 */
65
66#if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
67
68#define HAVE_RDRAND 1
69
70static int get_rdrand_seed()
71{
72 DEBUG_SEED("get_rdrand_seed");
73 int _eax;
74 // rdrand eax
75 __asm__ __volatile__("1: .byte 0x0F\n"
76 " .byte 0xC7\n"
77 " .byte 0xF0\n"
78 " jnc 1b;\n"
79 : "=a" (_eax));
80 return _eax;
81}
82
83#endif
84
85#if defined _MSC_VER
86
87#if _MSC_VER >= 1700
88#define HAVE_RDRAND 1
89
90/* get_rdrand_seed - Visual Studio 2012 and above */
91
92static int get_rdrand_seed()
93{
94 DEBUG_SEED("get_rdrand_seed");
95 int r;
96 while (_rdrand32_step(&r) == 0);
97 return r;
98}
99
100#elif defined _M_IX86
101#define HAVE_RDRAND 1
102
103/* get_rdrand_seed - Visual Studio 2010 and below - x86 only */
104
105static int get_rdrand_seed()
106{
107 DEBUG_SEED("get_rdrand_seed");
108 int _eax;
109retry:
110 // rdrand eax
111 __asm _emit 0x0F __asm _emit 0xC7 __asm _emit 0xF0
112 __asm jnc retry
113 __asm mov _eax, eax
114 return _eax;
115}
116
117#endif
118#endif
119
120#endif /* defined ENABLE_RDRAND */
121
122
123/* has_dev_urandom */
124
125#if defined (__APPLE__) || defined(__unix__) || defined(__linux__)
126
127#include <string.h>
128#include <fcntl.h>
129#include <unistd.h>
130#include <errno.h>
131#include <stdlib.h>
132#include <sys/stat.h>
133
134#define HAVE_DEV_RANDOM 1
135
136static const char *dev_random_file = "/dev/urandom";
137
138static int has_dev_urandom()
139{
140 struct stat buf;
141 if (stat(dev_random_file, &buf)) {
142 return 0;
143 }
144 return ((buf.st_mode & S_IFCHR) != 0);
145}
146
147
148/* get_dev_random_seed */
149
150static int get_dev_random_seed()
151{
152 DEBUG_SEED("get_dev_random_seed");
153
154 int fd = open(dev_random_file, O_RDONLY);
155 if (fd < 0) {
156 fprintf(stderr, "error opening %s: %s", dev_random_file, strerror(errno));
157 exit(1);
158 }
159
160 int r;
161 ssize_t nread = read(fd, &r, sizeof(r));
162 if (nread != sizeof(r)) {
Alexandru Costache4841c482014-07-04 11:58:14 +0300163 fprintf(stderr, "error short read %s: %s", dev_random_file, strerror(errno));
Michael Clark64e36902014-04-09 13:48:21 +0800164 exit(1);
165 }
Alexandru Costache4841c482014-07-04 11:58:14 +0300166
Michael Clark64e36902014-04-09 13:48:21 +0800167 close(fd);
168 return r;
169}
170
171#endif
172
173
174/* get_cryptgenrandom_seed */
175
176#ifdef WIN32
177
178#define HAVE_CRYPTGENRANDOM 1
179
180#include <windows.h>
Andrew Stubbsca0ebe02014-08-04 11:44:25 +0100181#include <wincrypt.h>
182#ifndef __GNUC__
Michael Clark64e36902014-04-09 13:48:21 +0800183#pragma comment(lib, "advapi32.lib")
Andrew Stubbsca0ebe02014-08-04 11:44:25 +0100184#endif
Michael Clark64e36902014-04-09 13:48:21 +0800185
186static int get_cryptgenrandom_seed()
187{
188 DEBUG_SEED("get_cryptgenrandom_seed");
189
190 HCRYPTPROV hProvider = 0;
191 int r;
192
193 if (!CryptAcquireContextW(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
194 fprintf(stderr, "error CryptAcquireContextW");
195 exit(1);
196 }
197
198 if (!CryptGenRandom(hProvider, sizeof(r), (BYTE*)&r)) {
199 fprintf(stderr, "error CryptGenRandom");
200 exit(1);
201 }
202
203 CryptReleaseContext(hProvider, 0);
204
205 return r;
206}
207
208#endif
209
210
211/* get_time_seed */
212
213#include <time.h>
214
215static int get_time_seed()
216{
217 DEBUG_SEED("get_time_seed");
218
219 return (int)time(NULL) * 433494437;
220}
221
222
223/* json_c_get_random_seed */
224
225int json_c_get_random_seed()
226{
227#if HAVE_RDRAND
228 if (has_rdrand()) return get_rdrand_seed();
229#endif
230#if HAVE_DEV_RANDOM
231 if (has_dev_urandom()) return get_dev_random_seed();
232#endif
233#if HAVE_CRYPTGENRANDOM
234 return get_cryptgenrandom_seed();
235#endif
236 return get_time_seed();
237}