This is a checkin for Issue #311 "CRYPT Storage Scheme Support". This
code was contributed externally by java.net user bdamm.
The crypt implementation itself was ported from the C-based one
OpenSolaris. Since the OpenSolaris code is covered under the CDDL,
we're okay to include it in OpenDS, but I've opened issue 1573 to
remind us to make sure that we got all of the copyrights right etc.
5 files added
4 files modified
| | |
| | | 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 |
| New file |
| | |
| | | /* |
| | | * 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; |
| | | } |
| | | } |
| | |
| | | |
| | | |
| | | /** |
| | | * 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. |
| | | */ |
| New file |
| | |
| | | /* |
| | | * 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); |
| | | } |
| | | } |
| | | |
| | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | 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.*; |
| | |
| | | |
| | | |
| | | |
| | | 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. |
| | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a temporary text file with the specified contents. It will be |
| | | * marked for automatic deletion when the JVM exits. |
| | |
| | | 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. |
| New file |
| | |
| | | /* |
| | | * 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; |
| | | } |
| | | } |
| | | |
| | |
| | | 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; |
| | | |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | |
| | | @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. |
| New file |
| | |
| | | /* |
| | | * 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); |
| | | } |
| | | } |
| | | |
| | | |