Unpatch Red Hat help message

This commit is contained in:
Koichiro Iwao 2025-11-04 03:04:55 +00:00 committed by root
commit bcbf9fa65f
2 changed files with 995 additions and 2 deletions

View File

@ -0,0 +1,987 @@
diff --git a/crypto_api.h b/crypto_api.h
index 8bbc3a08a..6940bbf0e 100644
--- a/crypto_api.h
+++ b/crypto_api.h
@@ -58,4 +58,8 @@ int crypto_kem_sntrup761_keypair(unsigned char *pk, unsigned char *sk);
#define crypto_kem_mlkem768_CIPHERTEXTBYTES 1088
#define crypto_kem_mlkem768_BYTES 32
+#define crypto_kem_mlkem1024_PUBLICKEYBYTES 1568
+#define crypto_kem_mlkem1024_SECRETKEYBYTES 3168
+#define crypto_kem_mlkem1024_CIPHERTEXTBYTES 1568
+
#endif /* crypto_api_h */
diff --git a/kex-names.c b/kex-names.c
index 36a953ab2..a728e0b38 100644
--- a/kex-names.c
+++ b/kex-names.c
@@ -90,6 +90,10 @@ static const struct kexalg kexalgs[] = {
#ifdef USE_MLKEM768X25519
{ KEX_MLKEM768X25519_SHA256, KEX_KEM_MLKEM768X25519_SHA256, 0,
SSH_DIGEST_SHA256 },
+ { KEX_MLKEM768NISTP256_SHA256, KEX_KEM_MLKEM768NISTP256_SHA256, 0,
+ SSH_DIGEST_SHA256 },
+ { KEX_MLKEM1024NISTP384_SHA384, KEX_KEM_MLKEM1024NISTP384_SHA384, 0,
+ SSH_DIGEST_SHA384 },
#endif
#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
{ NULL, 0, -1, -1},
@@ -129,7 +133,9 @@ kex_alg_list_internal(char sep, const struct kexalg *algs)
const struct kexalg *k;
for (k = algs; k->name != NULL; k++) {
- if (strcmp(k->name, KEX_MLKEM768X25519_SHA256) == 0
+ if ( (strcmp(k->name, KEX_MLKEM768X25519_SHA256) == 0
+ || strcmp(k->name, KEX_MLKEM768NISTP256_SHA256) == 0
+ || strcmp(k->name, KEX_MLKEM1024NISTP384_SHA384) == 0)
&& !is_mlkem768_available())
continue;
if (ret != NULL)
@@ -163,7 +169,9 @@ kex_alg_by_name(const char *name)
{
const struct kexalg *k;
- if (strcmp(name, KEX_MLKEM768X25519_SHA256) == 0
+ if ( (strcmp(name, KEX_MLKEM768X25519_SHA256) == 0
+ || strcmp(name, KEX_MLKEM768NISTP256_SHA256) == 0
+ || strcmp(name, KEX_MLKEM1024NISTP384_SHA384) == 0)
&& !is_mlkem768_available())
return NULL;
diff --git a/kex.c b/kex.c
index ce6a7b818..efaf5749c 100644
--- a/kex.c
+++ b/kex.c
@@ -753,6 +753,7 @@ kex_free(struct kex *kex)
#ifdef OPENSSL_HAS_ECC
EC_KEY_free(kex->ec_client_key);
#endif /* OPENSSL_HAS_ECC */
+ EVP_PKEY_free(kex->ec_hybrid_client_key);
#endif /* WITH_OPENSSL */
for (mode = 0; mode < MODE_MAX; mode++) {
kex_free_newkeys(kex->newkeys[mode]);
diff --git a/kex.h b/kex.h
index 48f3bb875..50a461ddb 100644
--- a/kex.h
+++ b/kex.h
@@ -72,6 +72,8 @@
#define KEX_SNTRUP761X25519_SHA512 "sntrup761x25519-sha512"
#define KEX_SNTRUP761X25519_SHA512_OLD "sntrup761x25519-sha512@openssh.com"
#define KEX_MLKEM768X25519_SHA256 "mlkem768x25519-sha256"
+#define KEX_MLKEM768NISTP256_SHA256 "mlkem768nistp256-sha256"
+#define KEX_MLKEM1024NISTP384_SHA384 "mlkem1024nistp384-sha384"
#define COMP_NONE 0
#define COMP_DELAYED 2
@@ -110,6 +112,8 @@ enum kex_exchange {
KEX_C25519_SHA256,
KEX_KEM_SNTRUP761X25519_SHA512,
KEX_KEM_MLKEM768X25519_SHA256,
+ KEX_KEM_MLKEM768NISTP256_SHA256,
+ KEX_KEM_MLKEM1024NISTP384_SHA384,
#ifdef GSSAPI
KEX_GSS_GRP1_SHA1,
KEX_GSS_GRP14_SHA1,
@@ -206,6 +210,9 @@ struct kex {
u_char sntrup761_client_key[crypto_kem_sntrup761_SECRETKEYBYTES]; /* KEM */
u_char mlkem768_client_key[crypto_kem_mlkem768_SECRETKEYBYTES]; /* KEM */
struct sshbuf *client_pub;
+ /* FIXME */
+ EVP_PKEY *ec_hybrid_client_key; /* NIST hybrids */
+ u_char mlkem1024_client_key[crypto_kem_mlkem1024_SECRETKEYBYTES]; /* ML-KEM 1024 + NIST */
};
int kex_name_valid(const char *);
@@ -287,6 +294,18 @@ int kex_kem_mlkem768x25519_enc(struct kex *, const struct sshbuf *,
int kex_kem_mlkem768x25519_dec(struct kex *, const struct sshbuf *,
struct sshbuf **);
+int kex_kem_mlkem768nistp256_keypair(struct kex *);
+int kex_kem_mlkem768nistp256_enc(struct kex *, const struct sshbuf *,
+ struct sshbuf **, struct sshbuf **);
+int kex_kem_mlkem768nistp256_dec(struct kex *, const struct sshbuf *,
+ struct sshbuf **);
+
+int kex_kem_mlkem1024nistp384_keypair(struct kex *);
+int kex_kem_mlkem1024nistp384_enc(struct kex *, const struct sshbuf *,
+ struct sshbuf **, struct sshbuf **);
+int kex_kem_mlkem1024nistp384_dec(struct kex *, const struct sshbuf *,
+ struct sshbuf **);
+
int kex_dh_keygen(struct kex *);
int kex_dh_compute_key(struct kex *, BIGNUM *, struct sshbuf *);
diff --git a/kexgen.c b/kexgen.c
index eecdceba2..6e910f84f 100644
--- a/kexgen.c
+++ b/kexgen.c
@@ -139,6 +139,22 @@ kex_gen_client(struct ssh *ssh)
r = kex_kem_mlkem768x25519_keypair(kex);
}
break;
+ case KEX_KEM_MLKEM768NISTP256_SHA256:
+ if (FIPS_mode()) {
+ logit_f("Key exchange type mlkem768nistp256 is not allowed in FIPS mode");
+ r = SSH_ERR_INVALID_ARGUMENT;
+ } else {
+ r = kex_kem_mlkem768nistp256_keypair(kex);
+ }
+ break;
+ case KEX_KEM_MLKEM1024NISTP384_SHA384:
+ if (FIPS_mode()) {
+ logit_f("Key exchange type mlkem1024nistp384 is not allowed in FIPS mode");
+ r = SSH_ERR_INVALID_ARGUMENT;
+ } else {
+ r = kex_kem_mlkem1024nistp384_keypair(kex);
+ }
+ break;
default:
r = SSH_ERR_INVALID_ARGUMENT;
break;
@@ -230,6 +246,24 @@ input_kex_gen_reply(int type, u_int32_t seq, struct ssh *ssh)
&shared_secret);
}
break;
+ case KEX_KEM_MLKEM768NISTP256_SHA256:
+ if (FIPS_mode()) {
+ logit_f("Key exchange type mlkem768nistp256 is not allowed in FIPS mode");
+ r = SSH_ERR_INVALID_ARGUMENT;
+ } else {
+ r = kex_kem_mlkem768nistp256_dec(kex, server_blob,
+ &shared_secret);
+ }
+ break;
+ case KEX_KEM_MLKEM1024NISTP384_SHA384:
+ if (FIPS_mode()) {
+ logit_f("Key exchange type mlkem1024nistp384 is not allowed in FIPS mode");
+ r = SSH_ERR_INVALID_ARGUMENT;
+ } else {
+ r = kex_kem_mlkem1024nistp384_dec(kex, server_blob,
+ &shared_secret);
+ }
+ break;
default:
r = SSH_ERR_INVALID_ARGUMENT;
break;
@@ -283,6 +317,8 @@ out:
sizeof(kex->sntrup761_client_key));
explicit_bzero(kex->mlkem768_client_key,
sizeof(kex->mlkem768_client_key));
+ explicit_bzero(kex->mlkem1024_client_key,
+ sizeof(kex->mlkem1024_client_key));
sshbuf_free(server_host_key_blob);
free(signature);
sshbuf_free(tmp);
@@ -369,6 +405,24 @@ input_kex_gen_init(int type, u_int32_t seq, struct ssh *ssh)
&server_pubkey, &shared_secret);
}
break;
+ case KEX_KEM_MLKEM768NISTP256_SHA256:
+ if (FIPS_mode()) {
+ logit_f("Key exchange type mlkem768nistp256 is not allowed in FIPS mode");
+ r = SSH_ERR_INVALID_ARGUMENT;
+ } else {
+ r = kex_kem_mlkem768nistp256_enc(kex, client_pubkey,
+ &server_pubkey, &shared_secret);
+ }
+ break;
+ case KEX_KEM_MLKEM1024NISTP384_SHA384:
+ if (FIPS_mode()) {
+ logit_f("Key exchange type mlkem1024nistp384 is not allowed in FIPS mode");
+ r = SSH_ERR_INVALID_ARGUMENT;
+ } else {
+ r = kex_kem_mlkem1024nistp384_enc(kex, client_pubkey,
+ &server_pubkey, &shared_secret);
+ }
+ break;
default:
r = SSH_ERR_INVALID_ARGUMENT;
break;
diff --git a/kexmlkem768x25519.c b/kexmlkem768x25519.c
index 670049dcd..463d18771 100644
--- a/kexmlkem768x25519.c
+++ b/kexmlkem768x25519.c
@@ -53,14 +53,15 @@
#include <stdio.h>
static int
-mlkem768_keypair_gen(unsigned char *pubkeybuf, unsigned char *privkeybuf)
+mlkem_keypair_gen(const char *algname, unsigned char *pubkeybuf, size_t pubkey_size,
+ unsigned char *privkeybuf, size_t privkey_size)
{
EVP_PKEY_CTX *ctx = NULL;
EVP_PKEY *pkey = NULL;
int ret = SSH_ERR_INTERNAL_ERROR;
- size_t pubkey_size = crypto_kem_mlkem768_PUBLICKEYBYTES, privkey_size = crypto_kem_mlkem768_SECRETKEYBYTES;
+ size_t got_pub_size = pubkey_size, got_priv_size = privkey_size;
- ctx = EVP_PKEY_CTX_new_from_name(NULL, "mlkem768", NULL);
+ ctx = EVP_PKEY_CTX_new_from_name(NULL, algname, NULL);
if (ctx == NULL) {
ret = SSH_ERR_LIBCRYPTO_ERROR;
goto err;
@@ -72,17 +73,23 @@ mlkem768_keypair_gen(unsigned char *pubkeybuf, unsigned char *privkeybuf)
goto err;
}
- if (EVP_PKEY_get_raw_public_key(pkey, pubkeybuf, &pubkey_size) <= 0
- || EVP_PKEY_get_raw_private_key(pkey, privkeybuf, &privkey_size) <= 0) {
+ if (EVP_PKEY_get_raw_public_key(pkey, NULL, &got_pub_size) <= 0
+ || EVP_PKEY_get_raw_private_key(pkey, NULL, &got_priv_size) <= 0) {
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto err;
+ }
+
+ if (privkey_size != got_priv_size || pubkey_size != got_pub_size) {
ret = SSH_ERR_LIBCRYPTO_ERROR;
goto err;
}
- if (privkey_size != crypto_kem_mlkem768_SECRETKEYBYTES
- || pubkey_size != crypto_kem_mlkem768_PUBLICKEYBYTES) {
+ if (EVP_PKEY_get_raw_public_key(pkey, pubkeybuf, &got_pub_size) <= 0
+ || EVP_PKEY_get_raw_private_key(pkey, privkeybuf, &got_priv_size) <= 0) {
ret = SSH_ERR_LIBCRYPTO_ERROR;
goto err;
}
+
ret = 0;
err:
@@ -94,16 +101,40 @@ mlkem768_keypair_gen(unsigned char *pubkeybuf, unsigned char *privkeybuf)
}
static int
-mlkem768_encap_secret(const u_char *pubkeybuf, u_char *secret, u_char *out)
+mlkem768_keypair_gen(unsigned char *pubkeybuf, unsigned char *privkeybuf)
+{
+ return mlkem_keypair_gen("mlkem768", pubkeybuf, crypto_kem_mlkem768_PUBLICKEYBYTES,
+ privkeybuf, crypto_kem_mlkem768_SECRETKEYBYTES);
+}
+
+static int
+mlkem1024_keypair_gen(unsigned char *pubkeybuf, unsigned char *privkeybuf)
+{
+ return mlkem_keypair_gen("mlkem1024", pubkeybuf, crypto_kem_mlkem1024_PUBLICKEYBYTES,
+ privkeybuf, crypto_kem_mlkem1024_SECRETKEYBYTES);
+}
+
+static int
+mlkem_encap_secret(const char *mlkem_alg, const u_char *pubkeybuf, u_char *secret, u_char *out)
{
EVP_PKEY *pkey = NULL;
EVP_PKEY_CTX *ctx = NULL;
int r = SSH_ERR_INTERNAL_ERROR;
- size_t outlen = crypto_kem_mlkem768_CIPHERTEXTBYTES,
- secretlen = crypto_kem_mlkem768_BYTES;
+ size_t outlen, expected_outlen, publen, secretlen = crypto_kem_mlkem768_BYTES;
+
+ if (strcmp(mlkem_alg, "mlkem768") == 0) {
+ outlen = crypto_kem_mlkem768_CIPHERTEXTBYTES;
+ publen = crypto_kem_mlkem768_PUBLICKEYBYTES;
+ } else if (strcmp(mlkem_alg, "mlkem1024") == 0) {
+ outlen = crypto_kem_mlkem1024_CIPHERTEXTBYTES;
+ publen = crypto_kem_mlkem1024_PUBLICKEYBYTES;
+ } else
+ return r;
- pkey = EVP_PKEY_new_raw_public_key_ex(NULL, "mlkem768", NULL,
- pubkeybuf, crypto_kem_mlkem768_PUBLICKEYBYTES);
+ expected_outlen = outlen;
+
+ pkey = EVP_PKEY_new_raw_public_key_ex(NULL, mlkem_alg, NULL,
+ pubkeybuf, publen);
if (pkey == NULL) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto err;
@@ -112,9 +143,9 @@ mlkem768_encap_secret(const u_char *pubkeybuf, u_char *secret, u_char *out)
ctx = EVP_PKEY_CTX_new_from_pkey(NULL, pkey, NULL);
if (ctx == NULL
|| EVP_PKEY_encapsulate_init(ctx, NULL) <= 0
- || EVP_PKEY_encapsulate(ctx, out, &outlen, secret, &secretlen) <= 0
+ || EVP_PKEY_encapsulate(ctx, out, &expected_outlen, secret, &secretlen) <= 0
|| secretlen != crypto_kem_mlkem768_BYTES
- || outlen != crypto_kem_mlkem768_CIPHERTEXTBYTES) {
+ || outlen != expected_outlen) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto err;
}
@@ -130,16 +161,30 @@ mlkem768_encap_secret(const u_char *pubkeybuf, u_char *secret, u_char *out)
}
static int
-mlkem768_decap_secret(const u_char *privkeybuf, const u_char *wrapped, u_char *secret)
+mlkem768_encap_secret(const u_char *pubkeybuf, u_char *secret, u_char *out)
+{
+ return mlkem_encap_secret("mlkem768", pubkeybuf, secret, out);
+}
+
+static int
+mlkem1024_encap_secret(const u_char *pubkeybuf, u_char *secret, u_char *out)
+{
+ return mlkem_encap_secret("mlkem1024", pubkeybuf, secret, out);
+}
+
+static int
+mlkem_decap_secret(const char *algname,
+ const u_char *privkeybuf, size_t privkey_len,
+ const u_char *wrapped, size_t wrapped_len,
+ u_char *secret)
{
EVP_PKEY *pkey = NULL;
EVP_PKEY_CTX *ctx = NULL;
int r = SSH_ERR_INTERNAL_ERROR;
- size_t wrappedlen = crypto_kem_mlkem768_CIPHERTEXTBYTES,
- secretlen = crypto_kem_mlkem768_BYTES;
+ size_t secretlen = crypto_kem_mlkem768_BYTES;
- pkey = EVP_PKEY_new_raw_private_key_ex(NULL, "mlkem768", NULL,
- privkeybuf, crypto_kem_mlkem768_SECRETKEYBYTES);
+ pkey = EVP_PKEY_new_raw_private_key_ex(NULL, algname,
+ NULL, privkeybuf, privkey_len);
if (pkey == NULL) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto err;
@@ -148,7 +193,7 @@ mlkem768_decap_secret(const u_char *privkeybuf, const u_char *wrapped, u_char *s
ctx = EVP_PKEY_CTX_new_from_pkey(NULL, pkey, NULL);
if (ctx == NULL
|| EVP_PKEY_decapsulate_init(ctx, NULL) <= 0
- || EVP_PKEY_decapsulate(ctx, secret, &secretlen, wrapped, wrappedlen) <= 0
+ || EVP_PKEY_decapsulate(ctx, secret, &secretlen, wrapped, wrapped_len) <= 0
|| secretlen != crypto_kem_mlkem768_BYTES) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto err;
@@ -165,6 +210,20 @@ mlkem768_decap_secret(const u_char *privkeybuf, const u_char *wrapped, u_char *s
return r;
}
+static int
+mlkem768_decap_secret(const u_char *privkeybuf, const u_char *wrapped, u_char *secret)
+{
+ return mlkem_decap_secret("mlkem768", privkeybuf, crypto_kem_mlkem768_SECRETKEYBYTES,
+ wrapped, crypto_kem_mlkem768_CIPHERTEXTBYTES, secret);
+}
+
+static int
+mlkem1024_decap_secret(const u_char *privkeybuf, const u_char *wrapped, u_char *secret)
+{
+ return mlkem_decap_secret("mlkem1024", privkeybuf, crypto_kem_mlkem1024_SECRETKEYBYTES,
+ wrapped, crypto_kem_mlkem1024_CIPHERTEXTBYTES, secret);
+}
+
int
kex_kem_mlkem768x25519_keypair(struct kex *kex)
{
@@ -341,7 +400,7 @@ kex_kem_mlkem768x25519_enc(struct kex *kex,
u_char hash[SSH_DIGEST_MAX_LENGTH];
size_t need;
int r = SSH_ERR_INTERNAL_ERROR;
- struct libcrux_mlkem768_enc_result enc; /* FIXME */
+ struct libcrux_mlkem768_enc_result enc;
*server_blobp = NULL;
*shared_secretp = NULL;
@@ -550,6 +609,468 @@ kex_kem_mlkem768x25519_dec(struct kex *kex,
return r;
#endif
}
+
+#define NIST_P256_COMPRESSED_LEN 33
+#define NIST_P256_UNCOMPRESSED_LEN 65
+#define NIST_P384_COMPRESSED_LEN 49
+#define NIST_P384_UNCOMPRESSED_LEN 97
+#define NIST_BUF_MAX_SIZE NIST_P384_UNCOMPRESSED_LEN
+
+static const char ec256[] = "P-256";
+static const char ec384[] = "P-384";
+static const char *len2curve_name(size_t len)
+{
+ switch (len) {
+ case NIST_P256_COMPRESSED_LEN:
+ case NIST_P256_UNCOMPRESSED_LEN:
+ return ec256;
+ break;
+ case NIST_P384_COMPRESSED_LEN:
+ case NIST_P384_UNCOMPRESSED_LEN:
+ return ec384;
+ break;
+ }
+ return NULL;
+}
+
+static EVP_PKEY *
+buf2nist_key(const unsigned char *pub_key_buf, size_t pub_key_len)
+{
+ EVP_PKEY *pkey = NULL;
+ EVP_PKEY_CTX *ctx = NULL;
+ OSSL_PARAM params[3];
+ const char *curve_name = len2curve_name(pub_key_len);
+
+ if (curve_name == NULL)
+ return NULL;
+
+ ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
+ if (!ctx)
+ goto err;
+
+ if (EVP_PKEY_fromdata_init(ctx) <= 0)
+ goto err;
+
+ params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, curve_name, 0);
+ params[1] = OSSL_PARAM_construct_octet_string(
+ OSSL_PKEY_PARAM_PUB_KEY, (void *)pub_key_buf, pub_key_len);
+ params[2] = OSSL_PARAM_construct_end();
+
+ if (EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0)
+ goto err;
+
+ EVP_PKEY_CTX_free(ctx);
+ return pkey;
+
+err:
+ EVP_PKEY_CTX_free(ctx);
+ EVP_PKEY_free(pkey);
+ return NULL;
+}
+
+static int
+kex_nist_shared_key_ext(EVP_PKEY *priv_key,
+ const u_char *pub_key_buf, size_t pub_key_len, struct sshbuf *out)
+{
+ EVP_PKEY_CTX *ctx = NULL;
+ unsigned char *shared_secret = NULL;
+ size_t shared_secret_len = 0;
+ EVP_PKEY *peer_key = buf2nist_key(pub_key_buf, pub_key_len);
+ int r = SSH_ERR_INTERNAL_ERROR;
+
+ if (peer_key == NULL)
+ return SSH_ERR_KEY_LENGTH;
+
+ ctx = EVP_PKEY_CTX_new_from_pkey(NULL, priv_key, NULL);
+ if (!ctx)
+ goto end;
+
+ if ((EVP_PKEY_derive_init(ctx) <= 0)
+ || EVP_PKEY_derive_set_peer(ctx, peer_key) <= 0
+ || EVP_PKEY_derive(ctx, NULL, &shared_secret_len) <= 0)
+ goto end;
+
+ shared_secret = OPENSSL_malloc(shared_secret_len);
+ if (shared_secret == NULL)
+ goto end;
+
+ if (EVP_PKEY_derive(ctx, shared_secret, &shared_secret_len) <= 0)
+ goto end;
+
+ if ((r = sshbuf_put(out, shared_secret, shared_secret_len)) != 0)
+ goto end;
+
+ r = 0;
+
+end:
+ EVP_PKEY_free(peer_key);
+ if (shared_secret)
+ OPENSSL_clear_free(shared_secret, shared_secret_len);
+ EVP_PKEY_CTX_free(ctx);
+
+ return r;
+}
+
+static EVP_PKEY *
+nist_pkey_keygen(size_t pub_key_len)
+{
+ const char *curve_name = len2curve_name(pub_key_len);
+ EVP_PKEY_CTX *pctx = NULL;
+ EVP_PKEY *pkey = NULL;
+ OSSL_PARAM params[2];
+
+ if (curve_name == NULL)
+ return NULL;
+
+ pctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
+ if (!pctx)
+ return NULL;
+
+ if (EVP_PKEY_keygen_init(pctx) <= 0) {
+ EVP_PKEY_CTX_free(pctx);
+ return NULL;
+ }
+
+ params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, curve_name, 0);
+ params[1] = OSSL_PARAM_construct_end();
+
+ if (EVP_PKEY_CTX_set_params(pctx, params) <= 0
+ || EVP_PKEY_keygen(pctx, &pkey) <= 0) {
+ EVP_PKEY_CTX_free(pctx);
+ return NULL;
+ }
+
+ EVP_PKEY_CTX_free(pctx);
+ return pkey;
+}
+
+static int
+get_uncompressed_ec_pubkey(EVP_PKEY *pkey, unsigned char *buf, size_t buf_len)
+{
+ OSSL_PARAM params[2];
+ size_t required_len = 0, out_len = 0;
+
+ params[0] = OSSL_PARAM_construct_utf8_string(
+ OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT,
+ "uncompressed", 0);
+ params[1] = OSSL_PARAM_construct_end();
+
+ if (EVP_PKEY_set_params(pkey, params) <= 0
+ || EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY,
+ NULL, 0, &required_len) <= 0) {
+ return SSH_ERR_LIBCRYPTO_ERROR;
+ }
+
+ if (required_len != buf_len)
+ return SSH_ERR_INTERNAL_ERROR;
+
+ if (EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY,
+ buf, required_len, &out_len) <= 0) {
+ return SSH_ERR_LIBCRYPTO_ERROR;
+ }
+
+ return 0;
+}
+/* nist_bytes_len should always be uncompressed */
+static int
+kex_kem_mlkem_nist_keypair(struct kex *kex, size_t mlkem_bytes_len, size_t nist_bytes_len)
+{
+ struct sshbuf *buf = NULL;
+ u_char *cp = NULL;
+ size_t need;
+ int r = SSH_ERR_INTERNAL_ERROR;
+ u_char *client_key = NULL;
+
+ if ((buf = sshbuf_new()) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ need = mlkem_bytes_len + nist_bytes_len;
+ if ((r = sshbuf_reserve(buf, need, &cp)) != 0)
+ goto out;
+
+ if (mlkem_bytes_len == crypto_kem_mlkem768_PUBLICKEYBYTES) {
+ client_key = kex->mlkem768_client_key;
+ r = mlkem768_keypair_gen(cp, client_key);
+ }
+
+ if (mlkem_bytes_len == crypto_kem_mlkem1024_PUBLICKEYBYTES) {
+ client_key = kex->mlkem1024_client_key;
+ r = mlkem1024_keypair_gen(cp, client_key);
+ }
+
+ if (client_key == NULL)
+ goto out;
+
+ if (r != 0)
+ goto out;
+#ifdef DEBUG_KEXECDH
+ dump_digest("client public key mlkemXXX:", cp, mlkem_bytes_len);
+#endif
+ cp += mlkem_bytes_len;
+ if ((kex->ec_hybrid_client_key = nist_pkey_keygen(nist_bytes_len)) == NULL)
+ goto out;
+
+ if ((r = get_uncompressed_ec_pubkey(kex->ec_hybrid_client_key, cp, nist_bytes_len)) != 0)
+ goto out;
+
+#ifdef DEBUG_KEXECDH
+ dump_digest("client public key NIST:", cp, nist_bytes_len);
+#endif
+ /* success */
+ r = 0;
+ kex->client_pub = buf;
+ buf = NULL;
+ out:
+ sshbuf_free(buf);
+ if (r == SSH_ERR_LIBCRYPTO_ERROR)
+ ERR_print_errors_fp(stderr);
+
+ return r;
+}
+
+static int
+kex_kem_mlkem_nist_enc(struct kex *kex, const char *nist_curve,
+ const struct sshbuf *client_blob, struct sshbuf **server_blobp,
+ struct sshbuf **shared_secretp)
+{
+ struct sshbuf *server_blob = NULL;
+ struct sshbuf *buf = NULL;
+ const u_char *client_pub;
+ u_char server_pub[NIST_BUF_MAX_SIZE];
+ u_char enc_out[crypto_kem_mlkem1024_CIPHERTEXTBYTES];
+ u_char secret[crypto_kem_mlkem768_BYTES];
+ EVP_PKEY *server_key = NULL;
+ u_char hash[SSH_DIGEST_MAX_LENGTH];
+ size_t client_buf_len, mlkem_buf_len, ecdh_buf_len, server_key_len, enc_out_len;
+ int r = SSH_ERR_INTERNAL_ERROR;
+
+ *server_blobp = NULL;
+ *shared_secretp = NULL;
+
+ client_buf_len = sshbuf_len(client_blob);
+ /* client_blob contains both KEM and ECDH client pubkeys */
+ if (strcmp(nist_curve, "P-256") == 0) {
+ if (crypto_kem_mlkem768_PUBLICKEYBYTES > client_buf_len)
+ return r;
+
+ ecdh_buf_len = client_buf_len - crypto_kem_mlkem768_PUBLICKEYBYTES;
+ if (ecdh_buf_len != NIST_P256_COMPRESSED_LEN &&
+ ecdh_buf_len != NIST_P256_UNCOMPRESSED_LEN)
+ return r;
+ mlkem_buf_len = crypto_kem_mlkem768_PUBLICKEYBYTES;
+ enc_out_len = crypto_kem_mlkem768_CIPHERTEXTBYTES;
+ server_key_len = NIST_P256_UNCOMPRESSED_LEN;
+ } else if (strcmp(nist_curve, "P-384") == 0) {
+ if (crypto_kem_mlkem1024_PUBLICKEYBYTES > client_buf_len)
+ return r;
+
+ ecdh_buf_len = client_buf_len - crypto_kem_mlkem1024_PUBLICKEYBYTES;
+ if (ecdh_buf_len != NIST_P384_COMPRESSED_LEN &&
+ ecdh_buf_len != NIST_P384_UNCOMPRESSED_LEN)
+ return r;
+ mlkem_buf_len = crypto_kem_mlkem1024_PUBLICKEYBYTES;
+ enc_out_len = crypto_kem_mlkem1024_CIPHERTEXTBYTES;
+ server_key_len = NIST_P384_UNCOMPRESSED_LEN;
+ } else
+ return r;
+
+ client_pub = sshbuf_ptr(client_blob);
+#ifdef DEBUG_KEXECDH
+ dump_digest("client public key mlkem:", client_pub, mlkem_buf_len);
+ dump_digest("client public key NIST:", client_pub + mlkem_buf_len, ecdh_buf_len);
+#endif
+
+ /* allocate buffer for concatenation of KEM key and ECDH shared key */
+ /* the buffer will be hashed and the result is the shared secret */
+ if ((buf = sshbuf_new()) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ /* allocate space for encrypted KEM key and ECDH pub key */
+ if ((server_blob = sshbuf_new()) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ r = (mlkem_buf_len == crypto_kem_mlkem768_PUBLICKEYBYTES) ?
+ mlkem768_encap_secret(client_pub, secret, enc_out) :
+ mlkem1024_encap_secret(client_pub, secret, enc_out);
+
+ if (r != 0)
+ goto out;
+
+ /* generate ECDH key pair, store server pubkey after ciphertext */
+ server_key = nist_pkey_keygen(server_key_len);
+
+ if ((r = get_uncompressed_ec_pubkey(server_key, server_pub, server_key_len) != 0) ||
+ (r = sshbuf_put(buf, secret, sizeof(secret))) != 0 ||
+ (r = sshbuf_put(server_blob, enc_out, enc_out_len) != 0)||
+ (r = sshbuf_put(server_blob, server_pub, server_key_len)) != 0)
+ goto out;
+
+ /* append ECDH shared key */
+ client_pub += mlkem_buf_len;
+ if ((r = kex_nist_shared_key_ext(server_key, client_pub, ecdh_buf_len, buf)) < 0)
+ goto out;
+ if ((r = ssh_digest_buffer(kex->hash_alg, buf, hash, sizeof(hash))) != 0)
+ goto out;
+#ifdef DEBUG_KEXECDH
+ dump_digest("server public NIST:", server_pub, server_key_len);
+ dump_digest("server cipher text:", enc_out, enc_out_len);
+ dump_digest("server kem key:", secret, sizeof(secret));
+ dump_digest("concatenation of KEM key and ECDH shared key:",
+ sshbuf_ptr(buf), sshbuf_len(buf));
+#endif
+ /* string-encoded hash is resulting shared secret */
+ sshbuf_reset(buf);
+ if ((r = sshbuf_put_string(buf, hash,
+ ssh_digest_bytes(kex->hash_alg))) != 0)
+ goto out;
+#ifdef DEBUG_KEXECDH
+ dump_digest("encoded shared secret:", sshbuf_ptr(buf), sshbuf_len(buf));
+#endif
+ /* success */
+ r = 0;
+ *server_blobp = server_blob;
+ *shared_secretp = buf;
+ server_blob = NULL;
+ buf = NULL;
+ out:
+ explicit_bzero(hash, sizeof(hash));
+ EVP_PKEY_free(server_key);
+ explicit_bzero(enc_out, sizeof(enc_out));
+ explicit_bzero(secret, sizeof(secret));
+ sshbuf_free(server_blob);
+ sshbuf_free(buf);
+ return r;
+}
+
+static int
+kex_kem_mlkem_nist_dec(struct kex *kex,
+ const struct sshbuf *server_blob, struct sshbuf **shared_secretp,
+ size_t mlkem_len)
+{
+ struct sshbuf *buf = NULL;
+ const u_char *ciphertext, *server_pub;
+ u_char hash[SSH_DIGEST_MAX_LENGTH];
+ u_char decap[crypto_kem_mlkem768_BYTES];
+ int r;
+ size_t nist_len;
+
+ *shared_secretp = NULL;
+
+ if (sshbuf_len(server_blob) < mlkem_len) {
+ r = SSH_ERR_SIGNATURE_INVALID;
+ goto out;
+ }
+
+ nist_len = sshbuf_len(server_blob) - mlkem_len;
+
+ switch (mlkem_len) {
+ case crypto_kem_mlkem768_CIPHERTEXTBYTES:
+ if (nist_len != NIST_P256_COMPRESSED_LEN
+ && nist_len != NIST_P256_UNCOMPRESSED_LEN) {
+ r = SSH_ERR_SIGNATURE_INVALID;
+ goto out;
+ }
+ break;
+ case crypto_kem_mlkem1024_CIPHERTEXTBYTES:
+ if (nist_len != NIST_P384_COMPRESSED_LEN
+ && nist_len != NIST_P384_UNCOMPRESSED_LEN) {
+ r = SSH_ERR_SIGNATURE_INVALID;
+ goto out;
+ }
+ break;
+ }
+
+ ciphertext = sshbuf_ptr(server_blob);
+ server_pub = ciphertext + mlkem_len;
+ /* hash concatenation of KEM key and ECDH shared key */
+ if ((buf = sshbuf_new()) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+#ifdef DEBUG_KEXECDH
+ dump_digest("server cipher text:", ciphertext, mlkem_len);
+ dump_digest("server public key NIST:", server_pub, nist_len);
+#endif
+ r = (mlkem_len == crypto_kem_mlkem768_CIPHERTEXTBYTES) ?
+ mlkem768_decap_secret(kex->mlkem768_client_key, ciphertext, decap) :
+ mlkem1024_decap_secret(kex->mlkem1024_client_key, ciphertext, decap);
+
+ if (r != 0)
+ goto out;
+ if ((r = sshbuf_put(buf, decap, sizeof(decap))) != 0)
+ goto out;
+ if ((r = kex_nist_shared_key_ext(kex->ec_hybrid_client_key, server_pub,
+ nist_len, buf)) < 0)
+ goto out;
+ if ((r = ssh_digest_buffer(kex->hash_alg, buf,
+ hash, sizeof(hash))) != 0)
+ goto out;
+#ifdef DEBUG_KEXECDH
+ dump_digest("client kem key:", decap, sizeof(decap));
+ dump_digest("concatenation of KEM key and ECDH shared key:",
+ sshbuf_ptr(buf), sshbuf_len(buf));
+#endif
+ sshbuf_reset(buf);
+ if ((r = sshbuf_put_string(buf, hash,
+ ssh_digest_bytes(kex->hash_alg))) != 0)
+ goto out;
+#ifdef DEBUG_KEXECDH
+ dump_digest("encoded shared secret:", sshbuf_ptr(buf), sshbuf_len(buf));
+#endif
+ /* success */
+ r = 0;
+ *shared_secretp = buf;
+ buf = NULL;
+ out:
+ explicit_bzero(hash, sizeof(hash));
+ explicit_bzero(decap, sizeof(decap));
+ sshbuf_free(buf);
+ return r;
+}
+
+int
+kex_kem_mlkem768nistp256_keypair(struct kex *kex)
+{
+ return kex_kem_mlkem_nist_keypair(kex, crypto_kem_mlkem768_PUBLICKEYBYTES, NIST_P256_UNCOMPRESSED_LEN);
+}
+
+int
+kex_kem_mlkem768nistp256_enc(struct kex *kex, const struct sshbuf *client_blob,
+ struct sshbuf **server_blobp, struct sshbuf **shared_secretp)
+{
+ return kex_kem_mlkem_nist_enc(kex, "P-256", client_blob, server_blobp, shared_secretp);
+}
+
+int
+kex_kem_mlkem768nistp256_dec(struct kex *kex, const struct sshbuf *server_blob,
+ struct sshbuf **shared_secretp)
+{
+ return kex_kem_mlkem_nist_dec(kex, server_blob, shared_secretp,
+ crypto_kem_mlkem768_CIPHERTEXTBYTES);
+}
+
+int
+kex_kem_mlkem1024nistp384_keypair(struct kex *kex)
+{
+ return kex_kem_mlkem_nist_keypair(kex, crypto_kem_mlkem1024_PUBLICKEYBYTES, NIST_P384_UNCOMPRESSED_LEN);
+}
+
+int
+kex_kem_mlkem1024nistp384_enc(struct kex *kex, const struct sshbuf *client_blob,
+ struct sshbuf **server_blobp, struct sshbuf **shared_secretp)
+{
+ return kex_kem_mlkem_nist_enc(kex, "P-384", client_blob, server_blobp, shared_secretp);
+}
+
+int
+kex_kem_mlkem1024nistp384_dec(struct kex *kex, const struct sshbuf *server_blob,
+ struct sshbuf **shared_secretp)
+{
+ return kex_kem_mlkem_nist_dec(kex, server_blob, shared_secretp,
+ crypto_kem_mlkem1024_CIPHERTEXTBYTES);
+}
+
#else /* USE_MLKEM768X25519 */
int
kex_kem_mlkem768x25519_keypair(struct kex *kex)
@@ -571,4 +1092,39 @@ kex_kem_mlkem768x25519_dec(struct kex *kex,
{
return SSH_ERR_SIGN_ALG_UNSUPPORTED;
}
+
+int kex_kem_mlkem768nistp256_keypair(struct kex *)
+{
+ return SSH_ERR_SIGN_ALG_UNSUPPORTED;
+}
+
+int kex_kem_mlkem768nistp256_enc(struct kex *, const struct sshbuf *,
+ struct sshbuf **, struct sshbuf **)
+{
+ return SSH_ERR_SIGN_ALG_UNSUPPORTED;
+}
+
+int kex_kem_mlkem768nistp256_dec(struct kex *, const struct sshbuf *,
+ struct sshbuf **)
+{
+ return SSH_ERR_SIGN_ALG_UNSUPPORTED;
+}
+
+int kex_kem_mlkem1024nistp384_keypair(struct kex *)
+{
+ return SSH_ERR_SIGN_ALG_UNSUPPORTED;
+}
+
+int kex_kem_mlkem1024nistp384_enc(struct kex *, const struct sshbuf *,
+ struct sshbuf **, struct sshbuf **)
+{
+ return SSH_ERR_SIGN_ALG_UNSUPPORTED;
+}
+
+int kex_kem_mlkem1024nistp384_dec(struct kex *, const struct sshbuf *,
+ struct sshbuf **)
+{
+ return SSH_ERR_SIGN_ALG_UNSUPPORTED;
+}
+
#endif /* USE_MLKEM768X25519 */
diff --git a/monitor.c b/monitor.c
index 58fbac9df..75413ce71 100644
--- a/monitor.c
+++ b/monitor.c
@@ -2009,6 +2009,8 @@ monitor_apply_keystate(struct ssh *ssh, struct monitor *pmonitor)
kex->kex[KEX_C25519_SHA256] = kex_gen_server;
kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_server;
kex->kex[KEX_KEM_MLKEM768X25519_SHA256] = kex_gen_server;
+ kex->kex[KEX_KEM_MLKEM768NISTP256_SHA256] = kex_gen_server;
+ kex->kex[KEX_KEM_MLKEM1024NISTP384_SHA384] = kex_gen_server;
kex->load_host_public_key=&get_hostkey_public_by_type;
kex->load_host_private_key=&get_hostkey_private_by_type;
kex->host_key_index=&get_hostkey_index;
diff --git a/regress/unittests/kex/test_kex.c b/regress/unittests/kex/test_kex.c
index 09016aead..2baf9a7a0 100644
--- a/regress/unittests/kex/test_kex.c
+++ b/regress/unittests/kex/test_kex.c
@@ -155,6 +155,8 @@ do_kex_with_key(char *kex, int keytype, int bits)
server2->kex->kex[KEX_C25519_SHA256] = kex_gen_server;
server2->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_server;
server2->kex->kex[KEX_KEM_MLKEM768X25519_SHA256] = kex_gen_server;
+ server2->kex->kex[KEX_KEM_MLKEM768NISTP256_SHA256] = kex_gen_server;
+ server2->kex->kex[KEX_KEM_MLKEM1024NISTP384_SHA384] = kex_gen_server;
server2->kex->load_host_public_key = server->kex->load_host_public_key;
server2->kex->load_host_private_key = server->kex->load_host_private_key;
server2->kex->sign = server->kex->sign;
@@ -211,6 +213,8 @@ kex_tests(void)
do_kex("diffie-hellman-group1-sha1");
# ifdef USE_MLKEM768X25519
do_kex("mlkem768x25519-sha256");
+ do_kex("mlkem768nistp256-sha256");
+ do_kex("mlkem1024nistp384-sha384");
# endif /* USE_MLKEM768X25519 */
# ifdef USE_SNTRUP761X25519
do_kex("sntrup761x25519-sha512@openssh.com");
diff --git a/ssh-keyscan.c b/ssh-keyscan.c
index 9f76ad225..8660088b7 100644
--- a/ssh-keyscan.c
+++ b/ssh-keyscan.c
@@ -304,6 +304,8 @@ keygrab_ssh2(con *c)
c->c_ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client;
c->c_ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_client;
c->c_ssh->kex->kex[KEX_KEM_MLKEM768X25519_SHA256] = kex_gen_client;
+ c->c_ssh->kex->kex[KEX_KEM_MLKEM768NISTP256_SHA256] = kex_gen_client;
+ c->c_ssh->kex->kex[KEX_KEM_MLKEM1024NISTP384_SHA384] = kex_gen_client;
ssh_set_verify_host_key_callback(c->c_ssh, key_print_wrapper);
/*
* do the key-exchange until an error occurs or until
diff --git a/ssh_api.c b/ssh_api.c
index 7bdcee148..d20b03a00 100644
--- a/ssh_api.c
+++ b/ssh_api.c
@@ -135,6 +135,8 @@ ssh_init(struct ssh **sshp, int is_server, struct kex_params *kex_params)
ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_server;
ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_server;
ssh->kex->kex[KEX_KEM_MLKEM768X25519_SHA256] = kex_gen_server;
+ ssh->kex->kex[KEX_KEM_MLKEM768NISTP256_SHA256] = kex_gen_server;
+ ssh->kex->kex[KEX_KEM_MLKEM1024NISTP384_SHA384] = kex_gen_server;
ssh->kex->load_host_public_key=&_ssh_host_public_key;
ssh->kex->load_host_private_key=&_ssh_host_private_key;
ssh->kex->sign=&_ssh_host_key_sign;
@@ -154,6 +156,8 @@ ssh_init(struct ssh **sshp, int is_server, struct kex_params *kex_params)
ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client;
ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_client;
ssh->kex->kex[KEX_KEM_MLKEM768X25519_SHA256] = kex_gen_client;
+ ssh->kex->kex[KEX_KEM_MLKEM768NISTP256_SHA256] = kex_gen_client;
+ ssh->kex->kex[KEX_KEM_MLKEM1024NISTP384_SHA384] = kex_gen_client;
ssh->kex->verify_host_key =&_ssh_verify_host_key;
}
*sshp = ssh;
diff --git a/sshconnect2.c b/sshconnect2.c
index 5beae1c10..6d9f1238b 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -347,6 +347,8 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port,
ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client;
ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_client;
ssh->kex->kex[KEX_KEM_MLKEM768X25519_SHA256] = kex_gen_client;
+ ssh->kex->kex[KEX_KEM_MLKEM768NISTP256_SHA256] = kex_gen_client;
+ ssh->kex->kex[KEX_KEM_MLKEM1024NISTP384_SHA384] = kex_gen_client;
ssh->kex->verify_host_key=&verify_host_key_callback;
#if defined(GSSAPI) && defined(WITH_OPENSSL)
diff -up openssh-9.9p1-build/openssh-9.9p1/sshd-session.c.xxx openssh-9.9p1-build/openssh-9.9p1/sshd-session.c
--- a/sshd-session.c.xxx 2025-11-03 12:07:44.196167561 +0100
+++ b/sshd-session.c 2025-11-03 12:08:26.809214111 +0100
@@ -1659,6 +1659,8 @@ do_ssh2_kex(struct ssh *ssh)
kex->kex[KEX_C25519_SHA256] = kex_gen_server;
kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_server;
kex->kex[KEX_KEM_MLKEM768X25519_SHA256] = kex_gen_server;
+ kex->kex[KEX_KEM_MLKEM768NISTP256_SHA256] = kex_gen_server;
+ kex->kex[KEX_KEM_MLKEM1024NISTP384_SHA384] = kex_gen_server;
kex->load_host_public_key=&get_hostkey_public_by_type;
kex->load_host_private_key=&get_hostkey_private_by_type;
kex->host_key_index=&get_hostkey_index;

View File

@ -43,7 +43,7 @@
Summary: An open source implementation of SSH protocol version 2
Name: openssh
Version: %{openssh_ver}
Release: 15%{?dist}.alma.1
Release: 16%{?dist}.alma.1
URL: http://www.openssh.com/portable.html
Source0: ftp://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-%{version}.tar.gz
Source1: ftp://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-%{version}.tar.gz.asc
@ -220,6 +220,7 @@ Patch1028: openssh-9.9p1-fips-gss.patch
#upstream 4f14ca8633a2c8c0a1a19165663421f0ab32f6ab
Patch1029: openssh-9.9p1-scp-traversing.patch
Patch1030: openssh-9.9p1-canonical-match-user.patch
Patch1031: openssh-10.0-mlkem-nist.patch
License: BSD-3-Clause AND BSD-2-Clause AND ISC AND SSH-OpenSSH AND ssh-keyscan AND sprintf AND LicenseRef-Fedora-Public-Domain AND X11-distribute-modifications-variant
Requires: /sbin/nologin
@ -415,6 +416,7 @@ gpgv2 --quiet --keyring %{SOURCE3} %{SOURCE1} %{SOURCE0}
%patch -P 1028 -p1 -b .gss-fips
%patch -P 1029 -p1 -b .scp-traversing
%patch -P 1030 -p1 -b .canonical-match-user
%patch -P 1031 -p1 -b .mlkem-nist
%patch -P 100 -p1 -b .coverity
@ -695,9 +697,13 @@ test -f %{sysconfig_anaconda} && \
%attr(0755,root,root) %{_libdir}/sshtest/sk-dummy.so
%changelog
* Fri Oct 31 2025 Koichiro Iwao <meta@almalinux.org> - 9.9p1-15.alma.1
* Tue Nov 04 2025 Koichiro Iwao <meta@almalinux.org> - 9.9p1-16.alma.1
- Unpatch Red Hat help message
* Mon Nov 03 2025 Dmitry Belyavskiy <dbelyavs@redhat.com> - 9.9p1-16
- Implement mlkem768nistp256-sha256 and mlkem1024nistp384-sha384 KEX methods
Resolves: RHEL-70824
* Mon Oct 27 2025 Zoltan Fridrich <zfridric@redhat.com> - 9.9p1-15
- Fix implicit destination path selection when source path ends with ".."
Resolves: RHEL-118406