1/*
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)) {
163        fprintf(stderr, "error read %s: %s", dev_random_file, strerror(errno));
164        exit(1);
165    }
166    else if (nread != sizeof(r)) {
167        fprintf(stderr, "error short read %s", dev_random_file);
168        exit(1);
169    }
170    close(fd);
171    return r;
172}
173
174#endif
175
176
177/* get_cryptgenrandom_seed */
178
179#ifdef WIN32
180
181#define HAVE_CRYPTGENRANDOM 1
182
183#include <windows.h>
184#pragma comment(lib, "advapi32.lib")
185
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}
238