/* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.util;
import java.lang.reflect.Method;
import javax.crypto.SecretKey;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.prng.EntropySource;
import org.bouncycastle.crypto.prng.SP800SecureRandom;
import org.bouncycastle.crypto.prng.drbg.HashSP800DRBG;
import org.bouncycastle.crypto.prng.drbg.SP80090DRBG;
import org.cryptacular.generator.sp80038a.EncryptedNonce;
import org.cryptacular.generator.sp80038d.RBGNonce;
/**
* Utility class for generating secure nonce and initialization vectors.
*
* @author Middleware Services
*/
public final class NonceUtil
{
/** Private constructor of utility class. */
private NonceUtil() {}
/**
* Generates a nonce of the given size by repetitively concatenating system timestamps (i.e. {@link
* System#nanoTime()}) up to the required size.
*
* @param length Positive number of bytes in nonce.
*
* @return Nonce bytes.
*/
public static byte[] timestampNonce(final int length)
{
if (length <= 0) {
throw new IllegalArgumentException(length + " is invalid. Length must be positive.");
}
final byte[] nonce = new byte[length];
int count = 0;
long timestamp;
while (count < length) {
timestamp = System.nanoTime();
for (int i = 0; i < 8 && count < length; i++) {
nonce[count++] = (byte) (timestamp & 0xFF);
timestamp >>= 8;
}
}
return nonce;
}
/**
* Generates a nonce/IV using the strategy described in NIST <a
* href="http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf">SP-800-38d</a>, section 8.2.2, "RBG-based
* Construction". The implementation uses a hash-based DRBG based on a SHA-256 digest, and uses random data for all
* bits of the nonce; that is, the fixed field is null.
*
* <p>This nonce generation strategy is suitable for GCM ciphers.</p>
*
* @param length Number of bytes in nonce; MUST be 12 or more.
*
* @return Nonce bytes.
*/
public static byte[] nist80038d(final int length)
{
return new RBGNonce(length).generate();
}
/**
* Generates a random IV according to NIST <a href="http://goo.gl/S9z8qF">SP-800-63a</a>, appendix C, method 1
* (encrypted nonce), suitable for use with any block cipher mode described in that standard. This method uses an
* instance of {@link EncryptedNonce} for the implementation.
*
* @param cipher Block cipher.
* @param key Encryption key intended for use with IV.
*
* @return Cipher block size number of random bytes.
*
* @see EncryptedNonce
*/
public static byte[] nist80063a(final BlockCipher cipher, final SecretKey key)
{
BlockCipher raw = cipher;
// Get the underlying cipher if there is one
final Method method = ReflectUtil.getMethod(cipher.getClass(), "getUnderlyingCipher");
if (method != null) {
raw = (BlockCipher) ReflectUtil.invoke(cipher, method);
}
return new EncryptedNonce(raw, key).generate();
}
/**
* Generates a random IV according to NIST <a href="http://goo.gl/S9z8qF">SP-800-63a</a>, appendix C, method 2
* (pseudorandom), suitable for use with any block cipher mode described in that standard.
*
* @param prng NIST SP800-63a approved pseudorandom number generator.
* @param blockSize Cipher block size in bytes.
*
* @return Cipher block size number of random bytes.
*/
public static byte[] nist80063a(final SP800SecureRandom prng, final int blockSize)
{
prng.setSeed(System.nanoTime());
final byte[] iv = new byte[blockSize];
prng.nextBytes(iv);
return iv;
}
/**
* Generates a random IV according to NIST <a href="http://goo.gl/S9z8qF">SP-800-63a</a>, appendix C, method 2
* (pseudorandom), suitable for use with any block cipher mode described in that standard. Uses an instance of {@link
* RBGNonce} internally with length equal to block size of given cipher.
*
* @param cipher Block cipher.
*
* @return Cipher block size number of random bytes.
*
* @see RBGNonce
*/
public static byte[] nist80063a(final BlockCipher cipher)
{
return new RBGNonce(cipher.getBlockSize()).generate();
}
/**
* Creates a new DRBG instance based on a SHA-256 digest.
*
* @param length Length in bits of values to be produced by DRBG instance.
*
* @return New DRGB instance.
*/
public static SP80090DRBG newRBG(final int length)
{
return newRBG(new SHA256Digest(), length);
}
/**
* Creates a new hash-based DRBG instance that uses the given digest as the pseudorandom source.
*
* @param digest Digest algorithm.
* @param length Length in bits of values to be produced by DRBG instance.
*
* @return New DRGB instance.
*/
public static SP80090DRBG newRBG(final Digest digest, final int length)
{
return
new HashSP800DRBG(
digest,
length,
new EntropySource() {
@Override
public boolean isPredictionResistant()
{
return false;
}
@Override
public byte[] getEntropy()
{
return NonceUtil.timestampNonce(length);
}
@Override
public int entropySize()
{
return length;
}
},
null,
NonceUtil.timestampNonce(8));
}
}