@@ -627,15 +627,16 @@ static void HandshakeInfoFree(HandshakeInfo* hs, void* heap)
627627 }
628628#endif
629629#ifndef WOLFSSH_NO_ECDH
630- /* privKey is a union; the Curve25519+ML-KEM case also sets
631- * useEccMlKem but generates a curve25519 key, so free it below. */
632- if (hs->useEcc || (hs->useEccMlKem && !hs->useCurve25519MlKem)) {
630+ /* privKey is a union; the Curve25519+ML-KEM hybrid sets both
631+ * useEccMlKem and useCurve25519 but generates a curve25519 key, so
632+ * it is freed below rather than here. */
633+ if (hs->useEcc || (hs->useEccMlKem && !hs->useCurve25519)) {
633634 wc_ecc_free(&hs->privKey.ecc);
634635 }
635636#endif
636637#if !defined(WOLFSSH_NO_CURVE25519_SHA256) || \
637638 !defined(WOLFSSH_NO_CURVE25519_MLKEM768_SHA256)
638- if (hs->useCurve25519 || hs->useCurve25519MlKem ) {
639+ if (hs->useCurve25519) {
639640 wc_curve25519_free(&hs->privKey.curve25519);
640641 }
641642#endif
@@ -6078,12 +6079,14 @@ static int KeyAgree_client(WOLFSSH* ssh, byte hashId, const byte* f, word32 fSz)
60786079 else if (ssh->handshake->useEcc) {
60796080 ret = KeyAgreeEcdh_client(ssh, hashId, f, fSz);
60806081 }
6081- else if (ssh->handshake->useCurve25519) {
6082- ret = KeyAgreeCurve25519_client(ssh, hashId, f, fSz);
6083- }
6082+ /* Check useEccMlKem before useCurve25519: the Curve25519+ML-KEM hybrid
6083+ * sets both flags and must route to the hybrid agreement. */
60846084 else if (ssh->handshake->useEccMlKem) {
60856085 ret = KeyAgreeEcdhMlKem_client(ssh, hashId, f, fSz);
60866086 }
6087+ else if (ssh->handshake->useCurve25519) {
6088+ ret = KeyAgreeCurve25519_client(ssh, hashId, f, fSz);
6089+ }
60876090 else {
60886091 ret = WS_INVALID_ALGO_ID;
60896092 }
@@ -14000,8 +14003,11 @@ int SendKexDhInit(WOLFSSH* ssh)
1400014003#endif
1400114004#ifndef WOLFSSH_NO_CURVE25519_MLKEM768_SHA256
1400214005 case ID_CURVE25519_MLKEM768_SHA256:
14006+ /* The Curve25519+ML-KEM hybrid is identified by both flags:
14007+ * useEccMlKem (ML-KEM component) and useCurve25519 (classical
14008+ * component). */
1400314009 ssh->handshake->useEccMlKem = 1;
14004- ssh->handshake->useCurve25519MlKem = 1;
14010+ ssh->handshake->useCurve25519 = 1;
1400514011 msgId = MSGID_KEXKEM_INIT;
1400614012 break;
1400714013#endif
@@ -14027,8 +14033,11 @@ int SendKexDhInit(WOLFSSH* ssh)
1402714033 e, &eSz);
1402814034#endif
1402914035 }
14030- #ifndef WOLFSSH_NO_CURVE25519_SHA256
14036+ #if !defined(WOLFSSH_NO_CURVE25519_SHA256) || \
14037+ !defined(WOLFSSH_NO_CURVE25519_MLKEM768_SHA256)
1403114038 else if (ssh->handshake->useCurve25519) {
14039+ /* Plain Curve25519 or the Curve25519+ML-KEM hybrid; both need a
14040+ * Curve25519 key. The ML-KEM component, if any, is added below. */
1403214041 curve25519_key* privKey = &ssh->handshake->privKey.curve25519;
1403314042 if (ret == 0)
1403414043 ret = wc_curve25519_init_ex(privKey, ssh->ctx->heap,
@@ -14043,25 +14052,7 @@ int SendKexDhInit(WOLFSSH* ssh)
1404314052 PRIVATE_KEY_LOCK();
1404414053 }
1404514054 }
14046- #endif /* ! WOLFSSH_NO_CURVE25519_SHA256 */
14047- #ifndef WOLFSSH_NO_CURVE25519_MLKEM768_SHA256
14048- else if (ssh->handshake->useCurve25519MlKem) {
14049- /* Handle Curve25519+ML-KEM variant - generate Curve25519 key */
14050- curve25519_key* privKey = &ssh->handshake->privKey.curve25519;
14051- if (ret == 0)
14052- ret = wc_curve25519_init_ex(privKey, ssh->ctx->heap,
14053- INVALID_DEVID);
14054- if (ret == 0)
14055- ret = wc_curve25519_make_key(ssh->rng, CURVE25519_KEYSIZE,
14056- privKey);
14057- if (ret == 0) {
14058- PRIVATE_KEY_UNLOCK();
14059- ret = wc_curve25519_export_public_ex(privKey, e, &eSz,
14060- EC25519_LITTLE_ENDIAN);
14061- PRIVATE_KEY_LOCK();
14062- }
14063- }
14064- #endif /* WOLFSSH_NO_CURVE25519_MLKEM768_SHA256 */
14055+ #endif /* Curve25519 or Curve25519+ML-KEM */
1406514056 else if (ssh->handshake->useEcc
1406614057#if !defined(WOLFSSH_NO_NISTP256_MLKEM768_SHA256) || \
1406714058 !defined(WOLFSSH_NO_NISTP384_MLKEM1024_SHA384)
0 commit comments