/* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.generator;

import java.security.SecureRandom;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.prng.SP800SecureRandomBuilder;
import org.cryptacular.util.NonceUtil;

/**
 * Factory class with static methods for generating {@link SecretKey}s.
 *
 * @author  Middleware Services
 */
public final class SecretKeyGenerator
{

  /** Private constructor of static class. */
  private SecretKeyGenerator() {}


  /**
   * Generates a symmetric encryption key whose size is equal to the cipher block size.
   *
   * @param  cipher  Cipher with with key will be used.
   *
   * @return  Symmetric encryption key.
   */
  public static SecretKey generate(final BlockCipher cipher)
  {
    return generate(cipher.getBlockSize() * 8, cipher);
  }


  /**
   * Generates a symmetric encryption key of the given length.
   *
   * @param  bitLength  Desired key length in bits.
   * @param  cipher  Cipher with with key will be used.
   *
   * @return  Symmetric encryption key.
   */
  public static SecretKey generate(final int bitLength, final BlockCipher cipher)
  {
    // Want as much nonce data as key bits
    final byte[] nonce = NonceUtil.randomNonce((bitLength + 7) / 8);
    return generate(bitLength, cipher, new SP800SecureRandomBuilder().buildHash(new SHA256Digest(), nonce, false));
  }


  /**
   * Generates a symmetric encryption key of the given length.
   *
   * @param  bitLength  Desired key length in bits.
   * @param  cipher  Cipher with with key will be used.
   * @param  random  Randomness provider for key generation.
   *
   * @return  Symmetric encryption key.
   */
  public static SecretKey generate(final int bitLength, final BlockCipher cipher, final SecureRandom random)
  {
    // Round up for bit lengths that are not a multiple of 8
    final byte[] key = new byte[(bitLength + 7) / 8];
    random.nextBytes(key);
    return new SecretKeySpec(key, cipher.getAlgorithmName());
  }
}
