/*
* %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 "ec.h"
#include "bn.h"
#ifdef HAVE_EC
static EC_KEY* ec_key_new(ErlNifEnv* env, ERL_NIF_TERM curve_arg);
static ERL_NIF_TERM point2term(ErlNifEnv* env,
const EC_GROUP *group,
const EC_POINT *point,
point_conversion_form_t form);
ERL_NIF_TERM make_badarg_maybe(ErlNifEnv* env)
{
ERL_NIF_TERM reason;
if (enif_has_pending_exception(env, &reason))
return reason; /* dummy return value ignored */
else
return enif_make_badarg(env);
}
static EC_KEY* ec_key_new(ErlNifEnv* env, ERL_NIF_TERM curve_arg)
{
EC_KEY *key = NULL;
int c_arity = -1;
const ERL_NIF_TERM* curve;
ErlNifBinary seed;
BIGNUM *p = NULL;
BIGNUM *a = NULL;
BIGNUM *b = NULL;
BIGNUM *bn_order = NULL;
BIGNUM *cofactor = NULL;
EC_GROUP *group = NULL;
EC_POINT *point = NULL;
int f_arity = -1;
const ERL_NIF_TERM *field;
int p_arity = -1;
const ERL_NIF_TERM *prime;
long field_bits;
/* {Field, Prime, Point, Order, CoFactor} = Curve */
if (!enif_get_tuple(env, curve_arg, &c_arity, &curve))
goto err;
if (c_arity != 5)
goto err;
if (!get_bn_from_bin(env, curve[3], &bn_order))
goto err;
if (curve[4] != atom_none) {
if (!get_bn_from_bin(env, curve[4], &cofactor))
goto err;
}
/* {A, B, Seed} = Prime */
if (!enif_get_tuple(env, curve[1], &p_arity, &prime))
goto err;
if (!get_bn_from_bin(env, prime[0], &a))
goto err;
if (!get_bn_from_bin(env, prime[1], &b))
goto err;
if (!enif_get_tuple(env, curve[0], &f_arity, &field))
goto err;
if (f_arity == 2 && field[0] == atom_prime_field) {
/* {prime_field, Prime} */
if (!get_bn_from_bin(env, field[1], &p))
goto err;
if (BN_is_negative(p))
goto err;
if (BN_is_zero(p))
goto err;
field_bits = BN_num_bits(p);
if (field_bits > OPENSSL_ECC_MAX_FIELD_BITS)
goto err;
/* create the EC_GROUP structure */
if ((group = EC_GROUP_new_curve_GFp(p, a, b, NULL)) == NULL)
goto err;
} else if (f_arity == 3 && field[0] == atom_characteristic_two_field) {
#if defined(OPENSSL_NO_EC2M)
enif_raise_exception(env, atom_notsup);
goto out_err;
#else
/* {characteristic_two_field, M, Basis} */
int b_arity = -1;
const ERL_NIF_TERM* basis;
if ((p = BN_new()) == NULL)
goto err;
if (!enif_get_long(env, field[1], &field_bits))
goto err;
if (field_bits > OPENSSL_ECC_MAX_FIELD_BITS || field_bits > INT_MAX)
goto err;
if (enif_get_tuple(env, field[2], &b_arity, &basis)) {
if (b_arity == 2) {
unsigned int k1;
if (basis[0] != atom_tpbasis)
goto err;
if (!enif_get_uint(env, basis[1], &k1))
goto err;
/* {tpbasis, k} = Basis */
if (field_bits <= k1 || k1 == 0 || k1 > INT_MAX)
goto err;
/* create the polynomial */
if (!BN_set_bit(p, (int)field_bits))
goto err;
if (!BN_set_bit(p, (int)k1))
goto err;
if (!BN_set_bit(p, 0))
goto err;
} else if (b_arity == 4) {
unsigned int k1, k2, k3;
if (basis[0] != atom_ppbasis)
goto err;
if (!enif_get_uint(env, basis[1], &k1))
goto err;
if (!enif_get_uint(env, basis[2], &k2))
goto err;
if (!enif_get_uint(env, basis[3], &k3))
goto err;
/* {ppbasis, k1, k2, k3} = Basis */
if (field_bits <= k3 || k3 <= k2 || k2 <= k1 || k1 == 0 || k3 > INT_MAX || k2 > INT_MAX || k1 > INT_MAX)
goto err;
/* create the polynomial */
if (!BN_set_bit(p, (int)field_bits))
goto err;
if (!BN_set_bit(p, (int)k1))
goto err;
if (!BN_set_bit(p, (int)k2))
goto err;
if (!BN_set_bit(p, (int)k3))
goto err;
if (!BN_set_bit(p, 0))
goto err;
} else
goto err;
} else if (field[2] == atom_onbasis) {
/* onbasis = Basis */
/* no parameters */
goto err;
} else
goto err;
if ((group = EC_GROUP_new_curve_GF2m(p, a, b, NULL)) == NULL)
goto err;
#endif
} else
goto err;
if (enif_inspect_binary(env, prime[2], &seed)) {
if (!EC_GROUP_set_seed(group, seed.data, seed.size))
goto err;
}
if (!term2point(env, curve[2], group, &point))
goto err;
if (BN_is_negative(bn_order))
goto err;
if (BN_is_zero(bn_order))
goto err;
if (BN_num_bits(bn_order) > (int)field_bits + 1)
goto err;
if (!EC_GROUP_set_generator(group, point, bn_order, cofactor))
goto err;
EC_GROUP_set_asn1_flag(group, 0x0);
if ((key = EC_KEY_new()) == NULL)
goto err;
if (!EC_KEY_set_group(key, group))
goto err;
goto done;
err:
if (key)
EC_KEY_free(key);
key = NULL;
done:
/* some OpenSSL structures are mem-dup'ed into the key,
so we have to free our copies here */
if (bn_order)
BN_free(bn_order);
if (cofactor)
BN_free(cofactor);
if (a)
BN_free(a);
if (b)
BN_free(b);
if (p)
BN_free(p);
if (group)
EC_GROUP_free(group);
if (point)
EC_POINT_free(point);
return key;
}
static ERL_NIF_TERM point2term(ErlNifEnv* env,
const EC_GROUP *group,
const EC_POINT *point,
point_conversion_form_t form)
{
ERL_NIF_TERM ret;
size_t dlen;
ErlNifBinary bin;
int bin_alloc = 0;
if ((dlen = EC_POINT_point2oct(group, point, form, NULL, 0, NULL)) == 0)
return atom_undefined;
if (!enif_alloc_binary(dlen, &bin))
goto err;
bin_alloc = 1;
if (!EC_POINT_point2oct(group, point, form, bin.data, bin.size, NULL))
goto err;
ERL_VALGRIND_MAKE_MEM_DEFINED(bin.data, bin.size);
ret = enif_make_binary(env, &bin);
bin_alloc = 0;
goto done;
err:
if (bin_alloc)
enif_release_binary(&bin);
ret = enif_make_badarg(env);
done:
return ret;
}
int term2point(ErlNifEnv* env, ERL_NIF_TERM term, EC_GROUP *group, EC_POINT **pptr)
{
ErlNifBinary bin;
EC_POINT *point = NULL;
if (!enif_inspect_binary(env, term, &bin))
goto err;
if ((point = EC_POINT_new(group)) == NULL)
goto err;
/* set the point conversion form */
EC_GROUP_set_point_conversion_form(group, (point_conversion_form_t)(bin.data[0] & ~0x01));
/* extract the ec point */
if (!EC_POINT_oct2point(group, point, bin.data, bin.size, NULL))
goto err;
*pptr = point;
return 1;
err:
if (point)
EC_POINT_free(point);
return 0;
}
int get_ec_key(ErlNifEnv* env,
ERL_NIF_TERM curve, ERL_NIF_TERM priv, ERL_NIF_TERM pub,
EC_KEY** res)
{
EC_KEY *key = NULL;
BIGNUM *priv_key = NULL;
EC_POINT *pub_key = NULL;
EC_GROUP *group = NULL;
if (!(priv == atom_undefined || get_bn_from_bin(env, priv, &priv_key))
|| !(pub == atom_undefined || enif_is_binary(env, pub))) {
goto out_err;
}
key = ec_key_new(env, curve);
if (!key) {
goto out_err;
}
if (!group)
group = EC_GROUP_dup(EC_KEY_get0_group(key));
if (term2point(env, pub, group, &pub_key)) {
if (!EC_KEY_set_public_key(key, pub_key)) {
goto out_err;
}
}
if (priv != atom_undefined
&& !BN_is_zero(priv_key)) {
if (!EC_KEY_set_private_key(key, priv_key))
goto out_err;
/* calculate public key (if necessary) */
if (EC_KEY_get0_public_key(key) == NULL)
{
/* the public key was not included in the SEC1 private
* key => calculate the public key */
pub_key = EC_POINT_new(group);
if (pub_key == NULL
|| !EC_POINT_copy(pub_key, EC_GROUP_get0_generator(group))
|| !EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, NULL)
|| !EC_KEY_set_public_key(key, pub_key))
goto out_err;
}
}
goto out;
out_err:
if (key) EC_KEY_free(key);
key = NULL;
out:
/* some OpenSSL structures are mem-dup'ed into the key,
so we have to free our copies here */
if (priv_key) BN_clear_free(priv_key);
if (pub_key) EC_POINT_free(pub_key);
if (group) EC_GROUP_free(group);
if (!key)
return 0;
*res = key;
return 1;
}
#endif /* HAVE_EC */
ERL_NIF_TERM ec_key_generate(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
#if defined(HAVE_EC)
EC_KEY *key = NULL;
const EC_GROUP *group;
const EC_POINT *public_key;
ERL_NIF_TERM priv_key;
ERL_NIF_TERM pub_key = atom_undefined;
if (!get_ec_key(env, argv[0], argv[1], atom_undefined, &key))
goto badarg;
if (argv[1] == atom_undefined) {
if (!EC_KEY_generate_key(key))
goto badarg;
}
group = EC_KEY_get0_group(key);
public_key = EC_KEY_get0_public_key(key);
if (group && public_key) {
pub_key = point2term(env, group, public_key,
EC_KEY_get_conv_form(key));
}
priv_key = bn2term(env, EC_KEY_get0_private_key(key));
EC_KEY_free(key);
return enif_make_tuple2(env, pub_key, priv_key);
badarg:
if (key)
EC_KEY_free(key);
return make_badarg_maybe(env);
#else
return atom_notsup;
#endif
}