Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 24 additions & 7 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -8301,6 +8301,11 @@ static int KeyAgreeEcdhMlKem_client(WOLFSSH* ssh, byte hashId,
ret = WS_BUFFER_E;
}

/* ssh->k holds the ML-KEM secret first, then the classical secret. */
if ((ret == 0) && (ssh->kSz <= length_sharedsecret)) {
ret = WS_BUFFER_E;
}

#ifndef WOLFSSH_NO_CURVE25519_MLKEM768_SHA256
if (kexId == ID_CURVE25519_MLKEM768_SHA256) {
/* Handle Curve25519 variant */
Expand Down Expand Up @@ -8329,12 +8334,14 @@ static int KeyAgreeEcdhMlKem_client(WOLFSSH* ssh, byte hashId,
EC25519_LITTLE_ENDIAN);
}
if (ret == 0) {
word32 tmp_kSz = ssh->kSz - length_sharedsecret;
PRIVATE_KEY_UNLOCK();
ret = wc_curve25519_shared_secret_ex(
&ssh->handshake->privKey.curve25519,
x25519_key_ptr, ssh->k + length_sharedsecret,
&ssh->kSz, EC25519_LITTLE_ENDIAN);
&tmp_kSz, EC25519_LITTLE_ENDIAN);
PRIVATE_KEY_LOCK();
ssh->kSz = length_sharedsecret + tmp_kSz;
}
if (x25519KeyInited)
wc_curve25519_free(x25519_key_ptr);
Expand Down Expand Up @@ -8382,11 +8389,13 @@ static int KeyAgreeEcdhMlKem_client(WOLFSSH* ssh, byte hashId,
}

if (ret == 0) {
word32 tmp_kSz = ssh->kSz - length_sharedsecret;
PRIVATE_KEY_UNLOCK();
ret = wc_ecc_shared_secret(&ssh->handshake->privKey.ecc,
key_ptr, ssh->k + length_sharedsecret,
&ssh->kSz);
&tmp_kSz);
PRIVATE_KEY_LOCK();
ssh->kSz = length_sharedsecret + tmp_kSz;
}
if (eccKeyInited)
wc_ecc_free(key_ptr);
Expand All @@ -8400,17 +8409,15 @@ static int KeyAgreeEcdhMlKem_client(WOLFSSH* ssh, byte hashId,
}

if (ret == 0) {
wc_MlKemKey_DecodePrivateKey(&kem, ssh->handshake->x,
length_privatekey);
ret = wc_MlKemKey_DecodePrivateKey(&kem, ssh->handshake->x,
length_privatekey);
}

if (ret == 0) {
ret = wc_MlKemKey_Decapsulate(&kem, ssh->k, f, length_ciphertext);
}

if (ret == 0) {
ssh->kSz += length_sharedsecret;
} else {
if (ret != 0) {
ssh->kSz = 0;
/* Local faults (RNG, HSM, memory) are logged at ERROR;
* peer-driven rejects stay at DEBUG so a remote peer
Expand Down Expand Up @@ -25981,6 +25988,16 @@ int wolfSSH_TestKeyAgreeEcdh_client(WOLFSSH* ssh, byte hashId,
}
#endif /* !WOLFSSH_NO_ECDH */

#if !defined(WOLFSSH_NO_NISTP256_MLKEM768_SHA256) || \
!defined(WOLFSSH_NO_NISTP384_MLKEM1024_SHA384) || \
!defined(WOLFSSH_NO_CURVE25519_MLKEM768_SHA256)
int wolfSSH_TestKeyAgreeEcdhMlKem_client(WOLFSSH* ssh, byte hashId,
const byte* f, word32 fSz)
{
return KeyAgreeEcdhMlKem_client(ssh, hashId, f, fSz);
}
#endif

#ifndef WOLFSSH_NO_DH_GEX_SHA256

int wolfSSH_TestSendKexDhGexRequest(WOLFSSH* ssh)
Expand Down
39 changes: 39 additions & 0 deletions tests/regress.c
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,15 @@ static word32 LoadFileBuffer(const char* path, byte* buf, word32 bufSz)
#define REGRESS_TRUNC_KEX_ALGO "ecdh-sha2-nistp256"
#endif

/* Hybrid KEX algorithm for the truncated hybrid f test */
#if !defined(WOLFSSH_NO_CURVE25519_MLKEM768_SHA256)
#define REGRESS_HYBRID_KEX_ALGO "mlkem768x25519-sha256"
#elif !defined(WOLFSSH_NO_NISTP256_MLKEM768_SHA256)
#define REGRESS_HYBRID_KEX_ALGO "mlkem768nistp256-sha256"
#elif !defined(WOLFSSH_NO_NISTP384_MLKEM1024_SHA384)
#define REGRESS_HYBRID_KEX_ALGO "mlkem1024nistp384-sha384"
#endif

/* KEX algorithm for the GEX group test */
#ifndef WOLFSSH_NO_DH_GEX_SHA256
#define REGRESS_GEX_KEX_ALGO "diffie-hellman-group-exchange-sha256"
Expand Down Expand Up @@ -2051,6 +2060,33 @@ static void TestKexDhInitEmptyESendsDisconnect(void)
}
#endif /* REGRESS_TRUNC_KEX_ALGO */

#ifdef REGRESS_HYBRID_KEX_ALGO
/* A hybrid KEX failure on the client also ends with KEY_EXCHANGE_FAILED. The
* short f leaves the classical peer key one byte short. */
static void TestKexHybridReplyTruncatedFSendsDisconnect(void)
{
KexReplyHarness harness;
KexReplyRunResult result;

InitKexReplyHarnessKex(&harness, REGRESS_HYBRID_KEX_ALGO,
REGRESS_DEFAULT_KEY_ALGO, REGRESS_DEFAULT_KEY_PATH, 1,
REGRESS_MUTATE_F_TRUNC, NULL, 0);
RunKexReplyHandshake(&harness, &result);

AssertIntEQ(harness.mutator.parseError, 0);
AssertIntEQ(harness.mutator.mutatedPackets, 1);
AssertFalse(result.clientSuccess);
AssertFalse(harness.client->connectState >= CONNECT_KEYED);
AssertTrue(result.clientRet == WS_FATAL_ERROR);
AssertIntEQ(result.clientErr, WS_CRYPTO_FAILED);
AssertTrue(harness.clientIo.sawDisconnect);
AssertIntEQ(harness.clientIo.disconnectReason,
WOLFSSH_DISCONNECT_KEY_EXCHANGE_FAILED);

FreeKexReplyHarness(&harness);
}
#endif /* REGRESS_HYBRID_KEX_ALGO */

#ifdef REGRESS_GEX_KEX_ALGO
/* A GEX group below the floor this client enforces (RFC 8270) ends the key
* exchange. Covers the WS_DH_SIZE_E arm of the client's guard, which no
Expand Down Expand Up @@ -17411,6 +17447,9 @@ int main(int argc, char** argv)
TestKexDhInitTruncatedESendsDisconnect();
TestKexDhInitEmptyESendsDisconnect();
#endif
#ifdef REGRESS_HYBRID_KEX_ALGO
TestKexHybridReplyTruncatedFSendsDisconnect();
#endif
#ifdef REGRESS_GEX_KEX_ALGO
TestKexDhGexGroupShrunkPrimeSendsDisconnect();
TestKexDhGexGroupBadGeneratorSendsDisconnect();
Expand Down
Loading
Loading