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 #if OPENSSL_VERSION_NUMBER < 0x10100000L
48 static pthread_mutex_t *mutexes = NULL;
49 #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
51 static __thread bool current_thread_initialized = false;
52 static size_t threads_cnt = 0;
53 static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
54 static const RAND_METHOD *saved_rand_method = NULL;
56 static int urandom_fd = -2;
57 #endif /* SYS_getrandom */
59 static int getrandom_wrapper(unsigned char *buf, int num)
62 size_t remaining = num;
65 assert(urandom_fd != -2);
66 #endif /* SYS_getrandom */
68 while (remaining > 0) {
70 ssize_t n = TEMP_FAILURE_RETRY(syscall(SYS_getrandom, buf + received, remaining, 0));
71 #else /* SYS_getrandom */
72 ssize_t n = TEMP_FAILURE_RETRY(read(urandom_fd, buf + received, remaining));
73 #endif /* SYS_getrandom */
85 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
87 static int RAND_METHOD_seed(UNUSED const void *buf, UNUSED int num)
92 static int RAND_METHOD_add(UNUSED const void *buf, UNUSED int num, UNUSED double entropy)
97 #else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
99 static void RAND_METHOD_seed(UNUSED const void *buf, UNUSED int num)
103 static void RAND_METHOD_add(UNUSED const void *buf, UNUSED int num, UNUSED double entropy)
107 #endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
109 static int RAND_METHOD_bytes(unsigned char *buf, int num)
111 return getrandom_wrapper(buf, num);
114 static void RAND_METHOD_cleanup(void)
118 static int RAND_METHOD_pseudorand(UNUSED unsigned char *buf, UNUSED int num)
120 return getrandom_wrapper(buf, num);
123 static int RAND_METHOD_status(void)
127 int n = syscall(SYS_getrandom, &tmp, 1, GRND_NONBLOCK);
128 if (n == -1 && errno == EAGAIN)
130 #endif /* SYS_getrandom */
135 static const RAND_METHOD new_rand_method = {
140 RAND_METHOD_pseudorand,
144 #if OPENSSL_VERSION_NUMBER < 0x10100000L
146 static void locking_callback(int mode, int type, UNUSED const char *file, UNUSED int line)
148 /* Ignore NULL mutexes and lock/unlock error codes as we can't do anything
154 if (mode & CRYPTO_LOCK)
155 pthread_mutex_lock(&mutexes[type]);
156 else if (mode & CRYPTO_UNLOCK)
157 pthread_mutex_unlock(&mutexes[type]);
160 static unsigned long thread_id_callback()
162 return pthread_self();
165 static void destroy_mutexes(int count)
167 if (mutexes != NULL) {
168 for (int i = 0; i < count; i++) {
169 /* Ignore returned value as we can't do anything about it */
170 pthread_mutex_destroy(&mutexes[i]);
177 #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
179 API int yaca_initialize(void)
181 int ret = YACA_ERROR_NONE;
183 /* no calling yaca_initialize() twice on the same thread */
184 if (current_thread_initialized)
185 return YACA_ERROR_INTERNAL;
187 pthread_mutex_lock(&init_mutex);
189 if (threads_cnt == 0) {
191 #ifndef SYS_getrandom
192 if (urandom_fd == -2) {
196 fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
197 } while (fd == -1 && errno == EINTR);
200 ret = YACA_ERROR_INTERNAL;
206 #endif /* SYS_getrandom */
210 /* Use getrandom from urandom pool by default.
211 * As per the following:
212 * http://www.2uo.de/myths-about-urandom/
213 * http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/
215 * OpenSSL's PRNG has issues:
216 * https://eprint.iacr.org/2016/367.pdf
218 * Some other things to check/consider for the future:
219 * - entropy on a mobile device (no mouse/keyboard)
220 * - hardware random generator (RdRand on new Intels, Samsung hardware?)
222 saved_rand_method = RAND_get_rand_method();
223 RAND_set_rand_method(&new_rand_method);
225 OpenSSL_add_all_digests();
226 OpenSSL_add_all_ciphers();
228 #if OPENSSL_VERSION_NUMBER < 0x10100000L
229 /* enable threads support */
230 assert(mutexes == NULL);
232 if (CRYPTO_num_locks() > 0) {
233 ret = yaca_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t),
236 if (ret != YACA_ERROR_NONE)
239 for (int i = 0; i < CRYPTO_num_locks(); i++) {
240 if (pthread_mutex_init(&mutexes[i], NULL) != 0) {
241 ret = YACA_ERROR_NONE;
244 ret = YACA_ERROR_OUT_OF_MEMORY;
251 ret = YACA_ERROR_INTERNAL;
259 CRYPTO_set_id_callback(thread_id_callback);
260 CRYPTO_set_locking_callback(locking_callback);
262 #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
266 * - We should also decide on OpenSSL config.
267 * - Here's a good tutorial for initialization and cleanup:
268 * https://wiki.openssl.org/index.php/Library_Initialization
272 current_thread_initialized = true;
275 #if OPENSSL_VERSION_NUMBER < 0x10100000L || !defined SYS_getrandom
277 #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L || !defined SYS_getrandom */
279 pthread_mutex_unlock(&init_mutex);
284 API void yaca_cleanup(void)
286 /* calling cleanup twice on the same thread is a NOP */
287 if (!current_thread_initialized)
290 /* per thread cleanup */
291 #if OPENSSL_VERSION_NUMBER < 0x10100000L
292 ERR_remove_thread_state(NULL);
293 #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
294 CRYPTO_cleanup_all_ex_data();
296 pthread_mutex_lock(&init_mutex);
298 /* last one turns off the light */
299 if (threads_cnt == 1) {
303 RAND_set_rand_method(saved_rand_method);
305 #ifndef SYS_getrandom
308 #endif /* SYS_getrandom */
310 #if OPENSSL_VERSION_NUMBER < 0x10100000L
311 /* threads support cleanup */
312 CRYPTO_set_id_callback(NULL);
313 CRYPTO_set_locking_callback(NULL);
315 destroy_mutexes(CRYPTO_num_locks());
316 #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
319 assert(threads_cnt > 0);
322 current_thread_initialized = false;
324 pthread_mutex_unlock(&init_mutex);
327 API int yaca_malloc(size_t size, void **memory)
329 if (size == 0 || memory == NULL)
330 return YACA_ERROR_INVALID_PARAMETER;
332 *memory = OPENSSL_malloc(size);
333 if (*memory == NULL) {
334 const int ret = YACA_ERROR_OUT_OF_MEMORY;
339 return YACA_ERROR_NONE;
342 API int yaca_zalloc(size_t size, void **memory)
344 int ret = yaca_malloc(size, memory);
345 if (ret != YACA_ERROR_NONE)
348 memset(*memory, 0, size);
350 return YACA_ERROR_NONE;
353 API int yaca_realloc(size_t size, void **memory)
355 if (size == 0 || memory == NULL)
356 return YACA_ERROR_INVALID_PARAMETER;
358 void *tmp = OPENSSL_realloc(*memory, size);
360 const int ret = YACA_ERROR_OUT_OF_MEMORY;
367 return YACA_ERROR_NONE;
370 API void yaca_free(void *memory)
372 OPENSSL_free(memory);
375 API int yaca_randomize_bytes(char *data, size_t data_len)
379 if (data == NULL || data_len == 0)
380 return YACA_ERROR_INVALID_PARAMETER;
382 ret = RAND_bytes((unsigned char *)data, data_len);
384 ret = YACA_ERROR_INTERNAL;
389 return YACA_ERROR_NONE;
392 API int yaca_context_set_property(yaca_context_h ctx, yaca_property_e property,
393 const void *value, size_t value_len)
395 if (ctx == YACA_CONTEXT_NULL || ctx->set_property == NULL)
396 return YACA_ERROR_INVALID_PARAMETER;
398 return ctx->set_property(ctx, property, value, value_len);
401 API int yaca_context_get_property(const yaca_context_h ctx, yaca_property_e property,
402 void **value, size_t *value_len)
404 if (ctx == YACA_CONTEXT_NULL || ctx->get_property == NULL)
405 return YACA_ERROR_INVALID_PARAMETER;
407 return ctx->get_property(ctx, property, value, value_len);
410 API void yaca_context_destroy(yaca_context_h ctx)
412 if (ctx != YACA_CONTEXT_NULL) {
413 assert(ctx->context_destroy != NULL);
414 ctx->context_destroy(ctx);
419 API int yaca_context_get_output_length(const yaca_context_h ctx,
420 size_t input_len, size_t *output_len)
422 if (ctx == YACA_CONTEXT_NULL || output_len == NULL ||
423 ctx->get_output_length == NULL)
424 return YACA_ERROR_INVALID_PARAMETER;
426 return ctx->get_output_length(ctx, input_len, output_len);
429 API int yaca_memcmp(const void *first, const void *second, size_t len)
431 if (len > 0 && (first == NULL || second == NULL))
432 return YACA_ERROR_INVALID_PARAMETER;
434 if (CRYPTO_memcmp(first, second, len) == 0)
435 return YACA_ERROR_NONE;
437 return YACA_ERROR_DATA_MISMATCH;