Skip to content
Merged
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
15 changes: 6 additions & 9 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -2035,7 +2035,11 @@ then

if test "$ENABLED_EXPERIMENTAL" = "yes"
then
test "$enable_extra_pqc_hybrids" = "" && enable_extra_pqc_hybrids=yes
# FIPS v7 allows only the draft-ietf-tls-ecdhe-mlkem hybrid groups.
if test "$ENABLED_FIPS" = "no" || test "$HAVE_FIPS_VERSION" -lt 7
then
test "$enable_extra_pqc_hybrids" = "" && enable_extra_pqc_hybrids=yes
fi
test "$enable_frodokem" = "" && enable_frodokem=yes
test "$enable_frodokem" = yes && test "$enable_frodokem_shake" = "" && enable_frodokem_shake=yes
test "$enable_frodokem" = yes && test "$enable_frodokem_aes" = "" && enable_frodokem_aes=yes
Expand Down Expand Up @@ -7895,18 +7899,10 @@ AS_CASE([$FIPS_VERSION],
(test "$ENABLED_FIPS_DEV" != "yes" || test "$enable_ed25519" != "no")],
[ENABLED_ED25519="yes"; AM_CFLAGS="$AM_CFLAGS -DHAVE_ED25519 -DHAVE_ED25519_KEY_IMPORT"])

AS_IF([test "$ENABLED_CURVE25519" != "no" &&
(test "$ENABLED_FIPS_DEV" != "yes" || test "$enable_curve25519" = "")],
[ENABLED_CURVE25519="no"; AM_CFLAGS="$AM_CFLAGS"])

AS_IF([test "x$ENABLED_ED448" != "xyes" &&
(test "$ENABLED_FIPS_DEV" != "yes" || test "$enable_ed448" != "no")],
[ENABLED_ED448="yes"; AM_CFLAGS="$AM_CFLAGS -DHAVE_ED448 -DHAVE_ED448_KEY_IMPORT"])

AS_IF([test "$ENABLED_CURVE448" != "no" &&
(test "$ENABLED_FIPS_DEV" != "yes" || test "$enable_curve448" = "")],
[ENABLED_CURVE448="no"; AM_CFLAGS="$AM_CFLAGS"])

AS_IF([test "x$ENABLED_ED25519_STREAM" != "xyes" &&
(test "$ENABLED_FIPS_DEV" != "yes" || test "$enable_ed25519_stream" != "no")],
[ENABLED_ED25519_STREAM="yes"])
Expand Down Expand Up @@ -9150,6 +9146,7 @@ if test "$ENABLED_EXTRA_PQC_HYBRIDS" = "yes"
then
AS_IF([ test "$ENABLED_EXPERIMENTAL" != "yes" ],[ AC_MSG_ERROR([extra-pqc-hybrids requires --enable-experimental.]) ])
AS_IF([ test "$ENABLED_ML_KEM" = "no" ],[ AC_MSG_ERROR([extra-pqc-hybrids requires ML-KEM.]) ])
AS_IF([ test "$ENABLED_FIPS" != "no" && test "$HAVE_FIPS_VERSION" -ge 7 ],[ AC_MSG_ERROR([extra-pqc-hybrids is not available in FIPS v7 builds.]) ])
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_EXTRA_PQC_HYBRIDS"
fi

Expand Down
16 changes: 8 additions & 8 deletions src/include.am
Original file line number Diff line number Diff line change
Expand Up @@ -1299,18 +1299,10 @@ if BUILD_CMAC
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/cmac.c
endif

if BUILD_CURVE448
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/curve448.c
endif

if BUILD_ED448
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/ed448.c
endif

if BUILD_CURVE25519
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/curve25519.c
endif

if BUILD_ED25519
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/ed25519.c
endif
Expand Down Expand Up @@ -1482,6 +1474,14 @@ src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/wolfentropy.c
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/entropy-timers.c
endif

# Past wolfcrypt_last.c: X25519 and X448 are not v7 module services.
if BUILD_CURVE25519
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/curve25519.c
endif
if BUILD_CURVE448
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/curve448.c
endif

endif BUILD_FIPS_V7_PLUS

endif BUILD_FIPS
Expand Down
67 changes: 51 additions & 16 deletions src/tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -10640,6 +10640,7 @@ static int TLSX_KeyShare_ProcessPqcHybridClient(WOLFSSL* ssl,
KeyShareEntry *ecc_kse = NULL;
word32 ctSz = 0;
word32 ssSzPqc = 0;
byte ssPqc[WC_ML_KEM_SS_SZ];

if (ssl->options.side == WOLFSSL_SERVER_END) {
/* I am the server, the shared secret has already been generated and
Expand Down Expand Up @@ -10780,15 +10781,15 @@ static int TLSX_KeyShare_ProcessPqcHybridClient(WOLFSSL* ssl,
#endif
}

/* Process ECDH key share part. The generated shared secret is directly
* stored in the ssl->arrays->preMasterSecret buffer. Depending on the
* pqc_first flag, the ECDH shared secret part goes before or after the
* KEM part. */
/* Process ECDH key share part into ssl->arrays->preMasterSecret: at the
* start, or after the KEM part if pqc_first and the module cannot join. */
if (ret == 0) {
int offset = 0;

#ifndef WOLFSSL_HAVE_TLS13_HYBRID_SECRET
if (pqc_first)
offset = ssSzPqc;
#endif

#ifdef HAVE_CURVE25519
if (ecc_group == WOLFSSL_ECC_X25519) {
Expand Down Expand Up @@ -10846,22 +10847,36 @@ static int TLSX_KeyShare_ProcessPqcHybridClient(WOLFSSL* ssl,
}
}

/* Process PQC KEM key share part. Depending on the pqc_first flag, the
* KEM shared secret part goes before or after the ECDH part. */
/* Process PQC KEM key share part. The module joins its shared secret, or
* without WOLFSSL_HAVE_TLS13_HYBRID_SECRET pqc_first places it. */
if (ret == 0) {
#ifdef WOLFSSL_HAVE_TLS13_HYBRID_SECRET
ret = TLSX_KeyShare_ProcessPqcClient_ex(ssl, pqc_kse, ssPqc, &ssSzPqc);
#else
int offset = ssl->arrays->preMasterSz;

if (pqc_first)
offset = 0;

ret = TLSX_KeyShare_ProcessPqcClient_ex(ssl, pqc_kse,
ssl->arrays->preMasterSecret + offset, &ssSzPqc);
#endif
}

if (ret == 0) {
keyShareEntry->privKey = (byte*)pqc_kse->key;

#ifdef WOLFSSL_HAVE_TLS13_HYBRID_SECRET
/* The module joins the secrets; FIPS builds enforce the group's
* order and sizes. */
ret = wc_Tls13_HybridSharedSecret(keyShareEntry->group, pqc_first,
ssl->arrays->preMasterSecret, MAX_PREMASTER_SZ,
&ssl->arrays->preMasterSz, ssPqc, ssSzPqc);
if (ret != 0)
ForceZero(ssl->arrays->preMasterSecret, MAX_PREMASTER_SZ);
#else
ssl->arrays->preMasterSz += ssSzPqc;
#endif
}
else
#ifdef WOLFSSL_ASYNC_CRYPT
Expand All @@ -10886,6 +10901,7 @@ static int TLSX_KeyShare_ProcessPqcHybridClient(WOLFSSL* ssl,
}
}

ForceZero(ssPqc, sizeof(ssPqc));
TLSX_KeyShare_FreeAll(ecc_kse, ssl->heap);
TLSX_KeyShare_FreeAll(pqc_kse, ssl->heap);

Expand Down Expand Up @@ -11404,6 +11420,7 @@ int TLSX_KeyShare_HandlePqcHybridKeyServer(WOLFSSL* ssl,
word32 pubSz = 0;
word32 ctSz = 0;
word32 ssSzPqc = 0;
byte ssPqc[WC_ML_KEM_SS_SZ];

if (data == NULL) {
WOLFSSL_MSG("No hybrid key share data from the client.");
Expand Down Expand Up @@ -11550,10 +11567,8 @@ int TLSX_KeyShare_HandlePqcHybridKeyServer(WOLFSSL* ssl,
}
}

/* Process ECDH key share part. The generated shared secret is directly
* stored in the ssl->arrays->preMasterSecret buffer. Depending on the
* pqc_first flag, the ECDH shared secret part goes before or after the
* KEM part. */
/* Process ECDH key share part into ssl->arrays->preMasterSecret: at the
* start, or after the KEM part if pqc_first and the module cannot join. */
if (ret == 0) {
ecc_kse->keLen = len - pubSz;
ecc_kse->ke = (byte*)XMALLOC(ecc_kse->keLen, ssl->heap,
Expand All @@ -11568,7 +11583,9 @@ int TLSX_KeyShare_HandlePqcHybridKeyServer(WOLFSSL* ssl,

if (pqc_first) {
pubOffset = pubSz;
#ifndef WOLFSSL_HAVE_TLS13_HYBRID_SECRET
ssOffset = ssSzPqc;
#endif
}

XMEMCPY(ecc_kse->ke, data + pubOffset, ecc_kse->keLen);
Expand Down Expand Up @@ -11619,26 +11636,44 @@ int TLSX_KeyShare_HandlePqcHybridKeyServer(WOLFSSL* ssl,
ret = LENGTH_ERROR;
}

/* Process PQC KEM key share part. Depending on the pqc_first flag, the
* KEM shared secret part goes before or after the ECDH part. */
/* Process PQC KEM key share part. The module joins its shared secret, or
* without WOLFSSL_HAVE_TLS13_HYBRID_SECRET pqc_first places it. */
if (ret == 0) {
int input_offset = ecc_kse->keLen;
int output_offset = ssl->arrays->preMasterSz;
#ifdef WOLFSSL_HAVE_TLS13_HYBRID_SECRET
byte* output = ssPqc;
#else
byte* output = ssl->arrays->preMasterSecret + ssl->arrays->preMasterSz;
#endif

if (pqc_first) {
input_offset = 0;
output_offset = 0;
#ifndef WOLFSSL_HAVE_TLS13_HYBRID_SECRET
output = ssl->arrays->preMasterSecret;
#endif
}

ret = TLSX_KeyShare_HandlePqcKeyServer(ssl, pqc_kse,
data + input_offset, pubSz,
ssl->arrays->preMasterSecret + output_offset, &ssSzPqc);
data + input_offset, pubSz, output, &ssSzPqc);
}

#ifdef WOLFSSL_HAVE_TLS13_HYBRID_SECRET
/* The module joins the secrets; FIPS builds enforce the group's order and
* sizes. */
if (ret == 0) {
ret = wc_Tls13_HybridSharedSecret(keyShareEntry->group, pqc_first,
ssl->arrays->preMasterSecret, MAX_PREMASTER_SZ,
&ssl->arrays->preMasterSz, ssPqc, ssSzPqc);
}
#endif
ForceZero(ssPqc, sizeof(ssPqc));

if (ret == 0) {
XFREE(keyShareEntry->ke, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY);

#ifndef WOLFSSL_HAVE_TLS13_HYBRID_SECRET
ssl->arrays->preMasterSz += ssSzPqc;
#endif
keyShareEntry->ke = NULL;
keyShareEntry->keLen = 0;
#ifdef WOLFSSL_ASYNC_CRYPT
Expand Down
83 changes: 83 additions & 0 deletions tests/api/test_kdf.c
Original file line number Diff line number Diff line change
Expand Up @@ -1015,3 +1015,86 @@ int test_wc_SshKdfReuse(void)
#endif
return EXPECT_RESULT();
}

/* draft-ietf-tls-ecdhe-mlkem-03 sec 4.3: X25519MLKEM768 puts the ML-KEM secret
* first, SecP256r1MLKEM768 and SecP384r1MLKEM1024 put the ECDH secret first. */
int test_wc_Tls13_HybridSharedSecret(void)
{
EXPECT_DECLS;
#ifdef WOLFSSL_HAVE_TLS13_HYBRID_SECRET
byte ec[48], kem[32], ss[96], want[96];
word32 ssSz;
word32 i;

/* Distinct bytes, so a wrong offset or length inside a part shows. */
for (i = 0; i < sizeof(ec); i++)
ec[i] = (byte)i;
for (i = 0; i < sizeof(kem); i++)
kem[i] = (byte)(0x80 | i);

/* SecP256r1MLKEM768: ECDH (32) then ML-KEM (32), exact fit */
XMEMCPY(ss, ec, 32);
ssSz = 32;
ExpectIntEQ(wc_Tls13_HybridSharedSecret(WOLFSSL_SECP256R1MLKEM768, 0, ss,
64, &ssSz, kem, sizeof(kem)), 0);
ExpectIntEQ(ssSz, 64);
XMEMCPY(want, ec, 32);
XMEMCPY(want + 32, kem, sizeof(kem));
ExpectBufEQ(ss, want, 64);

/* SecP384r1MLKEM1024: ECDH (48) then ML-KEM (32) */
XMEMCPY(ss, ec, 48);
ssSz = 48;
ExpectIntEQ(wc_Tls13_HybridSharedSecret(WOLFSSL_SECP384R1MLKEM1024, 0, ss,
sizeof(ss), &ssSz, kem, sizeof(kem)), 0);
ExpectIntEQ(ssSz, 80);
XMEMCPY(want, ec, 48);
XMEMCPY(want + 48, kem, sizeof(kem));
ExpectBufEQ(ss, want, 80);

/* X25519MLKEM768: ML-KEM (32) then X25519 (32), exact fit */
XMEMCPY(ss, ec, 32);
ssSz = 32;
ExpectIntEQ(wc_Tls13_HybridSharedSecret(WOLFSSL_X25519MLKEM768, 1, ss,
64, &ssSz, kem, sizeof(kem)), 0);
ExpectIntEQ(ssSz, 64);
XMEMCPY(want, kem, sizeof(kem));
XMEMCPY(want + 32, ec, 32);
ExpectBufEQ(ss, want, 64);

/* Bad arguments and a result that does not fit. */
ssSz = 32;
ExpectIntEQ(wc_Tls13_HybridSharedSecret(WOLFSSL_SECP256R1MLKEM768, 0, NULL,
sizeof(ss), &ssSz, kem, sizeof(kem)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_Tls13_HybridSharedSecret(WOLFSSL_SECP256R1MLKEM768, 0, ss,
sizeof(ss), NULL, kem, sizeof(kem)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_Tls13_HybridSharedSecret(WOLFSSL_SECP256R1MLKEM768, 0, ss,
sizeof(ss), &ssSz, NULL, sizeof(kem)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_Tls13_HybridSharedSecret(WOLFSSL_SECP256R1MLKEM768, 0, ss,
sizeof(ss), &ssSz, kem, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_Tls13_HybridSharedSecret(WOLFSSL_SECP256R1MLKEM768, 0, ss,
63, &ssSz, kem, sizeof(kem)), WC_NO_ERR_TRACE(BUFFER_E));
ssSz = 0;
ExpectIntEQ(wc_Tls13_HybridSharedSecret(WOLFSSL_SECP256R1MLKEM768, 0, ss,
sizeof(ss), &ssSz, kem, sizeof(kem)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
#ifndef HAVE_FIPS
ssSz = 97;
ExpectIntEQ(wc_Tls13_HybridSharedSecret(WOLFSSL_SECP256R1MLKEM768, 0, ss,
sizeof(ss), &ssSz, kem, sizeof(kem)), WC_NO_ERR_TRACE(BUFFER_E));
#else
/* FIPS: only the module's groups, each in its own order and sizes. */
ssSz = 32;
ExpectIntEQ(wc_Tls13_HybridSharedSecret(WOLFSSL_SECP256R1MLKEM768, 1, ss,
sizeof(ss), &ssSz, kem, sizeof(kem)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_Tls13_HybridSharedSecret(WOLFSSL_X25519MLKEM768, 0, ss,
sizeof(ss), &ssSz, kem, sizeof(kem)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_Tls13_HybridSharedSecret(WOLFSSL_SECP256R1MLKEM512, 0, ss,
sizeof(ss), &ssSz, kem, sizeof(kem)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_Tls13_HybridSharedSecret(WOLFSSL_SECP384R1MLKEM1024, 0, ss,
sizeof(ss), &ssSz, kem, sizeof(kem)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_Tls13_HybridSharedSecret(WOLFSSL_SECP256R1MLKEM768, 0, ss,
sizeof(ss), &ssSz, kem, 16), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
#endif
#endif
return EXPECT_RESULT();
}
4 changes: 3 additions & 1 deletion tests/api/test_kdf.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@
int test_wc_KdfDecisionCoverage(void);
int test_wc_KdfFeatureCoverage(void);
int test_wc_SshKdfReuse(void);
int test_wc_Tls13_HybridSharedSecret(void);

#define TEST_KDF_DECLS \
TEST_DECL_GROUP("kdf", test_wc_KdfDecisionCoverage), \
TEST_DECL_GROUP("kdf", test_wc_KdfFeatureCoverage), \
TEST_DECL_GROUP("kdf", test_wc_SshKdfReuse)
TEST_DECL_GROUP("kdf", test_wc_SshKdfReuse), \
TEST_DECL_GROUP("kdf", test_wc_Tls13_HybridSharedSecret)

#endif /* WOLFCRYPT_TEST_KDF_H */
Loading
Loading