001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2008 Sun Microsystems, Inc.
025 *      Portions Copyright 2013-2015 ForgeRock AS
026 */
027package org.opends.server.extensions;
028
029
030
031import org.forgerock.i18n.LocalizableMessage;
032import org.opends.server.admin.std.server.RC4PasswordStorageSchemeCfg;
033import org.opends.server.api.PasswordStorageScheme;
034import org.forgerock.opendj.config.server.ConfigException;
035import org.opends.server.core.DirectoryServer;
036import org.forgerock.i18n.slf4j.LocalizedLogger;
037import org.opends.server.types.*;
038import org.forgerock.opendj.ldap.ResultCode;
039import org.forgerock.opendj.ldap.ByteString;
040import org.forgerock.opendj.ldap.ByteSequence;
041import org.opends.server.util.Base64;
042
043import java.util.Arrays;
044
045import static org.opends.messages.ExtensionMessages.*;
046import static org.opends.server.extensions.ExtensionsConstants.*;
047import static org.opends.server.util.StaticUtils.*;
048
049
050/**
051 * This class defines a Directory Server password storage scheme that will
052 * encode values using the RC4 reversible encryption algorithm.  This
053 * implementation supports only the user password syntax and not the auth
054 * password syntax.
055 */
056public class RC4PasswordStorageScheme
057       extends PasswordStorageScheme<RC4PasswordStorageSchemeCfg>
058{
059  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
060
061
062
063  /**
064   * The reference to the Directory Server crypto manager that we will use to
065   * handle the encryption/decryption.
066   */
067  private CryptoManager cryptoManager;
068
069
070
071  /**
072   * Creates a new instance of this password storage scheme.  Note that no
073   * initialization should be performed here, as all initialization should be
074   * done in the {@code initializePasswordStorageScheme} method.
075   */
076  public RC4PasswordStorageScheme()
077  {
078    super();
079  }
080
081
082
083  /** {@inheritDoc} */
084  @Override
085  public void initializePasswordStorageScheme(
086                   RC4PasswordStorageSchemeCfg configuration)
087         throws ConfigException, InitializationException
088  {
089    cryptoManager = DirectoryServer.getCryptoManager();
090  }
091
092
093
094  /** {@inheritDoc} */
095  @Override
096  public String getStorageSchemeName()
097  {
098    return STORAGE_SCHEME_NAME_RC4;
099  }
100
101
102
103  /** {@inheritDoc} */
104  @Override
105  public ByteString encodePassword(ByteSequence plaintext)
106         throws DirectoryException
107  {
108    byte[] plaintextBytes = null;
109    try
110    {
111      // TODO: Can we avoid this copy?
112      plaintextBytes = plaintext.toByteArray();
113      byte[] encodedBytes = cryptoManager.encrypt(CIPHER_TRANSFORMATION_RC4,
114                                                  KEY_SIZE_RC4,
115                                                  plaintextBytes);
116      return ByteString.valueOfUtf8(Base64.encode(encodedBytes));
117    }
118    catch (Exception e)
119    {
120      logger.traceException(e);
121
122      LocalizableMessage m = ERR_PWSCHEME_CANNOT_ENCRYPT.get(STORAGE_SCHEME_NAME_RC4,
123                                                  getExceptionMessage(e));
124      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
125                                   m, e);
126    }
127    finally
128    {
129      if (plaintextBytes != null)
130      {
131        Arrays.fill(plaintextBytes, (byte) 0);
132      }
133    }
134  }
135
136
137
138  /** {@inheritDoc} */
139  @Override
140  public ByteString encodePasswordWithScheme(ByteSequence plaintext)
141         throws DirectoryException
142  {
143    StringBuilder buffer = new StringBuilder();
144    buffer.append('{');
145    buffer.append(STORAGE_SCHEME_NAME_RC4);
146    buffer.append('}');
147    byte[] plaintextBytes = null;
148
149    try
150    {
151      // TODO: Can we avoid this copy?
152      plaintextBytes = plaintext.toByteArray();
153      byte[] encodedBytes = cryptoManager.encrypt(CIPHER_TRANSFORMATION_RC4,
154                                                  KEY_SIZE_RC4,
155                                                  plaintextBytes);
156      buffer.append(Base64.encode(encodedBytes));
157    }
158    catch (Exception e)
159    {
160      logger.traceException(e);
161
162      LocalizableMessage m = ERR_PWSCHEME_CANNOT_ENCRYPT.get(STORAGE_SCHEME_NAME_RC4,
163                                                  getExceptionMessage(e));
164      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
165                                   m, e);
166    }
167    finally
168    {
169      if (plaintextBytes != null)
170      {
171        Arrays.fill(plaintextBytes, (byte) 0);
172      }
173    }
174
175    return ByteString.valueOfUtf8(buffer);
176  }
177
178
179
180  /** {@inheritDoc} */
181  @Override
182  public boolean passwordMatches(ByteSequence plaintextPassword,
183                                 ByteSequence storedPassword)
184  {
185    try
186    {
187      ByteString decryptedPassword =
188          ByteString.wrap(cryptoManager.decrypt(
189               Base64.decode(storedPassword.toString())));
190      return plaintextPassword.equals(decryptedPassword);
191    }
192    catch (Exception e)
193    {
194      logger.traceException(e);
195
196      return false;
197    }
198  }
199
200
201
202  /** {@inheritDoc} */
203  @Override
204  public boolean isReversible()
205  {
206    return true;
207  }
208
209
210
211  /** {@inheritDoc} */
212  @Override
213  public ByteString getPlaintextValue(ByteSequence storedPassword)
214         throws DirectoryException
215  {
216    try
217    {
218      byte[] decryptedPassword =
219           cryptoManager.decrypt(Base64.decode(storedPassword.toString()));
220      return ByteString.wrap(decryptedPassword);
221    }
222    catch (Exception e)
223    {
224      logger.traceException(e);
225
226      LocalizableMessage m = ERR_PWSCHEME_CANNOT_DECRYPT.get(STORAGE_SCHEME_NAME_RC4,
227                                                  getExceptionMessage(e));
228      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
229                                   m, e);
230    }
231  }
232
233
234
235  /** {@inheritDoc} */
236  @Override
237  public boolean supportsAuthPasswordSyntax()
238  {
239    // This storage scheme does not support the authentication password syntax.
240    return false;
241  }
242
243
244
245  /** {@inheritDoc} */
246  @Override
247  public ByteString encodeAuthPassword(ByteSequence plaintext)
248         throws DirectoryException
249  {
250    LocalizableMessage message =
251        ERR_PWSCHEME_DOES_NOT_SUPPORT_AUTH_PASSWORD.get(getStorageSchemeName());
252    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
253  }
254
255
256
257  /** {@inheritDoc} */
258  @Override
259  public boolean authPasswordMatches(ByteSequence plaintextPassword,
260                                     String authInfo, String authValue)
261  {
262    // This storage scheme does not support the authentication password syntax.
263    return false;
264  }
265
266
267
268  /** {@inheritDoc} */
269  @Override
270  public ByteString getAuthPasswordPlaintextValue(String authInfo,
271                                                  String authValue)
272         throws DirectoryException
273  {
274    LocalizableMessage message =
275        ERR_PWSCHEME_DOES_NOT_SUPPORT_AUTH_PASSWORD.get(getStorageSchemeName());
276    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
277  }
278
279
280
281  /** {@inheritDoc} */
282  @Override
283  public boolean isStorageSchemeSecure()
284  {
285    // This password storage scheme should be considered secure.
286    return true;
287  }
288}
289