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 "rsa.h"
22 #include "bn.h"
23 
24 static ERL_NIF_TERM rsa_generate_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
25 static ERL_NIF_TERM put_rsa_private_key(ErlNifEnv* env, const RSA *rsa);
26 static int check_erlang_interrupt(int maj, int min, BN_GENCB *ctxt);
27 
get_rsa_private_key(ErlNifEnv * env,ERL_NIF_TERM key,RSA * rsa)28 int get_rsa_private_key(ErlNifEnv* env, ERL_NIF_TERM key, RSA *rsa)
29 {
30     /* key=[E,N,D]|[E,N,D,P1,P2,E1,E2,C] */
31     ERL_NIF_TERM head, tail;
32     BIGNUM *e = NULL, *n = NULL, *d = NULL;
33     BIGNUM *p = NULL, *q = NULL;
34     BIGNUM *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL;
35 
36     if (!enif_get_list_cell(env, key, &head, &tail))
37         goto bad_arg;
38     if (!get_bn_from_bin(env, head, &e))
39         goto bad_arg;
40     if (!enif_get_list_cell(env, tail, &head, &tail))
41         goto bad_arg;
42     if (!get_bn_from_bin(env, head, &n))
43         goto bad_arg;
44     if (!enif_get_list_cell(env, tail, &head, &tail))
45         goto bad_arg;
46     if (!get_bn_from_bin(env, head, &d))
47         goto bad_arg;
48 
49     if (!RSA_set0_key(rsa, n, e, d))
50         goto err;
51     /* rsa now owns n, e, and d */
52     n = NULL;
53     e = NULL;
54     d = NULL;
55 
56     if (enif_is_empty_list(env, tail))
57         return 1;
58 
59     if (!enif_get_list_cell(env, tail, &head, &tail))
60         goto bad_arg;
61     if (!get_bn_from_bin(env, head, &p))
62         goto bad_arg;
63     if (!enif_get_list_cell(env, tail, &head, &tail))
64         goto bad_arg;
65     if (!get_bn_from_bin(env, head, &q))
66         goto bad_arg;
67     if (!enif_get_list_cell(env, tail, &head, &tail))
68         goto bad_arg;
69     if (!get_bn_from_bin(env, head, &dmp1))
70         goto bad_arg;
71     if (!enif_get_list_cell(env, tail, &head, &tail))
72         goto bad_arg;
73     if (!get_bn_from_bin(env, head, &dmq1))
74         goto bad_arg;
75     if (!enif_get_list_cell(env, tail, &head, &tail))
76         goto bad_arg;
77     if (!get_bn_from_bin(env, head, &iqmp))
78         goto bad_arg;
79     if (!enif_is_empty_list(env, tail))
80         goto bad_arg;
81 
82     if (!RSA_set0_factors(rsa, p, q))
83         goto err;
84     /* rsa now owns p and q */
85     p = NULL;
86     q = NULL;
87 
88     if (!RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp))
89         goto err;
90     /* rsa now owns dmp1, dmq1, and iqmp */
91     dmp1 = NULL;
92     dmq1 = NULL;
93     iqmp = NULL;
94 
95     return 1;
96 
97  bad_arg:
98  err:
99     if (e)
100         BN_free(e);
101     if (n)
102         BN_free(n);
103     if (d)
104         BN_free(d);
105     if (p)
106         BN_free(p);
107     if (q)
108         BN_free(q);
109     if (dmp1)
110         BN_free(dmp1);
111     if (dmq1)
112         BN_free(dmq1);
113     if (iqmp)
114         BN_free(iqmp);
115 
116     return 0;
117 }
118 
get_rsa_public_key(ErlNifEnv * env,ERL_NIF_TERM key,RSA * rsa)119 int get_rsa_public_key(ErlNifEnv* env, ERL_NIF_TERM key, RSA *rsa)
120 {
121     /* key=[E,N] */
122     ERL_NIF_TERM head, tail;
123     BIGNUM *e = NULL, *n = NULL;
124 
125     if (!enif_get_list_cell(env, key, &head, &tail))
126         goto bad_arg;
127     if (!get_bn_from_bin(env, head, &e))
128         goto bad_arg;
129     if (!enif_get_list_cell(env, tail, &head, &tail))
130         goto bad_arg;
131     if (!get_bn_from_bin(env, head, &n))
132         goto bad_arg;
133     if (!enif_is_empty_list(env, tail))
134         goto bad_arg;
135 
136     if (!RSA_set0_key(rsa, n, e, NULL))
137         goto err;
138     /* rsa now owns n and e */
139     n = NULL;
140     e = NULL;
141 
142     return 1;
143 
144  bad_arg:
145  err:
146     if (e)
147         BN_free(e);
148     if (n)
149         BN_free(n);
150 
151     return 0;
152 }
153 
154 /* Creates a term which can be parsed by get_rsa_private_key(). This is a list of plain integer binaries (not mpints). */
put_rsa_private_key(ErlNifEnv * env,const RSA * rsa)155 static ERL_NIF_TERM put_rsa_private_key(ErlNifEnv* env, const RSA *rsa)
156 {
157     ERL_NIF_TERM result[8];
158     const BIGNUM *n = NULL, *e = NULL, *d = NULL, *p = NULL, *q = NULL, *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL;
159 
160     /* Return at least [E,N,D] */
161     RSA_get0_key(rsa, &n, &e, &d);
162 
163     if ((result[0] = bin_from_bn(env, e)) == atom_error)  // Exponent E
164         goto err;
165     if ((result[1] = bin_from_bn(env, n)) == atom_error)  // Modulus N = p*q
166         goto err;
167     if ((result[2] = bin_from_bn(env, d)) == atom_error)  // Exponent D
168         goto err;
169 
170     /* Check whether the optional additional parameters are available */
171     RSA_get0_factors(rsa, &p, &q);
172     RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
173 
174     if (p && q && dmp1 && dmq1 && iqmp) {
175         if ((result[3] = bin_from_bn(env, p)) == atom_error)     // Factor p
176             goto err;
177         if ((result[4] = bin_from_bn(env, q)) == atom_error)     // Factor q
178             goto err;
179         if ((result[5] = bin_from_bn(env, dmp1)) == atom_error)  // D mod (p-1)
180             goto err;
181         if ((result[6] = bin_from_bn(env, dmq1)) == atom_error)  // D mod (q-1)
182             goto err;
183         if ((result[7] = bin_from_bn(env, iqmp)) == atom_error)  // (1/q) mod p
184             goto err;
185 
186 	return enif_make_list_from_array(env, result, 8);
187     } else {
188 	return enif_make_list_from_array(env, result, 3);
189     }
190 
191  err:
192     return enif_make_badarg(env);
193 }
194 
check_erlang_interrupt(int maj,int min,BN_GENCB * ctxt)195 static int check_erlang_interrupt(int maj, int min, BN_GENCB *ctxt)
196 {
197     ErlNifEnv *env = BN_GENCB_get_arg(ctxt);
198 
199     if (!enif_is_current_process_alive(env)) {
200 	return 0;
201     } else {
202 	return 1;
203     }
204 }
205 
rsa_generate_key(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])206 static ERL_NIF_TERM rsa_generate_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
207 {/* (ModulusSize, PublicExponent) */
208     ERL_NIF_TERM ret;
209     int modulus_bits;
210     BIGNUM *pub_exp = NULL, *three = NULL;
211     RSA *rsa = NULL;
212     BN_GENCB *intr_cb = NULL;
213 #ifndef HAVE_OPAQUE_BN_GENCB
214     BN_GENCB intr_cb_buf;
215 #endif
216 
217     ASSERT(argc == 2);
218 
219     if (!enif_get_int(env, argv[0], &modulus_bits))
220         goto bad_arg;
221     if (modulus_bits < 256)
222         goto bad_arg;
223     if (!get_bn_from_bin(env, argv[1], &pub_exp))
224         goto bad_arg;
225 
226     /* Make sure the public exponent is large enough (at least 3).
227      * Without this, RSA_generate_key_ex() can run forever. */
228     if ((three = BN_new()) == NULL)
229         goto err;
230     if (!BN_set_word(three, 3))
231         goto err;
232     if (BN_cmp(pub_exp, three) < 0)
233         goto err;
234 
235     /* For large keys, prime generation can take many seconds. Set up
236      * the callback which we use to test whether the process has been
237      * interrupted. */
238 #ifdef HAVE_OPAQUE_BN_GENCB
239     if ((intr_cb = BN_GENCB_new()) == NULL)
240         goto err;
241 #else
242     intr_cb = &intr_cb_buf;
243 #endif
244     BN_GENCB_set(intr_cb, check_erlang_interrupt, env);
245 
246     if ((rsa = RSA_new()) == NULL)
247         goto err;
248 
249     if (!RSA_generate_key_ex(rsa, modulus_bits, pub_exp, intr_cb))
250         goto err;
251 
252     ret = put_rsa_private_key(env, rsa);
253     goto done;
254 
255  bad_arg:
256     return enif_make_badarg(env);
257 
258  err:
259     ret = atom_error;
260 
261  done:
262     if (pub_exp)
263         BN_free(pub_exp);
264     if (three)
265         BN_free(three);
266 #ifdef HAVE_OPAQUE_BN_GENCB
267     if (intr_cb)
268         BN_GENCB_free(intr_cb);
269 #endif
270     if (rsa)
271         RSA_free(rsa);
272     return ret;
273 }
274 
rsa_generate_key_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])275 ERL_NIF_TERM rsa_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
276 {
277     /* RSA key generation can take a long time (>1 sec for a large
278      * modulus), so schedule it as a CPU-bound operation. */
279     return enif_schedule_nif(env, "rsa_generate_key",
280 			     ERL_NIF_DIRTY_JOB_CPU_BOUND,
281 			     rsa_generate_key, argc, argv);
282 }
283