GordianSecureRandom.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.random;
import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianSeededRandom;
import org.bouncycastle.crypto.prng.EntropySource;
import org.bouncycastle.crypto.prng.EntropyUtil;
import java.security.SecureRandom;
/**
* SecureRandom wrapper class.
*/
public class GordianSecureRandom
extends SecureRandom
implements GordianSeededRandom {
/**
* Serial Id.
*/
private static final long serialVersionUID = -6422187120154720941L;
/**
* The Basic Secure Random instance.
*/
private final SecureRandom theRandom;
/**
* The DRBG generator.
*/
private final transient GordianDRBGenerator theGenerator;
/**
* The DRBG provider.
*/
private final transient EntropySource theEntropy;
/**
* Is this instance prediction resistant?
*/
private final boolean predictionResistant;
/**
* The pre-calculated buffer.
*/
private final byte[] theBuffer;
/**
* Bytes available in buffer.
*/
private int bytesAvailable;
/**
* Constructor.
*
* @param pGenerator the random generator
* @param pRandom the secure random instance
* @param pEntropy the entropy source
* @param isPredictionResistant true/false
*/
GordianSecureRandom(final GordianDRBGenerator pGenerator,
final SecureRandom pRandom,
final EntropySource pEntropy,
final boolean isPredictionResistant) {
/* Store parameters */
theGenerator = pGenerator;
theRandom = pRandom;
theEntropy = pEntropy;
predictionResistant = isPredictionResistant;
theBuffer = new byte[pGenerator.getBlockSize()];
}
@Override
public SecureRandom getRandom() {
return this;
}
@Override
public void setSeed(final byte[] seed) {
synchronized (this) {
/* Pass the call on */
theRandom.setSeed(seed);
bytesAvailable = 0;
}
}
@Override
public void setSeed(final long seed) {
synchronized (this) {
if (theRandom != null) {
theRandom.setSeed(seed);
bytesAvailable = 0;
}
}
}
@Override
public void nextBytes(final byte[] bytes) {
synchronized (this) {
/* Fill buffer directly if we are prediction resistant */
if (predictionResistant) {
fillBuffer(bytes);
return;
}
/* Determine how many bytes are needed */
int bytesNeeded = bytes.length;
int bytesBuilt = 0;
/* If we have bytes available */
if (bytesAvailable > 0) {
/* Fulfil the request from the buffer as much as possible */
final int bytesToTransfer = Math.min(bytesNeeded, bytesAvailable);
System.arraycopy(theBuffer, theBuffer.length - bytesAvailable, bytes, 0, bytesToTransfer);
bytesAvailable -= bytesToTransfer;
bytesNeeded -= bytesToTransfer;
bytesBuilt += bytesToTransfer;
}
/* Loop to fulfil remaining bytes */
while (bytesNeeded > 0) {
/* Fill the buffer again */
nextBuffer();
/* Fulfil the request from the buffer as much as possible */
final int bytesToTransfer = Math.min(bytesNeeded, bytesAvailable);
System.arraycopy(theBuffer, 0, bytes, bytesBuilt, bytesToTransfer);
bytesAvailable -= bytesToTransfer;
bytesNeeded -= bytesToTransfer;
bytesBuilt += bytesToTransfer;
}
}
}
/**
* Next buffer of random data.
*/
private void nextBuffer() {
/* Generate, checking for reSeed request */
if (theGenerator.generate(theBuffer, null, predictionResistant) < 0) {
/* ReSeed and regenerate */
theGenerator.reseed(null);
theGenerator.generate(theBuffer, null, predictionResistant);
}
bytesAvailable = theBuffer.length;
}
/**
* Next buffer of random data.
*
* @param pBuffer the buffer to fill
*/
private void fillBuffer(final byte[] pBuffer) {
/* Generate, checking for reSeed request */
if (theGenerator.generate(pBuffer, null, predictionResistant) < 0) {
/* ReSeed and regenerate */
theGenerator.reseed(null);
theGenerator.generate(pBuffer, null, predictionResistant);
}
bytesAvailable = 0;
}
@Override
public byte[] generateSeed(final int numBytes) {
return EntropyUtil.generateSeed(theEntropy, numBytes);
}
@Override
public String getAlgorithm() {
return theGenerator.getAlgorithm();
}
@Override
public String toString() {
return getAlgorithm();
}
@Override
public void reseed(final byte[] pXtraInput) {
synchronized (this) {
theGenerator.reseed(pXtraInput);
bytesAvailable = 0;
}
}
}