OpenSslJnaCryptoRandom.java

  1.  /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one
  3.  * or more contributor license agreements.  See the NOTICE file
  4.  * distributed with this work for additional information
  5.  * regarding copyright ownership.  The ASF licenses this file
  6.  * to you under the Apache License, Version 2.0 (the
  7.  * "License"); you may not use this file except in compliance
  8.  * with the License.  You may obtain a copy of the License at
  9.  *
  10.  *     http://www.apache.org/licenses/LICENSE-2.0
  11.  *
  12.  * Unless required by applicable law or agreed to in writing, software
  13.  * distributed under the License is distributed on an "AS IS" BASIS,
  14.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15.  * See the License for the specific language governing permissions and
  16.  * limitations under the License.
  17.  */
  18. package org.apache.commons.crypto.jna;

  19. import java.nio.ByteBuffer;
  20. import java.security.GeneralSecurityException;
  21. import java.security.NoSuchAlgorithmException;
  22. import java.util.Properties;

  23. import org.apache.commons.crypto.random.CryptoRandom;

  24. import com.sun.jna.NativeLong;
  25. import com.sun.jna.ptr.PointerByReference;

  26. /**
  27.  * <p>
  28.  * OpenSSL secure random using JNA. This implementation is thread-safe.
  29.  * </p>
  30.  *
  31.  * <p>
  32.  * If using an Intel chipset with RDRAND, the high-performance hardware random
  33.  * number generator will be used and it's much faster than SecureRandom. If
  34.  * RDRAND is unavailable, default OpenSSL secure random generator will be used.
  35.  * It's still faster and can generate strong random bytes.
  36.  * </p>
  37.  *
  38.  * @see <a href="https://wiki.openssl.org/index.php/Random_Numbers">
  39.  *      https://wiki.openssl.org/index.php/Random_Numbers</a>
  40.  * @see <a href="http://en.wikipedia.org/wiki/RdRand">
  41.  *      http://en.wikipedia.org/wiki/RdRand</a>
  42.  */
  43. final class OpenSslJnaCryptoRandom implements CryptoRandom {

  44.     private static final int ENGINE_METHOD_RAND = 0x0008;

  45.     private final boolean rdrandEnabled;
  46.     private final transient PointerByReference rdrandEngine;

  47.     /**
  48.      * Constructs a {@link OpenSslJnaCryptoRandom}.
  49.      *
  50.      * @param props the configuration properties (not used)
  51.      * @throws GeneralSecurityException  if could not enable JNA access
  52.      */
  53.     public OpenSslJnaCryptoRandom(final Properties props) //NOPMD
  54.             throws GeneralSecurityException {
  55.         if (!OpenSslJna.isEnabled()) {
  56.             throw new GeneralSecurityException("Could not enable JNA access", OpenSslJna.initialisationError());
  57.         }

  58.         boolean rdrandLoaded = false;
  59.         try {
  60.             OpenSslNativeJna.ENGINE_load_rdrand();
  61.             rdrandEngine = OpenSslNativeJna.ENGINE_by_id("rdrand");
  62.             if (rdrandEngine != null) {
  63.                 final int rc = OpenSslNativeJna.ENGINE_init(rdrandEngine);

  64.                 if (rc != 0) {
  65.                     final int rc2 = OpenSslNativeJna.ENGINE_set_default(rdrandEngine, ENGINE_METHOD_RAND);
  66.                     if (rc2 != 0) {
  67.                         rdrandLoaded = true;
  68.                     }
  69.                 }
  70.             }

  71.         } catch (final Exception e) {
  72.             throw new NoSuchAlgorithmException();
  73.         }

  74.         rdrandEnabled = rdrandLoaded;

  75.         if (!rdrandLoaded) {
  76.             closeRdrandEngine(false);
  77.         }
  78.     }

  79.     /**
  80.      * Overrides {@link java.lang.AutoCloseable#close()}. Closes OpenSSL context
  81.      * if native enabled.
  82.      */
  83.     @Override
  84.     public void close() {
  85.         closeRdrandEngine(true);
  86.         OpenSslNativeJna.ENGINE_cleanup();

  87.         //cleanup locks
  88.         //OpenSslNativeJna.CRYPTO_set_locking_callback(null);
  89.         //LOCK.unlock();
  90.     }

  91.     /**
  92.      * Closes the rdrand engine.
  93.      * @param closing true when called while closing.
  94.      */
  95.     private void closeRdrandEngine(final boolean closing) {

  96.         if (rdrandEngine != null) {
  97.             throwOnError(OpenSslNativeJna.ENGINE_finish(rdrandEngine), closing);
  98.             throwOnError(OpenSslNativeJna.ENGINE_free(rdrandEngine), closing);
  99.         }
  100.     }

  101.     /**
  102.      * Checks if rdrand engine is used to retrieve random bytes
  103.      *
  104.      * @return true if rdrand is used, false if default engine is used
  105.      */
  106.     public boolean isRdrandEnabled() {
  107.         return rdrandEnabled;
  108.     }

  109.     /**
  110.      * Generates a user-specified number of random bytes. It's thread-safe.
  111.      *
  112.      * @param bytes the array to be filled in with random bytes.
  113.      */
  114.     @Override
  115.     public void nextBytes(final byte[] bytes) {

  116.         synchronized (OpenSslJnaCryptoRandom.class) {
  117.             // this method is synchronized for now
  118.             // to support multithreading https://wiki.openssl.org/index.php/Manual:Threads(3) needs to be done

  119.             if (rdrandEnabled && OpenSslNativeJna.RAND_get_rand_method().equals(OpenSslNativeJna.RAND_SSLeay())) {
  120.                 close();
  121.                 throw new IllegalStateException("rdrand should be used but default is detected");
  122.             }

  123.             final int byteLength = bytes.length;
  124.             final ByteBuffer buf = ByteBuffer.allocateDirect(byteLength);
  125.             throwOnError(OpenSslNativeJna.RAND_bytes(buf, byteLength), false);
  126.             buf.rewind();
  127.             buf.get(bytes, 0, byteLength);
  128.         }
  129.     }

  130.     /**
  131.      * @param retVal the result value of error.
  132.      * @param closing true when called while closing.
  133.      */
  134.     private void throwOnError(final int retVal, final boolean closing) {
  135.         if (retVal != 1) {
  136.             final NativeLong err = OpenSslNativeJna.ERR_peek_error();
  137.             final String errdesc = OpenSslNativeJna.ERR_error_string(err, null);
  138.             if (!closing) {
  139.                 close();
  140.             }
  141.             throw new IllegalStateException("return code " + retVal + " from OpenSSL. Err code is " + err + ": " + errdesc);
  142.         }
  143.     }
  144. }