Merge "Adjust naming convention to API." into tizen
[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_initialize() 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 initialization 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 void yaca_cleanup(void)
161 {
162         /* calling cleanup twice on the same thread is a NOP */
163         if (!current_thread_initialized)
164                 return;
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
195 API int yaca_malloc(size_t size, void **memory)
196 {
197         if (size == 0 || memory == NULL)
198                 return YACA_ERROR_INVALID_PARAMETER;
199
200         *memory = OPENSSL_malloc(size);
201         if (*memory == NULL) {
202                 ERROR_DUMP(YACA_ERROR_OUT_OF_MEMORY);
203                 return YACA_ERROR_OUT_OF_MEMORY;
204         }
205
206         return YACA_ERROR_NONE;
207 }
208
209 API int yaca_zalloc(size_t size, void **memory)
210 {
211         int ret = yaca_malloc(size, memory);
212         if (ret != YACA_ERROR_NONE)
213                 return ret;
214
215         memset(*memory, 0, size);
216
217         return YACA_ERROR_NONE;
218 }
219
220 API int yaca_realloc(size_t size, void **memory)
221 {
222         if (size == 0 || memory == NULL)
223                 return YACA_ERROR_INVALID_PARAMETER;
224
225         void *tmp = OPENSSL_realloc(*memory, size);
226         if (tmp == NULL) {
227                 ERROR_DUMP(YACA_ERROR_OUT_OF_MEMORY);
228                 return YACA_ERROR_OUT_OF_MEMORY;
229         }
230
231         *memory = tmp;
232
233         return YACA_ERROR_NONE;
234 }
235
236 API void yaca_free(void *memory)
237 {
238         OPENSSL_free(memory);
239 }
240
241 API int yaca_randomize_bytes(char *data, size_t data_len)
242 {
243         int ret;
244
245         if (data == NULL || data_len == 0)
246                 return YACA_ERROR_INVALID_PARAMETER;
247
248         ret = RAND_bytes((unsigned char *)data, data_len);
249         if (ret != 1) {
250                 ret = YACA_ERROR_INTERNAL;
251                 ERROR_DUMP(ret);
252                 return ret;
253         }
254
255         return YACA_ERROR_NONE;
256 }
257
258 API int yaca_context_set_property(yaca_context_h ctx, yaca_property_e property,
259                                   const void *value, size_t value_len)
260 {
261         if (ctx == YACA_CONTEXT_NULL || ctx->set_property == NULL)
262                 return YACA_ERROR_INVALID_PARAMETER;
263
264         return ctx->set_property(ctx, property, value, value_len);
265 }
266
267 API int yaca_context_get_property(const yaca_context_h ctx, yaca_property_e property,
268                                   void **value, size_t *value_len)
269 {
270         if (ctx == YACA_CONTEXT_NULL || ctx->get_property == NULL)
271                 return YACA_ERROR_INVALID_PARAMETER;
272
273         return ctx->get_property(ctx, property, value, value_len);
274 }
275
276 API void yaca_context_destroy(yaca_context_h ctx)
277 {
278         if (ctx != YACA_CONTEXT_NULL) {
279                 assert(ctx->context_destroy != NULL);
280                 ctx->context_destroy(ctx);
281                 yaca_free(ctx);
282         }
283 }
284
285 API int yaca_context_get_output_length(const yaca_context_h ctx,
286                                        size_t input_len, size_t *output_len)
287 {
288         if (ctx == YACA_CONTEXT_NULL || output_len == NULL ||
289             ctx->get_output_length == NULL)
290                 return YACA_ERROR_INVALID_PARAMETER;
291
292         return ctx->get_output_length(ctx, input_len, output_len);
293 }
294
295 API int yaca_memcmp(const void *first, const void *second, size_t len)
296 {
297         if (CRYPTO_memcmp(first, second, len) == 0)
298                 return YACA_ERROR_NONE;
299
300         return YACA_ERROR_DATA_MISMATCH;
301 }