Prechádzať zdrojové kódy

pwmrandom refactoring

jrivard@gmail.com 6 rokov pred
rodič
commit
5b0cb1b77d

+ 1 - 2
server/src/main/java/password/pwm/PwmAboutProperty.java

@@ -29,7 +29,6 @@ import password.pwm.util.db.DatabaseService;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.logging.PwmLogger;
-import password.pwm.util.secure.PwmRandom;
 
 import java.lang.management.ManagementFactory;
 import java.nio.charset.Charset;
@@ -94,7 +93,7 @@ public enum PwmAboutProperty
     java_osName( "Java OS Name", pwmApplication -> System.getProperty( "os.name" ) ),
     java_osVersion( "Java OS Version", pwmApplication -> System.getProperty( "os.version" ) ),
     java_osArch( "Java OS Architecture", pwmApplication -> System.getProperty( "os.arch" ) ),
-    java_randomAlgorithm( null, pwmApplication -> PwmRandom.getInstance().getAlgorithm() ),
+    java_randomAlgorithm( null, pwmApplication -> pwmApplication.getSecureService().pwmRandom().getAlgorithm() ),
     java_defaultCharset( null, pwmApplication -> Charset.defaultCharset().name() ),
     java_appServerInfo( "Java AppServer Info", pwmApplication -> pwmApplication.getPwmEnvironment().getContextManager().getServerInfo() ),
 

+ 1 - 1
server/src/main/java/password/pwm/PwmApplication.java

@@ -671,7 +671,7 @@ public class PwmApplication
 
         if ( newInstanceID == null || newInstanceID.length() < 1 )
         {
-            newInstanceID = Long.toHexString( PwmRandom.getInstance().nextLong() ).toUpperCase();
+            newInstanceID = Long.toHexString( pwmApplication.getSecureService().pwmRandom().nextLong() ).toUpperCase();
             LOGGER.info( "generated new random instanceID " + newInstanceID );
 
             if ( localDB != null )

+ 3 - 3
server/src/main/java/password/pwm/bean/LocalSessionStateBean.java

@@ -23,8 +23,8 @@
 package password.pwm.bean;
 
 import lombok.Data;
+import password.pwm.PwmApplication;
 import password.pwm.ldap.UserInfoBean;
-import password.pwm.util.secure.PwmRandom;
 
 import java.io.Serializable;
 import java.time.Instant;
@@ -86,9 +86,9 @@ public class LocalSessionStateBean implements Serializable
         intruderAttempts = 0;
     }
 
-    public void regenerateSessionVerificationKey( )
+    public void regenerateSessionVerificationKey( final PwmApplication pwmApplication )
     {
-        sessionVerificationKey = PwmRandom.getInstance().alphaNumericString( sessionVerificationKeyLength ) + Long.toHexString( System.currentTimeMillis() );
+        sessionVerificationKey = pwmApplication.getSecureService().pwmRandom().alphaNumericString( sessionVerificationKeyLength ) + Long.toHexString( System.currentTimeMillis() );
     }
 }
 

+ 1 - 2
server/src/main/java/password/pwm/http/PwmRequest.java

@@ -48,7 +48,6 @@ import password.pwm.ldap.UserInfo;
 import password.pwm.util.Validator;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.logging.PwmLogger;
-import password.pwm.util.secure.PwmRandom;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.ws.server.RestResultBean;
 
@@ -481,7 +480,7 @@ public class PwmRequest extends PwmHttpRequestWrapper
         if ( getAttribute( PwmRequestAttribute.CspNonce ) == null )
         {
             final int nonceLength = Integer.parseInt( getConfig().readAppProperty( AppProperty.HTTP_HEADER_CSP_NONCE_BYTES ) );
-            final byte[] cspNonce = PwmRandom.getInstance().newBytes( nonceLength );
+            final byte[] cspNonce = pwmApplication.getSecureService().pwmRandom().newBytes( nonceLength );
             final String cspString = StringUtil.base64Encode( cspNonce );
             setAttribute( PwmRequestAttribute.CspNonce, cspString );
         }

+ 1 - 1
server/src/main/java/password/pwm/http/PwmSession.java

@@ -96,7 +96,7 @@ public class PwmSession implements Serializable
 
         final int sessionValidationKeyLength = Integer.parseInt( pwmApplication.getConfig().readAppProperty( AppProperty.HTTP_SESSION_VALIDATION_KEY_LENGTH ) );
         sessionStateBean = new LocalSessionStateBean( sessionValidationKeyLength );
-        sessionStateBean.regenerateSessionVerificationKey();
+        sessionStateBean.regenerateSessionVerificationKey( pwmApplication );
         this.sessionStateBean.setSessionID( null );
 
         final StatisticsManager statisticsManager = pwmApplication.getStatisticsManager();

+ 10 - 5
server/src/main/java/password/pwm/http/filter/RequestInitializationFilter.java

@@ -396,7 +396,11 @@ public class RequestInitializationFilter implements Filter
         }
     }
 
-    public static void addStaticResponseHeaders( final PwmApplication pwmApplication, final HttpServletResponse resp ) throws PwmUnrecoverableException
+    public static void addStaticResponseHeaders(
+            final PwmApplication pwmApplication,
+            final HttpServletResponse resp
+    )
+            throws PwmUnrecoverableException
     {
         final Configuration config = pwmApplication.getConfig();
 
@@ -409,7 +413,7 @@ public class RequestInitializationFilter implements Filter
         final boolean includeXAmb = Boolean.parseBoolean( config.readAppProperty( AppProperty.HTTP_HEADER_SEND_XAMB ) );
 
         {
-            final String noiseHeader = makeNoiseHeader( config );
+            final String noiseHeader = makeNoiseHeader( pwmApplication, config );
             if ( noiseHeader != null )
             {
                 resp.setHeader( HttpHeader.XNoise.getHttpName(), noiseHeader );
@@ -450,7 +454,7 @@ public class RequestInitializationFilter implements Filter
         if ( includeXAmb )
         {
             resp.setHeader( HttpHeader.XAmb.getHttpName(), PwmConstants.X_AMB_HEADER.get(
-                    PwmRandom.getInstance().nextInt( PwmConstants.X_AMB_HEADER.size() )
+                    pwmApplication.getSecureService().pwmRandom().nextInt( PwmConstants.X_AMB_HEADER.size() )
             ) );
         }
 
@@ -830,14 +834,15 @@ public class RequestInitializationFilter implements Filter
         return StringUtil.mapToString( values );
     }
 
-    private static String makeNoiseHeader( final Configuration configuration )
+    private static String makeNoiseHeader( final PwmApplication pwmApplication, final Configuration configuration )
     {
         final boolean sendNoise = Boolean.parseBoolean( configuration.readAppProperty( AppProperty.HTTP_HEADER_SEND_XNOISE ) );
 
         if ( sendNoise )
         {
             final int noiseLength = Integer.parseInt( configuration.readAppProperty( AppProperty.HTTP_HEADER_NOISE_LENGTH ) );
-            return PwmRandom.getInstance().alphaNumericString( PwmRandom.getInstance().nextInt( noiseLength ) + 11 );
+            final PwmRandom pwmRandom = pwmApplication.getSecureService().pwmRandom();
+            return pwmRandom.alphaNumericString( pwmRandom.nextInt( noiseLength ) + 11 );
         }
 
         return null;

+ 1 - 2
server/src/main/java/password/pwm/http/servlet/forgottenpw/ForgottenPasswordServlet.java

@@ -89,7 +89,6 @@ import password.pwm.util.operations.ActionExecutor;
 import password.pwm.util.operations.PasswordUtility;
 import password.pwm.util.operations.cr.NMASCrOperator;
 import password.pwm.util.operations.otp.OTPUserRecord;
-import password.pwm.util.secure.PwmRandom;
 import password.pwm.ws.server.RestResultBean;
 
 import javax.servlet.ServletException;
@@ -860,7 +859,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet
             final FormConfiguration formConfiguration = forgottenPasswordBean.getAttributeForm().iterator().next();
 
             // add a bit of jitter to pretend like we're checking a data source
-            JavaHelper.pause( 300 + PwmRandom.getInstance().nextInt( 700 ) );
+            JavaHelper.pause( 300 + pwmRequest.getPwmApplication().getSecureService().pwmRandom().nextInt( 700 ) );
 
             if ( forgottenPasswordBean.getUserSearchValues() != null )
             {

+ 2 - 2
server/src/main/java/password/pwm/http/servlet/forgottenpw/RemoteVerificationMethod.java

@@ -44,7 +44,6 @@ import password.pwm.ldap.UserInfo;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.macro.MacroMachine;
-import password.pwm.util.secure.PwmRandom;
 
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
@@ -58,7 +57,7 @@ public class RemoteVerificationMethod implements VerificationMethodSystem
     private static final PwmLogger LOGGER = PwmLogger.forClass( RemoteVerificationMethod.class );
 
 
-    private String remoteSessionID = PwmRandom.getInstance().randomUUID().toString();
+    private String remoteSessionID;
 
     private RemoteVerificationResponseBean lastResponse;
     private PwmHttpClient pwmHttpClient;
@@ -120,6 +119,7 @@ public class RemoteVerificationMethod implements VerificationMethodSystem
     public void init( final PwmApplication pwmApplication, final UserInfo userInfo, final SessionLabel sessionLabel, final Locale locale ) throws PwmUnrecoverableException
     {
         pwmHttpClient = new PwmHttpClient( pwmApplication, sessionLabel );
+        this.remoteSessionID = pwmApplication.getSecureService().pwmRandom().randomUUID().toString();
         this.userInfo = userInfo;
         this.sessionLabel = sessionLabel;
         this.locale = locale;

+ 105 - 100
server/src/main/java/password/pwm/util/RandomPasswordGenerator.java

@@ -52,7 +52,6 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -77,11 +76,6 @@ public class RandomPasswordGenerator
             "@", "&", "!", "?", "%", "$", "#", "^", ")", "(", "+", "-", "=", ".", ",", "/", "\\"
     ) ) );
 
-
-    private static final SeedMachine DEFAULT_SEED_MACHINE = new SeedMachine( DEFAULT_SEED_PHRASES );
-
-    private static final PwmRandom RANDOM = PwmRandom.getInstance();
-
     private static final PwmLogger LOGGER = PwmLogger.forClass( RandomPasswordGenerator.class );
 
     public static PasswordData createRandomPassword(
@@ -115,7 +109,7 @@ public class RandomPasswordGenerator
 
     /**
      * <p>Creates a new password that satisfies the password rules.  All rules are checked for.  If for some
-     * reason the RANDOM algorithm can not generate a valid password, null will be returned.</p>
+     * reason the pwmRandom algorithm can not generate a valid password, null will be returned.</p>
      *
      * <p>If there is an identifiable reason the password can not be created (such as mis-configured rules) then
      * an {@link com.novell.ldapchai.exception.ImpossiblePasswordPolicyException} will be thrown.</p>
@@ -136,6 +130,7 @@ public class RandomPasswordGenerator
             throws PwmUnrecoverableException
     {
         final Instant startTimeMS = Instant.now();
+        final PwmRandom pwmRandom = pwmApplication.getSecureService().pwmRandom();
 
         randomGeneratorConfig.validateSettings( pwmApplication );
 
@@ -170,7 +165,7 @@ public class RandomPasswordGenerator
             }
         }
 
-        final SeedMachine seedMachine = new SeedMachine( normalizeSeeds( effectiveConfig.getSeedlistPhrases() ) );
+        final SeedMachine seedMachine = new SeedMachine( pwmRandom, normalizeSeeds( effectiveConfig.getSeedlistPhrases() ) );
 
         int tryCount = 0;
         final StringBuilder password = new StringBuilder();
@@ -199,7 +194,7 @@ public class RandomPasswordGenerator
         }
 
         // initial creation
-        password.append( generateNewPassword( seedMachine, effectiveConfig.getMinimumLength() ) );
+        password.append( generateNewPassword( pwmRandom, seedMachine, effectiveConfig.getMinimumLength() ) );
 
         // read a rule validator
         final PwmPasswordRuleValidator pwmPasswordRuleValidator = new PwmPasswordRuleValidator( pwmApplication, randomGenPolicy );
@@ -216,7 +211,7 @@ public class RandomPasswordGenerator
             if ( tryCount % jitterCount == 0 )
             {
                 password.delete( 0, password.length() );
-                password.append( generateNewPassword( seedMachine, effectiveConfig.getMinimumLength() ) );
+                password.append( generateNewPassword( pwmRandom, seedMachine, effectiveConfig.getMinimumLength() ) );
             }
 
             final List<ErrorInformation> errors = pwmPasswordRuleValidator.internalPwmPolicyValidator(
@@ -224,13 +219,13 @@ public class RandomPasswordGenerator
             if ( errors != null && !errors.isEmpty() )
             {
                 validPassword = false;
-                modifyPasswordBasedOnErrors( password, errors, seedMachine );
+                modifyPasswordBasedOnErrors( pwmRandom, password, errors, seedMachine );
             }
             else if ( checkPasswordAgainstDisallowedHttpValues( pwmApplication.getConfig(), password.toString() ) )
             {
                 validPassword = false;
                 password.delete( 0, password.length() );
-                password.append( generateNewPassword( seedMachine, effectiveConfig.getMinimumLength() ) );
+                password.append( generateNewPassword( pwmRandom, seedMachine, effectiveConfig.getMinimumLength() ) );
             }
         }
 
@@ -262,6 +257,7 @@ public class RandomPasswordGenerator
     }
 
     private static void modifyPasswordBasedOnErrors(
+            final PwmRandom pwmRandom,
             final StringBuilder password,
             final List<ErrorInformation> errors,
             final SeedMachine seedMachine
@@ -282,13 +278,13 @@ public class RandomPasswordGenerator
 
         if ( errorMessages.contains( PwmError.PASSWORD_TOO_SHORT ) )
         {
-            addRandChar( password, seedMachine.getAllChars() );
+            addRandChar( pwmRandom, password, seedMachine.getAllChars() );
             touched = true;
         }
 
         if ( errorMessages.contains( PwmError.PASSWORD_TOO_LONG ) )
         {
-            password.deleteCharAt( RANDOM.nextInt( password.length() ) );
+            password.deleteCharAt( pwmRandom.nextInt( password.length() ) );
             touched = true;
         }
 
@@ -306,70 +302,74 @@ public class RandomPasswordGenerator
 
         if ( errorMessages.contains( PwmError.PASSWORD_NOT_ENOUGH_NUM ) )
         {
-            addRandChar( password, seedMachine.getNumChars() );
+            addRandChar( pwmRandom, password, seedMachine.getNumChars() );
             touched = true;
         }
 
         if ( errorMessages.contains( PwmError.PASSWORD_NOT_ENOUGH_SPECIAL ) )
         {
-            addRandChar( password, seedMachine.getSpecialChars() );
+            addRandChar( pwmRandom, password, seedMachine.getSpecialChars() );
             touched = true;
         }
 
         if ( errorMessages.contains( PwmError.PASSWORD_NOT_ENOUGH_UPPER ) )
         {
-            addRandChar( password, seedMachine.getUpperChars() );
+            addRandChar( pwmRandom, password, seedMachine.getUpperChars() );
             touched = true;
         }
 
         if ( errorMessages.contains( PwmError.PASSWORD_NOT_ENOUGH_LOWER ) )
         {
-            addRandChar( password, seedMachine.getLowerChars() );
+            addRandChar( pwmRandom, password, seedMachine.getLowerChars() );
             touched = true;
         }
 
         PasswordCharCounter passwordCharCounter = new PasswordCharCounter( password.toString() );
         if ( errorMessages.contains( PwmError.PASSWORD_TOO_MANY_NUMERIC ) && passwordCharCounter.getNumericCharCount() > 0 )
         {
-            deleteRandChar( password, passwordCharCounter.getNumericChars() );
+            deleteRandChar( pwmRandom, password, passwordCharCounter.getNumericChars() );
             touched = true;
             passwordCharCounter = new PasswordCharCounter( password.toString() );
         }
 
         if ( errorMessages.contains( PwmError.PASSWORD_TOO_MANY_SPECIAL ) && passwordCharCounter.getSpecialCharsCount() > 0 )
         {
-            deleteRandChar( password, passwordCharCounter.getSpecialChars() );
+            deleteRandChar( pwmRandom, password, passwordCharCounter.getSpecialChars() );
             touched = true;
             passwordCharCounter = new PasswordCharCounter( password.toString() );
         }
 
         if ( errorMessages.contains( PwmError.PASSWORD_TOO_MANY_UPPER ) && passwordCharCounter.getUpperCharCount() > 0 )
         {
-            deleteRandChar( password, passwordCharCounter.getUpperChars() );
+            deleteRandChar( pwmRandom, password, passwordCharCounter.getUpperChars() );
             touched = true;
             passwordCharCounter = new PasswordCharCounter( password.toString() );
         }
 
         if ( errorMessages.contains( PwmError.PASSWORD_TOO_MANY_LOWER ) && passwordCharCounter.getLowerCharCount() > 0 )
         {
-            deleteRandChar( password, passwordCharCounter.getLowerChars() );
+            deleteRandChar( pwmRandom, password, passwordCharCounter.getLowerChars() );
             touched = true;
         }
 
         if ( errorMessages.contains( PwmError.PASSWORD_TOO_WEAK ) )
         {
-            randomPasswordModifier( password, seedMachine );
+            randomPasswordModifier( pwmRandom, password, seedMachine );
             touched = true;
         }
 
         if ( !touched )
         {
-            // dunno whats wrong, try just deleting a RANDOM char, and hope a re-insert will add another.
-            randomPasswordModifier( password, seedMachine );
+            // dunno whats wrong, try just deleting a pwmRandom char, and hope a re-insert will add another.
+            randomPasswordModifier( pwmRandom, password, seedMachine );
         }
     }
 
-    private static void deleteRandChar( final StringBuilder password, final String charsToRemove )
+    private static void deleteRandChar(
+            final PwmRandom pwmRandom,
+            final StringBuilder password,
+            final String charsToRemove
+    )
             throws ImpossiblePasswordPolicyException
     {
         final List<Integer> removePossibilities = new ArrayList<>();
@@ -386,39 +386,46 @@ public class RandomPasswordGenerator
         {
             throw new ImpossiblePasswordPolicyException( ImpossiblePasswordPolicyException.ErrorEnum.UNEXPECTED_ERROR );
         }
-        final Integer charToDelete = removePossibilities.get( RANDOM.nextInt( removePossibilities.size() ) );
+        final Integer charToDelete = removePossibilities.get( pwmRandom.nextInt( removePossibilities.size() ) );
         password.deleteCharAt( charToDelete );
     }
 
-    private static void randomPasswordModifier( final StringBuilder password, final SeedMachine seedMachine )
+    private static void randomPasswordModifier(
+            final PwmRandom pwmRandom,
+            final StringBuilder password,
+            final SeedMachine seedMachine
+    )
     {
-        switch ( RANDOM.nextInt( 6 ) )
+        switch ( pwmRandom.nextInt( 6 ) )
         {
             case 0:
             case 1:
-                addRandChar( password, seedMachine.getSpecialChars() );
+                addRandChar( pwmRandom, password, seedMachine.getSpecialChars() );
                 break;
             case 2:
             case 3:
-                addRandChar( password, seedMachine.getNumChars() );
+                addRandChar( pwmRandom, password, seedMachine.getNumChars() );
                 break;
             case 4:
-                addRandChar( password, seedMachine.getUpperChars() );
+                addRandChar( pwmRandom, password, seedMachine.getUpperChars() );
                 break;
             case 5:
-                addRandChar( password, seedMachine.getLowerChars() );
+                addRandChar( pwmRandom, password, seedMachine.getLowerChars() );
                 break;
             default:
-                switchRandomCase( password );
+                switchRandomCase( pwmRandom, password );
                 break;
         }
     }
 
-    private static void switchRandomCase( final StringBuilder password )
+    private static void switchRandomCase(
+            final PwmRandom pwmRandom,
+            final StringBuilder password
+    )
     {
         for ( int i = 0; i < password.length(); i++ )
         {
-            final int randspot = RANDOM.nextInt( password.length() );
+            final int randspot = pwmRandom.nextInt( password.length() );
             final char oldChar = password.charAt( randspot );
             if ( Character.isLetter( oldChar ) )
             {
@@ -430,14 +437,14 @@ public class RandomPasswordGenerator
         }
     }
 
-    private static void addRandChar( final StringBuilder password, final String allowedChars )
+    private static void addRandChar( final PwmRandom pwmRandom, final StringBuilder password, final String allowedChars )
             throws ImpossiblePasswordPolicyException
     {
-        final int insertPosition = RANDOM.nextInt( password.length() );
-        addRandChar( password, allowedChars, insertPosition );
+        final int insertPosition = pwmRandom.nextInt( password.length() );
+        addRandChar( pwmRandom, password, allowedChars, insertPosition );
     }
 
-    private static void addRandChar( final StringBuilder password, final String allowedChars, final int insertPosition )
+    private static void addRandChar( final PwmRandom pwmRandom, final StringBuilder password, final String allowedChars, final int insertPosition )
             throws ImpossiblePasswordPolicyException
     {
         if ( allowedChars.length() < 1 )
@@ -446,7 +453,7 @@ public class RandomPasswordGenerator
         }
         else
         {
-            final int newCharPosition = RANDOM.nextInt( allowedChars.length() );
+            final int newCharPosition = pwmRandom.nextInt( allowedChars.length() );
             final char charToAdd = allowedChars.charAt( newCharPosition );
             password.insert( insertPosition, charToAdd );
         }
@@ -475,16 +482,28 @@ public class RandomPasswordGenerator
     protected static class SeedMachine
     {
         private final Collection<String> seeds;
-        private final String allChars;
-        private final String numChars;
-        private final String specialChars;
-        private final String upperChars;
-        private final String lowerChars;
+        private final PwmRandom pwmRandom;
+
+        private String allChars;
+        private String numChars;
+        private String specialChars;
+        private String upperChars;
+        private String lowerChars;
 
-        public SeedMachine( final Collection<String> seeds )
+        public SeedMachine( final PwmRandom pwmRandom, final Collection<String> seeds )
         {
+            this.pwmRandom = pwmRandom;
             this.seeds = seeds;
+        }
+
+        public String getRandomSeed( )
+        {
+            return new ArrayList<>( seeds ).get( pwmRandom.nextInt( seeds.size() ) );
+        }
 
+        public String getAllChars( )
+        {
+            if ( allChars == null )
             {
                 final StringBuilder sb = new StringBuilder();
                 for ( final String s : seeds )
@@ -497,9 +516,15 @@ public class RandomPasswordGenerator
                         }
                     }
                 }
-                allChars = sb.length() > 2 ? sb.toString() : ( new SeedMachine( DEFAULT_SEED_PHRASES ) ).getAllChars();
+                allChars = sb.length() > 2 ? sb.toString() : ( new SeedMachine( pwmRandom, DEFAULT_SEED_PHRASES ) ).getAllChars();
             }
 
+            return allChars;
+        }
+
+        public String getNumChars( )
+        {
+            if ( numChars == null )
             {
                 final StringBuilder sb = new StringBuilder();
                 for ( final Character c : allChars.toCharArray() )
@@ -509,9 +534,15 @@ public class RandomPasswordGenerator
                         sb.append( c );
                     }
                 }
-                numChars = sb.length() > 2 ? sb.toString() : ( new SeedMachine( DEFAULT_SEED_PHRASES ) ).getNumChars();
+                numChars = sb.length() > 2 ? sb.toString() : ( new SeedMachine( pwmRandom, DEFAULT_SEED_PHRASES ) ).getNumChars();
             }
 
+            return numChars;
+        }
+
+        public String getSpecialChars( )
+        {
+            if ( specialChars == null )
             {
                 final StringBuilder sb = new StringBuilder();
                 for ( final Character c : allChars.toCharArray() )
@@ -521,66 +552,49 @@ public class RandomPasswordGenerator
                         sb.append( c );
                     }
                 }
-                specialChars = sb.length() > 2 ? sb.toString() : ( new SeedMachine( DEFAULT_SEED_PHRASES ) ).getSpecialChars();
+                specialChars = sb.length() > 2 ? sb.toString() : ( new SeedMachine( pwmRandom, DEFAULT_SEED_PHRASES ) ).getSpecialChars();
             }
 
+            return specialChars;
+        }
+
+        public String getUpperChars( )
+        {
+            if ( upperChars == null )
             {
                 final StringBuilder sb = new StringBuilder();
                 for ( final Character c : allChars.toCharArray() )
                 {
-                    if ( Character.isLowerCase( c ) )
+                    if ( Character.isUpperCase( c ) )
                     {
                         sb.append( c );
                     }
                 }
-                lowerChars = sb.length() > 0 ? sb.toString() : ( new SeedMachine( DEFAULT_SEED_PHRASES ) ).getLowerChars();
+                upperChars = sb.length() > 0 ? sb.toString() : ( new SeedMachine( pwmRandom, DEFAULT_SEED_PHRASES ) ).getUpperChars();
             }
+            return upperChars;
+        }
 
+        public String getLowerChars( )
+        {
+            if ( lowerChars == null )
             {
                 final StringBuilder sb = new StringBuilder();
                 for ( final Character c : allChars.toCharArray() )
                 {
-                    if ( Character.isUpperCase( c ) )
+                    if ( Character.isLowerCase( c ) )
                     {
                         sb.append( c );
                     }
                 }
-                upperChars = sb.length() > 0 ? sb.toString() : ( new SeedMachine( DEFAULT_SEED_PHRASES ) ).getUpperChars();
+                lowerChars = sb.length() > 0 ? sb.toString() : ( new SeedMachine( pwmRandom, DEFAULT_SEED_PHRASES ) ).getLowerChars();
             }
-        }
-
-        public String getRandomSeed( )
-        {
-            return new ArrayList<>( seeds ).get( RANDOM.nextInt( seeds.size() ) );
-        }
-
-        public String getAllChars( )
-        {
-            return allChars;
-        }
-
-        public String getNumChars( )
-        {
-            return numChars;
-        }
 
-        public String getSpecialChars( )
-        {
-            return specialChars;
-        }
-
-        public String getUpperChars( )
-        {
-            return upperChars;
-        }
-
-        public String getLowerChars( )
-        {
             return lowerChars;
         }
     }
 
-    private static String generateNewPassword( final SeedMachine seedMachine, final int desiredLength )
+    private static String generateNewPassword( final PwmRandom pwmRandom, final SeedMachine seedMachine, final int desiredLength )
     {
         final StringBuilder password = new StringBuilder();
 
@@ -590,14 +604,15 @@ public class RandomPasswordGenerator
             password.append( seedMachine.getRandomSeed() );
         }
 
-        if ( RANDOM.nextInt( 3 ) == 0 )
+        if ( pwmRandom.nextInt( 3 ) == 0 )
         {
-            addRandChar( password, DEFAULT_SEED_MACHINE.getNumChars(), RANDOM.nextInt( password.length() ) );
+            final SeedMachine defaultSeedMachine = new SeedMachine( pwmRandom, DEFAULT_SEED_PHRASES );
+            addRandChar( pwmRandom, password, defaultSeedMachine.getNumChars(), pwmRandom.nextInt( password.length() ) );
         }
 
-        if ( RANDOM.nextBoolean() )
+        if ( pwmRandom.nextBoolean() )
         {
-            switchRandomCase( password );
+            switchRandomCase( pwmRandom, password );
         }
 
         return password.toString();
@@ -605,23 +620,13 @@ public class RandomPasswordGenerator
 
     private static Collection<String> normalizeSeeds( final Collection<String> inputSeeds )
     {
-
         if ( inputSeeds == null )
         {
             return DEFAULT_SEED_PHRASES;
         }
 
-        final Collection<String> newSeeds = new HashSet<>();
-        newSeeds.addAll( inputSeeds );
-
-        for ( final Iterator<String> iter = newSeeds.iterator(); iter.hasNext(); )
-        {
-            final String s = iter.next();
-            if ( s == null || s.length() < 1 )
-            {
-                iter.remove();
-            }
-        }
+        final Collection<String> newSeeds = new HashSet<>( inputSeeds );
+        newSeeds.removeIf( s -> s == null || s.length() < 1 );
 
         return newSeeds.isEmpty() ? DEFAULT_SEED_PHRASES : newSeeds;
     }
@@ -638,7 +643,7 @@ public class RandomPasswordGenerator
         private static final int MAXIMUM_STRENGTH = 100;
 
         /**
-         * A set of phrases (Strings) used to generate the RANDOM passwords.  There must be enough
+         * A set of phrases (Strings) used to generate the pwmRandom passwords.  There must be enough
          * values in the phrases to build a random password that meets rule requirements
          */
         @Builder.Default

+ 4 - 20
server/src/main/java/password/pwm/util/localdb/WorkQueueProcessor.java

@@ -30,12 +30,12 @@ import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.svc.stats.EventRateMeter;
+import password.pwm.util.java.AtomicLoopIntIncrementer;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
-import password.pwm.util.secure.PwmRandom;
 
 import java.io.Serializable;
 import java.math.BigDecimal;
@@ -71,7 +71,7 @@ public final class WorkQueueProcessor<W extends Serializable>
 
     private volatile WorkerThread workerThread;
 
-    private IDGenerator idGenerator = new IDGenerator();
+    private final AtomicLoopIntIncrementer idGenerator = new AtomicLoopIntIncrementer( 0 );
     private Instant eldestItem = null;
 
     private ThreadPoolExecutor executorService;
@@ -92,22 +92,6 @@ public final class WorkQueueProcessor<W extends Serializable>
         NOOP,
     }
 
-    private static class IDGenerator
-    {
-        private int currentID;
-
-        IDGenerator( )
-        {
-            currentID = PwmRandom.getInstance().nextInt();
-        }
-
-        synchronized String nextID( )
-        {
-            currentID += 1;
-            return Integer.toString( Math.abs( currentID ), 36 );
-        }
-    }
-
     public WorkQueueProcessor(
             final PwmApplication pwmApplication,
             final Deque<String> queue,
@@ -188,14 +172,14 @@ public final class WorkQueueProcessor<W extends Serializable>
 
     public void submitImmediate( final W workItem )
     {
-        final ItemWrapper<W> itemWrapper = new ItemWrapper<>( Instant.now(), workItem, idGenerator.nextID() );
+        final ItemWrapper<W> itemWrapper = new ItemWrapper<>( Instant.now(), workItem, String.valueOf( idGenerator.next() ) );
         sendAndQueueIfNecessary( itemWrapper );
     }
 
     public void submit( final W workItem )
             throws PwmOperationalException
     {
-        final ItemWrapper<W> itemWrapper = new ItemWrapper<>( Instant.now(), workItem, idGenerator.nextID() );
+        final ItemWrapper<W> itemWrapper = new ItemWrapper<>( Instant.now(), workItem, String.valueOf( idGenerator.next() ) );
 
         if ( settings.getPreThreads() < 0 )
         {

+ 7 - 4
server/src/main/java/password/pwm/util/operations/OtpService.java

@@ -185,6 +185,8 @@ public class OtpService implements PwmService
     )
             throws IOException, PwmUnrecoverableException
     {
+        final PwmRandom pwmRandom = pwmApplication.getSecureService().pwmRandom();
+
         otpUserRecord.setIdentifier( identifier );
 
         final byte[] rawSecret = generateSecret();
@@ -194,7 +196,7 @@ public class OtpService implements PwmService
         switch ( settings.getOtpType() )
         {
             case HOTP:
-                otpUserRecord.setAttemptCount( PwmRandom.getInstance().nextLong() );
+                otpUserRecord.setAttemptCount( pwmRandom.nextLong() );
                 otpUserRecord.setType( OTPUserRecord.Type.HOTP );
                 break;
 
@@ -216,7 +218,7 @@ public class OtpService implements PwmService
             {
                 LOGGER.trace( sessionLabel, "hashing the recovery codes" );
                 final int saltCharLength = Integer.parseInt( pwmApplication.getConfig().readAppProperty( AppProperty.OTP_SALT_CHARLENGTH ) );
-                recoveryInfo.setSalt( PwmRandom.getInstance().alphaNumericString( saltCharLength ) );
+                recoveryInfo.setSalt( pwmRandom.alphaNumericString( saltCharLength ) );
                 recoveryInfo.setHashCount( settings.getRecoveryHashIterations() );
                 recoveryInfo.setHashMethod( settings.getRecoveryHashMethod() );
             }
@@ -253,10 +255,11 @@ public class OtpService implements PwmService
         return rawRecoveryCodes;
     }
 
-    private static byte[] generateSecret( )
+    private byte[] generateSecret( )
     {
+        final PwmRandom pwmRandom = pwmApplication.getSecureService().pwmRandom();
         final byte[] secArray = new byte[ 10 ];
-        PwmRandom.getInstance().nextBytes( secArray );
+        pwmRandom.nextBytes( secArray );
         return secArray;
     }
 

+ 2 - 1
server/src/main/java/password/pwm/util/queue/SmsQueueManager.java

@@ -554,9 +554,10 @@ public class SmsQueueManager implements PwmService
 
             if ( requestData.contains( TOKEN_REQUESTID ) )
             {
+                final PwmRandom pwmRandom = pwmApplication.getSecureService().pwmRandom();
                 final String chars = config.readSettingAsString( PwmSetting.SMS_REQUESTID_CHARS );
                 final int idLength = Long.valueOf( config.readSettingAsLong( PwmSetting.SMS_REQUESTID_LENGTH ) ).intValue();
-                final String requestId = PwmRandom.getInstance().alphaNumericString( chars, idLength );
+                final String requestId = pwmRandom.alphaNumericString( chars, idLength );
                 requestData = requestData.replaceAll( TOKEN_REQUESTID, smsDataEncode( requestId, encoding ) );
             }
 

+ 94 - 1
server/src/main/java/password/pwm/util/secure/PwmRandom.java

@@ -24,8 +24,11 @@ package password.pwm.util.secure;
 
 import java.security.SecureRandom;
 import java.util.UUID;
+import java.util.stream.DoubleStream;
+import java.util.stream.IntStream;
+import java.util.stream.LongStream;
 
-public class PwmRandom
+public class PwmRandom extends SecureRandom
 {
 
     private final SecureRandom internalRand = new SecureRandom();
@@ -113,4 +116,94 @@ public class PwmRandom
         nextBytes( newBytes );
         return newBytes;
     }
+
+    @Override
+    public float nextFloat( )
+    {
+        return internalRand.nextFloat();
+    }
+
+    @Override
+    public double nextDouble( )
+    {
+        return internalRand.nextDouble();
+    }
+
+    @Override
+    public synchronized double nextGaussian( )
+    {
+        return internalRand.nextGaussian();
+    }
+
+    @Override
+    public IntStream ints( final long streamSize )
+    {
+        return internalRand.ints( streamSize );
+    }
+
+    @Override
+    public IntStream ints( )
+    {
+        return internalRand.ints();
+    }
+
+    @Override
+    public IntStream ints( final long streamSize, final int randomNumberOrigin, final int randomNumberBound )
+    {
+        return internalRand.ints( streamSize, randomNumberOrigin, randomNumberBound );
+    }
+
+    @Override
+    public IntStream ints( final int randomNumberOrigin, final int randomNumberBound )
+    {
+        return internalRand.ints( randomNumberOrigin, randomNumberBound );
+    }
+
+    @Override
+    public LongStream longs( final long streamSize )
+    {
+        return internalRand.longs( streamSize );
+    }
+
+    @Override
+    public LongStream longs( )
+    {
+        return internalRand.longs();
+    }
+
+    @Override
+    public LongStream longs( final long streamSize, final long randomNumberOrigin, final long randomNumberBound )
+    {
+        return internalRand.longs( streamSize, randomNumberOrigin, randomNumberBound );
+    }
+
+    @Override
+    public LongStream longs( final long randomNumberOrigin, final long randomNumberBound )
+    {
+        return internalRand.longs( randomNumberOrigin, randomNumberBound );
+    }
+
+    @Override
+    public DoubleStream doubles( final long streamSize )
+    {
+        return internalRand.doubles( streamSize );
+    }
+
+    @Override
+    public DoubleStream doubles( )
+    {
+        return internalRand.doubles();
+    }
+
+    @Override
+    public DoubleStream doubles( final long streamSize, final double randomNumberOrigin, final double randomNumberBound )
+    {
+        return internalRand.doubles( streamSize, randomNumberOrigin, randomNumberBound );
+    }
+
+    @Override
+    public DoubleStream doubles( final double randomNumberOrigin, final double randomNumberBound )
+    {
+        return internalRand.doubles( randomNumberOrigin, randomNumberBound );
+    }
 }

+ 2 - 2
webapp/src/main/webapp/WEB-INF/jsp/fragment/setupresponses-form.jsp

@@ -58,7 +58,7 @@
 <% } %>
 <% } %>
 <% } %>
-<%---------------------- display fields for RANDOM challenges using SIMPLE mode ----------------------------------------%>
+<%---------------------- display fields for pwmRandom challenges using SIMPLE mode ----------------------------------------%>
 <% if (setupData.isSimpleMode()) {  %>
 <% // need to output all the random options for the javascript functions.
     final List<String> questionTexts = new ArrayList<String>();
@@ -96,7 +96,7 @@
 </script>
 </pwm:script>
 <% } else { %>
-<%---------------------- display fields for RANDOM challenges using non-SIMPLE mode ----------------------------------------%>
+<%---------------------- display fields for pwmRandom challenges using non-SIMPLE mode ----------------------------------------%>
 <% if (!setupData.getChallengeSet().getRandomChallenges().isEmpty()) { %>
 <p><pwm:display key="Display_SetupRandomResponses" value1="<%= String.valueOf(setupData.getChallengeSet().getMinRandomRequired()) %>"/></p>
 <%