Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/sun/security/provider/SeedGenerator.java
41159 views
1
/*
2
* Copyright (c) 1996, 2021, 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.isEmpty()) {
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
public static 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
@SuppressWarnings("removal")
153
static byte[] getSystemEntropy() {
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<>() {
169
@Override
170
public Void run() {
171
try {
172
// System properties can change from machine to machine
173
Properties p = System.getProperties();
174
for (String s: p.stringPropertyNames()) {
175
md.update(s.getBytes());
176
md.update(p.getProperty(s).getBytes());
177
}
178
179
// Include network adapter names (and a Mac address)
180
addNetworkAdapterInfo(md);
181
182
// The temporary dir
183
File f = new File(p.getProperty("java.io.tmpdir"));
184
int count = 0;
185
try (
186
DirectoryStream<Path> stream =
187
Files.newDirectoryStream(f.toPath())) {
188
// We use a Random object to choose what file names
189
// should be used. Otherwise on a machine with too
190
// many files, the same first 1024 files always get
191
// used. Any, We make sure the first 512 files are
192
// always used.
193
Random r = new Random();
194
for (Path entry: stream) {
195
if (count < 512 || r.nextBoolean()) {
196
md.update(entry.getFileName()
197
.toString().getBytes());
198
}
199
if (count++ > 1024) {
200
break;
201
}
202
}
203
}
204
} catch (Exception ex) {
205
md.update((byte)ex.hashCode());
206
}
207
208
// get Runtime memory stats
209
Runtime rt = Runtime.getRuntime();
210
byte[] memBytes = longToByteArray(rt.totalMemory());
211
md.update(memBytes, 0, memBytes.length);
212
memBytes = longToByteArray(rt.freeMemory());
213
md.update(memBytes, 0, memBytes.length);
214
215
return null;
216
}
217
});
218
return md.digest();
219
}
220
221
/*
222
* Include network adapter names and, if available, a Mac address
223
*
224
* See also java.util.concurrent.ThreadLocalRandom.initialSeed()
225
*/
226
private static void addNetworkAdapterInfo(MessageDigest md) {
227
228
try {
229
Enumeration<NetworkInterface> ifcs =
230
NetworkInterface.getNetworkInterfaces();
231
while (ifcs.hasMoreElements()) {
232
NetworkInterface ifc = ifcs.nextElement();
233
md.update(ifc.toString().getBytes());
234
if (!ifc.isVirtual()) { // skip fake addresses
235
byte[] bs = ifc.getHardwareAddress();
236
if (bs != null) {
237
md.update(bs);
238
break;
239
}
240
}
241
}
242
} catch (Exception ignore) {
243
}
244
}
245
246
/**
247
* Helper function to convert a long into a byte array (least significant
248
* byte first).
249
*/
250
private static byte[] longToByteArray(long l) {
251
byte[] retVal = new byte[8];
252
253
for (int i=0; i<8; i++) {
254
retVal[i] = (byte) l;
255
l >>= 8;
256
}
257
258
return retVal;
259
}
260
261
/*
262
// This method helps the test utility receive unprocessed seed bytes.
263
public static int genTestSeed() {
264
return myself.getByte();
265
}
266
*/
267
268
269
private static class ThreadedSeedGenerator extends SeedGenerator
270
implements Runnable {
271
// Queue is used to collect seed bytes
272
private byte[] pool;
273
private int start, end, count;
274
275
// Thread group for our threads
276
ThreadGroup seedGroup;
277
278
/**
279
* The constructor is only called once to construct the one
280
* instance we actually use. It instantiates the message digest
281
* and starts the thread going.
282
*/
283
ThreadedSeedGenerator() {
284
pool = new byte[20];
285
start = end = 0;
286
287
MessageDigest digest;
288
289
try {
290
digest = MessageDigest.getInstance("SHA");
291
} catch (NoSuchAlgorithmException e) {
292
throw new InternalError("internal error: SHA-1 not available."
293
, e);
294
}
295
296
final ThreadGroup[] finalsg = new ThreadGroup[1];
297
@SuppressWarnings("removal")
298
Thread t = java.security.AccessController.doPrivileged
299
(new java.security.PrivilegedAction<>() {
300
@Override
301
public Thread run() {
302
ThreadGroup parent, group =
303
Thread.currentThread().getThreadGroup();
304
while ((parent = group.getParent()) != null) {
305
group = parent;
306
}
307
finalsg[0] = new ThreadGroup
308
(group, "SeedGenerator ThreadGroup");
309
Thread newT = new Thread(finalsg[0],
310
ThreadedSeedGenerator.this,
311
"SeedGenerator Thread",
312
0,
313
false);
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
public final 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", 0,
350
false);
351
t.start();
352
} catch (Exception e) {
353
throw new InternalError("internal error: " +
354
"SeedGenerator thread creation error.", e);
355
}
356
357
// We wait 250milli quanta, so the minimum wait time
358
// cannot be under 250milli.
359
int latch = 0;
360
long startTime = System.nanoTime();
361
while (System.nanoTime() - startTime < 250000000) {
362
synchronized(this){};
363
// Mask the sign bit and keep latch non-negative
364
latch = (latch + 1) & 0x1FFFFFFF;
365
}
366
367
// Translate the value using the permutation, and xor
368
// it with previous values gathered.
369
v ^= rndTab[latch % 255];
370
counter += latch;
371
}
372
373
// Push it into the queue and notify anybody who might
374
// be waiting for it.
375
synchronized(this) {
376
pool[end] = v;
377
end++;
378
count++;
379
if (end >= pool.length) {
380
end = 0;
381
}
382
383
notifyAll();
384
}
385
}
386
} catch (Exception e) {
387
throw new InternalError("internal error: " +
388
"SeedGenerator thread generated an exception.", e);
389
}
390
}
391
392
@Override
393
void getSeedBytes(byte[] result) {
394
for (int i = 0; i < result.length; i++) {
395
result[i] = getSeedByte();
396
}
397
}
398
399
byte getSeedByte() {
400
byte b;
401
402
try {
403
// Wait for it...
404
synchronized(this) {
405
while (count <= 0) {
406
wait();
407
}
408
}
409
} catch (Exception e) {
410
if (count <= 0) {
411
throw new InternalError("internal error: " +
412
"SeedGenerator thread generated an exception.", e);
413
}
414
}
415
416
synchronized(this) {
417
// Get it from the queue
418
b = pool[start];
419
pool[start] = 0;
420
start++;
421
count--;
422
if (start == pool.length) {
423
start = 0;
424
}
425
426
// Notify the daemon thread, just in case it is
427
// waiting for us to make room in the queue.
428
notifyAll();
429
}
430
431
return b;
432
}
433
434
// The permutation was calculated by generating 64k of random
435
// data and using it to mix the trivial permutation.
436
// It should be evenly distributed. The specific values
437
// are not crucial to the security of this class.
438
private static final byte[] rndTab = {
439
56, 30, -107, -6, -86, 25, -83, 75, -12, -64,
440
5, -128, 78, 21, 16, 32, 70, -81, 37, -51,
441
-43, -46, -108, 87, 29, 17, -55, 22, -11, -111,
442
-115, 84, -100, 108, -45, -15, -98, 72, -33, -28,
443
31, -52, -37, -117, -97, -27, 93, -123, 47, 126,
444
-80, -62, -93, -79, 61, -96, -65, -5, -47, -119,
445
14, 89, 81, -118, -88, 20, 67, -126, -113, 60,
446
-102, 55, 110, 28, 85, 121, 122, -58, 2, 45,
447
43, 24, -9, 103, -13, 102, -68, -54, -101, -104,
448
19, 13, -39, -26, -103, 62, 77, 51, 44, 111,
449
73, 18, -127, -82, 4, -30, 11, -99, -74, 40,
450
-89, 42, -76, -77, -94, -35, -69, 35, 120, 76,
451
33, -73, -7, 82, -25, -10, 88, 125, -112, 58,
452
83, 95, 6, 10, 98, -34, 80, 15, -91, 86,
453
-19, 52, -17, 117, 49, -63, 118, -90, 36, -116,
454
-40, -71, 97, -53, -109, -85, 109, -16, -3, 104,
455
-95, 68, 54, 34, 26, 114, -1, 106, -121, 3,
456
66, 0, 100, -84, 57, 107, 119, -42, 112, -61,
457
1, 48, 38, 12, -56, -57, 39, -106, -72, 41,
458
7, 71, -29, -59, -8, -38, 79, -31, 124, -124,
459
8, 91, 116, 99, -4, 9, -36, -78, 63, -49,
460
-67, -87, 59, 101, -32, 92, 94, 53, -41, 115,
461
-66, -70, -122, 50, -50, -22, -20, -18, -21, 23,
462
-2, -48, 96, 65, -105, 123, -14, -110, 69, -24,
463
-120, -75, 74, 127, -60, 113, 90, -114, 105, 46,
464
27, -125, -23, -44, 64
465
};
466
467
/**
468
* This inner thread causes the thread scheduler to become 'noisy',
469
* thus adding entropy to the system load.
470
* At least one instance of this class is generated for every seed byte.
471
*/
472
private static class BogusThread implements Runnable {
473
@Override
474
public final void run() {
475
try {
476
for (int i = 0; i < 5; i++) {
477
Thread.sleep(50);
478
}
479
// System.gc();
480
} catch (Exception e) {
481
}
482
}
483
}
484
}
485
486
static class URLSeedGenerator extends SeedGenerator {
487
488
private String deviceName;
489
private InputStream seedStream;
490
491
/**
492
* The constructor is only called once to construct the one
493
* instance we actually use. It opens the entropy gathering device
494
* which will supply the randomness.
495
*/
496
497
URLSeedGenerator(String egdurl) throws IOException {
498
if (egdurl == null) {
499
throw new IOException("No random source specified");
500
}
501
deviceName = egdurl;
502
init();
503
}
504
505
@SuppressWarnings("removal")
506
private void init() throws IOException {
507
final URL device = new URL(deviceName);
508
try {
509
seedStream = java.security.AccessController.doPrivileged
510
(new java.security.PrivilegedExceptionAction<>() {
511
@Override
512
public InputStream run() throws IOException {
513
/*
514
* return a shared InputStream for file URLs and
515
* avoid buffering.
516
* The URL.openStream() call wraps InputStream in a
517
* BufferedInputStream which
518
* can buffer up to 8K bytes. This read is a
519
* performance issue for entropy sources which
520
* can be slow to replenish.
521
*/
522
if (device.getProtocol().equalsIgnoreCase("file")) {
523
File deviceFile =
524
SunEntries.getDeviceFile(device);
525
return FileInputStreamPool
526
.getInputStream(deviceFile);
527
} else {
528
return device.openStream();
529
}
530
}
531
});
532
} catch (Exception e) {
533
throw new IOException(
534
"Failed to open " + deviceName, e.getCause());
535
}
536
}
537
538
@Override
539
void getSeedBytes(byte[] result) {
540
int len = result.length;
541
int read = 0;
542
try {
543
while (read < len) {
544
int count = seedStream.read(result, read, len - read);
545
// /dev/random blocks - should never have EOF
546
if (count < 0) {
547
throw new InternalError(
548
"URLSeedGenerator " + deviceName +
549
" reached end of file");
550
}
551
read += count;
552
}
553
} catch (IOException ioe) {
554
throw new InternalError("URLSeedGenerator " + deviceName +
555
" generated exception: " + ioe.getMessage(), ioe);
556
}
557
}
558
}
559
}
560
561