@@ -797,6 +797,7 @@ typedef struct DuplexEndpoint {
797797 /* What this endpoint put in its own plaintext KEXINIT */
798798 byte sentStrictKexMarker;
799799 byte sentExtInfoMarker;
800+ byte failSendMsgId; /* nonzero: fail the send of a packet with this id */
800801} DuplexEndpoint;
801802
802803typedef struct {
@@ -1505,6 +1506,12 @@ static int DuplexSend(WOLFSSH* ssh, void* buf, word32 sz, void* ctx)
15051506 return WS_CBIO_ERR_GENERAL;
15061507 }
15071508
1509+ if (endpoint->failSendMsgId != 0 &&
1510+ outputSz > UINT32_SZ + PAD_LENGTH_SZ &&
1511+ output[UINT32_SZ + PAD_LENGTH_SZ] == endpoint->failSendMsgId) {
1512+ return WS_CBIO_ERR_GENERAL;
1513+ }
1514+
15081515 if (endpoint->mutator != NULL &&
15091516 endpoint->mutator->enabled &&
15101517 IsTerrapinMode(endpoint->mutator->mode) &&
@@ -1741,29 +1748,34 @@ static void RunKexReplyHandshake(KexReplyHarness* harness,
17411748}
17421749
17431750/* The shared secret K does not outlive the key exchange. */
1744- static void AssertKexSecretWiped (const WOLFSSH* ssh)
1751+ static int KexSecretWiped (const WOLFSSH* ssh)
17451752{
17461753 word32 i;
17471754
1748- AssertIntEQ(ssh->kSz, 0);
1755+ if (ssh->kSz != 0)
1756+ return 0;
17491757 for (i = 0; i < (word32)sizeof(ssh->k); i++) {
1750- AssertIntEQ(ssh->k[i], 0);
1758+ if (ssh->k[i] != 0)
1759+ return 0;
17511760 }
1761+ return 1;
17521762}
17531763
1754- static void AssertHandshakeSucceeds(const char* keyAlgo, const char* keyPath)
1764+ /* A NULL kexAlgo uses the harness default. */
1765+ static void AssertHandshakeSucceedsKex(const char* kexAlgo,
1766+ const char* keyAlgo, const char* keyPath)
17551767{
17561768 KexReplyHarness harness;
17571769 KexReplyRunResult result;
17581770
1759- InitKexReplyHarnessEx (&harness, keyAlgo, keyPath, 0,
1771+ InitKexReplyHarnessKex (&harness, kexAlgo , keyAlgo, keyPath, 0,
17601772 REGRESS_MUTATE_SIG_NAME, NULL, 0);
17611773 RunKexReplyHandshake(&harness, &result);
17621774
17631775 AssertTrue(result.clientSuccess);
17641776 AssertTrue(result.serverSuccess);
1765- AssertKexSecretWiped( harness.client);
1766- AssertKexSecretWiped( harness.server);
1777+ AssertTrue(KexSecretWiped( harness.client) );
1778+ AssertTrue(KexSecretWiped( harness.server) );
17671779 AssertIntEQ(harness.mutator.mutatedPackets, 0);
17681780 AssertIntEQ(harness.client->connectState, CONNECT_SERVER_CHANNEL_REQUEST_DONE);
17691781 AssertIntEQ(harness.server->acceptState, ACCEPT_CLIENT_SESSION_ESTABLISHED);
@@ -1775,6 +1787,11 @@ static void AssertHandshakeSucceeds(const char* keyAlgo, const char* keyPath)
17751787 FreeKexReplyHarness(&harness);
17761788}
17771789
1790+ static void AssertHandshakeSucceeds(const char* keyAlgo, const char* keyPath)
1791+ {
1792+ AssertHandshakeSucceedsKex(NULL, keyAlgo, keyPath);
1793+ }
1794+
17781795static void AssertHandshakeRejectsMutatedReply(const char* keyAlgo,
17791796 const char* keyPath, const char* replaceName, int expectedErr)
17801797{
@@ -2173,7 +2190,7 @@ static void AssertHandshakeRejectsCorruptedSig(const char* keyAlgo,
21732190 result.clientErr != WS_WANT_WRITE);
21742191 AssertIntEQ(result.clientErr, expectedErr);
21752192 /* K was agreed before the verify failed. */
2176- AssertKexSecretWiped( harness.client);
2193+ AssertTrue(KexSecretWiped( harness.client) );
21772194
21782195 FreeKexReplyHarness(&harness);
21792196}
@@ -2202,23 +2219,35 @@ static void TestKexSecretWipedPerKex(void)
22022219#endif
22032220 NULL
22042221 };
2205- KexReplyHarness harness;
2206- KexReplyRunResult result;
22072222 word32 i;
22082223
22092224 for (i = 0; kexAlgos[i] != NULL; i++) {
2210- InitKexReplyHarnessKex(&harness, kexAlgos[i],
2211- REGRESS_DEFAULT_KEY_ALGO, REGRESS_DEFAULT_KEY_PATH, 0,
2212- REGRESS_MUTATE_SIG_NAME, NULL, 0);
2213- RunKexReplyHandshake(&harness, &result);
2225+ printf(" KEX secret wipe with %s.\n", kexAlgos[i]);
2226+ AssertHandshakeSucceedsKex(kexAlgos[i],
2227+ REGRESS_DEFAULT_KEY_ALGO, REGRESS_DEFAULT_KEY_PATH);
2228+ }
2229+ }
22142230
2215- AssertTrue(result.clientSuccess);
2216- AssertTrue(result.serverSuccess);
2217- AssertKexSecretWiped(harness.client);
2218- AssertKexSecretWiped(harness.server);
2231+ /* The server wipes K when sending its KEXDH_REPLY fails after key
2232+ * agreement. */
2233+ static void TestKexSecretWipedOnServerSendFail(void)
2234+ {
2235+ KexReplyHarness harness;
2236+ KexReplyRunResult result;
22192237
2220- FreeKexReplyHarness(&harness);
2221- }
2238+ InitKexReplyHarness(&harness, REGRESS_DEFAULT_KEY_ALGO,
2239+ REGRESS_DEFAULT_KEY_PATH, 0, NULL);
2240+ harness.serverIo.failSendMsgId = MSGID_KEXDH_REPLY;
2241+ RunKexReplyHandshake(&harness, &result);
2242+
2243+ AssertFalse(result.serverSuccess);
2244+ AssertTrue(result.serverErr != WS_WANT_READ &&
2245+ result.serverErr != WS_WANT_WRITE);
2246+ /* H is hashed over K, so K was agreed. */
2247+ AssertTrue(harness.server->sessionIdSz > 0);
2248+ AssertTrue(KexSecretWiped(harness.server));
2249+
2250+ FreeKexReplyHarness(&harness);
22222251}
22232252
22242253#ifndef WOLFSSH_NO_RSA_SHA2_256
@@ -16839,12 +16868,26 @@ static void TestGenerateKeysSplit(void)
1683916868 AssertTrue(WMEMCMP(ssh->handshake->peerKeys.encKey,
1684016869 ssh->handshake->keys.encKey, AES_128_KEY_SIZE) != 0);
1684116870
16842- /* A failed derivation leaves no key material behind. */
16871+ /* A failed derivation leaves no key material behind but keeps the
16872+ * negotiated sizes. */
1684316873 AssertTrue(wolfSSH_TestGenerateKeys(ssh, WC_HASH_TYPE_NONE) != WS_SUCCESS);
1684416874 WMEMSET(&zeroKeys, 0, sizeof(zeroKeys));
16845- AssertTrue(WMEMCMP(&ssh->handshake->keys, &zeroKeys, sizeof(Keys)) == 0);
16846- AssertTrue(WMEMCMP(&ssh->handshake->peerKeys, &zeroKeys,
16847- sizeof(Keys)) == 0);
16875+ AssertTrue(WMEMCMP(ssh->handshake->keys.iv, zeroKeys.iv,
16876+ sizeof(zeroKeys.iv)) == 0);
16877+ AssertTrue(WMEMCMP(ssh->handshake->keys.encKey, zeroKeys.encKey,
16878+ sizeof(zeroKeys.encKey)) == 0);
16879+ AssertTrue(WMEMCMP(ssh->handshake->keys.macKey, zeroKeys.macKey,
16880+ sizeof(zeroKeys.macKey)) == 0);
16881+ AssertTrue(WMEMCMP(ssh->handshake->peerKeys.iv, zeroKeys.iv,
16882+ sizeof(zeroKeys.iv)) == 0);
16883+ AssertTrue(WMEMCMP(ssh->handshake->peerKeys.encKey, zeroKeys.encKey,
16884+ sizeof(zeroKeys.encKey)) == 0);
16885+ AssertTrue(WMEMCMP(ssh->handshake->peerKeys.macKey, zeroKeys.macKey,
16886+ sizeof(zeroKeys.macKey)) == 0);
16887+ AssertIntEQ(ssh->handshake->peerKeys.encKeySz, AES_128_KEY_SIZE);
16888+ AssertIntEQ(ssh->handshake->peerKeys.macKeySz, WC_SHA_DIGEST_SIZE);
16889+ AssertIntEQ(ssh->handshake->keys.encKeySz, AES_256_KEY_SIZE);
16890+ AssertIntEQ(ssh->handshake->keys.macKeySz, WC_SHA256_DIGEST_SIZE);
1684816891
1684916892 wolfSSH_free(ssh);
1685016893
@@ -18727,6 +18770,7 @@ int main(int argc, char** argv)
1872718770 #endif
1872818771 TestKexDhReplyRejectsSigNameOverrun();
1872918772 TestKexSecretWipedPerKex();
18773+ TestKexSecretWipedOnServerSendFail();
1873018774 #ifdef REGRESS_TRUNC_KEX_ALGO
1873118775 TestKexDhReplyTruncatedFSendsDisconnect();
1873218776 TestKexDhInitTruncatedESendsDisconnect();
0 commit comments