001/*
002 * Copyright 2020-2022 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2020-2022 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-2022 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", 16_384, 16, 16,
056       "HmacSHA256"),
057
058
059
060  /**
061   * Cipher settings that use a 256-bit AES cipher.
062   */
063  AES_256("AES/CBC/PKCS5Padding", 256, "PBKDF2WithHmacSHA512", 131_072, 16, 16,
064       "HmacSHA512");
065
066
067
068  /**
069   * A reference to the strongest defined cipher type value that is supported by
070   * the underlying JVM.  Its value will be {@code null} until the first attempt
071   * is made to determine it.  The cached value will be used for subsequent
072   * attempts to retrieve the value.
073   */
074  @NotNull private static final AtomicReference<PassphraseEncryptionCipherType>
075       STRONGEST_AVAILABLE_CIPHER_TYPE = new AtomicReference<>();
076
077
078
079  // The length (in bytes) to use for the initialization vector when creating
080  // the cipher.
081  private final int initializationVectorLengthBytes;
082
083  // The iteration count that will be used when generating the encryption key
084  // from the passphrase.
085  private final int keyFactoryIterationCount;
086
087  // The length (in bytes) to use for the salt when generating the encryption
088  // key from the passphrase.
089  private final int keyFactorySaltLengthBytes;
090
091  // The length (in bits) for the encryption key to generate from the
092  // passphrase.
093  private final int keyLengthBits;
094
095  // The cipher transformation that will be used for the encryption.
096  @NotNull private final String cipherTransformation;
097
098  // The name of the algorithm that will be used to generate the encryption key
099  // from the passphrase.
100  @NotNull private final String keyFactoryAlgorithm;
101
102  // The name of the algorithm that will be used to generate a MAC of the
103  // encryption header contents.
104  @NotNull private final String macAlgorithm;
105
106
107
108  /**
109   * Creates a new passphrase encryption cipher type value with the provided
110   * information.
111   *
112   * @param  cipherTransformation
113   *              The cipher transformation that will be used for the
114   *              encryption.
115   * @param  keyLengthBits
116   *              The length (in bits) for the encryption key to generate.
117   * @param  keyFactoryAlgorithm
118   *              The name of the algorithm that will be used to generate the
119   *              encryption key from the passphrase.
120   * @param  keyFactoryIterationCount
121   *              The iteration count that will be used when generating the
122   *              encryption key from the passphrase.
123   * @param  keyFactorySaltLengthBytes
124   *              The length (in bytes) to use for the salt when generating the
125   *              encryption key from the passphrase.
126   * @param  initializationVectorLengthBytes
127   *              The length (in bytes) to use for the initialization vector
128   *              when creating the cipher.
129   * @param  macAlgorithm
130   *              The name of the algorithm that will be used to generate a MAC
131   *              of the encryption header contents.
132   */
133  PassphraseEncryptionCipherType(@NotNull final String cipherTransformation,
134                                 final int keyLengthBits,
135                                 @NotNull final String keyFactoryAlgorithm,
136                                 final int keyFactoryIterationCount,
137                                 final int keyFactorySaltLengthBytes,
138                                 final int initializationVectorLengthBytes,
139                                 @NotNull final String macAlgorithm)
140  {
141    this.cipherTransformation = cipherTransformation;
142    this.keyLengthBits = keyLengthBits;
143    this.keyFactoryAlgorithm = keyFactoryAlgorithm;
144    this.keyFactoryIterationCount = keyFactoryIterationCount;
145    this.keyFactorySaltLengthBytes = keyFactorySaltLengthBytes;
146    this.initializationVectorLengthBytes = initializationVectorLengthBytes;
147    this.macAlgorithm = macAlgorithm;
148  }
149
150
151
152  /**
153   * Retrieves the cipher transformation that will be used for the encryption.
154   *
155   * @return  The cipher transformation that will be used for the encryption.
156   */
157  @NotNull()
158  public String getCipherTransformation()
159  {
160    return cipherTransformation;
161  }
162
163
164
165  /**
166   * Retrieves the length (in bits) for the encryption key to generate.
167   *
168   * @return  The length (in bits) for the encryption key to generate.
169   */
170  public int getKeyLengthBits()
171  {
172    return keyLengthBits;
173  }
174
175
176
177  /**
178   * Retrieves the name of the algorithm that will be used to generate the
179   * encryption key from the passphrase.
180   *
181   * @return  The name of the algorithm that will be used to generate the
182   *          encryption key from the passphrase.
183   */
184  @NotNull()
185  public String getKeyFactoryAlgorithm()
186  {
187    return keyFactoryAlgorithm;
188  }
189
190
191
192  /**
193   * Retrieves the iteration count that will be used when generating the
194   * encryption key from the passphrase.
195   *
196   * @return  The iteration count that will be used when generating the
197   *          encryption key from the passphrase.
198   */
199  public int getKeyFactoryIterationCount()
200  {
201    return keyFactoryIterationCount;
202  }
203
204
205
206  /**
207   * Retrieves the length (in bytes) to use for the salt when generating the
208   * encryption key from the passphrase.
209   *
210   * @return  The length (in bytes) to use for the salt when generating the
211   *          encryption key from the passphrase.
212   */
213  public int getKeyFactorySaltLengthBytes()
214  {
215    return keyFactorySaltLengthBytes;
216  }
217
218
219
220  /**
221   * Retrieves the length (in bytes) to use for the initialization vector when
222   * generating the cipher.
223   *
224   * @return  The length (in bytes) to use for the initialization vector when
225   *          generating the cipher.
226   */
227  public int getInitializationVectorLengthBytes()
228  {
229    return initializationVectorLengthBytes;
230  }
231
232
233
234  /**
235   * Retrieves the name of the algorithm that will be used to generate a MAC of
236   * the encryption header contents.
237   *
238   * @return  The name of the algorithm that will be used to generate a MAC of
239   *          the encryption header contents.
240   */
241  @NotNull()
242  public String getMacAlgorithm()
243  {
244    return macAlgorithm;
245  }
246
247
248
249  /**
250   * Retrieves the cipher type value for the provided name.
251   *
252   * @param  name  The name of the cipher type value to retrieve.
253   *
254   * @return  The cipher type object for the given name, or {@code null} if the
255   *          provided name does not map to any cipher type value.
256   */
257  @Nullable()
258  public static PassphraseEncryptionCipherType forName(
259              @NotNull final String name)
260  {
261    final String transformedName =
262         StaticUtils.toUpperCase(name).replace('-', '_');
263    for (final PassphraseEncryptionCipherType value : values())
264    {
265      if (value.name().equals(transformedName))
266      {
267        return value;
268      }
269    }
270
271    return null;
272  }
273
274
275
276  /**
277   * Retrieves the cipher type value that corresponds to the strongest supported
278   * level of protection that is available in the underlying JVM.
279   *
280   * @return  The cipher type value that corresponds to the strongest supported
281   *          level of protection in the underlying JVM.
282   */
283  @NotNull()
284  public static PassphraseEncryptionCipherType getStrongestAvailableCipherType()
285  {
286    PassphraseEncryptionCipherType cipherType =
287         STRONGEST_AVAILABLE_CIPHER_TYPE.get();
288    if (cipherType == null)
289    {
290      cipherType = PassphraseEncryptionCipherType.AES_128;
291
292      try
293      {
294        final PassphraseEncryptionCipherType ct =
295             PassphraseEncryptionCipherType.AES_256;
296        final PassphraseEncryptedStreamHeader header =
297             new PassphraseEncryptedStreamHeader(
298                  "dummy-passphrase".toCharArray(), ct.getKeyFactoryAlgorithm(),
299                  ct.getKeyFactoryIterationCount(),
300                  new byte[ct.getKeyFactorySaltLengthBytes()],
301                  ct.getKeyLengthBits(), ct.getCipherTransformation(),
302                  new byte[ct.getInitializationVectorLengthBytes()],
303                  null, ct.getMacAlgorithm());
304        header.createCipher(Cipher.ENCRYPT_MODE);
305        cipherType = ct;
306      }
307      catch (final Exception e)
308      {
309        Debug.debugException(e);
310      }
311
312      if (! STRONGEST_AVAILABLE_CIPHER_TYPE.compareAndSet(null, cipherType))
313      {
314        cipherType = STRONGEST_AVAILABLE_CIPHER_TYPE.get();
315      }
316    }
317
318    return cipherType;
319  }
320
321
322
323  /**
324   * Retrieves a string representation of this cipher type value.
325   *
326   * @return  A string representation of this cipher type value.
327   */
328  @Override()
329  @NotNull()
330  public String toString()
331  {
332    final StringBuilder buffer = new StringBuilder();
333    toString(buffer);
334    return buffer.toString();
335  }
336
337
338
339  /**
340   * Appends a string representation of this cipher type value to the provided
341   * buffer.
342   *
343   * @param  buffer  The buffer to which the information should be appended.
344   */
345  public void toString(@NotNull final StringBuilder buffer)
346  {
347    buffer.append("PassphraseEncryptedCipherType(cipherTransformation='");
348    buffer.append(cipherTransformation);
349    buffer.append("', keyLengthBits=");
350    buffer.append(keyLengthBits);
351    buffer.append(", keyFactoryAlgorithm='");
352    buffer.append(keyFactoryAlgorithm);
353    buffer.append("', keyFactoryIterationCount=");
354    buffer.append(keyFactoryIterationCount);
355    buffer.append(", keyFactorySaltLengthBytes=");
356    buffer.append(keyFactorySaltLengthBytes);
357    buffer.append(", initializationVectorLengthBytes=");
358    buffer.append(initializationVectorLengthBytes);
359    buffer.append(", macAlgorithm='");
360    buffer.append(macAlgorithm);
361    buffer.append("')");
362  }
363}