Simplify redundant KEX method flags

- remove redundant useCurve25519MlKem HandshakeInfo flag
- merge the duplicate Curve25519 keygen branch in SendKexDhInit
- order the client KeyAgree dispatch to match the combined flags
- rename the surviving flags for clarity: useEcc->useEcdh and
  useEccMlKem->useMlKem
- make the flag set orthogonal: each ML-KEM hybrid now sets its
  classical-component flag (useEcdh or useCurve25519) alongside useMlKem,
  so the four bits independently describe every KEX family
- apply the same flag handling to the server SendKexDhReply path
pull/1086/head
John Safranek 2026-06-22 11:48:51 -07:00 committed by Aidan Garske
parent 71d8529306
commit dcee1ffa87
3 changed files with 59 additions and 67 deletions

View File

@ -642,15 +642,17 @@ static void HandshakeInfoFree(HandshakeInfo* hs, void* heap)
}
#endif
#ifndef WOLFSSH_NO_ECDH
/* privKey is a union; Curve25519+ML-KEM sets useEccMlKem but uses
* a curve25519 key, freed in the curve25519 block below. */
if (hs->useEcc || (hs->useEccMlKem && !hs->useCurve25519MlKem)) {
/* privKey is a union; the Curve25519+ML-KEM hybrid sets both
* useMlKem and useCurve25519 but generates a curve25519 key, which is
* freed below. The NIST ECC hybrid sets useEcdh, so useEcdh alone
* covers every case that generates an ecc key. */
if (hs->useEcdh) {
wc_ecc_free(&hs->privKey.ecc);
}
#endif
#if !defined(WOLFSSH_NO_CURVE25519_SHA256) || \
!defined(WOLFSSH_NO_CURVE25519_MLKEM768_SHA256)
if (hs->useCurve25519 || hs->useCurve25519MlKem) {
if (hs->useCurve25519) {
wc_curve25519_free(&hs->privKey.curve25519);
}
#endif
@ -6584,15 +6586,18 @@ static int KeyAgree_client(WOLFSSH* ssh, byte hashId, const byte* f, word32 fSz)
if (ssh->handshake->useDh) {
ret = KeyAgreeDh_client(ssh, hashId, f, fSz);
}
else if (ssh->handshake->useEcc) {
/* Check useMlKem before the plain classical flags: a hybrid sets both
* useMlKem and its classical flag (useEcdh or useCurve25519) and must
* route to the hybrid agreement. */
else if (ssh->handshake->useMlKem) {
ret = KeyAgreeEcdhMlKem_client(ssh, hashId, f, fSz);
}
else if (ssh->handshake->useEcdh) {
ret = KeyAgreeEcdh_client(ssh, hashId, f, fSz);
}
else if (ssh->handshake->useCurve25519) {
ret = KeyAgreeCurve25519_client(ssh, hashId, f, fSz);
}
else if (ssh->handshake->useEccMlKem) {
ret = KeyAgreeEcdhMlKem_client(ssh, hashId, f, fSz);
}
else {
ret = WS_INVALID_ALGO_ID;
}
@ -6821,7 +6826,7 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
/* Hash in the shared secret K. */
if (ret == WS_SUCCESS) {
if (!ssh->handshake->useEccMlKem) {
if (!ssh->handshake->useMlKem) {
ret = CreateMpint(ssh->k, &ssh->kSz, &kPad);
}
}
@ -7001,8 +7006,8 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
}
if (ret == WS_SUCCESS) {
/* If we aren't using ECC with ML-KEM, use padding. */
ret = GenerateKeys(ssh, hashId, !ssh->handshake->useEccMlKem);
/* If we aren't using an ML-KEM hybrid, use padding. */
ret = GenerateKeys(ssh, hashId, !ssh->handshake->useMlKem);
}
if (ret == WS_SUCCESS) {
@ -14224,9 +14229,9 @@ int SendKexDhReply(WOLFSSH* ssh)
#endif
byte msgId = 0;
byte useDh = 0;
byte useEcc = 0;
byte useEcdh = 0;
byte useCurve25519 = 0;
byte useEccMlKem = 0;
byte useMlKem = 0;
WLOG(WS_LOG_DEBUG, "Entering SendKexDhReply()");
@ -14314,19 +14319,19 @@ int SendKexDhReply(WOLFSSH* ssh)
#endif
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256
case ID_ECDH_SHA2_NISTP256:
useEcc = 1;
useEcdh = 1;
msgId = MSGID_KEXDH_REPLY;
break;
#endif
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP384
case ID_ECDH_SHA2_NISTP384:
useEcc = 1;
useEcdh = 1;
msgId = MSGID_KEXDH_REPLY;
break;
#endif
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP521
case ID_ECDH_SHA2_NISTP521:
useEcc = 1;
useEcdh = 1;
msgId = MSGID_KEXDH_REPLY;
break;
#endif
@ -14339,19 +14344,22 @@ int SendKexDhReply(WOLFSSH* ssh)
#endif
#ifndef WOLFSSH_NO_NISTP256_MLKEM768_SHA256
case ID_NISTP256_MLKEM768_SHA256:
useEccMlKem = 1;
useEcdh = 1;
useMlKem = 1;
msgId = MSGID_KEXKEM_REPLY;
break;
#endif
#ifndef WOLFSSH_NO_NISTP384_MLKEM1024_SHA384
case ID_NISTP384_MLKEM1024_SHA384:
useEccMlKem = 1;
useEcdh = 1;
useMlKem = 1;
msgId = MSGID_KEXKEM_REPLY;
break;
#endif
#ifndef WOLFSSH_NO_CURVE25519_MLKEM768_SHA256
case ID_CURVE25519_MLKEM768_SHA256:
useEccMlKem = 1;
useCurve25519 = 1;
useMlKem = 1;
msgId = MSGID_KEXKEM_REPLY;
break;
#endif
@ -14393,19 +14401,22 @@ int SendKexDhReply(WOLFSSH* ssh)
if (useDh) {
ret = KeyAgreeDh_server(ssh, hashId, f_ptr, &fSz);
}
else if (useEcc) {
/* Check useMlKem before the plain classical flags: a hybrid sets
* both useMlKem and its classical flag. */
else if (useMlKem) {
ret = KeyAgreeEcdhMlKem_server(ssh, hashId, f_ptr, &fSz);
}
else if (useEcdh) {
ret = KeyAgreeEcdh_server(ssh, hashId, f_ptr, &fSz);
}
else if (useCurve25519) {
ret = KeyAgreeCurve25519_server(ssh, hashId, f_ptr, &fSz);
}
else if (useEccMlKem) {
ret = KeyAgreeEcdhMlKem_server(ssh, hashId, f_ptr, &fSz);
}
}
/* Hash in the server's DH f-value. */
if (ret == 0 && (useDh || useEcc)) {
/* Hash in the server's DH f-value. Only plain DH and plain ECDH
* encode f as an mpint. */
if (ret == 0 && (useDh || (useEcdh && !useMlKem))) {
ret = CreateMpint(f_ptr, &fSz, &fPad);
}
if (ret == 0) {
@ -14421,7 +14432,7 @@ int SendKexDhReply(WOLFSSH* ssh)
}
/* Hash in the shared secret K. */
if (ret == 0 && !useEccMlKem) {
if (ret == 0 && !useMlKem) {
ret = CreateMpint(ssh->k, &ssh->kSz, &kPad);
}
if (ret == 0) {
@ -14511,8 +14522,8 @@ int SendKexDhReply(WOLFSSH* ssh)
}
if (ret == WS_SUCCESS) {
/* If we aren't using ECC with ML-KEM, use padding. */
ret = GenerateKeys(ssh, hashId, !useEccMlKem);
/* If we aren't using an ML-KEM hybrid, use padding. */
ret = GenerateKeys(ssh, hashId, !useMlKem);
}
/* Get the buffer, copy the packet data, once f is laid into the buffer,
@ -15012,19 +15023,19 @@ int SendKexDhInit(WOLFSSH* ssh)
#endif
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256
case ID_ECDH_SHA2_NISTP256:
ssh->handshake->useEcc = 1;
ssh->handshake->useEcdh = 1;
msgId = MSGID_KEXECDH_INIT;
break;
#endif
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP384
case ID_ECDH_SHA2_NISTP384:
ssh->handshake->useEcc = 1;
ssh->handshake->useEcdh = 1;
msgId = MSGID_KEXECDH_INIT;
break;
#endif
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP521
case ID_ECDH_SHA2_NISTP521:
ssh->handshake->useEcc = 1;
ssh->handshake->useEcdh = 1;
msgId = MSGID_KEXECDH_INIT;
break;
#endif
@ -15037,20 +15048,22 @@ int SendKexDhInit(WOLFSSH* ssh)
#endif
#ifndef WOLFSSH_NO_NISTP256_MLKEM768_SHA256
case ID_NISTP256_MLKEM768_SHA256:
ssh->handshake->useEccMlKem = 1;
ssh->handshake->useEcdh = 1;
ssh->handshake->useMlKem = 1;
msgId = MSGID_KEXKEM_INIT;
break;
#endif
#ifndef WOLFSSH_NO_NISTP384_MLKEM1024_SHA384
case ID_NISTP384_MLKEM1024_SHA384:
ssh->handshake->useEccMlKem = 1;
ssh->handshake->useEcdh = 1;
ssh->handshake->useMlKem = 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;
ssh->handshake->useCurve25519 = 1;
ssh->handshake->useMlKem = 1;
msgId = MSGID_KEXKEM_INIT;
break;
#endif
@ -15076,8 +15089,11 @@ int SendKexDhInit(WOLFSSH* ssh)
e, &eSz);
#endif
}
#ifndef WOLFSSH_NO_CURVE25519_SHA256
#if !defined(WOLFSSH_NO_CURVE25519_SHA256) || \
!defined(WOLFSSH_NO_CURVE25519_MLKEM768_SHA256)
else if (ssh->handshake->useCurve25519) {
/* Plain Curve25519 or the Curve25519+ML-KEM hybrid; both need a
* Curve25519 key. The ML-KEM component, if any, is added below. */
curve25519_key* privKey = &ssh->handshake->privKey.curve25519;
if (ret == 0)
ret = wc_curve25519_init_ex(privKey, ssh->ctx->heap,
@ -15092,31 +15108,8 @@ int SendKexDhInit(WOLFSSH* ssh)
PRIVATE_KEY_LOCK();
}
}
#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
#if !defined(WOLFSSH_NO_NISTP256_MLKEM768_SHA256) || \
!defined(WOLFSSH_NO_NISTP384_MLKEM1024_SHA384)
|| ssh->handshake->useEccMlKem
#endif
) {
#endif /* Curve25519 or Curve25519+ML-KEM */
else if (ssh->handshake->useEcdh) {
#if !defined(WOLFSSH_NO_ECDH)
ecc_key* privKey = &ssh->handshake->privKey.ecc;
int primeId = wcPrimeForId(ssh->handshake->kexId);
@ -15151,7 +15144,7 @@ int SendKexDhInit(WOLFSSH* ssh)
#if !defined(WOLFSSH_NO_NISTP256_MLKEM768_SHA256) || \
!defined(WOLFSSH_NO_NISTP384_MLKEM1024_SHA384) || \
!defined(WOLFSSH_NO_CURVE25519_MLKEM768_SHA256)
if (ret == WS_SUCCESS && ssh->handshake->useEccMlKem) {
if (ret == WS_SUCCESS && ssh->handshake->useMlKem) {
MlKemKey kem;
word32 length_publickey = 0;
word32 length_privatekey = 0;
@ -15209,7 +15202,7 @@ int SendKexDhInit(WOLFSSH* ssh)
#if !defined(WOLFSSH_NO_NISTP256_MLKEM768_SHA256) || \
!defined(WOLFSSH_NO_NISTP384_MLKEM1024_SHA384) || \
!defined(WOLFSSH_NO_CURVE25519_MLKEM768_SHA256)
&& !ssh->handshake->useEccMlKem
&& !ssh->handshake->useMlKem
#endif
#ifndef WOLFSSH_NO_CURVE25519_SHA256
&& !ssh->handshake->useCurve25519

View File

@ -5765,7 +5765,7 @@ static int test_KeyAgreeEcdh_client_rejectsOffCurvePoint(void)
result = -772;
goto out;
}
ssh->handshake->useEcc = 1;
ssh->handshake->useEcdh = 1;
#ifdef HAVE_WC_ECC_SET_RNG
if (wc_ecc_set_rng(&ssh->handshake->privKey.ecc, ssh->rng) != 0) {
result = -774;

View File

@ -736,10 +736,9 @@ typedef struct HandshakeInfo {
byte ignoreNextKexMsg:1;
byte useDh:1;
byte useEcc:1;
byte useEccMlKem:1;
byte useEcdh:1;
byte useCurve25519:1;
byte useCurve25519MlKem:1;
byte useMlKem:1;
#ifdef WOLFSSH_TPM
byte useTpm:1;
#endif