-
Notifications
You must be signed in to change notification settings - Fork 306
Basic TLS Encrypted ClientHello (ECH) support (native layer) #1374
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
Conversation
…SL. Unit tests also added.
There was a problem hiding this 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
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 for the feedback. I will be on vacation for 2 weeks but I will start making changes as soon as I return.
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. |
There was a problem hiding this 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"> |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
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. |
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. |
The CI is running with the property The diff from |
On clang-format, if you omit the |
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?
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? |
Thanks for taking a look at these. The 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 For instance,
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? |
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. |
I agree, |
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(); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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
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. |
No worries at all. Thanks for the update! |
…s to see if CI tests pass.
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? |
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. |
Makes sense! Was missing context. :-) |
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. |
No worries at all. Yes, please feel free to send a quick follow up fix. Smaller PRs are always best. Thanks! |
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