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