Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/solaris/classes/sun/security/provider/NativePRNG.java
32288 views
1
/*
2
* Copyright (c) 2003, 2016, 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
import java.io.*;
29
import java.net.*;
30
import java.security.*;
31
import java.util.Arrays;
32
33
import sun.security.util.Debug;
34
35
/**
36
* Native PRNG implementation for Solaris/Linux/MacOS.
37
* <p>
38
* It obtains seed and random numbers by reading system files such as
39
* the special device files /dev/random and /dev/urandom. This
40
* implementation respects the {@code securerandom.source} Security
41
* property and {@code java.security.egd} System property for obtaining
42
* seed material. If the file specified by the properties does not
43
* exist, /dev/random is the default seed source. /dev/urandom is
44
* the default source of random numbers.
45
* <p>
46
* On some Unix platforms, /dev/random may block until enough entropy is
47
* available, but that may negatively impact the perceived startup
48
* time. By selecting these sources, this implementation tries to
49
* strike a balance between performance and security.
50
* <p>
51
* generateSeed() and setSeed() attempt to directly read/write to the seed
52
* source. However, this file may only be writable by root in many
53
* configurations. Because we cannot just ignore bytes specified via
54
* setSeed(), we keep a SHA1PRNG around in parallel.
55
* <p>
56
* nextBytes() reads the bytes directly from the source of random
57
* numbers (and then mixes them with bytes from the SHA1PRNG for the
58
* reasons explained above). Reading bytes from the random generator means
59
* that we are generally getting entropy from the operating system. This
60
* is a notable advantage over the SHA1PRNG model, which acquires
61
* entropy only initially during startup although the VM may be running
62
* for months.
63
* <p>
64
* Also note for nextBytes() that we do not need any initial pure random
65
* seed from /dev/random. This is an advantage because on some versions
66
* of Linux entropy can be exhausted very quickly and could thus impact
67
* startup time.
68
* <p>
69
* Finally, note that we use a singleton for the actual work (RandomIO)
70
* to avoid having to open and close /dev/[u]random constantly. However,
71
* there may be many NativePRNG instances created by the JCA framework.
72
*
73
* @since 1.5
74
* @author Andreas Sterbenz
75
*/
76
public final class NativePRNG extends SecureRandomSpi {
77
78
private static final long serialVersionUID = -6599091113397072932L;
79
80
private static final Debug debug = Debug.getInstance("provider");
81
82
// name of the pure random file (also used for setSeed())
83
private static final String NAME_RANDOM = "/dev/random";
84
// name of the pseudo random file
85
private static final String NAME_URANDOM = "/dev/urandom";
86
87
// which kind of RandomIO object are we creating?
88
private enum Variant {
89
MIXED, BLOCKING, NONBLOCKING
90
}
91
92
// singleton instance or null if not available
93
private static final RandomIO INSTANCE = initIO(Variant.MIXED);
94
95
/**
96
* Get the System egd source (if defined). We only allow "file:"
97
* URLs for now. If there is a egd value, parse it.
98
*
99
* @return the URL or null if not available.
100
*/
101
private static URL getEgdUrl() {
102
// This will return "" if nothing was set.
103
String egdSource = SunEntries.getSeedSource();
104
URL egdUrl;
105
106
if (egdSource.length() != 0) {
107
if (debug != null) {
108
debug.println("NativePRNG egdUrl: " + egdSource);
109
}
110
try {
111
egdUrl = new URL(egdSource);
112
if (!egdUrl.getProtocol().equalsIgnoreCase("file")) {
113
return null;
114
}
115
} catch (MalformedURLException e) {
116
return null;
117
}
118
} else {
119
egdUrl = null;
120
}
121
122
return egdUrl;
123
}
124
125
/**
126
* Create a RandomIO object for all I/O of this Variant type.
127
*/
128
private static RandomIO initIO(final Variant v) {
129
return AccessController.doPrivileged(
130
new PrivilegedAction<RandomIO>() {
131
@Override
132
public RandomIO run() {
133
134
File seedFile;
135
File nextFile;
136
137
switch(v) {
138
case MIXED:
139
URL egdUrl;
140
File egdFile = null;
141
142
if ((egdUrl = getEgdUrl()) != null) {
143
try {
144
egdFile = SunEntries.getDeviceFile(egdUrl);
145
} catch (IOException e) {
146
// Swallow, seedFile is still null
147
}
148
}
149
150
// Try egd first.
151
if ((egdFile != null) && egdFile.canRead()) {
152
seedFile = egdFile;
153
} else {
154
// fall back to /dev/random.
155
seedFile = new File(NAME_RANDOM);
156
}
157
nextFile = new File(NAME_URANDOM);
158
break;
159
160
case BLOCKING:
161
seedFile = new File(NAME_RANDOM);
162
nextFile = new File(NAME_RANDOM);
163
break;
164
165
case NONBLOCKING:
166
seedFile = new File(NAME_URANDOM);
167
nextFile = new File(NAME_URANDOM);
168
break;
169
170
default:
171
// Shouldn't happen!
172
return null;
173
}
174
175
if (debug != null) {
176
debug.println("NativePRNG." + v +
177
" seedFile: " + seedFile +
178
" nextFile: " + nextFile);
179
}
180
181
if (!seedFile.canRead() || !nextFile.canRead()) {
182
if (debug != null) {
183
debug.println("NativePRNG." + v +
184
" Couldn't read Files.");
185
}
186
return null;
187
}
188
189
try {
190
return new RandomIO(seedFile, nextFile);
191
} catch (Exception e) {
192
return null;
193
}
194
}
195
});
196
}
197
198
// return whether the NativePRNG is available
199
static boolean isAvailable() {
200
return INSTANCE != null;
201
}
202
203
// constructor, called by the JCA framework
204
public NativePRNG() {
205
super();
206
if (INSTANCE == null) {
207
throw new AssertionError("NativePRNG not available");
208
}
209
}
210
211
// set the seed
212
@Override
213
protected void engineSetSeed(byte[] seed) {
214
INSTANCE.implSetSeed(seed);
215
}
216
217
// get pseudo random bytes
218
@Override
219
protected void engineNextBytes(byte[] bytes) {
220
INSTANCE.implNextBytes(bytes);
221
}
222
223
// get true random bytes
224
@Override
225
protected byte[] engineGenerateSeed(int numBytes) {
226
return INSTANCE.implGenerateSeed(numBytes);
227
}
228
229
/**
230
* A NativePRNG-like class that uses /dev/random for both
231
* seed and random material.
232
*
233
* Note that it does not respect the egd properties, since we have
234
* no way of knowing what those qualities are.
235
*
236
* This is very similar to the outer NativePRNG class, minimizing any
237
* breakage to the serialization of the existing implementation.
238
*
239
* @since 1.8
240
*/
241
public static final class Blocking extends SecureRandomSpi {
242
private static final long serialVersionUID = -6396183145759983347L;
243
244
private static final RandomIO INSTANCE = initIO(Variant.BLOCKING);
245
246
// return whether this is available
247
static boolean isAvailable() {
248
return INSTANCE != null;
249
}
250
251
// constructor, called by the JCA framework
252
public Blocking() {
253
super();
254
if (INSTANCE == null) {
255
throw new AssertionError("NativePRNG$Blocking not available");
256
}
257
}
258
259
// set the seed
260
@Override
261
protected void engineSetSeed(byte[] seed) {
262
INSTANCE.implSetSeed(seed);
263
}
264
265
// get pseudo random bytes
266
@Override
267
protected void engineNextBytes(byte[] bytes) {
268
INSTANCE.implNextBytes(bytes);
269
}
270
271
// get true random bytes
272
@Override
273
protected byte[] engineGenerateSeed(int numBytes) {
274
return INSTANCE.implGenerateSeed(numBytes);
275
}
276
}
277
278
/**
279
* A NativePRNG-like class that uses /dev/urandom for both
280
* seed and random material.
281
*
282
* Note that it does not respect the egd properties, since we have
283
* no way of knowing what those qualities are.
284
*
285
* This is very similar to the outer NativePRNG class, minimizing any
286
* breakage to the serialization of the existing implementation.
287
*
288
* @since 1.8
289
*/
290
public static final class NonBlocking extends SecureRandomSpi {
291
private static final long serialVersionUID = -1102062982994105487L;
292
293
private static final RandomIO INSTANCE = initIO(Variant.NONBLOCKING);
294
295
// return whether this is available
296
static boolean isAvailable() {
297
return INSTANCE != null;
298
}
299
300
// constructor, called by the JCA framework
301
public NonBlocking() {
302
super();
303
if (INSTANCE == null) {
304
throw new AssertionError(
305
"NativePRNG$NonBlocking not available");
306
}
307
}
308
309
// set the seed
310
@Override
311
protected void engineSetSeed(byte[] seed) {
312
INSTANCE.implSetSeed(seed);
313
}
314
315
// get pseudo random bytes
316
@Override
317
protected void engineNextBytes(byte[] bytes) {
318
INSTANCE.implNextBytes(bytes);
319
}
320
321
// get true random bytes
322
@Override
323
protected byte[] engineGenerateSeed(int numBytes) {
324
return INSTANCE.implGenerateSeed(numBytes);
325
}
326
}
327
328
/**
329
* Nested class doing the actual work. Singleton, see INSTANCE above.
330
*/
331
private static class RandomIO {
332
333
// we buffer data we read from the "next" file for efficiency,
334
// but we limit the lifetime to avoid using stale bits
335
// lifetime in ms, currently 100 ms (0.1 s)
336
private final static long MAX_BUFFER_TIME = 100;
337
338
// size of the "next" buffer
339
private static final int MAX_BUFFER_SIZE = 65536;
340
private static final int MIN_BUFFER_SIZE = 32;
341
private int bufferSize = 256;
342
343
// Holder for the seedFile. Used if we ever add seed material.
344
File seedFile;
345
346
// In/OutputStream for "seed" and "next"
347
private final InputStream seedIn, nextIn;
348
private OutputStream seedOut;
349
350
// flag indicating if we have tried to open seedOut yet
351
private boolean seedOutInitialized;
352
353
// SHA1PRNG instance for mixing
354
// initialized lazily on demand to avoid problems during startup
355
private volatile sun.security.provider.SecureRandom mixRandom;
356
357
// buffer for next bits
358
private byte[] nextBuffer;
359
360
// number of bytes left in nextBuffer
361
private int buffered;
362
363
// time we read the data into the nextBuffer
364
private long lastRead;
365
366
// Count for the number of buffer size changes requests
367
// Positive value in increase size, negative to lower it.
368
private int change_buffer = 0;
369
370
// Request limit to trigger an increase in nextBuffer size
371
private static final int REQ_LIMIT_INC = 1000;
372
373
// Request limit to trigger a decrease in nextBuffer size
374
private static final int REQ_LIMIT_DEC = -100;
375
376
// mutex lock for nextBytes()
377
private final Object LOCK_GET_BYTES = new Object();
378
379
// mutex lock for generateSeed()
380
private final Object LOCK_GET_SEED = new Object();
381
382
// mutex lock for setSeed()
383
private final Object LOCK_SET_SEED = new Object();
384
385
// constructor, called only once from initIO()
386
private RandomIO(File seedFile, File nextFile) throws IOException {
387
this.seedFile = seedFile;
388
seedIn = new FileInputStream(seedFile);
389
nextIn = new FileInputStream(nextFile);
390
nextBuffer = new byte[bufferSize];
391
}
392
393
// get the SHA1PRNG for mixing
394
// initialize if not yet created
395
private sun.security.provider.SecureRandom getMixRandom() {
396
sun.security.provider.SecureRandom r = mixRandom;
397
if (r == null) {
398
synchronized (LOCK_GET_BYTES) {
399
r = mixRandom;
400
if (r == null) {
401
r = new sun.security.provider.SecureRandom();
402
try {
403
byte[] b = new byte[20];
404
readFully(nextIn, b);
405
r.engineSetSeed(b);
406
} catch (IOException e) {
407
throw new ProviderException("init failed", e);
408
}
409
mixRandom = r;
410
}
411
}
412
}
413
return r;
414
}
415
416
// read data.length bytes from in
417
// These are not normal files, so we need to loop the read.
418
// just keep trying as long as we are making progress
419
private static void readFully(InputStream in, byte[] data)
420
throws IOException {
421
int len = data.length;
422
int ofs = 0;
423
while (len > 0) {
424
int k = in.read(data, ofs, len);
425
if (k <= 0) {
426
throw new EOFException("File(s) closed?");
427
}
428
ofs += k;
429
len -= k;
430
}
431
if (len > 0) {
432
throw new IOException("Could not read from file(s)");
433
}
434
}
435
436
// get true random bytes, just read from "seed"
437
private byte[] implGenerateSeed(int numBytes) {
438
synchronized (LOCK_GET_SEED) {
439
try {
440
byte[] b = new byte[numBytes];
441
readFully(seedIn, b);
442
return b;
443
} catch (IOException e) {
444
throw new ProviderException("generateSeed() failed", e);
445
}
446
}
447
}
448
449
// supply random bytes to the OS
450
// write to "seed" if possible
451
// always add the seed to our mixing random
452
private void implSetSeed(byte[] seed) {
453
synchronized (LOCK_SET_SEED) {
454
if (seedOutInitialized == false) {
455
seedOutInitialized = true;
456
seedOut = AccessController.doPrivileged(
457
new PrivilegedAction<OutputStream>() {
458
@Override
459
public OutputStream run() {
460
try {
461
return new FileOutputStream(seedFile, true);
462
} catch (Exception e) {
463
return null;
464
}
465
}
466
});
467
}
468
if (seedOut != null) {
469
try {
470
seedOut.write(seed);
471
} catch (IOException e) {
472
// Ignored. On Mac OS X, /dev/urandom can be opened
473
// for write, but actual write is not permitted.
474
}
475
}
476
getMixRandom().engineSetSeed(seed);
477
}
478
}
479
480
// ensure that there is at least one valid byte in the buffer
481
// if not, read new bytes
482
private void ensureBufferValid() throws IOException {
483
long time = System.currentTimeMillis();
484
int new_buffer_size = 0;
485
486
// Check if buffer has bytes available that are not too old
487
if (buffered > 0) {
488
if (time - lastRead < MAX_BUFFER_TIME) {
489
return;
490
} else {
491
// byte is old, so subtract from counter to shrink buffer
492
change_buffer--;
493
}
494
} else {
495
// No bytes available, so add to count to increase buffer
496
change_buffer++;
497
}
498
499
// If counter has it a limit, increase or decrease size
500
if (change_buffer > REQ_LIMIT_INC) {
501
new_buffer_size = nextBuffer.length * 2;
502
} else if (change_buffer < REQ_LIMIT_DEC) {
503
new_buffer_size = nextBuffer.length / 2;
504
}
505
506
// If buffer size is to be changed, replace nextBuffer.
507
if (new_buffer_size > 0) {
508
if (new_buffer_size <= MAX_BUFFER_SIZE &&
509
new_buffer_size >= MIN_BUFFER_SIZE) {
510
nextBuffer = new byte[new_buffer_size];
511
if (debug != null) {
512
debug.println("Buffer size changed to " +
513
new_buffer_size);
514
}
515
} else {
516
if (debug != null) {
517
debug.println("Buffer reached limit: " +
518
nextBuffer.length);
519
}
520
}
521
change_buffer = 0;
522
}
523
524
// Load fresh random bytes into nextBuffer
525
lastRead = time;
526
readFully(nextIn, nextBuffer);
527
buffered = nextBuffer.length;
528
}
529
530
// get pseudo random bytes
531
// read from "next" and XOR with bytes generated by the
532
// mixing SHA1PRNG
533
private void implNextBytes(byte[] data) {
534
try {
535
getMixRandom().engineNextBytes(data);
536
int data_len = data.length;
537
int ofs = 0;
538
int len;
539
int buf_pos;
540
int localofs;
541
byte[] localBuffer;
542
543
while (data_len > 0) {
544
synchronized (LOCK_GET_BYTES) {
545
ensureBufferValid();
546
buf_pos = nextBuffer.length - buffered;
547
if (data_len > buffered) {
548
len = buffered;
549
buffered = 0;
550
} else {
551
len = data_len;
552
buffered -= len;
553
}
554
localBuffer = Arrays.copyOfRange(nextBuffer, buf_pos,
555
buf_pos + len);
556
}
557
localofs = 0;
558
while (len > localofs) {
559
data[ofs] ^= localBuffer[localofs];
560
ofs++;
561
localofs++;
562
}
563
data_len -= len;
564
}
565
} catch (IOException e){
566
throw new ProviderException("nextBytes() failed", e);
567
}
568
}
569
}
570
}
571
572