1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
25 import org.apache.commons.crypto.random.CryptoRandom;
26
27 import com.sun.jna.NativeLong;
28 import com.sun.jna.ptr.PointerByReference;
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 final class OpenSslJnaCryptoRandom implements CryptoRandom {
48
49 private static final int ENGINE_METHOD_RAND = 0x0008;
50
51 private final boolean rdrandEnabled;
52 private final transient PointerByReference rdrandEngine;
53
54
55
56
57
58
59
60 public OpenSslJnaCryptoRandom(final Properties props)
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 if (rdrandEngine != null) {
71 final int rc = OpenSslNativeJna.ENGINE_init(rdrandEngine);
72
73 if (rc != 0) {
74 final int rc2 = OpenSslNativeJna.ENGINE_set_default(rdrandEngine, ENGINE_METHOD_RAND);
75 if (rc2 != 0) {
76 rdrandLoaded = true;
77 }
78 }
79 }
80
81 } catch (final Exception e) {
82 throw new NoSuchAlgorithmException();
83 }
84
85 rdrandEnabled = rdrandLoaded;
86
87 if (!rdrandLoaded) {
88 closeRdrandEngine(false);
89 }
90 }
91
92
93
94
95
96 @Override
97 public void close() {
98 closeRdrandEngine(true);
99 OpenSslNativeJna.ENGINE_cleanup();
100
101
102
103
104 }
105
106
107
108
109
110 private void closeRdrandEngine(final boolean closing) {
111
112 if (rdrandEngine != null) {
113 throwOnError(OpenSslNativeJna.ENGINE_finish(rdrandEngine), closing);
114 throwOnError(OpenSslNativeJna.ENGINE_free(rdrandEngine), closing);
115 }
116 }
117
118
119
120
121
122
123 public boolean isRdrandEnabled() {
124 return rdrandEnabled;
125 }
126
127
128
129
130
131
132 @Override
133 public void nextBytes(final byte[] bytes) {
134
135 synchronized (OpenSslJnaCryptoRandom.class) {
136
137
138
139 if (rdrandEnabled && OpenSslNativeJna.RAND_get_rand_method().equals(OpenSslNativeJna.RAND_SSLeay())) {
140 close();
141 throw new IllegalStateException("rdrand should be used but default is detected");
142 }
143
144 final int byteLength = bytes.length;
145 final ByteBuffer buf = ByteBuffer.allocateDirect(byteLength);
146 throwOnError(OpenSslNativeJna.RAND_bytes(buf, byteLength), false);
147 buf.rewind();
148 buf.get(bytes, 0, byteLength);
149 }
150 }
151
152
153
154
155
156 private void throwOnError(final int retVal, final boolean closing) {
157 if (retVal != 1) {
158 final NativeLong err = OpenSslNativeJna.ERR_peek_error();
159 final String errdesc = OpenSslNativeJna.ERR_error_string(err, null);
160 if (!closing) {
161 close();
162 }
163 throw new IllegalStateException("return code " + retVal + " from OpenSSL. Err code is " + err + ": " + errdesc);
164 }
165 }
166 }