From e3deb8b9b6b1e1fbf53309380f5b30da848c507f Mon Sep 17 00:00:00 2001
From: Jean-Noel Rouvignac <jean-noel.rouvignac@forgerock.com>
Date: Fri, 12 Sep 2014 14:36:55 +0000
Subject: [PATCH] CR-4097 Big code cleanup of some storage schemes.

---
 opendj3-server-dev/src/server/org/opends/server/extensions/PBKDF2PasswordStorageScheme.java |  215 +++++++++++++++++++++++------------------------------
 1 files changed, 94 insertions(+), 121 deletions(-)

diff --git a/opendj3-server-dev/src/server/org/opends/server/extensions/PBKDF2PasswordStorageScheme.java b/opendj3-server-dev/src/server/org/opends/server/extensions/PBKDF2PasswordStorageScheme.java
index a842995..b3a1908 100644
--- a/opendj3-server-dev/src/server/org/opends/server/extensions/PBKDF2PasswordStorageScheme.java
+++ b/opendj3-server-dev/src/server/org/opends/server/extensions/PBKDF2PasswordStorageScheme.java
@@ -62,14 +62,14 @@
  * implementation uses a configurable number of iterations.
  */
 public class PBKDF2PasswordStorageScheme
-  extends PasswordStorageScheme<PBKDF2PasswordStorageSchemeCfg>
-  implements ConfigurationChangeListener<PBKDF2PasswordStorageSchemeCfg>
+    extends PasswordStorageScheme<PBKDF2PasswordStorageSchemeCfg>
+    implements ConfigurationChangeListener<PBKDF2PasswordStorageSchemeCfg>
 {
   private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
 
   /** The fully-qualified name of this class. */
   private static final String CLASS_NAME =
-       "org.opends.server.extensions.PBKDF2PasswordStorageScheme";
+      "org.opends.server.extensions.PBKDF2PasswordStorageScheme";
 
 
   /**
@@ -93,7 +93,6 @@
   /** The current configuration for this storage scheme. */
   private volatile PBKDF2PasswordStorageSchemeCfg config;
 
-
   /**
    * Creates a new instance of this password storage scheme.  Note that no
    * initialization should be performed here, as all initialization should be
@@ -107,8 +106,8 @@
   /** {@inheritDoc} */
   @Override
   public void initializePasswordStorageScheme(
-                   PBKDF2PasswordStorageSchemeCfg configuration)
-         throws ConfigException, InitializationException
+      PBKDF2PasswordStorageSchemeCfg configuration)
+      throws ConfigException, InitializationException
   {
     try
     {
@@ -152,18 +151,13 @@
   /** {@inheritDoc} */
   @Override
   public ByteString encodePassword(ByteSequence plaintext)
-         throws DirectoryException
+      throws DirectoryException
   {
     byte[] saltBytes      = new byte[NUM_SALT_BYTES];
     int    iterations     = config.getPBKDF2Iterations();
 
-    byte[] digestBytes = getDigestBytes(plaintext, saltBytes, iterations);
-    // Append the salt to the hashed value and base64-the whole thing.
-    byte[] hashPlusSalt = new byte[digestBytes.length + NUM_SALT_BYTES];
-
-    System.arraycopy(digestBytes, 0, hashPlusSalt, 0, digestBytes.length);
-    System.arraycopy(saltBytes, 0, hashPlusSalt, digestBytes.length,
-                     NUM_SALT_BYTES);
+    byte[] digestBytes = encodeWithRandomSalt(plaintext, saltBytes, iterations);
+    byte[] hashPlusSalt = concatenateHashPlusSalt(saltBytes, digestBytes);
 
     return ByteString.valueOf(iterations + ":" + Base64.encode(hashPlusSalt));
   }
@@ -171,7 +165,7 @@
   /** {@inheritDoc} */
   @Override
   public ByteString encodePasswordWithScheme(ByteSequence plaintext)
-         throws DirectoryException
+      throws DirectoryException
   {
     return ByteString.valueOf('{' + STORAGE_SCHEME_NAME_PBKDF2 + '}'
         + encodePassword(plaintext));
@@ -186,8 +180,8 @@
     // Base64-decode the remaining value and take the last 8 bytes as the salt.
     try
     {
-      String stored = storedPassword.toString();
-      int pos = stored.indexOf(':');
+      final String stored = storedPassword.toString();
+      final int pos = stored.indexOf(':');
       if (pos == -1)
       {
         throw new Exception();
@@ -217,43 +211,6 @@
     }
   }
 
-  private boolean encodeAndMatch(ByteSequence plaintextPassword,
-      final byte[] saltBytes, byte[] digestBytes, int iterations)
-  {
-    // Use the salt to generate a digest based on the provided plain-text value.
-    int plainBytesLength = plaintextPassword.length();
-    byte[] plainPlusSalt = new byte[plainBytesLength + saltBytes.length];
-    plaintextPassword.copyTo(plainPlusSalt);
-    System.arraycopy(saltBytes, 0, plainPlusSalt, plainBytesLength, saltBytes.length);
-
-
-    char[] plaintextChars = null;
-    synchronized (factoryLock)
-    {
-      try
-      {
-        plaintextChars = plaintextPassword.toString().toCharArray();
-        KeySpec spec = new PBEKeySpec(
-            plaintextChars, saltBytes,
-            iterations, SHA1_LENGTH * 8);
-        final byte[] userDigestBytes = factory.generateSecret(spec).getEncoded();
-        return Arrays.equals(digestBytes, userDigestBytes);
-      }
-      catch (Exception e)
-      {
-        logger.traceException(e);
-        return false;
-      }
-      finally
-      {
-        if (plaintextChars != null)
-        {
-          Arrays.fill(plaintextChars, '0');
-        }
-      }
-    }
-  }
-
   /** {@inheritDoc} */
   @Override
   public boolean supportsAuthPasswordSyntax()
@@ -271,11 +228,11 @@
   /** {@inheritDoc} */
   @Override
   public ByteString encodeAuthPassword(ByteSequence plaintext)
-         throws DirectoryException
+      throws DirectoryException
   {
     byte[] saltBytes      = new byte[NUM_SALT_BYTES];
     int    iterations     = config.getPBKDF2Iterations();
-    byte[] digestBytes = getDigestBytes(plaintext, saltBytes, iterations);
+    byte[] digestBytes = encodeWithRandomSalt(plaintext, saltBytes, iterations);
 
     // Encode and return the value.
     return ByteString.valueOf(AUTH_PASSWORD_SCHEME_NAME_PBKDF2 + '$'
@@ -283,41 +240,6 @@
         + Base64.encode(digestBytes));
   }
 
-  private byte[] getDigestBytes(ByteSequence plaintext, byte[] saltBytes,
-      int iterations) throws DirectoryException
-  {
-    char[] plaintextChars = null;
-    synchronized (factoryLock)
-    {
-      try
-      {
-        random.nextBytes(saltBytes);
-
-        plaintextChars = plaintext.toString().toCharArray();
-        KeySpec spec = new PBEKeySpec(
-            plaintextChars, saltBytes,
-            iterations, SHA1_LENGTH * 8);
-        return factory.generateSecret(spec).getEncoded();
-      }
-      catch (Exception e)
-      {
-        logger.traceException(e);
-
-        LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get(
-            CLASS_NAME, getExceptionMessage(e));
-        throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
-                                     message, e);
-      }
-      finally
-      {
-        if (plaintextChars != null)
-        {
-          Arrays.fill(plaintextChars, '0');
-        }
-      }
-    }
-  }
-
   /** {@inheritDoc} */
   @Override
   public boolean authPasswordMatches(ByteSequence plaintextPassword,
@@ -352,7 +274,7 @@
   /** {@inheritDoc} */
   @Override
   public ByteString getPlaintextValue(ByteSequence storedPassword)
-         throws DirectoryException
+      throws DirectoryException
   {
     LocalizableMessage message =
         ERR_PWSCHEME_NOT_REVERSIBLE.get(STORAGE_SCHEME_NAME_PBKDF2);
@@ -363,7 +285,7 @@
   @Override
   public ByteString getAuthPasswordPlaintextValue(String authInfo,
                                                   String authValue)
-         throws DirectoryException
+      throws DirectoryException
   {
     LocalizableMessage message =
         ERR_PWSCHEME_NOT_REVERSIBLE.get(AUTH_PASSWORD_SCHEME_NAME_PBKDF2);
@@ -386,54 +308,56 @@
    * user password).
    *
    * @param  passwordBytes  The bytes that make up the clear-text password.
-   *
    * @return  The encoded password string, including the scheme name in curly
    *          braces.
-   *
    * @throws  DirectoryException  If a problem occurs during processing.
    */
   public static String encodeOffline(byte[] passwordBytes)
-         throws DirectoryException
+      throws DirectoryException
   {
     byte[] saltBytes      = new byte[NUM_SALT_BYTES];
     int    iterations     = 10000;
 
-    byte[] digestBytes = getDigestBytes(passwordBytes, saltBytes, iterations);
-    // Append the salt to the hashed value and base64-the whole thing.
-    byte[] hashPlusSalt = new byte[digestBytes.length + NUM_SALT_BYTES];
+    final ByteString password = ByteString.wrap(passwordBytes);
+    byte[] digestBytes = encodeWithRandomSalt(password, saltBytes, iterations);
+    byte[] hashPlusSalt = concatenateHashPlusSalt(saltBytes, digestBytes);
 
-    System.arraycopy(digestBytes, 0, hashPlusSalt, 0, digestBytes.length);
-    System.arraycopy(saltBytes, 0, hashPlusSalt, digestBytes.length,
-                     NUM_SALT_BYTES);
-
-    return '{' + STORAGE_SCHEME_NAME_PBKDF2 + '}' + iterations + ':' +
-      Base64.encode(hashPlusSalt);
+    return '{' + STORAGE_SCHEME_NAME_PBKDF2 + '}' + iterations + ':'
+        + Base64.encode(hashPlusSalt);
   }
 
-  private static byte[] getDigestBytes(byte[] plaintext, byte[] saltBytes,
+  private static byte[] encodeWithRandomSalt(ByteString plaintext, byte[] saltBytes,
       int iterations) throws DirectoryException
   {
-    char[] plaintextChars = null;
     try
     {
-      SecureRandom.getInstance(SECURE_PRNG_SHA1).nextBytes(saltBytes);
-
-      plaintextChars = plaintext.toString().toCharArray();
-      KeySpec spec = new PBEKeySpec(
-          plaintextChars, saltBytes,
-          iterations, SHA1_LENGTH * 8);
-      return SecretKeyFactory
-          .getInstance(MESSAGE_DIGEST_ALGORITHM_PBKDF2)
-          .generateSecret(spec).getEncoded();
+      final SecureRandom random = SecureRandom.getInstance(SECURE_PRNG_SHA1);
+      final SecretKeyFactory factory = SecretKeyFactory.getInstance(MESSAGE_DIGEST_ALGORITHM_PBKDF2);
+      return encodeWithRandomSalt(plaintext, saltBytes, iterations, random, factory);
+    }
+    catch (DirectoryException e)
+    {
+      throw e;
     }
     catch (Exception e)
     {
-      logger.traceException(e);
+      throw cannotEncodePassword(e);
+    }
+  }
 
-      LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get(
-          CLASS_NAME, getExceptionMessage(e));
-      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
-          message, e);
+  private static byte[] encodeWithSalt(ByteSequence plaintext, byte[] saltBytes,
+      int iterations, final SecretKeyFactory factory) throws DirectoryException
+  {
+    final char[] plaintextChars = plaintext.toString().toCharArray();
+    try
+    {
+      KeySpec spec =
+          new PBEKeySpec(plaintextChars, saltBytes, iterations, SHA1_LENGTH * 8);
+      return factory.generateSecret(spec).getEncoded();
+    }
+    catch (Exception e)
+    {
+      throw cannotEncodePassword(e);
     }
     finally
     {
@@ -444,4 +368,53 @@
     }
   }
 
+  private boolean encodeAndMatch(ByteSequence plaintext, byte[] saltBytes,
+      byte[] digestBytes, int iterations)
+  {
+    synchronized (factoryLock)
+    {
+      try
+      {
+        final byte[] userDigestBytes =
+            encodeWithSalt(plaintext, saltBytes, iterations, factory);
+        return Arrays.equals(digestBytes, userDigestBytes);
+      }
+      catch (Exception e)
+      {
+        return false;
+      }
+    }
+  }
+
+  private byte[] encodeWithRandomSalt(ByteSequence plaintext, byte[] saltBytes,
+      int iterations) throws DirectoryException
+  {
+    synchronized (factoryLock)
+    {
+      return encodeWithRandomSalt(plaintext, saltBytes, iterations, random, factory);
+    }
+  }
+
+  private static byte[] encodeWithRandomSalt(ByteSequence plaintext, byte[] saltBytes,
+      int iterations, SecureRandom random, final SecretKeyFactory factory) throws DirectoryException
+  {
+    random.nextBytes(saltBytes);
+    return encodeWithSalt(plaintext, saltBytes, iterations, factory);
+  }
+
+  private static DirectoryException cannotEncodePassword(Exception e)
+  {
+    logger.traceException(e);
+
+    LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get(CLASS_NAME, getExceptionMessage(e));
+    return new DirectoryException(DirectoryServer.getServerErrorResultCode(), message, e);
+  }
+
+  private static byte[] concatenateHashPlusSalt(byte[] saltBytes, byte[] digestBytes) {
+    final byte[] hashPlusSalt = new byte[digestBytes.length + NUM_SALT_BYTES];
+    System.arraycopy(digestBytes, 0, hashPlusSalt, 0, digestBytes.length);
+    System.arraycopy(saltBytes, 0, hashPlusSalt, digestBytes.length, NUM_SALT_BYTES);
+    return hashPlusSalt;
+  }
+
 }

--
Gitblit v1.10.0