mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-15 04:18:53 +01:00
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:
@@ -0,0 +1,89 @@
|
|||||||
|
package org.briarproject.bramble.system;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.wifi.WifiConfiguration;
|
||||||
|
import android.net.wifi.WifiManager;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.provider.Settings;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static android.content.Context.WIFI_SERVICE;
|
||||||
|
import static android.provider.Settings.Secure.ANDROID_ID;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
class AndroidSecureRandomProvider extends LinuxSecureRandomProvider {
|
||||||
|
|
||||||
|
private static final int SEED_LENGTH = 32;
|
||||||
|
|
||||||
|
private final Context appContext;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
AndroidSecureRandomProvider(Application app) {
|
||||||
|
appContext = app.getApplicationContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeToEntropyPool(DataOutputStream out) throws IOException {
|
||||||
|
super.writeToEntropyPool(out);
|
||||||
|
out.writeInt(android.os.Process.myPid());
|
||||||
|
out.writeInt(android.os.Process.myTid());
|
||||||
|
out.writeInt(android.os.Process.myUid());
|
||||||
|
if (Build.FINGERPRINT != null) out.writeUTF(Build.FINGERPRINT);
|
||||||
|
if (Build.SERIAL != null) out.writeUTF(Build.SERIAL);
|
||||||
|
ContentResolver contentResolver = appContext.getContentResolver();
|
||||||
|
String id = Settings.Secure.getString(contentResolver, ANDROID_ID);
|
||||||
|
if (id != null) out.writeUTF(id);
|
||||||
|
Parcel parcel = Parcel.obtain();
|
||||||
|
WifiManager wm =
|
||||||
|
(WifiManager) appContext.getSystemService(WIFI_SERVICE);
|
||||||
|
for (WifiConfiguration config : wm.getConfiguredNetworks())
|
||||||
|
parcel.writeParcelable(config, 0);
|
||||||
|
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
||||||
|
if (bt != null) {
|
||||||
|
for (BluetoothDevice device : bt.getBondedDevices())
|
||||||
|
parcel.writeParcelable(device, 0);
|
||||||
|
}
|
||||||
|
out.write(parcel.marshall());
|
||||||
|
parcel.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeSeed() {
|
||||||
|
super.writeSeed();
|
||||||
|
if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT <= 18)
|
||||||
|
applyOpenSslFix();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
|
||||||
|
private void applyOpenSslFix() {
|
||||||
|
byte[] seed = new LinuxSecureRandomSpi().engineGenerateSeed(
|
||||||
|
SEED_LENGTH);
|
||||||
|
try {
|
||||||
|
// Seed the OpenSSL PRNG
|
||||||
|
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
||||||
|
.getMethod("RAND_seed", byte[].class)
|
||||||
|
.invoke(null, seed);
|
||||||
|
// Mix the output of the Linux PRNG into the OpenSSL 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();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new SecurityException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package org.briarproject.bramble.system;
|
|
||||||
|
|
||||||
import android.app.Application;
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.provider.Settings;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import static android.provider.Settings.Secure.ANDROID_ID;
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
class AndroidSeedProvider extends LinuxSeedProvider {
|
|
||||||
|
|
||||||
private final Context appContext;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
AndroidSeedProvider(Application app) {
|
|
||||||
appContext = app.getApplicationContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void writeToEntropyPool(DataOutputStream out) throws IOException {
|
|
||||||
out.writeInt(android.os.Process.myPid());
|
|
||||||
out.writeInt(android.os.Process.myTid());
|
|
||||||
out.writeInt(android.os.Process.myUid());
|
|
||||||
if (Build.FINGERPRINT != null) out.writeUTF(Build.FINGERPRINT);
|
|
||||||
if (Build.SERIAL != null) out.writeUTF(Build.SERIAL);
|
|
||||||
ContentResolver contentResolver = appContext.getContentResolver();
|
|
||||||
String id = Settings.Secure.getString(contentResolver, ANDROID_ID);
|
|
||||||
if (id != null) out.writeUTF(id);
|
|
||||||
super.writeToEntropyPool(out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@ import android.app.Application;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
import org.briarproject.bramble.api.system.SeedProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
@@ -16,8 +16,8 @@ public class AndroidSystemModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
SeedProvider provideSeedProvider(Application app) {
|
SecureRandomProvider provideSecureRandomProvider(Application app) {
|
||||||
return new AndroidSeedProvider(app);
|
return new AndroidSecureRandomProvider(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package org.briarproject.bramble.api.system;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.security.Provider;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for a platform-specific secure random number generator.
|
||||||
|
*/
|
||||||
|
@NotNullByDefault
|
||||||
|
public interface SecureRandomProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link Provider} that provides a strong {@link SecureRandom}
|
||||||
|
* implementation, or null if the platform's default implementation should
|
||||||
|
* be used.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
Provider getProvider();
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.system;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses a platform-specific source to provide a seed for a pseudo-random
|
|
||||||
* number generator.
|
|
||||||
*/
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface SeedProvider {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The length of the seed in bytes.
|
|
||||||
*/
|
|
||||||
int SEED_BYTES = 32;
|
|
||||||
|
|
||||||
byte[] getSeed();
|
|
||||||
}
|
|
||||||
@@ -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, "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,7 @@ import org.briarproject.bramble.api.crypto.PseudoRandom;
|
|||||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
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.IncomingKeys;
|
||||||
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
@@ -29,7 +29,10 @@ import org.spongycastle.crypto.params.KeyParameter;
|
|||||||
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.Provider;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
import java.security.Security;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -101,16 +104,26 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
private final MessageEncrypter messageEncrypter;
|
private final MessageEncrypter messageEncrypter;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
CryptoComponentImpl(SeedProvider seedProvider) {
|
CryptoComponentImpl(SecureRandomProvider secureRandomProvider) {
|
||||||
if (!FortunaSecureRandom.selfTest()) throw new RuntimeException();
|
|
||||||
SecureRandom platformSecureRandom = new SecureRandom();
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO)) {
|
||||||
String provider = platformSecureRandom.getProvider().getName();
|
SecureRandom defaultSecureRandom = new SecureRandom();
|
||||||
String algorithm = platformSecureRandom.getAlgorithm();
|
String name = defaultSecureRandom.getProvider().getName();
|
||||||
LOG.info("Default SecureRandom: " + provider + " " + algorithm);
|
String algorithm = defaultSecureRandom.getAlgorithm();
|
||||||
|
LOG.info("Default SecureRandom: " + name + " " + algorithm);
|
||||||
}
|
}
|
||||||
SecureRandom fortuna = new FortunaSecureRandom(seedProvider.getSeed());
|
Provider provider = secureRandomProvider.getProvider();
|
||||||
secureRandom = new CombinedSecureRandom(platformSecureRandom, fortuna);
|
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(
|
ECKeyGenerationParameters params = new ECKeyGenerationParameters(
|
||||||
PARAMETERS, secureRandom);
|
PARAMETERS, secureRandom);
|
||||||
agreementKeyPairGenerator = new ECKeyPairGenerator();
|
agreementKeyPairGenerator = new ECKeyPairGenerator();
|
||||||
@@ -124,6 +137,31 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
messageEncrypter = new MessageEncrypter(secureRandom);
|
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
|
@Override
|
||||||
public SecretKey generateSecretKey() {
|
public SecretKey generateSecretKey() {
|
||||||
byte[] b = new byte[SecretKey.LENGTH];
|
byte[] b = new byte[SecretKey.LENGTH];
|
||||||
@@ -133,7 +171,10 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PseudoRandom getPseudoRandom(int seed1, int seed2) {
|
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
|
@Override
|
||||||
@@ -296,7 +337,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
public SecretKey deriveMasterSecret(byte[] theirPublicKey,
|
public SecretKey deriveMasterSecret(byte[] theirPublicKey,
|
||||||
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
|
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
|
||||||
return deriveMasterSecret(deriveSharedSecret(
|
return deriveMasterSecret(deriveSharedSecret(
|
||||||
theirPublicKey,ourKeyPair, alice));
|
theirPublicKey, ourKeyPair, alice));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -607,7 +648,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private long sampleRunningTime(int iterations) {
|
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];
|
byte[] salt = new byte[PBKDF_SALT_BYTES];
|
||||||
int keyLengthInBits = SecretKey.LENGTH * 8;
|
int keyLengthInBits = SecretKey.LENGTH * 8;
|
||||||
long start = System.nanoTime();
|
long start = System.nanoTime();
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
|||||||
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;
|
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;
|
||||||
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
|
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
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.security.SecureRandom;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
@@ -60,8 +60,9 @@ public class CryptoModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
CryptoComponent provideCryptoComponent(SeedProvider seedProvider) {
|
CryptoComponent provideCryptoComponent(
|
||||||
return new CryptoComponentImpl(seedProvider);
|
SecureRandomProvider secureRandomProvider) {
|
||||||
|
return new CryptoComponentImpl(secureRandomProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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, "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,30 +2,34 @@ package org.briarproject.bramble.crypto;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.PseudoRandom;
|
import org.briarproject.bramble.api.crypto.PseudoRandom;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
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 javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
|
||||||
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
|
||||||
|
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class PseudoRandomImpl implements PseudoRandom {
|
class PseudoRandomImpl implements PseudoRandom {
|
||||||
|
|
||||||
private final FortunaGenerator generator;
|
private final Salsa20Engine cipher = new Salsa20Engine();
|
||||||
|
|
||||||
PseudoRandomImpl(int seed1, int seed2) {
|
PseudoRandomImpl(byte[] seed) {
|
||||||
byte[] seed = new byte[INT_32_BYTES * 2];
|
// Hash the seed to produce a 32-byte key
|
||||||
ByteUtils.writeUint32(seed1, seed, 0);
|
byte[] key = new byte[32];
|
||||||
ByteUtils.writeUint32(seed2, seed, INT_32_BYTES);
|
Digest digest = new Blake2sDigest();
|
||||||
generator = new FortunaGenerator(seed);
|
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
|
@Override
|
||||||
public byte[] nextBytes(int length) {
|
public byte[] nextBytes(int length) {
|
||||||
byte[] b = new byte[length];
|
byte[] in = new byte[length], out = new byte[length];
|
||||||
int offset = 0;
|
cipher.processBytes(in, 0, length, out, 0);
|
||||||
while (offset < length) offset += generator.nextBytes(b, offset, length);
|
return out;
|
||||||
return b;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -45,7 +45,7 @@ public class EllipticCurveMultiplicationTest extends BrambleTestCase {
|
|||||||
byte[] seed = new byte[32];
|
byte[] seed = new byte[32];
|
||||||
new SecureRandom().nextBytes(seed);
|
new SecureRandom().nextBytes(seed);
|
||||||
// Montgomery ladder multiplier
|
// Montgomery ladder multiplier
|
||||||
SecureRandom random = new FortunaSecureRandom(seed);
|
SecureRandom random = new PseudoSecureRandom(seed);
|
||||||
ECKeyGenerationParameters montgomeryGeneratorParams =
|
ECKeyGenerationParameters montgomeryGeneratorParams =
|
||||||
new ECKeyGenerationParameters(PARAMETERS, random);
|
new ECKeyGenerationParameters(PARAMETERS, random);
|
||||||
ECKeyPairGenerator montgomeryGenerator = new ECKeyPairGenerator();
|
ECKeyPairGenerator montgomeryGenerator = new ECKeyPairGenerator();
|
||||||
@@ -63,7 +63,7 @@ public class EllipticCurveMultiplicationTest extends BrambleTestCase {
|
|||||||
ECPublicKeyParameters montgomeryPublic2 =
|
ECPublicKeyParameters montgomeryPublic2 =
|
||||||
(ECPublicKeyParameters) montgomeryKeyPair2.getPublic();
|
(ECPublicKeyParameters) montgomeryKeyPair2.getPublic();
|
||||||
// Default multiplier
|
// Default multiplier
|
||||||
random = new FortunaSecureRandom(seed);
|
random = new PseudoSecureRandom(seed);
|
||||||
ECKeyGenerationParameters defaultGeneratorParams =
|
ECKeyGenerationParameters defaultGeneratorParams =
|
||||||
new ECKeyGenerationParameters(defaultParameters, random);
|
new ECKeyGenerationParameters(defaultParameters, random);
|
||||||
ECKeyPairGenerator defaultGenerator = new ECKeyPairGenerator();
|
ECKeyPairGenerator defaultGenerator = new ECKeyPairGenerator();
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.spongycastle.crypto.BlockCipher;
|
|
||||||
import org.spongycastle.crypto.engines.AESLightEngine;
|
|
||||||
import org.spongycastle.crypto.params.KeyParameter;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
public class FortunaGeneratorTest extends BrambleTestCase {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCounterInitialisedToOne() {
|
|
||||||
FortunaGenerator f = new FortunaGenerator(new byte[32]);
|
|
||||||
// The counter is little-endian
|
|
||||||
byte[] expected = new byte[16];
|
|
||||||
expected[0] = 1;
|
|
||||||
assertArrayEquals(expected, f.getCounter());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIncrementCounter() {
|
|
||||||
FortunaGenerator f = new FortunaGenerator(new byte[32]);
|
|
||||||
// Increment the counter until it reaches 255
|
|
||||||
for (int i = 1; i < 255; i++) f.incrementCounter();
|
|
||||||
byte[] expected = new byte[16];
|
|
||||||
expected[0] = (byte) 255;
|
|
||||||
assertArrayEquals(expected, f.getCounter());
|
|
||||||
// Increment the counter again - it should carry into the next byte
|
|
||||||
f.incrementCounter();
|
|
||||||
expected[0] = 0;
|
|
||||||
expected[1] = 1;
|
|
||||||
assertArrayEquals(expected, f.getCounter());
|
|
||||||
// Increment the counter until it carries into the next byte
|
|
||||||
for (int i = 256; i < 65536; i++) f.incrementCounter();
|
|
||||||
expected[0] = 0;
|
|
||||||
expected[1] = 0;
|
|
||||||
expected[2] = 1;
|
|
||||||
assertArrayEquals(expected, f.getCounter());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNextBytes() {
|
|
||||||
// Generate several outputs with the same seed - they should all match
|
|
||||||
byte[] seed = new byte[32];
|
|
||||||
byte[] out1 = new byte[48];
|
|
||||||
new FortunaGenerator(seed).nextBytes(out1, 0, 48);
|
|
||||||
// One byte longer than a block, with an offset of one
|
|
||||||
byte[] out2 = new byte[49];
|
|
||||||
new FortunaGenerator(seed).nextBytes(out2, 1, 48);
|
|
||||||
for (int i = 0; i < 48; i++) assertEquals(out1[i], out2[i + 1]);
|
|
||||||
// One byte shorter than a block
|
|
||||||
byte[] out3 = new byte[47];
|
|
||||||
new FortunaGenerator(seed).nextBytes(out3, 0, 47);
|
|
||||||
for (int i = 0; i < 47; i++) assertEquals(out1[i], out3[i]);
|
|
||||||
// Less than a block, with an offset greater than a block
|
|
||||||
byte[] out4 = new byte[32];
|
|
||||||
new FortunaGenerator(seed).nextBytes(out4, 17, 15);
|
|
||||||
for (int i = 0; i < 15; i++) assertEquals(out1[i], out4[i + 17]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRekeying() {
|
|
||||||
byte[] seed = new byte[32];
|
|
||||||
FortunaGenerator f = new FortunaGenerator(seed);
|
|
||||||
// Generate three blocks of output
|
|
||||||
byte[] out1 = new byte[48];
|
|
||||||
f.nextBytes(out1, 0, 48);
|
|
||||||
// Create another generator with the same seed and generate one block
|
|
||||||
f = new FortunaGenerator(seed);
|
|
||||||
byte[] out2 = new byte[16];
|
|
||||||
f.nextBytes(out2, 0, 16);
|
|
||||||
// The generator should have rekeyed with the 2nd and 3rd blocks
|
|
||||||
byte[] expectedKey = new byte[32];
|
|
||||||
System.arraycopy(out1, 16, expectedKey, 0, 32);
|
|
||||||
// The generator's counter should have been incremented 3 times
|
|
||||||
byte[] expectedCounter = new byte[16];
|
|
||||||
expectedCounter[0] = 4;
|
|
||||||
// The next expected output block is the counter encrypted with the key
|
|
||||||
byte[] expectedOutput = new byte[16];
|
|
||||||
BlockCipher c = new AESLightEngine();
|
|
||||||
c.init(true, new KeyParameter(expectedKey));
|
|
||||||
c.processBlock(expectedCounter, 0, expectedOutput, 0);
|
|
||||||
// Check that the generator produces the expected output block
|
|
||||||
byte[] out3 = new byte[16];
|
|
||||||
f.nextBytes(out3, 0, 16);
|
|
||||||
assertArrayEquals(expectedOutput, out3);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMaximumRequestLength() {
|
|
||||||
int expectedMax = 1024 * 1024;
|
|
||||||
byte[] output = new byte[expectedMax + 123];
|
|
||||||
FortunaGenerator f = new FortunaGenerator(new byte[32]);
|
|
||||||
assertEquals(expectedMax, f.nextBytes(output, 0, output.length));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.spongycastle.crypto.BlockCipher;
|
|
||||||
import org.spongycastle.crypto.digests.SHA256Digest;
|
|
||||||
import org.spongycastle.crypto.engines.AESLightEngine;
|
|
||||||
import org.spongycastle.crypto.params.KeyParameter;
|
|
||||||
|
|
||||||
import static org.briarproject.bramble.crypto.FortunaSecureRandom.SELF_TEST_VECTOR_1;
|
|
||||||
import static org.briarproject.bramble.crypto.FortunaSecureRandom.SELF_TEST_VECTOR_2;
|
|
||||||
import static org.briarproject.bramble.crypto.FortunaSecureRandom.SELF_TEST_VECTOR_3;
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
public class FortunaSecureRandomTest extends BrambleTestCase {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testClassPassesSelfTest() {
|
|
||||||
assertTrue(FortunaSecureRandom.selfTest());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSelfTestVectorsAreReproducible() {
|
|
||||||
byte[] key = new byte[32], seed = new byte[32];
|
|
||||||
byte[] counter = new byte[16], output = new byte[16];
|
|
||||||
byte[] newKey = new byte[32];
|
|
||||||
// Calculate the initial key
|
|
||||||
DoubleDigest digest = new DoubleDigest(new SHA256Digest());
|
|
||||||
digest.update(key);
|
|
||||||
digest.update(seed);
|
|
||||||
digest.digest(key, 0, 32);
|
|
||||||
// Calculate the first output block and the new key
|
|
||||||
BlockCipher c = new AESLightEngine();
|
|
||||||
c.init(true, new KeyParameter(key));
|
|
||||||
counter[0] = 1;
|
|
||||||
c.processBlock(counter, 0, output, 0);
|
|
||||||
counter[0] = 2;
|
|
||||||
c.processBlock(counter, 0, newKey, 0);
|
|
||||||
counter[0] = 3;
|
|
||||||
c.processBlock(counter, 0, newKey, 16);
|
|
||||||
System.arraycopy(newKey, 0, key, 0, 32);
|
|
||||||
// The first self-test vector should match the first output block
|
|
||||||
assertArrayEquals(SELF_TEST_VECTOR_1, output);
|
|
||||||
// Calculate the second output block and the new key before reseeding
|
|
||||||
c.init(true, new KeyParameter(key));
|
|
||||||
counter[0] = 4;
|
|
||||||
c.processBlock(counter, 0, output, 0);
|
|
||||||
counter[0] = 5;
|
|
||||||
c.processBlock(counter, 0, newKey, 0);
|
|
||||||
counter[0] = 6;
|
|
||||||
c.processBlock(counter, 0, newKey, 16);
|
|
||||||
System.arraycopy(newKey, 0, key, 0, 32);
|
|
||||||
// The second self-test vector should match the second output block
|
|
||||||
assertArrayEquals(SELF_TEST_VECTOR_2, output);
|
|
||||||
// Calculate the new key after reseeding
|
|
||||||
digest.update(key);
|
|
||||||
digest.update(seed);
|
|
||||||
digest.digest(key, 0, 32);
|
|
||||||
// Calculate the third output block
|
|
||||||
c.init(true, new KeyParameter(key));
|
|
||||||
counter[0] = 8;
|
|
||||||
c.processBlock(counter, 0, output, 0);
|
|
||||||
// The third self-test vector should match the third output block
|
|
||||||
assertArrayEquals(SELF_TEST_VECTOR_3, output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@ package org.briarproject.bramble.crypto;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSeedProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ public class HashTest extends BrambleTestCase {
|
|||||||
private final byte[] inputBytes2 = new byte[0];
|
private final byte[] inputBytes2 = new byte[0];
|
||||||
|
|
||||||
public HashTest() {
|
public HashTest() {
|
||||||
crypto = new CryptoComponentImpl(new TestSeedProvider());
|
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ package org.briarproject.bramble.crypto;
|
|||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.system.SeedProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSeedProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
@@ -14,8 +14,9 @@ public class KeyAgreementTest extends BrambleTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeriveMasterSecret() throws Exception {
|
public void testDeriveMasterSecret() throws Exception {
|
||||||
SeedProvider seedProvider = new TestSeedProvider();
|
SecureRandomProvider
|
||||||
CryptoComponent crypto = new CryptoComponentImpl(seedProvider);
|
secureRandomProvider = new TestSecureRandomProvider();
|
||||||
|
CryptoComponent crypto = new CryptoComponentImpl(secureRandomProvider);
|
||||||
KeyPair aPair = crypto.generateAgreementKeyPair();
|
KeyPair aPair = crypto.generateAgreementKeyPair();
|
||||||
byte[] aPub = aPair.getPublic().getEncoded();
|
byte[] aPub = aPair.getPublic().getEncoded();
|
||||||
KeyPair bPair = crypto.generateAgreementKeyPair();
|
KeyPair bPair = crypto.generateAgreementKeyPair();
|
||||||
@@ -27,8 +28,9 @@ public class KeyAgreementTest extends BrambleTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeriveSharedSecret() throws Exception {
|
public void testDeriveSharedSecret() throws Exception {
|
||||||
SeedProvider seedProvider = new TestSeedProvider();
|
SecureRandomProvider
|
||||||
CryptoComponent crypto = new CryptoComponentImpl(seedProvider);
|
secureRandomProvider = new TestSecureRandomProvider();
|
||||||
|
CryptoComponent crypto = new CryptoComponentImpl(secureRandomProvider);
|
||||||
KeyPair aPair = crypto.generateAgreementKeyPair();
|
KeyPair aPair = crypto.generateAgreementKeyPair();
|
||||||
byte[] aPub = aPair.getPublic().getEncoded();
|
byte[] aPub = aPair.getPublic().getEncoded();
|
||||||
KeyPair bPair = crypto.generateAgreementKeyPair();
|
KeyPair bPair = crypto.generateAgreementKeyPair();
|
||||||
|
|||||||
@@ -1,20 +1,24 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Bytes;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSeedProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class KeyDerivationTest extends BrambleTestCase {
|
public class KeyDerivationTest extends BrambleTestCase {
|
||||||
|
|
||||||
@@ -23,7 +27,7 @@ public class KeyDerivationTest extends BrambleTestCase {
|
|||||||
private final SecretKey master;
|
private final SecretKey master;
|
||||||
|
|
||||||
public KeyDerivationTest() {
|
public KeyDerivationTest() {
|
||||||
crypto = new CryptoComponentImpl(new TestSeedProvider());
|
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||||
master = TestUtils.getSecretKey();
|
master = TestUtils.getSecretKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,11 +160,7 @@ public class KeyDerivationTest extends BrambleTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void assertAllDifferent(List<SecretKey> keys) {
|
private void assertAllDifferent(List<SecretKey> keys) {
|
||||||
for (SecretKey ki : keys) {
|
Set<Bytes> set = new HashSet<Bytes>();
|
||||||
for (SecretKey kj : keys) {
|
for (SecretKey k : keys) assertTrue(set.add(new Bytes(k.getBytes())));
|
||||||
if (ki == kj) assertArrayEquals(ki.getBytes(), kj.getBytes());
|
|
||||||
else assertFalse(Arrays.equals(ki.getBytes(), kj.getBytes()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.crypto.KeyParser;
|
|||||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSeedProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ import static org.junit.Assert.assertTrue;
|
|||||||
public class KeyEncodingAndParsingTest extends BrambleTestCase {
|
public class KeyEncodingAndParsingTest extends BrambleTestCase {
|
||||||
|
|
||||||
private final CryptoComponentImpl crypto =
|
private final CryptoComponentImpl crypto =
|
||||||
new CryptoComponentImpl(new TestSeedProvider());
|
new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAgreementPublicKeyLength() throws Exception {
|
public void testAgreementPublicKeyLength() throws Exception {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package org.briarproject.bramble.crypto;
|
|||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSeedProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ public class MacTest extends BrambleTestCase {
|
|||||||
private final byte[] inputBytes2 = new byte[0];
|
private final byte[] inputBytes2 = new byte[0];
|
||||||
|
|
||||||
public MacTest() {
|
public MacTest() {
|
||||||
crypto = new CryptoComponentImpl(new TestSeedProvider());
|
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSeedProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ import static org.junit.Assert.assertTrue;
|
|||||||
public class PasswordBasedKdfTest extends BrambleTestCase {
|
public class PasswordBasedKdfTest extends BrambleTestCase {
|
||||||
|
|
||||||
private final CryptoComponentImpl crypto =
|
private final CryptoComponentImpl crypto =
|
||||||
new CryptoComponentImpl(new TestSeedProvider());
|
new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncryptionAndDecryption() {
|
public void testEncryptionAndDecryption() {
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.PseudoRandom;
|
||||||
|
|
||||||
|
import java.security.Provider;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.security.SecureRandomSpi;
|
||||||
|
|
||||||
|
class PseudoSecureRandom extends SecureRandom {
|
||||||
|
|
||||||
|
private static final Provider PROVIDER = new PseudoSecureRandomProvider();
|
||||||
|
|
||||||
|
PseudoSecureRandom(byte[] seed) {
|
||||||
|
super(new PseudoSecureRandomSpi(seed), PROVIDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PseudoSecureRandomSpi extends SecureRandomSpi {
|
||||||
|
|
||||||
|
private final PseudoRandom pseudoRandom;
|
||||||
|
|
||||||
|
private PseudoSecureRandomSpi(byte[] seed) {
|
||||||
|
pseudoRandom = new PseudoRandomImpl(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected byte[] engineGenerateSeed(int length) {
|
||||||
|
return pseudoRandom.nextBytes(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void engineNextBytes(byte[] b) {
|
||||||
|
byte[] random = pseudoRandom.nextBytes(b.length);
|
||||||
|
System.arraycopy(random, 0, b, 0, b.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void engineSetSeed(byte[] seed) {
|
||||||
|
// Thank you for your input
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PseudoSecureRandomProvider extends Provider {
|
||||||
|
|
||||||
|
private PseudoSecureRandomProvider() {
|
||||||
|
super("PseudoSecureRandom", 1.0, "Only for testing");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ package org.briarproject.bramble.crypto;
|
|||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSeedProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ public class SignatureTest extends BrambleTestCase {
|
|||||||
private final byte[] inputBytes = TestUtils.getRandomBytes(123);
|
private final byte[] inputBytes = TestUtils.getRandomBytes(123);
|
||||||
|
|
||||||
public SignatureTest() {
|
public SignatureTest() {
|
||||||
crypto = new CryptoComponentImpl(new TestSeedProvider());
|
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||||
KeyPair k = crypto.generateSignatureKeyPair();
|
KeyPair k = crypto.generateSignatureKeyPair();
|
||||||
publicKey = k.getPublic().getEncoded();
|
publicKey = k.getPublic().getEncoded();
|
||||||
privateKey = k.getPrivate().getEncoded();
|
privateKey = k.getPrivate().getEncoded();
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package org.briarproject.bramble.system;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
|
import org.briarproject.bramble.util.OsUtils;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.security.Provider;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class LinuxSecureRandomProviderTest extends BrambleTestCase {
|
||||||
|
|
||||||
|
private final File testDir = TestUtils.getTestDirectory();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
testDir.mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetProviderWritesToRandomDeviceOnFirstCall()
|
||||||
|
throws Exception {
|
||||||
|
if (!(OsUtils.isLinux())) {
|
||||||
|
System.err.println("WARNING: Skipping test, can't run on this OS");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Redirect the provider's output to a file
|
||||||
|
File urandom = new File(testDir, "urandom");
|
||||||
|
urandom.delete();
|
||||||
|
assertTrue(urandom.createNewFile());
|
||||||
|
assertEquals(0, urandom.length());
|
||||||
|
LinuxSecureRandomProvider p = new LinuxSecureRandomProvider(urandom);
|
||||||
|
// Getting a provider should write entropy to the file
|
||||||
|
Provider provider = p.getProvider();
|
||||||
|
assertNotNull(provider);
|
||||||
|
assertEquals("LinuxPRNG", provider.getName());
|
||||||
|
// There should be at least 16 bytes from the clock, 8 from the runtime
|
||||||
|
long length = urandom.length();
|
||||||
|
assertTrue(length >= 24);
|
||||||
|
// Getting another provider should not write to the file again
|
||||||
|
provider = p.getProvider();
|
||||||
|
assertNotNull(provider);
|
||||||
|
assertEquals("LinuxPRNG", provider.getName());
|
||||||
|
assertEquals(length, urandom.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
TestUtils.deleteTestDirectory(testDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
package org.briarproject.bramble.system;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Bytes;
|
||||||
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
|
import org.briarproject.bramble.util.IoUtils;
|
||||||
|
import org.briarproject.bramble.util.OsUtils;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
|
||||||
|
public class LinuxSecureRandomSpiTest extends BrambleTestCase {
|
||||||
|
|
||||||
|
private static final File RANDOM_DEVICE = new File("/dev/urandom");
|
||||||
|
private static final int SEED_BYTES = 32;
|
||||||
|
|
||||||
|
private final File testDir = TestUtils.getTestDirectory();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
testDir.mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSeedsAreDistinct() {
|
||||||
|
if (!(OsUtils.isLinux())) {
|
||||||
|
System.err.println("WARNING: Skipping test, can't run on this OS");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Bytes> seeds = new HashSet<Bytes>();
|
||||||
|
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi();
|
||||||
|
for (int i = 0; i < 1000; i++) {
|
||||||
|
byte[] seed = engine.engineGenerateSeed(SEED_BYTES);
|
||||||
|
assertEquals(SEED_BYTES, seed.length);
|
||||||
|
assertTrue(seeds.add(new Bytes(seed)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEngineSetSeedWritesToRandomDevice() throws Exception {
|
||||||
|
if (!(OsUtils.isLinux())) {
|
||||||
|
System.err.println("WARNING: Skipping test, can't run on this OS");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Redirect the engine's output to a file
|
||||||
|
File urandom = new File(testDir, "urandom");
|
||||||
|
urandom.delete();
|
||||||
|
assertTrue(urandom.createNewFile());
|
||||||
|
assertEquals(0, urandom.length());
|
||||||
|
// Generate a seed
|
||||||
|
byte[] seed = TestUtils.getRandomBytes(SEED_BYTES);
|
||||||
|
// Check that the engine writes the seed to the file
|
||||||
|
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi(RANDOM_DEVICE,
|
||||||
|
urandom);
|
||||||
|
engine.engineSetSeed(seed);
|
||||||
|
assertEquals(SEED_BYTES, urandom.length());
|
||||||
|
byte[] written = new byte[SEED_BYTES];
|
||||||
|
FileInputStream in = new FileInputStream(urandom);
|
||||||
|
IoUtils.read(in, written);
|
||||||
|
in.close();
|
||||||
|
assertArrayEquals(seed, written);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEngineNextBytesReadsFromRandomDevice() throws Exception {
|
||||||
|
if (!(OsUtils.isLinux())) {
|
||||||
|
System.err.println("WARNING: Skipping test, can't run on this OS");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Generate some entropy
|
||||||
|
byte[] entropy = TestUtils.getRandomBytes(SEED_BYTES);
|
||||||
|
// Write the entropy to a file
|
||||||
|
File urandom = new File(testDir, "urandom");
|
||||||
|
urandom.delete();
|
||||||
|
FileOutputStream out = new FileOutputStream(urandom);
|
||||||
|
out.write(entropy);
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
assertTrue(urandom.exists());
|
||||||
|
assertEquals(SEED_BYTES, urandom.length());
|
||||||
|
// Check that the engine reads from the file
|
||||||
|
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi(urandom,
|
||||||
|
RANDOM_DEVICE);
|
||||||
|
byte[] b = new byte[SEED_BYTES];
|
||||||
|
engine.engineNextBytes(b);
|
||||||
|
assertArrayEquals(entropy, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEngineGenerateSeedReadsFromRandomDevice() throws Exception {
|
||||||
|
if (!(OsUtils.isLinux())) {
|
||||||
|
System.err.println("WARNING: Skipping test, can't run on this OS");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Generate some entropy
|
||||||
|
byte[] entropy = TestUtils.getRandomBytes(SEED_BYTES);
|
||||||
|
// Write the entropy to a file
|
||||||
|
File urandom = new File(testDir, "urandom");
|
||||||
|
urandom.delete();
|
||||||
|
FileOutputStream out = new FileOutputStream(urandom);
|
||||||
|
out.write(entropy);
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
assertTrue(urandom.exists());
|
||||||
|
assertEquals(SEED_BYTES, urandom.length());
|
||||||
|
// Check that the engine reads from the file
|
||||||
|
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi(urandom,
|
||||||
|
RANDOM_DEVICE);
|
||||||
|
byte[] b = engine.engineGenerateSeed(SEED_BYTES);
|
||||||
|
assertArrayEquals(entropy, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
TestUtils.deleteTestDirectory(testDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
package org.briarproject.bramble.system;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.Bytes;
|
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
|
||||||
import org.briarproject.bramble.util.OsUtils;
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.system.SeedProvider.SEED_BYTES;
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
public class LinuxSeedProviderTest extends BrambleTestCase {
|
|
||||||
|
|
||||||
private final File testDir = TestUtils.getTestDirectory();
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
testDir.mkdirs();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSeedAppearsSane() {
|
|
||||||
if (!(OsUtils.isLinux())) {
|
|
||||||
System.err.println("WARNING: Skipping test, can't run on this OS");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Set<Bytes> seeds = new HashSet<Bytes>();
|
|
||||||
LinuxSeedProvider p = new LinuxSeedProvider();
|
|
||||||
for (int i = 0; i < 1000; i++) {
|
|
||||||
byte[] seed = p.getSeed();
|
|
||||||
assertEquals(SEED_BYTES, seed.length);
|
|
||||||
assertTrue(seeds.add(new Bytes(seed)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEntropyIsWrittenToPool() throws Exception {
|
|
||||||
if (!(OsUtils.isLinux())) {
|
|
||||||
System.err.println("WARNING: Skipping test, can't run on this OS");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Redirect the provider's entropy to a file
|
|
||||||
File urandom = new File(testDir, "urandom");
|
|
||||||
urandom.delete();
|
|
||||||
assertTrue(urandom.createNewFile());
|
|
||||||
assertEquals(0, urandom.length());
|
|
||||||
String path = urandom.getAbsolutePath();
|
|
||||||
LinuxSeedProvider p = new LinuxSeedProvider(path, "/dev/urandom");
|
|
||||||
p.getSeed();
|
|
||||||
// There should be 16 bytes from the clock, plus network interfaces
|
|
||||||
assertTrue(urandom.length() > 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSeedIsReadFromPool() throws Exception {
|
|
||||||
if (!(OsUtils.isLinux())) {
|
|
||||||
System.err.println("WARNING: Skipping test, can't run on this OS");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Generate a seed
|
|
||||||
byte[] seed = TestUtils.getRandomBytes(SEED_BYTES);
|
|
||||||
// Write the seed to a file
|
|
||||||
File urandom = new File(testDir, "urandom");
|
|
||||||
urandom.delete();
|
|
||||||
FileOutputStream out = new FileOutputStream(urandom);
|
|
||||||
out.write(seed);
|
|
||||||
out.flush();
|
|
||||||
out.close();
|
|
||||||
assertTrue(urandom.exists());
|
|
||||||
assertEquals(SEED_BYTES, urandom.length());
|
|
||||||
// Check that the provider reads the seed from the file
|
|
||||||
String path = urandom.getAbsolutePath();
|
|
||||||
LinuxSeedProvider p = new LinuxSeedProvider("/dev/urandom", path);
|
|
||||||
assertArrayEquals(seed, p.getSeed());
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void tearDown() {
|
|
||||||
TestUtils.deleteTestDirectory(testDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package org.briarproject.bramble.test;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
|
|
||||||
|
import java.security.Provider;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
public class TestSecureRandomProvider implements SecureRandomProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Provider getProvider() {
|
||||||
|
// Use the default provider
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package org.briarproject.bramble.test;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.system.SeedProvider;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
public class TestSeedProvider implements SeedProvider {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getSeed() {
|
|
||||||
return TestUtils.getRandomBytes(32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package org.briarproject.bramble.test;
|
package org.briarproject.bramble.test;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.system.SeedProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ public class TestSeedProviderModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
SeedProvider provideSeedProvider() {
|
SecureRandomProvider provideSeedProvider() {
|
||||||
return new TestSeedProvider();
|
return new TestSecureRandomProvider();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package org.briarproject.bramble.system;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
|
import org.briarproject.bramble.util.OsUtils;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
public class DesktopSecureRandomModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
SecureRandomProvider provideSecureRandomProvider() {
|
||||||
|
return OsUtils.isLinux() ? new LinuxSecureRandomProvider() : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package org.briarproject.bramble.system;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.system.SeedProvider;
|
|
||||||
import org.briarproject.bramble.util.OsUtils;
|
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
|
|
||||||
@Module
|
|
||||||
public class DesktopSeedProviderModule {
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
SeedProvider provideSeedProvider() {
|
|
||||||
return OsUtils.isLinux() ? new LinuxSeedProvider() : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user