@@ -16493,19 +16493,28 @@ typedef struct {
1649316493 const char* keyList;
1649416494 byte firstPacketFollows;
1649516495 byte expectIgnore;
16496+ byte endpointType;
1649616497} FirstPacketFollowsCase;
1649716498
1649816499static const FirstPacketFollowsCase firstPacketFollowsCases[] = {
1649916500 { "follows=0, guesses irrelevant: flag stays off",
16500- FPF_KEX_BAD "," FPF_KEX_GOOD, FPF_KEY_BAD "," FPF_KEY_GOOD, 0, 0 },
16501+ FPF_KEX_BAD "," FPF_KEX_GOOD, FPF_KEY_BAD "," FPF_KEY_GOOD, 0, 0,
16502+ WOLFSSH_ENDPOINT_SERVER },
1650116503 { "follows=1, both guesses match: do not skip",
16502- FPF_KEX_GOOD, FPF_KEY_GOOD, 1, 0 },
16504+ FPF_KEX_GOOD, FPF_KEY_GOOD, 1, 0, WOLFSSH_ENDPOINT_SERVER },
1650316505 { "follows=1, KEX guess wrong: skip",
16504- FPF_KEX_BAD "," FPF_KEX_GOOD, FPF_KEY_GOOD, 1, 1 },
16506+ FPF_KEX_BAD "," FPF_KEX_GOOD, FPF_KEY_GOOD, 1, 1,
16507+ WOLFSSH_ENDPOINT_SERVER },
1650516508 { "follows=1, host-key guess wrong: skip", /* regression case */
16506- FPF_KEX_GOOD, FPF_KEY_BAD "," FPF_KEY_GOOD, 1, 1 },
16509+ FPF_KEX_GOOD, FPF_KEY_BAD "," FPF_KEY_GOOD, 1, 1,
16510+ WOLFSSH_ENDPOINT_SERVER },
1650716511 { "follows=1, both guesses wrong: skip",
16508- FPF_KEX_BAD "," FPF_KEX_GOOD, FPF_KEY_BAD "," FPF_KEY_GOOD, 1, 1 },
16512+ FPF_KEX_BAD "," FPF_KEX_GOOD, FPF_KEY_BAD "," FPF_KEY_GOOD, 1, 1,
16513+ WOLFSSH_ENDPOINT_SERVER },
16514+ /* A server never sends a guessed packet; the client ignores the flag. */
16515+ { "client, server follows=1, both guesses wrong: do not skip",
16516+ FPF_KEX_BAD "," FPF_KEX_GOOD, FPF_KEY_BAD "," FPF_KEY_GOOD, 1, 0,
16517+ WOLFSSH_ENDPOINT_CLIENT },
1650916518};
1651016519
1651116520static void RunFirstPacketFollowsCase(const FirstPacketFollowsCase* tc)
@@ -16516,7 +16525,7 @@ static void RunFirstPacketFollowsCase(const FirstPacketFollowsCase* tc)
1651616525 word32 payloadSz;
1651716526 word32 idx = 0;
1651816527
16519- ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER , NULL);
16528+ ctx = wolfSSH_CTX_new(tc->endpointType , NULL);
1652016529 AssertNotNull(ctx);
1652116530
1652216531 ssh = wolfSSH_new(ctx);
@@ -16535,6 +16544,11 @@ static void RunFirstPacketFollowsCase(const FirstPacketFollowsCase* tc)
1653516544 (void)wolfSSH_TestDoKexInit(ssh, payload, payloadSz, &idx);
1653616545
1653716546 AssertNotNull(ssh->handshake);
16547+ /* The algorithm ids are set only after first_packet_follows is read. */
16548+ if (ssh->handshake->peerEncryptId == ID_NONE) {
16549+ Fail(("DoKexInit parses past first_packet_follows (%s)",
16550+ tc->description), ("peerEncryptId == ID_NONE"));
16551+ }
1653816552 if (ssh->handshake->ignoreNextKexMsg != tc->expectIgnore) {
1653916553 Fail(("ignoreNextKexMsg == %u (%s)",
1654016554 tc->expectIgnore, tc->description),
@@ -16548,29 +16562,26 @@ static void RunFirstPacketFollowsCase(const FirstPacketFollowsCase* tc)
1654816562typedef int (*FirstPacketFollowsSkipFn)(WOLFSSH* ssh, byte* buf, word32 len,
1654916563 word32* idx);
1655016564
16551- /* With ignoreNextKexMsg set, the target Do* handler must consume the packet,
16552- * clear the flag, and not advance the peer 's state past KEXINIT_DONE. */
16565+ /* With ignoreNextKexMsg set, the server's Do* handler must consume the packet,
16566+ * clear the flag, and not advance the client 's state past KEXINIT_DONE. */
1655316567static void RunFirstPacketFollowsSkipCase(FirstPacketFollowsSkipFn fn,
16554- const char* label, byte endpointType, byte initState )
16568+ const char* label)
1655516569{
1655616570 WOLFSSH_CTX* ctx;
1655716571 WOLFSSH* ssh;
1655816572 byte payload[8];
1655916573 word32 idx = 0;
1656016574 int ret;
1656116575
16562- ctx = wolfSSH_CTX_new(endpointType , NULL);
16576+ ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER , NULL);
1656316577 AssertNotNull(ctx);
1656416578
1656516579 ssh = wolfSSH_new(ctx);
1656616580 AssertNotNull(ssh);
1656716581 AssertNotNull(ssh->handshake);
1656816582
1656916583 ssh->handshake->ignoreNextKexMsg = 1;
16570- if (endpointType == WOLFSSH_ENDPOINT_SERVER)
16571- ssh->clientState = initState;
16572- else
16573- ssh->serverState = initState;
16584+ ssh->clientState = CLIENT_KEXINIT_DONE;
1657416585
1657516586 /* Garbage payload that must never be parsed when skipped. */
1657616587 WMEMSET(payload, 0xAB, sizeof(payload));
@@ -16581,10 +16592,38 @@ static void RunFirstPacketFollowsSkipCase(FirstPacketFollowsSkipFn fn,
1658116592 }
1658216593 AssertIntEQ(idx, sizeof(payload));
1658316594 AssertIntEQ(ssh->handshake->ignoreNextKexMsg, 0);
16584- if (endpointType == WOLFSSH_ENDPOINT_SERVER)
16585- AssertIntEQ(ssh->clientState, initState);
16586- else
16587- AssertIntEQ(ssh->serverState, initState);
16595+ AssertIntEQ(ssh->clientState, CLIENT_KEXINIT_DONE);
16596+
16597+ wolfSSH_free(ssh);
16598+ wolfSSH_CTX_free(ctx);
16599+ }
16600+
16601+ /* The client never discards a server KEX message as a guess; even with the
16602+ * flag forced on, the handler must parse the packet and reject the garbage. */
16603+ static void RunFirstPacketFollowsClientNoSkipCase(FirstPacketFollowsSkipFn fn,
16604+ const char* label)
16605+ {
16606+ WOLFSSH_CTX* ctx;
16607+ WOLFSSH* ssh;
16608+ byte payload[8];
16609+ word32 idx = 0;
16610+ int ret;
16611+
16612+ ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
16613+ AssertNotNull(ctx);
16614+ ssh = wolfSSH_new(ctx);
16615+ AssertNotNull(ssh);
16616+ AssertNotNull(ssh->handshake);
16617+
16618+ ssh->handshake->ignoreNextKexMsg = 1;
16619+ ssh->serverState = SERVER_KEXINIT_DONE;
16620+ WMEMSET(payload, 0xAB, sizeof(payload));
16621+
16622+ ret = fn(ssh, payload, sizeof(payload), &idx);
16623+ if (ret == WS_SUCCESS)
16624+ Fail(("%s rejects the packet on a client", label), ("%d", ret));
16625+ AssertIntEQ(idx, 0);
16626+ AssertIntEQ(ssh->handshake->ignoreNextKexMsg, 1);
1658816627
1658916628 wolfSSH_free(ssh);
1659016629 wolfSSH_CTX_free(ctx);
@@ -16637,13 +16676,10 @@ static void RunFirstPacketFollowsCrossBoundaryCase(FirstPacketFollowsSkipFn fn,
1663716676
1663816677static void TestFirstPacketFollowsSkipped(void)
1663916678{
16640- RunFirstPacketFollowsSkipCase(wolfSSH_TestDoKexDhInit,
16641- "DoKexDhInit", WOLFSSH_ENDPOINT_SERVER, CLIENT_KEXINIT_DONE);
16679+ RunFirstPacketFollowsSkipCase(wolfSSH_TestDoKexDhInit, "DoKexDhInit");
1664216680#ifndef WOLFSSH_NO_DH_GEX_SHA256
1664316681 RunFirstPacketFollowsSkipCase(wolfSSH_TestDoKexDhGexRequest,
16644- "DoKexDhGexRequest", WOLFSSH_ENDPOINT_SERVER, CLIENT_KEXINIT_DONE);
16645- RunFirstPacketFollowsSkipCase(wolfSSH_TestDoKexDhGexGroup,
16646- "DoKexDhGexGroup", WOLFSSH_ENDPOINT_CLIENT, SERVER_KEXINIT_DONE);
16682+ "DoKexDhGexRequest");
1664716683 /* Guess/negotiation straddling the GEX boundary, both directions. */
1664816684#ifndef NO_WOLFSSH_SERVER
1664916685 RunFirstPacketFollowsCrossBoundaryCase(wolfSSH_TestDoKexDhInit,
@@ -16652,8 +16688,12 @@ static void TestFirstPacketFollowsSkipped(void)
1665216688 "DoKexDhGexRequest->KEXDH_INIT", MSGID_KEXDH_INIT);
1665316689#endif /* NO_WOLFSSH_SERVER */
1665416690#endif /* WOLFSSH_NO_DH_GEX_SHA256 */
16655- RunFirstPacketFollowsSkipCase(wolfSSH_TestDoKexDhReply,
16656- "DoKexDhReply", WOLFSSH_ENDPOINT_CLIENT, SERVER_KEXINIT_DONE);
16691+ RunFirstPacketFollowsClientNoSkipCase(wolfSSH_TestDoKexDhReply,
16692+ "DoKexDhReply");
16693+ #ifndef WOLFSSH_NO_DH_GEX_SHA256
16694+ RunFirstPacketFollowsClientNoSkipCase(wolfSSH_TestDoKexDhGexGroup,
16695+ "DoKexDhGexGroup");
16696+ #endif
1665716697}
1665816698
1665916699static void TestFirstPacketFollows(void)
0 commit comments