001/*
002 * Copyright 2020-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2020-2024 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2020-2024 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.util;
037
038
039
040import java.util.concurrent.atomic.AtomicReference;
041import javax.crypto.Cipher;
042
043
044
045/**
046 * This enum defines sets of settings that may be used when encrypting data with
047 * a {@link PassphraseEncryptedOutputStream}.
048 */
049@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
050public enum PassphraseEncryptionCipherType
051{
052  /**
053   * Cipher settings that use a 128-bit AES cipher.
054   */
055  AES_128("AES/CBC/PKCS5Padding", 128, "PBKDF2WithHmacSHA1",
056       PassphraseEncryptedOutputStream.
057            DEFAULT_AES_128_CIPHER_TYPE_ITERATION_COUNT,
058       16, 16, "HmacSHA256"),
059
060
061
062  /**
063   * Cipher settings that use a 256-bit AES cipher.
064   */
065  AES_256("AES/CBC/PKCS5Padding", 256, "PBKDF2WithHmacSHA512",
066       PassphraseEncryptedOutputStream.
067            DEFAULT_AES_256_CIPHER_TYPE_ITERATION_COUNT,
068       16, 16, "HmacSHA512");
069
070
071
072  /**
073   * A reference to the strongest defined cipher type value that is supported by
074   * the underlying JVM.  Its value will be {@code null} until the first attempt
075   * is made to determine it.  The cached value will be used for subsequent
076   * attempts to retrieve the value.
077   */
078  @NotNull private static final AtomicReference<PassphraseEncryptionCipherType>
079       STRONGEST_AVAILABLE_CIPHER_TYPE = new AtomicReference<>();
080
081
082
083  // The length (in bytes) to use for the initialization vector when creating
084  // the cipher.
085  private final int initializationVectorLengthBytes;
086
087  // The iteration count that will be used when generating the encryption key
088  // from the passphrase.
089  private final int keyFactoryIterationCount;
090
091  // The length (in bytes) to use for the salt when generating the encryption
092  // key from the passphrase.
093  private final int keyFactorySaltLengthBytes;
094
095  // The length (in bits) for the encryption key to generate from the
096  // passphrase.
097  private final int keyLengthBits;
098
099  // The cipher transformation that will be used for the encryption.
100  @NotNull private final String cipherTransformation;
101
102  // The name of the algorithm that will be used to generate the encryption key
103  // from the passphrase.
104  @NotNull private final String keyFactoryAlgorithm;
105
106  // The name of the algorithm that will be used to generate a MAC of the
107  // encryption header contents.
108  @NotNull private final String macAlgorithm;
109
110
111
112  /**
113   * Creates a new passphrase encryption cipher type value with the provided
114   * information.
115   *
116   * @param  cipherTransformation
117   *              The cipher transformation that will be used for the
118   *              encryption.
119   * @param  keyLengthBits
120   *              The length (in bits) for the encryption key to generate.
121   * @param  keyFactoryAlgorithm
122   *              The name of the algorithm that will be used to generate the
123   *              encryption key from the passphrase.
124   * @param  keyFactoryIterationCount
125   *              The iteration count that will be used when generating the
126   *              encryption key from the passphrase.
127   * @param  keyFactorySaltLengthBytes
128   *              The length (in bytes) to use for the salt when generating the
129   *              encryption key from the passphrase.
130   * @param  initializationVectorLengthBytes
131   *              The length (in bytes) to use for the initialization vector
132   *              when creating the cipher.
133   * @param  macAlgorithm
134   *              The name of the algorithm that will be used to generate a MAC
135   *              of the encryption header contents.
136   */
137  PassphraseEncryptionCipherType(@NotNull final String cipherTransformation,
138                                 final int keyLengthBits,
139                                 @NotNull final String keyFactoryAlgorithm,
140                                 final int keyFactoryIterationCount,
141                                 final int keyFactorySaltLengthBytes,
142                                 final int initializationVectorLengthBytes,
143                                 @NotNull final String macAlgorithm)
144  {
145    this.cipherTransformation = cipherTransformation;
146    this.keyLengthBits = keyLengthBits;
147    this.keyFactoryAlgorithm = keyFactoryAlgorithm;
148    this.keyFactoryIterationCount = keyFactoryIterationCount;
149    this.keyFactorySaltLengthBytes = keyFactorySaltLengthBytes;
150    this.initializationVectorLengthBytes = initializationVectorLengthBytes;
151    this.macAlgorithm = macAlgorithm;
152  }
153
154
155
156  /**
157   * Retrieves the cipher transformation that will be used for the encryption.
158   *
159   * @return  The cipher transformation that will be used for the encryption.
160   */
161  @NotNull()
162  public String getCipherTransformation()
163  {
164    return cipherTransformation;
165  }
166
167
168
169  /**
170   * Retrieves the length (in bits) for the encryption key to generate.
171   *
172   * @return  The length (in bits) for the encryption key to generate.
173   */
174  public int getKeyLengthBits()
175  {
176    return keyLengthBits;
177  }
178
179
180
181  /**
182   * Retrieves the name of the algorithm that will be used to generate the
183   * encryption key from the passphrase.
184   *
185   * @return  The name of the algorithm that will be used to generate the
186   *          encryption key from the passphrase.
187   */
188  @NotNull()
189  public String getKeyFactoryAlgorithm()
190  {
191    return keyFactoryAlgorithm;
192  }
193
194
195
196  /**
197   * Retrieves the iteration count that will be used when generating the
198   * encryption key from the passphrase.
199   *
200   * @return  The iteration count that will be used when generating the
201   *          encryption key from the passphrase.
202   */
203  public int getKeyFactoryIterationCount()
204  {
205    return keyFactoryIterationCount;
206  }
207
208
209
210  /**
211   * Retrieves the length (in bytes) to use for the salt when generating the
212   * encryption key from the passphrase.
213   *
214   * @return  The length (in bytes) to use for the salt when generating the
215   *          encryption key from the passphrase.
216   */
217  public int getKeyFactorySaltLengthBytes()
218  {
219    return keyFactorySaltLengthBytes;
220  }
221
222
223
224  /**
225   * Retrieves the length (in bytes) to use for the initialization vector when
226   * generating the cipher.
227   *
228   * @return  The length (in bytes) to use for the initialization vector when
229   *          generating the cipher.
230   */
231  public int getInitializationVectorLengthBytes()
232  {
233    return initializationVectorLengthBytes;
234  }
235
236
237
238  /**
239   * Retrieves the name of the algorithm that will be used to generate a MAC of
240   * the encryption header contents.
241   *
242   * @return  The name of the algorithm that will be used to generate a MAC of
243   *          the encryption header contents.
244   */
245  @NotNull()
246  public String getMacAlgorithm()
247  {
248    return macAlgorithm;
249  }
250
251
252
253  /**
254   * Retrieves the cipher type value for the provided name.
255   *
256   * @param  name  The name of the cipher type value to retrieve.
257   *
258   * @return  The cipher type object for the given name, or {@code null} if the
259   *          provided name does not map to any cipher type value.
260   */
261  @Nullable()
262  public static PassphraseEncryptionCipherType forName(
263              @NotNull final String name)
264  {
265    final String transformedName =
266         StaticUtils.toUpperCase(name).replace('-', '_');
267    for (final PassphraseEncryptionCipherType value : values())
268    {
269      if (value.name().equals(transformedName))
270      {
271        return value;
272      }
273    }
274
275    return null;
276  }
277
278
279
280  /**
281   * Retrieves the cipher type value that corresponds to the strongest supported
282   * level of protection that is available in the underlying JVM.
283   *
284   * @return  The cipher type value that corresponds to the strongest supported
285   *          level of protection in the underlying JVM.
286   */
287  @NotNull()
288  public static PassphraseEncryptionCipherType getStrongestAvailableCipherType()
289  {
290    PassphraseEncryptionCipherType cipherType =
291         STRONGEST_AVAILABLE_CIPHER_TYPE.get();
292    if (cipherType == null)
293    {
294      cipherType = PassphraseEncryptionCipherType.AES_128;
295
296      try
297      {
298        final PassphraseEncryptionCipherType ct =
299             PassphraseEncryptionCipherType.AES_256;
300        final PassphraseEncryptedStreamHeader header =
301             new PassphraseEncryptedStreamHeader(
302                  "dummy-passphrase".toCharArray(), ct.getKeyFactoryAlgorithm(),
303                  ct.getKeyFactoryIterationCount(),
304                  new byte[ct.getKeyFactorySaltLengthBytes()],
305                  ct.getKeyLengthBits(), ct.getCipherTransformation(),
306                  new byte[ct.getInitializationVectorLengthBytes()],
307                  null, ct.getMacAlgorithm());
308        header.createCipher(Cipher.ENCRYPT_MODE);
309        cipherType = ct;
310      }
311      catch (final Exception e)
312      {
313        Debug.debugException(e);
314      }
315
316      if (! STRONGEST_AVAILABLE_CIPHER_TYPE.compareAndSet(null, cipherType))
317      {
318        cipherType = STRONGEST_AVAILABLE_CIPHER_TYPE.get();
319      }
320    }
321
322    return cipherType;
323  }
324
325
326
327  /**
328   * Retrieves a string representation of this cipher type value.
329   *
330   * @return  A string representation of this cipher type value.
331   */
332  @Override()
333  @NotNull()
334  public String toString()
335  {
336    final StringBuilder buffer = new StringBuilder();
337    toString(buffer);
338    return buffer.toString();
339  }
340
341
342
343  /**
344   * Appends a string representation of this cipher type value to the provided
345   * buffer.
346   *
347   * @param  buffer  The buffer to which the information should be appended.
348   */
349  public void toString(@NotNull final StringBuilder buffer)
350  {
351    buffer.append("PassphraseEncryptedCipherType(cipherTransformation='");
352    buffer.append(cipherTransformation);
353    buffer.append("', keyLengthBits=");
354    buffer.append(keyLengthBits);
355    buffer.append(", keyFactoryAlgorithm='");
356    buffer.append(keyFactoryAlgorithm);
357    buffer.append("', keyFactoryIterationCount=");
358    buffer.append(keyFactoryIterationCount);
359    buffer.append(", keyFactorySaltLengthBytes=");
360    buffer.append(keyFactorySaltLengthBytes);
361    buffer.append(", initializationVectorLengthBytes=");
362    buffer.append(initializationVectorLengthBytes);
363    buffer.append(", macAlgorithm='");
364    buffer.append(macAlgorithm);
365    buffer.append("')");
366  }
367}