From d5d02aea51850c5989d08f7e6cdb435d805c48cb Mon Sep 17 00:00:00 2001 From: Anthony Hu Date: Tue, 23 Dec 2025 16:06:24 -0500 Subject: [PATCH 1/7] mlkem1024nistp384-sha384 mlkem768x25519-sha256 --- src/internal.c | 483 ++++++++++++++++++++++++++++++++++----------- wolfssh/internal.h | 25 ++- 2 files changed, 392 insertions(+), 116 deletions(-) diff --git a/src/internal.c b/src/internal.c index 6a208283b..719a8eb93 100644 --- a/src/internal.c +++ b/src/internal.c @@ -850,6 +850,12 @@ static const char cannedKexAlgoNames[] = #if !defined(WOLFSSH_NO_NISTP256_MLKEM768_SHA256) "mlkem768nistp256-sha256," #endif +#if !defined(WOLFSSH_NO_NISTP384_MLKEM1024_SHA384) + "mlkem1024nistp384-sha384," +#endif +#if !defined(WOLFSSH_NO_CURVE25519_MLKEM768_SHA256) + "mlkem768x25519-sha256," +#endif #ifndef WOLFSSH_NO_CURVE25519_SHA256 "curve25519-sha256," "curve25519-sha256@libssh.org," @@ -2746,6 +2752,14 @@ static const NameIdPair NameIdMap[] = { { ID_NISTP256_MLKEM768_SHA256, TYPE_KEX, "mlkem768nistp256-sha256" }, #endif +#ifndef WOLFSSH_NO_NISTP384_MLKEM1024_SHA384 + { ID_NISTP384_MLKEM1024_SHA384, TYPE_KEX, + "mlkem1024nistp384-sha384" }, +#endif +#ifndef WOLFSSH_NO_CURVE25519_MLKEM768_SHA256 + { ID_CURVE25519_MLKEM768_SHA256, TYPE_KEX, + "mlkem768x25519-sha256" }, +#endif #ifndef WOLFSSH_NO_CURVE25519_SHA256 /* See RFC 8731 */ { ID_CURVE25519_SHA256, TYPE_KEX, "curve25519-sha256" }, @@ -4026,6 +4040,10 @@ enum wc_HashType HashForId(byte id) case ID_NISTP256_MLKEM768_SHA256: return WC_HASH_TYPE_SHA256; #endif +#ifndef WOLFSSH_NO_CURVE25519_MLKEM768_SHA256 + case ID_CURVE25519_MLKEM768_SHA256: + return WC_HASH_TYPE_SHA256; +#endif #ifndef WOLFSSH_NO_CURVE25519_SHA256 case ID_CURVE25519_SHA256: case ID_CURVE25519_SHA256_LIBSSH: @@ -4045,6 +4063,10 @@ enum wc_HashType HashForId(byte id) case ID_ECDH_SHA2_NISTP384: return WC_HASH_TYPE_SHA384; #endif +#ifndef WOLFSSH_NO_NISTP384_MLKEM1024_SHA384 + case ID_NISTP384_MLKEM1024_SHA384: + return WC_HASH_TYPE_SHA384; +#endif #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384 case ID_ECDSA_SHA2_NISTP384: #ifdef WOLFSSH_CERTS @@ -4100,10 +4122,18 @@ int wcPrimeForId(byte id) case ID_ECDH_SHA2_NISTP384: return ECC_SECP384R1; #endif +#ifndef WOLFSSH_NO_NISTP384_MLKEM1024_SHA384 + case ID_NISTP384_MLKEM1024_SHA384: + return ECC_SECP384R1; +#endif #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384 case ID_ECDSA_SHA2_NISTP384: return ECC_SECP384R1; #endif +#ifndef WOLFSSH_NO_CURVE25519_MLKEM768_SHA256 + case ID_CURVE25519_MLKEM768_SHA256: + return ECC_X25519; +#endif #ifndef WOLFSSH_NO_CURVE25519_SHA256 case ID_CURVE25519_SHA256: case ID_CURVE25519_SHA256_LIBSSH: @@ -5471,38 +5501,48 @@ static int KeyAgreeCurve25519_client(WOLFSSH* ssh, byte hashId, */ static int KeyAgreeEcdhMlKem_client(WOLFSSH* ssh, byte hashId, const byte* f, word32 fSz) -#ifndef WOLFSSH_NO_NISTP256_MLKEM768_SHA256 +#if !defined(WOLFSSH_NO_NISTP256_MLKEM768_SHA256) || \ + !defined(WOLFSSH_NO_NISTP384_MLKEM1024_SHA384) || \ + !defined(WOLFSSH_NO_CURVE25519_MLKEM768_SHA256) { int ret = WS_SUCCESS; byte sharedSecretHashSz = 0; byte *sharedSecretHash = NULL; - ecc_key *key_ptr = NULL; MlKemKey kem = {0}; word32 length_ciphertext = 0; word32 length_sharedsecret = 0; word32 length_privatekey = 0; - + int mlKemType = WC_ML_KEM_768; + byte kexId = ssh->handshake->kexId; +#if !defined(WOLFSSH_NO_NISTP256_MLKEM768_SHA256) || \ + !defined(WOLFSSH_NO_NISTP384_MLKEM1024_SHA384) + ecc_key *key_ptr = NULL; #ifndef WOLFSSH_SMALL_STACK ecc_key key_s; #endif - #ifdef WOLFSSH_SMALL_STACK - key_ptr = (ecc_key*)WMALLOC(sizeof(ecc_key), - ssh->ctx->heap, DYNTYPE_PRIVKEY); - if (key_ptr == NULL) { - ret = WS_MEMORY_E; - } - #else /* ! WOLFSSH_SMALL_STACK */ - key_ptr = &key_s; - #endif /* WOLFSSH_SMALL_STACK */ +#endif +#ifndef WOLFSSH_NO_CURVE25519_MLKEM768_SHA256 + curve25519_key *x25519_key_ptr = NULL; + #ifndef WOLFSSH_SMALL_STACK + curve25519_key x25519_key_s; + #endif +#endif WLOG(WS_LOG_DEBUG, "Entering KeyAgreeEcdhMlKem_client()"); - /* This is a a hybrid of ECDHE and a post-quantum KEM. In this - * case, I need to generated the ECC shared secret and + /* Determine ML-KEM type based on kexId */ +#ifndef WOLFSSH_NO_NISTP384_MLKEM1024_SHA384 + if (kexId == ID_NISTP384_MLKEM1024_SHA384) { + mlKemType = WC_ML_KEM_1024; + } +#endif + + /* This is a hybrid of ECC Curve25519 and a post-quantum KEM. + * In this case, it needs to generate the ECC shared secret and * decapsulate the ciphertext of the post-quantum KEM. */ if (ret == 0) { - ret = wc_MlKemKey_Init(&kem, WC_ML_KEM_768, ssh->ctx->heap, INVALID_DEVID); + ret = wc_MlKemKey_Init(&kem, mlKemType, ssh->ctx->heap, INVALID_DEVID); } if (ret == 0) { @@ -5525,33 +5565,92 @@ static int KeyAgreeEcdhMlKem_client(WOLFSSH* ssh, byte hashId, ret = WS_BUFFER_E; } - if (ret == 0) { - ret = wc_ecc_init(key_ptr); +#ifndef WOLFSSH_NO_CURVE25519_MLKEM768_SHA256 + if (kexId == ID_CURVE25519_MLKEM768_SHA256) { + /* Handle Curve25519 variant */ + #ifdef WOLFSSH_SMALL_STACK + x25519_key_ptr = (curve25519_key*)WMALLOC(sizeof(curve25519_key), + ssh->ctx->heap, DYNTYPE_PRIVKEY); + if (x25519_key_ptr == NULL) { + ret = WS_MEMORY_E; + } + #else + x25519_key_ptr = &x25519_key_s; + #endif + + if (ret == 0) { + ret = wc_curve25519_init(x25519_key_ptr); + } + if (ret == 0) { + ret = wc_curve25519_check_public(f + length_ciphertext, + fSz - length_ciphertext, EC25519_LITTLE_ENDIAN); + } + if (ret == 0) { + ret = wc_curve25519_import_public_ex(f + length_ciphertext, + fSz - length_ciphertext, x25519_key_ptr, + EC25519_LITTLE_ENDIAN); + } + if (ret == 0) { + PRIVATE_KEY_UNLOCK(); + ret = wc_curve25519_shared_secret_ex( + &ssh->handshake->privKey.curve25519, + x25519_key_ptr, ssh->k + length_sharedsecret, + &ssh->kSz, EC25519_LITTLE_ENDIAN); + PRIVATE_KEY_LOCK(); + } + wc_curve25519_free(x25519_key_ptr); + #ifdef WOLFSSH_SMALL_STACK + if (x25519_key_ptr) { + WFREE(x25519_key_ptr, ssh->ctx->heap, DYNTYPE_PRIVKEY); + } + #endif + wc_curve25519_free(&ssh->handshake->privKey.curve25519); } + else +#endif /* WOLFSSH_NO_CURVE25519_MLKEM768_SHA256 */ + { +#if !defined(WOLFSSH_NO_NISTP256_MLKEM768_SHA256) || \ + !defined(WOLFSSH_NO_NISTP384_MLKEM1024_SHA384) + /* Handle ECC variants (P-256 or P-384) */ + #ifdef WOLFSSH_SMALL_STACK + key_ptr = (ecc_key*)WMALLOC(sizeof(ecc_key), + ssh->ctx->heap, DYNTYPE_PRIVKEY); + if (key_ptr == NULL) { + ret = WS_MEMORY_E; + } + #else /* ! WOLFSSH_SMALL_STACK */ + key_ptr = &key_s; + #endif /* WOLFSSH_SMALL_STACK */ + + if (ret == 0) { + ret = wc_ecc_init(key_ptr); + } #ifdef HAVE_WC_ECC_SET_RNG - if (ret == 0) { - ret = wc_ecc_set_rng(key_ptr, ssh->rng); - } + if (ret == 0) { + ret = wc_ecc_set_rng(key_ptr, ssh->rng); + } #endif - if (ret == 0) { - ret = wc_ecc_import_x963(f + length_ciphertext, fSz - length_ciphertext, - key_ptr); - } + if (ret == 0) { + ret = wc_ecc_import_x963(f + length_ciphertext, + fSz - length_ciphertext, key_ptr); + } - if (ret == 0) { - PRIVATE_KEY_UNLOCK(); - ret = wc_ecc_shared_secret(&ssh->handshake->privKey.ecc, - key_ptr, ssh->k + length_sharedsecret, - &ssh->kSz); - PRIVATE_KEY_LOCK(); - } - wc_ecc_free(key_ptr); + if (ret == 0) { + PRIVATE_KEY_UNLOCK(); + ret = wc_ecc_shared_secret(&ssh->handshake->privKey.ecc, + key_ptr, ssh->k + length_sharedsecret, + &ssh->kSz); + PRIVATE_KEY_LOCK(); + } + wc_ecc_free(key_ptr); #ifdef WOLFSSH_SMALL_STACK - if (key_ptr) { - WFREE(key_ptr, ssh->ctx->heap, DYNTYPE_PRIVKEY); - } + if (key_ptr) { + WFREE(key_ptr, ssh->ctx->heap, DYNTYPE_PRIVKEY); + } #endif - wc_ecc_free(&ssh->handshake->privKey.ecc); + wc_ecc_free(&ssh->handshake->privKey.ecc); +#endif /* !WOLFSSH_NO_NISTP256_MLKEM768_SHA256 || !WOLFSSH_NO_NISTP384_MLKEM1024_SHA384 */ + } if (ret == 0) { wc_MlKemKey_DecodePrivateKey(&kem, ssh->handshake->x, @@ -5603,7 +5702,7 @@ static int KeyAgreeEcdhMlKem_client(WOLFSSH* ssh, byte hashId, WLOG(WS_LOG_DEBUG, "Leaving KeyAgreeEcdhMlKem_client(), ret = %d", ret); return ret; } -#else /* WOLFSSH_NO_NISTP256_MLKEM768_SHA256 */ +#else /* All ML-KEM variants disabled */ { WOLFSSH_UNUSED(ssh); WOLFSSH_UNUSED(hashId); @@ -5611,7 +5710,7 @@ static int KeyAgreeEcdhMlKem_client(WOLFSSH* ssh, byte hashId, WOLFSSH_UNUSED(fSz); return WS_INVALID_ALGO_ID; } -#endif /* WOLFSSH_NO_NISTP256_MLKEM768_SHA256 */ +#endif /* ML-KEM variants */ /* KeyAgree_client @@ -10846,9 +10945,12 @@ struct wolfSSH_sigKeyBlockFull { } sk; }; -#ifndef WOLFSSH_NO_NISTP256_MLKEM768_SHA256 - /* Size of ML-KEM public key (bigger than ciphertext) and some extra for the - * ECC hybrid component. */ +#ifndef WOLFSSH_NO_NISTP384_MLKEM1024_SHA384 + /* Size of ML-KEM-1024 ciphertext (1568) plus ECC P-384 component (97). */ + #define KEX_F_SIZE 1700 +#elif !defined(WOLFSSH_NO_NISTP256_MLKEM768_SHA256) || \ + !defined(WOLFSSH_NO_CURVE25519_MLKEM768_SHA256) + /* Size of ML-KEM-768 public key (1184) plus ECC/X25519 component. */ #define KEX_F_SIZE 1300 #elif !defined(WOLFSSH_NO_DH_GROUP16_SHA512) #define KEX_F_SIZE (512 + 1) @@ -11760,7 +11862,9 @@ static int KeyAgreeCurve25519_server(WOLFSSH* ssh, byte hashId, */ static int KeyAgreeEcdhMlKem_server(WOLFSSH* ssh, byte hashId, byte* f, word32* fSz) -#ifndef WOLFSSH_NO_NISTP256_MLKEM768_SHA256 +#if !defined(WOLFSSH_NO_NISTP256_MLKEM768_SHA256) || \ + !defined(WOLFSSH_NO_NISTP384_MLKEM1024_SHA384) || \ + !defined(WOLFSSH_NO_CURVE25519_MLKEM768_SHA256) { int ret = WS_SUCCESS; byte sharedSecretHashSz = 0; @@ -11769,39 +11873,36 @@ static int KeyAgreeEcdhMlKem_server(WOLFSSH* ssh, byte hashId, word32 length_publickey = 0; word32 length_ciphertext = 0; word32 length_sharedsecret = 0; + int mlKemType = WC_ML_KEM_768; + byte kexId = ssh->handshake->kexId; +#if !defined(WOLFSSH_NO_NISTP256_MLKEM768_SHA256) || \ + !defined(WOLFSSH_NO_NISTP384_MLKEM1024_SHA384) ecc_key* pubKey = NULL; ecc_key* privKey = NULL; int primeId; -#ifndef WOLFSSH_SMALL_STACK - ecc_key eccKeys[2]; + #ifndef WOLFSSH_SMALL_STACK + ecc_key eccKeys[2]; + #endif +#endif +#ifndef WOLFSSH_NO_CURVE25519_MLKEM768_SHA256 + curve25519_key* x25519PubKey = NULL; + curve25519_key* x25519PrivKey = NULL; + #ifndef WOLFSSH_SMALL_STACK + curve25519_key x25519Keys[2]; + #endif #endif WLOG(WS_LOG_DEBUG, "Entering KeyAgreeEcdhMlKem_server()"); -#ifdef WOLFSSH_SMALL_STACK - pubKey = (ecc_key*)WMALLOC(sizeof(ecc_key), - ssh->ctx->heap, DYNTYPE_PUBKEY); - privKey = (ecc_key*)WMALLOC(sizeof(ecc_key), - ssh->ctx->heap, DYNTYPE_PRIVKEY); - if (pubKey == NULL || privKey == NULL) { - ret = WS_MEMORY_E; + /* Determine ML-KEM type based on kexId */ +#ifndef WOLFSSH_NO_NISTP384_MLKEM1024_SHA384 + if (kexId == ID_NISTP384_MLKEM1024_SHA384) { + mlKemType = WC_ML_KEM_1024; } -#else - pubKey = &eccKeys[0]; - privKey = &eccKeys[1]; #endif if (ret == 0) { - XMEMSET(pubKey, 0, sizeof(*pubKey)); - XMEMSET(privKey, 0, sizeof(*privKey)); - - primeId = wcPrimeForId(ssh->handshake->kexId); - if (primeId == ECC_CURVE_INVALID) - ret = WS_INVALID_PRIME_CURVE; - } - - if (ret == 0) { - ret = wc_MlKemKey_Init(&kem, WC_ML_KEM_768, ssh->ctx->heap, + ret = wc_MlKemKey_Init(&kem, mlKemType, ssh->ctx->heap, INVALID_DEVID); } @@ -11844,50 +11945,148 @@ static int KeyAgreeEcdhMlKem_server(WOLFSSH* ssh, byte hashId, wc_MlKemKey_Free(&kem); - if (ret == 0) { - ret = wc_ecc_init_ex(pubKey, ssh->ctx->heap, INVALID_DEVID); - } - if (ret == 0) { - ret = wc_ecc_init_ex(privKey, ssh->ctx->heap, INVALID_DEVID); - } -#ifdef HAVE_WC_ECC_SET_RNG - if (ret == 0) { - ret = wc_ecc_set_rng(privKey, ssh->rng); - } -#endif - if (ret == 0) { - ret = wc_ecc_import_x963_ex( - ssh->handshake->e + length_publickey, - ssh->handshake->eSz - length_publickey, - pubKey, primeId); - } - if (ret == 0) { - ret = wc_ecc_make_key_ex(ssh->rng, - wc_ecc_get_curve_size_from_id(primeId), - privKey, primeId); - } - if (ret == 0) { - PRIVATE_KEY_UNLOCK(); - ret = wc_ecc_export_x963(privKey, f + length_ciphertext, fSz); - PRIVATE_KEY_LOCK(); - *fSz += length_ciphertext; +#ifndef WOLFSSH_NO_CURVE25519_MLKEM768_SHA256 + if (kexId == ID_CURVE25519_MLKEM768_SHA256) { + /* Handle Curve25519 variant */ + #ifdef WOLFSSH_SMALL_STACK + x25519PubKey = (curve25519_key*)WMALLOC(sizeof(curve25519_key), + ssh->ctx->heap, DYNTYPE_PUBKEY); + x25519PrivKey = (curve25519_key*)WMALLOC(sizeof(curve25519_key), + ssh->ctx->heap, DYNTYPE_PRIVKEY); + if (x25519PubKey == NULL || x25519PrivKey == NULL) { + ret = WS_MEMORY_E; + } + #else + x25519PubKey = &x25519Keys[0]; + x25519PrivKey = &x25519Keys[1]; + #endif + + if (ret == 0) { + ret = wc_curve25519_init(x25519PubKey); + } + if (ret == 0) { + ret = wc_curve25519_init(x25519PrivKey); + } + if (ret == 0) { + ret = wc_curve25519_check_public( + ssh->handshake->e + length_publickey, + ssh->handshake->eSz - length_publickey, + EC25519_LITTLE_ENDIAN); + } + if (ret == 0) { + ret = wc_curve25519_import_public_ex( + ssh->handshake->e + length_publickey, + ssh->handshake->eSz - length_publickey, + x25519PubKey, EC25519_LITTLE_ENDIAN); + } + if (ret == 0) { + ret = wc_curve25519_make_key(ssh->rng, CURVE25519_KEYSIZE, + x25519PrivKey); + } + if (ret == 0) { + word32 pubKeySz = CURVE25519_KEYSIZE; + PRIVATE_KEY_UNLOCK(); + ret = wc_curve25519_export_public_ex(x25519PrivKey, + f + length_ciphertext, &pubKeySz, EC25519_LITTLE_ENDIAN); + PRIVATE_KEY_LOCK(); + *fSz = length_ciphertext + pubKeySz; + } + if (ret == 0) { + word32 tmp_kSz = ssh->kSz; + PRIVATE_KEY_UNLOCK(); + ret = wc_curve25519_shared_secret_ex(x25519PrivKey, x25519PubKey, + ssh->k + length_sharedsecret, &tmp_kSz, + EC25519_LITTLE_ENDIAN); + PRIVATE_KEY_LOCK(); + ssh->kSz = length_sharedsecret + tmp_kSz; + } + if (x25519PrivKey) + wc_curve25519_free(x25519PrivKey); + if (x25519PubKey) + wc_curve25519_free(x25519PubKey); + #ifdef WOLFSSH_SMALL_STACK + if (x25519PubKey) + WFREE(x25519PubKey, ssh->ctx->heap, DYNTYPE_PUBKEY); + if (x25519PrivKey) + WFREE(x25519PrivKey, ssh->ctx->heap, DYNTYPE_PRIVKEY); + #endif } - if (ret == 0) { - word32 tmp_kSz = ssh->kSz; - PRIVATE_KEY_UNLOCK(); - ret = wc_ecc_shared_secret(privKey, pubKey, - ssh->k + length_sharedsecret, &tmp_kSz); - PRIVATE_KEY_LOCK(); - ssh->kSz = length_sharedsecret + tmp_kSz; + else +#endif /* WOLFSSH_NO_CURVE25519_MLKEM768_SHA256 */ + { +#if !defined(WOLFSSH_NO_NISTP256_MLKEM768_SHA256) || \ + !defined(WOLFSSH_NO_NISTP384_MLKEM1024_SHA384) + /* Handle ECC variants (P-256 or P-384) */ + #ifdef WOLFSSH_SMALL_STACK + pubKey = (ecc_key*)WMALLOC(sizeof(ecc_key), + ssh->ctx->heap, DYNTYPE_PUBKEY); + privKey = (ecc_key*)WMALLOC(sizeof(ecc_key), + ssh->ctx->heap, DYNTYPE_PRIVKEY); + if (pubKey == NULL || privKey == NULL) { + ret = WS_MEMORY_E; + } + #else + pubKey = &eccKeys[0]; + privKey = &eccKeys[1]; + #endif + + if (ret == 0) { + XMEMSET(pubKey, 0, sizeof(*pubKey)); + XMEMSET(privKey, 0, sizeof(*privKey)); + + primeId = wcPrimeForId(ssh->handshake->kexId); + if (primeId == ECC_CURVE_INVALID) + ret = WS_INVALID_PRIME_CURVE; + } + + if (ret == 0) { + ret = wc_ecc_init_ex(pubKey, ssh->ctx->heap, INVALID_DEVID); + } + if (ret == 0) { + ret = wc_ecc_init_ex(privKey, ssh->ctx->heap, INVALID_DEVID); + } + #ifdef HAVE_WC_ECC_SET_RNG + if (ret == 0) { + ret = wc_ecc_set_rng(privKey, ssh->rng); + } + #endif + if (ret == 0) { + ret = wc_ecc_import_x963_ex( + ssh->handshake->e + length_publickey, + ssh->handshake->eSz - length_publickey, + pubKey, primeId); + } + if (ret == 0) { + ret = wc_ecc_make_key_ex(ssh->rng, + wc_ecc_get_curve_size_from_id(primeId), + privKey, primeId); + } + if (ret == 0) { + PRIVATE_KEY_UNLOCK(); + ret = wc_ecc_export_x963(privKey, f + length_ciphertext, fSz); + PRIVATE_KEY_LOCK(); + *fSz += length_ciphertext; + } + if (ret == 0) { + word32 tmp_kSz = ssh->kSz; + PRIVATE_KEY_UNLOCK(); + ret = wc_ecc_shared_secret(privKey, pubKey, + ssh->k + length_sharedsecret, &tmp_kSz); + PRIVATE_KEY_LOCK(); + ssh->kSz = length_sharedsecret + tmp_kSz; + } + if (privKey) + wc_ecc_free(privKey); + if (pubKey) + wc_ecc_free(pubKey); + #ifdef WOLFSSH_SMALL_STACK + if (pubKey) + WFREE(pubKey, ssh->ctx->heap, DYNTYPE_PUBKEY); + if (privKey) + WFREE(privKey, ssh->ctx->heap, DYNTYPE_PRIVKEY); + #endif +#endif /* !WOLFSSH_NO_NISTP256_MLKEM768_SHA256 || !WOLFSSH_NO_NISTP384_MLKEM1024_SHA384 */ } - wc_ecc_free(privKey); - wc_ecc_free(pubKey); -#ifdef WOLFSSH_SMALL_STACK - if (pubKey) - WFREE(pubKey, ssh->ctx->heap, DYNTYPE_PUBKEY); - if (privKey) - WFREE(privKey, ssh->ctx->heap, DYNTYPE_PUBKEY); -#endif /* Replace the concatenated shared secrets with the hash. That * will become the new shared secret.*/ @@ -11916,7 +12115,7 @@ static int KeyAgreeEcdhMlKem_server(WOLFSSH* ssh, byte hashId, WLOG(WS_LOG_DEBUG, "Leaving KeyAgreeEcdhMlKem_server(), ret = %d", ret); return ret; } -#else /* WOLFSSH_NO_NISTP256_MLKEM768_SHA256 */ +#else /* All ML-KEM variants disabled */ { WOLFSSH_UNUSED(ssh); WOLFSSH_UNUSED(hashId); @@ -11924,7 +12123,7 @@ static int KeyAgreeEcdhMlKem_server(WOLFSSH* ssh, byte hashId, WOLFSSH_UNUSED(fSz); return WS_INVALID_ALGO_ID; } -#endif /* WOLFSSH_NO_NISTP256_MLKEM768_SHA256 */ +#endif /* ML-KEM variants */ static int SignHRsa(WOLFSSH* ssh, byte* sig, word32* sigSz, @@ -12332,7 +12531,19 @@ int SendKexDhReply(WOLFSSH* ssh) #endif #ifndef WOLFSSH_NO_NISTP256_MLKEM768_SHA256 case ID_NISTP256_MLKEM768_SHA256: - useEccMlKem = 1; /* Only support level 1 for now. */ + useEccMlKem = 1; + msgId = MSGID_KEXKEM_REPLY; + break; +#endif +#ifndef WOLFSSH_NO_NISTP384_MLKEM1024_SHA384 + case ID_NISTP384_MLKEM1024_SHA384: + useEccMlKem = 1; + msgId = MSGID_KEXKEM_REPLY; + break; +#endif +#ifndef WOLFSSH_NO_CURVE25519_MLKEM768_SHA256 + case ID_CURVE25519_MLKEM768_SHA256: + useEccMlKem = 1; msgId = MSGID_KEXKEM_REPLY; break; #endif @@ -12925,10 +13136,22 @@ int SendKexDhInit(WOLFSSH* ssh) #endif #ifndef WOLFSSH_NO_NISTP256_MLKEM768_SHA256 case ID_NISTP256_MLKEM768_SHA256: - /* Only support level 1 for now. */ ssh->handshake->useEccMlKem = 1; msgId = MSGID_KEXKEM_INIT; break; +#endif +#ifndef WOLFSSH_NO_NISTP384_MLKEM1024_SHA384 + case ID_NISTP384_MLKEM1024_SHA384: + ssh->handshake->useEccMlKem = 1; + msgId = MSGID_KEXKEM_INIT; + break; +#endif +#ifndef WOLFSSH_NO_CURVE25519_MLKEM768_SHA256 + case ID_CURVE25519_MLKEM768_SHA256: + ssh->handshake->useEccMlKem = 1; + ssh->handshake->useCurve25519MlKem = 1; + msgId = MSGID_KEXKEM_INIT; + break; #endif default: WLOG(WS_LOG_DEBUG, "Invalid algo: %u", ssh->handshake->kexId); @@ -12976,8 +13199,27 @@ int SendKexDhInit(WOLFSSH* ssh) } } #endif /* ! WOLFSSH_NO_CURVE25519_SHA256 */ +#ifndef WOLFSSH_NO_CURVE25519_MLKEM768_SHA256 + else if (ssh->handshake->useCurve25519MlKem) { + /* Handle Curve25519+ML-KEM variant - generate Curve25519 key */ + curve25519_key* privKey = &ssh->handshake->privKey.curve25519; + if (ret == 0) + ret = wc_curve25519_init_ex(privKey, ssh->ctx->heap, + INVALID_DEVID); + if (ret == 0) + ret = wc_curve25519_make_key(ssh->rng, CURVE25519_KEYSIZE, + privKey); + if (ret == 0) { + PRIVATE_KEY_UNLOCK(); + ret = wc_curve25519_export_public_ex(privKey, e, &eSz, + EC25519_LITTLE_ENDIAN); + PRIVATE_KEY_LOCK(); + } + } +#endif /* WOLFSSH_NO_CURVE25519_MLKEM768_SHA256 */ else if (ssh->handshake->useEcc -#ifndef WOLFSSH_NO_NISTP256_MLKEM768_SHA256 +#if !defined(WOLFSSH_NO_NISTP256_MLKEM768_SHA256) || \ + !defined(WOLFSSH_NO_NISTP384_MLKEM1024_SHA384) || ssh->handshake->useEccMlKem #endif ) { @@ -13012,15 +13254,24 @@ int SendKexDhInit(WOLFSSH* ssh) ret = WS_INVALID_ALGO_ID; } -#ifndef WOLFSSH_NO_NISTP256_MLKEM768_SHA256 +#if !defined(WOLFSSH_NO_NISTP256_MLKEM768_SHA256) || \ + !defined(WOLFSSH_NO_NISTP384_MLKEM1024_SHA384) || \ + !defined(WOLFSSH_NO_CURVE25519_MLKEM768_SHA256) if (ssh->handshake->useEccMlKem) { MlKemKey kem = {0}; word32 length_publickey = 0; word32 length_privatekey = 0; + int mlKemType = WC_ML_KEM_768; ret = 0; +#ifndef WOLFSSH_NO_NISTP384_MLKEM1024_SHA384 + if (ssh->handshake->kexId == ID_NISTP384_MLKEM1024_SHA384) { + mlKemType = WC_ML_KEM_1024; + } +#endif + if (ret == 0) { - ret = wc_MlKemKey_Init(&kem, WC_ML_KEM_768, ssh->ctx->heap, + ret = wc_MlKemKey_Init(&kem, mlKemType, ssh->ctx->heap, INVALID_DEVID); } @@ -13053,14 +13304,16 @@ int SendKexDhInit(WOLFSSH* ssh) wc_MlKemKey_Free(&kem); } -#endif /* ! WOLFSSH_NO_NISTP256_MLKEM768_SHA256 */ +#endif /* ML-KEM hybrid algorithms */ if (ret == 0) { ret = WS_SUCCESS; } } if (ret == WS_SUCCESS -#ifndef WOLFSSH_NO_NISTP256_MLKEM768_SHA256 +#if !defined(WOLFSSH_NO_NISTP256_MLKEM768_SHA256) || \ + !defined(WOLFSSH_NO_NISTP384_MLKEM1024_SHA384) || \ + !defined(WOLFSSH_NO_CURVE25519_MLKEM768_SHA256) && !ssh->handshake->useEccMlKem #endif #ifndef WOLFSSH_NO_CURVE25519_SHA256 diff --git a/wolfssh/internal.h b/wolfssh/internal.h index 76db31316..4af8bf6c2 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -184,6 +184,16 @@ extern "C" { #undef WOLFSSH_NO_NISTP256_MLKEM768_SHA256 #define WOLFSSH_NO_NISTP256_MLKEM768_SHA256 #endif +#if !defined(WOLFSSL_HAVE_MLKEM) || !defined(WOLFSSL_SHA384) \ + || defined(WOLFSSH_NO_ECDH_SHA2_NISTP384) + #undef WOLFSSH_NO_NISTP384_MLKEM1024_SHA384 + #define WOLFSSH_NO_NISTP384_MLKEM1024_SHA384 +#endif +#if !defined(WOLFSSL_HAVE_MLKEM) || defined(NO_SHA256) \ + || !defined(HAVE_CURVE25519) + #undef WOLFSSH_NO_CURVE25519_MLKEM768_SHA256 + #define WOLFSSH_NO_CURVE25519_MLKEM768_SHA256 +#endif #if !defined(HAVE_CURVE25519) || defined(NO_SHA256) #undef WOLFSSH_NO_CURVE25519_SHA256 #define WOLFSSH_NO_CURVE25519_SHA256 @@ -198,6 +208,8 @@ extern "C" { defined(WOLFSSH_NO_ECDH_SHA2_NISTP384) && \ defined(WOLFSSH_NO_ECDH_SHA2_NISTP521) && \ defined(WOLFSSH_NO_NISTP256_MLKEM768_SHA256) && \ + defined(WOLFSSH_NO_NISTP384_MLKEM1024_SHA384) && \ + defined(WOLFSSH_NO_CURVE25519_MLKEM768_SHA256) && \ defined(WOLFSSH_NO_CURVE25519_SHA256) #error "You need at least one key agreement algorithm." #endif @@ -343,6 +355,12 @@ enum { #ifndef WOLFSSH_NO_NISTP256_MLKEM768_SHA256 ID_NISTP256_MLKEM768_SHA256, #endif +#ifndef WOLFSSH_NO_NISTP384_MLKEM1024_SHA384 + ID_NISTP384_MLKEM1024_SHA384, +#endif +#ifndef WOLFSSH_NO_CURVE25519_MLKEM768_SHA256 + ID_CURVE25519_MLKEM768_SHA256, +#endif #ifndef WOLFSSH_NO_CURVE25519_SHA256 ID_CURVE25519_SHA256, ID_CURVE25519_SHA256_LIBSSH, @@ -452,7 +470,11 @@ enum NameIdType { #define WOLFSSH_DEFAULT_GEXDH_MAX 8192 #endif #ifndef MAX_KEX_KEY_SZ - #ifndef WOLFSSH_NO_NISTP256_MLKEM768_SHA256 + #ifndef WOLFSSH_NO_NISTP384_MLKEM1024_SHA384 + /* Private key size of ML-KEM 1024. Biggest artifact. */ + #define MAX_KEX_KEY_SZ 3168 + #elif !defined(WOLFSSH_NO_NISTP256_MLKEM768_SHA256) || \ + !defined(WOLFSSH_NO_CURVE25519_MLKEM768_SHA256) /* Private key size of ML-KEM 768. Biggest artifact. */ #define MAX_KEX_KEY_SZ 2400 #else @@ -645,6 +667,7 @@ typedef struct HandshakeInfo { byte useEcc:1; byte useEccMlKem:1; byte useCurve25519:1; + byte useCurve25519MlKem:1; union { #ifndef WOLFSSH_NO_DH From 303cafc9be92afee8a50da2ef2d7e7007f913fd6 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Tue, 13 Jan 2026 17:03:27 -0800 Subject: [PATCH 2/7] Add ML-KEM with Curve25519 and NISTp384 1. Reorganize the KEX test to allow for testing any KEX algorithm. Add test cases for the new algorithms to the KEX test. 2. Reorder the cannedKexAlgoNames with the ML-KEM algos first. 3. Add the new algos to wolfSSH_GetText(). 4. Add comments and whitespace cleanup. --- src/internal.c | 18 ++++++++----- src/ssh.c | 17 +++++++++++- tests/kex.c | 71 +++++++++++++++++++++++++++----------------------- 3 files changed, 66 insertions(+), 40 deletions(-) diff --git a/src/internal.c b/src/internal.c index 719a8eb93..f30fc5188 100644 --- a/src/internal.c +++ b/src/internal.c @@ -146,8 +146,14 @@ Set when ECC or SHA2-512 are disabled. Set to disable use of ECDSA server authentication with prime NISTP521. WOLFSSH_NO_NISTP256_MLKEM768_SHA256 - Set when ML-KEM is disabled in wolfssl. Set to disable use of ECDHE with - prime NISTP256 hybridized with post-quantum ML-KEM 768. + Set when ML-KEM, ECC, or SHA2-256 are disabled in wolfssl. Set to disable + use of ECDHE with prime NISTP256 hybridized with post-quantum ML-KEM 768. + WOLFSSH_NO_NISTP384_MLKEM1024_SHA384 + Set when ML-KEM, ECC, or SHA2-384 are disabled in wolfssl. Set to disable + use of ECDHE with prime NISTP384 hybridized with post-quantum ML-KEM 1024. + WOLFSSH_NO_CURVE25519_MLKEM768_SHA256 + Set when ML-KEM, Curve25519, or SHA2-256 are disabled in wolfssl. Set to + disable use of Curve25519 hybridized with post-quantum ML-KEM 768. WOLFSSH_NO_AES_CBC_SOFT_DISABLE AES-CBC is normally soft-disabled. The default configuration will not advertise the availability of AES-CBC algorithms during KEX. AES-CBC @@ -847,14 +853,14 @@ int wolfSSH_TestIsMessageAllowed(WOLFSSH* ssh, byte msg, byte state) static const char cannedKexAlgoNames[] = -#if !defined(WOLFSSH_NO_NISTP256_MLKEM768_SHA256) - "mlkem768nistp256-sha256," +#if !defined(WOLFSSH_NO_CURVE25519_MLKEM768_SHA256) + "mlkem768x25519-sha256," #endif #if !defined(WOLFSSH_NO_NISTP384_MLKEM1024_SHA384) "mlkem1024nistp384-sha384," #endif -#if !defined(WOLFSSH_NO_CURVE25519_MLKEM768_SHA256) - "mlkem768x25519-sha256," +#if !defined(WOLFSSH_NO_NISTP256_MLKEM768_SHA256) + "mlkem768nistp256-sha256," #endif #ifndef WOLFSSH_NO_CURVE25519_SHA256 "curve25519-sha256," diff --git a/src/ssh.c b/src/ssh.c index d52d6105f..4e16e1115 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -3199,7 +3199,22 @@ size_t wolfSSH_GetText(WOLFSSH *ssh, WS_Text id, char *str, size_t strSz) #ifndef WOLFSSH_NO_NISTP256_MLKEM768_SHA256 case ID_NISTP256_MLKEM768_SHA256: - ret = WSNPRINTF(str, strSz, "%s", "ECDH-MLKEM768"); + ret = WSNPRINTF(str, strSz, "%s", + "ECDH-NISTP256-MLKEM768"); + break; + #endif + + #ifndef WOLFSSH_NO_NISTP384_MLKEM1024_SHA384 + case ID_NISTP384_MLKEM1024_SHA384: + ret = WSNPRINTF(str, strSz, "%s", + "ECDH-NISTP384-MLKEM1024"); + break; + #endif + + #ifndef WOLFSSH_NO_CURVE25519_MLKEM768_SHA256 + case ID_CURVE25519_MLKEM768_SHA256: + ret = WSNPRINTF(str, strSz, "%s", + "ECDH-CURVE25519-MLKEM768"); break; #endif diff --git a/tests/kex.c b/tests/kex.c index 245977067..1ce924879 100644 --- a/tests/kex.c +++ b/tests/kex.c @@ -138,9 +138,17 @@ #if !defined(NO_WOLFSSH_SERVER) && !defined(NO_WOLFSSH_CLIENT) && \ - !defined(SINGLE_THREADED) && !defined(WOLFSSH_TEST_BLOCK) && \ - !defined(WOLFSSH_NO_DH_GROUP16_SHA512) && !defined(WOLFSSH_NO_HMAC_SHA2_512) + !defined(SINGLE_THREADED) && !defined(WOLFSSH_TEST_BLOCK) + #if !defined(WOLFSSH_NO_DH_GROUP16_SHA512) \ + || !defined(WOLFSSH_NO_CURVE25519_MLKEM768_SHA256) \ + || !defined(WOLFSSH_NO_NISTP384_MLKEM1024_SHA384) + + #define KEXTEST_AVAILABLE + #endif +#endif + +#ifdef KEXTEST_AVAILABLE static int tsClientUserAuth(byte authType, WS_UserAuthData* authData, void* ctx) { static char password[] = "upthehill"; @@ -163,7 +171,7 @@ static int tsClientUserAuth(byte authType, WS_UserAuthData* authData, void* ctx) #define NUMARGS 12 #define ARGLEN 32 -/* +/* * Macro: ADD_ARG * Purpose: Adds a string argument to the argument list. * Parameters: @@ -185,7 +193,7 @@ static int tsClientUserAuth(byte authType, WS_UserAuthData* authData, void* ctx) WSTRNCPY((argList)[(argListCount)++], (arg), ARGLEN); \ } while (0) -/* +/* * Macro: ADD_ARG_INT * Purpose: Adds an integer argument to the argument list as a string. * Parameters: @@ -209,7 +217,7 @@ static int tsClientUserAuth(byte authType, WS_UserAuthData* authData, void* ctx) } while (0) -static int wolfSSH_wolfSSH_Group16_512(void) +static int wolfSSH_KexTest_Connect(const char* kex) { tcp_ready ready; THREAD_TYPE serverThread; @@ -226,27 +234,6 @@ static int wolfSSH_wolfSSH_Group16_512(void) int serverArgc = 0; int clientArgc = 0; - WSTARTTCP(); - - #if defined(DEBUG_WOLFSSH) - wolfSSH_Debugging_ON(); - #endif - - wolfSSH_Init(); - - #if defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,2) - { - int i; - for (i = 0; i < FIPS_CAST_COUNT; i++) { - wc_RunCast_fips(i); - } - } - #endif /* HAVE_FIPS */ - - #if !defined(WOLFSSL_TIRTOS) - ChangeToWolfSshRoot(); - #endif - InitTcpReady(&ready); ADD_ARG(serverArgv, serverArgc, "echoserver"); @@ -257,7 +244,7 @@ static int wolfSSH_wolfSSH_Group16_512(void) ADD_ARG(serverArgv, serverArgc, "-0"); #endif ADD_ARG(serverArgv, serverArgc, "-x"); - ADD_ARG(serverArgv, serverArgc, "diffie-hellman-group16-sha512"); + ADD_ARG(serverArgv, serverArgc, kex); ADD_ARG(serverArgv, serverArgc, "-m"); ADD_ARG(serverArgv, serverArgc, "hmac-sha2-512"); ADD_ARG(serverArgv, serverArgc, "-c"); @@ -318,7 +305,7 @@ static int wolfSSH_wolfSSH_Group16_512(void) return EXIT_SUCCESS; } -#endif +#endif /* KEXTEST_AVAILABLE */ int wolfSSH_KexTest(int argc, char** argv) { @@ -326,10 +313,19 @@ int wolfSSH_KexTest(int argc, char** argv) (void)argv; -#if defined(NO_WOLFSSH_SERVER) || defined(NO_WOLFSSH_CLIENT) || \ - defined(SINGLE_THREADED) || defined(WOLFSSH_TEST_BLOCK) +#if !defined(KEXTEST_AVAILABLE) return 77; #else + WSTARTTCP(); + + #if defined(DEBUG_WOLFSSH) + wolfSSH_Debugging_ON(); + #endif + + #if !defined(WOLFSSL_TIRTOS) + ChangeToWolfSshRoot(); + #endif + AssertIntEQ(wolfSSH_Init(), WS_SUCCESS); #if defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,2) @@ -341,14 +337,23 @@ int wolfSSH_KexTest(int argc, char** argv) } #endif /* HAVE_FIPS */ -#if !defined(WOLFSSH_NO_DH_GROUP16_SHA512) && !defined(WOLFSSH_NO_HMAC_SHA2_512) - wolfSSH_wolfSSH_Group16_512(); +#if !defined(WOLFSSH_NO_DH_GROUP16_SHA512) + AssertIntEQ(wolfSSH_KexTest_Connect("diffie-hellman-group16-sha512"), + EXIT_SUCCESS); +#endif +#if !defined(WOLFSSH_NO_CURVE25519_MLKEM768_SHA256) + AssertIntEQ(wolfSSH_KexTest_Connect("mlkem768x25519-sha256"), + EXIT_SUCCESS); +#endif +#if !defined(WOLFSSH_NO_NISTP384_MLKEM1024_SHA384) + AssertIntEQ(wolfSSH_KexTest_Connect("mlkem1024nistp384-sha384"), + EXIT_SUCCESS); #endif AssertIntEQ(wolfSSH_Cleanup(), WS_SUCCESS); return 0; -#endif +#endif /* KEXTEST_AVAILABLE */ } From 4d712da4dbf8621509a07c3a36e0a4b8ee8c796d Mon Sep 17 00:00:00 2001 From: John Safranek Date: Thu, 15 Jan 2026 09:09:56 -0800 Subject: [PATCH 3/7] Add ML-KEM with Curve25519 and NISTp384 1. Add GitHub action to test wolfSSL against OpenSSH using MLKEM. --- .github/workflows/interop-mlkem.yml | 100 ++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 .github/workflows/interop-mlkem.yml diff --git a/.github/workflows/interop-mlkem.yml b/.github/workflows/interop-mlkem.yml new file mode 100644 index 000000000..11fbe93df --- /dev/null +++ b/.github/workflows/interop-mlkem.yml @@ -0,0 +1,100 @@ +name: ML-KEM Interop Tests + +on: + pull_request: + branches: [ '*' ] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + wolfssl: v5.8.4-stable + openssh: V_10_2_P1 + +jobs: + build_wolfssl: + name: Build wolfSSL + runs-on: ubuntu-latest + timeout-minutes: 4 + steps: + - name: Checking cache for wolfSSL + uses: actions/cache@v4 + id: cache-wolfssl + with: + path: build-dir/ + key: wolfssh-mlkem-wolfssl-${{ env.wolfssl }} + lookup-only: true + + - name: Checkout, build, and install wolfSSL + if: steps.cache-wolfssl.outputs.cache-hit != 'true' + uses: wolfSSL/actions-build-autotools-project@v1 + with: + repository: wolfSSL/wolfssl + ref: ${{ env.wolfssl }} + path: wolfssl + configure: --enable-wolfssh --enable-mlkem --enable-ed25519 --enable-ed25519-stream --enable-curve25519 --enable-base64encode --enable-cryptonly --disable-examples --disable-crypttests + check: false + install: true + + build_openssh: + runs-on: ubuntu-latest + timeout-minutes: 4 + steps: + - name: Checking cache for OpenSSH + uses: actions/cache@v4 + id: cache-openssh + with: + path: build-dir/ + key: wolfssh-mlkem-openssh-${{ env.openssh }} + lookup-only: true + + - name: Checkout, build, and install wolfSSL + if: steps.cache-openssh.outputs.cache-hit != 'true' + uses: wolfSSL/actions-build-autotools-project@v1 + with: + repository: openssh/openssh-portable + ref: ${{ env.openssh }} + path: openssh + configure: + check: false + install: true + + build_wolfssh: + name: Build wolfSSH + runs-on: ubuntu-latest + timeout-minutes: 4 + needs: [build_wolfssl, build_openssh] + env: + build_dir: ${{ github.workspace }}/build-dir + steps: + - name: Checking cache for wolfSSL + uses: actions/cache@v4 + with: + path: build-dir/ + key: wolfssh-mlkem-wolfssl-${{ env.wolfssl }} + fail-on-cache-miss: true + + - name: Checking cache for OpenSSH + uses: actions/cache@v4 + with: + path: build-dir/ + key: wolfssh-mlkem-openssh-${{ env.openssh }} + fail-on-cache-miss: true + + - name: Checkout, build, and test wolfSSH + uses: wolfSSL/actions-build-autotools-project@v1 + with: + repository: wolfSSL/wolfssh + path: wolfssh + configure: --enable-debug --with-wolfssl=${{ env.build_dir }} + check: true + + - name: Run connect test + working-directory: ./wolfssh/ + run: | + mkdir -p /var/empty + ${{ env.build_dir }}/bin/ssh-keygen -f ~/.ssh/id_ed25519 -N "" -t ed25519 + cp ~/.ssh/id_ed25519.pub ~/.ssh/authorized_keys + ${{ env.build_dir }}/sbin/sshd -p 22222 -o KbdInteractiveAuthentication=no -o PasswordAuthentication=no -o KexAlgorithms=mlkem768x25519-sha256 + ./examples/client/client -u root -i ~/.ssh/id_ed25519 -j ~/.ssh/id_ed25519.pub -c "ls /" From 26c8d67968548f3354073d66ca82aa9d88c5f8d3 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Thu, 15 Jan 2026 19:52:02 -0800 Subject: [PATCH 4/7] test code --- .github/workflows/interop-mlkem.yml | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/.github/workflows/interop-mlkem.yml b/.github/workflows/interop-mlkem.yml index 11fbe93df..e5c407533 100644 --- a/.github/workflows/interop-mlkem.yml +++ b/.github/workflows/interop-mlkem.yml @@ -38,6 +38,7 @@ jobs: install: true build_openssh: + name: Build OpenSSH runs-on: ubuntu-latest timeout-minutes: 4 steps: @@ -56,12 +57,12 @@ jobs: repository: openssh/openssh-portable ref: ${{ env.openssh }} path: openssh - configure: + configure: --with-privsep-path=/tmp/empty check: false install: true build_wolfssh: - name: Build wolfSSH + name: Build and test wolfSSH runs-on: ubuntu-latest timeout-minutes: 4 needs: [build_wolfssl, build_openssh] @@ -87,14 +88,24 @@ jobs: with: repository: wolfSSL/wolfssh path: wolfssh - configure: --enable-debug --with-wolfssl=${{ env.build_dir }} + configure: --with-wolfssl=${{ env.build_dir }} check: true - - name: Run connect test + - name: Make test key working-directory: ./wolfssh/ run: | - mkdir -p /var/empty ${{ env.build_dir }}/bin/ssh-keygen -f ~/.ssh/id_ed25519 -N "" -t ed25519 cp ~/.ssh/id_ed25519.pub ~/.ssh/authorized_keys + + - name: Run connect wolfSSH to OpenSSH test + working-directory: ./wolfssh/ + run: | + mkdir -p /tmp/empty ${{ env.build_dir }}/sbin/sshd -p 22222 -o KbdInteractiveAuthentication=no -o PasswordAuthentication=no -o KexAlgorithms=mlkem768x25519-sha256 - ./examples/client/client -u root -i ~/.ssh/id_ed25519 -j ~/.ssh/id_ed25519.pub -c "ls /" + ./examples/client/client -u $(whoami) -i ~/.ssh/id_ed25519 -j ~/.ssh/id_ed25519.pub -c "ls /" + + - name: Run connect OpenSSH to wolfSSH test + working-directory: ./wolfssh/ + run: | + ./examples/echoserver/echoserver -p 22222 -x mlkem768x25519-sha256 -I runner:~/.ssh/authorized_keys & + ${{ env.build_dir }}/bin/ssh -p 22222 localhost ls / From b3ff089de005caa12f8777dd4cbdcf126a914583 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Thu, 15 Jan 2026 19:54:48 -0800 Subject: [PATCH 5/7] 2 --- .github/workflows/interop-mlkem.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/interop-mlkem.yml b/.github/workflows/interop-mlkem.yml index e5c407533..ea107b9f6 100644 --- a/.github/workflows/interop-mlkem.yml +++ b/.github/workflows/interop-mlkem.yml @@ -103,9 +103,10 @@ jobs: mkdir -p /tmp/empty ${{ env.build_dir }}/sbin/sshd -p 22222 -o KbdInteractiveAuthentication=no -o PasswordAuthentication=no -o KexAlgorithms=mlkem768x25519-sha256 ./examples/client/client -u $(whoami) -i ~/.ssh/id_ed25519 -j ~/.ssh/id_ed25519.pub -c "ls /" + ls /etc/ssh - name: Run connect OpenSSH to wolfSSH test working-directory: ./wolfssh/ run: | - ./examples/echoserver/echoserver -p 22222 -x mlkem768x25519-sha256 -I runner:~/.ssh/authorized_keys & - ${{ env.build_dir }}/bin/ssh -p 22222 localhost ls / + ./examples/echoserver/echoserver -p 22223 -x mlkem768x25519-sha256 -I runner:~/.ssh/authorized_keys & + ${{ env.build_dir }}/bin/ssh -p 22223 localhost ls / From 209034558d651653bfcd34ff02aef6a946035bf0 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Thu, 15 Jan 2026 20:01:57 -0800 Subject: [PATCH 6/7] 3 --- .github/workflows/interop-mlkem.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/interop-mlkem.yml b/.github/workflows/interop-mlkem.yml index ea107b9f6..a9af5d4b7 100644 --- a/.github/workflows/interop-mlkem.yml +++ b/.github/workflows/interop-mlkem.yml @@ -103,10 +103,9 @@ jobs: mkdir -p /tmp/empty ${{ env.build_dir }}/sbin/sshd -p 22222 -o KbdInteractiveAuthentication=no -o PasswordAuthentication=no -o KexAlgorithms=mlkem768x25519-sha256 ./examples/client/client -u $(whoami) -i ~/.ssh/id_ed25519 -j ~/.ssh/id_ed25519.pub -c "ls /" - ls /etc/ssh - name: Run connect OpenSSH to wolfSSH test working-directory: ./wolfssh/ run: | ./examples/echoserver/echoserver -p 22223 -x mlkem768x25519-sha256 -I runner:~/.ssh/authorized_keys & - ${{ env.build_dir }}/bin/ssh -p 22223 localhost ls / + ${{ env.build_dir }}/bin/ssh -o StrictHostKeyChecking=no -p 22223 localhost ls / From ec46a4efa98f350e0af8b8e89bc728cec6a16b54 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Thu, 15 Jan 2026 20:04:00 -0800 Subject: [PATCH 7/7] 4 --- .github/workflows/interop-mlkem.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/interop-mlkem.yml b/.github/workflows/interop-mlkem.yml index a9af5d4b7..a10348f66 100644 --- a/.github/workflows/interop-mlkem.yml +++ b/.github/workflows/interop-mlkem.yml @@ -88,7 +88,7 @@ jobs: with: repository: wolfSSL/wolfssh path: wolfssh - configure: --with-wolfssl=${{ env.build_dir }} + configure: --enable-debug --with-wolfssl=${{ env.build_dir }} check: true - name: Make test key