aboutsummaryrefslogtreecommitdiffstats
path: root/lib/crypto/c_src/ec.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/crypto/c_src/ec.c')
-rw-r--r--lib/crypto/c_src/ec.c414
1 files changed, 414 insertions, 0 deletions
diff --git a/lib/crypto/c_src/ec.c b/lib/crypto/c_src/ec.c
new file mode 100644
index 0000000000..51a3547694
--- /dev/null
+++ b/lib/crypto/c_src/ec.c
@@ -0,0 +1,414 @@
+/*
+ * %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 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) {
+ if (!get_bn_from_bin(env, priv, &priv_key))
+ goto err;
+ }
+ if (pub != atom_undefined) {
+ if (!enif_is_binary(env, pub))
+ goto err;
+ }
+
+ if ((key = ec_key_new(env, curve)) == NULL)
+ goto err;
+
+ if ((group = EC_GROUP_dup(EC_KEY_get0_group(key))) == NULL)
+ goto err;
+
+ if (term2point(env, pub, group, &pub_key)) {
+ if (!EC_KEY_set_public_key(key, pub_key))
+ goto err;
+ }
+
+ if (priv != atom_undefined && !BN_is_zero(priv_key)) {
+ if (!EC_KEY_set_private_key(key, priv_key))
+ goto 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 */
+ if ((pub_key = EC_POINT_new(group)) == NULL)
+ goto err;
+ if (!EC_POINT_copy(pub_key, EC_GROUP_get0_generator(group)))
+ goto err;
+ if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, NULL))
+ goto err;
+ if (!EC_KEY_set_public_key(key, pub_key))
+ 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 (priv_key)
+ BN_clear_free(priv_key);
+ if (group)
+ EC_GROUP_free(group);
+ if (pub_key)
+ EC_POINT_free(pub_key);
+
+ if (key == NULL)
+ 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;
+ ERL_NIF_TERM ret;
+
+ if (!get_ec_key(env, argv[0], argv[1], atom_undefined, &key))
+ goto bad_arg;
+
+ if (argv[1] == atom_undefined) {
+ if (!EC_KEY_generate_key(key))
+ goto err;
+ }
+
+ group = EC_KEY_get0_group(key);
+ public_key = EC_KEY_get0_public_key(key);
+
+ if (group == NULL || public_key == NULL) {
+ pub_key = atom_undefined;
+
+ } else {
+ pub_key = point2term(env, group, public_key,
+ EC_KEY_get_conv_form(key));
+ }
+
+ priv_key = bn2term(env, EC_KEY_get0_private_key(key));
+ ret = enif_make_tuple2(env, pub_key, priv_key);
+ goto done;
+
+ err:
+ bad_arg:
+ ret = make_badarg_maybe(env);
+
+ done:
+ if (key)
+ EC_KEY_free(key);
+ return ret;
+
+#else
+ return atom_notsup;
+#endif
+}