2 * Copyright (c) 2016-2020 Samsung Electronics Co., Ltd All Rights Reserved
4 * Contact: Krzysztof Jackiewicz <k.jackiewicz@samsung.com>
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License
31 #include <sys/syscall.h>
32 #include <sys/types.h>
34 #include <linux/random.h>
36 #include <openssl/crypto.h>
37 #include <openssl/evp.h>
38 #include <openssl/rand.h>
39 #include <openssl/err.h>
40 #include <openssl/rand.h>
42 #include <yaca_crypto.h>
43 #include <yaca_error.h>
47 static __thread bool current_thread_initialized = false;
48 static size_t threads_cnt = 0;
49 static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
50 static const RAND_METHOD *saved_rand_method = NULL;
52 static int urandom_fd = -2;
53 #endif /* SYS_getrandom */
55 static int getrandom_wrapper(unsigned char *buf, int num)
58 size_t remaining = num;
61 assert(urandom_fd != -2);
62 #endif /* SYS_getrandom */
64 while (remaining > 0) {
66 ssize_t n = TEMP_FAILURE_RETRY(syscall(SYS_getrandom, buf + received, remaining, 0));
67 #else /* SYS_getrandom */
68 ssize_t n = TEMP_FAILURE_RETRY(read(urandom_fd, buf + received, remaining));
69 #endif /* SYS_getrandom */
81 static int RAND_METHOD_seed(UNUSED const void *buf, UNUSED int num)
86 static int RAND_METHOD_add(UNUSED const void *buf, UNUSED int num, UNUSED double entropy)
91 static int RAND_METHOD_bytes(unsigned char *buf, int num)
93 return getrandom_wrapper(buf, num);
96 static void RAND_METHOD_cleanup(void)
100 static int RAND_METHOD_pseudorand(UNUSED unsigned char *buf, UNUSED int num)
102 return getrandom_wrapper(buf, num);
105 static int RAND_METHOD_status(void)
109 int n = syscall(SYS_getrandom, &tmp, 1, GRND_NONBLOCK);
110 if (n == -1 && errno == EAGAIN)
112 #endif /* SYS_getrandom */
117 static const RAND_METHOD new_rand_method = {
122 RAND_METHOD_pseudorand,
126 API int yaca_initialize(void)
128 int ret = YACA_ERROR_NONE;
130 /* no calling yaca_initialize() twice on the same thread */
131 if (current_thread_initialized)
132 return YACA_ERROR_INTERNAL;
134 pthread_mutex_lock(&init_mutex);
136 if (threads_cnt == 0) {
138 #ifndef SYS_getrandom
139 if (urandom_fd == -2) {
143 fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
144 } while (fd == -1 && errno == EINTR);
147 ret = YACA_ERROR_INTERNAL;
153 #endif /* SYS_getrandom */
157 /* Use getrandom from urandom pool by default.
158 * As per the following:
159 * http://www.2uo.de/myths-about-urandom/
160 * http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/
162 * OpenSSL's PRNG has issues:
163 * https://eprint.iacr.org/2016/367.pdf
165 * Some other things to check/consider for the future:
166 * - entropy on a mobile device (no mouse/keyboard)
167 * - hardware random generator (RdRand on new Intels, Samsung hardware?)
169 saved_rand_method = RAND_get_rand_method();
170 RAND_set_rand_method(&new_rand_method);
172 OpenSSL_add_all_digests();
173 OpenSSL_add_all_ciphers();
177 * - We should also decide on OpenSSL config.
178 * - Here's a good tutorial for initialization and cleanup:
179 * https://wiki.openssl.org/index.php/Library_Initialization
183 current_thread_initialized = true;
186 #if !defined SYS_getrandom
188 #endif /* !defined SYS_getrandom */
190 pthread_mutex_unlock(&init_mutex);
195 API void yaca_cleanup(void)
197 /* calling cleanup twice on the same thread is a NOP */
198 if (!current_thread_initialized)
201 /* per thread cleanup */
202 CRYPTO_cleanup_all_ex_data();
204 pthread_mutex_lock(&init_mutex);
206 /* last one turns off the light */
207 if (threads_cnt == 1) {
211 RAND_set_rand_method(saved_rand_method);
213 #ifndef SYS_getrandom
216 #endif /* SYS_getrandom */
220 assert(threads_cnt > 0);
223 current_thread_initialized = false;
225 pthread_mutex_unlock(&init_mutex);
228 API int yaca_malloc(size_t size, void **memory)
230 if (size == 0 || memory == NULL)
231 return YACA_ERROR_INVALID_PARAMETER;
233 *memory = OPENSSL_malloc(size);
234 if (*memory == NULL) {
235 const int ret = YACA_ERROR_OUT_OF_MEMORY;
240 return YACA_ERROR_NONE;
243 API int yaca_zalloc(size_t size, void **memory)
245 int ret = yaca_malloc(size, memory);
246 if (ret != YACA_ERROR_NONE)
249 memset(*memory, 0, size);
251 return YACA_ERROR_NONE;
254 API int yaca_realloc(size_t size, void **memory)
256 if (size == 0 || memory == NULL)
257 return YACA_ERROR_INVALID_PARAMETER;
259 void *tmp = OPENSSL_realloc(*memory, size);
261 const int ret = YACA_ERROR_OUT_OF_MEMORY;
268 return YACA_ERROR_NONE;
271 API void yaca_free(void *memory)
273 OPENSSL_free(memory);
276 API int yaca_randomize_bytes(char *data, size_t data_len)
280 if (data == NULL || data_len == 0)
281 return YACA_ERROR_INVALID_PARAMETER;
283 ret = RAND_bytes((unsigned char *)data, data_len);
285 ret = YACA_ERROR_INTERNAL;
290 return YACA_ERROR_NONE;
293 API int yaca_context_set_property(yaca_context_h ctx, yaca_property_e property,
294 const void *value, size_t value_len)
296 if (ctx == YACA_CONTEXT_NULL || ctx->set_property == NULL)
297 return YACA_ERROR_INVALID_PARAMETER;
299 return ctx->set_property(ctx, property, value, value_len);
302 API int yaca_context_get_property(const yaca_context_h ctx, yaca_property_e property,
303 void **value, size_t *value_len)
305 if (ctx == YACA_CONTEXT_NULL || ctx->get_property == NULL)
306 return YACA_ERROR_INVALID_PARAMETER;
308 return ctx->get_property(ctx, property, value, value_len);
311 API void yaca_context_destroy(yaca_context_h ctx)
313 if (ctx != YACA_CONTEXT_NULL) {
314 assert(ctx->context_destroy != NULL);
315 ctx->context_destroy(ctx);
320 API int yaca_context_get_output_length(const yaca_context_h ctx,
321 size_t input_len, size_t *output_len)
323 if (ctx == YACA_CONTEXT_NULL || output_len == NULL ||
324 ctx->get_output_length == NULL)
325 return YACA_ERROR_INVALID_PARAMETER;
327 return ctx->get_output_length(ctx, input_len, output_len);
330 API int yaca_memcmp(const void *first, const void *second, size_t len)
332 if (len > 0 && (first == NULL || second == NULL))
333 return YACA_ERROR_INVALID_PARAMETER;
335 if (CRYPTO_memcmp(first, second, len) == 0)
336 return YACA_ERROR_NONE;
338 return YACA_ERROR_DATA_MISMATCH;