blob: 63c7a827fb11b437f5da8c28381e5f838cab5cad [file] [log] [blame]
package org.bouncycastle.util.test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.Provider;
import java.security.SecureRandom;
import org.bouncycastle.util.Pack;
import org.bouncycastle.util.encoders.Hex;
/**
* A secure random that returns pre-seeded data to calls of nextBytes() or generateSeed().
*/
public class FixedSecureRandom
extends SecureRandom
{
private static java.math.BigInteger REGULAR = new java.math.BigInteger("01020304ffffffff0506070811111111", 16);
private static java.math.BigInteger ANDROID = new java.math.BigInteger("1111111105060708ffffffff01020304", 16);
private static java.math.BigInteger CLASSPATH = new java.math.BigInteger("3020104ffffffff05060708111111", 16);
private static final boolean isAndroidStyle;
private static final boolean isClasspathStyle;
private static final boolean isRegularStyle;
static
{
java.math.BigInteger check1 = new java.math.BigInteger(128, new RandomChecker());
java.math.BigInteger check2 = new java.math.BigInteger(120, new RandomChecker());
isAndroidStyle = check1.equals(ANDROID);
isRegularStyle = check1.equals(REGULAR);
isClasspathStyle = check2.equals(CLASSPATH);
}
private byte[] _data;
private int _index;
/**
* Base class for sources of fixed "Randomness"
*/
public static class Source
{
byte[] data;
Source(byte[] data)
{
this.data = data;
}
}
/**
* Data Source - in this case we just expect requests for byte arrays.
*/
public static class Data
extends Source
{
public Data(byte[] data)
{
super(data);
}
}
/**
* BigInteger Source - in this case we expect requests for data that will be used
* for BigIntegers. The FixedSecureRandom will attempt to compensate for platform differences here.
*/
public static class BigInteger
extends Source
{
public BigInteger(byte[] data)
{
super(data);
}
public BigInteger(int bitLength, byte[] data)
{
super(expandToBitLength(bitLength, data));
}
public BigInteger(String hexData)
{
this(Hex.decode(hexData));
}
public BigInteger(int bitLength, String hexData)
{
super(expandToBitLength(bitLength, Hex.decode(hexData)));
}
}
public FixedSecureRandom(byte[] value)
{
this(new Source[] { new Data(value) });
}
public FixedSecureRandom(
byte[][] values)
{
this(buildDataArray(values));
}
private static Data[] buildDataArray(byte[][] values)
{
Data[] res = new Data[values.length];
for (int i = 0; i != values.length; i++)
{
res[i] = new Data(values[i]);
}
return res;
}
public FixedSecureRandom(
Source[] sources)
{
super(null, new DummyProvider()); // to prevent recursion in provider creation
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
if (isRegularStyle)
{
if (isClasspathStyle)
{
for (int i = 0; i != sources.length; i++)
{
try
{
if (sources[i] instanceof BigInteger)
{
byte[] data = sources[i].data;
int len = data.length - (data.length % 4);
for (int w = data.length - len - 1; w >= 0; w--)
{
bOut.write(data[w]);
}
for (int w = data.length - len; w < data.length; w += 4)
{
bOut.write(data, w, 4);
}
}
else
{
bOut.write(sources[i].data);
}
}
catch (IOException e)
{
throw new IllegalArgumentException("can't save value source.");
}
}
}
else
{
for (int i = 0; i != sources.length; i++)
{
try
{
bOut.write(sources[i].data);
}
catch (IOException e)
{
throw new IllegalArgumentException("can't save value source.");
}
}
}
}
else if (isAndroidStyle)
{
for (int i = 0; i != sources.length; i++)
{
try
{
if (sources[i] instanceof BigInteger)
{
byte[] data = sources[i].data;
int len = data.length - (data.length % 4);
for (int w = 0; w < len; w += 4)
{
bOut.write(data, data.length - (w + 4), 4);
}
if (data.length - len != 0)
{
for (int w = 0; w != 4 - (data.length - len); w++)
{
bOut.write(0);
}
}
for (int w = 0; w != data.length - len; w++)
{
bOut.write(data[len + w]);
}
}
else
{
bOut.write(sources[i].data);
}
}
catch (IOException e)
{
throw new IllegalArgumentException("can't save value source.");
}
}
}
else
{
throw new IllegalStateException("Unrecognized BigInteger implementation");
}
_data = bOut.toByteArray();
}
public void nextBytes(byte[] bytes)
{
System.arraycopy(_data, _index, bytes, 0, bytes.length);
_index += bytes.length;
}
public byte[] generateSeed(int numBytes)
{
byte[] bytes = new byte[numBytes];
this.nextBytes(bytes);
return bytes;
}
//
// classpath's implementation of SecureRandom doesn't currently go back to nextBytes
// when next is called. We can't override next as it's a final method.
//
public int nextInt()
{
int val = 0;
val |= nextValue() << 24;
val |= nextValue() << 16;
val |= nextValue() << 8;
val |= nextValue();
return val;
}
//
// classpath's implementation of SecureRandom doesn't currently go back to nextBytes
// when next is called. We can't override next as it's a final method.
//
public long nextLong()
{
long val = 0;
val |= (long)nextValue() << 56;
val |= (long)nextValue() << 48;
val |= (long)nextValue() << 40;
val |= (long)nextValue() << 32;
val |= (long)nextValue() << 24;
val |= (long)nextValue() << 16;
val |= (long)nextValue() << 8;
val |= (long)nextValue();
return val;
}
public boolean isExhausted()
{
return _index == _data.length;
}
private int nextValue()
{
return _data[_index++] & 0xff;
}
private static class RandomChecker
extends SecureRandom
{
RandomChecker()
{
super(null, new DummyProvider()); // to prevent recursion in provider creation
}
byte[] data = Hex.decode("01020304ffffffff0506070811111111");
int index = 0;
public void nextBytes(byte[] bytes)
{
System.arraycopy(data, index, bytes, 0, bytes.length);
index += bytes.length;
}
}
private static byte[] expandToBitLength(int bitLength, byte[] v)
{
if ((bitLength + 7) / 8 > v.length)
{
byte[] tmp = new byte[(bitLength + 7) / 8];
System.arraycopy(v, 0, tmp, tmp.length - v.length, v.length);
if (isAndroidStyle)
{
if (bitLength % 8 != 0)
{
int i = Pack.bigEndianToInt(tmp, 0);
Pack.intToBigEndian(i << (8 - (bitLength % 8)), tmp, 0);
}
}
return tmp;
}
else
{
if (isAndroidStyle && bitLength < (v.length * 8))
{
if (bitLength % 8 != 0)
{
int i = Pack.bigEndianToInt(v, 0);
Pack.intToBigEndian(i << (8 - (bitLength % 8)), v, 0);
}
}
}
return v;
}
private static class DummyProvider
extends Provider
{
DummyProvider()
{
super("BCFIPS_FIXED_RNG", 1.0, "BCFIPS Fixed Secure Random Provider");
}
}
}