From b63272d9eb8efef881524b2fed0d658780b197a7 Mon Sep 17 00:00:00 2001 From: Norbert Pocs Date: Apr 13 2023 17:12:46 +0000 Subject: 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 --- diff --git a/openssh-9.0p1-evp-fips-dh.patch b/openssh-9.0p1-evp-fips-dh.patch new file mode 100644 index 0000000..7494245 --- /dev/null +++ b/openssh-9.0p1-evp-fips-dh.patch @@ -0,0 +1,266 @@ +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 + #include + #include ++#include ++#include ++#include + + #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 ++#include ++#include ++#include + + #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 + # include + # include ++# include ++# include ++# include + # ifdef OPENSSL_HAS_ECC + # include + # 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); diff --git a/openssh-9.0p1-evp-fips-ecdh.patch b/openssh-9.0p1-evp-fips-ecdh.patch new file mode 100644 index 0000000..0313c6f --- /dev/null +++ b/openssh-9.0p1-evp-fips-ecdh.patch @@ -0,0 +1,207 @@ +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/kexecdh.c ./kexecdh.c +--- ../openssh-8.7p1/kexecdh.c 2021-08-20 06:03:49.000000000 +0200 ++++ ./kexecdh.c 2023-04-13 14:30:14.882449593 +0200 +@@ -35,17 +35,57 @@ + #include + + #include ++#include ++#include ++#include ++#include + + #include "sshkey.h" + #include "kex.h" + #include "sshbuf.h" + #include "digest.h" + #include "ssherr.h" ++#include "log.h" + + static int + kex_ecdh_dec_key_group(struct kex *, const struct sshbuf *, EC_KEY *key, + const EC_GROUP *, struct sshbuf **); + ++static EC_KEY * ++generate_ec_keys(int ec_nid) ++{ ++ EC_KEY *client_key = NULL; ++ EVP_PKEY *pkey = NULL; ++ EVP_PKEY_CTX *ctx = NULL; ++ OSSL_PARAM_BLD *param_bld = NULL; ++ OSSL_PARAM *params = NULL; ++ const char *group_name; ++ ++ if ((ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL)) == NULL || ++ (param_bld = OSSL_PARAM_BLD_new()) == NULL) ++ goto out; ++ if ((group_name = OSSL_EC_curve_nid2name(ec_nid)) == NULL || ++ OSSL_PARAM_BLD_push_utf8_string(param_bld, ++ OSSL_PKEY_PARAM_GROUP_NAME, group_name, 0) != 1 || ++ (params = OSSL_PARAM_BLD_to_param(param_bld)) == NULL) { ++ error_f("Could not create OSSL_PARAM"); ++ goto out; ++ } ++ if (EVP_PKEY_keygen_init(ctx) != 1 || ++ EVP_PKEY_CTX_set_params(ctx, params) != 1 || ++ EVP_PKEY_generate(ctx, &pkey) != 1 || ++ (client_key = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) { ++ error_f("Could not generate ec keys"); ++ goto out; ++ } ++out: ++ EVP_PKEY_free(pkey); ++ EVP_PKEY_CTX_free(ctx); ++ OSSL_PARAM_BLD_free(param_bld); ++ OSSL_PARAM_free(params); ++ return client_key; ++} ++ + int + kex_ecdh_keypair(struct kex *kex) + { +@@ -55,11 +95,7 @@ + struct sshbuf *buf = NULL; + int r; + +- if ((client_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) { +- r = SSH_ERR_ALLOC_FAIL; +- goto out; +- } +- if (EC_KEY_generate_key(client_key) != 1) { ++ if ((client_key = generate_ec_keys(kex->ec_nid)) == NULL) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } +@@ -101,11 +137,7 @@ + *server_blobp = NULL; + *shared_secretp = NULL; + +- if ((server_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) { +- r = SSH_ERR_ALLOC_FAIL; +- goto out; +- } +- if (EC_KEY_generate_key(server_key) != 1) { ++ if ((server_key = generate_ec_keys(kex->ec_nid)) == NULL) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } +@@ -140,11 +172,21 @@ + { + struct sshbuf *buf = NULL; + BIGNUM *shared_secret = NULL; +- EC_POINT *dh_pub = NULL; +- u_char *kbuf = NULL; +- size_t klen = 0; ++ EVP_PKEY_CTX *ctx = NULL; ++ EVP_PKEY *pkey = NULL, *dh_pkey = NULL; ++ OSSL_PARAM_BLD *param_bld = NULL; ++ OSSL_PARAM *params = NULL; ++ u_char *kbuf = NULL, *pub = NULL; ++ size_t klen = 0, publen; ++ const char *group_name; + int r; + ++ /* import EC_KEY to EVP_PKEY */ ++ if ((r = ssh_create_evp_ec(key, kex->ec_nid, &pkey)) != 0) { ++ error_f("Could not create EVP_PKEY"); ++ goto out; ++ } ++ + *shared_secretp = NULL; + + if ((buf = sshbuf_new()) == NULL) { +@@ -153,45 +195,82 @@ + } + if ((r = sshbuf_put_stringb(buf, ec_blob)) != 0) + goto out; +- if ((dh_pub = EC_POINT_new(group)) == NULL) { ++ ++ /* the public key is in the buffer in octet string UNCOMPRESSED ++ * format. See sshbuf_put_ec */ ++ if ((r = sshbuf_get_string(buf, &pub, &publen)) != 0) ++ goto out; ++ sshbuf_reset(buf); ++ if ((ctx = EVP_PKEY_CTX_new_from_pkey(NULL, pkey, NULL)) == NULL || ++ (param_bld = OSSL_PARAM_BLD_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } +- if ((r = sshbuf_get_ec(buf, dh_pub, group)) != 0) { ++ if ((group_name = OSSL_EC_curve_nid2name(kex->ec_nid)) == NULL) { ++ r = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ if (OSSL_PARAM_BLD_push_octet_string(param_bld, ++ OSSL_PKEY_PARAM_PUB_KEY, pub, publen) != 1 || ++ OSSL_PARAM_BLD_push_utf8_string(param_bld, ++ OSSL_PKEY_PARAM_GROUP_NAME, group_name, 0) != 1 || ++ (params = OSSL_PARAM_BLD_to_param(param_bld)) == NULL) { ++ error_f("Failed to set params for dh_pkey"); ++ r = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ if (EVP_PKEY_fromdata_init(ctx) != 1 || ++ EVP_PKEY_fromdata(ctx, &dh_pkey, ++ EVP_PKEY_PUBLIC_KEY, params) != 1 || ++ EVP_PKEY_public_check(ctx) != 1) { ++ error_f("Peer public key import failed"); ++ r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } +- sshbuf_reset(buf); + + #ifdef DEBUG_KEXECDH + fputs("public key:\n", stderr); +- sshkey_dump_ec_point(group, dh_pub); ++ EVP_PKEY_print_public_fp(stderr, dh_pkey, 0, NULL); + #endif +- if (sshkey_ec_validate_public(group, dh_pub) != 0) { +- r = SSH_ERR_MESSAGE_INCOMPLETE; ++ EVP_PKEY_CTX_free(ctx); ++ ctx = NULL; ++ if ((ctx = EVP_PKEY_CTX_new_from_pkey(NULL, pkey, NULL)) == NULL || ++ EVP_PKEY_derive_init(ctx) != 1 || ++ EVP_PKEY_derive_set_peer(ctx, dh_pkey) != 1 || ++ EVP_PKEY_derive(ctx, NULL, &klen) != 1) { ++ error_f("Failed to get derive information"); ++ r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } +- klen = (EC_GROUP_get_degree(group) + 7) / 8; +- if ((kbuf = malloc(klen)) == NULL || +- (shared_secret = BN_new()) == NULL) { ++ if ((kbuf = malloc(klen)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } +- if (ECDH_compute_key(kbuf, klen, dh_pub, key, NULL) != (int)klen || +- BN_bin2bn(kbuf, klen, shared_secret) == NULL) { ++ if (EVP_PKEY_derive(ctx, kbuf, &klen) != 1) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + #ifdef DEBUG_KEXECDH + dump_digest("shared secret", kbuf, klen); + #endif ++ if ((shared_secret = BN_new()) == NULL || ++ (BN_bin2bn(kbuf, klen, shared_secret) == NULL)) { ++ r = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } + if ((r = sshbuf_put_bignum2(buf, shared_secret)) != 0) + goto out; + *shared_secretp = buf; + buf = NULL; + out: +- EC_POINT_clear_free(dh_pub); ++ EVP_PKEY_CTX_free(ctx); ++ EVP_PKEY_free(pkey); ++ EVP_PKEY_free(dh_pkey); ++ OSSL_PARAM_BLD_free(param_bld); ++ OSSL_PARAM_free(params); + BN_clear_free(shared_secret); + freezero(kbuf, klen); ++ freezero(pub, publen); + sshbuf_free(buf); + return r; + } diff --git a/openssh-9.0p1-evp-fips-sign.patch b/openssh-9.0p1-evp-fips-sign.patch new file mode 100644 index 0000000..fc71678 --- /dev/null +++ b/openssh-9.0p1-evp-fips-sign.patch @@ -0,0 +1,468 @@ +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/ssh-dss.c ./ssh-dss.c +--- ../../openssh-8.7p1/ssh-dss.c 2023-03-08 15:35:14.669943335 +0100 ++++ ./ssh-dss.c 2023-03-08 15:34:33.508578129 +0100 +@@ -32,6 +32,8 @@ + #include + #include + #include ++#include ++#include + + #include + #include +@@ -72,9 +74,8 @@ + sshkey_type_plain(key->type) != KEY_DSA) + return SSH_ERR_INVALID_ARGUMENT; + +- if ((pkey = EVP_PKEY_new()) == NULL || +- EVP_PKEY_set1_DSA(pkey, key->dsa) != 1) +- return SSH_ERR_ALLOC_FAIL; ++ if ((ret = ssh_create_evp_dss(key, &pkey)) != 0) ++ return ret; + ret = sshkey_calculate_signature(pkey, SSH_DIGEST_SHA1, &sigb, &len, + data, datalen); + EVP_PKEY_free(pkey); +@@ -201,11 +202,8 @@ + goto out; + } + +- if ((pkey = EVP_PKEY_new()) == NULL || +- EVP_PKEY_set1_DSA(pkey, key->dsa) != 1) { +- ret = SSH_ERR_ALLOC_FAIL; ++ if ((ret = ssh_create_evp_dss(key, &pkey)) != 0) + goto out; +- } + ret = sshkey_verify_signature(pkey, SSH_DIGEST_SHA1, data, datalen, + sigb, slen); + EVP_PKEY_free(pkey); +@@ -221,4 +219,63 @@ + freezero(sigblob, len); + return ret; + } ++ ++int ++ssh_create_evp_dss(const struct sshkey *k, EVP_PKEY **pkey) ++{ ++ OSSL_PARAM_BLD *param_bld = NULL; ++ EVP_PKEY_CTX *ctx = NULL; ++ const BIGNUM *p = NULL, *q = NULL, *g = NULL, *pub = NULL, *priv = NULL; ++ int ret = 0; ++ ++ if (k == NULL) ++ return SSH_ERR_INVALID_ARGUMENT; ++ if ((ctx = EVP_PKEY_CTX_new_from_name(NULL, "DSA", NULL)) == NULL || ++ (param_bld = OSSL_PARAM_BLD_new()) == NULL) { ++ ret = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ ++ DSA_get0_pqg(k->dsa, &p, &q, &g); ++ DSA_get0_key(k->dsa, &pub, &priv); ++ ++ if (p != NULL && ++ OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_P, p) != 1) { ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ if (q != NULL && ++ OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_Q, q) != 1) { ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ if (g != NULL && ++ OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_G, g) != 1) { ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ if (pub != NULL && ++ OSSL_PARAM_BLD_push_BN(param_bld, ++ OSSL_PKEY_PARAM_PUB_KEY, ++ pub) != 1) { ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ if (priv != NULL && ++ OSSL_PARAM_BLD_push_BN(param_bld, ++ OSSL_PKEY_PARAM_PRIV_KEY, ++ priv) != 1) { ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ if ((*pkey = sshkey_create_evp(param_bld, ctx)) == NULL) { ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ ++out: ++ OSSL_PARAM_BLD_free(param_bld); ++ EVP_PKEY_CTX_free(ctx); ++ return ret; ++} + #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/ssh-ecdsa.c ./ssh-ecdsa.c +--- ../../openssh-8.7p1/ssh-ecdsa.c 2023-03-08 15:35:14.669943335 +0100 ++++ ./ssh-ecdsa.c 2023-03-08 15:40:52.628201267 +0100 +@@ -34,6 +34,8 @@ + #include + #include + #include ++#include ++#include + + #include + +@@ -72,9 +74,8 @@ + if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1) + return SSH_ERR_INTERNAL_ERROR; + +- if ((pkey = EVP_PKEY_new()) == NULL || +- EVP_PKEY_set1_EC_KEY(pkey, key->ecdsa) != 1) +- return SSH_ERR_ALLOC_FAIL; ++ if ((ret = ssh_create_evp_ec(key->ecdsa, key->ecdsa_nid, &pkey)) != 0) ++ return ret; + ret = sshkey_calculate_signature(pkey, hash_alg, &sigb, &len, data, + datalen); + EVP_PKEY_free(pkey); +@@ -193,11 +194,8 @@ + goto out; + } + +- if ((pkey = EVP_PKEY_new()) == NULL || +- EVP_PKEY_set1_EC_KEY(pkey, key->ecdsa) != 1) { +- ret = SSH_ERR_ALLOC_FAIL; ++ if (ssh_create_evp_ec(key->ecdsa, key->ecdsa_nid, &pkey) != 0) + goto out; +- } + ret = sshkey_verify_signature(pkey, hash_alg, data, datalen, sigb, len); + EVP_PKEY_free(pkey); + +@@ -212,4 +210,76 @@ + return ret; + } + ++int ++ssh_create_evp_ec(EC_KEY *k, int ecdsa_nid, EVP_PKEY **pkey) ++{ ++ OSSL_PARAM_BLD *param_bld = NULL; ++ EVP_PKEY_CTX *ctx = NULL; ++ BN_CTX *bn_ctx = NULL; ++ uint8_t *pub_ser = NULL; ++ const char *group_name; ++ const EC_POINT *pub = NULL; ++ const BIGNUM *priv = NULL; ++ int ret = 0; ++ ++ if (k == NULL) ++ return SSH_ERR_INVALID_ARGUMENT; ++ if ((ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL)) == NULL || ++ (param_bld = OSSL_PARAM_BLD_new()) == NULL || ++ (bn_ctx = BN_CTX_new()) == NULL) { ++ ret = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ ++ if ((group_name = OSSL_EC_curve_nid2name(ecdsa_nid)) == NULL || ++ OSSL_PARAM_BLD_push_utf8_string(param_bld, ++ OSSL_PKEY_PARAM_GROUP_NAME, ++ group_name, ++ strlen(group_name)) != 1) { ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ if ((pub = EC_KEY_get0_public_key(k)) != NULL) { ++ const EC_GROUP *group; ++ size_t len; ++ ++ group = EC_KEY_get0_group(k); ++ len = EC_POINT_point2oct(group, pub, ++ POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); ++ if ((pub_ser = malloc(len)) == NULL) { ++ ret = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ EC_POINT_point2oct(group, ++ pub, ++ POINT_CONVERSION_UNCOMPRESSED, ++ pub_ser, ++ len, ++ bn_ctx); ++ if (OSSL_PARAM_BLD_push_octet_string(param_bld, ++ OSSL_PKEY_PARAM_PUB_KEY, ++ pub_ser, ++ len) != 1) { ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ } ++ if ((priv = EC_KEY_get0_private_key(k)) != NULL && ++ OSSL_PARAM_BLD_push_BN(param_bld, ++ OSSL_PKEY_PARAM_PRIV_KEY, priv) != 1) { ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ if ((*pkey = sshkey_create_evp(param_bld, ctx)) == NULL) { ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ ++out: ++ OSSL_PARAM_BLD_free(param_bld); ++ EVP_PKEY_CTX_free(ctx); ++ BN_CTX_free(bn_ctx); ++ free(pub_ser); ++ return ret; ++} + #endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */ +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/sshkey.c ./sshkey.c +--- ../../openssh-8.7p1/sshkey.c 2023-03-08 15:35:14.702943628 +0100 ++++ ./sshkey.c 2023-03-08 15:39:03.354082015 +0100 +@@ -35,6 +35,8 @@ + #include + #include + #include ++#include ++#include + #endif + + #include "crypto_api.h" +@@ -492,13 +494,14 @@ + { + EVP_MD_CTX *ctx = NULL; + u_char *sig = NULL; +- int ret, slen, len; ++ int ret, slen; ++ size_t len; + + if (sigp == NULL || lenp == NULL) { + return SSH_ERR_INVALID_ARGUMENT; + } + +- slen = EVP_PKEY_size(pkey); ++ slen = EVP_PKEY_get_size(pkey); + if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM) + return SSH_ERR_INVALID_ARGUMENT; + +@@ -511,9 +514,10 @@ + ret = SSH_ERR_ALLOC_FAIL; + goto error; + } +- if (EVP_SignInit_ex(ctx, ssh_digest_to_md(hash_alg), NULL) <= 0 || +- EVP_SignUpdate(ctx, data, datalen) <= 0 || +- EVP_SignFinal(ctx, sig, &len, pkey) <= 0) { ++ if (EVP_DigestSignInit(ctx, NULL, ssh_digest_to_md(hash_alg), ++ NULL, pkey) != 1 || ++ EVP_DigestSignUpdate(ctx, data, datalen) != 1 || ++ EVP_DigestSignFinal(ctx, sig, &len) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto error; + } +@@ -540,12 +544,13 @@ + if ((ctx = EVP_MD_CTX_new()) == NULL) { + return SSH_ERR_ALLOC_FAIL; + } +- if (EVP_VerifyInit_ex(ctx, ssh_digest_to_md(hash_alg), NULL) <= 0 || +- EVP_VerifyUpdate(ctx, data, datalen) <= 0) { ++ if (EVP_DigestVerifyInit(ctx, NULL, ssh_digest_to_md(hash_alg), ++ NULL, pkey) != 1 || ++ EVP_DigestVerifyUpdate(ctx, data, datalen) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto done; + } +- ret = EVP_VerifyFinal(ctx, sigbuf, siglen, pkey); ++ ret = EVP_DigestVerifyFinal(ctx, sigbuf, siglen); + switch (ret) { + case 1: + ret = 0; +@@ -5038,3 +5043,27 @@ + return 0; + } + #endif /* WITH_XMSS */ ++ ++#ifdef WITH_OPENSSL ++EVP_PKEY * ++sshkey_create_evp(OSSL_PARAM_BLD *param_bld, EVP_PKEY_CTX *ctx) ++{ ++ EVP_PKEY *ret = NULL; ++ OSSL_PARAM *params = NULL; ++ if (param_bld == NULL || ctx == NULL) { ++ debug2_f("param_bld or ctx is NULL"); ++ return NULL; ++ } ++ if ((params = OSSL_PARAM_BLD_to_param(param_bld)) == NULL) { ++ debug2_f("Could not build param list"); ++ return NULL; ++ } ++ if (EVP_PKEY_fromdata_init(ctx) != 1 || ++ EVP_PKEY_fromdata(ctx, &ret, EVP_PKEY_KEYPAIR, params) != 1) { ++ debug2_f("EVP_PKEY_fromdata failed"); ++ OSSL_PARAM_free(params); ++ return NULL; ++ } ++ return ret; ++} ++#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/sshkey.h ./sshkey.h +--- ../../openssh-8.7p1/sshkey.h 2023-03-08 15:35:14.702943628 +0100 ++++ ./sshkey.h 2023-03-08 15:34:33.509578138 +0100 +@@ -31,6 +31,9 @@ + #ifdef WITH_OPENSSL + #include + #include ++#include ++#include ++#include + # ifdef OPENSSL_HAS_ECC + # include + # include +@@ -293,6 +295,13 @@ + + void sshkey_sig_details_free(struct sshkey_sig_details *); + ++#ifdef WITH_OPENSSL ++EVP_PKEY *sshkey_create_evp(OSSL_PARAM_BLD *, EVP_PKEY_CTX *); ++int ssh_create_evp_dss(const struct sshkey *, EVP_PKEY **); ++int ssh_create_evp_rsa(const struct sshkey *, EVP_PKEY **); ++int ssh_create_evp_ec(EC_KEY *, int, EVP_PKEY **); ++#endif /* WITH_OPENSSL */ ++ + #ifdef SSHKEY_INTERNAL + int ssh_rsa_sign(const struct sshkey *key, + u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, +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/ssh-rsa.c ./ssh-rsa.c +--- ../../openssh-8.7p1/ssh-rsa.c 2023-03-08 15:35:14.669943335 +0100 ++++ ./ssh-rsa.c 2023-03-08 15:34:33.509578138 +0100 +@@ -23,6 +23,8 @@ + + #include + #include ++#include ++#include + + #include + #include +@@ -172,9 +174,8 @@ + if (RSA_bits(key->rsa) < SSH_RSA_MINIMUM_MODULUS_SIZE) + return SSH_ERR_KEY_LENGTH; + +- if ((pkey = EVP_PKEY_new()) == NULL || +- EVP_PKEY_set1_RSA(pkey, key->rsa) != 1) +- return SSH_ERR_ALLOC_FAIL; ++ if ((ret = ssh_create_evp_rsa(key, &pkey)) != 0) ++ return ret; + ret = sshkey_calculate_signature(pkey, hash_alg, &sig, &len, data, + datalen); + EVP_PKEY_free(pkey); +@@ -285,11 +286,8 @@ + len = modlen; + } + +- if ((pkey = EVP_PKEY_new()) == NULL || +- EVP_PKEY_set1_RSA(pkey, key->rsa) != 1) { +- ret = SSH_ERR_ALLOC_FAIL; ++ if ((ret = ssh_create_evp_rsa(key, &pkey)) != 0) + goto out; +- } + ret = openssh_RSA_verify(hash_alg, data, datalen, sigblob, len, pkey); + EVP_PKEY_free(pkey); + +@@ -306,11 +304,9 @@ + u_char *sigbuf, size_t siglen, EVP_PKEY *pkey) + { + size_t rsasize = 0; +- const RSA *rsa; + int ret; + +- rsa = EVP_PKEY_get0_RSA(pkey); +- rsasize = RSA_size(rsa); ++ rsasize = EVP_PKEY_get_size(pkey); + if (rsasize <= 0 || rsasize > SSHBUF_MAX_BIGNUM || + siglen == 0 || siglen > rsasize) { + ret = SSH_ERR_INVALID_ARGUMENT; +@@ -323,4 +319,87 @@ + done: + return ret; + } ++ ++int ++ssh_create_evp_rsa(const struct sshkey *k, EVP_PKEY **pkey) ++{ ++ OSSL_PARAM_BLD *param_bld = NULL; ++ EVP_PKEY_CTX *ctx = NULL; ++ int ret = 0; ++ const BIGNUM *n = NULL, *e = NULL, *d = NULL, *p = NULL, *q = NULL; ++ const BIGNUM *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL; ++ ++ if (k == NULL) ++ return SSH_ERR_INVALID_ARGUMENT; ++ if ((ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL)) == NULL || ++ (param_bld = OSSL_PARAM_BLD_new()) == NULL) { ++ ret = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ ++ RSA_get0_key(k->rsa, &n, &e, &d); ++ RSA_get0_factors(k->rsa, &p, &q); ++ RSA_get0_crt_params(k->rsa, &dmp1, &dmq1, &iqmp); ++ ++ if (n != NULL && ++ OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_N, n) != 1) { ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ if (e != NULL && ++ OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_E, e) != 1) { ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ if (d != NULL && ++ OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_D, d) != 1) { ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ ++ if ((*pkey = sshkey_create_evp(param_bld, ctx)) == NULL) { ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ ++ /* setting this to param_build makes the creation process fail */ ++ if (p != NULL && ++ EVP_PKEY_set_bn_param(*pkey, OSSL_PKEY_PARAM_RSA_FACTOR1, p) != 1) { ++ debug2_f("failed to add 'p' param"); ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ if (q != NULL && ++ EVP_PKEY_set_bn_param(*pkey, OSSL_PKEY_PARAM_RSA_FACTOR2, q) != 1) { ++ debug2_f("failed to add 'q' param"); ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ if (dmp1 != NULL && ++ EVP_PKEY_set_bn_param(*pkey, ++ OSSL_PKEY_PARAM_RSA_EXPONENT1, dmp1) != 1) { ++ debug2_f("failed to add 'dmp1' param"); ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ if (dmq1 != NULL && ++ EVP_PKEY_set_bn_param(*pkey, ++ OSSL_PKEY_PARAM_RSA_EXPONENT2, dmq1) != 1) { ++ debug2_f("failed to add 'dmq1' param"); ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ if (iqmp != NULL && ++ EVP_PKEY_set_bn_param(*pkey, ++ OSSL_PKEY_PARAM_RSA_COEFFICIENT1, iqmp) != 1) { ++ debug2_f("failed to add 'iqmp' param"); ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ ++out: ++ OSSL_PARAM_BLD_free(param_bld); ++ EVP_PKEY_CTX_free(ctx); ++ return ret; ++} + #endif /* WITH_OPENSSL */ diff --git a/openssh.spec b/openssh.spec index 2d72e88..dcd8b70 100644 --- a/openssh.spec +++ b/openssh.spec @@ -47,7 +47,7 @@ # Do not forget to bump pam_ssh_agent_auth release if you rewind the main package release to 1 %global openssh_ver 9.0p1 -%global openssh_rel 15 +%global openssh_rel 16 %global pam_ssh_agent_ver 0.10.4 %global pam_ssh_agent_rel 8 @@ -243,6 +243,10 @@ Patch1009: openssh-configure-c99-3.patch Patch1010: openssh-8.7p1-CVE-2023-25136.patch +Patch1011: openssh-9.0p1-evp-fips-sign.patch +Patch1012: openssh-9.0p1-evp-fips-dh.patch +Patch1013: openssh-9.0p1-evp-fips-ecdh.patch + # downstream only # we skip some ssh-rsa/ssh-dss tests to make native test suite pass #Patch1100: openssh-8.8p1-skip-some-tests.patch @@ -454,6 +458,10 @@ popd %patch1009 -p1 -b .configure-c99-3 %patch1010 -p1 -b .cve-2023-25136 +%patch1011 -p1 -b .evp-fips-sign +%patch1012 -p1 -b .evp-fips-dh +%patch1013 -p1 -b .evp-fips-ecdh + #%patch1100 -p1 -b .skipsshrsadsstests %patch100 -p1 -b .coverity @@ -762,6 +770,10 @@ test -f %{sysconfig_anaconda} && \ %endif %changelog +* Thu Apr 13 2023 Norbert Pocs - 9.0p1-16 +- Make the sign, dh, ecdh processes FIPS compliant by adopting to + openssl 3.0 + * Thu Apr 13 2023 Dmitry Belyavskiy - 9.0p1-15 - Fix self-DoS Resolves: CVE-2023-25136