mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

davidely
03.13.2007 258951ecec5c5c6af08da341a60b124007270298
opendj-sdk/opends/resource/config/config.ldif
@@ -1085,6 +1085,13 @@
ds-cfg-password-storage-scheme-class: org.opends.server.extensions.SHA1PasswordStorageScheme
ds-cfg-password-storage-scheme-enabled: true
dn: cn=CRYPT,cn=Password Storage Schemes,cn=config
objectClass: top
objectClass: ds-cfg-password-storage-scheme
cn: CRYPT
ds-cfg-password-storage-scheme-class: org.opends.server.extensions.CryptPasswordStorageScheme
ds-cfg-password-storage-scheme-enabled: true
dn: cn=Password Validators,cn=config
objectClass: top
objectClass: ds-cfg-branch
opendj-sdk/opends/src/server/org/opends/server/extensions/CryptPasswordStorageScheme.java
New file
@@ -0,0 +1,314 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.extensions;
import static org.opends.server.extensions.ExtensionsConstants.*;
import static org.opends.server.messages.ExtensionsMessages.*;
import static org.opends.server.messages.MessageHandler.getMessage;
import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.locks.ReentrantLock;
import org.opends.server.api.PasswordStorageScheme;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.ByteString;
import org.opends.server.types.ByteStringFactory;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
import org.opends.server.util.Crypt;
import org.opends.server.admin.std.server.PasswordStorageSchemeCfg;
/**
 * This class defines a Directory Server password storage scheme based on the
 * UNIX Crypt algorithm.  This is a legacy one-way digest algorithm
 * intended only for situations where passwords have not yet been
 * updated to modern hashes such as SHA-1 and friends.  This
 * implementation does perform weak salting, which means that it is more
 * vulnerable to dictionary attacks than schemes with larger salts.
 */
public class CryptPasswordStorageScheme
       extends PasswordStorageScheme
{
  /**
   * The fully-qualified name of this class for debugging purposes.
   */
  private static final String CLASS_NAME =
       "org.opends.server.extensions.CryptPasswordStorageScheme";
  /**
   * An array of values that can be used to create salt characters
   * when encoding new crypt hashes.
   * */
  private static final byte[] SALT_CHARS =
    ("./0123456789abcdefghijklmnopqrstuvwxyz"
    +"ABCDEFGHIJKLMNOPQRSTUVWXYZ").getBytes();
  private final Random randomSaltIndex = new Random();
  private final ReentrantLock saltLock = new ReentrantLock();
  private final Crypt crypt = new Crypt();
  /**
   * Creates a new instance of this password storage scheme.  Note that no
   * initialization should be performed here, as all initialization should be
   * done in the <CODE>initializePasswordStorageScheme</CODE> method.
   */
  public CryptPasswordStorageScheme()
  {
    super();
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void initializePasswordStorageScheme(
          PasswordStorageSchemeCfg configuration)
          throws ConfigException, InitializationException {
    // Nothing to configure
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public String getStorageSchemeName()
  {
    return STORAGE_SCHEME_NAME_CRYPT;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public ByteString encodePassword(ByteString plaintext)
         throws DirectoryException
  {
    byte[] digestBytes;
    try
    {
      digestBytes = crypt.crypt(plaintext.value(), randomSalt());
    }
    catch (Exception e)
    {
      int    msgID   = MSGID_PWSCHEME_CANNOT_ENCODE_PASSWORD;
      String message = getMessage(msgID, CLASS_NAME,
                                  stackTraceToSingleLineString(e));
      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
                                   message, msgID, e);
    }
    return ByteStringFactory.create(digestBytes);
  }
  /**
   * Return a random 2-byte salt.
   *
   * @return a random 2-byte salt
   */
  private byte[] randomSalt() {
    saltLock.lock();
    try {
      byte[] salt = new byte[2];
      int sb1 = randomSaltIndex.nextInt(SALT_CHARS.length);
      int sb2 = randomSaltIndex.nextInt(SALT_CHARS.length);
      salt[0] = SALT_CHARS[sb1];
      salt[1] = SALT_CHARS[sb2];
      return salt;
    } finally {
      saltLock.unlock();
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public ByteString encodePasswordWithScheme(ByteString plaintext)
         throws DirectoryException
  {
    StringBuilder buffer =
      new StringBuilder(STORAGE_SCHEME_NAME_CRYPT.length()+12);
    buffer.append('{');
    buffer.append(STORAGE_SCHEME_NAME_CRYPT);
    buffer.append('}');
    buffer.append(encodePassword(plaintext));
    return ByteStringFactory.create(buffer.toString());
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean passwordMatches(ByteString plaintextPassword,
                                 ByteString storedPassword)
  {
    byte[] storedPWDigestBytes = storedPassword.value();
    byte[] userPWDigestBytes;
    try
    {
      // The salt is stored as the first two bytes of the storedPassword
      // value, and crypt.crypt() only looks at the first two bytes, so
      // we can pass it in directly.
      byte[] salt = storedPWDigestBytes;
      userPWDigestBytes = crypt.crypt(plaintextPassword.value(), salt);
    }
    catch (Exception e)
    {
      return false;
    }
    return Arrays.equals(userPWDigestBytes, storedPWDigestBytes);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean supportsAuthPasswordSyntax()
  {
    // This storage scheme does not support the authentication password syntax.
    return false;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public ByteString encodeAuthPassword(ByteString plaintext)
         throws DirectoryException
  {
    int    msgID   = MSGID_PWSCHEME_DOES_NOT_SUPPORT_AUTH_PASSWORD;
    String message = getMessage(msgID, getStorageSchemeName());
    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                 msgID);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean authPasswordMatches(ByteString plaintextPassword,
                                     String authInfo, String authValue)
  {
    // This storage scheme does not support the authentication password syntax.
    return false;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean isReversible()
  {
    return false;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public ByteString getPlaintextValue(ByteString storedPassword)
         throws DirectoryException
  {
    int msgID = MSGID_PWSCHEME_NOT_REVERSIBLE;
    String message = getMessage(msgID, STORAGE_SCHEME_NAME_CRYPT);
    throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message,
                                 msgID);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public ByteString getAuthPasswordPlaintextValue(String authInfo,
                                                  String authValue)
         throws DirectoryException
  {
    int    msgID   = MSGID_PWSCHEME_DOES_NOT_SUPPORT_AUTH_PASSWORD;
    String message = getMessage(msgID, getStorageSchemeName());
    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                 msgID);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean isStorageSchemeSecure()
  {
    // FIXME:
    // Technically, this isn't quite in keeping with the original spirit of
    // this method, since the point was to determine whether the scheme could
    // be trivially reversed.  I'm not sure I would put crypt into that
    // category, but it's certainly a lot more vulnerable to lookup tables
    // than most other algorithms.  I'd say we can keep it this way for now,
    // but it might be something to reconsider later.
    //
    // Currently, this method is unused.  However, the intended purpose is
    // eventually for use in issue #321, where we could do things like prevent
    // even authorized users from seeing the password value over an insecure
    // connection if it isn't considered secure.
    return false;
  }
}
opendj-sdk/opends/src/server/org/opends/server/extensions/ExtensionsConstants.java
@@ -191,6 +191,14 @@
  /**
   * The password storage scheme name that will be used for passwords stored in
   * a UNIX crypt representation.
   */
  public static final String STORAGE_SCHEME_NAME_CRYPT = "CRYPT";
  /**
   * The string that will appear before the name of the password storage scheme
   * in an encoded password.
   */
opendj-sdk/opends/src/server/org/opends/server/util/Crypt.java
New file
@@ -0,0 +1,490 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */
/*      Copyright (c) 1984,1988 AT&T */
/*        All Rights Reserved   */
package org.opends.server.util;
import java.util.concurrent.locks.ReentrantLock;
/**
 * UNIX Crypt cipher, ported from the Sun OpenSolaris project.
 * */
public class Crypt
{
  /* LINTLIBRARY */
  /*
   * This program implements the Proposed Federal Information Processing Data
   * Encryption Standard. See Federal Register, March 17, 1975 (40FR12134)
   */
  /*
   * Initial permutation,
   */
  private static final byte IP[]     =
                       { 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20,
      12, 4, 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, 57,
      49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37,
      29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7, };
  /*
   * Final permutation, FP = IP^(-1)
   */
  private static final byte FP[]     =
                       { 40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23,
      63, 31, 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, 36,
      4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10,
      50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25, };
  /*
   * Permuted-choice 1 from the key bits to yield C and D. Note that bits
   * 8,16... are left out: They are intended for a parity check.
   */
  private static final byte PC1_C[]  =
                       { 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18,
      10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, };
  private static final byte PC1_D[]  =
                       { 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22,
      14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4, };
  /*
   * Sequence of shifts used for the key schedule.
   */
  private static final byte shifts[] =
                       { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1, };
  /*
   * Permuted-choice 2, to pick out the bits from the CD array that generate the
   * key schedule.
   */
  private static final int  PC2_C[]  =
                       { 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19,
      12, 4, 26, 8, 16, 7, 27, 20, 13, 2, };
  private static final byte PC2_D[]  =
                       { 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, 44,
      49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32, };
  /**
   * Container for many variables altered throughout the encryption process.
   * */
  private static class SubCrypt
  {
    /*
     * The C and D arrays used to calculate the key schedule.
     */
    int _C[]      = new int[28];
    int _D[]      = new int[28];
    /*
     * The key schedule. Generated from the key.
     */
    int _KS[][]   = new int[16][48];
    /*
     * The E bit-selection table.
     */
    int _E[]      = new int[48];
    /*
     * The current block, divided into 2 halves.
     */
    int _L[]      = new int[32];
    int _R[]      = new int[32];
    int _tempL[]  = new int[32];
    int _f[]      = new int[32];
    /*
     * The combination of the key and the input, before selection.
     */
    int _preS[]   = new int[48];
    /*
     * Temps for crypt
     */
    int _ablock[] = new int[66];
    int _iobuf[]  = new int[16];
  }
  private final SubCrypt _crypt;
  /**
   * Constructor.
   */
  public Crypt() {
    _crypt = new SubCrypt();
    for (int i = 0; i < _crypt._E.length; i++)
      _crypt._E[i] = e[i];
  }
  /**
   * Sets up the key schedule from the key.
   */
  private void setkey(int[] key)
  {
    int i, j, k;
    int t;
    SubCrypt _c = _crypt;
    /*
     * if (_c == null) { _cryptinit(); _c = __crypt; }
     */
    /*
     * First, generate C and D by permuting the key. The low order bit of each
     * 8-bit char is not used, so C and D are only 28 bits apiece.
     */
    for (i = 0; i < 28; i++)
    {
      _c._C[i] = key[PC1_C[i] - 1];
      _c._D[i] = key[PC1_D[i] - 1];
    }
    /*
     * To generate Ki, rotate C and D according to schedule and pick up a
     * permutation using PC2.
     */
    for (i = 0; i < 16; i++)
    {
      /*
       * rotate.
       */
      for (k = 0; k < shifts[i]; k++)
      {
        t = _c._C[0];
        for (j = 0; j < 28 - 1; j++)
          _c._C[j] = _c._C[j + 1];
        _c._C[27] = t;
        t = _c._D[0];
        for (j = 0; j < 28 - 1; j++)
          _c._D[j] = _c._D[j + 1];
        _c._D[27] = t;
      }
      /*
       * get Ki. Note C and D are concatenated.
       */
      for (j = 0; j < 24; j++)
      {
        _c._KS[i][j] = _c._C[PC2_C[j] - 1];
        _c._KS[i][j + 24] = _c._D[PC2_D[j] - 28 - 1];
      }
    }
  }
  /*
   * The E bit-selection table.
   */
  private static final byte e[]   =
                    { 32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, 8, 9, 10, 11, 12,
      13, 12, 13, 14, 15, 16, 17, 16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24,
      25, 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1, };
  /*
   * The 8 selection functions. For some reason, they give a 0-origin index,
   * unlike everything else.
   */
  private static final int  S[][] =
    {
      { 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, 0, 15, 7, 4, 14,
      2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, 4, 1, 14, 8, 13, 6, 2, 11, 15, 12,
      9, 7, 3, 10, 5, 0, 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 },
      {
      15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, 3, 13, 4, 7, 15, 2,
      8, 14, 12, 0, 1, 10, 6, 9, 11, 5, 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12,
      6, 9, 3, 2, 15, 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 },
      {
      10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, 13, 7, 0, 9, 3, 4,
      6, 10, 2, 8, 5, 14, 12, 11, 15, 1, 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2,
      12, 5, 10, 14, 7, 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 },
      {
      7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, 13, 8, 11, 5, 6,
      15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, 10, 6, 9, 0, 12, 11, 7, 13, 15, 1,
      3, 14, 5, 2, 8, 4, 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 },
      {
      2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, 14, 11, 2, 12, 4,
      7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12,
      5, 6, 3, 0, 14, 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 },
      {
      12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, 10, 15, 4, 2, 7,
      12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4,
      10, 1, 13, 11, 6, 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 },
      {
      4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, 13, 0, 11, 7, 4, 9,
      1, 10, 14, 3, 5, 12, 2, 15, 8, 6, 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6,
      8, 0, 5, 9, 2, 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 },
      {
      13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, 1, 15, 13, 8, 10,
      3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10,
      13, 15, 3, 5, 8, 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 }
    };
  /*
   * P is a permutation on the selected combination of the current L and key.
   */
  private static final int  P[]   =
                    { 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31,
      10, 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25, };
  /**
   * Encrypts a block in place.
   */
  private final void encrypt(int block[], int edflag)
  {
    int i, ii;
    int t, j, k;
    SubCrypt _c = _crypt;
    int a = 0;
    /*
     * First, permute the bits in the input
     */
    for (j = 0; j < 64; j++)
    {
      a = IP[j] - 1;
      int b = block[a];
      if (j <= 31)
        _c._L[j] = b;
      else
        _c._R[j - 32] = b;
    }
    /*
     * Perform an encryption operation 16 times.
     */
    for (ii = 0; ii < 16; ii++)
    {
      /*
       * Set direction
       */
      if (edflag != 0)
      {
        i = 15 - ii;
      }
      else
      {
        i = ii;
      }
      /*
       * Save the R array, which will be the new L.
       */
      for (j = 0; j < 32; j++)
        _c._tempL[j] = _c._R[j];
      /*
       * Expand R to 48 bits using the E selector; exclusive-or with the current
       * key bits.
       */
      for (j = 0; j < 48; j++)
        _c._preS[j] = _c._R[_c._E[j] - 1] ^ _c._KS[i][j];
      /*
       * The pre-select bits are now considered in 8 groups of 6 bits each. The
       * 8 selection functions map these 6-bit quantities into 4-bit quantities
       * and the results permuted to make an f(R, K). The indexing into the
       * selection functions is peculiar; it could be simplified by rewriting
       * the tables.
       */
      for (j = 0; j < 8; j++)
      {
        t = 6 * j;
        k = S[j][(_c._preS[t + 0] << 5) + (_c._preS[t + 1] << 3)
            + (_c._preS[t + 2] << 2) + (_c._preS[t + 3] << 1)
            + (_c._preS[t + 4] << 0) + (_c._preS[t + 5] << 4)];
        t = 4 * j;
        _c._f[t + 0] = (k >> 3) & 01;
        _c._f[t + 1] = (k >> 2) & 01;
        _c._f[t + 2] = (k >> 1) & 01;
        _c._f[t + 3] = (k >> 0) & 01;
      }
      /*
       * The new R is L ^ f(R, K). The f here has to be permuted first, though.
       */
      for (j = 0; j < 32; j++)
        _c._R[j] = _c._L[j] ^ _c._f[P[j] - 1];
      /*
       * Finally, the new L (the original R) is copied back.
       */
      for (j = 0; j < 32; j++)
        _c._L[j] = _c._tempL[j];
    }
    /*
     * The output L and R are reversed.
     */
    for (j = 0; j < 32; j++)
    {
      t = _c._L[j];
      _c._L[j] = _c._R[j];
      _c._R[j] = t;
    }
    /*
     * The final output gets the inverse permutation of the very original.
     */
    for (j = 0; j < 64; j++)
    {
      int iv = FP[j] - 1;
      a = (iv <= 31) ? _c._L[iv] : _c._R[iv - 32];
      block[j] = a;
    }
  }
  private ReentrantLock digestLock = new ReentrantLock();
  /**
   * Encode the supplied password in unix crypt form with the provided
   * salt.
   *
   * @param pw A password to encode.
   * @param salt A salt array of any size, of which only the first
   * 2 bytes will be considered.
   * @return A trimmed array
   *
   * */
  public byte[] crypt(byte[] pw, byte[] salt)
  {
    digestLock.lock();
    int[] r;
    try
    {
      r = _crypt(pw, salt);
    }
    finally
    {
      digestLock.unlock();
    }
    //TODO: crypt always returns same size array?  So don't mess
    // around calculating the number of zeros at the end.
    // The _crypt algorithm pads the
    // result block with zeros; we need to
    // copy the array into a byte string,
    // but without these zeros.
    int zeroCount = 0;
    for (int i = r.length - 1; i >= 0; --i)
    {
      if (r[i] == 0)
      {
        ++zeroCount;
      }
      else
      {
        // Zeros can only occur at the end
        // of the block.
        break;
      }
    }
    byte[] b = new byte[r.length - zeroCount];
    // Convert to byte
    for (int i = 0; i < b.length; ++i)
    {
      b[i] = (byte) r[i];
    }
    return b;
  }
  private int[] _crypt(byte[] pw, byte[] salt)
  {
    int i, j, c, n;
    int temp;
    SubCrypt _c = _crypt;
    for (i = 0; i < 66; i++)
      _c._ablock[i] = 0;
    for (i = 0, n = 0; n < pw.length && i < 64; n++)
    {
      c = pw[n];
      for (j = 0; j < 7; j++, i++)
        _c._ablock[i] = (c >> (6 - j)) & 01;
      i++;
    }
    setkey(_c._ablock);
    for (i = 0; i < 66; i++)
      _c._ablock[i] = 0;
    for (i = 0; i < 48; i++)
      _c._E[i] = e[i];
    for (i = 0; i < 2; i++)
    {
      c = salt[i];
      _c._iobuf[i] = c;
      if (c > 'Z') c -= 6;
      if (c > '9') c -= 7;
      c -= '.';
      for (j = 0; j < 6; j++)
      {
        if (((c >> j) & 01) != 0)
        {
          temp = _c._E[6 * i + j];
          _c._E[6 * i + j] = _c._E[6 * i + j + 24];
          _c._E[6 * i + j + 24] = temp;
        }
      }
    }
    for (i = 0; i < 25; i++)
      encrypt(_c._ablock, 0);
    for (i = 0; i < 11; i++)
    {
      c = 0;
      for (j = 0; j < 6; j++)
      {
        c <<= 1;
        c |= _c._ablock[6 * i + j];
      }
      c += '.';
      if (c > '9') c += 7;
      if (c > 'Z') c += 6;
      _c._iobuf[i + 2] = c;
    }
    _c._iobuf[i + 2] = 0;
    if (_c._iobuf[1] == 0) _c._iobuf[1] = _c._iobuf[0];
    return (_c._iobuf);
  }
}
opendj-sdk/opends/tests/unit-tests-testng/resource/password-with-all-crypt-salts.txt
New file
Diff too large
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java
@@ -45,6 +45,7 @@
import java.net.ServerSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.Socket;
import org.opends.server.backends.MemoryBackend;
import org.opends.server.backends.jeb.BackendImpl;
@@ -61,6 +62,12 @@
import org.opends.server.loggers.debug.DebugLogger;
import org.opends.server.plugins.InvocationCounterPlugin;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.asn1.ASN1Reader;
import org.opends.server.protocols.asn1.ASN1Writer;
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.protocols.ldap.BindRequestProtocolOp;
import org.opends.server.protocols.ldap.LDAPMessage;
import org.opends.server.protocols.ldap.BindResponseProtocolOp;
import org.opends.server.tools.LDAPModify;
import org.opends.server.types.DN;
import org.opends.server.types.FilePermission;
@@ -75,6 +82,7 @@
import org.opends.server.util.ModifyDNChangeRecordEntry;
import static org.testng.Assert.*;
import static org.testng.Assert.assertEquals;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
@@ -787,6 +795,40 @@
  public static boolean canBind(String dn, String pw) throws Exception
  {
    // Check that the user can bind.
    Socket s = null;
    try {
      s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
      ASN1Reader r = new ASN1Reader(s);
      ASN1Writer w = new ASN1Writer(s);
      r.setIOTimeout(3000);
      BindRequestProtocolOp bindRequest =
        new BindRequestProtocolOp(
                 new ASN1OctetString(dn),
                 3,
                new ASN1OctetString(pw));
      LDAPMessage message = new LDAPMessage(1, bindRequest);
      w.writeElement(message.encode());
      message = LDAPMessage.decode(r.readElement().decodeAsSequence());
      BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
      if (bindResponse.getResultCode() == 0) {
        return true;
      }
    } catch (Exception t) {
      t.printStackTrace();
    } finally {
      if (s != null) {
        s.close();
      }
    }
    return false;
  }
  /**
   * Adds the provided entry to the Directory Server using an internal
   * operation.
@@ -889,6 +931,8 @@
  /**
   * Creates a temporary text file with the specified contents.  It will be
   * marked for automatic deletion when the JVM exits.
@@ -1044,6 +1088,29 @@
    return new String(bytes);
  }
  /**
   * Returns the contents of file as a List of the lines as defined by
   * java.io.BufferedReader#readLine() (i.e. the line terminator is not
   * included).  An ArrayList is explicitly returned, so that callers know that
   * random access is not expensive.
   */
  public static ArrayList<String> readFileToLines(File file)
          throws IOException {
    BufferedReader reader =
      new BufferedReader(
        new InputStreamReader(
          new DataInputStream(
                  new FileInputStream(file))));
    ArrayList<String> lines = new ArrayList<String>();
    String line;
    while ((line = reader.readLine()) != null)   {
      lines.add(line);
    }
    return lines;
  }
  /**
   * Read the contents of a file and return it as a String.
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/CryptPasswordStorageSchemeTestCase.java
New file
@@ -0,0 +1,67 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.extensions;
import org.opends.server.api.PasswordStorageScheme;
/**
 * A set of test cases for the salted MD5 password storage scheme.
 */
public class CryptPasswordStorageSchemeTestCase
       extends PasswordStorageSchemeTestCase
{
  /**
   * Creates a new instance of this storage scheme test case.
   */
  public CryptPasswordStorageSchemeTestCase()
  {
    super("cn=Crypt,cn=Password Storage Schemes,cn=config");
  }
  /**
   * Retrieves an initialized instance of this password storage scheme.
   *
   * @return  An initialized instance of this password storage scheme.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  public PasswordStorageScheme getScheme()
         throws Exception
  {
    CryptPasswordStorageScheme scheme =
         new CryptPasswordStorageScheme();
    scheme.initializePasswordStorageScheme(null);
    return scheme;
  }
}
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/PasswordStorageSchemeTestCase.java
@@ -36,15 +36,25 @@
import org.opends.server.api.PasswordStorageScheme;
import org.opends.server.config.ConfigEntry;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.PasswordPolicy;
import org.opends.server.core.ModifyOperation;
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.schema.AuthPasswordSyntax;
import org.opends.server.schema.UserPasswordSyntax;
import org.opends.server.types.ByteString;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.ResultCode;
import org.opends.server.types.Modification;
import org.opends.server.types.ModificationType;
import org.opends.server.types.Attribute;
import static org.testng.Assert.*;
import static org.testng.Assert.assertEquals;
import java.util.ArrayList;
/**
@@ -220,13 +230,117 @@
  }
  @DataProvider
  public Object[][] passwordsForBinding()
  {
    return new Object[][]
    {
      // In the case of a clear-text password, these values will be shoved
      // un-excaped into an LDIF file, so make sure they don't include \n
      // or other characters that will cause LDIF parsing errors.
      // We really don't need many test cases here, since that functionality
      // is tested above.
      new Object[] { new ASN1OctetString("a") },
      new Object[] { new ASN1OctetString("abcdefgh") },
      new Object[] { new ASN1OctetString("abcdefghi") },
    };
  }
  /**
   * An end-to-end test that verifies that we can set a pre-encoded password
   * in a user entry, and then bind as that user using the cleartext password.
   */
  @Test(dataProvider = "passwordsForBinding")
  public void testSettingEncodedPassword(ASN1OctetString plainPassword) throws Exception
  {
    // Start/clear-out the memory backend
    TestCaseUtils.initializeTestBackend(true);
    boolean allowPreencodedDefault = setAllowPreencodedPasswords(true);
    try {
      PasswordStorageScheme scheme = getScheme();
      ByteString schemeEncodedPassword =
           scheme.encodePasswordWithScheme(plainPassword);
      //
      // This code creates a user with the encoded password,
      // and then verifies that they can bind with the raw password.
      //
      Entry userEntry = TestCaseUtils.makeEntry(
           "dn: uid=test.user,o=test",
           "objectClass: top",
           "objectClass: person",
           "objectClass: organizationalPerson",
           "objectClass: inetOrgPerson",
           "uid: test.user",
           "givenName: Test",
           "sn: User",
           "cn: Test User",
           "ds-privilege-name: bypass-acl",
           "userPassword: " + schemeEncodedPassword.stringValue());
      // Add the entry
      TestCaseUtils.addEntry(userEntry);
      assertTrue(TestCaseUtils.canBind("uid=test.user,o=test",
                 plainPassword.stringValue()),
                 "Failed to bind when pre-encoded password = \"" +
                         schemeEncodedPassword.stringValue() + "\" and " +
                         "plaintext password = \"" +
                         plainPassword.stringValue() + "\"");
    } finally {
      setAllowPreencodedPasswords(allowPreencodedDefault);
    }
  }
  /**
   * Sets whether or not to allow pre-encoded password values for the
   * current password storage scheme and returns the previous value so that
   * it can be restored.
   *
   * @param allowPreencoded whether or not to allow pre-encoded passwords
   * @return the previous value for the allow preencoded passwords
   */
  private boolean setAllowPreencodedPasswords(boolean allowPreencoded)
          throws Exception
  {
    // This code was borrowed from
    // PasswordPolicyTestCase.testAllowPreEncodedPasswordsAuth
    boolean previousValue = false;
    try {
      DN dn = DN.decode("cn=Default Password Policy,cn=Password Policies,cn=config");
      PasswordPolicy p = DirectoryServer.getPasswordPolicy(dn);
      previousValue = p.allowPreEncodedPasswords();
      String attr  = "ds-cfg-allow-pre-encoded-passwords";
      ArrayList<Modification> mods = new ArrayList<Modification>();
      mods.add(new Modification(ModificationType.REPLACE,
                                new Attribute(attr, ""+allowPreencoded)));
      InternalClientConnection conn =
           InternalClientConnection.getRootConnection();
      ModifyOperation modifyOperation = conn.processModify(dn, mods);
      assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
      p = DirectoryServer.getPasswordPolicy(dn);
      assertEquals(p.allowPreEncodedPasswords(), allowPreencoded);
    } catch (Exception e) {
      System.err.println("Failed to set ds-cfg-allow-pre-encoded-password " +
                         " to " + allowPreencoded);
      e.printStackTrace();
      throw e;
    }
    return previousValue;
  }
  /**
   * Retrieves an initialized instance of this password storage scheme.
   *
   * @param  configEntry  The configuration entry for the password storage
   *                      scheme, or <CODE>null</CODE> if none is available.
   *
   * @return  An initialized instance of this password storage scheme.
   *
   * @throws  Exception  If an unexpected problem occurs.
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/util/TestCrypt.java
New file
@@ -0,0 +1,281 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 *      Portions Copyright 2007 Brighton Consulting, Inc.
 */
package org.opends.server.util;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.opends.server.TestCaseUtils;
import java.io.File;
import java.util.List;
/**
 * This class defines a set of tests for the
 * {@link org.opends.server.util.Crypt} class.
 */
@Test(groups = { "slow" })  // Make them slow, since they are unlikely to break and since there are 4K+ they can take a while
public final class TestCrypt extends UtilTestCase {
  private Crypt crypt = new Crypt();
  /**
   * Crypt valid test data provider.  All crypts are variants of the password
   * "password".
   *
   * @return Returns an array of all salted version of "password".
   */
  @DataProvider
  public Object[][] passwordWithAllSalts() throws Exception {
    File cryptFile = TestCaseUtils.getTestResource("password-with-all-crypt-salts.txt");
    List <String> cryptedPasswords = TestCaseUtils.readFileToLines(cryptFile);
    // Convert the passwords list into the 2D array needed expected by
    // DataProviders
    String[][] passwordArgs = new String[cryptedPasswords.size()][];
    for (int i = 0; i < cryptedPasswords.size(); i++) {
      passwordArgs[i] = new String[]{cryptedPasswords.get(i)};
    }
    return passwordArgs;
  }
  /**
   * Tests the encryption of "password" against all possible salts.
   * Because the salt is part of the crypt'ed password parameter, the
   * single parameter serves as an input parameter (the salt at the
   * start of the parameter), as well as to validate that the parameter
   * was properly encrypted.
   *
   * @param saltedPassword
   *          A hashed value of "password".
   */
  @Test(dataProvider = "passwordWithAllSalts")
  public void testAllSalts(String saltedPassword) throws Exception
  {
    validateCryptedPassword("password", saltedPassword);
  }
  // Passowrds and their corresponding crypt values for various salts.
  // These were generated using the crypt() implementation in Perl.
  private static final Object[][] PASSWORDS_AND_CRYPTS = {
       {"", "AEypjPqkpWrm."},
       {"", "DEkEMk50GlpvQ"},
       {"", "SeF2s6N.rrxzE"},
       {"", "seGYEzzfNS6p6"},
       {"", "76gkheUg6SU6g"},
       {"", "02oawyZdjhhpg"},
       {"", "05/lfLEyErrp2"},
       {"1", "AEycSgLoZE2NU"},
       {"1", "DEcGEo4stCwKU"},
       {"1", "Selkb/5Kk.a5U"},
       {"1", "sexFKbViIp1BM"},
       {"1", "76PZeYZkUIiJY"},
       {"1", "02g/Is9QzZ59."},
       {"1", "05Nc5lRLZkOL2"},
       {"12", "AEo9g/hrWcroY"},
       {"12", "DEhWm60GBm51E"},
       {"12", "SeC1ub4/n1onQ"},
       {"12", "seLOcGzRFXQ8A"},
       {"12", "76RWqjxIlxH.s"},
       {"12", "02Yg9V0ugcDK2"},
       {"12", "05YvAHbiMsS/s"},
       {"123", "AEeiDhPbE8FZA"},
       {"123", "DEDUHYzEvFtCg"},
       {"123", "SeKFH3Nooq92c"},
       {"123", "seu.8HpuOxyTU"},
       {"123", "76t8XtqAiQew2"},
       {"123", "025QwM9.cl3TU"},
       {"123", "05pundxrmpCa."},
       {"1234", "AENOqZDrZtpEA"},
       {"1234", "DE.28cJyrukGE"},
       {"1234", "SedVHY1MtpuW."},
       {"1234", "seUc7O/85EPvQ"},
       {"1234", "76bHlu4JyJ4UU"},
       {"1234", "02YlX9ogYhIOw"},
       {"1234", "05pGklGDVsooY"},
       {"12345", "AEsOUkNAVAV/k"},
       {"12345", "DEa555/OS0gCw"},
       {"12345", "Sevs2NnMDrR52"},
       {"12345", "seWfr4VdeLxO."},
       {"12345", "76fEe.ryflQn."},
       {"12345", "02oa0q9I0b8YI"},
       {"12345", "05pIZ2uxl1Oxc"},
       {"123456", "AElA7SvR5BWYs"},
       {"123456", "DERqDk0hJcemI"},
       {"123456", "SeEsHpRQ3Ws4Y"},
       {"123456", "seldpJxxtvI7E"},
       {"123456", "76odHJ.uJuNlM"},
       {"123456", "0274Bl5Z.iP8A"},
       {"123456", "050o6La.i0hjQ"},
       {"1234567", "AEvQ6nKFdz1/Q"},
       {"1234567", "DEqKJFIOJAeP2"},
       {"1234567", "SeevFxkmw4OPM"},
       {"1234567", "seKgNYFPEl0YU"},
       {"1234567", "76Q7Ud2dSYUyY"},
       {"1234567", "02FYf5CcW.z9Y"},
       {"1234567", "053NyeyMsGY3M"},
       {"12345678", "AESEqSznA5e5o"},
       {"12345678", "DE0gVuzpcrIb2"},
       {"12345678", "Sev2LR/CJDCXo"},
       {"12345678", "seXUEz1n7cRRY"},
       {"12345678", "76fpN4iZgIRE2"},
       {"12345678", "0291YyAhKiURA"},
       {"12345678", "05tb4hpIF2mIE"},
       {"123456789", "AESEqSznA5e5o"},
       {"123456789", "DE0gVuzpcrIb2"},
       {"123456789", "Sev2LR/CJDCXo"},
       {"123456789", "seXUEz1n7cRRY"},
       {"123456789", "76fpN4iZgIRE2"},
       {"123456789", "0291YyAhKiURA"},
       {"123456789", "05tb4hpIF2mIE"},
       {"1234567890", "AESEqSznA5e5o"},
       {"1234567890", "DE0gVuzpcrIb2"},
       {"1234567890", "Sev2LR/CJDCXo"},
       {"1234567890", "seXUEz1n7cRRY"},
       {"1234567890", "76fpN4iZgIRE2"},
       {"1234567890", "0291YyAhKiURA"},
       {"1234567890", "05tb4hpIF2mIE"},
       {"!@#0^&*", "AEs3QeaYDQTJE"},
       {"!@#0^&*", "DE9RJdfpk3gEM"},
       {"!@#0^&*", "SeF/SBOVIQgbM"},
       {"!@#0^&*", "seyp8Lbs6DyRc"},
       {"!@#0^&*", "76vSuJ/ho1RxE"},
       {"!@#0^&*", "02i4O5873w8G6"},
       {"!@#0^&*", "05.ep5H/6Gb/Q"},
       {"()_+-=[]", "AE2JFPze7oG2s"},
       {"()_+-=[]", "DE77ctMC0YTs6"},
       {"()_+-=[]", "Se5tUe6lyM2nE"},
       {"()_+-=[]", "sehWSNixuAffY"},
       {"()_+-=[]", "76v/azh6t29QA"},
       {"()_+-=[]", "02.j5xgLpYyto"},
       {"()_+-=[]", "053cNRDrF.GOs"},
       {"{}\\:;\"'<", "AEnWr4wYnt/Sg"},
       {"{}\\:;\"'<", "DEveQ9wxQrdfQ"},
       {"{}\\:;\"'<", "Se6LMgmv4jYSw"},
       {"{}\\:;\"'<", "se4Lmhm8eCYvk"},
       {"{}\\:;\"'<", "76vVkpMqk8MPc"},
       {"{}\\:;\"'<", "024d3tyjpySTc"},
       {"{}\\:;\"'<", "057OTKpDRH1yk"},
       {",>.?/`~", "AEu8PanMV4Yos"},
       {",>.?/`~", "DEfL4O2tY1pbQ"},
       {",>.?/`~", "Se1o/Ln0pz53s"},
       {",>.?/`~", "sevdxlNkzBC9s"},
       {",>.?/`~", "76Oe5rgApyjqQ"},
       {",>.?/`~", "02gwWt.cN0ZW2"},
       {",>.?/`~", "05TkTNlUHKj0o"},
       {"abcdefgh", "AE5sk6ZnF2ne6"},
       {"abcdefgh", "DEgNPea/fV7o6"},
       {"abcdefgh", "SeveLGJNvFGqo"},
       {"abcdefgh", "seoDbs8R1TfGs"},
       {"abcdefgh", "76OgHaEBgUoY6"},
       {"abcdefgh", "02YewT6mKnbtc"},
       {"abcdefgh", "05SENcp2oS3N2"},
       {"ijklmnop", "AElKD7vOnptw2"},
       {"ijklmnop", "DEpcYg28J1nGY"},
       {"ijklmnop", "SemEov1ZT2QeA"},
       {"ijklmnop", "sed20nQs9.5QU"},
       {"ijklmnop", "76fgwIPFi/Yxw"},
       {"ijklmnop", "02xnTfvOZm1Fw"},
       {"ijklmnop", "053/TROBqFM5A"},
       {"qrstuvwx", "AEcIntiaWvH4U"},
       {"qrstuvwx", "DEjMRKWIS8RcE"},
       {"qrstuvwx", "Sedkn.517oB1Q"},
       {"qrstuvwx", "se7WusSyMU74A"},
       {"qrstuvwx", "76KwGhfk1D9.o"},
       {"qrstuvwx", "02NBEYIkvCEDw"},
       {"qrstuvwx", "05WDiAK7932VI"},
       {"yzABCDEF", "AEuBybr3w07MU"},
       {"yzABCDEF", "DEnh3Aa09jH5c"},
       {"yzABCDEF", "SeES5RpbP6IBg"},
       {"yzABCDEF", "sepW867O5DecA"},
       {"yzABCDEF", "76fkw0n09AlVY"},
       {"yzABCDEF", "02b4yKXa2Ezmg"},
       {"yzABCDEF", "05CwQ5.PMAOiw"},
       {"GHIJKLMN", "AEdma.Wd8OVuc"},
       {"GHIJKLMN", "DEvyOA86U40Yw"},
       {"GHIJKLMN", "Se3i1.wK7SvFw"},
       {"GHIJKLMN", "sey8LKM8jnnnY"},
       {"GHIJKLMN", "76eFLLP7Y4z2g"},
       {"GHIJKLMN", "02tr7rNC/2zeU"},
       {"GHIJKLMN", "0527iVLJRmBz6"},
       {"OPQRTSTU", "AEzOvkB5zDp2o"},
       {"OPQRTSTU", "DE6epaIuO/6Xw"},
       {"OPQRTSTU", "SemZ8Rw5UkXW2"},
       {"OPQRTSTU", "seD6BA6YFbqhg"},
       {"OPQRTSTU", "76/WPs6GC5pOU"},
       {"OPQRTSTU", "020T4ievozzgU"},
       {"OPQRTSTU", "05d/UmYKRWeME"},
       {"VWXYZ", "AE.Wv.eqyQmso"},
       {"VWXYZ", "DEM/AU49DMri2"},
       {"VWXYZ", "Se26LxmPj52qM"},
       {"VWXYZ", "sefTLuF2.KvE."},
       {"VWXYZ", "76qVtYProZCDA"},
       {"VWXYZ", "02HY6XzgJWEIU"},
       {"VWXYZ", "051moQVL2PkfU"},
       // Some non-ascii tests
       {"\u00C4", "CyaE7.kWcy.fs"},  // \xC3\x84 in UTF-8
       {"\u00C5", "AEUV1DMPEIDnA"},  // \xC3\x85 in UTF-8
       {"\u00C7", "AEpWNQaF3IUno"},  // \xC3\x87 in UTF-8
       {"\u2020", "AEboIqjs64Y0U"},  // \xE2\x80\xA0 in UTF-8
       {"\u00BF", "AE35zYeBlLaTs"},  // \xC2\xBF in UTF-8
       {"\u02C7", "AE0WyV2GLggXI"},  // \xCB\x87 in UTF-8
       {"\uF8FF", "AEa0xrpV1JyiA"},  // \xEF\xA3\xBF in UTF-8
  };
  @DataProvider
  public Object[][] passwordsAndCrypts() {
    return PASSWORDS_AND_CRYPTS;
  }
  /**
   * Tests various clear-text passwords and a corresponding crypt value.
   * We use the salt from the cryptedPassword so that we can regenerate
   * the same crypted value.
   */
  @Test(dataProvider = "passwordsAndCrypts")
  public void testVarious(String clearPassword, String cryptedPassword) throws Exception
  {
    validateCryptedPassword(clearPassword, cryptedPassword);
  }
  private void validateCryptedPassword(String clearPassword, String cryptedPassword) throws Exception
  {
    byte[] pw = clearPassword.getBytes("UTF-8");
    byte[] s = cryptedPassword.getBytes("UTF-8");
    // The first two bytes of the saltedPassword are used as the salt,
    // so the bytes that we get back from this should be
    byte[] r = crypt.crypt(pw, s);
    String st = new String(r, "UTF-8");
    Assert.assertEquals(st, cryptedPassword);
  }
}