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