158bcf85f8154e8a699995b4a8fa222e11440cc8
[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 #include <assert.h>
25 #include <string.h>
26 #include <pthread.h>
27 #include <stdbool.h>
28
29 #include <openssl/crypto.h>
30 #include <openssl/evp.h>
31 #include <openssl/rand.h>
32 #include <openssl/err.h>
33
34 #include <yaca_crypto.h>
35 #include <yaca_error.h>
36
37 #include "internal.h"
38
39 static pthread_mutex_t *mutexes = NULL;
40
41 static __thread bool current_thread_initialized = false;
42 static size_t threads_cnt = 0;
43 static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
44
45 static void locking_callback(int mode, int type, UNUSED const char *file, UNUSED int line)
46 {
47         /* Ignore NULL mutexes and lock/unlock error codes as we can't do anything
48          * about them. */
49
50         if (mutexes == NULL)
51                 return;
52
53         if (mode & CRYPTO_LOCK)
54                 pthread_mutex_lock(&mutexes[type]);
55         else if (mode & CRYPTO_UNLOCK)
56                 pthread_mutex_unlock(&mutexes[type]);
57 }
58
59 static unsigned long thread_id_callback()
60 {
61         return pthread_self();
62 }
63
64 static void destroy_mutexes(int count)
65 {
66         if (mutexes != NULL) {
67                 for (int i = 0; i < count; i++) {
68                         /* Ignore returned value as we can't do anything about it */
69                         pthread_mutex_destroy(&mutexes[i]);
70                 }
71                 yaca_free(mutexes);
72                 mutexes = NULL;
73         }
74 }
75
76 API int yaca_initialize(void)
77 {
78         int ret = YACA_ERROR_NONE;
79
80         /* no calling yaca_initalize() twice on the same thread */
81         if (current_thread_initialized)
82                 return YACA_ERROR_INTERNAL;
83
84         pthread_mutex_lock(&init_mutex);
85         {
86                 if (threads_cnt == 0)
87                 {
88                         assert(mutexes == NULL);
89
90                         OPENSSL_init();
91
92                         /* This should never fail on a /dev/random equipped system. If it does it
93                          * means we might need to figure out another way of a truly random seed.
94                          * https://wiki.openssl.org/index.php/Random_Numbers
95                          *
96                          * Another things to maybe consider for the future:
97                          * - entropy on a mobile device (no mouse/keyboard)
98                          * - fork safety: https://wiki.openssl.org/index.php/Random_fork-safety
99                          * - hardware random generator (RdRand on new Intels, Samsung hardware?)
100                          */
101                         if (RAND_status() != 1) {
102                                 ERROR_DUMP(YACA_ERROR_INTERNAL);
103                                 ret = YACA_ERROR_INTERNAL;
104                                 goto exit;
105                         }
106
107                         OpenSSL_add_all_digests();
108                         OpenSSL_add_all_ciphers();
109
110                         /* enable threads support */
111                         if (CRYPTO_num_locks() > 0) {
112                                 ret = yaca_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t),
113                                                   (void**)&mutexes);
114
115                                 if (ret != YACA_ERROR_NONE)
116                                         goto exit;
117
118                                 for (int i = 0; i < CRYPTO_num_locks(); i++) {
119                                         if (pthread_mutex_init(&mutexes[i], NULL) != 0) {
120                                                 ret = YACA_ERROR_NONE;
121                                                 switch (errno) {
122                                                 case ENOMEM:
123                                                         ret = YACA_ERROR_OUT_OF_MEMORY;
124                                                         break;
125                                                 case EAGAIN:
126                                                 case EPERM:
127                                                 case EBUSY:
128                                                 case EINVAL:
129                                                 default:
130                                                         ret = YACA_ERROR_INTERNAL;
131                                                 }
132                                                 destroy_mutexes(i);
133
134                                                 goto exit;
135                                         }
136                                 }
137
138                                 CRYPTO_set_id_callback(thread_id_callback);
139                                 CRYPTO_set_locking_callback(locking_callback);
140                         }
141
142                         /*
143                          * TODO:
144                          * - We should also decide on Openssl config.
145                          * - Here's a good tutorial for initalization and cleanup:
146                          *   https://wiki.openssl.org/index.php/Library_Initialization
147                          * - We should also initialize the entropy for random number generator:
148                          *   https://wiki.openssl.org/index.php/Random_Numbers#Initialization
149                          */
150                 }
151                 threads_cnt++;
152                 current_thread_initialized = true;
153         }
154 exit:
155         pthread_mutex_unlock(&init_mutex);
156
157         return ret;
158 }
159
160 API int yaca_cleanup(void)
161 {
162         /* calling cleanup twice on the same thread is a NOP */
163         if (!current_thread_initialized)
164                 return YACA_ERROR_NONE;
165
166         /* per thread cleanup */
167         ERR_remove_thread_state(NULL);
168         CRYPTO_cleanup_all_ex_data();
169
170         pthread_mutex_lock(&init_mutex);
171         {
172                 /* last one turns off the light */
173                 if (threads_cnt == 1) {
174                         ERR_free_strings();
175                         ERR_remove_thread_state(NULL);
176                         EVP_cleanup();
177                         RAND_cleanup();
178                         CRYPTO_cleanup_all_ex_data();
179
180                         /* threads support cleanup */
181                         CRYPTO_set_id_callback(NULL);
182                         CRYPTO_set_locking_callback(NULL);
183
184                         destroy_mutexes(CRYPTO_num_locks());
185                 }
186
187                 assert(threads_cnt > 0);
188
189                 threads_cnt--;
190                 current_thread_initialized = false;
191         }
192         pthread_mutex_unlock(&init_mutex);
193
194         return YACA_ERROR_NONE;
195 }
196
197 API int yaca_malloc(size_t size, void **memory)
198 {
199         if (size == 0 || memory == NULL)
200                 return YACA_ERROR_INVALID_PARAMETER;
201
202         *memory = OPENSSL_malloc(size);
203         if (*memory == NULL) {
204                 ERROR_DUMP(YACA_ERROR_OUT_OF_MEMORY);
205                 return YACA_ERROR_OUT_OF_MEMORY;
206         }
207
208         return YACA_ERROR_NONE;
209 }
210
211 API int yaca_zalloc(size_t size, void **memory)
212 {
213         int ret = yaca_malloc(size, memory);
214         if (ret != YACA_ERROR_NONE)
215                 return ret;
216
217         memset(*memory, 0, size);
218
219         return YACA_ERROR_NONE;
220 }
221
222 API int yaca_realloc(size_t size, void **memory)
223 {
224         if (size == 0 || memory == NULL)
225                 return YACA_ERROR_INVALID_PARAMETER;
226
227         void *tmp = OPENSSL_realloc(*memory, size);
228         if (tmp == NULL) {
229                 ERROR_DUMP(YACA_ERROR_OUT_OF_MEMORY);
230                 return YACA_ERROR_OUT_OF_MEMORY;
231         }
232
233         *memory = tmp;
234
235         return YACA_ERROR_NONE;
236 }
237
238 API void yaca_free(void *memory)
239 {
240         OPENSSL_free(memory);
241 }
242
243 API int yaca_randomize_bytes(char *data, size_t data_len)
244 {
245         int ret;
246
247         if (data == NULL || data_len == 0)
248                 return YACA_ERROR_INVALID_PARAMETER;
249
250         ret = RAND_bytes((unsigned char *)data, data_len);
251         if (ret != 1) {
252                 ret = YACA_ERROR_INTERNAL;
253                 ERROR_DUMP(ret);
254                 return ret;
255         }
256
257         return YACA_ERROR_NONE;
258 }
259
260 API int yaca_context_set_property(yaca_context_h ctx, yaca_property_e property,
261                                   const void *value, size_t value_len)
262 {
263         if (ctx == YACA_CONTEXT_NULL || ctx->set_param == NULL)
264                 return YACA_ERROR_INVALID_PARAMETER;
265
266         return ctx->set_param(ctx, property, value, value_len);
267 }
268
269 API int yaca_context_get_property(const yaca_context_h ctx, yaca_property_e property,
270                                   void **value, size_t *value_len)
271 {
272         if (ctx == YACA_CONTEXT_NULL || ctx->get_param == NULL)
273                 return YACA_ERROR_INVALID_PARAMETER;
274
275         return ctx->get_param(ctx, property, value, value_len);
276 }
277
278 API void yaca_context_destroy(yaca_context_h ctx)
279 {
280         if (ctx != YACA_CONTEXT_NULL) {
281                 assert(ctx->ctx_destroy != NULL);
282                 ctx->ctx_destroy(ctx);
283                 yaca_free(ctx);
284         }
285 }
286
287 API int yaca_context_get_output_length(const yaca_context_h ctx,
288                                        size_t input_len, size_t *output_len)
289 {
290         if (ctx == YACA_CONTEXT_NULL || output_len == NULL ||
291             ctx->get_output_length == NULL)
292                 return YACA_ERROR_INVALID_PARAMETER;
293
294         return ctx->get_output_length(ctx, input_len, output_len);
295 }
296
297 API int yaca_memcmp(const void *first, const void *second, size_t len)
298 {
299         if (CRYPTO_memcmp(first, second, len) == 0)
300                 return YACA_ERROR_NONE;
301
302         return YACA_ERROR_DATA_MISMATCH;
303 }