Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

mnbogner
Copy link
Contributor

As requested by @tweksteen here: #1340 (comment), this is a first step towards adding ECH support to Conscrypt. This PR includes changes to the native layer to expose parts of the BoringSSL API.

Original description of #1044:

This is the first stage of implementing Encrypted ClientHello (ECH) in Conscrypt #730. It provides the APIs required for clients to make TLS connections using ECH. This implements enough of the server-side to provide ECH in the test suite using ECH Key and Configs generated by boringssl. This should be enough to let libs like OkHTTP fully implement ECH square/okhttp#6539

@tweksteen tweksteen self-requested a review August 15, 2025 00:15
@tweksteen tweksteen self-assigned this Aug 15, 2025
Copy link
Member

@tweksteen tweksteen left a comment

Choose a reason for hiding this comment

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

Thank you so much for your pull request. This is looking good! I haven't reviewed all the changes yet, but see my comments below. Thanks!

@bjiang7 FYI

@mnbogner
Copy link
Contributor Author

Thank you so much for your pull request. This is looking good! I haven't reviewed all the changes yet, but see my comments below. Thanks!

@bjiang7 FYI

Thank you for the feedback. I will be on vacation for 2 weeks but I will start making changes as soon as I return.

@mnbogner
Copy link
Contributor Author

mnbogner commented Sep 3, 2025

Thank you so much for your pull request. This is looking good! I haven't reviewed all the changes yet, but see my comments below. Thanks!

@bjiang7 FYI

Thank you for the feedback. I will be on vacation for 2 weeks but I will start making changes as soon as I return.

Thank you so much for your pull request. This is looking good! I haven't reviewed all the changes yet, but see my comments below. Thanks!

@bjiang7 FYI

Aside from the question about test_SSL_do_handshake_ech_retry_configs, I believe I've resolved the rest of the requests. Let me know if there's any other changes to make. I'll remove my temporary changes once everything else is ready.

Copy link
Member

@tweksteen tweksteen left a comment

Choose a reason for hiding this comment

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

Thanks for following-up with the changes! Just a few more comments. Happy to merge once these are resolved

</issue>

<!-- Workaround for "Unexpected failure during lint analysis". -->
<issue id="LintError">
Copy link
Member

Choose a reason for hiding this comment

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

side note: do you know why this is necessary for your setup? I don't see any issues locally, but it might be due to some version difference?

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 had forgotten about this change. I assume it's an issue with my local configuration but the error wasn't specific. I can pull it out when I'm done.

char token;
do {
(void)read(appData->fdsEmergency[0], &token, 1);
// TEMP - fixes build error
Copy link
Member

Choose a reason for hiding this comment

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

Do you get a compiler warning here otherwise?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This one was more specific:

error: ignoring return value of ‘ssize_t read(int, void*, size_t)’ declared with attribute ‘warn_unused_result’ [-Werror=unused-result]
 7798 |                 (void)read(appData->fdsEmergency[0], &token, 1);
      |                       ~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

First I tried assigning the result to a variable, but then it complained that the variable was unused and that's how I ended up with the current fix. There's about 4 places where this happens but this was the only one in a file I had to commit.

@tweksteen
Copy link
Member

There are a few failures in CI tests, could you please take a look? Thanks

@mnbogner
Copy link
Contributor Author

mnbogner commented Sep 4, 2025

There are a few failures in CI tests, could you please take a look? Thanks

2 of those look like test failures which i think i can reproduce locally, but clang_format_check just shows a diff and then an error message so I'm not sure where to start.

@mnbogner
Copy link
Contributor Author

mnbogner commented Sep 4, 2025

There are a few failures in CI tests, could you please take a look? Thanks

Perhaps I spoke too soon. test_get_RSA_public_params throws a RuntimeException in git but throws a InvalidKeyException locally. EVP_PKEY_cmp_BothNullParameters and test_get_RSA_public_params throw java.lang.AssertionError: "Error queue should have been empty" in git but both pass locally.

@tweksteen
Copy link
Member

The CI is running with the property checkErrorQueue on (see /gradlew :conscrypt-openjdk:testJar -PcheckErrorQueue). That might help you to reproduce locally.

The diff from clang-format should show you how to fix the format to match the expected output.

@tweksteen
Copy link
Member

On clang-format, if you omit the --diff option, it will attempt to fix the formatting automatically. Something like: git clang-format --style=file origin/master '*.c' '*.h' '*.cc' '*.cpp' '*.java'

@mnbogner
Copy link
Contributor Author

mnbogner commented Sep 5, 2025

The CI is running with the property checkErrorQueue on (see /gradlew :conscrypt-openjdk:testJar -PcheckErrorQueue). That might help you to reproduce locally.

I was able to reproduce it locally, but now that I see that flag, I noticed that elsewhere in NativeCryptoTest it looks as if this was solved by adding try/catch blocks. Is there any reason I shouldn't do the same thing?

// ignored when running with checkErrorQueue

// AssertionError when running with checkErrorQueue

EDIT: Now I see that these are also ECH tests so maybe there are other failures being obfuscated by this. Is that flag causing the tests to catch errors spawned by the native code?

@tweksteen
Copy link
Member

Thanks for taking a look at these. The checkErrorQueue parameter exists to make sure that we don't leave any error in the BoringSSL error queue after a call. Otherwise, later calls might be reported as false positives.

I think we should handle the clean up of the queue in the method itself, and the same behaviour should be observed in our tests regardless of checkErrorQueue value.

For instance, NativeCrypto_SSL_set1_ech_config_list could be updated similarly to NativeCrypto_EVP_parse_private_key with something like:

    if (!ret) {
        conscrypt::jniutil::throwIllegalArgumentException(env, "Error parsing the ECHConfig");
        ERR_clear_error();
        JNI_TRACE(...);
        return;
    }

This is changing the signature of the function here, from returning a boolean to throwing an exception, but I think this makes more sense. What do you think?

@mnbogner
Copy link
Contributor Author

mnbogner commented Sep 9, 2025

NativeCrypto_EVP_parse_private_key

It looks as if NativeCrypto_EVP_parse_private_key does return a value in addition to throwing an exception, and appears to return 0 in the case of an error. For NativeCrypto_SSL_set1_ech_config_list, I assume i would change the signature because the return value is just an indicator of success or failure. I am curious why NativeCrypto_EVP_parse_private_key throws an exception for one failure state (failed to parse private key) but not for the other (key bytes are null). It looks like NativeCrypto_SSL_set1_ech_config_list is similar, where it can either fail because the bytes passed in are null, or because the config list is invalid, and if the null input shouldn't cause an exception, I would need to return some other error indicator.

EDIT: I still think your suggestion makes sense in terms of preserving consistency, but it looks as if all three of these tests pass in null values and expect failure, but for some reason it seems like they don't fail at the null check, but continue on to the code that attempts to parse the input and generates an unexpected error.

@tweksteen
Copy link
Member

I agree, NativeCrypto_EVP_parse_private_key was probably not the best example. What about calling conscrypt::jniutil::throwNullPointerException if the config is null? We would then raise an exception for all the error scenarios?

@mnbogner
Copy link
Contributor Author

I agree, NativeCrypto_EVP_parse_private_key was probably not the best example. What about calling conscrypt::jniutil::throwNullPointerException if the config is null? We would then raise an exception for all the error scenarios?

Oops, I'd already finished a batch of changes so hopefully they make sense. I tried to follow the pattern I saw elsewhere of throwing an exception but still returning a value so the function signatures remained the same. I also tried to match them to sensible jniutil throw methods (mostly null pointer with some parsing exceptions). The tests are now all passing locally with the -PcheckErrorQueue flag.

return result;
if (result < 0) {
conscrypt::jniutil::throwInvalidKeyException(env, "Decoding error");
ERR_clear_error();
Copy link
Contributor

Choose a reason for hiding this comment

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

Drive-by comment: If you're going to throw a checked exception, you should reflect that in the Java method signature and add a regression test for it. Same for the parsing exception below.

Copy link
Member

Choose a reason for hiding this comment

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

+1, but more generally let's not change the non-ECH part of the code in this pull request. If you wanted to, please follow up with a new PR.

Could you please revert the changes here that are not related to ECH? Thanks

Copy link
Contributor Author

@mnbogner mnbogner Sep 23, 2025

Choose a reason for hiding this comment

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

I ended up making these changes because one of the failing tests was the one that tested this method:

org.conscrypt.ConscryptOpenJdkSuite > org.conscrypt.NativeCryptoTest.EVP_PKEY_cmp_BothNullParameters FAILED
    java.lang.AssertionError: unexpected exception type thrown; expected:<java.lang.NullPointerException> but was:<java.lang.AssertionError>
        at org.junit.Assert.assertThrows(Assert.java:1020)
        at org.junit.Assert.assertThrows(Assert.java:981)
        at org.conscrypt.NativeCryptoTest.EVP_PKEY_cmp_BothNullParameters(NativeCryptoTest.java:244)
        Caused by:
        java.lang.AssertionError: Error queue should have been empty but was (/home/mnbogner/Software/Repository/boringssl/ssl/encrypted_client_hello.cc:1039) error:10000089:SSL routines:OPENSSL_internal:DECODE_ERROR
            at org.conscrypt.NativeCrypto.EVP_PKEY_cmp(Native Method)
            at org.conscrypt.NativeCryptoTest.lambda$EVP_PKEY_cmp_BothNullParameters$0(NativeCryptoTest.java:244)
            at org.junit.Assert.assertThrows(Assert.java:1001)
            ... 2 more

Could this just be failing locally for some reason? I suppose I can revert it and see what happens with the CI tests.

(UPDATE) Commented this part out for now and pushed it. If there's no CI failures I'll pull out all the extra stuff and we can wrap this up.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

+1, but more generally let's not change the non-ECH part of the code in this pull request. If you wanted to, please follow up with a new PR.

Could you please revert the changes here that are not related to ECH? Thanks

Unfortunately it looks like EVP_PKEY_cmp_BothNullParameters still fails (and test_get_RSA_public_params, which doesn't fail locally). I assume this isn't failing elsewhere, but I'm not sure how my changes could have caused this. Could the failure be caused by errors placed in the queue by a prior test? I notice that the failing test is right after one of the new ECH tests.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks. I think your hunch is correct. If you look at the error in the CI, the complete error is:

2025-09-23T20:09:49.2214351Z org.conscrypt.ConscryptOpenJdkSuite > org.conscrypt.NativeCryptoTest.EVP_PKEY_cmp_BothNullParameters FAILED
2025-09-23T20:09:49.2215827Z     java.lang.AssertionError: unexpected exception type thrown; expected:<java.lang.NullPointerException> but was:<java.lang.AssertionError>
2025-09-23T20:09:49.2229754Z         at org.junit.Assert.assertThrows(Assert.java:1020)
2025-09-23T20:09:49.2230440Z         at org.junit.Assert.assertThrows(Assert.java:981)
2025-09-23T20:09:49.2231640Z         at org.conscrypt.NativeCryptoTest.EVP_PKEY_cmp_BothNullParameters(NativeCryptoTest.java:248)
2025-09-23T20:09:49.2232278Z 
2025-09-23T20:09:49.2232411Z         Caused by:
2025-09-23T20:09:49.2233793Z         java.lang.AssertionError: Error queue should have been empty but was (/home/runner/work/_temp/boringssl/ssl/encrypted_client_hello.cc:1039) error:10000089:SSL routines:OPENSSL_internal:DECODE_ERROR
2025-09-23T20:09:49.2235956Z             at org.conscrypt.NativeCrypto.EVP_PKEY_cmp(Native Method)
2025-09-23T20:09:49.2236915Z             at org.conscrypt.NativeCryptoTest.lambda$EVP_PKEY_cmp_BothNullParameters$0(NativeCryptoTest.java:248)
2025-09-23T20:09:49.2237827Z             at org.junit.Assert.assertThrows(Assert.java:1001)
2025-09-23T20:09:49.2238310Z             ... 2 more

Note how the error queue is populated in encrypted_client_hello.cc:1039. Looking at NativeCrypto_SSL_CTX_ech_enable_server, I think you would need to clear the queue on line 1193, if SSL_ECH_KEYS_add failed.

return result;
if (result < 0) {
conscrypt::jniutil::throwInvalidKeyException(env, "Decoding error");
ERR_clear_error();
Copy link
Member

Choose a reason for hiding this comment

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

+1, but more generally let's not change the non-ECH part of the code in this pull request. If you wanted to, please follow up with a new PR.

Could you please revert the changes here that are not related to ECH? Thanks

@tweksteen
Copy link
Member

Thanks @mnbogner . See my comments above. Please revert the changes to the non-ECH code, we can follow up in a separate pull request.

I really would like to merge this PR now, please revert any changes that shouldn't be there anymore (ie LintError). Thanks!

@mnbogner
Copy link
Contributor Author

Thanks @mnbogner . See my comments above. Please revert the changes to the non-ECH code, we can follow up in a separate pull request.

I really would like to merge this PR now, please revert any changes that shouldn't be there anymore (ie LintError). Thanks!

Sorry for the delay, I'm finishing up things on another project. I should have time to finish this up in the next few days.

@tweksteen
Copy link
Member

No worries at all. Thanks for the update!

@davidben
Copy link
Contributor

This should be enough to let libs like OkHTTP fully implement ECH square/okhttp#6539

I haven't not reviewed the C++ code for correctness, but I don't see any actual Java APIs here. How is this sufficient for OkHTTP? Are you all proposing that OkHTTP will call into NativeCrypto directly?

@tweksteen
Copy link
Member

Thanks for the feedback @davidben. You are correct this PR by itself is not sufficient to make it useful for HTTP libraries. As you can see we went through a few iterations for this work, but the general outline is described in #1340 (comment)

I think that specific comment is a carry-on from the previous pull request. I should be able to remove that part from the commit message when squashing the PR. Thanks.

@davidben
Copy link
Contributor

Makes sense! Was missing context. :-)

@tweksteen
Copy link
Member

Thanks for the last set of changes @mnbogner ! CI is now passing fine. I'm going to go ahead and merge this PR now.

I would like to thank you (@mnbogner) and @eighthave for your perseverance to land these critical changes to support ECH in Conscrypt!🎉

@tweksteen tweksteen merged commit 354957b into google:master Sep 29, 2025
38 checks passed
@mnbogner
Copy link
Contributor Author

Thanks for the last set of changes @mnbogner ! CI is now passing fine. I'm going to go ahead and merge this PR now.

I would like to thank you (@mnbogner) and @eighthave for your perseverance to land these critical changes to support ECH in Conscrypt!🎉

Happy to hear it, but since this was on the weekend I was going to check the CI results later and then pull out the local changes I'd made to get this to build. Would you rather I remove those changes now or just wait until the next ECH PR? I don't think the changes should cause any issues, but they do look a bit sloppy.

@tweksteen
Copy link
Member

No worries at all. Yes, please feel free to send a quick follow up fix. Smaller PRs are always best. Thanks!

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.

4 participants