On Mon, 15 Mar 2021 22:11:51 GMT, SalusaSecondus 
<github.com+829871+salusasecon...@openjdk.org> wrote:

>> Cipher and KeyPairGenerator could be used a lot in TLS context.  The 
>> synchronization may impact in some circumstances.  Supporting dynamic 
>> configuration could be error-prone.  For example, one thread to set the 
>> providers, and another thread get the providers by calling Cipher.init().  
>> Unless the set and get use the same lock, the return value of the get method 
>> is not predictable.  The lock here is on CachedSecureRandomHolder for 
>> providers configuration getting, and no lock (or different lock) for 
>> providers configuration setting.  So applications may be able to get the 
>> expected behaviors by introducing this synchronization.
>> 
>> As this behavior has been a while, I may not update the implementation.  
>> Instead, I would like to add an "implNote" tag to state that once the 
>> default provider is loaded, an implementation may be changed it any longer.  
>> Applications could use provider parameter if there is a preference.
>> 
>> If you want go ahead, maybe the performance impacted could be mitigated by 
>> locking less processes.  For example:
>> 
>> if (checkConfig) { 
>>     Provider[] currConfig = Security.getProviders();
>>     if (!Arrays.equals(cachedConfig, currConfig)) {
>>        synchronized (CachedSecureRandomHolder.class) {
>>            // double check the currConfig
>>            currConfig = Security.getProviders();
>>           if (!Arrays.equals(cachedConfig, currConfig)) {
>>               instance = ...
>>               cachedConfig = ...
>>            }
>>        }
>>     }
>> }
>> return instance;
>> 
>> However, I still not very sure of the performance of Security.getProviders() 
>> because it is synchronized as well.  Further, I may want to evaluate if the 
>> update of Security provider (for example Providers.setProviderList) could 
>> trigger an even to update the default instances.
>
> 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();
    }

-------------

PR: https://git.openjdk.java.net/jdk/pull/3018

Reply via email to