View Javadoc
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  
20  import java.nio.ByteBuffer;
21  import java.security.GeneralSecurityException;
22  import java.security.NoSuchAlgorithmException;
23  import java.util.Properties;
24  import java.util.Random;
25  
26  import org.apache.commons.crypto.random.CryptoRandom;
27  import org.apache.commons.crypto.utils.Utils;
28  
29  import com.sun.jna.NativeLong;
30  import com.sun.jna.ptr.PointerByReference;
31  
32  /**
33   * <p>
34   * OpenSSL secure random using JNA. This implementation is thread-safe.
35   * </p>
36   *
37   * <p>
38   * If using an Intel chipset with RDRAND, the high-performance hardware random
39   * number generator will be used and it's much faster than SecureRandom. If
40   * RDRAND is unavailable, default OpenSSL secure random generator will be used.
41   * It's still faster and can generate strong random bytes.
42   * </p>
43   *
44   * @see <a href="https://wiki.openssl.org/index.php/Random_Numbers">
45   *      https://wiki.openssl.org/index.php/Random_Numbers</a>
46   * @see <a href="http://en.wikipedia.org/wiki/RdRand">
47   *      http://en.wikipedia.org/wiki/RdRand</a>;
48   */
49  class OpenSslJnaCryptoRandom extends Random implements CryptoRandom {
50      private static final long serialVersionUID = -7128193502768749585L;
51      private final boolean rdrandEnabled;
52      private PointerByReference rdrandEngine;
53  
54      /**
55       * Constructs a {@link OpenSslJnaCryptoRandom}.
56       *
57       * @param props the configuration properties (not used)
58       * @throws GeneralSecurityException  if could not enable JNA access
59       */
60      public OpenSslJnaCryptoRandom(Properties props) //NOPMD
61              throws GeneralSecurityException {
62          if (!OpenSslJna.isEnabled()) {
63              throw new GeneralSecurityException("Could not enable JNA access", OpenSslJna.initialisationError());
64          }
65  
66          boolean rdrandLoaded = false;
67          try {
68              OpenSslNativeJna.ENGINE_load_rdrand();
69              rdrandEngine = OpenSslNativeJna.ENGINE_by_id("rdrand");
70              int ENGINE_METHOD_RAND = 0x0008;
71              if(rdrandEngine != null) {
72                  int rc = OpenSslNativeJna.ENGINE_init(rdrandEngine);
73                  
74                  if(rc != 0) {
75                      int rc2 = OpenSslNativeJna.ENGINE_set_default(rdrandEngine, ENGINE_METHOD_RAND);
76                      if(rc2 != 0) {
77                          rdrandLoaded = true;
78                      }
79                  }
80              }
81              
82          } catch (Exception e) {
83              throw new NoSuchAlgorithmException();
84          }
85          
86          rdrandEnabled = rdrandLoaded;
87          
88          if(!rdrandLoaded) {
89              closeRdrandEngine();
90          }
91      }
92  
93      /**
94       * Generates a user-specified number of random bytes. It's thread-safe.
95       *
96       * @param bytes the array to be filled in with random bytes.
97       */
98      @Override
99      public void nextBytes(byte[] bytes) {
100         
101         synchronized (OpenSslJnaCryptoRandom.class) {
102             //this method is synchronized for now
103             //to support multithreading https://wiki.openssl.org/index.php/Manual:Threads(3) needs to be done
104             
105             if(rdrandEnabled && OpenSslNativeJna.RAND_get_rand_method().equals(OpenSslNativeJna.RAND_SSLeay())) {
106                 close();
107                 throw new RuntimeException("rdrand should be used but default is detected");
108             }
109             
110             ByteBuffer buf = ByteBuffer.allocateDirect(bytes.length);
111             int retVal = OpenSslNativeJna.RAND_bytes(buf, bytes.length);
112             throwOnError(retVal);
113             buf.rewind();
114             buf.get(bytes,0, bytes.length);
115         }
116     }
117 
118     /**
119      * Overrides {@link OpenSslJnaCryptoRandom}. For {@link OpenSslJnaCryptoRandom},
120      * we don't need to set seed.
121      *
122      * @param seed the initial seed.
123      */
124     @Override
125     public void setSeed(long seed) {
126         // Self-seeding.
127     }
128 
129     /**
130      * Overrides Random#next(). Generates an integer containing the
131      * user-specified number of random bits(right justified, with leading
132      * zeros).
133      *
134      * @param numBits number of random bits to be generated, where 0
135      *        {@literal <=} <code>numBits</code> {@literal <=} 32.
136      * @return int an <code>int</code> containing the user-specified number of
137      *         random bits (right justified, with leading zeros).
138      */
139     @Override
140     final protected int next(int numBits) {
141         Utils.checkArgument(numBits >= 0 && numBits <= 32);
142         int numBytes = (numBits + 7) / 8;
143         byte b[] = new byte[numBytes];
144         int next = 0;
145 
146         nextBytes(b);
147         for (int i = 0; i < numBytes; i++) {
148             next = (next << 8) + (b[i] & 0xFF);
149         }
150 
151         return next >>> (numBytes * 8 - numBits);
152     }
153 
154     /**
155      * Overrides {@link java.lang.AutoCloseable#close()}. Closes OpenSSL context
156      * if native enabled.
157      */
158     @Override
159     public void close() {
160         closeRdrandEngine();
161         OpenSslNativeJna.ENGINE_cleanup();
162         
163         //cleanup locks
164         //OpenSslNativeJna.CRYPTO_set_locking_callback(null);
165         //LOCK.unlock();
166     }
167 
168     /**
169      * Closes the rdrand engine.
170      */
171     private void closeRdrandEngine() {
172         
173         if(rdrandEngine != null) {
174             OpenSslNativeJna.ENGINE_finish(rdrandEngine);
175             OpenSslNativeJna.ENGINE_free(rdrandEngine);
176         }
177     }
178 
179     /**
180      * Checks if rdrand engine is used to retrieve random bytes
181      * 
182      * @return true if rdrand is used, false if default engine is used
183      */
184     public boolean isRdrandEnabled() {
185         return rdrandEnabled;
186     }
187 
188     /**
189      * @param retVal the result value of error.
190      */
191     private void throwOnError(int retVal) {  
192         if (retVal != 1) {
193             NativeLong err = OpenSslNativeJna.ERR_peek_error();
194             String errdesc = OpenSslNativeJna.ERR_error_string(err, null);
195             close();
196             throw new RuntimeException("return code " + retVal + " from OpenSSL. Err code is " + err + ": " + errdesc);
197         }
198     }
199 }