File 8707-crypto-ec_generate_key_nif-use-old-code-for-the-fail.patch of Package erlang

From f896a9c595666820dcd6c4585953ac246475d1fd Mon Sep 17 00:00:00 2001
From: Hans Nilsson <hans@erlang.org>
Date: Thu, 28 Apr 2022 20:53:51 +0200
Subject: [PATCH 7/7] crypto: ec_generate_key_nif - use old code for the
 failing case

---
 lib/crypto/c_src/ec.c | 278 ++++++++++++++++++++++++++++++++----------
 lib/crypto/c_src/ec.h |   8 +-
 2 files changed, 219 insertions(+), 67 deletions(-)

diff --git a/lib/crypto/c_src/ec.c b/lib/crypto/c_src/ec.c
index 58a51f862c..f14768d134 100644
--- a/lib/crypto/c_src/ec.c
+++ b/lib/crypto/c_src/ec.c
@@ -22,7 +22,6 @@
 #include "bn.h"
 
 #ifdef HAVE_EC
-
 # if defined(HAS_3_0_API)
 
 int get_curve_definition(ErlNifEnv* env, ERL_NIF_TERM *ret, ERL_NIF_TERM def,
@@ -205,7 +204,7 @@ int get_curve_definition(ErlNifEnv* env, ERL_NIF_TERM *ret, ERL_NIF_TERM def,
 }
 
 int get_ec_public_key(ErlNifEnv* env, ERL_NIF_TERM key, EVP_PKEY **pkey)
-{
+{ /* key :: {CurveDef::{_,_,_,_,_}, PubKey::binary()} */
     ERL_NIF_TERM ret = atom_undefined;
     const ERL_NIF_TERM *tpl_terms;
     int tpl_arity;
@@ -248,40 +247,41 @@ int get_ec_public_key(ErlNifEnv* env, ERL_NIF_TERM key, EVP_PKEY **pkey)
 }
 
 
-int get_ec_private_key(ErlNifEnv* env, ERL_NIF_TERM key, EVP_PKEY **pkey)
+int get_ec_private_key_2(ErlNifEnv* env,
+                         ERL_NIF_TERM curve, ERL_NIF_TERM key,
+                         EVP_PKEY **pkey,
+                         ERL_NIF_TERM *ret,
+                         size_t *order_size);
+
+int get_ec_private_key_2(ErlNifEnv* env,
+                         ERL_NIF_TERM curve, ERL_NIF_TERM key,
+                         EVP_PKEY **pkey,
+                         ERL_NIF_TERM *ret,
+                         size_t *order_size)
 {
-    ERL_NIF_TERM ret = atom_undefined;
-    const ERL_NIF_TERM *tpl_terms;
-    int tpl_arity;
     int i = 0;
     OSSL_PARAM params[15];
     EVP_PKEY_CTX *pctx = NULL;
 
-    if (!enif_get_tuple(env, key, &tpl_arity, &tpl_terms) ||
-        (tpl_arity != 2) ||
-        !enif_is_tuple(env, tpl_terms[0]) ||
-        !enif_is_binary(env, tpl_terms[1]) )
-        assign_goto(ret, err, EXCP_BADARG_N(env, 0, "Bad private key format"));
-    
-    if (!get_ossl_BN_param_from_bin(env, "priv",  tpl_terms[1], &params[i++]))
-        assign_goto(ret, err, EXCP_BADARG_N(env, 0, "Bad private key"));
+    if (!get_ossl_BN_param_from_bin(env, "priv",  key, &params[i++]))
+        assign_goto(*ret, err, EXCP_BADARG_N(env, 0, "Bad private key"));
 
-    if (!get_curve_definition(env, &ret, tpl_terms[0], params, &i, NULL))
+    if (!get_curve_definition(env, ret, curve, params, &i, order_size))
         goto err;
 
     params[i++] = OSSL_PARAM_construct_end();
 
     if (!(pctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL)))
-        assign_goto(ret, err, EXCP_ERROR(env, "Can't make EVP_PKEY_CTX"));
+        assign_goto(*ret, err, EXCP_ERROR(env, "Can't make EVP_PKEY_CTX"));
 
     if (EVP_PKEY_fromdata_init(pctx) <= 0)
-        assign_goto(ret, err, EXCP_ERROR(env, "Can't init fromdata"));
+        assign_goto(*ret, err, EXCP_ERROR(env, "Can't init fromdata"));
     
     if (EVP_PKEY_fromdata(pctx, pkey, EVP_PKEY_KEYPAIR, params) <= 0)
-        assign_goto(ret, err, EXCP_ERROR(env, "Can't do fromdata"));
+        assign_goto(*ret, err, EXCP_ERROR(env, "Can't do fromdata"));
 
     if (!*pkey)
-        assign_goto(ret, err, EXCP_ERROR(env, "Couldn't get a private key"));
+        assign_goto(*ret, err, EXCP_ERROR(env, "Couldn't get a private key"));
     
     if (pctx) EVP_PKEY_CTX_free(pctx);
     return 1;
@@ -291,29 +291,180 @@ int get_ec_private_key(ErlNifEnv* env, ERL_NIF_TERM key, EVP_PKEY **pkey)
     return 0;
 }
 
-# endif /* HAS_3_0_API */
 
+int get_ec_private_key(ErlNifEnv* env, ERL_NIF_TERM key, EVP_PKEY **pkey)
+{  /* key ::  {CurveDef::{_,_,_,_,_}, PrivKey::binary()}  */
+    ERL_NIF_TERM ret = atom_undefined;
+    const ERL_NIF_TERM *tpl_terms;
+    int tpl_arity;
 
+    if (!enif_get_tuple(env, key, &tpl_arity, &tpl_terms) ||
+        (tpl_arity != 2) ||
+        !enif_is_tuple(env, tpl_terms[0]) ||
+        !enif_is_binary(env, tpl_terms[1]) )
+        assign_goto(ret, err, EXCP_BADARG_N(env, 0, "Bad private key format"));
+    
+    if (!get_ec_private_key_2(env, tpl_terms[0], tpl_terms[1], pkey, &ret, NULL))
+        goto err;
 
+    return 1;
 
-/*----------------------------------------------------------------
-  Non 3.0-specific functions
-*/
+ err:
+    return 0;
+}
 
-static EC_KEY* ec_key_new(ErlNifEnv* env, ERL_NIF_TERM curve_arg, size_t *size);
-static ERL_NIF_TERM point2term(ErlNifEnv* env,
-			       const EC_GROUP *group,
-			       const EC_POINT *point,
-			       point_conversion_form_t form);
+int mk_pub_key_binary(ErlNifEnv* env, EVP_PKEY **peer_pkey, ErlNifBinary *pubkey_bin, ERL_NIF_TERM *ret);
 
-ERL_NIF_TERM make_badarg_maybe(ErlNifEnv* env)
+ERL_NIF_TERM ec_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{ /* (Curve, PrivKey|undefined)  */
+    ERL_NIF_TERM ret = atom_undefined;
+    int i = 0;
+    OSSL_PARAM params[15];
+    EVP_PKEY_CTX *pctx = NULL;
+    EVP_PKEY *pkey = NULL, *peer_pkey = NULL;
+    size_t sz, order_size;
+    BIGNUM *priv_bn = NULL;
+    ErlNifBinary pubkey_bin;
+    
+    if (argv[1] != atom_undefined)
+        {
+            if (!get_ec_private_key_2(env, argv[0], argv[1], &peer_pkey, &ret, &order_size))
+                goto err;
+            
+            /* Get the two keys, pub as binary and priv as BN.
+               Since the private key is explicitly given, it must be calculated.
+               I haven't found any way to do that with the pure 3.0 interface yet.
+            */
+            if (!mk_pub_key_binary(env, &peer_pkey, &pubkey_bin, &ret))
+                goto err;
+
+            if (!EVP_PKEY_get_bn_param(peer_pkey, "priv", &priv_bn))
+                assign_goto(ret, err, EXCP_BADARG_N(env, 1, "Couldn't get peer priv key bytes"));
+        }
+    else
+        {
+            /* PrivKey (that is, argv[1]) == atom_undefined */
+            if (!get_curve_definition(env, &ret, argv[0], params, &i, &order_size))
+                // INSERT "ret" parameter in get_curve_definition !!
+                assign_goto(ret, err, EXCP_BADARG_N(env, 0, "Couldn't get Curve definition"));
+    
+            params[i++] = OSSL_PARAM_construct_end();
+
+            /* Neither the private nor the public key is known, so we generate the pair: */
+            if (!(pctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL)))
+                assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_CTX_new_from_name"));
+
+            if (EVP_PKEY_keygen_init(pctx) <= 0)
+                assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_keygen_init"));
+
+            if (!EVP_PKEY_CTX_set_params(pctx, params))
+                assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_CTX_set_params"));
+        
+            if (!EVP_PKEY_generate(pctx, &pkey))
+                assign_goto(ret, err, EXCP_ERROR(env, "Couldn't generate EC key"));
+    
+            /* Get the two keys, pub as binary and priv as BN */
+            if (!EVP_PKEY_get_octet_string_param(pkey, "encoded-pub-key", NULL, 0, &sz))
+                assign_goto(ret, err, EXCP_ERROR(env, "Can't get pub octet string size"));
+
+            if (!enif_alloc_binary(sz, &pubkey_bin))
+                assign_goto(ret, err, EXCP_ERROR(env, "Can't allocate pub octet string"));
+
+            if (!EVP_PKEY_get_octet_string_param(pkey, "encoded-pub-key",
+                                                 pubkey_bin.data,
+                                                 sz,
+                                                 &pubkey_bin.size))
+                assign_goto(ret, err, EXCP_ERROR(env, "Can't get pub octet string"));
+
+            if (!EVP_PKEY_get_bn_param(pkey, "priv", &priv_bn))
+                assign_goto(ret, err, EXCP_BADARG_N(env, 1, "Couldn't get priv key bytes"));
+        }
+
+    ret = enif_make_tuple2(env,
+                           enif_make_binary(env, &pubkey_bin),
+                           bn2term(env, order_size, priv_bn));
+ err:
+    if (pkey) EVP_PKEY_free(pkey);
+    if (peer_pkey) EVP_PKEY_free(peer_pkey);
+    if (pctx) EVP_PKEY_CTX_free(pctx);
+    if (priv_bn) BN_free(priv_bn);
+
+    return ret;
+}
+
+int mk_pub_key_binary(ErlNifEnv* env, EVP_PKEY **peer_pkey, ErlNifBinary *pubkey_bin, ERL_NIF_TERM *ret)
 {
-    ERL_NIF_TERM reason;
-    if (enif_has_pending_exception(env, &reason))
-	return reason; /* dummy return value ignored */
+    EC_KEY *ec_key = NULL;
+    EC_POINT *public_key = NULL;
+    EC_GROUP *group = NULL;
+    BIGNUM *priv_bn = NULL;
+    
+    *ret = atom_undefined;
+
+    /* Use the deprecated interface to get the curve and
+       private key in pre 3.0 form: */
+    if ((ec_key = EVP_PKEY_get1_EC_KEY(*peer_pkey)) == NULL)
+        assign_goto(*ret, err, EXCP_ERROR(env, "Couldn't get EC key"));
+
+    if ((group = EC_GROUP_dup(EC_KEY_get0_group(ec_key))) == NULL)
+        assign_goto(*ret, err, EXCP_ERROR(env, "Couldn't get EC_GROUP"));
+
+    if ((public_key = EC_POINT_new(group)) == NULL)
+        assign_goto(*ret, err, EXCP_ERROR(env, "Couldn't create POINT"));
+
+    if (!EC_POINT_copy(public_key, EC_GROUP_get0_generator(group)))
+        assign_goto(*ret, err, EXCP_ERROR(env, "Couldn't copy POINT"));
+
+    /* Make the corresponding public key */
+    if (!EVP_PKEY_get_bn_param(*peer_pkey, "priv", &priv_bn))
+        assign_goto(*ret, err, EXCP_BADARG_N(env, 1, "Couldn't get peer priv key bytes"));
+
+    if (BN_is_zero(priv_bn))
+        assign_goto(*ret, err, EXCP_BADARG_N(env, 1, "peer priv key must not be 0"));
+
+    if (!EC_POINT_mul(group, public_key, priv_bn, NULL, NULL, NULL))
+        assign_goto(*ret, err, EXCP_ERROR(env, "Couldn't multiply POINT"));
+
+    if (!EC_KEY_set_public_key(ec_key, public_key))
+        assign_goto(*ret, err, EXCP_ERROR(env, "Couldn't set EC_KEY"));
+
+    if (!EVP_PKEY_assign_EC_KEY(*peer_pkey, ec_key))
+        assign_goto(*ret, err, EXCP_ERROR(env, "Couldn't assign EC_KEY to PKEY"));
+            
+    /* And now get the binary representation (by some reason we can't read it from
+       peer_pubkey in the calling function with 3.0-functions.)
+    */
+    {
+        point_conversion_form_t form = EC_KEY_get_conv_form(ec_key);
+        size_t dlen = EC_POINT_point2oct(group, public_key, form, NULL, 0, NULL);
+
+        if (!enif_alloc_binary(dlen, pubkey_bin) ||
+            !EC_POINT_point2oct(group, public_key, form, pubkey_bin->data, pubkey_bin->size, NULL)
+            )
+            assign_goto(*ret, err, EXCP_ERROR(env, "Couldn't get public key"));
+    }
+
+ err:
+    if (public_key) EC_POINT_free(public_key);
+    if (group) EC_GROUP_free(group);
+    if (priv_bn) BN_free(priv_bn);
+
+    if (*ret == atom_undefined)
+        return 1;
     else
-	return enif_make_badarg(env);
+        return 0;
 }
+    
+# endif /* HAS_3_0_API */
+
+
+
+
+/*----------------------------------------------------------------
+  Non 3.0-specific functions
+*/
+
+# if ! defined(HAS_3_0_API)
 
 static EC_KEY* ec_key_new(ErlNifEnv* env, ERL_NIF_TERM curve_arg, size_t *size)
 {
@@ -509,6 +660,33 @@ static EC_KEY* ec_key_new(ErlNifEnv* env, ERL_NIF_TERM curve_arg, size_t *size)
     return key;
 }
 
+int term2point(ErlNifEnv* env, ERL_NIF_TERM term, EC_GROUP *group, EC_POINT **pptr)
+{
+    ErlNifBinary bin;
+    EC_POINT *point = NULL;
+
+    if (!enif_inspect_binary(env, term, &bin))
+        goto err;
+
+    if ((point = EC_POINT_new(group)) == NULL)
+        goto err;
+
+    /* set the point conversion form */
+    EC_GROUP_set_point_conversion_form(group, (point_conversion_form_t)(bin.data[0] & ~0x01));
+
+    /* extract the ec point */
+    if (!EC_POINT_oct2point(group, point, bin.data, bin.size, NULL))
+        goto err;
+
+    *pptr = point;
+    return 1;
+
+ err:
+    if (point)
+        EC_POINT_free(point);
+    return 0;
+}
+
 static ERL_NIF_TERM point2term(ErlNifEnv* env,
 			       const EC_GROUP *group,
 			       const EC_POINT *point,
@@ -544,35 +722,6 @@ static ERL_NIF_TERM point2term(ErlNifEnv* env,
     return ret;
 }
 
-int term2point(ErlNifEnv* env, ERL_NIF_TERM term, EC_GROUP *group, EC_POINT **pptr)
-{
-    ErlNifBinary bin;
-    EC_POINT *point = NULL;
-
-    if (!enif_inspect_binary(env, term, &bin))
-        goto err;
-
-    if ((point = EC_POINT_new(group)) == NULL)
-        goto err;
-
-    /* set the point conversion form */
-    EC_GROUP_set_point_conversion_form(group, (point_conversion_form_t)(bin.data[0] & ~0x01));
-
-    /* extract the ec point */
-    if (!EC_POINT_oct2point(group, point, bin.data, bin.size, NULL))
-        goto err;
-
-    *pptr = point;
-    return 1;
-
- err:
-    if (point)
-        EC_POINT_free(point);
-    return 0;
-}
-
-# if ! defined(HAS_3_0_API)
-
 int get_ec_private_key(ErlNifEnv* env, ERL_NIF_TERM key, EVP_PKEY **pkey)
 {
     const ERL_NIF_TERM *tpl_terms;
@@ -633,7 +782,6 @@ int get_ec_public_key(ErlNifEnv* env, ERL_NIF_TERM key, EVP_PKEY **pkey)
     return 0;
 }
 
-# endif /* !  HAS_3_0_API */
 
 int get_ec_key_sz(ErlNifEnv* env,
                   ERL_NIF_TERM curve, ERL_NIF_TERM priv, ERL_NIF_TERM pub,
@@ -707,7 +855,6 @@ int get_ec_key_sz(ErlNifEnv* env,
     return 1;
 }
 
-
 ERL_NIF_TERM ec_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
 { /* (Curve, PrivKey)  */
     EC_KEY *key = NULL;
@@ -747,6 +894,9 @@ ERL_NIF_TERM ec_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
         EC_KEY_free(key);
     return ret;
 }
+
+# endif /* ! HAS_3_0_API */
+
 #endif /* HAVE_EC */
 
 
diff --git a/lib/crypto/c_src/ec.h b/lib/crypto/c_src/ec.h
index 3b010f0c42..e53986d64e 100644
--- a/lib/crypto/c_src/ec.h
+++ b/lib/crypto/c_src/ec.h
@@ -31,13 +31,15 @@ int get_curve_definition(ErlNifEnv* env, ERL_NIF_TERM *ret, ERL_NIF_TERM def,
                          size_t *order_size);
 # endif /* HAS_3_0_API */
 
+# if ! defined(HAS_3_0_API)
+int get_ec_key_sz(ErlNifEnv* env, ERL_NIF_TERM curve, ERL_NIF_TERM priv, ERL_NIF_TERM pub,
+                  EC_KEY** res, size_t* size);
+# endif
+
 int get_ec_public_key(ErlNifEnv* env, ERL_NIF_TERM key, EVP_PKEY **pkey);
 int get_ec_private_key(ErlNifEnv* env, ERL_NIF_TERM key, EVP_PKEY **pkey);
 
-int get_ec_key_sz(ErlNifEnv* env, ERL_NIF_TERM curve, ERL_NIF_TERM priv, ERL_NIF_TERM pub,
-                  EC_KEY** res, size_t* size);
 int term2point(ErlNifEnv* env, ERL_NIF_TERM term, EC_GROUP *group, EC_POINT **pptr);
-ERL_NIF_TERM make_badarg_maybe(ErlNifEnv* env);
 #endif
 
 ERL_NIF_TERM ec_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
-- 
2.35.3

openSUSE Build Service is sponsored by