Openssl: add thread support and fix initialization
[platform/core/security/key-manager.git] / src / manager / common / crypto-init.cpp
1 /*
2  *  Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License");
5  *  you may not use this file except in compliance with the License.
6  *  You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License
15  */
16 /*
17  * @file       crypto-init.cpp
18  * @author     Maciej Karpiuk (m.karpiuk2@samsung.com)
19  * @version    1.0
20  */
21
22 #include "crypto-init.h"
23
24 #include <mutex>
25 #include <atomic>
26 #include <functional>
27 #include <thread>
28 #include <fstream>
29
30 #include <openssl/evp.h>
31 #include <openssl/crypto.h>
32 #include <openssl/ssl.h>
33 #include <openssl/conf.h>
34 #include <openssl/err.h>
35 #include <openssl/rand.h>
36
37 #include <dpl/log/log.h>
38
39 namespace CKM {
40 namespace {
41
42 const char* DEV_HW_RANDOM_FILE = "/dev/hwrng";
43 const char* DEV_URANDOM_FILE = "/dev/urandom";
44 const size_t RANDOM_BUFFER_LEN = 32;
45
46 std::mutex* g_mutexes = NULL;
47
48 void lockingCallback(int mode, int type, const char*, int)
49 {
50     if(!g_mutexes) {
51         LogError("Openssl mutexes do not exist");
52         return;
53     }
54
55     if (mode & CRYPTO_LOCK)
56         g_mutexes[type].lock();
57     else if (mode & CRYPTO_UNLOCK)
58         g_mutexes[type].unlock();
59 }
60
61 unsigned long threadIdCallback() {
62     std::hash<std::thread::id> hasher;
63     return hasher(std::this_thread::get_id());
64 }
65
66 void opensslInstallLocks()
67 {
68     g_mutexes = new std::mutex[CRYPTO_num_locks()];
69
70     CRYPTO_set_id_callback(threadIdCallback);
71     CRYPTO_set_locking_callback(lockingCallback);
72 }
73
74 void opensslUninstallLocks()
75 {
76     CRYPTO_set_id_callback(NULL);
77     CRYPTO_set_locking_callback(NULL);
78
79     delete[] g_mutexes;
80     g_mutexes = NULL;
81 }
82
83 } // namespace anonymous
84
85
86 void initOpenSsl() {
87     // Loads all error strings (crypto and ssl)
88     SSL_load_error_strings();
89
90     /*
91      * Initialize libcrypto (add all algorithms, digests & ciphers)
92      * It also does the stuff from SSL_library_init() except for ssl_load_ciphers()
93      */
94     OpenSSL_add_all_algorithms(); // Can be optimized by using EVP_add_cipher instead
95
96     /*
97      *  Initialize libssl (OCSP uses it)
98      *  SSL_library_init() == OpenSSL_add_ssl_algorithms()
99      *  It always returns 1
100      */
101     SSL_library_init();
102
103     // load default configuration (/etc/ssl/openssl.cnf)
104     OPENSSL_config(NULL);
105
106     // enable FIPS mode by default
107     if(0 == FIPS_mode_set(1)) {
108         LogError("Failed to set FIPS mode");
109     }
110
111     /*
112      * Initialize entropy
113      * entropy sources - /dev/random,/dev/urandom(Default)
114      */
115     int ret = 0;
116
117     std::ifstream ifile(DEV_HW_RANDOM_FILE);
118     if(ifile.is_open())
119         ret= RAND_load_file(DEV_HW_RANDOM_FILE, RANDOM_BUFFER_LEN);
120
121     if(ret != RANDOM_BUFFER_LEN ){
122         LogWarning("Error in HW_RAND file load");
123         ret = RAND_load_file(DEV_URANDOM_FILE, RANDOM_BUFFER_LEN);
124
125         if(ret != RANDOM_BUFFER_LEN)
126             LogError("Error in U_RAND_file_load");
127     }
128
129     // Install locks for multithreading support
130     opensslInstallLocks();
131 }
132
133 void deinitOpenSsl() {
134     opensslUninstallLocks();
135     CONF_modules_unload(1);
136     EVP_cleanup();
137     ERR_free_strings();
138     deinitOpenSslThread();
139 }
140
141 void deinitOpenSslThread() {
142     CRYPTO_cleanup_all_ex_data();
143     ERR_remove_thread_state(NULL);
144 }
145
146 namespace {
147 std::mutex cryptoInitMutex;
148
149 void initOpenSslAndDetach();
150
151 typedef void(*initFnPtr)();
152
153 // has to be atomic as storing function pointer is not an atomic operation on armv7l
154 std::atomic<initFnPtr> initFn (&initOpenSslAndDetach);
155
156 void initEmpty() {}
157
158 void initOpenSslAndDetach() {
159     // DCLP
160     std::lock_guard<std::mutex> lock(cryptoInitMutex);
161     /*
162      * We don't care about memory ordering here. Current thread will order it correctly and for
163      * other threads only store matters. Also only one thread can be here at once because of lock.
164      */
165     if(initFn.load(std::memory_order_relaxed) != &initEmpty)
166     {
167         initOpenSsl();
168
169         /*
170          * Synchronizes with load. Everything that happened before this store in this thread is
171          * visible to everything that happens after load in another thread. We switch to an empty
172          * function here.
173          */
174         initFn.store(&initEmpty, std::memory_order_release);
175     }
176 }
177
178 } // namespace anonymous
179
180 void initOpenSslOnce() {
181     /*
182      * Synchronizes with store. Everything that happened before store in another thread will be
183      * visible in this thread after load.
184      */
185     initFn.load(std::memory_order_acquire)();
186 }
187
188 } /* namespace CKM */