Skip to content

Commit bafde45

Browse files
committed
First Kex Packet Follows Test
Add a regression for checking the `first_kex_packet_follows` flag versus the guesses for KEX algorithm and public key algorithm.
1 parent 88ca5ba commit bafde45

3 files changed

Lines changed: 165 additions & 0 deletions

File tree

src/internal.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,19 @@ int wolfSSH_TestIsMessageAllowed(WOLFSSH* ssh, byte msg, byte state)
858858
{
859859
return IsMessageAllowed(ssh, msg, state);
860860
}
861+
862+
static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx);
863+
static int DoKexDhInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx);
864+
865+
int wolfSSH_TestDoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
866+
{
867+
return DoKexInit(ssh, buf, len, idx);
868+
}
869+
870+
int wolfSSH_TestDoKexDhInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
871+
{
872+
return DoKexDhInit(ssh, buf, len, idx);
873+
}
861874
#endif
862875

863876

tests/regress.c

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1886,6 +1886,149 @@ static void TestKeyboardResponseNullCtx(WOLFSSH* ssh)
18861886
#endif /* WOLFSSH_KEYBOARD_INTERACTIVE */
18871887

18881888

1889+
#if !defined(WOLFSSH_NO_ECDH_SHA2_NISTP256) \
1890+
&& !defined(WOLFSSH_NO_RSA) \
1891+
&& !defined(WOLFSSH_NO_CURVE25519_SHA256) \
1892+
&& !defined(WOLFSSH_NO_RSA_SHA2_256)
1893+
1894+
#define FPF_KEX_GOOD "ecdh-sha2-nistp256"
1895+
#define FPF_KEX_BAD "curve25519-sha256"
1896+
#define FPF_KEY_GOOD "ssh-rsa"
1897+
#define FPF_KEY_BAD "rsa-sha2-256"
1898+
1899+
static word32 BuildKexInitPayload(const char* kexList, const char* keyList,
1900+
byte firstPacketFollows, byte* out, word32 outSz)
1901+
{
1902+
word32 idx = 0;
1903+
1904+
/* cookie */
1905+
AssertTrue(idx + COOKIE_SZ <= outSz);
1906+
WMEMSET(out + idx, 0, COOKIE_SZ);
1907+
idx += COOKIE_SZ;
1908+
1909+
idx = AppendString(out, outSz, idx, kexList);
1910+
idx = AppendString(out, outSz, idx, keyList);
1911+
idx = AppendString(out, outSz, idx, "aes128-ctr");
1912+
idx = AppendString(out, outSz, idx, "aes128-ctr");
1913+
idx = AppendString(out, outSz, idx, "hmac-sha2-256");
1914+
idx = AppendString(out, outSz, idx, "hmac-sha2-256");
1915+
idx = AppendString(out, outSz, idx, "none");
1916+
idx = AppendString(out, outSz, idx, "none");
1917+
idx = AppendString(out, outSz, idx, "");
1918+
idx = AppendString(out, outSz, idx, "");
1919+
1920+
idx = AppendByte(out, outSz, idx, firstPacketFollows);
1921+
idx = AppendUint32(out, outSz, idx, 0); /* reserved */
1922+
1923+
return idx;
1924+
}
1925+
1926+
typedef struct {
1927+
const char* description;
1928+
const char* kexList;
1929+
const char* keyList;
1930+
byte firstPacketFollows;
1931+
byte expectIgnore;
1932+
} FirstPacketFollowsCase;
1933+
1934+
static const FirstPacketFollowsCase firstPacketFollowsCases[] = {
1935+
{ "follows=0, guesses irrelevant: flag stays off",
1936+
FPF_KEX_BAD "," FPF_KEX_GOOD, FPF_KEY_BAD "," FPF_KEY_GOOD, 0, 0 },
1937+
{ "follows=1, both guesses match: do not skip",
1938+
FPF_KEX_GOOD, FPF_KEY_GOOD, 1, 0 },
1939+
{ "follows=1, KEX guess wrong: skip",
1940+
FPF_KEX_BAD "," FPF_KEX_GOOD, FPF_KEY_GOOD, 1, 1 },
1941+
{ "follows=1, host-key guess wrong: skip", /* regression case */
1942+
FPF_KEX_GOOD, FPF_KEY_BAD "," FPF_KEY_GOOD, 1, 1 },
1943+
{ "follows=1, both guesses wrong: skip",
1944+
FPF_KEX_BAD "," FPF_KEX_GOOD, FPF_KEY_BAD "," FPF_KEY_GOOD, 1, 1 },
1945+
};
1946+
1947+
static void RunFirstPacketFollowsCase(const FirstPacketFollowsCase* tc)
1948+
{
1949+
WOLFSSH_CTX* ctx;
1950+
WOLFSSH* ssh;
1951+
byte payload[512];
1952+
word32 payloadSz;
1953+
word32 idx = 0;
1954+
1955+
ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL);
1956+
AssertNotNull(ctx);
1957+
1958+
ssh = wolfSSH_new(ctx);
1959+
AssertNotNull(ssh);
1960+
1961+
AssertIntEQ(wolfSSH_SetAlgoListKex(ssh, FPF_KEX_GOOD), WS_SUCCESS);
1962+
AssertIntEQ(wolfSSH_SetAlgoListKey(ssh, FPF_KEY_GOOD), WS_SUCCESS);
1963+
1964+
payloadSz = BuildKexInitPayload(tc->kexList, tc->keyList,
1965+
tc->firstPacketFollows, payload, sizeof(payload));
1966+
1967+
/* DoKexInit's tail hashes and sends a response; on a stripped-down
1968+
* WOLFSSH without a loaded host key or a primed peer proto id, that
1969+
* tail errors. We only care about the parse path up through
1970+
* first_packet_follows, where ignoreNextKexMsg is set. */
1971+
(void)wolfSSH_TestDoKexInit(ssh, payload, payloadSz, &idx);
1972+
1973+
AssertNotNull(ssh->handshake);
1974+
if (ssh->handshake->ignoreNextKexMsg != tc->expectIgnore) {
1975+
Fail(("ignoreNextKexMsg == %u (%s)",
1976+
tc->expectIgnore, tc->description),
1977+
("%u", ssh->handshake->ignoreNextKexMsg));
1978+
}
1979+
1980+
wolfSSH_free(ssh);
1981+
wolfSSH_CTX_free(ctx);
1982+
}
1983+
1984+
/* With ignoreNextKexMsg set, DoKexDhInit must consume the packet, clear the
1985+
* flag, and not advance clientState to CLIENT_KEXDH_INIT_DONE. */
1986+
static void TestFirstPacketFollowsDhInitSkipped(void)
1987+
{
1988+
WOLFSSH_CTX* ctx;
1989+
WOLFSSH* ssh;
1990+
byte dhPayload[8];
1991+
word32 idx = 0;
1992+
int ret;
1993+
1994+
ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL);
1995+
AssertNotNull(ctx);
1996+
1997+
ssh = wolfSSH_new(ctx);
1998+
AssertNotNull(ssh);
1999+
AssertNotNull(ssh->handshake);
2000+
2001+
ssh->handshake->ignoreNextKexMsg = 1;
2002+
ssh->clientState = CLIENT_KEXINIT_DONE;
2003+
2004+
/* Garbage payload — must never be parsed when skipped. */
2005+
WMEMSET(dhPayload, 0xAB, sizeof(dhPayload));
2006+
2007+
ret = wolfSSH_TestDoKexDhInit(ssh, dhPayload, sizeof(dhPayload), &idx);
2008+
AssertIntEQ(ret, WS_SUCCESS);
2009+
AssertIntEQ(idx, sizeof(dhPayload));
2010+
AssertIntEQ(ssh->handshake->ignoreNextKexMsg, 0);
2011+
AssertIntEQ(ssh->clientState, CLIENT_KEXINIT_DONE);
2012+
2013+
wolfSSH_free(ssh);
2014+
wolfSSH_CTX_free(ctx);
2015+
}
2016+
2017+
static void TestFirstPacketFollows(void)
2018+
{
2019+
size_t i;
2020+
size_t n = sizeof(firstPacketFollowsCases)
2021+
/ sizeof(firstPacketFollowsCases[0]);
2022+
2023+
for (i = 0; i < n; i++) {
2024+
RunFirstPacketFollowsCase(&firstPacketFollowsCases[i]);
2025+
}
2026+
TestFirstPacketFollowsDhInitSkipped();
2027+
}
2028+
2029+
#endif /* first_packet_follows coverage guard */
2030+
2031+
18892032
int main(int argc, char** argv)
18902033
{
18912034
WOLFSSH_CTX* ctx;
@@ -1926,6 +2069,11 @@ int main(int argc, char** argv)
19262069
TestAgentChannelNullAgentSendsOpenFail();
19272070
#endif
19282071
TestKexInitRejectedWhenKeying(ssh);
2072+
#if !defined(WOLFSSH_NO_ECDH_SHA2_NISTP256) && !defined(WOLFSSH_NO_RSA) \
2073+
&& !defined(WOLFSSH_NO_CURVE25519_SHA256) \
2074+
&& !defined(WOLFSSH_NO_RSA_SHA2_256)
2075+
TestFirstPacketFollows();
2076+
#endif
19292077
TestDisconnectSetsDisconnectError();
19302078
TestClientBuffersIdempotent();
19312079
TestPasswordEofNoCrash();

wolfssh/internal.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1326,6 +1326,10 @@ enum WS_MessageIdLimits {
13261326
WOLFSSH_API int wolfSSH_TestIsMessageAllowed(WOLFSSH* ssh, byte msg,
13271327
byte state);
13281328
WOLFSSH_API int wolfSSH_TestDoReceive(WOLFSSH* ssh);
1329+
WOLFSSH_API int wolfSSH_TestDoKexInit(WOLFSSH* ssh, byte* buf,
1330+
word32 len, word32* idx);
1331+
WOLFSSH_API int wolfSSH_TestDoKexDhInit(WOLFSSH* ssh, byte* buf,
1332+
word32 len, word32* idx);
13291333
#ifndef WOLFSSH_NO_DH_GEX_SHA256
13301334
WOLFSSH_API int wolfSSH_TestValidateKexDhGexGroup(const byte* primeGroup,
13311335
word32 primeGroupSz, const byte* generator, word32 generatorSz,

0 commit comments

Comments
 (0)