Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more PQC hybrid key exchange algorithms #7821

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

Frauschi
Copy link
Contributor

@Frauschi Frauschi commented Aug 1, 2024

Hi,

This PR adds support for all remaining hybrid PQC + ECC hybrid key exchange groups to match OQS. Next to two new combinations with SECP curves, this mainly also adds support for combinations with X25519 and X448.

This also enables compatibility with the PQC key exchange support in Chromium browsers and Mozilla Firefox (hybrid Kyber768 and X25519; when WOLFSSL_ML_KEM is not defined).

In the process of extending support, some code and logic cleanup happened. Furthermore, two memory leaks within the hybrid code path have been fixed.

Looking forward to your feedback.

@wolfSSL-Bot
Copy link

Can one of the admins verify this patch?

@dgarske
Copy link
Contributor

dgarske commented Aug 1, 2024

Okay to test. Contributor agreement on file. @anhu please review. Thanks

@dgarske
Copy link
Contributor

dgarske commented Aug 1, 2024

Seems to be consistently failing with:

   842: test_multiple_crls_same_issuer                      :error = -361, certificate revoked
error = -313, received alert fatal error

ERROR - tests/api.c line 7417 failed with:
    expected: test_ssl_memio_do_handshake(&test_ctx, 10, ((void *)0)) == (1)
    result:   0 != 1

error = -361, certificate revoked
error = -313, received alert fatal error

ERROR - tests/api.c line 7417 failed with:
    expected: test_ssl_memio_do_handshake(&test_ctx, 10, ((void *)0)) == (1)
    result:   0 != 1

...

Server Random : 136CBD0B1E6A36EBA50790532D5963534E14BF12E3SSL_accept error -352, Bad ECC Peer Key
wolfSSL error: SSL_accept failed
C35D79C957A2C04C2DF2C2
Client message: hello wolfssl!
I hear you fa shizzle!
trying server command line[1541]: SuiteTest -v 4 -l TLS13-AES128-GCM-SHA256 -c ./certs/server-ecc.pem -k ./certs/ecc-key.pem -Y -2 -p 0 
trying client command line[1542]: SuiteTest -v 4 -l TLS13-AES128-GCM-SHA256 -A ./certs/ca-ecc-cert.pem -y -2 -p 34205 
FAIL scripts/unit.test (exit status: 1)

Copy link
Member

@anhu anhu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please be sure to test against --enable-kyber

Comment on lines 299 to 317
{ WOLFSSL_X25519_KYBER_LEVEL1, "X25519_KYBER_LEVEL1" },
{ WOLFSSL_X448_KYBER_LEVEL3, "X448_KYBER_LEVEL3" },
{ WOLFSSL_X25519_KYBER_LEVEL3, "X25519_KYBER_LEVEL3" },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its interesting.

You are adding :

  • WOLFSSL_X25519_KYBER_LEVEL1
  • WOLFSSL_X448_KYBER_LEVEL3
  • WOLFSSL_X25519_KYBER_LEVEL3
  • WOLFSSL_P256_KYBER_LEVEL3
  • WOLFSSL_P384_KYBER_LEVEL5

Can you let us know why you are adding these? I suppose they are to interop with other places, but can you let us know specifically (ie which ones are mozilla and which ones are liboqs)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am currently investigating the real-world performance impact of various PQC deployments in different embedded context (for my PhD). As hybrid constellations using X25519 and X448 are pretty popular in other projects, I wanted to test them, too. That's why I started this work in the first place.

The compatibility with the web browsers is based on the use case to access a web-based management interface of an embedded device and to make that PQC secure. For that, WOLFSSL_X25519_KYBER_LEVEL3 is the relevant hybrid constellation (that is what browser vendors are currently settled on).

The other constellations are based on what OQS has defined. I added them (besides my internal performance tests) for general interoperability with OQS and because it wasn't much overhead anyway.

@Frauschi
Copy link
Contributor Author

Frauschi commented Aug 2, 2024

Updated the PR with a fix for the failing tests.

Please be sure to test against --enable-kyber

The tests now properly work with --enable-all --enable-asn=template, --enable-all --enable-asn=template --enable-experimental --enable-kyber and --enable-all --enable-asn=template --enable-experimental --with-liboqs

Furthermore, I updated the unit tests to also test the new changes. The only thing I had to test manually are the new hybrid curves with DTLS (when also enabled with --enable-dtls13 --enable-dtls-frag-ch), as the unit tests break before even reaching those tests (pretty surely not related to this PR, as it fails equally on current master). Using the example client and server with the same arguments as the unit tests, the new curves also work with DTLS. I opened an issue for the failing DTLS tests (see #7825).

@Frauschi Frauschi requested a review from anhu August 2, 2024 14:16
@dgarske
Copy link
Contributor

dgarske commented Aug 5, 2024

Hi @Frauschi we had some issues with our GitHub CI actions. If you rebase this to latest master it should resolve the errors you are seeing. Thank you

@Frauschi
Copy link
Contributor Author

Frauschi commented Aug 5, 2024

Rebased to current master. Thanks for the hint.

FYI, I'm on vacation for the next two weeks, so any upcoming work on this will probably be delayed until afterward.

@Frauschi
Copy link
Contributor Author

Rebased to current master (since #7807 has been merged). I also think that the one NGINX test that has been failing wasn't related to this PR's changes? Can someone retest this please?

@douzzer
Copy link
Contributor

douzzer commented Aug 23, 2024

retest this please.

@JacobBarthelmeh
Copy link
Contributor

This says the branch has conflicts that must be resolved. Could you rebase on top of wolfssl/master please?

@Frauschi
Copy link
Contributor Author

This says the branch has conflicts that must be resolved. Could you rebase on top of wolfssl/master please?

Rebased to current master.

I also updated the PR to reflect the latest Code Point changes in OQS and also incorporated the new Code Points currently set in draft-kwiatkowski-tls-ecdhe-mlkem.

The only thing that is not done yet is the proposed swap of classic and PQC key material within draft-kwiatkowski-tls-ecdhe-mlkem in the key share in case of X25519 hybrids. This is done to always have a FIPS approved algorithms first in case of hybrids (and X25519 is not FIPS approved). See here.

This swap still has to be implemented. However, that will require more thorough code changes. I think I can tackle that around next week. You can decide if this should also go into this PR, or if that should go in a separate one.

@Frauschi
Copy link
Contributor Author

I updated the PR with two new commits to implement the reversed order of X25519/X448 based hybrids, following draft-kwiatkowski-tls-ecdhe-mlkem now.

I tested compatibility with both Firefox and Google Chrome nightly, as both have support for X25519MLKEM768. When compiling with Kyber Round 3 compatibility (WOLFSSL_KYBER_ORIGINALdefined), then we are still compatible to current versions using hybrids of X25519 and Kyber Round 3 (with the old order).

For testing the Round 3 compatibility, I used liboqs as the WolfSSL implementation is not working properly currently (see #8023).

@Frauschi
Copy link
Contributor Author

With the fixes in #8027 applied, we now also have compatibility with current versions of Chrome and Firefox when compiled with the WolfSSL Kyber implementation and with WOLFSSL_KYBER_ORIGINAL defined. (I manually patched the preferredGroup array to make sure the PQC key share is selected during the handshake, as indicated in #8024).

@Frauschi
Copy link
Contributor Author

Frauschi commented Nov 12, 2024

I updated the PR, replacing the three commits with two new ones. The first is a concatenation of the old three ones with further additions. The second one is a proposed fix for #8024.

The first commit adds the new X25519 / X448 hybrids, both for the draft and final versions of ML-KEM (based on the recent addition in #8143). This enables PQC compatibility with all current browsers.

I also updated some of the code points to reflect the latest drafts of draft-connolly-tls-mlkem-key-agreement and draft-kwiatkowski-tls-ecdhe-mlkem.

The proposed fix in the second commit is a simple but pragmatic solution for #8024. If you don't see that as a proper solution or if you prefer to handle that in a separate PR, I can drop the commit.

Tagging @SparkiDev and @ColtonWilley in addition to @anhu to also take a look at the changes.

@Frauschi
Copy link
Contributor Author

The failing GitHub CI tests should be fixed now.

Besides the spelling, the second commit regarding the group ranking caused problems in the tests related to the WOLFSSL_STATIC_EPHEMERAL option. The change results in a different selection of a key share group in case of a HelloRetryRequest message. For the tests, where static ephemeral keys are used with the WOLFSSL_STATIC_EPHEMERAL option , an additional check is necessary to make sure the correct key is used for the ECDH calculation.

@JacobBarthelmeh
Copy link
Contributor

JacobBarthelmeh commented Nov 13, 2024

Results of some of the tests:

[check-source-text] [2 of 7] [wolfssl]

C++-style comments:

./src/tls.c:9160:    word32   ssSzEcc = ssl->arrays->preMasterSz; // We need the buffer size for

./src/tls.c:9161:                                                 // the logic to work downwards.

./src/tls.c:9830:    word32 ssSzEcc = ssl->arrays->preMasterSz; // We need the buffer size for

./src/tls.c:9831:                                               // the logic to work downwards.

overlong lines added:

src/tls.c:8434     #if (!defined(NO_DH) || defined(HAVE_FALCON) || defined(HAVE_DILITHIUM)) && \

    check-source-text fail_E

we use the /**/ comment style and have a check for lines longer than 80 characters.

@Frauschi
Copy link
Contributor Author

Fixed the formatting issues (comment style and overlong lines. Sorry for that.

Not sure about the socat fails, as the two failing tests seem to be about IPv4 multicast?

@JacobBarthelmeh
Copy link
Contributor

Fixed the formatting issues (comment style and overlong lines. Sorry for that.

Not sure about the socat fails, as the two failing tests seem to be about IPv4 multicast?

Thanks @Frauschi . Yeah the scoat failure is unrelated to your changes. Looks like multiple pull requests are having the issue.

@dgarske
Copy link
Contributor

dgarske commented Nov 15, 2024

Retest this please. PRB-master-history lost.

@Frauschi
Copy link
Contributor Author

Rebased to current master to fix the conflicts.

Copy link
Contributor

@dgarske dgarske left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These clang-tidy warnings are all new with your PR:

[quantum-safe-wolfssl-all-clang-tidy] [1 of 1] [dbd3d04f75] [previously skipped]
    autogen.sh dbd3d04f75...   real 0m10.759s  user 0m8.836s  sys 0m1.106s
    configure...   real 0m27.885s  user 0m14.690s  sys 0m15.295s
    build.../tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9958:13: warning: Null pointer passed to 2nd parameter expecting 'nonnull' [clang-analyzer-core.NonNullParamChecker]
9958 |             XMEMCPY(ecc_kse->ke, data + pubOffset, ecc_kse->keLen);
|             ^
./wolfssl/wolfcrypt/types.h:735:35: note: expanded from macro 'XMEMCPY'
735 |         #define XMEMCPY(d,s,l)    memcpy((d),(s),(l))
|                                   ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15742:23: note: Assuming 'msgType' is not equal to client_hello
15742 |     byte isRequest = (msgType == client_hello ||
|                       ^~~~~~~~~~~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15742:23: note: Left side of '||' is false
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15743:23: note: Assuming 'msgType' is not equal to certificate_request
15743 |                       msgType == certificate_request);
|                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15753:9: note: Assuming 'ssl' is non-null
15753 |     if (!ssl || !input || (isRequest && !suites))
|         ^~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15753:9: note: Left side of '||' is false
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15753:17: note: Assuming 'input' is non-null
15753 |     if (!ssl || !input || (isRequest && !suites))
|                 ^~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15753:9: note: Left side of '||' is false
15753 |     if (!ssl || !input || (isRequest && !suites))
|         ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15753:28: note: 'isRequest' is 0
15753 |     if (!ssl || !input || (isRequest && !suites))
|                            ^~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15753:38: note: Left side of '&&' is false
15753 |     if (!ssl || !input || (isRequest && !suites))
|                                      ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15759:12: note: 'ret' is equal to 0
15759 |     while (ret == 0 && offset < length) {
|            ^~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15759:12: note: Left side of '&&' is true
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15759:24: note: Assuming 'offset' is < 'length'
15759 |     while (ret == 0 && offset < length) {
|                        ^~~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15759:5: note: Loop condition is true.  Entering loop body
15759 |     while (ret == 0 && offset < length) {
|     ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15764:13: note: 'msgType' is not equal to client_hello
15764 |         if (msgType == client_hello && pskDone) {
|             ^~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15764:37: note: Left side of '&&' is false
15764 |         if (msgType == client_hello && pskDone) {
|                                     ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15770:13: note: Assuming the condition is false
15770 |         if (length - offset < HELLO_EXT_TYPE_SZ + OPAQUE16_LEN)
|             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15770:9: note: Taking false branch
15770 |         if (length - offset < HELLO_EXT_TYPE_SZ + OPAQUE16_LEN)
|         ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15780:14: note: Assuming 'type' is <= 62
15780 |         if ((type <= 62) || (type == TLSX_RENEGOTIATION_INFO)
|              ^~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15780:26: note: Left side of '||' is true
15780 |         if ((type <= 62) || (type == TLSX_RENEGOTIATION_INFO)
|                          ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15787:17: note: Assuming right operand of bit shift is non-negative
15787 |             if (IS_OFF(seenType, TLSX_ToSemaphore(type))) {
|                 ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:1488:45: note: expanded from macro 'IS_OFF'
1488 |     (!(((semaphore)[(light) / 8] &  (byte) (0x01 << ((light) % 8)))))
|                                             ^~~~~~~~~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15787:17: note: Assuming the condition is true
15787 |             if (IS_OFF(seenType, TLSX_ToSemaphore(type))) {
|                 ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:1488:6: note: expanded from macro 'IS_OFF'
1488 |     (!(((semaphore)[(light) / 8] &  (byte) (0x01 << ((light) % 8)))))
|      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15787:13: note: Taking true branch
15787 |             if (IS_OFF(seenType, TLSX_ToSemaphore(type))) {
|             ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15795:13: note: Assuming the condition is false
15795 |         if (length - offset < size)
|             ^~~~~~~~~~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15795:9: note: Taking false branch
15795 |         if (length - offset < size)
|         ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15799:9: note: Control jumps to 'case hello_retry_request:'  at line 15810
15799 |         switch (msgType) {
|         ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15811:21: note: Assuming the condition is false
15811 |                 if (size < TLSX_GET_MIN_SIZE_SERVER(&type)){
|                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15811:17: note: Taking false branch
15811 |                 if (size < TLSX_GET_MIN_SIZE_SERVER(&type)){
|                 ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15815:13: note:  Execution continues on line 15821
15815 |             break;
|             ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:15821:9: note: Control jumps to 'case TLSX_KEY_SHARE:'  at line 16283
15821 |         switch (type) {
|         ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:16284:17: note: Loop condition is false.  Exiting loop
16284 |                 WOLFSSL_MSG("Key Share extension received");
|                 ^
./wolfssl/wolfcrypt/logging.h:228:35: note: expanded from macro 'WOLFSSL_MSG'
228 |     #define WOLFSSL_MSG(m)        WC_DO_NOTHING
|                                   ^
./wolfssl/wolfcrypt/types.h:341:31: note: expanded from macro 'WC_DO_NOTHING'
341 |         #define WC_DO_NOTHING do {} while (0)
|                               ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:16290:21: note: Assuming the condition is false
16290 |                 if (!IsAtLeastTLSv1_3(ssl->version))
|                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:16290:17: note: Taking false branch
16290 |                 if (!IsAtLeastTLSv1_3(ssl->version))
|                 ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:16293:21: note: 'msgType' is not equal to client_hello
16293 |                 if (msgType != client_hello && msgType != server_hello &&
|                     ^~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:16293:21: note: Left side of '&&' is true
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:16293:48: note: 'msgType' is not equal to server_hello
16293 |                 if (msgType != client_hello && msgType != server_hello &&
|                                                ^~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:16293:21: note: Left side of '&&' is true
16293 |                 if (msgType != client_hello && msgType != server_hello &&
|                     ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:16294:25: note: 'msgType' is equal to hello_retry_request
16294 |                         msgType != hello_retry_request) {
|                         ^~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:16293:17: note: Taking false branch
16293 |                 if (msgType != client_hello && msgType != server_hello &&
|                 ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:16300:23: note: Calling 'TLSX_KeyShare_Parse'
16300 |                 ret = KS_PARSE(ssl, input + offset, size, msgType);
|                       ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:11029:22: note: expanded from macro 'KS_PARSE'
11029 | #define KS_PARSE     TLSX_KeyShare_Parse
|                      ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9579:9: note: 'msgType' is not equal to client_hello
9579 |     if (msgType == client_hello) {
|         ^~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9579:5: note: Taking false branch
9579 |     if (msgType == client_hello) {
|     ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9583:14: note: 'msgType' is not equal to server_hello
9583 |     else if (msgType == server_hello) {
|              ^~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9583:10: note: Taking false branch
9583 |     else if (msgType == server_hello) {
|          ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9625:14: note: 'msgType' is equal to hello_retry_request
9625 |     else if (msgType == hello_retry_request) {
|              ^~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9625:10: note: Taking true branch
9625 |     else if (msgType == hello_retry_request) {
|          ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9626:13: note: Assuming 'length' is equal to OPAQUE16_LEN
9626 |         if (length != OPAQUE16_LEN)
|             ^~~~~~~~~~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9626:9: note: Taking false branch
9626 |         if (length != OPAQUE16_LEN)
|         ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9638:13: note: Taking false branch
9638 |             if (!TLSX_SupportedGroups_Find(ssl, group, ssl->extensions)) {
|             ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9644:13: note: Taking false branch
9644 |             if (TLSX_KeyShare_Find(ssl, group)) {
|             ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9651:17: note: 'ret' is equal to 0
9651 |             if (ret != 0)
|                 ^~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9651:13: note: Taking false branch
9651 |             if (ret != 0)
|             ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9655:48: note: Passing null pointer value via 4th parameter 'data'
9655 |         ret = TLSX_KeyShare_Use(ssl, group, 0, NULL, NULL, &ssl->extensions);
|                                                ^
/usr/lib/llvm-18/lib/clang/18/include/__stddef_null.h:26:14: note: expanded from macro 'NULL'
26 | #define NULL ((void*)0)
|              ^~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9655:15: note: Calling 'TLSX_KeyShare_Use'
9655 |         ret = TLSX_KeyShare_Use(ssl, group, 0, NULL, NULL, &ssl->extensions);
|               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:10061:9: note: Assuming 'extension' is not equal to NULL
10061 |     if (extension == NULL) {
|         ^~~~~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:10061:5: note: Taking false branch
10061 |     if (extension == NULL) {
|     ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:10075:12: note: Assuming 'keyShareEntry' is equal to NULL
10075 |     while (keyShareEntry != NULL) {
|            ^~~~~~~~~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:10075:5: note: Loop condition is false. Execution continues on line 10082
10075 |     while (keyShareEntry != NULL) {
|     ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:10082:9: note: 'keyShareEntry' is equal to NULL
10082 |     if (keyShareEntry == NULL) {
|         ^~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:10082:5: note: Taking true branch
10082 |     if (keyShareEntry == NULL) {
|     ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:10085:13: note: 'ret' is equal to 0
10085 |         if (ret != 0)
|             ^~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:10085:9: note: Taking false branch
10085 |         if (ret != 0)
|         ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:10091:9: note: Assuming field 'side' is equal to WOLFSSL_SERVER_END
10091 |     if (ssl->options.side == WOLFSSL_SERVER_END &&
|         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:10091:9: note: Left side of '&&' is true
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:10092:13: note: Assuming the condition is false
10092 |             WOLFSSL_NAMED_GROUP_IS_PQC(group)) {
|             ^
./wolfssl/internal.h:1868:43: note: expanded from macro 'WOLFSSL_NAMED_GROUP_IS_PQC'
1868 | #define WOLFSSL_NAMED_GROUP_IS_PQC(group) NamedGroupIsPqc(group)
|                                           ^~~~~~~~~~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:10091:5: note: Taking false branch
10091 |     if (ssl->options.side == WOLFSSL_SERVER_END &&
|     ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:10101:27: note: Field 'side' is equal to WOLFSSL_SERVER_END
10101 |     else if (ssl->options.side == WOLFSSL_SERVER_END &&
|                           ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:10101:14: note: Left side of '&&' is true
10101 |     else if (ssl->options.side == WOLFSSL_SERVER_END &&
|              ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:10102:14: note: Assuming the condition is true
10102 |              WOLFSSL_NAMED_GROUP_IS_PQC_HYBRID(group)) {
|              ^
./wolfssl/internal.h:1869:50: note: expanded from macro 'WOLFSSL_NAMED_GROUP_IS_PQC_HYBRID'
1869 | #define WOLFSSL_NAMED_GROUP_IS_PQC_HYBRID(group) NamedGroupIsPqcHybrid(group)
|                                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:10101:10: note: Taking true branch
10101 |     else if (ssl->options.side == WOLFSSL_SERVER_END &&
|          ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:10105:54: note: Passing null pointer value via 3rd parameter 'data'
10105 |                                                      data, len);
|                                                      ^~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:10103:15: note: Calling 'TLSX_KeyShare_HandlePqcHybridKeyServer'
10103 |         ret = TLSX_KeyShare_HandlePqcHybridKeyServer((WOLFSSL*)ssl,
|               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 10104 |                                                      keyShareEntry,
|                                                      ~~~~~~~~~~~~~~
 10105 |                                                      data, len);
|                                                      ~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9837:9: note: Assuming 'ecc_group' is not equal to 0
9837 |     if (ecc_group == 0 || pqc_group == 0) {
|         ^~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9837:9: note: Left side of '||' is false
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9837:27: note: Assuming 'pqc_group' is not equal to 0
9837 |     if (ecc_group == 0 || pqc_group == 0) {
|                           ^~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9837:5: note: Taking false branch
9837 |     if (ecc_group == 0 || pqc_group == 0) {
|     ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9842:9: note: 'ret' is equal to 0
9842 |     if (ret == 0) {
|         ^~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9842:5: note: Taking true branch
9842 |     if (ret == 0) {
|     ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9847:13: note: Assuming 'ecc_kse' is not equal to NULL
9847 |         if (ecc_kse == NULL || pqc_kse == NULL) {
|             ^~~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9847:13: note: Left side of '||' is false
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9847:32: note: Assuming 'pqc_kse' is not equal to NULL
9847 |         if (ecc_kse == NULL || pqc_kse == NULL) {
|                                ^~~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9847:9: note: Taking false branch
9847 |         if (ecc_kse == NULL || pqc_kse == NULL) {
|         ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9856:9: note: 'ret' is equal to 0
9856 |     if (ret == 0) {
|         ^~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9856:5: note: Taking true branch
9856 |     if (ret == 0) {
|     ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9863:13: note: Assuming field 'key' is not equal to NULL
9863 |         if (pqc_kse->key == NULL) {
|             ^~~~~~~~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9863:9: note: Taking false branch
9863 |         if (pqc_kse->key == NULL) {
|         ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9867:13: note: 'ret' is equal to 0
9867 |         if (ret == 0) {
|             ^~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9867:9: note: Taking true branch
9867 |         if (ret == 0) {
|         ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9870:13: note: 'ret' is equal to 0
9870 |         if (ret != 0) {
|             ^~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9870:9: note: Taking false branch
9870 |         if (ret != 0) {
|         ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9874:13: note: 'ret' is equal to 0
9874 |         if (ret == 0) {
|             ^~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9874:9: note: Taking true branch
9874 |         if (ret == 0) {
|         ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9877:17: note: Assuming 'ret' is equal to 0
9877 |             if (ret != 0) {
|                 ^~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9877:13: note: Taking false branch
9877 |             if (ret != 0) {
|             ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9881:13: note: 'ret' is equal to 0
9881 |         if (ret == 0) {
|             ^~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9881:9: note: Taking true branch
9881 |         if (ret == 0) {
|         ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9885:13: note: Assuming 'ret' is equal to 0
9885 |         if (ret == 0) {
|             ^~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9885:9: note: Taking true branch
9885 |         if (ret == 0) {
|         ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9889:13: note: Assuming 'ret' is equal to 0
9889 |         if (ret == 0) {
|             ^~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9889:9: note: Taking true branch
9889 |         if (ret == 0) {
|         ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9896:9: note: Assuming 'ret' is equal to 0
9896 |     if (ret == 0 && ecc_group != 0) {
|         ^~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9896:9: note: Left side of '&&' is true
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9896:21: note: 'ecc_group' is not equal to 0
9896 |     if (ret == 0 && ecc_group != 0) {
|                     ^~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9896:5: note: Taking true branch
9896 |     if (ret == 0 && ecc_group != 0) {
|     ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9900:13: note: Assuming 'ecc_group' is equal to WOLFSSL_ECC_X25519
9900 |         if (ecc_group == WOLFSSL_ECC_X25519) {
|             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9900:9: note: Taking true branch
9900 |         if (ecc_group == WOLFSSL_ECC_X25519) {
|         ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9917:9: note: 'ret' is equal to 0
9917 |     if (ret == 0 && len != pubSz + ecc_kse->pubKeyLen) {
|         ^~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9917:9: note: Left side of '&&' is true
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9917:21: note: Assuming the condition is false
9917 |     if (ret == 0 && len != pubSz + ecc_kse->pubKeyLen) {
|                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9917:5: note: Taking false branch
9917 |     if (ret == 0 && len != pubSz + ecc_kse->pubKeyLen) {
|     ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9924:9: note: 'ret' is equal to 0
9924 |     if (ret == 0) {
|         ^~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9924:5: note: Taking true branch
9924 |     if (ret == 0) {
|     ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9928:13: note: Assuming 'ciphertext' is not equal to NULL
9928 |         if (ciphertext == NULL) {
|             ^~~~~~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9928:9: note: Taking false branch
9928 |         if (ciphertext == NULL) {
|         ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9938:9: note: 'ret' is equal to 0
9938 |     if (ret == 0) {
|         ^~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9938:5: note: Taking true branch
9938 |     if (ret == 0) {
|     ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9942:13: note: Assuming field 'ke' is not equal to NULL
9942 |         if (ecc_kse->ke == NULL) {
|             ^~~~~~~~~~~~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9942:9: note: Taking false branch
9942 |         if (ecc_kse->ke == NULL) {
|         ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9946:13: note: 'ret' is equal to 0
9946 |         if (ret == 0) {
|             ^~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9946:9: note: Taking true branch
9946 |         if (ret == 0) {
|         ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9953:17: note: Assuming 'pqc_first' is 0
9953 |             if (pqc_first) {
|                 ^~~~~~~~~
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9953:13: note: Taking false branch
9953 |             if (pqc_first) {
|             ^
/tmp/wolfssl_test_workdir.3225822/wolfssl/src/tls.c:9958:13: note: Null pointer passed to 2nd parameter expecting 'nonnull'
9958 |             XMEMCPY(ecc_kse->ke, data + pubOffset, ecc_kse->keLen);
|             ^
./wolfssl/wolfcrypt/types.h:735:35: note: expanded from macro 'XMEMCPY'
735 |         #define XMEMCPY(d,s,l)    memcpy((d),(s),(l))
|                                   ^          ~~~
make[2]: *** [Makefile:8244: src/libwolfssl_la-tls.lo] Error 1
make[2]: *** Waiting for unfinished jobs....
make[1]: *** [Makefile:9032: all-recursive] Error 1
make: *** [Makefile:5382: all] Error 2
   real 3m23.468s  user 19m51.936s  sys 0m27.259s
    quantum-safe-wolfssl-all-clang-tidy fail_build
    failed config: '--srcdir' '.' '--disable-jobserver' '--enable-option-checking=fatal' '--enable-all' '--enable-testcert' '--enable-intelasm' '--enable-sp-asm' '--enable-experimental' '--enable-kyber' '--enable-lms' '--enable-xmss' '--enable-dilithium' '--disable-qt' 'CPPFLAGS=-DWOLFSSL_SP_INT_NEGATIVE -DNO_WOLFSSL_CIPHER_SUITE_TEST -DWOLFSSL_OLD_PRIME_CHECK -pedantic -DSP_ALLOC -DWOLFSSL_CLANG_TIDY' 'CC=/home/davidgarske/GitHub/testing/git-hooks/clang-tidy-builder.sh' 'CLANG=/usr/lib/llvm-18/bin/clang-18' 'CFLAGS=-Wunreachable-code-aggressive -Wthread-safety -Wloop-analysis -Wenum-compare-conditional -fcolor-diagnostics -fcomplete-member-pointers -Wheader-hygiene -Wstring-conversion -Wtautological-overlap-compare -Wno-language-extension-token -Wunreachable-code-break -Wunreachable-code-return -Wimplicit-fallthrough -Wno-language-extension-token'
    BUILD_ENV: 'CLANG_TIDY=/usr/lib/llvm-18/bin/clang-tidy' 'CLANG=/usr/lib/llvm-18/bin/clang-18' 'CLANG_TIDY_EXTRA_ARGS=--use-color=1 --quiet -line-filter=[{"name":"asn1.h","lines":[[1,166]]},{"name":".c"},{"name":".h"},{"name":".s"},{"name":".S"},{"name":".i"}]' 'CLANG_OVERRIDE_CFLAGS=' 'FAIL_BUILD_CODENAME=fail_analytic_build' 'MAX_FIPS_CODE_SZ=10000000'
exiting with status 2
results for dbd3d04f75:
failed: quantum-safe-wolfssl-all-clang-tidy
final tally for dbd3d04f75 with build env de3c7da84a: 0 of 1 check succeeded, 0 skipped, 0 forced, and 1 failed.
stopped at config: 'quantum-safe-wolfssl-all-clang-tidy' '--srcdir' '.' '--disable-jobserver' '--enable-option-checking=fatal' '--enable-all' '--enable-testcert' '--enable-intelasm' '--enable-sp-asm' '--enable-experimental' '--enable-kyber' '--enable-lms' '--enable-xmss' '--enable-dilithium' '--disable-qt' 'CPPFLAGS=-DWOLFSSL_SP_INT_NEGATIVE -DNO_WOLFSSL_CIPHER_SUITE_TEST -DWOLFSSL_OLD_PRIME_CHECK -pedantic -DSP_ALLOC -DWOLFSSL_CLANG_TIDY' 'CC=/home/davidgarske/GitHub/testing/git-hooks/clang-tidy-builder.sh' 'CLANG=/usr/lib/llvm-18/bin/clang-18' 'CFLAGS=-Wunreachable-code-aggressive -Wthread-safety -Wloop-analysis -Wenum-compare-conditional -fcolor-diagnostics -fcomplete-member-pointers -Wheader-hygiene -Wstring-conversion -Wtautological-overlap-compare -Wno-language-extension-token -Wunreachable-code-break -Wunreachable-code-return -Wimplicit-fallthrough -Wno-language-extension-token'

@douzzer
Copy link
Contributor

douzzer commented Nov 16, 2024

@Frauschi note build failure in CI testing:

Testing configuration:
--with-liboqs --enable-experimental
[...]
src/tls.c: In function ‘TLSX_KeyShare_IsSupported’:
src/tls.c:10321:43: error: passing argument 3 of ‘findEccPqc’ makes pointer from integer without a cast [-Werror=int-conversion]
10321 |             findEccPqc(NULL, &namedGroup, namedGroup);
      |                                           ^~~~~~~~~~
      |                                           |
      |                                           int
src/tls.c:8193:49: note: expected ‘int *’ but argument is of type ‘int’
 8193 | static void findEccPqc(int *ecc, int *pqc, int *pqc_first, int group)
      |                                            ~~~~~^~~~~~~~~
src/tls.c:10321:13: error: too few arguments to function ‘findEccPqc’
10321 |             findEccPqc(NULL, &namedGroup, namedGroup);
      |             ^~~~~~~~~~
src/tls.c:8193:13: note: declared here
 8193 | static void findEccPqc(int *ecc, int *pqc, int *pqc_first, int group)
      |             ^~~~~~~~~~

@douzzer douzzer assigned anhu and douzzer and unassigned wolfSSL-Bot Nov 16, 2024
Add support for X25519 and X448 based hybrid PQC + ECC key exchange
groups. Furthermore, two new combinations with SECP curves are added to
match OQS combinations.

This also incorporates the changed order of X25519 and X448 based
combinations to place the PQC material before the ECDH material. This is
motivated by the necessity to always have material of a FIPS approved
algorithm first.

Also, codepoints are updated to reflect the latest draft standards for
pure ML-KEM and some of the hybrids. With these changes and based on the
recent additions to both enable ML-KEM final and draft versions
simultaneously, a WolfSSL TLS server is now compatible with all recent
browsers that support either the draft version of ML-KEM (Chromium based
browsers and Firefox < version 132; only when the draft version is
enabled in the build) or the final version already (Firefox > version 132).

In the process of extending support, some code and logic cleanup
happened. Furthermore, some memory leaks within the hybrid code path have
been fixed.

Signed-off-by: Tobias Frauenschläger <[email protected]>
In case no user group ranking is set, all groups are now ranked equally
instead of the order in the `preferredGroup` array. This is the
behavior already indicated in the comment header of the function.

This change is necessary for applications that do not set their own
group ranking (via `wolfSSL_CTX_set_groups()` for example). When such an
application creates a TLS server and receives a ClientHello message with
multiple key shares, now the first key share is selected instead of the
one with the lowest index in the `preferredGroup` array.

Recent browsers with PQC support place two key shares in their
ClientHello message: a hybrid PQC + X25519 one and at least one
classic-only one. The hybrid one is the first one, indicating a
preference. Without this change, however, always the classic-only key
share has been selected, as these algorithms have a lower index in the
`preferredGroup` array compared to the PQC hybrids.

Tested using a patched version of NGINX.

This change also results in a different selection of a key share group
in case of a HelloRetryRequest message. For the tests, where static
ephemeral keys are used (`WOLFSSL_STATIC_EPHEMERAL`), an additional
check is necessary to make sure the correct key is used for the ECDH
calculation.

Signed-off-by: Tobias Frauenschläger <[email protected]>
@Frauschi
Copy link
Contributor Author

@dgarske @douzzer Thanks for the error logs, should both be fixed now.

@dgarske dgarske self-requested a review November 18, 2024 15:41
@dgarske
Copy link
Contributor

dgarske commented Nov 18, 2024

Okay to test. Thank you @Frauschi

Copy link
Contributor

@dgarske dgarske left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incredible amount of good work here! Reviewed all the code and also ran it through the quantum-safe-wolfssl-all-clang-tidy test successfully. Thank you @Frauschi

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants