Save This Page
Home » openjdk-7 » sun.security » provider » [javadoc | source]
    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   }

Save This Page
Home » openjdk-7 » sun.security » provider » [javadoc | source]