@@ -1500,6 +1500,17 @@ static void RunKexReplyHandshake(KexReplyHarness* harness,
15001500 result->steps = REGRESS_MAX_HANDSHAKE_STEPS;
15011501}
15021502
1503+ /* The shared secret K does not outlive the key exchange. */
1504+ static void AssertKexSecretWiped(const WOLFSSH* ssh)
1505+ {
1506+ word32 i;
1507+
1508+ AssertIntEQ(ssh->kSz, 0);
1509+ for (i = 0; i < (word32)sizeof(ssh->k); i++) {
1510+ AssertIntEQ(ssh->k[i], 0);
1511+ }
1512+ }
1513+
15031514static void AssertHandshakeSucceeds(const char* keyAlgo, const char* keyPath)
15041515{
15051516 KexReplyHarness harness;
@@ -1511,6 +1522,8 @@ static void AssertHandshakeSucceeds(const char* keyAlgo, const char* keyPath)
15111522
15121523 AssertTrue(result.clientSuccess);
15131524 AssertTrue(result.serverSuccess);
1525+ AssertKexSecretWiped(harness.client);
1526+ AssertKexSecretWiped(harness.server);
15141527 AssertIntEQ(harness.mutator.mutatedPackets, 0);
15151528 AssertIntEQ(harness.client->connectState, CONNECT_SERVER_CHANNEL_REQUEST_DONE);
15161529 AssertIntEQ(harness.server->acceptState, ACCEPT_CLIENT_SESSION_ESTABLISHED);
@@ -1919,10 +1932,55 @@ static void AssertHandshakeRejectsCorruptedSig(const char* keyAlgo,
19191932 AssertTrue(result.clientErr != WS_WANT_READ &&
19201933 result.clientErr != WS_WANT_WRITE);
19211934 AssertIntEQ(result.clientErr, expectedErr);
1935+ /* K was agreed before the verify failed. */
1936+ AssertKexSecretWiped(harness.client);
19221937
19231938 FreeKexReplyHarness(&harness);
19241939}
19251940
1941+ /* Every key agreement, the ML-KEM hybrids included, wipes K on both sides. */
1942+ static void TestKexSecretWipedPerKex(void)
1943+ {
1944+ static const char* kexAlgos[] = {
1945+ #ifndef WOLFSSH_NO_DH_GROUP14_SHA256
1946+ "diffie-hellman-group14-sha256",
1947+ #endif
1948+ #ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256
1949+ "ecdh-sha2-nistp256",
1950+ #endif
1951+ #ifndef WOLFSSH_NO_CURVE25519_SHA256
1952+ "curve25519-sha256",
1953+ #endif
1954+ #ifndef WOLFSSH_NO_NISTP256_MLKEM768_SHA256
1955+ "mlkem768nistp256-sha256",
1956+ #endif
1957+ #ifndef WOLFSSH_NO_NISTP384_MLKEM1024_SHA384
1958+ "mlkem1024nistp384-sha384",
1959+ #endif
1960+ #ifndef WOLFSSH_NO_CURVE25519_MLKEM768_SHA256
1961+ "mlkem768x25519-sha256",
1962+ #endif
1963+ NULL
1964+ };
1965+ KexReplyHarness harness;
1966+ KexReplyRunResult result;
1967+ word32 i;
1968+
1969+ for (i = 0; kexAlgos[i] != NULL; i++) {
1970+ InitKexReplyHarnessKex(&harness, kexAlgos[i],
1971+ REGRESS_DEFAULT_KEY_ALGO, REGRESS_DEFAULT_KEY_PATH, 0,
1972+ REGRESS_MUTATE_SIG_NAME, NULL, 0);
1973+ RunKexReplyHandshake(&harness, &result);
1974+
1975+ AssertTrue(result.clientSuccess);
1976+ AssertTrue(result.serverSuccess);
1977+ AssertKexSecretWiped(harness.client);
1978+ AssertKexSecretWiped(harness.server);
1979+
1980+ FreeKexReplyHarness(&harness);
1981+ }
1982+ }
1983+
19261984#ifndef WOLFSSH_NO_RSA_SHA2_256
19271985static void TestKexDhReplyRejectsRsaSha2_256CorruptSig(void)
19281986{
@@ -15674,6 +15732,7 @@ static void TestGenerateKeysSplit(void)
1567415732 word32 payloadSz;
1567515733 word32 idx;
1567615734 byte zeros[AES_256_KEY_SIZE];
15735+ Keys zeroKeys;
1567715736
1567815737 WMEMSET(zeros, 0, sizeof(zeros));
1567915738
@@ -15741,6 +15800,13 @@ static void TestGenerateKeysSplit(void)
1574115800 AssertTrue(WMEMCMP(ssh->handshake->peerKeys.encKey,
1574215801 ssh->handshake->keys.encKey, AES_128_KEY_SIZE) != 0);
1574315802
15803+ /* A failed derivation leaves no key material behind. */
15804+ AssertTrue(wolfSSH_TestGenerateKeys(ssh, WC_HASH_TYPE_NONE) != WS_SUCCESS);
15805+ WMEMSET(&zeroKeys, 0, sizeof(zeroKeys));
15806+ AssertTrue(WMEMCMP(&ssh->handshake->keys, &zeroKeys, sizeof(Keys)) == 0);
15807+ AssertTrue(WMEMCMP(&ssh->handshake->peerKeys, &zeroKeys,
15808+ sizeof(Keys)) == 0);
15809+
1574415810 wolfSSH_free(ssh);
1574515811
1574615812#ifndef WOLFSSH_NO_AES_GCM
@@ -17406,6 +17472,7 @@ int main(int argc, char** argv)
1740617472 TestKexDhReplyRejectsEd25519CorruptSig();
1740717473 #endif
1740817474 TestKexDhReplyRejectsSigNameOverrun();
17475+ TestKexSecretWipedPerKex();
1740917476 #ifdef REGRESS_TRUNC_KEX_ALGO
1741017477 TestKexDhReplyTruncatedFSendsDisconnect();
1741117478 TestKexDhInitTruncatedESendsDisconnect();
0 commit comments