diff options
author | Andreas Schultz <aschultz@tpip.net> | 2013-03-28 16:19:40 +0100 |
---|---|---|
committer | Ingela Anderton Andin <ingela@erlang.org> | 2013-05-08 10:39:16 +0200 |
commit | c6c2e82f8b94010e78bfa5fedce7629e7fd32d58 (patch) | |
tree | 5bfe63388a215b4f4e11b38fcee552779ebb797b | |
parent | 8e00f4ce7a49b2fd1da7e481dc0985703e4131a5 (diff) | |
download | otp-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.c | 835 | ||||
-rw-r--r--[-rwxr-xr-x] | lib/crypto/doc/src/crypto.xml | 149 | ||||
-rw-r--r-- | lib/crypto/src/crypto.erl | 107 | ||||
-rw-r--r-- | lib/crypto/test/crypto_SUITE.erl | 67 |
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) -> []; |