Home | History | Annotate | Download | only in crypto
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 
     19 package org.apache.harmony.security.provider.crypto;
     20 
     21 
     22 import java.io.File;
     23 import java.io.FileInputStream;
     24 import java.io.FileNotFoundException;
     25 import java.io.IOException;
     26 import java.security.ProviderException;
     27 
     28 /**
     29  *  The static class providing access on Linux platform
     30  *  to system means for generating true random bits. <BR>
     31  *
     32  *  The source for true random bits is one of Linux's devices "/dev/urandom" or
     33  *  "/dev/random" depends on which one is available; if both the first is used. <BR>
     34  *
     35  *  If no device available the service is not available,
     36  *  that is, provider shouldn't register the algorithm. <BR>
     37  */
     38 public class RandomBitsSupplier implements SHA1_Data {
     39 
     40 
     41     /**
     42      * InputStream to read from device
     43      *
     44      * Using a BufferedInputStream leads to problems
     45      * on Android in rare cases, since the
     46      * BufferedInputStream's available() issues an
     47      * ioctl(), and the pseudo device doesn't seem
     48      * to like that. Since we're reading bigger
     49      * chunks and not single bytes, the FileInputStream
     50      * shouldn't be slower, so we use that. Same might
     51      * apply to other Linux platforms.
     52      *
     53      * TODO: the above doesn't sound true.
     54      */
     55     private static FileInputStream fis = null;
     56 
     57     /**
     58      * File to connect to device
     59      */
     60     private static File randomFile = null;
     61 
     62     /**
     63      * value of field is "true" only if a device is available
     64      */
     65     private static boolean serviceAvailable = false;
     66 
     67     /**
     68      *  names of random devices on Linux platform
     69      */
     70     private static final String DEVICE_NAMES[] = { "/dev/urandom" /*, "/dev/random" */ };
     71 
     72     static {
     73         for (String deviceName : DEVICE_NAMES) {
     74             try {
     75                 File file = new File(deviceName);
     76                 if (file.canRead()) {
     77                     fis = new FileInputStream(file);
     78                     randomFile = file;
     79                     serviceAvailable = true;
     80                 }
     81             } catch (FileNotFoundException e) {
     82             }
     83         }
     84     }
     85 
     86 
     87     /**
     88      * The method is called by provider to determine if a device is available.
     89      */
     90     static boolean isServiceAvailable() {
     91         return serviceAvailable;
     92     }
     93 
     94 
     95     /**
     96      * On platforms with "random" devices available,
     97      * the method reads random bytes from the device.  <BR>
     98      *
     99      * In case of any runtime failure ProviderException gets thrown.
    100      */
    101     private static synchronized byte[] getUnixDeviceRandom(int numBytes) {
    102 
    103         byte[] bytes = new byte[numBytes];
    104 
    105         int total = 0;
    106         int bytesRead;
    107         int offset = 0;
    108         try {
    109             for ( ; ; ) {
    110 
    111                 bytesRead = fis.read(bytes, offset, numBytes-total);
    112 
    113 
    114                 // the below case should not occur because /dev/random or /dev/urandom is a special file
    115                 // hence, if it is happened there is some internal problem
    116                 if ( bytesRead == -1 ) {
    117                     throw new ProviderException("bytesRead == -1");
    118                 }
    119 
    120                 total  += bytesRead;
    121                 offset += bytesRead;
    122 
    123                 if ( total >= numBytes ) {
    124                     break;
    125                 }
    126             }
    127         } catch (IOException e) {
    128 
    129             // actually there should be no IOException because device is a special file;
    130             // hence, there is either some internal problem or, for instance,
    131             // device was removed in runtime, or something else
    132             throw new ProviderException("ATTENTION: IOException in RandomBitsSupplier.getLinuxRandomBits(): " + e);
    133         }
    134         return bytes;
    135     }
    136 
    137     /**
    138      * The method returns byte array of requested length provided service is available.
    139      * ProviderException gets thrown otherwise.
    140      *
    141      * @param
    142      *       numBytes - length of bytes requested
    143      * @return
    144      *       byte array
    145      * @throws
    146      *       InvalidArgumentException - if numBytes <= 0
    147      */
    148     public static byte[] getRandomBits(int numBytes) {
    149         if (numBytes <= 0) {
    150             throw new IllegalArgumentException(Integer.toString(numBytes));
    151         }
    152 
    153         // We have been unable to get a random device or fall back to the
    154         // native security module code - throw an exception.
    155         if ( !serviceAvailable ) {
    156             throw new ProviderException("ATTENTION: service is not available : no random devices");
    157         }
    158 
    159         return getUnixDeviceRandom(numBytes);
    160     }
    161 }
    162