Skip to content

Commit 9496bbf

Browse files
ejohnstownphilljj
authored andcommitted
Keep negotiated key sizes when derivation fails
GenerateKeys() now wipes only the iv, encKey and macKey bytes when a derivation fails, so the negotiated sizes stay put and a second call cannot report success with no keys. - tests/regress.c checks a failed server KEXDH_REPLY send wipes K - AssertHandshakeSucceeds() takes a KEX algorithm for the per-KEX check - KEX wipe failures name the algorithm and the endpoint Issue: F-8845, F-11680, F-11681, F-8843, F-11682, F-14001
1 parent 5e096fd commit 9496bbf

2 files changed

Lines changed: 76 additions & 27 deletions

File tree

‎src/internal.c‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3737,10 +3737,15 @@ static int GenerateKeys(WOLFSSH* ssh, byte hashId, byte doKeyPad)
37373737
}
37383738
#endif /* SHOW_SECRETS */
37393739

3740-
/* Do not keep a partial key set from a failed derivation. */
3740+
/* Do not keep a partial key set from a failed derivation. Keep the
3741+
* negotiated sizes so a retry cannot succeed with no keys. */
37413742
if (ret != WS_SUCCESS && cK != NULL) {
3742-
WS_FORCEZERO(cK, sizeof(Keys));
3743-
WS_FORCEZERO(sK, sizeof(Keys));
3743+
WS_FORCEZERO(cK->iv, sizeof(cK->iv));
3744+
WS_FORCEZERO(cK->encKey, sizeof(cK->encKey));
3745+
WS_FORCEZERO(cK->macKey, sizeof(cK->macKey));
3746+
WS_FORCEZERO(sK->iv, sizeof(sK->iv));
3747+
WS_FORCEZERO(sK->encKey, sizeof(sK->encKey));
3748+
WS_FORCEZERO(sK->macKey, sizeof(sK->macKey));
37443749
}
37453750

37463751
return ret;

‎tests/regress.c‎

Lines changed: 68 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -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

802803
typedef 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+
17781795
static 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
@@ -17635,12 +17664,26 @@ static void TestGenerateKeysSplit(void)
1763517664
AssertTrue(WMEMCMP(ssh->handshake->peerKeys.encKey,
1763617665
ssh->handshake->keys.encKey, AES_128_KEY_SIZE) != 0);
1763717666

17638-
/* A failed derivation leaves no key material behind. */
17667+
/* A failed derivation leaves no key material behind but keeps the
17668+
* negotiated sizes. */
1763917669
AssertTrue(wolfSSH_TestGenerateKeys(ssh, WC_HASH_TYPE_NONE) != WS_SUCCESS);
1764017670
WMEMSET(&zeroKeys, 0, sizeof(zeroKeys));
17641-
AssertTrue(WMEMCMP(&ssh->handshake->keys, &zeroKeys, sizeof(Keys)) == 0);
17642-
AssertTrue(WMEMCMP(&ssh->handshake->peerKeys, &zeroKeys,
17643-
sizeof(Keys)) == 0);
17671+
AssertTrue(WMEMCMP(ssh->handshake->keys.iv, zeroKeys.iv,
17672+
sizeof(zeroKeys.iv)) == 0);
17673+
AssertTrue(WMEMCMP(ssh->handshake->keys.encKey, zeroKeys.encKey,
17674+
sizeof(zeroKeys.encKey)) == 0);
17675+
AssertTrue(WMEMCMP(ssh->handshake->keys.macKey, zeroKeys.macKey,
17676+
sizeof(zeroKeys.macKey)) == 0);
17677+
AssertTrue(WMEMCMP(ssh->handshake->peerKeys.iv, zeroKeys.iv,
17678+
sizeof(zeroKeys.iv)) == 0);
17679+
AssertTrue(WMEMCMP(ssh->handshake->peerKeys.encKey, zeroKeys.encKey,
17680+
sizeof(zeroKeys.encKey)) == 0);
17681+
AssertTrue(WMEMCMP(ssh->handshake->peerKeys.macKey, zeroKeys.macKey,
17682+
sizeof(zeroKeys.macKey)) == 0);
17683+
AssertIntEQ(ssh->handshake->peerKeys.encKeySz, AES_128_KEY_SIZE);
17684+
AssertIntEQ(ssh->handshake->peerKeys.macKeySz, WC_SHA_DIGEST_SIZE);
17685+
AssertIntEQ(ssh->handshake->keys.encKeySz, AES_256_KEY_SIZE);
17686+
AssertIntEQ(ssh->handshake->keys.macKeySz, WC_SHA256_DIGEST_SIZE);
1764417687

1764517688
wolfSSH_free(ssh);
1764617689

@@ -19556,6 +19599,7 @@ int main(int argc, char** argv)
1955619599
#endif
1955719600
TestKexDhReplyRejectsSigNameOverrun();
1955819601
TestKexSecretWipedPerKex();
19602+
TestKexSecretWipedOnServerSendFail();
1955919603
#ifdef REGRESS_TRUNC_KEX_ALGO
1956019604
TestKexDhReplyTruncatedFSendsDisconnect();
1956119605
TestKexDhInitTruncatedESendsDisconnect();

0 commit comments

Comments
 (0)