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); // 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); // 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; } }