File 3291-crypto-Accept-and-adjust-too-large-DH-priv-key-lengt.patch of Package erlang
From 5746c1e720b337e1ab344708d4fa101a27bb990e Mon Sep 17 00:00:00 2001
From: Sverker Eriksson <sverker@erlang.org>
Date: Thu, 20 Nov 2025 15:49:54 +0100
Subject: [PATCH] crypto: Accept and adjust too large DH priv key lengths
crypto:generate_key(dh, [P, G, MaxPrivateKeyBitLength])
will now allow MaxPrivateKeyBitLength >= bit_length(P)
and adjust it to bit_length(P) - 1.
Also renamed it from PrivateKeyBitLength to MaxPrivateKeyBitLength
to make clear that it is a max limit.
---
lib/crypto/c_src/bn.c | 21 ++++++++++++++-------
lib/crypto/c_src/bn.h | 12 +++++++++---
lib/crypto/c_src/dh.c | 18 ++++++++++++------
lib/crypto/src/crypto.erl | 6 +++---
4 files changed, 38 insertions(+), 19 deletions(-)
diff --git a/lib/crypto/c_src/bn.c b/lib/crypto/c_src/bn.c
index c7bdb1c5c7..e02f7a312a 100644
--- a/lib/crypto/c_src/bn.c
+++ b/lib/crypto/c_src/bn.c
@@ -209,13 +209,14 @@ int get_ossl_octet_string_param_from_bin(ErlNifEnv* env, const char* key, ERL_NI
}
-int get_ossl_BN_param_from_bin(ErlNifEnv* env, char* key, ERL_NIF_TERM bin, OSSL_PARAM *dest)
+int get_ossl_BN_param_from_bin_x(ErlNifEnv* env, char* key, ERL_NIF_TERM bin,
+ OSSL_PARAM *dest, BIGNUM** bn_out)
{
- return get_ossl_BN_param_from_bin_sz(env, key, bin, dest, NULL);
+ return get_ossl_BN_param_from_bin_sz_x(env, key, bin, dest, NULL, bn_out);
}
-int get_ossl_BN_param_from_bin_sz(ErlNifEnv* env, char* key, ERL_NIF_TERM bin,
- OSSL_PARAM *dest, size_t *size)
+int get_ossl_BN_param_from_bin_sz_x(ErlNifEnv* env, char* key, ERL_NIF_TERM bin,
+ OSSL_PARAM *dest, size_t *size, BIGNUM** bn_out)
{
BIGNUM *bn = NULL;
int ok = 0;
@@ -224,7 +225,11 @@ int get_ossl_BN_param_from_bin_sz(ErlNifEnv* env, char* key, ERL_NIF_TERM bin,
return 0;
ok = get_ossl_BN_param_from_bn(env, key, bn, dest);
- BN_free(bn);
+ if (ok && bn_out) {
+ *bn_out = bn;
+ } else {
+ BN_free(bn);
+ }
return ok;
}
@@ -246,13 +251,15 @@ int get_ossl_BN_param_from_bn(ErlNifEnv* env, char* key, const BIGNUM* bn,
-int get_ossl_param_from_bin_in_list(ErlNifEnv* env, char* key, ERL_NIF_TERM *listcell, OSSL_PARAM *dest)
+int get_ossl_param_from_bin_in_list_x(ErlNifEnv* env, char* key,
+ ERL_NIF_TERM *listcell, OSSL_PARAM *dest,
+ BIGNUM** bn_out)
{
ERL_NIF_TERM head;
return
enif_get_list_cell(env, *listcell, &head, listcell) &&
- get_ossl_BN_param_from_bin(env, key, head, dest);
+ get_ossl_BN_param_from_bin_x(env, key, head, dest, bn_out);
}
#endif
diff --git a/lib/crypto/c_src/bn.h b/lib/crypto/c_src/bn.h
index e0ad63ab76..0f326047f8 100644
--- a/lib/crypto/c_src/bn.h
+++ b/lib/crypto/c_src/bn.h
@@ -38,11 +38,17 @@ int get_bn_from_bin_sz(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp, size_t*
#ifdef HAS_3_0_API
int get_ossl_octet_string_param_from_bin(ErlNifEnv* env, const char* key, ERL_NIF_TERM bin, OSSL_PARAM *dest);
-int get_ossl_BN_param_from_bin(ErlNifEnv* env, char* key, ERL_NIF_TERM bin, OSSL_PARAM *dest);
-int get_ossl_BN_param_from_bin_sz(ErlNifEnv* env, char* key, ERL_NIF_TERM bin, OSSL_PARAM *dest, size_t *size);
+int get_ossl_BN_param_from_bin_x(ErlNifEnv* env, char* key, ERL_NIF_TERM bin, OSSL_PARAM *dest, BIGNUM** bn_out);
+#define get_ossl_BN_param_from_bin(ENV,KEY,BIN,DEST) get_ossl_BN_param_from_bin_x(ENV,KEY,BIN,DEST,NULL)
+
+int get_ossl_BN_param_from_bin_sz_x(ErlNifEnv* env, char* key, ERL_NIF_TERM bin, OSSL_PARAM *dest, size_t *size, BIGNUM** bn_out);
+#define get_ossl_BN_param_from_bin_sz(ENV,KEY,BIN,DEST,SIZE) get_ossl_BN_param_from_bin_sz_x(ENV,KEY,BIN,DEST,SIZE,NULL)
+
int get_ossl_BN_param_from_bn(ErlNifEnv* env, char* key, const BIGNUM* bn, OSSL_PARAM *dest);
-int get_ossl_param_from_bin_in_list(ErlNifEnv* env, char* key, ERL_NIF_TERM *listcell, OSSL_PARAM *dest);
+int get_ossl_param_from_bin_in_list_x(ErlNifEnv* env, char* key, ERL_NIF_TERM *listcell, OSSL_PARAM *dest, BIGNUM** bn_out);
+#define get_ossl_param_from_bin_in_list(ENV,KEY,CELL,DEST) get_ossl_param_from_bin_in_list_x(ENV,KEY,CELL,DEST,NULL)
+
#endif
#endif /* E_BN_H__ */
diff --git a/lib/crypto/c_src/dh.c b/lib/crypto/c_src/dh.c
index fb7ec9d83b..417bed16cb 100644
--- a/lib/crypto/c_src/dh.c
+++ b/lib/crypto/c_src/dh.c
@@ -48,6 +48,7 @@ ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
OSSL_PARAM params[8];
EVP_PKEY *pkey = NULL, *pkey_gen = NULL;
EVP_PKEY_CTX *pctx = NULL, *pctx_gen = NULL;
+ BIGNUM *p_bn = NULL;
BIGNUM *pub_key_gen = NULL, *priv_key_gen = NULL;
unsigned char *pub_ptr, *prv_ptr;
int pub_len, prv_len;
@@ -64,7 +65,7 @@ ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
ERL_NIF_TERM head, tail;
head = argv[1];
- if (!get_ossl_param_from_bin_in_list(env, "p", &head, ¶ms[i++]) ) {
+ if (!get_ossl_param_from_bin_in_list_x(env, "p", &head, ¶ms[i++], &p_bn) ) {
ret = EXCP_BADARG_N(env, 1, "Bad value of 'p'");
goto done;
}
@@ -91,9 +92,13 @@ ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
ret = EXCP_BADARG_N(env, 3, "Bad value of length element");
goto done;
}
- else if (len)
+ else if (len) {
+ if (len >= BN_num_bits(p_bn)) {
+ len = BN_num_bits(p_bn) - 1;
+ }
params[i++] = OSSL_PARAM_construct_uint64("priv_len", &len);
-
+ }
+
/* End of parameter fetching */
params[i++] = OSSL_PARAM_construct_end();
@@ -159,6 +164,7 @@ ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
ret = enif_make_tuple2(env, ret_pub, ret_prv);
done:
+ if (p_bn) BN_free(p_bn);
if (pub_key_gen) BN_free(pub_key_gen);
if (priv_key_gen) BN_free(priv_key_gen);
if (pkey) EVP_PKEY_free(pkey);
@@ -334,9 +340,9 @@ ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
if ((bn_len = BN_num_bits(dh_p_shared)) < 0)
assign_goto(ret, err, EXCP_ERROR(env, "BN_num_bits < 0"));
dh_p_shared = NULL; /* dh_params owns the reference */
- if (len >= (size_t)bn_len)
- assign_goto(ret, err, EXCP_ERROR_N(env, 3, "Too big length"));
-
+ if (len >= (size_t)bn_len) {
+ len = bn_len - 1;
+ }
if (!DH_set_length(dh_params, (long)len))
assign_goto(ret, err, EXCP_ERROR_N(env, 3, "The length is not accepted"));
}
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index de1f20c0fb..8d2931abbc 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -452,11 +452,11 @@ Scrambler is `u` (optional will be generated if not provided) from
-type dh_private() :: key_integer() .
-doc """
```text
-dh_params() = [P, G] | [P, G, PrivateKeyBitLength]
+dh_params() = [P, G] | [P, G, MaxPrivateKeyBitLength]
```
""".
-doc(#{title => <<"Diffie-Hellman Keys and parameters">>}).
--type dh_params() :: [key_integer()] . % [P, G] | [P, G, PrivateKeyBitLength]
+-type dh_params() :: [key_integer()] . % [P, G] | [P, G, MaxPrivateKeyBitLength]
-doc(#{title => <<"Diffie-Hellman Keys and parameters">>,
equiv => ecdh_params()}).
@@ -2829,7 +2829,7 @@ Uses the [3-tuple style](`m:crypto#error_3tup`) for error handling.
> - and the `Type` is `dh` (diffie-hellman)
> - and the parameter `P` (in `t:dh_params/0`) is one of the MODP groups (see
> [RFC 3526](https://tools.ietf.org/html/rfc3526))
-> - and the optional `PrivateKeyBitLength` parameter (in `t:dh_params/0`) is
+> - and the optional `MaxPrivateKeyBitLength` parameter (in `t:dh_params/0`) is
> present,
>
> then the optional key length parameter must be at least 224, 256, 302, 352 and
--
2.51.0