Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/provider/SeedGenerator.java
38830 views
1
/*
2
* Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package sun.security.provider;
27
28
/**
29
* This class generates seeds for the SHA1PRNG cryptographically strong
30
* random number generator.
31
* <p>
32
* The seed is produced using one of two techniques, via a computation
33
* of current system activity or from an entropy gathering device.
34
* <p>
35
* In the default technique the seed is produced by counting the
36
* number of times the VM manages to loop in a given period. This number
37
* roughly reflects the machine load at that point in time.
38
* The samples are translated using a permutation (s-box)
39
* and then XORed together. This process is non linear and
40
* should prevent the samples from "averaging out". The s-box
41
* was designed to have even statistical distribution; it's specific
42
* values are not crucial for the security of the seed.
43
* We also create a number of sleeper threads which add entropy
44
* to the system by keeping the scheduler busy.
45
* Twenty such samples should give us roughly 160 bits of randomness.
46
* <p>
47
* These values are gathered in the background by a daemon thread
48
* thus allowing the system to continue performing it's different
49
* activites, which in turn add entropy to the random seed.
50
* <p>
51
* The class also gathers miscellaneous system information, some
52
* machine dependent, some not. This information is then hashed together
53
* with the 20 seed bytes.
54
* <p>
55
* The alternative to the above approach is to acquire seed material
56
* from an entropy gathering device, such as /dev/random. This can be
57
* accomplished by setting the value of the {@code securerandom.source}
58
* Security property to a URL specifying the location of the entropy
59
* gathering device, or by setting the {@code java.security.egd} System
60
* property.
61
* <p>
62
* In the event the specified URL cannot be accessed the default
63
* threading mechanism is used.
64
*
65
* @author Joshua Bloch
66
* @author Gadi Guy
67
*/
68
69
import java.security.*;
70
import java.io.*;
71
import java.util.Properties;
72
import java.util.Enumeration;
73
import java.net.*;
74
import java.nio.file.DirectoryStream;
75
import java.nio.file.Files;
76
import java.nio.file.Path;
77
import java.util.Random;
78
import sun.security.util.Debug;
79
80
abstract class SeedGenerator {
81
82
// Static instance is created at link time
83
private static SeedGenerator instance;
84
85
private static final Debug debug = Debug.getInstance("provider");
86
87
// Static initializer to hook in selected or best performing generator
88
static {
89
String egdSource = SunEntries.getSeedSource();
90
91
/*
92
* Try the URL specifying the source (e.g. file:/dev/random)
93
*
94
* The URLs "file:/dev/random" or "file:/dev/urandom" are used to
95
* indicate the SeedGenerator should use OS support, if available.
96
*
97
* On Windows, this causes the MS CryptoAPI seeder to be used.
98
*
99
* On Solaris/Linux/MacOS, this is identical to using
100
* URLSeedGenerator to read from /dev/[u]random
101
*/
102
if (egdSource.equals(SunEntries.URL_DEV_RANDOM) ||
103
egdSource.equals(SunEntries.URL_DEV_URANDOM)) {
104
try {
105
instance = new NativeSeedGenerator(egdSource);
106
if (debug != null) {
107
debug.println(
108
"Using operating system seed generator" + egdSource);
109
}
110
} catch (IOException e) {
111
if (debug != null) {
112
debug.println("Failed to use operating system seed "
113
+ "generator: " + e.toString());
114
}
115
}
116
} else if (egdSource.length() != 0) {
117
try {
118
instance = new URLSeedGenerator(egdSource);
119
if (debug != null) {
120
debug.println("Using URL seed generator reading from "
121
+ egdSource);
122
}
123
} catch (IOException e) {
124
if (debug != null) {
125
debug.println("Failed to create seed generator with "
126
+ egdSource + ": " + e.toString());
127
}
128
}
129
}
130
131
// Fall back to ThreadedSeedGenerator
132
if (instance == null) {
133
if (debug != null) {
134
debug.println("Using default threaded seed generator");
135
}
136
instance = new ThreadedSeedGenerator();
137
}
138
}
139
140
/**
141
* Fill result with bytes from the queue. Wait for it if it isn't ready.
142
*/
143
static public void generateSeed(byte[] result) {
144
instance.getSeedBytes(result);
145
}
146
147
abstract void getSeedBytes(byte[] result);
148
149
/**
150
* Retrieve some system information, hashed.
151
*/
152
static byte[] getSystemEntropy() {
153
byte[] ba;
154
final MessageDigest md;
155
156
try {
157
md = MessageDigest.getInstance("SHA");
158
} catch (NoSuchAlgorithmException nsae) {
159
throw new InternalError("internal error: SHA-1 not available."
160
, nsae);
161
}
162
163
// The current time in millis
164
byte b =(byte)System.currentTimeMillis();
165
md.update(b);
166
167
java.security.AccessController.doPrivileged
168
(new java.security.PrivilegedAction<Void>() {
169
@Override
170
public Void run() {
171
try {
172
// System properties can change from machine to machine
173
String s;
174
Properties p = System.getProperties();
175
Enumeration<?> e = p.propertyNames();
176
while (e.hasMoreElements()) {
177
s =(String)e.nextElement();
178
md.update(s.getBytes());
179
md.update(p.getProperty(s).getBytes());
180
}
181
182
// Include network adapter names (and a Mac address)
183
addNetworkAdapterInfo(md);
184
185
// The temporary dir
186
File f = new File(p.getProperty("java.io.tmpdir"));
187
int count = 0;
188
try (
189
DirectoryStream<Path> stream =
190
Files.newDirectoryStream(f.toPath())) {
191
// We use a Random object to choose what file names
192
// should be used. Otherwise on a machine with too
193
// many files, the same first 1024 files always get
194
// used. Any, We make sure the first 512 files are
195
// always used.
196
Random r = new Random();
197
for (Path entry: stream) {
198
if (count < 512 || r.nextBoolean()) {
199
md.update(entry.getFileName()
200
.toString().getBytes());
201
}
202
if (count++ > 1024) {
203
break;
204
}
205
}
206
}
207
} catch (Exception ex) {
208
md.update((byte)ex.hashCode());
209
}
210
211
// get Runtime memory stats
212
Runtime rt = Runtime.getRuntime();
213
byte[] memBytes = longToByteArray(rt.totalMemory());
214
md.update(memBytes, 0, memBytes.length);
215
memBytes = longToByteArray(rt.freeMemory());
216
md.update(memBytes, 0, memBytes.length);
217
218
return null;
219
}
220
});
221
return md.digest();
222
}
223
224
/*
225
* Include network adapter names and, if available, a Mac address
226
*
227
* See also java.util.concurrent.ThreadLocalRandom.initialSeed()
228
*/
229
private static void addNetworkAdapterInfo(MessageDigest md) {
230
231
try {
232
Enumeration<NetworkInterface> ifcs =
233
NetworkInterface.getNetworkInterfaces();
234
while (ifcs.hasMoreElements()) {
235
NetworkInterface ifc = ifcs.nextElement();
236
md.update(ifc.toString().getBytes());
237
if (!ifc.isVirtual()) { // skip fake addresses
238
byte[] bs = ifc.getHardwareAddress();
239
if (bs != null) {
240
md.update(bs);
241
break;
242
}
243
}
244
}
245
} catch (Exception ignore) {
246
}
247
}
248
249
/**
250
* Helper function to convert a long into a byte array (least significant
251
* byte first).
252
*/
253
private static byte[] longToByteArray(long l) {
254
byte[] retVal = new byte[8];
255
256
for (int i=0; i<8; i++) {
257
retVal[i] = (byte) l;
258
l >>= 8;
259
}
260
261
return retVal;
262
}
263
264
/*
265
// This method helps the test utility receive unprocessed seed bytes.
266
public static int genTestSeed() {
267
return myself.getByte();
268
}
269
*/
270
271
272
private static class ThreadedSeedGenerator extends SeedGenerator
273
implements Runnable {
274
// Queue is used to collect seed bytes
275
private byte[] pool;
276
private int start, end, count;
277
278
// Thread group for our threads
279
ThreadGroup seedGroup;
280
281
/**
282
* The constructor is only called once to construct the one
283
* instance we actually use. It instantiates the message digest
284
* and starts the thread going.
285
*/
286
ThreadedSeedGenerator() {
287
pool = new byte[20];
288
start = end = 0;
289
290
MessageDigest digest;
291
292
try {
293
digest = MessageDigest.getInstance("SHA");
294
} catch (NoSuchAlgorithmException e) {
295
throw new InternalError("internal error: SHA-1 not available."
296
, e);
297
}
298
299
final ThreadGroup[] finalsg = new ThreadGroup[1];
300
Thread t = java.security.AccessController.doPrivileged
301
(new java.security.PrivilegedAction<Thread>() {
302
@Override
303
public Thread run() {
304
ThreadGroup parent, group =
305
Thread.currentThread().getThreadGroup();
306
while ((parent = group.getParent()) != null) {
307
group = parent;
308
}
309
finalsg[0] = new ThreadGroup
310
(group, "SeedGenerator ThreadGroup");
311
Thread newT = new Thread(finalsg[0],
312
ThreadedSeedGenerator.this,
313
"SeedGenerator Thread");
314
newT.setPriority(Thread.MIN_PRIORITY);
315
newT.setDaemon(true);
316
return newT;
317
}
318
});
319
seedGroup = finalsg[0];
320
t.start();
321
}
322
323
/**
324
* This method does the actual work. It collects random bytes and
325
* pushes them into the queue.
326
*/
327
@Override
328
final public void run() {
329
try {
330
while (true) {
331
// Queue full? Wait till there's room.
332
synchronized(this) {
333
while (count >= pool.length) {
334
wait();
335
}
336
}
337
338
int counter, quanta;
339
byte v = 0;
340
341
// Spin count must not be under 64000
342
for (counter = quanta = 0;
343
(counter < 64000) && (quanta < 6); quanta++) {
344
345
// Start some noisy threads
346
try {
347
BogusThread bt = new BogusThread();
348
Thread t = new Thread
349
(seedGroup, bt, "SeedGenerator Thread");
350
t.start();
351
} catch (Exception e) {
352
throw new InternalError("internal error: " +
353
"SeedGenerator thread creation error.", e);
354
}
355
356
// We wait 250milli quanta, so the minimum wait time
357
// cannot be under 250milli.
358
int latch = 0;
359
long l = System.currentTimeMillis() + 250;
360
while (System.currentTimeMillis() < l) {
361
synchronized(this){};
362
latch++;
363
}
364
365
// Translate the value using the permutation, and xor
366
// it with previous values gathered.
367
v ^= rndTab[latch % 255];
368
counter += latch;
369
}
370
371
// Push it into the queue and notify anybody who might
372
// be waiting for it.
373
synchronized(this) {
374
pool[end] = v;
375
end++;
376
count++;
377
if (end >= pool.length) {
378
end = 0;
379
}
380
381
notifyAll();
382
}
383
}
384
} catch (Exception e) {
385
throw new InternalError("internal error: " +
386
"SeedGenerator thread generated an exception.", e);
387
}
388
}
389
390
@Override
391
void getSeedBytes(byte[] result) {
392
for (int i = 0; i < result.length; i++) {
393
result[i] = getSeedByte();
394
}
395
}
396
397
byte getSeedByte() {
398
byte b;
399
400
try {
401
// Wait for it...
402
synchronized(this) {
403
while (count <= 0) {
404
wait();
405
}
406
}
407
} catch (Exception e) {
408
if (count <= 0) {
409
throw new InternalError("internal error: " +
410
"SeedGenerator thread generated an exception.", e);
411
}
412
}
413
414
synchronized(this) {
415
// Get it from the queue
416
b = pool[start];
417
pool[start] = 0;
418
start++;
419
count--;
420
if (start == pool.length) {
421
start = 0;
422
}
423
424
// Notify the daemon thread, just in case it is
425
// waiting for us to make room in the queue.
426
notifyAll();
427
}
428
429
return b;
430
}
431
432
// The permutation was calculated by generating 64k of random
433
// data and using it to mix the trivial permutation.
434
// It should be evenly distributed. The specific values
435
// are not crucial to the security of this class.
436
private static byte[] rndTab = {
437
56, 30, -107, -6, -86, 25, -83, 75, -12, -64,
438
5, -128, 78, 21, 16, 32, 70, -81, 37, -51,
439
-43, -46, -108, 87, 29, 17, -55, 22, -11, -111,
440
-115, 84, -100, 108, -45, -15, -98, 72, -33, -28,
441
31, -52, -37, -117, -97, -27, 93, -123, 47, 126,
442
-80, -62, -93, -79, 61, -96, -65, -5, -47, -119,
443
14, 89, 81, -118, -88, 20, 67, -126, -113, 60,
444
-102, 55, 110, 28, 85, 121, 122, -58, 2, 45,
445
43, 24, -9, 103, -13, 102, -68, -54, -101, -104,
446
19, 13, -39, -26, -103, 62, 77, 51, 44, 111,
447
73, 18, -127, -82, 4, -30, 11, -99, -74, 40,
448
-89, 42, -76, -77, -94, -35, -69, 35, 120, 76,
449
33, -73, -7, 82, -25, -10, 88, 125, -112, 58,
450
83, 95, 6, 10, 98, -34, 80, 15, -91, 86,
451
-19, 52, -17, 117, 49, -63, 118, -90, 36, -116,
452
-40, -71, 97, -53, -109, -85, 109, -16, -3, 104,
453
-95, 68, 54, 34, 26, 114, -1, 106, -121, 3,
454
66, 0, 100, -84, 57, 107, 119, -42, 112, -61,
455
1, 48, 38, 12, -56, -57, 39, -106, -72, 41,
456
7, 71, -29, -59, -8, -38, 79, -31, 124, -124,
457
8, 91, 116, 99, -4, 9, -36, -78, 63, -49,
458
-67, -87, 59, 101, -32, 92, 94, 53, -41, 115,
459
-66, -70, -122, 50, -50, -22, -20, -18, -21, 23,
460
-2, -48, 96, 65, -105, 123, -14, -110, 69, -24,
461
-120, -75, 74, 127, -60, 113, 90, -114, 105, 46,
462
27, -125, -23, -44, 64
463
};
464
465
/**
466
* This inner thread causes the thread scheduler to become 'noisy',
467
* thus adding entropy to the system load.
468
* At least one instance of this class is generated for every seed byte.
469
*/
470
private static class BogusThread implements Runnable {
471
@Override
472
final public void run() {
473
try {
474
for (int i = 0; i < 5; i++) {
475
Thread.sleep(50);
476
}
477
// System.gc();
478
} catch (Exception e) {
479
}
480
}
481
}
482
}
483
484
static class URLSeedGenerator extends SeedGenerator {
485
486
private String deviceName;
487
private InputStream seedStream;
488
489
/**
490
* The constructor is only called once to construct the one
491
* instance we actually use. It opens the entropy gathering device
492
* which will supply the randomness.
493
*/
494
495
URLSeedGenerator(String egdurl) throws IOException {
496
if (egdurl == null) {
497
throw new IOException("No random source specified");
498
}
499
deviceName = egdurl;
500
init();
501
}
502
503
private void init() throws IOException {
504
final URL device = new URL(deviceName);
505
try {
506
seedStream = java.security.AccessController.doPrivileged
507
(new java.security.PrivilegedExceptionAction<InputStream>() {
508
@Override
509
public InputStream run() throws IOException {
510
/*
511
* return a FileInputStream for file URLs and
512
* avoid buffering. The openStream() call wraps
513
* InputStream in a BufferedInputStream which
514
* can buffer up to 8K bytes. This read is a
515
* performance issue for entropy sources which
516
* can be slow to replenish.
517
*/
518
if (device.getProtocol().equalsIgnoreCase("file")) {
519
File deviceFile =
520
SunEntries.getDeviceFile(device);
521
return new FileInputStream(deviceFile);
522
} else {
523
return device.openStream();
524
}
525
}
526
});
527
} catch (Exception e) {
528
throw new IOException(
529
"Failed to open " + deviceName, e.getCause());
530
}
531
}
532
533
@Override
534
void getSeedBytes(byte[] result) {
535
int len = result.length;
536
int read = 0;
537
try {
538
while (read < len) {
539
int count = seedStream.read(result, read, len - read);
540
// /dev/random blocks - should never have EOF
541
if (count < 0) {
542
throw new InternalError(
543
"URLSeedGenerator " + deviceName +
544
" reached end of file");
545
}
546
read += count;
547
}
548
} catch (IOException ioe) {
549
throw new InternalError("URLSeedGenerator " + deviceName +
550
" generated exception: " + ioe.getMessage(), ioe);
551
}
552
}
553
}
554
}
555
556