Skip to content

Commit 689e95d

Browse files
committed
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 1edee3c commit 689e95d

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
@@ -3700,10 +3700,15 @@ static int GenerateKeys(WOLFSSH* ssh, byte hashId, byte doKeyPad)
37003700
}
37013701
#endif /* SHOW_SECRETS */
37023702

3703-
/* Do not keep a partial key set from a failed derivation. */
3703+
/* Do not keep a partial key set from a failed derivation. Keep the
3704+
* negotiated sizes so a retry cannot succeed with no keys. */
37043705
if (ret != WS_SUCCESS && cK != NULL) {
3705-
WS_FORCEZERO(cK, sizeof(Keys));
3706-
WS_FORCEZERO(sK, sizeof(Keys));
3706+
WS_FORCEZERO(cK->iv, sizeof(cK->iv));
3707+
WS_FORCEZERO(cK->encKey, sizeof(cK->encKey));
3708+
WS_FORCEZERO(cK->macKey, sizeof(cK->macKey));
3709+
WS_FORCEZERO(sK->iv, sizeof(sK->iv));
3710+
WS_FORCEZERO(sK->encKey, sizeof(sK->encKey));
3711+
WS_FORCEZERO(sK->macKey, sizeof(sK->macKey));
37073712
}
37083713

37093714
return ret;

‎tests/regress.c‎

Lines changed: 68 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,7 @@ typedef struct DuplexEndpoint {
761761
word32 disconnectReason;
762762
byte isServer;
763763
byte sawDisconnect;
764+
byte failSendMsgId; /* nonzero: fail the send of a packet with this id */
764765
} DuplexEndpoint;
765766

766767
typedef struct {
@@ -1278,6 +1279,12 @@ static int DuplexSend(WOLFSSH* ssh, void* buf, word32 sz, void* ctx)
12781279
return WS_CBIO_ERR_GENERAL;
12791280
}
12801281

1282+
if (endpoint->failSendMsgId != 0 &&
1283+
outputSz > UINT32_SZ + PAD_LENGTH_SZ &&
1284+
output[UINT32_SZ + PAD_LENGTH_SZ] == endpoint->failSendMsgId) {
1285+
return WS_CBIO_ERR_GENERAL;
1286+
}
1287+
12811288
if (endpoint->mutator != NULL &&
12821289
endpoint->mutator->enabled &&
12831290
MutatorTargetsEndpoint(endpoint->mutator->mode,
@@ -1501,29 +1508,34 @@ static void RunKexReplyHandshake(KexReplyHarness* harness,
15011508
}
15021509

15031510
/* The shared secret K does not outlive the key exchange. */
1504-
static void AssertKexSecretWiped(const WOLFSSH* ssh)
1511+
static int KexSecretWiped(const WOLFSSH* ssh)
15051512
{
15061513
word32 i;
15071514

1508-
AssertIntEQ(ssh->kSz, 0);
1515+
if (ssh->kSz != 0)
1516+
return 0;
15091517
for (i = 0; i < (word32)sizeof(ssh->k); i++) {
1510-
AssertIntEQ(ssh->k[i], 0);
1518+
if (ssh->k[i] != 0)
1519+
return 0;
15111520
}
1521+
return 1;
15121522
}
15131523

1514-
static void AssertHandshakeSucceeds(const char* keyAlgo, const char* keyPath)
1524+
/* A NULL kexAlgo uses the harness default. */
1525+
static void AssertHandshakeSucceedsKex(const char* kexAlgo,
1526+
const char* keyAlgo, const char* keyPath)
15151527
{
15161528
KexReplyHarness harness;
15171529
KexReplyRunResult result;
15181530

1519-
InitKexReplyHarnessEx(&harness, keyAlgo, keyPath, 0,
1531+
InitKexReplyHarnessKex(&harness, kexAlgo, keyAlgo, keyPath, 0,
15201532
REGRESS_MUTATE_SIG_NAME, NULL, 0);
15211533
RunKexReplyHandshake(&harness, &result);
15221534

15231535
AssertTrue(result.clientSuccess);
15241536
AssertTrue(result.serverSuccess);
1525-
AssertKexSecretWiped(harness.client);
1526-
AssertKexSecretWiped(harness.server);
1537+
AssertTrue(KexSecretWiped(harness.client));
1538+
AssertTrue(KexSecretWiped(harness.server));
15271539
AssertIntEQ(harness.mutator.mutatedPackets, 0);
15281540
AssertIntEQ(harness.client->connectState, CONNECT_SERVER_CHANNEL_REQUEST_DONE);
15291541
AssertIntEQ(harness.server->acceptState, ACCEPT_CLIENT_SESSION_ESTABLISHED);
@@ -1535,6 +1547,11 @@ static void AssertHandshakeSucceeds(const char* keyAlgo, const char* keyPath)
15351547
FreeKexReplyHarness(&harness);
15361548
}
15371549

1550+
static void AssertHandshakeSucceeds(const char* keyAlgo, const char* keyPath)
1551+
{
1552+
AssertHandshakeSucceedsKex(NULL, keyAlgo, keyPath);
1553+
}
1554+
15381555
static void AssertHandshakeRejectsMutatedReply(const char* keyAlgo,
15391556
const char* keyPath, const char* replaceName, int expectedErr)
15401557
{
@@ -1933,7 +1950,7 @@ static void AssertHandshakeRejectsCorruptedSig(const char* keyAlgo,
19331950
result.clientErr != WS_WANT_WRITE);
19341951
AssertIntEQ(result.clientErr, expectedErr);
19351952
/* K was agreed before the verify failed. */
1936-
AssertKexSecretWiped(harness.client);
1953+
AssertTrue(KexSecretWiped(harness.client));
19371954

19381955
FreeKexReplyHarness(&harness);
19391956
}
@@ -1962,23 +1979,35 @@ static void TestKexSecretWipedPerKex(void)
19621979
#endif
19631980
NULL
19641981
};
1965-
KexReplyHarness harness;
1966-
KexReplyRunResult result;
19671982
word32 i;
19681983

19691984
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);
1985+
printf(" KEX secret wipe with %s.\n", kexAlgos[i]);
1986+
AssertHandshakeSucceedsKex(kexAlgos[i],
1987+
REGRESS_DEFAULT_KEY_ALGO, REGRESS_DEFAULT_KEY_PATH);
1988+
}
1989+
}
19741990

1975-
AssertTrue(result.clientSuccess);
1976-
AssertTrue(result.serverSuccess);
1977-
AssertKexSecretWiped(harness.client);
1978-
AssertKexSecretWiped(harness.server);
1991+
/* The server wipes K when sending its KEXDH_REPLY fails after key
1992+
* agreement. */
1993+
static void TestKexSecretWipedOnServerSendFail(void)
1994+
{
1995+
KexReplyHarness harness;
1996+
KexReplyRunResult result;
19791997

1980-
FreeKexReplyHarness(&harness);
1981-
}
1998+
InitKexReplyHarness(&harness, REGRESS_DEFAULT_KEY_ALGO,
1999+
REGRESS_DEFAULT_KEY_PATH, 0, NULL);
2000+
harness.serverIo.failSendMsgId = MSGID_KEXDH_REPLY;
2001+
RunKexReplyHandshake(&harness, &result);
2002+
2003+
AssertFalse(result.serverSuccess);
2004+
AssertTrue(result.serverErr != WS_WANT_READ &&
2005+
result.serverErr != WS_WANT_WRITE);
2006+
/* H is hashed over K, so K was agreed. */
2007+
AssertTrue(harness.server->sessionIdSz > 0);
2008+
AssertTrue(KexSecretWiped(harness.server));
2009+
2010+
FreeKexReplyHarness(&harness);
19822011
}
19832012

19842013
#ifndef WOLFSSH_NO_RSA_SHA2_256
@@ -15800,12 +15829,26 @@ static void TestGenerateKeysSplit(void)
1580015829
AssertTrue(WMEMCMP(ssh->handshake->peerKeys.encKey,
1580115830
ssh->handshake->keys.encKey, AES_128_KEY_SIZE) != 0);
1580215831

15803-
/* A failed derivation leaves no key material behind. */
15832+
/* A failed derivation leaves no key material behind but keeps the
15833+
* negotiated sizes. */
1580415834
AssertTrue(wolfSSH_TestGenerateKeys(ssh, WC_HASH_TYPE_NONE) != WS_SUCCESS);
1580515835
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);
15836+
AssertTrue(WMEMCMP(ssh->handshake->keys.iv, zeroKeys.iv,
15837+
sizeof(zeroKeys.iv)) == 0);
15838+
AssertTrue(WMEMCMP(ssh->handshake->keys.encKey, zeroKeys.encKey,
15839+
sizeof(zeroKeys.encKey)) == 0);
15840+
AssertTrue(WMEMCMP(ssh->handshake->keys.macKey, zeroKeys.macKey,
15841+
sizeof(zeroKeys.macKey)) == 0);
15842+
AssertTrue(WMEMCMP(ssh->handshake->peerKeys.iv, zeroKeys.iv,
15843+
sizeof(zeroKeys.iv)) == 0);
15844+
AssertTrue(WMEMCMP(ssh->handshake->peerKeys.encKey, zeroKeys.encKey,
15845+
sizeof(zeroKeys.encKey)) == 0);
15846+
AssertTrue(WMEMCMP(ssh->handshake->peerKeys.macKey, zeroKeys.macKey,
15847+
sizeof(zeroKeys.macKey)) == 0);
15848+
AssertIntEQ(ssh->handshake->peerKeys.encKeySz, AES_128_KEY_SIZE);
15849+
AssertIntEQ(ssh->handshake->peerKeys.macKeySz, WC_SHA_DIGEST_SIZE);
15850+
AssertIntEQ(ssh->handshake->keys.encKeySz, AES_256_KEY_SIZE);
15851+
AssertIntEQ(ssh->handshake->keys.macKeySz, WC_SHA256_DIGEST_SIZE);
1580915852

1581015853
wolfSSH_free(ssh);
1581115854

@@ -17473,6 +17516,7 @@ int main(int argc, char** argv)
1747317516
#endif
1747417517
TestKexDhReplyRejectsSigNameOverrun();
1747517518
TestKexSecretWipedPerKex();
17519+
TestKexSecretWipedOnServerSendFail();
1747617520
#ifdef REGRESS_TRUNC_KEX_ALGO
1747717521
TestKexDhReplyTruncatedFSendsDisconnect();
1747817522
TestKexDhInitTruncatedESendsDisconnect();

0 commit comments

Comments
 (0)