36
36
import org .jruby .runtime .ThreadContext ;
37
37
import org .jruby .runtime .builtin .IRubyObject ;
38
38
import org .jruby .util .ByteList ;
39
+ import org .jruby .util .SafePropertyAccessor ;
40
+
41
+ import java .lang .reflect .Field ;
42
+ import java .lang .reflect .InvocationTargetException ;
43
+ import java .lang .reflect .Method ;
44
+ import java .lang .reflect .Modifier ;
45
+ import java .util .concurrent .ThreadLocalRandom ;
39
46
40
47
/**
41
48
* @author <a href="mailto:[email protected] ">Ola Bini</a>
42
49
*/
43
50
public class Random {
44
51
45
- private static class Holder {
52
+ // thread-local (default), shared, strong
53
+ static final String HOLDER_TYPE = SafePropertyAccessor .getProperty ("jruby.openssl.random" , "" );
54
+
55
+ private static Holder createHolderImpl () {
56
+ if (HOLDER_TYPE .equals ("default" ) || HOLDER_TYPE .equals ("thread-local" )) {
57
+ return new ThreadLocalHolder ();
58
+ }
59
+ if (HOLDER_TYPE .equals ("" )) {
60
+ // NOTE: fine to remove when support for running on Java 6 is gone ...
61
+ if (OpenSSL .javaVersion6 (false )) {
62
+ return new SharedHolder (); // can not-use ThreadLocalRandom.current()
63
+ }
64
+ }
65
+ if (HOLDER_TYPE .equals ("shared" )) {
66
+ return new SharedHolder ();
67
+ }
68
+ if (HOLDER_TYPE .equals ("strong" )) {
69
+ return new StrongHolder ();
70
+ }
71
+ return new ThreadLocalHolder ();
72
+ }
73
+
74
+ static abstract class Holder {
75
+
76
+ abstract java .util .Random getPlainRandom () ;
77
+
78
+ abstract java .security .SecureRandom getSecureRandom (ThreadContext context ) ;
79
+
80
+ void seedSecureRandom (ThreadContext context , byte [] seed ) {
81
+ getSecureRandom (context ).setSeed (seed );
82
+ }
83
+
84
+ void seedPlainRandom (long seed ) {
85
+ getPlainRandom ().setSeed (seed );
86
+ }
87
+
88
+ }
89
+
90
+ private static class SharedHolder extends Holder {
46
91
47
92
private volatile java .util .Random plainRandom ;
48
93
private volatile java .security .SecureRandom secureRandom ;
@@ -58,7 +103,7 @@ java.util.Random getPlainRandom() {
58
103
return plainRandom ;
59
104
}
60
105
61
- java .security .SecureRandom getSecureRandom () {
106
+ java .security .SecureRandom getSecureRandom (ThreadContext context ) {
62
107
if (secureRandom == null ) {
63
108
synchronized (this ) {
64
109
if (secureRandom == null ) {
@@ -71,6 +116,131 @@ java.security.SecureRandom getSecureRandom() {
71
116
72
117
}
73
118
119
+ private static class ThreadLocalHolder extends Holder {
120
+
121
+ @ Override
122
+ java .util .Random getPlainRandom () {
123
+ return ThreadLocalRandom .current ();
124
+ }
125
+
126
+ @ Override
127
+ void seedPlainRandom (long seed ) {
128
+ return ; // NO-OP - UnsupportedOperationException
129
+ }
130
+
131
+ @ Override
132
+ java .security .SecureRandom getSecureRandom (ThreadContext context ) {
133
+ java .security .SecureRandom secureRandom = context .secureRandom ;
134
+ if (secureRandom == null ) {
135
+ secureRandom = getSecureRandomImpl ();
136
+ setSecureRandom (context , secureRandom ); // context.secureRandom = ...
137
+ }
138
+ return secureRandom ;
139
+ }
140
+
141
+ private static final Field secureRandomField ;
142
+
143
+ private static void setSecureRandom (ThreadContext context , java .security .SecureRandom secureRandom ) {
144
+ if (secureRandomField != null ) {
145
+ try {
146
+ secureRandomField .set (context , secureRandom );
147
+ }
148
+ catch (Exception ex ) { /* IllegalAccessException should not happen */ }
149
+ }
150
+ }
151
+
152
+ private static final String PREFERRED_PRNG ;
153
+ static {
154
+ PREFERRED_PRNG = SafePropertyAccessor .getProperty ("jruby.preferred.prng" , "NativePRNGNonBlocking" );
155
+
156
+ Field secureRandom = null ;
157
+ try {
158
+ secureRandom = ThreadContext .class .getField ("secureRandom" );
159
+ if ( ! secureRandom .isAccessible () || Modifier .isFinal (secureRandom .getModifiers ()) ) {
160
+ secureRandom = null ;
161
+ }
162
+ }
163
+ catch (Exception ex ) { /* ignore NoSuchFieldException */ }
164
+ secureRandomField = secureRandom ;
165
+ }
166
+
167
+ private static boolean tryPreferredPRNG = true ;
168
+ private static boolean trySHA1PRNG = true ;
169
+
170
+ // copied from JRuby (not available in all 1.7.x) :
171
+ public java .security .SecureRandom getSecureRandomImpl () {
172
+ java .security .SecureRandom secureRandom = null ;
173
+ // Try preferred PRNG, which defaults to NativePRNGNonBlocking
174
+ if (tryPreferredPRNG ) {
175
+ try {
176
+ secureRandom = java .security .SecureRandom .getInstance (PREFERRED_PRNG );
177
+ }
178
+ catch (Exception e ) { tryPreferredPRNG = false ; }
179
+ }
180
+
181
+ // Try SHA1PRNG
182
+ if (secureRandom == null && trySHA1PRNG ) {
183
+ try {
184
+ secureRandom = java .security .SecureRandom .getInstance ("SHA1PRNG" );
185
+ }
186
+ catch (Exception e ) { trySHA1PRNG = false ; }
187
+ }
188
+
189
+ // Just let JDK do whatever it does
190
+ if (secureRandom == null ) {
191
+ secureRandom = new java .security .SecureRandom ();
192
+ }
193
+
194
+ return secureRandom ;
195
+ }
196
+
197
+ }
198
+
199
+ private static class StrongHolder extends Holder {
200
+
201
+ static {
202
+ Method method = null ;
203
+ if (OpenSSL .javaVersion8 (true )) {
204
+ try {
205
+ method = java .security .SecureRandom .class .getMethod ("getInstanceStrong" );
206
+ }
207
+ catch (NoSuchMethodException ex ) { OpenSSL .debugStackTrace (ex ); }
208
+ }
209
+ getInstanceStrong = method ;
210
+ }
211
+
212
+ private static final Method getInstanceStrong ;
213
+
214
+ @ Override
215
+ java .util .Random getPlainRandom () {
216
+ return new java .util .Random ();
217
+ }
218
+
219
+ @ Override
220
+ java .security .SecureRandom getSecureRandom (ThreadContext context ) {
221
+ // return java.security.SecureRandom.getInstanceStrong(); (on Java 8)
222
+ if (getInstanceStrong == null ) return SecurityHelper .getSecureRandom ();
223
+ try {
224
+ return (java .security .SecureRandom ) getInstanceStrong .invoke (null );
225
+ }
226
+ catch (IllegalAccessException ex ) {
227
+ Utils .throwException (ex ); return null ; // won't happen
228
+ }
229
+ catch (InvocationTargetException ex ) {
230
+ Utils .throwException (ex .getTargetException ()); return null ;
231
+ }
232
+ }
233
+
234
+ void seedSecureRandom (ThreadContext context , byte [] seed ) {
235
+ // NOOP - new instance returned for getSecureRandom
236
+ }
237
+
238
+ void seedPlainRandom (long seed ) {
239
+ // NOOP - new instance returned for getPlainRandom
240
+ }
241
+
242
+ }
243
+
74
244
public static void createRandom (final Ruby runtime , final RubyModule OpenSSL ) {
75
245
final RubyModule Random = OpenSSL .defineModuleUnder ("Random" );
76
246
@@ -79,31 +249,29 @@ public static void createRandom(final Ruby runtime, final RubyModule OpenSSL) {
79
249
80
250
Random .defineAnnotatedMethods (Random .class );
81
251
82
- Random .dataWrapStruct (new Holder ());
252
+ Random .dataWrapStruct (createHolderImpl ());
83
253
}
84
254
85
255
@ JRubyMethod (meta = true )
86
256
public static RubyString random_bytes (final ThreadContext context ,
87
257
final IRubyObject self , final IRubyObject arg ) {
88
- final Ruby runtime = context .runtime ;
89
- return random_bytes (runtime , self , toInt (runtime , arg ));
258
+ return random_bytes (context , self , toInt (context .runtime , arg ));
90
259
}
91
260
92
- static RubyString random_bytes (final Ruby runtime , final int len ) {
93
- final RubyModule Random = (RubyModule ) runtime .getModule ("OpenSSL" ).getConstantAt ("Random" );
94
- return generate (runtime , Random , len , true ); // secure-random
261
+ static RubyString random_bytes (final ThreadContext context , final int len ) {
262
+ final RubyModule Random = (RubyModule ) context . runtime .getModule ("OpenSSL" ).getConstantAt ("Random" );
263
+ return generate (context , Random , len , true ); // secure-random
95
264
}
96
265
97
- private static RubyString random_bytes (final Ruby runtime ,
266
+ private static RubyString random_bytes (final ThreadContext context ,
98
267
final IRubyObject self , final int len ) {
99
- return generate (runtime , self , len , true ); // secure-random
268
+ return generate (context , self , len , true ); // secure-random
100
269
}
101
270
102
271
@ JRubyMethod (meta = true )
103
272
public static RubyString pseudo_bytes (final ThreadContext context ,
104
273
final IRubyObject self , final IRubyObject len ) {
105
- final Ruby runtime = context .runtime ;
106
- return generate (runtime , self , toInt (runtime , len ), false ); // plain-random
274
+ return generate (context , self , toInt (context .runtime , len ), false ); // plain-random
107
275
}
108
276
109
277
private static int toInt (final Ruby runtime , final IRubyObject arg ) {
@@ -114,12 +282,16 @@ private static int toInt(final Ruby runtime, final IRubyObject arg) {
114
282
return (int ) len ;
115
283
}
116
284
117
- private static RubyString generate (final Ruby runtime ,
285
+ private static RubyString generate (final ThreadContext context ,
118
286
final IRubyObject self , final int len , final boolean secure ) {
119
287
final Holder holder = retrieveHolder ((RubyModule ) self );
120
288
final byte [] bytes = new byte [len ];
121
- ( secure ? holder .getSecureRandom () : holder .getPlainRandom () ).nextBytes (bytes );
122
- return RubyString .newString (runtime , new ByteList (bytes , false ));
289
+ ( secure ? holder .getSecureRandom (context ) : holder .getPlainRandom () ).nextBytes (bytes );
290
+ return RubyString .newString (context .runtime , new ByteList (bytes , false ));
291
+ }
292
+
293
+ static Holder getHolder (final Ruby runtime ) {
294
+ return retrieveHolder ((RubyModule ) runtime .getModule ("OpenSSL" ).getConstantAt ("Random" ));
123
295
}
124
296
125
297
private static Holder retrieveHolder (final RubyModule Random ) {
@@ -129,23 +301,23 @@ private static Holder retrieveHolder(final RubyModule Random) {
129
301
@ JRubyMethod (meta = true ) // seed(str) -> str
130
302
public static IRubyObject seed (final ThreadContext context ,
131
303
final IRubyObject self , IRubyObject str ) {
132
- seedImpl ((RubyModule ) self , str );
304
+ seedImpl (context , (RubyModule ) self , str );
133
305
return str ;
134
306
}
135
307
136
- private static void seedImpl (final RubyModule Random , final IRubyObject str ) {
308
+ private static void seedImpl (ThreadContext context , final RubyModule Random , final IRubyObject str ) {
137
309
final byte [] seed = str .asString ().getBytes ();
138
310
final Holder holder = retrieveHolder (Random );
139
311
140
- holder .getSecureRandom (). setSeed ( seed ); // seed supplements existing (secure) seeding mechanism
312
+ holder .seedSecureRandom ( context , seed ); // seed supplements existing (secure) seeding mechanism
141
313
142
314
long s ; int l = seed .length ;
143
315
if ( l >= 4 ) {
144
316
s = (seed [0 ] << 24 ) | (seed [1 ] << 16 ) | (seed [2 ] << 8 ) | seed [3 ];
145
317
if ( l >= 8 ) {
146
318
s = s ^ (seed [l -4 ] << 24 ) | (seed [l -3 ] << 16 ) | (seed [l -2 ] << 8 ) | seed [l -1 ];
147
319
}
148
- holder .getPlainRandom (). setSeed (s );
320
+ holder .seedPlainRandom (s );
149
321
}
150
322
}
151
323
@@ -158,7 +330,7 @@ public static IRubyObject status_p(final ThreadContext context, final IRubyObjec
158
330
@ JRubyMethod (meta = true , name = { "random_add" , "add" }) // random_add(str, entropy) -> self
159
331
public static IRubyObject random_add (final ThreadContext context ,
160
332
final IRubyObject self , IRubyObject str , IRubyObject entropy ) {
161
- seedImpl ((RubyModule ) self , str ); // simply ignoring _entropy_ hint
333
+ seedImpl (context , (RubyModule ) self , str ); // simply ignoring _entropy_ hint
162
334
return self ;
163
335
}
164
336
0 commit comments