Compare commits

..

1 Commits

Author SHA1 Message Date
akwizgran
d311557f09 Check whether first visible message ID is null before storing. 2017-05-29 10:05:49 +01:00
254 changed files with 4927 additions and 8047 deletions

View File

@@ -1,4 +1,4 @@
image: registry.gitlab.com/fdroid/ci-images-base:latest
image: registry.gitlab.com/fdroid/ci-images:client-latest
cache:
paths:
@@ -7,7 +7,8 @@ cache:
before_script:
- export GRADLE_USER_HOME=$PWD/.gradle
- echo y | /opt/android-sdk/tools/bin/sdkmanager "build-tools;23.0.3"
# - export ANDROID_COMPILE_SDK=`sed -n 's,.*compileSdkVersion\s*\([0-9][0-9]*\).*,\1,p' app/build.gradle`
# - echo y | android --silent update sdk --no-ui --filter android-${ANDROID_COMPILE_SDK}
test:
script:

View File

@@ -12,8 +12,8 @@ android {
defaultConfig {
minSdkVersion 14
targetSdkVersion 22
versionCode 1611
versionName "0.16.11"
versionCode 14
versionName "0.14"
consumerProguardFiles 'proguard-rules.txt'
}
@@ -25,38 +25,38 @@ android {
dependencies {
compile project(':bramble-core')
compile fileTree(dir: 'libs', include: '*.jar')
compile fileTree(dir: 'libs', include: ['*.jar'])
provided 'javax.annotation:jsr250-api:1.0'
}
def torBinaryDir = 'src/main/res/raw'
task downloadTorGeoIp(type: Download) {
src 'https://briarproject.org/build/geoip-2017-09-06.zip'
src 'https://briarproject.org/build/geoip-2015-12-01.zip'
dest "$torBinaryDir/geoip.zip"
onlyIfNewer true
}
task downloadTorBinaryArm(type: Download) {
src 'https://briarproject.org/build/tor-0.2.9.12-arm.zip'
src 'https://briarproject.org/build/tor-0.2.7.6-arm.zip'
dest "$torBinaryDir/tor_arm.zip"
onlyIfNewer true
}
task downloadTorBinaryArmPie(type: Download) {
src 'https://briarproject.org/build/tor-0.2.9.12-arm-pie.zip'
src 'https://briarproject.org/build/tor-0.2.7.6-arm-pie.zip'
dest "$torBinaryDir/tor_arm_pie.zip"
onlyIfNewer true
}
task downloadTorBinaryX86(type: Download) {
src 'https://briarproject.org/build/tor-0.2.9.12-x86.zip'
src 'https://briarproject.org/build/tor-0.2.7.6-x86.zip'
dest "$torBinaryDir/tor_x86.zip"
onlyIfNewer true
}
task downloadTorBinaryX86Pie(type: Download) {
src 'https://briarproject.org/build/tor-0.2.9.12-x86-pie.zip'
src 'https://briarproject.org/build/tor-0.2.7.6-x86-pie.zip'
dest "$torBinaryDir/tor_x86_pie.zip"
onlyIfNewer true
}
@@ -64,31 +64,31 @@ task downloadTorBinaryX86Pie(type: Download) {
task verifyTorGeoIp(type: Verify, dependsOn: 'downloadTorGeoIp') {
src "$torBinaryDir/geoip.zip"
algorithm 'SHA-256'
checksum 'fe49d3adb86d3c512373101422a017dbb86c85a570524663f09dd8ce143a24f3'
checksum '9bcdaf0a7ba0933735328d8ec466c25c25dbb459efc2bce9e55c774eabea5162'
}
task verifyTorBinaryArm(type: Verify, dependsOn: 'downloadTorBinaryArm') {
src "$torBinaryDir/tor_arm.zip"
algorithm 'SHA-256'
checksum '8ed0b347ffed1d6a4d2fd14495118eb92be83e9cc06e057e15220dc288b31688'
checksum '83272962eda701cd5d74d2418651c4ff0f0b1dff51f558a292d1a1c42bf12146'
}
task verifyTorBinaryArmPie(type: Verify, dependsOn: 'downloadTorBinaryArmPie') {
src "$torBinaryDir/tor_arm_pie.zip"
algorithm 'SHA-256'
checksum '64403262511c29f462ca5e7c7621bfc3c944898364d1d5ad35a016bb8a034283'
checksum 'd0300d1e45de11ebb24ed62b9c492be9c2e88590b7822195ab38c7a76ffcf646'
}
task verifyTorBinaryX86(type: Verify, dependsOn: 'downloadTorBinaryX86') {
src "$torBinaryDir/tor_x86.zip"
algorithm 'SHA-256'
checksum '61e014607a2079bcf1646289c67bff6372b1aded6e1d8d83d7791efda9a4d5ab'
checksum 'b8813d97b01ee1b9c9a4233c1b9bbe9f9f6b494ae6f9cbd84de8a3911911615e'
}
task verifyTorBinaryX86Pie(type: Verify, dependsOn: 'downloadTorBinaryX86Pie') {
src "$torBinaryDir/tor_x86_pie.zip"
algorithm 'SHA-256'
checksum '18fbc98356697dd0895836ab46d5c9877d1c539193464f7db1e82a65adaaf288'
checksum '9c66e765aa196dc089951a1b2140cc8290305c2fcbf365121f99e01a233baf4e'
}
project.afterEvaluate {

View File

@@ -39,7 +39,7 @@ public class AndroidPluginModule {
EventBus eventBus) {
Context appContext = app.getApplicationContext();
DuplexPluginFactory bluetooth = new DroidtoothPluginFactory(ioExecutor,
androidExecutor, appContext, random, eventBus, backoffFactory);
androidExecutor, appContext, random, backoffFactory);
DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, appContext,
locationUtils, reporter, eventBus, torSocketFactory,
backoffFactory);

View File

@@ -11,9 +11,8 @@ import android.content.IntentFilter;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
@@ -24,8 +23,6 @@ import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.plugin.event.DisableBluetoothEvent;
import org.briarproject.bramble.api.plugin.event.EnableBluetoothEvent;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.util.AndroidUtils;
@@ -33,14 +30,23 @@ import org.briarproject.bramble.util.StringUtils;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
@@ -55,6 +61,8 @@ import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERA
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
import static android.bluetooth.BluetoothAdapter.STATE_OFF;
import static android.bluetooth.BluetoothAdapter.STATE_ON;
import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
@@ -67,10 +75,14 @@ import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
class DroidtoothPlugin implements DuplexPlugin, EventListener {
class DroidtoothPlugin implements DuplexPlugin {
private static final Logger LOG =
Logger.getLogger(DroidtoothPlugin.class.getName());
private static final String FOUND =
"android.bluetooth.device.action.FOUND";
private static final String DISCOVERY_FINISHED =
"android.bluetooth.adapter.action.DISCOVERY_FINISHED";
private final Executor ioExecutor;
private final AndroidExecutor androidExecutor;
@@ -154,7 +166,9 @@ class DroidtoothPlugin implements DuplexPlugin, EventListener {
} else {
// Enable Bluetooth if settings allow
if (callback.getSettings().getBoolean(PREF_BT_ENABLE, false)) {
enableAdapter();
wasEnabledByUs = true;
if (adapter.enable()) LOG.info("Enabling Bluetooth");
else LOG.info("Could not enable Bluetooth");
} else {
LOG.info("Not enabling Bluetooth");
}
@@ -245,27 +259,13 @@ class DroidtoothPlugin implements DuplexPlugin, EventListener {
return new DroidtoothTransportConnection(this, s);
}
private void enableAdapter() {
if (adapter != null && !adapter.isEnabled()) {
if (adapter.enable()) {
LOG.info("Enabling Bluetooth");
wasEnabledByUs = true;
} else {
LOG.info("Could not enable Bluetooth");
}
}
}
@Override
public void stop() {
running = false;
if (receiver != null) appContext.unregisterReceiver(receiver);
tryToClose(socket);
disableAdapter();
}
private void disableAdapter() {
if (adapter != null && adapter.isEnabled() && wasEnabledByUs) {
// Disable Bluetooth if we enabled it and it's still enabled
if (wasEnabledByUs && adapter.isEnabled()) {
if (adapter.disable()) LOG.info("Disabling Bluetooth");
else LOG.info("Could not disable Bluetooth");
}
@@ -363,7 +363,8 @@ class DroidtoothPlugin implements DuplexPlugin, EventListener {
@Override
public DuplexTransportConnection createConnection(ContactId c) {
if (!isRunning()) return null;
TransportProperties p = callback.getRemoteProperties(c);
TransportProperties p = callback.getRemoteProperties().get(c);
if (p == null) return null;
String address = p.get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) return null;
String uuid = p.get(PROP_UUID);
@@ -373,6 +374,90 @@ class DroidtoothPlugin implements DuplexPlugin, EventListener {
return new DroidtoothTransportConnection(this, s);
}
@Override
public boolean supportsInvitations() {
return true;
}
@Override
public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
long timeout, boolean alice) {
if (!isRunning()) return null;
// Use the invitation codes to generate the UUID
byte[] b = r.nextBytes(UUID_BYTES);
UUID uuid = UUID.nameUUIDFromBytes(b);
if (LOG.isLoggable(INFO)) LOG.info("Invitation UUID " + uuid);
// Bind a server socket for receiving invitation connections
BluetoothServerSocket ss;
try {
ss = adapter.listenUsingInsecureRfcommWithServiceRecord(
"RFCOMM", uuid);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
}
// Create the background tasks
CompletionService<BluetoothSocket> complete =
new ExecutorCompletionService<>(ioExecutor);
List<Future<BluetoothSocket>> futures = new ArrayList<>();
if (alice) {
// Return the first connected socket
futures.add(complete.submit(new ListeningTask(ss)));
futures.add(complete.submit(new DiscoveryTask(uuid.toString())));
} else {
// Return the first socket with readable data
futures.add(complete.submit(new ReadableTask(
new ListeningTask(ss))));
futures.add(complete.submit(new ReadableTask(
new DiscoveryTask(uuid.toString()))));
}
BluetoothSocket chosen = null;
try {
Future<BluetoothSocket> f = complete.poll(timeout, MILLISECONDS);
if (f == null) return null; // No task completed within the timeout
chosen = f.get();
return new DroidtoothTransportConnection(this, chosen);
} catch (InterruptedException e) {
LOG.info("Interrupted while exchanging invitations");
Thread.currentThread().interrupt();
return null;
} catch (ExecutionException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
} finally {
// Closing the socket will terminate the listener task
tryToClose(ss);
closeSockets(futures, chosen);
}
}
private void closeSockets(final List<Future<BluetoothSocket>> futures,
@Nullable final BluetoothSocket chosen) {
ioExecutor.execute(new Runnable() {
@Override
public void run() {
for (Future<BluetoothSocket> f : futures) {
try {
if (f.cancel(true)) {
LOG.info("Cancelled task");
} else {
BluetoothSocket s = f.get();
if (s != null && s != chosen) {
LOG.info("Closing unwanted socket");
s.close();
}
}
} catch (InterruptedException e) {
LOG.info("Interrupted while closing sockets");
return;
} catch (ExecutionException | IOException e) {
if (LOG.isLoggable(INFO)) LOG.info(e.toString());
}
}
}
});
}
@Override
public boolean supportsKeyAgreement() {
return true;
@@ -387,7 +472,7 @@ class DroidtoothPlugin implements DuplexPlugin, EventListener {
// No truncation necessary because COMMIT_LENGTH = 16
UUID uuid = UUID.nameUUIDFromBytes(commitment);
if (LOG.isLoggable(INFO)) LOG.info("Key agreement UUID " + uuid);
// Bind a server socket for receiving key agreement connections
// Bind a server socket for receiving invitation connections
BluetoothServerSocket ss;
try {
ss = adapter.listenUsingInsecureRfcommWithServiceRecord(
@@ -428,33 +513,6 @@ class DroidtoothPlugin implements DuplexPlugin, EventListener {
return StringUtils.macToString(mac);
}
@Override
public void eventOccurred(Event e) {
if (e instanceof EnableBluetoothEvent) {
enableAdapterAsync();
} else if (e instanceof DisableBluetoothEvent) {
disableAdapterAsync();
}
}
private void enableAdapterAsync() {
ioExecutor.execute(new Runnable() {
@Override
public void run() {
enableAdapter();
}
});
}
private void disableAdapterAsync() {
ioExecutor.execute(new Runnable() {
@Override
public void run() {
disableAdapter();
}
});
}
private class BluetoothStateReceiver extends BroadcastReceiver {
@Override
@@ -478,6 +536,115 @@ class DroidtoothPlugin implements DuplexPlugin, EventListener {
}
}
private class DiscoveryTask implements Callable<BluetoothSocket> {
private final String uuid;
private DiscoveryTask(String uuid) {
this.uuid = uuid;
}
@Override
public BluetoothSocket call() throws Exception {
// Repeat discovery until we connect or get interrupted
while (true) {
// Discover nearby devices
LOG.info("Discovering nearby devices");
List<String> addresses = discoverDevices();
if (addresses.isEmpty()) {
LOG.info("No devices discovered");
continue;
}
// Connect to any device with the right UUID
for (String address : addresses) {
BluetoothSocket s = connect(address, uuid);
if (s != null) {
LOG.info("Outgoing connection");
return s;
}
}
}
}
private List<String> discoverDevices() throws InterruptedException {
IntentFilter filter = new IntentFilter();
filter.addAction(FOUND);
filter.addAction(DISCOVERY_FINISHED);
DiscoveryReceiver disco = new DiscoveryReceiver();
appContext.registerReceiver(disco, filter);
LOG.info("Starting discovery");
adapter.startDiscovery();
return disco.waitForAddresses();
}
}
private static class DiscoveryReceiver extends BroadcastReceiver {
private final CountDownLatch finished = new CountDownLatch(1);
private final List<String> addresses = new CopyOnWriteArrayList<>();
@Override
public void onReceive(Context ctx, Intent intent) {
String action = intent.getAction();
if (action.equals(DISCOVERY_FINISHED)) {
LOG.info("Discovery finished");
ctx.unregisterReceiver(this);
finished.countDown();
} else if (action.equals(FOUND)) {
BluetoothDevice d = intent.getParcelableExtra(EXTRA_DEVICE);
if (LOG.isLoggable(INFO)) {
LOG.info("Discovered device: " +
scrubMacAddress(d.getAddress()));
}
addresses.add(d.getAddress());
}
}
private List<String> waitForAddresses() throws InterruptedException {
finished.await();
List<String> shuffled = new ArrayList<>(addresses);
Collections.shuffle(shuffled);
return shuffled;
}
}
private static class ListeningTask implements Callable<BluetoothSocket> {
private final BluetoothServerSocket serverSocket;
private ListeningTask(BluetoothServerSocket serverSocket) {
this.serverSocket = serverSocket;
}
@Override
public BluetoothSocket call() throws IOException {
BluetoothSocket s = serverSocket.accept();
LOG.info("Incoming connection");
return s;
}
}
private static class ReadableTask implements Callable<BluetoothSocket> {
private final Callable<BluetoothSocket> connectionTask;
private ReadableTask(Callable<BluetoothSocket> connectionTask) {
this.connectionTask = connectionTask;
}
@Override
public BluetoothSocket call() throws Exception {
BluetoothSocket s = connectionTask.call();
InputStream in = s.getInputStream();
while (in.available() == 0) {
LOG.info("Waiting for data");
Thread.sleep(1000);
}
LOG.info("Data available");
return s;
}
}
private class BluetoothKeyAgreementListener extends KeyAgreementListener {
private final BluetoothServerSocket ss;

View File

@@ -2,7 +2,6 @@ package org.briarproject.bramble.plugin.droidtooth;
import android.content.Context;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.BackoffFactory;
@@ -32,18 +31,15 @@ public class DroidtoothPluginFactory implements DuplexPluginFactory {
private final AndroidExecutor androidExecutor;
private final Context appContext;
private final SecureRandom secureRandom;
private final EventBus eventBus;
private final BackoffFactory backoffFactory;
public DroidtoothPluginFactory(Executor ioExecutor,
AndroidExecutor androidExecutor, Context appContext,
SecureRandom secureRandom, EventBus eventBus,
BackoffFactory backoffFactory) {
SecureRandom secureRandom, BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor;
this.androidExecutor = androidExecutor;
this.appContext = appContext;
this.secureRandom = secureRandom;
this.eventBus = eventBus;
this.backoffFactory = backoffFactory;
}
@@ -61,10 +57,7 @@ public class DroidtoothPluginFactory implements DuplexPluginFactory {
public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
DroidtoothPlugin plugin = new DroidtoothPlugin(ioExecutor,
androidExecutor, appContext, secureRandom, backoff, callback,
MAX_LATENCY);
eventBus.addListener(plugin);
return plugin;
return new DroidtoothPlugin(ioExecutor, androidExecutor, appContext,
secureRandom, backoff, callback, MAX_LATENCY);
}
}

View File

@@ -17,6 +17,7 @@ import net.freehaven.tor.control.EventHandler;
import net.freehaven.tor.control.TorControlConnection;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener;
@@ -55,7 +56,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
@@ -85,13 +85,13 @@ import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_NEVER;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_WIFI;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_PORT;
import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION;
import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private static final String PROP_ONION = "onion";
private static final String[] EVENTS = {
"CIRC", "ORCONN", "HS_DESC", "NOTICE", "WARN", "ERR"
};
@@ -539,21 +539,16 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
public void poll(Collection<ContactId> connected) {
if (!isRunning()) return;
backoff.increment();
Map<ContactId, TransportProperties> remote =
callback.getRemoteProperties();
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
ContactId c = e.getKey();
if (!connected.contains(c)) connectAndCallBack(c, e.getValue());
}
// TODO: Pass properties to connectAndCallBack()
for (ContactId c : callback.getRemoteProperties().keySet())
if (!connected.contains(c)) connectAndCallBack(c);
}
private void connectAndCallBack(final ContactId c,
final TransportProperties p) {
private void connectAndCallBack(final ContactId c) {
ioExecutor.execute(new Runnable() {
@Override
public void run() {
if (!isRunning()) return;
DuplexTransportConnection d = createConnection(p);
DuplexTransportConnection d = createConnection(c);
if (d != null) {
backoff.reset();
callback.outgoingConnectionCreated(c, d);
@@ -565,11 +560,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override
public DuplexTransportConnection createConnection(ContactId c) {
if (!isRunning()) return null;
return createConnection(callback.getRemoteProperties(c));
}
@Nullable
private DuplexTransportConnection createConnection(TransportProperties p) {
TransportProperties p = callback.getRemoteProperties().get(c);
if (p == null) return null;
String onion = p.get(PROP_ONION);
if (StringUtils.isNullOrEmpty(onion)) return null;
if (!ONION.matcher(onion).matches()) {
@@ -597,6 +589,17 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
}
}
@Override
public boolean supportsInvitations() {
return false;
}
@Override
public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
long timeout, boolean alice) {
throw new UnsupportedOperationException();
}
@Override
public boolean supportsKeyAgreement() {
return false;

View File

@@ -3,7 +3,6 @@ package org.briarproject.bramble.plugin.tor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
import org.briarproject.bramble.util.IoUtils;
import java.io.IOException;
import java.io.InputStream;
@@ -22,12 +21,12 @@ class TorTransportConnection extends AbstractDuplexTransportConnection {
@Override
protected InputStream getInputStream() throws IOException {
return IoUtils.getInputStream(socket);
return socket.getInputStream();
}
@Override
protected OutputStream getOutputStream() throws IOException {
return IoUtils.getOutputStream(socket);
return socket.getOutputStream();
}
@Override

View File

@@ -7,12 +7,12 @@ apply plugin: 'witness'
dependencies {
compile "com.google.dagger:dagger:2.0.2"
compile 'com.google.dagger:dagger-compiler:2.0.2'
compile 'com.google.code.findbugs:jsr305:3.0.2'
compile 'com.google.code.findbugs:jsr305:3.0.1'
testCompile 'junit:junit:4.12'
testCompile "org.jmock:jmock:2.8.2"
testCompile "org.jmock:jmock-junit4:2.8.2"
testCompile "org.jmock:jmock-legacy:2.8.2"
testCompile "org.jmock:jmock:2.8.1"
testCompile "org.jmock:jmock-junit4:2.8.1"
testCompile "org.jmock:jmock-legacy:2.8.1"
testCompile "org.hamcrest:hamcrest-library:1.3"
testCompile "org.hamcrest:hamcrest-core:1.3"
}
@@ -21,7 +21,7 @@ dependencyVerification {
verify = [
'com.google.dagger:dagger:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
'com.google.dagger:dagger-compiler:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
'com.google.code.findbugs:jsr305:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.code.findbugs:jsr305:c885ce34249682bc0236b4a7d56efcc12048e6135a5baf7a9cde8ad8cda13fcd',
'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'com.google.dagger:dagger-producers:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
'com.google.guava:guava:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',

View File

@@ -10,6 +10,8 @@ public interface CryptoComponent {
SecretKey generateSecretKey();
PseudoRandom getPseudoRandom(int seed1, int seed2);
SecureRandom getSecureRandom();
KeyPair generateAgreementKeyPair();
@@ -22,6 +24,15 @@ public interface CryptoComponent {
KeyParser getMessageKeyParser();
/** Generates a random invitation code. */
int generateBTInvitationCode();
/**
* Derives a confirmation code from the given master secret.
* @param alice whether the code is for use by Alice or Bob.
*/
int deriveBTConfirmationCode(SecretKey master, boolean alice);
/**
* Derives a stream header key from the given master secret.
* @param alice whether the key is for use by Alice or Bob.
@@ -126,8 +137,7 @@ public interface CryptoComponent {
TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod);
/** Encodes the pseudo-random tag that is used to recognise a stream. */
void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
long streamNumber);
void encodeTag(byte[] tag, SecretKey tagKey, long streamNumber);
/**
* Signs the given byte[] with the given PrivateKey.

View File

@@ -0,0 +1,12 @@
package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/**
* A deterministic pseudo-random number generator.
*/
@NotNullByDefault
public interface PseudoRandom {
byte[] nextBytes(int bytes);
}

View File

@@ -14,9 +14,8 @@ public interface StreamDecrypterFactory {
StreamDecrypter createStreamDecrypter(InputStream in, StreamContext ctx);
/**
* Creates a {@link StreamDecrypter} for decrypting a contact exchange
* stream.
* Creates a {@link StreamDecrypter} for decrypting an invitation stream.
*/
StreamDecrypter createContactExchangeStreamDecrypter(InputStream in,
StreamDecrypter createInvitationStreamDecrypter(InputStream in,
SecretKey headerKey);
}

View File

@@ -14,9 +14,8 @@ public interface StreamEncrypterFactory {
StreamEncrypter createStreamEncrypter(OutputStream out, StreamContext ctx);
/**
* Creates a {@link StreamEncrypter} for encrypting a contact exchange
* stream.
* Creates a {@link StreamEncrypter} for encrypting an invitation stream.
*/
StreamEncrypter createContactExchangeStreamDecrypter(OutputStream out,
StreamEncrypter createInvitationStreamEncrypter(OutputStream out,
SecretKey headerKey);
}

View File

@@ -0,0 +1,20 @@
package org.briarproject.bramble.api.invitation;
public interface InvitationConstants {
/**
* The connection timeout in milliseconds.
*/
long CONNECTION_TIMEOUT = 60 * 1000;
/**
* The confirmation timeout in milliseconds.
*/
long CONFIRMATION_TIMEOUT = 60 * 1000;
/**
* The number of bits in an invitation or confirmation code. Codes must fit
* into six decimal digits.
*/
int CODE_BITS = 19;
}

View File

@@ -0,0 +1,47 @@
package org.briarproject.bramble.api.invitation;
/**
* An interface for receiving updates about the state of an
* {@link InvitationTask}.
*/
public interface InvitationListener {
/** Called if a connection to the remote peer is established. */
void connectionSucceeded();
/**
* Called if a connection to the remote peer cannot be established. This
* indicates that the protocol has ended unsuccessfully.
*/
void connectionFailed();
/** Called if key agreement with the remote peer succeeds. */
void keyAgreementSucceeded(int localCode, int remoteCode);
/**
* Called if key agreement with the remote peer fails or the connection is
* lost. This indicates that the protocol has ended unsuccessfully.
*/
void keyAgreementFailed();
/** Called if the remote peer's confirmation check succeeds. */
void remoteConfirmationSucceeded();
/**
* Called if remote peer's confirmation check fails or the connection is
* lost. This indicates that the protocol has ended unsuccessfully.
*/
void remoteConfirmationFailed();
/**
* Called if the exchange of pseudonyms succeeds. This indicates that the
* protocol has ended successfully.
*/
void pseudonymExchangeSucceeded(String remoteName);
/**
* Called if the exchange of pseudonyms fails or the connection is lost.
* This indicates that the protocol has ended unsuccessfully.
*/
void pseudonymExchangeFailed();
}

View File

@@ -0,0 +1,85 @@
package org.briarproject.bramble.api.invitation;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
/**
* A snapshot of the state of an {@link InvitationTask}.
*/
@Immutable
@NotNullByDefault
public class InvitationState {
private final int localInvitationCode, remoteInvitationCode;
private final int localConfirmationCode, remoteConfirmationCode;
private final boolean connected, connectionFailed;
private final boolean localCompared, remoteCompared;
private final boolean localMatched, remoteMatched;
@Nullable
private final String contactName;
public InvitationState(int localInvitationCode, int remoteInvitationCode,
int localConfirmationCode, int remoteConfirmationCode,
boolean connected, boolean connectionFailed, boolean localCompared,
boolean remoteCompared, boolean localMatched,
boolean remoteMatched, @Nullable String contactName) {
this.localInvitationCode = localInvitationCode;
this.remoteInvitationCode = remoteInvitationCode;
this.localConfirmationCode = localConfirmationCode;
this.remoteConfirmationCode = remoteConfirmationCode;
this.connected = connected;
this.connectionFailed = connectionFailed;
this.localCompared = localCompared;
this.remoteCompared = remoteCompared;
this.localMatched = localMatched;
this.remoteMatched = remoteMatched;
this.contactName = contactName;
}
public int getLocalInvitationCode() {
return localInvitationCode;
}
public int getRemoteInvitationCode() {
return remoteInvitationCode;
}
public int getLocalConfirmationCode() {
return localConfirmationCode;
}
public int getRemoteConfirmationCode() {
return remoteConfirmationCode;
}
public boolean getConnected() {
return connected;
}
public boolean getConnectionFailed() {
return connectionFailed;
}
public boolean getLocalCompared() {
return localCompared;
}
public boolean getRemoteCompared() {
return remoteCompared;
}
public boolean getLocalMatched() {
return localMatched;
}
public boolean getRemoteMatched() {
return remoteMatched;
}
@Nullable
public String getContactName() {
return contactName;
}
}

View File

@@ -0,0 +1,38 @@
package org.briarproject.bramble.api.invitation;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/**
* A task for exchanging invitations with a remote peer.
*/
@NotNullByDefault
public interface InvitationTask {
/**
* Adds a listener to be informed of state changes and returns the
* task's current state.
*/
InvitationState addListener(InvitationListener l);
/**
* Removes the given listener.
*/
void removeListener(InvitationListener l);
/**
* Asynchronously starts the connection process.
*/
void connect();
/**
* Asynchronously informs the remote peer that the local peer's
* confirmation codes matched.
*/
void localConfirmationSucceeded();
/**
* Asynchronously informs the remote peer that the local peer's
* confirmation codes did not match.
*/
void localConfirmationFailed();
}

View File

@@ -0,0 +1,15 @@
package org.briarproject.bramble.api.invitation;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/**
* Creates tasks for exchanging invitations with remote peers.
*/
@NotNullByDefault
public interface InvitationTaskFactory {
/**
* Creates a task using the given local and remote invitation codes.
*/
InvitationTask createTask(int localCode, int remoteCode);
}

View File

@@ -4,10 +4,5 @@ public interface LanTcpConstants {
TransportId ID = new TransportId("org.briarproject.bramble.lan");
// a transport property (shared with contacts)
String PROP_IP_PORTS = "ipPorts";
// a local setting
String PREF_LAN_IP_PORTS = "ipPorts";
}

View File

@@ -29,11 +29,6 @@ public interface PluginCallback {
*/
Map<ContactId, TransportProperties> getRemoteProperties();
/**
* Returns the plugin's remote transport properties for the given contact.
*/
TransportProperties getRemoteProperties(ContactId c);
/**
* Merges the given settings with the namespaced settings
*/

View File

@@ -32,6 +32,11 @@ public interface PluginManager {
*/
Collection<DuplexPlugin> getDuplexPlugins();
/**
* Returns any duplex plugins that support invitations.
*/
Collection<DuplexPlugin> getInvitationPlugins();
/**
* Returns any duplex plugins that support key agreement.
*/

View File

@@ -4,8 +4,6 @@ public interface TorConstants {
TransportId ID = new TransportId("org.briarproject.bramble.tor");
String PROP_ONION = "onion";
int SOCKS_PORT = 59050;
int CONTROL_PORT = 59051;
@@ -18,5 +16,4 @@ public interface TorConstants {
int PREF_TOR_NETWORK_NEVER = 0;
int PREF_TOR_NETWORK_WIFI = 1;
int PREF_TOR_NETWORK_ALWAYS = 2;
}

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.api.plugin.duplex;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -22,6 +23,20 @@ public interface DuplexPlugin extends Plugin {
@Nullable
DuplexTransportConnection createConnection(ContactId c);
/**
* Returns true if the plugin supports exchanging invitations.
*/
boolean supportsInvitations();
/**
* Attempts to create and return an invitation connection to the remote
* peer. Returns null if no connection can be established within the given
* time.
*/
@Nullable
DuplexTransportConnection createInvitationConnection(PseudoRandom r,
long timeout, boolean alice);
/**
* Returns true if the plugin supports short-range key agreement.
*/

View File

@@ -1,15 +0,0 @@
package org.briarproject.bramble.api.plugin.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* An event that asks the Bluetooth plugin to disable the Bluetooth adapter if
* we previously enabled it.
*/
@Immutable
@NotNullByDefault
public class DisableBluetoothEvent extends Event {
}

View File

@@ -1,14 +0,0 @@
package org.briarproject.bramble.api.plugin.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* An event asks the Bluetooth plugin to enable the Bluetooth adapter.
*/
@Immutable
@NotNullByDefault
public class EnableBluetoothEvent extends Event {
}

View File

@@ -49,13 +49,6 @@ public interface TransportPropertyManager {
Map<ContactId, TransportProperties> getRemoteProperties(TransportId t)
throws DbException;
/**
* Returns the remote transport properties for the given contact and
* transport.
*/
TransportProperties getRemoteProperties(ContactId c, TransportId t)
throws DbException;
/**
* Merges the given properties with the existing local properties for the
* given transport.

View File

@@ -15,9 +15,9 @@ public interface StreamReaderFactory {
InputStream createStreamReader(InputStream in, StreamContext ctx);
/**
* Creates an {@link InputStream InputStream} for reading from a contact
* exchangestream.
* Creates an {@link InputStream InputStream} for reading from an
* invitation stream.
*/
InputStream createContactExchangeStreamReader(InputStream in,
InputStream createInvitationStreamReader(InputStream in,
SecretKey headerKey);
}

View File

@@ -15,9 +15,9 @@ public interface StreamWriterFactory {
OutputStream createStreamWriter(OutputStream out, StreamContext ctx);
/**
* Creates an {@link OutputStream OutputStream} for writing to a contact
* exchange stream.
* Creates an {@link OutputStream OutputStream} for writing to an
* invitation stream.
*/
OutputStream createContactExchangeStreamWriter(OutputStream out,
OutputStream createInvitationStreamWriter(OutputStream out,
SecretKey headerKey);
}

View File

@@ -4,11 +4,6 @@ import org.briarproject.bramble.api.crypto.SecretKey;
public interface TransportConstants {
/**
* The current version of the transport protocol.
*/
int PROTOCOL_VERSION = 3;
/**
* The length of the pseudo-random tag in bytes.
*/
@@ -19,22 +14,21 @@ public interface TransportConstants {
*/
int STREAM_HEADER_NONCE_LENGTH = 24;
/**
* The length of the stream header initialisation vector (IV) in bytes.
*/
int STREAM_HEADER_IV_LENGTH = STREAM_HEADER_NONCE_LENGTH - 8;
/**
* The length of the message authentication code (MAC) in bytes.
*/
int MAC_LENGTH = 16;
/**
* The length of the stream header plaintext in bytes. The stream header
* contains the protocol version, stream number and frame key.
*/
int STREAM_HEADER_PLAINTEXT_LENGTH = 2 + 8 + SecretKey.LENGTH;
/**
* The length of the stream header in bytes.
*/
int STREAM_HEADER_LENGTH = STREAM_HEADER_NONCE_LENGTH
+ STREAM_HEADER_PLAINTEXT_LENGTH + MAC_LENGTH;
int STREAM_HEADER_LENGTH = STREAM_HEADER_IV_LENGTH + SecretKey.LENGTH
+ MAC_LENGTH;
/**
* The length of the frame nonce in bytes.

View File

@@ -8,7 +8,6 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import javax.annotation.Nullable;
@@ -60,24 +59,4 @@ public class IoUtils {
offset += read;
}
}
// Workaround for a bug in Android 7, see
// https://android-review.googlesource.com/#/c/271775/
public static InputStream getInputStream(Socket s) throws IOException {
try {
return s.getInputStream();
} catch (NullPointerException e) {
throw new IOException(e);
}
}
// Workaround for a bug in Android 7, see
// https://android-review.googlesource.com/#/c/271775/
public static OutputStream getOutputStream(Socket s) throws IOException {
try {
return s.getOutputStream();
} catch (NullPointerException e) {
throw new IOException(e);
}
}
}

View File

@@ -8,7 +8,6 @@ import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Collection;
import java.util.Random;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
@@ -28,7 +27,6 @@ public class StringUtils {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
private static final Random random = new Random();
public static boolean isNullOrEmpty(@Nullable String s) {
return s == null || s.length() == 0;
@@ -141,12 +139,4 @@ public class StringUtils {
}
return s.toString();
}
public static String getRandomString(int length) {
char[] c = new char[length];
for (int i = 0; i < length; i++)
c[i] = (char) ('a' + random.nextInt(26));
return new String(c);
}
}

View File

@@ -34,6 +34,13 @@ public class TestUtils {
return getRandomBytes(UniqueId.LENGTH);
}
public static String getRandomString(int length) {
char[] c = new char[length];
for (int i = 0; i < length; i++)
c[i] = (char) ('a' + random.nextInt(26));
return new String(c);
}
public static SecretKey getSecretKey() {
return new SecretKey(getRandomBytes(SecretKey.LENGTH));
}

View File

@@ -1,9 +1,8 @@
plugins {
id 'java'
id 'net.ltgt.apt' version '0.9'
id 'idea'
id "java"
id "net.ltgt.apt" version "0.9"
id "idea"
}
sourceCompatibility = 1.6
targetCompatibility = 1.6
@@ -11,18 +10,17 @@ apply plugin: 'witness'
dependencies {
compile project(':bramble-api')
compile 'com.madgag.spongycastle:core:1.58.0.0'
compile 'com.h2database:h2:1.4.192' // This is the last version that supports Java 1.6
compile 'org.bitlet:weupnp:0.1.4'
compile fileTree(dir: 'libs', include: '*.jar')
compile 'com.madgag.spongycastle:core:1.54.0.0'
compile 'com.h2database:h2:1.4.190'
testCompile project(path: ':bramble-api', configuration: 'testOutput')
}
dependencyVerification {
verify = [
'com.madgag.spongycastle:core:199617dd5698c5a9312b898c0a4cec7ce9dd8649d07f65d91629f58229d72728',
'com.h2database:h2:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
'org.bitlet:weupnp:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
'com.madgag.spongycastle:core:1e7fa4b19ccccd1011364ab838d0b4702470c178bbbdd94c5c90b2d4d749ea1e',
'com.h2database:h2:23ba495a07bbbb3bd6c3084d10a96dad7a23741b8b6d64b213459a784195a98c'
]
}

Binary file not shown.

View File

@@ -8,6 +8,7 @@ import org.briarproject.bramble.db.DatabaseExecutorModule;
import org.briarproject.bramble.db.DatabaseModule;
import org.briarproject.bramble.event.EventModule;
import org.briarproject.bramble.identity.IdentityModule;
import org.briarproject.bramble.invitation.InvitationModule;
import org.briarproject.bramble.keyagreement.KeyAgreementModule;
import org.briarproject.bramble.lifecycle.LifecycleModule;
import org.briarproject.bramble.plugin.PluginModule;
@@ -31,6 +32,7 @@ import dagger.Module;
DatabaseExecutorModule.class,
EventModule.class,
IdentityModule.class,
InvitationModule.class,
KeyAgreementModule.class,
LifecycleModule.class,
PluginModule.class,
@@ -52,7 +54,6 @@ public class BrambleCoreModule {
c.inject(new IdentityModule.EagerSingletons());
c.inject(new LifecycleModule.EagerSingletons());
c.inject(new PluginModule.EagerSingletons());
c.inject(new PropertiesModule.EagerSingletons());
c.inject(new SyncModule.EagerSingletons());
c.inject(new SystemModule.EagerSingletons());
c.inject(new TransportModule.EagerSingletons());

View File

@@ -80,7 +80,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
private volatile boolean alice;
@Inject
ContactExchangeTaskImpl(DatabaseComponent db,
public ContactExchangeTaskImpl(DatabaseComponent db,
AuthorFactory authorFactory, BdfReaderFactory bdfReaderFactory,
BdfWriterFactory bdfWriterFactory, Clock clock,
ConnectionManager connectionManager, ContactManager contactManager,
@@ -146,12 +146,12 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
// Create the readers
InputStream streamReader =
streamReaderFactory.createContactExchangeStreamReader(in,
streamReaderFactory.createInvitationStreamReader(in,
alice ? bobHeaderKey : aliceHeaderKey);
BdfReader r = bdfReaderFactory.createReader(streamReader);
// Create the writers
OutputStream streamWriter =
streamWriterFactory.createContactExchangeStreamWriter(out,
streamWriterFactory.createInvitationStreamWriter(out,
alice ? aliceHeaderKey : bobHeaderKey);
BdfWriter w = bdfWriterFactory.createWriter(streamWriter);

View File

@@ -4,6 +4,7 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.KeyParser;
import org.briarproject.bramble.api.crypto.PrivateKey;
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;
@@ -40,13 +41,12 @@ import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static org.briarproject.bramble.api.invitation.InvitationConstants.CODE_BITS;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS;
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
class CryptoComponentImpl implements CryptoComponent {
@@ -66,6 +66,9 @@ class CryptoComponentImpl implements CryptoComponent {
return s.getBytes(Charset.forName("US-ASCII"));
}
// KDF labels for bluetooth confirmation code derivation
private static final byte[] BT_A_CONFIRM = ascii("ALICE_CONFIRMATION_CODE");
private static final byte[] BT_B_CONFIRM = ascii("BOB_CONFIRMATION_CODE");
// KDF labels for contact exchange stream header key derivation
private static final byte[] A_INVITE = ascii("ALICE_INVITATION_KEY");
private static final byte[] B_INVITE = ascii("BOB_INVITATION_KEY");
@@ -166,6 +169,14 @@ class CryptoComponentImpl implements CryptoComponent {
return new SecretKey(b);
}
@Override
public PseudoRandom getPseudoRandom(int seed1, int 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
public SecureRandom getSecureRandom() {
return secureRandom;
@@ -237,6 +248,20 @@ class CryptoComponentImpl implements CryptoComponent {
return messageEncrypter.getKeyParser();
}
@Override
public int generateBTInvitationCode() {
int codeBytes = (CODE_BITS + 7) / 8;
byte[] random = new byte[codeBytes];
secureRandom.nextBytes(random);
return ByteUtils.readUint(random, CODE_BITS);
}
@Override
public int deriveBTConfirmationCode(SecretKey master, boolean alice) {
byte[] b = macKdf(master, alice ? BT_A_CONFIRM : BT_B_CONFIRM);
return ByteUtils.readUint(b, CODE_BITS);
}
@Override
public SecretKey deriveHeaderKey(SecretKey master,
boolean alice) {
@@ -387,11 +412,8 @@ class CryptoComponentImpl implements CryptoComponent {
}
@Override
public void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
long streamNumber) {
public void encodeTag(byte[] tag, SecretKey tagKey, long streamNumber) {
if (tag.length < TAG_LENGTH) throw new IllegalArgumentException();
if (protocolVersion < 0 || protocolVersion > MAX_16_BIT_UNSIGNED)
throw new IllegalArgumentException();
if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
throw new IllegalArgumentException();
// Initialise the PRF
@@ -399,14 +421,10 @@ class CryptoComponentImpl implements CryptoComponent {
// The output of the PRF must be long enough to use as a tag
int macLength = prf.getDigestSize();
if (macLength < TAG_LENGTH) throw new IllegalStateException();
// The input is the protocol version as a 16-bit integer, followed by
// the stream number as a 64-bit integer
byte[] protocolVersionBytes = new byte[INT_16_BYTES];
ByteUtils.writeUint16(protocolVersion, protocolVersionBytes, 0);
prf.update(protocolVersionBytes, 0, protocolVersionBytes.length);
byte[] streamNumberBytes = new byte[INT_64_BYTES];
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
prf.update(streamNumberBytes, 0, streamNumberBytes.length);
// The input is the stream number as a 64-bit integer
byte[] input = new byte[INT_64_BYTES];
ByteUtils.writeUint64(streamNumber, input, 0);
prf.update(input, 0, input.length);
byte[] mac = new byte[macLength];
prf.doFinal(mac, 0);
// The output is the first TAG_LENGTH bytes of the MAC

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.spongycastle.crypto.Digest;
import org.spongycastle.crypto.engines.Salsa20Engine;
@@ -10,11 +11,11 @@ import javax.annotation.concurrent.NotThreadSafe;
@NotThreadSafe
@NotNullByDefault
class PseudoRandom {
class PseudoRandomImpl implements PseudoRandom {
private final Salsa20Engine cipher = new Salsa20Engine();
PseudoRandom(byte[] seed) {
PseudoRandomImpl(byte[] seed) {
// Hash the seed to produce a 32-byte key
byte[] key = new byte[32];
Digest digest = new Blake2sDigest();
@@ -25,7 +26,8 @@ class PseudoRandom {
cipher.init(true, new ParametersWithIV(new KeyParameter(key), nonce));
}
byte[] nextBytes(int length) {
@Override
public byte[] nextBytes(int length) {
byte[] in = new byte[length], out = new byte[length];
cipher.processBytes(in, 0, length, out, 0);
return out;

View File

@@ -32,7 +32,7 @@ class StreamDecrypterFactoryImpl implements StreamDecrypterFactory {
}
@Override
public StreamDecrypter createContactExchangeStreamDecrypter(InputStream in,
public StreamDecrypter createInvitationStreamDecrypter(InputStream in,
SecretKey headerKey) {
return new StreamDecrypterImpl(in, cipherProvider.get(), 0, headerKey);
}

View File

@@ -20,11 +20,9 @@ import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_NO
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_PLAINTEXT_LENGTH;
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
@NotThreadSafe
@@ -119,7 +117,7 @@ class StreamDecrypterImpl implements StreamDecrypter {
private void readStreamHeader() throws IOException {
byte[] streamHeaderCiphertext = new byte[STREAM_HEADER_LENGTH];
byte[] streamHeaderPlaintext = new byte[STREAM_HEADER_PLAINTEXT_LENGTH];
byte[] streamHeaderPlaintext = new byte[SecretKey.LENGTH];
// Read the stream header
int offset = 0;
while (offset < STREAM_HEADER_LENGTH) {
@@ -128,35 +126,21 @@ class StreamDecrypterImpl implements StreamDecrypter {
if (read == -1) throw new EOFException();
offset += read;
}
// Extract the nonce
// The nonce consists of the stream number followed by the IV
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
System.arraycopy(streamHeaderCiphertext, 0, streamHeaderNonce, 0,
STREAM_HEADER_NONCE_LENGTH);
ByteUtils.writeUint64(streamNumber, streamHeaderNonce, 0);
System.arraycopy(streamHeaderCiphertext, 0, streamHeaderNonce,
INT_64_BYTES, STREAM_HEADER_IV_LENGTH);
// Decrypt and authenticate the stream header
try {
cipher.init(false, streamHeaderKey, streamHeaderNonce);
int decrypted = cipher.process(streamHeaderCiphertext,
STREAM_HEADER_NONCE_LENGTH,
STREAM_HEADER_PLAINTEXT_LENGTH + MAC_LENGTH,
STREAM_HEADER_IV_LENGTH, SecretKey.LENGTH + MAC_LENGTH,
streamHeaderPlaintext, 0);
if (decrypted != STREAM_HEADER_PLAINTEXT_LENGTH)
throw new RuntimeException();
if (decrypted != SecretKey.LENGTH) throw new RuntimeException();
} catch (GeneralSecurityException e) {
throw new FormatException();
}
// Check the protocol version
int receivedProtocolVersion =
ByteUtils.readUint16(streamHeaderPlaintext, 0);
if (receivedProtocolVersion != PROTOCOL_VERSION)
throw new FormatException();
// Check the stream number
long receivedStreamNumber = ByteUtils.readUint64(streamHeaderPlaintext,
INT_16_BYTES);
if (receivedStreamNumber != streamNumber) throw new FormatException();
// Extract the frame key
byte[] frameKeyBytes = new byte[SecretKey.LENGTH];
System.arraycopy(streamHeaderPlaintext, INT_16_BYTES + INT_64_BYTES,
frameKeyBytes, 0, SecretKey.LENGTH);
frameKey = new SecretKey(frameKeyBytes);
frameKey = new SecretKey(streamHeaderPlaintext);
}
}

View File

@@ -13,8 +13,7 @@ import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import javax.inject.Provider;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
@Immutable
@@ -37,22 +36,22 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
AuthenticatedCipher cipher = cipherProvider.get();
long streamNumber = ctx.getStreamNumber();
byte[] tag = new byte[TAG_LENGTH];
crypto.encodeTag(tag, ctx.getTagKey(), PROTOCOL_VERSION, streamNumber);
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
crypto.getSecureRandom().nextBytes(streamHeaderNonce);
crypto.encodeTag(tag, ctx.getTagKey(), streamNumber);
byte[] streamHeaderIv = new byte[STREAM_HEADER_IV_LENGTH];
crypto.getSecureRandom().nextBytes(streamHeaderIv);
SecretKey frameKey = crypto.generateSecretKey();
return new StreamEncrypterImpl(out, cipher, streamNumber, tag,
streamHeaderNonce, ctx.getHeaderKey(), frameKey);
streamHeaderIv, ctx.getHeaderKey(), frameKey);
}
@Override
public StreamEncrypter createContactExchangeStreamDecrypter(
OutputStream out, SecretKey headerKey) {
public StreamEncrypter createInvitationStreamEncrypter(OutputStream out,
SecretKey headerKey) {
AuthenticatedCipher cipher = cipherProvider.get();
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
crypto.getSecureRandom().nextBytes(streamHeaderNonce);
byte[] streamHeaderIv = new byte[STREAM_HEADER_IV_LENGTH];
crypto.getSecureRandom().nextBytes(streamHeaderIv);
SecretKey frameKey = crypto.generateSecretKey();
return new StreamEncrypterImpl(out, cipher, 0, null, streamHeaderNonce,
return new StreamEncrypterImpl(out, cipher, 0, null, streamHeaderIv,
headerKey, frameKey);
}
}

View File

@@ -18,11 +18,9 @@ import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_NO
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_PLAINTEXT_LENGTH;
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
@NotThreadSafe
@@ -35,7 +33,7 @@ class StreamEncrypterImpl implements StreamEncrypter {
private final long streamNumber;
@Nullable
private final byte[] tag;
private final byte[] streamHeaderNonce;
private final byte[] streamHeaderIv;
private final byte[] frameNonce, frameHeader;
private final byte[] framePlaintext, frameCiphertext;
@@ -43,13 +41,13 @@ class StreamEncrypterImpl implements StreamEncrypter {
private boolean writeTag, writeStreamHeader;
StreamEncrypterImpl(OutputStream out, AuthenticatedCipher cipher,
long streamNumber, @Nullable byte[] tag, byte[] streamHeaderNonce,
long streamNumber, @Nullable byte[] tag, byte[] streamHeaderIv,
SecretKey streamHeaderKey, SecretKey frameKey) {
this.out = out;
this.cipher = cipher;
this.streamNumber = streamNumber;
this.tag = tag;
this.streamHeaderNonce = streamHeaderNonce;
this.streamHeaderIv = streamHeaderIv;
this.streamHeaderKey = streamHeaderKey;
this.frameKey = frameKey;
frameNonce = new byte[FRAME_NONCE_LENGTH];
@@ -116,23 +114,22 @@ class StreamEncrypterImpl implements StreamEncrypter {
}
private void writeStreamHeader() throws IOException {
// The header contains the protocol version, stream number and frame key
byte[] streamHeaderPlaintext = new byte[STREAM_HEADER_PLAINTEXT_LENGTH];
ByteUtils.writeUint16(PROTOCOL_VERSION, streamHeaderPlaintext, 0);
ByteUtils.writeUint64(streamNumber, streamHeaderPlaintext,
INT_16_BYTES);
System.arraycopy(frameKey.getBytes(), 0, streamHeaderPlaintext,
INT_16_BYTES + INT_64_BYTES, SecretKey.LENGTH);
// The nonce consists of the stream number followed by the IV
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
ByteUtils.writeUint64(streamNumber, streamHeaderNonce, 0);
System.arraycopy(streamHeaderIv, 0, streamHeaderNonce, INT_64_BYTES,
STREAM_HEADER_IV_LENGTH);
byte[] streamHeaderPlaintext = frameKey.getBytes();
byte[] streamHeaderCiphertext = new byte[STREAM_HEADER_LENGTH];
System.arraycopy(streamHeaderNonce, 0, streamHeaderCiphertext, 0,
STREAM_HEADER_NONCE_LENGTH);
// Encrypt and authenticate the stream header key
System.arraycopy(streamHeaderIv, 0, streamHeaderCiphertext, 0,
STREAM_HEADER_IV_LENGTH);
// Encrypt and authenticate the frame key
try {
cipher.init(true, streamHeaderKey, streamHeaderNonce);
int encrypted = cipher.process(streamHeaderPlaintext, 0,
STREAM_HEADER_PLAINTEXT_LENGTH, streamHeaderCiphertext,
STREAM_HEADER_NONCE_LENGTH);
if (encrypted != STREAM_HEADER_PLAINTEXT_LENGTH + MAC_LENGTH)
SecretKey.LENGTH, streamHeaderCiphertext,
STREAM_HEADER_IV_LENGTH);
if (encrypted != SecretKey.LENGTH + MAC_LENGTH)
throw new RuntimeException();
} catch (GeneralSecurityException badCipher) {
throw new RuntimeException(badCipher);

View File

@@ -70,7 +70,25 @@ class XSalsa20Poly1305AuthenticatedCipher implements AuthenticatedCipher {
byte[] subKey = new byte[SUBKEY_LENGTH];
xSalsa20Engine.processBytes(zero, 0, SUBKEY_LENGTH, subKey, 0);
// Clamp the subkey
// Reverse the order of the Poly130 subkey
//
// NaCl and libsodium use the first 32 bytes of XSalsa20 as the
// subkey for crypto_onetimeauth_poly1305, which interprets it
// as r[0] ... r[15], k[0] ... k[15]. See section 9 of the NaCl
// paper (http://cr.yp.to/highspeed/naclcrypto-20090310.pdf),
// where the XSalsa20 output is defined as (r, s, t, ...).
//
// BC's Poly1305 implementation interprets the subkey as
// k[0] ... k[15], r[0] ... r[15] (per poly1305_aes_clamp in
// the reference implementation).
//
// To be NaCl-compatible, we reverse the subkey.
System.arraycopy(subKey, 0, zero, 0, SUBKEY_LENGTH / 2);
System.arraycopy(subKey, SUBKEY_LENGTH / 2, subKey, 0,
SUBKEY_LENGTH / 2);
System.arraycopy(zero, 0, subKey, SUBKEY_LENGTH / 2,
SUBKEY_LENGTH / 2);
// Now we can clamp the correct part of the subkey
Poly1305KeyGenerator.clamp(subKey);
// Initialize Poly1305 with the subkey

View File

@@ -0,0 +1,119 @@
package org.briarproject.bramble.invitation;
import org.briarproject.bramble.api.contact.ContactExchangeTask;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.data.BdfReader;
import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.data.BdfWriter;
import org.briarproject.bramble.api.data.BdfWriterFactory;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.util.logging.Logger;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
/**
* A connection thread for the peer being Alice in the invitation protocol.
*/
@NotNullByDefault
class AliceConnector extends Connector {
private static final Logger LOG =
Logger.getLogger(AliceConnector.class.getName());
AliceConnector(CryptoComponent crypto, BdfReaderFactory bdfReaderFactory,
BdfWriterFactory bdfWriterFactory,
ContactExchangeTask contactExchangeTask, ConnectorGroup group,
DuplexPlugin plugin, LocalAuthor localAuthor, PseudoRandom random) {
super(crypto, bdfReaderFactory, bdfWriterFactory, contactExchangeTask,
group, plugin, localAuthor, random);
}
@Override
public void run() {
// Create an incoming or outgoing connection
DuplexTransportConnection conn = createInvitationConnection(true);
if (conn == null) return;
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " connected");
// Don't proceed with more than one connection
if (group.getAndSetConnected()) {
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " redundant");
tryToClose(conn, false);
return;
}
// Carry out the key agreement protocol
InputStream in;
OutputStream out;
BdfReader r;
BdfWriter w;
SecretKey master;
try {
in = conn.getReader().getInputStream();
out = conn.getWriter().getOutputStream();
r = bdfReaderFactory.createReader(in);
w = bdfWriterFactory.createWriter(out);
// Alice goes first
sendPublicKeyHash(w);
byte[] hash = receivePublicKeyHash(r);
sendPublicKey(w);
byte[] key = receivePublicKey(r);
master = deriveMasterSecret(hash, key, true);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
group.keyAgreementFailed();
tryToClose(conn, true);
return;
} catch (GeneralSecurityException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
group.keyAgreementFailed();
tryToClose(conn, true);
return;
}
// The key agreement succeeded - derive the confirmation codes
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " agreement succeeded");
int aliceCode = crypto.deriveBTConfirmationCode(master, true);
int bobCode = crypto.deriveBTConfirmationCode(master, false);
group.keyAgreementSucceeded(aliceCode, bobCode);
// Exchange confirmation results
boolean localMatched, remoteMatched;
try {
localMatched = group.waitForLocalConfirmationResult();
sendConfirmation(w, localMatched);
remoteMatched = receiveConfirmation(r);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
group.remoteConfirmationFailed();
tryToClose(conn, true);
return;
} catch (InterruptedException e) {
LOG.warning("Interrupted while waiting for confirmation");
group.remoteConfirmationFailed();
tryToClose(conn, true);
Thread.currentThread().interrupt();
return;
}
if (remoteMatched) group.remoteConfirmationSucceeded();
else group.remoteConfirmationFailed();
if (!(localMatched && remoteMatched)) {
if (LOG.isLoggable(INFO))
LOG.info(pluginName + " confirmation failed");
tryToClose(conn, false);
return;
}
// Confirmation succeeded - upgrade to a secure connection
if (LOG.isLoggable(INFO))
LOG.info(pluginName + " confirmation succeeded");
contactExchangeTask.startExchange(group, localAuthor, master, conn,
plugin.getId(), true);
}
}

View File

@@ -0,0 +1,119 @@
package org.briarproject.bramble.invitation;
import org.briarproject.bramble.api.contact.ContactExchangeTask;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.data.BdfReader;
import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.data.BdfWriter;
import org.briarproject.bramble.api.data.BdfWriterFactory;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.util.logging.Logger;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
/**
* A connection thread for the peer being Bob in the invitation protocol.
*/
@NotNullByDefault
class BobConnector extends Connector {
private static final Logger LOG =
Logger.getLogger(BobConnector.class.getName());
BobConnector(CryptoComponent crypto, BdfReaderFactory bdfReaderFactory,
BdfWriterFactory bdfWriterFactory,
ContactExchangeTask contactExchangeTask, ConnectorGroup group,
DuplexPlugin plugin, LocalAuthor localAuthor, PseudoRandom random) {
super(crypto, bdfReaderFactory, bdfWriterFactory, contactExchangeTask,
group, plugin, localAuthor, random);
}
@Override
public void run() {
// Create an incoming or outgoing connection
DuplexTransportConnection conn = createInvitationConnection(false);
if (conn == null) return;
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " connected");
// Carry out the key agreement protocol
InputStream in;
OutputStream out;
BdfReader r;
BdfWriter w;
SecretKey master;
try {
in = conn.getReader().getInputStream();
out = conn.getWriter().getOutputStream();
r = bdfReaderFactory.createReader(in);
w = bdfWriterFactory.createWriter(out);
// Alice goes first
byte[] hash = receivePublicKeyHash(r);
// Don't proceed with more than one connection
if (group.getAndSetConnected()) {
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " redundant");
tryToClose(conn, false);
return;
}
sendPublicKeyHash(w);
byte[] key = receivePublicKey(r);
sendPublicKey(w);
master = deriveMasterSecret(hash, key, false);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
group.keyAgreementFailed();
tryToClose(conn, true);
return;
} catch (GeneralSecurityException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
group.keyAgreementFailed();
tryToClose(conn, true);
return;
}
// The key agreement succeeded - derive the confirmation codes
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " agreement succeeded");
int aliceCode = crypto.deriveBTConfirmationCode(master, true);
int bobCode = crypto.deriveBTConfirmationCode(master, false);
group.keyAgreementSucceeded(bobCode, aliceCode);
// Exchange confirmation results
boolean localMatched, remoteMatched;
try {
remoteMatched = receiveConfirmation(r);
localMatched = group.waitForLocalConfirmationResult();
sendConfirmation(w, localMatched);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
group.remoteConfirmationFailed();
tryToClose(conn, true);
return;
} catch (InterruptedException e) {
LOG.warning("Interrupted while waiting for confirmation");
group.remoteConfirmationFailed();
tryToClose(conn, true);
Thread.currentThread().interrupt();
return;
}
if (remoteMatched) group.remoteConfirmationSucceeded();
else group.remoteConfirmationFailed();
if (!(localMatched && remoteMatched)) {
if (LOG.isLoggable(INFO))
LOG.info(pluginName + " confirmation failed");
tryToClose(conn, false);
return;
}
// Confirmation succeeded - upgrade to a secure connection
if (LOG.isLoggable(INFO))
LOG.info(pluginName + " confirmation succeeded");
contactExchangeTask.startExchange(group, localAuthor, master, conn,
plugin.getId(), false);
}
}

View File

@@ -0,0 +1,150 @@
package org.briarproject.bramble.invitation;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.contact.ContactExchangeTask;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.KeyParser;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.data.BdfReader;
import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.data.BdfWriter;
import org.briarproject.bramble.api.data.BdfWriterFactory;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.bramble.api.invitation.InvitationConstants.CONNECTION_TIMEOUT;
// FIXME: This class has way too many dependencies
@NotNullByDefault
abstract class Connector extends Thread {
private static final Logger LOG =
Logger.getLogger(Connector.class.getName());
private static final String LABEL_PUBLIC_KEY =
"org.briarproject.bramble.invitation.PUBLIC_KEY";
protected final CryptoComponent crypto;
protected final BdfReaderFactory bdfReaderFactory;
protected final BdfWriterFactory bdfWriterFactory;
protected final ContactExchangeTask contactExchangeTask;
protected final ConnectorGroup group;
protected final DuplexPlugin plugin;
protected final LocalAuthor localAuthor;
protected final PseudoRandom random;
protected final String pluginName;
private final KeyPair keyPair;
private final KeyParser keyParser;
Connector(CryptoComponent crypto, BdfReaderFactory bdfReaderFactory,
BdfWriterFactory bdfWriterFactory,
ContactExchangeTask contactExchangeTask, ConnectorGroup group,
DuplexPlugin plugin, LocalAuthor localAuthor, PseudoRandom random) {
super("Connector");
this.crypto = crypto;
this.bdfReaderFactory = bdfReaderFactory;
this.bdfWriterFactory = bdfWriterFactory;
this.contactExchangeTask = contactExchangeTask;
this.group = group;
this.plugin = plugin;
this.localAuthor = localAuthor;
this.random = random;
pluginName = plugin.getClass().getName();
keyPair = crypto.generateAgreementKeyPair();
keyParser = crypto.getAgreementKeyParser();
}
@Nullable
DuplexTransportConnection createInvitationConnection(boolean alice) {
if (LOG.isLoggable(INFO))
LOG.info(pluginName + " creating invitation connection");
return plugin.createInvitationConnection(random, CONNECTION_TIMEOUT,
alice);
}
void sendPublicKeyHash(BdfWriter w) throws IOException {
byte[] hash =
crypto.hash(LABEL_PUBLIC_KEY, keyPair.getPublic().getEncoded());
w.writeRaw(hash);
w.flush();
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " sent hash");
}
byte[] receivePublicKeyHash(BdfReader r) throws IOException {
int hashLength = crypto.getHashLength();
byte[] b = r.readRaw(hashLength);
if (b.length < hashLength) throw new FormatException();
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " received hash");
return b;
}
void sendPublicKey(BdfWriter w) throws IOException {
byte[] key = keyPair.getPublic().getEncoded();
w.writeRaw(key);
w.flush();
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " sent key");
}
byte[] receivePublicKey(BdfReader r)
throws GeneralSecurityException, IOException {
byte[] b = r.readRaw(MAX_PUBLIC_KEY_LENGTH);
keyParser.parsePublicKey(b);
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " received key");
return b;
}
SecretKey deriveMasterSecret(byte[] hash, byte[] key, boolean alice)
throws GeneralSecurityException {
// Check that the hash matches the key
byte[] keyHash =
crypto.hash(LABEL_PUBLIC_KEY, keyPair.getPublic().getEncoded());
if (!Arrays.equals(hash, keyHash)) {
if (LOG.isLoggable(INFO))
LOG.info(pluginName + " hash does not match key");
throw new GeneralSecurityException();
}
// Derive the master secret
if (LOG.isLoggable(INFO))
LOG.info(pluginName + " deriving master secret");
return crypto.deriveMasterSecret(key, keyPair, alice);
}
void sendConfirmation(BdfWriter w, boolean confirmed) throws IOException {
w.writeBoolean(confirmed);
w.flush();
if (LOG.isLoggable(INFO))
LOG.info(pluginName + " sent confirmation: " + confirmed);
}
boolean receiveConfirmation(BdfReader r) throws IOException {
boolean confirmed = r.readBoolean();
if (LOG.isLoggable(INFO))
LOG.info(pluginName + " received confirmation: " + confirmed);
return confirmed;
}
protected void tryToClose(DuplexTransportConnection conn,
boolean exception) {
try {
LOG.info("Closing connection");
conn.getReader().dispose(exception, true);
conn.getWriter().dispose(exception);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
}

View File

@@ -0,0 +1,278 @@
package org.briarproject.bramble.invitation;
import org.briarproject.bramble.api.contact.ContactExchangeListener;
import org.briarproject.bramble.api.contact.ContactExchangeTask;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.data.BdfWriterFactory;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.invitation.InvitationListener;
import org.briarproject.bramble.api.invitation.InvitationState;
import org.briarproject.bramble.api.invitation.InvitationTask;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.invitation.InvitationConstants.CONFIRMATION_TIMEOUT;
/**
* A task consisting of one or more parallel connection attempts.
*/
@MethodsNotNullByDefault
@ParametersNotNullByDefault
class ConnectorGroup extends Thread implements InvitationTask,
ContactExchangeListener {
private static final Logger LOG =
Logger.getLogger(ConnectorGroup.class.getName());
private final CryptoComponent crypto;
private final BdfReaderFactory bdfReaderFactory;
private final BdfWriterFactory bdfWriterFactory;
private final ContactExchangeTask contactExchangeTask;
private final IdentityManager identityManager;
private final PluginManager pluginManager;
private final int localInvitationCode, remoteInvitationCode;
private final Collection<InvitationListener> listeners;
private final AtomicBoolean connected;
private final CountDownLatch localConfirmationLatch;
private final Lock lock = new ReentrantLock();
// The following are locking: lock
private int localConfirmationCode = -1, remoteConfirmationCode = -1;
private boolean connectionFailed = false;
private boolean localCompared = false, remoteCompared = false;
private boolean localMatched = false, remoteMatched = false;
private String remoteName = null;
ConnectorGroup(CryptoComponent crypto, BdfReaderFactory bdfReaderFactory,
BdfWriterFactory bdfWriterFactory,
ContactExchangeTask contactExchangeTask,
IdentityManager identityManager, PluginManager pluginManager,
int localInvitationCode, int remoteInvitationCode) {
super("ConnectorGroup");
this.crypto = crypto;
this.bdfReaderFactory = bdfReaderFactory;
this.bdfWriterFactory = bdfWriterFactory;
this.contactExchangeTask = contactExchangeTask;
this.identityManager = identityManager;
this.pluginManager = pluginManager;
this.localInvitationCode = localInvitationCode;
this.remoteInvitationCode = remoteInvitationCode;
listeners = new CopyOnWriteArrayList<InvitationListener>();
connected = new AtomicBoolean(false);
localConfirmationLatch = new CountDownLatch(1);
}
@Override
public InvitationState addListener(InvitationListener l) {
lock.lock();
try {
listeners.add(l);
return new InvitationState(localInvitationCode,
remoteInvitationCode, localConfirmationCode,
remoteConfirmationCode, connected.get(), connectionFailed,
localCompared, remoteCompared, localMatched, remoteMatched,
remoteName);
} finally {
lock.unlock();
}
}
@Override
public void removeListener(InvitationListener l) {
listeners.remove(l);
}
@Override
public void connect() {
start();
}
@Override
public void run() {
LocalAuthor localAuthor;
// Load the local pseudonym
try {
localAuthor = identityManager.getLocalAuthor();
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
lock.lock();
try {
connectionFailed = true;
} finally {
lock.unlock();
}
for (InvitationListener l : listeners) l.connectionFailed();
return;
}
// Start the connection threads
Collection<Connector> connectors = new ArrayList<Connector>();
// Alice is the party with the smaller invitation code
if (localInvitationCode < remoteInvitationCode) {
for (DuplexPlugin plugin : pluginManager.getInvitationPlugins()) {
Connector c = createAliceConnector(plugin, localAuthor);
connectors.add(c);
c.start();
}
} else {
for (DuplexPlugin plugin : pluginManager.getInvitationPlugins()) {
Connector c = createBobConnector(plugin, localAuthor);
connectors.add(c);
c.start();
}
}
// Wait for the connection threads to finish
try {
for (Connector c : connectors) c.join();
} catch (InterruptedException e) {
LOG.warning("Interrupted while waiting for connectors");
Thread.currentThread().interrupt();
}
// If none of the threads connected, inform the listeners
if (!connected.get()) {
lock.lock();
try {
connectionFailed = true;
} finally {
lock.unlock();
}
for (InvitationListener l : listeners) l.connectionFailed();
}
}
private Connector createAliceConnector(DuplexPlugin plugin,
LocalAuthor localAuthor) {
PseudoRandom random = crypto.getPseudoRandom(localInvitationCode,
remoteInvitationCode);
return new AliceConnector(crypto, bdfReaderFactory, bdfWriterFactory,
contactExchangeTask, this, plugin, localAuthor, random);
}
private Connector createBobConnector(DuplexPlugin plugin,
LocalAuthor localAuthor) {
PseudoRandom random = crypto.getPseudoRandom(remoteInvitationCode,
localInvitationCode);
return new BobConnector(crypto, bdfReaderFactory, bdfWriterFactory,
contactExchangeTask, this, plugin, localAuthor, random);
}
@Override
public void localConfirmationSucceeded() {
lock.lock();
try {
localCompared = true;
localMatched = true;
} finally {
lock.unlock();
}
localConfirmationLatch.countDown();
}
@Override
public void localConfirmationFailed() {
lock.lock();
try {
localCompared = true;
localMatched = false;
} finally {
lock.unlock();
}
localConfirmationLatch.countDown();
}
boolean getAndSetConnected() {
boolean redundant = connected.getAndSet(true);
if (!redundant)
for (InvitationListener l : listeners) l.connectionSucceeded();
return redundant;
}
void keyAgreementSucceeded(int localCode, int remoteCode) {
lock.lock();
try {
localConfirmationCode = localCode;
remoteConfirmationCode = remoteCode;
} finally {
lock.unlock();
}
for (InvitationListener l : listeners)
l.keyAgreementSucceeded(localCode, remoteCode);
}
void keyAgreementFailed() {
for (InvitationListener l : listeners) l.keyAgreementFailed();
}
boolean waitForLocalConfirmationResult() throws InterruptedException {
localConfirmationLatch.await(CONFIRMATION_TIMEOUT, MILLISECONDS);
lock.lock();
try {
return localMatched;
} finally {
lock.unlock();
}
}
void remoteConfirmationSucceeded() {
lock.lock();
try {
remoteCompared = true;
remoteMatched = true;
} finally {
lock.unlock();
}
for (InvitationListener l : listeners) l.remoteConfirmationSucceeded();
}
void remoteConfirmationFailed() {
lock.lock();
try {
remoteCompared = true;
remoteMatched = false;
} finally {
lock.unlock();
}
for (InvitationListener l : listeners) l.remoteConfirmationFailed();
}
@Override
public void contactExchangeSucceeded(Author remoteAuthor) {
String name = remoteAuthor.getName();
lock.lock();
try {
remoteName = name;
} finally {
lock.unlock();
}
for (InvitationListener l : listeners)
l.pseudonymExchangeSucceeded(name);
}
@Override
public void duplicateContact(Author remoteAuthor) {
// TODO differentiate
for (InvitationListener l : listeners) l.pseudonymExchangeFailed();
}
@Override
public void contactExchangeFailed() {
for (InvitationListener l : listeners) l.pseudonymExchangeFailed();
}
}

View File

@@ -0,0 +1,16 @@
package org.briarproject.bramble.invitation;
import org.briarproject.bramble.api.invitation.InvitationTaskFactory;
import dagger.Module;
import dagger.Provides;
@Module
public class InvitationModule {
@Provides
InvitationTaskFactory provideInvitationTaskFactory(
InvitationTaskFactoryImpl invitationTaskFactory) {
return invitationTaskFactory;
}
}

View File

@@ -0,0 +1,47 @@
package org.briarproject.bramble.invitation;
import org.briarproject.bramble.api.contact.ContactExchangeTask;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.data.BdfWriterFactory;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.invitation.InvitationTask;
import org.briarproject.bramble.api.invitation.InvitationTaskFactory;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginManager;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
@Immutable
@NotNullByDefault
class InvitationTaskFactoryImpl implements InvitationTaskFactory {
private final CryptoComponent crypto;
private final BdfReaderFactory bdfReaderFactory;
private final BdfWriterFactory bdfWriterFactory;
private final ContactExchangeTask contactExchangeTask;
private final IdentityManager identityManager;
private final PluginManager pluginManager;
@Inject
InvitationTaskFactoryImpl(CryptoComponent crypto,
BdfReaderFactory bdfReaderFactory,
BdfWriterFactory bdfWriterFactory,
ContactExchangeTask contactExchangeTask,
IdentityManager identityManager, PluginManager pluginManager) {
this.crypto = crypto;
this.bdfReaderFactory = bdfReaderFactory;
this.bdfWriterFactory = bdfWriterFactory;
this.contactExchangeTask = contactExchangeTask;
this.identityManager = identityManager;
this.pluginManager = pluginManager;
}
@Override
public InvitationTask createTask(int localCode, int remoteCode) {
return new ConnectorGroup(crypto, bdfReaderFactory, bdfWriterFactory,
contactExchangeTask, identityManager, pluginManager,
localCode, remoteCode);
}
}

View File

@@ -164,6 +164,14 @@ class PluginManagerImpl implements PluginManager, Service {
return new ArrayList<DuplexPlugin>(duplexPlugins);
}
@Override
public Collection<DuplexPlugin> getInvitationPlugins() {
List<DuplexPlugin> supported = new ArrayList<DuplexPlugin>();
for (DuplexPlugin d : duplexPlugins)
if (d.supportsInvitations()) supported.add(d);
return supported;
}
@Override
public Collection<DuplexPlugin> getKeyAgreementPlugins() {
List<DuplexPlugin> supported = new ArrayList<DuplexPlugin>();
@@ -283,16 +291,6 @@ class PluginManagerImpl implements PluginManager, Service {
}
}
@Override
public TransportProperties getRemoteProperties(ContactId c) {
try {
return transportPropertyManager.getRemoteProperties(c, id);
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return new TransportProperties();
}
}
@Override
public void mergeSettings(Settings s) {
try {

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.plugin.tcp;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
@@ -34,7 +35,6 @@ import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.PREF_LAN_IP_PORTS;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_IP_PORTS;
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress;
@@ -45,6 +45,7 @@ class LanTcpPlugin extends TcpPlugin {
Logger.getLogger(LanTcpPlugin.class.getName());
private static final int MAX_ADDRESSES = 4;
private static final String PROP_IP_PORTS = "ipPorts";
private static final String SEPARATOR = ",";
LanTcpPlugin(Executor ioExecutor, Backoff backoff,
@@ -125,8 +126,9 @@ class LanTcpPlugin extends TcpPlugin {
}
@Override
protected List<InetSocketAddress> getRemoteSocketAddresses(
TransportProperties p) {
protected List<InetSocketAddress> getRemoteSocketAddresses(ContactId c) {
TransportProperties p = callback.getRemoteProperties().get(c);
if (p == null) return Collections.emptyList();
return parseSocketAddresses(p.get(PROP_IP_PORTS));
}

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.plugin.tcp;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
@@ -9,7 +10,6 @@ import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.util.StringUtils;
import java.io.IOException;
@@ -25,8 +25,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
@@ -69,11 +67,11 @@ abstract class TcpPlugin implements DuplexPlugin {
protected abstract void setLocalSocketAddress(InetSocketAddress a);
/**
* Returns zero or more socket addresses for connecting to a contact with
* the given transport properties.
* Returns zero or more socket addresses for connecting to the given
* contact.
*/
protected abstract List<InetSocketAddress> getRemoteSocketAddresses(
TransportProperties p);
ContactId c);
/**
* Returns true if connections to the given address can be attempted.
@@ -210,21 +208,16 @@ abstract class TcpPlugin implements DuplexPlugin {
public void poll(Collection<ContactId> connected) {
if (!isRunning()) return;
backoff.increment();
Map<ContactId, TransportProperties> remote =
callback.getRemoteProperties();
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
ContactId c = e.getKey();
if (!connected.contains(c)) connectAndCallBack(c, e.getValue());
}
// TODO: Pass properties to connectAndCallBack()
for (ContactId c : callback.getRemoteProperties().keySet())
if (!connected.contains(c)) connectAndCallBack(c);
}
private void connectAndCallBack(final ContactId c,
final TransportProperties p) {
private void connectAndCallBack(final ContactId c) {
ioExecutor.execute(new Runnable() {
@Override
public void run() {
if (!isRunning()) return;
DuplexTransportConnection d = createConnection(p);
DuplexTransportConnection d = createConnection(c);
if (d != null) {
backoff.reset();
callback.outgoingConnectionCreated(c, d);
@@ -236,12 +229,7 @@ abstract class TcpPlugin implements DuplexPlugin {
@Override
public DuplexTransportConnection createConnection(ContactId c) {
if (!isRunning()) return null;
return createConnection(callback.getRemoteProperties(c));
}
@Nullable
private DuplexTransportConnection createConnection(TransportProperties p) {
for (InetSocketAddress remote : getRemoteSocketAddresses(p)) {
for (InetSocketAddress remote : getRemoteSocketAddresses(c)) {
if (!isConnectable(remote)) {
if (LOG.isLoggable(INFO)) {
SocketAddress local = socket.getLocalSocketAddress();
@@ -293,6 +281,17 @@ abstract class TcpPlugin implements DuplexPlugin {
}
}
@Override
public boolean supportsInvitations() {
return false;
}
@Override
public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
long timeout, boolean alice) {
throw new UnsupportedOperationException();
}
@Override
public boolean supportsKeyAgreement() {
return false;

View File

@@ -3,7 +3,6 @@ package org.briarproject.bramble.plugin.tcp;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
import org.briarproject.bramble.util.IoUtils;
import java.io.IOException;
import java.io.InputStream;
@@ -25,12 +24,12 @@ class TcpTransportConnection extends AbstractDuplexTransportConnection {
@Override
protected InputStream getInputStream() throws IOException {
return IoUtils.getInputStream(socket);
return socket.getInputStream();
}
@Override
protected OutputStream getOutputStream() throws IOException {
return IoUtils.getOutputStream(socket);
return socket.getOutputStream();
}
@Override

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.plugin.tcp;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
@@ -77,8 +78,9 @@ class WanTcpPlugin extends TcpPlugin {
}
@Override
protected List<InetSocketAddress> getRemoteSocketAddresses(
TransportProperties p) {
protected List<InetSocketAddress> getRemoteSocketAddresses(ContactId c) {
TransportProperties p = callback.getRemoteProperties().get(c);
if (p == null) return Collections.emptyList();
InetSocketAddress parsed = parseSocketAddress(p.get(PROP_IP_PORT));
if (parsed == null) return Collections.emptyList();
return Collections.singletonList(parsed);

View File

@@ -160,52 +160,35 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Override
public Map<ContactId, TransportProperties> getRemoteProperties(
TransportId t) throws DbException {
Map<ContactId, TransportProperties> remote =
new HashMap<ContactId, TransportProperties>();
Transaction txn = db.startTransaction(true);
try {
for (Contact c : db.getContacts(txn))
remote.put(c.getId(), getRemoteProperties(txn, c, t));
db.commitTransaction(txn);
} finally {
db.endTransaction(txn);
}
return remote;
}
private TransportProperties getRemoteProperties(Transaction txn, Contact c,
TransportId t) throws DbException {
// Don't return properties for inactive contacts
if (!c.isActive()) return new TransportProperties();
Group g = getContactGroup(c);
try {
// Find the latest remote update
LatestUpdate latest = findLatest(txn, g.getId(), t, false);
if (latest == null) return new TransportProperties();
// Retrieve and parse the latest remote properties
BdfList message =
clientHelper.getMessageAsList(txn, latest.messageId);
if (message == null) throw new DbException();
return parseProperties(message);
Map<ContactId, TransportProperties> remote =
new HashMap<ContactId, TransportProperties>();
Transaction txn = db.startTransaction(true);
try {
for (Contact c : db.getContacts(txn)) {
// Don't return properties for inactive contacts
if (!c.isActive()) continue;
Group g = getContactGroup(c);
// Find the latest remote update
LatestUpdate latest = findLatest(txn, g.getId(), t, false);
if (latest != null) {
// Retrieve and parse the latest remote properties
BdfList message = clientHelper.getMessageAsList(txn,
latest.messageId);
if (message == null) throw new DbException();
remote.put(c.getId(), parseProperties(message));
}
}
db.commitTransaction(txn);
} finally {
db.endTransaction(txn);
}
return remote;
} catch (FormatException e) {
throw new DbException(e);
}
}
@Override
public TransportProperties getRemoteProperties(ContactId c, TransportId t)
throws DbException {
TransportProperties p;
Transaction txn = db.startTransaction(true);
try {
p = getRemoteProperties(txn, db.getContact(txn, c), t);
db.commitTransaction(txn);
} finally {
db.endTransaction(txn);
}
return p;
}
@Override
public void mergeLocalProperties(TransportId t, TransportProperties p)
throws DbException {

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.reporting;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.util.IoUtils;
import java.io.Closeable;
import java.io.File;
@@ -131,7 +130,7 @@ public class DevReportServer {
OutputStream out = null;
try {
socket.setSoTimeout(SOCKET_TIMEOUT_MS);
in = IoUtils.getInputStream(socket);
in = socket.getInputStream();
reportDir.mkdirs();
reportFile = File.createTempFile(FILE_PREFIX, FILE_SUFFIX,
reportDir);

View File

@@ -93,7 +93,7 @@ class DevReporterImpl implements DevReporter {
InputStream in = null;
try {
Socket s = connectToDevelopers();
out = IoUtils.getOutputStream(s);
out = s.getOutputStream();
in = new FileInputStream(f);
IoUtils.copyAndClose(in, out);
f.delete();

View File

@@ -57,8 +57,8 @@ class SocksSocket extends Socket {
// Connect to the proxy
super.connect(proxy, connectToProxyTimeout);
OutputStream out = IoUtils.getOutputStream(this);
InputStream in = IoUtils.getInputStream(this);
OutputStream out = getOutputStream();
InputStream in = getInputStream();
// Request SOCKS 5 with no authentication
sendMethodRequest(out);

View File

@@ -29,10 +29,10 @@ class StreamReaderFactoryImpl implements StreamReaderFactory {
}
@Override
public InputStream createContactExchangeStreamReader(InputStream in,
public InputStream createInvitationStreamReader(InputStream in,
SecretKey headerKey) {
return new StreamReaderImpl(
streamDecrypterFactory.createContactExchangeStreamDecrypter(in,
streamDecrypterFactory.createInvitationStreamDecrypter(in,
headerKey));
}
}

View File

@@ -30,10 +30,10 @@ class StreamWriterFactoryImpl implements StreamWriterFactory {
}
@Override
public OutputStream createContactExchangeStreamWriter(OutputStream out,
public OutputStream createInvitationStreamWriter(OutputStream out,
SecretKey headerKey) {
return new StreamWriterImpl(
streamEncrypterFactory.createContactExchangeStreamDecrypter(out,
streamEncrypterFactory.createInvitationStreamEncrypter(out,
headerKey));
}
}

View File

@@ -29,7 +29,6 @@ import javax.annotation.concurrent.ThreadSafe;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
@@ -127,8 +126,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
for (long streamNumber : inKeys.getWindow().getUnseen()) {
TagContext tagCtx = new TagContext(c, inKeys, streamNumber);
byte[] tag = new byte[TAG_LENGTH];
crypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION,
streamNumber);
crypto.encodeTag(tag, inKeys.getTagKey(), streamNumber);
inContexts.put(new Bytes(tag), tagCtx);
}
}
@@ -244,8 +242,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
// Add tags for any stream numbers added to the window
for (long streamNumber : change.getAdded()) {
byte[] addTag = new byte[TAG_LENGTH];
crypto.encodeTag(addTag, inKeys.getTagKey(), PROTOCOL_VERSION,
streamNumber);
crypto.encodeTag(addTag, inKeys.getTagKey(), streamNumber);
inContexts.put(new Bytes(addTag), new TagContext(
tagCtx.contactId, inKeys, streamNumber));
}
@@ -253,8 +250,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
for (long streamNumber : change.getRemoved()) {
if (streamNumber == tagCtx.streamNumber) continue;
byte[] removeTag = new byte[TAG_LENGTH];
crypto.encodeTag(removeTag, inKeys.getTagKey(),
PROTOCOL_VERSION, streamNumber);
crypto.encodeTag(removeTag, inKeys.getTagKey(), streamNumber);
inContexts.remove(new Bytes(removeTag));
}
// Write the window back to the DB

View File

@@ -20,7 +20,7 @@ import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageFactory;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.util.StringUtils;
import org.briarproject.bramble.test.TestUtils;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
@@ -65,7 +65,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
new Message(messageId, groupId, timestamp, rawMessage);
private final Metadata metadata = new Metadata();
private final BdfList list = BdfList.of("Sign this!", getRandomBytes(42));
private final String label = StringUtils.getRandomString(5);
private final String label = TestUtils.getRandomString(5);
public ClientHelperImplTest() {
clientHelper =

View File

@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.StringUtils;
import org.junit.Test;
import java.util.Arrays;
@@ -16,7 +15,7 @@ public class HashTest extends BrambleTestCase {
private final CryptoComponent crypto;
private final String label = StringUtils.getRandomString(42);
private final String label = TestUtils.getRandomString(42);
private final byte[] inputBytes = TestUtils.getRandomBytes(123);
private final byte[] inputBytes1 = TestUtils.getRandomBytes(234);
private final byte[] inputBytes2 = new byte[0];
@@ -41,7 +40,7 @@ public class HashTest extends BrambleTestCase {
@Test
public void testDifferentLabelsProduceDifferentHashes() {
String label2 = StringUtils.getRandomString(42);
String label2 = TestUtils.getRandomString(42);
byte[] hash1 = crypto.hash(label, inputBytes, inputBytes1, inputBytes2);
byte[] hash2 =
crypto.hash(label2, inputBytes, inputBytes1, inputBytes2);

View File

@@ -1,5 +1,7 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.SecureRandomSpi;
@@ -17,7 +19,7 @@ class PseudoSecureRandom extends SecureRandom {
private final PseudoRandom pseudoRandom;
private PseudoSecureRandomSpi(byte[] seed) {
pseudoRandom = new PseudoRandom(seed);
pseudoRandom = new PseudoRandomImpl(seed);
}
@Override

View File

@@ -5,7 +5,6 @@ import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.StringUtils;
import org.junit.Test;
import java.util.Arrays;
@@ -19,7 +18,7 @@ public class SignatureTest extends BrambleTestCase {
private final CryptoComponent crypto;
private final byte[] publicKey, privateKey;
private final String label = StringUtils.getRandomString(42);
private final String label = TestUtils.getRandomString(42);
private final byte[] inputBytes = TestUtils.getRandomBytes(123);
public SignatureTest() {
@@ -65,7 +64,7 @@ public class SignatureTest extends BrambleTestCase {
public void testDifferentLabelsProduceDifferentSignatures()
throws Exception {
// Generate a second label
String label2 = StringUtils.getRandomString(42);
String label2 = TestUtils.getRandomString(42);
// Calculate the signature with different inputs
// the results should be different
byte[] sig1 = crypto.sign(label, inputBytes, privateKey);
@@ -101,7 +100,7 @@ public class SignatureTest extends BrambleTestCase {
@Test
public void testDifferentLabelFailsVerification() throws Exception {
// Generate a second label
String label2 = StringUtils.getRandomString(42);
String label2 = TestUtils.getRandomString(42);
// calculate the signature with different label, should fail to verify
byte[] sig = crypto.sign(label, inputBytes, privateKey);
assertFalse(crypto.verify(label2, inputBytes, publicKey, sig));

View File

@@ -14,8 +14,7 @@ import static junit.framework.Assert.assertEquals;
import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
import static org.junit.Assert.assertArrayEquals;
@@ -23,8 +22,7 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
private final AuthenticatedCipher cipher;
private final SecretKey streamHeaderKey, frameKey;
private final byte[] streamHeaderNonce, protocolVersionBytes;
private final byte[] streamNumberBytes, payload;
private final byte[] streamHeaderIv, payload;
private final int payloadLength = 123, paddingLength = 234;
private final long streamNumber = 1234;
@@ -32,12 +30,7 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
cipher = new TestAuthenticatedCipher(); // Null cipher
streamHeaderKey = TestUtils.getSecretKey();
frameKey = TestUtils.getSecretKey();
streamHeaderNonce =
TestUtils.getRandomBytes(STREAM_HEADER_NONCE_LENGTH);
protocolVersionBytes = new byte[2];
ByteUtils.writeUint16(PROTOCOL_VERSION, protocolVersionBytes, 0);
streamNumberBytes = new byte[8];
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
streamHeaderIv = TestUtils.getRandomBytes(STREAM_HEADER_IV_LENGTH);
payload = TestUtils.getRandomBytes(payloadLength);
}
@@ -54,9 +47,7 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
byte[] payload1 = TestUtils.getRandomBytes(payloadLength1);
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(streamHeaderIv);
out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader);
@@ -85,85 +76,6 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
assertEquals(-1, s.readFrame(buffer));
}
@Test(expected = IOException.class)
public void testWrongProtocolVersionThrowsException() throws Exception {
byte[] wrongProtocolVersionBytes = new byte[2];
ByteUtils.writeUint16(PROTOCOL_VERSION + 1, wrongProtocolVersionBytes,
0);
byte[] frameHeader = new byte[FRAME_HEADER_LENGTH];
FrameEncoder.encodeHeader(frameHeader, false, payloadLength,
paddingLength);
byte[] frameHeader1 = new byte[FRAME_HEADER_LENGTH];
int payloadLength1 = 345, paddingLength1 = 456;
FrameEncoder.encodeHeader(frameHeader1, true, payloadLength1,
paddingLength1);
byte[] payload1 = TestUtils.getRandomBytes(payloadLength1);
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderNonce);
out.write(wrongProtocolVersionBytes);
out.write(streamNumberBytes);
out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader);
out.write(payload);
out.write(new byte[paddingLength]);
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader1);
out.write(payload1);
out.write(new byte[paddingLength1]);
out.write(new byte[MAC_LENGTH]);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
StreamDecrypterImpl s = new StreamDecrypterImpl(in, cipher,
streamNumber, streamHeaderKey);
// Try to read the first frame
byte[] buffer = new byte[MAX_PAYLOAD_LENGTH];
s.readFrame(buffer);
}
@Test(expected = IOException.class)
public void testWrongStreamNumberThrowsException() throws Exception {
byte[] wrongStreamNumberBytes = new byte[8];
ByteUtils.writeUint64(streamNumber + 1, wrongStreamNumberBytes, 0);
byte[] frameHeader = new byte[FRAME_HEADER_LENGTH];
FrameEncoder.encodeHeader(frameHeader, false, payloadLength,
paddingLength);
byte[] frameHeader1 = new byte[FRAME_HEADER_LENGTH];
int payloadLength1 = 345, paddingLength1 = 456;
FrameEncoder.encodeHeader(frameHeader1, true, payloadLength1,
paddingLength1);
byte[] payload1 = TestUtils.getRandomBytes(payloadLength1);
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(wrongStreamNumberBytes);
out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader);
out.write(payload);
out.write(new byte[paddingLength]);
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader1);
out.write(payload1);
out.write(new byte[paddingLength1]);
out.write(new byte[MAC_LENGTH]);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
StreamDecrypterImpl s = new StreamDecrypterImpl(in, cipher,
streamNumber, streamHeaderKey);
// Try to read the first frame
byte[] buffer = new byte[MAX_PAYLOAD_LENGTH];
s.readFrame(buffer);
}
@Test(expected = IOException.class)
public void testTruncatedFrameThrowsException() throws Exception {
byte[] frameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -171,9 +83,7 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
paddingLength);
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(streamHeaderIv);
out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader);
@@ -201,9 +111,7 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
byte[] payload = TestUtils.getRandomBytes(payloadLength);
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(streamHeaderIv);
out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader);
@@ -230,9 +138,7 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
padding[paddingLength - 1] = 1;
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(streamHeaderIv);
out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader);
@@ -256,9 +162,7 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
paddingLength);
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(streamHeaderIv);
out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader);

View File

@@ -3,7 +3,6 @@ package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.ByteUtils;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
@@ -12,9 +11,8 @@ import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_HE
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -23,8 +21,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
private final AuthenticatedCipher cipher;
private final SecretKey streamHeaderKey, frameKey;
private final byte[] tag, streamHeaderNonce, protocolVersionBytes;
private final byte[] streamNumberBytes, payload;
private final byte[] tag, streamHeaderIv, payload;
private final long streamNumber = 1234;
private final int payloadLength = 123, paddingLength = 234;
@@ -33,12 +30,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
streamHeaderKey = TestUtils.getSecretKey();
frameKey = TestUtils.getSecretKey();
tag = TestUtils.getRandomBytes(TAG_LENGTH);
streamHeaderNonce =
TestUtils.getRandomBytes(STREAM_HEADER_NONCE_LENGTH);
protocolVersionBytes = new byte[2];
ByteUtils.writeUint16(PROTOCOL_VERSION, protocolVersionBytes, 0);
streamNumberBytes = new byte[8];
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
streamHeaderIv = TestUtils.getRandomBytes(STREAM_HEADER_IV_LENGTH);
payload = TestUtils.getRandomBytes(payloadLength);
}
@@ -46,8 +38,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testRejectsNegativePayloadLength() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
s.writeFrame(payload, -1, 0, false);
}
@@ -56,8 +47,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testRejectsNegativePaddingLength() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
s.writeFrame(payload, 0, -1, false);
}
@@ -66,8 +56,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testRejectsMaxPayloadPlusPadding() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH + 1];
s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH, 1, false);
@@ -77,8 +66,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testAcceptsMaxPayloadIncludingPadding() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH];
s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH - 1, 1, false);
@@ -90,8 +78,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testAcceptsMaxPayloadWithoutPadding() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH];
s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH, 0, false);
@@ -103,17 +90,14 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWriteUnpaddedNonFinalFrameWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
s.writeFrame(payload, payloadLength, 0, false);
// Expect the tag, stream header, frame header, payload and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(streamHeaderIv);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -129,17 +113,14 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWriteUnpaddedFinalFrameWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
s.writeFrame(payload, payloadLength, 0, true);
// Expect the tag, stream header, frame header, payload and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(streamHeaderIv);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -155,16 +136,13 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWriteUnpaddedNonFinalFrameWithoutTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey);
s.writeFrame(payload, payloadLength, 0, false);
// Expect the stream header, frame header, payload and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(streamHeaderIv);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -180,16 +158,13 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWriteUnpaddedFinalFrameWithoutTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey);
s.writeFrame(payload, payloadLength, 0, true);
// Expect the stream header, frame header, payload and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(streamHeaderIv);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -205,17 +180,14 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWritePaddedNonFinalFrameWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
s.writeFrame(payload, payloadLength, paddingLength, false);
// Expect the tag, stream header, frame header, payload, padding and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(streamHeaderIv);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -233,17 +205,14 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWritePaddedFinalFrameWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
s.writeFrame(payload, payloadLength, paddingLength, true);
// Expect the tag, stream header, frame header, payload, padding and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(streamHeaderIv);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -261,16 +230,13 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWritePaddedNonFinalFrameWithoutTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey);
s.writeFrame(payload, payloadLength, paddingLength, false);
// Expect the stream header, frame header, payload, padding and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(streamHeaderIv);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -288,16 +254,13 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWritePaddedFinalFrameWithoutTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey);
s.writeFrame(payload, payloadLength, paddingLength, true);
// Expect the stream header, frame header, payload, padding and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(streamHeaderIv);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -315,8 +278,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWriteTwoFramesWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
int payloadLength1 = 345, paddingLength1 = 456;
byte[] payload1 = TestUtils.getRandomBytes(payloadLength1);
@@ -327,9 +289,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
// MAC, second frame header, payload, padding, MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(streamHeaderIv);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -355,8 +315,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
// Flush the stream once
s.flush();
@@ -364,9 +323,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
// Expect the tag and stream header
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(streamHeaderIv);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
@@ -378,8 +335,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
// Flush the stream twice
s.flush();
@@ -388,9 +344,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
// Expect the tag and stream header
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(streamHeaderIv);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
@@ -401,17 +355,14 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testFlushDoesNotWriteTagIfNull() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey);
// Flush the stream once
s.flush();
// Expect the stream header
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(streamHeaderIv);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);

View File

@@ -1,59 +0,0 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.briarproject.bramble.test.TestUtils;
import org.junit.Test;
import java.util.HashSet;
import java.util.Set;
import static junit.framework.TestCase.assertTrue;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
public class TagEncodingTest extends BrambleTestCase {
private final CryptoComponent crypto;
private final SecretKey tagKey;
private final long streamNumber = 1234567890;
public TagEncodingTest() {
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
tagKey = TestUtils.getSecretKey();
}
@Test
public void testKeyAffectsTag() throws Exception {
Set<Bytes> set = new HashSet<Bytes>();
for (int i = 0; i < 100; i++) {
byte[] tag = new byte[TAG_LENGTH];
SecretKey tagKey = TestUtils.getSecretKey();
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, streamNumber);
assertTrue(set.add(new Bytes(tag)));
}
}
@Test
public void testProtocolVersionAffectsTag() throws Exception {
Set<Bytes> set = new HashSet<Bytes>();
for (int i = 0; i < 100; i++) {
byte[] tag = new byte[TAG_LENGTH];
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION + i, streamNumber);
assertTrue(set.add(new Bytes(tag)));
}
}
@Test
public void testStreamNumberAffectsTag() throws Exception {
Set<Bytes> set = new HashSet<Bytes>();
for (int i = 0; i < 100; i++) {
byte[] tag = new byte[TAG_LENGTH];
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, streamNumber + i);
assertTrue(set.add(new Bytes(tag)));
}
}
}

View File

@@ -4,6 +4,7 @@ import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.StringUtils;
import org.junit.Test;
@@ -158,7 +159,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
@Test
public void testReadString8() throws Exception {
String longest = StringUtils.getRandomString(Byte.MAX_VALUE);
String longest = TestUtils.getRandomString(Byte.MAX_VALUE);
String longHex = StringUtils.toHexString(longest.getBytes("UTF-8"));
// "foo", the empty string, and 127 random letters
setContents("41" + "03" + "666F6F" + "41" + "00" +
@@ -180,7 +181,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
@Test
public void testSkipString8() throws Exception {
String longest = StringUtils.getRandomString(Byte.MAX_VALUE);
String longest = TestUtils.getRandomString(Byte.MAX_VALUE);
String longHex = StringUtils.toHexString(longest.getBytes("UTF-8"));
// "foo", the empty string, and 127 random letters
setContents("41" + "03" + "666F6F" + "41" + "00" +
@@ -193,9 +194,9 @@ public class BdfReaderImplTest extends BrambleTestCase {
@Test
public void testReadString16() throws Exception {
String shortest = StringUtils.getRandomString(Byte.MAX_VALUE + 1);
String shortest = TestUtils.getRandomString(Byte.MAX_VALUE + 1);
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8"));
String longest = StringUtils.getRandomString(Short.MAX_VALUE);
String longest = TestUtils.getRandomString(Short.MAX_VALUE);
String longHex = StringUtils.toHexString(longest.getBytes("UTF-8"));
// 128 random letters and 2^15 -1 random letters
setContents("42" + "0080" + shortHex + "42" + "7FFF" + longHex);
@@ -206,7 +207,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
@Test(expected = FormatException.class)
public void testReadString16ChecksMaxLength() throws Exception {
String shortest = StringUtils.getRandomString(Byte.MAX_VALUE + 1);
String shortest = TestUtils.getRandomString(Byte.MAX_VALUE + 1);
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8"));
// 128 random letters, twice
setContents("42" + "0080" + shortHex + "42" + "0080" + shortHex);
@@ -217,9 +218,9 @@ public class BdfReaderImplTest extends BrambleTestCase {
@Test
public void testSkipString16() throws Exception {
String shortest = StringUtils.getRandomString(Byte.MAX_VALUE + 1);
String shortest = TestUtils.getRandomString(Byte.MAX_VALUE + 1);
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8"));
String longest = StringUtils.getRandomString(Short.MAX_VALUE);
String longest = TestUtils.getRandomString(Short.MAX_VALUE);
String longHex = StringUtils.toHexString(longest.getBytes("UTF-8"));
// 128 random letters and 2^15 - 1 random letters
setContents("42" + "0080" + shortHex + "42" + "7FFF" + longHex);
@@ -230,7 +231,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
@Test
public void testReadString32() throws Exception {
String shortest = StringUtils.getRandomString(Short.MAX_VALUE + 1);
String shortest = TestUtils.getRandomString(Short.MAX_VALUE + 1);
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8"));
// 2^15 random letters
setContents("44" + "00008000" + shortHex);
@@ -240,7 +241,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
@Test(expected = FormatException.class)
public void testReadString32ChecksMaxLength() throws Exception {
String shortest = StringUtils.getRandomString(Short.MAX_VALUE + 1);
String shortest = TestUtils.getRandomString(Short.MAX_VALUE + 1);
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8"));
// 2^15 random letters, twice
setContents("44" + "00008000" + shortHex +
@@ -252,7 +253,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
@Test
public void testSkipString32() throws Exception {
String shortest = StringUtils.getRandomString(Short.MAX_VALUE + 1);
String shortest = TestUtils.getRandomString(Short.MAX_VALUE + 1);
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8"));
// 2^15 random letters, twice
setContents("44" + "00008000" + shortHex +

View File

@@ -81,7 +81,7 @@ public class BdfWriterImplTest extends BrambleTestCase {
@Test
public void testWriteString8() throws IOException {
String longest = StringUtils.getRandomString(Byte.MAX_VALUE);
String longest = TestUtils.getRandomString(Byte.MAX_VALUE);
String longHex = StringUtils.toHexString(longest.getBytes("UTF-8"));
w.writeString("foo bar baz bam ");
w.writeString(longest);
@@ -93,9 +93,9 @@ public class BdfWriterImplTest extends BrambleTestCase {
@Test
public void testWriteString16() throws IOException {
String shortest = StringUtils.getRandomString(Byte.MAX_VALUE + 1);
String shortest = TestUtils.getRandomString(Byte.MAX_VALUE + 1);
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8"));
String longest = StringUtils.getRandomString(Short.MAX_VALUE);
String longest = TestUtils.getRandomString(Short.MAX_VALUE);
String longHex = StringUtils.toHexString(longest.getBytes("UTF-8"));
w.writeString(shortest);
w.writeString(longest);
@@ -106,7 +106,7 @@ public class BdfWriterImplTest extends BrambleTestCase {
@Test
public void testWriteString32() throws IOException {
String shortest = StringUtils.getRandomString(Short.MAX_VALUE + 1);
String shortest = TestUtils.getRandomString(Short.MAX_VALUE + 1);
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8"));
w.writeString(shortest);
// STRING_32 tag, length 2^15, UTF-8 bytes

View File

@@ -2,7 +2,6 @@ package org.briarproject.bramble.db;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.StringUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -49,8 +48,8 @@ public class BasicH2Test extends BrambleTestCase {
createTable(connection);
// Generate an ID and two names
byte[] id = TestUtils.getRandomId();
String oldName = StringUtils.getRandomString(50);
String newName = StringUtils.getRandomString(50);
String oldName = TestUtils.getRandomString(50);
String newName = TestUtils.getRandomString(50);
// Insert the ID and old name into the table
insertRow(id, oldName);
// Check that the old name can be retrieved using the ID
@@ -79,8 +78,8 @@ public class BasicH2Test extends BrambleTestCase {
String[] newNames = new String[BATCH_SIZE];
for (int i = 0; i < BATCH_SIZE; i++) {
ids[i] = TestUtils.getRandomId();
oldNames[i] = StringUtils.getRandomString(50);
newNames[i] = StringUtils.getRandomString(50);
oldNames[i] = TestUtils.getRandomString(50);
newNames[i] = TestUtils.getRandomString(50);
}
// Insert the IDs and old names into the table as a batch
insertBatch(ids, oldNames);

View File

@@ -49,7 +49,6 @@ import org.briarproject.bramble.api.transport.OutgoingKeys;
import org.briarproject.bramble.api.transport.TransportKeys;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.StringUtils;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
@@ -95,7 +94,7 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
private final Contact contact;
public DatabaseComponentImplTest() {
clientId = new ClientId(StringUtils.getRandomString(5));
clientId = new ClientId(TestUtils.getRandomString(5));
groupId = new GroupId(TestUtils.getRandomId());
byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH];
group = new Group(groupId, clientId, descriptor);

View File

@@ -24,7 +24,6 @@ import org.briarproject.bramble.system.SystemClock;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestDatabaseConfig;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.StringUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -85,7 +84,7 @@ public class H2DatabaseTest extends BrambleTestCase {
public H2DatabaseTest() throws Exception {
groupId = new GroupId(TestUtils.getRandomId());
clientId = new ClientId(StringUtils.getRandomString(5));
clientId = new ClientId(TestUtils.getRandomString(5));
byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH];
group = new Group(groupId, clientId, descriptor);
AuthorId authorId = new AuthorId(TestUtils.getRandomId());

View File

@@ -11,7 +11,6 @@ import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.StringUtils;
import org.jmock.Expectations;
import org.junit.Test;
@@ -32,9 +31,8 @@ public class IdentityManagerImplTest extends BrambleMockTestCase {
private final Transaction txn = new Transaction(null, false);
private final LocalAuthor localAuthor =
new LocalAuthor(new AuthorId(TestUtils.getRandomId()),
StringUtils.getRandomString(8),
TestUtils.getRandomBytes(42), TestUtils.getRandomBytes(42),
0);
TestUtils.getRandomString(8), TestUtils.getRandomBytes(42),
TestUtils.getRandomBytes(42), 0);
private final Collection<LocalAuthor> localAuthors =
Collections.singletonList(localAuthor);
@@ -95,7 +93,7 @@ public class IdentityManagerImplTest extends BrambleMockTestCase {
assertEquals(UNKNOWN, identityManager.getAuthorStatus(authorId));
// add one unverified contact
Author author = new Author(authorId, StringUtils.getRandomString(8),
Author author = new Author(authorId, TestUtils.getRandomString(8),
TestUtils.getRandomBytes(42));
Contact contact =
new Contact(new ContactId(1), author, localAuthor.getId(),

View File

@@ -311,11 +311,6 @@ public class LanTcpPluginTest extends BrambleTestCase {
return remote;
}
@Override
public TransportProperties getRemoteProperties(ContactId c) {
return remote.get(c);
}
@Override
public void mergeSettings(Settings s) {
}

View File

@@ -14,7 +14,6 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.StringUtils;
import org.jmock.Mockery;
import org.junit.Test;
@@ -37,7 +36,7 @@ public class TransportPropertyValidatorTest extends BrambleTestCase {
bdfDictionary = new BdfDictionary();
GroupId groupId = new GroupId(TestUtils.getRandomId());
ClientId clientId = new ClientId(StringUtils.getRandomString(5));
ClientId clientId = new ClientId(TestUtils.getRandomString(5));
byte[] descriptor = TestUtils.getRandomBytes(12);
group = new Group(groupId, clientId, descriptor);
@@ -86,7 +85,7 @@ public class TransportPropertyValidatorTest extends BrambleTestCase {
public void testValidateLongTransportId() throws IOException {
String wrongTransportIdString =
StringUtils.getRandomString(MAX_TRANSPORT_ID_LENGTH + 1);
TestUtils.getRandomString(MAX_TRANSPORT_ID_LENGTH + 1);
BdfList body = BdfList.of(wrongTransportIdString, 4, bdfDictionary);
tpv.validateMessage(message, group, body);
}

View File

@@ -22,7 +22,6 @@ import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.StringUtils;
import org.junit.Test;
import java.io.ByteArrayInputStream;
@@ -35,7 +34,6 @@ import java.util.Collection;
import javax.inject.Inject;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -79,7 +77,7 @@ public class SyncIntegrationTest extends BrambleTestCase {
headerKey = TestUtils.getSecretKey();
streamNumber = 123;
// Create a group
ClientId clientId = new ClientId(StringUtils.getRandomString(5));
ClientId clientId = new ClientId(TestUtils.getRandomString(5));
byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH];
Group group = groupFactory.createGroup(clientId, descriptor);
// Add two messages to the group
@@ -117,7 +115,7 @@ public class SyncIntegrationTest extends BrambleTestCase {
private void read(byte[] connectionData) throws Exception {
// Calculate the expected tag
byte[] expectedTag = new byte[TAG_LENGTH];
crypto.encodeTag(expectedTag, tagKey, PROTOCOL_VERSION, streamNumber);
crypto.encodeTag(expectedTag, tagKey, streamNumber);
// Read the tag
InputStream in = new ByteArrayInputStream(connectionData);

View File

@@ -23,7 +23,6 @@ import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.ImmediateExecutor;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.ByteUtils;
import org.briarproject.bramble.util.StringUtils;
import org.jmock.Expectations;
import org.junit.Before;
import org.junit.Test;
@@ -52,7 +51,7 @@ public class ValidationManagerImplTest extends BrambleMockTestCase {
private final Executor dbExecutor = new ImmediateExecutor();
private final Executor validationExecutor = new ImmediateExecutor();
private final ClientId clientId =
new ClientId(StringUtils.getRandomString(5));
new ClientId(TestUtils.getRandomString(5));
private final MessageId messageId = new MessageId(TestUtils.getRandomId());
private final MessageId messageId1 = new MessageId(TestUtils.getRandomId());
private final MessageId messageId2 = new MessageId(TestUtils.getRandomId());

View File

@@ -9,7 +9,6 @@ import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.util.StringUtils;
public abstract class ValidatorTestCase extends BrambleMockTestCase {
@@ -29,7 +28,7 @@ public abstract class ValidatorTestCase extends BrambleMockTestCase {
protected final Message message =
new Message(messageId, groupId, timestamp, raw);
protected final ClientId clientId =
new ClientId(StringUtils.getRandomString(123));
new ClientId(TestUtils.getRandomString(123));
protected final byte[] descriptor = TestUtils.getRandomBytes(123);
protected final Group group = new Group(groupId, clientId, descriptor);

View File

@@ -33,7 +33,6 @@ import java.util.concurrent.ScheduledExecutorService;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
@@ -87,7 +86,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets per contact)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(6).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(i));
with(tagKey), with(i));
will(new EncodeTagAction());
}
// Save the keys that were rotated
@@ -134,7 +133,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(i));
with(tagKey), with(i));
will(new EncodeTagAction());
}
// Save the keys
@@ -200,7 +199,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(i));
with(tagKey), with(i));
will(new EncodeTagAction());
}
// Rotate the transport keys (the keys are unaffected)
@@ -248,7 +247,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(i));
with(tagKey), with(i));
will(new EncodeTagAction());
}
// Rotate the transport keys (the keys are unaffected)
@@ -307,7 +306,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(i));
with(tagKey), with(i));
will(new EncodeTagAction());
}
// Rotate the transport keys (the keys are unaffected)
@@ -356,7 +355,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(i));
with(tagKey), with(i));
will(new EncodeTagAction(tags));
}
// Rotate the transport keys (the keys are unaffected)
@@ -366,8 +365,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
// Encode a new tag after sliding the window
oneOf(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION),
with((long) REORDERING_WINDOW_SIZE));
with(tagKey), with((long) REORDERING_WINDOW_SIZE));
will(new EncodeTagAction(tags));
// Save the reordering window (previous rotation period, base 1)
oneOf(db).setReorderingWindow(txn, contactId, transportId, 999,
@@ -430,7 +428,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(i));
with(tagKey), with(i));
will(new EncodeTagAction());
}
// Schedule key rotation at the start of the next rotation period
@@ -452,7 +450,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(i));
with(tagKey), with(i));
will(new EncodeTagAction());
}
// Save the keys that were rotated

View File

@@ -7,19 +7,10 @@ apply plugin: 'witness'
dependencies {
compile project(':bramble-core')
compile fileTree(dir: 'libs', include: '*.jar')
compile 'net.java.dev.jna:jna:4.4.0'
compile 'net.java.dev.jna:jna-platform:4.4.0'
testCompile project(path: ':bramble-core', configuration: 'testOutput')
}
dependencyVerification {
verify = [
'net.java.dev.jna:jna:c4dadeeecaa90c8847902082aee5eb107fcf59c5d0e63a17fcaf273c0e2d2bd1',
'net.java.dev.jna:jna-platform:e9dda9e884fc107eb6367710540789a12dfa8ad28be9326b22ca6e352e325499',
]
}
tasks.withType(Test) {
systemProperty 'java.library.path', 'libs'
}

Binary file not shown.

Binary file not shown.

View File

@@ -2,6 +2,7 @@ package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
@@ -18,23 +19,33 @@ import org.briarproject.bramble.util.OsUtils;
import org.briarproject.bramble.util.StringUtils;
import java.io.IOException;
import java.io.InputStream;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.LocalDevice;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import javax.microedition.io.StreamConnectionNotifier;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static javax.bluetooth.DiscoveryAgent.GIAC;
@@ -56,6 +67,7 @@ class BluetoothPlugin implements DuplexPlugin {
private final Backoff backoff;
private final DuplexPluginCallback callback;
private final int maxLatency;
private final Semaphore discoverySemaphore = new Semaphore(1);
private final AtomicBoolean used = new AtomicBoolean(false);
private volatile boolean running = false;
@@ -249,7 +261,8 @@ class BluetoothPlugin implements DuplexPlugin {
@Override
public DuplexTransportConnection createConnection(ContactId c) {
if (!running) return null;
TransportProperties p = callback.getRemoteProperties(c);
TransportProperties p = callback.getRemoteProperties().get(c);
if (p == null) return null;
String address = p.get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) return null;
String uuid = p.get(PROP_UUID);
@@ -260,6 +273,95 @@ class BluetoothPlugin implements DuplexPlugin {
return new BluetoothTransportConnection(this, s);
}
@Override
public boolean supportsInvitations() {
return true;
}
@Override
public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
long timeout, boolean alice) {
if (!running) return null;
// Use the invitation codes to generate the UUID
byte[] b = r.nextBytes(UUID_BYTES);
String uuid = UUID.nameUUIDFromBytes(b).toString();
String url = makeUrl("localhost", uuid);
// Make the device discoverable if possible
makeDeviceDiscoverable();
// Bind a server socket for receiving invitation connections
final StreamConnectionNotifier ss;
try {
ss = (StreamConnectionNotifier) Connector.open(url);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
}
if (!running) {
tryToClose(ss);
return null;
}
// Create the background tasks
CompletionService<StreamConnection> complete =
new ExecutorCompletionService<>(ioExecutor);
List<Future<StreamConnection>> futures = new ArrayList<>();
if (alice) {
// Return the first connected socket
futures.add(complete.submit(new ListeningTask(ss)));
futures.add(complete.submit(new DiscoveryTask(uuid)));
} else {
// Return the first socket with readable data
futures.add(complete.submit(new ReadableTask(
new ListeningTask(ss))));
futures.add(complete.submit(new ReadableTask(
new DiscoveryTask(uuid))));
}
StreamConnection chosen = null;
try {
Future<StreamConnection> f = complete.poll(timeout, MILLISECONDS);
if (f == null) return null; // No task completed within the timeout
chosen = f.get();
return new BluetoothTransportConnection(this, chosen);
} catch (InterruptedException e) {
LOG.info("Interrupted while exchanging invitations");
Thread.currentThread().interrupt();
return null;
} catch (ExecutionException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
} finally {
// Closing the socket will terminate the listener task
tryToClose(ss);
closeSockets(futures, chosen);
}
}
private void closeSockets(final List<Future<StreamConnection>> futures,
@Nullable final StreamConnection chosen) {
ioExecutor.execute(new Runnable() {
@Override
public void run() {
for (Future<StreamConnection> f : futures) {
try {
if (f.cancel(true)) {
LOG.info("Cancelled task");
} else {
StreamConnection s = f.get();
if (s != null && s != chosen) {
LOG.info("Closing unwanted socket");
s.close();
}
}
} catch (InterruptedException e) {
LOG.info("Interrupted while closing sockets");
return;
} catch (ExecutionException | IOException e) {
if (LOG.isLoggable(INFO)) LOG.info(e.toString());
}
}
}
});
}
@Override
public boolean supportsKeyAgreement() {
return true;
@@ -274,7 +376,7 @@ class BluetoothPlugin implements DuplexPlugin {
String url = makeUrl("localhost", uuid);
// Make the device discoverable if possible
makeDeviceDiscoverable();
// Bind a server socket for receiving key agreementconnections
// Bind a server socket for receiving invitation connections
final StreamConnectionNotifier ss;
try {
ss = (StreamConnectionNotifier) Connector.open(url);
@@ -329,6 +431,77 @@ class BluetoothPlugin implements DuplexPlugin {
}
}
private class DiscoveryTask implements Callable<StreamConnection> {
private final String uuid;
private DiscoveryTask(String uuid) {
this.uuid = uuid;
}
@Override
public StreamConnection call() throws Exception {
// Repeat discovery until we connect or get interrupted
DiscoveryAgent discoveryAgent = localDevice.getDiscoveryAgent();
while (true) {
if (!discoverySemaphore.tryAcquire())
throw new Exception("Discovery is already in progress");
try {
InvitationListener listener =
new InvitationListener(discoveryAgent, uuid);
discoveryAgent.startInquiry(GIAC, listener);
String url = listener.waitForUrl();
if (url != null) {
StreamConnection s = connect(url);
if (s != null) {
LOG.info("Outgoing connection");
return s;
}
}
} finally {
discoverySemaphore.release();
}
}
}
}
private static class ListeningTask implements Callable<StreamConnection> {
private final StreamConnectionNotifier serverSocket;
private ListeningTask(StreamConnectionNotifier serverSocket) {
this.serverSocket = serverSocket;
}
@Override
public StreamConnection call() throws Exception {
StreamConnection s = serverSocket.acceptAndOpen();
LOG.info("Incoming connection");
return s;
}
}
private static class ReadableTask implements Callable<StreamConnection> {
private final Callable<StreamConnection> connectionTask;
private ReadableTask(Callable<StreamConnection> connectionTask) {
this.connectionTask = connectionTask;
}
@Override
public StreamConnection call() throws Exception {
StreamConnection s = connectionTask.call();
InputStream in = s.openInputStream();
while (in.available() == 0) {
LOG.info("Waiting for data");
Thread.sleep(1000);
}
LOG.info("Data available");
return s;
}
}
private class BluetoothKeyAgreementListener extends KeyAgreementListener {
private final StreamConnectionNotifier ss;

View File

@@ -0,0 +1,109 @@
package org.briarproject.bramble.plugin.bluetooth;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DataElement;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.UUID;
import static java.util.logging.Level.WARNING;
class InvitationListener implements DiscoveryListener {
private static final Logger LOG =
Logger.getLogger(InvitationListener.class.getName());
private final AtomicInteger searches = new AtomicInteger(1);
private final CountDownLatch finished = new CountDownLatch(1);
private final DiscoveryAgent discoveryAgent;
private final String uuid;
private volatile String url = null;
InvitationListener(DiscoveryAgent discoveryAgent, String uuid) {
this.discoveryAgent = discoveryAgent;
this.uuid = uuid;
}
String waitForUrl() throws InterruptedException {
finished.await();
return url;
}
@Override
public void deviceDiscovered(RemoteDevice device, DeviceClass deviceClass) {
UUID[] uuids = new UUID[] {new UUID(uuid, false)};
// Try to discover the services associated with the UUID
try {
discoveryAgent.searchServices(null, uuids, device, this);
searches.incrementAndGet();
} catch (BluetoothStateException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
@Override
public void servicesDiscovered(int transaction, ServiceRecord[] services) {
for (ServiceRecord record : services) {
// Does this service have a URL?
String serviceUrl = record.getConnectionURL(
ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
if (serviceUrl == null) continue;
// Does this service have the UUID we're looking for?
Collection<String> uuids = new TreeSet<>();
findNestedClassIds(record.getAttributeValue(0x1), uuids);
for (String u : uuids) {
if (uuid.equalsIgnoreCase(u)) {
// The UUID matches - store the URL
url = serviceUrl;
finished.countDown();
return;
}
}
}
}
@Override
public void inquiryCompleted(int discoveryType) {
if (searches.decrementAndGet() == 0) finished.countDown();
}
@Override
public void serviceSearchCompleted(int transaction, int response) {
if (searches.decrementAndGet() == 0) finished.countDown();
}
// UUIDs are sometimes buried in nested data elements
private void findNestedClassIds(Object o, Collection<String> ids) {
o = getDataElementValue(o);
if (o instanceof Enumeration<?>) {
for (Object o1 : Collections.list((Enumeration<?>) o))
findNestedClassIds(o1, ids);
} else if (o instanceof UUID) {
ids.add(o.toString());
}
}
private Object getDataElementValue(Object o) {
if (o instanceof DataElement) {
// Bluecove throws an exception if the type is unknown
try {
return ((DataElement) o).getValue();
} catch (ClassCastException e) {
return null;
}
}
return null;
}
}

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.plugin.modem;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
@@ -145,7 +146,8 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
String fromIso = callback.getLocalProperties().get("iso3166");
if (StringUtils.isNullOrEmpty(fromIso)) return null;
// Get the ISO 3166 code for the callee's country
TransportProperties properties = callback.getRemoteProperties(c);
TransportProperties properties = callback.getRemoteProperties().get(c);
if (properties == null) return null;
String toIso = properties.get("iso3166");
if (StringUtils.isNullOrEmpty(toIso)) return null;
// Get the callee's phone number
@@ -165,6 +167,17 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
return new ModemTransportConnection();
}
@Override
public boolean supportsInvitations() {
return false;
}
@Override
public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
long timeout, boolean alice) {
throw new UnsupportedOperationException();
}
@Override
public boolean supportsKeyAgreement() {
return false;

View File

@@ -9,6 +9,8 @@ import org.jmock.Mockery;
import org.junit.Test;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -63,10 +65,12 @@ public class ModemPluginTest extends BrambleTestCase {
final Modem modem = context.mock(Modem.class);
final TransportProperties local = new TransportProperties();
local.put("iso3166", ISO_1336);
final TransportProperties remote = new TransportProperties();
remote.put("iso3166", ISO_1336);
remote.put("number", NUMBER);
final ContactId contactId = new ContactId(234);
TransportProperties p = new TransportProperties();
p.put("iso3166", ISO_1336);
p.put("number", NUMBER);
ContactId contactId = new ContactId(234);
final Map<ContactId, TransportProperties> remote =
Collections.singletonMap(contactId, p);
context.checking(new Expectations() {{
// start()
oneOf(serialPortList).getPortNames();
@@ -78,7 +82,7 @@ public class ModemPluginTest extends BrambleTestCase {
// createConnection()
oneOf(callback).getLocalProperties();
will(returnValue(local));
oneOf(callback).getRemoteProperties(contactId);
oneOf(callback).getRemoteProperties();
will(returnValue(remote));
oneOf(modem).dial(NUMBER);
will(returnValue(true));
@@ -102,10 +106,12 @@ public class ModemPluginTest extends BrambleTestCase {
final Modem modem = context.mock(Modem.class);
final TransportProperties local = new TransportProperties();
local.put("iso3166", ISO_1336);
final TransportProperties remote = new TransportProperties();
remote.put("iso3166", ISO_1336);
remote.put("number", NUMBER);
final ContactId contactId = new ContactId(234);
TransportProperties p = new TransportProperties();
p.put("iso3166", ISO_1336);
p.put("number", NUMBER);
ContactId contactId = new ContactId(234);
final Map<ContactId, TransportProperties> remote =
Collections.singletonMap(contactId, p);
context.checking(new Expectations() {{
// start()
oneOf(serialPortList).getPortNames();
@@ -117,7 +123,7 @@ public class ModemPluginTest extends BrambleTestCase {
// createConnection()
oneOf(callback).getLocalProperties();
will(returnValue(local));
oneOf(callback).getRemoteProperties(contactId);
oneOf(callback).getRemoteProperties();
will(returnValue(remote));
oneOf(modem).dial(NUMBER);
will(returnValue(false));
@@ -141,10 +147,12 @@ public class ModemPluginTest extends BrambleTestCase {
final Modem modem = context.mock(Modem.class);
final TransportProperties local = new TransportProperties();
local.put("iso3166", ISO_1336);
final TransportProperties remote = new TransportProperties();
remote.put("iso3166", ISO_1336);
remote.put("number", NUMBER);
final ContactId contactId = new ContactId(234);
TransportProperties p = new TransportProperties();
p.put("iso3166", ISO_1336);
p.put("number", NUMBER);
ContactId contactId = new ContactId(234);
final Map<ContactId, TransportProperties> remote =
Collections.singletonMap(contactId, p);
context.checking(new Expectations() {{
// start()
oneOf(serialPortList).getPortNames();
@@ -156,7 +164,7 @@ public class ModemPluginTest extends BrambleTestCase {
// createConnection()
oneOf(callback).getLocalProperties();
will(returnValue(local));
oneOf(callback).getRemoteProperties(contactId);
oneOf(callback).getRemoteProperties();
will(returnValue(remote));
oneOf(modem).dial(NUMBER);
will(throwException(new IOException()));

View File

@@ -1,6 +1,6 @@
[main]
host = https://www.transifex.com
lang_map = pt_BR: pt-rBR, fr_FR: fr, nb_NO: nb, zh-Hans: zh-rCN
lang_map = pt_BR: pt-rBR, fr_FR: fr
[briar.stringsxml-5]
file_filter = src/main/res/values-<lang>/strings.xml

View File

@@ -5,10 +5,13 @@ dependencies {
def supportVersion = '23.2.1'
compile project(':briar-core')
compile project(':bramble-android')
compile fileTree(dir: 'libs', include: '*.jar')
compile "com.android.support:support-v4:$supportVersion"
compile("com.android.support:appcompat-v7:$supportVersion") {
exclude module: 'support-v4'
}
compile("com.android.support:preference-v14:$supportVersion") {
exclude module: 'support-v4'
}
@@ -17,7 +20,7 @@ dependencies {
exclude module: 'recyclerview-v7'
}
compile "com.android.support:cardview-v7:$supportVersion"
compile "com.android.support:support-annotations:$supportVersion"
compile 'com.android.support:support-annotations:23.4.0'
compile('ch.acra:acra:4.8.5') {
exclude module: 'support-v4'
exclude module: 'support-annotations'
@@ -25,16 +28,15 @@ dependencies {
compile 'info.guardianproject.panic:panic:0.5'
compile 'info.guardianproject.trustedintents:trustedintents:0.2'
compile 'de.hdodenhof:circleimageview:2.1.0'
compile 'com.google.zxing:core:3.3.0'
compile 'com.jpardogo.materialtabstrip:library:1.1.0'
compile 'com.github.bumptech.glide:glide:3.8.0'
compile 'uk.co.samuelwall:material-tap-target-prompt:1.9.2'
compile 'com.google.zxing:core:3.2.1'
provided 'javax.annotation:jsr250-api:1.0'
compile 'com.jpardogo.materialtabstrip:library:1.1.0'
compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'uk.co.samuelwall:material-tap-target-prompt:1.3.0'
testCompile project(path: ':bramble-core', configuration: 'testOutput')
testCompile 'org.robolectric:robolectric:3.0'
testCompile 'org.mockito:mockito-core:2.8.9'
testCompile 'org.mockito:mockito-core:1.10.19'
}
dependencyVerification {
@@ -43,18 +45,20 @@ dependencyVerification {
'info.guardianproject.panic:panic:a7ed9439826db2e9901649892cf9afbe76f00991b768d8f4c26332d7c9406cb2',
'info.guardianproject.trustedintents:trustedintents:6221456d8821a8d974c2acf86306900237cf6afaaa94a4c9c44e161350f80f3e',
'de.hdodenhof:circleimageview:bcbc588e19e6dcf8c120b1957776bfe229efba5d2fbe5da7156372eeacf65503',
'com.google.zxing:core:bba7724e02a997cec38213af77133ee8e24b0d5cf5fa7ecbc16a4fa93f11ee0d',
'com.jpardogo.materialtabstrip:library:24d19232b319f8c73e25793432357919a7ed972186f57a3b2c9093ea74ad8311',
'com.github.bumptech.glide:glide:750d9e7b940dc0ee48f8680623b55d46e14e8727acc922d7b156e57e7c549655',
'uk.co.samuelwall:material-tap-target-prompt:5d4951124366bc5c52e57beaa294db7611f0aa2a8d80e0163e1383e1966ba5b2',
'com.google.zxing:core:b4d82452e7a6bf6ec2698904b332431717ed8f9a850224f295aec89de80f2259',
'com.android.support:support-v4:81ce890f26d35c75ad17d0f998a7e3230330c3b41e0b629566bc744bee89e448',
'com.android.support:appcompat-v7:00f9d93acacd6731f309724054bf51492814b4b2869f16d7d5c0038dcb8c9a0d',
'com.android.support:preference-v14:44881bb46094e86d0bc2426f205419674a5b4eb514b44b5a4659b5de29f71eb7',
'com.android.support:design:003e0c0bea0a6891f8b2bc43f20ae7af2a49a17363e5bb10df5ee0bae12fa686',
'com.android.support:cardview-v7:4595f1c4a28cfa083b6c0920ad4d49e1c2ca4b8302a955e548f68eb63b74931b',
'com.android.support:support-annotations:e91a88dd0c5e99069b7f09d4a46b5e06f1e9c4c72fc0a8e987e25d86af480f01',
'com.android.support:animated-vector-drawable:06d1963b85aa917099d7757e6a7b3e4dc06889413dc747f625ae8683606db3a1',
'com.android.support:support-vector-drawable:799bafe4c3de812386f0b291f744d5d6876452722dd40189b9ab87dbbf594ea1',
'com.android.support:recyclerview-v7:44040a888e23e0c93162a3377cfe06751080e3c22d369ab0d4301ef60d63b0fe',
'com.android.support:preference-v7:775101bd07bd052e455761c5c5d9523d7ad59f2f320e3e8cbde241fd6b1d6025',
'com.android.support:cardview-v7:4595f1c4a28cfa083b6c0920ad4d49e1c2ca4b8302a955e548f68eb63b74931b',
'com.jpardogo.materialtabstrip:library:24d19232b319f8c73e25793432357919a7ed972186f57a3b2c9093ea74ad8311',
'com.github.bumptech.glide:glide:76ef123957b5fbaebb05fcbe6606dd58c3bc3fcdadb257f99811d0ac9ea9b88b',
'uk.co.samuelwall:material-tap-target-prompt:f67e1caead12a914525b32cbf6da52a96b93ff89573f93cb41102ef3130fb64a',
]
}
@@ -78,19 +82,14 @@ android {
defaultConfig {
minSdkVersion 14
targetSdkVersion 22
versionCode 1611
versionName "0.16.11"
applicationId "org.briarproject.briar.beta"
resValue "string", "app_package", "org.briarproject.briar.beta"
resValue "string", "app_name", "Briar Beta"
versionCode 14
versionName "0.14"
resValue "string", "app_package", "org.briarproject.briar"
buildConfigField "String", "GitHash", "\"${getGitHash()}\""
}
buildTypes {
debug {
applicationIdSuffix ".debug"
resValue "string", "app_package", "org.briarproject.briar.beta.debug"
resValue "string", "app_name", "Briar Debug"
shrinkResources false
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
@@ -107,13 +106,8 @@ android {
targetCompatibility JavaVersion.VERSION_1_7
}
aaptOptions {
cruncherEnabled = false
}
lintOptions {
warning 'MissingTranslation'
warning 'ImpliedQuantity'
warning 'ExtraTranslation'
}
}

View File

@@ -15,9 +15,11 @@
<uses-permission android:name="android.permission.READ_LOGS"/>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- Since API 23, this is needed to add contacts via Bluetooth -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:name="org.briarproject.briar.android.BriarApplicationImpl"
android:name=".android.BriarApplicationImpl"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher_round"
android:label="@string/app_name"
@@ -25,20 +27,15 @@
android:theme="@style/BriarTheme">
<service
android:name="org.briarproject.briar.android.BriarService"
android:name=".android.BriarService"
android:exported="false">
<intent-filter>
<action android:name="org.briarproject.briar.android.BriarService"/>
</intent-filter>
</service>
<service
android:name="org.briarproject.briar.android.NotificationCleanupService"
android:exported="false">
</service>
<activity
android:name="org.briarproject.briar.android.reporting.DevReportActivity"
android:name=".android.reporting.DevReportActivity"
android:excludeFromRecents="true"
android:exported="false"
android:finishOnTaskLaunch="true"
@@ -50,24 +47,24 @@
</activity>
<activity
android:name="org.briarproject.briar.android.splash.ExpiredActivity"
android:name=".android.splash.ExpiredActivity"
android:label="@string/app_name">
</activity>
<activity
android:name="org.briarproject.briar.android.login.PasswordActivity"
android:name=".android.login.PasswordActivity"
android:label="@string/app_name"
android:windowSoftInputMode="stateVisible">
</activity>
<activity
android:name="org.briarproject.briar.android.login.SetupActivity"
android:name=".android.login.SetupActivity"
android:label="@string/setup_title"
android:windowSoftInputMode="adjustResize">
</activity>
<activity
android:name="org.briarproject.briar.android.splash.SplashScreenActivity"
android:name=".android.splash.SplashScreenActivity"
android:theme="@style/BriarTheme.NoActionBar"
android:label="@string/app_name">
<intent-filter>
@@ -77,258 +74,268 @@
</activity>
<activity
android:name="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:name=".android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar"
android:launchMode="singleTop">
</activity>
<activity
android:name="org.briarproject.briar.android.contact.ConversationActivity"
android:name=".android.contact.ConversationActivity"
android:label="@string/app_name"
android:theme="@style/BriarTheme.NoActionBar"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
android:windowSoftInputMode="stateHidden|adjustResize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:value=".android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.privategroup.creation.CreateGroupActivity"
android:name=".android.privategroup.creation.CreateGroupActivity"
android:label="@string/groups_create_group_title"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
android:windowSoftInputMode="adjustResize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:value=".android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
android:name=".android.privategroup.conversation.GroupActivity"
android:label="@string/app_name"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar"
android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:value=".android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.privategroup.invitation.GroupInvitationActivity"
android:name=".android.privategroup.invitation.GroupInvitationActivity"
android:label="@string/groups_invitations_title"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
android:parentActivityName=".android.navdrawer.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/>
android:value=".android.navdrawer.NavDrawerActivity"/>
</activity>
<activity
android:name="org.briarproject.briar.android.privategroup.memberlist.GroupMemberListActivity"
android:name=".android.privategroup.memberlist.GroupMemberListActivity"
android:label="@string/groups_member_list"
android:parentActivityName="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
android:parentActivityName=".android.privategroup.conversation.GroupActivity"
android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
android:value=".android.privategroup.conversation.GroupActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity"
android:name=".android.privategroup.reveal.RevealContactsActivity"
android:label="@string/groups_reveal_contacts"
android:parentActivityName="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
android:parentActivityName=".android.privategroup.conversation.GroupActivity"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
android:value=".android.privategroup.conversation.GroupActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.privategroup.creation.GroupInviteActivity"
android:name=".android.privategroup.creation.GroupInviteActivity"
android:label="@string/groups_invite_members"
android:parentActivityName="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
android:parentActivityName=".android.privategroup.conversation.GroupActivity"
android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.privategroup.conversation.GroupActivity"/>
android:value=".android.privategroup.conversation.GroupActivity"/>
</activity>
<activity
android:name="org.briarproject.briar.android.sharing.ForumInvitationActivity"
android:name=".android.sharing.ForumInvitationActivity"
android:label="@string/forum_invitations_title"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
android:parentActivityName=".android.navdrawer.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:value=".android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.sharing.BlogInvitationActivity"
android:name=".android.sharing.BlogInvitationActivity"
android:label="@string/blogs_sharing_invitations_title"
android:parentActivityName="org.briarproject.briar.android.contact.ConversationActivity">
android:parentActivityName=".android.contact.ConversationActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.contact.ConversationActivity"
android:value=".android.contact.ConversationActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.forum.CreateForumActivity"
android:name=".android.forum.CreateForumActivity"
android:label="@string/create_forum_title"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:windowSoftInputMode="adjustResize">
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
android:windowSoftInputMode="stateVisible">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:value=".android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.forum.ForumActivity"
android:name=".android.forum.ForumActivity"
android:label="@string/app_name"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar"
android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:value=".android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.sharing.ShareForumActivity"
android:name=".android.sharing.ShareForumActivity"
android:label="@string/activity_share_toolbar_header"
android:parentActivityName="org.briarproject.briar.android.forum.ForumActivity"
android:parentActivityName=".android.forum.ForumActivity"
android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.forum.ForumActivity"
android:value=".android.forum.ForumActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.sharing.ShareBlogActivity"
android:name=".android.sharing.ShareBlogActivity"
android:label="@string/activity_share_toolbar_header"
android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity"
android:parentActivityName=".android.blog.BlogActivity"
android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.blog.BlogActivity"
android:value=".android.blog.BlogActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.sharing.ForumSharingStatusActivity"
android:name=".android.sharing.ForumSharingStatusActivity"
android:label="@string/sharing_status"
android:parentActivityName="org.briarproject.briar.android.forum.ForumActivity">
android:parentActivityName=".android.forum.ForumActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.forum.ForumActivity"
android:value=".android.forum.ForumActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.sharing.BlogSharingStatusActivity"
android:name=".android.sharing.BlogSharingStatusActivity"
android:label="@string/sharing_status"
android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity">
android:parentActivityName=".android.blog.BlogActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.blog.BlogActivity"
android:value=".android.blog.BlogActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.blog.BlogActivity"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:name=".android.blog.BlogActivity"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/>
android:value=".android.navdrawer.NavDrawerActivity"/>
</activity>
<activity
android:name="org.briarproject.briar.android.blog.WriteBlogPostActivity"
android:name=".android.blog.WriteBlogPostActivity"
android:label="@string/blogs_write_blog_post"
android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity"
android:parentActivityName=".android.blog.BlogActivity"
android:windowSoftInputMode="stateVisible|adjustResize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.blog.BlogActivity"
android:value=".android.blog.BlogActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.blog.ReblogActivity"
android:name=".android.blog.ReblogActivity"
android:label="@string/blogs_reblog_button"
android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity"
android:parentActivityName=".android.blog.BlogActivity"
android:windowSoftInputMode="stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.blog.BlogActivity"
android:value=".android.blog.BlogActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.blog.RssFeedImportActivity"
android:name=".android.blog.RssFeedImportActivity"
android:label="@string/blogs_rss_feeds_import"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
android:windowSoftInputMode="stateVisible|adjustResize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:value=".android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.blog.RssFeedManageActivity"
android:name=".android.blog.RssFeedManageActivity"
android:label="@string/blogs_rss_feeds_manage"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
android:parentActivityName=".android.navdrawer.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:value=".android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.keyagreement.KeyAgreementActivity"
android:name=".android.invitation.AddContactActivity"
android:label="@string/add_contact_title"
android:parentActivityName=".android.navdrawer.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name=".android.keyagreement.KeyAgreementActivity"
android:label="@string/add_contact_title"
android:theme="@style/BriarTheme.NoActionBar"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
android:parentActivityName=".android.navdrawer.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/>
android:value=".android.navdrawer.NavDrawerActivity"/>
</activity>
<activity
android:name="org.briarproject.briar.android.introduction.IntroductionActivity"
android:name=".android.introduction.IntroductionActivity"
android:label="@string/introduction_activity_title"
android:parentActivityName="org.briarproject.briar.android.contact.ConversationActivity"
android:parentActivityName=".android.contact.ConversationActivity"
android:windowSoftInputMode="stateHidden|adjustResize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.contact.ConversationActivity"
android:value=".android.contact.ConversationActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.StartupFailureActivity"
android:name=".android.StartupFailureActivity"
android:label="@string/startup_failed_activity_title">
</activity>
<activity
android:name="org.briarproject.briar.android.settings.SettingsActivity"
android:name=".android.settings.SettingsActivity"
android:label="@string/settings_button"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
android:permission="android.permission.READ_NETWORK_USAGE_HISTORY">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:value=".android.navdrawer.NavDrawerActivity"
/>
<intent-filter>
<action android:name="android.intent.action.MANAGE_NETWORK_USAGE"/>
@@ -337,27 +344,27 @@
</activity>
<activity
android:name="org.briarproject.briar.android.login.ChangePasswordActivity"
android:name=".android.login.ChangePasswordActivity"
android:label="@string/change_password"
android:parentActivityName="org.briarproject.briar.android.settings.SettingsActivity">
android:parentActivityName=".android.settings.SettingsActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.settings.SettingsActivity"
android:value=".android.settings.SettingsActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.panic.PanicPreferencesActivity"
android:name=".android.panic.PanicPreferencesActivity"
android:label="@string/panic_setting"
android:parentActivityName="org.briarproject.briar.android.settings.SettingsActivity">
android:parentActivityName=".android.settings.SettingsActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.settings.SettingsActivity"
android:value=".android.settings.SettingsActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.panic.PanicResponderActivity"
android:name=".android.panic.PanicResponderActivity"
android:noHistory="true"
android:theme="@android:style/Theme.NoDisplay">
<!-- this can never have launchMode singleTask or singleInstance! -->
@@ -368,7 +375,7 @@
</activity>
<activity
android:name="org.briarproject.briar.android.panic.ExitActivity"
android:name=".android.panic.ExitActivity"
android:theme="@android:style/Theme.NoDisplay">
</activity>

View File

@@ -12,6 +12,7 @@ import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.invitation.InvitationTaskFactory;
import org.briarproject.bramble.api.keyagreement.KeyAgreementTaskFactory;
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
import org.briarproject.bramble.api.keyagreement.PayloadParser;
@@ -26,6 +27,7 @@ import org.briarproject.briar.BriarCoreEagerSingletons;
import org.briarproject.briar.BriarCoreModule;
import org.briarproject.briar.android.reporting.BriarReportSender;
import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.android.ReferenceManager;
import org.briarproject.briar.api.android.ScreenFilterMonitor;
import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.blog.BlogPostFactory;
@@ -43,7 +45,6 @@ import org.briarproject.briar.api.privategroup.PrivateGroupFactory;
import org.briarproject.briar.api.privategroup.PrivateGroupManager;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationFactory;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
import org.briarproject.briar.api.test.TestDataCreator;
import org.thoughtcrime.securesms.components.emoji.EmojiProvider;
import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel;
@@ -73,6 +74,8 @@ public interface AndroidComponent
DatabaseConfig databaseConfig();
ReferenceManager referenceMangager();
@DatabaseExecutor
Executor databaseExecutor();
@@ -86,6 +89,8 @@ public interface AndroidComponent
EventBus eventBus();
InvitationTaskFactory invitationTaskFactory();
AndroidNotificationManager androidNotificationManager();
ScreenFilterMonitor screenFilterMonitor();
@@ -138,8 +143,6 @@ public interface AndroidComponent
Clock clock();
TestDataCreator testDataCreator();
@IoExecutor
Executor ioExecutor();
@@ -151,8 +154,6 @@ public interface AndroidComponent
void inject(RecentEmojiPageModel recentEmojiPageModel);
void inject(NotificationCleanupService notificationCleanupService);
// Eager singleton load
void inject(AppModule.EagerSingletons init);
}

View File

@@ -2,12 +2,14 @@ package org.briarproject.briar.android;
import android.app.Application;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.UiThread;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.support.v4.content.ContextCompat;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DatabaseExecutor;
@@ -23,14 +25,12 @@ import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.R;
import org.briarproject.briar.android.contact.ConversationActivity;
import org.briarproject.briar.android.forum.ForumActivity;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import org.briarproject.briar.android.privategroup.conversation.GroupActivity;
import org.briarproject.briar.android.util.BriarNotificationBuilder;
import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.blog.event.BlogPostAddedEvent;
import org.briarproject.briar.api.forum.event.ForumPostReceivedEvent;
@@ -48,7 +48,6 @@ import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
@@ -62,6 +61,7 @@ import static android.content.Context.NOTIFICATION_SERVICE;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.support.v4.app.NotificationCompat.CATEGORY_MESSAGE;
import static android.support.v4.app.NotificationCompat.CATEGORY_SOCIAL;
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
import static java.util.logging.Level.WARNING;
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
import static org.briarproject.briar.android.contact.ConversationActivity.CONTACT_ID;
@@ -84,7 +84,15 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
private static final int BLOG_POST_NOTIFICATION_ID = 6;
private static final int INTRODUCTION_SUCCESS_NOTIFICATION_ID = 7;
private static final long SOUND_DELAY = TimeUnit.SECONDS.toMillis(2);
// Content URIs to differentiate between pending intents
private static final String CONTACT_URI =
"content://org.briarproject.briar/contact";
private static final String GROUP_URI =
"content://org.briarproject.briar/group";
private static final String FORUM_URI =
"content://org.briarproject.briar/forum";
private static final String BLOG_URI =
"content://org.briarproject.briar/blog";
private static final Logger LOG =
Logger.getLogger(AndroidNotificationManagerImpl.class.getName());
@@ -93,7 +101,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
private final SettingsManager settingsManager;
private final AndroidExecutor androidExecutor;
private final Context appContext;
private final Clock clock;
private final AtomicBoolean used = new AtomicBoolean(false);
// The following must only be accessed on the main UI thread
@@ -109,18 +116,16 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
private boolean blockContacts = false, blockGroups = false;
private boolean blockForums = false, blockBlogs = false;
private boolean blockIntroductions = false;
private long lastSound = 0;
private volatile Settings settings = new Settings();
@Inject
AndroidNotificationManagerImpl(@DatabaseExecutor Executor dbExecutor,
SettingsManager settingsManager, AndroidExecutor androidExecutor,
Application app, Clock clock) {
Application app) {
this.dbExecutor = dbExecutor;
this.settingsManager = settingsManager;
this.androidExecutor = androidExecutor;
this.clock = clock;
appContext = app.getApplicationContext();
}
@@ -259,7 +264,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
if (count == null) contactCounts.put(c, 1);
else contactCounts.put(c, count + 1);
contactTotal++;
updateContactNotification(true);
updateContactNotification();
}
});
}
@@ -272,30 +277,32 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
Integer count = contactCounts.remove(c);
if (count == null) return; // Already cleared
contactTotal -= count;
updateContactNotification(false);
updateContactNotification();
}
});
}
@UiThread
private void updateContactNotification(boolean mayAlertAgain) {
private void updateContactNotification() {
if (contactTotal == 0) {
clearContactNotification();
} else if (settings.getBoolean(PREF_NOTIFY_PRIVATE, true)) {
BriarNotificationBuilder b =
new BriarNotificationBuilder(appContext);
NotificationCompat.Builder b =
new NotificationCompat.Builder(appContext);
b.setSmallIcon(R.drawable.notification_private_message);
b.setColorRes(R.color.briar_primary);
b.setColor(ContextCompat.getColor(appContext,
R.color.briar_primary));
b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString(
R.plurals.private_message_notification_text, contactTotal,
contactTotal));
b.setNumber(contactTotal);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_MESSAGE, showOnLockScreen);
if (mayAlertAgain) setAlertProperties(b);
setDeleteIntent(b, CONTACT_URI);
boolean sound = settings.getBoolean(PREF_NOTIFY_SOUND, true);
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
if (sound && !StringUtils.isNullOrEmpty(ringtoneUri))
b.setSound(Uri.parse(ringtoneUri));
b.setDefaults(getDefaults());
b.setOnlyAlertOnce(true);
b.setAutoCancel(true);
if (contactCounts.size() == 1) {
// Touching the notification shows the relevant conversation
Intent i = new Intent(appContext, ConversationActivity.class);
@@ -318,25 +325,16 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
t.addNextIntent(i);
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
}
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_MESSAGE);
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
nm.notify(PRIVATE_MESSAGE_NOTIFICATION_ID, b.build());
}
}
@UiThread
private void setAlertProperties(BriarNotificationBuilder b) {
long currentTime = clock.currentTimeMillis();
if (currentTime - lastSound > SOUND_DELAY) {
boolean sound = settings.getBoolean(PREF_NOTIFY_SOUND, true);
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
if (sound && !StringUtils.isNullOrEmpty(ringtoneUri))
b.setSound(Uri.parse(ringtoneUri));
b.setDefaults(getDefaults());
lastSound = currentTime;
}
}
@UiThread
private int getDefaults() {
int defaults = DEFAULT_LIGHTS;
@@ -349,19 +347,13 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
return defaults;
}
private void setDeleteIntent(BriarNotificationBuilder b, String uri) {
Intent i = new Intent(appContext, NotificationCleanupService.class);
i.setData(Uri.parse(uri));
b.setDeleteIntent(PendingIntent.getService(appContext, nextRequestId++,
i, 0));
}
@Override
public void clearAllContactNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
clearContactNotification();
clearIntroductionSuccessNotification();
}
});
}
@@ -377,7 +369,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
if (count == null) groupCounts.put(g, 1);
else groupCounts.put(g, count + 1);
groupTotal++;
updateGroupMessageNotification(true);
updateGroupMessageNotification();
}
});
}
@@ -390,30 +382,31 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
Integer count = groupCounts.remove(g);
if (count == null) return; // Already cleared
groupTotal -= count;
updateGroupMessageNotification(false);
updateGroupMessageNotification();
}
});
}
@UiThread
private void updateGroupMessageNotification(boolean mayAlertAgain) {
private void updateGroupMessageNotification() {
if (groupTotal == 0) {
clearGroupMessageNotification();
} else if (settings.getBoolean(PREF_NOTIFY_GROUP, true)) {
BriarNotificationBuilder b =
new BriarNotificationBuilder(appContext);
NotificationCompat.Builder b =
new NotificationCompat.Builder(appContext);
b.setSmallIcon(R.drawable.notification_private_group);
b.setColorRes(R.color.briar_primary);
b.setColor(ContextCompat.getColor(appContext,
R.color.briar_primary));
b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString(
R.plurals.group_message_notification_text, groupTotal,
groupTotal));
b.setNumber(groupTotal);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_SOCIAL, showOnLockScreen);
if (mayAlertAgain) setAlertProperties(b);
setDeleteIntent(b, GROUP_URI);
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
if (!StringUtils.isNullOrEmpty(ringtoneUri))
b.setSound(Uri.parse(ringtoneUri));
b.setDefaults(getDefaults());
b.setOnlyAlertOnce(true);
b.setAutoCancel(true);
if (groupCounts.size() == 1) {
// Touching the notification shows the relevant group
Intent i = new Intent(appContext, GroupActivity.class);
@@ -437,6 +430,10 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
t.addNextIntent(i);
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
}
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_SOCIAL);
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
nm.notify(GROUP_MESSAGE_NOTIFICATION_ID, b.build());
@@ -464,7 +461,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
if (count == null) forumCounts.put(g, 1);
else forumCounts.put(g, count + 1);
forumTotal++;
updateForumPostNotification(true);
updateForumPostNotification();
}
});
}
@@ -477,30 +474,31 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
Integer count = forumCounts.remove(g);
if (count == null) return; // Already cleared
forumTotal -= count;
updateForumPostNotification(false);
updateForumPostNotification();
}
});
}
@UiThread
private void updateForumPostNotification(boolean mayAlertAgain) {
private void updateForumPostNotification() {
if (forumTotal == 0) {
clearForumPostNotification();
} else if (settings.getBoolean(PREF_NOTIFY_FORUM, true)) {
BriarNotificationBuilder b =
new BriarNotificationBuilder(appContext);
NotificationCompat.Builder b =
new NotificationCompat.Builder(appContext);
b.setSmallIcon(R.drawable.notification_forum);
b.setColorRes(R.color.briar_primary);
b.setColor(ContextCompat.getColor(appContext,
R.color.briar_primary));
b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString(
R.plurals.forum_post_notification_text, forumTotal,
forumTotal));
b.setNumber(forumTotal);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_SOCIAL, showOnLockScreen);
if (mayAlertAgain) setAlertProperties(b);
setDeleteIntent(b, FORUM_URI);
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
if (!StringUtils.isNullOrEmpty(ringtoneUri))
b.setSound(Uri.parse(ringtoneUri));
b.setDefaults(getDefaults());
b.setOnlyAlertOnce(true);
b.setAutoCancel(true);
if (forumCounts.size() == 1) {
// Touching the notification shows the relevant forum
Intent i = new Intent(appContext, ForumActivity.class);
@@ -524,6 +522,10 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
t.addNextIntent(i);
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
}
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_SOCIAL);
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
nm.notify(FORUM_POST_NOTIFICATION_ID, b.build());
@@ -551,7 +553,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
if (count == null) blogCounts.put(g, 1);
else blogCounts.put(g, count + 1);
blogTotal++;
updateBlogPostNotification(true);
updateBlogPostNotification();
}
});
}
@@ -564,30 +566,31 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
Integer count = blogCounts.remove(g);
if (count == null) return; // Already cleared
blogTotal -= count;
updateBlogPostNotification(false);
updateBlogPostNotification();
}
});
}
@UiThread
private void updateBlogPostNotification(boolean mayAlertAgain) {
private void updateBlogPostNotification() {
if (blogTotal == 0) {
clearBlogPostNotification();
} else if (settings.getBoolean(PREF_NOTIFY_BLOG, true)) {
BriarNotificationBuilder b =
new BriarNotificationBuilder(appContext);
NotificationCompat.Builder b =
new NotificationCompat.Builder(appContext);
b.setSmallIcon(R.drawable.notification_blog);
b.setColorRes(R.color.briar_primary);
b.setColor(ContextCompat.getColor(appContext,
R.color.briar_primary));
b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString(
R.plurals.blog_post_notification_text, blogTotal,
blogTotal));
b.setNumber(blogTotal);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_SOCIAL, showOnLockScreen);
if (mayAlertAgain) setAlertProperties(b);
setDeleteIntent(b, BLOG_URI);
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
if (!StringUtils.isNullOrEmpty(ringtoneUri))
b.setSound(Uri.parse(ringtoneUri));
b.setDefaults(getDefaults());
b.setOnlyAlertOnce(true);
b.setAutoCancel(true);
// Touching the notification shows the combined blog feed
Intent i = new Intent(appContext, NavDrawerActivity.class);
i.putExtra(INTENT_BLOGS, true);
@@ -597,7 +600,10 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
t.addParentStack(NavDrawerActivity.class);
t.addNextIntent(i);
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_SOCIAL);
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
nm.notify(BLOG_POST_NOTIFICATION_ID, b.build());
@@ -627,18 +633,20 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
@UiThread
private void updateIntroductionNotification() {
BriarNotificationBuilder b = new BriarNotificationBuilder(appContext);
NotificationCompat.Builder b =
new NotificationCompat.Builder(appContext);
b.setSmallIcon(R.drawable.notification_introduction);
b.setColorRes(R.color.briar_primary);
b.setColor(ContextCompat.getColor(appContext, R.color.briar_primary));
b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString(
R.plurals.introduction_notification_text, introductionTotal,
introductionTotal));
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_MESSAGE, showOnLockScreen);
setAlertProperties(b);
setDeleteIntent(b, INTRODUCTION_URI);
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
if (!StringUtils.isNullOrEmpty(ringtoneUri))
b.setSound(Uri.parse(ringtoneUri));
b.setDefaults(getDefaults());
b.setOnlyAlertOnce(true);
b.setAutoCancel(true);
// Touching the notification shows the contact list
Intent i = new Intent(appContext, NavDrawerActivity.class);
i.putExtra(INTENT_CONTACTS, true);
@@ -648,22 +656,15 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
t.addParentStack(NavDrawerActivity.class);
t.addNextIntent(i);
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_MESSAGE);
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
nm.notify(INTRODUCTION_SUCCESS_NOTIFICATION_ID, b.build());
}
@Override
public void clearAllIntroductionNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
clearIntroductionSuccessNotification();
}
});
}
@Override
public void blockNotification(final GroupId g) {
androidExecutor.runOnUiThread(new Runnable() {
@@ -704,6 +705,68 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
});
}
@Override
public void blockAllContactNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
blockContacts = true;
blockIntroductions = true;
}
});
}
@Override
public void unblockAllContactNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
blockContacts = false;
blockIntroductions = false;
}
});
}
@Override
public void blockAllGroupMessageNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
blockGroups = true;
}
});
}
@Override
public void unblockAllGroupMessageNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
blockGroups = false;
}
});
}
@Override
public void blockAllForumPostNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
blockForums = true;
}
});
}
@Override
public void unblockAllForumPostNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
blockForums = false;
}
});
}
@Override
public void blockAllBlogPostNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@@ -723,4 +786,5 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
}
});
}
}

View File

@@ -38,6 +38,8 @@ public class AppModule {
static class EagerSingletons {
@Inject
AndroidNotificationManager androidNotificationManager;
@Inject
ScreenFilterMonitor screenFilterMonitor;
}
private final Application application;
@@ -169,8 +171,10 @@ public class AppModule {
}
@Provides
@Singleton
ScreenFilterMonitor provideScreenFilterMonitor(
ScreenFilterMonitorImpl screenFilterMonitor) {
return screenFilterMonitor;
LifecycleManager lifecycleManager, ScreenFilterMonitorImpl sfm) {
lifecycleManager.registerService(sfm);
return sfm;
}
}

View File

@@ -6,9 +6,5 @@ package org.briarproject.briar.android;
*/
public interface BriarApplication {
// This build expires on 31 December 2017
long EXPIRY_DATE = 1514761200 * 1000L;
AndroidComponent getApplicationComponent();
}

View File

@@ -2,9 +2,6 @@ package org.briarproject.briar.android;
import android.app.Application;
import android.content.Context;
import android.os.StrictMode;
import android.os.StrictMode.ThreadPolicy;
import android.os.StrictMode.VmPolicy;
import org.acra.ACRA;
import org.acra.ReportingInteractionMode;
@@ -36,8 +33,6 @@ import static org.acra.ReportField.REPORT_ID;
import static org.acra.ReportField.STACK_TRACE;
import static org.acra.ReportField.USER_APP_START_DATE;
import static org.acra.ReportField.USER_CRASH_DATE;
import static org.briarproject.briar.android.TestingConstants.DEFAULT_LOG_LEVEL;
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
@ReportsCrashes(
reportPrimerClass = BriarReportPrimer.class,
@@ -77,9 +72,6 @@ public class BriarApplicationImpl extends Application
@Override
public void onCreate() {
super.onCreate();
if (IS_DEBUG_BUILD) enableStrictMode();
Logger.getLogger("").setLevel(DEFAULT_LOG_LEVEL);
LOG.info("Created");
applicationComponent = DaggerAndroidComponent.builder()
@@ -93,17 +85,6 @@ public class BriarApplicationImpl extends Application
AndroidEagerSingletons.initEagerSingletons(applicationComponent);
}
private void enableStrictMode() {
ThreadPolicy.Builder threadPolicy = new ThreadPolicy.Builder();
threadPolicy.detectAll();
threadPolicy.penaltyLog();
StrictMode.setThreadPolicy(threadPolicy.build());
VmPolicy.Builder vmPolicy = new VmPolicy.Builder();
vmPolicy.detectAll();
vmPolicy.penaltyLog();
StrictMode.setVmPolicy(vmPolicy.build());
}
@Override
public AndroidComponent getApplicationComponent() {
return applicationComponent;

View File

@@ -1,53 +0,0 @@
package org.briarproject.briar.android;
import android.app.IntentService;
import android.content.Intent;
import android.support.annotation.Nullable;
import org.briarproject.briar.api.android.AndroidNotificationManager;
import javax.inject.Inject;
import static org.briarproject.briar.api.android.AndroidNotificationManager.BLOG_URI;
import static org.briarproject.briar.api.android.AndroidNotificationManager.CONTACT_URI;
import static org.briarproject.briar.api.android.AndroidNotificationManager.FORUM_URI;
import static org.briarproject.briar.api.android.AndroidNotificationManager.GROUP_URI;
import static org.briarproject.briar.api.android.AndroidNotificationManager.INTRODUCTION_URI;
public class NotificationCleanupService extends IntentService {
private static final String TAG =
NotificationCleanupService.class.getName();
@Inject
AndroidNotificationManager notificationManager;
public NotificationCleanupService() {
super(TAG);
}
@Override
public void onCreate() {
super.onCreate();
AndroidComponent applicationComponent =
((BriarApplication) getApplication()).getApplicationComponent();
applicationComponent.inject(this);
}
@Override
protected void onHandleIntent(@Nullable Intent i) {
if (i == null || i.getData() == null) return;
String uri = i.getData().toString();
if (uri.equals(CONTACT_URI)) {
notificationManager.clearAllContactNotifications();
} else if (uri.equals(GROUP_URI)) {
notificationManager.clearAllGroupMessageNotifications();
} else if (uri.equals(FORUM_URI)) {
notificationManager.clearAllForumPostNotifications();
} else if (uri.equals(BLOG_URI)) {
notificationManager.clearAllBlogPostNotifications();
} else if (uri.equals(INTRODUCTION_URI)) {
notificationManager.clearAllIntroductionNotifications();
}
}
}

View File

@@ -1,13 +1,23 @@
package org.briarproject.briar.android;
import android.app.Application;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.support.annotation.UiThread;
import android.support.v7.preference.PreferenceManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.lifecycle.ServiceException;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.api.android.ScreenFilterMonitor;
@@ -16,26 +26,38 @@ import java.io.InputStream;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.EXTRA_REPLACING;
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.GET_SIGNATURES;
import static java.util.logging.Level.WARNING;
@NotNullByDefault
class ScreenFilterMonitorImpl implements ScreenFilterMonitor {
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class ScreenFilterMonitorImpl extends BroadcastReceiver
implements Service, ScreenFilterMonitor {
private static final Logger LOG =
Logger.getLogger(ScreenFilterMonitorImpl.class.getName());
private static final String PREF_SCREEN_FILTER_APPS =
"shownScreenFilterApps";
/*
* Ignore Play Services if it uses this package name and public key - it's
@@ -56,17 +78,124 @@ class ScreenFilterMonitorImpl implements ScreenFilterMonitor {
"82BA35E003C1B4B10DD244A8EE24FFFD333872AB5221985EDAB0FC0D" +
"0B145B6AA192858E79020103";
private final Context appContext;
private final AndroidExecutor androidExecutor;
private final PackageManager pm;
private final SharedPreferences prefs;
private final AtomicBoolean used = new AtomicBoolean(false);
// The following must only be accessed on the UI thread
private final Set<String> apps = new HashSet<>();
private final Set<String> shownApps;
private boolean serviceStarted = false;
@Inject
ScreenFilterMonitorImpl(Application app) {
pm = app.getPackageManager();
ScreenFilterMonitorImpl(AndroidExecutor executor, Application app) {
this.androidExecutor = executor;
this.appContext = app;
pm = appContext.getPackageManager();
prefs = PreferenceManager.getDefaultSharedPreferences(appContext);
shownApps = getShownScreenFilterApps();
}
@Override
public void startService() throws ServiceException {
if (used.getAndSet(true)) throw new IllegalStateException();
Future<Void> f = androidExecutor.runOnUiThread(new Callable<Void>() {
@Override
public Void call() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_PACKAGE_ADDED);
intentFilter.addDataScheme("package");
appContext.registerReceiver(ScreenFilterMonitorImpl.this,
intentFilter);
apps.addAll(getInstalledScreenFilterApps());
serviceStarted = true;
return null;
}
});
try {
f.get();
} catch (InterruptedException | ExecutionException e) {
throw new ServiceException(e);
}
}
@Override
public void stopService() throws ServiceException {
Future<Void> f = androidExecutor.runOnUiThread(new Callable<Void>() {
@Override
public Void call() {
serviceStarted = false;
appContext.unregisterReceiver(ScreenFilterMonitorImpl.this);
return null;
}
});
try {
f.get();
} catch (InterruptedException | ExecutionException e) {
throw new ServiceException(e);
}
}
private Set<String> getShownScreenFilterApps() {
// Result must not be modified
Set<String> s = prefs.getStringSet(PREF_SCREEN_FILTER_APPS, null);
HashSet<String> result = new HashSet<>();
if (s != null) {
result.addAll(s);
}
return result;
}
@Override
public void onReceive(Context context, Intent intent) {
if (!intent.getBooleanExtra(EXTRA_REPLACING, false)) {
final String packageName =
intent.getData().getEncodedSchemeSpecificPart();
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
String pkg = isOverlayApp(packageName);
if (pkg == null) {
return;
}
apps.add(pkg);
}
});
}
}
@Override
@UiThread
public Set<String> getApps() {
Set<String> screenFilterApps = new TreeSet<>();
if (!serviceStarted) {
apps.addAll(getInstalledScreenFilterApps());
}
TreeSet<String> buf = new TreeSet<>();
if (apps.isEmpty()) {
return buf;
}
buf.addAll(apps);
buf.removeAll(shownApps);
return buf;
}
@Override
@UiThread
public void storeAppsAsShown(Collection<String> s, boolean persistent) {
HashSet<String> buf = new HashSet<>(s);
shownApps.addAll(buf);
if (persistent && !s.isEmpty()) {
buf.addAll(getShownScreenFilterApps());
prefs.edit()
.putStringSet(PREF_SCREEN_FILTER_APPS, buf)
.apply();
}
}
private Set<String> getInstalledScreenFilterApps() {
HashSet<String> screenFilterApps = new HashSet<>();
List<PackageInfo> packageInfos =
pm.getInstalledPackages(GET_PERMISSIONS);
for (PackageInfo packageInfo : packageInfos) {
@@ -80,6 +209,21 @@ class ScreenFilterMonitorImpl implements ScreenFilterMonitor {
return screenFilterApps;
}
// Checks if a package uses the SYSTEM_ALERT_WINDOW permission and if so
// returns the app name.
@Nullable
private String isOverlayApp(String pkg) {
try {
PackageInfo pkgInfo = pm.getPackageInfo(pkg, GET_PERMISSIONS);
if (isOverlayApp(pkgInfo)) {
return pkgToString(pkgInfo);
}
} catch (NameNotFoundException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
return null;
}
// Fetches the application name for a given package.
@Nullable
private String pkgToString(PackageInfo pkgInfo) {

View File

@@ -10,21 +10,13 @@ import static java.util.logging.Level.OFF;
public interface TestingConstants {
/**
* Whether this is a debug build.
*/
boolean IS_DEBUG_BUILD = BuildConfig.DEBUG;
/**
* Whether this is a beta build. This should be set to false for final
* Whether this is an alpha or beta build. This should be set to false for
* release builds.
*/
boolean IS_BETA_BUILD = true;
boolean TESTING = BuildConfig.DEBUG;
/**
* Default log level. Disable logging for final release builds.
*/
@SuppressWarnings("ConstantConditions")
Level DEFAULT_LOG_LEVEL = IS_DEBUG_BUILD || IS_BETA_BUILD ? INFO : OFF;
/** Default log level. */
Level DEFAULT_LOG_LEVEL = TESTING ? INFO : OFF;
/**
* Whether to prevent screenshots from being taken. Setting this to true
@@ -32,5 +24,5 @@ public interface TestingConstants {
* Unfortunately this also prevents the user from taking screenshots
* intentionally.
*/
boolean PREVENT_SCREENSHOTS = !IS_DEBUG_BUILD;
boolean PREVENT_SCREENSHOTS = !TESTING;
}

View File

@@ -24,6 +24,7 @@ import org.briarproject.briar.android.forum.ForumModule;
import org.briarproject.briar.android.introduction.ContactChooserFragment;
import org.briarproject.briar.android.introduction.IntroductionActivity;
import org.briarproject.briar.android.introduction.IntroductionMessageFragment;
import org.briarproject.briar.android.invitation.AddContactActivity;
import org.briarproject.briar.android.keyagreement.IntroFragment;
import org.briarproject.briar.android.keyagreement.KeyAgreementActivity;
import org.briarproject.briar.android.keyagreement.ShowQrCodeFragment;
@@ -38,7 +39,7 @@ import org.briarproject.briar.android.privategroup.conversation.GroupConversatio
import org.briarproject.briar.android.privategroup.creation.CreateGroupActivity;
import org.briarproject.briar.android.privategroup.creation.CreateGroupFragment;
import org.briarproject.briar.android.privategroup.creation.CreateGroupMessageFragment;
import org.briarproject.briar.android.privategroup.creation.CreateGroupModule;
import org.briarproject.briar.android.privategroup.creation.GroupCreateModule;
import org.briarproject.briar.android.privategroup.creation.GroupInviteActivity;
import org.briarproject.briar.android.privategroup.creation.GroupInviteFragment;
import org.briarproject.briar.android.privategroup.invitation.GroupInvitationActivity;
@@ -51,7 +52,6 @@ import org.briarproject.briar.android.privategroup.reveal.GroupRevealModule;
import org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity;
import org.briarproject.briar.android.privategroup.reveal.RevealContactsFragment;
import org.briarproject.briar.android.settings.SettingsActivity;
import org.briarproject.briar.android.settings.SettingsFragment;
import org.briarproject.briar.android.sharing.BlogInvitationActivity;
import org.briarproject.briar.android.sharing.BlogSharingStatusActivity;
import org.briarproject.briar.android.sharing.ForumInvitationActivity;
@@ -71,7 +71,7 @@ import dagger.Component;
@Component(
modules = {ActivityModule.class, ForumModule.class, SharingModule.class,
BlogModule.class, ContactModule.class, GroupListModule.class,
CreateGroupModule.class, GroupInvitationModule.class,
GroupCreateModule.class, GroupInvitationModule.class,
GroupConversationModule.class, GroupMemberModule.class,
GroupRevealModule.class},
dependencies = AndroidComponent.class)
@@ -91,6 +91,8 @@ public interface ActivityComponent {
void inject(PanicPreferencesActivity activity);
void inject(AddContactActivity activity);
void inject(KeyAgreementActivity activity);
void inject(ConversationActivity activity);
@@ -180,6 +182,4 @@ public interface ActivityComponent {
void inject(IntroductionMessageFragment fragment);
void inject(SettingsFragment fragment);
}

View File

@@ -2,13 +2,9 @@ package org.briarproject.briar.android.activity;
import android.os.Bundle;
import android.os.IBinder;
import android.support.annotation.LayoutRes;
import android.support.annotation.UiThread;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.inputmethod.InputMethodManager;
import org.briarproject.bramble.api.db.DbException;
@@ -17,9 +13,7 @@ import org.briarproject.briar.android.BriarApplication;
import org.briarproject.briar.android.DestroyableContext;
import org.briarproject.briar.android.controller.ActivityLifecycleController;
import org.briarproject.briar.android.forum.ForumModule;
import org.briarproject.briar.android.fragment.ScreenFilterDialogFragment;
import org.briarproject.briar.android.widget.TapSafeFrameLayout;
import org.briarproject.briar.android.widget.TapSafeFrameLayout.OnTapFilteredListener;
import org.briarproject.briar.android.fragment.SFDialogFragment;
import org.briarproject.briar.api.android.ScreenFilterMonitor;
import java.util.ArrayList;
@@ -29,23 +23,21 @@ import java.util.Set;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOTS;
public abstract class BaseActivity extends AppCompatActivity
implements DestroyableContext, OnTapFilteredListener {
@Inject
protected ScreenFilterMonitor screenFilterMonitor;
implements DestroyableContext {
protected ActivityComponent activityComponent;
private final List<ActivityLifecycleController> lifecycleControllers =
new ArrayList<>();
private boolean destroyed = false;
private ScreenFilterDialogFragment dialogFrag;
@Inject
protected ScreenFilterMonitor screenFilterMonitor;
private SFDialogFragment dialogFrag;
public abstract void injectActivity(ActivityComponent component);
@@ -73,6 +65,7 @@ public abstract class BaseActivity extends AppCompatActivity
for (ActivityLifecycleController alc : lifecycleControllers) {
alc.onActivityCreate(this);
}
}
public ActivityComponent getActivityComponent() {
@@ -104,6 +97,12 @@ public abstract class BaseActivity extends AppCompatActivity
}
}
@Override
protected void onPostResume() {
super.onPostResume();
showNewScreenFilterWarning();
}
@Override
protected void onPause() {
super.onPause();
@@ -113,14 +112,18 @@ public abstract class BaseActivity extends AppCompatActivity
}
}
private void showScreenFilterWarning() {
if (dialogFrag != null && dialogFrag.isVisible()) return;
Set<String> apps = screenFilterMonitor.getApps();
if (apps.isEmpty()) return;
dialogFrag =
ScreenFilterDialogFragment.newInstance(new ArrayList<>(apps));
protected void showNewScreenFilterWarning() {
final Set<String> apps = screenFilterMonitor.getApps();
if (apps.isEmpty()) {
return;
}
dialogFrag = SFDialogFragment.newInstance(new ArrayList<>(apps));
dialogFrag.setCancelable(false);
dialogFrag.show(getSupportFragmentManager(), dialogFrag.getTag());
dialogFrag.show(getSupportFragmentManager(), "SFDialog");
}
public void rememberShownApps(ArrayList<String> s, boolean permanent) {
screenFilterMonitor.storeAppsAsShown(s, permanent);
}
@Override
@@ -158,70 +161,4 @@ public abstract class BaseActivity extends AppCompatActivity
supportFinishAfterTransition();
}
/*
* Wraps the given view in a wrapper that notifies this activity when an
* obscured touch has been filtered, and returns the wrapper.
*/
private View makeTapSafeWrapper(View v) {
TapSafeFrameLayout wrapper = new TapSafeFrameLayout(this);
wrapper.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
wrapper.setOnTapFilteredListener(this);
wrapper.addView(v);
return wrapper;
}
/*
* Finds the AppCompat toolbar, if any, and configures it to filter
* obscured touches. If a custom toolbar is used, it will be part of the
* content view and thus protected by the wrapper. But the default toolbar
* is outside the wrapper.
*/
private void protectToolbar() {
View decorView = getWindow().getDecorView();
if (decorView instanceof ViewGroup) {
Toolbar toolbar = findToolbar((ViewGroup) decorView);
if (toolbar != null) toolbar.setFilterTouchesWhenObscured(true);
}
}
@Nullable
private Toolbar findToolbar(ViewGroup vg) {
for (int i = 0, len = vg.getChildCount(); i < len; i++) {
View child = vg.getChildAt(i);
if (child instanceof Toolbar) return (Toolbar) child;
if (child instanceof ViewGroup) {
Toolbar toolbar = findToolbar((ViewGroup) child);
if (toolbar != null) return toolbar;
}
}
return null;
}
@Override
public void setContentView(@LayoutRes int layoutRes) {
setContentView(getLayoutInflater().inflate(layoutRes, null));
}
@Override
public void setContentView(View v) {
super.setContentView(makeTapSafeWrapper(v));
protectToolbar();
}
@Override
public void setContentView(View v, LayoutParams layoutParams) {
super.setContentView(makeTapSafeWrapper(v), layoutParams);
protectToolbar();
}
@Override
public void addContentView(View v, LayoutParams layoutParams) {
super.addContentView(makeTapSafeWrapper(v), layoutParams);
protectToolbar();
}
@Override
public void onTapFiltered() {
showScreenFilterWarning();
}
}

Some files were not shown because too many files have changed in this diff Show More