001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2015-2016 ForgeRock AS.
015 */
016package org.opends.server.extensions;
017
018
019import org.forgerock.i18n.LocalizableMessage;
020import org.forgerock.i18n.slf4j.LocalizedLogger;
021import org.forgerock.opendj.config.server.ConfigChangeResult;
022import org.forgerock.opendj.config.server.ConfigException;
023import org.forgerock.opendj.config.server.ConfigurationChangeListener;
024import org.forgerock.opendj.ldap.ByteSequence;
025import org.forgerock.opendj.ldap.ByteString;
026import org.forgerock.opendj.ldap.ResultCode;
027import org.forgerock.opendj.server.config.server.BcryptPasswordStorageSchemeCfg;
028import org.opends.server.api.PasswordStorageScheme;
029import org.opends.server.types.DirectoryException;
030import org.opends.server.types.InitializationException;
031
032import java.util.List;
033
034import static org.opends.messages.ExtensionMessages.*;
035import static org.opends.server.extensions.ExtensionsConstants.*;
036
037
038/**
039 * This class defines a Directory Server password storage scheme that will
040 * encode values using the Blowfish reversible encryption algorithm.  This
041 * implementation supports only the user password syntax and not the auth
042 * password syntax.
043 */
044public class BcryptPasswordStorageScheme
045       extends PasswordStorageScheme<BcryptPasswordStorageSchemeCfg>
046    implements ConfigurationChangeListener<BcryptPasswordStorageSchemeCfg>
047{
048  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
049  /** The current configuration for this storage scheme. */
050  private volatile BcryptPasswordStorageSchemeCfg config;
051
052  /**
053   * Creates a new instance of this password storage scheme.  Note that no
054   * initialization should be performed here, as all initialization should be
055   * done in the {@link #initializePasswordStorageScheme(BcryptPasswordStorageSchemeCfg)} method.
056   */
057  public BcryptPasswordStorageScheme()
058  {
059  }
060
061
062  @Override
063  public void initializePasswordStorageScheme(BcryptPasswordStorageSchemeCfg configuration)
064         throws ConfigException, InitializationException
065  {
066    this.config = configuration;
067    config.addBcryptChangeListener(this);
068  }
069
070
071  @Override
072  public String getStorageSchemeName()
073  {
074    return STORAGE_SCHEME_NAME_BCRYPT;
075  }
076
077
078  @Override
079  public boolean isConfigurationChangeAcceptable(BcryptPasswordStorageSchemeCfg configuration,
080                                                 List<LocalizableMessage> unacceptableReasons)
081  {
082    return true;
083  }
084
085
086  @Override
087  public ConfigChangeResult applyConfigurationChange(BcryptPasswordStorageSchemeCfg configuration)
088  {
089    this.config = configuration;
090    return new ConfigChangeResult();
091  }
092
093
094  @Override
095  public ByteString encodePassword(ByteSequence plaintext)
096         throws DirectoryException
097  {
098    String salt = BCrypt.gensalt(config.getBcryptCost());
099    String hashed_password = BCrypt.hashpw(plaintext.toByteArray(), salt);
100    return ByteString.valueOfUtf8(hashed_password);
101  }
102
103
104  @Override
105  public ByteString encodePasswordWithScheme(ByteSequence plaintext)
106         throws DirectoryException
107  {
108    return ByteString.valueOfUtf8('{' + getStorageSchemeName() + '}' +  encodePassword(plaintext));
109  }
110
111
112  @Override
113  public boolean passwordMatches(ByteSequence plaintextPassword,
114                                 ByteSequence storedPassword)
115  {
116    try
117    {
118      return BCrypt.checkpw(plaintextPassword.toString(), storedPassword.toString());
119    }
120    catch (IllegalArgumentException e)
121    {
122      logger.traceException(e);
123      logger.error(ERR_PWSCHEME_INVALID_STORED_PASSWORD, e);
124      return false;
125    }
126  }
127
128
129  @Override
130  public boolean isReversible()
131  {
132    return false;
133  }
134
135
136  @Override
137  public ByteString getPlaintextValue(ByteSequence storedPassword)
138         throws DirectoryException
139  {
140    LocalizableMessage message = ERR_PWSCHEME_NOT_REVERSIBLE.get(getStorageSchemeName());
141    throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
142  }
143
144
145  @Override
146  public boolean supportsAuthPasswordSyntax()
147  {
148    return false;
149  }
150
151
152  @Override
153  public ByteString encodeAuthPassword(ByteSequence plaintext)
154         throws DirectoryException
155  {
156    LocalizableMessage message =
157        ERR_PWSCHEME_DOES_NOT_SUPPORT_AUTH_PASSWORD.get(getStorageSchemeName());
158    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
159  }
160
161
162  @Override
163  public boolean authPasswordMatches(ByteSequence plaintextPassword,
164                                     String authInfo, String authValue)
165  {
166    return false;
167  }
168
169
170  @Override
171  public ByteString getAuthPasswordPlaintextValue(String authInfo,
172                                                  String authValue)
173         throws DirectoryException
174  {
175    LocalizableMessage message =
176        ERR_PWSCHEME_DOES_NOT_SUPPORT_AUTH_PASSWORD.get(getStorageSchemeName());
177    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
178  }
179
180
181  @Override
182  public boolean isStorageSchemeSecure()
183  {
184    return true;
185  }
186}
187