erlang/otp-0013-crypto-Fix-generate_key-3-for-ecdh-to-only-use-OpenS.patch
Peter Lemenkov 9aea5ce05a Reenable OpenSSL 3
Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>
2023-08-14 20:15:10 +02:00

216 lines
8.6 KiB
Diff

From: Sverker Eriksson <sverker@erlang.org>
Date: Wed, 7 Jun 2023 18:52:46 +0200
Subject: [PATCH] crypto: Fix generate_key/3 for ecdh to only use OpenSSL 3.0
API
to prepare for using FIPS on OpenSSL 3.0
diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in
index 2345970fa9..28f7b595e8 100644
--- a/lib/crypto/c_src/Makefile.in
+++ b/lib/crypto/c_src/Makefile.in
@@ -205,9 +205,9 @@ $(LIBDIR)/otp_test_engine$(TYPEMARKER).dll: $(TEST_ENGINE_OBJS)
$(V_LD) $(LDFLAGS) -o $@ $(SSL_DED_LD_RUNTIME_LIBRARY_PATH) -L$(SSL_LIBDIR) $(TEST_ENGINE_OBJS) -l$(SSL_CRYPTO_LIBNAME) -l$(SSL_SSL_LIBNAME) $(SSL_EXTRA_LIBS)
endif
-$(OBJDIR)/ec$(TYPEMARKER).o: ec.c
- $(V_at)$(INSTALL_DIR) $(OBJDIR)
- $(V_CC) -c -o $@ $(ALL_CFLAGS) $(CRYPTO_NO_DEPRECATE_WARN) $<
+# $(OBJDIR)/ec$(TYPEMARKER).o: ec.c
+# $(V_at)$(INSTALL_DIR) $(OBJDIR)
+# $(V_CC) -c -o $@ $(ALL_CFLAGS) $(CRYPTO_NO_DEPRECATE_WARN) $<
$(OBJDIR)/%$(TYPEMARKER).o: %.c
$(V_at)$(INSTALL_DIR) $(OBJDIR)
diff --git a/lib/crypto/c_src/ec.c b/lib/crypto/c_src/ec.c
index 124582c4f8..852f3ba79c 100644
--- a/lib/crypto/c_src/ec.c
+++ b/lib/crypto/c_src/ec.c
@@ -24,6 +24,8 @@
#ifdef HAVE_EC
# if defined(HAS_3_0_API)
+# include <openssl/core_names.h>
+
int get_curve_definition(ErlNifEnv* env, ERL_NIF_TERM *ret, ERL_NIF_TERM def,
OSSL_PARAM params[], int *i,
size_t *order_size)
@@ -253,13 +255,7 @@ int get_ec_public_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,
+static int get_ec_private_key_2(ErlNifEnv* env,
ERL_NIF_TERM curve, ERL_NIF_TERM key,
EVP_PKEY **pkey,
ERL_NIF_TERM *ret,
@@ -319,7 +315,8 @@ int get_ec_private_key(ErlNifEnv* env, ERL_NIF_TERM key, EVP_PKEY **pkey)
return 0;
}
-int mk_pub_key_binary(ErlNifEnv* env, EVP_PKEY **peer_pkey, ErlNifBinary *pubkey_bin, ERL_NIF_TERM *ret);
+static int mk_pub_key_binary(ErlNifEnv* env, EVP_PKEY *peer_pkey,
+ ErlNifBinary *pubkey_bin, ERL_NIF_TERM *ret);
ERL_NIF_TERM ec_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{ /* (Curve, PrivKey|undefined) */
@@ -339,9 +336,8 @@ ERL_NIF_TERM ec_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
/* 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))
+ if (!mk_pub_key_binary(env, peer_pkey, &pubkey_bin, &ret))
goto err;
if (!EVP_PKEY_get_bn_param(peer_pkey, "priv", &priv_bn))
@@ -398,67 +394,81 @@ ERL_NIF_TERM ec_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
return ret;
}
-int mk_pub_key_binary(ErlNifEnv* env, EVP_PKEY **peer_pkey, ErlNifBinary *pubkey_bin, ERL_NIF_TERM *ret)
+static int mk_pub_key_binary(ErlNifEnv* env, EVP_PKEY *peer_pkey,
+ ErlNifBinary *pubkey_bin, ERL_NIF_TERM *ret)
{
- 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)
+ size_t pub_key_size = 0;
+ size_t group_name_size = 0;
+ char group_name_buf[20];
+ char* group_name = group_name_buf;
+ int group_nid;
+ EC_GROUP* ec_group = NULL;
+ EC_POINT* pub_key = NULL;
+ BIGNUM* priv_bn = NULL;
+ int ok = 0;
+
+ /* This code was inspired by
+ * https://github.com/openssl/openssl/issues/18437
+ * which first tried to get public key directly with
+ * EVP_PKEY_get_octet_string_param(peer_pkey, OSSL_PKEY_PARAM_PUB_KEY,..)
+ *
+ * I removed that since I don't know what key format that will produce
+ * if it succeeds. That is, we go directly to the "fallback" and calculate
+ * the public key.
+ */
+
+ if (!EVP_PKEY_get_utf8_string_param(peer_pkey, OSSL_PKEY_PARAM_GROUP_NAME,
+ NULL, 0, &group_name_size))
+ assign_goto(*ret, err, EXCP_ERROR(env, "Couldn't get EC group name size"));
+
+ if (group_name_size >= sizeof(group_name_buf))
+ group_name = enif_alloc(group_name_size + 1);
+ if (!EVP_PKEY_get_utf8_string_param(peer_pkey, OSSL_PKEY_PARAM_GROUP_NAME,
+ group_name, group_name_size+1,
+ NULL))
+ assign_goto(*ret, err, EXCP_ERROR(env, "Couldn't get EC group name"));
+
+ group_nid = OBJ_sn2nid(group_name);
+ if (group_nid == NID_undef)
+ assign_goto(*ret, err, EXCP_ERROR(env, "Couldn't get EC group nid"));
+
+ ec_group = EC_GROUP_new_by_curve_name(group_nid);
+ if (ec_group == NULL)
assign_goto(*ret, err, EXCP_ERROR(env, "Couldn't get EC_GROUP"));
- if ((public_key = EC_POINT_new(group)) == NULL)
+ pub_key = EC_POINT_new(ec_group);
+ if (pub_key == 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))
+ if (!EVP_PKEY_get_bn_param(peer_pkey, OSSL_PKEY_PARAM_PRIV_KEY, &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))
+ if (!EC_POINT_mul(ec_group, pub_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"));
+ pub_key_size = EC_POINT_point2oct(ec_group, pub_key,
+ POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);
+ if (pub_key_size == 0)
+ assign_goto(*ret, err, EXCP_ERROR(env, "Couldn't get pub_key_size"));
+
+ enif_alloc_binary(pub_key_size, pubkey_bin);
+ if (!EC_POINT_point2oct(ec_group, pub_key, POINT_CONVERSION_UNCOMPRESSED,
+ pubkey_bin->data,
+ pubkey_bin->size, NULL)) {
+ enif_release_binary(pubkey_bin);
+ assign_goto(*ret, err, EXCP_ERROR(env, "Couldn't get pub key bytes"));
}
- err:
- if (public_key) EC_POINT_free(public_key);
- if (group) EC_GROUP_free(group);
+ *ret = enif_make_binary(env, pubkey_bin);
+ ok = 1;
+
+err:
+ if (group_name != group_name_buf) enif_free(group_name);
+ if (pub_key) EC_POINT_free(pub_key);
+ if (ec_group) EC_GROUP_free(ec_group);
if (priv_bn) BN_free(priv_bn);
- if (*ret == atom_undefined)
- return 1;
- else
- return 0;
+ return ok;
}
# endif /* HAS_3_0_API */
@@ -908,10 +918,8 @@ ERL_NIF_TERM ec_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
# endif /* ! HAS_3_0_API */
-#endif /* HAVE_EC */
-
+#else /* ifndef HAVE_EC */
-#if ! defined(HAVE_EC)
ERL_NIF_TERM ec_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{ /* (Curve, PrivKey) */
return EXCP_NOTSUP_N(env, 0, "EC not supported");