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+
26+package 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+
66+import java.security.*;
67+import java.io.*;
68+import java.util.Properties;
69+import java.util.Enumeration;
70+import java.net.*;
71+import java.nio.file.DirectoryStream;
72+import java.nio.file.Files;
73+import java.nio.file.Path;
74+import java.util.Random;
75+import sun.security.util.Debug;
76+
77+abstract 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+}