aboutsummaryrefslogtreecommitdiffstats
path: root/lib/crypto/c_src/rsa.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/crypto/c_src/rsa.c')
-rw-r--r--lib/crypto/c_src/rsa.c282
1 files changed, 282 insertions, 0 deletions
diff --git a/lib/crypto/c_src/rsa.c b/lib/crypto/c_src/rsa.c
new file mode 100644
index 0000000000..e9f29aa496
--- /dev/null
+++ b/lib/crypto/c_src/rsa.c
@@ -0,0 +1,282 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2010-2018. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#include "rsa.h"
+#include "bn.h"
+
+static ERL_NIF_TERM rsa_generate_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM put_rsa_private_key(ErlNifEnv* env, const RSA *rsa);
+static int check_erlang_interrupt(int maj, int min, BN_GENCB *ctxt);
+
+int get_rsa_private_key(ErlNifEnv* env, ERL_NIF_TERM key, RSA *rsa)
+{
+ /* key=[E,N,D]|[E,N,D,P1,P2,E1,E2,C] */
+ ERL_NIF_TERM head, tail;
+ BIGNUM *e = NULL, *n = NULL, *d = NULL;
+ BIGNUM *p = NULL, *q = NULL;
+ BIGNUM *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL;
+
+ if (!enif_get_list_cell(env, key, &head, &tail))
+ goto bad_arg;
+ if (!get_bn_from_bin(env, head, &e))
+ goto bad_arg;
+ if (!enif_get_list_cell(env, tail, &head, &tail))
+ goto bad_arg;
+ if (!get_bn_from_bin(env, head, &n))
+ goto bad_arg;
+ if (!enif_get_list_cell(env, tail, &head, &tail))
+ goto bad_arg;
+ if (!get_bn_from_bin(env, head, &d))
+ goto bad_arg;
+
+ if (!RSA_set0_key(rsa, n, e, d))
+ goto err;
+ /* rsa now owns n, e, and d */
+ n = NULL;
+ e = NULL;
+ d = NULL;
+
+ if (enif_is_empty_list(env, tail))
+ return 1;
+
+ if (!enif_get_list_cell(env, tail, &head, &tail))
+ goto bad_arg;
+ if (!get_bn_from_bin(env, head, &p))
+ goto bad_arg;
+ if (!enif_get_list_cell(env, tail, &head, &tail))
+ goto bad_arg;
+ if (!get_bn_from_bin(env, head, &q))
+ goto bad_arg;
+ if (!enif_get_list_cell(env, tail, &head, &tail))
+ goto bad_arg;
+ if (!get_bn_from_bin(env, head, &dmp1))
+ goto bad_arg;
+ if (!enif_get_list_cell(env, tail, &head, &tail))
+ goto bad_arg;
+ if (!get_bn_from_bin(env, head, &dmq1))
+ goto bad_arg;
+ if (!enif_get_list_cell(env, tail, &head, &tail))
+ goto bad_arg;
+ if (!get_bn_from_bin(env, head, &iqmp))
+ goto bad_arg;
+ if (!enif_is_empty_list(env, tail))
+ goto bad_arg;
+
+ if (!RSA_set0_factors(rsa, p, q))
+ goto err;
+ /* rsa now owns p and q */
+ p = NULL;
+ q = NULL;
+
+ if (!RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp))
+ goto err;
+ /* rsa now owns dmp1, dmq1, and iqmp */
+ dmp1 = NULL;
+ dmq1 = NULL;
+ iqmp = NULL;
+
+ return 1;
+
+ bad_arg:
+ err:
+ if (e)
+ BN_free(e);
+ if (n)
+ BN_free(n);
+ if (d)
+ BN_free(d);
+ if (p)
+ BN_free(p);
+ if (q)
+ BN_free(q);
+ if (dmp1)
+ BN_free(dmp1);
+ if (dmq1)
+ BN_free(dmq1);
+ if (iqmp)
+ BN_free(iqmp);
+
+ return 0;
+}
+
+int get_rsa_public_key(ErlNifEnv* env, ERL_NIF_TERM key, RSA *rsa)
+{
+ /* key=[E,N] */
+ ERL_NIF_TERM head, tail;
+ BIGNUM *e = NULL, *n = NULL;
+
+ if (!enif_get_list_cell(env, key, &head, &tail))
+ goto bad_arg;
+ if (!get_bn_from_bin(env, head, &e))
+ goto bad_arg;
+ if (!enif_get_list_cell(env, tail, &head, &tail))
+ goto bad_arg;
+ if (!get_bn_from_bin(env, head, &n))
+ goto bad_arg;
+ if (!enif_is_empty_list(env, tail))
+ goto bad_arg;
+
+ if (!RSA_set0_key(rsa, n, e, NULL))
+ goto err;
+ /* rsa now owns n and e */
+ n = NULL;
+ e = NULL;
+
+ return 1;
+
+ bad_arg:
+ err:
+ if (e)
+ BN_free(e);
+ if (n)
+ BN_free(n);
+
+ return 0;
+}
+
+/* Creates a term which can be parsed by get_rsa_private_key(). This is a list of plain integer binaries (not mpints). */
+static ERL_NIF_TERM put_rsa_private_key(ErlNifEnv* env, const RSA *rsa)
+{
+ ERL_NIF_TERM result[8];
+ const BIGNUM *n = NULL, *e = NULL, *d = NULL, *p = NULL, *q = NULL, *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL;
+
+ /* Return at least [E,N,D] */
+ RSA_get0_key(rsa, &n, &e, &d);
+
+ if ((result[0] = bin_from_bn(env, e)) == atom_error) // Exponent E
+ goto err;
+ if ((result[1] = bin_from_bn(env, n)) == atom_error) // Modulus N = p*q
+ goto err;
+ if ((result[2] = bin_from_bn(env, d)) == atom_error) // Exponent D
+ goto err;
+
+ /* Check whether the optional additional parameters are available */
+ RSA_get0_factors(rsa, &p, &q);
+ RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
+
+ if (p && q && dmp1 && dmq1 && iqmp) {
+ if ((result[3] = bin_from_bn(env, p)) == atom_error) // Factor p
+ goto err;
+ if ((result[4] = bin_from_bn(env, q)) == atom_error) // Factor q
+ goto err;
+ if ((result[5] = bin_from_bn(env, dmp1)) == atom_error) // D mod (p-1)
+ goto err;
+ if ((result[6] = bin_from_bn(env, dmq1)) == atom_error) // D mod (q-1)
+ goto err;
+ if ((result[7] = bin_from_bn(env, iqmp)) == atom_error) // (1/q) mod p
+ goto err;
+
+ return enif_make_list_from_array(env, result, 8);
+ } else {
+ return enif_make_list_from_array(env, result, 3);
+ }
+
+ err:
+ return enif_make_badarg(env);
+}
+
+static int check_erlang_interrupt(int maj, int min, BN_GENCB *ctxt)
+{
+ ErlNifEnv *env = BN_GENCB_get_arg(ctxt);
+
+ if (!enif_is_current_process_alive(env)) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static ERL_NIF_TERM rsa_generate_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (ModulusSize, PublicExponent) */
+ ERL_NIF_TERM ret;
+ int modulus_bits;
+ BIGNUM *pub_exp = NULL, *three = NULL;
+ RSA *rsa = NULL;
+ BN_GENCB *intr_cb = NULL;
+#ifndef HAVE_OPAQUE_BN_GENCB
+ BN_GENCB intr_cb_buf;
+#endif
+
+ ASSERT(argc == 2);
+
+ if (!enif_get_int(env, argv[0], &modulus_bits))
+ goto bad_arg;
+ if (modulus_bits < 256)
+ goto bad_arg;
+ if (!get_bn_from_bin(env, argv[1], &pub_exp))
+ goto bad_arg;
+
+ /* Make sure the public exponent is large enough (at least 3).
+ * Without this, RSA_generate_key_ex() can run forever. */
+ if ((three = BN_new()) == NULL)
+ goto err;
+ if (!BN_set_word(three, 3))
+ goto err;
+ if (BN_cmp(pub_exp, three) < 0)
+ goto err;
+
+ /* For large keys, prime generation can take many seconds. Set up
+ * the callback which we use to test whether the process has been
+ * interrupted. */
+#ifdef HAVE_OPAQUE_BN_GENCB
+ if ((intr_cb = BN_GENCB_new()) == NULL)
+ goto err;
+#else
+ intr_cb = &intr_cb_buf;
+#endif
+ BN_GENCB_set(intr_cb, check_erlang_interrupt, env);
+
+ if ((rsa = RSA_new()) == NULL)
+ goto err;
+
+ if (!RSA_generate_key_ex(rsa, modulus_bits, pub_exp, intr_cb))
+ goto err;
+
+ ret = put_rsa_private_key(env, rsa);
+ goto done;
+
+ bad_arg:
+ return enif_make_badarg(env);
+
+ err:
+ ret = atom_error;
+
+ done:
+ if (pub_exp)
+ BN_free(pub_exp);
+ if (three)
+ BN_free(three);
+#ifdef HAVE_OPAQUE_BN_GENCB
+ if (intr_cb)
+ BN_GENCB_free(intr_cb);
+#endif
+ if (rsa)
+ RSA_free(rsa);
+ return ret;
+}
+
+ERL_NIF_TERM rsa_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ /* RSA key generation can take a long time (>1 sec for a large
+ * modulus), so schedule it as a CPU-bound operation. */
+ return enif_schedule_nif(env, "rsa_generate_key",
+ ERL_NIF_DIRTY_JOB_CPU_BOUND,
+ rsa_generate_key, argc, argv);
+}