From 08753545c4b08b191234f60efe65f78ed113f0d2 Mon Sep 17 00:00:00 2001
From: Andreas Schultz <aschultz@tpip.net>
Date: Sun, 16 Nov 2014 18:45:53 +0100
Subject: [crypto] enhance generate_key for ECC keys

enhance generate_key to calculate ECC public keys from private keys
---
 lib/crypto/c_src/crypto.c        | 47 ++++++++++++++++++++++------------------
 lib/crypto/doc/src/crypto.xml    |  2 +-
 lib/crypto/src/crypto.erl        |  7 +++---
 lib/crypto/test/crypto_SUITE.erl | 40 ++++++++++++++++++++++++++++++++--
 4 files changed, 68 insertions(+), 28 deletions(-)

(limited to 'lib')

diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index a42de9adb1..005f4a964b 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -410,7 +410,7 @@ static ErlNifFunc nif_funcs[] = {
     {"bf_ecb_crypt", 3, bf_ecb_crypt},
     {"blowfish_ofb64_encrypt", 3, blowfish_ofb64_encrypt},
 
-    {"ec_key_generate", 1, ec_key_generate},
+    {"ec_key_generate", 2, ec_key_generate},
     {"ecdsa_sign_nif", 4, ecdsa_sign_nif},
     {"ecdsa_verify_nif", 5, ecdsa_verify_nif},
     {"ecdh_compute_key_nif", 3, ecdh_compute_key_nif},
@@ -3689,32 +3689,37 @@ out:
 static ERL_NIF_TERM ec_key_generate(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
 {
 #if defined(HAVE_EC)
-    EC_KEY *key = ec_key_new(env, argv[0]);
+    EC_KEY *key;
+    const EC_GROUP *group;
+    const EC_POINT *public_key;
+    ERL_NIF_TERM priv_key;
+    ERL_NIF_TERM pub_key = atom_undefined;
 
     CHECK_OSE_CRYPTO();
 
-    if (key && EC_KEY_generate_key(key)) {
-	const EC_GROUP *group;
-	const EC_POINT *public_key;
-	ERL_NIF_TERM priv_key;
-	ERL_NIF_TERM pub_key = atom_undefined;
-
-	group = EC_KEY_get0_group(key);
-	public_key = EC_KEY_get0_public_key(key);
+    if (!get_ec_key(env, argv[0], argv[1], atom_undefined, &key))
+	goto badarg;
 
-	if (group && public_key) {
-	    pub_key = point2term(env, group, public_key,
-				 EC_KEY_get_conv_form(key));
-	}
-	priv_key = bn2term(env, EC_KEY_get0_private_key(key));
-	EC_KEY_free(key);
-	return enif_make_tuple2(env, pub_key, priv_key);
+    if (argv[1] == atom_undefined) {
+	if (!EC_KEY_generate_key(key))
+	    goto badarg;
     }
-    else {
-	if (key)
-	    EC_KEY_free(key);
-	return enif_make_badarg(env);
+
+    group = EC_KEY_get0_group(key);
+    public_key = EC_KEY_get0_public_key(key);
+
+    if (group && public_key) {
+	pub_key = point2term(env, group, public_key,
+			     EC_KEY_get_conv_form(key));
     }
+    priv_key = bn2term(env, EC_KEY_get0_private_key(key));
+    EC_KEY_free(key);
+    return enif_make_tuple2(env, pub_key, priv_key);
+
+badarg:
+    if (key)
+	EC_KEY_free(key);
+    return enif_make_badarg(env);
 #else
     return atom_notsup;
 #endif
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 271130a9e6..bad60adfe7 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -269,7 +269,7 @@
 	<v>SrpUserParams = {user, [Generator::binary(), Prime::binary(), Version::atom()]}</v>
 	<v>SrpHostParams = {host, [Verifier::binary(), Generator::binary(), Prime::binary(), Version::atom()]}</v>
 	<v>PublicKey =  dh_public() | ecdh_public() | srp_public() </v>
-	<v>PrivKeyIn = undefined | dh_private() | srp_private() </v>
+	<v>PrivKeyIn = undefined | dh_private() | ecdh_private() | srp_private() </v>
 	<v>PrivKeyOut =  dh_private() | ecdh_private() | srp_private() </v>
       </type>
       <desc>
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index 7f82fa83fd..92da8efba4 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -588,9 +588,8 @@ generate_key(srp, {user, [Generator, Prime, Version]}, PrivateArg)
 	      end,
     user_srp_gen_key(Private, Generator, Prime);
 
-generate_key(ecdh, Curve, undefined) ->
-    ec_key_generate(nif_curve_params(Curve)).
-
+generate_key(ecdh, Curve, PrivKey) ->
+    ec_key_generate(nif_curve_params(Curve), ensure_int_as_bin(PrivKey)).
 
 compute_key(dh, OthersPublicKey, MyPrivateKey, DHParameters) ->
     case dh_compute_key_nif(ensure_int_as_bin(OthersPublicKey),
@@ -1555,7 +1554,7 @@ dh_compute_key(OthersPublicKey, MyPrivateKey, DHParameters) ->
 
 dh_compute_key_nif(_OthersPublicKey, _MyPrivateKey, _DHParameters) -> ?nif_stub.
 
-ec_key_generate(_Key) -> ?nif_stub.
+ec_key_generate(_Curve, _Key) -> ?nif_stub.
 
 ecdh_compute_key_nif(_Others, _Curve, _My) -> ?nif_stub.
 
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index 1031e6403f..3a0c6d7b1e 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -84,7 +84,7 @@ groups() ->
      {dss, [], [sign_verify]},
      {ecdsa, [], [sign_verify]},
      {dh, [], [generate_compute]},
-     {ecdh, [], [compute]},
+     {ecdh, [], [compute, generate]},
      {srp, [], [generate_compute]},
      {des_cbc, [], [block]},
      {des_cfb, [], [block]},
@@ -243,6 +243,12 @@ compute(Config) when is_list(Config) ->
     Gen = proplists:get_value(compute, Config),
     lists:foreach(fun do_compute/1, Gen).
 %%--------------------------------------------------------------------
+generate() ->
+     [{doc, " Test crypto:generate_key"}].
+generate(Config) when is_list(Config) ->
+    Gen = proplists:get_value(generate, Config),
+    lists:foreach(fun do_generate/1, Gen).
+%%--------------------------------------------------------------------
 mod_pow() ->
     [{doc, "mod_pow testing (A ^ M % P with bignums)"}].
 mod_pow(Config) when is_list(Config) ->
@@ -494,6 +500,14 @@ do_compute({ecdh = Type, Pub, Priv, Curve, SharedSecret}) ->
 	     ct:fail({{crypto, compute_key, [Type, Pub, Priv, Curve]}, {expected, SharedSecret}, {got, Other}})
      end.
 
+do_generate({ecdh = Type, Curve, Priv, Pub}) ->
+    case crypto:generate_key(Type, Curve, Priv) of
+	{Pub, _} ->
+	    ok;
+	{Other, _} ->
+	    ct:fail({{crypto, generate_key, [Type, Priv, Curve]}, {expected, Pub}, {got, Other}})
+    end.
+
 hexstr2point(X, Y) ->
     <<4:8, (hexstr2bin(X))/binary, (hexstr2bin(Y))/binary>>.
 
@@ -721,7 +735,8 @@ group_config(srp, Config) ->
     [{generate_compute, GenerateCompute} | Config];
 group_config(ecdh, Config) ->
     Compute = ecdh(),
-    [{compute, Compute} | Config];
+    Generate = ecc(),
+    [{compute, Compute}, {generate, Generate} | Config];
 group_config(dh, Config) ->
     GenerateCompute = [dh()],
     [{generate_compute, GenerateCompute} | Config];
@@ -1968,6 +1983,27 @@ rsa_oaep() ->
     Msg =  hexstr2bin("750c4047f547e8e41411856523298ac9bae245efaf1397fbe56f9dd5"),
     {rsa, Public, Private, Msg, rsa_pkcs1_oaep_padding}.
 
+ecc() ->
+%%               http://point-at-infinity.org/ecc/nisttv
+%%
+%% Test vectors for the NIST elliptic curves P192, P224, P256, P384, P521,
+%% B163, B233, B283, B409, B571, K163, K233, K283, K409 and K571. For more
+%% information about the curves see
+%%       http://csrc.nist.gov/encryption/dss/ecdsa/NISTReCur.pdf
+%%
+    [{ecdh,secp192r1,1,
+      hexstr2point("188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012",
+		   "07192B95FFC8DA78631011ED6B24CDD573F977A11E794811")},
+     {ecdh,secp192r1,2,
+      hexstr2point("DAFEBF5828783F2AD35534631588A3F629A70FB16982A888",
+		   "DD6BDA0D993DA0FA46B27BBC141B868F59331AFA5C7E93AB")},
+     {ecdh,secp192r1,3,
+      hexstr2point("76E32A2557599E6EDCD283201FB2B9AADFD0D359CBB263DA",
+		   "782C37E372BA4520AA62E0FED121D49EF3B543660CFD05FD")},
+     {ecdh,secp192r1,4,
+      hexstr2point("35433907297CC378B0015703374729D7A4FE46647084E4BA",
+		   "A2649984F2135C301EA3ACB0776CD4F125389B311DB3BE32")}].
+
 no_padding() ->
     Public = [_, Mod] = rsa_public(),
     Private = rsa_private(),
-- 
cgit v1.2.3