openssh/openssh-9.0p1-evp-fips-dh.patch
Norbert Pocs b63272d9eb Make the sign, dh, ecdh processes FIPS compliant
FIPS compliancy can be stated by using only compliant crypto
functions. This is achieved by using EVP API from openssl 3.0
version. The solution uses a non-intrusive approach - instead
of rewriting everything to use EVP API it converts the data
to it at the critical places.

Signed-off-by: Norbert Pocs <npocs@redhat.com>
2023-04-13 19:12:46 +02:00

267 lines
8.1 KiB
Diff

diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x config.status -x configure~ -x configure.ac ../../openssh-8.7p1/dh.c ./dh.c
--- ../../openssh-8.7p1/dh.c 2023-03-01 14:26:52.504445780 +0100
+++ ./dh.c 2023-03-01 14:20:09.823193384 +0100
@@ -37,6 +37,9 @@
#include <openssl/bn.h>
#include <openssl/dh.h>
#include <openssl/fips.h>
+#include <openssl/evp.h>
+#include <openssl/core_names.h>
+#include <openssl/param_build.h>
#include "dh.h"
#include "pathnames.h"
@@ -289,10 +292,15 @@
int
dh_gen_key(DH *dh, int need)
{
- int pbits;
- const BIGNUM *dh_p, *pub_key;
+ const BIGNUM *dh_p, *dh_g;
+ BIGNUM *pub_key = NULL, *priv_key = NULL;
+ EVP_PKEY *pkey = NULL;
+ EVP_PKEY_CTX *ctx = NULL;
+ OSSL_PARAM_BLD *param_bld = NULL;
+ OSSL_PARAM *params = NULL;
+ int pbits, r = 0;
- DH_get0_pqg(dh, &dh_p, NULL, NULL);
+ DH_get0_pqg(dh, &dh_p, NULL, &dh_g);
if (need < 0 || dh_p == NULL ||
(pbits = BN_num_bits(dh_p)) <= 0 ||
@@ -300,19 +308,85 @@
return SSH_ERR_INVALID_ARGUMENT;
if (need < 256)
need = 256;
+
+ if ((param_bld = OSSL_PARAM_BLD_new()) == NULL ||
+ (ctx = EVP_PKEY_CTX_new_from_name(NULL, "DH", NULL)) == NULL) {
+ OSSL_PARAM_BLD_free(param_bld);
+ return SSH_ERR_ALLOC_FAIL;
+ }
+
+ if (OSSL_PARAM_BLD_push_BN(param_bld,
+ OSSL_PKEY_PARAM_FFC_P, dh_p) != 1 ||
+ OSSL_PARAM_BLD_push_BN(param_bld,
+ OSSL_PKEY_PARAM_FFC_G, dh_g) != 1) {
+ error_f("Could not set p,q,g parameters");
+ r = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
/*
* Pollard Rho, Big step/Little Step attacks are O(sqrt(n)),
* so double requested need here.
*/
- if (!DH_set_length(dh, MINIMUM(need * 2, pbits - 1)))
- return SSH_ERR_LIBCRYPTO_ERROR;
-
- if (DH_generate_key(dh) == 0)
- return SSH_ERR_LIBCRYPTO_ERROR;
- DH_get0_key(dh, &pub_key, NULL);
- if (!dh_pub_is_valid(dh, pub_key))
- return SSH_ERR_INVALID_FORMAT;
- return 0;
+ if (OSSL_PARAM_BLD_push_int(param_bld,
+ OSSL_PKEY_PARAM_DH_PRIV_LEN,
+ MINIMUM(need * 2, pbits - 1)) != 1 ||
+ (params = OSSL_PARAM_BLD_to_param(param_bld)) == NULL) {
+ r = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ if (EVP_PKEY_fromdata_init(ctx) != 1) {
+ r = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ if (EVP_PKEY_fromdata(ctx, &pkey,
+ EVP_PKEY_KEY_PARAMETERS, params) != 1) {
+ error_f("Failed key generation");
+ r = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+
+ /* reuse context for key generation */
+ EVP_PKEY_CTX_free(ctx);
+ ctx = NULL;
+
+ if ((ctx = EVP_PKEY_CTX_new_from_pkey(NULL, pkey, NULL)) == NULL ||
+ EVP_PKEY_keygen_init(ctx) != 1) {
+ error_f("Could not create or init context");
+ r = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ if (EVP_PKEY_generate(ctx, &pkey) != 1) {
+ error_f("Could not generate keys");
+ r = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ if (EVP_PKEY_public_check(ctx) != 1) {
+ error_f("The public key is incorrect");
+ r = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+
+ if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PUB_KEY,
+ &pub_key) != 1 ||
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY,
+ &priv_key) != 1 ||
+ DH_set0_key(dh, pub_key, priv_key) != 1) {
+ error_f("Could not set pub/priv keys to DH struct");
+ r = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+
+ /* transferred */
+ pub_key = NULL;
+ priv_key = NULL;
+out:
+ OSSL_PARAM_free(params);
+ OSSL_PARAM_BLD_free(param_bld);
+ EVP_PKEY_CTX_free(ctx);
+ EVP_PKEY_free(pkey);
+ BN_clear_free(pub_key);
+ BN_clear_free(priv_key);
+ return r;
}
DH *
diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x config.status -x configure~ -x configure.ac ../../openssh-8.7p1/kex.c ./kex.c
--- ../../openssh-8.7p1/kex.c 2023-03-01 14:26:52.508445832 +0100
+++ ./kex.c 2023-02-28 14:09:27.164743771 +0100
@@ -1602,3 +1602,47 @@
return r;
}
+#ifdef WITH_OPENSSL
+/*
+ * Creates an EVP_PKEY from the given parameters and keys.
+ * The private key can be omitted.
+ */
+int
+kex_create_evp_dh(EVP_PKEY **pkey, const BIGNUM *p, const BIGNUM *q,
+ const BIGNUM *g, const BIGNUM *pub, const BIGNUM *priv)
+{
+ OSSL_PARAM_BLD *param_bld = NULL;
+ EVP_PKEY_CTX *ctx = NULL;
+ int r = 0;
+
+ /* create EVP_PKEY-DH key */
+ if ((ctx = EVP_PKEY_CTX_new_from_name(NULL, "DH", NULL)) == NULL ||
+ (param_bld = OSSL_PARAM_BLD_new()) == NULL) {
+ error_f("EVP_PKEY_CTX or PARAM_BLD init failed");
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if (OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_P, p) != 1 ||
+ OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_Q, q) != 1 ||
+ OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_G, g) != 1 ||
+ OSSL_PARAM_BLD_push_BN(param_bld,
+ OSSL_PKEY_PARAM_PUB_KEY, pub) != 1) {
+ error_f("Failed pushing params to OSSL_PARAM_BLD");
+ r = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ if (priv != NULL &&
+ OSSL_PARAM_BLD_push_BN(param_bld,
+ OSSL_PKEY_PARAM_PRIV_KEY, priv) != 1) {
+ error_f("Failed pushing private key to OSSL_PARAM_BLD");
+ r = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ if ((*pkey = sshkey_create_evp(param_bld, ctx)) == NULL)
+ r = SSH_ERR_LIBCRYPTO_ERROR;
+out:
+ OSSL_PARAM_BLD_free(param_bld);
+ EVP_PKEY_CTX_free(ctx);
+ return r;
+}
+#endif /* WITH_OPENSSL */
diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x config.status -x configure~ -x configure.ac ../../openssh-8.7p1/kexdh.c ./kexdh.c
--- ../../openssh-8.7p1/kexdh.c 2023-03-01 14:26:52.448445050 +0100
+++ ./kexdh.c 2023-02-28 14:05:00.700902124 +0100
@@ -35,6 +35,9 @@
#include "openbsd-compat/openssl-compat.h"
#include <openssl/dh.h>
+#include <openssl/evp.h>
+#include <openssl/core_names.h>
+#include <openssl/param_build.h>
#include "sshkey.h"
#include "kex.h"
@@ -83,6 +86,9 @@
kex_dh_compute_key(struct kex *kex, BIGNUM *dh_pub, struct sshbuf *out)
{
BIGNUM *shared_secret = NULL;
+ const BIGNUM *pub, *priv, *p, *q, *g;
+ EVP_PKEY *pkey = NULL, *dh_pkey = NULL;
+ EVP_PKEY_CTX *ctx = NULL;
u_char *kbuf = NULL;
size_t klen = 0;
int kout, r;
@@ -106,18 +112,39 @@
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
- if ((kout = DH_compute_key(kbuf, dh_pub, kex->dh)) < 0 ||
- BN_bin2bn(kbuf, kout, shared_secret) == NULL) {
+
+ DH_get0_key(kex->dh, &pub, &priv);
+ DH_get0_pqg(kex->dh, &p, &q, &g);
+ /* import key */
+ kex_create_evp_dh(&pkey, p, q, g, pub, priv);
+ /* import peer key
+ * the parameters should be the same as with pkey
+ */
+ kex_create_evp_dh(&dh_pkey, p, q, g, dh_pub, NULL);
+
+ if ((ctx = EVP_PKEY_CTX_new_from_pkey(NULL, pkey, NULL)) == NULL) {
+ error_f("Could not init EVP_PKEY_CTX for dh");
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if (EVP_PKEY_derive_init(ctx) != 1 ||
+ EVP_PKEY_derive_set_peer(ctx, dh_pkey) != 1 ||
+ EVP_PKEY_derive(ctx, kbuf, &klen) != 1 ||
+ BN_bin2bn(kbuf, klen, shared_secret) == NULL) {
+ error_f("Could not derive key");
r = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
#ifdef DEBUG_KEXDH
- dump_digest("shared secret", kbuf, kout);
+ dump_digest("shared secret", kbuf, klen);
#endif
r = sshbuf_put_bignum2(out, shared_secret);
out:
freezero(kbuf, klen);
BN_clear_free(shared_secret);
+ EVP_PKEY_free(pkey);
+ EVP_PKEY_free(dh_pkey);
+ EVP_PKEY_CTX_free(ctx);
return r;
}
diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x config.status -x configure~ -x configure.ac ../../openssh-8.7p1/kex.h ./kex.h
--- ../../openssh-8.7p1/kex.h 2023-03-01 14:26:52.508445832 +0100
+++ ./kex.h 2023-02-28 13:16:49.811047554 +0100
@@ -33,6 +33,9 @@
# include <openssl/bn.h>
# include <openssl/dh.h>
# include <openssl/ecdsa.h>
+# include <openssl/evp.h>
+# include <openssl/core_names.h>
+# include <openssl/param_build.h>
# ifdef OPENSSL_HAS_ECC
# include <openssl/ec.h>
# else /* OPENSSL_HAS_ECC */
@@ -278,6 +281,8 @@
const u_char pub[CURVE25519_SIZE], struct sshbuf *out, int)
__attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE)))
__attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE)));
+int kex_create_evp_dh(EVP_PKEY **, const BIGNUM *, const BIGNUM *,
+ const BIGNUM *, const BIGNUM *, const BIGNUM *);
#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
void dump_digest(const char *, const u_char *, int);