98e941ac0a82a739cba7c079e6f6f6728db636ef
[platform/core/security/yaca.git] / src / crypto.c
1 /*
2  *  Copyright (c) 2016-2020 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Contact: Krzysztof Jackiewicz <k.jackiewicz@samsung.com>
5  *
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
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
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
17  */
18
19 /**
20  * @file crypto.c
21  * @brief
22  */
23
24 #define _GNU_SOURCE
25 #include <assert.h>
26 #include <string.h>
27 #include <pthread.h>
28 #include <stdbool.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <sys/syscall.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <linux/random.h>
35
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>
41
42 #include <yaca_crypto.h>
43 #include <yaca_error.h>
44
45 #include "internal.h"
46
47 #if OPENSSL_VERSION_NUMBER < 0x10100000L
48 static pthread_mutex_t *mutexes = NULL;
49 #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
50
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;
55 #ifndef SYS_getrandom
56 static int urandom_fd = -2;
57 #endif  /* SYS_getrandom */
58
59 static int getrandom_wrapper(unsigned char *buf, int num)
60 {
61         size_t received = 0;
62         size_t remaining = num;
63
64 #ifndef SYS_getrandom
65         assert(urandom_fd != -2);
66 #endif /* SYS_getrandom */
67
68         while (remaining > 0) {
69 #ifdef SYS_getrandom
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 */
74
75                 if (n == -1)
76                         return 0;
77
78                 received += n;
79                 remaining -= n;
80         }
81
82         return 1;
83 }
84
85 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
86
87 static int RAND_METHOD_seed(UNUSED const void *buf, UNUSED int num)
88 {
89         return 1;
90 }
91
92 static int RAND_METHOD_add(UNUSED const void *buf, UNUSED int num, UNUSED double entropy)
93 {
94         return 1;
95 }
96
97 #else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
98
99 static void RAND_METHOD_seed(UNUSED const void *buf, UNUSED int num)
100 {
101 }
102
103 static void RAND_METHOD_add(UNUSED const void *buf, UNUSED int num, UNUSED double entropy)
104 {
105 }
106
107 #endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
108
109 static int RAND_METHOD_bytes(unsigned char *buf, int num)
110 {
111         return getrandom_wrapper(buf, num);
112 }
113
114 static void RAND_METHOD_cleanup(void)
115 {
116 }
117
118 static int RAND_METHOD_pseudorand(UNUSED unsigned char *buf, UNUSED int num)
119 {
120         return getrandom_wrapper(buf, num);
121 }
122
123 static int RAND_METHOD_status(void)
124 {
125 #ifdef SYS_getrandom
126         char tmp;
127         int n = syscall(SYS_getrandom, &tmp, 1, GRND_NONBLOCK);
128         if (n == -1 && errno == EAGAIN)
129                 return 0;
130 #endif /* SYS_getrandom */
131
132         return 1;
133 }
134
135 static const RAND_METHOD new_rand_method = {
136         RAND_METHOD_seed,
137         RAND_METHOD_bytes,
138         RAND_METHOD_cleanup,
139         RAND_METHOD_add,
140         RAND_METHOD_pseudorand,
141         RAND_METHOD_status,
142 };
143
144 #if OPENSSL_VERSION_NUMBER < 0x10100000L
145
146 static void locking_callback(int mode, int type, UNUSED const char *file, UNUSED int line)
147 {
148         /* Ignore NULL mutexes and lock/unlock error codes as we can't do anything
149          * about them. */
150
151         if (mutexes == NULL)
152                 return;
153
154         if (mode & CRYPTO_LOCK)
155                 pthread_mutex_lock(&mutexes[type]);
156         else if (mode & CRYPTO_UNLOCK)
157                 pthread_mutex_unlock(&mutexes[type]);
158 }
159
160 static unsigned long thread_id_callback()
161 {
162         return pthread_self();
163 }
164
165 static void destroy_mutexes(int count)
166 {
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]);
171                 }
172                 yaca_free(mutexes);
173                 mutexes = NULL;
174         }
175 }
176
177 #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
178
179 API int yaca_initialize(void)
180 {
181         int ret = YACA_ERROR_NONE;
182
183         /* no calling yaca_initialize() twice on the same thread */
184         if (current_thread_initialized)
185                 return YACA_ERROR_INTERNAL;
186
187         pthread_mutex_lock(&init_mutex);
188         {
189                 if (threads_cnt == 0) {
190
191 #ifndef SYS_getrandom
192                         if (urandom_fd == -2) {
193                                 int fd;
194
195                                 do {
196                                         fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
197                                 } while (fd == -1 && errno == EINTR);
198
199                                 if (fd < 0) {
200                                         ret = YACA_ERROR_INTERNAL;
201                                         goto exit;
202                                 }
203
204                                 urandom_fd = fd;
205                         }
206 #endif /* SYS_getrandom */
207
208                         OPENSSL_init();
209
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/
214                          *
215                          * OpenSSL's PRNG has issues:
216                          * https://eprint.iacr.org/2016/367.pdf
217
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?)
221                          */
222                         saved_rand_method = RAND_get_rand_method();
223                         RAND_set_rand_method(&new_rand_method);
224
225                         OpenSSL_add_all_digests();
226                         OpenSSL_add_all_ciphers();
227
228 #if OPENSSL_VERSION_NUMBER < 0x10100000L
229                         /* enable threads support */
230                         assert(mutexes == NULL);
231
232                         if (CRYPTO_num_locks() > 0) {
233                                 ret = yaca_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t),
234                                                                   (void**)&mutexes);
235
236                                 if (ret != YACA_ERROR_NONE)
237                                         goto exit;
238
239                                 for (int i = 0; i < CRYPTO_num_locks(); i++) {
240                                         if (pthread_mutex_init(&mutexes[i], NULL) != 0) {
241                                                 ret = YACA_ERROR_NONE;
242                                                 switch (errno) {
243                                                 case ENOMEM:
244                                                         ret = YACA_ERROR_OUT_OF_MEMORY;
245                                                         break;
246                                                 case EAGAIN:
247                                                 case EPERM:
248                                                 case EBUSY:
249                                                 case EINVAL:
250                                                 default:
251                                                         ret = YACA_ERROR_INTERNAL;
252                                                 }
253                                                 destroy_mutexes(i);
254
255                                                 goto exit;
256                                         }
257                                 }
258
259                                 CRYPTO_set_id_callback(thread_id_callback);
260                                 CRYPTO_set_locking_callback(locking_callback);
261                         }
262 #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
263
264                         /*
265                          * TODO:
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
269                          */
270                 }
271                 threads_cnt++;
272                 current_thread_initialized = true;
273         }
274
275 #if OPENSSL_VERSION_NUMBER < 0x10100000L || !defined SYS_getrandom
276 exit:
277 #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L || !defined SYS_getrandom */
278
279         pthread_mutex_unlock(&init_mutex);
280
281         return ret;
282 }
283
284 API void yaca_cleanup(void)
285 {
286         /* calling cleanup twice on the same thread is a NOP */
287         if (!current_thread_initialized)
288                 return;
289
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();
295
296         pthread_mutex_lock(&init_mutex);
297         {
298                 /* last one turns off the light */
299                 if (threads_cnt == 1) {
300                         ERR_free_strings();
301                         EVP_cleanup();
302                         RAND_cleanup();
303                         RAND_set_rand_method(saved_rand_method);
304
305 #ifndef SYS_getrandom
306                         close(urandom_fd);
307                         urandom_fd = -2;
308 #endif /* SYS_getrandom */
309
310 #if OPENSSL_VERSION_NUMBER < 0x10100000L
311                         /* threads support cleanup */
312                         CRYPTO_set_id_callback(NULL);
313                         CRYPTO_set_locking_callback(NULL);
314
315                         destroy_mutexes(CRYPTO_num_locks());
316 #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
317                 }
318
319                 assert(threads_cnt > 0);
320
321                 threads_cnt--;
322                 current_thread_initialized = false;
323         }
324         pthread_mutex_unlock(&init_mutex);
325 }
326
327 API int yaca_malloc(size_t size, void **memory)
328 {
329         if (size == 0 || memory == NULL)
330                 return YACA_ERROR_INVALID_PARAMETER;
331
332         *memory = OPENSSL_malloc(size);
333         if (*memory == NULL) {
334                 const int ret = YACA_ERROR_OUT_OF_MEMORY;
335                 ERROR_DUMP(ret);
336                 return ret;
337         }
338
339         return YACA_ERROR_NONE;
340 }
341
342 API int yaca_zalloc(size_t size, void **memory)
343 {
344         int ret = yaca_malloc(size, memory);
345         if (ret != YACA_ERROR_NONE)
346                 return ret;
347
348         memset(*memory, 0, size);
349
350         return YACA_ERROR_NONE;
351 }
352
353 API int yaca_realloc(size_t size, void **memory)
354 {
355         if (size == 0 || memory == NULL)
356                 return YACA_ERROR_INVALID_PARAMETER;
357
358         void *tmp = OPENSSL_realloc(*memory, size);
359         if (tmp == NULL) {
360                 const int ret = YACA_ERROR_OUT_OF_MEMORY;
361                 ERROR_DUMP(ret);
362                 return ret;
363         }
364
365         *memory = tmp;
366
367         return YACA_ERROR_NONE;
368 }
369
370 API void yaca_free(void *memory)
371 {
372         OPENSSL_free(memory);
373 }
374
375 API int yaca_randomize_bytes(char *data, size_t data_len)
376 {
377         int ret;
378
379         if (data == NULL || data_len == 0)
380                 return YACA_ERROR_INVALID_PARAMETER;
381
382         ret = RAND_bytes((unsigned char *)data, data_len);
383         if (ret != 1) {
384                 ret = YACA_ERROR_INTERNAL;
385                 ERROR_DUMP(ret);
386                 return ret;
387         }
388
389         return YACA_ERROR_NONE;
390 }
391
392 API int yaca_context_set_property(yaca_context_h ctx, yaca_property_e property,
393                                                                   const void *value, size_t value_len)
394 {
395         if (ctx == YACA_CONTEXT_NULL || ctx->set_property == NULL)
396                 return YACA_ERROR_INVALID_PARAMETER;
397
398         return ctx->set_property(ctx, property, value, value_len);
399 }
400
401 API int yaca_context_get_property(const yaca_context_h ctx, yaca_property_e property,
402                                                                   void **value, size_t *value_len)
403 {
404         if (ctx == YACA_CONTEXT_NULL || ctx->get_property == NULL)
405                 return YACA_ERROR_INVALID_PARAMETER;
406
407         return ctx->get_property(ctx, property, value, value_len);
408 }
409
410 API void yaca_context_destroy(yaca_context_h ctx)
411 {
412         if (ctx != YACA_CONTEXT_NULL) {
413                 assert(ctx->context_destroy != NULL);
414                 ctx->context_destroy(ctx);
415                 yaca_free(ctx);
416         }
417 }
418
419 API int yaca_context_get_output_length(const yaca_context_h ctx,
420                                                                            size_t input_len, size_t *output_len)
421 {
422         if (ctx == YACA_CONTEXT_NULL || output_len == NULL ||
423                 ctx->get_output_length == NULL)
424                 return YACA_ERROR_INVALID_PARAMETER;
425
426         return ctx->get_output_length(ctx, input_len, output_len);
427 }
428
429 API int yaca_memcmp(const void *first, const void *second, size_t len)
430 {
431         if (len > 0 && (first == NULL || second == NULL))
432                 return YACA_ERROR_INVALID_PARAMETER;
433
434         if (CRYPTO_memcmp(first, second, len) == 0)
435                 return YACA_ERROR_NONE;
436
437         return YACA_ERROR_DATA_MISMATCH;
438 }