@@ -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
@@ -6096,12 +6097,14 @@ static int KeyAgree_client(WOLFSSH* ssh, byte hashId, const byte* f, word32 fSz)
60966097 else if (ssh->handshake->useEcc) {
60976098 ret = KeyAgreeEcdh_client(ssh, hashId, f, fSz);
60986099 }
6099- else if (ssh->handshake->useCurve25519) {
6100- ret = KeyAgreeCurve25519_client(ssh, hashId, f, fSz);
6101- }
6100+ /* Check useEccMlKem before useCurve25519: the Curve25519+ML-KEM hybrid
6101+ * sets both flags and must route to the hybrid agreement. */
61026102 else if (ssh->handshake->useEccMlKem) {
61036103 ret = KeyAgreeEcdhMlKem_client(ssh, hashId, f, fSz);
61046104 }
6105+ else if (ssh->handshake->useCurve25519) {
6106+ ret = KeyAgreeCurve25519_client(ssh, hashId, f, fSz);
6107+ }
61056108 else {
61066109 ret = WS_INVALID_ALGO_ID;
61076110 }
@@ -14001,8 +14004,11 @@ int SendKexDhInit(WOLFSSH* ssh)
1400114004#endif
1400214005#ifndef WOLFSSH_NO_CURVE25519_MLKEM768_SHA256
1400314006 case ID_CURVE25519_MLKEM768_SHA256:
14007+ /* The Curve25519+ML-KEM hybrid is identified by both flags:
14008+ * useEccMlKem (ML-KEM component) and useCurve25519 (classical
14009+ * component). */
1400414010 ssh->handshake->useEccMlKem = 1;
14005- ssh->handshake->useCurve25519MlKem = 1;
14011+ ssh->handshake->useCurve25519 = 1;
1400614012 msgId = MSGID_KEXKEM_INIT;
1400714013 break;
1400814014#endif
@@ -14028,8 +14034,11 @@ int SendKexDhInit(WOLFSSH* ssh)
1402814034 e, &eSz);
1402914035#endif
1403014036 }
14031- #ifndef WOLFSSH_NO_CURVE25519_SHA256
14037+ #if !defined(WOLFSSH_NO_CURVE25519_SHA256) || \
14038+ !defined(WOLFSSH_NO_CURVE25519_MLKEM768_SHA256)
1403214039 else if (ssh->handshake->useCurve25519) {
14040+ /* Plain Curve25519 or the Curve25519+ML-KEM hybrid; both need a
14041+ * Curve25519 key. The ML-KEM component, if any, is added below. */
1403314042 curve25519_key* privKey = &ssh->handshake->privKey.curve25519;
1403414043 if (ret == 0)
1403514044 ret = wc_curve25519_init_ex(privKey, ssh->ctx->heap,
@@ -14044,25 +14053,7 @@ int SendKexDhInit(WOLFSSH* ssh)
1404414053 PRIVATE_KEY_LOCK();
1404514054 }
1404614055 }
14047- #endif /* ! WOLFSSH_NO_CURVE25519_SHA256 */
14048- #ifndef WOLFSSH_NO_CURVE25519_MLKEM768_SHA256
14049- else if (ssh->handshake->useCurve25519MlKem) {
14050- /* Handle Curve25519+ML-KEM variant - generate Curve25519 key */
14051- curve25519_key* privKey = &ssh->handshake->privKey.curve25519;
14052- if (ret == 0)
14053- ret = wc_curve25519_init_ex(privKey, ssh->ctx->heap,
14054- INVALID_DEVID);
14055- if (ret == 0)
14056- ret = wc_curve25519_make_key(ssh->rng, CURVE25519_KEYSIZE,
14057- privKey);
14058- if (ret == 0) {
14059- PRIVATE_KEY_UNLOCK();
14060- ret = wc_curve25519_export_public_ex(privKey, e, &eSz,
14061- EC25519_LITTLE_ENDIAN);
14062- PRIVATE_KEY_LOCK();
14063- }
14064- }
14065- #endif /* WOLFSSH_NO_CURVE25519_MLKEM768_SHA256 */
14056+ #endif /* Curve25519 or Curve25519+ML-KEM */
1406614057 else if (ssh->handshake->useEcc
1406714058#if !defined(WOLFSSH_NO_NISTP256_MLKEM768_SHA256) || \
1406814059 !defined(WOLFSSH_NO_NISTP384_MLKEM1024_SHA384)
0 commit comments