1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 2010-2020. All Rights Reserved.
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  * %CopyrightEnd%
19  */
20 
21 /*
22  * Purpose:  Dynamically loadable NIF library for cryptography.
23  * Based on OpenSSL.
24  */
25 
26 #include "common.h"
27 
28 #include "aead.h"
29 #include "aes.h"
30 #include "algorithms.h"
31 #include "api_ng.h"
32 #include "bn.h"
33 #include "cipher.h"
34 #include "mac.h"
35 #include "dh.h"
36 #include "digest.h"
37 #include "dss.h"
38 #include "ec.h"
39 #include "ecdh.h"
40 #include "eddsa.h"
41 #include "engine.h"
42 #include "evp.h"
43 #include "fips.h"
44 #include "hash.h"
45 #include "hmac.h"
46 #include "info.h"
47 #include "math.h"
48 #include "pkey.h"
49 #include "rand.h"
50 #include "rsa.h"
51 #include "srp.h"
52 
53 /* NIF interface declarations */
54 static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info);
55 static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info);
56 static void unload(ErlNifEnv* env, void* priv_data);
57 
58 static int library_refc = 0; /* number of users of this dynamic library */
59 static int library_initialized = 0;
60 
61 static ErlNifFunc nif_funcs[] = {
62     {"info_nif", 0, info_nif, 0},
63     {"info_lib", 0, info_lib, 0},
64     {"info_fips", 0, info_fips, 0},
65     {"enable_fips_mode_nif", 1, enable_fips_mode_nif, 0},
66     {"hash_algorithms", 0, hash_algorithms, 0},
67     {"pubkey_algorithms", 0, pubkey_algorithms, 0},
68     {"cipher_algorithms", 0, cipher_algorithms, 0},
69     {"mac_algorithms", 0, mac_algorithms, 0},
70     {"curve_algorithms", 0, curve_algorithms, 0},
71     {"rsa_opts_algorithms", 0, rsa_opts_algorithms, 0},
72     {"hash_info", 1, hash_info_nif, 0},
73     {"hash_nif", 2, hash_nif, 0},
74     {"hash_init_nif", 1, hash_init_nif, 0},
75     {"hash_update_nif", 2, hash_update_nif, 0},
76     {"hash_final_nif", 1, hash_final_nif, 0},
77     {"mac_nif", 4, mac_nif, 0},
78     {"mac_init_nif", 3, mac_init_nif, 0},
79     {"mac_update_nif", 2, mac_update_nif, 0},
80     {"mac_final_nif", 1, mac_final_nif, 0},
81     {"cipher_info_nif", 1, cipher_info_nif, 0},
82     {"ng_crypto_init_nif", 5, ng_crypto_init_nif, 0},
83     {"ng_crypto_update_nif", 2, ng_crypto_update_nif, 0},
84     {"ng_crypto_update_nif", 3, ng_crypto_update_nif, 0},
85     {"ng_crypto_final_nif", 1, ng_crypto_final_nif, 0},
86     {"ng_crypto_get_data_nif", 1, ng_crypto_get_data_nif, 0},
87     {"ng_crypto_one_time_nif", 6, ng_crypto_one_time_nif, 0},
88     {"strong_rand_bytes_nif", 1, strong_rand_bytes_nif, 0},
89     {"strong_rand_range_nif", 1, strong_rand_range_nif, 0},
90     {"rand_uniform_nif", 2, rand_uniform_nif, 0},
91     {"mod_exp_nif", 4, mod_exp_nif, 0},
92     {"do_exor", 2, do_exor, 0},
93     {"pkey_sign_nif", 5, pkey_sign_nif, 0},
94     {"pkey_verify_nif", 6, pkey_verify_nif, 0},
95     {"pkey_crypt_nif", 6, pkey_crypt_nif, 0},
96     {"rsa_generate_key_nif", 2, rsa_generate_key_nif, 0},
97     {"dh_generate_key_nif", 4, dh_generate_key_nif, 0},
98     {"dh_compute_key_nif", 3, dh_compute_key_nif, 0},
99     {"evp_compute_key_nif", 3, evp_compute_key_nif, 0},
100     {"evp_generate_key_nif", 2, evp_generate_key_nif, 0},
101     {"privkey_to_pubkey_nif", 2, privkey_to_pubkey_nif, 0},
102     {"srp_value_B_nif", 5, srp_value_B_nif, 0},
103     {"srp_user_secret_nif", 7, srp_user_secret_nif, 0},
104     {"srp_host_secret_nif", 5, srp_host_secret_nif, 0},
105 
106     {"ec_key_generate", 2, ec_key_generate, 0},
107     {"ecdh_compute_key_nif", 3, ecdh_compute_key_nif, 0},
108 
109     {"rand_seed_nif", 1, rand_seed_nif, 0},
110 
111     {"aead_cipher_nif", 7, aead_cipher_nif, 0},
112 
113     {"engine_by_id_nif", 1, engine_by_id_nif, 0},
114     {"engine_init_nif", 1, engine_init_nif, 0},
115     {"engine_free_nif", 1, engine_free_nif, 0},
116     {"engine_load_dynamic_nif", 0, engine_load_dynamic_nif, 0},
117     {"engine_ctrl_cmd_strings_nif", 3, engine_ctrl_cmd_strings_nif, 0},
118     {"engine_register_nif", 2, engine_register_nif, 0},
119     {"engine_unregister_nif", 2, engine_unregister_nif, 0},
120     {"engine_add_nif", 1, engine_add_nif, 0},
121     {"engine_remove_nif", 1, engine_remove_nif, 0},
122     {"engine_get_first_nif", 0, engine_get_first_nif, 0},
123     {"engine_get_next_nif", 1, engine_get_next_nif, 0},
124     {"engine_get_id_nif", 1, engine_get_id_nif, 0},
125     {"engine_get_name_nif", 1, engine_get_name_nif, 0},
126     {"engine_get_all_methods_nif", 0, engine_get_all_methods_nif, 0}
127 };
128 
ERL_NIF_INIT(crypto,nif_funcs,load,NULL,upgrade,unload)129 ERL_NIF_INIT(crypto,nif_funcs,load,NULL,upgrade,unload)
130 
131 
132 static int verify_lib_version(void)
133 {
134 #if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0)
135     const unsigned long libv = SSLeay();
136 #else
137     const unsigned long libv = OpenSSL_version_num();
138 #endif
139     const unsigned long hdrv = OPENSSL_VERSION_NUMBER;
140 
141 #   define MAJOR_VER(V) ((unsigned long)(V) >> (7*4))
142 
143     if (MAJOR_VER(libv) != MAJOR_VER(hdrv)) {
144 	PRINTF_ERR2("CRYPTO: INCOMPATIBLE SSL VERSION"
145 		    " lib=%lx header=%lx\n", libv, hdrv);
146 	return 0;
147     }
148     return 1;
149 }
150 
initialize(ErlNifEnv * env,ERL_NIF_TERM load_info)151 static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info)
152 {
153 #if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0)
154 #ifdef OPENSSL_THREADS
155     ErlNifSysInfo sys_info;
156 #endif
157 #endif
158     get_crypto_callbacks_t* funcp;
159     struct crypto_callbacks* ccb;
160     int nlocks = 0;
161     int tpl_arity;
162     const ERL_NIF_TERM* tpl_array;
163     int vernum;
164     ErlNifBinary lib_bin;
165 #ifdef HAVE_DYNAMIC_CRYPTO_LIB
166     char lib_buf[1000];
167     void *handle;
168 #endif
169 
170     if (!verify_lib_version())
171 	return __LINE__;
172 
173     /* load_info: {302, <<"/full/path/of/this/library">>,true|false} */
174     if (!enif_get_tuple(env, load_info, &tpl_arity, &tpl_array))
175         return __LINE__;
176     if (tpl_arity != 3)
177         return __LINE__;
178     if (!enif_get_int(env, tpl_array[0], &vernum))
179         return __LINE__;
180     if (vernum != 302)
181         return __LINE__;
182     if (!enif_inspect_binary(env, tpl_array[1], &lib_bin))
183         return __LINE__;
184 
185 #ifdef HAS_EVP_PKEY_CTX
186     if (!init_mac_ctx(env)) {
187 	return __LINE__;
188     }
189 #else
190     if (!init_hmac_ctx(env)) {
191 	return __LINE__;
192     }
193 #endif
194     if (!init_hash_ctx(env)) {
195         return __LINE__;
196     }
197     if (!init_cipher_ctx(env)) {
198         return __LINE__;
199     }
200     if (!init_engine_ctx(env)) {
201         return __LINE__;
202     }
203 
204     if (library_initialized) {
205 	/* Repeated loading of this library (module upgrade).
206 	 * Atoms and callbacks are already set, we are done.
207 	 */
208 	return 0;
209     }
210 
211     if (!init_atoms(env, tpl_array[2], load_info)) {
212         return __LINE__;
213     }
214 
215 #ifdef HAVE_DYNAMIC_CRYPTO_LIB
216     if (!change_basename(&lib_bin, lib_buf, sizeof(lib_buf), crypto_callback_name))
217         return __LINE__;
218     if ((handle = enif_dlopen(lib_buf, &error_handler, NULL)) == NULL)
219         return __LINE__;
220     if ((funcp = (get_crypto_callbacks_t*) enif_dlsym(handle, "get_crypto_callbacks",
221                                                        &error_handler, NULL)) == NULL)
222         return __LINE__;
223 #else /* !HAVE_DYNAMIC_CRYPTO_LIB */
224     funcp = &get_crypto_callbacks;
225 #endif
226 
227 #if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0)
228 #ifdef OPENSSL_THREADS
229     enif_system_info(&sys_info, sizeof(sys_info));
230     if (sys_info.scheduler_threads > 1) {
231 	nlocks = CRYPTO_num_locks();
232     }
233     /* else no need for locks */
234 #endif
235 #endif
236 
237     ccb = (*funcp)(nlocks);
238 
239     if (!ccb || ccb->sizeof_me != sizeof(*ccb)) {
240 	PRINTF_ERR0("Invalid 'crypto_callbacks'");
241 	return __LINE__;
242     }
243 
244 #ifdef HAS_CRYPTO_MEM_FUNCTIONS
245     if (!CRYPTO_set_mem_functions(ccb->crypto_alloc, ccb->crypto_realloc, ccb->crypto_free))
246         return __LINE__;
247 #endif
248 
249 #if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0)
250 #ifdef OPENSSL_THREADS
251     if (nlocks > 0) {
252 	CRYPTO_set_add_lock_callback(ccb->add_lock_function);
253 	CRYPTO_set_locking_callback(ccb->locking_function);
254 	CRYPTO_set_id_callback(ccb->id_function);
255 	CRYPTO_set_dynlock_create_callback(ccb->dyn_create_function);
256 	CRYPTO_set_dynlock_lock_callback(ccb->dyn_lock_function);
257 	CRYPTO_set_dynlock_destroy_callback(ccb->dyn_destroy_function);
258     }
259 #endif /* OPENSSL_THREADS */
260 #endif
261 
262     init_digest_types(env);
263     init_mac_types(env);
264     init_cipher_types(env);
265     init_algorithms_types(env);
266 
267     library_initialized = 1;
268     return 0;
269 }
270 
load(ErlNifEnv * env,void ** priv_data,ERL_NIF_TERM load_info)271 static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
272 {
273     int errline = initialize(env, load_info);
274     if (errline) {
275 	return errline;
276     }
277 
278     *priv_data = NULL;
279     library_refc++;
280     return 0;
281 }
282 
upgrade(ErlNifEnv * env,void ** priv_data,void ** old_priv_data,ERL_NIF_TERM load_info)283 static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data,
284 		   ERL_NIF_TERM load_info)
285 {
286     int errline;
287     if (*old_priv_data != NULL) {
288 	return __LINE__; /* Don't know how to do that */
289     }
290     if (*priv_data != NULL) {
291 	return __LINE__; /* Don't know how to do that */
292     }
293     errline = initialize(env, load_info);
294     if (errline) {
295 	return errline;
296     }
297     library_refc++;
298     return 0;
299 }
300 
unload(ErlNifEnv * env,void * priv_data)301 static void unload(ErlNifEnv* env, void* priv_data)
302 {
303     if (--library_refc == 0)
304         cleanup_algorithms_types(env);
305 }
306