/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 com.intel.diceros.provider.securerandom;
import java.nio.ByteBuffer;
import java.security.SecureRandomSpi;
/**
* This class implements the SecureRandom based on DRNG(IntelĀ® Digital Random
* Number Generator). When the underlying DRNG is unavailable, will use the
* SecureRandom provided by the jre or jdk.
*/
public class SecureRandom {
public static final class DRNG extends SecureRandomSpi {
private static final long serialVersionUID = 6163865817555691868L;
private static final int BUFFER_SIZE = 1024;
private static boolean drngAvailable = false;
private java.security.SecureRandom randomDefault = null;
static {
try {
System.loadLibrary("diceros");
boolean ret = drngInit();
if (ret) {
drngAvailable = true;
} else {
drngAvailable = false;
}
} catch (UnsatisfiedLinkError e) {
drngAvailable = false;
}
}
public DRNG() {
randomDefault = new java.security.SecureRandom();
}
/**
* The buffer of random data. PreAllocate some data will improve the
* performance.
*/
private static final ThreadLocal<ByteBuffer> bufferThreadLocal = new ThreadLocal<ByteBuffer>() {
@Override
protected ByteBuffer initialValue() {
ByteBuffer directBuffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
directBuffer.position(BUFFER_SIZE);
return directBuffer;
}
};
private static final ThreadLocal<Boolean> supportJniAccessDirectBuffer = new ThreadLocal<Boolean>() {
@Override
protected Boolean initialValue() {
return new Boolean(true);
}
};
/**
* Fill the array <code>bytes</code> with random bytes directly generated by
* the underlying DRNG hardware.
*
* @param bytes the array to be filled in with random bytes
*/
private void nextBytesNoBuffer(byte[] bytes) {
if (!drngAvailable || !drngRandBytes(bytes)) {
randomDefault.nextBytes(bytes);
}
}
/**
* Fill the local buffer with random bytes generated by the default
* SecureRandom provided by the jre or jdk.
*/
private void fillBufferFromParent() {
if (bufferThreadLocal.get().hasArray()) {
randomDefault.nextBytes(bufferThreadLocal.get().array());
} else {
byte[] byteArray = new byte[BUFFER_SIZE];
randomDefault.nextBytes(byteArray);
bufferThreadLocal.get().position(0);
bufferThreadLocal.get().put(byteArray);
}
bufferThreadLocal.get().position(0);
}
/**
* Fill the local buffer (byte array) with random bytes generated by the underlying DRNG
* hardware.
*/
private void fillBufferFromDrngNonDirect() {
if (bufferThreadLocal.get().hasArray()) {
if (!drngRandBytes(bufferThreadLocal.get().array())) {
randomDefault.nextBytes(bufferThreadLocal.get().array());
}
} else {
byte[] byteArray = new byte[BUFFER_SIZE];
if (!drngRandBytes(byteArray)) {
randomDefault.nextBytes(byteArray);
}
bufferThreadLocal.get().position(0);
bufferThreadLocal.get().put(byteArray);
}
bufferThreadLocal.get().position(0);
}
/**
* Fill the local buffer (direct byte buffer) with random bytes generated by the underlying DRNG
* hardware.
*
* @return the error code
*/
private int fillBufferFromDrngDirect() {
int ret = drngRandBytes(bufferThreadLocal.get());
if (ret == 0) {
bufferThreadLocal.get().position(0);
}
return ret;
}
/**
* Fill the local buffer with random bytes generated by the underlying DRNG
* hardware if it's available. Otherwise, fill the local buffer with random
* bytes generated by the default SecureRandom provided by the jre or jdk.
*/
private void fillBuffer() {
if (drngAvailable) {
if (supportJniAccessDirectBuffer.get()) {
int ret = fillBufferFromDrngDirect();
if (ret == -1) {
fillBufferFromParent();
} else if (ret == -2) {
supportJniAccessDirectBuffer.set(false);
fillBufferFromDrngNonDirect();
}
} else {
fillBufferFromDrngNonDirect();
}
} else {
fillBufferFromParent();
}
}
/**
* Fill the array <code>bytes</code> with random bytes from local buffer.
*
* @param bytes the array to be filled in with random bytes
*/
private void nextBytesFromBuffer(byte[] bytes) {
if (bytes.length + bufferThreadLocal.get().position() > BUFFER_SIZE)
fillBuffer();
bufferThreadLocal.get().get(bytes);
}
@Override
protected void engineSetSeed(byte[] seed) {
randomDefault.setSeed(seed);
}
@Override
protected void engineNextBytes(byte[] bytes) {
if (bytes.length >= BUFFER_SIZE) {
nextBytesNoBuffer(bytes);
} else {
nextBytesFromBuffer(bytes);
}
}
@Override
protected byte[] engineGenerateSeed(int numBytes) {
return randomDefault.generateSeed(numBytes);
}
private static native boolean drngInit();
private native boolean drngRandBytes(byte[] buffer);
private native int drngRandBytes(ByteBuffer directBuffer);
}
}