/*
* %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);
}