diff options
Diffstat (limited to 'lib/crypto/c_src/crypto.c')
-rw-r--r-- | lib/crypto/c_src/crypto.c | 835 |
1 files changed, 833 insertions, 2 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 */ |