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 #include "dh.h"
22 #include "bn.h"
23
dh_generate_key_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])24 ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
25 {/* (PrivKey|undefined, DHParams=[P,G], Mpint, Len|0) */
26 #ifdef HAVE_DH
27 DH *dh_params = NULL;
28 unsigned int mpint; /* 0 or 4 */
29 ERL_NIF_TERM head, tail;
30 BIGNUM *dh_p = NULL;
31 BIGNUM *dh_p_shared;
32 BIGNUM *dh_g = NULL;
33 BIGNUM *priv_key_in = NULL;
34 unsigned long len = 0;
35 unsigned char *pub_ptr, *prv_ptr;
36 int pub_len, prv_len;
37 ERL_NIF_TERM ret_pub, ret_prv, ret;
38 const BIGNUM *pub_key_gen, *priv_key_gen;
39 #if defined(HAS_EVP_PKEY_CTX) && (! DISABLE_EVP_DH)
40 EVP_PKEY_CTX *ctx = NULL;
41 EVP_PKEY *dhkey = NULL, *params = NULL;
42 #endif
43
44 ASSERT(argc == 4);
45
46 if (argv[0] != atom_undefined) {
47 if (!get_bn_from_bin(env, argv[0], &priv_key_in))
48 goto bad_arg;
49 }
50 if (!enif_get_list_cell(env, argv[1], &head, &tail))
51 goto bad_arg;
52 if (!get_bn_from_bin(env, head, &dh_p))
53 goto bad_arg;
54
55 if (!enif_get_list_cell(env, tail, &head, &tail))
56 goto bad_arg;
57 if (!get_bn_from_bin(env, head, &dh_g))
58 goto bad_arg;
59
60 if (!enif_is_empty_list(env, tail))
61 goto bad_arg;
62
63 if (!enif_get_uint(env, argv[2], &mpint))
64 goto bad_arg;
65 if (mpint != 0 && mpint != 4)
66 goto bad_arg;
67
68 if (!enif_get_ulong(env, argv[3], &len))
69 goto bad_arg;
70 if (len > LONG_MAX)
71 goto bad_arg;
72
73 /* Load dh_params with values to use by the generator.
74 Mem mgmnt transfered from dh_p etc to dh_params */
75 if ((dh_params = DH_new()) == NULL)
76 goto bad_arg;
77 if (priv_key_in) {
78 if (!DH_set0_key(dh_params, NULL, priv_key_in))
79 goto bad_arg;
80 /* On success, dh_params owns priv_key_in */
81 priv_key_in = NULL;
82 }
83 if (!DH_set0_pqg(dh_params, dh_p, NULL, dh_g))
84 goto bad_arg;
85 dh_p_shared = dh_p; /* Don't free this because dh_params owns it */
86 /* On success, dh_params owns dh_p and dh_g */
87 dh_p = NULL;
88 dh_g = NULL;
89
90 if (len) {
91 int bn_len;
92
93 if ((bn_len = BN_num_bits(dh_p_shared)) < 0)
94 goto bad_arg;
95 dh_p_shared = NULL; /* dh_params owns the reference */
96 if (len >= (size_t)bn_len)
97 goto bad_arg;
98
99 if (!DH_set_length(dh_params, (long)len))
100 goto bad_arg;
101 }
102
103 #if defined(HAS_EVP_PKEY_CTX) && (! DISABLE_EVP_DH)
104 if ((params = EVP_PKEY_new()) == NULL)
105 goto err;
106
107 /* set the key referenced by params to dh_params... */
108 if (EVP_PKEY_set1_DH(params, dh_params) != 1)
109 goto err;
110
111 if ((ctx = EVP_PKEY_CTX_new(params, NULL)) == NULL)
112 goto err;
113
114 if (EVP_PKEY_keygen_init(ctx) != 1)
115 goto err;
116
117 if ((dhkey = EVP_PKEY_new()) == NULL)
118 goto err;
119
120 /* key gen op, key written to ppkey (=last arg) */
121 if (EVP_PKEY_keygen(ctx, &dhkey) != 1)
122 goto err;
123
124 DH_free(dh_params);
125 if ((dh_params = EVP_PKEY_get1_DH(dhkey)) == NULL)
126 goto err;
127
128 #else
129 if (!DH_generate_key(dh_params))
130 goto err;
131 #endif
132
133 DH_get0_key(dh_params, &pub_key_gen, &priv_key_gen);
134
135 if ((pub_len = BN_num_bytes(pub_key_gen)) < 0)
136 goto err;
137 if ((prv_len = BN_num_bytes(priv_key_gen)) < 0)
138 goto err;
139
140 if ((pub_ptr = enif_make_new_binary(env, (size_t)pub_len+mpint, &ret_pub)) == NULL)
141 goto err;
142 if ((prv_ptr = enif_make_new_binary(env, (size_t)prv_len+mpint, &ret_prv)) == NULL)
143 goto err;
144
145 if (mpint) {
146 put_uint32(pub_ptr, (unsigned int)pub_len);
147 pub_ptr += 4;
148
149 put_uint32(prv_ptr, (unsigned int)prv_len);
150 prv_ptr += 4;
151 }
152
153 if (BN_bn2bin(pub_key_gen, pub_ptr) < 0)
154 goto err;
155 if (BN_bn2bin(priv_key_gen, prv_ptr) < 0)
156 goto err;
157
158 ERL_VALGRIND_MAKE_MEM_DEFINED(pub_ptr, pub_len);
159 ERL_VALGRIND_MAKE_MEM_DEFINED(prv_ptr, prv_len);
160
161 ret = enif_make_tuple2(env, ret_pub, ret_prv);
162 goto done;
163
164 bad_arg:
165 ret = enif_make_badarg(env);
166 goto done;
167
168 err:
169 ret = atom_error;
170
171 done:
172 if (priv_key_in)
173 BN_free(priv_key_in);
174 if (dh_p)
175 BN_free(dh_p);
176 if (dh_g)
177 BN_free(dh_g);
178 if (dh_params)
179 DH_free(dh_params);
180
181 #if defined(HAS_EVP_PKEY_CTX) && (! DISABLE_EVP_DH)
182 if (ctx)
183 EVP_PKEY_CTX_free(ctx);
184 if (dhkey)
185 EVP_PKEY_free(dhkey);
186 if (params)
187 EVP_PKEY_free(params);
188 #endif
189
190 return ret;
191 #else
192 return enif_raise_exception(env, atom_notsup);
193 #endif
194 }
195
dh_compute_key_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])196 ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
197 {/* (OthersPublicKey, MyPrivateKey, DHParams=[P,G]) */
198 #ifdef HAVE_DH
199 BIGNUM *other_pub_key = NULL;
200 BIGNUM *dh_p = NULL;
201 BIGNUM *dh_g = NULL;
202 BIGNUM *dummy_pub_key = NULL;
203 BIGNUM *priv_key = NULL;
204 DH *dh_priv = NULL;
205 ERL_NIF_TERM head, tail, ret;
206 ErlNifBinary ret_bin;
207 int size;
208 int ret_bin_alloc = 0;
209 int dh_size;
210
211 /* Check the arguments and get
212 my private key (dh_priv),
213 the peer's public key (other_pub_key),
214 the parameters p & q
215 */
216 ASSERT(argc == 3);
217
218 if (!get_bn_from_bin(env, argv[0], &other_pub_key))
219 goto bad_arg;
220 if (!get_bn_from_bin(env, argv[1], &priv_key))
221 goto bad_arg;
222
223 if (!enif_get_list_cell(env, argv[2], &head, &tail))
224 goto bad_arg;
225 if (!get_bn_from_bin(env, head, &dh_p))
226 goto bad_arg;
227
228 if (!enif_get_list_cell(env, tail, &head, &tail))
229 goto bad_arg;
230 if (!get_bn_from_bin(env, head, &dh_g))
231 goto bad_arg;
232
233 if (!enif_is_empty_list(env, tail))
234 goto bad_arg;
235
236 /* Note: DH_set0_key() does not allow setting only the
237 * private key, although DH_compute_key() does not use the
238 * public key. Work around this limitation by setting
239 * the public key to a copy of the private key.
240 */
241 if ((dummy_pub_key = BN_dup(priv_key)) == NULL)
242 goto err;
243 if ((dh_priv = DH_new()) == NULL)
244 goto err;
245
246 if (!DH_set0_key(dh_priv, dummy_pub_key, priv_key))
247 goto err;
248 /* dh_priv owns dummy_pub_key and priv_key now */
249 dummy_pub_key = NULL;
250 priv_key = NULL;
251
252 if (!DH_set0_pqg(dh_priv, dh_p, NULL, dh_g))
253 goto err;
254 /* dh_priv owns dh_p and dh_g now */
255 dh_p = NULL;
256 dh_g = NULL;
257
258 if ((dh_size = DH_size(dh_priv)) < 0)
259 goto err;
260 if (!enif_alloc_binary((size_t)dh_size, &ret_bin))
261 goto err;
262 ret_bin_alloc = 1;
263
264 if ((size = DH_compute_key(ret_bin.data, other_pub_key, dh_priv)) < 0)
265 goto err;
266 if (size == 0)
267 goto err;
268
269 if ((size_t)size != ret_bin.size) {
270 if (!enif_realloc_binary(&ret_bin, (size_t)size))
271 goto err;
272 }
273
274 ret = enif_make_binary(env, &ret_bin);
275 ret_bin_alloc = 0;
276 goto done;
277
278 bad_arg:
279 err:
280 if (ret_bin_alloc)
281 enif_release_binary(&ret_bin);
282 ret = enif_make_badarg(env);
283
284 done:
285 if (other_pub_key)
286 BN_free(other_pub_key);
287 if (priv_key)
288 BN_free(priv_key);
289 if (dh_p)
290 BN_free(dh_p);
291 if (dh_g)
292 BN_free(dh_g);
293 if (dummy_pub_key)
294 BN_free(dummy_pub_key);
295 if (dh_priv)
296 DH_free(dh_priv);
297
298 return ret;
299 #else
300 return enif_raise_exception(env, atom_notsup);
301 #endif
302 }
303