aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Schultz <aschultz@tpip.net>2013-03-28 16:19:40 +0100
committerIngela Anderton Andin <ingela@erlang.org>2013-05-08 10:39:16 +0200
commitc6c2e82f8b94010e78bfa5fedce7629e7fd32d58 (patch)
tree5bfe63388a215b4f4e11b38fcee552779ebb797b
parent8e00f4ce7a49b2fd1da7e481dc0985703e4131a5 (diff)
downloadotp-c6c2e82f8b94010e78bfa5fedce7629e7fd32d58.tar.gz
otp-c6c2e82f8b94010e78bfa5fedce7629e7fd32d58.tar.bz2
otp-c6c2e82f8b94010e78bfa5fedce7629e7fd32d58.zip
CRYPTO: add support for Elliptic Curves to crypto app
Conflicts: lib/crypto/src/crypto.erl
-rw-r--r--lib/crypto/c_src/crypto.c835
-rw-r--r--[-rwxr-xr-x]lib/crypto/doc/src/crypto.xml149
-rw-r--r--lib/crypto/src/crypto.erl107
-rw-r--r--lib/crypto/test/crypto_SUITE.erl67
4 files changed, 1152 insertions, 6 deletions
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index fac77308f6..1e6c2f24e7 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -74,6 +74,19 @@
# define HAVE_DES_ede3_cfb_encrypt
#endif
+#if OPENSSL_VERSION_NUMBER >= 0x009080ffL \
+ && !defined(OPENSSL_NO_EC) \
+ && !defined(OPENSSL_NO_ECDH) \
+ && !defined(OPENSSL_NO_ECDSA)
+# define HAVE_EC
+#endif
+
+#if defined(HAVE_EC)
+#include <openssl/ec.h>
+#include <openssl/ecdh.h>
+#include <openssl/ecdsa.h>
+#endif
+
#ifdef VALGRIND
# include <valgrind/memcheck.h>
@@ -216,6 +229,13 @@ static ERL_NIF_TERM bf_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
static ERL_NIF_TERM bf_ecb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM blowfish_ofb64_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM ec_key_new(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM ec_key_to_term_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM term_to_ec_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM ec_key_generate(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM ecdsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM ecdsa_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM ecdh_compute_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
/* helpers */
@@ -333,9 +353,114 @@ static ErlNifFunc nif_funcs[] = {
{"bf_cfb64_crypt", 4, bf_cfb64_crypt},
{"bf_cbc_crypt", 4, bf_cbc_crypt},
{"bf_ecb_crypt", 3, bf_ecb_crypt},
- {"blowfish_ofb64_encrypt", 3, blowfish_ofb64_encrypt}
+ {"blowfish_ofb64_encrypt", 3, blowfish_ofb64_encrypt},
+
+ {"ec_key_new", 1, ec_key_new},
+ {"ec_key_to_term_nif", 1, ec_key_to_term_nif},
+ {"term_to_ec_key_nif", 3, term_to_ec_key_nif},
+ {"ec_key_generate", 1, ec_key_generate},
+ {"ecdsa_sign_nif", 3, ecdsa_sign_nif},
+ {"ecdsa_verify_nif", 4, ecdsa_verify_nif},
+ {"ecdh_compute_key", 2, ecdh_compute_key}
};
+#if defined(HAVE_EC)
+struct nid_map {
+ char *name;
+ int nid;
+ ERL_NIF_TERM atom;
+};
+
+static struct nid_map ec_curves[] = {
+ /* prime field curves */
+ /* secg curves */
+ { "secp112r1", NID_secp112r1 },
+ { "secp112r2", NID_secp112r2 },
+ { "secp128r1", NID_secp128r1 },
+ { "secp128r2", NID_secp128r2 },
+ { "secp160k1", NID_secp160k1 },
+ { "secp160r1", NID_secp160r1 },
+ { "secp160r2", NID_secp160r2 },
+ /* SECG secp192r1 is the same as X9.62 prime192v1 */
+ { "secp192r1", NID_X9_62_prime192v1 },
+ { "secp192k1", NID_secp192k1 },
+ { "secp224k1", NID_secp224k1 },
+ { "secp224r1", NID_secp224r1 },
+ { "secp256k1", NID_secp256k1 },
+ /* SECG secp256r1 is the same as X9.62 prime256v1 */
+ { "secp256r1", NID_X9_62_prime256v1 },
+ { "secp384r1", NID_secp384r1 },
+ { "secp521r1", NID_secp521r1 },
+ /* X9.62 curves */
+ { "prime192v1", NID_X9_62_prime192v1 },
+ { "prime192v2", NID_X9_62_prime192v2 },
+ { "prime192v3", NID_X9_62_prime192v3 },
+ { "prime239v1", NID_X9_62_prime239v1 },
+ { "prime239v2", NID_X9_62_prime239v2 },
+ { "prime239v3", NID_X9_62_prime239v3 },
+ { "prime256v1", NID_X9_62_prime256v1 },
+ /* characteristic two field curves */
+ /* NIST/SECG curves */
+ { "sect113r1", NID_sect113r1 },
+ { "sect113r2", NID_sect113r2 },
+ { "sect131r1", NID_sect131r1 },
+ { "sect131r2", NID_sect131r2 },
+ { "sect163k1", NID_sect163k1 },
+ { "sect163r1", NID_sect163r1 },
+ { "sect163r2", NID_sect163r2 },
+ { "sect193r1", NID_sect193r1 },
+ { "sect193r2", NID_sect193r2 },
+ { "sect233k1", NID_sect233k1 },
+ { "sect233r1", NID_sect233r1 },
+ { "sect239k1", NID_sect239k1 },
+ { "sect283k1", NID_sect283k1 },
+ { "sect283r1", NID_sect283r1 },
+ { "sect409k1", NID_sect409k1 },
+ { "sect409r1", NID_sect409r1 },
+ { "sect571k1", NID_sect571k1 },
+ { "sect571r1", NID_sect571r1 },
+ /* X9.62 curves */
+ { "c2pnb163v1", NID_X9_62_c2pnb163v1 },
+ { "c2pnb163v2", NID_X9_62_c2pnb163v2 },
+ { "c2pnb163v3", NID_X9_62_c2pnb163v3 },
+ { "c2pnb176v1", NID_X9_62_c2pnb176v1 },
+ { "c2tnb191v1", NID_X9_62_c2tnb191v1 },
+ { "c2tnb191v2", NID_X9_62_c2tnb191v2 },
+ { "c2tnb191v3", NID_X9_62_c2tnb191v3 },
+ { "c2pnb208w1", NID_X9_62_c2pnb208w1 },
+ { "c2tnb239v1", NID_X9_62_c2tnb239v1 },
+ { "c2tnb239v2", NID_X9_62_c2tnb239v2 },
+ { "c2tnb239v3", NID_X9_62_c2tnb239v3 },
+ { "c2pnb272w1", NID_X9_62_c2pnb272w1 },
+ { "c2pnb304w1", NID_X9_62_c2pnb304w1 },
+ { "c2tnb359v1", NID_X9_62_c2tnb359v1 },
+ { "c2pnb368w1", NID_X9_62_c2pnb368w1 },
+ { "c2tnb431r1", NID_X9_62_c2tnb431r1 },
+ /* the WAP/WTLS curves
+ * [unlike SECG, spec has its own OIDs for curves from X9.62] */
+ { "wtls1", NID_wap_wsg_idm_ecid_wtls1 },
+ { "wtls3", NID_wap_wsg_idm_ecid_wtls3 },
+ { "wtls4", NID_wap_wsg_idm_ecid_wtls4 },
+ { "wtls5", NID_wap_wsg_idm_ecid_wtls5 },
+ { "wtls6", NID_wap_wsg_idm_ecid_wtls6 },
+ { "wtls7", NID_wap_wsg_idm_ecid_wtls7 },
+ { "wtls8", NID_wap_wsg_idm_ecid_wtls8 },
+ { "wtls9", NID_wap_wsg_idm_ecid_wtls9 },
+ { "wtls10", NID_wap_wsg_idm_ecid_wtls10 },
+ { "wtls11", NID_wap_wsg_idm_ecid_wtls11 },
+ { "wtls12", NID_wap_wsg_idm_ecid_wtls12 },
+ /* IPSec curves */
+ { "ipsec3", NID_ipsec3 },
+ { "ipsec4", NID_ipsec4 }
+};
+
+#define EC_CURVES_CNT (sizeof(ec_curves)/sizeof(struct nid_map))
+
+struct nif_ec_key {
+ EC_KEY *key;
+};
+#endif
+
ERL_NIF_INIT(crypto,nif_funcs,load,NULL,upgrade,unload)
@@ -386,6 +511,19 @@ static ERL_NIF_TERM atom_none;
static ERL_NIF_TERM atom_notsup;
static ERL_NIF_TERM atom_digest;
+static ERL_NIF_TERM atom_ec;
+
+#if defined(HAVE_EC)
+static ERL_NIF_TERM atom_prime_field;
+static ERL_NIF_TERM atom_characteristic_two_field;
+static ERL_NIF_TERM atom_tpbasis;
+static ERL_NIF_TERM atom_ppbasis;
+static ERL_NIF_TERM atom_onbasis;
+
+static ErlNifResourceType* res_type_ec_key;
+static void ec_key_dtor(ErlNifEnv* env, void* obj);
+#endif
+
/*
#define PRINTF_ERR0(FMT) enif_fprintf(stderr, FMT "\n")
#define PRINTF_ERR1(FMT, A1) enif_fprintf(stderr, FMT "\n", A1)
@@ -415,6 +553,7 @@ static void error_handler(void* null, const char* errstr)
static int init(ErlNifEnv* env, ERL_NIF_TERM load_info)
{
+ int i;
ErlNifSysInfo sys_info;
get_crypto_callbacks_t* funcp;
struct crypto_callbacks* ccb;
@@ -466,6 +605,22 @@ static int init(ErlNifEnv* env, ERL_NIF_TERM load_info)
atom_notsup = enif_make_atom(env,"notsup");
atom_digest = enif_make_atom(env,"digest");
+#if defined(HAVE_EC)
+ atom_ec = enif_make_atom(env,"ec");
+ atom_prime_field = enif_make_atom(env,"prime_field");
+ atom_characteristic_two_field = enif_make_atom(env,"characteristic_two_field");
+ atom_tpbasis = enif_make_atom(env,"tpbasis");
+ atom_ppbasis = enif_make_atom(env,"ppbasis");
+ atom_onbasis = enif_make_atom(env,"onbasis");
+
+ for (i = 0; i < EC_CURVES_CNT; i++)
+ ec_curves[i].atom = enif_make_atom(env,ec_curves[i].name);
+
+ res_type_ec_key = enif_open_resource_type(env,NULL,"crypto.EC_KEY",
+ ec_key_dtor,
+ ERL_NIF_RT_CREATE, NULL);
+#endif
+
init_digest_types(env);
init_algorithms_types();
@@ -549,7 +704,7 @@ static void unload(ErlNifEnv* env, void* priv_data)
}
static int algos_cnt;
-static ERL_NIF_TERM algos[7]; /* increase when extending the list */
+static ERL_NIF_TERM algos[8]; /* increase when extending the list */
static void init_algorithms_types(void)
{
@@ -570,6 +725,9 @@ static void init_algorithms_types(void)
#ifdef HAVE_SHA512
algos[algos_cnt++] = atom_sha512;
#endif
+#if defined(HAVE_EC)
+ algos[algos_cnt++] = atom_ec;
+#endif
}
static ERL_NIF_TERM algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
@@ -2686,7 +2844,680 @@ static ERL_NIF_TERM blowfish_ofb64_encrypt(ErlNifEnv* env, int argc, const ERL_N
return ret;
}
+#if defined(HAVE_EC)
+static int term2curve_id(ERL_NIF_TERM nid)
+{
+ int i;
+
+ for (i = 0; i < EC_CURVES_CNT; i++)
+ if (ec_curves[i].atom == nid)
+ return ec_curves[i].nid;
+
+ return 0;
+}
+
+static ERL_NIF_TERM curve_id2term(int nid)
+{
+ int i;
+
+ for (i = 0; i < EC_CURVES_CNT; i++)
+ if (ec_curves[i].nid == nid)
+ return ec_curves[i].atom;
+
+ return atom_undefined;
+}
+#endif
+
+static ERL_NIF_TERM ec_key_new(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+#if defined(HAVE_EC)
+ EC_KEY *key = NULL;
+ int nid = 0;
+
+ nid = term2curve_id(argv[0]);
+ if (nid == 0)
+ return enif_make_badarg(env);
+ key = EC_KEY_new_by_curve_name(nid);
+
+ if (key) {
+ ERL_NIF_TERM term;
+ struct nif_ec_key *obj = enif_alloc_resource(res_type_ec_key, sizeof(struct nif_ec_key));
+ if (!obj)
+ return atom_error;
+ obj->key = key;
+ term = enif_make_resource(env, obj);
+ enif_release_resource(obj);
+
+ return term;
+ } else
+ return enif_make_badarg(env);
+#else
+ return atom_notsup;
+#endif
+}
+
+#if defined(HAVE_EC)
+static ERL_NIF_TERM bn2term(ErlNifEnv* env, const BIGNUM *bn)
+{
+ unsigned dlen;
+ unsigned char* ptr;
+ ERL_NIF_TERM ret;
+
+ if (!bn)
+ return atom_undefined;
+
+ dlen = BN_num_bytes(bn);
+ ptr = enif_make_new_binary(env, dlen+4, &ret);
+ put_int32(ptr, dlen);
+ BN_bn2bin(bn, ptr+4);
+
+ return ret;
+}
+
+static ERL_NIF_TERM point2term(ErlNifEnv* env,
+ const EC_GROUP *group,
+ const EC_POINT *point,
+ point_conversion_form_t form)
+{
+ unsigned dlen;
+ ErlNifBinary bin;
+
+ dlen = EC_POINT_point2oct(group, point, form, NULL, 0, NULL);
+ if (dlen == 0)
+ return atom_undefined;
+
+ if (!enif_alloc_binary(dlen, &bin))
+ return enif_make_badarg(env);
+
+ if (!EC_POINT_point2oct(group, point, form, bin.data, bin.size, NULL)) {
+ enif_release_binary(&bin);
+ return enif_make_badarg(env);
+ }
+
+ return enif_make_binary(env, &bin);
+}
+#endif
+
+static ERL_NIF_TERM ec_key_to_term_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+#if defined(HAVE_EC)
+ struct nif_ec_key *obj;
+ const EC_GROUP *group;
+ const EC_POINT *public_key;
+ BIGNUM *order=NULL;
+ const BIGNUM *priv_key = NULL;
+ ERL_NIF_TERM pub_key = atom_undefined;
+ ERL_NIF_TERM group_term = atom_undefined;
+
+ if (!enif_get_resource(env, argv[0], res_type_ec_key, (void **)&obj))
+ return enif_make_badarg(env);
+
+ group = EC_KEY_get0_group(obj->key);
+ public_key = EC_KEY_get0_public_key(obj->key);
+ priv_key = EC_KEY_get0_private_key(obj->key);
+
+ if (group) {
+ if (EC_GROUP_get_curve_name(group) != 0) {
+ /* named group */
+ group_term = curve_id2term(EC_GROUP_get_curve_name(group));
+ } else {
+ /* export group paramters */
+ ERL_NIF_TERM field_term = atom_undefined;
+ ERL_NIF_TERM prime_term = atom_undefined;
+ ERL_NIF_TERM seed_term = atom_none;
+ ERL_NIF_TERM point_term = atom_none;
+ ERL_NIF_TERM order_term = atom_none;
+ ERL_NIF_TERM cofactor_term = atom_none;
+
+ int nid = 0;
+ const EC_POINT *point = NULL;
+ BIGNUM *tmp = BN_new();
+ BIGNUM *tmp_1 = BN_new();
+ BIGNUM *tmp_2 = BN_new();
+
+ nid = EC_METHOD_get_field_type(EC_GROUP_method_of(group));
+ if (nid == NID_X9_62_prime_field) {
+ /* the parameters are specified by the prime number p */
+ EC_GROUP_get_curve_GFp(group, tmp, tmp_1, tmp_2, NULL);
+
+ /* {prime_field, Prime} */
+ field_term = enif_make_tuple2(env, atom_prime_field, bn2term(env, tmp));
+ } else { /* nid == NID_X9_62_characteristic_two_field */
+ int field_type;
+ ERL_NIF_TERM basis_term;
+ ERL_NIF_TERM m_term;
+
+ EC_GROUP_get_curve_GF2m(group, NULL, tmp_1, tmp_2, NULL);
+
+ m_term = enif_make_long(env, (long)EC_GROUP_get_degree(group));
+
+ field_type = EC_GROUP_get_basis_type(group);
+ if (field_type == 0) {
+ basis_term = atom_undefined;
+ } else if (field_type == NID_X9_62_tpBasis) {
+ unsigned int k;
+ ERL_NIF_TERM k_term = atom_undefined;
+
+ if (EC_GROUP_get_trinomial_basis(group, &k))
+ k_term = enif_make_uint(env, k);
+
+ basis_term = enif_make_tuple2(env, atom_tpbasis, k_term);
+ } else if (field_type == NID_X9_62_ppBasis) {
+ unsigned int k1, k2, k3;
+ ERL_NIF_TERM k1_term = atom_undefined;
+ ERL_NIF_TERM k2_term = atom_undefined;
+ ERL_NIF_TERM k3_term = atom_undefined;
+
+ if (EC_GROUP_get_pentanomial_basis(group, &k1, &k2, &k3)) {
+ /* set k? values */
+ k1_term = enif_make_uint(env, k1);
+ k2_term = enif_make_uint(env, k2);
+ k3_term = enif_make_uint(env, k3);
+ }
+ basis_term = enif_make_tuple4(env, atom_ppbasis, k1_term, k2_term, k3_term);
+ } else { /* field_type == NID_X9_62_onBasis */
+ basis_term = atom_onbasis;
+ }
+ /* {characteristic_two_field, M, Basis} */
+ field_term = enif_make_tuple3(env, atom_characteristic_two_field, m_term, basis_term);
+ }
+
+ if (EC_GROUP_get0_seed(group)) {
+ unsigned char* ptr;
+
+ ptr = enif_make_new_binary(env, EC_GROUP_get_seed_len(group), &seed_term);
+ memcpy(ptr, EC_GROUP_get0_seed(group), EC_GROUP_get_seed_len(group));
+ }
+
+
+ /* set the base point */
+ point = EC_GROUP_get0_generator(group);
+ if (point)
+ point_term = point2term(env, group, point, EC_GROUP_get_point_conversion_form(group));
+
+ /* set the order */
+ if (EC_GROUP_get_order(group, tmp, NULL))
+ order_term = bn2term(env, tmp);
+
+ /* set the cofactor (optional) */
+ if (EC_GROUP_get_cofactor(group, tmp, NULL))
+ cofactor_term = bn2term(env, tmp);
+
+ prime_term = enif_make_tuple3(env, bn2term(env, tmp_1), bn2term(env, tmp_2), seed_term);
+ group_term = enif_make_tuple5(env, field_term, prime_term, point_term, order_term, cofactor_term);
+ BN_free(tmp);
+ BN_free(tmp_1);
+ BN_free(tmp_2);
+ }
+
+ if (public_key)
+ pub_key = point2term(env, group, public_key, EC_KEY_get_conv_form(obj->key));
+
+ if (order) BN_free(order);
+ }
+
+ return enif_make_tuple3(env, group_term, bn2term(env, priv_key), pub_key);
+#else
+ return atom_notsup;
+#endif
+}
+
+#if defined(HAVE_EC)
+static int term2point(ErlNifEnv* env, ERL_NIF_TERM term,
+ EC_GROUP *group, EC_POINT **pptr)
+{
+ int ret = 0;
+ ErlNifBinary bin;
+ EC_POINT *point;
+
+ if (!enif_inspect_binary(env,term,&bin)) {
+ return 0;
+ }
+
+ if ((*pptr = point = EC_POINT_new(group)) == NULL) {
+ return 0;
+ }
+
+ /* 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)) {
+ EC_POINT_free(point);
+ *pptr = NULL;
+ } else
+ ret = 1;
+
+ return ret;
+}
+#endif
+
+static ERL_NIF_TERM term_to_ec_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+#if defined(HAVE_EC)
+ ERL_NIF_TERM ret;
+ EC_KEY *key = NULL;
+ BIGNUM *priv_key = NULL;
+ EC_POINT *pub_key = NULL;
+ struct nif_ec_key *obj;
+ 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;
+
+ if (!(argv[1] == atom_undefined || get_bn_from_mpint(env, argv[1], &priv_key))
+ || !(argv[2] == atom_undefined || enif_is_binary(env, argv[2]))) {
+ printf("#1\n");
+ goto out_err;
+ }
+
+ if (enif_is_atom(env, argv[0])) {
+ int nid;
+
+ nid = term2curve_id(argv[0]);
+ if (nid == 0) {
+ printf("#2\n");
+ goto out_err;
+ }
+
+ key = EC_KEY_new_by_curve_name(nid);
+ }
+ else if (enif_is_tuple(env, argv[0])
+ && enif_get_tuple(env,argv[0],&c_arity,&curve)
+ && c_arity == 5
+ && get_bn_from_mpint(env, curve[3], &bn_order)
+ && (curve[4] != atom_none && get_bn_from_mpint(env, curve[4], &cofactor))) {
+ //* {Field, Prime, Point, Order, CoFactor} = Curve */
+
+ int f_arity = -1;
+ const ERL_NIF_TERM* field;
+ int p_arity = -1;
+ const ERL_NIF_TERM* prime;
+
+ long field_bits;
+
+ /* {A, B, Seed} = Prime */
+ if (!enif_get_tuple(env,curve[1],&p_arity,&prime)
+ || !get_bn_from_mpint(env, prime[0], &a)
+ || !get_bn_from_mpint(env, prime[1], &b))
+ goto out_err;
+
+ if (!enif_get_tuple(env,curve[0],&f_arity,&field))
+ goto out_err;
+
+ if (f_arity == 2 && field[0] == atom_prime_field) {
+ /* {prime_field, Prime} */
+
+ if (!get_bn_from_mpint(env, field[1], &p))
+ goto out_err;
+
+ if (BN_is_negative(p) || BN_is_zero(p))
+ goto out_err;
+
+ field_bits = BN_num_bits(p);
+ if (field_bits > OPENSSL_ECC_MAX_FIELD_BITS)
+ goto out_err;
+
+ /* create the EC_GROUP structure */
+ group = EC_GROUP_new_curve_GFp(p, a, b, NULL);
+
+ } else if (f_arity == 3 && field[0] == atom_characteristic_two_field) {
+ /* {characteristic_two_field, M, Basis} */
+
+ int b_arity = -1;
+ const ERL_NIF_TERM* basis;
+ unsigned int k1, k2, k3;
+
+ if ((p = BN_new()) == NULL)
+ goto out_err;
+
+ if (!enif_get_long(env, field[1], &field_bits)
+ || field_bits > OPENSSL_ECC_MAX_FIELD_BITS)
+ goto out_err;
+
+ if (enif_get_tuple(env,field[2],&b_arity,&basis)) {
+ if (b_arity == 2
+ && basis[0] == atom_tpbasis
+ && enif_get_uint(env, basis[1], &k1)) {
+ /* {tpbasis, k} = Basis */
+
+ if (!(field_bits > k1 && k1 > 0))
+ goto out_err;
+
+ /* create the polynomial */
+ if (!BN_set_bit(p, (int)field_bits)
+ || !BN_set_bit(p, (int)k1)
+ || !BN_set_bit(p, 0))
+ goto out_err;
+
+ } else if (b_arity == 4
+ && basis[0] == atom_ppbasis
+ && enif_get_uint(env, basis[1], &k1)
+ && enif_get_uint(env, basis[2], &k2)
+ && enif_get_uint(env, basis[3], &k3)) {
+ /* {ppbasis, k1, k2, k3} = Basis */
+
+ if (!(field_bits > k3 && k3 > k2 && k2 > k1 && k1 > 0))
+ goto out_err;
+
+ /* create the polynomial */
+ if (!BN_set_bit(p, (int)field_bits)
+ || !BN_set_bit(p, (int)k1)
+ || !BN_set_bit(p, (int)k2)
+ || !BN_set_bit(p, (int)k3)
+ || !BN_set_bit(p, 0))
+ goto out_err;
+
+ } else
+ goto out_err;
+ } else if (field[2] == atom_onbasis) {
+ /* onbasis = Basis */
+ /* no parameters */
+ goto out_err;
+
+ } else
+ goto out_err;
+
+ group = EC_GROUP_new_curve_GF2m(p, a, b, NULL);
+ } else
+ goto out_err;
+
+ if (enif_inspect_binary(env, prime[2], &seed)) {
+ EC_GROUP_set_seed(group, seed.data, seed.size);
+ }
+
+ if (!term2point(env, curve[2], group, &point))
+ goto out_err;
+
+ if (BN_is_negative(bn_order)
+ || BN_is_zero(bn_order)
+ || BN_num_bits(bn_order) > (int)field_bits + 1)
+ goto out_err;
+
+ if (!EC_GROUP_set_generator(group, point, bn_order, cofactor))
+ goto out_err;
+
+ EC_GROUP_set_asn1_flag(group, 0x0);
+
+ key = EC_KEY_new();
+ if (!key)
+ goto out_err;
+ EC_KEY_set_group(key, group);
+ }
+ else {
+ printf("#3\n");
+ goto out_err;
+ }
+
+ if (!key) {
+ printf("#4\n");
+ goto out_err;
+ }
+
+ if (!group)
+ group = EC_GROUP_dup(EC_KEY_get0_group(key));
+
+ if (term2point(env, argv[2], group, &pub_key)) {
+ if (!EC_KEY_set_public_key(key, pub_key)) {
+ printf("#5\n");
+ goto out_err;
+ }
+ }
+ if (argv[1] != 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;
+ }
+ }
+
+ obj = enif_alloc_resource(res_type_ec_key, sizeof(struct nif_ec_key));
+ if (!obj)
+ goto out_err;
+
+ obj->key = key;
+ ret = enif_make_resource(env, obj);
+ enif_release_resource(obj);
+
+ goto out;
+
+out_err:
+ if (key) EC_KEY_free(key);
+ ret = enif_make_badarg(env);
+
+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 (p) BN_free(p);
+ if (a) BN_free(a);
+ if (b) BN_free(b);
+ if (bn_order) BN_free(bn_order);
+ if (cofactor) BN_free(cofactor);
+ if (group) EC_GROUP_free(group);
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM ec_key_generate(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+#if defined(HAVE_EC)
+ struct nif_ec_key *obj;
+
+ if (!enif_get_resource(env, argv[0], res_type_ec_key, (void **)&obj))
+ return enif_make_badarg(env);
+
+ if (EC_KEY_generate_key(obj->key))
+ return atom_ok;
+ else
+ return atom_error;
+#else
+ return atom_notsup;
+#endif
+}
+
+#if defined(HAVE_EC)
+static void ec_key_dtor(ErlNifEnv* env, void* obj)
+{
+ struct nif_ec_key *key = (struct nif_ec_key*) obj;
+ EC_KEY_free(key->key);
+}
+#endif
+
+static ERL_NIF_TERM ecdsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Type, Data|{digest,Digest}, Key) */
+#if defined(HAVE_EC)
+ ErlNifBinary data_bin, ret_bin;
+ unsigned char hmacbuf[SHA_DIGEST_LENGTH];
+ unsigned int dsa_s_len;
+ struct nif_ec_key *obj;
+ int i;
+ const ERL_NIF_TERM* tpl_terms;
+ int tpl_arity;
+ struct digest_type_t *digp;
+ unsigned char* digest;
+
+ digp = get_digest_type(argv[0]);
+ if (!digp) {
+ return enif_make_badarg(env);
+ }
+ if (!digp->len) {
+ return atom_notsup;
+ }
+
+ if (!enif_get_resource(env, argv[2], res_type_ec_key, (void **)&obj))
+ return enif_make_badarg(env);
+
+ if (enif_get_tuple(env, argv[1], &tpl_arity, &tpl_terms)) {
+ if (tpl_arity != 2 || tpl_terms[0] != atom_digest
+ || !enif_inspect_binary(env, tpl_terms[1], &data_bin)
+ || data_bin.size != digp->len) {
+
+ return enif_make_badarg(env);
+ }
+ digest = data_bin.data;
+ }
+ else {
+ if (!inspect_mpint(env,argv[1],&data_bin)) {
+ return enif_make_badarg(env);
+ }
+ digest = hmacbuf;
+ digp->funcp(data_bin.data+4, data_bin.size-4, digest);
+ }
+
+ enif_alloc_binary(ECDSA_size(obj->key), &ret_bin);
+
+ i = ECDSA_sign(digp->NID_type, digest, digp->len,
+ ret_bin.data, &dsa_s_len, obj->key);
+ if (i) {
+ if (dsa_s_len != ret_bin.size) {
+ enif_realloc_binary(&ret_bin, dsa_s_len);
+ }
+ return enif_make_binary(env, &ret_bin);
+ }
+ else {
+ enif_release_binary(&ret_bin);
+ return atom_error;
+ }
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM ecdsa_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Type, Data|{digest,Digest}, Signature, Key) */
+#if defined(HAVE_EC)
+ ErlNifBinary data_bin, sign_bin;
+ unsigned char hmacbuf[SHA512_LEN];
+ int i;
+ struct nif_ec_key *obj;
+ const ERL_NIF_TERM type = argv[0];
+ const ERL_NIF_TERM* tpl_terms;
+ int tpl_arity;
+ struct digest_type_t* digp = NULL;
+ unsigned char* digest = NULL;
+
+ digp = get_digest_type(type);
+ if (!digp) {
+ return enif_make_badarg(env);
+ }
+ if (!digp->len) {
+ return atom_notsup;
+ }
+
+ if (!inspect_mpint(env, argv[2], &sign_bin)
+ || !enif_get_resource(env, argv[3], res_type_ec_key, (void **)&obj))
+ return enif_make_badarg(env);
+
+ if (enif_get_tuple(env, argv[1], &tpl_arity, &tpl_terms)) {
+ if (tpl_arity != 2 || tpl_terms[0] != atom_digest
+ || !enif_inspect_binary(env, tpl_terms[1], &data_bin)
+ || data_bin.size != digp->len) {
+
+ return enif_make_badarg(env);
+ }
+ digest = data_bin.data;
+ }
+ else if (inspect_mpint(env, argv[1], &data_bin)) {
+ digest = hmacbuf;
+ digp->funcp(data_bin.data+4, data_bin.size-4, digest);
+ }
+ else {
+ return enif_make_badarg(env);
+ }
+
+ i = ECDSA_verify(digp->NID_type, digest, digp->len,
+ sign_bin.data+4, sign_bin.size-4, obj->key);
+
+ return (i==1 ? atom_true : atom_false);
+#else
+ return atom_notsup;
+#endif
+}
+
+/*
+ (_OthersPublicKey, _MyPrivateKey)
+ (_OthersPublicKey, _MyEC_Point)
+*/
+static ERL_NIF_TERM ecdh_compute_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+#if defined(HAVE_EC)
+ ERL_NIF_TERM ret;
+ unsigned char *p;
+ struct nif_ec_key *other_key;
+ int field_size = 0;
+ int i;
+
+ EC_GROUP *group;
+ const BIGNUM *priv_key;
+ EC_POINT *my_ecpoint;
+ EC_KEY *other_ecdh = NULL;
+ if (!enif_get_resource(env, argv[0], res_type_ec_key, (void **)&other_key))
+ return enif_make_badarg(env);
+
+ group = EC_GROUP_dup(EC_KEY_get0_group(other_key->key));
+ priv_key = EC_KEY_get0_private_key(other_key->key);
+
+ if (!term2point(env, argv[1], group, &my_ecpoint)) {
+ struct nif_ec_key *my_key;
+
+ if (!enif_get_resource(env, argv[1], res_type_ec_key, (void **)&my_key))
+ goto out_err;
+
+ if ((my_ecpoint = EC_POINT_new(group)) == NULL)
+ goto out_err;
+ EC_POINT_copy(my_ecpoint, EC_KEY_get0_public_key(my_key->key));
+ }
+
+ if ((other_ecdh = EC_KEY_new()) == NULL
+ || !EC_KEY_set_group(other_ecdh, group)
+ || !EC_KEY_set_private_key(other_ecdh, priv_key))
+ goto out_err;
+
+ field_size = EC_GROUP_get_degree(group);
+ if (field_size <= 0)
+ goto out_err;
+
+ p = enif_make_new_binary(env, (field_size+7)/8, &ret);
+ i = ECDH_compute_key(p, (field_size+7)/8, my_ecpoint, other_ecdh, NULL);
+
+ if (i < 0)
+ goto out_err;
+out:
+ if (group) EC_GROUP_free(group);
+ if (my_ecpoint) EC_POINT_free(my_ecpoint);
+ if (other_ecdh) EC_KEY_free(other_ecdh);
+
+ return ret;
+
+out_err:
+ ret = enif_make_badarg(env);
+ goto out;
+#else
+ return atom_notsup;
+#endif
+}
/* HMAC */
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 7eca4557d9..9201d649d7 100755..100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -66,8 +66,20 @@
<item>
<p>srp: Secure Remote Password Protocol (RFC 2945)</p>
</item>
-
-
+ <item>
+ <p>ecdsa: "Public Key Cryptography for the Financial
+ Services Industry: The Elliptic Curve Digital
+ Signature Standard (ECDSA)", November, 2005.</p>
+ </item>
+ <item>
+ <p>ec: Standards for Efficient Cryptography Group (SECG), "SEC 1:
+ Elliptic Curve Cryptography", Version 1.0, September 2000.</p>
+ </item>
+ <item>
+ <p>ecdsa: American National Standards Institute (ANSI),
+ ANS X9.62-2005: The Elliptic Curve Digital Signature
+ Algorithm (ECDSA), 2005.</p>
+ </item>
</list>
<p>The above publications can be found at <url href="http://csrc.nist.gov/publications">NIST publications</url>, at <url href="http://www.ietf.org">IETF</url>.
</p>
@@ -1360,6 +1372,116 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]>
</func>
<func>
+ <name>ec_key_new(NamedCurve) -> ECKey</name>
+ <type>
+ <v>NamedCurve = atom()</v>
+ <v>ECKey = EC key resource()</v>
+ </type>
+ <desc>
+ <p>Generate an new EC key from the named curve. The private key
+ will be initialized with random data.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>ec_key_generate(ECKey) -> ok | error</name>
+ <type>
+ <v>ECKey = EC key resource()</v>
+ </type>
+ <desc>
+ <p>Fills in the public key if only the private key is known or generates
+ a new private/public key pair if only the curve parameters are known.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>ec_key_to_term(ECKey) -> ECKeyTerm.</name>
+ <type>
+ <v>ECKey = EC key resource()</v>
+ <v>ECKeyTerm = EC key as Erlang term</v>
+ </type>
+ <desc>
+ <p>Convert a EC key from a NIF resource into an Erlang term.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>term_to_ec_key(ECKeyTerm) -> ECKey</name>
+ <type>
+ <v>ECKeyTerm = EC key as Erlang term</v>
+ <v>ECKey = EC key resource()</v>
+ </type>
+ <desc>
+ <p>Convert a EC key an Erlang term into a NIF resource.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>ecdsa_sign(DataOrDigest, ECKey) -> Signature</name>
+ <name>ecdsa_sign(DigestType, DataOrDigest, ECKey) -> Signature</name>
+ <fsummary>Sign the data using ecdsa with the given key.</fsummary>
+ <type>
+ <v>DataOrDigest = Data | {digest,Digest}</v>
+ <v>Data = Mpint</v>
+ <v>Digest = binary()</v>
+ <v>ECKey = EC key resource()</v>
+ <v>DigestType = md5 | sha | sha256 | sha384 | sha512</v>
+ <d>The default <c>DigestType</c> is sha.</d>
+ <v>Mpint = binary()</v>
+ <v>Signature = binary()</v>
+ </type>
+ <desc>
+ <p>Creates a ESDSA signature with the private key <c>Key</c>
+ of a digest. The digest is either calculated as a
+ <c>DigestType</c> digest of <c>Data</c> or a precalculated
+ binary <c>Digest</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>ecdsa_verify(DataOrDigest, Signature, ECKey) -> Verified</name>
+ <name>ecdsa_verify(DigestType, DataOrDigest, Signature, ECKey) -> Verified </name>
+ <fsummary>Verify the digest and signature using ecdsa with given public key.</fsummary>
+ <type>
+ <v>Verified = boolean()</v>
+ <v>DataOrDigest = Data | {digest|Digest}</v>
+ <v>Data, Signature = Mpint</v>
+ <v>Digest = binary()</v>
+ <v>ECKey = EC key resource()</v>
+ <v>DigestType = md5 | sha | sha256 | sha384 | sha512</v>
+ <d>The default <c>DigestType</c> is sha.</d>
+ <v>Mpint = binary()</v>
+ </type>
+ <desc>
+ <p>Verifies that a digest matches the ECDSA signature using the
+ signer's public key <c>Key</c>.
+ The digest is either calculated as a <c>DigestType</c>
+ digest of <c>Data</c> or a precalculated binary <c>Digest</c>.</p>
+ <p>May throw exception <c>notsup</c> in case the chosen <c>DigestType</c>
+ is not supported by the underlying OpenSSL implementation.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>ecdh_compute_key(OthersPublicKey, MyPrivateKey) -> SharedSecret</name>
+ <name>ecdh_compute_key(OthersPublicKey, MyECPoint) -> SharedSecret</name>
+ <fsummary>Computes the shared secret</fsummary>
+ <type>
+ <v>OthersPublicKey, MyPrivateKey = ECKey()</v>
+ <v>MyPrivatePoint = binary()</v>
+ <v>SharedSecret = binary()</v>
+ </type>
+ <desc>
+ <p>Computes the shared secret from the private key and the other party's public key.
+ </p>
+ </desc>
+ </func>
+
+ <func>
<name>exor(Data1, Data2) -> Result</name>
<fsummary>XOR data</fsummary>
<type>
@@ -1373,6 +1495,29 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]>
</funcs>
<section>
+ <title>Elliptic Curve Key</title>
+ <p>Elliptic Curve keys consist of the curve paramters and a the
+ private and public keys (points on the curve). Translating the
+ raw curve paraters into something usable for the underlying
+ OpenSSL implementation is a complicated process. The main cryptografic
+ functions therefore expect a NIF resource as input that contains the
+ key in an internal format. Two functions <b>ec_key_to_term/1</b>
+ and <b>term_to_ec_key</b> are provided to convert between Erlang
+ terms and the resource format</p>
+ <p><em>Key in term form</em></p>
+ <pre>
+ec_named_curve() = atom()
+ec_point() = binary()
+ec_basis() = {tpbasis, K :: non_neg_integer()} | {ppbasis, K1 :: non_neg_integer(), K2 :: non_neg_integer(), K3 :: non_neg_integer()} | onbasis
+ec_field() = {prime_field, Prime :: Mpint()} | {characteristic_two_field, M :: integer(), Basis :: ec_basis()}
+ec_prime() = {A :: Mpint(), B :: Mpint(), Seed :: binary()}
+ec_curve_spec() = {Field :: ec_field(), Prime :: ec_prime(), Point :: ec_point(), Order :: Mpint(), CoFactor :: none | Mpint()}
+ec_curve() = ec_named_curve() | ec_curve_spec()
+ec_key() = {Curve :: ec_curve(), PrivKey :: Mpint() | undefined, PubKey :: ec_point() | undefined}
+ </pre>
+ </section>
+
+ <section>
<title>DES in CBC mode</title>
<p>The Data Encryption Standard (DES) defines an algorithm for
encrypting and decrypting an 8 byte quantity using an 8 byte key
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index 1d0a9943c3..7558087d99 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -67,6 +67,8 @@
-export([aes_cbc_ivec/1]).
-export([aes_ctr_encrypt/3, aes_ctr_decrypt/3]).
-export([aes_ctr_stream_init/2, aes_ctr_stream_encrypt/2, aes_ctr_stream_decrypt/2]).
+-export([ec_key_new/1, ec_key_to_term/1, term_to_ec_key/1, ec_key_generate/1]).
+-export([ecdsa_sign/2, ecdsa_sign/3, ecdsa_verify/3, ecdsa_verify/4, ecdh_compute_key/2]).
-export([dh_generate_parameters/2, dh_check/1]). %% Testing see below
@@ -113,12 +115,25 @@
hmac, hmac_init, hmac_update, hmac_final, hmac_final_n, info,
rc2_cbc_encrypt, rc2_cbc_decrypt,
srp_generate_key, srp_compute_key,
+ ec_key_new, ec_key_to_term, term_to_ec_key, ec_key_generate,
+ ecdsa_sign, ecdsa_verify, ecdh_compute_key,
info_lib, algorithms]).
+-type mpint() :: binary().
-type rsa_digest_type() :: 'md5' | 'sha' | 'sha224' | 'sha256' | 'sha384' | 'sha512'.
-type dss_digest_type() :: 'none' | 'sha'.
+-type ecdsa_digest_type() :: 'md5' | 'sha' | 'sha256' | 'sha384' | 'sha512'.
-type data_or_digest() :: binary() | {digest, binary()}.
-type crypto_integer() :: binary() | integer().
+-type ec_key_res() :: any(). %% nif resource
+-type ec_named_curve() :: atom().
+-type ec_point() :: binary().
+-type ec_basis() :: {tpbasis, K :: non_neg_integer()} | {ppbasis, K1 :: non_neg_integer(), K2 :: non_neg_integer(), K3 :: non_neg_integer()} | onbasis.
+-type ec_field() :: {prime_field, Prime :: mpint()} | {characteristic_two_field, M :: integer(), Basis :: ec_basis()}.
+-type ec_prime() :: {A :: mpint(), B :: mpint(), Seed :: binary()}.
+-type ec_curve_spec() :: {Field :: ec_field(), Prime :: ec_prime(), Point :: ec_point(), Order :: mpint(), CoFactor :: none | mpint()}.
+-type ec_curve() :: ec_named_curve() | ec_curve_spec().
+-type ec_key() :: {Curve :: ec_curve(), PrivKey :: mpint() | undefined, PubKey :: ec_point() | undefined}.
-define(nif_stub,nif_stub_error(?LINE)).
@@ -1154,6 +1169,94 @@ srp_compute_key(Verifier, Prime, ClientPublic, ServerPublic, ServerPrivate, Vers
srp_server_secret_nif(Verifier, ServerPrivate, Scrambler, ClientPublic, Prime).
%%
+%% EC
+%%
+-spec ec_key_new(ec_named_curve()) -> ec_key_res().
+ec_key_new(_Curve) -> ?nif_stub.
+
+-spec ec_key_generate(ec_key_res()) -> ok | error.
+ec_key_generate(_Key) -> ?nif_stub.
+
+nif_prime_to_term({prime_field, Prime}) ->
+ {prime_field, erlint(Prime)};
+nif_prime_to_term(PrimeField) ->
+ PrimeField.
+nif_curve_to_term({A, B, Seed}) ->
+ {erlint(A), erlint(B), Seed}.
+nif_curve_parameters_to_term({PrimeField, Curve, BasePoint, Order, CoFactor}) ->
+ {nif_prime_to_term(PrimeField), nif_curve_to_term(Curve), BasePoint, erlint(Order), erlint(CoFactor)};
+nif_curve_parameters_to_term(Curve) when is_atom(Curve) ->
+ %% named curve
+ Curve.
+
+-spec ec_key_to_term(ec_key_res()) -> ec_key().
+ec_key_to_term(Key) ->
+ case ec_key_to_term_nif(Key) of
+ {Curve, PrivKey, PubKey} ->
+ {nif_curve_parameters_to_term(Curve), erlint(PrivKey), PubKey};
+ _ ->
+ erlang:error(conversion_failed)
+ end.
+
+ec_key_to_term_nif(_Key) -> ?nif_stub.
+
+term_to_nif_prime({prime_field, Prime}) ->
+ {prime_field, mpint(Prime)};
+term_to_nif_prime(PrimeField) ->
+ PrimeField.
+term_to_nif_curve({A, B, Seed}) ->
+ {mpint(A), mpint(B), Seed}.
+term_to_nif_curve_parameters({PrimeField, Curve, BasePoint, Order, CoFactor}) ->
+ {term_to_nif_prime(PrimeField), term_to_nif_curve(Curve), BasePoint, mpint(Order), mpint(CoFactor)};
+term_to_nif_curve_parameters(Curve) when is_atom(Curve) ->
+ %% named curve
+ Curve.
+
+-spec term_to_ec_key(ec_key()) -> ec_key_res().
+term_to_ec_key({Curve, undefined, PubKey}) ->
+ term_to_ec_key_nif(term_to_nif_curve_parameters(Curve), undefined, PubKey);
+term_to_ec_key({Curve, PrivKey, PubKey}) ->
+ term_to_ec_key_nif(term_to_nif_curve_parameters(Curve), mpint(PrivKey), PubKey).
+
+term_to_ec_key_nif(_Curve, _PrivKey, _PubKey) -> ?nif_stub.
+
+%%
+%% ECDSA - sign
+%%
+-spec ecdsa_sign(data_or_digest(), ec_key_res()) -> binary().
+-spec ecdsa_sign(ecdsa_digest_type(), data_or_digest(), ec_key_res()) -> binary().
+
+ecdsa_sign(DataOrDigest,Key) ->
+ ecdsa_sign(sha, DataOrDigest, Key).
+ecdsa_sign(Type, DataOrDigest, Key) ->
+ case ecdsa_sign_nif(Type,DataOrDigest,Key) of
+ error -> erlang:error(badkey, [Type,DataOrDigest,Key]);
+ Sign -> Sign
+ end.
+
+ecdsa_sign_nif(_Type, _DataOrDigest, _Key) -> ?nif_stub.
+
+%%
+%% ECDSA - verify
+%%
+-spec ecdsa_verify(data_or_digest(), binary(), ec_key_res()) -> boolean().
+-spec ecdsa_verify(ecdsa_digest_type(), data_or_digest(), binary(), ec_key_res()) ->
+ boolean().
+
+ecdsa_verify(Data,Signature,Key) ->
+ ecdsa_verify_nif(sha, Data,Signature,Key).
+ecdsa_verify(Type, DataOrDigest, Signature, Key) ->
+ case ecdsa_verify_nif(Type, DataOrDigest, Signature, Key) of
+ notsup -> erlang:error(notsup);
+ Bool -> Bool
+ end.
+
+ecdsa_verify_nif(_Type, _DataOrDigest, _Signature, _Key) -> ?nif_stub.
+
+-spec ecdh_compute_key(ec_key_res(), ec_key_res() | ec_point()) -> binary().
+ecdh_compute_key(_Others, _My) -> ?nif_stub.
+
+
%% LOCAL FUNCTIONS
%%
@@ -1262,7 +1365,9 @@ bin_to_int(Bin) ->
erlint(<<MPIntSize:32/integer,MPIntValue/binary>>) ->
Bits= MPIntSize * 8,
<<Integer:Bits/integer>> = MPIntValue,
- Integer.
+ Integer;
+erlint(undefined) ->
+ undefined.
mpint_to_bin(<<Len:32, Bin:Len/binary>>) ->
Bin.
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index 08ecad3233..c5597be34c 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -76,6 +76,7 @@
rsa_encrypt_decrypt/1,
dh/1,
srp3/1, srp6/1, srp6a/1,
+ ec/1,
exor_test/1,
rc4_test/1,
rc4_stream_test/1,
@@ -105,7 +106,7 @@ groups() ->
rand_uniform_test, strong_rand_test,
rsa_verify_test, dsa_verify_test, rsa_sign_test,
rsa_sign_hash_test, dsa_sign_test, dsa_sign_hash_test,
- rsa_encrypt_decrypt, dh, srp3, srp6, srp6a, exor_test,
+ rsa_encrypt_decrypt, dh, srp3, srp6, srp6a, ec, exor_test,
rc4_test, rc4_stream_test, mod_exp_test, blowfish_cfb64,
smp]}].
@@ -1847,6 +1848,70 @@ dh(Config) when is_list(Config) ->
exit(Pid, kill)
end.
+ec(doc) ->
+ ["Test ec (Ecliptic Curve) functions."];
+ec(suite) -> [];
+ec(Config) when is_list(Config) ->
+ if_supported(ec, fun() -> ec_do() end).
+
+ec_do() ->
+ %% test for a name curve
+ L2 = crypto:ec_key_new(sect113r2),
+ crypto:ec_key_generate(L2),
+
+ D2 = crypto:ec_key_to_term(L2),
+ T2 = crypto:term_to_ec_key(D2),
+ ?line D2 = crypto:ec_key_to_term(T2),
+
+ %%TODO: find a published test case for a EC key
+
+ %% test for a full specified curve and public key,
+ %% taken from csca-germany_013_self_signed_cer.pem
+ PubKey = <<16#04, 16#4a, 16#94, 16#49, 16#81, 16#77, 16#9d, 16#df,
+ 16#1d, 16#a5, 16#e7, 16#c5, 16#27, 16#e2, 16#7d, 16#24,
+ 16#71, 16#a9, 16#28, 16#eb, 16#4d, 16#7b, 16#67, 16#75,
+ 16#ae, 16#09, 16#0a, 16#51, 16#45, 16#19, 16#9b, 16#d4,
+ 16#7e, 16#a0, 16#81, 16#e5, 16#5e, 16#d4, 16#a4, 16#3f,
+ 16#60, 16#7c, 16#6a, 16#50, 16#ee, 16#36, 16#41, 16#8a,
+ 16#87, 16#ff, 16#cd, 16#a6, 16#10, 16#39, 16#ca, 16#95,
+ 16#76, 16#7d, 16#ae, 16#ca, 16#c3, 16#44, 16#3f, 16#e3, 16#2c>>,
+ <<P:264/integer>> = <<16#00, 16#a9, 16#fb, 16#57, 16#db, 16#a1, 16#ee, 16#a9,
+ 16#bc, 16#3e, 16#66, 16#0a, 16#90, 16#9d, 16#83, 16#8d,
+ 16#72, 16#6e, 16#3b, 16#f6, 16#23, 16#d5, 16#26, 16#20,
+ 16#28, 16#20, 16#13, 16#48, 16#1d, 16#1f, 16#6e, 16#53, 16#77>>,
+ <<A:256/integer>> = <<16#7d, 16#5a, 16#09, 16#75, 16#fc, 16#2c, 16#30, 16#57,
+ 16#ee, 16#f6, 16#75, 16#30, 16#41, 16#7a, 16#ff, 16#e7,
+ 16#fb, 16#80, 16#55, 16#c1, 16#26, 16#dc, 16#5c, 16#6c,
+ 16#e9, 16#4a, 16#4b, 16#44, 16#f3, 16#30, 16#b5, 16#d9>>,
+ <<B:256/integer>> = <<16#26, 16#dc, 16#5c, 16#6c, 16#e9, 16#4a, 16#4b, 16#44,
+ 16#f3, 16#30, 16#b5, 16#d9, 16#bb, 16#d7, 16#7c, 16#bf,
+ 16#95, 16#84, 16#16, 16#29, 16#5c, 16#f7, 16#e1, 16#ce,
+ 16#6b, 16#cc, 16#dc, 16#18, 16#ff, 16#8c, 16#07, 16#b6>>,
+ BasePoint = <<16#04, 16#8b, 16#d2, 16#ae, 16#b9, 16#cb, 16#7e, 16#57,
+ 16#cb, 16#2c, 16#4b, 16#48, 16#2f, 16#fc, 16#81, 16#b7,
+ 16#af, 16#b9, 16#de, 16#27, 16#e1, 16#e3, 16#bd, 16#23,
+ 16#c2, 16#3a, 16#44, 16#53, 16#bd, 16#9a, 16#ce, 16#32,
+ 16#62, 16#54, 16#7e, 16#f8, 16#35, 16#c3, 16#da, 16#c4,
+ 16#fd, 16#97, 16#f8, 16#46, 16#1a, 16#14, 16#61, 16#1d,
+ 16#c9, 16#c2, 16#77, 16#45, 16#13, 16#2d, 16#ed, 16#8e,
+ 16#54, 16#5c, 16#1d, 16#54, 16#c7, 16#2f, 16#04, 16#69, 16#97>>,
+ <<Order:264/integer>> = <<16#00, 16#a9, 16#fb, 16#57, 16#db, 16#a1, 16#ee, 16#a9,
+ 16#bc, 16#3e, 16#66, 16#0a, 16#90, 16#9d, 16#83, 16#8d,
+ 16#71, 16#8c, 16#39, 16#7a, 16#a3, 16#b5, 16#61, 16#a6,
+ 16#f7, 16#90, 16#1e, 16#0e, 16#82, 16#97, 16#48, 16#56, 16#a7>>,
+ CoFactor = 1,
+ Curve = {{prime_field,P},{A,B,none},BasePoint, Order,CoFactor},
+ CsCaKey = {Curve, undefined, PubKey},
+ T3 = crypto:term_to_ec_key(CsCaKey),
+ ?line CsCaKey = crypto:ec_key_to_term(T3),
+
+ Msg = <<99,234,6,64,190,237,201,99,80,248,58,40,70,45,149,218,5,246,242,63>>,
+ Sign = crypto:ecdsa_sign(sha, sized_binary(Msg), L2),
+ ?line true = crypto:ecdsa_verify(sha, sized_binary(Msg), sized_binary(Sign), L2),
+ ?line false = crypto:ecdsa_verify(sha, sized_binary(Msg), sized_binary(<<10,20>>), L2),
+
+ ok.
+
srp3(doc) ->
["SRP-3 test vectors generated by http://srp.stanford.edu/demo/demo.html"];
srp3(suite) -> [];