Replace spaces with tabs
[platform/core/security/yaca.git] / src / crypto.c
1 /*
2  *  Copyright (c) 2016 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         if (urandom_fd == -2)
66                 return 0;
67 #endif /* SYS_getrandom */
68
69         while (remaining > 0) {
70 #ifdef SYS_getrandom
71                 ssize_t n = syscall(SYS_getrandom, buf + received, remaining, 0);
72 #else /* SYS_getrandom */
73                 ssize_t n = read(urandom_fd, buf + received, remaining);
74 #endif /* SYS_getrandom */
75
76                 if (n == -1) {
77                         if (errno == EINTR)
78                                 continue;
79
80                         return 0;
81                 }
82
83                 received += n;
84                 remaining -= n;
85         }
86
87         return 1;
88 }
89
90 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
91
92 static int RAND_METHOD_seed(UNUSED const void *buf, UNUSED int num)
93 {
94         return 1;
95 }
96
97 static int RAND_METHOD_add(UNUSED const void *buf, UNUSED int num, UNUSED double entropy)
98 {
99         return 1;
100 }
101
102 #else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
103
104 static void RAND_METHOD_seed(UNUSED const void *buf, UNUSED int num)
105 {
106 }
107
108 static void RAND_METHOD_add(UNUSED const void *buf, UNUSED int num, UNUSED double entropy)
109 {
110 }
111
112 #endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
113
114 static int RAND_METHOD_bytes(unsigned char *buf, int num)
115 {
116         return getrandom_wrapper(buf, num);
117 }
118
119 static void RAND_METHOD_cleanup(void)
120 {
121 }
122
123 static int RAND_METHOD_pseudorand(UNUSED unsigned char *buf, UNUSED int num)
124 {
125         return getrandom_wrapper(buf, num);
126 }
127
128 static int RAND_METHOD_status(void)
129 {
130 #ifdef SYS_getrandom
131         char tmp;
132         int n = syscall(SYS_getrandom, &tmp, 1, GRND_NONBLOCK);
133         if (n == -1 && errno == EAGAIN)
134                 return 0;
135 #endif /* SYS_getrandom */
136
137         return 1;
138 }
139
140 static const RAND_METHOD new_rand_method = {
141         RAND_METHOD_seed,
142         RAND_METHOD_bytes,
143         RAND_METHOD_cleanup,
144         RAND_METHOD_add,
145         RAND_METHOD_pseudorand,
146         RAND_METHOD_status,
147 };
148
149 #if OPENSSL_VERSION_NUMBER < 0x10100000L
150
151 static void locking_callback(int mode, int type, UNUSED const char *file, UNUSED int line)
152 {
153         /* Ignore NULL mutexes and lock/unlock error codes as we can't do anything
154          * about them. */
155
156         if (mutexes == NULL)
157                 return;
158
159         if (mode & CRYPTO_LOCK)
160                 pthread_mutex_lock(&mutexes[type]);
161         else if (mode & CRYPTO_UNLOCK)
162                 pthread_mutex_unlock(&mutexes[type]);
163 }
164
165 static unsigned long thread_id_callback()
166 {
167         return pthread_self();
168 }
169
170 static void destroy_mutexes(int count)
171 {
172         if (mutexes != NULL) {
173                 for (int i = 0; i < count; i++) {
174                         /* Ignore returned value as we can't do anything about it */
175                         pthread_mutex_destroy(&mutexes[i]);
176                 }
177                 yaca_free(mutexes);
178                 mutexes = NULL;
179         }
180 }
181
182 #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
183
184 API int yaca_initialize(void)
185 {
186         int ret = YACA_ERROR_NONE;
187
188         /* no calling yaca_initialize() twice on the same thread */
189         if (current_thread_initialized)
190                 return YACA_ERROR_INTERNAL;
191
192         pthread_mutex_lock(&init_mutex);
193         {
194                 if (threads_cnt == 0) {
195
196 #ifndef SYS_getrandom
197                         if (urandom_fd == -2) {
198                                 int fd;
199
200                                 do {
201                                         fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
202                                 } while (fd == -1 && errno == EINTR);
203
204                                 if (fd < 0) {
205                                         ret = YACA_ERROR_INTERNAL;
206                                         goto exit;
207                                 }
208
209                                 urandom_fd = fd;
210                         }
211 #endif /* SYS_getrandom */
212
213                         OPENSSL_init();
214
215                         /* Use getrandom from urandom pool by default.
216                          * As per the following:
217                          * http://www.2uo.de/myths-about-urandom/
218                          * http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/
219                          *
220                          * OpenSSL's PRNG has issues:
221                          * https://eprint.iacr.org/2016/367.pdf
222
223                          * Some other things to check/consider for the future:
224                          * - entropy on a mobile device (no mouse/keyboard)
225                          * - hardware random generator (RdRand on new Intels, Samsung hardware?)
226                          */
227                         saved_rand_method = RAND_get_rand_method();
228                         RAND_set_rand_method(&new_rand_method);
229
230                         OpenSSL_add_all_digests();
231                         OpenSSL_add_all_ciphers();
232
233 #if OPENSSL_VERSION_NUMBER < 0x10100000L
234                         /* enable threads support */
235                         assert(mutexes == NULL);
236
237                         if (CRYPTO_num_locks() > 0) {
238                                 ret = yaca_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t),
239                                                                   (void**)&mutexes);
240
241                                 if (ret != YACA_ERROR_NONE)
242                                         goto exit;
243
244                                 for (int i = 0; i < CRYPTO_num_locks(); i++) {
245                                         if (pthread_mutex_init(&mutexes[i], NULL) != 0) {
246                                                 ret = YACA_ERROR_NONE;
247                                                 switch (errno) {
248                                                 case ENOMEM:
249                                                         ret = YACA_ERROR_OUT_OF_MEMORY;
250                                                         break;
251                                                 case EAGAIN:
252                                                 case EPERM:
253                                                 case EBUSY:
254                                                 case EINVAL:
255                                                 default:
256                                                         ret = YACA_ERROR_INTERNAL;
257                                                 }
258                                                 destroy_mutexes(i);
259
260                                                 goto exit;
261                                         }
262                                 }
263
264                                 CRYPTO_set_id_callback(thread_id_callback);
265                                 CRYPTO_set_locking_callback(locking_callback);
266                         }
267 #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
268
269                         /*
270                          * TODO:
271                          * - We should also decide on OpenSSL config.
272                          * - Here's a good tutorial for initialization and cleanup:
273                          *   https://wiki.openssl.org/index.php/Library_Initialization
274                          */
275                 }
276                 threads_cnt++;
277                 current_thread_initialized = true;
278         }
279
280 #if OPENSSL_VERSION_NUMBER < 0x10100000L || !defined SYS_getrandom
281 exit:
282 #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L || !defined SYS_getrandom */
283
284         pthread_mutex_unlock(&init_mutex);
285
286         return ret;
287 }
288
289 API void yaca_cleanup(void)
290 {
291         /* calling cleanup twice on the same thread is a NOP */
292         if (!current_thread_initialized)
293                 return;
294
295         /* per thread cleanup */
296 #if OPENSSL_VERSION_NUMBER < 0x10100000L
297         ERR_remove_thread_state(NULL);
298 #endif  /* OPENSSL_VERSION_NUMBER < 0x10100000L */
299         CRYPTO_cleanup_all_ex_data();
300
301         pthread_mutex_lock(&init_mutex);
302         {
303                 /* last one turns off the light */
304                 if (threads_cnt == 1) {
305                         ERR_free_strings();
306                         EVP_cleanup();
307                         RAND_cleanup();
308                         RAND_set_rand_method(saved_rand_method);
309
310 #ifndef SYS_getrandom
311                         close(urandom_fd);
312                         urandom_fd = -2;
313 #endif /* SYS_getrandom */
314
315 #if OPENSSL_VERSION_NUMBER < 0x10100000L
316                         /* threads support cleanup */
317                         CRYPTO_set_id_callback(NULL);
318                         CRYPTO_set_locking_callback(NULL);
319
320                         destroy_mutexes(CRYPTO_num_locks());
321 #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
322                 }
323
324                 assert(threads_cnt > 0);
325
326                 threads_cnt--;
327                 current_thread_initialized = false;
328         }
329         pthread_mutex_unlock(&init_mutex);
330 }
331
332 API int yaca_malloc(size_t size, void **memory)
333 {
334         if (size == 0 || memory == NULL)
335                 return YACA_ERROR_INVALID_PARAMETER;
336
337         *memory = OPENSSL_malloc(size);
338         if (*memory == NULL) {
339                 ERROR_DUMP(YACA_ERROR_OUT_OF_MEMORY);
340                 return YACA_ERROR_OUT_OF_MEMORY;
341         }
342
343         return YACA_ERROR_NONE;
344 }
345
346 API int yaca_zalloc(size_t size, void **memory)
347 {
348         int ret = yaca_malloc(size, memory);
349         if (ret != YACA_ERROR_NONE)
350                 return ret;
351
352         memset(*memory, 0, size);
353
354         return YACA_ERROR_NONE;
355 }
356
357 API int yaca_realloc(size_t size, void **memory)
358 {
359         if (size == 0 || memory == NULL)
360                 return YACA_ERROR_INVALID_PARAMETER;
361
362         void *tmp = OPENSSL_realloc(*memory, size);
363         if (tmp == NULL) {
364                 ERROR_DUMP(YACA_ERROR_OUT_OF_MEMORY);
365                 return YACA_ERROR_OUT_OF_MEMORY;
366         }
367
368         *memory = tmp;
369
370         return YACA_ERROR_NONE;
371 }
372
373 API void yaca_free(void *memory)
374 {
375         OPENSSL_free(memory);
376 }
377
378 API int yaca_randomize_bytes(char *data, size_t data_len)
379 {
380         int ret;
381
382         if (data == NULL || data_len == 0)
383                 return YACA_ERROR_INVALID_PARAMETER;
384
385         ret = RAND_bytes((unsigned char *)data, data_len);
386         if (ret != 1) {
387                 ret = YACA_ERROR_INTERNAL;
388                 ERROR_DUMP(ret);
389                 return ret;
390         }
391
392         return YACA_ERROR_NONE;
393 }
394
395 API int yaca_context_set_property(yaca_context_h ctx, yaca_property_e property,
396                                                                   const void *value, size_t value_len)
397 {
398         if (ctx == YACA_CONTEXT_NULL || ctx->set_property == NULL)
399                 return YACA_ERROR_INVALID_PARAMETER;
400
401         return ctx->set_property(ctx, property, value, value_len);
402 }
403
404 API int yaca_context_get_property(const yaca_context_h ctx, yaca_property_e property,
405                                                                   void **value, size_t *value_len)
406 {
407         if (ctx == YACA_CONTEXT_NULL || ctx->get_property == NULL)
408                 return YACA_ERROR_INVALID_PARAMETER;
409
410         return ctx->get_property(ctx, property, value, value_len);
411 }
412
413 API void yaca_context_destroy(yaca_context_h ctx)
414 {
415         if (ctx != YACA_CONTEXT_NULL) {
416                 assert(ctx->context_destroy != NULL);
417                 ctx->context_destroy(ctx);
418                 yaca_free(ctx);
419         }
420 }
421
422 API int yaca_context_get_output_length(const yaca_context_h ctx,
423                                                                            size_t input_len, size_t *output_len)
424 {
425         if (ctx == YACA_CONTEXT_NULL || output_len == NULL ||
426                 ctx->get_output_length == NULL)
427                 return YACA_ERROR_INVALID_PARAMETER;
428
429         return ctx->get_output_length(ctx, input_len, output_len);
430 }
431
432 API int yaca_memcmp(const void *first, const void *second, size_t len)
433 {
434         if (CRYPTO_memcmp(first, second, len) == 0)
435                 return YACA_ERROR_NONE;
436
437         return YACA_ERROR_DATA_MISMATCH;
438 }