blob: 993914a8e5f42d8eba325c636752e2da320b45ce [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.harmony.security.provider.crypto;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.AccessController;
import java.security.ProviderException;
/**
* The static class providing access on Linux platform
* to system means for generating true random bits. <BR>
*
* The source for true random bits is one of Linux's devices "/dev/urandom" or
* "/dev/random" depends on which one is available; if both the first is used. <BR>
*
* If no device available the service is not available,
* that is, provider shouldn't register the algorithm. <BR>
*/
public class RandomBitsSupplier implements SHA1_Data {
/**
* BufferedInputStream to read from device
*/
// BEGIN android-changed
// Using a BufferedInputStream leads to problems
// on Android in rare cases, since the
// BufferedInputStream's available() issues an
// iotcl(), and the pseudo device doesn't seem
// to like that. Since we're reading bigger
// chunks and not single bytes, the FileInputStream
// shouldn't be slower, so we use that. Same might
// apply to other Linux platforms.
private static FileInputStream bis = null;
// END android-changed
/**
* File to connect to device
*/
private static File randomFile = null;
/**
* value of field is "true" only if a device is available
*/
private static boolean serviceAvailable = false;
static {
AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run() {
for ( int i = 0 ; i < DEVICE_NAMES.length ; i++ ) {
File file = new File(DEVICE_NAMES[i]);
try {
if ( file.canRead() ) {
// BEGIN android-modified
bis = new FileInputStream(file);
// END android-modified
randomFile = file;
serviceAvailable = true;
return null;
}
} catch (FileNotFoundException e) {
}
}
// BEGIN android-removed
// // If we have come out of the above loop, then we have been unable to
// // access /dev/*random, so try to fall back to using the system random() API
// try {
// System.loadLibrary(LIBRARY_NAME);
// serviceAvailable = true;
// } catch (UnsatisfiedLinkError e) {
// serviceAvailable = false;
// }
return null;
}
}
);
}
/**
* The method is called by provider to determine if a device is available.
*/
static boolean isServiceAvailable() {
return serviceAvailable;
}
/**
* On platforms with "random" devices available,
* the method reads random bytes from the device. <BR>
*
* In case of any runtime failure ProviderException gets thrown.
*/
private static synchronized byte[] getUnixDeviceRandom(int numBytes) {
byte[] bytes = new byte[numBytes];
int total = 0;
int bytesRead;
int offset = 0;
try {
for ( ; ; ) {
bytesRead = bis.read(bytes, offset, numBytes-total);
// the below case should not occur because /dev/random or /dev/urandom is a special file
// hence, if it is happened there is some internal problem
if ( bytesRead == -1 ) {
throw new ProviderException("bytesRead == -1");
}
total += bytesRead;
offset += bytesRead;
if ( total >= numBytes ) {
break;
}
}
} catch (IOException e) {
// actually there should be no IOException because device is a special file;
// hence, there is either some internal problem or, for instance,
// device was removed in runtime, or something else
throw new ProviderException("ATTENTION: IOException in RandomBitsSupplier.getLinuxRandomBits(): " + e);
}
return bytes;
}
// BEGIN android-removed
// /**
// * On platforms with no "random" devices available, this native
// * method uses system API calls to generate random numbers<BR>
// *
// * In case of any runtime failure ProviderException gets thrown.
// */
// private static native synchronized boolean getUnixSystemRandom(byte[] randomBits, int numBytes);
// END android-removed
/**
* The method returns byte array of requested length provided service is available.
* ProviderException gets thrown otherwise.
*
* @param
* numBytes - length of bytes requested
* @return
* byte array
* @throws
* InvalidArgumentException - if numBytes <= 0
*/
public static byte[] getRandomBits(int numBytes) {
if (numBytes <= 0) {
throw new IllegalArgumentException(Integer.toString(numBytes));
}
// We have been unable to get a random device or fall back to the
// native security module code - throw an exception.
if ( !serviceAvailable ) {
throw new ProviderException("ATTENTION: service is not available : no random devices");
}
// BEGIN android-changed
return getUnixDeviceRandom(numBytes);
// END android-changed
}
}