package com.oblivm.backend.rand;
import java.security.SecureRandom;
import java.security.SecureRandomSpi;
/**
* This class defines the Service Provider for the ISAAC algorithm.
*
* @author Daniel Berlin
*/
public class ISAACEngine extends SecureRandomSpi {
/**
*
*/
private static final long serialVersionUID = 1L;
private ISAACAlgorithm isaac;
private byte[] remainder;
private int remCount = 0;
/**
* Creates a new instance and seeds it with random data obtained from
* java.security.SecureRandom
's getSeed()
method.
*/
public ISAACEngine() {
byte[] temp = new byte[1024];// SecureRandom.getSeed (1024);
new SecureRandom().nextBytes(temp);
this.isaac = new ISAACAlgorithm(ISAACEngine.packToIntArray(temp));
}
/**
* Returns the given number of seed bytes computed using the ISAAC
* algorithm.
* It just calls engineNextBytes()
internally.
* This call may be used to seed other random number generators.
*
* @param numBytes
* The number of seed bytes to generate.
* @return The seed bytes.
*/
public byte[] engineGenerateSeed(int numBytes) {
byte[] seed = new byte[numBytes];
this.engineNextBytes(seed);
return (seed);
}
/**
* Generates a user-specified number of random bytes.
*
* @param bytes
* The array to fill with random bytes.
*/
public void engineNextBytes(byte[] bytes) {
int index = 0;
int todo;
byte[] output = this.remainder;
// First use remainder from last time
int rC = this.remCount;
if (rC > 0) {
todo = (bytes.length - index) < (4 - rC) ? (bytes.length - index) : (4 - rC);
for (int i = 0; i < todo; i++, rC++)
bytes[i] = output[rC];
this.remCount += todo;
index += todo;
}
// If we need more bytes, get them
while (index < bytes.length) {
output = ISAACEngine.toByteArray(this.isaac.nextInt());
todo = (bytes.length - index) > 4 ? 4 : (bytes.length - index);
for (int i = 0; i < todo; i++, index++)
bytes[index] = output[i];
this.remCount += todo;
}
// Store remainder for next time
this.remainder = output;
this.remCount %= 4;
}
/**
* Reseeds this random object. The given seed supplements, rather than
* replaces, the existing seed.
*
* @param seed
* The seed.
*/
public void engineSetSeed(byte[] seed) {
this.isaac.supplementSeed(ISAACEngine.packToIntArray(seed));
}
// ====================================
// ===== private "helper" methods =====
// ====================================
private static final int[] mask = new int[] { 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF };
/**
* Returns a byte array containing the two's-complement representation of
* the integer.
* The byte array will be in big-endian byte-order with a fixes length of 4
* (the least significant byte is in the 4th element).
*
* Example:
* toByteArray(258)
returns { 0, 0, 1, 2 },
* BigInteger.valueOf(258).toByteArray()
returns { 1, 2 }.
*
* @param intValue
* The integer to be converted.
* @return The byte array of length 4.
*/
private static byte[] toByteArray(final int intValue) {
int byteNum = (40 - numberOfLeadingZeros(intValue < 0 ? ~intValue : intValue)) / 8;
byte[] byteArray = new byte[4];
for (int i = 0; i < byteNum; i++)
byteArray[3 - i] = (byte) (intValue >>> (i * 8));
return (byteArray);
}
/**
* Returns the number of zero bits preceding the highest-order ("leftmost")
* one-bit in the two's complement binary representation of the specified
* integer value.
*
* @return The number of zero bits preceding the highest-order one-bit in
* the two's complement binary representation of the specified
* integer value, or 32 if the value is equal to zero.
*/
private static int numberOfLeadingZeros(int intValue) {
if (intValue == 0)
return (32);
int num = 1;
for (int i = 16, j = 16; j > 1; i += j) {
if (intValue >>> i == 0) {
num += j;
intValue <<= j;
}
j /= 2;
}
num -= intValue >>> 31;
return (num);
}
/**
* Creates an array of integers and "packs" the bytes from the byte array
* into it.
* This method returns an integer array of length zero if ba
is
* null
.
*
* Example:
* packToIntArray(new byte[] { 0x1, 0x2, 0x3, 0x4, 0x5 })
* returns an integer array
* containing the values 0x01020304
and 0x00000005
* .
*
* @param ba
* The byte array, may be null
.
* @return An array of integers containing the "packed" byte(s).
*/
private static int[] packToIntArray(final byte[] ba) {
if (ba == null)
return (new int[0]);
int[] ia = new int[(ba.length + 3) / 4];
for (int i = 0; i < ia.length; i++) {
int restLen = Math.min(4, ba.length - i * 4);
for (int b = 0; b < restLen; b++)
ia[i] |= (ba[b + i * 4] << ((restLen - b - 1) * 8)) & mask[b + 4 - restLen];
}
return (ia);
}
}