PRNG fixes were a nop ever since we switched to 4.4+

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/utils/PRNGFixes.java | 327 ---------
1 file changed, 327 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/utils/PRNGFixes.java 🔗

@@ -1,327 +0,0 @@
-package eu.siacs.conversations.utils;
-
-import android.os.Build;
-import android.os.Process;
-import android.util.Log;
-
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
-import java.security.NoSuchAlgorithmException;
-import java.security.Provider;
-import java.security.SecureRandom;
-import java.security.SecureRandomSpi;
-import java.security.Security;
-
-/**
- * Fixes for the output of the default PRNG having low entropy.
- * 
- * The fixes need to be applied via {@link #apply()} before any use of Java
- * Cryptography Architecture primitives. A good place to invoke them is in the
- * application's {@code onCreate}.
- */
-public final class PRNGFixes {
-
-	private static final int VERSION_CODE_JELLY_BEAN = 16;
-	private static final int VERSION_CODE_JELLY_BEAN_MR2 = 18;
-	private static final byte[] BUILD_FINGERPRINT_AND_DEVICE_SERIAL = getBuildFingerprintAndDeviceSerial();
-
-	/** Hidden constructor to prevent instantiation. */
-	private PRNGFixes() {
-	}
-
-	/**
-	 * Applies all fixes.
-	 * 
-	 * @throws SecurityException
-	 *             if a fix is needed but could not be applied.
-	 */
-	public static void apply() {
-		applyOpenSSLFix();
-		installLinuxPRNGSecureRandom();
-	}
-
-	/**
-	 * Applies the fix for OpenSSL PRNG having low entropy. Does nothing if the
-	 * fix is not needed.
-	 * 
-	 * @throws SecurityException
-	 *             if the fix is needed but could not be applied.
-	 */
-	private static void applyOpenSSLFix() throws SecurityException {
-		if ((Build.VERSION.SDK_INT < VERSION_CODE_JELLY_BEAN)
-				|| (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2)) {
-			// No need to apply the fix
-			return;
-		}
-
-		try {
-			// Mix in the device- and invocation-specific seed.
-			Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
-					.getMethod("RAND_seed", byte[].class)
-					.invoke(null, generateSeed());
-
-			// Mix output of Linux PRNG into OpenSSL's PRNG
-			int bytesRead = (Integer) Class
-					.forName(
-							"org.apache.harmony.xnet.provider.jsse.NativeCrypto")
-					.getMethod("RAND_load_file", String.class, long.class)
-					.invoke(null, "/dev/urandom", 1024);
-			if (bytesRead != 1024) {
-				throw new IOException(
-						"Unexpected number of bytes read from Linux PRNG: "
-								+ bytesRead);
-			}
-		} catch (Exception e) {
-			throw new SecurityException("Failed to seed OpenSSL PRNG", e);
-		}
-	}
-
-	/**
-	 * Installs a Linux PRNG-backed {@code SecureRandom} implementation as the
-	 * default. Does nothing if the implementation is already the default or if
-	 * there is not need to install the implementation.
-	 * 
-	 * @throws SecurityException
-	 *             if the fix is needed but could not be applied.
-	 */
-	private static void installLinuxPRNGSecureRandom() throws SecurityException {
-		if (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2) {
-			// No need to apply the fix
-			return;
-		}
-
-		// Install a Linux PRNG-based SecureRandom implementation as the
-		// default, if not yet installed.
-		Provider[] secureRandomProviders = Security
-				.getProviders("SecureRandom.SHA1PRNG");
-		if ((secureRandomProviders == null)
-				|| (secureRandomProviders.length < 1)
-				|| (!LinuxPRNGSecureRandomProvider.class
-						.equals(secureRandomProviders[0].getClass()))) {
-			Security.insertProviderAt(new LinuxPRNGSecureRandomProvider(), 1);
-		}
-
-		// Assert that new SecureRandom() and
-		// SecureRandom.getInstance("SHA1PRNG") return a SecureRandom backed
-		// by the Linux PRNG-based SecureRandom implementation.
-		SecureRandom rng1 = new SecureRandom();
-		if (!LinuxPRNGSecureRandomProvider.class.equals(rng1.getProvider()
-				.getClass())) {
-			throw new SecurityException(
-					"new SecureRandom() backed by wrong Provider: "
-							+ rng1.getProvider().getClass());
-		}
-
-		SecureRandom rng2;
-		try {
-			rng2 = SecureRandom.getInstance("SHA1PRNG");
-		} catch (NoSuchAlgorithmException e) {
-			throw new SecurityException("SHA1PRNG not available", e);
-		}
-		if (!LinuxPRNGSecureRandomProvider.class.equals(rng2.getProvider()
-				.getClass())) {
-			throw new SecurityException(
-					"SecureRandom.getInstance(\"SHA1PRNG\") backed by wrong"
-							+ " Provider: " + rng2.getProvider().getClass());
-		}
-	}
-
-	/**
-	 * {@code Provider} of {@code SecureRandom} engines which pass through all
-	 * requests to the Linux PRNG.
-	 */
-	private static class LinuxPRNGSecureRandomProvider extends Provider {
-
-		public LinuxPRNGSecureRandomProvider() {
-			super("LinuxPRNG", 1.0,
-					"A Linux-specific random number provider that uses"
-							+ " /dev/urandom");
-			// Although /dev/urandom is not a SHA-1 PRNG, some apps
-			// explicitly request a SHA1PRNG SecureRandom and we thus need to
-			// prevent them from getting the default implementation whose output
-			// may have low entropy.
-			put("SecureRandom.SHA1PRNG", LinuxPRNGSecureRandom.class.getName());
-			put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
-		}
-	}
-
-	/**
-	 * {@link SecureRandomSpi} which passes all requests to the Linux PRNG (
-	 * {@code /dev/urandom}).
-	 */
-	public static class LinuxPRNGSecureRandom extends SecureRandomSpi {
-
-		/*
-		 * IMPLEMENTATION NOTE: Requests to generate bytes and to mix in a seed
-		 * are passed through to the Linux PRNG (/dev/urandom). Instances of
-		 * this class seed themselves by mixing in the current time, PID, UID,
-		 * build fingerprint, and hardware serial number (where available) into
-		 * Linux PRNG.
-		 * 
-		 * Concurrency: Read requests to the underlying Linux PRNG are
-		 * serialized (on sLock) to ensure that multiple threads do not get
-		 * duplicated PRNG output.
-		 */
-
-		private static final File URANDOM_FILE = new File("/dev/urandom");
-
-		private static final Object sLock = new Object();
-
-		/**
-		 * Input stream for reading from Linux PRNG or {@code null} if not yet
-		 * opened.
-		 * 
-		 * @GuardedBy("sLock")
-		 */
-		private static DataInputStream sUrandomIn;
-
-		/**
-		 * Output stream for writing to Linux PRNG or {@code null} if not yet
-		 * opened.
-		 * 
-		 * @GuardedBy("sLock")
-		 */
-		private static OutputStream sUrandomOut;
-
-		/**
-		 * Whether this engine instance has been seeded. This is needed because
-		 * each instance needs to seed itself if the client does not explicitly
-		 * seed it.
-		 */
-		private boolean mSeeded;
-
-		@Override
-		protected void engineSetSeed(byte[] bytes) {
-			try {
-				OutputStream out;
-				synchronized (sLock) {
-					out = getUrandomOutputStream();
-				}
-				out.write(bytes);
-				out.flush();
-			} catch (IOException e) {
-				// On a small fraction of devices /dev/urandom is not writable.
-				// Log and ignore.
-				Log.w(PRNGFixes.class.getSimpleName(),
-						"Failed to mix seed into " + URANDOM_FILE);
-			} finally {
-				mSeeded = true;
-			}
-		}
-
-		@Override
-		protected void engineNextBytes(byte[] bytes) {
-			if (!mSeeded) {
-				// Mix in the device- and invocation-specific seed.
-				engineSetSeed(generateSeed());
-			}
-
-			try {
-				DataInputStream in;
-				synchronized (sLock) {
-					in = getUrandomInputStream();
-				}
-				synchronized (in) {
-					in.readFully(bytes);
-				}
-			} catch (IOException e) {
-				throw new SecurityException("Failed to read from "
-						+ URANDOM_FILE, e);
-			}
-		}
-
-		@Override
-		protected byte[] engineGenerateSeed(int size) {
-			byte[] seed = new byte[size];
-			engineNextBytes(seed);
-			return seed;
-		}
-
-		private DataInputStream getUrandomInputStream() {
-			synchronized (sLock) {
-				if (sUrandomIn == null) {
-					// NOTE: Consider inserting a BufferedInputStream between
-					// DataInputStream and FileInputStream if you need higher
-					// PRNG output performance and can live with future PRNG
-					// output being pulled into this process prematurely.
-					try {
-						sUrandomIn = new DataInputStream(new FileInputStream(
-								URANDOM_FILE));
-					} catch (IOException e) {
-						throw new SecurityException("Failed to open "
-								+ URANDOM_FILE + " for reading", e);
-					}
-				}
-				return sUrandomIn;
-			}
-		}
-
-		private OutputStream getUrandomOutputStream() throws IOException {
-			synchronized (sLock) {
-				if (sUrandomOut == null) {
-					sUrandomOut = new FileOutputStream(URANDOM_FILE);
-				}
-				return sUrandomOut;
-			}
-		}
-	}
-
-	/**
-	 * Generates a device- and invocation-specific seed to be mixed into the
-	 * Linux PRNG.
-	 */
-	private static byte[] generateSeed() {
-		try {
-			ByteArrayOutputStream seedBuffer = new ByteArrayOutputStream();
-			DataOutputStream seedBufferOut = new DataOutputStream(seedBuffer);
-			seedBufferOut.writeLong(System.currentTimeMillis());
-			seedBufferOut.writeLong(System.nanoTime());
-			seedBufferOut.writeInt(Process.myPid());
-			seedBufferOut.writeInt(Process.myUid());
-			seedBufferOut.write(BUILD_FINGERPRINT_AND_DEVICE_SERIAL);
-			seedBufferOut.close();
-			return seedBuffer.toByteArray();
-		} catch (IOException e) {
-			throw new SecurityException("Failed to generate seed", e);
-		}
-	}
-
-	/**
-	 * Gets the hardware serial number of this device.
-	 * 
-	 * @return serial number or {@code null} if not available.
-	 */
-	private static String getDeviceSerialNumber() {
-		// We're using the Reflection API because Build.SERIAL is only available
-		// since API Level 9 (Gingerbread, Android 2.3).
-		try {
-			return (String) Build.class.getField("SERIAL").get(null);
-		} catch (Exception ignored) {
-			return null;
-		}
-	}
-
-	private static byte[] getBuildFingerprintAndDeviceSerial() {
-		StringBuilder result = new StringBuilder();
-		String fingerprint = Build.FINGERPRINT;
-		if (fingerprint != null) {
-			result.append(fingerprint);
-		}
-		String serial = getDeviceSerialNumber();
-		if (serial != null) {
-			result.append(serial);
-		}
-		try {
-			return result.toString().getBytes("UTF-8");
-		} catch (UnsupportedEncodingException e) {
-			throw new RuntimeException("UTF-8 encoding not supported");
-		}
-	}
-}