History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/security/provider/SeedGenerator.java (1 of 18352 files). Show all

Unified · Split

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