Remove support for OpenSSL 1.0.x, it's EOL
[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 static __thread bool current_thread_initialized = false;
48 static size_t threads_cnt = 0;
49 static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
50 static const RAND_METHOD *saved_rand_method = NULL;
51 #ifndef SYS_getrandom
52 static int urandom_fd = -2;
53 #endif  /* SYS_getrandom */
54
55 static int getrandom_wrapper(unsigned char *buf, int num)
56 {
57         size_t received = 0;
58         size_t remaining = num;
59
60 #ifndef SYS_getrandom
61         assert(urandom_fd != -2);
62 #endif /* SYS_getrandom */
63
64         while (remaining > 0) {
65 #ifdef SYS_getrandom
66                 ssize_t n = TEMP_FAILURE_RETRY(syscall(SYS_getrandom, buf + received, remaining, 0));
67 #else /* SYS_getrandom */
68                 ssize_t n = TEMP_FAILURE_RETRY(read(urandom_fd, buf + received, remaining));
69 #endif /* SYS_getrandom */
70
71                 if (n == -1)
72                         return 0;
73
74                 received += n;
75                 remaining -= n;
76         }
77
78         return 1;
79 }
80
81 static int RAND_METHOD_seed(UNUSED const void *buf, UNUSED int num)
82 {
83         return 1;
84 }
85
86 static int RAND_METHOD_add(UNUSED const void *buf, UNUSED int num, UNUSED double entropy)
87 {
88         return 1;
89 }
90
91 static int RAND_METHOD_bytes(unsigned char *buf, int num)
92 {
93         return getrandom_wrapper(buf, num);
94 }
95
96 static void RAND_METHOD_cleanup(void)
97 {
98 }
99
100 static int RAND_METHOD_pseudorand(UNUSED unsigned char *buf, UNUSED int num)
101 {
102         return getrandom_wrapper(buf, num);
103 }
104
105 static int RAND_METHOD_status(void)
106 {
107 #ifdef SYS_getrandom
108         char tmp;
109         int n = syscall(SYS_getrandom, &tmp, 1, GRND_NONBLOCK);
110         if (n == -1 && errno == EAGAIN)
111                 return 0;
112 #endif /* SYS_getrandom */
113
114         return 1;
115 }
116
117 static const RAND_METHOD new_rand_method = {
118         RAND_METHOD_seed,
119         RAND_METHOD_bytes,
120         RAND_METHOD_cleanup,
121         RAND_METHOD_add,
122         RAND_METHOD_pseudorand,
123         RAND_METHOD_status,
124 };
125
126 API int yaca_initialize(void)
127 {
128         int ret = YACA_ERROR_NONE;
129
130         /* no calling yaca_initialize() twice on the same thread */
131         if (current_thread_initialized)
132                 return YACA_ERROR_INTERNAL;
133
134         pthread_mutex_lock(&init_mutex);
135         {
136                 if (threads_cnt == 0) {
137
138 #ifndef SYS_getrandom
139                         if (urandom_fd == -2) {
140                                 int fd;
141
142                                 do {
143                                         fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
144                                 } while (fd == -1 && errno == EINTR);
145
146                                 if (fd < 0) {
147                                         ret = YACA_ERROR_INTERNAL;
148                                         goto exit;
149                                 }
150
151                                 urandom_fd = fd;
152                         }
153 #endif /* SYS_getrandom */
154
155                         OPENSSL_init();
156
157                         /* Use getrandom from urandom pool by default.
158                          * As per the following:
159                          * http://www.2uo.de/myths-about-urandom/
160                          * http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/
161                          *
162                          * OpenSSL's PRNG has issues:
163                          * https://eprint.iacr.org/2016/367.pdf
164
165                          * Some other things to check/consider for the future:
166                          * - entropy on a mobile device (no mouse/keyboard)
167                          * - hardware random generator (RdRand on new Intels, Samsung hardware?)
168                          */
169                         saved_rand_method = RAND_get_rand_method();
170                         RAND_set_rand_method(&new_rand_method);
171
172                         OpenSSL_add_all_digests();
173                         OpenSSL_add_all_ciphers();
174
175                         /*
176                          * TODO:
177                          * - We should also decide on OpenSSL config.
178                          * - Here's a good tutorial for initialization and cleanup:
179                          *   https://wiki.openssl.org/index.php/Library_Initialization
180                          */
181                 }
182                 threads_cnt++;
183                 current_thread_initialized = true;
184         }
185
186 #if !defined SYS_getrandom
187 exit:
188 #endif /* !defined SYS_getrandom */
189
190         pthread_mutex_unlock(&init_mutex);
191
192         return ret;
193 }
194
195 API void yaca_cleanup(void)
196 {
197         /* calling cleanup twice on the same thread is a NOP */
198         if (!current_thread_initialized)
199                 return;
200
201         /* per thread cleanup */
202         CRYPTO_cleanup_all_ex_data();
203
204         pthread_mutex_lock(&init_mutex);
205         {
206                 /* last one turns off the light */
207                 if (threads_cnt == 1) {
208                         ERR_free_strings();
209                         EVP_cleanup();
210                         RAND_cleanup();
211                         RAND_set_rand_method(saved_rand_method);
212
213 #ifndef SYS_getrandom
214                         close(urandom_fd);
215                         urandom_fd = -2;
216 #endif /* SYS_getrandom */
217
218                 }
219
220                 assert(threads_cnt > 0);
221
222                 threads_cnt--;
223                 current_thread_initialized = false;
224         }
225         pthread_mutex_unlock(&init_mutex);
226 }
227
228 API int yaca_malloc(size_t size, void **memory)
229 {
230         if (size == 0 || memory == NULL)
231                 return YACA_ERROR_INVALID_PARAMETER;
232
233         *memory = OPENSSL_malloc(size);
234         if (*memory == NULL) {
235                 const int ret = YACA_ERROR_OUT_OF_MEMORY;
236                 ERROR_DUMP(ret);
237                 return ret;
238         }
239
240         return YACA_ERROR_NONE;
241 }
242
243 API int yaca_zalloc(size_t size, void **memory)
244 {
245         int ret = yaca_malloc(size, memory);
246         if (ret != YACA_ERROR_NONE)
247                 return ret;
248
249         memset(*memory, 0, size);
250
251         return YACA_ERROR_NONE;
252 }
253
254 API int yaca_realloc(size_t size, void **memory)
255 {
256         if (size == 0 || memory == NULL)
257                 return YACA_ERROR_INVALID_PARAMETER;
258
259         void *tmp = OPENSSL_realloc(*memory, size);
260         if (tmp == NULL) {
261                 const int ret = YACA_ERROR_OUT_OF_MEMORY;
262                 ERROR_DUMP(ret);
263                 return ret;
264         }
265
266         *memory = tmp;
267
268         return YACA_ERROR_NONE;
269 }
270
271 API void yaca_free(void *memory)
272 {
273         OPENSSL_free(memory);
274 }
275
276 API int yaca_randomize_bytes(char *data, size_t data_len)
277 {
278         int ret;
279
280         if (data == NULL || data_len == 0)
281                 return YACA_ERROR_INVALID_PARAMETER;
282
283         ret = RAND_bytes((unsigned char *)data, data_len);
284         if (ret != 1) {
285                 ret = YACA_ERROR_INTERNAL;
286                 ERROR_DUMP(ret);
287                 return ret;
288         }
289
290         return YACA_ERROR_NONE;
291 }
292
293 API int yaca_context_set_property(yaca_context_h ctx, yaca_property_e property,
294                                                                   const void *value, size_t value_len)
295 {
296         if (ctx == YACA_CONTEXT_NULL || ctx->set_property == NULL)
297                 return YACA_ERROR_INVALID_PARAMETER;
298
299         return ctx->set_property(ctx, property, value, value_len);
300 }
301
302 API int yaca_context_get_property(const yaca_context_h ctx, yaca_property_e property,
303                                                                   void **value, size_t *value_len)
304 {
305         if (ctx == YACA_CONTEXT_NULL || ctx->get_property == NULL)
306                 return YACA_ERROR_INVALID_PARAMETER;
307
308         return ctx->get_property(ctx, property, value, value_len);
309 }
310
311 API void yaca_context_destroy(yaca_context_h ctx)
312 {
313         if (ctx != YACA_CONTEXT_NULL) {
314                 assert(ctx->context_destroy != NULL);
315                 ctx->context_destroy(ctx);
316                 yaca_free(ctx);
317         }
318 }
319
320 API int yaca_context_get_output_length(const yaca_context_h ctx,
321                                                                            size_t input_len, size_t *output_len)
322 {
323         if (ctx == YACA_CONTEXT_NULL || output_len == NULL ||
324                 ctx->get_output_length == NULL)
325                 return YACA_ERROR_INVALID_PARAMETER;
326
327         return ctx->get_output_length(ctx, input_len, output_len);
328 }
329
330 API int yaca_memcmp(const void *first, const void *second, size_t len)
331 {
332         if (len > 0 && (first == NULL || second == NULL))
333                 return YACA_ERROR_INVALID_PARAMETER;
334
335         if (CRYPTO_memcmp(first, second, len) == 0)
336                 return YACA_ERROR_NONE;
337
338         return YACA_ERROR_DATA_MISMATCH;
339 }