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