1/*
2 * Copyright 2008-2018 Aerospike, Inc.
3 *
4 * Portions may be licensed to Aerospike, Inc. under one or more contributor
5 * license agreements.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
8 * use this file except in compliance with the License. You may obtain a copy of
9 * the License at http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations under
15 * the License.
16 */
17#include <citrusleaf/cf_random.h>
18#include <fcntl.h>
19#include <openssl/rand.h>
20#include <pthread.h>
21#include <stdio.h>
22#include <string.h>
23
24#if !defined ENHANCED_ALLOC
25#include <aerospike/as_log_macros.h>
26#endif
27
28#define SEED_SZ 64
29static uint8_t rand_buf[1024 * 8];
30static uint32_t rand_buf_off = 0;
31static int seeded = 0;
32static pthread_mutex_t rand_buf_lock = PTHREAD_MUTEX_INITIALIZER;
33
34#if defined(__linux__) || defined(__FreeBSD__)
35#include <unistd.h>
36
37int
38cf_rand_reload()
39{
40 if (seeded == 0) {
41 int rfd = open("/dev/urandom", O_RDONLY);
42 int rsz = (int)read(rfd, rand_buf, SEED_SZ);
43 if (rsz < SEED_SZ) {
44#if !defined ENHANCED_ALLOC
45 as_log_error("Failed to seed random number generator");
46#endif
47 return(-1);
48 }
49 close(rfd);
50 RAND_seed(rand_buf, rsz);
51 seeded = 1;
52 }
53
54 if (1 != RAND_bytes(rand_buf, sizeof(rand_buf))) {
55#if !defined ENHANCED_ALLOC
56 as_log_error("Failed to reload random buffer");
57#endif
58 return(-1);
59 }
60
61 rand_buf_off = sizeof(rand_buf);
62 return 0;
63}
64
65#elif defined (__APPLE__)
66
67int
68cf_rand_reload()
69{
70 if (seeded == 0) {
71 arc4random_stir();
72 seeded = 1;
73 }
74
75 arc4random_buf(rand_buf, sizeof(rand_buf));
76 rand_buf_off = sizeof(rand_buf);
77 return 0;
78}
79
80#elif defined (_MSC_VER)
81
82#define WIN32_LEAN_AND_MEAN
83#include <windows.h>
84
85int
86cf_rand_reload()
87{
88 // Acquire/Release context every buffer reload.
89 HCRYPTPROV hProvider;
90
91 if (!CryptAcquireContext(&hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
92#if !defined ENHANCED_ALLOC
93 as_log_error("Failed to seed random number generator");
94#endif
95 return -1;
96 }
97
98 if (!CryptGenRandom(hProvider, sizeof(rand_buf), rand_buf)) {
99#if !defined ENHANCED_ALLOC
100 as_log_error("Failed to reload random buffer");
101#endif
102 CryptReleaseContext(hProvider, 0);
103 return -1;
104 }
105
106 CryptReleaseContext(hProvider, 0);
107 rand_buf_off = sizeof(rand_buf);
108 return 0;
109}
110#endif
111
112int
113cf_get_rand_buf(uint8_t *buf, int len)
114{
115 if ((uint32_t)len >= sizeof(rand_buf)) return(-1);
116
117 pthread_mutex_lock(&rand_buf_lock);
118
119 if (rand_buf_off < (uint32_t)len ) {
120 if (-1 == cf_rand_reload()) {
121 pthread_mutex_unlock(&rand_buf_lock);
122 return(-1);
123 }
124 }
125
126 rand_buf_off -= len;
127 memcpy(buf, &rand_buf[rand_buf_off] ,len);
128
129 pthread_mutex_unlock(&rand_buf_lock);
130
131 return(0);
132}
133
134uint64_t
135cf_get_rand64()
136{
137 pthread_mutex_lock(&rand_buf_lock);
138 if (rand_buf_off < sizeof(uint64_t) ) {
139 if (-1 == cf_rand_reload()) {
140 pthread_mutex_unlock(&rand_buf_lock);
141 return(0);
142 }
143 }
144 rand_buf_off -= sizeof(uint64_t);
145 uint64_t r = *(uint64_t *) (&rand_buf[rand_buf_off]);
146 pthread_mutex_unlock(&rand_buf_lock);
147 return(r);
148}
149
150uint32_t
151cf_get_rand32()
152{
153 pthread_mutex_lock(&rand_buf_lock);
154 if (rand_buf_off < sizeof(uint64_t) ) {
155 if (-1 == cf_rand_reload()) {
156 pthread_mutex_unlock(&rand_buf_lock);
157 return(0);
158 }
159 }
160
161 rand_buf_off -= sizeof(uint64_t);
162 uint64_t r = *(uint64_t *) (&rand_buf[rand_buf_off]);
163 pthread_mutex_unlock(&rand_buf_lock);
164 return((uint32_t)r);
165}
166