Skip to content

Conversation

@RSam25
Copy link
Contributor

@RSam25 RSam25 commented Jan 9, 2026

Synthetic Benchmarks:
image

Tests done:

  • Unit tests for CSTG. This class does not require unit tests because it is a KeyAgreement wrapper with a ThreadLocal cache. The main selection and fallback logic is environment dependent
  • Ran in validator (no mismatches)
  • Profiled using JFR. Findings show CPU use share down from 14.6% to 0.01%

private static final Logger LOGGER = LoggerFactory.getLogger(CryptoProviderService.class);

// ECDH provider selection: tries ACCP first, falls back to default (SunEC)
private static final String ECDH_PROVIDER_NAME = initEcdhProvider();
Copy link
Contributor

Choose a reason for hiding this comment

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

Just call initEcdhProvider in createKeyAgreement

Copy link
Contributor Author

@RSam25 RSam25 Jan 11, 2026

Choose a reason for hiding this comment

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

This is to ensure the provider is registered once globally. Calling initECDHProvider in createKeyAgreement would try to register it for every thread that tries to access it. While Security.addProvider is idempotent, I didn't want to repeat that operation unnecessarily, especially since if ACCP is not available for some reason, it would throw an exception (which is caught) that would unwind the stack and introduce some overhead.

Copy link
Contributor

Choose a reason for hiding this comment

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

Calling initECDHProvider once vs 10 times is a bit of a microoptimisation which sacrifices readability, for example, I had to read twice to understand why this was a static variable, if it was used anywhere else, etc

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed. However, I removed ThreadLocal caching due to the issue Andrei pointed out and now it is important we don't throw an exception every time if ACCP doesn't load for some reason.

@vishalegbert-ttd
Copy link
Contributor

I'm guessing we will use ACCP for other encryption functions in future MRs? or does this automatically optimise the other encryption functions as well?

@RSam25
Copy link
Contributor Author

RSam25 commented Jan 11, 2026

I'm guessing we will use ACCP for other encryption functions in future MRs? or does this automatically optimise the other encryption functions as well?

It explicitly only registers ACCP for ECDH. I ran a few benchmarks and found that ACCP (over gcompat which we have to use due to alpine) is slower than native SunJCE for AES. If we want to use ACCP for other crypto we should consider moving to a glibc based distribution where I have found ACCP AES is up to 3 times faster.

return KeyAgreement.getInstance("ECDH");
}

public static KeyAgreement getKeyAgreement() {
Copy link
Contributor

Choose a reason for hiding this comment

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

This has a subtle semantic change comparing to KeyAgreement.getInstance():

  • the original method returned a new object; if called multiple times - every call would result in a new object and returned results can be used independently of each other
  • in your case the caller would get the same object (assuming same thread) - if you try to use both in parallel (e.g. to compute with two different private keys) for whatever reason, it wouldn't work

In the current codebase it does not matter, but over time requirements might change this could bite. How much benefit do you get from re-using a threadlocal vs creating a new instance every time? How can the new semantics be made clearer to the caller?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That is true. I checked JFR profiling and getInstance (in this case) doesn't cost us much. I removed the ThreadLocal caching and made createKeyAgreement public, in case requirements change. Thanks for pointing this out. I did not think of this case.

Copy link
Contributor

Choose a reason for hiding this comment

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

I've approved the PR, but please check again how much CPU is spent initialising the key agreement object after this is deployed

@RSam25 RSam25 merged commit e80b0d4 into main Jan 12, 2026
7 of 9 checks passed
@RSam25 RSam25 deleted the srm-UID2-6479-change-ecdh-crypto-to-accp branch January 12, 2026 22:46
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