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 "ec.h"
22 #include "bn.h"
23 
24 #ifdef HAVE_EC
25 static EC_KEY* ec_key_new(ErlNifEnv* env, ERL_NIF_TERM curve_arg, size_t *size);
26 static ERL_NIF_TERM point2term(ErlNifEnv* env,
27 			       const EC_GROUP *group,
28 			       const EC_POINT *point,
29 			       point_conversion_form_t form);
30 
make_badarg_maybe(ErlNifEnv * env)31 ERL_NIF_TERM make_badarg_maybe(ErlNifEnv* env)
32 {
33     ERL_NIF_TERM reason;
34     if (enif_has_pending_exception(env, &reason))
35 	return reason; /* dummy return value ignored */
36     else
37 	return enif_make_badarg(env);
38 }
39 
ec_key_new(ErlNifEnv * env,ERL_NIF_TERM curve_arg,size_t * size)40 static EC_KEY* ec_key_new(ErlNifEnv* env, ERL_NIF_TERM curve_arg, size_t *size)
41 {
42     EC_KEY *key = NULL;
43     int c_arity = -1;
44     const ERL_NIF_TERM* curve;
45     ErlNifBinary seed;
46     BIGNUM *p = NULL;
47     BIGNUM *a = NULL;
48     BIGNUM *b = NULL;
49     BIGNUM *bn_order = NULL;
50     BIGNUM *cofactor = NULL;
51     EC_GROUP *group = NULL;
52     EC_POINT *point = NULL;
53     int f_arity = -1;
54     const ERL_NIF_TERM *field;
55     int p_arity = -1;
56     const ERL_NIF_TERM *prime;
57     long field_bits;
58 
59     /* {Field, Prime, Point, Order, CoFactor} = Curve */
60     if (!enif_get_tuple(env, curve_arg, &c_arity, &curve))
61         goto err;
62     if (c_arity != 5)
63         goto err;
64     if (!get_bn_from_bin_sz(env, curve[3], &bn_order, size))
65         goto err;
66     if (curve[4] != atom_none) {
67         if (!get_bn_from_bin(env, curve[4], &cofactor))
68             goto err;
69     }
70 
71     /* {A, B, Seed} = Prime */
72     if (!enif_get_tuple(env, curve[1], &p_arity, &prime))
73         goto err;
74     if (!get_bn_from_bin(env, prime[0], &a))
75         goto err;
76     if (!get_bn_from_bin(env, prime[1], &b))
77         goto err;
78 
79     if (!enif_get_tuple(env, curve[0], &f_arity, &field))
80         goto err;
81 
82     if (f_arity == 2 && field[0] == atom_prime_field) {
83         /* {prime_field, Prime} */
84         if (!get_bn_from_bin(env, field[1], &p))
85             goto err;
86         if (BN_is_negative(p))
87             goto err;
88         if (BN_is_zero(p))
89             goto err;
90 
91         field_bits = BN_num_bits(p);
92         if (field_bits > OPENSSL_ECC_MAX_FIELD_BITS)
93             goto err;
94 
95         /* create the EC_GROUP structure */
96         if ((group = EC_GROUP_new_curve_GFp(p, a, b, NULL)) == NULL)
97             goto err;
98 
99     } else if (f_arity == 3 && field[0] == atom_characteristic_two_field) {
100 #if defined(OPENSSL_NO_EC2M)
101         enif_raise_exception(env, atom_notsup);
102         goto err;
103 #else
104         /* {characteristic_two_field, M, Basis} */
105         int b_arity = -1;
106         const ERL_NIF_TERM* basis;
107 
108         if ((p = BN_new()) == NULL)
109             goto err;
110         if (!enif_get_long(env, field[1], &field_bits))
111             goto err;
112         if (field_bits > OPENSSL_ECC_MAX_FIELD_BITS || field_bits > INT_MAX)
113             goto err;
114 
115         if (enif_get_tuple(env, field[2], &b_arity, &basis)) {
116             if (b_arity == 2) {
117                 unsigned int k1;
118 
119                 if (basis[0] != atom_tpbasis)
120                     goto err;
121                 if (!enif_get_uint(env, basis[1], &k1))
122                     goto err;
123 
124                 /* {tpbasis, k} = Basis */
125                 if (field_bits <= k1 || k1 == 0 || k1 > INT_MAX)
126                     goto err;
127 
128                 /* create the polynomial */
129                 if (!BN_set_bit(p, (int)field_bits))
130                     goto err;
131                 if (!BN_set_bit(p, (int)k1))
132                     goto err;
133                 if (!BN_set_bit(p, 0))
134                     goto err;
135 
136             } else if (b_arity == 4) {
137                 unsigned int k1, k2, k3;
138 
139                 if (basis[0] != atom_ppbasis)
140                     goto err;
141                 if (!enif_get_uint(env, basis[1], &k1))
142                     goto err;
143                 if (!enif_get_uint(env, basis[2], &k2))
144                     goto err;
145                 if (!enif_get_uint(env, basis[3], &k3))
146                     goto err;
147 
148                 /* {ppbasis, k1, k2, k3} = Basis */
149                 if (field_bits <= k3 || k3 <= k2 || k2 <= k1 || k1 == 0 || k3 > INT_MAX || k2 > INT_MAX || k1 > INT_MAX)
150                     goto err;
151 
152                 /* create the polynomial */
153                 if (!BN_set_bit(p, (int)field_bits))
154                     goto err;
155                 if (!BN_set_bit(p, (int)k1))
156                     goto err;
157                 if (!BN_set_bit(p, (int)k2))
158                     goto err;
159                 if (!BN_set_bit(p, (int)k3))
160                     goto err;
161                 if (!BN_set_bit(p, 0))
162                     goto err;
163 
164             } else
165                 goto err;
166         } else if (field[2] == atom_onbasis) {
167             /* onbasis = Basis */
168             /* no parameters */
169             goto err;
170 
171         } else
172             goto err;
173 
174         if ((group = EC_GROUP_new_curve_GF2m(p, a, b, NULL)) == NULL)
175             goto err;
176 #endif
177     } else
178         goto err;
179 
180     if (enif_inspect_binary(env, prime[2], &seed)) {
181         if (!EC_GROUP_set_seed(group, seed.data, seed.size))
182             goto err;
183     }
184 
185     if (!term2point(env, curve[2], group, &point))
186         goto err;
187 
188     if (BN_is_negative(bn_order))
189         goto err;
190     if (BN_is_zero(bn_order))
191         goto err;
192     if (BN_num_bits(bn_order) > (int)field_bits + 1)
193         goto err;
194 
195     if (!EC_GROUP_set_generator(group, point, bn_order, cofactor))
196         goto err;
197 
198     EC_GROUP_set_asn1_flag(group, 0x0);
199 
200     if ((key = EC_KEY_new()) == NULL)
201         goto err;
202 
203     if (!EC_KEY_set_group(key, group))
204         goto err;
205 
206     goto done;
207 
208  err:
209     if (key)
210         EC_KEY_free(key);
211     key = NULL;
212 
213  done:
214     /* some OpenSSL structures are mem-dup'ed into the key,
215        so we have to free our copies here */
216     if (bn_order)
217         BN_free(bn_order);
218     if (cofactor)
219         BN_free(cofactor);
220     if (a)
221         BN_free(a);
222     if (b)
223         BN_free(b);
224     if (p)
225         BN_free(p);
226     if (group)
227         EC_GROUP_free(group);
228     if (point)
229         EC_POINT_free(point);
230 
231     return key;
232 }
233 
point2term(ErlNifEnv * env,const EC_GROUP * group,const EC_POINT * point,point_conversion_form_t form)234 static ERL_NIF_TERM point2term(ErlNifEnv* env,
235 			       const EC_GROUP *group,
236 			       const EC_POINT *point,
237 			       point_conversion_form_t form)
238 {
239     ERL_NIF_TERM ret;
240     size_t dlen;
241     ErlNifBinary bin;
242     int bin_alloc = 0;
243 
244     if ((dlen = EC_POINT_point2oct(group, point, form, NULL, 0, NULL)) == 0)
245 	return atom_undefined;
246 
247     if (!enif_alloc_binary(dlen, &bin))
248         goto err;
249     bin_alloc = 1;
250 
251     if (!EC_POINT_point2oct(group, point, form, bin.data, bin.size, NULL))
252         goto err;
253 
254     ERL_VALGRIND_MAKE_MEM_DEFINED(bin.data, bin.size);
255 
256     ret = enif_make_binary(env, &bin);
257     bin_alloc = 0;
258     goto done;
259 
260  err:
261     if (bin_alloc)
262         enif_release_binary(&bin);
263     ret = enif_make_badarg(env);
264 
265  done:
266     return ret;
267 }
268 
term2point(ErlNifEnv * env,ERL_NIF_TERM term,EC_GROUP * group,EC_POINT ** pptr)269 int term2point(ErlNifEnv* env, ERL_NIF_TERM term, EC_GROUP *group, EC_POINT **pptr)
270 {
271     ErlNifBinary bin;
272     EC_POINT *point = NULL;
273 
274     if (!enif_inspect_binary(env, term, &bin))
275         goto err;
276 
277     if ((point = EC_POINT_new(group)) == NULL)
278         goto err;
279 
280     /* set the point conversion form */
281     EC_GROUP_set_point_conversion_form(group, (point_conversion_form_t)(bin.data[0] & ~0x01));
282 
283     /* extract the ec point */
284     if (!EC_POINT_oct2point(group, point, bin.data, bin.size, NULL))
285         goto err;
286 
287     *pptr = point;
288     return 1;
289 
290  err:
291     if (point)
292         EC_POINT_free(point);
293     return 0;
294 }
295 
get_ec_key(ErlNifEnv * env,ERL_NIF_TERM curve,ERL_NIF_TERM priv,ERL_NIF_TERM pub,EC_KEY ** res)296 int get_ec_key(ErlNifEnv* env,
297 		      ERL_NIF_TERM curve, ERL_NIF_TERM priv, ERL_NIF_TERM pub,
298 		      EC_KEY** res)
299 {
300     return get_ec_key_sz(env, curve, priv, pub, res, NULL);
301 }
302 
get_ec_key_sz(ErlNifEnv * env,ERL_NIF_TERM curve,ERL_NIF_TERM priv,ERL_NIF_TERM pub,EC_KEY ** res,size_t * size)303 int get_ec_key_sz(ErlNifEnv* env,
304                   ERL_NIF_TERM curve, ERL_NIF_TERM priv, ERL_NIF_TERM pub,
305                   EC_KEY** res,
306                   size_t* size)
307 {
308     EC_KEY *key = NULL;
309     BIGNUM *priv_key = NULL;
310     EC_POINT *pub_key = NULL;
311     EC_GROUP *group = NULL;
312 
313     if (priv != atom_undefined) {
314         if (!get_bn_from_bin(env, priv, &priv_key))
315             goto err;
316     }
317     if (pub != atom_undefined) {
318         if (!enif_is_binary(env, pub))
319             goto err;
320     }
321 
322     if ((key = ec_key_new(env, curve, size)) == NULL)
323         goto err;
324 
325     if ((group = EC_GROUP_dup(EC_KEY_get0_group(key))) == NULL)
326         goto err;
327 
328     if (term2point(env, pub, group, &pub_key)) {
329         if (!EC_KEY_set_public_key(key, pub_key))
330             goto err;
331     }
332 
333     if (priv != atom_undefined && !BN_is_zero(priv_key)) {
334         if (!EC_KEY_set_private_key(key, priv_key))
335             goto err;
336 
337         /* calculate public key (if necessary) */
338         if (EC_KEY_get0_public_key(key) == NULL) {
339             /* the public key was not included in the SEC1 private
340              * key => calculate the public key */
341             if ((pub_key = EC_POINT_new(group)) == NULL)
342                 goto err;
343             if (!EC_POINT_copy(pub_key, EC_GROUP_get0_generator(group)))
344                 goto err;
345             if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, NULL))
346                 goto err;
347             if (!EC_KEY_set_public_key(key, pub_key))
348                 goto err;
349         }
350     }
351     goto done;
352 
353  err:
354     if (key)
355         EC_KEY_free(key);
356     key = NULL;
357 
358  done:
359     /* some OpenSSL structures are mem-dup'ed into the key,
360        so we have to free our copies here */
361     if (priv_key)
362         BN_clear_free(priv_key);
363     if (group)
364         EC_GROUP_free(group);
365     if (pub_key)
366         EC_POINT_free(pub_key);
367 
368     if (key == NULL)
369         return 0;
370 
371     *res = key;
372     return 1;
373 }
374 
375 #endif /* HAVE_EC */
376 
ec_key_generate(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])377 ERL_NIF_TERM ec_key_generate(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
378 {
379 #if defined(HAVE_EC)
380     EC_KEY *key = NULL;
381     const EC_GROUP *group;
382     const EC_POINT *public_key;
383     ERL_NIF_TERM priv_key;
384     ERL_NIF_TERM pub_key;
385     ERL_NIF_TERM ret;
386     size_t size;
387 
388     if (!get_ec_key_sz(env, argv[0], argv[1], atom_undefined, &key, &size))
389         goto bad_arg;
390 
391     if (argv[1] == atom_undefined) {
392 	if (!EC_KEY_generate_key(key))
393             goto err;
394     }
395 
396     group = EC_KEY_get0_group(key);
397     public_key = EC_KEY_get0_public_key(key);
398 
399     if (group == NULL || public_key == NULL) {
400         pub_key = atom_undefined;
401 
402     } else {
403         pub_key = point2term(env, group, public_key,
404                              EC_KEY_get_conv_form(key));
405     }
406 
407     priv_key = bn2term(env, size, EC_KEY_get0_private_key(key));
408     ret = enif_make_tuple2(env, pub_key, priv_key);
409     goto done;
410 
411  err:
412  bad_arg:
413     ret = make_badarg_maybe(env);
414 
415  done:
416     if (key)
417         EC_KEY_free(key);
418     return ret;
419 
420 #else
421     return atom_notsup;
422 #endif
423 }
424