GordianRandomSource.java
/*
* GordianKnot: Security Suite
* Copyright 2012-2026. Tony Washer
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package io.github.tonywasher.joceanus.gordianknot.impl.core.base;
import io.github.tonywasher.joceanus.gordianknot.api.base.GordianException;
import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianCryptoException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
/**
* SecureRandom source.
*/
public class GordianRandomSource {
/**
* The number of entropy bits required.
*/
private static final int NUM_ENTROPY_BITS_REQUIRED = 256;
/**
* The number of entropy bits required.
*/
private static final int NUM_ENTROPY_BYTES_REQUIRED = NUM_ENTROPY_BITS_REQUIRED / Byte.SIZE;
/**
* The Initial strongRandom.
*/
private static SecureRandom theStrongEntropy;
/**
* The random source.
*/
private SecureRandom theRandom;
/**
* Constructor.
*
* @throws GordianException on error
*/
public GordianRandomSource() throws GordianException {
theRandom = getStrongRandom();
}
/**
* Obtain the random.
*
* @return the random
*/
public SecureRandom getRandom() {
return theRandom;
}
/**
* Set the random.
*
* @param pRandom the random
*/
public void setRandom(final SecureRandom pRandom) {
/* Seed the Random */
pRandom.setSeed(defaultPersonalisation());
/* Store the new random */
theRandom = pRandom;
}
/**
* Access the strong Secure Random.
*
* @return the secure random
* @throws GordianException on error
*/
public static SecureRandom getStrongRandom() throws GordianException {
/* Return the entropy if it has been created */
SecureRandom myStrong = theStrongEntropy;
if (myStrong != null) {
return myStrong;
}
/* Synchronize the attempts */
synchronized (GordianRandomSource.class) {
/* If we have not yet created the strong entropy */
myStrong = theStrongEntropy;
if (myStrong == null) {
/* Protect against exceptions */
try {
/* Handle differently for Windows and *nix */
final boolean isWindows = System.getProperty("os.name").startsWith("Windows");
myStrong = isWindows
? SecureRandom.getInstanceStrong()
: SecureRandom.getInstance("NativePRNGNonBlocking");
/* Seed the Entropy */
myStrong.setSeed(createPersonalisation(null));
theStrongEntropy = myStrong;
} catch (NoSuchAlgorithmException e) {
throw new GordianCryptoException("No strong random", e);
}
}
return myStrong;
}
}
/**
* Create a personalisation Vector.
*
* @return initVector.
*/
public byte[] defaultPersonalisation() {
/* Obtain some underlying entropy */
final byte[] mySeed = new byte[NUM_ENTROPY_BYTES_REQUIRED];
theRandom.nextBytes(mySeed);
/* Create the personalisation */
return createPersonalisation(mySeed);
}
/**
* Create a personalisation Vector.
*
* @param pSeed the seed (or null)
* @return initVector.
*/
private static byte[] createPersonalisation(final byte[] pSeed) {
/* Create the source arrays */
final byte[] myThread = GordianDataConverter.longToByteArray(Thread.currentThread().threadId());
final byte[] myTime = GordianDataConverter.longToByteArray(System.currentTimeMillis());
final byte[] myNanos = GordianDataConverter.longToByteArray(System.nanoTime());
/* Create the final initVector */
int myLen = myThread.length + myTime.length + myNanos.length;
if (pSeed != null) {
myLen += pSeed.length;
}
final byte[] myVector = new byte[myLen];
/* Build the vector */
System.arraycopy(myThread, 0, myVector, 0, myThread.length);
System.arraycopy(myTime, 0, myVector, myThread.length, myTime.length);
System.arraycopy(myNanos, 0, myVector, myThread.length + myTime.length, myNanos.length);
if (pSeed != null) {
System.arraycopy(pSeed, 0, myVector, myThread.length + myTime.length + myNanos.length, pSeed.length);
}
/* return it */
return myVector;
}
}