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], ¶ms[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, ¶ms[i++]) ) {
+ ret = EXCP_BADARG_N(env, 1, "Bad value of 'p'");
+ goto done;
+ }
+
+ if (!get_ossl_param_from_bin_in_list(env, "g", &head, ¶ms[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], ¶ms[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, ¶ms[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, ¶ms[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], ¶ms[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