@@ -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}
@@ -2205,23 +2222,35 @@ static void TestKexSecretWipedPerKex(void)
22052222#endif
22062223 NULL
22072224 };
2208- KexReplyHarness harness;
2209- KexReplyRunResult result;
22102225 word32 i;
22112226
22122227 for (i = 0; kexAlgos[i] != NULL; i++) {
2213- InitKexReplyHarnessKex(&harness, kexAlgos[i],
2214- REGRESS_DEFAULT_KEY_ALGO, REGRESS_DEFAULT_KEY_PATH, 0,
2215- REGRESS_MUTATE_SIG_NAME, NULL, 0);
2216- RunKexReplyHandshake(&harness, &result);
2228+ printf(" KEX secret wipe with %s.\n", kexAlgos[i]);
2229+ AssertHandshakeSucceedsKex(kexAlgos[i],
2230+ REGRESS_DEFAULT_KEY_ALGO, REGRESS_DEFAULT_KEY_PATH);
2231+ }
2232+ }
22172233
2218- AssertTrue(result.clientSuccess);
2219- AssertTrue(result.serverSuccess);
2220- AssertKexSecretWiped(harness.client);
2221- AssertKexSecretWiped(harness.server);
2234+ /* The server wipes K when sending its KEXDH_REPLY fails after key
2235+ * agreement. */
2236+ static void TestKexSecretWipedOnServerSendFail(void)
2237+ {
2238+ KexReplyHarness harness;
2239+ KexReplyRunResult result;
22222240
2223- FreeKexReplyHarness(&harness);
2224- }
2241+ InitKexReplyHarness(&harness, REGRESS_DEFAULT_KEY_ALGO,
2242+ REGRESS_DEFAULT_KEY_PATH, 0, NULL);
2243+ harness.serverIo.failSendMsgId = MSGID_KEXDH_REPLY;
2244+ RunKexReplyHandshake(&harness, &result);
2245+
2246+ AssertFalse(result.serverSuccess);
2247+ AssertTrue(result.serverErr != WS_WANT_READ &&
2248+ result.serverErr != WS_WANT_WRITE);
2249+ /* H is hashed over K, so K was agreed. */
2250+ AssertTrue(harness.server->sessionIdSz > 0);
2251+ AssertTrue(KexSecretWiped(harness.server));
2252+
2253+ FreeKexReplyHarness(&harness);
22252254}
22262255
22272256#ifndef WOLFSSH_NO_RSA_SHA2_256
@@ -16842,12 +16871,26 @@ static void TestGenerateKeysSplit(void)
1684216871 AssertTrue(WMEMCMP(ssh->handshake->peerKeys.encKey,
1684316872 ssh->handshake->keys.encKey, AES_128_KEY_SIZE) != 0);
1684416873
16845- /* A failed derivation leaves no key material behind. */
16874+ /* A failed derivation leaves no key material behind but keeps the
16875+ * negotiated sizes. */
1684616876 AssertTrue(wolfSSH_TestGenerateKeys(ssh, WC_HASH_TYPE_NONE) != WS_SUCCESS);
1684716877 WMEMSET(&zeroKeys, 0, sizeof(zeroKeys));
16848- AssertTrue(WMEMCMP(&ssh->handshake->keys, &zeroKeys, sizeof(Keys)) == 0);
16849- AssertTrue(WMEMCMP(&ssh->handshake->peerKeys, &zeroKeys,
16850- sizeof(Keys)) == 0);
16878+ AssertTrue(WMEMCMP(ssh->handshake->keys.iv, zeroKeys.iv,
16879+ sizeof(zeroKeys.iv)) == 0);
16880+ AssertTrue(WMEMCMP(ssh->handshake->keys.encKey, zeroKeys.encKey,
16881+ sizeof(zeroKeys.encKey)) == 0);
16882+ AssertTrue(WMEMCMP(ssh->handshake->keys.macKey, zeroKeys.macKey,
16883+ sizeof(zeroKeys.macKey)) == 0);
16884+ AssertTrue(WMEMCMP(ssh->handshake->peerKeys.iv, zeroKeys.iv,
16885+ sizeof(zeroKeys.iv)) == 0);
16886+ AssertTrue(WMEMCMP(ssh->handshake->peerKeys.encKey, zeroKeys.encKey,
16887+ sizeof(zeroKeys.encKey)) == 0);
16888+ AssertTrue(WMEMCMP(ssh->handshake->peerKeys.macKey, zeroKeys.macKey,
16889+ sizeof(zeroKeys.macKey)) == 0);
16890+ AssertIntEQ(ssh->handshake->peerKeys.encKeySz, AES_128_KEY_SIZE);
16891+ AssertIntEQ(ssh->handshake->peerKeys.macKeySz, WC_SHA_DIGEST_SIZE);
16892+ AssertIntEQ(ssh->handshake->keys.encKeySz, AES_256_KEY_SIZE);
16893+ AssertIntEQ(ssh->handshake->keys.macKeySz, WC_SHA256_DIGEST_SIZE);
1685116894
1685216895 wolfSSH_free(ssh);
1685316896
@@ -18730,6 +18773,7 @@ int main(int argc, char** argv)
1873018773 #endif
1873118774 TestKexDhReplyRejectsSigNameOverrun();
1873218775 TestKexSecretWipedPerKex();
18776+ TestKexSecretWipedOnServerSendFail();
1873318777 #ifdef REGRESS_TRUNC_KEX_ALGO
1873418778 TestKexDhReplyTruncatedFSendsDisconnect();
1873518779 TestKexDhInitTruncatedESendsDisconnect();
0 commit comments