org.bouncycastle.crypto.prng.drbg.CTRSP800DRBG Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com.liferay.saml.opensaml.integration Show documentation
Show all versions of com.liferay.saml.opensaml.integration Show documentation
Liferay SAML OpenSAML Integration
package org.bouncycastle.crypto.prng.drbg;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.prng.EntropySource;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;
/**
* A SP800-90A CTR DRBG.
*/
public class CTRSP800DRBG
implements SP80090DRBG
{
private static final long TDEA_RESEED_MAX = 1L << (32 - 1);
private static final long AES_RESEED_MAX = 1L << (48 - 1);
private static final int TDEA_MAX_BITS_REQUEST = 1 << (13 - 1);
private static final int AES_MAX_BITS_REQUEST = 1 << (19 - 1);
private EntropySource _entropySource;
private BlockCipher _engine;
private int _keySizeInBits;
private int _seedLength;
private int _securityStrength;
// internal state
private byte[] _Key;
private byte[] _V;
private long _reseedCounter = 0;
private boolean _isTDEA = false;
/**
* Construct a SP800-90A CTR DRBG.
*
* Minimum entropy requirement is the security strength requested.
*
* @param engine underlying block cipher to use to support DRBG
* @param keySizeInBits size of the key to use with the block cipher.
* @param securityStrength security strength required (in bits)
* @param entropySource source of entropy to use for seeding/reseeding.
* @param personalizationString personalization string to distinguish this DRBG (may be null).
* @param nonce nonce to further distinguish this DRBG (may be null).
*/
public CTRSP800DRBG(BlockCipher engine, int keySizeInBits, int securityStrength, EntropySource entropySource, byte[] personalizationString, byte[] nonce)
{
_entropySource = entropySource;
_engine = engine;
_keySizeInBits = keySizeInBits;
_securityStrength = securityStrength;
_seedLength = keySizeInBits + engine.getBlockSize() * 8;
_isTDEA = isTDEA(engine);
if (securityStrength > 256)
{
throw new IllegalArgumentException("Requested security strength is not supported by the derivation function");
}
if (getMaxSecurityStrength(engine, keySizeInBits) < securityStrength)
{
throw new IllegalArgumentException("Requested security strength is not supported by block cipher and key size");
}
if (entropySource.entropySize() < securityStrength)
{
throw new IllegalArgumentException("Not enough entropy for security strength required");
}
byte[] entropy = getEntropy(); // Get_entropy_input
CTR_DRBG_Instantiate_algorithm(entropy, nonce, personalizationString);
}
private void CTR_DRBG_Instantiate_algorithm(byte[] entropy, byte[] nonce,
byte[] personalisationString)
{
byte[] seedMaterial = Arrays.concatenate(entropy, nonce, personalisationString);
byte[] seed = Block_Cipher_df(seedMaterial, _seedLength);
int outlen = _engine.getBlockSize();
_Key = new byte[(_keySizeInBits + 7) / 8];
_V = new byte[outlen];
// _Key & _V are modified by this call
CTR_DRBG_Update(seed, _Key, _V);
_reseedCounter = 1;
}
private void CTR_DRBG_Update(byte[] seed, byte[] key, byte[] v)
{
byte[] temp = new byte[seed.length];
byte[] outputBlock = new byte[_engine.getBlockSize()];
int i=0;
int outLen = _engine.getBlockSize();
_engine.init(true, new KeyParameter(expandKey(key)));
while (i*outLen < seed.length)
{
addOneTo(v);
_engine.processBlock(v, 0, outputBlock, 0);
int bytesToCopy = ((temp.length - i * outLen) > outLen)
? outLen : (temp.length - i * outLen);
System.arraycopy(outputBlock, 0, temp, i * outLen, bytesToCopy);
++i;
}
XOR(temp, seed, temp, 0);
System.arraycopy(temp, 0, key, 0, key.length);
System.arraycopy(temp, key.length, v, 0, v.length);
}
private void CTR_DRBG_Reseed_algorithm(byte[] additionalInput)
{
byte[] seedMaterial = Arrays.concatenate(getEntropy(), additionalInput);
seedMaterial = Block_Cipher_df(seedMaterial, _seedLength);
CTR_DRBG_Update(seedMaterial, _Key, _V);
_reseedCounter = 1;
}
private void XOR(byte[] out, byte[] a, byte[] b, int bOff)
{
for (int i=0; i< out.length; i++)
{
out[i] = (byte)(a[i] ^ b[i+bOff]);
}
}
private void addOneTo(byte[] longer)
{
int carry = 1;
for (int i = 1; i <= longer.length; i++) // warning
{
int res = (longer[longer.length - i] & 0xff) + carry;
carry = (res > 0xff) ? 1 : 0;
longer[longer.length - i] = (byte)res;
}
}
private byte[] getEntropy()
{
byte[] entropy = _entropySource.getEntropy();
if (entropy.length < (_securityStrength + 7) / 8)
{
throw new IllegalStateException("Insufficient entropy provided by entropy source");
}
return entropy;
}
// -- Internal state migration ---
private static final byte[] K_BITS = Hex.decodeStrict("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
// 1. If (number_of_bits_to_return > max_number_of_bits), then return an
// ERROR_FLAG.
// 2. L = len (input_string)/8.
// 3. N = number_of_bits_to_return/8.
// Comment: L is the bitstring represention of
// the integer resulting from len (input_string)/8.
// L shall be represented as a 32-bit integer.
//
// Comment : N is the bitstring represention of
// the integer resulting from
// number_of_bits_to_return/8. N shall be
// represented as a 32-bit integer.
//
// 4. S = L || N || input_string || 0x80.
// 5. While (len (S) mod outlen)
// Comment : Pad S with zeros, if necessary.
// 0, S = S || 0x00.
//
// Comment : Compute the starting value.
// 6. temp = the Null string.
// 7. i = 0.
// 8. K = Leftmost keylen bits of 0x00010203...1D1E1F.
// 9. While len (temp) < keylen + outlen, do
//
// IV = i || 0outlen - len (i).
//
// 9.1
//
// temp = temp || BCC (K, (IV || S)).
//
// 9.2
//
// i = i + 1.
//
// 9.3
//
// Comment : i shall be represented as a 32-bit
// integer, i.e., len (i) = 32.
//
// Comment: The 32-bit integer represenation of
// i is padded with zeros to outlen bits.
//
// Comment: Compute the requested number of
// bits.
//
// 10. K = Leftmost keylen bits of temp.
//
// 11. X = Next outlen bits of temp.
//
// 12. temp = the Null string.
//
// 13. While len (temp) < number_of_bits_to_return, do
//
// 13.1 X = Block_Encrypt (K, X).
//
// 13.2 temp = temp || X.
//
// 14. requested_bits = Leftmost number_of_bits_to_return of temp.
//
// 15. Return SUCCESS and requested_bits.
private byte[] Block_Cipher_df(byte[] inputString, int bitLength)
{
int outLen = _engine.getBlockSize();
int L = inputString.length; // already in bytes
int N = bitLength / 8;
// 4 S = L || N || inputstring || 0x80
int sLen = 4 + 4 + L + 1;
int blockLen = ((sLen + outLen - 1) / outLen) * outLen;
byte[] S = new byte[blockLen];
copyIntToByteArray(S, L, 0);
copyIntToByteArray(S, N, 4);
System.arraycopy(inputString, 0, S, 8, L);
S[8 + L] = (byte)0x80;
// S already padded with zeros
byte[] temp = new byte[_keySizeInBits / 8 + outLen];
byte[] bccOut = new byte[outLen];
byte[] IV = new byte[outLen];
int i = 0;
byte[] K = new byte[_keySizeInBits / 8];
System.arraycopy(K_BITS, 0, K, 0, K.length);
while (i*outLen*8 < _keySizeInBits + outLen *8)
{
copyIntToByteArray(IV, i, 0);
BCC(bccOut, K, IV, S);
int bytesToCopy = ((temp.length - i * outLen) > outLen)
? outLen
: (temp.length - i * outLen);
System.arraycopy(bccOut, 0, temp, i * outLen, bytesToCopy);
++i;
}
byte[] X = new byte[outLen];
System.arraycopy(temp, 0, K, 0, K.length);
System.arraycopy(temp, K.length, X, 0, X.length);
temp = new byte[bitLength / 8];
i = 0;
_engine.init(true, new KeyParameter(expandKey(K)));
while (i * outLen < temp.length)
{
_engine.processBlock(X, 0, X, 0);
int bytesToCopy = ((temp.length - i * outLen) > outLen)
? outLen
: (temp.length - i * outLen);
System.arraycopy(X, 0, temp, i * outLen, bytesToCopy);
i++;
}
return temp;
}
/*
* 1. chaining_value = 0^outlen
* . Comment: Set the first chaining value to outlen zeros.
* 2. n = len (data)/outlen.
* 3. Starting with the leftmost bits of data, split the data into n blocks of outlen bits
* each, forming block(1) to block(n).
* 4. For i = 1 to n do
* 4.1 input_block = chaining_value ^ block(i) .
* 4.2 chaining_value = Block_Encrypt (Key, input_block).
* 5. output_block = chaining_value.
* 6. Return output_block.
*/
private void BCC(byte[] bccOut, byte[] k, byte[] iV, byte[] data)
{
int outlen = _engine.getBlockSize();
byte[] chainingValue = new byte[outlen]; // initial values = 0
int n = data.length / outlen;
byte[] inputBlock = new byte[outlen];
_engine.init(true, new KeyParameter(expandKey(k)));
_engine.processBlock(iV, 0, chainingValue, 0);
for (int i = 0; i < n; i++)
{
XOR(inputBlock, chainingValue, data, i*outlen);
_engine.processBlock(inputBlock, 0, chainingValue, 0);
}
System.arraycopy(chainingValue, 0, bccOut, 0, bccOut.length);
}
private void copyIntToByteArray(byte[] buf, int value, int offSet)
{
buf[offSet + 0] = ((byte)(value >> 24));
buf[offSet + 1] = ((byte)(value >> 16));
buf[offSet + 2] = ((byte)(value >> 8));
buf[offSet + 3] = ((byte)(value));
}
/**
* Return the block size (in bits) of the DRBG.
*
* @return the number of bits produced on each internal round of the DRBG.
*/
public int getBlockSize()
{
return _V.length * 8;
}
/**
* Populate a passed in array with random data.
*
* @param output output array for generated bits.
* @param additionalInput additional input to be added to the DRBG in this step.
* @param predictionResistant true if a reseed should be forced, false otherwise.
*
* @return number of bits generated, -1 if a reseed required.
*/
public int generate(byte[] output, byte[] additionalInput, boolean predictionResistant)
{
if (_isTDEA)
{
if (_reseedCounter > TDEA_RESEED_MAX)
{
return -1;
}
if (Utils.isTooLarge(output, TDEA_MAX_BITS_REQUEST / 8))
{
throw new IllegalArgumentException("Number of bits per request limited to " + TDEA_MAX_BITS_REQUEST);
}
}
else
{
if (_reseedCounter > AES_RESEED_MAX)
{
return -1;
}
if (Utils.isTooLarge(output, AES_MAX_BITS_REQUEST / 8))
{
throw new IllegalArgumentException("Number of bits per request limited to " + AES_MAX_BITS_REQUEST);
}
}
if (predictionResistant)
{
CTR_DRBG_Reseed_algorithm(additionalInput);
additionalInput = null;
}
if (additionalInput != null)
{
additionalInput = Block_Cipher_df(additionalInput, _seedLength);
CTR_DRBG_Update(additionalInput, _Key, _V);
}
else
{
additionalInput = new byte[_seedLength / 8];
}
byte[] out = new byte[_V.length];
_engine.init(true, new KeyParameter(expandKey(_Key)));
for (int i = 0; i <= output.length / out.length; i++)
{
int bytesToCopy = ((output.length - i * out.length) > out.length)
? out.length
: (output.length - i * _V.length);
if (bytesToCopy != 0)
{
addOneTo(_V);
_engine.processBlock(_V, 0, out, 0);
System.arraycopy(out, 0, output, i * out.length, bytesToCopy);
}
}
CTR_DRBG_Update(additionalInput, _Key, _V);
_reseedCounter++;
return output.length * 8;
}
/**
* Reseed the DRBG.
*
* @param additionalInput additional input to be added to the DRBG in this step.
*/
public void reseed(byte[] additionalInput)
{
CTR_DRBG_Reseed_algorithm(additionalInput);
}
private boolean isTDEA(BlockCipher cipher)
{
return cipher.getAlgorithmName().equals("DESede") || cipher.getAlgorithmName().equals("TDEA");
}
private int getMaxSecurityStrength(BlockCipher cipher, int keySizeInBits)
{
if (isTDEA(cipher) && keySizeInBits == 168)
{
return 112;
}
if (cipher.getAlgorithmName().equals("AES"))
{
return keySizeInBits;
}
return -1;
}
byte[] expandKey(byte[] key)
{
if (_isTDEA)
{
// expand key to 192 bits.
byte[] tmp = new byte[24];
padKey(key, 0, tmp, 0);
padKey(key, 7, tmp, 8);
padKey(key, 14, tmp, 16);
return tmp;
}
else
{
return key;
}
}
/**
* Pad out a key for TDEA, setting odd parity for each byte.
*
* @param keyMaster
* @param keyOff
* @param tmp
* @param tmpOff
*/
private void padKey(byte[] keyMaster, int keyOff, byte[] tmp, int tmpOff)
{
tmp[tmpOff + 0] = (byte)(keyMaster[keyOff + 0] & 0xfe);
tmp[tmpOff + 1] = (byte)((keyMaster[keyOff + 0] << 7) | ((keyMaster[keyOff + 1] & 0xfc) >>> 1));
tmp[tmpOff + 2] = (byte)((keyMaster[keyOff + 1] << 6) | ((keyMaster[keyOff + 2] & 0xf8) >>> 2));
tmp[tmpOff + 3] = (byte)((keyMaster[keyOff + 2] << 5) | ((keyMaster[keyOff + 3] & 0xf0) >>> 3));
tmp[tmpOff + 4] = (byte)((keyMaster[keyOff + 3] << 4) | ((keyMaster[keyOff + 4] & 0xe0) >>> 4));
tmp[tmpOff + 5] = (byte)((keyMaster[keyOff + 4] << 3) | ((keyMaster[keyOff + 5] & 0xc0) >>> 5));
tmp[tmpOff + 6] = (byte)((keyMaster[keyOff + 5] << 2) | ((keyMaster[keyOff + 6] & 0x80) >>> 6));
tmp[tmpOff + 7] = (byte)(keyMaster[keyOff + 6] << 1);
for (int i = tmpOff; i <= tmpOff + 7; i++)
{
int b = tmp[i];
tmp[i] = (byte)((b & 0xfe) |
((((b >> 1) ^
(b >> 2) ^
(b >> 3) ^
(b >> 4) ^
(b >> 5) ^
(b >> 6) ^
(b >> 7)) ^ 0x01) & 0x01));
}
}
}