File 2581-crypto-3.0-and-dh.c.patch of Package erlang

From c2fbff1fcbfbe7600de4fce177cecb2237613fb3 Mon Sep 17 00:00:00 2001
From: Hans Nilsson <hans@erlang.org>
Date: Thu, 3 Mar 2022 15:57:29 +0100
Subject: [PATCH 1/2] crypto: 3.0 and dh.c

---
 lib/crypto/c_src/dh.c | 308 ++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 293 insertions(+), 15 deletions(-)

diff --git a/lib/crypto/c_src/dh.c b/lib/crypto/c_src/dh.c
index 8d3f53d06e..43b5c6c2aa 100644
--- a/lib/crypto/c_src/dh.c
+++ b/lib/crypto/c_src/dh.c
@@ -21,9 +21,287 @@
 #include "dh.h"
 #include "bn.h"
 
+
+#if !defined(HAVE_DH)
+ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+    return  enif_raise_exception(env, atom_notsup);
+}
+
+ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+    return  enif_raise_exception(env, atom_notsup);
+}
+
+#else /* HAVE_DH */
+
+
+# ifdef HAS_3_0_API
+
+/**** Begin debug-help functions ****/
+#  if 0
+ERL_NIF_TERM debug_put_pkey(ErlNifEnv* env, EVP_PKEY *pkey);
+
+#  define PUT1(env,bn,t)                                                \
+     if (bn) {if ((t = bin_from_bn(env, bn)) == atom_error) goto err;}   \
+     else t = atom_undefined
+
+ERL_NIF_TERM debug_put_pkey(ErlNifEnv* env, EVP_PKEY *pkey)
+{
+    const BIGNUM *p1 = NULL, *q1 = NULL, *g1 = NULL, *pub1 = NULL, *priv1 = NULL;
+    DH *dh = NULL;
+    ERL_NIF_TERM result[8];
+    int i = 0;
+
+    if (pkey) {
+        dh = EVP_PKEY_get1_DH(pkey);
+        DH_get0_pqg(dh, &p1, &q1, &g1);
+
+        pub1 = DH_get0_pub_key(dh);
+        priv1= DH_get0_priv_key(dh);
+
+        PUT1(env, p1, result[i++]);
+        PUT1(env, g1, result[i++]);
+        PUT1(env, q1, result[i++]);
+        PUT1(env, pub1, result[i++]);
+        PUT1(env, priv1, result[i++]);
+
+        return enif_make_list_from_array(env,result,i);
+    } else
+        return atom_false;
+
+ err:
+    return atom_error;
+}
+#  endif
+/**** End debug-help functions ****/
+
+
+/* Has 3_0 */
+ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (PrivKey|undefined, DHParams=[P,G], 0, Len|0) */
+    unsigned long len = 0;
+    int i = 0;
+    OSSL_PARAM params[8];
+    EVP_PKEY *pkey = NULL, *pkey_gen = NULL;
+    EVP_PKEY_CTX *pctx = NULL, *pctx_gen = NULL;
+    BIGNUM *pub_key_gen = NULL, *priv_key_gen = NULL;
+    unsigned char *pub_ptr, *prv_ptr;
+    int pub_len, prv_len;
+    ERL_NIF_TERM ret_pub, ret_prv, ret = atom_undefined;
+
+    /* Fetch parameters and assign them to params[] */
+    if (argv[0] != atom_undefined)
+        if (!get_ossl_param_from_bin(env, "priv",  argv[0], &params[i++]))  {
+            ret = EXCP_BADARG_N(env, 0, "PrivKeyIn");
+            goto done;
+        }
+
+    { /*argv[1] - the lists [P,G] */
+        ERL_NIF_TERM head, tail;
+
+        head = argv[1];
+        if (!get_ossl_param_from_bin_in_list(env, "p",  &head, &params[i++]) ) {
+            ret = EXCP_BADARG_N(env, 1, "Bad value of 'p'");
+            goto done;
+        }
+
+        if (!get_ossl_param_from_bin_in_list(env, "g",  &head, &params[i++]) ) {
+            ret = EXCP_BADARG_N(env, 1, "Bad value of 'g'");
+            goto done;
+        }
+
+        tail = head;
+        if (!enif_is_empty_list(env,tail)) {
+            ret = EXCP_BADARG_N(env, 1, "Not a two-element list");
+            goto done;
+        }
+    }
+
+    /* argv[2] is always hardcoded to 0 in crypto.erl. A left over from some older version?
+       Let's skip that one
+    */
+
+    /* argv[3] is the length of the private key that is to be generated */
+    if (!enif_get_ulong(env, argv[3], &len) ||
+        (len > LONG_MAX) ) {
+        ret = EXCP_BADARG_N(env, 3, "Bad value of length element");
+        goto done;
+    }
+    else if (len)
+        params[i++] = OSSL_PARAM_construct_uint64("priv_len", &len);
+
+    /* End of parameter fetching */
+    params[i++] = OSSL_PARAM_construct_end();
+
+    pctx = EVP_PKEY_CTX_new_from_name(NULL, "DH", NULL);
+
+    if (EVP_PKEY_fromdata_init(pctx) <= 0) {
+        ret = EXCP_ERROR(env, "Can't init fromdata");
+        goto done;
+    }
+    if (EVP_PKEY_fromdata(pctx, &pkey, EVP_PKEY_KEYPAIR, params) <= 0) {
+        ret = EXCP_ERROR(env, "Can't do fromdata");
+        goto done;
+    }
+
+    /* Generate a new pkey from the data in the old */
+    
+    pctx_gen = EVP_PKEY_CTX_new_from_pkey(NULL, pkey, NULL);
+        
+    if (!EVP_PKEY_keygen_init(pctx_gen)) {
+        ret = EXCP_ERROR(env, "Can't init DH generation");
+        goto done;
+    }
+
+    if (!EVP_PKEY_CTX_set_params(pctx_gen, params))  {
+        ret = EXCP_ERROR(env, "Can't set params");
+        goto done;
+    }
+
+    if (!EVP_PKEY_generate(pctx_gen, &pkey_gen)) {
+        ret = EXCP_ERROR(env, "Can't generate DH key pair");
+        goto done;
+    }
+    
+    /* Finally return the OTP representation of the keys: */
+
+    if (!EVP_PKEY_get_bn_param(pkey_gen, "pub",  &pub_key_gen)) {
+        ret = EXCP_ERROR(env, "Can't get public key");
+        goto done;
+    }
+    if ((pub_len = BN_num_bytes(pub_key_gen)) < 0 ||
+        (pub_ptr = enif_make_new_binary(env, (size_t)pub_len, &ret_pub)) == NULL ||
+        BN_bn2bin(pub_key_gen, pub_ptr) < 0)
+        {
+            ret = EXCP_ERROR(env, "Can't convert public key");
+            goto done;
+        }
+
+    if (!EVP_PKEY_get_bn_param(pkey_gen, "priv",  &priv_key_gen)) {
+        ret = EXCP_ERROR(env, "Can't get private key");
+        goto done;
+    }
+    if ((prv_len = BN_num_bytes(priv_key_gen)) < 0 ||
+        (prv_ptr = enif_make_new_binary(env, (size_t)prv_len, &ret_prv)) == NULL ||
+        BN_bn2bin(priv_key_gen, prv_ptr) < 0)
+        {
+            ret = EXCP_ERROR(env, "Can't convert private key");
+            goto done;
+        }
+
+    ERL_VALGRIND_MAKE_MEM_DEFINED(pub_ptr, pub_len);
+    ERL_VALGRIND_MAKE_MEM_DEFINED(prv_ptr, prv_len);
+
+    ret = enif_make_tuple2(env, ret_pub, ret_prv);
+
+ done:
+    if (pub_key_gen)  BN_free(pub_key_gen);
+    if (priv_key_gen) BN_free(priv_key_gen);
+    if (pkey) EVP_PKEY_free(pkey);
+    if (pkey_gen) EVP_PKEY_free(pkey_gen);
+    if (pctx) EVP_PKEY_CTX_free(pctx);
+    if (pctx_gen) EVP_PKEY_CTX_free(pctx_gen);
+
+    return ret;
+}
+
+/* Has 3_0 */
+ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (OthersPublicKey, MyPrivateKey, DHParams=[P,G]) */
+    ERL_NIF_TERM ret;
+    ErlNifBinary ret_bin;
+    size_t sz;
+    int ret_bin_alloc = 0;
+    int i = 0;
+    OSSL_PARAM params[5];
+    EVP_PKEY_CTX *own_pctx = NULL, *peer_pctx = NULL, *pctx_gen = NULL;
+    EVP_PKEY *own_pkey = NULL, *peer_pkey = NULL;
+
+    /* Fetch parameters */
+
+    /* Build peer_pkey */
+    
+    if (!get_ossl_param_from_bin(env, "pub",  argv[0], &params[i++]))
+        assign_goto(ret, err, EXCP_BADARG_N(env, 0, "Bad peer public key; binary expected"));
+
+    { /*argv[2] - the lists [P,G] */
+        ERL_NIF_TERM head, tail;
+
+        head = argv[2];
+        if (!get_ossl_param_from_bin_in_list(env, "p",  &head, &params[i++]))
+            assign_goto(ret, err, EXCP_BADARG_N(env, 2, "Bad value of 'p'"));
+
+        if (!get_ossl_param_from_bin_in_list(env, "g",  &head, &params[i++]))
+            assign_goto(ret, err, EXCP_BADARG_N(env, 2, "Bad value of 'g'"));
+
+        tail = head;
+        if (!enif_is_empty_list(env,tail))
+            assign_goto(ret, err, EXCP_BADARG_N(env, 2, "Not a two-element list"));
+    }
+
+    params[i++] = OSSL_PARAM_construct_end();
+
+    peer_pctx = EVP_PKEY_CTX_new_from_name(NULL, "DH", NULL);
+
+    if (EVP_PKEY_fromdata_init(peer_pctx) <= 0)
+        assign_goto(ret, err, EXCP_ERROR(env, "Can't init fromdata"));
+    if (EVP_PKEY_fromdata(peer_pctx, &peer_pkey, EVP_PKEY_KEYPAIR, params) <= 0)
+        assign_goto(ret, err, EXCP_ERROR(env, "Can't do fromdata"));
+
+    /* Build own_pkey. Just replace the pub key with the priv key in params */
+    if (!get_ossl_param_from_bin(env, "priv",  argv[1], &params[0]))
+        assign_goto(ret, err, EXCP_BADARG_N(env, 0, "Bad peer public key; binary expected"));
+
+    own_pctx = EVP_PKEY_CTX_new_from_name(NULL, "DH", NULL);
+
+    if (EVP_PKEY_fromdata_init(own_pctx) <= 0)
+        assign_goto(ret, err, EXCP_ERROR(env, "Can't init fromdata"));
+
+    if (EVP_PKEY_fromdata(own_pctx, &own_pkey, EVP_PKEY_KEYPAIR, params) <= 0)
+        assign_goto(ret, err, EXCP_ERROR(env, "Can't do fromdata"));
+
+
+    /* Derive the common secret */
+    pctx_gen = EVP_PKEY_CTX_new(own_pkey, NULL);
+    
+    if (!EVP_PKEY_derive_init(pctx_gen))
+        assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_derive_init"));
+
+    if (!EVP_PKEY_derive_set_peer(pctx_gen, peer_pkey))
+        assign_goto(ret, err, EXCP_ERROR(env, "Can't derive secret or set peer"));
+    
+    if (!EVP_PKEY_derive(pctx_gen, NULL, &sz))
+        assign_goto(ret, err, EXCP_ERROR(env, "Can't get result size"));
+
+    if (!enif_alloc_binary(sz, &ret_bin))
+        assign_goto(ret, err, EXCP_ERROR(env, "Can't allcate binary"));
+    ret_bin_alloc = 1;
+
+    if (!EVP_PKEY_derive(pctx_gen, ret_bin.data, &ret_bin.size))
+        assign_goto(ret, err, EXCP_ERROR(env, "Can't get result"));
+
+    if (sz != ret_bin.size)
+        if (!enif_realloc_binary(&ret_bin, ret_bin.size))
+            assign_goto(ret, err, EXCP_ERROR(env, "Can't realloc binary"));
+    
+    ret = enif_make_binary(env, &ret_bin);
+    ret_bin_alloc = 0;
+
+ err:
+    if (ret_bin_alloc)  enif_release_binary(&ret_bin);
+    if (peer_pctx) EVP_PKEY_CTX_free(peer_pctx);
+    if (peer_pkey) EVP_PKEY_free(peer_pkey);
+    if (own_pctx) EVP_PKEY_CTX_free(own_pctx);
+    if (own_pkey) EVP_PKEY_free(own_pkey);
+    if (pctx_gen) EVP_PKEY_CTX_free(pctx_gen);
+    return ret;
+}
+
+# else
+/* Has not 3.0 */
 ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
 {/* (PrivKey|undefined, DHParams=[P,G], Mpint, Len|0) */
-#ifdef HAVE_DH
     DH *dh_params = NULL;
     unsigned int mpint; /* 0 or 4 */
     ERL_NIF_TERM head, tail;
@@ -36,10 +314,10 @@ ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
     int pub_len, prv_len;
     ERL_NIF_TERM ret_pub, ret_prv, ret;
     const BIGNUM *pub_key_gen, *priv_key_gen;
-#if defined(HAS_EVP_PKEY_CTX) && (! DISABLE_EVP_DH)
+#  if defined(HAS_EVP_PKEY_CTX) && (! DISABLE_EVP_DH)
     EVP_PKEY_CTX *ctx = NULL;
     EVP_PKEY *dhkey = NULL, *params = NULL;
-#endif
+#  endif
 
     ASSERT(argc == 4);
 
@@ -100,7 +378,7 @@ ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
             assign_goto(ret, err, EXCP_ERROR_N(env, 3, "The length is not accepted"));
     }
 
-#if defined(HAS_EVP_PKEY_CTX) && (! DISABLE_EVP_DH)
+#  if defined(HAS_EVP_PKEY_CTX) && (! DISABLE_EVP_DH)
     if ((params = EVP_PKEY_new()) == NULL)
         assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_new"));
 
@@ -125,10 +403,10 @@ ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
     if ((dh_params = EVP_PKEY_get1_DH(dhkey)) == NULL)
         assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_get1_DH"));
 
-#else
+#  else
     if (!DH_generate_key(dh_params))
         assign_goto(ret, err, EXCP_ERROR(env, "Can't DH_generate_key"));
-#endif
+#  endif
 
     DH_get0_key(dh_params, &pub_key_gen, &priv_key_gen);
 
@@ -172,24 +450,21 @@ ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
     if (dh_params)
         DH_free(dh_params);
 
-#if defined(HAS_EVP_PKEY_CTX) && (! DISABLE_EVP_DH)
+#  if defined(HAS_EVP_PKEY_CTX) && (! DISABLE_EVP_DH)
     if (ctx)
         EVP_PKEY_CTX_free(ctx);
     if (dhkey)
         EVP_PKEY_free(dhkey);
     if (params)
         EVP_PKEY_free(params);
-#endif
+#  endif
 
     return ret;
-#else
-    return enif_raise_exception(env, atom_notsup);
-#endif
 }
 
+
 ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
 {/* (OthersPublicKey, MyPrivateKey, DHParams=[P,G]) */
-#ifdef HAVE_DH
     BIGNUM *other_pub_key = NULL;
     BIGNUM *dh_p = NULL;
     BIGNUM *dh_g = NULL;
@@ -287,7 +562,10 @@ ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
         DH_free(dh_priv);
 
     return ret;
-#else
-    return enif_raise_exception(env, atom_notsup);
-#endif
 }
+
+# endif /* else part of HAS_3_0_API  */
+
+
+
+#endif /* HAVE_DH */
-- 
2.34.1

openSUSE Build Service is sponsored by