RFR: 8260274: Cipher.init(int, key) does not use highest priority provider for random bytes
SalusaSecondus
github.com+829871+salusasecondus at openjdk.java.net
Tue Mar 16 00:13:07 UTC 2021
On Mon, 15 Mar 2021 23:31:33 GMT, Valerie Peng <valeriep at openjdk.org> wrote:
>> I would definitely not call `new SecureRandom()` each time as that can have real performance problems (especially on systems which pull from `/dev/random` and may not have hardware entropy). The synchronization worries me too.
>>
>> Could `java.security.Security` contain a "provider version" `int` (volatile, atomic, or similar) which is incremented every time the provider list is changed? Then this class (clearly using some non-public API) would check that value and if it is the same, just return the cached instance. If it has changed since the last time, then it would update `instance` and its remembered value of the index. I think that we might be able to do it without taking any locks in this class because even if the providers change out from under us, we returned a reasonable value (subject to multi-threading) and will fix things up when we're called the next time.
>>
>> // using this and name collision to highlight what's a field and what's local
>> int providerListVersion = Security.nonPublicGetProviderListVersion();
>> if (this.providerListVersion != providerListVersion) {
>> this.providerListVersion = providerListVersion;
>> this.instance = new SecureRandom();
>> }
>> return this.instance;
>
> Or, I can also separate out the instance returned by getSecureRandom() so that code path should be unaffected, e.g.:
> // cached SecureRandom instance
> private static class CachedSecureRandomHolder {
> public static SecureRandom instance = new SecureRandom();
> private static Provider[] cachedConfig = Security.getProviders();
> private static SecureRandom def = instance;
>
> public static SecureRandom getDefault() {
> synchronized (CachedSecureRandomHolder.class) {
> Provider[] currConfig = Security.getProviders();
> if (!Arrays.equals(cachedConfig, currConfig)) {
> def = new SecureRandom();
> cachedConfig = currConfig;
> }
> }
> return def;
> }
> }
>
> /**
> * Get a SecureRandom instance. This method should be used by JDK
> * internal code in favor of calling "new SecureRandom()". That needs to
> * iterate through the provider table to find the default SecureRandom
> * implementation, which is fairly inefficient.
> */
> public static SecureRandom getSecureRandom() {
> return CachedSecureRandomHolder.instance;
> }
>
> /**
> * Get the default SecureRandom instance. This method is the
> * optimized version of "new SecureRandom()" which re-uses the default
> * SecureRandom impl if the provider table is the same.
> */
> public static SecureRandom getDefSecureRandom() {
> return CachedSecureRandomHolder.getDefault();
> }
Doesn't that still incur a JVM-wide lock whenever `CachedSecureRandomHolder.getDefSecureRandom()` is called? I recall a different JVM-wide lock in the `getInstance` path ([JDK-7107615](https://bugs.openjdk.java.net/browse/JDK-7107615)) creating significant performance issues.
-------------
PR: https://git.openjdk.java.net/jdk/pull/3018
More information about the security-dev
mailing list