Merge branch '914-simpler-secure-random' into 'master'

Remove Fortuna generator, fix Android SecureRandom bug

Closes #914

See merge request !500
This commit is contained in:
akwizgran
2017-04-05 10:55:25 +00:00
35 changed files with 663 additions and 816 deletions

View File

@@ -1,62 +0,0 @@
package org.briarproject.bramble.crypto;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.SecureRandomSpi;
/**
* A {@link SecureRandom} implementation that combines the outputs of two or
* more other implementations using XOR.
*/
class CombinedSecureRandom extends SecureRandom {
private static final Provider PROVIDER = new CombinedProvider();
CombinedSecureRandom(SecureRandom... randoms) {
super(new CombinedSecureRandomSpi(randoms), PROVIDER);
}
private static class CombinedSecureRandomSpi extends SecureRandomSpi {
private final SecureRandom[] randoms;
private CombinedSecureRandomSpi(SecureRandom... randoms) {
if (randoms.length < 2) throw new IllegalArgumentException();
this.randoms = randoms;
}
@Override
protected byte[] engineGenerateSeed(int numBytes) {
byte[] combined = new byte[numBytes];
for (SecureRandom random : randoms) {
byte[] b = random.generateSeed(numBytes);
int length = Math.min(numBytes, b.length);
for (int i = 0; i < length; i++)
combined[i] = (byte) (combined[i] ^ b[i]);
}
return combined;
}
@Override
protected void engineNextBytes(byte[] b) {
byte[] temp = new byte[b.length];
for (SecureRandom random : randoms) {
random.nextBytes(temp);
for (int i = 0; i < b.length; i++)
b[i] = (byte) (b[i] ^ temp[i]);
}
}
@Override
protected void engineSetSeed(byte[] seed) {
for (SecureRandom random : randoms) random.setSeed(seed);
}
}
private static class CombinedProvider extends Provider {
private CombinedProvider() {
super("Combined", 1.0, "");
}
}
}

View File

@@ -8,7 +8,7 @@ import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.system.SeedProvider;
import org.briarproject.bramble.api.system.SecureRandomProvider;
import org.briarproject.bramble.api.transport.IncomingKeys;
import org.briarproject.bramble.api.transport.OutgoingKeys;
import org.briarproject.bramble.api.transport.TransportKeys;
@@ -29,7 +29,10 @@ import org.spongycastle.crypto.params.KeyParameter;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -101,16 +104,26 @@ class CryptoComponentImpl implements CryptoComponent {
private final MessageEncrypter messageEncrypter;
@Inject
CryptoComponentImpl(SeedProvider seedProvider) {
if (!FortunaSecureRandom.selfTest()) throw new RuntimeException();
SecureRandom platformSecureRandom = new SecureRandom();
CryptoComponentImpl(SecureRandomProvider secureRandomProvider) {
if (LOG.isLoggable(INFO)) {
String provider = platformSecureRandom.getProvider().getName();
String algorithm = platformSecureRandom.getAlgorithm();
LOG.info("Default SecureRandom: " + provider + " " + algorithm);
SecureRandom defaultSecureRandom = new SecureRandom();
String name = defaultSecureRandom.getProvider().getName();
String algorithm = defaultSecureRandom.getAlgorithm();
LOG.info("Default SecureRandom: " + name + " " + algorithm);
}
SecureRandom fortuna = new FortunaSecureRandom(seedProvider.getSeed());
secureRandom = new CombinedSecureRandom(platformSecureRandom, fortuna);
Provider provider = secureRandomProvider.getProvider();
if (provider == null) {
LOG.info("Using default");
} else {
installSecureRandomProvider(provider);
if (LOG.isLoggable(INFO)) {
SecureRandom installedSecureRandom = new SecureRandom();
String name = installedSecureRandom.getProvider().getName();
String algorithm = installedSecureRandom.getAlgorithm();
LOG.info("Installed SecureRandom: " + name + " " + algorithm);
}
}
secureRandom = new SecureRandom();
ECKeyGenerationParameters params = new ECKeyGenerationParameters(
PARAMETERS, secureRandom);
agreementKeyPairGenerator = new ECKeyPairGenerator();
@@ -124,6 +137,31 @@ class CryptoComponentImpl implements CryptoComponent {
messageEncrypter = new MessageEncrypter(secureRandom);
}
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
private void installSecureRandomProvider(Provider provider) {
Provider[] providers = Security.getProviders("SecureRandom.SHA1PRNG");
if (providers == null || providers.length == 0
|| !provider.getClass().equals(providers[0].getClass())) {
Security.insertProviderAt(provider, 1);
}
// Check the new provider is the default when no algorithm is specified
SecureRandom random = new SecureRandom();
if (!provider.getClass().equals(random.getProvider().getClass())) {
throw new SecurityException("Wrong SecureRandom provider: "
+ random.getProvider().getClass());
}
// Check the new provider is the default when SHA1PRNG is specified
try {
random = SecureRandom.getInstance("SHA1PRNG");
} catch (NoSuchAlgorithmException e) {
throw new SecurityException(e);
}
if (!provider.getClass().equals(random.getProvider().getClass())) {
throw new SecurityException("Wrong SHA1PRNG provider: "
+ random.getProvider().getClass());
}
}
@Override
public SecretKey generateSecretKey() {
byte[] b = new byte[SecretKey.LENGTH];
@@ -133,7 +171,10 @@ class CryptoComponentImpl implements CryptoComponent {
@Override
public PseudoRandom getPseudoRandom(int seed1, int seed2) {
return new PseudoRandomImpl(seed1, seed2);
byte[] seed = new byte[INT_32_BYTES * 2];
ByteUtils.writeUint32(seed1, seed, 0);
ByteUtils.writeUint32(seed2, seed, INT_32_BYTES);
return new PseudoRandomImpl(seed);
}
@Override
@@ -296,7 +337,7 @@ class CryptoComponentImpl implements CryptoComponent {
public SecretKey deriveMasterSecret(byte[] theirPublicKey,
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
return deriveMasterSecret(deriveSharedSecret(
theirPublicKey,ourKeyPair, alice));
theirPublicKey, ourKeyPair, alice));
}
@Override
@@ -607,7 +648,7 @@ class CryptoComponentImpl implements CryptoComponent {
}
private long sampleRunningTime(int iterations) {
byte[] password = { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' };
byte[] password = {'p', 'a', 's', 's', 'w', 'o', 'r', 'd'};
byte[] salt = new byte[PBKDF_SALT_BYTES];
int keyLengthInBits = SecretKey.LENGTH * 8;
long start = System.nanoTime();

View File

@@ -6,7 +6,7 @@ import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.system.SeedProvider;
import org.briarproject.bramble.api.system.SecureRandomProvider;
import java.security.SecureRandom;
import java.util.concurrent.BlockingQueue;
@@ -60,8 +60,9 @@ public class CryptoModule {
@Provides
@Singleton
CryptoComponent provideCryptoComponent(SeedProvider seedProvider) {
return new CryptoComponentImpl(seedProvider);
CryptoComponent provideCryptoComponent(
SecureRandomProvider secureRandomProvider) {
return new CryptoComponentImpl(secureRandomProvider);
}
@Provides

View File

@@ -1,76 +0,0 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.spongycastle.crypto.Digest;
import javax.annotation.concurrent.NotThreadSafe;
/**
* A message digest that prevents length extension attacks - see Ferguson and
* Schneier, <i>Practical Cryptography</i>, chapter 6.
* <p>
* "Let h be an interative hash function. The hash function h<sub>d</sub> is
* defined by h<sub>d</sub> := h(h(m)), and has a claimed security level of
* min(k, n/2) where k is the security level of h and n is the size of the hash
* result."
*/
@NotThreadSafe
@NotNullByDefault
class DoubleDigest implements Digest {
private final Digest delegate;
DoubleDigest(Digest delegate) {
this.delegate = delegate;
}
private byte[] digest() {
byte[] digest = new byte[delegate.getDigestSize()];
delegate.doFinal(digest, 0); // h(m)
delegate.update(digest, 0, digest.length);
delegate.doFinal(digest, 0); // h(h(m))
return digest;
}
public int digest(byte[] buf, int offset, int len) {
byte[] digest = digest();
len = Math.min(len, digest.length);
System.arraycopy(digest, 0, buf, offset, len);
return len;
}
@Override
public int getDigestSize() {
return delegate.getDigestSize();
}
@Override
public String getAlgorithmName() {
return "Double " + delegate.getAlgorithmName();
}
@Override
public void reset() {
delegate.reset();
}
@Override
public void update(byte input) {
delegate.update(input);
}
public void update(byte[] input) {
delegate.update(input, 0, input.length);
}
@Override
public void update(byte[] input, int offset, int len) {
delegate.update(input, offset, len);
}
@Override
public int doFinal(byte[] out, int outOff) {
return digest(out, outOff, delegate.getDigestSize());
}
}

View File

@@ -1,114 +0,0 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.spongycastle.crypto.BlockCipher;
import org.spongycastle.crypto.digests.SHA256Digest;
import org.spongycastle.crypto.engines.AESLightEngine;
import org.spongycastle.crypto.params.KeyParameter;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.annotation.concurrent.ThreadSafe;
/**
* Implements the Fortuna pseudo-random number generator, as described in
* Ferguson and Schneier, <i>Practical Cryptography</i>, chapter 9.
*/
@ThreadSafe
@NotNullByDefault
class FortunaGenerator {
private static final int MAX_BYTES_PER_REQUEST = 1024 * 1024;
private static final int KEY_BYTES = 32;
private static final int BLOCK_BYTES = 16;
private final Lock lock = new ReentrantLock();
// The following are locking: lock
private final DoubleDigest digest = new DoubleDigest(new SHA256Digest());
private final BlockCipher cipher = new AESLightEngine();
private final byte[] key = new byte[KEY_BYTES];
private final byte[] counter = new byte[BLOCK_BYTES];
private final byte[] buffer = new byte[BLOCK_BYTES];
private final byte[] newKey = new byte[KEY_BYTES];
FortunaGenerator(byte[] seed) {
reseed(seed);
}
void reseed(byte[] seed) {
lock.lock();
try {
digest.update(key);
digest.update(seed);
digest.digest(key, 0, KEY_BYTES);
incrementCounter();
} finally {
lock.unlock();
}
}
// Package access for testing
void incrementCounter() {
lock.lock();
try {
counter[0]++;
for (int i = 0; counter[i] == 0; i++) {
if (i + 1 == BLOCK_BYTES)
throw new RuntimeException("Counter exhausted");
counter[i + 1]++;
}
} finally {
lock.unlock();
}
}
// Package access for testing
byte[] getCounter() {
lock.lock();
try {
return counter;
} finally {
lock.unlock();
}
}
int nextBytes(byte[] dest, int off, int len) {
lock.lock();
try {
// Don't write more than the maximum number of bytes in one request
if (len > MAX_BYTES_PER_REQUEST) len = MAX_BYTES_PER_REQUEST;
cipher.init(true, new KeyParameter(key));
// Generate full blocks directly into the output buffer
int fullBlocks = len / BLOCK_BYTES;
for (int i = 0; i < fullBlocks; i++) {
cipher.processBlock(counter, 0, dest, off + i * BLOCK_BYTES);
incrementCounter();
}
// Generate a partial block if needed
int done = fullBlocks * BLOCK_BYTES, remaining = len - done;
if (remaining >= BLOCK_BYTES) throw new AssertionError();
if (remaining > 0) {
cipher.processBlock(counter, 0, buffer, 0);
incrementCounter();
// Copy the partial block to the output buffer and erase our copy
System.arraycopy(buffer, 0, dest, off + done, remaining);
for (int i = 0; i < BLOCK_BYTES; i++) buffer[i] = 0;
}
// Generate a new key
for (int i = 0; i < KEY_BYTES / BLOCK_BYTES; i++) {
cipher.processBlock(counter, 0, newKey, i * BLOCK_BYTES);
incrementCounter();
}
System.arraycopy(newKey, 0, key, 0, KEY_BYTES);
for (int i = 0; i < KEY_BYTES; i++) newKey[i] = 0;
// Return the number of bytes written
return len;
} finally {
lock.unlock();
}
}
}

View File

@@ -1,81 +0,0 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.util.StringUtils;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.SecureRandomSpi;
import java.util.Arrays;
/**
* A {@link java.security.SecureRandom SecureRandom} implementation based on a
* {@link FortunaGenerator}.
*/
class FortunaSecureRandom extends SecureRandom {
// Package access for testing
static final byte[] SELF_TEST_VECTOR_1 =
StringUtils.fromHexString("4BD6EA599D47E3EE9DD911833C29CA22");
static final byte[] SELF_TEST_VECTOR_2 =
StringUtils.fromHexString("10984D576E6850E505CA9F42A9BFD88A");
static final byte[] SELF_TEST_VECTOR_3 =
StringUtils.fromHexString("1E12DA166BD86DCECDE50A8296018DE2");
private static final Provider PROVIDER = new FortunaProvider();
FortunaSecureRandom(byte[] seed) {
super(new FortunaSecureRandomSpi(seed), PROVIDER);
}
/**
* Tests that the {@link #nextBytes(byte[])} and {@link #setSeed(byte[])}
* methods are passed through to the generator in the expected way.
*/
static boolean selfTest() {
byte[] seed = new byte[32];
SecureRandom r = new FortunaSecureRandom(seed);
byte[] output = new byte[16];
r.nextBytes(output);
if (!Arrays.equals(SELF_TEST_VECTOR_1, output)) return false;
r.nextBytes(output);
if (!Arrays.equals(SELF_TEST_VECTOR_2, output)) return false;
r.setSeed(seed);
r.nextBytes(output);
return Arrays.equals(SELF_TEST_VECTOR_3, output);
}
private static class FortunaSecureRandomSpi extends SecureRandomSpi {
private final FortunaGenerator generator;
private FortunaSecureRandomSpi(byte[] seed) {
generator = new FortunaGenerator(seed);
}
@Override
protected byte[] engineGenerateSeed(int numBytes) {
byte[] b = new byte[numBytes];
engineNextBytes(b);
return b;
}
@Override
protected void engineNextBytes(byte[] b) {
int offset = 0;
while (offset < b.length)
offset += generator.nextBytes(b, offset, b.length - offset);
}
@Override
protected void engineSetSeed(byte[] seed) {
generator.reseed(seed);
}
}
private static class FortunaProvider extends Provider {
private FortunaProvider() {
super("Fortuna", 1.0, "");
}
}
}

View File

@@ -2,30 +2,34 @@ package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.util.ByteUtils;
import org.spongycastle.crypto.Digest;
import org.spongycastle.crypto.engines.Salsa20Engine;
import org.spongycastle.crypto.params.KeyParameter;
import org.spongycastle.crypto.params.ParametersWithIV;
import javax.annotation.concurrent.NotThreadSafe;
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
@NotThreadSafe
@NotNullByDefault
class PseudoRandomImpl implements PseudoRandom {
private final FortunaGenerator generator;
private final Salsa20Engine cipher = new Salsa20Engine();
PseudoRandomImpl(int seed1, int seed2) {
byte[] seed = new byte[INT_32_BYTES * 2];
ByteUtils.writeUint32(seed1, seed, 0);
ByteUtils.writeUint32(seed2, seed, INT_32_BYTES);
generator = new FortunaGenerator(seed);
PseudoRandomImpl(byte[] seed) {
// Hash the seed to produce a 32-byte key
byte[] key = new byte[32];
Digest digest = new Blake2sDigest();
digest.update(seed, 0, seed.length);
digest.doFinal(key, 0);
// Initialise the stream cipher with an all-zero nonce
byte[] nonce = new byte[8];
cipher.init(true, new ParametersWithIV(new KeyParameter(key), nonce));
}
@Override
public byte[] nextBytes(int length) {
byte[] b = new byte[length];
int offset = 0;
while (offset < length) offset += generator.nextBytes(b, offset, length);
return b;
byte[] in = new byte[length], out = new byte[length];
cipher.processBytes(in, 0, length, out, 0);
return out;
}
}

View File

@@ -0,0 +1,42 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.SecureRandomProvider;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
abstract class AbstractSecureRandomProvider implements SecureRandomProvider {
// Contribute whatever slightly unpredictable info we have to the pool
protected void writeToEntropyPool(DataOutputStream out) throws IOException {
out.writeLong(System.currentTimeMillis());
out.writeLong(System.nanoTime());
out.writeLong(Runtime.getRuntime().freeMemory());
List<NetworkInterface> ifaces =
Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface i : ifaces) {
List<InetAddress> addrs = Collections.list(i.getInetAddresses());
for (InetAddress a : addrs) out.write(a.getAddress());
byte[] hardware = i.getHardwareAddress();
if (hardware != null) out.write(hardware);
}
for (Entry<String, String> e : System.getenv().entrySet()) {
out.writeUTF(e.getKey());
out.writeUTF(e.getValue());
}
Properties properties = System.getProperties();
for (String key : properties.stringPropertyNames())
out.writeUTF(properties.getProperty(key));
}
}

View File

@@ -0,0 +1,69 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.Provider;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable;
import static java.util.logging.Level.WARNING;
@Immutable
@NotNullByDefault
class LinuxSecureRandomProvider extends AbstractSecureRandomProvider {
private static final Logger LOG =
Logger.getLogger(LinuxSecureRandomProvider.class.getName());
private static final File RANDOM_DEVICE = new File("/dev/urandom");
private final AtomicBoolean seeded = new AtomicBoolean(false);
private final File outputDevice;
LinuxSecureRandomProvider() {
this(RANDOM_DEVICE);
}
LinuxSecureRandomProvider(File outputDevice) {
this.outputDevice = outputDevice;
}
@Override
public Provider getProvider() {
if (!seeded.getAndSet(true)) writeSeed();
return new LinuxProvider();
}
protected void writeSeed() {
try {
DataOutputStream out = new DataOutputStream(
new FileOutputStream(outputDevice));
writeToEntropyPool(out);
out.flush();
out.close();
} catch (IOException e) {
// On some devices /dev/urandom isn't writable - this isn't fatal
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
private static class LinuxProvider extends Provider {
private LinuxProvider() {
super("LinuxPRNG", 1.1, "A Linux-specific PRNG using /dev/urandom");
// Although /dev/urandom is not a SHA-1 PRNG, some callers
// explicitly request a SHA1PRNG SecureRandom and we need to
// prevent them from getting the default implementation whose
// output may have low entropy.
put("SecureRandom.SHA1PRNG", LinuxSecureRandomSpi.class.getName());
put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
}
}
}

View File

@@ -0,0 +1,64 @@
package org.briarproject.bramble.system;
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.security.SecureRandomSpi;
import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
public class LinuxSecureRandomSpi extends SecureRandomSpi {
private static final Logger LOG =
Logger.getLogger(LinuxSecureRandomSpi.class.getName());
private static final File RANDOM_DEVICE = new File("/dev/urandom");
private final File inputDevice, outputDevice;
public LinuxSecureRandomSpi() {
this(RANDOM_DEVICE, RANDOM_DEVICE);
}
LinuxSecureRandomSpi(File inputDevice, File outputDevice) {
this.inputDevice = inputDevice;
this.outputDevice = outputDevice;
}
@Override
protected void engineSetSeed(byte[] seed) {
try {
DataOutputStream out = new DataOutputStream(
new FileOutputStream(outputDevice));
out.write(seed);
out.flush();
out.close();
} catch (IOException e) {
// On some devices /dev/urandom isn't writable - this isn't fatal
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
@Override
protected void engineNextBytes(byte[] bytes) {
try {
DataInputStream in = new DataInputStream(
new FileInputStream(inputDevice));
in.readFully(bytes);
in.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected byte[] engineGenerateSeed(int len) {
byte[] seed = new byte[len];
engineNextBytes(seed);
return seed;
}
}

View File

@@ -1,75 +0,0 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.SeedProvider;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable;
import static java.util.logging.Level.WARNING;
@Immutable
@NotNullByDefault
class LinuxSeedProvider implements SeedProvider {
private static final Logger LOG =
Logger.getLogger(LinuxSeedProvider.class.getName());
private final String outputFile, inputFile;
LinuxSeedProvider() {
this("/dev/urandom", "/dev/urandom");
}
LinuxSeedProvider(String outputFile, String inputFile) {
this.outputFile = outputFile;
this.inputFile = inputFile;
}
@Override
public byte[] getSeed() {
byte[] seed = new byte[SEED_BYTES];
// Contribute whatever slightly unpredictable info we have to the pool
try {
DataOutputStream out = new DataOutputStream(
new FileOutputStream(outputFile));
writeToEntropyPool(out);
out.flush();
out.close();
} catch (IOException e) {
// On some devices /dev/urandom isn't writable - this isn't fatal
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
// Read the seed from the pool
try {
DataInputStream in = new DataInputStream(
new FileInputStream(inputFile));
in.readFully(seed);
in.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
return seed;
}
void writeToEntropyPool(DataOutputStream out) throws IOException {
out.writeLong(System.currentTimeMillis());
out.writeLong(System.nanoTime());
List<NetworkInterface> ifaces =
Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface i : ifaces) {
List<InetAddress> addrs = Collections.list(i.getInetAddresses());
for (InetAddress a : addrs) out.write(a.getAddress());
}
}
}